v0.0.2 next step: acutally try to nsupdate

upto the point where DNS update is next.
    start displaying real AAAA & naming buttons
    add RFC 2136 defining nsupdate. Vixie et al in 1997
        Personal thansk to Paul for meeting with me some years back
    ready to pull DNS records
    starting a checkDNS() function
    dampen output. actually track IPs
    poll every 2 seconds (netlink is not the right thing here)
    ready to start looking for changes
    screw everything about logging. I hate log.whatthefuck*(){}
    Do you know what I don't care about? log()
    You shouldn't care either. Ignore it until you need it
    that is what logging is for. building something that works.
    So, here you go. a damn log() function in one place
    Also, because I'm annoyed today sleep() and exit()
    Because, when I want you to sleep or exit, I don't
    want to go to the top of a file and declare stupid shit related
    to nanoseconds or add "import os.Exit" or whatever the hell
    stop wasting my time. life is short.
    if he sit tunnelbroker down
    add IsRealIP() and IsIPv6()
    need a netlink function to trigger on changes (nope)
    put the gui plugin's in the debian package for now
    set the window title
    build a .deb package

    Signed-off-by: Jeff Carr <jcarr@wit.com>
This commit is contained in:
Jeff Carr 2023-02-18 23:37:11 -06:00
parent 3959b6c328
commit 7ad38cdf6c
21 changed files with 2027 additions and 126 deletions

2
.gitignore vendored
View File

