diff options
Diffstat (limited to 'dnsLookupStatus.go')
| -rw-r--r-- | dnsLookupStatus.go | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/dnsLookupStatus.go b/dnsLookupStatus.go new file mode 100644 index 0000000..e623cae --- /dev/null +++ b/dnsLookupStatus.go @@ -0,0 +1,370 @@ +/* + '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 ( + "log" + "fmt" + "time" + "strconv" + + "github.com/miekg/dns" + "go.wit.com/gui" + "go.wit.com/control-panel-dns/cloudflare" + "go.wit.com/shell" +) + +type digStatus struct { + ready bool + statusIPv4 string + statusIPv6 string + + parent *gui.Node + window *gui.Node + group *gui.Node + grid *gui.Node + box *gui.Node + + summary *gui.Node + status *cloudflare.OneLiner + statusAAAA *cloudflare.OneLiner + speed *cloudflare.OneLiner + speedActual *cloudflare.OneLiner + + details *gui.Node + dsLocalhost *dnsStatus + dsLocalNetwork *dnsStatus + dsCloudflare *dnsStatus + dsGoogle *dnsStatus + DnsDigUDP *gui.Node + DnsDigTCP *gui.Node +} + +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.window = p.NewWindow("DNS Lookup Status") + ds.box = ds.window.NewBox("hBox", true) + + // summary of the current state of things + ds.summary = ds.box.NewGroup("Summary") + + b := ds.summary.NewBox("hBox", true) + ds.status = cloudflare.NewOneLiner(b, "status") + ds.status.Set("unknown") + + b = ds.summary.NewBox("hBox", true) + ds.statusAAAA = cloudflare.NewOneLiner(b, "IPv6 status") + ds.statusAAAA.Set("unknown") + + b = ds.summary.NewBox("hBox", true) + ds.speed = cloudflare.NewOneLiner(b, "speed") + ds.speed.Set("unknown") + + b = ds.summary.NewBox("hBox", true) + ds.speedActual = cloudflare.NewOneLiner(b, "actual") + ds.speedActual.Set("unknown") + + // make the area to store the raw details + ds.details = ds.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() + + return ds +} + +func (ds *digStatus) Update() { + duration := timeFunction(func () { ds.updateDnsStatus() }) + s := fmt.Sprint(duration) + ds.speedActual.Set(s) + + if (duration > 500 * time.Millisecond ) { + ds.speed.Set("SLOW") + } else if (duration > 100 * time.Millisecond ) { + ds.speed.Set("OK") + } else { + ds.speed.Set("FAST") + } +} + +// Returns true if the status is valid +func (ds *digStatus) Ready() bool { + return ds.ready +} + +// Returns true if IPv4 is working +func (ds *digStatus) IPv4() bool { + 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.statusIPv6 == "GOOD") { + return true + } + return false +} + +func (ds *digStatus) setIPv4(s string) { + ds.status.Set(s) + ds.statusIPv4 = s +} + +func (ds *digStatus) setIPv6(s string) { + ds.statusAAAA.Set(s) + ds.statusIPv6 = s +} + +func (ds *digStatus) updateDnsStatus() { + var cmd, out string + var ipv4, ipv6 bool + + ipv4, ipv6 = ds.dsLocalhost.Update() + ipv4, ipv6 = ds.dsLocalNetwork.Update() + ipv4, ipv6 = ds.dsCloudflare.Update() + ipv4, ipv6 = ds.dsGoogle.Update() + + 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) + ds.DnsDigUDP.SetText(out) + + cmd = "dig +noall +answer www.wit.com AAAA" + out = shell.Run(cmd) + log.Println("makeDnsStatusGrid() dig", out) + ds.DnsDigTCP.SetText(out) + + ds.ready = true +} + +// 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) { + ds.udpA.SetText("BROKEN") + ds.aFailc += 1 + } else { + ds.udpA.SetText("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) { + ds.tcpA.SetText("BROKEN") + ds.aFailc += 1 + } else { + ds.tcpA.SetText("WORKING") + ds.aSuccessc += 1 + a = true + } + + ds.aFail.SetText(strconv.Itoa(ds.aFailc)) + ds.aSuccess.SetText(strconv.Itoa(ds.aSuccessc)) + + results, _ = dnsUdpLookup(ds.server, ds.hostname, dns.TypeAAAA) + log.Println("dnsStatus.Update() UDP type AAAA =", results) + + if (len(results) == 0) { + ds.udpAAAA.SetText("BROKEN") + ds.aaaaFailc += 1 + ds.aaaaFail.SetText(strconv.Itoa(ds.aaaaFailc)) + } else { + ds.udpAAAA.SetText("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) { + ds.tcpAAAA.SetText("BROKEN") + ds.aaaaFailc += 1 + ds.aaaaFail.SetText(strconv.Itoa(ds.aaaaFailc)) + } else { + ds.tcpAAAA.SetText("WORKING") + ds.aaaaSuccessc += 1 + aaaa = true + } + + ds.aaaaFail.SetText(strconv.Itoa(ds.aaaaFailc)) + ds.aaaaSuccess.SetText(strconv.Itoa(ds.aaaaSuccessc)) + + return a, aaaa +} + +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) + ds.DnsDigUDP.SetText(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) + ds.DnsDigTCP.SetText(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 +} |
