diff --git a/build.sh b/build.sh index 419abfd..0285a05 100755 --- a/build.sh +++ b/build.sh @@ -1,7 +1,9 @@ #!/bin/bash [ -z "$1" ] && { echo "Usage: build.sh VERSION" >&1; exit 1; } + VERSION="$1" +DEST="$HOME/tmp/dingo-$VERSION" ############################################### @@ -11,14 +13,15 @@ function build() echo "Building dingo v. $VERSION for $TARGET" GOOS="${TARGET%-*}" GOARCH="${TARGET##*-}" go build \ - -o release/dingo-$VERSION/dingo-$TARGET \ - ./dingo.go ./gdns.go + -o $DEST/dingo-$TARGET \ + ./*.go } ############################################### -rm -fr ./release/dingo-$VERSION -mkdir -p ./release/dingo-$VERSION +echo "Building in $DEST" +rm -fr $DEST +mkdir -p $DEST for target in \ darwin-386 darwin-amd64 \ diff --git a/dingo.go b/dingo.go index 83cb180..931cb97 100644 --- a/dingo.go +++ b/dingo.go @@ -79,25 +79,31 @@ func register(name string, mod Module) *Module { /* UDP request handler */ func handle(buf []byte, addr *net.UDPAddr, uc *net.UDPConn) { - dbg(3, "new request from %s (%d bytes)", addr, len(buf)) - /* try unpacking */ msg := new(dns.Msg) - if err := msg.Unpack(buf); err != nil { dbg(3, "Unpack failed: %s", err); return } - dbg(7, "unpacked: %s", msg) + if err := msg.Unpack(buf); err != nil { + dbg(3, "unpack failed: %s", err) + return + } else { + dbg(7, "unpacked message: %s", msg) + } - /* for each question */ + /* any questions? */ if (len(msg.Question) < 1) { dbg(3, "no questions"); return } + qname := msg.Question[0].Name + qtype := msg.Question[0].Qtype + dbg(2, "resolving %s/%s", qname, dns.TypeToString[qtype]) + /* check cache */ var r Reply - cid := fmt.Sprintf("%s/%d", msg.Question[0].Name, msg.Question[0].Qtype) + cid := fmt.Sprintf("%s/%d", qname, qtype) if x, found := rcache.Get(cid); found { // FIXME: update TTLs r = x.(Reply) } else { /* pass to resolvers and block until the response comes */ - r = resolve(msg.Question[0].Name, int(msg.Question[0].Qtype)) + r = resolve(qname, int(qtype)) dbg(8, "got reply: %+v", r) /* put to cache for 10 seconds (FIXME: use minimum TTL) */ @@ -167,7 +173,7 @@ func main() { for _, mod := range Modules { mod.Start() } /* accept new connections forever */ - dbg(1, "dingo ver. 0.12-dev started on UDP port %d", laddr.Port) + dbg(1, "dingo ver. 0.12 listening on %s UDP port %d", *bindip, laddr.Port) var buf []byte for { buf = make([]byte, 1500) diff --git a/gdns.go b/gdns.go index 5979679..3a5a8ab 100644 --- a/gdns.go +++ b/gdns.go @@ -19,7 +19,7 @@ import "flag" type Gdns struct { workers *int server *string - noauto *bool + auto *bool sni *string host *string edns *string @@ -32,8 +32,8 @@ func (r *Gdns) Init() { "Google DNS: number of independent workers") r.server = flag.String("gdns:server", "216.58.195.78", "Google DNS: server address") - r.noauto = flag.Bool("gdns:noauto", false, - "Google DNS: dont try to lookup a closer server") + r.auto = flag.Bool("gdns:auto", false, + "Google DNS: try to lookup the closest IPv4 server") r.sni = flag.String("gdns:sni", "www.google.com", "Google DNS: SNI string to send (should match server certificate)") r.host = flag.String("gdns:host", "dns.google.com", @@ -49,11 +49,12 @@ func (r *Gdns) Init() { func (R *Gdns) Start() { if *R.workers <= 0 { return } - // FIXME: naive (IPv4, Answer[0].Data, etc?) - if !*R.noauto { + if *R.auto { dbg(1, "resolving dns.google.com...") r4 := R.resolve(NewHttps(*R.sni), *R.server, "dns.google.com", 1) - if r4.Status == 0 { R.server = &r4.Answer[0].Data } + if r4.Status == 0 && len(r4.Answer) > 0 { + R.server = &r4.Answer[0].Data + } } dbg(1, "starting %d Google Public DNS client(s) querying server %s", diff --git a/https.go b/https.go index 2a83985..033d9dd 100644 --- a/https.go +++ b/https.go @@ -13,7 +13,6 @@ import "net/http" import "io/ioutil" import "crypto/tls" import "errors" -//import "encoding/json" type Https struct { client http.Client diff --git a/odns.go b/odns.go index d152bbf..c06c10d 100644 --- a/odns.go +++ b/odns.go @@ -14,12 +14,6 @@ import "time" import "flag" import "github.com/miekg/dns" -type OdnsQS struct { - Qclass string - Qtype string - Qname string -} - type OdnsReply struct { ReturnCode string ID int @@ -28,10 +22,10 @@ type OdnsReply struct { RA bool RD bool TC bool - QuestionSection OdnsQS - AnswerSection []interface{} - AdditionalSection []interface{} - AuthoritySection []interface{} + QuestionSection map[string]interface{} + AnswerSection []map[string]interface{} + AdditionalSection []map[string]interface{} + AuthoritySection []map[string]interface{} } /***********************************************************/ @@ -41,6 +35,9 @@ type Odns struct { server *string sni *string host *string + + string2rcode map[string]int + string2rtype map[string]uint16 } func (R *Odns) Init() { @@ -52,8 +49,19 @@ func (R *Odns) Init() { "OpenDNS: TLS SNI string to send (unencrypted, must validate as server cert)") R.host = flag.String("odns:host", "api.openresolve.com", "OpenDNS: HTTP 'Host' header (real FQDN, encrypted in TLS)") + + R.string2rcode = make(map[string]int) + for rcode,str := range dns.RcodeToString { + R.string2rcode[str] = rcode + } + + R.string2rtype = make(map[string]uint16) + for rtype,str := range dns.TypeToString { + R.string2rtype[str] = rtype + } } +/* start OpenDNS workers */ func (R *Odns) Start() { if *R.workers <= 0 { return } @@ -62,11 +70,15 @@ func (R *Odns) Start() { for i := 0; i < *R.workers; i++ { go R.worker(*R.server) } } +/* handler of new requests */ func (R *Odns) worker(server string) { var https = NewHttps(*R.sni) - for q := range qchan { *q.rchan <- *R.resolve(https, server, q.Name, q.Type) } + for q := range qchan { + *q.rchan <- *R.resolve(https, server, q.Name, q.Type) + } } +/* resolve single request */ func (R *Odns) resolve(https *Https, server string, qname string, qtype int) *Reply { r := Reply{ Status: -1 } @@ -81,10 +93,99 @@ func (R *Odns) resolve(https *Https, server string, qname string, qtype int) *Re /* parse */ var f OdnsReply json.Unmarshal(buf, &f) - dbg(1, "TODO: %+v", f) + + /* rewrite */ + r.Status = R.string2rcode[f.ReturnCode] + r.TC = f.TC + r.RD = f.RD + r.RA = f.RA + r.AD = f.AD + r.CD = false + + for _,v := range f.AnswerSection { + rr := R.odns2grr(v) + if rr != nil { r.Answer = append(r.Answer, *rr) } + } + + for _,v := range f.AdditionalSection { + rr := R.odns2grr(v) + if rr != nil { r.Additional = append(r.Additional, *rr) } + } + + for _,v := range f.AuthoritySection { + rr := R.odns2grr(v) + if rr != nil { r.Authority = append(r.Authority, *rr) } + } return &r } +func (R *Odns) odns2grr(v map[string]interface{}) (*GRR) { + /* catch panics */ + defer func() { + if r := recover(); r != nil { dbg(1, "panic in odns2grr()") } + }() + + /* get basic data */ + rname := v["Name"].(string) + rtypes := v["Type"].(string) + rttl := uint32(v["TTL"].(float64)) + + /* parse type & data */ + var rdata string + var rtype uint16 + switch rtypes { + case "A": + rtype = dns.TypeA + rdata = v["Address"].(string) + case "AAAA": + rtype = dns.TypeAAAA + rdata = v["Address"].(string) + case "CNAME": + rtype = dns.TypeCNAME + rdata = v["Target"].(string) + case "MX": + rtype = dns.TypeMX + mx := v["MailExchanger"].(string) + pref := v["Preference"].(float64) + rdata = fmt.Sprintf("%d %s", int(pref), mx) + case "NS": + rtype = dns.TypeNS + rdata = v["Target"].(string) + case "NAPTR": + rtype = dns.TypeNAPTR + flg := v["Flags"].(string) + ord := v["Order"].(float64) + svc := v["Service"].(string) + prf := v["Preference"].(float64) + reg := v["Regexp"].(string) + rep := v["Replacement"].(string) + rdata = fmt.Sprintf("%d %d \"%s\" \"%s\" \"%s\" %s", + int(ord), int(prf), flg, svc, reg, rep) + case "PTR": + rtype = dns.TypePTR + rdata = v["Target"].(string) + case "SOA": + rtype = dns.TypeSOA + msn := v["MasterServerName"].(string) + mn := v["MaintainerName"].(string) + ser := v["Serial"].(float64) + ref := v["Refresh"].(float64) + ret := v["Retry"].(float64) + exp := v["Expire"].(float64) + nttl := v["NegativeTtl"].(float64) + rdata = fmt.Sprintf("%s %s %d %d %d %d %d", + msn, mn, int(ser), int(ref), int(ret), int(exp), int(nttl)) + case "TXT": + rtype = dns.TypeTXT + rdata = v["TxtData"].(string) + default: + dbg(1, "odns2grr(): %s unsupported", rtypes) + return nil + } + + return &GRR{rname, rtype, rttl, rdata} +} + /* register module */ var _ = register("odns", new(Odns))