@ -1 +1,3 @@
control-panel-dns
/files/*
/*.deb

View File

@ -1,16 +1,59 @@
run: build
./control-panel-dns
verbose: build
./control-panel-dns --verbose --verbose-net --gui-debug --toolkit-debug
dns: build
./control-panel-dns --verbose-dns
build-release:
reset
go get -v -u -x .
go build
build:
reset
GO111MODULE="off" go get -v -x .
GO111MODULE="off" go build
GO111MODULE="off" go build -v
test:
GO111MODULE="off" go test -v
update:
GO111MODULE="off" go get -v -u -x .
clean:
rm control-panel-dns
-rm control-panel-dns
-rm -rf files/
-rm *.deb
deb:
cd debian && make
-wit mirrors
netlink:
GO111MODULE="off" go get -v -u github.com/vishvananda/netlink
####### MODULE STUFF DOWN HERE
#
# What again is the 'right' way to do this?
# It seems like it changes from year to year. This is better than 'vendor/' (that was a terrible hack)
# maybe it's settled down finally. Use GO111MODULE="off" when you are developing. (?)
# When you are ready to release, version this and all the packages correctly. (?)
#
# At least, that is what I'm going to try to do as of Feb 18 2023.
#
build-with-custom-go.mod:
go build -modfile=local.go.mod ./...
# module <yourname>
# go 1.18
# require (
# github.com/versent/saml2aws/v2 v2.35.0
# )
# replace github.com/versent/saml2aws/v2 v2.35.0 => github.com/marcottedan/saml2aws/v2 master
# replace github.com/versent/saml2aws/v2 => /Users/dmarcotte/git/saml2aws/

View File

@ -9,6 +9,13 @@ Goals:
* Run as a daemon
* When run in GUI, add status via systray
# Rational
With the advent of IPv6, it is finally possible again to have real hostnames for
your machines, desktops, laptops, vm's, etc. This control panel will poll for
changes, find out what the DNS entries are, then, if they are not correct, attempt
to update the DNS server.
## References
Useful links and other

1138
RFC-2136 Normal file

File diff suppressed because it is too large Load Diff

View File

@ -6,8 +6,10 @@ import (
)
type LogOptions struct {
LogFile string
LogFile string `help:"write all output to a file"`
Verbose bool
VerboseNet bool `arg:"--verbose-net" help:"debug network settings"`
VerboseDNS bool `arg:"--verbose-dns" help:"debug dns settings"`
// GuiDebug bool `help:"open up the wit/gui Debugging Window"`
// GuiDemo bool `help:"open the wit/gui Demo Window"`
User string `arg:"env:USER"`

3
debian/Makefile vendored
View File

@ -20,6 +20,9 @@ clean:
extract:
mkdir -p ../files/usr/bin
mkdir -p ../files/usr/lib/control-panel-dns/
-cp ~/go/src/git.wit.org/wit/gui/toolkit/*.so ../files/usr/lib/control-panel-dns/
cp ../README.md ../files/usr/lib/control-panel-dns/
cp ../control-panel-dns ../files/usr/bin/
# makes the DEBIAN/ directory

10
debian/control vendored
View File

@ -1,9 +1,9 @@
Source: go-wit-gui
Source: control-panel-dns
Build-Depends: golang
Package: go-wit-gui
Package: control-panel-dns
Maintainer: Jeff Carr <jcarr@wit.com>
Architecture: amd64
Depends:
Recommends: libgtk-3-0
Description: a abstraction layer for Go visual elements (GTK, QT, etc)
Package gui implements a abstraction layer for Go visual elements.
Recommends: libgtk-3-0, ddclient, ddupdate
Description: a control panel for DNS and IPv6 settings
Goals: show the settings, validate & update DNS

65
dns.go
View File

@ -5,28 +5,10 @@
package main
import (
// "os"
// "os/exec"
"log"
"net"
// "git.wit.org/wit/gui"
// "github.com/davecgh/go-spew/spew"
)
type IPtype struct {
// IP string
IPv4 bool
IPv6 bool
LinkLocal bool
}
type Host struct {
Name string
domainname string
hostname string
fqdn string
ips map[string]*IPtype
}
var dnsTTL int = 3600; // Recheck DNS is working every TTL (in seconds)
/*
Check a bunch of things. If they don't work right, then things are not correctly configured
@ -43,9 +25,50 @@ func (h *Host) verifyETC() bool {
func (h *Host) updateIPs(host string) {
ips, err := net.LookupIP(host)
if err != nil {
log.Fatal(err)
exit(err)
}
for _, ip := range ips {
log.Println(host, ip)
log(host, ip)
}
}
func (h *Host) setIPv4(ipv4s map[string]*IPtype) {
for ip, t := range ipv4s {
log("IPv4", ip, t)
}
}
func (h *Host) checkDNS() {
var ip4 bool = false
var ip6 bool = false
for s, t := range h.ipmap {
i := t.iface
ipt := "IPv4"
if (t.ipv6) {
ipt = "IPv6"
}
if (! t.IsReal()) {
log(args.VerboseDNS, "\tIP is not Real", ipt, i.Index, i.Name, s)
continue
}
log(args.VerboseDNS, "\tIP is Real ", ipt, i.Index, i.Name, s)
if (t.ipv6) {
ip6 = true
} else {
ip4 = true
}
}
if (ip4 == true) {
log(args.VerboseDNS, "IPv4 should work. Wow. You actually have a real IPv4 address")
} else {
log(args.VerboseDNS, "IPv4 is broken. (be nice and setup ipv4-only.wit.com)")
}
if (ip6 == true) {
log(args.VerboseDNS, "IPv6 should be working. Need to test it here.")
} else {
log(args.VerboseDNS, "IPv6 is broken. Need to fix it here.")
}
}

32
dynamic-dns-update.go Normal file
View File

@ -0,0 +1,32 @@
package main
/*
https://pkg.go.dev/github.com/miekg/dns#section-readme
DYNAMIC UPDATES
Dynamic updates reuses the DNS message format, but renames three of the sections. Question is Zone, Answer is Prerequisite, Authority is Update, only the Additional is not renamed. See RFC 2136 for the gory details.
You can set a rather complex set of rules for the existence of absence of certain resource records or names in a zone to specify if resource records should be added or removed. The table from RFC 2136 supplemented with the Go DNS function shows which functions exist to specify the prerequisites.
3.2.4 - Table Of Metavalues Used In Prerequisite Section
CLASS TYPE RDATA Meaning Function
--------------------------------------------------------------
ANY ANY empty Name is in use dns.NameUsed
ANY rrset empty RRset exists (value indep) dns.RRsetUsed
NONE ANY empty Name is not in use dns.NameNotUsed
NONE rrset empty RRset does not exist dns.RRsetNotUsed
zone rrset rr RRset exists (value dep) dns.Used
The prerequisite section can also be left empty. If you have decided on the prerequisites you can tell what RRs should be added or deleted. The next table shows the options you have and what functions to call.
3.4.2.6 - Table Of Metavalues Used In Update Section
CLASS TYPE RDATA Meaning Function
---------------------------------------------------------------
ANY ANY empty Delete all RRsets from name dns.RemoveName
ANY rrset empty Delete an RRset dns.RemoveRRset
NONE rrset rr Delete an RR from RRset dns.Remove
zone rrset rr Add to an RRset dns.Insert
*/

