├── rude-responder.go ├── Makefile ├── LICENSE ├── test-scapy.py ├── reflector-responder.go ├── as112.go ├── types.go ├── README ├── myflag.go └── server.go /rude-responder.go: -------------------------------------------------------------------------------- 1 | package responder 2 | 3 | import ( 4 | "./types" 5 | ) 6 | 7 | func Respond(query types.DNSquery, config map[string]interface{}) types.DNSresponse { 8 | var ( 9 | result types.DNSresponse 10 | ) 11 | result.Responsecode = types.REFUSED 12 | return result 13 | } 14 | 15 | func Init(firstoption int) { 16 | } 17 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include $(GOROOT)/src/Make.inc 2 | 3 | TARBALL=/tmp/grong.tar.gz 4 | DEFAULTPORT=8053 5 | 6 | all: grong 7 | 8 | test: grong 9 | @echo "Running server on port $(DEFAULTPORT)..." 10 | ./grong -debug=4 -nodaemon -address ":$(DEFAULTPORT)" -servername "grong.dns.test" 11 | 12 | server.$O: responder.$O types.$O myflag.$O 13 | 14 | responder.$O: types.$O 15 | 16 | %.$O: %.go 17 | ${GC} $< 18 | 19 | grong: server.$O 20 | ${LD} -o $@ server.$O 21 | 22 | dist: distclean 23 | (cd ..; tar czvf ${TARBALL} grong/*) 24 | 25 | clean: 26 | rm -f grong *.$O *.a 27 | 28 | distclean: clean 29 | rm -f *~ responder.go -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | [Executive summary: "BSD-3clause" license. Free software. See 2 | ] 3 | 4 | -------------------------------------------------------------------- 5 | Copyright (c) 2009 Stéphane Bortzmeyer 6 | 7 | All rights reserved. 8 | 9 | Redistribution and use in source and binary forms, with or without 10 | modification, are permitted provided that the following conditions 11 | are met: 12 | 1. Redistributions of source code must retain the above copyright 13 | notice, this list of conditions and the following disclaimer. 14 | 2. Redistributions in binary form must reproduce the above copyright 15 | notice, this list of conditions and the following disclaimer in the 16 | documentation and/or other materials provided with the distribution. 17 | 3. The name of the author may not be used to endorse or promote products 18 | derived from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /test-scapy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | try: 4 | from scapy.all import * 5 | except ImportError: # Old Scapy version? 6 | from scapy import * 7 | import sys 8 | import getopt 9 | 10 | dst=None 11 | dport = 53 12 | qname = "www.slashdot.org" 13 | max_fuzzy = 20 14 | 15 | def usage(msg=None): 16 | print >>sys.stderr, "Usage: %s -s server-to-query [-p port-to-use] [-q query]" % sys.argv[0] 17 | if msg is not None: 18 | print >>sys.stderr, msg 19 | 20 | try: 21 | optlist, args = getopt.getopt (sys.argv[1:], "s:p:q:h", 22 | ["server=", "port=", "query", "help"]) 23 | for option, value in optlist: 24 | if option == "--help" or option == "-h": 25 | usage() 26 | sys.exit(0) 27 | elif option == "--server" or option == "-s": 28 | dst = value 29 | elif option == "--query" or option == "-q": 30 | qname = value 31 | elif option == "--port" or option == "-p": 32 | dport = int(value) 33 | else: 34 | # Should never occur, it is trapped by getopt 35 | print >>sys.stderr, "Unknown option %s" % option 36 | usage() 37 | sys.exit(1) 38 | except getopt.error, reason: 39 | usage(reason) 40 | sys.exit(1) 41 | if len(args) != 0: 42 | usage() 43 | sys.exit(1) 44 | if dst is None: 45 | usage() 46 | sys.exit(1) 47 | 48 | if dst == "127.0.0.1" or dst == "::1": # TODO: not sufficient because there are also global addresses that are on the machine 49 | old_setting=conf.L3socket 50 | conf.L3socket=L3RawSocket # http://wiki.spiritofhack.net/index.php/Scapy-erreurs#Je_ne_peux_pas_pinguer_127.0.0.1._Scapy_ne_marche_pas_avec_127.0.0.1_ni_localhost 51 | # Test it (Scapy bug #193 http://trac.secdev.org/scapy/ticket/193) 52 | try: 53 | sr1(p, timeout=0.01) 54 | except NameError: 55 | print >>sys.stderr, "Warning, setting the local link as raw failed (Scapy bug #193)" 56 | conf.L3socket=old_setting 57 | 58 | p = IP(dst=dst)/UDP(sport=RandShort(),dport=dport)/DNS(rd=1,qd=DNSQR(qname=qname)) 59 | 60 | # Send a normal packet. We wait for an answer. 61 | sr1(p) 62 | 63 | # For wrong packets, we do not wait for an answer. 64 | 65 | # Send a packet with a wrong qdcount 66 | p.qdcount = 0 67 | sr1(p, timeout=0.1) 68 | 69 | p.qdcount = 2 70 | sr1(p, timeout=0.1) 71 | 72 | # Modify the length field of a label 73 | s = str(p) 74 | s2 = s[:44] + '\x030' + s[45:] 75 | p2 = IP(s2) 76 | p2[UDP].sport=RandShort() 77 | del p2[UDP].chksum 78 | sr1(p2, timeout=0.5, retry=-2) 79 | 80 | # Truncates the packet 81 | s2 = s[:-4] 82 | p2 = IP(s2) 83 | p2[UDP].sport=RandShort() 84 | del p2[IP].len 85 | del p2[IP].chksum 86 | del p2[UDP].len 87 | del p2[UDP].chksum 88 | sr1(p2, timeout=0.5, retry=-2) 89 | 90 | # Fuzzy testing 91 | f = IP(dst=dst)/UDP(sport=RandShort(),dport=dport)/fuzz(DNS(rd=1,qd=DNSQR(qname=qname))) 92 | for i in range(0,max_fuzzy): 93 | sr1(f, timeout=0.1) 94 | 95 | -------------------------------------------------------------------------------- /reflector-responder.go: -------------------------------------------------------------------------------- 1 | /* A name server which sends back the IP address of its client, the 2 | recursive resolver. When queried for type TXT, it sends back the text 3 | form of the address. When queried for type A (resp. AAAA), it sends 4 | back the IPv4 (resp. v6) address. 5 | 6 | Similar services: whoami.ultradns.net, whoami.akamai.net. Also (but it 7 | is not their normal goal): rs.dns-oarc.net, porttest.dns-oarc.net, 8 | amiopen.openresolvers.org. 9 | 10 | Stephane Bortzmeyer 11 | 12 | */ 13 | 14 | package responder 15 | 16 | import ( 17 | "net" 18 | "reflect" 19 | "./types" 20 | ) 21 | 22 | // Compile-time options 23 | const includesPort = false // If false, sends only the address for TXT queries. 24 | // If true, includes the UDP or TCP port. 25 | // TODO: allow to secify the Qname it must respond to 26 | 27 | func txtRecord(client net.Addr) []byte { 28 | sclient := client.String() 29 | if !includesPort { 30 | tcpAddr, _ := net.ResolveTCPAddr(sclient) 31 | sclient = tcpAddr.IP.String() 32 | } 33 | return types.ToTXT(sclient) 34 | } 35 | 36 | func txtSection(qname string, client net.Addr) (result types.RR) { 37 | result.Name = qname 38 | result.Type = types.TXT 39 | result.Class = types.IN 40 | result.TTL = 0 41 | result.Data = txtRecord(client) 42 | return 43 | } 44 | 45 | func addressSection(qname string, client net.IP) (result types.RR) { 46 | result.Name = qname 47 | result.Type = types.A 48 | result.Class = types.IN 49 | result.TTL = 0 50 | result.Data = client 51 | return 52 | } 53 | 54 | func aaaaSection(qname string, client net.IP) (result types.RR) { 55 | result.Name = qname 56 | result.Type = types.AAAA 57 | result.Class = types.IN 58 | result.TTL = 0 59 | result.Data = client 60 | return 61 | } 62 | 63 | func Respond(query types.DNSquery, config map[string]interface{}) types.DNSresponse { 64 | var ( 65 | result types.DNSresponse 66 | ) 67 | result.Ansection = nil 68 | tcpAddr, _ := net.ResolveTCPAddr(query.Client.String()) 69 | ipaddressV4 := tcpAddr.IP.To4() 70 | zonei, zoneset := config["zonename"] 71 | zone := "" 72 | if zoneset { 73 | zone = reflect.NewValue(zonei).(*reflect.StringValue).Get() 74 | } 75 | switch { 76 | case query.Qclass != types.IN: 77 | result.Responsecode = types.SERVFAIL 78 | case zone != "" && query.Qname != zone: 79 | result.Responsecode = types.SERVFAIL 80 | case query.Qtype == types.A: 81 | result.Responsecode = types.NOERROR 82 | if ipaddressV4 != nil { 83 | ancount := 1 84 | result.Ansection = make([]types.RR, ancount) 85 | result.Ansection[0] = addressSection(query.Qname, ipaddressV4) 86 | } else { 87 | // ancount := 0 88 | } 89 | case query.Qtype == types.AAAA: 90 | result.Responsecode = types.NOERROR 91 | if ipaddressV4 == nil { 92 | ancount := 1 93 | result.Ansection = make([]types.RR, ancount) 94 | result.Ansection[0] = aaaaSection(query.Qname, tcpAddr.IP) 95 | } else { 96 | // ancount := 0 97 | } 98 | case query.Qtype == types.TXT: 99 | result.Responsecode = types.NOERROR 100 | ancount := 1 101 | result.Ansection = make([]types.RR, ancount) 102 | result.Ansection[0] = txtSection(query.Qname, query.Client) 103 | case query.Qtype == types.ALL: 104 | result.Responsecode = types.NOERROR 105 | ancount := 2 106 | result.Ansection = make([]types.RR, ancount) 107 | result.Ansection[0] = txtSection(query.Qname, query.Client) 108 | if ipaddressV4 == nil { 109 | result.Ansection[1] = aaaaSection(query.Qname, tcpAddr.IP) 110 | } else { 111 | result.Ansection[1] = addressSection(query.Qname, ipaddressV4) 112 | } 113 | default: 114 | result.Responsecode = types.NOERROR 115 | } 116 | return result 117 | } 118 | 119 | func Init(firstoption int) { 120 | } 121 | -------------------------------------------------------------------------------- /as112.go: -------------------------------------------------------------------------------- 1 | /* A name server for the AS112 sink system. See http://www.as112.net/ 2 | 3 | Stephane Bortzmeyer 4 | 5 | Example of use: 6 | 7 | grong -servername "grong.cloud.as112.test" -- -email toto.example.net -hostname me.as112.net -location "In the cloud" 8 | 9 | */ 10 | 11 | package responder 12 | 13 | import ( 14 | "regexp" 15 | "strings" 16 | "fmt" 17 | "os" 18 | "./types" 19 | "./myflag" 20 | ) 21 | 22 | const as112Regexp = "(168\\.192\\.in-addr\\.arpa|154\\.169\\.in-addr\\.arpa|16\\.172\\.in-addr\\.arpa|17\\.172\\.in-addr\\.arpa|18\\.172\\.in-addr\\.arpa|19\\.172\\.in-addr\\.arpa|20\\.172\\.in-addr\\.arpa|21\\.172\\.in-addr\\.arpa|22\\.172\\.in-addr\\.arpa|23\\.172\\.in-addr\\.arpa|24\\.172\\.in-addr\\.arpa|25\\.172\\.in-addr\\.arpa|26\\.172\\.in-addr\\.arpa|27\\.172\\.in-addr\\.arpa|28\\.172\\.in-addr\\.arpa|29\\.172\\.in-addr\\.arpa|30\\.172\\.in-addr\\.arpa|31\\.172\\.in-addr\\.arpa|10\\.in-addr\\.arpa)$" 23 | 24 | const defaultTTL = 3600 25 | 26 | var ( 27 | as112Domain = regexp.MustCompile("^" + as112Regexp) 28 | as112SubDomain = regexp.MustCompile("\\." + as112Regexp) 29 | // Answers to "TXT hostname.as112.net" 30 | hostnameAnswers = [...]string{ 31 | "Unknown location on Earth.", 32 | "GRONG, name server written in Go.", 33 | "See http://as112.net/ for more information.", 34 | } 35 | 36 | // Name servers of AS112, currently two 37 | as112nameServers = [...]string{ 38 | "blackhole-1.iana.org", 39 | "blackhole-2.iana.org", 40 | } 41 | 42 | hostnamesoa = types.SOArecord{ 43 | Mname: "NOT-CONFIGURED-use-hostname-option.as112.example.net", // Put the real hostname with the -hostname command-line option. We do not use -server which has lightly different semantics. 44 | Rname: "UNKNOWN-use-email-option.as112.example.net", // Put your email address (with @ replaced by .) with the -email command-line option 45 | Serial: 2003030100, 46 | Refresh: 3600, 47 | Retry: 600, 48 | Expire: 2592000, 49 | Minimum: 15, 50 | } 51 | 52 | as112soa = types.SOArecord{ 53 | Mname: "prisoner.iana.org", 54 | Rname: "hostmaster.root-servers.org", 55 | Serial: 2002040800, 56 | Refresh: 1800, 57 | Retry: 900, 58 | Expire: 604800, 59 | Minimum: 604800, 60 | } 61 | ) 62 | 63 | func nsRecords(domain string) (result []types.RR) { 64 | result = make([]types.RR, len(as112nameServers)) 65 | for i, text := range as112nameServers { 66 | result[i] = types.RR{ 67 | Name: domain, 68 | TTL: defaultTTL, 69 | Type: types.NS, 70 | Class: types.IN, 71 | Data: types.Encode(text), 72 | } 73 | } 74 | return 75 | } 76 | 77 | func soaRecord(domain string, soa types.SOArecord) (result types.RR) { 78 | result = types.RR{ 79 | Name: domain, 80 | TTL: defaultTTL, 81 | Type: types.SOA, 82 | Class: types.IN, 83 | Data: types.EncodeSOA(soa), 84 | } 85 | return 86 | } 87 | 88 | func Respond(query types.DNSquery, config map[string]interface{}) (result types.DNSresponse) { 89 | result.Ansection = nil 90 | qname := strings.ToLower(query.Qname) 91 | if query.Qclass == types.IN { 92 | switch { 93 | case as112Domain.Match([]byte(qname)): 94 | result.Responsecode = types.NOERROR 95 | switch { 96 | case query.Qtype == types.NS: 97 | result.Ansection = nsRecords(query.Qname) 98 | case query.Qtype == types.SOA: 99 | result.Ansection = make([]types.RR, 1) 100 | result.Ansection[0] = soaRecord(query.Qname, as112soa) 101 | default: 102 | // Do nothing 103 | } 104 | case as112SubDomain.Match([]byte(qname)): 105 | result.Responsecode = types.NXDOMAIN 106 | // TODO: send the proper SOA in the authority section (so we 107 | // must find which domain matched) 108 | case qname == "hostname.as112.net": 109 | result.Responsecode = types.NOERROR 110 | switch query.Qtype { // TODO: handle ANY qtypes 111 | case types.TXT: 112 | result.Ansection = make([]types.RR, len(hostnameAnswers)) 113 | for i, text := range hostnameAnswers { 114 | result.Ansection[i] = types.RR{ 115 | Name: query.Qname, 116 | TTL: defaultTTL, 117 | Type: types.TXT, 118 | Class: types.IN, 119 | Data: types.ToTXT(text), 120 | } 121 | } 122 | case types.NS: 123 | result.Ansection = nsRecords(query.Qname) 124 | case types.SOA: 125 | result.Ansection = []types.RR{soaRecord(query.Qname, hostnamesoa)} 126 | default: 127 | // Do nothing 128 | } 129 | default: 130 | result.Responsecode = types.SERVFAIL 131 | } 132 | } else { 133 | result.Responsecode = types.SERVFAIL 134 | } 135 | return result 136 | } 137 | 138 | func Init(firstoption int) { 139 | flag.Reinit(firstoption) 140 | helpptr := flag.Bool("help", false, "Displays usage instructions") 141 | emailptr := flag.String("email", "", 142 | "Set the email address of the manager for this server (in DNS format, with . instead of @)") 143 | locationptr := flag.String("location", "", 144 | "Set the location of this server, for instance \"ALIX exchange point in Somewhere, Somestate\"") 145 | hostnameptr := flag.String("hostname", "", 146 | "Set the official host name for this server") 147 | flag.Parse() 148 | help := *helpptr 149 | if help { 150 | fmt.Printf("Usage of the AS112 responder:\n") 151 | flag.PrintDefaults() 152 | os.Exit(0) 153 | } 154 | if *emailptr != "" { 155 | hostnamesoa.Rname = *emailptr 156 | } 157 | if *locationptr != "" { 158 | hostnameAnswers[0] = *locationptr 159 | } 160 | if *hostnameptr != "" { 161 | hostnamesoa.Mname = *hostnameptr 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /types.go: -------------------------------------------------------------------------------- 1 | /* Various types and constants for a DNS server. 2 | Mostly taken directly from RFC 1035. 3 | Various functions for things like encoding. 4 | TODO: replace them by things from package net/? 5 | */ 6 | 7 | package types 8 | 9 | import ( 10 | "net" 11 | "strings" 12 | "fmt" 13 | "bytes" 14 | "encoding/binary" 15 | ) 16 | 17 | // This type is used for the communication between the server (the 18 | // front-end and its responder (the back-end). So, only a part of DNS 19 | // info can be represented. 20 | type DNSresponse struct { 21 | Responsecode uint 22 | Ansection []RR 23 | // TODO: allow to have other sections? 24 | } 25 | // TODO: provides a String() method 26 | 27 | // This type is used for the communication between the server (the 28 | // front-end and its responder (the back-end). So, only a part of DNS 29 | // info can be represented. 30 | type DNSquery struct { 31 | Client net.Addr 32 | Qname string 33 | Qclass uint16 34 | Qtype uint16 35 | BufferSize uint16 36 | } 37 | // TODO: provides a String() method 38 | 39 | // Probably obsolete, will be deleted 40 | type DNSheader struct { 41 | Qid uint16 42 | Misc uint16 43 | Qdcount, Ancount, Nscount, Arcount uint16 44 | } 45 | 46 | // This type represents the DNS packet, with all its fields, as 47 | // described in RFC 1035, section 4.1. So, it is a bit long 48 | type DNSpacket struct { 49 | Id uint16 50 | Opcode uint 51 | Rcode uint 52 | Edns bool 53 | EdnsBufferSize uint16 54 | Query, Recursion, Authoritative bool 55 | Qdcount, Ancount, Arcount, Nscount uint16 // Question, Answer, Additional and Authority. May be use the implicit length 56 | // of the following arrays, instead? 57 | Qsection []Qentry 58 | Ansection []RR // Answer section 59 | Arsection []RR // Additional section 60 | // TODO: other sections 61 | Nsid bool // RFC 5001 62 | } 63 | 64 | func (packet DNSpacket) String() string { 65 | return fmt.Sprintf("Query is %t, Opcode is %d, Recursion is %t, Rcode is %d, FQDN is %s, type is %d, class is %d", 66 | packet.Query, packet.Opcode, packet.Recursion, packet.Rcode, packet.Qsection[0].Qname, packet.Qsection[0].Qtype, packet.Qsection[0].Qclass) 67 | } 68 | 69 | // Entries in the Question section. RFC 1035, section 4.1.2 70 | type Qentry struct { 71 | Qname string 72 | Qtype, Qclass uint16 73 | } 74 | 75 | // Entries in Answer, Authority and NS sections. RFC 1035, section 4.1.3 76 | type RR struct { 77 | Name string 78 | Type, Class uint16 79 | TTL uint32 80 | // Length is implicit 81 | Data []byte 82 | } 83 | 84 | type SOArecord struct { 85 | Mname string 86 | Rname string 87 | Serial uint32 88 | Refresh, Retry, Expire uint32 89 | Minimum uint32 90 | } 91 | 92 | const ( 93 | // Response codes 94 | NOERROR = 0 95 | FORMERR = 1 96 | SERVFAIL = 2 97 | NXDOMAIN = 3 98 | NOTIMPL = 4 99 | REFUSED = 5 100 | 101 | // Classes 102 | IN = 1 103 | CS = 2 104 | CH = 3 105 | HS = 4 106 | 107 | // Types 108 | A = 1 109 | NS = 2 110 | SOA = 6 111 | PTR = 12 112 | HINFO = 13 113 | MX = 15 114 | TXT = 16 115 | AAAA = 28 116 | OPT = 41 117 | ALL = 255 118 | 119 | // Opcodes 120 | STDQUERY = 0 121 | IQUERY = 1 122 | STATUS = 2 123 | 124 | // EDNS Option codes 125 | NSID = 3 126 | ) 127 | 128 | // Various utility functions 129 | 130 | // Converts a string to the wire format {length, data} 131 | func ToTXT(s string) []byte { 132 | result := make([]byte, 1+len(s)) 133 | result[0] = uint8(len(s)) 134 | for i := 0; i < len(s); i++ { 135 | result[i+1] = s[i] 136 | } 137 | return result 138 | } 139 | 140 | // Encodes a FQDN in wire-format (for each label, length+data) 141 | // TODO: see packDomainName in net/dnsmsg.go. 142 | func Encode(name string) []byte { 143 | var ( 144 | totalresult []byte 145 | ) 146 | labels := make([]string, 0) 147 | if name != "." { // The root is a special case. See issue #4 148 | labels = strings.Split(name, ".", -1) 149 | } 150 | totallength := 0 151 | totalresult = make([]byte, 256) // TODO what a waste 152 | for _, label := range labels { 153 | result := make([]byte, 1+len(label)) 154 | result[0] = uint8(len(label)) 155 | for i := 0; i < len(label); i++ { 156 | result[i+1] = label[i] 157 | } 158 | for i := 0; i < 1+len(label); i++ { 159 | totalresult[totallength+i] = result[i] 160 | } 161 | totallength = totallength + int(result[0]) + 1 162 | } 163 | totalresult[totallength] = 0 // Domain names end in a null byte for the root 164 | totallength++ 165 | return totalresult[0:totallength] 166 | } 167 | 168 | func EncodeSOA(soa SOArecord) []byte { 169 | var ( 170 | result []byte 171 | temp32 []byte 172 | ) 173 | mname := Encode(soa.Mname) 174 | length := len(mname) 175 | rname := Encode(soa.Rname) 176 | length = length + len(rname) 177 | length = length + (5 * 4) // Five 32-bits counter at the end 178 | /* "It's probably cleaner to write to a bytes.Buffer than to 179 | repeatedly call bytes.Add." Russ Cox, go-nuts ML */ 180 | result = bytes.Add(result, mname) 181 | result = bytes.Add(result, rname) 182 | temp32 = make([]byte, 4) 183 | binary.BigEndian.PutUint32(temp32, soa.Serial) 184 | result = bytes.Add(result, temp32) 185 | binary.BigEndian.PutUint32(temp32, soa.Refresh) 186 | result = bytes.Add(result, temp32) 187 | binary.BigEndian.PutUint32(temp32, soa.Retry) 188 | result = bytes.Add(result, temp32) 189 | binary.BigEndian.PutUint32(temp32, soa.Expire) 190 | result = bytes.Add(result, temp32) 191 | binary.BigEndian.PutUint32(temp32, soa.Minimum) 192 | result = bytes.Add(result, temp32) 193 | return result[0:length] 194 | } 195 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | GRONG (Gross and ROugh Nameserver written in Go) is a DNS (Domain Name 2 | System) authoritative name server. It is intended as a research 3 | project and is not probably suitable for use on the wild Internet. 4 | 5 | MAY NOT BE SUITABLE ON A PRODUCTION SITE. YOU HAVE BEEN WARNED!!! 6 | 7 | Disclaimer: I've never been to this city 8 | 9 | 10 | GRONG can only be used as an authoritative name server (like, for 11 | instance, nsd), not as a recursive one. 12 | 13 | GRONG provides a general DNS engine, the front-end, which receives 14 | packets, parses them and sends a proper response, and several possible 15 | back-ends (named "responders") which generates a response, given the 16 | query. Some are provided with GRONG and you are welcome to write 17 | others. 18 | 19 | The official source is 20 | 21 | Usage 22 | ***** 23 | 24 | ./grong [-address="[ADDRESS]:PORT"] [-debug=N] [-nodaemon] [-domain="DOMAIN NAME"] 25 | 26 | Run with -help to see the defaults (and the other, less common, options) 27 | 28 | The -address option takes either a port (in the syntax ":NNN"), in 29 | that case GRONG listens on all IP addresses, or one address (in the 30 | syntax "x.y.z.T:NNN" for IPv4 and "[xxxx:yyyy::zzzz]:NNN" for 31 | IPv6). There is currently no way to listen on some (but not all) of the IP 32 | addresses. 33 | 34 | The -domain option takes a domain name, which will be the name of the 35 | zone for which the name server will be authoritative for. Not all 36 | responders use it. 37 | 38 | The back-end is choosen at compile-time only (I have no idea about the 39 | support for dynamic linking in Go) 40 | 41 | Among the provided responders: 42 | * rude-responder: responds REFUSED to every query 43 | * reflector-responder: responds with the IP address of the client (for TXT 44 | requests, in text form, for A or AAAA requests, as binary). -domain indicates 45 | the zone name it uses (e.g. whoami.example.net) 46 | * as112: an AS 112 name server (see ) 47 | 48 | For the person who compiles 49 | ************************** 50 | 51 | You need a working Go environment. Today, only the 52 | gc compiler is supported. 53 | 54 | To choose a responder (here, foobar-responder): 55 | 56 | make clean 57 | ln -sf foobar-responder.go responder.go 58 | make 59 | mv ./server /where/you/want/grong-foobar 60 | 61 | 62 | For the person who writes a responder 63 | ************************************ 64 | 65 | The interface of the responder is: 66 | 67 | It must be in package "responder" and imports package "types". Read 68 | "types.go" first, it contains useful constants (named from the RFC 69 | 1035). 70 | 71 | The front-end checks that the request is a query and, if so, calls the 72 | responder. The prototype is: 73 | 74 | func Respond(query types.DNSquery, config map[string]interface{}) types.DNSresponse 75 | 76 | To see what is available for you in the query, see the description of 77 | type DNSquery. Important: the query name (Qname) is always in 78 | lowercase, to ease comparisons. 79 | 80 | In the DNSresponse, RRs (Resource Records) have to be in the wire 81 | format (the front-end does not know the format of the RR, to keep it 82 | generic). For instance, data in TXT RR has to be {length, 83 | value}. There are some utilities functions in types to help you to do 84 | so. 85 | 86 | The map named "config" above gives you access to global variables. You 87 | can never be sure what variables are defined so it is careful to test 88 | their existence before using them. Typical variables (read the source 89 | code of server.go to find others) are: 90 | * debug: an integer for the debug level (0 = no debug, 1 = a bit of 91 | debug, etc). Always set. 92 | * servername: if set, a string identifying this specific name server 93 | (for instance "cdg1.a.ns.example.net"). 94 | * zonename: if set, the name of the zone currently served (for instance 95 | "myself.example.net") 96 | 97 | Since the map stores various types (the "interface{}", which means the 98 | empty interface, something which is fulfilled by every possible 99 | object), you often have to convert data with the reflect package. For 100 | instance, to get the integer value of debug: 101 | debug, exists := myconfig["debug"] 102 | if exists { 103 | debugi := reflect.NewValue(debug).(*reflect.IntValue).Get() 104 | } 105 | 106 | The responder must also provide a function: 107 | 108 | func Init(firstoption int) 109 | 110 | which will be called at server startup and which can be used to 111 | process additional command-line flags (see as112.go for a good 112 | example) or any other stuff. 113 | 114 | Implementation notes 115 | ******************** 116 | 117 | One goroutine is run for every DNS request. Makes the code much 118 | simpler and easier to read but may explain the fact that performance 119 | are behind BIND. 120 | 121 | TODO 122 | **** 123 | 124 | Finish the AS112 responder (SOA and NS records) 125 | 126 | The ability to listen to more than one address (but not all). Can I 127 | give several -address option to the flag module? If so, it probably 128 | just means firing several udpListeners and several tcpListeners. Since 129 | it interacts with the IPv4/IPv6 listening abilities, we may have to 130 | wait for the solution of 131 | . 132 | 133 | Debugging of Go runtime performance issues, hit it harder with 134 | queryperf! 135 | 136 | Test with gccgo 137 | 138 | Implements version.server (and version.bind and hostname.bind ?) 139 | 140 | See if we can replace a good part of package "types" by standard 141 | package net/ It does not 142 | seem easy, the dns* files do not export anything outside of package 143 | net, they are meant for internal use only. 144 | 145 | Daemonizing seems very difficult with Go 146 | 147 | In the mean time, nohup + & + disown seems the only solution. Provide 148 | an example at least for Debian, using start-stop-daemon? 149 | 150 | A name server with data, for instance able to serve a zone with a 151 | simple setup, one SOA, a few NS, and one A record for www.$ORIGIN. The 152 | list of zones (all with identical data) and the IP address of the Web 153 | server being taken from a file or on the command-line. 154 | 155 | Configuration file. What is idiomatic in Go? .INI ? 156 | uses JSON. 157 | 158 | Continue hardening against rogue packets. See the example 159 | test-scapy.py in the distribution. 160 | 161 | Michael Hoisie had a very good suggestion to turn GRONG into a 162 | package. He plans: 163 | 1) all the source files should have a common package, like "grong". 164 | there shouldn't be a main package 165 | 2) there could be a Responder interface, that looks like: 166 | type Responder interface { 167 | Respond(query.DNSquery) DNSresponse 168 | } 169 | And all the various responders would implement this. 170 | 3) The Makefile would have a format like (except modified for Grong): 171 | http://github.com/hoisie/web.go/blob/master/Makefile 172 | 4) Instead of main() method, there would be a method like Run( resp 173 | Responder ) that takes a responder and starts the server 174 | 175 | Rewrite a good part of Grong to use Go DNS? https://github.com/miekg/godns 176 | 177 | DNSSEC (no, I'm joking) 178 | 179 | 180 | Author 181 | ****** 182 | 183 | Stéphane Bortzmeyer 184 | 185 | 186 | License 187 | ******* 188 | 189 | This is free software. Free as in free speech, not as in free beer. 190 | 191 | See the actual license in LICENSE 192 | -------------------------------------------------------------------------------- /myflag.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | The flag package implements command-line flag parsing. 7 | 8 | Usage: 9 | 10 | 1) Define flags using flag.String(), Bool(), Int(), etc. Example: 11 | import "flag" 12 | var ip *int = flag.Int("flagname", 1234, "help message for flagname") 13 | If you like, you can bind the flag to a variable using the Var() functions. 14 | var flagvar int 15 | func init() { 16 | flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname") 17 | } 18 | Or you can create custom flags that satisfy the Value interface (with 19 | pointer receivers) and couple them to flag parsing by 20 | flag.Var(&flagVal, "name", "help message for flagname") 21 | For such flags, the default value is just the initial value of the variable. 22 | 23 | 2) After all flags are defined, call 24 | flag.Parse() 25 | to parse the command line into the defined flags. 26 | 27 | 3) Flags may then be used directly. If you're using the flags themselves, 28 | they are all pointers; if you bind to variables, they're values. 29 | fmt.Println("ip has value ", *ip); 30 | fmt.Println("flagvar has value ", flagvar); 31 | 32 | 4) After parsing, flag.Arg(i) is the i'th argument after the flags. 33 | Args are indexed from 0 up to flag.NArg(). 34 | 35 | Command line flag syntax: 36 | -flag 37 | -flag=x 38 | -flag x // non-boolean flags only 39 | One or two minus signs may be used; they are equivalent. 40 | The last form is not permitted for boolean flags because the 41 | meaning of the command 42 | cmd -x * 43 | will change if there is a file called 0, false, etc. You must 44 | use the -flag=false form to turn off a boolean flag. 45 | 46 | Flag parsing stops just before the first non-flag argument 47 | ("-" is a non-flag argument) or after the terminator "--". 48 | 49 | Integer flags accept 1234, 0664, 0x1234 and may be negative. 50 | Boolean flags may be 1, 0, t, f, true, false, TRUE, FALSE, True, False. 51 | */ 52 | package flag 53 | 54 | import ( 55 | "fmt" 56 | "os" 57 | "strconv" 58 | ) 59 | 60 | // -- Bool Value 61 | type boolValue bool 62 | 63 | func newBoolValue(val bool, p *bool) *boolValue { 64 | *p = val 65 | return (*boolValue)(p) 66 | } 67 | 68 | func (b *boolValue) Set(s string) bool { 69 | v, err := strconv.Atob(s) 70 | *b = boolValue(v) 71 | return err == nil 72 | } 73 | 74 | func (b *boolValue) String() string { return fmt.Sprintf("%v", *b) } 75 | 76 | // -- Int Value 77 | type intValue int 78 | 79 | func newIntValue(val int, p *int) *intValue { 80 | *p = val 81 | return (*intValue)(p) 82 | } 83 | 84 | func (i *intValue) Set(s string) bool { 85 | v, err := strconv.Atoi(s) 86 | *i = intValue(v) 87 | return err == nil 88 | } 89 | 90 | func (i *intValue) String() string { return fmt.Sprintf("%v", *i) } 91 | 92 | // -- Int64 Value 93 | type int64Value int64 94 | 95 | func newInt64Value(val int64, p *int64) *int64Value { 96 | *p = val 97 | return (*int64Value)(p) 98 | } 99 | 100 | func (i *int64Value) Set(s string) bool { 101 | v, err := strconv.Atoi64(s) 102 | *i = int64Value(v) 103 | return err == nil 104 | } 105 | 106 | func (i *int64Value) String() string { return fmt.Sprintf("%v", *i) } 107 | 108 | // -- Uint Value 109 | type uintValue uint 110 | 111 | func newUintValue(val uint, p *uint) *uintValue { 112 | *p = val 113 | return (*uintValue)(p) 114 | } 115 | 116 | func (i *uintValue) Set(s string) bool { 117 | v, err := strconv.Atoui(s) 118 | *i = uintValue(v) 119 | return err == nil 120 | } 121 | 122 | func (i *uintValue) String() string { return fmt.Sprintf("%v", *i) } 123 | 124 | // -- uint64 Value 125 | type uint64Value uint64 126 | 127 | func newUint64Value(val uint64, p *uint64) *uint64Value { 128 | *p = val 129 | return (*uint64Value)(p) 130 | } 131 | 132 | func (i *uint64Value) Set(s string) bool { 133 | v, err := strconv.Atoui64(s) 134 | *i = uint64Value(v) 135 | return err == nil 136 | } 137 | 138 | func (i *uint64Value) String() string { return fmt.Sprintf("%v", *i) } 139 | 140 | // -- string Value 141 | type stringValue string 142 | 143 | func newStringValue(val string, p *string) *stringValue { 144 | *p = val 145 | return (*stringValue)(p) 146 | } 147 | 148 | func (s *stringValue) Set(val string) bool { 149 | *s = stringValue(val) 150 | return true 151 | } 152 | 153 | func (s *stringValue) String() string { return fmt.Sprintf("%s", *s) } 154 | 155 | // -- Float Value 156 | type floatValue float 157 | 158 | func newFloatValue(val float, p *float) *floatValue { 159 | *p = val 160 | return (*floatValue)(p) 161 | } 162 | 163 | func (f *floatValue) Set(s string) bool { 164 | v, err := strconv.Atof(s) 165 | *f = floatValue(v) 166 | return err == nil 167 | } 168 | 169 | func (f *floatValue) String() string { return fmt.Sprintf("%v", *f) } 170 | 171 | // -- Float64 Value 172 | type float64Value float64 173 | 174 | func newFloat64Value(val float64, p *float64) *float64Value { 175 | *p = val 176 | return (*float64Value)(p) 177 | } 178 | 179 | func (f *float64Value) Set(s string) bool { 180 | v, err := strconv.Atof64(s) 181 | *f = float64Value(v) 182 | return err == nil 183 | } 184 | 185 | func (f *float64Value) String() string { return fmt.Sprintf("%v", *f) } 186 | 187 | // Value is the interface to the dynamic value stored in a flag. 188 | // (The default value is represented as a string.) 189 | type Value interface { 190 | String() string 191 | Set(string) bool 192 | } 193 | 194 | // A Flag represents the state of a flag. 195 | type Flag struct { 196 | Name string // name as it appears on command line 197 | Usage string // help message 198 | Value Value // value as set 199 | DefValue string // default value (as text); for usage message 200 | } 201 | 202 | type allFlags struct { 203 | actual map[string]*Flag 204 | formal map[string]*Flag 205 | first_arg int // 0 is the program name, 1 is first arg 206 | offset int // Always 0 unless the client calls Reinit 207 | } 208 | 209 | var flags *allFlags 210 | 211 | // VisitAll visits the flags, calling fn for each. It visits all flags, even those not set. 212 | func VisitAll(fn func(*Flag)) { 213 | for _, f := range flags.formal { 214 | fn(f) 215 | } 216 | } 217 | 218 | // Visit visits the flags, calling fn for each. It visits only those flags that have been set. 219 | func Visit(fn func(*Flag)) { 220 | for _, f := range flags.actual { 221 | fn(f) 222 | } 223 | } 224 | 225 | // Lookup returns the Flag structure of the named flag, returning nil if none exists. 226 | func Lookup(name string) *Flag { 227 | return flags.formal[name] 228 | } 229 | 230 | // Set sets the value of the named flag. It returns true if the set succeeded; false if 231 | // there is no such flag defined. 232 | func Set(name, value string) bool { 233 | f, ok := flags.formal[name] 234 | if !ok { 235 | return false 236 | } 237 | ok = f.Value.Set(value) 238 | if !ok { 239 | return false 240 | } 241 | flags.actual[name] = f 242 | return true 243 | } 244 | 245 | // PrintDefaults prints to standard error the default values of all defined flags. 246 | func PrintDefaults() { 247 | VisitAll(func(f *Flag) { 248 | format := " -%s=%s: %s\n" 249 | if _, ok := f.Value.(*stringValue); ok { 250 | // put quotes on the value 251 | format = " -%s=%q: %s\n" 252 | } 253 | fmt.Fprintf(os.Stderr, format, f.Name, f.DefValue, f.Usage) 254 | }) 255 | } 256 | 257 | // Usage prints to standard error a default usage message documenting all defined flags. 258 | // The function is a variable that may be changed to point to a custom function. 259 | var Usage = func() { 260 | fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) 261 | PrintDefaults() 262 | } 263 | 264 | var panicOnError = false 265 | 266 | func fail() { 267 | Usage() 268 | if panicOnError { 269 | panic("flag parse error") 270 | } 271 | os.Exit(2) 272 | } 273 | 274 | func NFlag() int { return len(flags.actual) } 275 | 276 | // Arg returns the i'th command-line argument. Arg(0) is the first remaining argument 277 | // after flags have been processed. 278 | func Arg(i int) string { 279 | i += flags.first_arg + flags.offset 280 | if i < 0 || i >= len(os.Args) { 281 | return "" 282 | } 283 | return os.Args[i] 284 | } 285 | 286 | // NArg is the number of arguments remaining after flags have been processed. 287 | func NArg() int { return len(os.Args) - flags.offset - flags.first_arg } 288 | 289 | // Args returns the non-flag command-line arguments. 290 | func Args() []string { return os.Args[flags.first_arg + flags.offset:] } 291 | 292 | // LastOption is the index of the last option. Useful for Reinit() 293 | func LastOption() int { return flags.first_arg - 1 } 294 | 295 | // BoolVar defines a bool flag with specified name, default value, and usage string. 296 | // The argument p points to a bool variable in which to store the value of the flag. 297 | func BoolVar(p *bool, name string, value bool, usage string) { 298 | Var(newBoolValue(value, p), name, usage) 299 | } 300 | 301 | // Bool defines a bool flag with specified name, default value, and usage string. 302 | // The return value is the address of a bool variable that stores the value of the flag. 303 | func Bool(name string, value bool, usage string) *bool { 304 | p := new(bool) 305 | BoolVar(p, name, value, usage) 306 | return p 307 | } 308 | 309 | // IntVar defines an int flag with specified name, default value, and usage string. 310 | // The argument p points to an int variable in which to store the value of the flag. 311 | func IntVar(p *int, name string, value int, usage string) { 312 | Var(newIntValue(value, p), name, usage) 313 | } 314 | 315 | // Int defines an int flag with specified name, default value, and usage string. 316 | // The return value is the address of an int variable that stores the value of the flag. 317 | func Int(name string, value int, usage string) *int { 318 | p := new(int) 319 | IntVar(p, name, value, usage) 320 | return p 321 | } 322 | 323 | // Int64Var defines an int64 flag with specified name, default value, and usage string. 324 | // The argument p points to an int64 variable in which to store the value of the flag. 325 | func Int64Var(p *int64, name string, value int64, usage string) { 326 | Var(newInt64Value(value, p), name, usage) 327 | } 328 | 329 | // Int64 defines an int64 flag with specified name, default value, and usage string. 330 | // The return value is the address of an int64 variable that stores the value of the flag. 331 | func Int64(name string, value int64, usage string) *int64 { 332 | p := new(int64) 333 | Int64Var(p, name, value, usage) 334 | return p 335 | } 336 | 337 | // UintVar defines a uint flag with specified name, default value, and usage string. 338 | // The argument p points to a uint variable in which to store the value of the flag. 339 | func UintVar(p *uint, name string, value uint, usage string) { 340 | Var(newUintValue(value, p), name, usage) 341 | } 342 | 343 | // Uint defines a uint flag with specified name, default value, and usage string. 344 | // The return value is the address of a uint variable that stores the value of the flag. 345 | func Uint(name string, value uint, usage string) *uint { 346 | p := new(uint) 347 | UintVar(p, name, value, usage) 348 | return p 349 | } 350 | 351 | // Uint64Var defines a uint64 flag with specified name, default value, and usage string. 352 | // The argument p points to a uint64 variable in which to store the value of the flag. 353 | func Uint64Var(p *uint64, name string, value uint64, usage string) { 354 | Var(newUint64Value(value, p), name, usage) 355 | } 356 | 357 | // Uint64 defines a uint64 flag with specified name, default value, and usage string. 358 | // The return value is the address of a uint64 variable that stores the value of the flag. 359 | func Uint64(name string, value uint64, usage string) *uint64 { 360 | p := new(uint64) 361 | Uint64Var(p, name, value, usage) 362 | return p 363 | } 364 | 365 | // StringVar defines a string flag with specified name, default value, and usage string. 366 | // The argument p points to a string variable in which to store the value of the flag. 367 | func StringVar(p *string, name, value string, usage string) { 368 | Var(newStringValue(value, p), name, usage) 369 | } 370 | 371 | // String defines a string flag with specified name, default value, and usage string. 372 | // The return value is the address of a string variable that stores the value of the flag. 373 | func String(name, value string, usage string) *string { 374 | p := new(string) 375 | StringVar(p, name, value, usage) 376 | return p 377 | } 378 | 379 | // FloatVar defines a float flag with specified name, default value, and usage string. 380 | // The argument p points to a float variable in which to store the value of the flag. 381 | func FloatVar(p *float, name string, value float, usage string) { 382 | Var(newFloatValue(value, p), name, usage) 383 | } 384 | 385 | // Float defines a float flag with specified name, default value, and usage string. 386 | // The return value is the address of a float variable that stores the value of the flag. 387 | func Float(name string, value float, usage string) *float { 388 | p := new(float) 389 | FloatVar(p, name, value, usage) 390 | return p 391 | } 392 | 393 | // Float64Var defines a float64 flag with specified name, default value, and usage string. 394 | // The argument p points to a float64 variable in which to store the value of the flag. 395 | func Float64Var(p *float64, name string, value float64, usage string) { 396 | Var(newFloat64Value(value, p), name, usage) 397 | } 398 | 399 | // Float64 defines a float64 flag with specified name, default value, and usage string. 400 | // The return value is the address of a float64 variable that stores the value of the flag. 401 | func Float64(name string, value float64, usage string) *float64 { 402 | p := new(float64) 403 | Float64Var(p, name, value, usage) 404 | return p 405 | } 406 | 407 | // Var defines a user-typed flag with specified name, default value, and usage string. 408 | // The argument p points to a Value variable in which to store the value of the flag. 409 | func Var(value Value, name string, usage string) { 410 | // Remember the default value as a string; it won't change. 411 | f := &Flag{name, usage, value, value.String()} 412 | _, alreadythere := flags.formal[name] 413 | if alreadythere { 414 | fmt.Fprintln(os.Stderr, "flag redefined:", name) 415 | panic("flag redefinition") // Happens only if flags are declared with identical names 416 | } 417 | flags.formal[name] = f 418 | } 419 | 420 | 421 | func (f *allFlags) parseOne(index int) (ok bool, next int) { 422 | s := os.Args[index] 423 | f.first_arg = index // until proven otherwise 424 | if len(s) == 0 { 425 | return false, -1 426 | } 427 | if s[0] != '-' { 428 | return false, -1 429 | } 430 | num_minuses := 1 431 | if len(s) == 1 { 432 | return false, index 433 | } 434 | if s[1] == '-' { 435 | num_minuses++ 436 | if len(s) == 2 { // "--" terminates the flags 437 | return false, index + 1 438 | } 439 | } 440 | name := s[num_minuses:] 441 | if len(name) == 0 || name[0] == '-' || name[0] == '=' { 442 | fmt.Fprintln(os.Stderr, "bad flag syntax:", s) 443 | fail() 444 | } 445 | 446 | // it's a flag. does it have an argument? 447 | has_value := false 448 | value := "" 449 | for i := 1; i < len(name); i++ { // equals cannot be first 450 | if name[i] == '=' { 451 | value = name[i+1:] 452 | has_value = true 453 | name = name[0:i] 454 | break 455 | } 456 | } 457 | m := flags.formal 458 | flag, alreadythere := m[name] // BUG 459 | if !alreadythere { 460 | fmt.Fprintf(os.Stderr, "flag provided but not defined: -%s\n", name) 461 | fail() 462 | } 463 | if f, ok := flag.Value.(*boolValue); ok { // special case: doesn't need an arg 464 | if has_value { 465 | if !f.Set(value) { 466 | fmt.Fprintf(os.Stderr, "invalid boolean value %t for flag: -%s\n", value, name) 467 | fail() 468 | } 469 | } else { 470 | f.Set("true") 471 | } 472 | } else { 473 | // It must have a value, which might be the next argument. 474 | if !has_value && index < len(os.Args)-1 { 475 | // value is the next arg 476 | has_value = true 477 | index++ 478 | value = os.Args[index] 479 | } 480 | if !has_value { 481 | fmt.Fprintf(os.Stderr, "flag needs an argument: -%s\n", name) 482 | fail() 483 | } 484 | ok = flag.Value.Set(value) 485 | if !ok { 486 | fmt.Fprintf(os.Stderr, "invalid value %s for flag: -%s\n", value, name) 487 | fail() 488 | } 489 | } 490 | flags.actual[name] = flag 491 | return true, index + 1 492 | } 493 | 494 | // Parse parses the command-line flags. Must be called after all flags are defined 495 | // and before any are accessed by the program. 496 | func Parse() { 497 | for i := flags.offset+1; i < len(os.Args); { 498 | ok, next := flags.parseOne(i) 499 | if next > 0 { 500 | flags.first_arg = next 501 | i = next 502 | } 503 | if !ok { 504 | break 505 | } 506 | } 507 | } 508 | 509 | // Reinitializes the package for a new parsing starting at argument where 510 | func Reinit(where int) { 511 | flags = &allFlags{make(map[string]*Flag), make(map[string]*Flag), where, where} 512 | } 513 | 514 | // ResetForTesting clears all flag state and sets the usage function as directed. 515 | // After calling ResetForTesting, parse errors in flag handling will panic rather 516 | // than exit the program. 517 | // For testing only! 518 | func ResetForTesting(usage func()) { 519 | flags = &allFlags{make(map[string]*Flag), make(map[string]*Flag), 1, 0} 520 | Usage = usage 521 | panicOnError = true 522 | } 523 | 524 | // ParseForTesting parses the flag state using the provided arguments. It 525 | // should be called after 1) ResetForTesting and 2) setting up the new flags. 526 | // The return value reports whether the parse was error-free. 527 | // For testing only! 528 | func ParseForTesting(args []string) (result bool) { 529 | defer func() { 530 | if recover() != nil { 531 | result = false 532 | } 533 | }() 534 | os.Args = args 535 | Parse() 536 | return true 537 | } 538 | 539 | func init() { 540 | flags = &allFlags{make(map[string]*Flag), make(map[string]*Flag), 1, 0} 541 | } 542 | -------------------------------------------------------------------------------- /server.go: -------------------------------------------------------------------------------- 1 | /* Main program for the GRONG authoritative name server 2 | Stephane Bortzmeyer 3 | */ 4 | 5 | package main 6 | 7 | import ( 8 | "bytes" 9 | "encoding/binary" 10 | "./myflag" 11 | "fmt" 12 | "net" 13 | "os" 14 | "strings" 15 | "reflect" 16 | "log" 17 | "syslog" 18 | "./responder" 19 | "./types" 20 | ) 21 | 22 | const defaultTTL = 3600 23 | const loggerOptions = log.Ldate | log.Ltime | log.Lshortfile 24 | 25 | var ( 26 | /* Configuration, indexed by keywords, for instance "debug" or 27 | "servername" */ 28 | globalConfig map[string]interface{} 29 | debug int // Not mandatory but it is simpler to use than 30 | // globalConfig["debug"], which has type interface{}. Same thing for the 31 | // others: 32 | daemon bool 33 | debuglogger, infologger, crisislogger *log.Logger 34 | zone string 35 | ) 36 | 37 | func fatal(msg string) { 38 | crisislogger.Logf("%s\n", msg) 39 | os.Exit(1) 40 | } 41 | 42 | // TODO: it would be nice to have a "fatal bool" parameter to indicate 43 | // if we should stop but Go's functions do not have parameters with 44 | // default values :-( 45 | func checkError(msg string, error os.Error) { 46 | if error != nil { 47 | fatal(fmt.Sprintf("%s: %s", msg, error)) 48 | } 49 | } 50 | 51 | func serialize(packet types.DNSpacket) []byte { 52 | // TODO: rewrite result as an io.Writer so we can just use Write? See 53 | // the example in "Effective Go", section "Pointers vs Values" 54 | result := make([]byte, packet.EdnsBufferSize) 55 | // ID 56 | binary.BigEndian.PutUint16(result[0:2], packet.Id) 57 | // Misc flags... 58 | result[2] = 0x80 // QR 1 (response), everything else 0 59 | result[3] = byte(packet.Rcode) 60 | binary.BigEndian.PutUint16(result[4:6], packet.Qdcount) 61 | // Ancount 62 | binary.BigEndian.PutUint16(result[6:8], packet.Ancount) 63 | // Nscount 64 | result[8] = 0 65 | result[9] = 0 66 | // Arcount 67 | result[10] = 0 68 | if packet.Edns { 69 | result[11] = 1 70 | } else { 71 | result[11] = 0 72 | } 73 | if len(packet.Qsection) != 1 { 74 | fatal(fmt.Sprintf("Qsection's length is not 1: %d\n", len(packet.Qsection))) 75 | } 76 | encoded_qname := types.Encode(packet.Qsection[0].Qname) 77 | n := copy(result[12:], encoded_qname) 78 | if n != len(encoded_qname) { 79 | fatal(fmt.Sprintf("Copying %d bytes from a name of %d bytes\n", 80 | n, len(encoded_qname))) 81 | } 82 | last := 12 + len(encoded_qname) 83 | binary.BigEndian.PutUint16(result[last:], packet.Qsection[0].Qtype) 84 | binary.BigEndian.PutUint16(result[last+2:], packet.Qsection[0].Qclass) 85 | last = last + 4 86 | for rrnum, rr := range packet.Ansection { 87 | encoded_qname := types.Encode(packet.Ansection[rrnum].Name) 88 | n = copy(result[last:], encoded_qname) 89 | if n != len(encoded_qname) { 90 | fatal(fmt.Sprintf("Copying %d bytes from a name of %d bytes\n", 91 | n, len(encoded_qname))) 92 | } 93 | last = last + len(encoded_qname) 94 | binary.BigEndian.PutUint16(result[last:last+2], rr.Type) 95 | binary.BigEndian.PutUint16(result[last+2:last+4], rr.Class) 96 | binary.BigEndian.PutUint32(result[last+4:last+8], rr.TTL) 97 | binary.BigEndian.PutUint16(result[last+8:last+10], uint16(len(rr.Data))) 98 | last = last + 10 99 | n = copy(result[last:], packet.Ansection[rrnum].Data) 100 | if n != len(packet.Ansection[rrnum].Data) { 101 | fatal(fmt.Sprintf("Copying %d bytes from data of %d bytes\n", 102 | n, len(packet.Ansection[rrnum].Data))) 103 | } 104 | last = last + len(packet.Ansection[rrnum].Data) 105 | } 106 | if packet.Edns { 107 | result[last] = 0 // EDNS0's Name 108 | binary.BigEndian.PutUint16(result[last+1:last+3], types.OPT) 109 | binary.BigEndian.PutUint16(result[last+3:last+5], packet.EdnsBufferSize) 110 | binary.BigEndian.PutUint32(result[last+5:last+9], 0) 111 | servernamei, nameexists := globalConfig["servername"] 112 | if nameexists { 113 | servername := reflect.NewValue(servernamei).(*reflect.StringValue).Get() 114 | if packet.Nsid { 115 | binary.BigEndian.PutUint16(result[last+9:last+11], 116 | uint16(4+len(servername))) 117 | binary.BigEndian.PutUint16(result[last+11:last+13], types.NSID) 118 | binary.BigEndian.PutUint16(result[last+13:last+15], uint16(len(servername))) 119 | last += 15 120 | n = copy(result[last:], []byte(servername)) 121 | if n != len(servername) { 122 | fatal(fmt.Sprintf("Cannot copy servername (length %d bytes), %d bytes actually copied\n", len(servername), n)) 123 | } 124 | last += int(len(servername)) 125 | } else { 126 | // Zero EDNS options 127 | binary.BigEndian.PutUint16(result[last+9:last+11], 0) 128 | last += 11 129 | } 130 | } 131 | } 132 | return result[0:last] 133 | } 134 | 135 | func readShortInteger(buf *bytes.Buffer) (uint16, bool) { 136 | slice := make([]byte, 2) 137 | n, error := buf.Read(slice[0:2]) 138 | if error != nil || n != 2 { 139 | if debug > 2 { 140 | debuglogger.Logf("Error in Read of an int16: %s (%d bytes read)\n", error, n) 141 | } 142 | return 0, false 143 | } 144 | return binary.BigEndian.Uint16(slice[0:2]), true 145 | } 146 | 147 | func readInteger(buf *bytes.Buffer) (uint32, bool) { 148 | slice := make([]byte, 4) 149 | n, error := buf.Read(slice[0:4]) 150 | if error != nil || n != 4 { 151 | if debug > 2 { 152 | debuglogger.Logf("Error in Read of an int32: %s (%d bytes read)\n", error, n) 153 | } 154 | return 0, false 155 | } 156 | return binary.BigEndian.Uint32(slice[0:4]), true 157 | } 158 | 159 | func parse(buf *bytes.Buffer) (types.DNSpacket, bool) { 160 | var ( 161 | packet types.DNSpacket 162 | ok bool 163 | ) 164 | // Initialize with sensible values 165 | packet.Edns = false 166 | packet.EdnsBufferSize = 512 167 | packet.Nsid = false 168 | 169 | packet.Id, ok = readShortInteger(buf) 170 | if !ok { 171 | return packet, false 172 | } 173 | dnsmisc, ok := readShortInteger(buf) 174 | if !ok { 175 | return packet, false 176 | } 177 | qr := (dnsmisc & 0x8000) >> 15 178 | packet.Query = false 179 | if qr == 0 { 180 | packet.Query = true 181 | } 182 | packet.Opcode = uint((dnsmisc >> 11) & 0x000F) 183 | rd := (dnsmisc & 0x0100) >> 8 184 | packet.Recursion = false 185 | if rd == 1 { 186 | packet.Recursion = true 187 | } 188 | packet.Rcode = uint(dnsmisc & 0x000F) 189 | packet.Qdcount, ok = readShortInteger(buf) 190 | if !ok { 191 | return packet, false 192 | } 193 | if packet.Qdcount != 1 { 194 | // This may be legal but we would not know what to do with it 195 | return packet, false 196 | } 197 | packet.Ancount, ok = readShortInteger(buf) 198 | if !ok { 199 | return packet, false 200 | } 201 | // TODO: reject packets with non-empty answer or authority sections 202 | packet.Nscount, ok = readShortInteger(buf) 203 | if !ok { 204 | return packet, false 205 | } 206 | packet.Arcount, ok = readShortInteger(buf) 207 | if !ok { 208 | return packet, false 209 | } 210 | over := false 211 | labels_max := make([]string, 63) 212 | labels := labels_max[0:0] 213 | // Parse the Question section 214 | nlabels := 0 215 | for !over { 216 | labelsize, error := buf.ReadByte() 217 | if error != nil { 218 | if error == os.EOF { 219 | return packet, false 220 | } else { 221 | if debug > 2 { 222 | debuglogger.Logf("Error in ReadByte: %s\n", error) 223 | } 224 | return packet, false 225 | } 226 | } 227 | if labelsize == 0 { 228 | over = true 229 | break 230 | } 231 | label := make([]byte, labelsize) 232 | n, error := buf.Read(label) 233 | if error != nil || n != int(labelsize) { 234 | if error == nil { 235 | // Client left after leaving only a few bytes 236 | return packet, false 237 | } else { 238 | if debug > 2 { 239 | debuglogger.Logf("Error in Read %d bytes: %s\n", n, error) 240 | } 241 | return packet, false 242 | } 243 | } 244 | nlabels += 1 245 | labels = labels[0:nlabels] 246 | labels[nlabels-1] = string(label) 247 | } 248 | packet.Qsection = make([]types.Qentry, packet.Qdcount) 249 | if len(labels) == 0 { // A special case, the root (see issue #4) 250 | packet.Qsection[0].Qname = "." 251 | } else { 252 | packet.Qsection[0].Qname = strings.Join(labels, ".") 253 | } 254 | packet.Qsection[0].Qtype, ok = readShortInteger(buf) 255 | if !ok { 256 | return packet, false 257 | } 258 | packet.Qsection[0].Qclass, ok = readShortInteger(buf) 259 | if !ok { 260 | return packet, false 261 | } 262 | if packet.Arcount > 0 { 263 | labelsize, error := buf.ReadByte() 264 | if error != nil { 265 | if error == os.EOF { 266 | return packet, false 267 | } else { 268 | if debug > 2 { 269 | debuglogger.Logf("Error in ReadByte: %s\n", error) 270 | } 271 | return packet, false 272 | } 273 | } 274 | if labelsize != 0 { 275 | if debug > 2 { 276 | debuglogger.Logf("Additional section with non-empty name\n") 277 | } 278 | return packet, false 279 | } 280 | artype, ok := readShortInteger(buf) 281 | if !ok { 282 | return packet, false 283 | } 284 | if artype == types.OPT { 285 | packet.Edns = true 286 | packet.EdnsBufferSize, ok = readShortInteger(buf) 287 | if !ok { 288 | return packet, false 289 | } 290 | extrcode, ok := readInteger(buf) 291 | if !ok { 292 | return packet, false 293 | } 294 | ednslength, ok := readShortInteger(buf) 295 | if !ok { 296 | return packet, false 297 | } 298 | options := make([]byte, ednslength) 299 | if ednslength > 0 { 300 | n, error := buf.Read(options) 301 | if error != nil || n != int(ednslength) { 302 | if error == nil { 303 | // Client left after leaving only a few bytes 304 | return packet, false 305 | } else { 306 | if debug > 2 { 307 | debuglogger.Logf("Error in Read %d bytes: %s\n", n, error) 308 | } 309 | return packet, false 310 | } 311 | } 312 | over = false 313 | counter := 0 314 | for !over { 315 | optcode := binary.BigEndian.Uint16(options[counter : counter+2]) 316 | if optcode == types.NSID { 317 | packet.Nsid = true 318 | } 319 | optlen := int(binary.BigEndian.Uint16(options[counter+2 : counter+4])) 320 | if optlen > 0 { 321 | if counter+4+optlen > len(options) { 322 | return packet, false 323 | } 324 | _ = options[counter+4 : counter+4+optlen] // Yes, useless, I know 325 | } 326 | counter += (4 + optlen) 327 | if counter >= len(options) { 328 | over = true 329 | } 330 | if debug > 3 { 331 | debuglogger.Logf("EDNS option code %d\n", optcode) 332 | } 333 | 334 | } 335 | } 336 | if debug > 2 { 337 | debuglogger.Logf("EDNS0 found, buffer size is %d, extended rcode is %d, ", packet.EdnsBufferSize, extrcode) 338 | if ednslength > 0 { 339 | debuglogger.Logf("length of options is %d\n", ednslength) 340 | } else { 341 | debuglogger.Logf("no options\n") 342 | } 343 | } 344 | } else { 345 | if debug > 2 { 346 | debuglogger.Logf("Ignore additional section if not EDNS") 347 | } 348 | } 349 | } 350 | return packet, true 351 | } 352 | 353 | func generichandle(buf *bytes.Buffer, remaddr net.Addr) (response types.DNSpacket, noresponse bool) { 354 | var ( 355 | query types.DNSquery 356 | desiredresponse types.DNSresponse 357 | ) 358 | noresponse = true 359 | packet, valid := parse(buf) 360 | if !valid { // Invalid packet or client too impatient 361 | if debug > 3 { 362 | debuglogger.Logf("Invalid packet received\n") 363 | } 364 | return 365 | } 366 | if debug > 2 { 367 | debuglogger.Logf("%s\n", packet) 368 | } 369 | if packet.Query && packet.Opcode == types.STDQUERY { 370 | if debug > 2 { 371 | debuglogger.Logf("Replying with ID %d...\n", packet.Id) 372 | } 373 | noresponse = false 374 | response.Id = packet.Id 375 | response.Query = false 376 | response.Opcode = packet.Opcode 377 | response.Qdcount = 1 // Or packet.Qdcount ? 378 | response.Qsection = make([]types.Qentry, response.Qdcount) 379 | response.Qsection[0].Qname = packet.Qsection[0].Qname 380 | response.Qsection[0].Qclass = packet.Qsection[0].Qclass 381 | response.Qsection[0].Qtype = packet.Qsection[0].Qtype 382 | response.Edns = packet.Edns 383 | response.Nsid = packet.Nsid 384 | query.Client = remaddr 385 | query.Qname = strings.ToLower(packet.Qsection[0].Qname) 386 | query.Qclass = packet.Qsection[0].Qclass 387 | query.Qtype = packet.Qsection[0].Qtype 388 | if packet.Edns { 389 | query.BufferSize = packet.EdnsBufferSize 390 | response.EdnsBufferSize = packet.EdnsBufferSize 391 | } else { 392 | query.BufferSize = 512 // Traditional value 393 | response.EdnsBufferSize = 512 394 | } 395 | servernamei, nameexists := globalConfig["servername"] 396 | if query.Qclass == types.CH && query.Qtype == types.TXT && 397 | (query.Qname == "hostname.bind" || 398 | query.Qname == "id.server") && nameexists { 399 | servername := reflect.NewValue(servernamei).(*reflect.StringValue).Get() 400 | desiredresponse.Responsecode = types.NOERROR 401 | desiredresponse.Ansection = make([]types.RR, 1) 402 | desiredresponse.Ansection[0] = types.RR{ 403 | Name: query.Qname, 404 | TTL: defaultTTL, 405 | Type: types.TXT, 406 | Class: types.IN, 407 | Data: types.ToTXT(servername)} 408 | } else { 409 | desiredresponse = responder.Respond(query, globalConfig) 410 | } 411 | response.Rcode = desiredresponse.Responsecode 412 | response.Ancount = uint16(len(desiredresponse.Ansection)) 413 | if response.Ancount > 0 { 414 | response.Ansection = desiredresponse.Ansection 415 | } 416 | return 417 | } 418 | // Else, ignore the incoming query. May be we should reply REFUSED instead? 419 | noresponse = true 420 | return 421 | } 422 | 423 | func udphandle(conn *net.UDPConn, remaddr net.Addr, buf *bytes.Buffer) { 424 | var response types.DNSpacket 425 | if debug > 1 { 426 | debuglogger.Logf("%d bytes packet from %s\n", buf.Len(), remaddr) 427 | } 428 | response, noresponse := generichandle(buf, remaddr) 429 | if !noresponse { 430 | binaryresponse := serialize(response) 431 | _, error := conn.WriteTo(binaryresponse, remaddr) 432 | if error != nil { 433 | if debug > 2 { 434 | debuglogger.Logf("Error in Write: %s\n", error) 435 | return 436 | } 437 | } 438 | } 439 | // Else, ignore the incoming packet. May be we should reply REFUSED instead? 440 | } 441 | 442 | func tcphandle(connection net.Conn) { 443 | if debug > 1 { 444 | debuglogger.Logf("TCP connection accepted from %s\n", connection.RemoteAddr()) 445 | } 446 | smallbuf := make([]byte, 2) 447 | n, error := connection.Read(smallbuf) 448 | if error != nil { 449 | if debug > 2 { 450 | debuglogger.Logf("Cannot read message length from TCP connection: %s\n", error) 451 | return 452 | } 453 | } 454 | msglength := binary.BigEndian.Uint16(smallbuf) // RFC 1035, section 4.2.2 "TCP usage" 455 | message := make([]byte, msglength) 456 | n, error = connection.Read(message) 457 | if error != nil { 458 | if debug > 2 { 459 | debuglogger.Logf("Cannot read message from TCP connection with %s: %s\n", connection.RemoteAddr(), error) 460 | return 461 | } 462 | } 463 | if debug > 1 { 464 | debuglogger.Logf("%d bytes read from %s\n", n, connection.RemoteAddr()) 465 | } 466 | response, noresponse := generichandle(bytes.NewBuffer(message), connection.RemoteAddr()) 467 | if !noresponse { 468 | binaryresponse := serialize(response) 469 | shortbuf := make([]byte, 2) 470 | binary.BigEndian.PutUint16(shortbuf, uint16(len(binaryresponse))) 471 | n, error := connection.Write(shortbuf) 472 | if n != 2 || error != nil { 473 | if debug > 2 { 474 | debuglogger.Logf("Error in TCP message length Write: %s\n", error) 475 | return 476 | } 477 | } 478 | n, error = connection.Write(binaryresponse) 479 | if error != nil { 480 | if debug > 2 { 481 | debuglogger.Logf("Error in TCP message Write: %s\n", error) 482 | return 483 | } 484 | } 485 | } 486 | connection.Close() // In theory, we may have other requests. We clearly violate the RFC by not waiting for them. TODO 487 | } 488 | 489 | func tcpListener(address *net.TCPAddr, comm chan bool) { 490 | listener, error := net.ListenTCP("udp", address) 491 | checkError("Cannot listen", error) 492 | for { 493 | connection, error := listener.Accept() 494 | if error != nil { 495 | if debug > 1 { 496 | debuglogger.Logf("Cannot accept TCP connection: %s\n", error) 497 | continue 498 | } 499 | } 500 | go tcphandle(connection) 501 | } 502 | listener.Close() 503 | comm <- true 504 | } 505 | 506 | func udpListener(address *net.UDPAddr, comm chan bool) { 507 | listener, error := net.ListenUDP("udp", address) 508 | checkError("Cannot listen", error) 509 | for { 510 | message := make([]byte, 512) // 512 is a reasonable upper limit 511 | // for *incoming* queries 512 | n, remaddr, error := listener.ReadFrom(message) 513 | if error != nil { 514 | if debug > 1 { 515 | debuglogger.Logf("Cannot read UDP from %s: %s\n", remaddr.String(), error) 516 | continue 517 | } 518 | } 519 | buf := bytes.NewBuffer(message[0:n]) 520 | go udphandle(listener, remaddr, buf) 521 | } 522 | listener.Close() 523 | comm <- true 524 | } 525 | 526 | func main() { 527 | debugptr := flag.Int("debug", 0, "Set the debug level, the higher, the more verbose") 528 | nodaemonptr := flag.Bool("nodaemon", false, "Run in the foreground and not as a daemon") 529 | listen := flag.String("address", ":8053", "Set the port (+optional address) to listen at") 530 | nameptr := flag.String("servername", "", 531 | "Set the server name (and send it to clients)") 532 | helpptr := flag.Bool("help", false, "Displays usage instructions") 533 | zoneptr := flag.String("domain", "", "Set the name of the zone we are authoritative for") 534 | 535 | flag.Parse() 536 | help := *helpptr 537 | if help { 538 | fmt.Printf("Usage of %s:\n", os.Args[0]) 539 | flag.PrintDefaults() 540 | os.Exit(0) 541 | } 542 | globalConfig = make(map[string]interface{}) 543 | namemsg := "" 544 | if *nameptr != "" { 545 | globalConfig["servername"] = *nameptr 546 | namemsg = fmt.Sprintf(" %s", *nameptr) 547 | } 548 | zonemsg := "" 549 | if *zoneptr != "" { 550 | zone = strings.ToLower(*zoneptr) 551 | globalConfig["zonename"] = zone 552 | zonemsg = fmt.Sprintf(" on zone %s", zone) 553 | } 554 | debug = *debugptr 555 | globalConfig["debug"] = *debugptr 556 | globalConfig["daemon"] = !*nodaemonptr 557 | daemon = !reflect.NewValue(*nodaemonptr).(*reflect.BoolValue).Get() 558 | udpaddr, error := net.ResolveUDPAddr(*listen) 559 | checkError(fmt.Sprintf("Cannot parse \"%s\": %s\n", *listen), error) 560 | tcpaddr, error := net.ResolveTCPAddr(*listen) 561 | checkError(fmt.Sprintf("Cannot parse \"%s\": %s\n", *listen), error) 562 | if daemon { 563 | debuglogger = syslog.NewLogger(syslog.LOG_DEBUG, 564 | loggerOptions) 565 | infologger = syslog.NewLogger(syslog.LOG_NOTICE, 566 | loggerOptions) 567 | crisislogger = syslog.NewLogger(syslog.LOG_CRIT, 568 | loggerOptions) 569 | } else { 570 | debuglogger = log.New(os.Stderr, nil, "[DEBUG] ", 571 | loggerOptions) 572 | infologger = log.New(os.Stderr, nil, "[INFO] ", 573 | loggerOptions) 574 | crisislogger = log.New(os.Stderr, nil, "[FATAL] ", 575 | loggerOptions) 576 | } 577 | responder.Init(flag.LastOption()) 578 | infologger.Logf("%s", fmt.Sprintf("Starting%s%s...", namemsg, zonemsg)) 579 | udpchan := make(chan bool) 580 | go udpListener(udpaddr, udpchan) 581 | tcpchan := make(chan bool) 582 | go tcpListener(tcpaddr, tcpchan) 583 | 584 | <-udpchan // Just to wait the listener, otherwise, the Go runtime ends 585 | // even if there are live goroutines 586 | <-tcpchan 587 | infologger.Logf("%s", "Terminating...") 588 | } 589 | --------------------------------------------------------------------------------