/* '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 ( "errors" "fmt" "os" "reflect" "strings" "time" "go.wit.com/gui" "go.wit.com/lib/gadgets" "go.wit.com/lib/gui/shell" "go.wit.com/log" ) 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 detailsGroup *gui.Node details *gui.Node dsLocalhost *resolverStatus dsLocalNetwork *resolverStatus dsCloudflare *resolverStatus dsGoogle *resolverStatus DnsDigUDP *gui.Node DnsDigTCP *gui.Node httpGoWitCom *gadgets.OneLiner statusHTTP *gadgets.OneLiner } func InitDigStatus() *digStatus { var ds *digStatus ds = new(digStatus) ds.ready = false ds.hidden = true ds.window = gadgets.RawBasicWindow("DNS Resolver Status") ds.window.Make() // 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").SetText("unknown") ds.statusAAAA = gadgets.NewOneLiner(g, "IPv6 status").SetText("unknown") ds.statusHTTP = gadgets.NewOneLiner(g, "IPv6 via HTTP").SetText("unknown") ds.speed = gadgets.NewOneLiner(g, "speed").SetText("unknown") ds.speedActual = gadgets.NewOneLiner(g, "actual").SetText("unknown") // make the area to store the raw details ds.detailsGroup = ds.window.Box().NewGroup("Details") ds.details = ds.detailsGroup.NewBox("bw vbox", false) 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.SetText(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.SetText(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.SetText(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.statusOS.ValidHostname() { if ds.checkLookupDoH(me.statusOS.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") } r := shell.Run([]string{"dig", "+noall", "+answer", "www.wit.com", "A"}) log.Log(DNS, "makeDnsStatusGrid() dig", r.Stdout) me.digStatus.set(ds.DnsDigUDP, strings.Join(r.Stdout, "\n")) r = shell.Run([]string{"dig", "+noall", "+answer", "www.wit.com", "AAAA"}) log.Log(DNS, "makeDnsStatusGrid() dig", r.Stdout) me.digStatus.set(ds.DnsDigTCP, strings.Join(r.Stdout, "\n")) /* g2.NewButton("dig +trace", func () { log.Log(NOW, "TODO: redo this") // o := shell.Run("dig +trace +noadditional DS " + me.hostname + " @8.8.8.8") // log.Println(o) }) */ } 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() { group := ds.details.NewGroup("dig results") grid := group.NewGrid("LookupStatus", 2, 2) grid.NewLabel("dig +noall +answer go.wit.com A") ds.DnsDigUDP = grid.NewLabel("?") grid.NewLabel("dig +noall +answer go.wit.com AAAA") ds.DnsDigTCP = grid.NewLabel("?") 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 } func digLoop() { me.digStatus.Update() if me.digStatus.Ready() { current := me.statusIPv6.String() if me.digStatus.IPv6() { if current != "WORKING" { log.Log(CHANGE, "IPv6 resolution is WORKING") me.statusIPv6.SetText("WORKING") } } else { if current != "Need VPN" { log.Log(CHANGE, "IPv6 resolution seems to have broken") me.statusIPv6.SetText("Need VPN") } } } }