76
fsnotify.go Normal file
View File

@ -0,0 +1,76 @@
package main
// Watches for changes to a directory. Works cross-platform
import (
"github.com/fsnotify/fsnotify"
)
// This would be a really dumb way to watch for new network interfaces
// since it then watches a linux only directory /sys/class/net for changes
func watchSysClassNet() {
// Create new watcher.
watcher, err := fsnotify.NewWatcher()
if err != nil {
exit(err)
}
defer watcher.Close()
// Start listening for events.
go func() {
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
log("event:", event)
if event.Has(fsnotify.Write) {
log("modified file:", event.Name)
}
case err, ok := <-watcher.Errors:
if !ok {
return
}
log("error:", err)
}
}
}()
// Add a path.
err = watcher.Add("/tmp")
if err != nil {
exit(err)
}
// Block main goroutine forever.
<-make(chan struct{})
}
func fsnotifyNetworkInterfaceChanges() error {
watcher, err := fsnotify.NewWatcher()
if err != nil {
return err
}
defer watcher.Close()
// Watch for network interface changes
err = watcher.Add("/sys/class/net")
if err != nil {
return err
}
for {
select {
case event := <-watcher.Events:
log("fsnotifyNetworkInterfaceChanges() event =", event)
if event.Op&fsnotify.Create == fsnotify.Create {
// Do something on network interface creation
}
case err := <-watcher.Errors:
log("fsnotifyNetworkInterfaceChanges() event err =", err)
return err
}
}
}

59
gui.go
View File

