Mercurial > lbo > hg > dnsoverrpc
changeset 0:d31b0316199e
Initial working commit
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Thu, 01 Oct 2020 11:09:51 +0200 |
parents | |
children | f9cbcf818af6 |
files | .hgignore README.md client/client.go go.mod go.sum server/server.go |
diffstat | 5 files changed, 298 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README.md Thu Oct 01 11:09:51 2020 +0200 @@ -0,0 +1,3 @@ +# dnsoverrpc + +tunnels DNS queries via `clusterrpc`.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/client.go Thu Oct 01 11:09:51 2020 +0200 @@ -0,0 +1,119 @@ +package main + +import ( + "flag" + "log" + "net" + "strconv" + + "golang.org/x/net/dns/dnsmessage" + + "github.com/dermesser/clusterrpc/client" + rpclog "github.com/dermesser/clusterrpc/log" + "github.com/dermesser/clusterrpc/securitymanager" +) + +func extractQueryHost(pkg []byte) (string, error) { + p := dnsmessage.Parser{} + _, err := p.Start(pkg) + if err != nil { + return "", err + } + hosts := "" + for { + q, err := p.Question() + if err != nil && err != dnsmessage.ErrSectionDone { + return "", err + } + if err == dnsmessage.ErrSectionDone { + break + } + hosts += q.Name.String() + " " + } + return hosts, nil +} + +type dnsclient struct { + rpccl *client.Client + listener *net.UDPConn +} + +func (dc *dnsclient) run() { + pkg := make([]byte, 1500) + + for { + sz, from, err := dc.listener.ReadFrom(pkg) + if err != nil { + log.Print(err) + continue + } + log.Println("Received", sz, "bytes from", from) + + hosts, err := extractQueryHost(pkg[0:sz]) + if err != nil { + log.Print(err) + continue + } + log.Print(hosts) + + resp, err := dc.rpccl.Request(pkg[0:sz], "DNSOverRPC", "Resolve", nil) + if err != nil { + log.Print(err) + continue + } + + sz, err = dc.listener.WriteTo(resp, from) + if err != nil { + log.Print(err) + continue + } + if sz < len(resp) { + log.Print("truncated send:", sz, "<", len(resp)) + } + } +} + +func main() { + log.SetFlags(log.LstdFlags | log.Lshortfile) + rpclog.SetLoglevel(rpclog.LOGLEVEL_INFO) + + addr := flag.String("addr", "127.0.0.1:5353", "Listen address for DNS stub") + serverAddr := flag.String("server", "127.0.0.1:53", "Upstream resolver address") + pubkeyfile := flag.String("pubkeyfile", "", "Public key file for RPC encryption") + privkeyfile := flag.String("privkeyfile", "", "Private key file for RPC encryption") + flag.Parse() + + sm := securitymanager.NewClientSecurityManager() + sm.LoadKeys(*pubkeyfile, *privkeyfile) + + if *pubkeyfile == "" || *privkeyfile == "" { + sm = nil + } + + host, port, err := net.SplitHostPort(*serverAddr) + if err != nil { + log.Fatal(err) + } + iport, err := strconv.Atoi(port) + if err != nil { + log.Fatal(err) + } + + peer := client.Peer(host, uint(iport)) + ch, err := client.NewChannelAndConnect(peer, sm) + if err != nil { + log.Fatal(err) + } + cl := client.New("Main", ch) + + ua, err := net.ResolveUDPAddr("udp", *addr) + if err != nil { + log.Fatal(err) + } + uc, err := net.ListenUDP("udp", ua) + if err != nil { + log.Fatal(err) + } + dc := dnsclient{rpccl: &cl, listener: uc} + dc.run() +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/go.mod Thu Oct 01 11:09:51 2020 +0200 @@ -0,0 +1,8 @@ +module github.com/dermesser/dnsoverrpc + +go 1.14 + +require ( + github.com/dermesser/clusterrpc v0.1.1 + golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 +)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/go.sum Thu Oct 01 11:09:51 2020 +0200 @@ -0,0 +1,20 @@ +github.com/dermesser/clusterrpc v0.1.0 h1:aLqhyuzHYCJWlC6AgVJOPTX9JLUcM6LjWxlE1v4XEYU= +github.com/dermesser/clusterrpc v0.1.0/go.mod h1:fTvk9LKIf++inkSQusY9Z4WYwATKeTsvzcs/XxG7ACw= +github.com/dermesser/clusterrpc v0.1.1 h1:/WjD641Ni8WFNSnHTbazR0IY/6lWSCFf8Ub3GbqxqVM= +github.com/dermesser/clusterrpc v0.1.1/go.mod h1:fTvk9LKIf++inkSQusY9Z4WYwATKeTsvzcs/XxG7ACw= +github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/pebbe/zmq4 v1.2.1 h1:jrXQW3mD8Si2mcSY/8VBs2nNkK/sKCOEM0rHAfxyc8c= +github.com/pebbe/zmq4 v1.2.1/go.mod h1:7N4y5R18zBiu3l0vajMUWQgZyjv464prE8RCyBcmnZM= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 h1:YfxMZzv3PjGonQYNUaeU2+DhAdqOxerQ30JFB6WgAXo= +golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/server/server.go Thu Oct 01 11:09:51 2020 +0200 @@ -0,0 +1,148 @@ +package main + +import ( + "flag" + "log" + "net" + "strconv" + + "golang.org/x/net/dns/dnsmessage" + + rpclog "github.com/dermesser/clusterrpc/log" + "github.com/dermesser/clusterrpc/securitymanager" + "github.com/dermesser/clusterrpc/server" +) + +func extractQueryHost(pkg []byte) (string, error) { + p := dnsmessage.Parser{} + _, err := p.Start(pkg) + if err != nil { + return "", err + } + hosts := "" + for { + q, err := p.Question() + if err != nil && err != dnsmessage.ErrSectionDone { + return "", err + } + if err == dnsmessage.ErrSectionDone { + break + } + hosts += q.Name.String() + " " + } + return hosts, nil +} + +// Internal request to the serializer. +type request struct { + pkg []byte + resp chan []byte +} + +// Serialize queries to simplify matching. Acts as a server via req channel. +type serializer struct { + upstream string + conn *net.UDPConn + + reqs chan request +} + +// Serve requests one by one +func (s *serializer) run() { + if s.conn == nil { + ua, err := net.ResolveUDPAddr("udp", s.upstream) + if err != nil { + log.Fatal(err) + } + conn, err := net.DialUDP("udp", nil, ua) + if err != nil { + log.Fatal(err) + } + s.conn = conn + } + + for req := range s.reqs { + pkg := req.pkg + respch := req.resp + + hosts, err := extractQueryHost(pkg) + if err != nil { + log.Print("Invalid DNS package:", err) + respch <- nil + continue + } + log.Print("Querying:", hosts) + + sz, err := s.conn.Write(pkg) + if err != nil { + log.Print(err) + respch <- nil + continue + } + if sz != len(pkg) { + log.Println("Warning: Wrote only", sz, "of", len(pkg), "bytes!") + } + dst := make([]byte, 1500) + sz, err = s.conn.Read(dst) + if err != nil { + log.Print(err) + respch <- nil + continue + } + respch <- dst[0:sz] + } +} + +type resolver struct { + s *serializer +} + +func (r *resolver) handle(c *server.Context) { + pkg := c.GetInput() + + ch := make(chan []byte) + r.s.reqs <- request{pkg, ch} + resp := <-ch + if resp == nil { + c.Fail("Lookup didn't succeed") + return + } + c.Success(resp) +} + +func main() { + log.SetFlags(log.LstdFlags | log.Lshortfile) + rpclog.SetLoglevel(rpclog.LOGLEVEL_INFO) + + addr := flag.String("addr", "127.0.0.1:5555", "Listen address for RPC server") + upstream := flag.String("upstream", "127.0.0.1:53", "Upstream resolver address") + pubkeyfile := flag.String("pubkeyfile", "", "Public key file for RPC encryption") + privkeyfile := flag.String("privkeyfile", "", "Private key file for RPC encryption") + flag.Parse() + + sm := securitymanager.NewServerSecurityManager() + sm.LoadKeys(*pubkeyfile, *privkeyfile) + if *pubkeyfile == "" || *privkeyfile == "" { + sm = nil + } + + host, port, err := net.SplitHostPort(*addr) + if err != nil { + log.Fatal(err) + } + iport, err := strconv.Atoi(port) + if err != nil { + log.Fatal(err) + } + srv, err := server.NewServer(host, uint(iport), 2, nil) + if err != nil { + log.Fatal(err) + } + + s := &serializer{upstream: *upstream, reqs: make(chan request)} + go s.run() + r := &resolver{s: s} + srv.RegisterHandler("DNSOverRPC", "Resolve", r.handle) + + log.Println(srv.Start()) +}