Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package main
- import (
- "context"
- "encoding/base64"
- "encoding/json"
- "fmt"
- "github.com/gorilla/mux"
- "github.com/miekg/dns"
- "html/template"
- "io/ioutil"
- "log"
- "net"
- "net/http"
- "os"
- "os/signal"
- "strings"
- "sync"
- "syscall"
- "time"
- )
- // DNSRecord represents a DNS record entry.
- type DNSRecord struct {
- Name string
- Type string
- Value string
- Timestamp string
- }
- // DNSDatabase represents the DNS database.
- type DNSDatabase struct {
- sync.RWMutex
- records map[string]DNSRecord
- }
- const (
- appName = "DDNSMY"
- userName = "userName"
- userPass = "userPassword"
- httpHost = "0.0.0.0"
- httpPort = "8080"
- dnsHost = "0.0.0.0"
- dnsPort = "8053"
- logFileName = "ddnsmy.log"
- dnsDBFilename = "ddnsmy.json"
- )
- var (
- dnsDB DNSDatabase
- logger *log.Logger
- )
- func init() {
- dnsDB.records = make(map[string]DNSRecord)
- }
- func loadDNSDatabaseFromFile(filename string) (DNSDatabase, error) {
- jsonBytes, err := ioutil.ReadFile(filename)
- if err != nil {
- return DNSDatabase{}, err
- }
- var db DNSDatabase
- err = json.Unmarshal(jsonBytes, &db.records)
- return db, err
- }
- func saveDNSDatabaseToFile(filename string, db *DNSDatabase) error {
- jsonBytes, err := json.Marshal(db.records)
- if err != nil {
- return err
- }
- err = ioutil.WriteFile(filename, jsonBytes, 0644)
- return err
- }
- func getClientIPAddress(req *http.Request) (string, error) {
- ip, _, err := net.SplitHostPort(req.RemoteAddr)
- return ip, err
- }
- func isAuth(w http.ResponseWriter, r *http.Request) bool {
- authHeader := r.Header.Get("Authorization")
- authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(userName+":"+userPass))
- return authHeader == authStr
- }
- func httpUpdateDNSHandler(w http.ResponseWriter, r *http.Request, dnsDB *DNSDatabase, logger *log.Logger) {
- vars := r.URL.Query()
- // set DNS
- if vars.Get("hostname") != "" && vars.Get("myip") != "" {
- name := strings.Trim(vars.Get("hostname"), ".")
- typ := "A"
- value := vars.Get("myip")
- timestamp := time.Now().Format(time.RFC3339)
- dnsDB.Lock()
- dnsDB.records[name] = DNSRecord{Name: name, Type: typ, Value: value, Timestamp: timestamp}
- dnsDB.Unlock()
- saveDNSDatabaseToFile(dnsDBFilename, dnsDB)
- w.WriteHeader(http.StatusOK)
- respInfo := fmt.Sprintf("DNS Set %s %s %s from %s", name, typ, value, r.RemoteAddr)
- fmt.Fprintf(w, respInfo)
- logger.Printf(respInfo)
- return
- }
- // default
- showDNSRecords(w, r, dnsDB)
- }
- func dnsHandler(w dns.ResponseWriter, r *dns.Msg, dnsDB *DNSDatabase, logger *log.Logger) {
- m := new(dns.Msg)
- m.SetReply(r)
- for _, q := range r.Question {
- name := strings.Trim(q.Name, ".")
- dnsDB.RLock()
- record, ok := dnsDB.records[name]
- dnsDB.RUnlock()
- if ok {
- rr, err := dns.NewRR(fmt.Sprintf("%s %s %s", name, record.Type, record.Value))
- logger.Printf("DNS Responce %s %s %s from %s\n", name, record.Type, record.Value, w.RemoteAddr().String())
- if err == nil {
- m.Answer = append(m.Answer, rr)
- }
- }
- }
- w.WriteMsg(m)
- }
- func setupLogger(logFileName string) *log.Logger {
- logFile, err := os.OpenFile(logFileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
- if err != nil {
- log.Fatalf("Failed to open log file: %v", err)
- }
- return log.New(logFile, "", log.Ldate|log.Ltime)
- }
- func startDNSServer(logger *log.Logger, dnsDB *DNSDatabase) {
- dnsServer := &dns.Server{Addr: net.JoinHostPort(dnsHost, dnsPort), Net: "udp"}
- dns.HandleFunc(".", func(w dns.ResponseWriter, r *dns.Msg) {
- dnsHandler(w, r, dnsDB, logger)
- })
- logger.Printf("DNS server: %s", net.JoinHostPort(dnsHost, dnsPort))
- if err := dnsServer.ListenAndServe(); err != nil {
- logger.Fatalf("DNS server failed: %v", err)
- }
- }
- func startHTTPServer(logger *log.Logger, dnsDB *DNSDatabase) {
- router := mux.NewRouter()
- router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
- if !isAuth(w, r) {
- w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
- w.WriteHeader(http.StatusUnauthorized)
- fmt.Fprintln(w, "Unauthorized")
- return
- }
- httpUpdateDNSHandler(w, r, dnsDB, logger)
- })
- httpServer := &http.Server{
- Addr: net.JoinHostPort(httpHost, httpPort),
- Handler: router,
- ReadTimeout: 5 * time.Second,
- WriteTimeout: 10 * time.Second,
- MaxHeaderBytes: 1 << 20, // 1 MB
- }
- logger.Printf("HTTP server: %s", net.JoinHostPort(httpHost, httpPort))
- if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
- logger.Fatalf("HTTP server failed: %v", err)
- }
- }
- func renderHTMLTable(w http.ResponseWriter, dnsDB *DNSDatabase) {
- const htmlTemplate = `
- <!DOCTYPE html>
- <html>
- <head>
- <title>DNS Records</title>
- </head>
- <body>
- <h1>DNS Records</h1>
- <table border="1" cellspacing="0" cellpadding="2">
- <tr>
- <th>Name</th>
- <th>Type</th>
- <th>Value</th>
- <th>Timestamp</th>
- </tr>
- {{range $name, $record := .}}
- <tr>
- <td>{{$name}}</td>
- <td>{{$record.Type}}</td>
- <td>{{$record.Value}}</td>
- <td>{{$record.Timestamp}}</td>
- </tr>
- {{end}}
- </table>
- </body>
- </html>
- `
- t, err := template.New("dnsTable").Parse(htmlTemplate)
- if err != nil {
- http.Error(w, "Error rendering HTML template", http.StatusInternalServerError)
- return
- }
- dnsDB.RLock()
- defer dnsDB.RUnlock()
- err = t.Execute(w, dnsDB.records)
- if err != nil {
- http.Error(w, "Error executing template", http.StatusInternalServerError)
- return
- }
- }
- // This function should be called to render the HTML table.
- func showDNSRecords(w http.ResponseWriter, r *http.Request, dnsDB *DNSDatabase) {
- w.Header().Set("Content-Type", "text/html; charset=utf-8")
- renderHTMLTable(w, dnsDB)
- }
- func setupGracefulShutdown(logger *log.Logger, dnsDB *DNSDatabase) (context.Context, context.CancelFunc) {
- logger.Printf("START %s", appName)
- shutdownCtx, shutdownFunc := context.WithCancel(context.Background())
- sigCh := make(chan os.Signal, 1)
- signal.Notify(sigCh,
- syscall.SIGHUP,
- syscall.SIGINT,
- syscall.SIGTERM,
- syscall.SIGQUIT)
- go func() {
- sig := <-sigCh
- logger.Printf("STOP %s received signal: %v", appName, sig)
- saveDNSDatabaseToFile(dnsDBFilename, dnsDB)
- shutdownFunc()
- }()
- return shutdownCtx, shutdownFunc
- }
- func main() {
- // Set up graceful shutdown
- logger := setupLogger(logFileName)
- shutdownCtx, _ := setupGracefulShutdown(logger, &dnsDB)
- // Load the DNS database from a JSON file on startup
- if loadedDB, err := loadDNSDatabaseFromFile(dnsDBFilename); err == nil {
- dnsDB = loadedDB
- }
- // Start the DNS server
- go startDNSServer(logger, &dnsDB)
- // Start the HTTP server
- go startHTTPServer(logger, &dnsDB)
- // Block until the shutdown signal is received
- <-shutdownCtx.Done()
- }
Add Comment
Please, Sign In to add comment