@ -4,7 +4,6 @@ package main
import (
"os"
"os/user"
"log"
"net"
"git.wit.org/wit/gui"
"github.com/davecgh/go-spew/spew"
@ -13,14 +12,14 @@ import (
// This initializes the first window
func initGUI() {
var w *gui.Node
gui.Config.Title = "Hello World golang wit/gui Window"
gui.Config.Title = "DNS and IPv6 Control Panel"
gui.Config.Width = 640
gui.Config.Height = 480
gui.Config.Exit = myDefaultExit
w = gui.NewWindow()
w.Dump()
addDemoTab(w, "A Simple Tab Demo")
addDNSTab(w, "DNS")
// TODO: add these back
if (args.GuiDemo) {
@ -32,16 +31,14 @@ func initGUI() {
}
}
func addDemoTab(window *gui.Node, title string) {
func addDNSTab(window *gui.Node, title string) {
var newNode, g, g2, tb *gui.Node
var err error
var name string
newNode = window.NewTab(title)
log.Println("addDemoTab() newNode.Dump")
log("addDemoTab() newNode.Dump")
newNode.Dump()
g = newNode.NewGroup("group 1")
g = newNode.NewGroup("junk")
dd := g.NewDropdown("demoCombo2")
dd.AddDropdownName("more 1")
dd.AddDropdownName("more 2")
@ -49,27 +46,49 @@ func addDemoTab(window *gui.Node, title string) {
dd.OnChanged = func(*gui.Node) {
s := dd.GetText()
tb.SetText("hello world " + args.User + "\n" + s)
log("text =", s)
}
g.NewLabel("UID =")
g.NewButton("hello", func () {
log("world")
})
g2 = newNode.NewGroup("group 2")
g2 = newNode.NewGroup("Real Stuff")
tb = g2.NewTextbox("tb")
log.Println("tb =", tb.GetText())
log("tb =", tb.GetText())
tb.OnChanged = func(*gui.Node) {
s := tb.GetText()
log.Println("text =", s)
log("text =", s)
}
g2.NewButton("hello", func () {
log.Println("world")
scanInterfaces()
g2.NewButton("Network Interfaces", func () {
for i, t := range me.ifmap {
log("name =", t.iface.Name)
log("int =", i, "name =", t.name, t.iface)
dd.AddDropdownName(t.iface.Name)
}
})
g2.NewButton("os.Hostname()", func () {
name, err = os.Hostname()
log.Println("name =", name, err)
g2.NewButton("Hostname", func () {
getHostname()
g.NewLabel("FQDN = " + me.fqdn)
})
g2.NewButton("Actual AAAA", func () {
var aaaa []string
aaaa = realAAAA()
for _, s := range aaaa {
g.NewLabel("my actual AAAA = " + s)
}
})
g2.NewButton("checkDNS()", func () {
checkDNS()
})
g2.NewButton("os.User()", func () {
user, _ := user.Current()
spew.Dump(user)
log.Println("os.Getuid =", os.Getuid())
log("os.Getuid =", os.Getuid())
})
g2.NewButton("Example_listLink()", func () {
Example_listLink()
})
g2.NewButton("Escalate()", func () {
Escalate()
@ -79,7 +98,7 @@ func addDemoTab(window *gui.Node, title string) {
if err != nil {
return
}
log.Println("host =", host)
log("host =", host)
})
g2.NewButton("DumpPublicDNSZone(apple.com)", func () {
DumpPublicDNSZone("apple.com")
@ -88,6 +107,6 @@ func addDemoTab(window *gui.Node, title string) {
}
func myDefaultExit(n *gui.Node) {
log.Println("You can Do exit() things here")
log("You can Do exit() things here")
os.Exit(0)
}

57
he-ipv6-tunnel.sh Executable file
View File

@ -0,0 +1,57 @@
#!/bin/bash -x
## Tunnel ID: 818143
# Creation Date:Feb 12, 2023
# Description:
# IPv6 Tunnel Endpoints
# Server IPv4 Address:184.105.253.14
# Server IPv6 Address:2001:470:1f10:2a::1/64
# Client IPv4 Address:74.87.91.117
# Client IPv6 Address:2001:470:1f10:2a::2/64
# Routed IPv6 Prefixes
# Routed /64:2001:470:1f11:2a::/64
# Routed /48:Assign /48
# DNS Resolvers
# Anycast IPv6 Caching Nameserver:2001:470:20::2
# Anycast IPv4 Caching Nameserver:74.82.42.42
# DNS over HTTPS / DNS over TLS:ordns.he.net
# rDNS DelegationsEdit
# rDNS Delegated NS1:
# rDNS Delegated NS2:
# rDNS Delegated NS3:
# rDNS Delegated NS4:
# rDNS Delegated NS5:
# ifconfig sit0 up
# ifconfig sit0 inet6 tunnel ::184.105.253.14
# ifconfig sit1 up
# ifconfig sit1 inet6 add 2001:470:1f10:2a::2/64
# route -A inet6 add ::/0 dev sit1
if [ "$1" = "down" ]; then
ip tunnel del he-ipv6
rmmod sit
exit
fi
if [ "$1" = "ping" ]; then
ping -c 3 2001:470:1f10:13d::1
exit
fi
modprobe ipv6
ip tunnel add he-ipv6 mode sit remote 184.105.253.14 local 40.132.180.131 ttl 255
ip link set he-ipv6 up
ip addr add 2001:470:1f10:13d::2/64 dev he-ipv6
ip route add ::/0 dev he-ipv6
ip -f inet6 addr
ifconfig he-ipv6 mtu 1460
# old attempt from the something or pabtz hotel
# modprobe ipv6
# ip tunnel add he-ipv6 mode sit remote 184.105.253.14 local 74.87.91.117 ttl 255
# ip link set he-ipv6 up
# ip addr add 2001:470:1f10:2a::2/64 dev he-ipv6
# ip route add ::/0 dev he-ipv6
# ip -f inet6 addr

45
hostname.go Normal file
View File

@ -0,0 +1,45 @@
// inspired from:
// https://github.com/mactsouk/opensource.com.git
// and
// https://coderwall.com/p/wohavg/creating-a-simple-tcp-server-in-go
package main
// import "net"
// will try to get this hosts FQDN
import "github.com/Showmax/go-fqdn"
// this is the king of dns libraries
import "github.com/miekg/dns"
// dnssec IPv6 socket library
import "git.wit.org/jcarr/dnssecsocket"
func getHostname() {
var err error
me.fqdn, err = fqdn.FqdnHostname()
if (err != nil) {
log("FQDN hostname error =", err)
exit()
return
}
log("FQDN hostname is", me.fqdn)
var aaaa []string
aaaa = getAAAA(me.fqdn)
log("AAAA =", aaaa)
}
func getAAAA(s string) []string {
// lookup the IP address from DNS
dnsRR := dnssecsocket.Dnstrace(s, "AAAA")
log(args.VerboseDNS, SPEW, dnsRR)
if (dnsRR == nil) {
return nil
}
ipaddr1 := dns.Field(dnsRR, 1)
ipaddr2 := dns.Field(dnsRR, 2)
log("ipaddr", ipaddr1, ipaddr2)
return []string{ipaddr1, ipaddr2}
}

103
log.go Normal file
View File

@ -0,0 +1,103 @@
// I like things to be easy. Why can't the standard language be like this?
package main
import (
"os"
golog "log"
"time"
"reflect"
"github.com/davecgh/go-spew/spew"
// "net"
)
var LOGOFF bool = false // turn this off, all logging stops
var WARN bool
var INFO bool
type spewt struct {
a bool
}
var SPEW spewt
/*
sleep() # you know what this does? sleeps for 1 second. yep. dump. easy.
sleep(.1) # you know what this does? yes, it sleeps for 1/10th of a second
*/
func sleep(a ...any) {
if (a == nil) {
time.Sleep(time.Second)
return
}
log(args.Verbose, "sleep", a[0])
switch a[0].(type) {
case int:
time.Sleep(time.Duration(a[0].(int)) * time.Second)
case float64:
time.Sleep(time.Duration(a[0].(float64) * 1000) * time.Millisecond)
default:
log("sleep a[0], type = ", a[0], reflect.TypeOf(a[0]))
}
}
/*
exit() # yep. exits. I guess everything must be fine
exit(3) # I guess 3 it is then
exit("dont like apples") # ok. I'll make a note of that
*/
func exit(a ...any) {
log("exit", a)
//if (a) {
// os.Exit(a)
//}
os.Exit(0)
}
/*
I've spent, am spending, too much time thinking about 'logging'. 'log', 'logrus', 'zap', whatever.
I'm not twitter. i don't give a fuck about how many nanoseconds it takes to log. Anyway, this
implementation is probably faster than all of those because you just set one bool to FALSE
and it all stops.
Sometimes I need to capture to stdout, sometimes stdout can't
work because it doesn't exist for the user. This whole thing is a PITA. Then it's spread
over 8 million references in every .go file. I'm tapping out and putting
it in one place. here it is. Also, this makes having debug levels really fucking easy.
You can define whatever level of logging you want from anywhere (command line) etc.
log() # doesn't do anything
log(stuff) # sends it to whatever log you define in a single place. here is the place
*/
func log(a ...any) {
if (LOGOFF) {
return
}
if (a == nil) {
return
}
var blah bool
if (reflect.TypeOf(a[0]) == reflect.TypeOf(blah)) {
// golog.Println("\t a[0] = bool")
if (a[0] == false) {
return
}
a = a[1:]
}
if (reflect.TypeOf(a[0]) == reflect.TypeOf(SPEW)) {
a = a[1:]
spew.Dump(a)
return
}
golog.Println(a...)
// golog.Println("\t a[0] =", a[0])
// for argNum, arg := range a {
// golog.Println("\t", argNum, arg)
// }
}

32
lookupAAAA.go Normal file
View File

@ -0,0 +1,32 @@
package main
/*
import "log"
import "github.com/miekg/dns"
import "git.wit.org/jcarr/dnssecsocket"
import "github.com/davecgh/go-spew/spew"
// import "github.com/Showmax/go-fqdn"
func lookupAAAA(hostname string) string {
// lookup the IP address from DNS
dnsRR := dnssecsocket.Dnstrace(hostname, "AAAA")
spew.Dump(dnsRR)
if (dnsRR == nil) {
return "BROKEN"
}
ipaddr := dns.Field(dnsRR, 1)
log.Println("ipaddr", ipaddr)
return ipaddr
}
*/
/*
func main() {
hostname := "check.lab.wit.org"
// 2604:bbc0:2:248:5054:f0ff:fe00:156
lookupAAAA(hostname)
}
*/

101
main.go
View File

@ -1,90 +1,49 @@
// This creates a simple hello world window
// This is a control panel for DNS
package main
import (
"log"
"net"
"github.com/fsnotify/fsnotify"
"runtime"
// "net"
"git.wit.org/wit/gui"
arg "github.com/alexflint/go-arg"
)
var me Host
func main() {
arg.MustParse(&args)
// fmt.Println(args.Foo, args.Bar, args.User)
log.Println("Toolkit = ", args.Toolkit)
me.ips = make(map[string]*IPtype)
// initialize the maps to track IP addresses and network interfaces
me.ipmap = make(map[string]*IPtype)
me.ifmap = make(map[int]*IFtype)
go checkNetworkChanges()
log()
log(true, "this is true")
log(false, "this is false")
sleep(.4)
sleep(.3)
sleep(.2)
sleep("done scanning net")
// exit("done scanning net")
// Example_listLink()
// exit()
log("Toolkit = ", args.Toolkit)
// gui.InitPlugins([]string{"andlabs"})
scanInterfaces()
watchNetworkInterfaces()
go inotifyNetworkInterfaceChanges()
gui.Main(initGUI)
}
func watchNetworkInterfaces() {
// Get list of network interfaces
interfaces, _ := net.Interfaces()
// Set up a notification channel
notification := make(chan net.Interface)
// Start goroutine to watch for changes
go func() {
for {
// Check for changes in each interface
for _, i := range interfaces {
if status := i.Flags & net.FlagUp; status != 0 {
notification <- i
log.Println("something on i =", i)
}
}
}
}()
}
func inotifyNetworkInterfaceChanges() error {
watcher, err := fsnotify.NewWatcher()
if err != nil {
return err
}
defer watcher.Close()
// Watch for network interface changes
err = watcher.Add("/sys/class/net")
if err != nil {
return err
}
/*
Poll for changes to the networking settings
*/
func checkNetworkChanges() {
for {
select {
case event := <-watcher.Events:
log.Println("inotifyNetworkInterfaceChanges() event =", event)
if event.Op&fsnotify.Create == fsnotify.Create {
// Do something on network interface creation
}
case err := <-watcher.Errors:
return err
}
}
}
func scanInterfaces() {
ifaces, _ := net.Interfaces()
for _, i := range ifaces {
log.Println(i)
addrs, _ := i.Addrs()
for _, addr := range addrs {
log.Println("\taddr =", addr)
switch v := addr.(type) {
case *net.IPNet:
log.Println("\t\taddr.(type) = *net.IPNet")
default:
log.Println("\t\taddr.(type) =", v)
}
sleep(2)
if (runtime.GOOS == "linux") {
scanInterfaces()
} else {
log("Windows and MacOS don't work yet")
}
}
}

223
net.go Normal file
View File

@ -0,0 +1,223 @@
// This creates a simple hello world window
package main
import (
"net"
"strings"
// "git.wit.org/wit/gui"
)
var DEBUGNET bool = false
// this doesn't work
/*
func watchNetworkInterfaces() {
// Get list of network interfaces
interfaces, _ := net.Interfaces()
// Set up a notification channel
notification := make(chan net.Interface)
log(DEBUGNET, "watchNet()")
// Start goroutine to watch for changes
go func() {
log(DEBUGNET, "watchNet() func")
for {
log(DEBUGNET, "forever loop start")
// Check for changes in each interface
for _, i := range interfaces {
log(DEBUGNET, "something on i =", i)
if status := i.Flags & net.FlagUp; status != 0 {
notification <- i
log(DEBUGNET, "something on i =", i)
}
}
log(DEBUGNET, "forever loop end")
}
}()
}
*/
func IsIPv6(address string) bool {
return strings.Count(address, ":") >= 2
}
func (t *IPtype) IsReal() bool {
if (t.ip.IsPrivate() || t.ip.IsLoopback() || t.ip.IsLinkLocalUnicast()) {
log(DEBUGNET, "\t\tIP is Real = false")
return false
} else {
log(DEBUGNET, "\t\tIP is Real = true")
return true
}
}
func IsReal(ip *net.IP) bool {
if (ip.IsPrivate() || ip.IsLoopback() || ip.IsLinkLocalUnicast()) {
log(DEBUGNET, "\t\tIP is Real = false")
return false
} else {
log(DEBUGNET, "\t\tIP is Real = true")
return true
}
}
func renameInterface(i *net.Interface) {
/*
/sbin/ip link set eth1 down
/sbin/ip link set eth1 name eth123
/sbin/ip link set eth123 up
*/
}
// Will figure out if an interface was just added
func checkInterface(i net.Interface) {
val, ok := me.ifmap[i.Index]
if ! ok {
log(i.Name, "is a new network interface. The linux kernel index =", i.Index)
me.ifmap[i.Index] = new(IFtype)
me.ifmap[i.Index].gone = false
me.ifmap[i.Index].iface = &i
me.ipchange = true
return
}
me.ifmap[i.Index].gone = false
log(args.VerboseNet, "me.ifmap[i] does exist. Need to compare everything.", i.Index, i.Name, val.iface.Index, val.iface.Name)
if (val.iface.Name != i.Name) {
log(val.iface.Name, "has changed to it's name to", i.Name)
me.ifmap[i.Index].iface = &i
me.ipchange = true
return
}
}
func realAAAA() []string {
var aaaa []string
for s, t := range me.ipmap {
if (t.IsReal()) {
if (t.ipv6) {
aaaa = append(aaaa, s)
}
}
}
return aaaa
}
func checkDNS() (map[string]*IPtype, map[string]*IPtype) {
var ipv4s map[string]*IPtype
var ipv6s map[string]*IPtype
ipv4s = make(map[string]*IPtype)
ipv6s = make(map[string]*IPtype)
for s, t := range me.ipmap {
i := t.iface
ipt := "IPv4"
if (t.ipv6) {
ipt = "IPv6"
}
if (t.IsReal()) {
log("\tIP is Real ", ipt, i.Index, i.Name, s)
if (t.ipv6) {
ipv6s[s] = t
} else {
ipv4s[s] = t
}
} else {
log("\tIP is not Real", ipt, i.Index, i.Name, s)
}
}
return ipv6s, ipv4s
}
// Will figure out if an IP address is new
func checkIP(ip *net.IPNet, i net.Interface) bool {
log(args.VerboseNet, "\t\taddr.(type) = *net.IPNet")
log(args.VerboseNet, "\t\taddr.(type) =", ip)
var realip string
realip = ip.IP.String()
val, ok := me.ipmap[realip]
if ok {
log(args.VerboseNet, val.ipnet.IP.String(), "is already a defined IP address")
me.ipmap[realip].gone = false
return false
}
me.ipmap[realip] = new(IPtype)
me.ipmap[realip].gone = false
me.ipmap[realip].ipv4 = true
me.ipmap[realip].ipnet = ip
me.ipmap[realip].ip = ip.IP
me.ipmap[realip].iface = &i
t := "IPv4"
if (IsIPv6(ip.String())) {
me.ipmap[realip].ipv6 = true
me.ipmap[realip].ipv4 = false
t = "IPv6"
} else {
me.ipmap[realip].ipv6 = false
me.ipmap[realip].ipv4 = true
}
if (IsReal(&ip.IP)) {
log("\tIP is Real ", t, i.Index, i.Name, realip)
} else {
log("\tIP is not Real", t, i.Index, i.Name, realip)
}
log(args.VerboseNet, "\t\tIP is IsPrivate() =", ip.IP.IsPrivate())
log(args.VerboseNet, "\t\tIP is IsLoopback() =", ip.IP.IsLoopback())
log(args.VerboseNet, "\t\tIP is IsLinkLocalUnicast() =", ip.IP.IsLinkLocalUnicast())
// log("HERE HERE", "realip =", realip, "me.ip[realip]=", me.ipmap[realip])
return true
}
func scanInterfaces() {
me.ipchange = false
ifaces, _ := net.Interfaces()
// me.ifnew = ifaces
log(DEBUGNET, SPEW, ifaces)
for _, i := range ifaces {
addrs, _ := i.Addrs()
// log("range ifaces = ", i)
checkInterface(i)
log(args.VerboseNet, "*net.Interface.Name = ", i.Name, i.Index)
log(args.VerboseNet, SPEW, i)
log(DEBUGNET, SPEW, addrs)
for _, addr := range addrs {
log(DEBUGNET, "\taddr =", addr)
log(DEBUGNET, SPEW, addrs)
ips, _ := net.LookupIP(addr.String())
log(DEBUGNET, "\tLookupIP(addr) =", ips)
switch v := addr.(type) {
case *net.IPNet:
checkIP(v, i)
// log("\t\tIP is () =", ip.())
default:
log(DEBUGNET, "\t\taddr.(type) = NO IDEA WHAT TO DO HERE v =", v)
}
}
}
deleteChanges()
}
// delete network interfaces and ip addresses from the gui
func deleteChanges() {
for i, t := range me.ifmap {
if (t.gone) {
log("DELETE int =", i, "name =", t.name, t.iface)
delete(me.ifmap, i)
}
t.gone = true
}
for s, t := range me.ipmap {
if (t.gone) {
log("DELETE name =", s, "IPv4 =", t.ipv4)
log("DELETE name =", s, "IPv6 =", t.ipv6)
log("DELETE name =", s, "iface =", t.iface)
log("DELETE name =", s, "ip =", t.ip)
delete(me.ipmap, s)
}
t.gone = true
}
}

66
netlink.go Normal file
View File

@ -0,0 +1,66 @@
package main
// examples of what ifconfig does
// example of AF_NETLINK change:
// https://stackoverflow.com/questions/579783/how-to-detect-ip-address-change-programmatically-in-linux/2353441#2353441
// from that page, a link to watch for any ip event:
// https://github.com/angt/ipevent/blob/master/ipevent.c
// https://github.com/mdlayher/talks : Linux, Netlink, and Go in 7 minutes or less! (GopherCon 2018, lightning talk)
/*
c example from ipevent.c :
int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
struct sockaddr_nl snl = {
.nl_family = AF_NETLINK,
.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR,
};
*/
/*
import (
// "os"
// "os/exec"
// "log"
// "net"
// "unix"
"github.com/vishvananda/netlink"
"github.com/jsimonetti/rtnetlink"
// "git.wit.org/wit/gui"
// "github.com/davecgh/go-spew/spew"
)
// In golang, write a function to register with netlink to detect changes to any network interface Use tab indentation. Do not include example usage.
func registerNetlink() error {
// Create netlink socket
sock, err := netlink.Socket(rtnetlink.NETLINK_ROUTE, 0)
if err != nil {
return err
}
// Register for interface change events
err = netlink.AddMembership(sock, netlink.RTNLGRP_LINK)
if err != nil {
return err
}
// Close the socket
defer sock.Close()
// Handle incoming notifications
for {
msgs, _, err := sock.Receive()
if err != nil {
return err
}
for _, msg := range msgs {
switch msg.Header.Type {
case unix.RTM_NEWLINK:
// Do something with new link
case unix.RTM_DELLINK:
// Do something with deleted link
}
}
}
return nil
}
*/

24
rtnetlink.go Normal file
View File

@ -0,0 +1,24 @@
package main
import (
"github.com/jsimonetti/rtnetlink"
)
// List all interfaces
func Example_listLink() {
// Dial a connection to the rtnetlink socket
conn, err := rtnetlink.Dial(nil)
if err != nil {
exit(err)
}
defer conn.Close()
// Request a list of interfaces
msg, err := conn.Link.List()
if err != nil {
log(err)
}
log("%#v", msg)
log(SPEW, msg)
}

35
structs.go Normal file
View File

@ -0,0 +1,35 @@
// This creates a simple hello world window
package main
import (
"net"
)
// It's probably a terrible idea to call this 'me'
var me Host
type Host struct {
hostname string // mirrors
domainname string // kernel.org
fqdn string // mirrors.kernel.org
ipmap map[string]*IPtype // the current ip addresses
ifmap map[int]*IFtype // the current interfaces
ipchange bool // set to true if things change
}
type IPtype struct {
gone bool // used to track if the ip exists
ipv6 bool // the future
ipv4 bool // the past
LinkLocal bool
iface *net.Interface
ip net.IP
ipnet *net.IPNet
}
type IFtype struct {
gone bool // used to track if the interface exists
name string // just a shortcut to the name. maybe this is dumb
// up bool // could be used to track ifup/ifdown
iface *net.Interface
}

24
unix.go
View File

@ -7,7 +7,6 @@ package main
import (
"os"
"os/exec"
"log"
"net"
// "git.wit.org/wit/gui"
// "github.com/davecgh/go-spew/spew"
@ -19,13 +18,13 @@ func CheckSuperuser() bool {
func Escalate() {
if os.Getuid() != 0 {
cmd := exec.Command("sudo", "./control-panel-dns")
cmd := exec.Command("sudo", "./control-panel-dns") // TODO: get the actual path
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
log.Fatal(err)
exit(err)
}
}
}
@ -39,16 +38,29 @@ func DumpPublicDNSZone(zone string) {
panic(err)
}
for _, entry := range entries {
log.Println(entry)
log(entry)
}
}
func dumpIPs(host string) {
ips, err := net.LookupIP(host)
if err != nil {
log.Fatal(err)
exit(err)
}
for _, ip := range ips {
log.Println(host, ip)
log(host, ip)
}
}
/*
check if ddclient is installed, working, and/or configured
https://github.com/ddclient/ddclient
*/
func ddclient() {
}
/*
check if ddupdate is installed, working, and/or configured
*/
func ddupdate() {
}