Skip to content

Commit

Permalink
dingo 0.12
Browse files Browse the repository at this point in the history
 * modular architecture
 * support for OpenDNS through www.openresolve.com (odns)
 * common HTTPS handling code in https.go (QUIC support ahead)
  • Loading branch information
pforemski committed Oct 16, 2016
1 parent c23c3ad commit 118eba9
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 31 deletions.
11 changes: 7 additions & 4 deletions build.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#!/bin/bash

[ -z "$1" ] && { echo "Usage: build.sh VERSION" >&1; exit 1; }

VERSION="$1"
DEST="$HOME/tmp/dingo-$VERSION"

###############################################

Expand All @@ -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 \
Expand Down
22 changes: 14 additions & 8 deletions dingo.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) */
Expand Down Expand Up @@ -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)
Expand Down
13 changes: 7 additions & 6 deletions gdns.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import "flag"
type Gdns struct {
workers *int
server *string
noauto *bool
auto *bool
sni *string
host *string
edns *string
Expand All @@ -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",
Expand All @@ -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",
Expand Down
1 change: 0 additions & 1 deletion https.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import "net/http"
import "io/ioutil"
import "crypto/tls"
import "errors"
//import "encoding/json"

type Https struct {
client http.Client
Expand Down
125 changes: 113 additions & 12 deletions odns.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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{}
}

/***********************************************************/
Expand All @@ -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() {
Expand All @@ -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 }

Expand All @@ -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 }

Expand All @@ -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))

0 comments on commit 118eba9

Please sign in to comment.