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())
+}