From c9a9534a6fbe2615490a789ab18d246711463e80 Mon Sep 17 00:00:00 2001 From: eugene Date: Thu, 23 Apr 2020 09:16:30 -0400 Subject: [PATCH 1/2] go fmt --- wintun-testing/cziti/cziti-cli/main.go | 6 +++--- wintun-testing/cziti/dns.go | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/wintun-testing/cziti/cziti-cli/main.go b/wintun-testing/cziti/cziti-cli/main.go index 6acecdb4..d829da63 100644 --- a/wintun-testing/cziti/cziti-cli/main.go +++ b/wintun-testing/cziti/cziti-cli/main.go @@ -38,7 +38,7 @@ func main() { } } -var commands = make(map[string]func([]string)error) +var commands = make(map[string]func([]string) error) func init() { commands["quit"] = quit @@ -77,7 +77,7 @@ func quit(_ []string) error { return nil } -func help(_[]string) error { +func help(_ []string) error { fmt.Println("available commands:") for k, _ := range commands { fmt.Println("\t", k) @@ -108,4 +108,4 @@ func list(args []string) error { fmt.Printf("\t%d:\t%s@%s\n", i, c.Name(), c.Controller()) } return nil -} \ No newline at end of file +} diff --git a/wintun-testing/cziti/dns.go b/wintun-testing/cziti/dns.go index e7b1484e..73184bae 100644 --- a/wintun-testing/cziti/dns.go +++ b/wintun-testing/cziti/dns.go @@ -16,6 +16,7 @@ type DnsManager interface { DeregisterService(ctx *CZitiCtx, name string) GetService(ip net.IP, port uint16) (*CZitiCtx, string, error) } + const defaultCidr = "169.254.0.0" const defaultMaskBits = 16 @@ -23,7 +24,7 @@ var initOnce = sync.Once{} var DNS = &dnsImpl{} type dnsImpl struct { - cidr uint32 + cidr uint32 counter uint32 serviceMap map[string]ctxService @@ -35,7 +36,7 @@ type dnsImpl struct { } type ctxService struct { - ctx *CZitiCtx + ctx *CZitiCtx name string } @@ -69,11 +70,11 @@ func (dns *dnsImpl) RegisterService(dnsName string, port uint16, ctx *CZitiCtx, return nextIp, nil } -func (dns *dnsImpl)Resolve(dnsName string) net.IP { +func (dns *dnsImpl) Resolve(dnsName string) net.IP { return dns.hostnameMap[dnsName] } -func (dns *dnsImpl)DeregisterService(ctx *CZitiCtx, name string) { +func (dns *dnsImpl) DeregisterService(ctx *CZitiCtx, name string) { for k, sc := range dns.serviceMap { if sc.ctx == ctx && sc.name == name { delete(dns.serviceMap, k) @@ -82,7 +83,7 @@ func (dns *dnsImpl)DeregisterService(ctx *CZitiCtx, name string) { } } -func (this *dnsImpl)GetService(ip net.IP, port uint16) (*CZitiCtx, string, error) { +func (this *dnsImpl) GetService(ip net.IP, port uint16) (*CZitiCtx, string, error) { ipv4 := binary.BigEndian.Uint32(ip) dns, found := this.ipMap[ipv4] if !found { @@ -108,4 +109,3 @@ func DnsInit(ip string, maskBits int) { DNS.counter = 2 }) } - From 4e11ba7eb8b442b3f215664905733f851614c27f Mon Sep 17 00:00:00 2001 From: eugene Date: Fri, 24 Apr 2020 14:34:51 -0400 Subject: [PATCH 2/2] handle DNS on windows --- wintun-testing/build.sh | 3 +- wintun-testing/cziti/ctun.go | 61 ++------ wintun-testing/cziti/dns-proxy.go | 202 ++++++++++++++++++++++++++ wintun-testing/cziti/windns/windns.go | 73 ++++++++++ wintun-testing/ziti-wintun/main.go | 67 +++++---- 5 files changed, 324 insertions(+), 82 deletions(-) create mode 100644 wintun-testing/cziti/dns-proxy.go create mode 100644 wintun-testing/cziti/windns/windns.go diff --git a/wintun-testing/build.sh b/wintun-testing/build.sh index 7271604c..e327b06f 100644 --- a/wintun-testing/build.sh +++ b/wintun-testing/build.sh @@ -26,5 +26,6 @@ cp ./install/lib/libuv.dll . export CGO_CFLAGS="-DNOGDI -I $(cygpath -a -m ./install/include)" export CGO_LDFLAGS="-L $(cygpath -a -m ./install/lib)" -go build -a ./ziti-wintun +rm ./ziti-wintun.exe +go build -v ./ziti-wintun diff --git a/wintun-testing/cziti/ctun.go b/wintun-testing/cziti/ctun.go index f90d5d45..a4039994 100644 --- a/wintun-testing/cziti/ctun.go +++ b/wintun-testing/cziti/ctun.go @@ -32,14 +32,11 @@ import "C" import ( "errors" "fmt" - "github.com/miekg/dns" "golang.zx2c4.com/wireguard/tun" "net" "unsafe" ) -const dnsIP = "169.254.1.253" - type Tunnel interface { AddIntercept(service string, hostname string, port int, ctx unsafe.Pointer) } @@ -61,7 +58,7 @@ type tunnel struct { var devMap = make(map[string]*tunnel) -func HookupTun(dev tun.Device) (Tunnel, error) { +func HookupTun(dev tun.Device, dns []net.IP) (Tunnel, error) { fmt.Println("in HookupTun ") defer fmt.Println("exiting HookupTun") name, err := dev.Name() @@ -89,9 +86,7 @@ func HookupTun(dev tun.Device) (Tunnel, error) { t.tunCtx = C.NF_tunneler_init(opts, _impl.libuvCtx.l) - dnsServer := C.CString(dnsIP) - rc := C.NF_udp_handler(t.tunCtx, dnsServer, C.int(53), C.ziti_udp_cb(C.dnsHandler), unsafe.Pointer(nil)) - fmt.Println("registered UDP handler = ", int64(rc)) + go runDNSserver(dns) return t, nil } @@ -241,47 +236,19 @@ func (t *tunnel) AddIntercept(service string, host string, port int, ctx unsafe. // extern void dnsHandler(tunneler_io_context tio, void *ctx, addr_t src, uint16_t sport, void *data, ssize_t len); //export dnsHandler func dnsHandler(tio C.tunneler_io_context, ctx unsafe.Pointer, src C.addr_t, sport C.uint16_t, b unsafe.Pointer, bl C.ssize_t) { - q := &dns.Msg{} - if err := q.Unpack(C.GoBytes(b, C.int(bl))); err != nil { - fmt.Println("error", err) - } + /* + reply, err := processDNSquery(C.GoBytes(b, C.int(bl))) - msg := dns.Msg{} - msg.SetReply(q) - msg.RecursionAvailable = false - - query := q.Question[0] - var ip net.IP - if query.Qtype == dns.TypeA { - ip = DNS.Resolve(query.Name) - } - - if ip != nil { - msg.Authoritative = true - msg.Rcode = dns.RcodeSuccess - answer := &dns.A{ - Hdr: dns.RR_Header{Name: query.Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 60}, - A: ip, + if err == nil { + replyC := C.CBytes(reply) + rc := C.NF_udp_send(tio, src, sport, replyC, C.ssize_t(len(reply))) + C.free(replyC) + if rc != 0 { + fmt.Println("NF_udp_reply rc=", rc) + } + } else { + fmt.Println("dns message error", err) } - msg.Answer = append(msg.Answer, answer) - } else { - // msg.Rcode = dns.RcodeNotImplemented - msg.Authoritative = false - msg.Rcode = dns.RcodeRefused - } - - reply, err := msg.Pack() - if err == nil { - replyC := C.CBytes(reply) - rc := C.NF_udp_send(tio, src, sport, replyC, C.ssize_t(len(reply))) - C.free(replyC) - if rc != 0 { - fmt.Println("NF_udp_reply rc=", rc) - } - } else { - fmt.Println("dns message error", err) - } + */ } - -/*******************************************************************/ diff --git a/wintun-testing/cziti/dns-proxy.go b/wintun-testing/cziti/dns-proxy.go new file mode 100644 index 00000000..bf38810d --- /dev/null +++ b/wintun-testing/cziti/dns-proxy.go @@ -0,0 +1,202 @@ +package cziti + +import ( + "fmt" + "github.com/miekg/dns" + "net" + "wintun-testing/cziti/windns" +) + +func processDNSquery(packet []byte, p *net.UDPAddr, s *net.UDPConn) { + q := &dns.Msg{} + if err := q.Unpack(packet); err != nil { + fmt.Println("ERROR", err) + return + } + + msg := dns.Msg{} + msg.SetReply(q) + msg.RecursionAvailable = false + msg.Authoritative = false + msg.Rcode = dns.RcodeRefused + + query := q.Question[0] + var ip net.IP + // fmt.Printf("query: Type(%d) name(%s)\n", query.Qtype, query.Name) + + ip = DNS.Resolve(query.Name) + + // never proxy hostnames that we know about regardless of type + if ip != nil { + fmt.Println("resolved ", query.Name, ip) + + var answer *dns.A + if query.Qtype == dns.TypeA && len(ip.To4()) == net.IPv4len { + answer = &dns.A{ + Hdr: dns.RR_Header{Name: query.Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 60}, + A: ip, + } + } else if query.Qtype == dns.TypeAAAA { + answer = &dns.A{ + Hdr: dns.RR_Header{Name: query.Name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 60}, + A: ip.To16(), + } + } + + if answer != nil { + msg.Authoritative = true + msg.Rcode = dns.RcodeSuccess + + msg.Answer = append(msg.Answer, answer) + } + + repB, err := msg.Pack() + if err == nil { + _, _, err = s.WriteMsgUDP(repB, nil, p) + } + if err != nil { + fmt.Println("dns error", err) + } + } else { + // fmt.Println("proxying ", dns.Type(query.Qtype), query.Name, q.Id, " for ", p) + proxyDNS(q, p, s) + } +} + +type dnsreq struct { + q []byte + s *net.UDPConn + p *net.UDPAddr +} + +func runDNSserver(dnsBind []net.IP) { + dnsServers := windns.GetUpstreamDNS() + go runDNSproxy(dnsServers) + + reqch := make(chan dnsreq) + + for _, bindAddr := range dnsBind { + go runListener(bindAddr, 53, reqch) + } + + windns.ReplaceDNS(dnsBind) + + for req := range reqch { + processDNSquery(req.q, req.p, req.s) + } +} + +func runListener(ip net.IP, port int, reqch chan dnsreq) { + laddr := &net.UDPAddr{ + IP: ip, + Port: 53, + Zone: "", + } + + network := "udp6" + if len(ip.To4()) == net.IPv4len { + network = "udp4" + } + fmt.Println(network, ip) + server, err := net.ListenUDP(network, laddr) + if err != nil { + panic(err) + } + + // oob := make([]byte, 1024) + for { + b := make([]byte, 1024) + nb, _, _, peer, err := server.ReadMsgUDP(b, nil) + if err != nil { + panic(err) + } + reqch <- dnsreq{ + q: b[:nb], + s: server, + p: peer, + } + } +} + +/*******************************************************************/ +type proxiedReq struct { + req *dns.Msg + peer *net.UDPAddr + s *net.UDPConn +} + +var proxiedRequests chan *proxiedReq + +func proxyDNS(req *dns.Msg, peer *net.UDPAddr, serv *net.UDPConn) { + proxiedRequests <- &proxiedReq{ + req: req, + peer: peer, + s: serv, + } +} + +func runDNSproxy(dnsServers []string) { + var dnsUpstreams []*net.UDPConn + for _, s := range dnsServers { + sAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:53", s)) + if err != nil { + fmt.Println("skipping upstream", s, err.Error()) + } else { + conn, err := net.DialUDP("udp", nil, sAddr) + if err != nil { + fmt.Println("skipping upstream", s, err.Error()) + } + dnsUpstreams = append(dnsUpstreams, conn) + } + } + + proxiedRequests = make(chan *proxiedReq, 16) + respChan := make(chan []byte, 16) + + for _, proxy := range dnsUpstreams { + go func() { + for { + resp := make([]byte, 1024) + n, err := proxy.Read(resp) + if err != nil { + fmt.Println("error receiving from ", proxy.RemoteAddr(), err) + } else { + respChan <- resp[:n] + } + } + }() + } + + reqs := make(map[uint32]*proxiedReq) + + for { + select { + case pr := <-proxiedRequests: + id := (uint32(pr.req.Id) << 16) | uint32(pr.req.Question[0].Qtype) + reqs[id] = pr + b, _ := pr.req.Pack() + // fmt.Println("sending proxy req", id, dns.Type(pr.req.Question[0].Qtype), pr.req.Question[0].Name) + for _, proxy := range dnsUpstreams { + // fmt.Println("sending proxy req", dns.Type(pr.req.Question[0].Qtype), pr.req.Question[0].Name, proxy.RemoteAddr()) + if _, err := proxy.Write(b); err != nil { + fmt.Println("failed to proxy DNS to ", proxy) + } + } + + case rep := <-respChan: + reply := dns.Msg{} + if err := reply.Unpack(rep); err == nil { + id := (uint32(reply.Id) << 16) | uint32(reply.Question[0].Qtype) + req, found := reqs[id] + if found { + delete(reqs, id) + // fmt.Printf("proxy resolved %+v for %v\n\n", reply, req.peer) + req.s.WriteMsgUDP(rep, nil, req.peer) + } else { + fmt.Println("matching request was not found for ", + dns.Type(reply.Question[0].Qtype), reply.Question[0].Name) + } + } + } + } +} diff --git a/wintun-testing/cziti/windns/windns.go b/wintun-testing/cziti/windns/windns.go new file mode 100644 index 00000000..e33116c4 --- /dev/null +++ b/wintun-testing/cziti/windns/windns.go @@ -0,0 +1,73 @@ +package windns + +import ( + "bytes" + "fmt" + "net" + "os" + "os/exec" + "strings" +) + +func ResetDNS() { + fmt.Println("Resetting DNS to original-ish state") + + script := `Get-NetIPInterface | ForEach-Object { Set-DnsClientServerAddress -InterfaceIndex $_.ifIndex -ResetServerAddresses }` + + cmd := exec.Command("powershell", "-Command", script) + cmd.Stderr = os.Stdout + cmd.Stdout = os.Stdout + + err := cmd.Run() + if err != nil { + fmt.Println("ERROR resetting DNS", err) + } +} + +func GetUpstreamDNS() []string { + ResetDNS() + + script := `Get-DnsClientServerAddress | ForEach-Object { $_.ServerAddresses } | Sort-Object | Get-Unique` + + cmd := exec.Command("powershell", "-Command", script) + cmd.Stderr = os.Stdout + output := new(bytes.Buffer) + cmd.Stdout = output + + err := cmd.Run() + + if err != nil { + panic(err) + } + + var names []string + for { + l, err := output.ReadString('\n') + if err != nil { + break + } + addr := net.ParseIP(strings.TrimSpace(l)) + if !addr.IsLoopback() { + names = append(names, addr.String()) + } + } + return names +} + +func ReplaceDNS(ips []net.IP) { + var names []string + for _, i := range ips { + names = append(names, i.String()) + } + addresses := strings.Join(names, ",") + script := fmt.Sprintf( + `Get-NetIPInterface | ForEach-Object { Set-DnsClientServerAddress -InterfaceIndex $_.ifIndex -ServerAddresses %s }`, + addresses) + cmd := exec.Command("powershell", "-Command", script) + cmd.Stderr = os.Stderr + cmd.Stdout = os.Stdout + + if err := cmd.Run(); err != nil { + fmt.Println("ERROR", err) + } +} diff --git a/wintun-testing/ziti-wintun/main.go b/wintun-testing/ziti-wintun/main.go index 5c4b9a7e..81031796 100644 --- a/wintun-testing/ziti-wintun/main.go +++ b/wintun-testing/ziti-wintun/main.go @@ -7,16 +7,28 @@ import ( "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" "net" "os" - "os/exec" "os/signal" user2 "os/user" "path/filepath" "syscall" "wintun-testing/cziti" + "wintun-testing/cziti/windns" ) const bufferSize = 64 * 1024 +// TUN IPs +const ipv4ip = "169.254.1.1" +const ipv4mask = 24 +const ipv4dns = "127.0.0.1" // use lo -- don't pass DNS queries through tunneler SDK + +// IPv6 CIDR fe80:6e66:7a69:7469::/64 +// : nf : zi : ti :: +const ipv6pfx = "fe80:6e66:7a69:7469" +const ipv6ip = "1" +const ipv6mask = 64 +const ipv6dns = "::1" // must be in "ipv6ip/ipv6mask" CIDR block + func main() { user, err := user2.Current() @@ -29,7 +41,7 @@ func main() { interfaceName := os.Args[1] fmt.Println("creating TUN device") - tunDevice, err := tun.CreateTUN(interfaceName, 64 * 1024) + tunDevice, err := tun.CreateTUN(interfaceName, 64*1024) if err == nil { realInterfaceName, err2 := tunDevice.Name() if err2 == nil { @@ -47,7 +59,7 @@ func main() { nativeTunDevice := tunDevice.(*tun.NativeTun) luid := winipcfg.LUID(nativeTunDevice.LUID()) - ip, ipnet, err := net.ParseCIDR("169.254.1.1/24") + ip, ipnet, err := net.ParseCIDR(fmt.Sprintf("%s/%d", ipv4ip, ipv4mask)) if err != nil { fatal(err) } @@ -56,7 +68,20 @@ func main() { if err != nil { fatal(err) } - fmt.Printf("TUN interface address set to [%s]\n", ip) + + dnsServers := []net.IP{ + net.ParseIP(ipv4dns).To4(), + net.ParseIP(ipv6dns), + } + err = luid.AddDNS(dnsServers) + if err != nil { + fatal(err) + } + dns, err := luid.DNS() + if err != nil { + fatal(err) + } + fmt.Println("dns servers = ", dns) fmt.Printf("routing destination [%s] through [%s]\n", *ipnet, ipnet.IP) err = luid.SetRoutes([]*winipcfg.RouteData{{*ipnet, ipnet.IP, 0}}) @@ -69,47 +94,19 @@ func main() { term := make(chan os.Signal, 1) fmt.Println("running") - cziti.DnsInit("169.254.1.1", 24) + cziti.DnsInit(ipv4ip, 24) cziti.Start() - _, err = cziti.HookupTun(tunDevice) + _, err = cziti.HookupTun(tunDevice, dns) if err != nil { panic(err) } - cmd := exec.Command("netsh", "interface", "ipv4", "set", - "dnsservers", "name="+interfaceName, - "source=static", "address=169.254.1.253", - "register=primary", "validate=no") - cmd.Stderr = os.Stderr - cmd.Stdout = os.Stdout - err = cmd.Run() - if err != nil { - fmt.Print(err) - } - if ctx, err := cziti.LoadZiti(os.Args[2]); err != nil { panic(err) } else { fmt.Printf("successfully loaded %s@%s\n", ctx.Name(), ctx.Controller()) } - //tunnel.AddIntercept("awesome sauce service", "169.254.1.42", 8080) - /* - for { - buffer := make([]byte, bufferSize) - n, err := tunDevice.Read(buffer, 0) - if err != nil { - fatal(err) - } - printPacket(buffer[:n]) - - //fmt.Printf("read [%d] bytes [", n) - //for i := 0; i < n; i++ { - // fmt.Printf("%x ", buffer[i]) - //} - //fmt.Println("]") - } - */ signal.Notify(term, os.Interrupt) signal.Notify(term, os.Kill) @@ -120,6 +117,8 @@ func main() { case <-errs: } + windns.ResetDNS() + fmt.Println("shutting down") }