From 3f55332cbf2ad2c626c42ed306b7da13f24a1d42 Mon Sep 17 00:00:00 2001 From: Jeff Carr Date: Sat, 6 Jan 2024 01:15:48 -0600 Subject: [PATCH] compiles again. failed digstatus/ attempt Signed-off-by: Jeff Carr --- digstatus/args.go | 21 ++ digstatus/digStatus.go | 500 +++++++++++++++++++++++++++++++++++++++++ digstatus/dns-https.go | 56 +++++ dns-https.go | 8 +- dnsLookupStatus.go | 6 +- gui.go | 2 - hostname.go | 2 +- hostnameStatus.go | 8 +- structs.go | 1 + 9 files changed, 589 insertions(+), 15 deletions(-) create mode 100644 digstatus/args.go create mode 100644 digstatus/digStatus.go create mode 100644 digstatus/dns-https.go diff --git a/digstatus/args.go b/digstatus/args.go new file mode 100644 index 0000000..91eefb1 --- /dev/null +++ b/digstatus/args.go @@ -0,0 +1,21 @@ +package digstatus + +/* + this parses the command line arguements + + this enables command line options from other packages like 'gui' and 'log' +*/ + +import ( + "go.wit.com/log" +) + +var DNS log.LogFlag + +func init() { + DNS.B = false + DNS.Name = "DNS" + DNS.Subsystem = "cpdns" + DNS.Desc = "dnsStatus.update()" + DNS.Register() +} diff --git a/digstatus/digStatus.go b/digstatus/digStatus.go new file mode 100644 index 0000000..c7b5fef --- /dev/null +++ b/digstatus/digStatus.go @@ -0,0 +1,500 @@ +/* + '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 digstatus + +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" +) + +var ds *DigStatus + +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 { + ds = new(DigStatus) + + ds.ready = false + ds.hidden = true + + ds.window = gadgets.NewBasicWindow(p, "DNS Resolver Status (DigStatus.go)") + 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.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) + set(ds.speedActual, s) + + if (duration > 500 * time.Millisecond ) { + set(ds.speed, "SLOW") + } else if (duration > 100 * time.Millisecond ) { + set(ds.speed, "OK") + } else { + 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} + set(ds.status, s) +} + +func (ds *DigStatus) setIPv6status(s string) { + ds.statusIPv6 = s + if ! ds.Ready() {return} + set(ds.statusAAAA, s) +} + +/* +func (ds *DigStatus) SetIPv6(s string) { + if ! ds.Ready() {return} + // me.DnsAAAA.Set(s) + // me.DigStatus.set(ds.httpGoWitCom, addr) +} +*/ + +func 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) +} + +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() + + // This looks up the known host go.wit.com to verify DNS over HTTP is working + // if this doesn't work, probably your internet connection isn't working either + if ds.checkLookupDoH("go.wit.com") { + log.Log(DNS, "updateDnsStatus() HTTP DNS lookups working") + 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") + set(ds.statusHTTP, "BROKEN") + } + + 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) + set(ds.DnsDigUDP, out) + + cmd = "dig +noall +answer www.wit.com AAAA" + out = shell.Run(cmd) + log.Log(DNS, "makeDnsStatusGrid() dig", out) + 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 (dns *dnsStatus) update() (bool, bool) { + if ! ds.Ready() {return false, false} + var results []string + var a bool = false + var aaaa bool = false + + if dns == nil { + log.Warn("ds == nil") + } else { + log.Warn("dnsStatus.update() For server", dns.server, "on", dns.hostname) + } + + log.Log(DNS, "dnsStatus.update() For server", dns.server, "on", dns.hostname) + results, _ = DnsUdpLookup(dns.server, dns.hostname, dns.TypeA) + log.Log(DNS, "dnsStatus.update() UDP type A =", results) + + if (len(results) == 0) { + set(dns.udpA, "BROKEN") + dns.aFailc += 1 + } else { + set(dns.udpA, "WORKING") + dns.aSuccessc += 1 + a = true + } + + results, _ = dnsTcpLookup(dns.server, dns.hostname, dns.TypeA) + log.Log(DNS, "dnsStatus.update() TCP type A =", results) + + if (len(results) == 0) { + set(ds.tcpA, "BROKEN") + ds.aFailc += 1 + } else { + set(ds.tcpA, "WORKING") + ds.aSuccessc += 1 + a = true + } + + set(ds.aFail, strconv.Itoa(ds.aFailc)) + set(ds.aSuccess,strconv.Itoa(ds.aSuccessc)) + + results, _ = DnsUdpLookup(ds.server, ds.hostname, dns.TypeAAAA) + log.Log(DNS, "dnsStatus.update() UDP type AAAA =", results) + + if (len(results) == 0) { + set(ds.udpAAAA, "BROKEN") + ds.aaaaFailc += 1 + set(ds.aaaaFail, strconv.Itoa(ds.aaaaFailc)) + } else { + set(ds.udpAAAA, "WORKING") + ds.aaaaSuccessc += 1 + aaaa = true + } + + results, _ = dnsTcpLookup(ds.server, ds.hostname, dns.TypeAAAA) + log.Log(DNS, "dnsStatus.update() UDP type AAAA =", results) + + if (len(results) == 0) { + set(ds.tcpAAAA, "BROKEN") + ds.aaaaFailc += 1 + set(ds.aaaaFail, strconv.Itoa(ds.aaaaFailc)) + } else { + set(ds.tcpAAAA, "WORKING") + ds.aaaaSuccessc += 1 + aaaa = true + } + + set(ds.aaaaFail, strconv.Itoa(ds.aaaaFailc)) + 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") + 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) + 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) + 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 + + aaaa := lookupDoH(hostname, "AAAA") + + log.Log(DNS, "IPv6 Addresses for ", hostname, "=", aaaa) + var s []string + for _, addr := range aaaa { + log.Log(DNS, addr) + s = append(s, addr) + status = true + } + return status +} + +func (ds *DigStatus) Show() { + log.Info("DigStatus.Show() window") + if ds.hidden { + ds.window.Show() + } + ds.hidden = false +} + +func (ds *DigStatus) Hide() { + log.Info("DigStatus.Hide() window") + if ! ds.hidden { + ds.window.Hide() + } + ds.hidden = true +} + +func (ds *DigStatus) Toggle() { + if ds.hidden { + ds.window.Show() + } else { + ds.window.Hide() + } +} + +// timeFunction takes a function as an argument and returns the execution time. +func timeFunction(f func()) time.Duration { + startTime := time.Now() // Record the start time + f() // Execute the function + return time.Since(startTime) // Calculate the elapsed time +} diff --git a/digstatus/dns-https.go b/digstatus/dns-https.go new file mode 100644 index 0000000..2ad13df --- /dev/null +++ b/digstatus/dns-https.go @@ -0,0 +1,56 @@ +package digstatus + +import ( + "fmt" + "go.wit.com/log" + "io/ioutil" + "encoding/json" + "net/http" +) + +// 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.com/resolve?name=%s&type=%s", hostname, rrType) + + 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) + if err != nil { + log.Error(err, "error performing DNS-over-HTTPS request") + return nil + } + defer resp.Body.Close() + + // Read and unmarshal the response body + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Error(fmt.Errorf("error reading response: %w", err)) + return nil + } + + var data struct { + Answer []struct { + Data string `json:"data"` + } `json:"Answer"` + } + + if err := json.Unmarshal(body, &data); err != nil { + log.Error(fmt.Errorf("error unmarshaling response: %w", err)) + return nil + } + + // Extract the IPv6 addresses + for _, answer := range data.Answer { + values = append(values, answer.Data) + } + + return values +} diff --git a/dns-https.go b/dns-https.go index c684835..f4b6b52 100644 --- a/dns-https.go +++ b/dns-https.go @@ -8,12 +8,13 @@ import ( "net/http" ) +/* // dnsLookupDoH performs a DNS lookup for AAAA records over HTTPS. -func dnsAAAAlookupDoH(hostname string) ([]string, error) { +func dnsAAAAlookupDoHold(hostname 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", hostname) + url := fmt.Sprintf("https://dns.google.com/resolve?name=%s&type=AAAA", hostname) log.Log(DNS, "dnsAAAAlookupDoH()", url) if hostname == "" { @@ -51,13 +52,14 @@ func dnsAAAAlookupDoH(hostname string) ([]string, error) { 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.Log(DNS, "lookupDoH()", url) if hostname == "" { diff --git a/dnsLookupStatus.go b/dnsLookupStatus.go index e06971b..8f5cb4c 100644 --- a/dnsLookupStatus.go +++ b/dnsLookupStatus.go @@ -458,11 +458,7 @@ func dnsTcpLookup(server string, domain string, recordType uint16) ([]string, er func (ds *digStatus) checkLookupDoH(hostname string) bool { var status bool = false - ipv6Addresses, err := dnsAAAAlookupDoH(hostname) - if err != nil { - log.Error(err, "checkLookupDoH()") - return status - } + ipv6Addresses := lookupDoH(hostname, "AAAA") log.Log(DNS, "IPv6 Addresses for ", hostname) var s []string diff --git a/gui.go b/gui.go index 0b96e39..8aa3659 100644 --- a/gui.go +++ b/gui.go @@ -25,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) { diff --git a/hostname.go b/hostname.go index b21aa9b..2181abf 100644 --- a/hostname.go +++ b/hostname.go @@ -121,7 +121,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) diff --git a/hostnameStatus.go b/hostnameStatus.go index 0e09709..2fd9bdd 100644 --- a/hostnameStatus.go +++ b/hostnameStatus.go @@ -286,10 +286,10 @@ func (hs *hostnameStatus) missingAAAA() bool { } func (hs *hostnameStatus) updateStatus() { + if ! hs.Ready() { return } var s string var vals []string log.Log(STATUS, "updateStatus() START") - if ! hs.Ready() { return } hs.hostShort.Set(me.hostshort.S) hs.domainname.Set(me.domainname.S) @@ -300,14 +300,14 @@ func (hs *hostnameStatus) updateStatus() { 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") } - } else { for _, addr := range vals { log.Log(STATUS, addr) - s += addr + " (DELETE)" + s += addr + " (DELETE)" + "\n" hs.setIPv6("NEEDS DELETE") hs.dnsValue.SetText(addr) hs.dnsAction.SetText("DELETE") @@ -335,7 +335,7 @@ func (hs *hostnameStatus) updateStatus() { 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") diff --git a/structs.go b/structs.go index ec32ca5..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" )