From 7ad38cdf6c3fdb7bc7f0c36ed2c1c63426a3b906 Mon Sep 17 00:00:00 2001 From: Jeff Carr Date: Sat, 18 Feb 2023 23:37:11 -0600 Subject: [PATCH] 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 --- .gitignore | 2 + Makefile | 47 +- README.md | 7 + RFC-2136 | 1138 +++++++++++++++++++++++++++++++++++++++++ args.go | 4 +- debian/Makefile | 3 + debian/control | 10 +- dns.go | 65 ++- dynamic-dns-update.go | 32 ++ fsnotify.go | 76 +++ gui.go | 59 ++- he-ipv6-tunnel.sh | 57 +++ hostname.go | 45 ++ log.go | 103 ++++ lookupAAAA.go | 32 ++ main.go | 101 ++-- net.go | 223 ++++++++ netlink.go | 66 +++ rtnetlink.go | 24 + structs.go | 35 ++ unix.go | 24 +- 21 files changed, 2027 insertions(+), 126 deletions(-) create mode 100644 RFC-2136 create mode 100644 dynamic-dns-update.go create mode 100644 fsnotify.go create mode 100755 he-ipv6-tunnel.sh create mode 100644 hostname.go create mode 100644 log.go create mode 100644 lookupAAAA.go create mode 100644 net.go create mode 100644 netlink.go create mode 100644 rtnetlink.go create mode 100644 structs.go diff --git a/.gitignore b/.gitignore index 1bfd4de..f58f295 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ control-panel-dns +/files/* +/*.deb diff --git a/Makefile b/Makefile index db0acb2..d8e6913 100644 --- a/Makefile +++ b/Makefile @@ -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 +# 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/ diff --git a/README.md b/README.md index fc69d97..e236dbc 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/RFC-2136 b/RFC-2136 new file mode 100644 index 0000000..edf51a7 --- /dev/null +++ b/RFC-2136 @@ -0,0 +1,1138 @@ +Network Working Group P. Vixie, Editor +Request for Comments: 2136 ISC +Updates: 1035 S. Thomson +Category: Standards Track Bellcore + Y. Rekhter + Cisco + J. Bound + DEC + April 1997 + + Dynamic Updates in the Domain Name System (DNS UPDATE) + +Status of this Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Abstract + + The Domain Name System was originally designed to support queries of + a statically configured database. While the data was expected to + change, the frequency of those changes was expected to be fairly low, + and all updates were made as external edits to a zone's Master File. + + Using this specification of the UPDATE opcode, it is possible to add + or delete RRs or RRsets from a specified zone. Prerequisites are + specified separately from update operations, and can specify a + dependency upon either the previous existence or nonexistence of an + RRset, or the existence of a single RR. + + UPDATE is atomic, i.e., all prerequisites must be satisfied or else + no update operations will take place. There are no data dependent + error conditions defined after the prerequisites have been met. + +1 - Definitions + + ## RR & RRsets == Resource Record & Resource Record Sets + ## NS == nameserver + ## SOA == Start Of Authority + ## MNAME: The name of the primary server for this zone is included in this field. + ## RNAME: The name of the party responsible for the domain. + ## + ## These lines were added to this git repo and are not part of the RFC. 2023/02/18 + + This document intentionally gives more definition to the roles of + "Master," "Slave," and "Primary Master" servers, and their + enumeration in NS RRs, and the SOA MNAME field. In that sense, the + following server type definitions can be considered an addendum to + [RFC1035], and are intended to be consistent with [RFC1996]: + + Slave an authoritative server that uses AXFR or IXFR to + retrieve the zone and is named in the zone's NS + RRset. + + Master an authoritative server configured to be the + source of AXFR or IXFR data for one or more slave + servers. + + Primary Master master server at the root of the AXFR/IXFR + dependency graph. The primary master is named in + the zone's SOA MNAME field and optionally by an NS + RR. There is by definition only one primary master + server per zone. + + A domain name identifies a node within the domain name space tree + structure. Each node has a set (possibly empty) of Resource Records + (RRs). All RRs having the same NAME, CLASS and TYPE are called a + Resource Record Set (RRset). + + The pseudocode used in this document is for example purposes only. + If it is found to disagree with the text, the text shall be + considered authoritative. If the text is found to be ambiguous, the + pseudocode can be used to help resolve the ambiguity. + + 1.1 - Comparison Rules + + 1.1.1. Two RRs are considered equal if their NAME, CLASS, TYPE, + RDLENGTH and RDATA fields are equal. Note that the time-to-live + (TTL) field is explicitly excluded from the comparison. + + 1.1.2. The rules for comparison of character strings in names are + specified in [RFC1035 2.3.3]. + + 1.1.3. Wildcarding is disabled. That is, a wildcard ("*") in an + update only matches a wildcard ("*") in the zone, and vice versa. + + 1.1.4. Aliasing is disabled: A CNAME in the zone matches a CNAME in + the update, and will not otherwise be followed. All UPDATE + operations are done on the basis of canonical names. + + 1.1.5. The following RR types cannot be appended to an RRset. If the + following comparison rules are met, then an attempt to add the new RR + will result in the replacement of the previous RR: + + SOA compare only NAME, CLASS and TYPE -- it is not possible to + have more than one SOA per zone, even if any of the data + fields differ. + + WKS compare only NAME, CLASS, TYPE, ADDRESS, and PROTOCOL + -- only one WKS RR is possible for this tuple, even if the + services masks differ. + + CNAME compare only NAME, CLASS, and TYPE -- it is not possible + to have more than one CNAME RR, even if their data fields + differ. + + 1.2 - Glue RRs + + For the purpose of determining whether a domain name used in the + UPDATE protocol is contained within a specified zone, a domain name + is "in" a zone if it is owned by that zone's domain name. See + section 7.18 for details. + + 1.3 - New Assigned Numbers + + CLASS = NONE (254) + RCODE = YXDOMAIN (6) + RCODE = YXRRSET (7) + RCODE = NXRRSET (8) + RCODE = NOTAUTH (9) + RCODE = NOTZONE (10) + Opcode = UPDATE (5) + +2 - Update Message Format + + The DNS Message Format is defined by [RFC1035 4.1]. Some extensions + are necessary (for example, more error codes are possible under + UPDATE than under QUERY) and some fields must be overloaded (see + description of CLASS fields below). + + The overall format of an UPDATE message is, following [ibid]: + + +---------------------+ + | Header | + +---------------------+ + | Zone | specifies the zone to be updated + +---------------------+ + | Prerequisite | RRs or RRsets which must (not) preexist + +---------------------+ + | Update | RRs or RRsets to be added or deleted + +---------------------+ + | Additional Data | additional data + +---------------------+ + + The Header Section specifies that this message is an UPDATE, and + describes the size of the other sections. The Zone Section names the + zone that is to be updated by this message. The Prerequisite Section + specifies the starting invariants (in terms of zone content) required + for this update. The Update Section contains the edits to be made, + and the Additional Data Section contains data which may be necessary + to complete, but is not part of, this update. + + 2.1 - Transport Issues + + An update transaction may be carried in a UDP datagram, if the + request fits, or in a TCP connection (at the discretion of the + requestor). When TCP is used, the message is in the format described + in [RFC1035 4.2.2]. + + 2.2 - Message Header + + The header of the DNS Message Format is defined by [RFC 1035 4.1]. + Not all opcodes define the same set of flag bits, though as a + practical matter most of the bits defined for QUERY (in [ibid]) are + identically defined by the other opcodes. UPDATE uses only one flag + bit (QR). + + The DNS Message Format specifies record counts for its four sections + (Question, Answer, Authority, and Additional). UPDATE uses the same + fields, and the same section formats, but the naming and use of these + sections differs as shown in the following modified header, after + [RFC1035 4.1.1]: + + 1 1 1 1 1 1 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | ID | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |QR| Opcode | Z | RCODE | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | ZOCOUNT | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | PRCOUNT | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | UPCOUNT | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | ADCOUNT | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + These fields are used as follows: + + ID A 16-bit identifier assigned by the entity that generates any + kind of request. This identifier is copied in the + corresponding reply and can be used by the requestor to match + replies to outstanding requests, or by the server to detect + duplicated requests from some requestor. + + QR A one bit field that specifies whether this message is a + request (0), or a response (1). + + Opcode A four bit field that specifies the kind of request in this + message. This value is set by the originator of a request + and copied into the response. The Opcode value that + identifies an UPDATE message is five (5). + + Z Reserved for future use. Should be zero (0) in all requests + and responses. A non-zero Z field should be ignored by + implementations of this specification. + + RCODE Response code - this four bit field is undefined in requests + and set in responses. The values and meanings of this field + within responses are as follows: + + Mneumonic Value Description + ------------------------------------------------------------ + NOERROR 0 No error condition. + FORMERR 1 The name server was unable to interpret + the request due to a format error. + SERVFAIL 2 The name server encountered an internal + failure while processing this request, + for example an operating system error + or a forwarding timeout. + NXDOMAIN 3 Some name that ought to exist, + does not exist. + NOTIMP 4 The name server does not support + the specified Opcode. + REFUSED 5 The name server refuses to perform the + specified operation for policy or + security reasons. + YXDOMAIN 6 Some name that ought not to exist, + does exist. + YXRRSET 7 Some RRset that ought not to exist, + does exist. + NXRRSET 8 Some RRset that ought to exist, + does not exist. + NOTAUTH 9 The server is not authoritative for + the zone named in the Zone Section. + NOTZONE 10 A name used in the Prerequisite or + Update Section is not within the + zone denoted by the Zone Section. + + ZOCOUNT The number of RRs in the Zone Section. + + PRCOUNT The number of RRs in the Prerequisite Section. + + UPCOUNT The number of RRs in the Update Section. + + ADCOUNT The number of RRs in the Additional Data Section. + + 2.3 - Zone Section + + The Zone Section has the same format as that specified in [RFC1035 + 4.1.2], with the fields redefined as follows: + + 1 1 1 1 1 1 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | | + / ZNAME / + / / + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | ZTYPE | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | ZCLASS | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + UPDATE uses this section to denote the zone of the records being + updated. All records to be updated must be in the same zone, and + therefore the Zone Section is allowed to contain exactly one record. + The ZNAME is the zone name, the ZTYPE must be SOA, and the ZCLASS is + the zone's class. + + 2.4 - Prerequisite Section + + This section contains a set of RRset prerequisites which must be + satisfied at the time the UPDATE packet is received by the primary + master server. The format of this section is as specified by + [RFC1035 4.1.3]. There are five possible sets of semantics that can + be expressed here, summarized as follows and then explained below. + + (1) RRset exists (value independent). At least one RR with a + specified NAME and TYPE (in the zone and class specified by + the Zone Section) must exist. + + (2) RRset exists (value dependent). A set of RRs with a + specified NAME and TYPE exists and has the same members + with the same RDATAs as the RRset specified here in this + Section. + + (3) RRset does not exist. No RRs with a specified NAME and TYPE + (in the zone and class denoted by the Zone Section) can exist. + + (4) Name is in use. At least one RR with a specified NAME (in + the zone and class specified by the Zone Section) must exist. + Note that this prerequisite is NOT satisfied by empty + nonterminals. + + (5) Name is not in use. No RR of any type is owned by a + specified NAME. Note that this prerequisite IS satisfied by + empty nonterminals. + + The syntax of these is as follows: + + 2.4.1 - RRset Exists (Value Independent) + + At least one RR with a specified NAME and TYPE (in the zone and class + specified in the Zone Section) must exist. + + For this prerequisite, a requestor adds to the section a single RR + whose NAME and TYPE are equal to that of the zone RRset whose + existence is required. RDLENGTH is zero and RDATA is therefore + empty. CLASS must be specified as ANY to differentiate this + condition from that of an actual RR whose RDLENGTH is naturally zero + (0) (e.g., NULL). TTL is specified as zero (0). + + 2.4.2 - RRset Exists (Value Dependent) + + A set of RRs with a specified NAME and TYPE exists and has the same + members with the same RDATAs as the RRset specified here in this + section. While RRset ordering is undefined and therefore not + significant to this comparison, the sets be identical in their + extent. + + For this prerequisite, a requestor adds to the section an entire + RRset whose preexistence is required. NAME and TYPE are that of the + RRset being denoted. CLASS is that of the zone. TTL must be + specified as zero (0) and is ignored when comparing RRsets for + identity. + + 2.4.3 - RRset Does Not Exist + + No RRs with a specified NAME and TYPE (in the zone and class denoted + by the Zone Section) can exist. + + For this prerequisite, a requestor adds to the section a single RR + whose NAME and TYPE are equal to that of the RRset whose nonexistence + is required. The RDLENGTH of this record is zero (0), and RDATA + field is therefore empty. CLASS must be specified as NONE in order + to distinguish this condition from a valid RR whose RDLENGTH is + naturally zero (0) (for example, the NULL RR). TTL must be specified + as zero (0). + + 2.4.4 - Name Is In Use + + Name is in use. At least one RR with a specified NAME (in the zone + and class specified by the Zone Section) must exist. Note that this + prerequisite is NOT satisfied by empty nonterminals. + + For this prerequisite, a requestor adds to the section a single RR + whose NAME is equal to that of the name whose ownership of an RR is + required. RDLENGTH is zero and RDATA is therefore empty. CLASS must + be specified as ANY to differentiate this condition from that of an + actual RR whose RDLENGTH is naturally zero (0) (e.g., NULL). TYPE + must be specified as ANY to differentiate this case from that of an + RRset existence test. TTL is specified as zero (0). + + 2.4.5 - Name Is Not In Use + + Name is not in use. No RR of any type is owned by a specified NAME. + Note that this prerequisite IS satisfied by empty nonterminals. + + For this prerequisite, a requestor adds to the section a single RR + whose NAME is equal to that of the name whose nonownership of any RRs + is required. RDLENGTH is zero and RDATA is therefore empty. CLASS + must be specified as NONE. TYPE must be specified as ANY. TTL must + be specified as zero (0). + + 2.5 - Update Section + + This section contains RRs to be added to or deleted from the zone. + The format of this section is as specified by [RFC1035 4.1.3]. There + are four possible sets of semantics, summarized below and with + details to follow. + + (1) Add RRs to an RRset. + (2) Delete an RRset. + (3) Delete all RRsets from a name. + (4) Delete an RR from an RRset. + + The syntax of these is as follows: + + 2.5.1 - Add To An RRset + + RRs are added to the Update Section whose NAME, TYPE, TTL, RDLENGTH + and RDATA are those being added, and CLASS is the same as the zone + class. Any duplicate RRs will be silently ignored by the primary + master. + + 2.5.2 - Delete An RRset + + One RR is added to the Update Section whose NAME and TYPE are those + of the RRset to be deleted. TTL must be specified as zero (0) and is + otherwise not used by the primary master. CLASS must be specified as + ANY. RDLENGTH must be zero (0) and RDATA must therefore be empty. + If no such RRset exists, then this Update RR will be silently ignored + by the primary master. + + 2.5.3 - Delete All RRsets From A Name + + One RR is added to the Update Section whose NAME is that of the name + to be cleansed of RRsets. TYPE must be specified as ANY. TTL must + be specified as zero (0) and is otherwise not used by the primary + master. CLASS must be specified as ANY. RDLENGTH must be zero (0) + and RDATA must therefore be empty. If no such RRsets exist, then + this Update RR will be silently ignored by the primary master. + + 2.5.4 - Delete An RR From An RRset + + RRs to be deleted are added to the Update Section. The NAME, TYPE, + RDLENGTH and RDATA must match the RR being deleted. TTL must be + specified as zero (0) and will otherwise be ignored by the primary + master. CLASS must be specified as NONE to distinguish this from an + RR addition. If no such RRs exist, then this Update RR will be + silently ignored by the primary master. + + 2.6 - Additional Data Section + + This section contains RRs which are related to the update itself, or + to new RRs being added by the update. For example, out of zone glue + (A RRs referred to by new NS RRs) should be presented here. The + server can use or ignore out of zone glue, at the discretion of the + server implementor. The format of this section is as specified by + [RFC1035 4.1.3]. + +3 - Server Behavior + + A server, upon receiving an UPDATE request, will signal NOTIMP to the + requestor if the UPDATE opcode is not recognized or if it is + recognized but has not been implemented. Otherwise, processing + continues as follows. + + 3.1 - Process Zone Section + + 3.1.1. The Zone Section is checked to see that there is exactly one + RR therein and that the RR's ZTYPE is SOA, else signal FORMERR to the + requestor. Next, the ZNAME and ZCLASS are checked to see if the zone + so named is one of this server's authority zones, else signal NOTAUTH + to the requestor. If the server is a zone slave, the request will be + forwarded toward the primary master. + + 3.1.2 - Pseudocode For Zone Section Processing + + if (zcount != 1 || ztype != SOA) + return (FORMERR) + if (zone_type(zname, zclass) == SLAVE) + return forward() + if (zone_type(zname, zclass) == MASTER) + return update() + return (NOTAUTH) + + Sections 3.2 through 3.8 describe the primary master's behaviour, + whereas Section 6 describes a forwarder's behaviour. + + 3.2 - Process Prerequisite Section + + Next, the Prerequisite Section is checked to see that all + prerequisites are satisfied by the current state of the zone. Using + the definitions expressed in Section 1.2, if any RR's NAME is not + within the zone specified in the Zone Section, signal NOTZONE to the + requestor. + + 3.2.1. For RRs in this section whose CLASS is ANY, test to see that + TTL and RDLENGTH are both zero (0), else signal FORMERR to the + requestor. If TYPE is ANY, test to see that there is at least one RR + in the zone whose NAME is the same as that of the Prerequisite RR, + else signal NXDOMAIN to the requestor. If TYPE is not ANY, test to + see that there is at least one RR in the zone whose NAME and TYPE are + the same as that of the Prerequisite RR, else signal NXRRSET to the + requestor. + + 3.2.2. For RRs in this section whose CLASS is NONE, test to see that + the TTL and RDLENGTH are both zero (0), else signal FORMERR to the + requestor. If the TYPE is ANY, test to see that there are no RRs in + the zone whose NAME is the same as that of the Prerequisite RR, else + signal YXDOMAIN to the requestor. If the TYPE is not ANY, test to + see that there are no RRs in the zone whose NAME and TYPE are the + same as that of the Prerequisite RR, else signal YXRRSET to the + requestor. + + 3.2.3. For RRs in this section whose CLASS is the same as the ZCLASS, + test to see that the TTL is zero (0), else signal FORMERR to the + requestor. Then, build an RRset for each unique and + compare each resulting RRset for set equality (same members, no more, + no less) with RRsets in the zone. If any Prerequisite RRset is not + entirely and exactly matched by a zone RRset, signal NXRRSET to the + requestor. If any RR in this section has a CLASS other than ZCLASS + or NONE or ANY, signal FORMERR to the requestor. + + 3.2.4 - Table Of Metavalues Used In Prerequisite Section + + CLASS TYPE RDATA Meaning + ------------------------------------------------------------ + ANY ANY empty Name is in use + ANY rrset empty RRset exists (value independent) + NONE ANY empty Name is not in use + NONE rrset empty RRset does not exist + zone rrset rr RRset exists (value dependent) + + 3.2.5 - Pseudocode for Prerequisite Section Processing + + for rr in prerequisites + if (rr.ttl != 0) + return (FORMERR) + if (zone_of(rr.name) != ZNAME) + return (NOTZONE); + if (rr.class == ANY) + if (rr.rdlength != 0) + return (FORMERR) + if (rr.type == ANY) + if (!zone_name) + return (NXDOMAIN) + else + if (!zone_rrset) + return (NXRRSET) + if (rr.class == NONE) + if (rr.rdlength != 0) + return (FORMERR) + if (rr.type == ANY) + if (zone_name) + return (YXDOMAIN) + else + if (zone_rrset) + return (YXRRSET) + if (rr.class == zclass) + temp += rr + else + return (FORMERR) + + for rrset in temp + if (zone_rrset != rrset) + return (NXRRSET) + + 3.3 - Check Requestor's Permissions + + 3.3.1. Next, the requestor's permission to update the RRs named in + the Update Section may be tested in an implementation dependent + fashion or using mechanisms specified in a subsequent Secure DNS + Update protocol. If the requestor does not have permission to + perform these updates, the server may write a warning message in its + operations log, and may either signal REFUSED to the requestor, or + ignore the permission problem and proceed with the update. + + 3.3.2. While the exact processing is implementation defined, if these + verification activities are to be performed, this is the point in the + server's processing where such performance should take place, since + if a REFUSED condition is encountered after an update has been + partially applied, it will be necessary to undo the partial update + and restore the zone to its original state before answering the + requestor. + + 3.3.3 - Pseudocode for Permission Checking + + if (security policy exists) + if (this update is not permitted) + if (local option) + log a message about permission problem + if (local option) + return (REFUSED) + + 3.4 - Process Update Section + + Next, the Update Section is processed as follows. + + 3.4.1 - Prescan + + The Update Section is parsed into RRs and each RR's CLASS is checked + to see if it is ANY, NONE, or the same as the Zone Class, else signal + a FORMERR to the requestor. Using the definitions in Section 1.2, + each RR's NAME must be in the zone specified by the Zone Section, + else signal NOTZONE to the requestor. + + 3.4.1.2. For RRs whose CLASS is not ANY, check the TYPE and if it is + ANY, AXFR, MAILA, MAILB, or any other QUERY metatype, or any + unrecognized type, then signal FORMERR to the requestor. For RRs + whose CLASS is ANY or NONE, check the TTL to see that it is zero (0), + else signal a FORMERR to the requestor. For any RR whose CLASS is + ANY, check the RDLENGTH to make sure that it is zero (0) (that is, + the RDATA field is empty), and that the TYPE is not AXFR, MAILA, + MAILB, or any other QUERY metatype besides ANY, or any unrecognized + type, else signal FORMERR to the requestor. + + 3.4.1.3 - Pseudocode For Update Section Prescan + + [rr] for rr in updates + if (zone_of(rr.name) != ZNAME) + return (NOTZONE); + if (rr.class == zclass) + if (rr.type & ANY|AXFR|MAILA|MAILB) + return (FORMERR) + elsif (rr.class == ANY) + if (rr.ttl != 0 || rr.rdlength != 0 + || rr.type & AXFR|MAILA|MAILB) + return (FORMERR) + elsif (rr.class == NONE) + if (rr.ttl != 0 || rr.type & ANY|AXFR|MAILA|MAILB) + return (FORMERR) + else + return (FORMERR) + + 3.4.2 - Update + + The Update Section is parsed into RRs and these RRs are processed in + order. + + 3.4.2.1. If any system failure (such as an out of memory condition, + or a hardware error in persistent storage) occurs during the + processing of this section, signal SERVFAIL to the requestor and undo + all updates applied to the zone during this transaction. + + 3.4.2.2. Any Update RR whose CLASS is the same as ZCLASS is added to + the zone. In case of duplicate RDATAs (which for SOA RRs is always + the case, and for WKS RRs is the case if the ADDRESS and PROTOCOL + fields both match), the Zone RR is replaced by Update RR. If the + TYPE is SOA and there is no Zone SOA RR, or the new SOA.SERIAL is + lower (according to [RFC1982]) than or equal to the current Zone SOA + RR's SOA.SERIAL, the Update RR is ignored. In the case of a CNAME + Update RR and a non-CNAME Zone RRset or vice versa, ignore the CNAME + Update RR, otherwise replace the CNAME Zone RR with the CNAME Update + RR. + + 3.4.2.3. For any Update RR whose CLASS is ANY and whose TYPE is ANY, + all Zone RRs with the same NAME are deleted, unless the NAME is the + same as ZNAME in which case only those RRs whose TYPE is other than + SOA or NS are deleted. For any Update RR whose CLASS is ANY and + whose TYPE is not ANY all Zone RRs with the same NAME and TYPE are + deleted, unless the NAME is the same as ZNAME in which case neither + SOA or NS RRs will be deleted. + + 3.4.2.4. For any Update RR whose class is NONE, any Zone RR whose + NAME, TYPE, RDATA and RDLENGTH are equal to the Update RR is deleted, + unless the NAME is the same as ZNAME and either the TYPE is SOA or + the TYPE is NS and the matching Zone RR is the only NS remaining in + the RRset, in which case this Update RR is ignored. + + 3.4.2.5. Signal NOERROR to the requestor. + + 3.4.2.6 - Table Of Metavalues Used In Update Section + + CLASS TYPE RDATA Meaning + --------------------------------------------------------- + ANY ANY empty Delete all RRsets from a name + ANY rrset empty Delete an RRset + NONE rrset rr Delete an RR from an RRset + zone rrset rr Add to an RRset + + 3.4.2.7 - Pseudocode For Update Section Processing + + [rr] for rr in updates + if (rr.class == zclass) + if (rr.type == CNAME) + if (zone_rrset) + next [rr] + elsif (zone_rrset) + next [rr] + if (rr.type == SOA) + if (!zone_rrset || + zone_rr.serial > rr.soa.serial) + next [rr] + for zrr in zone_rrset + if (rr.type == CNAME || rr.type == SOA || + (rr.type == WKS && rr.proto == zrr.proto && + rr.address == zrr.address) || + rr.rdata == zrr.rdata) + zrr = rr + next [rr] + zone_rrset += rr + elsif (rr.class == ANY) + if (rr.type == ANY) + if (rr.name == zname) + zone_rrset = Nil + else + zone_rrset = Nil + elsif (rr.name == zname && + (rr.type == SOA || rr.type == NS)) + next [rr] + else + zone_rrset = Nil + elsif (rr.class == NONE) + if (rr.type == SOA) + next [rr] + if (rr.type == NS && zone_rrset == rr) + next [rr] + zone_rr = Nil + return (NOERROR) + + 3.5 - Stability + + When a zone is modified by an UPDATE operation, the server must + commit the change to nonvolatile storage before sending a response to + the requestor or answering any queries or transfers for the modified + zone. It is reasonable for a server to store only the update records + as long as a system reboot or power failure will cause these update + records to be incorporated into the zone the next time the server is + started. It is also reasonable for the server to copy the entire + modified zone to nonvolatile storage after each update operation, + though this would have suboptimal performance for large zones. + + 3.6 - Zone Identity + + If the zone's SOA SERIAL is changed by an update operation, that + change must be in a positive direction (using modulo 2**32 arithmetic + as specified by [RFC1982]). Attempts to replace an SOA with one + whose SERIAL is less than the current one will be silently ignored by + the primary master server. + + If the zone's SOA's SERIAL is not changed as a result of an update + operation, then the server shall increment it automatically before + the SOA or any changed name or RR or RRset is included in any + response or transfer. The primary master server's implementor might + choose to autoincrement the SOA SERIAL if any of the following events + occurs: + + (1) Each update operation. + + (2) A name, RR or RRset in the zone has changed and has subsequently + been visible to a DNS client since the unincremented SOA was + visible to a DNS client, and the SOA is about to become visible + to a DNS client. + + (3) A configurable period of time has elapsed since the last update + operation. This period shall be less than or equal to one third + of the zone refresh time, and the default shall be the lesser of + that maximum and 300 seconds. + + (4) A configurable number of updates has been applied since the last + SOA change. The default value for this configuration parameter + shall be one hundred (100). + + It is imperative that the zone's contents and the SOA's SERIAL be + tightly synchronized. If the zone appears to change, the SOA must + appear to change as well. + + 3.7 - Atomicity + + During the processing of an UPDATE transaction, the server must + ensure atomicity with respect to other (concurrent) UPDATE or QUERY + transactions. No two transactions can be processed concurrently if + either depends on the final results of the other; in particular, a + QUERY should not be able to retrieve RRsets which have been partially + modified by a concurrent UPDATE, and an UPDATE should not be able to + start from prerequisites that might not still hold at the completion + of some other concurrent UPDATE. Finally, if two UPDATE transactions + would modify the same names, RRs or RRsets, then such UPDATE + transactions must be serialized. + + 3.8 - Response + + At the end of UPDATE processing, a response code will be known. A + response message is generated by copying the ID and Opcode fields + from the request, and either copying the ZOCOUNT, PRCOUNT, UPCOUNT, + and ADCOUNT fields and associated sections, or placing zeros (0) in + the these "count" fields and not including any part of the original + update. The QR bit is set to one (1), and the response is sent back + to the requestor. If the requestor used UDP, then the response will + be sent to the requestor's source UDP port. If the requestor used + TCP, then the response will be sent back on the requestor's open TCP + connection. + +4 - Requestor Behaviour + + 4.1. From a requestor's point of view, any authoritative server for + the zone can appear to be able to process update requests, even + though only the primary master server is actually able to modify the + zone's master file. Requestors are expected to know the name of the + zone they intend to update and to know or be able to determine the + name servers for that zone. + + 4.2. If update ordering is desired, the requestor will need to know + the value of the existing SOA RR. Requestors who update the SOA RR + must update the SOA SERIAL field in a positive direction (as defined + by [RFC1982]) and also preserve the other SOA fields unless the + requestor's explicit intent is to change them. The SOA SERIAL field + must never be set to zero (0). + + 4.3. If the requestor has reasonable cause to believe that all of a + zone's servers will be equally reachable, then it should arrange to + try the primary master server (as given by the SOA MNAME field if + matched by some NS NSDNAME) first to avoid unnecessary forwarding + inside the slave servers. (Note that the primary master will in some + cases not be reachable by all requestors, due to firewalls or network + partitioning.) + + 4.4. Once the zone's name servers been found and possibly sorted so + that the ones more likely to be reachable and/or support the UPDATE + opcode are listed first, the requestor composes an UPDATE message of + the following form and sends it to the first name server on its list: + + ID: (new) + Opcode: UPDATE + Zone zcount: 1 + Zone zname: (zone name) + Zone zclass: (zone class) + Zone ztype: T_SOA + Prerequisite Section: (see previous text) + Update Section: (see previous text) + Additional Data Section: (empty) + + 4.5. If the requestor receives a response, and the response has an + RCODE other than SERVFAIL or NOTIMP, then the requestor returns an + appropriate response to its caller. + + 4.6. If a response is received whose RCODE is SERVFAIL or NOTIMP, or + if no response is received within an implementation dependent timeout + period, or if an ICMP error is received indicating that the server's + port is unreachable, then the requestor will delete the unusable + server from its internal name server list and try the next one, + repeating until the name server list is empty. If the requestor runs + out of servers to try, an appropriate error will be returned to the + requestor's caller. + +5 - Duplicate Detection, Ordering and Mutual Exclusion + + 5.1. For correct operation, mechanisms may be needed to ensure + idempotence, order UPDATE requests and provide mutual exclusion. An + UPDATE message or response might be delivered zero times, one time, + or multiple times. Datagram duplication is of particular interest + since it covers the case of the so-called "replay attack" where a + correct request is duplicated maliciously by an intruder. + + 5.2. Multiple UPDATE requests or responses in transit might be + delivered in any order, due to network topology changes or load + balancing, or to multipath forwarding graphs wherein several slave + servers all forward to the primary master. In some cases, it might + be required that the earlier update not be applied after the later + update, where "earlier" and "later" are defined by an external time + base visible to some set of requestors, rather than by the order of + request receipt at the primary master. + + 5.3. A requestor can ensure transaction idempotence by explicitly + deleting some "marker RR" (rather than deleting the RRset of which it + is a part) and then adding a new "marker RR" with a different RDATA + field. The Prerequisite Section should specify that the original + "marker RR" must be present in order for this UPDATE message to be + accepted by the server. + + 5.4. If the request is duplicated by a network error, all duplicate + requests will fail since only the first will find the original + "marker RR" present and having its known previous value. The + decisions of whether to use such a "marker RR" and what RR to use are + left up to the application programmer, though one obvious choice is + the zone's SOA RR as described below. + + 5.5. Requestors can ensure update ordering by externally + synchronizing their use of successive values of the "marker RR." + Mutual exclusion can be addressed as a degenerate case, in that a + single succession of the "marker RR" is all that is needed. + + 5.6. A special case where update ordering and datagram duplication + intersect is when an RR validly changes to some new value and then + back to its previous value. Without a "marker RR" as described + above, this sequence of updates can leave the zone in an undefined + state if datagrams are duplicated. + + 5.7. To achieve an atomic multitransaction "read-modify-write" cycle, + a requestor could first retrieve the SOA RR, and build an UPDATE + message one of whose prerequisites was the old SOA RR. It would then + specify updates that would delete this SOA RR and add a new one with + an incremented SOA SERIAL, along with whatever actual prerequisites + and updates were the object of the transaction. If the transaction + succeeds, the requestor knows that the RRs being changed were not + otherwise altered by any other requestor. + +6 - Forwarding + + When a zone slave forwards an UPDATE message upward toward the zone's + primary master server, it must allocate a new ID and prepare to enter + the role of "forwarding server," which is a requestor with respect to + the forward server. + + 6.1. The set of forward servers will be same as the set of servers + this zone slave would use as the source of AXFR or IXFR data. So, + while the original requestor might have used the zone's NS RRset to + locate its update server, a forwarder always forwards toward its + designated zone master servers. + + 6.2. If the original requestor used TCP, then the TCP connection from + the requestor is still open and the forwarder must use TCP to forward + the message. If the original requestor used UDP, the forwarder may + use either UDP or TCP to forward the message, at the whim of the + implementor. + + 6.3. It is reasonable for forward servers to be forwarders + themselves, if the AXFR dependency graph being followed is a deep one + involving firewalls and multiple connectivity realms. In most cases + the AXFR dependency graph will be shallow and the forward server will + be the primary master server. + + 6.4. The forwarder will not respond to its requestor until it + receives a response from its forward server. UPDATE transactions + involving forwarders are therefore time synchronized with respect to + the original requestor and the primary master server. + + 6.5. When there are multiple possible sources of AXFR data and + therefore multiple possible forward servers, a forwarder will use the + same fallback strategy with respect to connectivity or timeout errors + that it would use when performing an AXFR. This is implementation + dependent. + + 6.6. When a forwarder receives a response from a forward server, it + copies this response into a new response message, assigns its + requestor's ID to that message, and sends the response back to the + requestor. + +7 - Design, Implementation, Operation, and Protocol Notes + + Some of the principles which guided the design of this UPDATE + specification are as follows. Note that these are not part of the + formal specification and any disagreement between this section and + any other section of this document should be resolved in favour of + the other section. + + 7.1. Using metavalues for CLASS is possible only because all RRs in + the packet are assumed to be in the same zone, and CLASS is an + attribute of a zone rather than of an RRset. (It is for this reason + that the Zone Section is not optional.) + + 7.2. Since there are no data-present or data-absent errors possible + from processing the Update Section, any necessary data-present and + data- absent dependencies should be specified in the Prerequisite + Section. + + 7.3. The Additional Data Section can be used to supply a server with + out of zone glue that will be needed in referrals. For example, if + adding a new NS RR to HOME.VIX.COM specifying a nameserver called + NS.AU.OZ, the A RR for NS.AU.OZ can be included in the Additional + Data Section. Servers can use this information or ignore it, at the + discretion of the implementor. We discourage caching this + information for use in subsequent DNS responses. + + 7.4. The Additional Data Section might be used if some of the RRs + later needed for Secure DNS Update are not actually zone updates, but + rather ancillary keys or signatures not intended to be stored in the + zone (as an update would be), yet necessary for validating the update + operation. + + 7.5. It is expected that in the absence of Secure DNS Update, a + server will only accept updates if they come from a source address + that has been statically configured in the server's description of a + primary master zone. DHCP servers would be likely candidates for + inclusion in this statically configured list. + + 7.6. It is not possible to create a zone using this protocol, since + there is no provision for a slave server to be told who its master + servers are. It is expected that this protocol will be extended in + the future to cover this case. Therefore, at this time, the addition + of SOA RRs is unsupported. For similar reasons, deletion of SOA RRs + is also unsupported. + + 7.7. The prerequisite for specifying that a name own at least one RR + differs semantically from QUERY, in that QUERY would return + rather than NXDOMAIN if queried for an RRset at + this name, while UPDATE's prerequisite condition [Section 2.4.4] + would NOT be satisfied. + + 7.8. It is possible for a UDP response to be lost in transit and for + a request to be retried due to a timeout condition. In this case an + UPDATE that was successful the first time it was received by the + primary master might ultimately appear to have failed when the + response to a duplicate request is finally received by the requestor. + (This is because the original prerequisites may no longer be + satisfied after the update has been applied.) For this reason, + requestors who require an accurate response code must use TCP. + + 7.9. Because a requestor who requires an accurate response code will + initiate their UPDATE transaction using TCP, a forwarder who receives + a request via TCP must forward it using TCP. + + 7.10. Deferral of SOA SERIAL autoincrements is made possible so that + serial numbers can be conserved and wraparound at 2**32 can be made + an infrequent occurance. Visible (to DNS clients) SOA SERIALs need + to differ if the zone differs. Note that the Authority Section SOA + in a QUERY response is a form of visibility, for the purposes of this + prerequisite. + + 7.11. A zone's SOA SERIAL should never be set to zero (0) due to + interoperability problems with some older but widely installed + implementations of DNS. When incrementing an SOA SERIAL, if the + result of the increment is zero (0) (as will be true when wrapping + around 2**32), it is necessary to increment it again or set it to one + (1). See [RFC1982] for more detail on this subject. + + 7.12. Due to the TTL minimalization necessary when caching an RRset, + it is recommended that all TTLs in an RRset be set to the same value. + While the DNS Message Format permits variant TTLs to exist in the + same RRset, and this variance can exist inside a zone, such variance + will have counterintuitive results and its use is discouraged. + + 7.13. Zone cut management presents some obscure corner cases to the + add and delete operations in the Update Section. It is possible to + delete an NS RR as long as it is not the last NS RR at the root of a + zone. If deleting all RRs from a name, SOA and NS RRs at the root of + a zone are unaffected. If deleting RRsets, it is not possible to + delete either SOA or NS RRsets at the top of a zone. An attempt to + add an SOA will be treated as a replace operation if an SOA already + exists, or as a no-op if the SOA would be new. + + 7.14. No semantic checking is required in the primary master server + when adding new RRs. Therefore a requestor can cause CNAME or NS or + any other kind of RR to be added even if their target name does not + exist or does not have the proper RRsets to make the original RR + useful. Primary master servers that DO implement this kind of + checking should take great care to avoid out-of-zone dependencies + (whose veracity cannot be authoritatively checked) and should + implement all such checking during the prescan phase. + + 7.15. Nonterminal or wildcard CNAMEs are not well specified by + [RFC1035] and their use will probably lead to unpredictable results. + Their use is discouraged. + + 7.16. Empty nonterminals (nodes with children but no RRs of their + own) will cause responses to be sent in response + to a query of any type for that name. There is no provision for + empty terminal nodes -- so if all RRs of a terminal node are deleted, + the name is no longer in use, and queries of any type for that name + will result in an NXDOMAIN response. + + 7.17. In a deep AXFR dependency graph, it has not historically been + an error for slaves to depend mutually upon each other. This + configuration has been used to enable a zone to flow from the primary + master to all slaves even though not all slaves have continuous + connectivity to the primary master. UPDATE's use of the AXFR + dependency graph for forwarding prohibits this kind of dependency + loop, since UPDATE forwarding has no loop detection analagous to the + SOA SERIAL pretest used by AXFR. + + 7.18. Previously existing names which are occluded by a new zone cut + are still considered part of the parent zone, for the purposes of + zone transfers, even though queries for such names will be referred + to the new subzone's servers. If a zone cut is removed, all parent + zone names that were occluded by it will again become visible to + queries. (This is a clarification of [RFC1034].) + + 7.19. If a server is authoritative for both a zone and its child, + then queries for names at the zone cut between them will be answered + authoritatively using only data from the child zone. (This is a + clarification of [RFC1034].) + + 7.20. Update ordering using the SOA RR is problematic since there is + no way to know which of a zone's NS RRs represents the primary + master, and the zone slaves can be out of date if their SOA.REFRESH + timers have not elapsed since the last time the zone was changed on + the primary master. We recommend that a zone needing ordered updates + use only servers which implement NOTIFY (see [RFC1996]) and IXFR (see + [RFC1995]), and that a client receiving a prerequisite error while + attempting an ordered update simply retry after a random delay period + to allow the zone to settle. + +8 - Security Considerations + + 8.1. In the absence of [RFC2137] or equivilent technology, the + protocol described by this document makes it possible for anyone who + can reach an authoritative name server to alter the contents of any + zones on that server. This is a serious increase in vulnerability + from the current technology. Therefore it is very strongly + recommended that the protocols described in this document not be used + without [RFC2137] or other equivalently strong security measures, + e.g. IPsec. + + 8.2. A denial of service attack can be launched by flooding an update + forwarder with TCP sessions containing updates that the primary + master server will ultimately refuse due to permission problems. + This arises due to the requirement that an update forwarder receiving + a request via TCP use a synchronous TCP session for its forwarding + operation. The connection management mechanisms of [RFC1035 4.2.2] + are sufficient to prevent large scale damage from such an attack, but + not to prevent some queries from going unanswered during the attack. + +Acknowledgements + + We would like to thank the IETF DNSIND working group for their input + and assistance, in particular, Rob Austein, Randy Bush, Donald + Eastlake, Masataka Ohta, Mark Andrews, and Robert Elz. Special + thanks to Bill Simpson, Ken Wallich and Bob Halley for reviewing this + document. + +References + + [RFC1035] + Mockapetris, P., "Domain Names - Implementation and + Specification", STD 13, RFC 1035, USC/Information Sciences + Institute, November 1987. + + [RFC1982] + Elz, R., "Serial Number Arithmetic", RFC 1982, University of + Melbourne, August 1996. + + [RFC1995] + Ohta, M., "Incremental Zone Transfer", RFC 1995, Tokyo Institute + of Technology, August 1996. + + [RFC1996] + Vixie, P., "A Mechanism for Prompt Notification of Zone Changes", + RFC 1996, Internet Software Consortium, August 1996. + + [RFC2065] + Eastlake, D., and C. Kaufman, "Domain Name System Protocol + Security Extensions", RFC 2065, January 1997. + + [RFC2137] + Eastlake, D., "Secure Domain Name System Dynamic Update", RFC + 2137, April 1997. + +Authors' Addresses + + Yakov Rekhter + Cisco Systems + 170 West Tasman Drive + San Jose, CA 95134-1706 + + Phone: +1 914 528 0090 + EMail: yakov@cisco.com + + Susan Thomson + Bellcore + 445 South Street + Morristown, NJ 07960 + + Phone: +1 201 829 4514 + EMail: set@thumper.bellcore.com + + Jim Bound + Digital Equipment Corp. + 110 Spitbrook Rd ZK3-3/U14 + Nashua, NH 03062-2698 + + Phone: +1 603 881 0400 + EMail: bound@zk3.dec.com + + Paul Vixie + Internet Software Consortium + Star Route Box 159A + Woodside, CA 94062 + + Phone: +1 415 747 0204 + EMail: paul@vix.com diff --git a/args.go b/args.go index aa8d224..9a9680b 100644 --- a/args.go +++ b/args.go @@ -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"` diff --git a/debian/Makefile b/debian/Makefile index d0fdeab..4f3193e 100644 --- a/debian/Makefile +++ b/debian/Makefile @@ -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 diff --git a/debian/control b/debian/control index 906f089..1a46081 100644 --- a/debian/control +++ b/debian/control @@ -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 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 diff --git a/dns.go b/dns.go index 017ffce..14b6683 100644 --- a/dns.go +++ b/dns.go @@ -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.") + } +} diff --git a/dynamic-dns-update.go b/dynamic-dns-update.go new file mode 100644 index 0000000..371a374 --- /dev/null +++ b/dynamic-dns-update.go @@ -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 +*/ diff --git a/fsnotify.go b/fsnotify.go new file mode 100644 index 0000000..814c9c7 --- /dev/null +++ b/fsnotify.go @@ -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 + } + } +} + diff --git a/gui.go b/gui.go index 732a7b3..ce7e7b7 100644 --- a/gui.go +++ b/gui.go @@ -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) } diff --git a/he-ipv6-tunnel.sh b/he-ipv6-tunnel.sh new file mode 100755 index 0000000..bc95429 --- /dev/null +++ b/he-ipv6-tunnel.sh @@ -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 diff --git a/hostname.go b/hostname.go new file mode 100644 index 0000000..4725d2c --- /dev/null +++ b/hostname.go @@ -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} +} diff --git a/log.go b/log.go new file mode 100644 index 0000000..b4d1e0b --- /dev/null +++ b/log.go @@ -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) + // } +} diff --git a/lookupAAAA.go b/lookupAAAA.go new file mode 100644 index 0000000..8ae3f02 --- /dev/null +++ b/lookupAAAA.go @@ -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) +} +*/ diff --git a/main.go b/main.go index 1d4d4df..7e172ad 100644 --- a/main.go +++ b/main.go @@ -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") } } } diff --git a/net.go b/net.go new file mode 100644 index 0000000..e24fe41 --- /dev/null +++ b/net.go @@ -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 + } +} diff --git a/netlink.go b/netlink.go new file mode 100644 index 0000000..7d3c3f5 --- /dev/null +++ b/netlink.go @@ -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 +} +*/ diff --git a/rtnetlink.go b/rtnetlink.go new file mode 100644 index 0000000..83d2c34 --- /dev/null +++ b/rtnetlink.go @@ -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) +} diff --git a/structs.go b/structs.go new file mode 100644 index 0000000..4300674 --- /dev/null +++ b/structs.go @@ -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 +} diff --git a/unix.go b/unix.go index 3cebd40..a8eb28b 100644 --- a/unix.go +++ b/unix.go @@ -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() { +}