diff --git a/data.json b/data.json deleted file mode 100644 index e69de29..0000000 diff --git a/data/data.json b/data/data.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/data/data.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/go.mod b/go.mod index 4b53ffb..73febc1 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,13 @@ module git.send.nrw/sendnrw/fritzbox_dyndns go 1.23.1 + +require github.com/miekg/dns v1.1.62 + +require ( + golang.org/x/mod v0.18.0 // indirect + golang.org/x/net v0.27.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/tools v0.22.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..95e8194 --- /dev/null +++ b/go.sum @@ -0,0 +1,12 @@ +github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= +github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= diff --git a/main.go b/main.go index 83b9c35..ed67490 100644 --- a/main.go +++ b/main.go @@ -1,12 +1,19 @@ package main import ( + "crypto/sha256" + "encoding/hex" "encoding/json" "fmt" + "log" "net/http" "os" + "os/signal" "strings" + "syscall" "time" + + "github.com/miekg/dns" ) type DB map[string]dns_entry @@ -15,7 +22,8 @@ var D map[string]dns_entry type dns_entry struct { Dns string `json:"dns"` - Ip string `json:"ip"` + Ipv4 string `json:"ipv4"` + Ipv6 string `json:"ipv6"` User string `json:"user"` Token string `json:"token"` LastSeen string `json:"lastseen"` @@ -60,9 +68,21 @@ func readFromFile(filename string) (DB, error) { return data, nil } +// HashToken hashes a token using SHA-256. +func HashToken(token string) string { + hash := sha256.Sum256([]byte(token)) + return hex.EncodeToString(hash[:]) +} + +// VerifyToken compares a plaintext token with a stored hash. +func VerifyToken(token, storedHash string) bool { + return HashToken(token) == storedHash +} + func handler(w http.ResponseWriter, r *http.Request) { Dns := r.URL.Query().Get("DDNS") Ip := r.URL.Query().Get("IP") + Ip6 := r.URL.Query().Get("IPv6") User := r.URL.Query().Get("USER") Token := r.URL.Query().Get("TOKEN") @@ -78,12 +98,12 @@ func handler(w http.ResponseWriter, r *http.Request) { } else { - if entry, exists := D[Dns]; exists { - if User == entry.User && Token == entry.Token { - D[Dns] = dns_entry{Dns: Dns, Ip: Ip, User: User, Token: Token, LastSeen: time.Now().String()} - fmt.Println("Eintrag aktualisiert: ", entry, D[Dns]) + if entry, exists := D[Dns+"."]; exists { + if User == entry.User && VerifyToken(Token, entry.Token) { + D[Dns+"."] = dns_entry{Dns: Dns, Ipv4: Ip, Ipv6: Ip6, User: User, Token: entry.Token, LastSeen: time.Now().String()} + fmt.Println("Eintrag aktualisiert: ", entry, D[Dns+"."]) // Datei speichern - filename := "data.json" + filename := "data/data.json" err := writeToFile(filename, D) if err != nil { fmt.Println("Fehler beim Schreiben:", err) @@ -98,10 +118,10 @@ func handler(w http.ResponseWriter, r *http.Request) { w.Write([]byte("nochg")) } } else { - D[Dns] = dns_entry{Dns: Dns, Ip: Ip, User: User, Token: Token, LastSeen: time.Now().String()} - fmt.Println("Eintrag erstellt: ", entry, D[Dns]) + D[Dns+"."] = dns_entry{Dns: Dns, Ipv4: Ip, Ipv6: Ip6, User: User, Token: HashToken(Token), LastSeen: time.Now().String()} + fmt.Println("Eintrag erstellt: ", entry, D[Dns+"."]) // Datei speichern - filename := "data.json" + filename := "data/data.json" err := writeToFile(filename, D) if err != nil { fmt.Println("Fehler beim Schreiben:", err) @@ -119,10 +139,93 @@ func handlerIP(w http.ResponseWriter, r *http.Request) { } +func handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) { + // Bereite die Antwort vor + msg := new(dns.Msg) + msg.SetReply(r) + msg.Authoritative = true + + // Durchlaufe alle Fragen in der Anfrage + for _, q := range r.Question { + switch q.Qtype { + case dns.TypeA: // IPv4-Anfrage + ip, exists := D[q.Name] + if exists { + rr, err := dns.NewRR(q.Name + " A " + ip.Ipv4) + if err == nil { + fmt.Println("Antwort (IPv4):", rr, q.Name) + msg.Answer = append(msg.Answer, rr) + } else { + fmt.Println("Fehler beim Erstellen der Antwort (4):", err) + } + } + case dns.TypeAAAA: // IPv6-Anfrage + // Beispielhafte IPv6-Adresse für Demonstration + ip, exists := D[q.Name] + if exists && !strings.EqualFold(ip.Ipv6, "") { + rr, err := dns.NewRR(q.Name + " AAAA " + ip.Ipv6) + if err == nil { + fmt.Println("Antwort (IPv6):", rr, q.Name) + msg.Answer = append(msg.Answer, rr) + } else { + fmt.Println("Fehler beim Erstellen der Antwort (6):", err) + } + } + default: + // Ignoriere nicht unterstützte Typen + } + } + + // Antwort senden + w.WriteMsg(msg) +} + +func prepareExit() { + fmt.Println("Running exit tasks...") + + fmt.Println("Exit completed.") +} + +func StopServer(e error) { + fmt.Println("Stopping server...") + prepareExit() + fmt.Println("Server stopped!") +} + func main() { + + HTTP_PORT := os.Getenv("HTTP_PORT") + HTTP_TLS := os.Getenv("HTTP_TLS") /* 1/0 */ + HTTP_TLS_PRIVATEKEY := os.Getenv("HTTP_TLS_PRIVATEKEY") + HTTP_TLS_CERTIFICATE := os.Getenv("HTTP_TLS_CERTIFICATE") + + if strings.EqualFold(HTTP_TLS, "") || strings.EqualFold(HTTP_PORT, "") || strings.EqualFold(HTTP_TLS_PRIVATEKEY, "") || strings.EqualFold(HTTP_TLS_CERTIFICATE, "") { + fmt.Println("No port or mode defined. Fallback to TLS=0 & Port=8080") + fmt.Println("ENV's: [HTTP_PORT=8080|443], [HTTP_TLS=0|1],[HTTP_TLS_PRIVATEKEY=#],[HTTP_TLS_CERTIFICATE=#]") + fmt.Println("Remember to set unused ENVs like [HTTP_TLS_PRIVATEKEY] or [HTTP_TLS_CERTIFICATE] to '#'") + HTTP_PORT = "8080" + HTTP_TLS = "0" + HTTP_TLS_CERTIFICATE = "" + HTTP_TLS_PRIVATEKEY = "" + } else { + fmt.Println("Port and mode defined.") + } + + // Signal-Kanal einrichten + stop := make(chan os.Signal, 1) + signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM) + + // Goroutine, die auf Signale wartet + go func() { + <-stop + fmt.Println("Received stop signal") + prepareExit() + os.Exit(0) + }() + D = make(map[string]dns_entry) // Datei lesen - filename := "data.json" + filename := "data/data.json" readData, err := readFromFile(filename) if err != nil { fmt.Println("Fehler beim Lesen:", err) @@ -133,9 +236,41 @@ func main() { http.HandleFunc("/", handler) http.HandleFunc("/ip", handlerIP) - fmt.Println("Server läuft auf http://*:8080") - srv_err := http.ListenAndServe(":8089", nil) + + /* DNS-PART */ + + dns.HandleFunc(".", handleDNSRequest) + + serverUDP := &dns.Server{Addr: ":53", Net: "udp"} + go func() { + log.Println("Starting DNS server on UDP :53") + if err := serverUDP.ListenAndServe(); err != nil { + log.Fatalf("Failed to start UDP server: %v", err) + } + }() + + serverTCP := &dns.Server{Addr: ":53", Net: "tcp"} + go func() { + log.Println("Starting DNS server on TCP :53") + if err := serverTCP.ListenAndServe(); err != nil { + log.Fatalf("Failed to start TCP server: %v", err) + } + }() + + /* HTTP-PART */ + + fmt.Println("Server listening on port :" + HTTP_PORT) + if HTTP_TLS == "0" { + fmt.Println("Protocol is http (insecure)") + StopServer(http.ListenAndServe(":"+HTTP_PORT, nil)) + } + if HTTP_TLS == "1" { + fmt.Println("Protocol is https (secure)") + StopServer(http.ListenAndServeTLS(":"+HTTP_PORT, HTTP_TLS_CERTIFICATE, HTTP_TLS_PRIVATEKEY, nil)) + } + + /*srv_err := http.ListenAndServe(":8080", nil) if srv_err != nil { fmt.Println("Starten des Servers fehlgeschlagen!", srv_err) - } + }*/ }