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, + . + + [RFC1035] Mockapetris, P., "Domain names - implementation and + specification", STD 13, RFC 1035, DOI 10.17487/RFC1035, + November 1987, . + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, + DOI 10.17487/RFC2119, March 1997, + . + + [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC + 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, + May 2017, . + +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, + . + + [RFC6895] Eastlake 3rd, D., "Domain Name System (DNS) IANA + Considerations", BCP 42, RFC 6895, DOI 10.17487/RFC6895, + April 2013, . + + [RFC8499] Hoffman, P., Sullivan, A., and K. Fujiwara, "DNS + Terminology", BCP 219, RFC 8499, DOI 10.17487/RFC8499, + January 2019, . + + [RR_TYPES] IANA, "Domain Name System (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 + Email: jabley@afilias.info + + + Olafur Gudmundsson + Cloudflare Inc. + + Email: olafur+ietf@cloudflare.com + + + Marek Majkowski + Cloudflare Inc. + + Email: marek@cloudflare.com + + + Evan Hunt + ISC + 950 Charter St + Redwood City, CA 94063 + United States of America + + Email: each@isc.org 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") + newRR = &cloudflare.CFdialog + + me.dnsTTL = 2 // how often to recheck DNS + me.dnsTTLsleep = 0.4 // sleep between loops + + me.dnsSleep = 500 * time.Millisecond + me.localSleep = 100 * time.Millisecond + + 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) - me.artificialSleep = 2.3 - log.Println("init() me.artificialSleep =", me.artificialSleep) sleep(me.artificialSleep) } - -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() - - 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 -} - -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 -} 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() == 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 } - nsupdate() + // 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() + } + */ }) 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") + + gridP.NewLabel("ping6.wit.com =") + gridP.NewLabel("unknown") + + problems.Margin() + problems.Pad() + gridP.Margin() + gridP.Pad() } -/* -var outJunk string -func output(s string, a bool) { - if (a) { - outJunk += s - } else { - outJunk = s - } - me.output.SetText(outJunk) - log.Println(outJunk) -} -*/ - +// 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) + debug(LogError, "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 - } - } - } - 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: +// +// . + +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 +}