Guest User

Untitled

a guest
Jan 7th, 2024
153
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Go 6.86 KB | None | 0 0
  1. package main
  2.  
  3. import (
  4.     "context"
  5.     "encoding/base64"
  6.     "encoding/json"
  7.     "fmt"
  8.     "github.com/gorilla/mux"
  9.     "github.com/miekg/dns"
  10.     "html/template"
  11.     "io/ioutil"
  12.     "log"
  13.     "net"
  14.     "net/http"
  15.     "os"
  16.     "os/signal"
  17.     "strings"
  18.     "sync"
  19.     "syscall"
  20.     "time"
  21. )
  22.  
  23. // DNSRecord represents a DNS record entry.
  24. type DNSRecord struct {
  25.     Name      string
  26.     Type      string
  27.     Value     string
  28.     Timestamp string
  29. }
  30.  
  31. // DNSDatabase represents the DNS database.
  32. type DNSDatabase struct {
  33.     sync.RWMutex
  34.     records map[string]DNSRecord
  35. }
  36.  
  37. const (
  38.     appName       = "DDNSMY"
  39.     userName      = "userName"
  40.     userPass      = "userPassword"
  41.     httpHost      = "0.0.0.0"
  42.     httpPort      = "8080"
  43.     dnsHost       = "0.0.0.0"
  44.     dnsPort       = "8053"
  45.     logFileName   = "ddnsmy.log"
  46.     dnsDBFilename = "ddnsmy.json"
  47. )
  48.  
  49. var (
  50.     dnsDB  DNSDatabase
  51.     logger *log.Logger
  52. )
  53.  
  54. func init() {
  55.     dnsDB.records = make(map[string]DNSRecord)
  56. }
  57.  
  58. func loadDNSDatabaseFromFile(filename string) (DNSDatabase, error) {
  59.     jsonBytes, err := ioutil.ReadFile(filename)
  60.     if err != nil {
  61.         return DNSDatabase{}, err
  62.     }
  63.  
  64.     var db DNSDatabase
  65.     err = json.Unmarshal(jsonBytes, &db.records)
  66.     return db, err
  67. }
  68.  
  69. func saveDNSDatabaseToFile(filename string, db *DNSDatabase) error {
  70.     jsonBytes, err := json.Marshal(db.records)
  71.     if err != nil {
  72.         return err
  73.     }
  74.  
  75.     err = ioutil.WriteFile(filename, jsonBytes, 0644)
  76.     return err
  77. }
  78.  
  79. func getClientIPAddress(req *http.Request) (string, error) {
  80.     ip, _, err := net.SplitHostPort(req.RemoteAddr)
  81.     return ip, err
  82. }
  83.  
  84. func isAuth(w http.ResponseWriter, r *http.Request) bool {
  85.     authHeader := r.Header.Get("Authorization")
  86.     authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(userName+":"+userPass))
  87.     return authHeader == authStr
  88. }
  89.  
  90. func httpUpdateDNSHandler(w http.ResponseWriter, r *http.Request, dnsDB *DNSDatabase, logger *log.Logger) {
  91.     vars := r.URL.Query()
  92.  
  93.     // set DNS
  94.     if vars.Get("hostname") != "" && vars.Get("myip") != "" {
  95.         name := strings.Trim(vars.Get("hostname"), ".")
  96.         typ := "A"
  97.         value := vars.Get("myip")
  98.         timestamp := time.Now().Format(time.RFC3339)
  99.  
  100.         dnsDB.Lock()
  101.         dnsDB.records[name] = DNSRecord{Name: name, Type: typ, Value: value, Timestamp: timestamp}
  102.         dnsDB.Unlock()
  103.  
  104.         saveDNSDatabaseToFile(dnsDBFilename, dnsDB)
  105.  
  106.         w.WriteHeader(http.StatusOK)
  107.  
  108.         respInfo := fmt.Sprintf("DNS Set %s %s %s from %s", name, typ, value, r.RemoteAddr)
  109.  
  110.         fmt.Fprintf(w, respInfo)
  111.         logger.Printf(respInfo)
  112.         return
  113.     }
  114.  
  115.     // default
  116.     showDNSRecords(w, r, dnsDB)
  117. }
  118.  
  119. func dnsHandler(w dns.ResponseWriter, r *dns.Msg, dnsDB *DNSDatabase, logger *log.Logger) {
  120.     m := new(dns.Msg)
  121.     m.SetReply(r)
  122.     for _, q := range r.Question {
  123.         name := strings.Trim(q.Name, ".")
  124.         dnsDB.RLock()
  125.         record, ok := dnsDB.records[name]
  126.         dnsDB.RUnlock()
  127.  
  128.         if ok {
  129.             rr, err := dns.NewRR(fmt.Sprintf("%s %s %s", name, record.Type, record.Value))
  130.             logger.Printf("DNS Responce %s %s %s from %s\n", name, record.Type, record.Value, w.RemoteAddr().String())
  131.             if err == nil {
  132.                 m.Answer = append(m.Answer, rr)
  133.             }
  134.         }
  135.     }
  136.  
  137.     w.WriteMsg(m)
  138. }
  139.  
  140. func setupLogger(logFileName string) *log.Logger {
  141.     logFile, err := os.OpenFile(logFileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
  142.     if err != nil {
  143.         log.Fatalf("Failed to open log file: %v", err)
  144.     }
  145.     return log.New(logFile, "", log.Ldate|log.Ltime)
  146. }
  147.  
  148. func startDNSServer(logger *log.Logger, dnsDB *DNSDatabase) {
  149.     dnsServer := &dns.Server{Addr: net.JoinHostPort(dnsHost, dnsPort), Net: "udp"}
  150.     dns.HandleFunc(".", func(w dns.ResponseWriter, r *dns.Msg) {
  151.         dnsHandler(w, r, dnsDB, logger)
  152.     })
  153.     logger.Printf("DNS server: %s", net.JoinHostPort(dnsHost, dnsPort))
  154.  
  155.     if err := dnsServer.ListenAndServe(); err != nil {
  156.         logger.Fatalf("DNS server failed: %v", err)
  157.     }
  158. }
  159.  
  160. func startHTTPServer(logger *log.Logger, dnsDB *DNSDatabase) {
  161.     router := mux.NewRouter()
  162.     router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  163.         if !isAuth(w, r) {
  164.             w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
  165.             w.WriteHeader(http.StatusUnauthorized)
  166.             fmt.Fprintln(w, "Unauthorized")
  167.             return
  168.         }
  169.         httpUpdateDNSHandler(w, r, dnsDB, logger)
  170.     })
  171.  
  172.     httpServer := &http.Server{
  173.         Addr:           net.JoinHostPort(httpHost, httpPort),
  174.         Handler:        router,
  175.         ReadTimeout:    5 * time.Second,
  176.         WriteTimeout:   10 * time.Second,
  177.         MaxHeaderBytes: 1 << 20, // 1 MB
  178.     }
  179.  
  180.     logger.Printf("HTTP server: %s", net.JoinHostPort(httpHost, httpPort))
  181.  
  182.     if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
  183.         logger.Fatalf("HTTP server failed: %v", err)
  184.     }
  185. }
  186.  
  187. func renderHTMLTable(w http.ResponseWriter, dnsDB *DNSDatabase) {
  188.     const htmlTemplate = `
  189. <!DOCTYPE html>
  190. <html>
  191. <head>
  192.     <title>DNS Records</title>
  193. </head>
  194. <body>
  195.     <h1>DNS Records</h1>
  196.     <table border="1" cellspacing="0" cellpadding="2">
  197.         <tr>
  198.             <th>Name</th>
  199.             <th>Type</th>
  200.             <th>Value</th>
  201.             <th>Timestamp</th>
  202.         </tr>
  203.         {{range $name, $record := .}}
  204.         <tr>
  205.             <td>{{$name}}</td>
  206.             <td>{{$record.Type}}</td>
  207.             <td>{{$record.Value}}</td>
  208.             <td>{{$record.Timestamp}}</td>
  209.         </tr>
  210.         {{end}}
  211.     </table>
  212. </body>
  213. </html>
  214. `
  215.  
  216.     t, err := template.New("dnsTable").Parse(htmlTemplate)
  217.     if err != nil {
  218.         http.Error(w, "Error rendering HTML template", http.StatusInternalServerError)
  219.         return
  220.     }
  221.  
  222.     dnsDB.RLock()
  223.     defer dnsDB.RUnlock()
  224.  
  225.     err = t.Execute(w, dnsDB.records)
  226.     if err != nil {
  227.         http.Error(w, "Error executing template", http.StatusInternalServerError)
  228.         return
  229.     }
  230. }
  231.  
  232. // This function should be called to render the HTML table.
  233. func showDNSRecords(w http.ResponseWriter, r *http.Request, dnsDB *DNSDatabase) {
  234.     w.Header().Set("Content-Type", "text/html; charset=utf-8")
  235.     renderHTMLTable(w, dnsDB)
  236. }
  237.  
  238. func setupGracefulShutdown(logger *log.Logger, dnsDB *DNSDatabase) (context.Context, context.CancelFunc) {
  239.  
  240.     logger.Printf("START %s", appName)
  241.  
  242.     shutdownCtx, shutdownFunc := context.WithCancel(context.Background())
  243.  
  244.     sigCh := make(chan os.Signal, 1)
  245.     signal.Notify(sigCh,
  246.         syscall.SIGHUP,
  247.         syscall.SIGINT,
  248.         syscall.SIGTERM,
  249.         syscall.SIGQUIT)
  250.  
  251.     go func() {
  252.         sig := <-sigCh
  253.         logger.Printf("STOP %s received signal: %v", appName, sig)
  254.  
  255.         saveDNSDatabaseToFile(dnsDBFilename, dnsDB)
  256.  
  257.         shutdownFunc()
  258.     }()
  259.  
  260.     return shutdownCtx, shutdownFunc
  261. }
  262.  
  263. func main() {
  264.  
  265.     // Set up graceful shutdown
  266.     logger := setupLogger(logFileName)
  267.     shutdownCtx, _ := setupGracefulShutdown(logger, &dnsDB)
  268.  
  269.     // Load the DNS database from a JSON file on startup
  270.     if loadedDB, err := loadDNSDatabaseFromFile(dnsDBFilename); err == nil {
  271.         dnsDB = loadedDB
  272.     }
  273.  
  274.     // Start the DNS server
  275.     go startDNSServer(logger, &dnsDB)
  276.  
  277.     // Start the HTTP server
  278.     go startHTTPServer(logger, &dnsDB)
  279.  
  280.     // Block until the shutdown signal is received
  281.     <-shutdownCtx.Done()
  282. }
  283.  
Add Comment
Please, Sign In to add comment