From ca3a01f478aa60fb039892d2cae6823188b5842c Mon Sep 17 00:00:00 2001 From: Jeff Carr Date: Sat, 6 Jan 2024 01:41:33 -0600 Subject: [PATCH] make new resolverStatus() also fix dns-https Signed-off-by: Jeff Carr --- args.go | 16 +- digStatus.go | 314 ++++++++++++++++++++++++++++++ dns-https.go | 64 +----- dnsLookupStatus.go | 476 --------------------------------------------- gui.go | 120 ++++++------ hostname.go | 70 ++----- hostnameStatus.go | 129 +++++++----- nsupdate.go | 2 +- resolverBox.go | 209 ++++++++++++++++++++ structs.go | 11 +- 10 files changed, 700 insertions(+), 711 deletions(-) create mode 100644 digStatus.go delete mode 100644 dnsLookupStatus.go create mode 100644 resolverBox.go diff --git a/args.go b/args.go index ba6ee85..6527c5b 100644 --- a/args.go +++ b/args.go @@ -18,11 +18,13 @@ var args struct { VerboseDNS bool `arg:"--verbose-dns" help:"debug your dns settings"` } -var NET log.LogFlag var NOW log.LogFlag +var NET log.LogFlag +var DNS log.LogFlag var PROC log.LogFlag var SPEW log.LogFlag var CHANGE log.LogFlag +var STATUS log.LogFlag func init() { arg.MustParse(&args) @@ -40,6 +42,12 @@ func init() { NET.Desc = "Network logging" NET.Register() + DNS.B = false + DNS.Name = "DNS" + DNS.Subsystem = "cpdns" + DNS.Desc = "dnsStatus.update()" + DNS.Register() + PROC.B = false PROC.Name = "PROC" PROC.Subsystem = "cpdns" @@ -58,6 +66,12 @@ func init() { CHANGE.Desc = "show droplet state changes" CHANGE.Register() + STATUS.B = false + STATUS.Name = "STATUS" + STATUS.Subsystem = "cpdns" + STATUS.Desc = "updateStatus()" + STATUS.Register() + if debugger.ArgDebug() { log.Log(true, "INIT() gui debug == true") } else { diff --git a/digStatus.go b/digStatus.go new file mode 100644 index 0000000..ffa9f2d --- /dev/null +++ b/digStatus.go @@ -0,0 +1,314 @@ +/* + 'dig' + + This is essentially doing what the command 'dig' does + It performing DNS queries on TCP and UDP + against localhost, cloudflare & google + + IPv4() and IPv6() return true if they are working + + with the 'gui' package, it can also display the results +*/ + +package main + +import ( + "os" + "fmt" + "time" + "strings" + "reflect" + "errors" + + "go.wit.com/log" + "go.wit.com/gui/gui" + "go.wit.com/gui/gadgets" + "go.wit.com/shell" +) + +type digStatus struct { + ready bool + hidden bool + statusIPv4 string + statusIPv6 string + + parent *gui.Node + window *gadgets.BasicWindow + group *gui.Node + grid *gui.Node + + summary *gui.Node + status *gadgets.OneLiner + statusAAAA *gadgets.OneLiner + speed *gadgets.OneLiner + speedActual *gadgets.OneLiner + + details *gui.Node + dsLocalhost *resolverStatus + dsLocalNetwork *resolverStatus + dsCloudflare *resolverStatus + dsGoogle *resolverStatus + DnsDigUDP *gui.Node + DnsDigTCP *gui.Node + + httpGoWitCom *gadgets.OneLiner + statusHTTP *gadgets.OneLiner +} + +func NewDigStatusWindow(p *gui.Node) *digStatus { + var ds *digStatus + ds = new(digStatus) + + ds.ready = false + ds.hidden = true + + ds.window = gadgets.NewBasicWindow(p, "DNS Resolver Status") + ds.window.Draw() + ds.window.Hide() + + // summary of the current state of things + ds.summary = ds.window.Box().NewGroup("Summary") + g := ds.summary.NewGrid("LookupStatus", 2, 2) + g.Pad() + + ds.status = gadgets.NewOneLiner(g, "status").Set("unknown") + ds.statusAAAA = gadgets.NewOneLiner(g, "IPv6 status").Set("unknown") + ds.statusHTTP = gadgets.NewOneLiner(g, "IPv6 via HTTP").Set("unknown") + ds.speed = gadgets.NewOneLiner(g, "speed").Set("unknown") + ds.speedActual = gadgets.NewOneLiner(g, "actual").Set("unknown") + + // make the area to store the raw details + ds.details = ds.window.Box().NewGroup("Details") + ds.dsLocalhost = NewResolverStatus(ds.details, "(localhost)", "127.0.0.1:53", "go.wit.com") + ds.dsLocalNetwork = NewResolverStatus(ds.details, "(Local Network)", "192.168.86.1:53", "go.wit.com") + ds.dsCloudflare = NewResolverStatus(ds.details, "(cloudflare)", "1.1.1.1:53", "go.wit.com") + ds.dsGoogle = NewResolverStatus(ds.details, "(google)", "8.8.8.8:53", "go.wit.com") + ds.makeDnsStatusGrid() + ds.makeHttpStatusGrid() + + ds.hidden = false + ds.ready = true + return ds +} + +func (ds *digStatus) Update() { + log.Info("digStatus() Update() START") + if ds == nil { + log.Error(errors.New("digStatus() Update() ds == nil")) + return + } + duration := timeFunction(func () { + ds.updateDnsStatus() + }) + s := fmt.Sprint(duration) + // ds.speedActual.Set(s) + me.digStatus.set(ds.speedActual, s) + + if (duration > 500 * time.Millisecond ) { + me.digStatus.set(ds.speed, "SLOW") + } else if (duration > 100 * time.Millisecond ) { + me.digStatus.set(ds.speed, "OK") + } else { + me.digStatus.set(ds.speed, "FAST") + } + log.Info("digStatus() Update() END") +} + +// Returns true if the status is valid +func (ds *digStatus) Ready() bool { + if ds == nil {return false} + return ds.ready +} + +// Returns true if IPv4 is working +func (ds *digStatus) IPv4() bool { + if ! ds.Ready() {return false} + if (ds.statusIPv4 == "OK") { + return true + } + if (ds.statusIPv4 == "GOOD") { + return true + } + return false +} + +// Returns true if IPv6 is working +func (ds *digStatus) IPv6() bool { + if ! ds.Ready() {return false} + if (ds.statusIPv6 == "GOOD") { + return true + } + return false +} + +func (ds *digStatus) setIPv4status(s string) { + ds.statusIPv4 = s + if ! ds.Ready() {return} + me.digStatus.set(ds.status, s) +} + +func (ds *digStatus) setIPv6status(s string) { + ds.statusIPv6 = s + if ! ds.Ready() {return} + me.digStatus.set(ds.statusAAAA, s) +} + +func (ds *digStatus) SetIPv6(s string) { + if ! ds.Ready() {return} + log.Warn("Should SetIPv6() here to", s) + log.Warn("Should SetIPv6() here to", s) + log.Warn("Should SetIPv6() here to", s) + log.Warn("Should SetIPv6() here to", s) + me.DnsAAAA.Set(s) + // me.digStatus.set(ds.httpGoWitCom, addr) +} + +func (ds *digStatus) set(a any, s string) { + if ! ds.Ready() {return} + if ds.hidden { + return + } + if a == nil { + return + } + var n *gui.Node + if reflect.TypeOf(a) == reflect.TypeOf(n) { + n = a.(*gui.Node) + n.SetText(s) + return + } + var ol *gadgets.OneLiner + if reflect.TypeOf(a) == reflect.TypeOf(ol) { + ol = a.(*gadgets.OneLiner) + ol.Set(s) + return + } + log.Warn("unknown type TypeOf(a) =", reflect.TypeOf(a), "a =", a) + os.Exit(0) +} + +func (ds *digStatus) updateDnsStatus() { + var cmd, out string + var ipv4, ipv6 bool + + log.Info("updateDnsStatus() START") + if (ds == nil) { + log.Error(errors.New("updateDnsStatus() not initialized yet. ds == nil")) + return + } + + if (! ds.ready) { + log.Error(errors.New("updateDnsStatus() not ready yet")) + return + } + + ipv4, ipv6 = ds.dsLocalhost.update() + ipv4, ipv6 = ds.dsLocalNetwork.update() + ipv4, ipv6 = ds.dsCloudflare.update() + ipv4, ipv6 = ds.dsGoogle.update() + + if me.status.ValidHostname() { + if ds.checkLookupDoH(me.status.GetHostname()) { + log.Log(DNS, "updateDnsStatus() HTTP DNS lookups working") + me.digStatus.set(ds.statusHTTP, "WORKING") + } else { + log.Log(DNS, "updateDnsStatus() HTTP DNS lookups not working") + log.Log(DNS, "updateDnsStatus() It's really unlikely you are on the internet") + me.digStatus.set(ds.statusHTTP, "BROKEN") + } + } else { + me.digStatus.set(ds.statusHTTP, "INVALID HOSTNAME") + } + + if (ipv4) { + log.Log(DNS, "updateDnsStatus() IPv4 A lookups working") + ds.setIPv4status("OK") + } else { + log.Log(DNS, "updateDnsStatus() IPv4 A lookups not working. No internet?") + ds.setIPv4status("No Internet?") + } + if (ipv6) { + log.Log(DNS, "updateDnsStatus() IPv6 AAAA lookups working") + ds.setIPv4status("GOOD") + ds.setIPv6status("GOOD") + } else { + log.Log(DNS, "updateDnsStatus() IPv6 AAAA lookups are not working") + ds.setIPv6status("Need VPN") + } + + cmd = "dig +noall +answer www.wit.com A" + out = shell.Run(cmd) + log.Log(DNS, "makeDnsStatusGrid() dig", out) + me.digStatus.set(ds.DnsDigUDP, out) + + cmd = "dig +noall +answer www.wit.com AAAA" + out = shell.Run(cmd) + log.Log(DNS, "makeDnsStatusGrid() dig", out) + me.digStatus.set(ds.DnsDigTCP, out) +} + +func (ds *digStatus) makeHttpStatusGrid() { + group := ds.details.NewGroup("dns.google.com via HTTPS") + grid := group.NewGrid("LookupStatus", 2, 2) + + ds.httpGoWitCom = gadgets.NewOneLiner(grid, "go.wit.com") + me.digStatus.set(ds.httpGoWitCom, "unknown") + + group.Pad() + grid.Pad() +} + +func (ds *digStatus) makeDnsStatusGrid() { + var cmd, out string + group := ds.details.NewGroup("dig results") + grid := group.NewGrid("LookupStatus", 2, 2) + + cmd = "dig +noall +answer go.wit.com A" + grid.NewLabel(cmd) + ds.DnsDigUDP = grid.NewLabel("?") + out = shell.Run(cmd) + log.Log(DNS, "makeDnsStatusGrid() dig", out) + me.digStatus.set(ds.DnsDigUDP, out) + + cmd = "dig +noall +answer go.wit.com AAAA" + grid.NewLabel(cmd) + ds.DnsDigTCP = grid.NewLabel("?") + out = shell.Run(cmd) + log.Log(DNS, "makeDnsStatusGrid() dig", out) + me.digStatus.set(ds.DnsDigTCP, out) + + group.Pad() + grid.Pad() +} + +func (ds *digStatus) checkLookupDoH(hostname string) bool { + var status bool = false + + ipv6Addresses := lookupDoH(hostname, "AAAA") + + log.Log(DNS, "IPv6 Addresses for ", hostname) + var s []string + for _, addr := range ipv6Addresses { + log.Log(DNS, addr) + s = append(s, addr) + status = true + } + me.digStatus.SetIPv6(strings.Join(s, "\n")) + return status +} + +func (ds *digStatus) Show() { + log.Info("digStatus.Show() window") + if me.digStatus.hidden { + me.digStatus.window.Show() + } + me.digStatus.hidden = false +} + +func (ds *digStatus) Hide() { + log.Info("digStatus.Hide() window") + if ! me.digStatus.hidden { + me.digStatus.window.Hide() + } + me.digStatus.hidden = true +} diff --git a/dns-https.go b/dns-https.go index 6579903..bd06d4c 100644 --- a/dns-https.go +++ b/dns-https.go @@ -8,70 +8,18 @@ import ( "net/http" ) -/* -func getAAAArecords() { - hostname := "go.wit.com" - ipv6Addresses, err := dnsLookupDoH(hostname) - if err != nil { - log.Error(err, "getAAAArecords") - return - } - - fmt.Printf("IPv6 Addresses for %s:\n", hostname) - for _, addr := range ipv6Addresses { - log.Println(addr) - } -} -*/ - -// dnsLookupDoH performs a DNS lookup for AAAA records over HTTPS. -func dnsAAAAlookupDoH(domain string) ([]string, error) { - var ipv6Addresses []string - - // Construct the URL for a DNS query with Google's DNS-over-HTTPS API - url := fmt.Sprintf("https://dns.google/resolve?name=%s&type=AAAA", domain) - - log.Println("curl", url) - - // Perform the HTTP GET request - resp, err := http.Get(url) - if err != nil { - return nil, fmt.Errorf("error performing DNS-over-HTTPS request: %w", err) - } - defer resp.Body.Close() - - // Read and unmarshal the response body - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("error reading response: %w", err) - } - - var data struct { - Answer []struct { - Data string `json:"data"` - } `json:"Answer"` - } - - if err := json.Unmarshal(body, &data); err != nil { - return nil, fmt.Errorf("error unmarshaling response: %w", err) - } - - // Extract the IPv6 addresses - for _, answer := range data.Answer { - ipv6Addresses = append(ipv6Addresses, answer.Data) - } - - return ipv6Addresses, nil -} - // dnsLookupDoH performs a DNS lookup for AAAA records over HTTPS. func lookupDoH(hostname string, rrType string) []string { var values []string // Construct the URL for a DNS query with Google's DNS-over-HTTPS API - url := fmt.Sprintf("https://dns.google/resolve?name=%s&type=%s", hostname, rrType) + url := fmt.Sprintf("https://dns.google.com/resolve?name=%s&type=%s", hostname, rrType) - log.Println("curl", url) + log.Log(DNS, "lookupDoH()", url) + if hostname == "" { + log.Warn("lookupDoH() was sent a empty hostname") + return nil + } // Perform the HTTP GET request resp, err := http.Get(url) diff --git a/dnsLookupStatus.go b/dnsLookupStatus.go deleted file mode 100644 index 6488635..0000000 --- a/dnsLookupStatus.go +++ /dev/null @@ -1,476 +0,0 @@ -/* - 'dig' - - This is essentially doing what the command 'dig' does - It performing DNS queries on TCP and UDP - against localhost, cloudflare & google - - IPv4() and IPv6() return true if they are working - - with the 'gui' package, it can also display the results -*/ - -package main - -import ( - "os" - "fmt" - "time" - "strconv" - "reflect" - "errors" - - "go.wit.com/log" - "go.wit.com/gui/gui" - "go.wit.com/gui/gadgets" - "go.wit.com/shell" - - "github.com/miekg/dns" -) - -type digStatus struct { - ready bool - hidden bool - statusIPv4 string - statusIPv6 string - - parent *gui.Node - window *gadgets.BasicWindow - group *gui.Node - grid *gui.Node - - summary *gui.Node - status *gadgets.OneLiner - statusAAAA *gadgets.OneLiner - speed *gadgets.OneLiner - speedActual *gadgets.OneLiner - - details *gui.Node - dsLocalhost *dnsStatus - dsLocalNetwork *dnsStatus - dsCloudflare *dnsStatus - dsGoogle *dnsStatus - DnsDigUDP *gui.Node - DnsDigTCP *gui.Node - - httpGoWitCom *gadgets.OneLiner - statusHTTP *gadgets.OneLiner -} - -type dnsStatus struct { - title string - server string // The DNS server. Example: "127.0.0.1:53" or "1.1.1.1:53" - hostname string // the hostname to lookup. Example: "www.google.com" or "go.wit.com" - - parent *gui.Node - group *gui.Node - grid *gui.Node - - // DNS setup options - udpA *gui.Node - tcpA *gui.Node - udpAAAA *gui.Node - tcpAAAA *gui.Node - - // show the display - aFail *gui.Node - aSuccess *gui.Node - aaaaFail *gui.Node - aaaaSuccess *gui.Node - - // interger counters - aFailc int - aSuccessc int - aaaaFailc int - aaaaSuccessc int -} - -func NewDigStatusWindow(p *gui.Node) *digStatus { - var ds *digStatus - ds = new(digStatus) - - ds.ready = false - ds.hidden = true - - ds.window = gadgets.NewBasicWindow(p, "DNS Resolver Status") - ds.window.Draw() - ds.window.Hide() - - // summary of the current state of things - ds.summary = ds.window.Box().NewGroup("Summary") - g := ds.summary.NewGrid("LookupStatus", 2, 2) - g.Pad() - - ds.status = gadgets.NewOneLiner(g, "status").Set("unknown") - ds.statusAAAA = gadgets.NewOneLiner(g, "IPv6 status").Set("unknown") - ds.statusHTTP = gadgets.NewOneLiner(g, "IPv6 via HTTP").Set("unknown") - ds.speed = gadgets.NewOneLiner(g, "speed").Set("unknown") - ds.speedActual = gadgets.NewOneLiner(g, "actual").Set("unknown") - - // make the area to store the raw details - ds.details = ds.window.Box().NewGroup("Details") - ds.dsLocalhost = NewDnsStatus(ds.details, "(localhost)", "127.0.0.1:53", "go.wit.com") - ds.dsLocalNetwork = NewDnsStatus(ds.details, "(Local Network)", "172.22.0.1:53", "go.wit.com") - ds.dsCloudflare = NewDnsStatus(ds.details, "(cloudflare)", "1.1.1.1:53", "go.wit.com") - ds.dsGoogle = NewDnsStatus(ds.details, "(google)", "8.8.8.8:53", "go.wit.com") - ds.makeDnsStatusGrid() - ds.makeHttpStatusGrid() - - ds.hidden = false - ds.ready = true - return ds -} - -func (ds *digStatus) Update() { - log.Info("digStatus() Update() START") - if ds == nil { - log.Error(errors.New("digStatus() Update() ds == nil")) - return - } - duration := timeFunction(func () { - ds.updateDnsStatus() - }) - s := fmt.Sprint(duration) - // ds.speedActual.Set(s) - me.digStatus.set(ds.speedActual, s) - - if (duration > 500 * time.Millisecond ) { - me.digStatus.set(ds.speed, "SLOW") - } else if (duration > 100 * time.Millisecond ) { - me.digStatus.set(ds.speed, "OK") - } else { - me.digStatus.set(ds.speed, "FAST") - } - log.Info("digStatus() Update() END") -} - -// Returns true if the status is valid -func (ds *digStatus) Ready() bool { - if ds == nil {return false} - return ds.ready -} - -// Returns true if IPv4 is working -func (ds *digStatus) IPv4() bool { - if ! ds.Ready() {return false} - if (ds.statusIPv4 == "OK") { - return true - } - if (ds.statusIPv4 == "GOOD") { - return true - } - return false -} - -// Returns true if IPv6 is working -func (ds *digStatus) IPv6() bool { - if ! ds.Ready() {return false} - if (ds.statusIPv6 == "GOOD") { - return true - } - return false -} - -func (ds *digStatus) setIPv4(s string) { - ds.statusIPv4 = s - if ! ds.Ready() {return} - me.digStatus.set(ds.status, s) -} - -func (ds *digStatus) setIPv6(s string) { - ds.statusIPv6 = s - if ! ds.Ready() {return} - me.digStatus.set(ds.statusAAAA, s) -} - -func (ds *digStatus) set(a any, s string) { - if ! ds.Ready() {return} - if ds.hidden { - return - } - if a == nil { - return - } - var n *gui.Node - if reflect.TypeOf(a) == reflect.TypeOf(n) { - n = a.(*gui.Node) - n.SetText(s) - return - } - var ol *gadgets.OneLiner - if reflect.TypeOf(a) == reflect.TypeOf(ol) { - ol = a.(*gadgets.OneLiner) - ol.Set(s) - return - } - log.Warn("unknown type TypeOf(a) =", reflect.TypeOf(a), "a =", a) - os.Exit(0) -} - -func (ds *digStatus) updateDnsStatus() { - var cmd, out string - var ipv4, ipv6 bool - - log.Info("updateDnsStatus() START") - if (ds == nil) { - log.Error(errors.New("updateDnsStatus() not initialized yet. ds == nil")) - return - } - - if (! ds.ready) { - log.Error(errors.New("updateDnsStatus() not ready yet")) - return - } - - ipv4, ipv6 = ds.dsLocalhost.update() - ipv4, ipv6 = ds.dsLocalNetwork.update() - ipv4, ipv6 = ds.dsCloudflare.update() - ipv4, ipv6 = ds.dsGoogle.update() - - if ds.checkLookupDoH("go.wit.com") { - log.Println("updateDnsStatus() HTTP DNS lookups working") - me.digStatus.set(ds.statusHTTP, "WORKING") - } else { - log.Println("updateDnsStatus() HTTP DNS lookups not working") - log.Println("updateDnsStatus() It's really unlikely you are on the internet") - me.digStatus.set(ds.statusHTTP, "BROKEN") - } - - if (ipv4) { - log.Println("updateDnsStatus() IPv4 A lookups working") - ds.setIPv4("OK") - } else { - log.Println("updateDnsStatus() IPv4 A lookups not working. No internet?") - ds.setIPv4("No Internet?") - } - if (ipv6) { - log.Println("updateDnsStatus() IPv6 AAAA lookups working") - ds.setIPv4("GOOD") - ds.setIPv6("GOOD") - } else { - log.Println("updateDnsStatus() IPv6 AAAA lookups are not working") - ds.setIPv6("Need VPN") - } - - cmd = "dig +noall +answer www.wit.com A" - out = shell.Run(cmd) - log.Println("makeDnsStatusGrid() dig", out) - me.digStatus.set(ds.DnsDigUDP, out) - - cmd = "dig +noall +answer www.wit.com AAAA" - out = shell.Run(cmd) - log.Println("makeDnsStatusGrid() dig", out) - me.digStatus.set(ds.DnsDigTCP, out) -} - -// Makes a DNS Status Grid -func NewDnsStatus(p *gui.Node, title string, server string, hostname string) *dnsStatus { - var ds *dnsStatus - ds = new(dnsStatus) - ds.parent = p - ds.group = p.NewGroup(server + " " + title + " lookup") - ds.grid = ds.group.NewGrid("LookupStatus", 5, 2) - - ds.server = server - ds.hostname = hostname - - ds.grid.NewLabel("") - ds.grid.NewLabel("UDP") - ds.grid.NewLabel("TCP") - ds.grid.NewLabel("Success") - ds.grid.NewLabel("Fail") - - ds.grid.NewLabel("A") - ds.udpA = ds.grid.NewLabel("?") - ds.tcpA = ds.grid.NewLabel("?") - ds.aSuccess = ds.grid.NewLabel("?") - ds.aFail = ds.grid.NewLabel("?") - - ds.grid.NewLabel("AAAA") - ds.udpAAAA = ds.grid.NewLabel("?") - ds.tcpAAAA = ds.grid.NewLabel("?") - ds.aaaaSuccess = ds.grid.NewLabel("?") - ds.aaaaFail = ds.grid.NewLabel("?") - - ds.group.Margin() - ds.grid.Margin() - ds.group.Pad() - ds.grid.Pad() - - return ds -} - -// special thanks to the Element Hotel wifi in Philidelphia that allowed me to -// easily debug this code since the internet connection here blocks port 53 traffic -func (ds *dnsStatus) update() (bool, bool) { - var results []string - var a bool = false - var aaaa bool = false - - log.Println("dnsStatus.update() For server", ds.server, "on", ds.hostname) - results, _ = dnsUdpLookup(ds.server, ds.hostname, dns.TypeA) - log.Println("dnsStatus.update() UDP type A =", results) - - if (len(results) == 0) { - me.digStatus.set(ds.udpA, "BROKEN") - ds.aFailc += 1 - } else { - me.digStatus.set(ds.udpA, "WORKING") - ds.aSuccessc += 1 - a = true - } - - results, _ = dnsTcpLookup(ds.server, ds.hostname, dns.TypeA) - log.Println("dnsStatus.update() TCP type A =", results) - - if (len(results) == 0) { - me.digStatus.set(ds.tcpA, "BROKEN") - ds.aFailc += 1 - } else { - me.digStatus.set(ds.tcpA, "WORKING") - ds.aSuccessc += 1 - a = true - } - - me.digStatus.set(ds.aFail, strconv.Itoa(ds.aFailc)) - me.digStatus.set(ds.aSuccess,strconv.Itoa(ds.aSuccessc)) - - results, _ = dnsUdpLookup(ds.server, ds.hostname, dns.TypeAAAA) - log.Println("dnsStatus.update() UDP type AAAA =", results) - - if (len(results) == 0) { - me.digStatus.set(ds.udpAAAA, "BROKEN") - ds.aaaaFailc += 1 - me.digStatus.set(ds.aaaaFail, strconv.Itoa(ds.aaaaFailc)) - } else { - me.digStatus.set(ds.udpAAAA, "WORKING") - ds.aaaaSuccessc += 1 - aaaa = true - } - - results, _ = dnsTcpLookup(ds.server, ds.hostname, dns.TypeAAAA) - log.Println("dnsStatus.update() UDP type AAAA =", results) - - if (len(results) == 0) { - me.digStatus.set(ds.tcpAAAA, "BROKEN") - ds.aaaaFailc += 1 - me.digStatus.set(ds.aaaaFail, strconv.Itoa(ds.aaaaFailc)) - } else { - me.digStatus.set(ds.tcpAAAA, "WORKING") - ds.aaaaSuccessc += 1 - aaaa = true - } - - me.digStatus.set(ds.aaaaFail, strconv.Itoa(ds.aaaaFailc)) - me.digStatus.set(ds.aaaaSuccess,strconv.Itoa(ds.aaaaSuccessc)) - - return a, aaaa -} - -func (ds *digStatus) makeHttpStatusGrid() { - group := ds.details.NewGroup("dns.google.com via HTTPS") - grid := group.NewGrid("LookupStatus", 2, 2) - - ds.httpGoWitCom = gadgets.NewOneLiner(grid, "go.wit.com") - me.digStatus.set(ds.httpGoWitCom, "unknown") - - group.Pad() - grid.Pad() -} - -func (ds *digStatus) makeDnsStatusGrid() { - var cmd, out string - group := ds.details.NewGroup("dig results") - grid := group.NewGrid("LookupStatus", 2, 2) - - cmd = "dig +noall +answer go.wit.com A" - grid.NewLabel(cmd) - ds.DnsDigUDP = grid.NewLabel("?") - out = shell.Run(cmd) - log.Println("makeDnsStatusGrid() dig", out) - me.digStatus.set(ds.DnsDigUDP, out) - - cmd = "dig +noall +answer go.wit.com AAAA" - grid.NewLabel(cmd) - ds.DnsDigTCP = grid.NewLabel("?") - out = shell.Run(cmd) - log.Println("makeDnsStatusGrid() dig", out) - me.digStatus.set(ds.DnsDigTCP, out) - - group.Pad() - grid.Pad() -} - -// dnsLookup performs a DNS lookup for the specified record type (e.g., "TXT", "AAAA") for a given domain. -func dnsUdpLookup(server string, domain string, recordType uint16) ([]string, error) { - var records []string - - c := new(dns.Client) - m := new(dns.Msg) - m.SetQuestion(dns.Fqdn(domain), recordType) - r, _, err := c.Exchange(m, server) // If server = "1.1.1.1:53" then use Cloudflare's DNS server - if err != nil { - return nil, err - } - - for _, ans := range r.Answer { - records = append(records, ans.String()) - } - - return records, nil -} - -func dnsTcpLookup(server string, domain string, recordType uint16) ([]string, error) { - var records []string - - c := new(dns.Client) - c.Net = "tcp" // Specify to use TCP for the query - c.Timeout = time.Second * 5 // Set a 5-second timeout - m := new(dns.Msg) - m.SetQuestion(dns.Fqdn(domain), recordType) - r, _, err := c.Exchange(m, server) // If server = "1.1.1.1:53" then use Cloudflare's DNS server - if err != nil { - return nil, err - } - - for _, ans := range r.Answer { - records = append(records, ans.String()) - } - - return records, nil -} - -func (ds *digStatus) checkLookupDoH(hostname string) bool { - var status bool = false - - domain := "go.wit.com" - ipv6Addresses, err := dnsAAAAlookupDoH(domain) - if err != nil { - log.Error(err, "checkLookupDoH()") - return status - } - - log.Println("IPv6 Addresses for %s:\n", domain) - for _, addr := range ipv6Addresses { - log.Println(addr) - me.digStatus.set(ds.httpGoWitCom, addr) - status = true - } - return status -} - -func (ds *digStatus) Show() { - log.Info("digStatus.Show() window") - if me.digStatus.hidden { - me.digStatus.window.Show() - } - me.digStatus.hidden = false -} - -func (ds *digStatus) Hide() { - log.Info("digStatus.Hide() window") - if ! me.digStatus.hidden { - me.digStatus.window.Hide() - } - me.digStatus.hidden = true -} diff --git a/gui.go b/gui.go index 9498fa3..8aa3659 100644 --- a/gui.go +++ b/gui.go @@ -6,11 +6,9 @@ import ( "os" "os/user" "strconv" -// "net" "strings" "go.wit.com/log" - "go.wit.com/shell" "go.wit.com/gui/gui" "go.wit.com/gui/gadgets" @@ -27,8 +25,6 @@ func setupControlPanelWindow() { mainWindow("DNS and IPv6 Control Panel") detailsTab("OS Details") debugTab("Debug") - - // me.digStatus = NewDigStatusWindow(me.window) } func detailsTab(title string) { @@ -100,8 +96,9 @@ func debugTab(title string) { }) g2.NewButton("dig +trace", func () { - o := shell.Run("dig +trace +noadditional DS " + me.hostname + " @8.8.8.8") - log.Println(o) + log.Log(NOW, "TODO: redo this") + // o := shell.Run("dig +trace +noadditional DS " + me.hostname + " @8.8.8.8") + // log.Println(o) }) g2 = me.debug.Box().NewGroup("debugging options") @@ -149,14 +146,14 @@ func missingAAAA() string { func displayDNS() string { var aaaa []string aaaa = dhcpAAAA() // your AAAA records right now - h := me.hostname + // h := me.hostname var all string var broken string = "unknown" for _, s := range aaaa { - log.Log(NOW, "host", h, "DNS AAAA =", s, "ipmap[s] =", me.ipmap[s]) + log.Log(STATUS, "host", "fixme", "DNS AAAA =", s, "ipmap[s] =", me.ipmap[s]) all += s + "\n" if ( me.ipmap[s] == nil) { - log.Warn("THIS IS THE WRONG AAAA DNS ENTRY: host", h, "DNS AAAA =", s) + log.Warn("THIS IS THE WRONG AAAA DNS ENTRY: host", "fixme", "DNS AAAA =", s) broken = "wrong AAAA entry" } else { if (broken == "unknown") { @@ -166,7 +163,7 @@ func displayDNS() string { } all = sortLines(all) if (me.workingIPv6.S != all) { - log.Warn("workingIPv6.SetText() to:", all) + log.Log(NOW, "workingIPv6.SetText() to:", all) me.workingIPv6.SetText(all) } @@ -174,11 +171,11 @@ func displayDNS() string { a = realA() all = sortLines(strings.Join(a, "\n")) if (all == "") { - log.Info("THERE IS NOT a real A DNS ENTRY") + log.Log(NOW, "THERE IS NOT a real A DNS ENTRY") all = "CNAME ipv6.wit.com" } if (me.DnsA.S != all) { - log.Warn("DnsA.SetText() to:", all) + log.Log(NOW, "DnsA.SetText() to:", all) me.DnsA.SetText(all) } return broken @@ -199,19 +196,18 @@ func mainWindow(title string) { grid.NewLabel("hostname =") me.fqdn = grid.NewLabel("?") - me.hostname = "" - grid.NewLabel("DNS AAAA =") - me.DnsAAAA = grid.NewLabel("?") + // grid.NewLabel("DNS AAAA =") + me.DnsAAAA = gadgets.NewOneLiner(grid, "DNS AAAA =").Set("unknown") grid.NewLabel("DNS A =") me.DnsA = grid.NewLabel("?") me.digStatus = NewDigStatusWindow(me.myGui) - me.hostnameStatus = NewHostnameStatusWindow(me.myGui) + me.status = NewHostnameStatusWindow(me.myGui) me.hostnameStatusButton = me.mainStatus.NewButton("Fix hostname DNS", func () { - me.hostnameStatus.window.Toggle() + me.status.window.Toggle() }) grid.Margin() @@ -220,19 +216,16 @@ func mainWindow(title string) { statusGrid(me.window.Box()) gr := me.window.Box().NewGroup("debugging") - gr.NewButton("GO GUI Debugger", func () { - debugger.DebugWindow(me.myGui) - }) gr.NewButton("OS Details", func () { me.details.Toggle() }) - gr.NewButton("DNS Debug", func () { - me.debug.Toggle() - }) gr.NewButton("Resolver Status", func () { if ! me.digStatus.Ready() {return} me.digStatus.window.Toggle() }) + gr.NewButton("Control Panel Debug", func () { + me.debug.Toggle() + }) } func statusGrid(n *gui.Node) { @@ -240,15 +233,15 @@ func statusGrid(n *gui.Node) { gridP := problems.NewGrid("nuts", 2, 2) + gridP.NewLabel("hostname =") + me.hostnameStatus = gridP.NewLabel("invalid") + gridP.NewLabel("DNS Status =") me.DnsStatus = gridP.NewLabel("unknown") me.statusIPv6 = gadgets.NewOneLiner(gridP, "IPv6 working") me.statusIPv6.Set("known") - gridP.NewLabel("hostname =") - me.hostnameStatusOLD = gridP.NewLabel("invalid") - gridP.NewLabel("dns resolution") me.DnsSpeed = gridP.NewLabel("unknown") @@ -279,52 +272,51 @@ func statusGrid(n *gui.Node) { // run everything because something has changed func updateDNS() { - var aaaa []string - h := me.hostname - if (h == "") { - h = "test.wit.com" - } - me.digStatus.Update() - me.hostnameStatus.Update() + me.status.Update() // log.Println("digAAAA()") - aaaa = digAAAA(h) - log.Log(NOW, "digAAAA() =", aaaa) - // log.Println(SPEW, me) - if (aaaa == nil) { - log.Warn("There are no DNS AAAA records for hostname: ", h) - me.DnsAAAA.SetText("(none)") - if (cloudflare.CFdialog.TypeNode != nil) { - cloudflare.CFdialog.TypeNode.SetText("AAAA new") - } + if me.status.ValidHostname() { + var aaaa []string + h := me.status.GetHostname() + aaaa = digAAAA(h) + log.Log(NOW, "digAAAA() for", h, "=", aaaa) - if (cloudflare.CFdialog.NameNode != nil) { - cloudflare.CFdialog.NameNode.SetText(me.hostname) - } - - d := deleteAAA() - if (d != "") { - if (cloudflare.CFdialog.ValueNode != nil) { - cloudflare.CFdialog.ValueNode.SetText(d) + // log.Println(SPEW, me) + if (aaaa == nil) { + log.Warn("There are no DNS AAAA records for hostname: ", h) + me.DnsAAAA.Set("(none)") + if (cloudflare.CFdialog.TypeNode != nil) { + cloudflare.CFdialog.TypeNode.SetText("AAAA new") } - } - m := missingAAAA() - if (m != "") { - if (cloudflare.CFdialog.ValueNode != nil) { - cloudflare.CFdialog.ValueNode.SetText(m) + + if (cloudflare.CFdialog.NameNode != nil) { + cloudflare.CFdialog.NameNode.SetText(h) } - /* - rr := &cloudflare.RRT{ - Type: "AAAA", - Name: me.hostname, - Ttl: "Auto", - Proxied: false, - Content: m, + + d := deleteAAA() + if (d != "") { + if (cloudflare.CFdialog.ValueNode != nil) { + cloudflare.CFdialog.ValueNode.SetText(d) + } + } + m := missingAAAA() + if (m != "") { + if (cloudflare.CFdialog.ValueNode != nil) { + cloudflare.CFdialog.ValueNode.SetText(m) + } + /* + rr := &cloudflare.RRT{ + Type: "AAAA", + Name: me.hostname, + Ttl: "Auto", + Proxied: false, + Content: m, + } + cloudflare.Update(rr) + */ } - cloudflare.Update(rr) - */ } } status := displayDNS() // update the GUI based on dig results diff --git a/hostname.go b/hostname.go index 9eb1a65..10211ba 100644 --- a/hostname.go +++ b/hostname.go @@ -22,14 +22,7 @@ func getHostname() { log.Error(err, "FQDN hostname error") return } - if (me.fqdn != nil) { - if (me.hostname != s) { - me.fqdn.SetText(s) - me.hostname = s - me.changed = true - } - } - log.Log(NET, "FQDN =", s) + me.status.SetHostname(s) dn := run("domainname") if (me.domainname.S != dn) { @@ -47,17 +40,17 @@ func getHostname() { var test string test = hshort + "." + dn - if (me.hostname != test) { - log.Info("me.hostname", me.hostname, "does not equal", test) - if (me.hostnameStatusOLD.S != "BROKEN") { - log.Log(CHANGE, "me.hostname", me.hostname, "does not equal", test) + if (me.status.GetHostname() != test) { + log.Log(CHANGE, "me.hostname", me.status.GetHostname(), "does not equal", test) + if (me.hostnameStatus.S != "BROKEN") { + log.Log(CHANGE, "me.hostname", me.status.GetHostname(), "does not equal", test) me.changed = true - me.hostnameStatusOLD.SetText("BROKEN") + me.hostnameStatus.SetText("BROKEN") } } else { - if (me.hostnameStatusOLD.S != "VALID") { - log.Log(CHANGE, "me.hostname", me.hostname, "is valid") - me.hostnameStatusOLD.SetText("VALID") + if (me.hostnameStatus.S != "VALID") { + log.Log(CHANGE, "me.hostname", me.status.GetHostname(), "is valid") + me.hostnameStatus.SetText("VALID") me.changed = true } // enable the cloudflare button if the provider is cloudflare @@ -76,7 +69,7 @@ func getHostname() { // check that all the OS settings are correct here // On Linux, /etc/hosts, /etc/hostname // and domainname and hostname -func goodHostname(h string) bool { +func goodHostname() bool { hostname := shell.Chomp(shell.Cat("/etc/hostname")) log.Log(NOW, "hostname =", hostname) @@ -93,30 +86,6 @@ func goodHostname(h string) bool { return false } -/* -func digAAAA(s string) []string { - var aaaa []string - // lookup the IP address from DNS - rrset := dnssecsocket.Dnstrace(s, "AAAA") - // log.Spew(args.VerboseDNS, SPEW, rrset) - for i, rr := range rrset { - ipaddr := dns.Field(rr, 1) - // how the hell do you detect a RRSIG AAAA record here? - if (ipaddr == "28") { - continue - } - log.Log(NOW, "r.Answer =", i, "rr =", rr, "ipaddr =", ipaddr) - aaaa = append(aaaa, ipaddr) - me.ipv6s[ipaddr] = rr - } - log.Info(args.VerboseDNS, "aaaa =", aaaa) - log.Println("digAAAA() returned =", aaaa) - log.Println("digAAAA() me.ipv6s =", me.ipv6s) - os.Exit(0) - return aaaa -} -*/ - func digAAAA(hostname string) []string { var blah, ipv6Addresses []string // domain := hostname @@ -128,7 +97,7 @@ func digAAAA(hostname string) []string { if (len(blah) == 0) { log.Println("digAAAA() RUNNING dnsAAAAlookupDoH(domain)") - ipv6Addresses, _ = dnsAAAAlookupDoH(hostname) + ipv6Addresses = lookupDoH(hostname, "AAAA") log.Println("digAAAA() has ipv6Addresses =", strings.Join(ipv6Addresses, " ")) for _, addr := range ipv6Addresses { log.Println(addr) @@ -141,20 +110,3 @@ func digAAAA(hostname string) []string { return blah } - -/* -func dnsHttpsLookup(domain string, recordType uint16) ([]string, error) { - domain := "google.com" - dnsLookupDoH(domain string) ([]string, error) { - ipv6Addresses, err := dnsLookupDoH(domain) - if err != nil { - log.Println("Error:", err) - return - } - - log.Printf("IPv6 Addresses for %s:\n", domain) - for _, addr := range ipv6Addresses { - log.Println(addr) - } -} -*/ diff --git a/hostnameStatus.go b/hostnameStatus.go index eb158bf..2fd9bdd 100644 --- a/hostnameStatus.go +++ b/hostnameStatus.go @@ -23,7 +23,8 @@ type hostnameStatus struct { ready bool hidden bool - hostname string // my hostname. Example: "test.wit.com" + // hostname string // my hostname. Example: "test.wit.com" + lastname string // used to watch for changes in the hostname window *gadgets.BasicWindow @@ -60,9 +61,9 @@ func NewHostnameStatusWindow(p *gui.Node) *hostnameStatus { hs.ready = false hs.hidden = true - hs.hostname = me.hostname + // hs.hostname = me.hostname - hs.window = gadgets.NewBasicWindow(p, hs.hostname + " Status") + hs.window = gadgets.NewBasicWindow(p, "fix hostname here" + " Status") hs.window.Draw() hs.window.Hide() @@ -116,6 +117,25 @@ func NewHostnameStatusWindow(p *gui.Node) *hostnameStatus { return hs } +func (hs *hostnameStatus) ValidHostname() bool { + return goodHostname() +} + +func (hs *hostnameStatus) GetHostname() string { + return hs.lastname +} + +func (hs *hostnameStatus) SetHostname(hostname string) { + if hostname == hs.lastname {return} + log.Log(CHANGE, "the hostname is changing from", hs.lastname, "to", hostname) + hs.lastname = hostname + me.changed = true + + if (me.fqdn != nil) { + me.fqdn.SetText(hostname) + } +} + func (hs *hostnameStatus) Domain() string { if ! hs.Ready() {return ""} return hs.domainname.Get() @@ -128,26 +148,26 @@ func (hs *hostnameStatus) API() string { func (hs *hostnameStatus) deleteDNSrecord(value string) bool { log.Info("deleteDNSrecord() START for", value) - log.Info("deleteDNSrecord() hostname =", me.hostname) + log.Info("deleteDNSrecord() hostname =", me.status.GetHostname()) log.Info("deleteDNSrecord() domain =", hs.Domain()) log.Info("deleteDNSrecord() DNS API Provider =", hs.API()) if (hs.API() == "cloudflare") { log.Info("deleteDNSrecord() Try to delete via cloudflare") - return cloudflare.Delete(hs.Domain(), me.hostname, value) + return cloudflare.Delete(hs.Domain(), me.status.GetHostname(), value) } return false } func (hs *hostnameStatus) createDNSrecord(value string) bool { log.Info("createDNSrecord() START for", value) - log.Info("createDNSrecord() hostname =", me.hostname) + log.Info("createDNSrecord() hostname =", me.status.GetHostname()) log.Info("createDNSrecord() domain =", hs.Domain()) log.Info("createDNSrecord() DNS API Provider =", hs.API()) if (hs.API() == "cloudflare") { - log.Warn("createDNSrecord() Try to delete via cloudflare:", me.hostname, value) - return cloudflare.Create(hs.Domain(), me.hostname, value) + log.Warn("createDNSrecord() Try to create via cloudflare:", me.status.GetHostname(), value) + return cloudflare.Create(hs.Domain(), me.status.GetHostname(), value) } return false } @@ -229,10 +249,10 @@ func (hs *hostnameStatus) set(a any, s string) { if reflect.TypeOf(a) == reflect.TypeOf(ol) { ol = a.(*gadgets.OneLiner) if ol == nil { - log.Println("ol = nil", reflect.TypeOf(a), "a =", a) + // log.Println("ol = nil", reflect.TypeOf(a), "a =", a) return } - log.Println("SETTING ol:", ol) + // log.Println("SETTING ol:", ol) ol.Set(s) return } @@ -240,69 +260,82 @@ func (hs *hostnameStatus) set(a any, s string) { os.Exit(0) } +// returns true if AAAA record already exists in DNS +func (hs *hostnameStatus) existsAAAA(s string) bool { + log.Log(NOW, "existsAAAA() try to see if AAAA is already set", s) + return false +} + // figure out if I'm missing any IPv6 address in DNS func (hs *hostnameStatus) missingAAAA() bool { var aaaa []string aaaa = dhcpAAAA() for _, s := range aaaa { log.Log(NET, "my actual AAAA = ",s) - hs.dnsValue.SetText(s) - hs.dnsAction.SetText("CREATE") - return true + if hs.existsAAAA(s) { + log.Log(NOW, "my actual AAAA already exists in DNS =",s) + } else { + log.Log(NOW, "my actual AAAA is missing from DNS",s) + hs.dnsValue.SetText(s) + hs.dnsAction.SetText("CREATE") + return true + } } return false } func (hs *hostnameStatus) updateStatus() { + if ! hs.Ready() { return } var s string var vals []string - log.Info("updateStatus() START") - if ! hs.Ready() { return } + log.Log(STATUS, "updateStatus() START") hs.hostShort.Set(me.hostshort.S) hs.domainname.Set(me.domainname.S) - vals = lookupDoH(hs.hostname, "AAAA") + if hs.ValidHostname() { + vals = lookupDoH(hs.GetHostname(), "AAAA") - log.Println("DNS IPv6 Addresses for ", hs.hostname, "=", vals) - if len(vals) == 0 { - s = "(none)" - hs.setIPv6("Check for real IPv6 addresses here") - if hs.missingAAAA() { - hs.setIPv6("Add the missing IPv6 address") + log.Log(STATUS, "DNS IPv6 Addresses for ", hs.GetHostname(), "=", vals) + if len(vals) == 0 { + s = "(none)" + } else { + hs.setIPv6("Check for real IPv6 addresses here") + if hs.missingAAAA() { + hs.setIPv6("Add the missing IPv6 address") + } + for _, addr := range vals { + log.Log(STATUS, addr) + s += addr + " (DELETE)" + "\n" + hs.setIPv6("NEEDS DELETE") + hs.dnsValue.SetText(addr) + hs.dnsAction.SetText("DELETE") + } } - } else { - for _, addr := range vals { - log.Println(addr) - s += addr + " (DELETE)" - hs.setIPv6("NEEDS DELETE") - hs.dnsValue.SetText(addr) - hs.dnsAction.SetText("DELETE") + hs.set(hs.dnsAAAA, s) + + vals = lookupDoH(hs.GetHostname(), "A") + log.Log(STATUS, "IPv4 Addresses for ", hs.GetHostname(), "=", vals) + s = strings.Join(vals, "\n") + if (s == "") { + s = "(none)" + hs.setIPv4("NEEDS CNAME") } - } - hs.set(hs.dnsAAAA, s) + hs.set(hs.dnsA, s) - vals = lookupDoH(hs.hostname, "A") - log.Println("IPv4 Addresses for ", hs.hostname, "=", vals) - s = strings.Join(vals, "\n") - if (s == "") { - s = "(none)" - hs.setIPv4("NEEDS CNAME") - } - hs.set(hs.dnsA, s) - - vals = lookupDoH(hs.hostname, "CNAME") - s = strings.Join(vals, "\n") - if (s != "") { - hs.set(hs.dnsA, "CNAME " + s) - hs.setIPv4("GOOD") + vals = lookupDoH(hs.GetHostname(), "CNAME") + s = strings.Join(vals, "\n") + if (s != "") { + hs.set(hs.dnsA, "CNAME " + s) + hs.setIPv4("GOOD") + } } hs.currentIPv4.Set(me.IPv4.S) hs.currentIPv6.Set(me.IPv6.S) - if hs.IPv4() && hs.IPv4() { + if hs.IPv4() && hs.IPv6() { hs.status.Set("GOOD") } else { hs.status.Set("BROKEN") @@ -312,7 +345,7 @@ func (hs *hostnameStatus) updateStatus() { } func (hs *hostnameStatus) Show() { - log.Info("hostnameStatus.Show() window") + log.Log(STATUS, "hostnameStatus.Show() window") if hs.hidden { hs.window.Show() } @@ -320,7 +353,7 @@ func (hs *hostnameStatus) Show() { } func (hs *hostnameStatus) Hide() { - log.Info("hostnameStatus.Hide() window") + log.Log(STATUS, "hostnameStatus.Hide() window") if ! hs.hidden { hs.window.Hide() } diff --git a/nsupdate.go b/nsupdate.go index d86178f..2757b75 100644 --- a/nsupdate.go +++ b/nsupdate.go @@ -22,7 +22,7 @@ func nsupdate() { cmd := "go-nsupdate --tsig-algorithm=hmac-sha512" tsigSecret = os.Getenv("TIG_SECRET") cmd += " --tig-secret=\"" + tsigSecret + "\"" - cmd += " -i wlo1 " + me.hostname + cmd += " -i wlo1 " + me.status.GetHostname() log.Log(NET, "nsupdate() RUN:", cmd) for s, t := range me.ipmap { diff --git a/resolverBox.go b/resolverBox.go new file mode 100644 index 0000000..8a7e2f3 --- /dev/null +++ b/resolverBox.go @@ -0,0 +1,209 @@ +/* + Performs DNS queries on TCP and UDP +*/ + +package main + +import ( + "os" + "time" + "strconv" + "reflect" + + "go.wit.com/log" + "go.wit.com/gui/gui" + "go.wit.com/gui/gadgets" + + "github.com/miekg/dns" +) + +type resolverStatus struct { + title string + server string // The DNS server. Example: "127.0.0.1:53" or "1.1.1.1:53" + hostname string // the hostname to lookup. Example: "www.google.com" or "go.wit.com" + + parent *gui.Node + group *gui.Node + grid *gui.Node + + // DNS setup options + udpA *gui.Node + tcpA *gui.Node + udpAAAA *gui.Node + tcpAAAA *gui.Node + + // show the display + aFail *gui.Node + aSuccess *gui.Node + aaaaFail *gui.Node + aaaaSuccess *gui.Node + + // interger counters + aFailc int + aSuccessc int + aaaaFailc int + aaaaSuccessc int +} + +func (rs *resolverStatus) set(a any, s string) { + if a == nil { + return + } + var n *gui.Node + if reflect.TypeOf(a) == reflect.TypeOf(n) { + n = a.(*gui.Node) + n.SetText(s) + return + } + var ol *gadgets.OneLiner + if reflect.TypeOf(a) == reflect.TypeOf(ol) { + ol = a.(*gadgets.OneLiner) + ol.Set(s) + return + } + log.Warn("unknown type TypeOf(a) =", reflect.TypeOf(a), "a =", a) + os.Exit(0) +} + +// Makes a DNS Status Grid +func NewResolverStatus(p *gui.Node, title string, server string, hostname string) *resolverStatus { + var rs *resolverStatus + rs = new(resolverStatus) + rs.parent = p + rs.group = p.NewGroup(server + " " + title + " lookup") + rs.grid = rs.group.NewGrid("LookupStatus", 5, 2) + + rs.server = server + rs.hostname = hostname + + rs.grid.NewLabel("") + rs.grid.NewLabel("UDP") + rs.grid.NewLabel("TCP") + rs.grid.NewLabel("Success") + rs.grid.NewLabel("Fail") + + rs.grid.NewLabel("A") + rs.udpA = rs.grid.NewLabel("?") + rs.tcpA = rs.grid.NewLabel("?") + rs.aSuccess = rs.grid.NewLabel("?") + rs.aFail = rs.grid.NewLabel("?") + + rs.grid.NewLabel("AAAA") + rs.udpAAAA = rs.grid.NewLabel("?") + rs.tcpAAAA = rs.grid.NewLabel("?") + rs.aaaaSuccess = rs.grid.NewLabel("?") + rs.aaaaFail = rs.grid.NewLabel("?") + + rs.group.Margin() + rs.grid.Margin() + rs.group.Pad() + rs.grid.Pad() + + return rs +} + +// special thanks to the Element Hotel wifi in Philidelphia that allowed me to +// easily debug this code since the internet connection here blocks port 53 traffic +func (rs *resolverStatus) update() (bool, bool) { + var results []string + var a bool = false + var aaaa bool = false + + log.Log(DNS, "resolverStatus.update() For server", rs.server, "on", rs.hostname) + results, _ = dnsUdpLookup(rs.server, rs.hostname, dns.TypeA) + log.Log(DNS, "resolverStatus.update() UDP type A =", results) + + if (len(results) == 0) { + rs.set(rs.udpA, "BROKEN") + rs.aFailc += 1 + } else { + rs.set(rs.udpA, "WORKING") + rs.aSuccessc += 1 + a = true + } + + results, _ = dnsTcpLookup(rs.server, rs.hostname, dns.TypeA) + log.Log(DNS, "resolverStatus.update() TCP type A =", results) + + if (len(results) == 0) { + rs.set(rs.tcpA, "BROKEN") + rs.aFailc += 1 + } else { + me.digStatus.set(rs.tcpA, "WORKING") + rs.aSuccessc += 1 + a = true + } + + me.digStatus.set(rs.aFail, strconv.Itoa(rs.aFailc)) + me.digStatus.set(rs.aSuccess,strconv.Itoa(rs.aSuccessc)) + + results, _ = dnsUdpLookup(rs.server, rs.hostname, dns.TypeAAAA) + log.Log(DNS, "resolverStatus.update() UDP type AAAA =", results) + + if (len(results) == 0) { + me.digStatus.set(rs.udpAAAA, "BROKEN") + rs.aaaaFailc += 1 + me.digStatus.set(rs.aaaaFail, strconv.Itoa(rs.aaaaFailc)) + } else { + me.digStatus.set(rs.udpAAAA, "WORKING") + rs.aaaaSuccessc += 1 + aaaa = true + } + + results, _ = dnsTcpLookup(rs.server, rs.hostname, dns.TypeAAAA) + log.Log(DNS, "resolverStatus.update() UDP type AAAA =", results) + + if (len(results) == 0) { + me.digStatus.set(rs.tcpAAAA, "BROKEN") + rs.aaaaFailc += 1 + me.digStatus.set(rs.aaaaFail, strconv.Itoa(rs.aaaaFailc)) + } else { + me.digStatus.set(rs.tcpAAAA, "WORKING") + rs.aaaaSuccessc += 1 + aaaa = true + } + + me.digStatus.set(rs.aaaaFail, strconv.Itoa(rs.aaaaFailc)) + me.digStatus.set(rs.aaaaSuccess,strconv.Itoa(rs.aaaaSuccessc)) + + return a, aaaa +} + +// dnsLookup performs a DNS lookup for the specified record type (e.g., "TXT", "AAAA") for a given domain. +func dnsUdpLookup(server string, domain string, recordType uint16) ([]string, error) { + var records []string + + c := new(dns.Client) + m := new(dns.Msg) + m.SetQuestion(dns.Fqdn(domain), recordType) + r, _, err := c.Exchange(m, server) // If server = "1.1.1.1:53" then use Cloudflare's DNS server + if err != nil { + return nil, err + } + + for _, ans := range r.Answer { + records = append(records, ans.String()) + } + + return records, nil +} + +func dnsTcpLookup(server string, domain string, recordType uint16) ([]string, error) { + var records []string + + c := new(dns.Client) + c.Net = "tcp" // Specify to use TCP for the query + c.Timeout = time.Second * 5 // Set a 5-second timeout + m := new(dns.Msg) + m.SetQuestion(dns.Fqdn(domain), recordType) + r, _, err := c.Exchange(m, server) // If server = "1.1.1.1:53" then use Cloudflare's DNS server + if err != nil { + return nil, err + } + + for _, ans := range r.Answer { + records = append(records, ans.String()) + } + + return records, nil +} diff --git a/structs.go b/structs.go index 3d4e153..f794773 100644 --- a/structs.go +++ b/structs.go @@ -6,6 +6,7 @@ import ( "time" "go.wit.com/gui/gui" "go.wit.com/gui/gadgets" + "github.com/miekg/dns" ) @@ -13,10 +14,13 @@ import ( var me Host type Host struct { - hostname string // mirrors + status *hostnameStatus // keeps track of the hostname and it's status + + hostnameStatus *gui.Node // a summary for the user of where things are + + // hostname string // mirrors domainname *gui.Node // kernel.org hostshort *gui.Node // hostname -s - hostnameStatusOLD *gui.Node // is the hostname configured correctly in the OS? // fqdn string // mirrors.kernel.org // dnsTTL int `default:"3"` // Recheck DNS is working every TTL (in seconds) @@ -59,7 +63,7 @@ type Host struct { // DNS stuff NSrr *gui.Node // NS resource records for the domain name DnsAPI *gui.Node // what DNS API to use? - DnsAAAA *gui.Node // the actual DNS AAAA results + DnsAAAA *gadgets.OneLiner // the actual DNS AAAA results workingIPv6 *gui.Node // currently working AAAA DnsA *gui.Node // the actual DNS A results (ignore for status since mostly never happens?) DnsStatus *gui.Node // the current state of DNS @@ -81,7 +85,6 @@ type Host struct { statusIPv6 *gadgets.OneLiner digStatusButton *gui.Node - hostnameStatus *hostnameStatus hostnameStatusButton *gui.Node myDebug *gui.Node