From b6df69358aefe5a2a5a14680a0c2a6285554aef2 Mon Sep 17 00:00:00 2001 From: wweir Date: Sat, 2 Feb 2019 06:21:51 +0800 Subject: [PATCH] Fix #2, add dhcp dynamic setting upstream dns, ipv4 only --- README.md | 2 +- conf/sower.toml | 3 +- dns/dhcp.go | 85 ++++++++++++++++++++++++++++++++++++++++++++++++ dns/dhcp_test.go | 21 ++++++++++++ dns/dns.go | 26 +++++++++++---- go.mod | 1 + go.sum | 2 ++ 7 files changed, 132 insertions(+), 8 deletions(-) create mode 100644 dns/dhcp.go create mode 100644 dns/dhcp_test.go diff --git a/README.md b/README.md index 9f87805..b164494 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ If you wanna uninstall sower, change `install` into `uninstall` and rerun the co ### Docker deploy The auto build docker images are [wweir/sower](https://hub.docker.com/r/wweir/sower). -It is very simple to use it on the server side. Map the port and run it directly. +It is very simple to use it on the server side. Export the port(5533) and run it directly. But the client is more troublesome and needs some understanding of the working mechanism of the sower. diff --git a/conf/sower.toml b/conf/sower.toml index 3291ffd..b07c346 100644 --- a/conf/sower.toml +++ b/conf/sower.toml @@ -4,7 +4,7 @@ password="12345678" server_port="5533" # server_addr="remote-server:5533" # replce it to remote server http_proxy=":8080" # eg: 192.168.0.2:8080 -dns_server="223.5.5.5" # Alibaba public dns +dns_server="" # Keep empty for dynamic setting from net env client_ip="127.0.0.1" # listen the IP, dns target is the IP # clear_dns_cache="killall -HUP mDNSResponder" blocklist=[ @@ -22,6 +22,7 @@ blocklist=[ "**.twimg.com", "**.blogspot.com", # blogspot "**.wikipedia.org", # wikipeida + "*.cloudfront.net", ] whitelist=[ "iamp.*.*.*", diff --git a/dns/dhcp.go b/dns/dhcp.go new file mode 100644 index 0000000..7932a03 --- /dev/null +++ b/dns/dhcp.go @@ -0,0 +1,85 @@ +package dns + +import ( + "math/rand" + "net" + + "github.com/golang/glog" + "github.com/wweir/netboot/dhcp4" +) + +func GetDefaultDNSServer() string { + xid := make([]byte, 4) + rand.Read(xid) + + pack := &dhcp4.Packet{ + Type: dhcp4.MsgDiscover, + TransactionID: xid, + Broadcast: true, + } + options := map[dhcp4.Option][]byte{ + dhcp4.OptRequestedOptions: []byte{byte(dhcp4.OptDNSServers)}, + } + + ifaces := mustGetInterfaces() + for _, iface := range ifaces { + conn, err := dhcp4.NewConn(iface.IP.String() + ":68") + if err != nil { + glog.Errorln(err) + continue + } + defer conn.Close() + + pack.HardwareAddr = iface.Interface.HardwareAddr + options[dhcp4.OptClientIdentifier] = iface.Interface.HardwareAddr + pack.Options = dhcp4.Options(options) + + if err := conn.SendDHCP(pack, iface.Interface); err != nil { + glog.Errorln(err) + continue + } + + pack, _, err = conn.RecvDHCP() + if err != nil { + glog.Errorln(err) + continue + } + + ip, err := pack.Options.IP(dhcp4.OptDNSServers) + if err != nil { + glog.Errorln(err) + continue + } + return ip.String() + } + return "" +} + +type netIface struct { + *net.Interface + net.Addr + net.IP +} + +func mustGetInterfaces() []*netIface { + ifaces, err := net.Interfaces() + if err != nil { + glog.Fatalln(err) + } + + v4Iface := make([]*netIface, 0, len(ifaces)) + for i := range ifaces { + if len(ifaces[i].HardwareAddr) == 0 { + continue + } + + addrs, _ := ifaces[i].Addrs() + for _, addr := range addrs { + if ip := addr.(*net.IPNet).IP.To4(); ip != nil { + v4Iface = append(v4Iface, &netIface{&ifaces[i], addr, ip}) + break + } + } + } + return v4Iface +} diff --git a/dns/dhcp_test.go b/dns/dhcp_test.go new file mode 100644 index 0000000..18b9a8b --- /dev/null +++ b/dns/dhcp_test.go @@ -0,0 +1,21 @@ +package dns + +import "testing" + +func TestGetDefaultDNSServer(t *testing.T) { + t.Skip("for net env") + tests := []struct { + name string + want string + }{{ + "", + "192.168.1.1", + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := GetDefaultDNSServer(); got != tt.want { + t.Errorf("GetDefaultDNSServer() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/dns/dns.go b/dns/dns.go index 7c7a666..ab7b4a7 100644 --- a/dns/dns.go +++ b/dns/dns.go @@ -18,7 +18,9 @@ func StartDNS(dnsServer, listenIP string) { ip := net.ParseIP(listenIP) suggest := &intelliSuggest{listenIP, 2 * time.Second, []string{"80", "443"}} mem.DefaultCache = mem.New(time.Hour) - dnsServer = net.JoinHostPort(dnsServer, "53") + if dnsServer != "" { + dnsServer = net.JoinHostPort(dnsServer, "53") + } dns.HandleFunc(".", func(w dns.ResponseWriter, r *dns.Msg) { // *Msg r has an TSIG record and it was validated @@ -37,15 +39,15 @@ func StartDNS(dnsServer, listenIP string) { domain = domain[:idx] } - matchAndServe(w, r, domain, listenIP, dnsServer, ip, suggest) + matchAndServe(w, r, domain, listenIP, &dnsServer, ip, suggest) }) server := &dns.Server{Addr: net.JoinHostPort(listenIP, "53"), Net: "udp"} glog.Fatalln(server.ListenAndServe()) } -func matchAndServe(w dns.ResponseWriter, r *dns.Msg, domain, listenIP, - dnsServer string, ipNet net.IP, suggest *intelliSuggest) { +func matchAndServe(w dns.ResponseWriter, r *dns.Msg, domain, listenIP string, + dnsServer *string, ipNet net.IP, suggest *intelliSuggest) { inWriteList := whiteList.Match(domain) @@ -59,7 +61,19 @@ func matchAndServe(w dns.ResponseWriter, r *dns.Msg, domain, listenIP, go mem.Remember(suggest, domain) } - msg, err := dns.Exchange(r, dnsServer) + if *dnsServer == "" { + host := GetDefaultDNSServer() + if host == "" { + return + } + *dnsServer = net.JoinHostPort(host, "53") + glog.Infoln("set dns server to", host) + } + + msg, err := dns.Exchange(r, *dnsServer) + if err != nil { + *dnsServer = "" + } if msg == nil { // expose any response except nil glog.V(1).Infof("get dns of %s fail: %s", domain, err) return @@ -84,7 +98,7 @@ func (i *intelliSuggest) GetOne(domain interface{}) (iface interface{}, e error) for _, port := range i.ports { // give local dial a hand, make it not so easy to be added into suggestions - <-util.HTTPPing(addr+port, addr, i.timeout/5) + <-util.HTTPPing(addr+port, addr, i.timeout/10) localCh := util.HTTPPing(addr+port, addr, i.timeout) remoteCh := util.HTTPPing(i.listenIP+port, addr, i.timeout) diff --git a/go.mod b/go.mod index 58ec5bb..34cb833 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( github.com/ulule/deepcopier v0.0.0-20171107155558-ca99b135e50f // indirect github.com/wweir/fsnotify v1.4.8 github.com/wweir/mem-go v0.0.0-20190109100331-8673ab596296 + github.com/wweir/netboot v0.0.2 github.com/xtaci/kcp-go v5.0.7+incompatible golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 // indirect diff --git a/go.sum b/go.sum index fbbd5f6..28c24a9 100644 --- a/go.sum +++ b/go.sum @@ -34,6 +34,8 @@ github.com/wweir/fsnotify v1.4.8 h1:zxOoqwE5p91F3rmcYLQheoSeTlIVqwi2b5T7UlpElWE= github.com/wweir/fsnotify v1.4.8/go.mod h1:4GuxIc23DXfJI1/LhBXG5eE/wJ4CzL+kk5M7woxVprc= github.com/wweir/mem-go v0.0.0-20190109100331-8673ab596296 h1:/HkUfg+ZMx/tNdnyJdVlhyv+xO3A7ZlpfL9nFLWLYcc= github.com/wweir/mem-go v0.0.0-20190109100331-8673ab596296/go.mod h1:k7rjBGWoJ+JKwvfe8juAX0zgybjo/Yo3JGkca5f/06s= +github.com/wweir/netboot v0.0.2 h1:DFuNd97kxzgblExEKn4Nqf1du0m9qUnaTQ70rFgIjLU= +github.com/wweir/netboot v0.0.2/go.mod h1:p/WOnPHdqwgAI6YLEfdmwjwutbc/zDZ7EojAHlXLi4s= github.com/xtaci/kcp-go v5.0.7+incompatible h1:zs9tc8XRID0m+aetu3qPWZFyRt2UIMqbXIBgw+vcnlE= github.com/xtaci/kcp-go v5.0.7+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE= golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b h1:Elez2XeF2p9uyVj0yEUDqQ56NFcDtcBNkYP7yv8YbUE=