Detect that a VPN is needed

IPv6() returns true if it's working
    display duration
    a 'DNS Lookup Status' window
    actual dig results
    display status and failure counters
    count lookup failures and successes
    add TCP dns lookup
    logic to test if dns is working at all
    add DNS over HTTPS
    cloudflare new & update kind of working
    holy shit, go.wit.com finally works with git mod tidy
    working, but cloudflare api stuff is broken
    AAAA '(none)' logic detection is better
    cloudflare control panel
    display the working real AAAA addresses

Signed-off-by: Jeff Carr <jcarr@wit.com>
This commit is contained in:
Jeff Carr 2023-12-28 09:43:45 -06:00
parent e697a29bab
commit 88fb976119
5 changed files with 431 additions and 75 deletions

2
.gitignore vendored
View File

@ -4,4 +4,4 @@ control-panel-dns
*.swp *.swp
/plugins/* /plugins/*
examples/cloudflare/cloudflare examples/control-panel-cloudflare/control-panel-cloudflare

370
dnsLookupStatus.go Normal file
View File

@ -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
}

View File

@ -1,71 +0,0 @@
// This is a simple example
package main
import (
"os"
"log"
"bufio"
"strings"
"go.wit.com/control-panel-dns/cloudflare"
)
func saveConfig() {
log.Println("TODO")
}
func readConfig() {
homeDir, err := os.UserHomeDir()
if err != nil {
log.Println("searchPaths() error. exiting here?")
}
filename := homeDir + "/" + configfile
log.Println("filename =", filename)
readFileLineByLine(filename)
// os.Exit(0)
}
// readFileLineByLine opens a file and reads through each line.
func readFileLineByLine(filename string) error {
// Open the file.
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close()
log.Println("readFileLineByLine() =", filename)
// Create a new Scanner for the file.
scanner := bufio.NewScanner(file)
// Read through each line using scanner.
for scanner.Scan() {
var newc *cloudflare.ConfigT
newc = new(cloudflare.ConfigT)
line := scanner.Text()
parts := strings.Fields(line)
if (len(parts) < 4) {
log.Println("readFileLineByLine() SKIP =", parts)
continue
}
newc.Domain = parts[0]
newc.ZoneID = parts[1]
newc.Auth = parts[2]
newc.Email = parts[3]
cloudflare.Config[parts[0]] = newc
log.Println("readFileLineByLine() =", newc.Domain, newc.ZoneID, newc.Auth, newc.Email)
}
// Check for errors during Scan.
if err := scanner.Err(); err != nil {
return err
}
return nil
}

View File

@ -6,17 +6,22 @@
package main package main
import ( import (
"log"
// "net"
"strings"
"go.wit.com/shell" "go.wit.com/shell"
"go.wit.com/control-panel-dns/cloudflare" "go.wit.com/control-panel-dns/cloudflare"
"go.wit.com/control-panel-dns/dnssecsocket"
"github.com/miekg/dns"
) )
// will try to get this hosts FQDN // will try to get this hosts FQDN
import "github.com/Showmax/go-fqdn" import "github.com/Showmax/go-fqdn"
// this is the king of dns libraries // this is the king of dns libraries
import "github.com/miekg/dns" // import "github.com/miekg/dns"
func getHostname() { func getHostname() {
@ -98,6 +103,7 @@ func goodHostname(h string) bool {
return false return false
} }
/*
func digAAAA(s string) []string { func digAAAA(s string) []string {
var aaaa []string var aaaa []string
// lookup the IP address from DNS // lookup the IP address from DNS
@ -114,5 +120,52 @@ func digAAAA(s string) []string {
me.ipv6s[ipaddr] = rr me.ipv6s[ipaddr] = rr
} }
debug(true, args.VerboseDNS, "aaaa =", aaaa) debug(true, args.VerboseDNS, "aaaa =", aaaa)
log.Println("digAAAA() returned =", aaaa)
log.Println("digAAAA() me.ipv6s =", me.ipv6s)
os.Exit(0)
return aaaa return aaaa
} }
*/
func digAAAA(hostname string) []string {
var blah, ipv6Addresses []string
// domain := hostname
recordType := dns.TypeAAAA // dns.TypeTXT
// Cloudflare's DNS server
blah, _ = dnsUdpLookup("1.1.1.1:53", hostname, recordType)
log.Println("digAAAA() has BLAH =", blah)
if (len(blah) == 0) {
log.Println("digAAAA() RUNNING dnsLookupDoH(domain)")
ipv6Addresses, _ = dnsLookupDoH(hostname)
log.Println("digAAAA() has ipv6Addresses =", strings.Join(ipv6Addresses, " "))
log.Printf("digAAAA() IPv6 Addresses for %s:\n", hostname)
for _, addr := range ipv6Addresses {
log.Println(addr)
}
return ipv6Addresses
}
// TODO: check digDoH vs blah, if so, then port 53 TCP and/or UDP is broken or blocked
log.Println("digAAAA() has BLAH =", blah)
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 {
fmt.Println("Error:", err)
return
}
fmt.Printf("IPv6 Addresses for %s:\n", domain)
for _, addr := range ipv6Addresses {
fmt.Println(addr)
}
}
*/

6
net.go
View File

@ -97,7 +97,11 @@ func checkInterface(i net.Interface) {
} }
} }
func realAAAA() []string { /*
These are the real IP address you have been
given from DHCP
*/
func dhcpAAAA() []string {
var aaaa []string var aaaa []string
for s, t := range me.ipmap { for s, t := range me.ipmap {