summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--RFC-8482450
-rw-r--r--args.go65
-rw-r--r--bash.go2
-rw-r--r--cloudflare/cloudflare.go257
-rw-r--r--cloudflare/json.go25
-rw-r--r--dns.go62
-rw-r--r--fsnotify.go4
-rw-r--r--gui.go299
-rw-r--r--hostname.go71
-rw-r--r--json.go25
-rw-r--r--log.go38
-rw-r--r--lookupAAAA.go32
-rw-r--r--main.go143
-rw-r--r--net.go140
-rw-r--r--nsupdate.go7
-rw-r--r--proc.go99
-rw-r--r--protobuf/Makefile22
-rw-r--r--protobuf/dnsmessage.pb.go749
-rw-r--r--protobuf/dnsmessage.proto105
-rw-r--r--protobuf/dnstap.proto262
-rw-r--r--rtnetlink.go2
-rw-r--r--run.go37
-rw-r--r--structs.go39
-rw-r--r--unix.go35
25 files changed, 2668 insertions, 304 deletions
diff --git a/Makefile b/Makefile
index 9a05fb7..cfe7f96 100644
--- a/Makefile
+++ b/Makefile
@@ -8,7 +8,7 @@ install:
go install -v go.wit.com/control-panel-dns@latest
# go install -v git.wit.com/wit/control-panel-dns@latest
-gocui:
+gocui: build
./control-panel-dns -gui gocui >/tmp/witgui.log.stderr 2>&1
debug: build
diff --git a/RFC-8482 b/RFC-8482
new file mode 100644
index 0000000..57f0f06
--- /dev/null
+++ b/RFC-8482
@@ -0,0 +1,450 @@
+Internet Engineering Task Force (IETF) J. Abley
+Request for Comments: 8482 Afilias
+Updates: 1034, 1035 O. Gudmundsson
+Category: Standards Track M. Majkowski
+ISSN: 2070-1721 Cloudflare Inc.
+ E. Hunt
+ ISC
+ January 2019
+
+
+ Providing Minimal-Sized Responses to DNS Queries That Have QTYPE=ANY
+
+Abstract
+
+ The Domain Name System (DNS) specifies a query type (QTYPE) "ANY".
+ The operator of an authoritative DNS server might choose not to
+ respond to such queries for reasons of local policy, motivated by
+ security, performance, or other reasons.
+
+ The DNS specification does not include specific guidance for the
+ behavior of DNS servers or clients in this situation. This document
+ aims to provide such guidance.
+
+ This document updates RFCs 1034 and 1035.
+
+Status of This Memo
+
+ This is an Internet Standards Track document.
+
+ This document is a product of the Internet Engineering Task Force
+ (IETF). It represents the consensus of the IETF community. It has
+ received public review and has been approved for publication by the
+ Internet Engineering Steering Group (IESG). Further information on
+ Internet Standards is available in Section 2 of RFC 7841.
+
+ Information about the current status of this document, any errata,
+ and how to provide feedback on it may be obtained at
+ https://www.rfc-editor.org/info/rfc8482.
+
+
+Copyright Notice
+
+ Copyright (c) 2019 IETF Trust and the persons identified as the
+ document authors. All rights reserved.
+
+ This document is subject to BCP 78 and the IETF Trust's Legal
+ Provisions Relating to IETF Documents
+ (https://trustee.ietf.org/license-info) in effect on the date of
+ publication of this document. Please review these documents
+ carefully, as they describe your rights and restrictions with respect
+ to this document. Code Components extracted from this document must
+ include Simplified BSD License text as described in Section 4.e of
+ the Trust Legal Provisions and are provided without warranty as
+ described in the Simplified BSD License.
+
+Table of Contents
+
+ 1. Introduction ....................................................3
+ 1.1. Terminology ................................................3
+ 2. Motivations for Use of ANY Queries ..............................3
+ 3. General Approach ................................................4
+ 4. Behavior of DNS Responders ......................................5
+ 4.1. Answer with a Subset of Available RRsets ...................5
+ 4.2. Answer with a Synthesized HINFO RRset ......................5
+ 4.3. Answer with Best Guess as to Intention .....................6
+ 4.4. Transport Considerations ...................................6
+ 5. Behavior of DNS Initiators ......................................7
+ 6. HINFO Considerations ............................................7
+ 7. Updates to RFCs 1034 and 1035 ...................................7
+ 8. Implementation Experience .......................................8
+ 9. Security Considerations .........................................8
+ 10. IANA Considerations ............................................9
+ 11. References .....................................................9
+ 11.1. Normative References ......................................9
+ 11.2. Informative References ....................................9
+ Acknowledgements ..................................................10
+ Authors' Addresses ................................................10
+
+
+1. Introduction
+
+ The Domain Name System (DNS) specifies a query type (QTYPE) "ANY".
+ The operator of an authoritative DNS server might choose not to
+ respond to such queries for reasons of local policy, motivated by
+ security, performance, or other reasons.
+
+ The DNS specification [RFC1034] [RFC1035] does not include specific
+ guidance for the behavior of DNS servers or clients in this
+ situation. This document aims to provide such guidance.
+
+1.1. Terminology
+
+ This document uses terminology specific to the Domain Name System
+ (DNS), descriptions of which can be found in [RFC8499].
+
+ [RFC1035] defined type 255 to be "*". However, DNS implementations
+ commonly use the keyword "ANY" to refer to that type code; this
+ document follows that common usage.
+
+ In this document, "ANY query" refers to a DNS meta-query with
+ QTYPE=ANY. An "ANY response" is a response to such a query.
+
+ In this document, "conventional ANY response" means an ANY response
+ that is constructed in accordance with the algorithm documented in
+ Section 4.3.2 of [RFC1034] and specifically without implementing any
+ of the mechanisms described in this document.
+
+ In an exchange of DNS messages between two hosts, this document
+ refers to the host sending a DNS request as the "initiator" and the
+ host sending a DNS response as the "responder".
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
+ "OPTIONAL" in this document are to be interpreted as described in
+ BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all
+ capitals, as shown here.
+
+2. Motivations for Use of ANY Queries
+
+ ANY queries are legitimately used for debugging and checking the
+ state of a DNS server for a particular name.
+
+ ANY queries are sometimes used as an attempt to reduce the number of
+ queries needed to get information, e.g., to obtain MX, A, and AAAA
+ resource record sets (RRsets) for a mail domain in a single query.
+ However, there is no documented guidance available for this use case,
+ and some implementations have been observed not to function as their
+
+ developers expected. If implementers assume that an ANY query will
+ ultimately be received by an authoritative server and will fetch all
+ existing RRsets, they should include a fallback mechanism to use when
+ that does not happen.
+
+ ANY queries are frequently used to exploit the amplification
+ potential of DNS servers and resolvers using spoofed source addresses
+ and UDP transport (see [RFC5358]). Having the ability to return
+ small responses to such queries makes DNS servers less attractive
+ amplifiers.
+
+ ANY queries are sometimes used to help mine authoritative-only DNS
+ servers for zone data, since they are expected to return all RRsets
+ for a particular query name. If DNS operators prefer to reduce the
+ potential for information leaks, they might choose not to send large
+ ANY responses.
+
+ Some authoritative-only DNS server implementations require additional
+ processing in order to send a conventional ANY response; avoiding
+ that processing expense might be desirable.
+
+3. General Approach
+
+ This proposal provides a mechanism for an authoritative DNS server to
+ signal that conventional ANY queries are not supported for a
+ particular QNAME. It does so in a way that is both compatible with
+ and triggers desirable behavior by unmodified clients (e.g., DNS
+ resolvers).
+
+ Alternative proposals for dealing with ANY queries have been
+ discussed. One approach proposes using a new RCODE to signal that an
+ authoritative server did not answer ANY queries in the standard way.
+ This approach was found to have an undesirable effect on both
+ resolvers and authoritative-only servers; resolvers receiving an
+ unknown RCODE would resend the same query to all available
+ authoritative servers rather than suppress future ANY queries for the
+ same QNAME.
+
+ The proposal described in this document avoids that outcome by
+ returning a non-empty RRset in the ANY response, which provides
+ resolvers with something to cache and effectively suppresses repeat
+ queries to the same or different authoritative DNS servers.
+
+
+4. Behavior of DNS Responders
+
+ Below are the three different modes of behavior by DNS responders
+ when processing queries with QNAMEs that exist, QCLASS=IN, and
+ QTYPE=ANY. Operators and implementers are free to choose whichever
+ mechanism best suits their environment.
+
+ 1. A DNS responder can choose to select one or a larger subset of
+ the available RRsets at the QNAME.
+
+ 2. A DNS responder can return a synthesized HINFO resource record.
+ See Section 6 for discussion of the use of HINFO.
+
+ 3. A resolver can try to give out the most likely records the
+ requester wants. This is not always possible, and the result
+ might well be a large response.
+
+ Except as described below in this section, the DNS responder MUST
+ follow the standard algorithms when constructing a response.
+
+4.1. Answer with a Subset of Available RRsets
+
+ A DNS responder that receives an ANY query MAY decline to provide a
+ conventional ANY response or MAY instead send a response with a
+ single RRset (or a larger subset of available RRsets) in the answer
+ section.
+
+ The RRsets returned in the answer section of the response MAY consist
+ of a single RRset owned by the name specified in the QNAME. Where
+ multiple RRsets exist, the responder SHOULD choose a small subset of
+ those available to reduce the amplification potential of the
+ response.
+
+ If the zone is signed, appropriate RRSIG records MUST be included in
+ the answer.
+
+ Note that this mechanism does not provide any signaling to indicate
+ to a client that an incomplete subset of the available RRsets has
+ been returned.
+
+4.2. Answer with a Synthesized HINFO RRset
+
+ If there is no CNAME present at the owner name matching the QNAME,
+ the resource record returned in the response MAY instead be
+ synthesized. In this case, a single HINFO resource record SHOULD be
+ returned. The CPU field of the HINFO RDATA SHOULD be set to
+ "RFC8482". The OS field of the HINFO RDATA SHOULD be set to the null
+ string to minimize the size of the response.
+
+ The TTL encoded for the synthesized HINFO resource record SHOULD be
+ chosen by the operator of the DNS responder to be large enough to
+ suppress frequent subsequent ANY queries from the same initiator with
+ the same QNAME, understanding that a TTL that is too long might make
+ policy changes relating to ANY queries difficult to change in the
+ future. The specific value used SHOULD be configurable by the
+ operator of the nameserver according to local policy, based on the
+ familiar considerations involved in choosing a TTL value for any
+ resource record in any zone.
+
+ If the DNS query includes DO=1 and the QNAME corresponds to a zone
+ that is known by the responder to be signed, a valid RRSIG for the
+ RRsets in the answer (or authority if answer is empty) section MUST
+ be returned. In the case of DO=0, the RRSIG SHOULD be omitted.
+
+ A system that receives an HINFO response SHOULD NOT infer that the
+ response was generated according to this specification and apply any
+ special processing of the response because, in general, it is not
+ possible to tell with certainty whether the HINFO RRset received was
+ synthesized. In particular, systems SHOULD NOT rely upon the HINFO
+ RDATA described in this section to distinguish between synthesized
+ and non-synthesized HINFO RRsets.
+
+4.3. Answer with Best Guess as to Intention
+
+ In some cases, it is possible to guess what the initiator wants in
+ the answer (but not always). Some implementations have implemented
+ the spirit of this document by returning all RRsets of RRTYPE CNAME,
+ MX, A, and AAAA that are present at the owner name while suppressing
+ others. This heuristic seems to work well in practice; it satisfies
+ the needs of some applications whilst suppressing other RRsets such
+ as TXT and DNSKEY that can often contribute to large responses.
+ Whilst some applications may be satisfied by this behavior, the
+ resulting responses in the general case are larger than in the
+ approaches described in Sections 4.1 and 4.2.
+
+ As before, if the zone is signed and the DO bit is set on the
+ corresponding query, an RRSIG RRset MUST be included in the response.
+
+4.4. Transport Considerations
+
+ A DNS responder MAY behave differently when processing ANY queries
+ received over different transports, e.g., by providing a conventional
+ ANY response over TCP whilst using one of the other mechanisms
+ specified in this document in the case where a query was received
+ using UDP.
+
+ Implementers MAY provide configuration options to allow operators to
+ specify different behavior over different transports.
+
+5. Behavior of DNS Initiators
+
+ A DNS initiator that sends a query with QTYPE=ANY and receives a
+ response containing an HINFO resource record or a single RRset, as
+ described in Section 4, MAY cache the response in the normal way.
+ Such cached resource records SHOULD be retained in the cache
+ following normal caching semantics, as with any other response
+ received from a DNS responder.
+
+ A DNS initiator MAY suppress queries with QTYPE=ANY in the event that
+ the local cache contains a matching HINFO resource record with the
+ CPU field of the HINFO RDATA, as described in Section 4. A DNS
+ initiator MAY instead respond to such queries with the contents of
+ the local cache in the usual way.
+
+6. HINFO Considerations
+
+ It is possible that the synthesized HINFO RRset in an ANY response,
+ once cached by the initiator, might suppress subsequent queries from
+ the same initiator with QTYPE=HINFO. Thus, the use of HINFO in this
+ proposal would effectively mask the HINFO RRset present in the zone.
+
+ Operators of authoritative servers who serve zones that rely upon
+ conventional use of the HINFO RRTYPE SHOULD sensibly choose the
+ "single RRset" method described in this document or select another
+ type.
+
+ The HINFO RRTYPE is believed to be rarely used in the DNS at the time
+ of writing, based on observations made in passive DNS and at
+ recursive and authoritative DNS servers.
+
+7. Updates to RFCs 1034 and 1035
+
+ This document extends the specification for processing ANY queries
+ described in Section 4.3.2 of [RFC1034].
+
+ It is important to note that returning a subset of available RRsets
+ when processing an ANY query is legitimate and consistent with
+ [RFC1035]; it can be argued that ANY does not always mean ALL, as
+ used in Section 3.2.3 of [RFC1035]. The main difference here is that
+ the TC bit SHOULD NOT be set in the response, thus indicating that
+ this is not a complete answer.
+
+ This document describes optional behavior for both DNS initiators and
+ responders; implementation of the guidance provided by this document
+ is OPTIONAL.
+
+ RRSIG queries (i.e., queries with QTYPE=RRSIG) are similar to ANY
+ queries in the sense that they have the potential to generate large
+ responses as well as extra work for the responders that process them,
+ e.g., in the case where signatures are generated on the fly. RRSIG
+ RRsets are not usually obtained using such explicit queries but are
+ rather included in the responses for other RRsets that the RRSIGs
+ cover. This document does not specify appropriate behavior for RRSIG
+ queries; however, future such advice might well benefit from
+ consistency with and experience with the approaches for ANY queries
+ described here.
+
+8. Implementation Experience
+
+ In October 2015, the Cloudflare authoritative nameserver
+ implementation implemented the HINFO response. A few minor problems
+ were reported and have since been resolved.
+
+ An implementation of the subset-mode response to ANY queries was
+ implemented in NSD 4.1 in 2016.
+
+ An implementation of a single RRset response to an ANY query was made
+ for BIND9 by Tony Finch, and that functionality was subsequently made
+ available in production releases starting in BIND 9.11.
+
+9. Security Considerations
+
+ Queries with QTYPE=ANY are frequently observed as part of reflection
+ attacks, since a relatively small query can be used to elicit a large
+ response. This is a desirable characteristic if the goal is to
+ maximize the amplification potential of a DNS server as part of a
+ volumetric attack. The ability of a DNS operator to suppress such
+ responses on a particular server makes that server a less useful
+ amplifier.
+
+ The optional behavior described in this document to reduce the size
+ of responses to queries with QTYPE=ANY is compatible with the use of
+ DNSSEC by both initiator and responder.
+
+
+10. IANA Considerations
+
+ IANA has updated the following entry in the "Resource Record (RR)
+ TYPEs" registry [RR_TYPES]:
+
+ +------+-------+-------------------------------+--------------------+
+ | TYPE | Value | Meaning | Reference |
+ +------+-------+-------------------------------+--------------------+
+ | * | 255 | A request for some or all | [RFC1035][RFC6895] |
+ | | | records the server has | [RFC8482] |
+ | | | available | |
+ +------+-------+-------------------------------+--------------------+
+
+11. References
+
+11.1. Normative References
+
+ [RFC1034] Mockapetris, P., "Domain names - concepts and facilities",
+ STD 13, RFC 1034, DOI 10.17487/RFC1034, November 1987,
+ <https://www.rfc-editor.org/info/rfc1034>.
+
+ [RFC1035] Mockapetris, P., "Domain names - implementation and
+ specification", STD 13, RFC 1035, DOI 10.17487/RFC1035,
+ November 1987, <https://www.rfc-editor.org/info/rfc1035>.
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119,
+ DOI 10.17487/RFC2119, March 1997,
+ <https://www.rfc-editor.org/info/rfc2119>.
+
+ [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
+ 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
+ May 2017, <https://www.rfc-editor.org/info/rfc8174>.
+
+11.2. Informative References
+
+ [RFC5358] Damas, J. and F. Neves, "Preventing Use of Recursive
+ Nameservers in Reflector Attacks", BCP 140, RFC 5358,
+ DOI 10.17487/RFC5358, October 2008,
+ <https://www.rfc-editor.org/info/rfc5358>.
+
+ [RFC6895] Eastlake 3rd, D., "Domain Name System (DNS) IANA
+ Considerations", BCP 42, RFC 6895, DOI 10.17487/RFC6895,
+ April 2013, <https://www.rfc-editor.org/info/rfc6895>.
+
+ [RFC8499] Hoffman, P., Sullivan, A., and K. Fujiwara, "DNS
+ Terminology", BCP 219, RFC 8499, DOI 10.17487/RFC8499,
+ January 2019, <https://www.rfc-editor.org/info/rfc8499>.
+
+ [RR_TYPES] IANA, "Domain Name System (DNS) Parameters",
+ <https://www.iana.org/assignments/dns-parameters>.
+
+Acknowledgements
+
+ David Lawrence provided valuable observations and concrete
+ suggestions. Jeremy Laidman helped make the document better. Tony
+ Finch realized that this document was valuable and implemented it
+ while under attack. Richard Gibson identified areas where more
+ detail and accuracy were useful. A large number of other people also
+ provided comments and suggestions; we thank them all for the
+ feedback.
+
+Authors' Addresses
+
+ Joe Abley
+ Afilias
+ 300-184 York Street
+ London, ON N6A 1B5
+ Canada
+
+ Phone: +1 519 670 9327
+
+
+ Olafur Gudmundsson
+ Cloudflare Inc.
+
+
+
+ Marek Majkowski
+ Cloudflare Inc.
+
+
+
+ Evan Hunt
+ ISC
+ 950 Charter St
+ Redwood City, CA 94063
+ United States of America
+
diff --git a/args.go b/args.go
index 8df208b..04abc96 100644
--- a/args.go
+++ b/args.go
@@ -7,13 +7,14 @@ package main
import (
"log"
"fmt"
- "reflect"
- "strconv"
+ "time"
arg "github.com/alexflint/go-arg"
"git.wit.org/wit/gui"
// log "git.wit.org/wit/gui/log"
+ "git.wit.org/jcarr/control-panel-dns/cloudflare"
)
+var newRR *cloudflare.RRT
var args struct {
Verbose bool
@@ -40,57 +41,17 @@ func init() {
}
log.Println(true, "INIT() args.GuiArg.Gui =", gui.GuiArg.Gui)
- Set(&me, "default")
- log.Println("init() me.artificialSleep =", me.artificialSleep)
- log.Println("init() me.artificialS =", me.artificialS)
- me.artificialSleep = 2.3
- log.Println("init() me.artificialSleep =", me.artificialSleep)
- sleep(me.artificialSleep)
-}
+ newRR = &cloudflare.CFdialog
-func Set(ptr interface{}, tag string) error {
- if reflect.TypeOf(ptr).Kind() != reflect.Ptr {
- log.Println(logError, "Set() Not a pointer", ptr, "with tag =", tag)
- return fmt.Errorf("Not a pointer")
- }
-
- v := reflect.ValueOf(ptr).Elem()
- t := v.Type()
+ me.dnsTTL = 2 // how often to recheck DNS
+ me.dnsTTLsleep = 0.4 // sleep between loops
- for i := 0; i < t.NumField(); i++ {
- defaultVal := t.Field(i).Tag.Get(tag)
- name := t.Field(i).Name
- // log("Set() try name =", name, "defaultVal =", defaultVal)
- setField(v.Field(i), defaultVal, name)
- }
- return nil
-}
+ me.dnsSleep = 500 * time.Millisecond
+ me.localSleep = 100 * time.Millisecond
-func setField(field reflect.Value, defaultVal string, name string) error {
-
- if !field.CanSet() {
- // log("setField() Can't set value", field, defaultVal)
- return fmt.Errorf("Can't set value\n")
- } else {
- log.Println("setField() Can set value", name, defaultVal)
- }
-
- switch field.Kind() {
- case reflect.Int:
- val, _ := strconv.Atoi(defaultVal)
- field.Set(reflect.ValueOf(int(val)).Convert(field.Type()))
- case reflect.Float64:
- val, _ := strconv.ParseFloat(defaultVal, 64)
- field.Set(reflect.ValueOf(float64(val)).Convert(field.Type()))
- case reflect.String:
- field.Set(reflect.ValueOf(defaultVal).Convert(field.Type()))
- case reflect.Bool:
- if defaultVal == "true" {
- field.Set(reflect.ValueOf(true))
- } else {
- field.Set(reflect.ValueOf(false))
- }
- }
-
- return nil
+ me.artificialSleep = me.dnsTTLsleep // seems to need to exist or GTK crashes
+ me.artificialS = "blah"
+ log.Println("init() me.artificialSleep =", me.artificialSleep)
+ log.Println("init() me.artificialS =", me.artificialS)
+ sleep(me.artificialSleep)
}
diff --git a/bash.go b/bash.go
index 5e63e5f..5c49a39 100644
--- a/bash.go
+++ b/bash.go
@@ -54,7 +54,7 @@ func test() error {
func mainBash() {
if err := test(); err != nil {
- log.Println(logError, "exit in mainBash()")
+ debug(LogError, "exit in mainBash()")
exit(err)
}
}
diff --git a/cloudflare/cloudflare.go b/cloudflare/cloudflare.go
new file mode 100644
index 0000000..de08fae
--- /dev/null
+++ b/cloudflare/cloudflare.go
@@ -0,0 +1,257 @@
+// This is a simple example
+package cloudflare
+
+import (
+ "log"
+ "os"
+ "bytes"
+ "io/ioutil"
+ "net/http"
+
+ "git.wit.org/wit/gui"
+)
+
+/*
+curl --request POST \
+ --url https://api.cloudflare.com/client/v4/zones/zone_identifier/dns_records \
+ --header 'Content-Type: application/json' \
+ --header 'X-Auth-Email: ' \
+ --data '{
+ "content": "198.51.100.4",
+ "name": "example.com",
+ "proxied": false,
+ "type": "A",
+ "comment": "Domain verification record",
+ "tags": [
+ "owner:dns-team"
+ ],
+ "ttl": 3600
+}'
+*/
+
+// CFdialog is everything you need forcreating
+// a new record: name, TTL, type (CNAME, A, etc)
+var CFdialog RRT
+
+// Resource Record (used in a DNS zonefile)
+type RRT struct {
+ cloudflareW *gui.Node // the window node
+ cloudflareB *gui.Node // the cloudflare button
+
+ TypeNode *gui.Node // CNAME, A, AAAA, ...
+ NameNode *gui.Node // www, mail, ...
+ ValueNode *gui.Node // 4.2.2.2, "dkim stuff", etc
+
+ proxyNode *gui.Node // If cloudflare is a port 80 & 443 proxy
+ ttlNode *gui.Node // just set to 1 which means automatic to cloudflare
+ curlNode *gui.Node // shows you what you could run via curl
+ resultNode *gui.Node // what the cloudflare API returned
+ saveNode *gui.Node // button to send it to cloudflare
+
+ zoneNode *gui.Node // "wit.com"
+ zoneIdNode *gui.Node // cloudflare zone ID
+ apiNode *gui.Node // cloudflare API key (from environment var CF_API_KEY)
+ emailNode *gui.Node // cloudflare email (from environment var CF_API_EMAIL)
+
+ ID string
+ Type string
+ Name string
+ Content string
+ ProxyS string
+ Proxied bool
+ Proxiable bool
+ Ttl string
+}
+
+func CreateRR(myGui *gui.Node, zone string, zoneID string) {
+ if (CFdialog.cloudflareW != nil) {
+ // skip this if the window has already been created
+ log.Println("createRR() the cloudflare window already exists")
+ CFdialog.cloudflareB.Disable()
+ return
+ }
+ CFdialog.cloudflareW = myGui.NewWindow("cloudflare " + zone + " API")
+ CFdialog.cloudflareW.Custom = func () {
+ log.Println("createRR() don't really exit here")
+ CFdialog.cloudflareW = nil
+ CFdialog.cloudflareB.Enable()
+ }
+
+ CFdialog.ID = zoneID
+
+ group := CFdialog.cloudflareW.NewGroup("Create a new DNS Resource Record (rr)")
+
+ // make a grid 2 things wide
+ grid := group.NewGrid("gridnuts", 2, 3)
+
+ grid.NewLabel("zone")
+ CFdialog.zoneNode = grid.NewLabel("zone")
+ CFdialog.zoneNode.SetText(zone)
+
+ grid.NewLabel("zone ID")
+ CFdialog.zoneIdNode = grid.NewLabel("zoneID")
+ CFdialog.zoneIdNode.SetText(zoneID)
+
+ grid.NewLabel("shell env $CF_API_EMAIL")
+ CFdialog.emailNode = grid.NewLabel("type")
+ CFdialog.emailNode.SetText(os.Getenv("CF_API_EMAIL"))
+
+ grid.NewLabel("shell env $CF_API_KEY")
+ CFdialog.apiNode = grid.NewLabel("type")
+ CFdialog.apiNode.SetText(os.Getenv("CF_API_KEY"))
+
+ grid.NewLabel("Record Type")
+ CFdialog.TypeNode = grid.NewCombobox("type")
+ CFdialog.TypeNode.AddText("A")
+ CFdialog.TypeNode.AddText("AAAA")
+ CFdialog.TypeNode.AddText("CNAME")
+ CFdialog.TypeNode.AddText("TXT")
+ CFdialog.TypeNode.AddText("MX")
+ CFdialog.TypeNode.AddText("NS")
+ CFdialog.TypeNode.Custom = func () {
+ CreateCurlRR()
+ }
+ CFdialog.TypeNode.SetText("AAAA")
+
+ grid.NewLabel("Name (usually the hostname)")
+ CFdialog.NameNode = grid.NewCombobox("name")
+ CFdialog.NameNode.AddText("www")
+ CFdialog.NameNode.AddText("mail")
+ CFdialog.NameNode.AddText("git")
+ CFdialog.NameNode.AddText("go")
+ CFdialog.NameNode.AddText("blog")
+ CFdialog.NameNode.AddText("ns1")
+ CFdialog.NameNode.Custom = func () {
+ CreateCurlRR()
+ }
+ CFdialog.NameNode.SetText("www")
+
+ grid.NewLabel("Cloudflare Proxy")
+ CFdialog.proxyNode = grid.NewDropdown("proxy")
+ CFdialog.proxyNode.AddText("On")
+ CFdialog.proxyNode.AddText("Off")
+ CFdialog.proxyNode.Custom = func () {
+ CreateCurlRR()
+ }
+ CFdialog.proxyNode.SetText("Off")
+
+ grid.NewLabel("Value")
+ CFdialog.ValueNode = grid.NewCombobox("value")
+ CFdialog.ValueNode.AddText("127.0.0.1")
+ CFdialog.ValueNode.AddText("2001:4860:4860::8888")
+ CFdialog.ValueNode.AddText("ipv6.wit.com")
+ CFdialog.ValueNode.Custom = func () {
+ CreateCurlRR()
+ }
+ CFdialog.ValueNode.SetText("127.0.0.1")
+ CFdialog.ValueNode.Expand()
+
+ group.NewLabel("curl")
+ CFdialog.curlNode = group.NewTextbox("curl")
+ CFdialog.curlNode.Custom = func () {
+ CreateCurlRR()
+ }
+ CFdialog.curlNode.SetText("put the curl text here")
+
+ CFdialog.resultNode = group.NewTextbox("result")
+ CFdialog.resultNode.SetText("API response will show here")
+
+ CFdialog.saveNode = group.NewButton("Save", func () {
+ url, data := CreateCurlRR()
+ result := curl(url, data)
+ CFdialog.resultNode.SetText(result)
+ })
+ CFdialog.saveNode.Disable()
+
+ group.Pad()
+ grid.Pad()
+ grid.Expand()
+}
+
+func CreateCurlRR() (string, string) {
+ // enable the Save/Create Button
+ if (CFdialog.saveNode != nil) {
+ CFdialog.saveNode.Enable()
+ }
+
+ if (CFdialog.TypeNode != nil) {
+ CFdialog.Type = CFdialog.TypeNode.S
+ }
+ if (CFdialog.NameNode != nil) {
+ CFdialog.Name = CFdialog.NameNode.S
+ }
+ if (CFdialog.proxyNode != nil) {
+ if (CFdialog.proxyNode.S == "On") {
+ CFdialog.ProxyS = "true"
+ } else {
+ CFdialog.ProxyS = "false"
+ }
+ }
+ if (CFdialog.ValueNode != nil) {
+ CFdialog.Content = CFdialog.ValueNode.S
+ }
+ CFdialog.Ttl = "3600"
+
+ var url string = "https://api.cloudflare.com/client/v4/zones/" + CFdialog.ID + "/dns_records"
+ // https://api.cloudflare.com/client/v4/zones/zone_identifier/dns_records \
+ // var authKey string = os.Getenv("CF_API_KEY")
+ // var email string = os.Getenv("CF_API_EMAIL")
+
+ // make a json record to send on port 80 to cloudflare
+ var tmp string
+ tmp = `{"content": "` + CFdialog.Content + `", `
+ tmp += `"name": "` + CFdialog.Name + `", `
+ tmp += `"type": "` + CFdialog.Type + `", `
+ tmp += `"ttl": ` + CFdialog.Ttl + `, `
+ tmp += `"proxied": ` + CFdialog.ProxyS + `, `
+ tmp += `"comment": "WIT DNS Control Panel"`
+ tmp += `}`
+ data := []byte(tmp)
+
+ log.Println("http PUT url =", url)
+ // log.Println("http PUT data =", data)
+ // spew.Dump(data)
+ pretty, _ := formatJSON(string(data))
+ log.Println("http URL =", url)
+ log.Println("http PUT data =", pretty)
+ if (CFdialog.curlNode != nil) {
+ CFdialog.curlNode.SetText("URL: " + url + "\n" + pretty)
+ }
+
+ return url, tmp
+}
+
+func curl(url string, tmp string) string {
+ var authKey string = CFdialog.apiNode.S
+ var email string = CFdialog.emailNode.S
+
+ log.Println("curl() START")
+ data := []byte(tmp)
+ req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(data))
+
+ // Set headers
+ req.Header.Set("Content-Type", "application/json")
+ req.Header.Set("X-Auth-Key", authKey)
+ req.Header.Set("X-Auth-Email", email)
+
+ client := &http.Client{}
+ resp, err := client.Do(req)
+ if err != nil {
+ log.Println(err)
+ return ""
+ }
+ defer resp.Body.Close()
+
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ log.Println(err)
+ return ""
+ }
+ // log.Println("http PUT body =", body)
+ // spew.Dump(body)
+
+ log.Println("result =", string(body))
+ log.Println("curl() END")
+ pretty, _ := formatJSON(string(body))
+ return pretty
+}
diff --git a/cloudflare/json.go b/cloudflare/json.go
new file mode 100644
index 0000000..f91b724
--- /dev/null
+++ b/cloudflare/json.go
@@ -0,0 +1,25 @@
+// This is a simple example
+package cloudflare
+
+import (
+ "encoding/json"
+)
+
+// formatJSON takes an unformatted JSON string and returns a formatted version.
+func formatJSON(unformattedJSON string) (string, error) {
+ var jsonData interface{}
+
+ // Decode the JSON string into an interface
+ err := json.Unmarshal([]byte(unformattedJSON), &jsonData)
+ if err != nil {
+ return "", err
+ }
+
+ // Re-encode the JSON with indentation for formatting
+ formattedJSON, err := json.MarshalIndent(jsonData, "", " ")
+ if err != nil {
+ return "", err
+ }
+
+ return string(formattedJSON), nil
+}
diff --git a/dns.go b/dns.go
index 2bb4232..8afbf26 100644
--- a/dns.go
+++ b/dns.go
@@ -7,6 +7,9 @@ package main
import (
"log"
"net"
+ "strings"
+
+ "git.wit.org/wit/shell"
)
/*
@@ -24,7 +27,7 @@ func (h *Host) verifyETC() bool {
func (h *Host) updateIPs(host string) {
ips, err := net.LookupIP(host)
if err != nil {
- log.Println(logError, "updateIPs failed", err)
+ debug(LogError, "updateIPs failed", err)
}
for _, ip := range ips {
log.Println(host, ip)
@@ -71,3 +74,60 @@ func (h *Host) checkDNS() {
log.Println(args.VerboseDNS, "IPv6 is broken. Need to fix it here.")
}
}
+
+// nsLookup performs an NS lookup on the given domain name.
+func lookupNS(domain string) {
+ var domains string
+
+ nsRecords, err := net.LookupNS(domain)
+ if err != nil {
+ return
+ }
+
+ var servers []string
+ for _, ns := range nsRecords {
+ servers = append(servers, ns.Host)
+ }
+
+ // checks to see if the NS records change
+ for _, server := range servers {
+ server = strings.TrimRight(server, ".")
+ if (me.nsmap[server] != domain) {
+ debug(LogChange, "lookupNS() domain", domain, "has NS", server)
+ me.nsmap[server] = domain
+ domains += server + "\n"
+ }
+ }
+
+ var tmp string
+ // checks to see if the NS records change
+ for s, d := range me.nsmap {
+ debug(LogChange, "lookupNS() domain =", d, "server =", s)
+ if (domain == d) {
+ tmp += s + "\n"
+ // figure out the provider (google, cloudflare, etc)
+ setProvider(s)
+ }
+ }
+ tmp = shell.Chomp(tmp)
+
+ if (tmp != me.NSrr.S) {
+ me.changed = true
+ debug(LogChange, "lookupNS() setting me.NSrr =", tmp)
+ me.NSrr.SetText(tmp)
+ }
+}
+
+// getDomain returns the second-to-last part of a domain name.
+func setProvider(hostname string) {
+ var provider string = ""
+ parts := strings.Split(hostname, ".")
+ if len(parts) >= 2 {
+ provider = parts[len(parts)-2]
+ }
+ if (me.DnsAPI.S != provider) {
+ me.changed = true
+ debug(LogChange, "setProvider() changed to =", provider)
+ me.DnsAPI.SetText(provider)
+ }
+}
diff --git a/fsnotify.go b/fsnotify.go
index 8e00527..23809e6 100644
--- a/fsnotify.go
+++ b/fsnotify.go
@@ -14,7 +14,7 @@ func watchSysClassNet() {
// Create new watcher.
watcher, err := fsnotify.NewWatcher()
if err != nil {
- log.Println(logError, "watchSysClassNet() failed:", err)
+ debug(LogError, "watchSysClassNet() failed:", err)
return
}
defer watcher.Close()
@@ -43,7 +43,7 @@ func watchSysClassNet() {
// Add a path.
err = watcher.Add("/tmp")
if err != nil {
- log.Println(logError, "watchSysClassNet() watcher.Add() failed:", err)
+ debug(LogError, "watchSysClassNet() watcher.Add() failed:", err)
return
}
diff --git a/gui.go b/gui.go
index ab07adf..3eb8582 100644
--- a/gui.go
+++ b/gui.go
@@ -7,23 +7,67 @@ import (
"os"
"os/user"
"strconv"
- "strings"
"net"
+ "strings"
+
"git.wit.org/wit/gui"
"git.wit.org/wit/shell"
+
"github.com/davecgh/go-spew/spew"
)
// This setups up the dns control panel window
func setupControlPanelWindow() {
- // me.window = myGui.New2().Window("DNS and IPv6 Control Panel").Standard()
- me.window = myGui.NewWindow("DNS and IPv6 Control Panel").Standard()
- me.window.Dump()
+ me.window = myGui.NewWindow("DNS and IPv6 Control Panel")
+ // me.window.Dump() // will dump out some info
+ debug("artificial sleep of:", me.artificialSleep)
sleep(me.artificialSleep)
dnsTab("DNS")
+ detailsTab("Details")
debugTab("Debug")
+}
+
+func detailsTab(title string) {
+ var g2 *gui.Node
+
+ tab := me.window.NewTab(title)
+
+ g2 = tab.NewGroup("Real Stuff")
+
+ grid := g2.NewGrid("gridnuts", 2, 2)
+
+ grid.SetNext(1,1)
+
+ grid.NewLabel("domainname =")
+ me.domainname = grid.NewLabel("domainname")
+ grid.NewLabel("hostname -s =")
+ me.hostshort = grid.NewLabel("hostname -s")
+
+ grid.NewLabel("NS records =")
+ me.NSrr = grid.NewLabel("NS RR's")
+
+ grid.NewLabel("UID =")
+ me.uid = grid.NewLabel("my uid")
+
+ grid.NewLabel("Current IPv4 =")
+ me.IPv4 = grid.NewLabel("?")
+
+ grid.NewLabel("Current IPv6 =")
+ me.IPv6 = grid.NewLabel("?")
+
+ grid.NewLabel("interfaces =")
+ me.Interfaces = grid.NewCombobox("Interfaces")
+
+ grid.NewLabel("refresh speed")
+ me.LocalSpeedActual = grid.NewLabel("unknown")
+
+ tab.Margin()
+ tab.Pad()
+
+ grid.Margin()
+ grid.Pad()
}
func debugTab(title string) {
@@ -50,25 +94,30 @@ func debugTab(title string) {
log.Println("iface = " + t.iface.Name)
}
})
+
g2.NewButton("Hostname", func () {
getHostname()
})
- g2.NewButton("Actual AAAA", func () {
- var aaaa []string
- aaaa = realAAAA()
- for _, s := range aaaa {
- log.Println("my actual AAAA = ", s)
- }
+
+ g2.NewButton("Actual AAAA & A", func () {
+ displayDNS() // doesn't re-query anything
})
- g2.NewButton("Update DNS", func () {
+ g2.NewButton("dig A & AAAA DNS records", func () {
log.Println("updateDNS()")
updateDNS()
})
- g2.NewButton("checkDNS()", func () {
- checkDNS()
+ g2.NewButton("checkDNS:", func () {
+ ipv6s, ipv4s := checkDNS()
+ for s, _ := range ipv6s {
+ debug(LogNow, "check if", s, "is in DNS")
+ }
+ for s, _ := range ipv4s {
+ debug(LogNow, "check if", s, "is in DNS")
+ }
})
+
g2.NewButton("os.User()", func () {
user, _ := user.Current()
spew.Dump(user)
@@ -77,17 +126,21 @@ func debugTab(title string) {
me.uid.SetText(user.Username + " (" + strconv.Itoa(os.Getuid()) + ")")
}
})
+
g2.NewButton("dig +trace", func () {
o := shell.Run("dig +trace +noadditional DS " + me.hostname + " @8.8.8.8")
log.Println(o)
// log.Println(o)
})
+
g2.NewButton("Example_listLink()", func () {
Example_listLink()
})
+
g2.NewButton("Escalate()", func () {
Escalate()
})
+
g2.NewButton("LookupAddr(<raw ipv6>) == fire from /etc/hosts", func () {
host, err := net.LookupAddr("2600:1700:afd5:6000:b26e:bfff:fe80:3c52")
if err != nil {
@@ -95,14 +148,34 @@ func debugTab(title string) {
}
log.Println("host =", host)
})
+
g2.NewButton("DumpPublicDNSZone(apple.com)", func () {
DumpPublicDNSZone("apple.com")
dumpIPs("www.apple.com")
})
+ g2 = tab.NewGroup("debugging options")
+
+ // DEBUG flags
+ me.dbOn = g2.NewCheckbox("turn on debugging (will override all flags below)")
+ me.dbOn.Custom = func() {
+ DEBUGON = me.dbOn.B
+ }
+
+ me.dbNet = g2.NewCheckbox("turn on network debugging)")
+ me.dbNet.Custom = func() {
+ LogNet = me.dbNet.B
+ }
+
+ me.dbProc = g2.NewCheckbox("turn on /proc debugging)")
+ me.dbProc.Custom = func() {
+ LogProc = me.dbProc.B
+ }
+
+ // various timeout settings
g2.NewLabel("control panel TTL (in tenths of seconds)")
ttl := g2.NewSlider("dnsTTL", 1, 100)
- ttl.Set(me.dnsTTL * 10)
+ ttl.Set(int(me.dnsTTL * 10))
ttl.Custom = func () {
me.dnsTTL = ttl.I / 10
log.Println("dnsTTL =", me.dnsTTL)
@@ -110,11 +183,54 @@ func debugTab(title string) {
g2.NewLabel("control panel loop delay (in tenths of seconds)")
ttl2 := g2.NewSlider("dnsTTL", 1, 100)
- ttl2.Set(me.dnsTTLsleep)
+ ttl2.Set(int(me.dnsTTLsleep * 10))
ttl2.Custom = func () {
me.dnsTTLsleep = float64(ttl2.I) / 10
log.Println("dnsTTLsleep =", me.dnsTTLsleep)
}
+
+ g2.Margin()
+ g2.Pad()
+}
+
+// doesn't actually do any network traffic
+// it just updates the GUI
+func displayDNS() int {
+ var aaaa []string
+ aaaa = realAAAA() // your AAAA records right now
+ h := me.hostname
+ var all string
+ var broken int = 0
+ for _, s := range aaaa {
+ debug(LogNow, "host", h, "DNS AAAA =", s, "ipmap[s] =", me.ipmap[s])
+ all += s + "\n"
+ if ( me.ipmap[s] == nil) {
+ debug(LogError, "THIS IS THE WRONG AAAA DNS ENTRY: host", h, "DNS AAAA =", s)
+ broken = 2
+ } else {
+ if (broken == 0) {
+ broken = 1
+ }
+ }
+ }
+ all = sortLines(all)
+ if (me.DnsAAAA.S != all) {
+ debug(LogError, "DnsAAAA.SetText() to:", all)
+ me.DnsAAAA.SetText(all)
+ }
+
+ var a []string
+ a = realA()
+ all = sortLines(strings.Join(a, "\n"))
+ if (all == "") {
+ debug(LogInfo, "THERE IS NOT a real A DNS ENTRY")
+ all = "CNAME ipv6.wit.com"
+ }
+ if (me.DnsA.S != all) {
+ debug(LogError, "DnsA.SetText() to:", all)
+ me.DnsA.SetText(all)
+ }
+ return broken
}
func myDefaultExit(n *gui.Node) {
@@ -125,47 +241,56 @@ func myDefaultExit(n *gui.Node) {
func dnsTab(title string) {
tab := me.window.NewTab(title)
- g := tab.NewGroup("dns update")
+ me.mainStatus = tab.NewGroup("dns update")
- grid := g.NewGrid("gridnuts", 2, 2)
+ grid := me.mainStatus.NewGrid("gridnuts", 2, 2)
grid.SetNext(1,1)
+
grid.NewLabel("hostname =")
me.fqdn = grid.NewLabel("?")
me.hostname = ""
- grid.NewLabel("UID =")
- me.uid = grid.NewLabel("?")
-
grid.NewLabel("DNS AAAA =")
me.DnsAAAA = grid.NewLabel("?")
grid.NewLabel("DNS A =")
me.DnsA = grid.NewLabel("?")
- grid.NewLabel("IPv4 =")
- me.IPv4 = grid.NewLabel("?")
-
- grid.NewLabel("IPv6 =")
- me.IPv6 = grid.NewLabel("?")
-
- grid.NewLabel("interfaces =")
- me.Interfaces = grid.NewCombobox("Interfaces")
-
- grid.NewLabel("DNS Status =")
- me.DnsStatus = grid.NewLabel("unknown")
-
- me.fix = g.NewButton("Fix", func () {
+ me.fix = me.mainStatus.NewButton("Fix", func () {
if (goodHostname(me.hostname)) {
- log.Println("hostname is good:", me.hostname)
+ debug(LogInfo, "hostname is good:", me.hostname)
} else {
- log.Println("you need to fix your hostname here", me.hostname)
+ debug(LogError, "FIX: you need to fix your hostname here", me.hostname)
+ return
+ }
+ // check to see if the cloudflare window exists
+ /*
+ if (me.cloudflareW != nil) {
+ newRR.NameNode.SetText(me.hostname)
+ newRR.TypeNode.SetText("AAAA")
+ for s, t := range me.ipmap {
+ if (t.IsReal()) {
+ if (t.ipv6) {
+ newRR.ValueNode.SetText(s)
+ cloudflare.CreateCurlRR()
+ return
+ }
+ }
+ }
+ cloudflare.CreateCurlRR()
return
+ } else {
+ // nsupdate()
+ // me.fixProc.Disable()
}
- nsupdate()
+ */
})
me.fix.Disable()
+ grid.Margin()
+ grid.Pad()
+
statusGrid(tab)
}
@@ -176,71 +301,65 @@ func statusGrid(n *gui.Node) {
gridP := problems.NewGrid("nuts", 2, 2)
gridP.NewLabel("DNS Status =")
- gridP.NewLabel("unknown")
+ me.DnsStatus = gridP.NewLabel("unknown")
gridP.NewLabel("hostname =")
- gridP.NewLabel("invalid")
+ me.hostnameStatus = gridP.NewLabel("invalid")
- gridP.NewLabel("dns provider =")
- gridP.NewLabel("unknown")
+ gridP.NewLabel("dns resolution")
+ me.DnsSpeed = gridP.NewLabel("unknown")
+
+ gridP.NewLabel("dns resolution speed")
+ me.DnsSpeedActual = gridP.NewLabel("unknown")
+
+ gridP.NewLabel("dns API provider =")
+ me.DnsAPI = gridP.NewLabel("unknown")
+
+ gridP.Margin()
+ gridP.Pad()
+
+ // TODO: these are notes for me things to figure out
+ ng := n.NewGroup("TODO:")
+ gridP = ng.NewGrid("nut2", 2, 2)
gridP.NewLabel("IPv6 working =")
gridP.NewLabel("unknown")
- gridP.NewLabel("dns resolution =")
+ gridP.NewLabel("ping.wit.com =")
gridP.NewLabel("unknown")
-}
-/*
-var outJunk string
-func output(s string, a bool) {
- if (a) {
- outJunk += s
- } else {
- outJunk = s
- }
- me.output.SetText(outJunk)
- log.Println(outJunk)
+ gridP.NewLabel("ping6.wit.com =")
+ gridP.NewLabel("unknown")
+
+ problems.Margin()
+ problems.Pad()
+ gridP.Margin()
+ gridP.Pad()
}
-*/
+// run everything because something has changed
func updateDNS() {
var aaaa []string
h := me.hostname
if (h == "") {
- h = "unknown.lab.wit.org"
- // h = "hpdevone.lab.wit.org"
+ h = "test.wit.com"
}
- log.Println("dnsAAAA()()")
- aaaa = dnsAAAA(h)
- log.Println("dnsAAAA()()")
- log.Println(SPEW, me)
+ // log.Println("digAAAA()")
+ aaaa = digAAAA(h)
+ debug(LogNow, "digAAAA() =", aaaa)
+ // log.Println(SPEW, me)
if (aaaa == nil) {
- log.Println("There are no DNS AAAA records for hostname: ", h)
- }
- var broken int = 0
- var all string
- for _, s := range aaaa {
- log.Println("host", h, "DNS AAAA =", s)
- all += s + "\n"
- if ( me.ipmap[s] == nil) {
- log.Println("THIS IS THE WRONG AAAA DNS ENTRY: host", h, "DNS AAAA =", s)
- broken = 2
- } else {
- if (broken == 0) {
- broken = 1
- }
- }
+ debug(LogError, "There are no DNS AAAA records for hostname: ", h)
}
- all = strings.TrimSpace(all)
- me.DnsAAAA.SetText(all)
+ broken := displayDNS() // update the GUI based on dig results
+
if (broken == 1) {
+ me.DnsStatus.SetText("PARTLY WORKING")
+ } else if (broken == 2) {
me.DnsStatus.SetText("WORKING")
} else {
me.DnsStatus.SetText("BROKEN")
me.fix.Enable()
- log.Println("Need to run go-nsupdate here")
- nsupdate()
}
user, _ := user.Current()
@@ -249,5 +368,29 @@ func updateDNS() {
if (me.uid != nil) {
me.uid.SetText(user.Username + " (" + strconv.Itoa(os.Getuid()) + ")")
}
+
+ // lookup the NS records for your domain
+ // if your host is test.wit.com, find the NS resource records for wit.com
+ lookupNS(me.domainname.S)
+
log.Println("updateDNS() END")
}
+
+func suggestProcDebugging() {
+ if (me.fixProc != nil) {
+ // me.fixProc.Disable()
+ return
+ }
+
+ me.fixProc = me.mainStatus.NewButton("Try debugging Slow DNS lookups", func () {
+ debug("You're DNS lookups are very slow")
+ me.dbOn.Set(true)
+ me.dbProc.Set(true)
+
+ DEBUGON = true
+ LogProc = true
+ processName := getProcessNameByPort(53)
+ log.Println("Process with port 53:", processName)
+ })
+ // me.fixProc.Disable()
+}
diff --git a/hostname.go b/hostname.go
index 62803c1..06e2ce7 100644
--- a/hostname.go
+++ b/hostname.go
@@ -6,8 +6,12 @@
package main
import (
- "log"
"git.wit.org/wit/shell"
+
+ // dnssec IPv6 socket library
+ "git.wit.org/jcarr/dnssecsocket"
+
+ "git.wit.org/jcarr/control-panel-dns/cloudflare"
)
// will try to get this hosts FQDN
@@ -16,15 +20,13 @@ 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
var s string = "gui.Label == nil"
s, err = fqdn.FqdnHostname()
if (err != nil) {
- log.Println("FQDN hostname error =", err)
+ debug(LogError, "FQDN hostname error =", err)
return
}
if (me.fqdn != nil) {
@@ -34,7 +36,47 @@ func getHostname() {
me.changed = true
}
}
- log.Println("FQDN =", s)
+ debug(LogNet, "FQDN =", s)
+
+ dn := run("domainname")
+ if (me.domainname.S != dn) {
+ debug(LogChange, "domainname has changed from", me.domainname.S, "to", dn)
+ me.domainname.SetText(dn)
+ me.changed = true
+ }
+
+ hshort := run("hostname -s")
+ if (me.hostshort.S != hshort) {
+ debug(LogChange, "hostname -s has changed from", me.hostshort.S, "to", hshort)
+ me.hostshort.SetText(hshort)
+ me.changed = true
+ }
+
+ var test string
+ test = hshort + "." + dn
+ if (me.hostname != test) {
+ debug(LogInfo, "me.hostname", me.hostname, "does not equal", test)
+ if (me.hostnameStatus.S != "BROKEN") {
+ debug(LogChange, "me.hostname", me.hostname, "does not equal", test)
+ me.changed = true
+ me.hostnameStatus.SetText("BROKEN")
+ }
+ } else {
+ if (me.hostnameStatus.S != "VALID") {
+ debug(LogChange, "me.hostname", me.hostname, "is valid")
+ me.hostnameStatus.SetText("VALID")
+ me.changed = true
+ }
+ // enable the cloudflare button if the provider is cloudflare
+ if (me.cloudflareB == nil) {
+ debug(LogChange, "me.cloudflare == nil; me.DnsAPI.S =", me.DnsAPI.S)
+ if (me.DnsAPI.S == "cloudflare") {
+ me.cloudflareB = me.mainStatus.NewButton("cloudflare wit.com", func () {
+ cloudflare.CreateRR(myGui, "wit.com", "3777302ac4a78cd7fa4f6d3f72086d06")
+ })
+ }
+ }
+ }
}
// returns true if the hostname is good
@@ -43,31 +85,36 @@ func getHostname() {
// and domainname and hostname
func goodHostname(h string) bool {
hostname := shell.Chomp(shell.Cat("/etc/hostname"))
- log.Println("hostname =", hostname)
+ debug(true, "hostname =", hostname)
hs := run("hostname -s")
dn := run("domainname")
- log.Println("hostname short =", hs, "domainname =", dn)
+ debug(true, "hostname short =", hs, "domainname =", dn)
tmp := hs + "." + dn
if (hostname == tmp) {
- log.Println("hostname seems to be good", hostname)
+ debug(true, "hostname seems to be good", hostname)
return true
}
return false
}
-func dnsAAAA(s string) []string {
+func digAAAA(s string) []string {
var aaaa []string
// lookup the IP address from DNS
rrset := dnssecsocket.Dnstrace(s, "AAAA")
- log.Println(args.VerboseDNS, SPEW, rrset)
+ // debug(true, args.VerboseDNS, SPEW, rrset)
for i, rr := range rrset {
- log.Println(args.VerboseDNS, "r.Answer =", i, rr)
ipaddr := dns.Field(rr, 1)
+ // how the hell do you detect a RRSIG AAAA record here?
+ if (ipaddr == "28") {
+ continue
+ }
+ debug(LogNow, "r.Answer =", i, "rr =", rr, "ipaddr =", ipaddr)
aaaa = append(aaaa, ipaddr)
+ me.ipv6s[ipaddr] = rr
}
- log.Println(args.VerboseDNS, "aaaa =", aaaa)
+ debug(true, args.VerboseDNS, "aaaa =", aaaa)
return aaaa
}
diff --git a/json.go b/json.go
new file mode 100644
index 0000000..6cb3af5
--- /dev/null
+++ b/json.go
@@ -0,0 +1,25 @@
+// This is a simple example
+package main
+
+import (
+ "encoding/json"
+)
+
+// formatJSON takes an unformatted JSON string and returns a formatted version.
+func formatJSON(unformattedJSON string) (string, error) {
+ var jsonData interface{}
+
+ // Decode the JSON string into an interface
+ err := json.Unmarshal([]byte(unformattedJSON), &jsonData)
+ if err != nil {
+ return "", err
+ }
+
+ // Re-encode the JSON with indentation for formatting
+ formattedJSON, err := json.MarshalIndent(jsonData, "", " ")
+ if err != nil {
+ return "", err
+ }
+
+ return string(formattedJSON), nil
+}
diff --git a/log.go b/log.go
index b0994c0..252f4da 100644
--- a/log.go
+++ b/log.go
@@ -2,15 +2,22 @@ package main
import (
"log"
+ "reflect"
witlog "git.wit.org/wit/gui/log"
)
+var LogPrefix = "ipv6cp" // ipv6 control panel debugging line
+
// various debugging flags
-var logNow bool = true // useful for active development
-var logError bool = true
-var logWarn bool = false
-var logInfo bool = false
-var logVerbose bool = false
+var DEBUGON bool = true
+var LogNow bool = true // useful for active development
+var LogError bool = true // probably always leave this one
+var LogChange bool = true // turn on /proc debugging output
+
+var LogInfo bool = false // general info
+var LogNet bool = false // general network debugging
+var LogProc bool = false // turn on /proc debugging output
+var LogExec bool = false // turn on os.Exec() debugging
var SPEW witlog.Spewt
@@ -28,6 +35,25 @@ func sleep(a ...any) {
}
func exit(a ...any) {
- log.Println(logError, "got to log() exit")
+ debug(LogError, "got to log() exit")
witlog.Exit(a...)
}
+
+func debug(a ...any) {
+ if (! DEBUGON) {
+ return
+ }
+
+ if (a == nil) {
+ return
+ }
+ var tbool bool
+ if (reflect.TypeOf(a[0]) == reflect.TypeOf(tbool)) {
+ if (a[0] == false) {
+ return
+ }
+ a[0] = LogPrefix // ipv6 control panel debugging line
+ }
+
+ log.Println(a...)
+}
diff --git a/lookupAAAA.go b/lookupAAAA.go
deleted file mode 100644
index 8ae3f02..0000000
--- a/lookupAAAA.go
+++ /dev/null
@@ -1,32 +0,0 @@
-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 7275572..657d989 100644
--- a/main.go
+++ b/main.go
@@ -5,12 +5,16 @@
package main
import (
- "log"
+ "fmt"
+ "strings"
+ "sort"
"strconv"
"runtime"
"time"
"embed"
+
"git.wit.org/wit/gui"
+ "github.com/miekg/dns"
)
var myGui *gui.Node
@@ -25,22 +29,30 @@ func main() {
me.ipmap = make(map[string]*IPtype)
me.dnsmap = make(map[string]*IPtype)
me.ifmap = make(map[int]*IFtype)
- me.dnsTTL = 2 // recheck DNS is working every 2 minutes // TODO: watch rx packets?
+ me.nsmap = make(map[string]string)
+
+ // initialize maps for the returned DNS records
+ me.ipv4s = make(map[string]dns.RR)
+ me.ipv6s = make(map[string]dns.RR)
// will set all debugging flags
// gui.SetDebug(true)
// myGui = gui.New().InitEmbed(resToolkit).LoadToolkit("gocui")
myGui = gui.New().Default()
+
sleep(me.artificialSleep)
setupControlPanelWindow()
- sleep(me.artificialSleep)
+
+ /*
if (args.GuiDebug) {
gui.DebugWindow()
}
gui.ShowDebugValues()
+ */
// forever monitor for network and dns changes
+ sleep(me.artificialSleep)
checkNetworkChanges()
}
@@ -48,44 +60,143 @@ func main() {
Poll for changes to the networking settings
*/
func checkNetworkChanges() {
- var ttl int = 0
+ var lastLocal time.Time = time.Now()
+ var lastDNS time.Time = time.Now()
+ /*
+func timeFunction(f func()) time.Duration {
+ startTime := time.Now() // Record the start time
+ f() // Execute the function
+ return time.Since(startTime) // Calculate the elapsed time
+}
+*/
for {
sleep(me.dnsTTLsleep)
- ttl -= 1
- if (ttl < 0) {
+ if (time.Since(lastLocal) > me.localSleep) {
if (runtime.GOOS == "linux") {
- dnsTTL()
+ duration := timeFunction(linuxLoop)
+ s := fmt.Sprint(duration)
+ me.LocalSpeedActual.SetText(s)
} else {
- log.Println("Windows and MacOS don't work yet")
+ // TODO: make windows and macos diagnostics
+ debug(LogError, "Windows and MacOS don't work yet")
}
- ttl = me.dnsTTL
+ lastLocal = time.Now()
+ }
+ if (time.Since(lastDNS) > me.dnsSleep) {
+ DNSloop()
+ lastDNS = time.Now()
+ }
+ }
+}
+
+// run this on each timeout
+func DNSloop() {
+ duration := timeFunction(dnsTTL)
+ debug(LogInfo, "dnsTTL() execution Time: ", duration)
+ var s, newSpeed string
+ if (duration > 5000 * time.Millisecond ) {
+ newSpeed = "VERY BAD"
+ suggestProcDebugging()
+ } else if (duration > 2000 * time.Millisecond ) {
+ newSpeed = "BAD"
+ suggestProcDebugging()
+ } else if (duration > 500 * time.Millisecond ) {
+ suggestProcDebugging()
+ newSpeed = "SLOW"
+ } else if (duration > 100 * time.Millisecond ) {
+ newSpeed = "OK"
+ if (me.fixProc != nil) {
+ me.fixProc.Disable()
+ }
+ } else {
+ newSpeed = "FAST"
+ if (me.fixProc != nil) {
+ me.fixProc.Disable()
}
}
+ if (newSpeed != me.DnsSpeedLast) {
+ debug(LogChange, "dns lookup speed changed =", newSpeed)
+ debug(LogChange, "dnsTTL() execution Time: ", duration)
+ me.DnsSpeed.SetText(newSpeed)
+ me.DnsSpeedLast = newSpeed
+ }
+ s = fmt.Sprint(duration)
+ me.DnsSpeedActual.SetText(s)
}
// This checks for changes to the network settings
// and verifies that DNS is working or not working
func dnsTTL() {
+ updateDNS()
+}
+
+func linuxLoop() {
me.changed = false
- log.Println("FQDN =", me.fqdn.GetText())
- getHostname()
- scanInterfaces()
+ debug(LogNet, "FQDN =", me.fqdn.GetText())
+ duration := timeFunction(getHostname)
+ debug(LogInfo, "getHostname() execution Time: ", duration, "me.changed =", me.changed)
+
+ duration = timeFunction(scanInterfaces)
+ debug(LogNet, "scanInterfaces() execution Time: ", duration)
for i, t := range me.ifmap {
- log.Println(strconv.Itoa(i) + " iface = " + t.iface.Name)
+ debug(LogNet, strconv.Itoa(i) + " iface = " + t.iface.Name)
}
var aaaa []string
aaaa = realAAAA()
var all string
for _, s := range aaaa {
- log.Println("my actual AAAA = ",s)
+ debug(LogNet, "my actual AAAA = ",s)
all += s + "\n"
}
// me.IPv6.SetText(all)
if (me.changed) {
stamp := time.Now().Format("2006/01/02 15:04:05")
- log.Println(logError, "Network things changed on", stamp)
- updateDNS()
+ debug(LogChange, "Network things changed on", stamp)
+ duration := timeFunction(updateDNS)
+ debug(LogChange, "updateDNS() execution Time: ", duration)
+ }
+
+ /*
+ processName := getProcessNameByPort(53)
+ fmt.Println("Process with port 53:", processName)
+
+ commPath := filepath.Join("/proc", proc.Name(), "comm")
+ comm, err := ioutil.ReadFile(commPath)
+ if err != nil {
+ return "", err // Error reading the process name
}
+ return strings.TrimSpace(string(comm)), nil
+ */
+}
+
+/*
+ // Example usage
+ duration := timeFunction(FunctionToTime)
+ log.Println("Execution Time: ", duration)
+*/
+
+// timeFunction takes a function as an argument and returns the execution time.
+func timeFunction(f func()) time.Duration {
+ startTime := time.Now() // Record the start time
+ f() // Execute the function
+ return time.Since(startTime) // Calculate the elapsed time
+}
+
+// sortLines takes a string, splits it on newlines, sorts the lines,
+// and rejoins them with newlines.
+func sortLines(input string) string {
+ lines := strings.Split(input, "\n")
+
+ // Trim leading and trailing whitespace from each line
+ for i, line := range lines {
+ lines[i] = strings.TrimSpace(line)
+ }
+
+ sort.Strings(lines)
+ tmp := strings.Join(lines, "\n")
+ tmp = strings.TrimLeft(tmp, "\n")
+ tmp = strings.TrimRight(tmp, "\n")
+ return tmp
}
diff --git a/net.go b/net.go
index e6d3d07..487486e 100644
--- a/net.go
+++ b/net.go
@@ -2,13 +2,11 @@
package main
import (
- "log"
+ // "log"
"net"
"strings"
)
-var DEBUGNET bool = false
-
// this doesn't work
/*
func watchNetworkInterfaces() {
@@ -18,21 +16,21 @@ func watchNetworkInterfaces() {
// Set up a notification channel
notification := make(chan net.Interface)
- log.Println(DEBUGNET, "watchNet()")
+ debug(LogNet, "watchNet()")
// Start goroutine to watch for changes
go func() {
- log.Println(DEBUGNET, "watchNet() func")
+ debug(LogNet, "watchNet() func")
for {
- log.Println(DEBUGNET, "forever loop start")
+ debug(LogNet, "forever loop start")
// Check for changes in each interface
for _, i := range interfaces {
- log.Println(DEBUGNET, "something on i =", i)
+ debug(LogNet, "something on i =", i)
if status := i.Flags & net.FlagUp; status != 0 {
notification <- i
- log.Println(DEBUGNET, "something on i =", i)
+ debug(LogNet, "something on i =", i)
}
}
- log.Println(DEBUGNET, "forever loop end")
+ debug(LogNet, "forever loop end")
}
}()
}
@@ -44,20 +42,20 @@ func IsIPv6(address string) bool {
func (t *IPtype) IsReal() bool {
if (t.ip.IsPrivate() || t.ip.IsLoopback() || t.ip.IsLinkLocalUnicast()) {
- log.Println(DEBUGNET, "\t\tIP is Real = false")
+ debug(LogNet, "\t\tIP is Real = false")
return false
} else {
- log.Println(DEBUGNET, "\t\tIP is Real = true")
+ debug(LogNet, "\t\tIP is Real = true")
return true
}
}
func IsReal(ip *net.IP) bool {
if (ip.IsPrivate() || ip.IsLoopback() || ip.IsLinkLocalUnicast()) {
- log.Println(DEBUGNET, "\t\tIP is Real = false")
+ debug(LogNet, "\t\tIP is Real = false")
return false
} else {
- log.Println(DEBUGNET, "\t\tIP is Real = true")
+ debug(LogNet, "\t\tIP is Real = true")
return true
}
}
@@ -74,7 +72,7 @@ func renameInterface(i *net.Interface) {
func checkInterface(i net.Interface) {
val, ok := me.ifmap[i.Index]
if ! ok {
- log.Println(i.Name, "is a new network interface. The linux kernel index =", i.Index)
+ debug(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
@@ -86,9 +84,9 @@ func checkInterface(i net.Interface) {
return
}
me.ifmap[i.Index].gone = false
- log.Println(args.VerboseNet, "me.ifmap[i] does exist. Need to compare everything.", i.Index, i.Name, val.iface.Index, val.iface.Name)
+ debug(LogNet, "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.Println(val.iface.Name, "has changed to it's name to", i.Name)
+ debug(val.iface.Name, "has changed to it's name to", i.Name)
me.ifmap[i.Index].iface = &i
me.changed = true
if (me.Interfaces != nil) {
@@ -112,6 +110,19 @@ func realAAAA() []string {
return aaaa
}
+func realA() []string {
+ var a []string
+
+ for s, t := range me.ipmap {
+ if (t.IsReal()) {
+ if (t.ipv4) {
+ a = append(a, s)
+ }
+ }
+ }
+ return a
+}
+
func checkDNS() (map[string]*IPtype, map[string]*IPtype) {
var ipv4s map[string]*IPtype
var ipv6s map[string]*IPtype
@@ -126,14 +137,14 @@ func checkDNS() (map[string]*IPtype, map[string]*IPtype) {
ipt = "IPv6"
}
if (t.IsReal()) {
- log.Println("\tIP is Real ", ipt, i.Index, i.Name, s)
+ debug("\tIP is Real ", ipt, i.Index, i.Name, s)
if (t.ipv6) {
ipv6s[s] = t
} else {
ipv4s[s] = t
}
} else {
- log.Println("\tIP is not Real", ipt, i.Index, i.Name, s)
+ debug("\tIP is not Real", ipt, i.Index, i.Name, s)
}
}
return ipv6s, ipv4s
@@ -141,14 +152,14 @@ func checkDNS() (map[string]*IPtype, map[string]*IPtype) {
// Will figure out if an IP address is new
func checkIP(ip *net.IPNet, i net.Interface) bool {
- log.Println(args.VerboseNet, "\t\taddr.(type) = *net.IPNet")
- log.Println(args.VerboseNet, "\t\taddr.(type) =", ip)
+ debug(LogNet, "\t\taddr.(type) = *net.IPNet")
+ debug(LogNet, "\t\taddr.(type) =", ip)
var realip string
realip = ip.IP.String()
val, ok := me.ipmap[realip]
if ok {
- log.Println(args.VerboseNet, val.ipnet.IP.String(), "is already a defined IP address")
+ debug(LogNet, val.ipnet.IP.String(), "is already a defined IP address")
me.ipmap[realip].gone = false
return false
}
@@ -175,82 +186,101 @@ func checkIP(ip *net.IPNet, i net.Interface) bool {
}
}
if (IsReal(&ip.IP)) {
- log.Println("\tIP is Real ", t, i.Index, i.Name, realip)
+ debug("\tIP is Real ", t, i.Index, i.Name, realip)
} else {
- log.Println("\tIP is not Real", t, i.Index, i.Name, realip)
+ debug("\tIP is not Real", t, i.Index, i.Name, realip)
}
- log.Println(args.VerboseNet, "\t\tIP is IsPrivate() =", ip.IP.IsPrivate())
- log.Println(args.VerboseNet, "\t\tIP is IsLoopback() =", ip.IP.IsLoopback())
- log.Println(args.VerboseNet, "\t\tIP is IsLinkLocalUnicast() =", ip.IP.IsLinkLocalUnicast())
- // log.Println("HERE HERE", "realip =", realip, "me.ip[realip]=", me.ipmap[realip])
+ debug(LogNet, "\t\tIP is IsPrivate() =", ip.IP.IsPrivate())
+ debug(LogNet, "\t\tIP is IsLoopback() =", ip.IP.IsLoopback())
+ debug(LogNet, "\t\tIP is IsLinkLocalUnicast() =", ip.IP.IsLinkLocalUnicast())
+ // debug("HERE HERE", "realip =", realip, "me.ip[realip]=", me.ipmap[realip])
return true
}
func scanInterfaces() {
- me.changed = false
+ debug(LogNet, "scanInterfaces() START")
ifaces, _ := net.Interfaces()
// me.ifnew = ifaces
- log.Println(DEBUGNET, SPEW, ifaces)
+ debug(LogNet, SPEW, ifaces)
for _, i := range ifaces {
addrs, _ := i.Addrs()
- // log.Println("range ifaces = ", i)
+ // debug("range ifaces = ", i)
checkInterface(i)
- log.Println(args.VerboseNet, "*net.Interface.Name = ", i.Name, i.Index)
- log.Println(args.VerboseNet, SPEW, i)
- log.Println(DEBUGNET, SPEW, addrs)
+ debug(LogNet, "*net.Interface.Name = ", i.Name, i.Index)
+ debug(LogNet, SPEW, i)
+ debug(LogNet, SPEW, addrs)
for _, addr := range addrs {
- log.Println(DEBUGNET, "\taddr =", addr)
- log.Println(DEBUGNET, SPEW, addrs)
+ debug(LogNet, "\taddr =", addr)
+ debug(LogNet, SPEW, addrs)
ips, _ := net.LookupIP(addr.String())
- log.Println(DEBUGNET, "\tLookupIP(addr) =", ips)
+ debug(LogNet, "\tLookupIP(addr) =", ips)
switch v := addr.(type) {
case *net.IPNet:
- checkIP(v, i)
- // log.Println("\t\tIP is () =", ip.())
+ if checkIP(v, i) {
+ debug(true, "scanInterfaces() IP is new () i =", v.IP.String())
+ }
default:
- log.Println(DEBUGNET, "\t\taddr.(type) = NO IDEA WHAT TO DO HERE v =", v)
+ debug(LogNet, "\t\taddr.(type) = NO IDEA WHAT TO DO HERE v =", v)
}
}
}
- deleteChanges()
+ if deleteChanges() {
+ me.changed = true
+ debug(LogNow, "deleteChanges() detected network changes")
+ }
+ updateRealAAAA()
+ debug(LogNet, "scanInterfaces() END")
+}
+
+// displays the IP address found on your network interfaces
+func updateRealAAAA() {
var all4 string
var all6 string
for s, t := range me.ipmap {
if (t.ipv4) {
all4 += s + "\n"
- log.Println("IPv4 =", s)
+ debug(LogNet, "IPv4 =", s)
} else if (t.ipv6) {
all6 += s + "\n"
- log.Println("IPv6 =", s)
+ debug(LogNet, "IPv6 =", s)
} else {
- log.Println("???? =", s)
+ debug(LogNet, "???? =", s)
}
}
- all4 = strings.TrimSpace(all4)
- all6 = strings.TrimSpace(all6)
- me.IPv4.SetText(all4)
- me.IPv6.SetText(all6)
+ all4 = sortLines(all4)
+ all6 = sortLines(all6)
+ if (me.IPv4.S != all4) {
+ debug(LogNow, "IPv4 addresses have changed", all4)
+ me.IPv4.SetText(all4)
+ }
+ if (me.IPv6.S != all6) {
+ debug(LogNow, "IPv6 addresses have changed", all6)
+ me.IPv6.SetText(all6)
+ }
}
// delete network interfaces and ip addresses from the gui
-func deleteChanges() {
+func deleteChanges() bool {
+ var changed bool = false
for i, t := range me.ifmap {
if (t.gone) {
- log.Println("DELETE int =", i, "name =", t.name, t.iface)
+ debug(LogChange, "DELETE int =", i, "name =", t.name, t.iface)
delete(me.ifmap, i)
- me.changed = true
+ changed = true
}
t.gone = true
}
for s, t := range me.ipmap {
if (t.gone) {
- log.Println("DELETE name =", s, "IPv4 =", t.ipv4)
- log.Println("DELETE name =", s, "IPv6 =", t.ipv6)
- log.Println("DELETE name =", s, "iface =", t.iface)
- log.Println("DELETE name =", s, "ip =", t.ip)
+ debug(LogChange, "DELETE name =", s, "IPv4 =", t.ipv4)
+ debug(LogChange, "DELETE name =", s, "IPv6 =", t.ipv6)
+ debug(LogChange, "DELETE name =", s, "iface =", t.iface)
+ debug(LogChange, "DELETE name =", s, "ip =", t.ip)
delete(me.ipmap, s)
- me.changed = true
+ changed = true
}
t.gone = true
}
+
+ return changed
}
diff --git a/nsupdate.go b/nsupdate.go
index 84f607d..db33018 100644
--- a/nsupdate.go
+++ b/nsupdate.go
@@ -6,7 +6,6 @@
package main
import (
- "log"
"os"
)
@@ -17,17 +16,17 @@ import (
func nsupdate() {
var tsigSecret string
- log.Println(true, "nsupdate() START")
+ debug(true, "nsupdate() START")
cmd := "go-nsupdate --tsig-algorithm=hmac-sha512"
tsigSecret = os.Getenv("TIG_SECRET")
cmd += " --tig-secret=\"" + tsigSecret + "\""
cmd += " -i wlo1 " + me.hostname
- log.Println(true, "nsupdate() RUN:", cmd)
+ debug(true, "nsupdate() RUN:", cmd)
for s, t := range me.ipmap {
if (t.IsReal()) {
if (t.ipv6) {
- log.Println(true, "nsupdate() found real AAAA =", s, "on iface", t.iface.Name)
+ debug(true, "nsupdate() found real AAAA =", s, "on iface", t.iface.Name)
}
}
}
diff --git a/proc.go b/proc.go
new file mode 100644
index 0000000..bf78c16
--- /dev/null
+++ b/proc.go
@@ -0,0 +1,99 @@
+package main
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+)
+
+func getProcessNameByPort(port int) string {
+ // Convert port to hex string
+ portHex := strconv.FormatInt(int64(port), 16)
+
+ // Function to search /proc/net/tcp or /proc/net/udp
+ searchProcNet := func(file string) string {
+ data, err := ioutil.ReadFile(file)
+ if err != nil {
+ return ""
+ }
+ // debug(LogProc, "searchProcNet() data:", string(data))
+
+ lines := strings.Split(string(data), "\n")
+ for _, line := range lines {
+ fields := strings.Fields(line)
+ debug(LogProc, "searchProcNet() portHex:", portHex)
+ if (len(fields) > 9) {
+ debug(LogProc, "searchProcNet() fields[9]", fields[9])
+ }
+ debug(LogProc, "searchProcNet() lines:", line)
+ if len(fields) > 1 {
+ parts := strings.Split(fields[1], ":")
+ if len(parts) > 1 {
+ // Convert the hexadecimal string to an integer
+ value, _ := strconv.ParseInt(parts[1], 16, 64)
+ debug(LogProc, "searchProcNet() value, port =", value, port, "parts[1] =", parts[1])
+ if (port == int(value)) {
+ debug(LogProc, "searchProcNet() THIS IS THE LINE:", fields)
+ return fields[9]
+ }
+ }
+ }
+ }
+
+ return ""
+ }
+
+ // Search TCP and then UDP
+ inode := searchProcNet("/proc/net/tcp")
+ if inode == "" {
+ inode = searchProcNet("/proc/net/udp")
+ }
+ debug(LogProc, "searchProcNet() inode =", inode)
+
+ // Search for process with the inode
+ procs, _ := ioutil.ReadDir("/proc")
+ for _, proc := range procs {
+ if !proc.IsDir() {
+ continue
+ }
+
+ fdPath := filepath.Join("/proc", proc.Name(), "fd")
+ fds, err := ioutil.ReadDir(fdPath)
+ if err != nil {
+ continue // Process might have exited; skip it
+ }
+
+ for _, fd := range fds {
+ fdLink, _ := os.Readlink(filepath.Join(fdPath, fd.Name()))
+ var s string
+ s = "socket:["+inode+"]"
+ if strings.Contains(fdLink, "socket:[") {
+ debug(LogProc, "searchProcNet() fdLink has socket:", fdLink)
+ debug(LogProc, "searchProcNet() proc.Name() =", proc.Name(), "s =", s)
+ }
+ if strings.Contains(fdLink, "socket:[35452]") {
+ debug(LogProc, "searchProcNet() found proc.Name() =", proc.Name(), fdLink)
+ return proc.Name()
+ }
+ if strings.Contains(fdLink, "socket:[35450]") {
+ debug(LogProc, "searchProcNet() found proc.Name() =", proc.Name(), fdLink)
+ return proc.Name()
+ }
+ if strings.Contains(fdLink, "socket:[35440]") {
+ debug(LogProc, "searchProcNet() found proc.Name() =", proc.Name(), fdLink)
+ return proc.Name()
+ }
+ if strings.Contains(fdLink, "socket:[21303]") {
+ debug(LogProc, "searchProcNet() found proc.Name() =", proc.Name(), fdLink)
+ // return proc.Name()
+ }
+ if strings.Contains(fdLink, "socket:["+inode+"]") {
+ return proc.Name()
+ }
+ }
+ }
+
+ return ""
+}
diff --git a/protobuf/Makefile b/protobuf/Makefile
new file mode 100644
index 0000000..35a9d9c
--- /dev/null
+++ b/protobuf/Makefile
@@ -0,0 +1,22 @@
+all:
+ protoc --version
+ make dnsmessage.pb.go
+
+clean:
+ rm -f *.pb.go
+
+dnsmessage.pb.go: dnsmessage.proto
+ protoc --go_out=. dnsmessage.proto
+
+compile:
+ protoc --go_out=. *.proto
+
+deps:
+ apt install golang-goprotobuf-dev
+ apt install protobuf-compiler
+
+push:
+ git pull
+ git add --all
+ git commit -a -s
+ git push
diff --git a/protobuf/dnsmessage.pb.go b/protobuf/dnsmessage.pb.go
new file mode 100644
index 0000000..50fab47
--- /dev/null
+++ b/protobuf/dnsmessage.pb.go
@@ -0,0 +1,749 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: dnsmessage.proto
+
+package dnsmessage
+
+import (
+ fmt "fmt"
+ proto "github.com/golang/protobuf/proto"
+ math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type PBDNSMessage_Type int32
+
+const (
+ PBDNSMessage_DNSQueryType PBDNSMessage_Type = 1
+ PBDNSMessage_DNSResponseType PBDNSMessage_Type = 2
+ PBDNSMessage_DNSOutgoingQueryType PBDNSMessage_Type = 3
+ PBDNSMessage_DNSIncomingResponseType PBDNSMessage_Type = 4
+)
+
+var PBDNSMessage_Type_name = map[int32]string{
+ 1: "DNSQueryType",
+ 2: "DNSResponseType",
+ 3: "DNSOutgoingQueryType",
+ 4: "DNSIncomingResponseType",
+}
+
+var PBDNSMessage_Type_value = map[string]int32{
+ "DNSQueryType": 1,
+ "DNSResponseType": 2,
+ "DNSOutgoingQueryType": 3,
+ "DNSIncomingResponseType": 4,
+}
+
+func (x PBDNSMessage_Type) Enum() *PBDNSMessage_Type {
+ p := new(PBDNSMessage_Type)
+ *p = x
+ return p
+}
+
+func (x PBDNSMessage_Type) String() string {
+ return proto.EnumName(PBDNSMessage_Type_name, int32(x))
+}
+
+func (x *PBDNSMessage_Type) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(PBDNSMessage_Type_value, data, "PBDNSMessage_Type")
+ if err != nil {
+ return err
+ }
+ *x = PBDNSMessage_Type(value)
+ return nil
+}
+
+func (PBDNSMessage_Type) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_c3136ceafbfed9e7, []int{0, 0}
+}
+
+type PBDNSMessage_SocketFamily int32
+
+const (
+ PBDNSMessage_INET PBDNSMessage_SocketFamily = 1
+ PBDNSMessage_INET6 PBDNSMessage_SocketFamily = 2
+)
+
+var PBDNSMessage_SocketFamily_name = map[int32]string{
+ 1: "INET",
+ 2: "INET6",
+}
+
+var PBDNSMessage_SocketFamily_value = map[string]int32{
+ "INET": 1,
+ "INET6": 2,
+}
+
+func (x PBDNSMessage_SocketFamily) Enum() *PBDNSMessage_SocketFamily {
+ p := new(PBDNSMessage_SocketFamily)
+ *p = x
+ return p
+}
+
+func (x PBDNSMessage_SocketFamily) String() string {
+ return proto.EnumName(PBDNSMessage_SocketFamily_name, int32(x))
+}
+
+func (x *PBDNSMessage_SocketFamily) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(PBDNSMessage_SocketFamily_value, data, "PBDNSMessage_SocketFamily")
+ if err != nil {
+ return err
+ }
+ *x = PBDNSMessage_SocketFamily(value)
+ return nil
+}
+
+func (PBDNSMessage_SocketFamily) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_c3136ceafbfed9e7, []int{0, 1}
+}
+
+type PBDNSMessage_SocketProtocol int32
+
+const (
+ PBDNSMessage_UDP PBDNSMessage_SocketProtocol = 1
+ PBDNSMessage_TCP PBDNSMessage_SocketProtocol = 2
+)
+
+var PBDNSMessage_SocketProtocol_name = map[int32]string{
+ 1: "UDP",
+ 2: "TCP",
+}
+
+var PBDNSMessage_SocketProtocol_value = map[string]int32{
+ "UDP": 1,
+ "TCP": 2,
+}
+
+func (x PBDNSMessage_SocketProtocol) Enum() *PBDNSMessage_SocketProtocol {
+ p := new(PBDNSMessage_SocketProtocol)
+ *p = x
+ return p
+}
+
+func (x PBDNSMessage_SocketProtocol) String() string {
+ return proto.EnumName(PBDNSMessage_SocketProtocol_name, int32(x))
+}
+
+func (x *PBDNSMessage_SocketProtocol) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(PBDNSMessage_SocketProtocol_value, data, "PBDNSMessage_SocketProtocol")
+ if err != nil {
+ return err
+ }
+ *x = PBDNSMessage_SocketProtocol(value)
+ return nil
+}
+
+func (PBDNSMessage_SocketProtocol) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_c3136ceafbfed9e7, []int{0, 2}
+}
+
+type PBDNSMessage_PolicyType int32
+
+const (
+ PBDNSMessage_UNKNOWN PBDNSMessage_PolicyType = 1
+ PBDNSMessage_QNAME PBDNSMessage_PolicyType = 2
+ PBDNSMessage_CLIENTIP PBDNSMessage_PolicyType = 3
+ PBDNSMessage_RESPONSEIP PBDNSMessage_PolicyType = 4
+ PBDNSMessage_NSDNAME PBDNSMessage_PolicyType = 5
+ PBDNSMessage_NSIP PBDNSMessage_PolicyType = 6
+)
+
+var PBDNSMessage_PolicyType_name = map[int32]string{
+ 1: "UNKNOWN",
+ 2: "QNAME",
+ 3: "CLIENTIP",
+ 4: "RESPONSEIP",
+ 5: "NSDNAME",
+ 6: "NSIP",
+}
+
+var PBDNSMessage_PolicyType_value = map[string]int32{
+ "UNKNOWN": 1,
+ "QNAME": 2,
+ "CLIENTIP": 3,
+ "RESPONSEIP": 4,
+ "NSDNAME": 5,
+ "NSIP": 6,
+}
+
+func (x PBDNSMessage_PolicyType) Enum() *PBDNSMessage_PolicyType {
+ p := new(PBDNSMessage_PolicyType)
+ *p = x
+ return p
+}
+
+func (x PBDNSMessage_PolicyType) String() string {
+ return proto.EnumName(PBDNSMessage_PolicyType_name, int32(x))
+}
+
+func (x *PBDNSMessage_PolicyType) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(PBDNSMessage_PolicyType_value, data, "PBDNSMessage_PolicyType")
+ if err != nil {
+ return err
+ }
+ *x = PBDNSMessage_PolicyType(value)
+ return nil
+}
+
+func (PBDNSMessage_PolicyType) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_c3136ceafbfed9e7, []int{0, 3}
+}
+
+type PBDNSMessage struct {
+ Type *PBDNSMessage_Type `protobuf:"varint,1,req,name=type,enum=PBDNSMessage_Type" json:"type,omitempty"`
+ MessageId []byte `protobuf:"bytes,2,opt,name=messageId" json:"messageId,omitempty"`
+ ServerIdentity []byte `protobuf:"bytes,3,opt,name=serverIdentity" json:"serverIdentity,omitempty"`
+ SocketFamily *PBDNSMessage_SocketFamily `protobuf:"varint,4,opt,name=socketFamily,enum=PBDNSMessage_SocketFamily" json:"socketFamily,omitempty"`
+ SocketProtocol *PBDNSMessage_SocketProtocol `protobuf:"varint,5,opt,name=socketProtocol,enum=PBDNSMessage_SocketProtocol" json:"socketProtocol,omitempty"`
+ From []byte `protobuf:"bytes,6,opt,name=from" json:"from,omitempty"`
+ To []byte `protobuf:"bytes,7,opt,name=to" json:"to,omitempty"`
+ InBytes *uint64 `protobuf:"varint,8,opt,name=inBytes" json:"inBytes,omitempty"`
+ TimeSec *uint32 `protobuf:"varint,9,opt,name=timeSec" json:"timeSec,omitempty"`
+ TimeUsec *uint32 `protobuf:"varint,10,opt,name=timeUsec" json:"timeUsec,omitempty"`
+ Id *uint32 `protobuf:"varint,11,opt,name=id" json:"id,omitempty"`
+ Question *PBDNSMessage_DNSQuestion `protobuf:"bytes,12,opt,name=question" json:"question,omitempty"`
+ Response *PBDNSMessage_DNSResponse `protobuf:"bytes,13,opt,name=response" json:"response,omitempty"`
+ OriginalRequestorSubnet []byte `protobuf:"bytes,14,opt,name=originalRequestorSubnet" json:"originalRequestorSubnet,omitempty"`
+ RequestorId *string `protobuf:"bytes,15,opt,name=requestorId" json:"requestorId,omitempty"`
+ InitialRequestId []byte `protobuf:"bytes,16,opt,name=initialRequestId" json:"initialRequestId,omitempty"`
+ DeviceId []byte `protobuf:"bytes,17,opt,name=deviceId" json:"deviceId,omitempty"`
+ NewlyObservedDomain *bool `protobuf:"varint,18,opt,name=newlyObservedDomain" json:"newlyObservedDomain,omitempty"`
+ DeviceName *string `protobuf:"bytes,19,opt,name=deviceName" json:"deviceName,omitempty"`
+ FromPort *uint32 `protobuf:"varint,20,opt,name=fromPort" json:"fromPort,omitempty"`
+ ToPort *uint32 `protobuf:"varint,21,opt,name=toPort" json:"toPort,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *PBDNSMessage) Reset() { *m = PBDNSMessage{} }
+func (m *PBDNSMessage) String() string { return proto.CompactTextString(m) }
+func (*PBDNSMessage) ProtoMessage() {}
+func (*PBDNSMessage) Descriptor() ([]byte, []int) {
+ return fileDescriptor_c3136ceafbfed9e7, []int{0}
+}
+
+func (m *PBDNSMessage) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_PBDNSMessage.Unmarshal(m, b)
+}
+func (m *PBDNSMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_PBDNSMessage.Marshal(b, m, deterministic)
+}
+func (m *PBDNSMessage) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_PBDNSMessage.Merge(m, src)
+}
+func (m *PBDNSMessage) XXX_Size() int {
+ return xxx_messageInfo_PBDNSMessage.Size(m)
+}
+func (m *PBDNSMessage) XXX_DiscardUnknown() {
+ xxx_messageInfo_PBDNSMessage.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_PBDNSMessage proto.InternalMessageInfo
+
+func (m *PBDNSMessage) GetType() PBDNSMessage_Type {
+ if m != nil && m.Type != nil {
+ return *m.Type
+ }
+ return PBDNSMessage_DNSQueryType
+}
+
+func (m *PBDNSMessage) GetMessageId() []byte {
+ if m != nil {
+ return m.MessageId
+ }
+ return nil
+}
+
+func (m *PBDNSMessage) GetServerIdentity() []byte {
+ if m != nil {
+ return m.ServerIdentity
+ }
+ return nil
+}
+
+func (m *PBDNSMessage) GetSocketFamily() PBDNSMessage_SocketFamily {
+ if m != nil && m.SocketFamily != nil {
+ return *m.SocketFamily
+ }
+ return PBDNSMessage_INET
+}
+
+func (m *PBDNSMessage) GetSocketProtocol() PBDNSMessage_SocketProtocol {
+ if m != nil && m.SocketProtocol != nil {
+ return *m.SocketProtocol
+ }
+ return PBDNSMessage_UDP
+}
+
+func (m *PBDNSMessage) GetFrom() []byte {
+ if m != nil {
+ return m.From
+ }
+ return nil
+}
+
+func (m *PBDNSMessage) GetTo() []byte {
+ if m != nil {
+ return m.To
+ }
+ return nil
+}
+
+func (m *PBDNSMessage) GetInBytes() uint64 {
+ if m != nil && m.InBytes != nil {
+ return *m.InBytes
+ }
+ return 0
+}
+
+func (m *PBDNSMessage) GetTimeSec() uint32 {
+ if m != nil && m.TimeSec != nil {
+ return *m.TimeSec
+ }
+ return 0
+}
+
+func (m *PBDNSMessage) GetTimeUsec() uint32 {
+ if m != nil && m.TimeUsec != nil {
+ return *m.TimeUsec
+ }
+ return 0
+}
+
+func (m *PBDNSMessage) GetId() uint32 {
+ if m != nil && m.Id != nil {
+ return *m.Id
+ }
+ return 0
+}
+
+func (m *PBDNSMessage) GetQuestion() *PBDNSMessage_DNSQuestion {
+ if m != nil {
+ return m.Question
+ }
+ return nil
+}
+
+func (m *PBDNSMessage) GetResponse() *PBDNSMessage_DNSResponse {
+ if m != nil {
+ return m.Response
+ }
+ return nil
+}
+
+func (m *PBDNSMessage) GetOriginalRequestorSubnet() []byte {
+ if m != nil {
+ return m.OriginalRequestorSubnet
+ }
+ return nil
+}
+
+func (m *PBDNSMessage) GetRequestorId() string {
+ if m != nil && m.RequestorId != nil {
+ return *m.RequestorId
+ }
+ return ""
+}
+
+func (m *PBDNSMessage) GetInitialRequestId() []byte {
+ if m != nil {
+ return m.InitialRequestId
+ }
+ return nil
+}
+
+func (m *PBDNSMessage) GetDeviceId() []byte {
+ if m != nil {
+ return m.DeviceId
+ }
+ return nil
+}
+
+func (m *PBDNSMessage) GetNewlyObservedDomain() bool {
+ if m != nil && m.NewlyObservedDomain != nil {
+ return *m.NewlyObservedDomain
+ }
+ return false
+}
+
+func (m *PBDNSMessage) GetDeviceName() string {
+ if m != nil && m.DeviceName != nil {
+ return *m.DeviceName
+ }
+ return ""
+}
+
+func (m *PBDNSMessage) GetFromPort() uint32 {
+ if m != nil && m.FromPort != nil {
+ return *m.FromPort
+ }
+ return 0
+}
+
+func (m *PBDNSMessage) GetToPort() uint32 {
+ if m != nil && m.ToPort != nil {
+ return *m.ToPort
+ }
+ return 0
+}
+
+type PBDNSMessage_DNSQuestion struct {
+ QName *string `protobuf:"bytes,1,opt,name=qName" json:"qName,omitempty"`
+ QType *uint32 `protobuf:"varint,2,opt,name=qType" json:"qType,omitempty"`
+ QClass *uint32 `protobuf:"varint,3,opt,name=qClass" json:"qClass,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *PBDNSMessage_DNSQuestion) Reset() { *m = PBDNSMessage_DNSQuestion{} }
+func (m *PBDNSMessage_DNSQuestion) String() string { return proto.CompactTextString(m) }
+func (*PBDNSMessage_DNSQuestion) ProtoMessage() {}
+func (*PBDNSMessage_DNSQuestion) Descriptor() ([]byte, []int) {
+ return fileDescriptor_c3136ceafbfed9e7, []int{0, 0}
+}
+
+func (m *PBDNSMessage_DNSQuestion) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_PBDNSMessage_DNSQuestion.Unmarshal(m, b)
+}
+func (m *PBDNSMessage_DNSQuestion) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_PBDNSMessage_DNSQuestion.Marshal(b, m, deterministic)
+}
+func (m *PBDNSMessage_DNSQuestion) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_PBDNSMessage_DNSQuestion.Merge(m, src)
+}
+func (m *PBDNSMessage_DNSQuestion) XXX_Size() int {
+ return xxx_messageInfo_PBDNSMessage_DNSQuestion.Size(m)
+}
+func (m *PBDNSMessage_DNSQuestion) XXX_DiscardUnknown() {
+ xxx_messageInfo_PBDNSMessage_DNSQuestion.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_PBDNSMessage_DNSQuestion proto.InternalMessageInfo
+
+func (m *PBDNSMessage_DNSQuestion) GetQName() string {
+ if m != nil && m.QName != nil {
+ return *m.QName
+ }
+ return ""
+}
+
+func (m *PBDNSMessage_DNSQuestion) GetQType() uint32 {
+ if m != nil && m.QType != nil {
+ return *m.QType
+ }
+ return 0
+}
+
+func (m *PBDNSMessage_DNSQuestion) GetQClass() uint32 {
+ if m != nil && m.QClass != nil {
+ return *m.QClass
+ }
+ return 0
+}
+
+type PBDNSMessage_DNSResponse struct {
+ Rcode *uint32 `protobuf:"varint,1,opt,name=rcode" json:"rcode,omitempty"`
+ Rrs []*PBDNSMessage_DNSResponse_DNSRR `protobuf:"bytes,2,rep,name=rrs" json:"rrs,omitempty"`
+ AppliedPolicy *string `protobuf:"bytes,3,opt,name=appliedPolicy" json:"appliedPolicy,omitempty"`
+ Tags []string `protobuf:"bytes,4,rep,name=tags" json:"tags,omitempty"`
+ QueryTimeSec *uint32 `protobuf:"varint,5,opt,name=queryTimeSec" json:"queryTimeSec,omitempty"`
+ QueryTimeUsec *uint32 `protobuf:"varint,6,opt,name=queryTimeUsec" json:"queryTimeUsec,omitempty"`
+ AppliedPolicyType *PBDNSMessage_PolicyType `protobuf:"varint,7,opt,name=appliedPolicyType,enum=PBDNSMessage_PolicyType" json:"appliedPolicyType,omitempty"`
+ AppliedPolicyTrigger *string `protobuf:"bytes,8,opt,name=appliedPolicyTrigger" json:"appliedPolicyTrigger,omitempty"`
+ AppliedPolicyHit *string `protobuf:"bytes,9,opt,name=appliedPolicyHit" json:"appliedPolicyHit,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *PBDNSMessage_DNSResponse) Reset() { *m = PBDNSMessage_DNSResponse{} }
+func (m *PBDNSMessage_DNSResponse) String() string { return proto.CompactTextString(m) }
+func (*PBDNSMessage_DNSResponse) ProtoMessage() {}
+func (*PBDNSMessage_DNSResponse) Descriptor() ([]byte, []int) {
+ return fileDescriptor_c3136ceafbfed9e7, []int{0, 1}
+}
+
+func (m *PBDNSMessage_DNSResponse) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_PBDNSMessage_DNSResponse.Unmarshal(m, b)
+}
+func (m *PBDNSMessage_DNSResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_PBDNSMessage_DNSResponse.Marshal(b, m, deterministic)
+}
+func (m *PBDNSMessage_DNSResponse) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_PBDNSMessage_DNSResponse.Merge(m, src)
+}
+func (m *PBDNSMessage_DNSResponse) XXX_Size() int {
+ return xxx_messageInfo_PBDNSMessage_DNSResponse.Size(m)
+}
+func (m *PBDNSMessage_DNSResponse) XXX_DiscardUnknown() {
+ xxx_messageInfo_PBDNSMessage_DNSResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_PBDNSMessage_DNSResponse proto.InternalMessageInfo
+
+func (m *PBDNSMessage_DNSResponse) GetRcode() uint32 {
+ if m != nil && m.Rcode != nil {
+ return *m.Rcode
+ }
+ return 0
+}
+
+func (m *PBDNSMessage_DNSResponse) GetRrs() []*PBDNSMessage_DNSResponse_DNSRR {
+ if m != nil {
+ return m.Rrs
+ }
+ return nil
+}
+
+func (m *PBDNSMessage_DNSResponse) GetAppliedPolicy() string {
+ if m != nil && m.AppliedPolicy != nil {
+ return *m.AppliedPolicy
+ }
+ return ""
+}
+
+func (m *PBDNSMessage_DNSResponse) GetTags() []string {
+ if m != nil {
+ return m.Tags
+ }
+ return nil
+}
+
+func (m *PBDNSMessage_DNSResponse) GetQueryTimeSec() uint32 {
+ if m != nil && m.QueryTimeSec != nil {
+ return *m.QueryTimeSec
+ }
+ return 0
+}
+
+func (m *PBDNSMessage_DNSResponse) GetQueryTimeUsec() uint32 {
+ if m != nil && m.QueryTimeUsec != nil {
+ return *m.QueryTimeUsec
+ }
+ return 0
+}
+
+func (m *PBDNSMessage_DNSResponse) GetAppliedPolicyType() PBDNSMessage_PolicyType {
+ if m != nil && m.AppliedPolicyType != nil {
+ return *m.AppliedPolicyType
+ }
+ return PBDNSMessage_UNKNOWN
+}
+
+func (m *PBDNSMessage_DNSResponse) GetAppliedPolicyTrigger() string {
+ if m != nil && m.AppliedPolicyTrigger != nil {
+ return *m.AppliedPolicyTrigger
+ }
+ return ""
+}
+
+func (m *PBDNSMessage_DNSResponse) GetAppliedPolicyHit() string {
+ if m != nil && m.AppliedPolicyHit != nil {
+ return *m.AppliedPolicyHit
+ }
+ return ""
+}
+
+// See exportTypes in https://docs.powerdns.com/recursor/lua-config/protobuf.html#protobufServer
+// for the list of supported resource record types.
+type PBDNSMessage_DNSResponse_DNSRR struct {
+ Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+ Type *uint32 `protobuf:"varint,2,opt,name=type" json:"type,omitempty"`
+ Class *uint32 `protobuf:"varint,3,opt,name=class" json:"class,omitempty"`
+ Ttl *uint32 `protobuf:"varint,4,opt,name=ttl" json:"ttl,omitempty"`
+ Rdata []byte `protobuf:"bytes,5,opt,name=rdata" json:"rdata,omitempty"`
+ Udr *bool `protobuf:"varint,6,opt,name=udr" json:"udr,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *PBDNSMessage_DNSResponse_DNSRR) Reset() { *m = PBDNSMessage_DNSResponse_DNSRR{} }
+func (m *PBDNSMessage_DNSResponse_DNSRR) String() string { return proto.CompactTextString(m) }
+func (*PBDNSMessage_DNSResponse_DNSRR) ProtoMessage() {}
+func (*PBDNSMessage_DNSResponse_DNSRR) Descriptor() ([]byte, []int) {
+ return fileDescriptor_c3136ceafbfed9e7, []int{0, 1, 0}
+}
+
+func (m *PBDNSMessage_DNSResponse_DNSRR) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_PBDNSMessage_DNSResponse_DNSRR.Unmarshal(m, b)
+}
+func (m *PBDNSMessage_DNSResponse_DNSRR) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_PBDNSMessage_DNSResponse_DNSRR.Marshal(b, m, deterministic)
+}
+func (m *PBDNSMessage_DNSResponse_DNSRR) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_PBDNSMessage_DNSResponse_DNSRR.Merge(m, src)
+}
+func (m *PBDNSMessage_DNSResponse_DNSRR) XXX_Size() int {
+ return xxx_messageInfo_PBDNSMessage_DNSResponse_DNSRR.Size(m)
+}
+func (m *PBDNSMessage_DNSResponse_DNSRR) XXX_DiscardUnknown() {
+ xxx_messageInfo_PBDNSMessage_DNSResponse_DNSRR.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_PBDNSMessage_DNSResponse_DNSRR proto.InternalMessageInfo
+
+func (m *PBDNSMessage_DNSResponse_DNSRR) GetName() string {
+ if m != nil && m.Name != nil {
+ return *m.Name
+ }
+ return ""
+}
+
+func (m *PBDNSMessage_DNSResponse_DNSRR) GetType() uint32 {
+ if m != nil && m.Type != nil {
+ return *m.Type
+ }
+ return 0
+}
+
+func (m *PBDNSMessage_DNSResponse_DNSRR) GetClass() uint32 {
+ if m != nil && m.Class != nil {
+ return *m.Class
+ }
+ return 0
+}
+
+func (m *PBDNSMessage_DNSResponse_DNSRR) GetTtl() uint32 {
+ if m != nil && m.Ttl != nil {
+ return *m.Ttl
+ }
+ return 0
+}
+
+func (m *PBDNSMessage_DNSResponse_DNSRR) GetRdata() []byte {
+ if m != nil {
+ return m.Rdata
+ }
+ return nil
+}
+
+func (m *PBDNSMessage_DNSResponse_DNSRR) GetUdr() bool {
+ if m != nil && m.Udr != nil {
+ return *m.Udr
+ }
+ return false
+}
+
+type PBDNSMessageList struct {
+ Msg []*PBDNSMessage `protobuf:"bytes,1,rep,name=msg" json:"msg,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *PBDNSMessageList) Reset() { *m = PBDNSMessageList{} }
+func (m *PBDNSMessageList) String() string { return proto.CompactTextString(m) }
+func (*PBDNSMessageList) ProtoMessage() {}
+func (*PBDNSMessageList) Descriptor() ([]byte, []int) {
+ return fileDescriptor_c3136ceafbfed9e7, []int{1}
+}
+
+func (m *PBDNSMessageList) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_PBDNSMessageList.Unmarshal(m, b)
+}
+func (m *PBDNSMessageList) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_PBDNSMessageList.Marshal(b, m, deterministic)
+}
+func (m *PBDNSMessageList) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_PBDNSMessageList.Merge(m, src)
+}
+func (m *PBDNSMessageList) XXX_Size() int {
+ return xxx_messageInfo_PBDNSMessageList.Size(m)
+}
+func (m *PBDNSMessageList) XXX_DiscardUnknown() {
+ xxx_messageInfo_PBDNSMessageList.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_PBDNSMessageList proto.InternalMessageInfo
+
+func (m *PBDNSMessageList) GetMsg() []*PBDNSMessage {
+ if m != nil {
+ return m.Msg
+ }
+ return nil
+}
+
+func init() {
+ proto.RegisterEnum("PBDNSMessage_Type", PBDNSMessage_Type_name, PBDNSMessage_Type_value)
+ proto.RegisterEnum("PBDNSMessage_SocketFamily", PBDNSMessage_SocketFamily_name, PBDNSMessage_SocketFamily_value)
+ proto.RegisterEnum("PBDNSMessage_SocketProtocol", PBDNSMessage_SocketProtocol_name, PBDNSMessage_SocketProtocol_value)
+ proto.RegisterEnum("PBDNSMessage_PolicyType", PBDNSMessage_PolicyType_name, PBDNSMessage_PolicyType_value)
+ proto.RegisterType((*PBDNSMessage)(nil), "PBDNSMessage")
+ proto.RegisterType((*PBDNSMessage_DNSQuestion)(nil), "PBDNSMessage.DNSQuestion")
+ proto.RegisterType((*PBDNSMessage_DNSResponse)(nil), "PBDNSMessage.DNSResponse")
+ proto.RegisterType((*PBDNSMessage_DNSResponse_DNSRR)(nil), "PBDNSMessage.DNSResponse.DNSRR")
+ proto.RegisterType((*PBDNSMessageList)(nil), "PBDNSMessageList")
+}
+
+func init() {
+ proto.RegisterFile("dnsmessage.proto", fileDescriptor_c3136ceafbfed9e7)
+}
+
+var fileDescriptor_c3136ceafbfed9e7 = []byte{
+ // 836 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x54, 0xdd, 0x8f, 0xdb, 0xc4,
+ 0x17, 0x95, 0x3f, 0xb2, 0x49, 0x6e, 0xec, 0xd4, 0x9d, 0xcd, 0xef, 0xd7, 0x21, 0x54, 0xd4, 0x0a,
+ 0xa8, 0xb2, 0x78, 0x58, 0x41, 0x10, 0x88, 0x27, 0x24, 0xba, 0x49, 0x85, 0x45, 0xeb, 0xf5, 0x8e,
+ 0xb3, 0x42, 0x3c, 0xba, 0xf6, 0x60, 0x8d, 0x48, 0x3c, 0x59, 0x7b, 0x52, 0x94, 0x27, 0x84, 0xf8,
+ 0xc7, 0xd1, 0x5c, 0xe7, 0xc3, 0xee, 0xee, 0xbe, 0xdd, 0x73, 0xee, 0xb9, 0xc7, 0x9e, 0x7b, 0xef,
+ 0x0c, 0x78, 0x79, 0x59, 0x6f, 0x78, 0x5d, 0xa7, 0x05, 0xbf, 0xda, 0x56, 0x52, 0xc9, 0xd9, 0x3f,
+ 0x2e, 0x38, 0xf1, 0x9b, 0x45, 0x94, 0xbc, 0x6f, 0x68, 0xf2, 0x1a, 0x6c, 0xb5, 0xdf, 0x72, 0x6a,
+ 0xf8, 0x66, 0x30, 0x9e, 0x93, 0xab, 0x76, 0xf2, 0x6a, 0xb5, 0xdf, 0x72, 0x86, 0x79, 0xf2, 0x12,
+ 0x86, 0x07, 0xa7, 0x30, 0xa7, 0xa6, 0x6f, 0x04, 0x0e, 0x3b, 0x13, 0xe4, 0x35, 0x8c, 0x6b, 0x5e,
+ 0x7d, 0xe4, 0x55, 0x98, 0xf3, 0x52, 0x09, 0xb5, 0xa7, 0x16, 0x4a, 0x3e, 0x61, 0xc9, 0x4f, 0xe0,
+ 0xd4, 0x32, 0xfb, 0x93, 0xab, 0xb7, 0xe9, 0x46, 0xac, 0xf7, 0xd4, 0xf6, 0x8d, 0x60, 0x3c, 0x9f,
+ 0x76, 0xbf, 0x9a, 0xb4, 0x14, 0xac, 0xa3, 0x27, 0x0b, 0x18, 0x37, 0x38, 0xd6, 0xa7, 0xc9, 0xe4,
+ 0x9a, 0xf6, 0xd0, 0xe1, 0xe5, 0x63, 0x0e, 0x47, 0x0d, 0xfb, 0xa4, 0x86, 0x10, 0xb0, 0xff, 0xa8,
+ 0xe4, 0x86, 0x5e, 0xe0, 0x3f, 0x62, 0x4c, 0xc6, 0x60, 0x2a, 0x49, 0xfb, 0xc8, 0x98, 0x4a, 0x12,
+ 0x0a, 0x7d, 0x51, 0xbe, 0xd9, 0x2b, 0x5e, 0xd3, 0x81, 0x6f, 0x04, 0x36, 0x3b, 0x42, 0x9d, 0x51,
+ 0x62, 0xc3, 0x13, 0x9e, 0xd1, 0xa1, 0x6f, 0x04, 0x2e, 0x3b, 0x42, 0x32, 0x85, 0x81, 0x0e, 0xef,
+ 0x6a, 0x9e, 0x51, 0xc0, 0xd4, 0x09, 0x6b, 0x7f, 0x91, 0xd3, 0x11, 0xb2, 0xa6, 0xc8, 0xc9, 0xf7,
+ 0x30, 0xb8, 0xdf, 0xf1, 0x5a, 0x09, 0x59, 0x52, 0xc7, 0x37, 0x82, 0xd1, 0xfc, 0xb3, 0xee, 0x19,
+ 0x16, 0x51, 0x72, 0x7b, 0x10, 0xb0, 0x93, 0x54, 0x97, 0x55, 0xbc, 0xde, 0xca, 0xb2, 0xe6, 0xd4,
+ 0x7d, 0xa2, 0x8c, 0x1d, 0x04, 0xec, 0x24, 0x25, 0x3f, 0xc2, 0x0b, 0x59, 0x89, 0x42, 0x94, 0xe9,
+ 0x9a, 0x71, 0x34, 0x93, 0x55, 0xb2, 0xfb, 0x50, 0x72, 0x45, 0xc7, 0x78, 0xe4, 0xa7, 0xd2, 0xc4,
+ 0x87, 0x51, 0x75, 0xa4, 0xc2, 0x9c, 0x3e, 0xf3, 0x8d, 0x60, 0xc8, 0xda, 0x14, 0xf9, 0x1a, 0x3c,
+ 0x51, 0x0a, 0x25, 0x4e, 0xb5, 0x61, 0x4e, 0x3d, 0x34, 0x7d, 0xc0, 0xeb, 0x0e, 0xe5, 0xfc, 0xa3,
+ 0xc8, 0xf4, 0x12, 0x3d, 0x47, 0xcd, 0x09, 0x93, 0x6f, 0xe0, 0xb2, 0xe4, 0x7f, 0xad, 0xf7, 0x37,
+ 0x1f, 0x70, 0x69, 0xf2, 0x85, 0xdc, 0xa4, 0xa2, 0xa4, 0xc4, 0x37, 0x82, 0x01, 0x7b, 0x2c, 0x45,
+ 0xbe, 0x00, 0x68, 0xaa, 0xa3, 0x74, 0xc3, 0xe9, 0x25, 0xfe, 0x5a, 0x8b, 0xd1, 0x5f, 0xd3, 0xb3,
+ 0x8d, 0x65, 0xa5, 0xe8, 0xa4, 0x99, 0xc7, 0x11, 0x93, 0xff, 0xc3, 0x85, 0x92, 0x98, 0xf9, 0x1f,
+ 0x66, 0x0e, 0x68, 0x7a, 0x0b, 0xa3, 0x56, 0xe7, 0xc9, 0x04, 0x7a, 0xf7, 0xe8, 0x6e, 0xa0, 0x7b,
+ 0x03, 0x90, 0xd5, 0x77, 0x03, 0x2f, 0x82, 0xcb, 0x1a, 0xa0, 0x2d, 0xef, 0xaf, 0xd7, 0x69, 0x5d,
+ 0xe3, 0xf2, 0xbb, 0xec, 0x80, 0xa6, 0xff, 0xda, 0xe8, 0x79, 0x1c, 0x8b, 0xae, 0xae, 0x32, 0x99,
+ 0x37, 0x9e, 0x2e, 0x6b, 0x00, 0xf9, 0x16, 0xac, 0xaa, 0xaa, 0xa9, 0xe9, 0x5b, 0xc1, 0x68, 0xfe,
+ 0xea, 0xc9, 0xa1, 0x62, 0xcc, 0x98, 0xd6, 0x92, 0xaf, 0xc0, 0x4d, 0xb7, 0xdb, 0xb5, 0xe0, 0x79,
+ 0x2c, 0xd7, 0x22, 0x6b, 0x2e, 0xdd, 0x90, 0x75, 0x49, 0xbd, 0xed, 0x2a, 0x2d, 0x6a, 0x6a, 0xfb,
+ 0x56, 0x30, 0x64, 0x18, 0x93, 0x19, 0x38, 0xf7, 0x3b, 0x5e, 0xed, 0x57, 0x87, 0x45, 0xee, 0xe1,
+ 0x9f, 0x74, 0x38, 0xed, 0x7e, 0xc2, 0xb8, 0xd2, 0x17, 0x28, 0xea, 0x92, 0xe4, 0x2d, 0x3c, 0xef,
+ 0x7c, 0x0e, 0xdb, 0xd2, 0xc7, 0x4b, 0x49, 0xbb, 0x87, 0x38, 0xe7, 0xd9, 0xc3, 0x12, 0x32, 0x87,
+ 0x49, 0x97, 0xac, 0x44, 0x51, 0xf0, 0x0a, 0x2f, 0xdf, 0x90, 0x3d, 0x9a, 0xd3, 0x9b, 0xd7, 0xe1,
+ 0x7f, 0x11, 0x0a, 0xaf, 0xe4, 0x90, 0x3d, 0xe0, 0xa7, 0x7f, 0x43, 0x0f, 0x3b, 0xa7, 0xdb, 0x51,
+ 0x9e, 0x07, 0x8a, 0x31, 0xb6, 0xe8, 0x3c, 0xce, 0xe6, 0xc1, 0x9b, 0x40, 0x2f, 0x6b, 0x0d, 0xb3,
+ 0x01, 0xc4, 0x03, 0x4b, 0xa9, 0x35, 0xbe, 0x5b, 0x2e, 0xd3, 0x21, 0x4e, 0x33, 0x4f, 0x55, 0x8a,
+ 0x3d, 0x74, 0x58, 0x03, 0xb4, 0x6e, 0x97, 0x57, 0xd8, 0xb2, 0x01, 0xd3, 0xe1, 0x2c, 0x07, 0x1b,
+ 0x0f, 0xea, 0x81, 0xd3, 0x2c, 0x58, 0x85, 0x07, 0xf7, 0x0c, 0x72, 0x09, 0xcf, 0x5a, 0x03, 0x46,
+ 0xd2, 0x24, 0x14, 0x26, 0x8b, 0x28, 0xb9, 0xd9, 0xa9, 0x42, 0x8a, 0xb2, 0x38, 0xcb, 0x2d, 0xf2,
+ 0x39, 0xbc, 0x58, 0x44, 0x49, 0x58, 0x66, 0x72, 0x23, 0xca, 0xa2, 0x53, 0x66, 0xcf, 0xbe, 0x04,
+ 0xa7, 0xfd, 0x7c, 0x92, 0x01, 0xd8, 0x61, 0xb4, 0x5c, 0x79, 0x06, 0x19, 0x42, 0x4f, 0x47, 0x3f,
+ 0x78, 0xe6, 0x6c, 0x06, 0xe3, 0xee, 0x0b, 0x49, 0xfa, 0x60, 0xdd, 0x2d, 0x62, 0xcf, 0xd0, 0xc1,
+ 0xea, 0x3a, 0xf6, 0xcc, 0xd9, 0xef, 0x00, 0xad, 0xe9, 0x8c, 0xa0, 0x7f, 0x17, 0xfd, 0x1a, 0xdd,
+ 0xfc, 0x16, 0x35, 0x4e, 0xb7, 0xd1, 0xcf, 0xef, 0x97, 0x9e, 0x49, 0x1c, 0x18, 0x5c, 0xbf, 0x0b,
+ 0x97, 0xd1, 0x2a, 0x8c, 0x3d, 0x8b, 0x8c, 0x01, 0xd8, 0x32, 0x89, 0x6f, 0xa2, 0x64, 0x19, 0xc6,
+ 0x9e, 0xad, 0xab, 0xa2, 0x64, 0x81, 0xd2, 0x9e, 0xfe, 0x93, 0x28, 0x09, 0x63, 0xef, 0x62, 0xf6,
+ 0x1d, 0x78, 0xed, 0xc5, 0x78, 0x27, 0x6a, 0x45, 0x5e, 0x81, 0xb5, 0xa9, 0x0b, 0x6a, 0xe0, 0xf6,
+ 0xbb, 0x9d, 0xc5, 0x61, 0x3a, 0xf3, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x3d, 0x56, 0xce, 0x98,
+ 0xcb, 0x06, 0x00, 0x00,
+}
diff --git a/protobuf/dnsmessage.proto b/protobuf/dnsmessage.proto
new file mode 100644
index 0000000..c75e67e
--- /dev/null
+++ b/protobuf/dnsmessage.proto
@@ -0,0 +1,105 @@
+/*
+ * This file describes the message format used by the protobuf logging feature in PowerDNS and dnsdist.
+ *
+ * MIT License
+ *
+ * Copyright (c) 2016-now PowerDNS.COM B.V. and its contributors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+syntax = "proto2";
+
+message PBDNSMessage {
+ enum Type {
+ DNSQueryType = 1; // Query received by the service
+ DNSResponseType = 2; // Response returned by the service
+ DNSOutgoingQueryType = 3; // Query sent out by the service to a remote server
+ DNSIncomingResponseType = 4; // Response returned by the remote server
+ }
+ enum SocketFamily {
+ INET = 1; // IPv4 (RFC 791)
+ INET6 = 2; // IPv6 (RFC 2460)
+ }
+ enum SocketProtocol {
+ UDP = 1; // User Datagram Protocol (RFC 768)
+ TCP = 2; // Transmission Control Protocol (RFC 793)
+ }
+ enum PolicyType {
+ UNKNOWN = 1; // No RPZ policy applied, or unknown type
+ QNAME = 2; // Policy matched on the QName
+ CLIENTIP = 3; // Policy matched on the client IP
+ RESPONSEIP = 4; // Policy matched on one of the IPs contained in the answer
+ NSDNAME = 5; // Policy matched on the name of one nameserver involved
+ NSIP = 6; // Policy matched on the IP of one nameserver involved
+ }
+ required Type type = 1; // Type of event
+ optional bytes messageId = 2; // UUID, shared by the query and the response
+ optional bytes serverIdentity = 3; // ID of the server emitting the protobuf message
+ optional SocketFamily socketFamily = 4;
+ optional SocketProtocol socketProtocol = 5;
+ optional bytes from = 6; // DNS requestor (client) as 4 (IPv4) or 16 (IPv6) raw bytes in network byte order
+ optional bytes to = 7; // DNS responder (server) as 4 (IPv4) or 16 (IPv6) raw bytes in network byte order
+ optional uint64 inBytes = 8; // Size of the query or response on the wire
+ optional uint32 timeSec = 9; // Time of message reception (seconds since epoch)
+ optional uint32 timeUsec = 10; // Time of message reception (additional micro-seconds)
+ optional uint32 id = 11; // ID of the query/response as found in the DNS header
+
+ message DNSQuestion {
+ optional string qName = 1; // Fully qualified DNS name (with trailing dot)
+ optional uint32 qType = 2; // https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4
+ optional uint32 qClass = 3; // Typically 1 (IN), see https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-2
+ }
+ optional DNSQuestion question = 12; // DNS query received from client
+
+ message DNSResponse {
+ // See exportTypes in https://docs.powerdns.com/recursor/lua-config/protobuf.html#protobufServer
+ // for the list of supported resource record types.
+ message DNSRR {
+ optional string name = 1; // Fully qualified DNS name (with trailing dot)
+ optional uint32 type = 2; // https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4
+ optional uint32 class = 3; // Typically 1 (IN), see https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-2
+ optional uint32 ttl = 4; // TTL in seconds
+ optional bytes rdata = 5; // raw address bytes in network byte order for A & AAAA; text representation for others, with fully qualified (trailing dot) domain names
+ optional bool udr = 6; // True if this is the first time this RR has been seen for this question
+ }
+ optional uint32 rcode = 1; // DNS Response code, or 65536 for a network error including a timeout
+ repeated DNSRR rrs = 2; // DNS resource records in response
+ optional string appliedPolicy = 3; // Filtering policy (RPZ or Lua) applied
+ repeated string tags = 4; // Additional tags applied
+ optional uint32 queryTimeSec = 5; // Time of the corresponding query reception (seconds since epoch)
+ optional uint32 queryTimeUsec = 6; // Time of the corresponding query reception (additional micro-seconds)
+ optional PolicyType appliedPolicyType = 7; // Type of the filtering policy (RPZ or Lua) applied
+ optional string appliedPolicyTrigger = 8; // The RPZ trigger
+ optional string appliedPolicyHit = 9; // The value (qname or IP) that caused the hit
+ }
+
+ optional DNSResponse response = 13;
+ optional bytes originalRequestorSubnet = 14; // EDNS Client Subnet value (4 or 16 raw bytes in network byte order)
+ optional string requestorId = 15; // Username of the requestor
+ optional bytes initialRequestId = 16; // UUID of the incoming query that initiated this outgoing query or incoming response
+ optional bytes deviceId = 17; // Device ID of the requestor (could be mac address IP address or e.g. IMEI, format implementation dependent)
+ optional bool newlyObservedDomain = 18; // True if the domain has not been seen before
+ optional string deviceName = 19; // Device name of the requestor
+ optional uint32 fromPort = 20; // Source port of the DNS query (client)
+ optional uint32 toPort = 21; // Destination port of the DNS query (server)
+}
+
+message PBDNSMessageList {
+ repeated PBDNSMessage msg = 1;
+}
diff --git a/protobuf/dnstap.proto b/protobuf/dnstap.proto
new file mode 100644
index 0000000..3504d99
--- /dev/null
+++ b/protobuf/dnstap.proto
@@ -0,0 +1,262 @@
+// dnstap: flexible, structured event replication format for DNS software
+//
+// This file contains the protobuf schemas for the "dnstap" structured event
+// replication format for DNS software.
+
+// Written in 2013-2014 by Farsight Security, Inc.
+//
+// To the extent possible under law, the author(s) have dedicated all
+// copyright and related and neighboring rights to this file to the public
+// domain worldwide. This file is distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along
+// with this file. If not, see:
+//
+// <http://creativecommons.org/publicdomain/zero/1.0/>.
+
+package dnstap;
+
+// "Dnstap": this is the top-level dnstap type, which is a "union" type that
+// contains other kinds of dnstap payloads, although currently only one type
+// of dnstap payload is defined.
+// See: https://developers.google.com/protocol-buffers/docs/techniques#union
+message Dnstap {
+ // DNS server identity.
+ // If enabled, this is the identity string of the DNS server which generated
+ // this message. Typically this would be the same string as returned by an
+ // "NSID" (RFC 5001) query.
+ optional bytes identity = 1;
+
+ // DNS server version.
+ // If enabled, this is the version string of the DNS server which generated
+ // this message. Typically this would be the same string as returned by a
+ // "version.bind" query.
+ optional bytes version = 2;
+
+ // Extra data for this payload.
+ // This field can be used for adding an arbitrary byte-string annotation to
+ // the payload. No encoding or interpretation is applied or enforced.
+ optional bytes extra = 3;
+
+ // Identifies which field below is filled in.
+ enum Type {
+ MESSAGE = 1;
+ }
+ required Type type = 15;
+
+ // One of the following will be filled in.
+ optional Message message = 14;
+}
+
+// SocketFamily: the network protocol family of a socket. This specifies how
+// to interpret "network address" fields.
+enum SocketFamily {
+ INET = 1; // IPv4 (RFC 791)
+ INET6 = 2; // IPv6 (RFC 2460)
+}
+
+// SocketProtocol: the transport protocol of a socket. This specifies how to
+// interpret "transport port" fields.
+enum SocketProtocol {
+ UDP = 1; // User Datagram Protocol (RFC 768)
+ TCP = 2; // Transmission Control Protocol (RFC 793)
+}
+
+// Message: a wire-format (RFC 1035 section 4) DNS message and associated
+// metadata. Applications generating "Message" payloads should follow
+// certain requirements based on the MessageType, see below.
+message Message {
+
+ // There are eight types of "Message" defined that correspond to the
+ // four arrows in the following diagram, slightly modified from RFC 1035
+ // section 2:
+
+ // +---------+ +----------+ +--------+
+ // | | query | | query | |
+ // | Stub |-SQ--------CQ->| Recursive|-RQ----AQ->| Auth. |
+ // | Resolver| | Server | | Name |
+ // | |<-SR--------CR-| |<-RR----AR-| Server |
+ // +---------+ response | | response | |
+ // +----------+ +--------+
+
+ // Each arrow has two Type values each, one for each "end" of each arrow,
+ // because these are considered to be distinct events. Each end of each
+ // arrow on the diagram above has been marked with a two-letter Type
+ // mnemonic. Clockwise from upper left, these mnemonic values are:
+ //
+ // SQ: STUB_QUERY
+ // CQ: CLIENT_QUERY
+ // RQ: RESOLVER_QUERY
+ // AQ: AUTH_QUERY
+ // AR: AUTH_RESPONSE
+ // RR: RESOLVER_RESPONSE
+ // CR: CLIENT_RESPONSE
+ // SR: STUB_RESPONSE
+
+ // Two additional types of "Message" have been defined for the
+ // "forwarding" case where an upstream DNS server is responsible for
+ // further recursion. These are not shown on the diagram above, but have
+ // the following mnemonic values:
+
+ // FQ: FORWARDER_QUERY
+ // FR: FORWARDER_RESPONSE
+
+ // The "Message" Type values are defined below.
+
+ enum Type {
+ // AUTH_QUERY is a DNS query message received from a resolver by an
+ // authoritative name server, from the perspective of the authorative
+ // name server.
+ AUTH_QUERY = 1;
+
+ // AUTH_RESPONSE is a DNS response message sent from an authoritative
+ // name server to a resolver, from the perspective of the authoritative
+ // name server.
+ AUTH_RESPONSE = 2;
+
+ // RESOLVER_QUERY is a DNS query message sent from a resolver to an
+ // authoritative name server, from the perspective of the resolver.
+ // Resolvers typically clear the RD (recursion desired) bit when
+ // sending queries.
+ RESOLVER_QUERY = 3;
+
+ // RESOLVER_RESPONSE is a DNS response message received from an
+ // authoritative name server by a resolver, from the perspective of
+ // the resolver.
+ RESOLVER_RESPONSE = 4;
+
+ // CLIENT_QUERY is a DNS query message sent from a client to a DNS
+ // server which is expected to perform further recursion, from the
+ // perspective of the DNS server. The client may be a stub resolver or
+ // forwarder or some other type of software which typically sets the RD
+ // (recursion desired) bit when querying the DNS server. The DNS server
+ // may be a simple forwarding proxy or it may be a full recursive
+ // resolver.
+ CLIENT_QUERY = 5;
+
+ // CLIENT_RESPONSE is a DNS response message sent from a DNS server to
+ // a client, from the perspective of the DNS server. The DNS server
+ // typically sets the RA (recursion available) bit when responding.
+ CLIENT_RESPONSE = 6;
+
+ // FORWARDER_QUERY is a DNS query message sent from a downstream DNS
+ // server to an upstream DNS server which is expected to perform
+ // further recursion, from the perspective of the downstream DNS
+ // server.
+ FORWARDER_QUERY = 7;
+
+ // FORWARDER_RESPONSE is a DNS response message sent from an upstream
+ // DNS server performing recursion to a downstream DNS server, from the
+ // perspective of the downstream DNS server.
+ FORWARDER_RESPONSE = 8;
+
+ // STUB_QUERY is a DNS query message sent from a stub resolver to a DNS
+ // server, from the perspective of the stub resolver.
+ STUB_QUERY = 9;
+
+ // STUB_RESPONSE is a DNS response message sent from a DNS server to a
+ // stub resolver, from the perspective of the stub resolver.
+ STUB_RESPONSE = 10;
+ }
+
+ // One of the Type values described above.
+ required Type type = 1;
+
+ // One of the SocketFamily values described above.
+ optional SocketFamily socket_family = 2;
+
+ // One of the SocketProtocol values described above.
+ optional SocketProtocol socket_protocol = 3;
+
+ // The network address of the message initiator.
+ // For SocketFamily INET, this field is 4 octets (IPv4 address).
+ // For SocketFamily INET6, this field is 16 octets (IPv6 address).
+ optional bytes query_address = 4;
+
+ // The network address of the message responder.
+ // For SocketFamily INET, this field is 4 octets (IPv4 address).
+ // For SocketFamily INET6, this field is 16 octets (IPv6 address).
+ optional bytes response_address = 5;
+
+ // The transport port of the message initiator.
+ // This is a 16-bit UDP or TCP port number, depending on SocketProtocol.
+ optional uint32 query_port = 6;
+
+ // The transport port of the message responder.
+ // This is a 16-bit UDP or TCP port number, depending on SocketProtocol.
+ optional uint32 response_port = 7;
+
+ // The time at which the DNS query message was sent or received, depending
+ // on whether this is an AUTH_QUERY, RESOLVER_QUERY, or CLIENT_QUERY.
+ // This is the number of seconds since the UNIX epoch.
+ optional uint64 query_time_sec = 8;
+
+ // The time at which the DNS query message was sent or received.
+ // This is the seconds fraction, expressed as a count of nanoseconds.
+ optional fixed32 query_time_nsec = 9;
+
+ // The initiator's original wire-format DNS query message, verbatim.
+ optional bytes query_message = 10;
+
+ // The "zone" or "bailiwick" pertaining to the DNS query message.
+ // This is a wire-format DNS domain name.
+ optional bytes query_zone = 11;
+
+ // The time at which the DNS response message was sent or received,
+ // depending on whether this is an AUTH_RESPONSE, RESOLVER_RESPONSE, or
+ // CLIENT_RESPONSE.
+ // This is the number of seconds since the UNIX epoch.
+ optional uint64 response_time_sec = 12;
+
+ // The time at which the DNS response message was sent or received.
+ // This is the seconds fraction, expressed as a count of nanoseconds.
+ optional fixed32 response_time_nsec = 13;
+
+ // The responder's original wire-format DNS response message, verbatim.
+ optional bytes response_message = 14;
+}
+
+// All fields except for 'type' in the Message schema are optional.
+// It is recommended that at least the following fields be filled in for
+// particular types of Messages.
+
+// AUTH_QUERY:
+// socket_family, socket_protocol
+// query_address, query_port
+// query_message
+// query_time_sec, query_time_nsec
+
+// AUTH_RESPONSE:
+// socket_family, socket_protocol
+// query_address, query_port
+// query_time_sec, query_time_nsec
+// response_message
+// response_time_sec, response_time_nsec
+
+// RESOLVER_QUERY:
+// socket_family, socket_protocol
+// query_name, query_type, query_class
+// query_message
+// query_time_sec, query_time_nsec
+// query_zone
+// response_address, response_port
+
+// RESOLVER_RESPONSE:
+// socket_family, socket_protocol
+// query_name, query_type, query_class
+// query_time_sec, query_time_nsec
+// query_zone
+// response_address, response_port
+// response_message
+// response_time_sec, response_time_nsec
+
+// CLIENT_QUERY:
+// socket_family, socket_protocol
+// query_message
+// query_time_sec, query_time_nsec
+
+// CLIENT_RESPONSE:
+// socket_family, socket_protocol
+// query_time_sec, query_time_nsec
+// response_message
+// response_time_sec, response_time_nsec
diff --git a/rtnetlink.go b/rtnetlink.go
index 46898b5..d4449c0 100644
--- a/rtnetlink.go
+++ b/rtnetlink.go
@@ -10,7 +10,7 @@ func Example_listLink() {
// Dial a connection to the rtnetlink socket
conn, err := rtnetlink.Dial(nil)
if err != nil {
- log.Println(logError, "Example_listLink() failed", err)
+ debug(LogError, "Example_listLink() failed", err)
return
}
defer conn.Close()
diff --git a/run.go b/run.go
deleted file mode 100644
index 510007e..0000000
--- a/run.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package main
-
-import (
- "bytes"
- "fmt"
- "strings"
- "os/exec"
-
- "git.wit.org/wit/shell"
-)
-
-func run(s string) string {
- cmdArgs := strings.Fields(s)
- // Define the command you want to run
- // cmd := exec.Command(cmdArgs)
- cmd := exec.Command(cmdArgs[0], cmdArgs[1:len(cmdArgs)]...)
-
- // Create a buffer to capture the output
- var out bytes.Buffer
-
- // Set the output of the command to the buffer
- cmd.Stdout = &out
-
- // Run the command
- err := cmd.Run()
- if err != nil {
- fmt.Println("Error running command:", err)
- return ""
- }
-
- tmp := shell.Chomp(out.String())
- // Output the results
- fmt.Println("Command Output:", tmp)
-
- return tmp
-}
-
diff --git a/structs.go b/structs.go
index c655738..9671078 100644
--- a/structs.go
+++ b/structs.go
@@ -3,7 +3,9 @@ package main
import (
"net"
+ "time"
"git.wit.org/wit/gui"
+ "github.com/miekg/dns"
)
// It's probably a terrible idea to call this 'me'
@@ -11,13 +13,18 @@ var me Host
type Host struct {
hostname string // mirrors
- domainname string // kernel.org
+ domainname *gui.Node // kernel.org
+ hostshort *gui.Node // hostname -s
+ hostnameStatus *gui.Node // is the hostname configured correctly in the OS?
// fqdn string // mirrors.kernel.org
- dnsTTL int `default:3` // Recheck DNS is working every TTL (in seconds)
+ dnsTTL int `default:"3"` // Recheck DNS is working every TTL (in seconds)
dnsTTLsleep float64 // sleep between loops
- artificialSleep float64 `default:0.7` // artificial sleep on startup
- artificialS string `default:0.7` // artificial sleep on startup
+ artificialSleep float64 `default:"0.7"` // artificial sleep on startup
+ artificialS string `default:"abc"` // artificial sleep on startup
+
+ dnsSleep time.Duration
+ localSleep time.Duration
changed bool // set to true if things changed
user string // name of the user
@@ -25,19 +32,43 @@ type Host struct {
ipmap map[string]*IPtype // the current ip addresses
dnsmap map[string]*IPtype // the current dns addresses
ifmap map[int]*IFtype // the current interfaces
+ nsmap map[string]string // the NS records
+
+ // DNS A and AAAA results
+ ipv4s map[string]dns.RR
+ ipv6s map[string]dns.RR
window *gui.Node // the main window
tab *gui.Node // the main dns tab
notes *gui.Node // using this to put notes here
+
+ // local OS settings, network interfaces, etc
uid *gui.Node // user
fqdn *gui.Node // display the full hostname
IPv4 *gui.Node // show valid IPv4 addresses
IPv6 *gui.Node // show valid IPv6 addresses
Interfaces *gui.Node // Interfaces
+ LocalSpeedActual *gui.Node // the time it takes to check each network interface
+
+ // DNS stuff
+ NSrr *gui.Node // NS resource records for the domain name
+ DnsAPI *gui.Node // what DNS API to use?
DnsAAAA *gui.Node // the actual DNS AAAA results
DnsA *gui.Node // the actual DNS A results (ignore for status since mostly never happens?)
DnsStatus *gui.Node // the current state of DNS
+ DnsSpeed *gui.Node // 'FAST', 'OK', 'SLOW', etc
+ DnsSpeedActual *gui.Node // the last actual duration
+ DnsSpeedLast string // the last state 'FAST', 'OK', etc
+
fix *gui.Node // button for the user to click
+ fixProc *gui.Node // button for the user to click
+
+ mainStatus *gui.Node // group for the main display of stuff
+ cloudflareB *gui.Node // cloudflare button
+
+ dbOn *gui.Node // button for setting debugging on
+ dbNet *gui.Node // button for setting network debugging on
+ dbProc *gui.Node // button for setting proc debugging on
}
type IPtype struct {
diff --git a/unix.go b/unix.go
index c9b5cc0..e9776ae 100644
--- a/unix.go
+++ b/unix.go
@@ -9,6 +9,11 @@ import (
"os"
"os/exec"
"net"
+ "bytes"
+ "fmt"
+ "strings"
+
+ "git.wit.org/wit/shell"
)
func CheckSuperuser() bool {
@@ -23,7 +28,7 @@ func Escalate() {
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
- log.Println(logError, "exit in Escalate()")
+ debug(LogError, "exit in Escalate()")
exit(err)
}
}
@@ -45,7 +50,7 @@ func DumpPublicDNSZone(zone string) {
func dumpIPs(host string) {
ips, err := net.LookupIP(host)
if err != nil {
- log.Println(logError, "dumpIPs() failed:", err)
+ debug(LogError, "dumpIPs() failed:", err)
}
for _, ip := range ips {
log.Println(host, ip)
@@ -64,3 +69,29 @@ func ddclient() {
*/
func ddupdate() {
}
+
+func run(s string) string {
+ cmdArgs := strings.Fields(s)
+ // Define the command you want to run
+ // cmd := exec.Command(cmdArgs)
+ cmd := exec.Command(cmdArgs[0], cmdArgs[1:len(cmdArgs)]...)
+
+ // Create a buffer to capture the output
+ var out bytes.Buffer
+
+ // Set the output of the command to the buffer
+ cmd.Stdout = &out
+
+ // Run the command
+ err := cmd.Run()
+ if err != nil {
+ fmt.Println("Error running command:", err)
+ return ""
+ }
+
+ tmp := shell.Chomp(out.String())
+ // Output the results
+ debug(LogExec, "Command Output:", tmp)
+
+ return tmp
+}