diff --git a/README.md b/README.md index 1cc3d8e..44299c4 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![GitHub star](https://img.shields.io/github/stars/wweir/sower.svg?style=popout)](https://github.com/wweir/sower/stargazers) [![GitHub license](https://img.shields.io/github/license/wweir/sower.svg?style=popout)](LICENSE) -The sower is a cross-platform transparent proxy tool base on DNS solution. +The sower is a cross-platform intelligent transparent proxy tool base on DNS solution. If you wanna enjoy the sower, you need to deploy sower on both server and client side. On client side, sower listening UDP `53` and TCP `80`/`443` ports, so that you need run it with privileged. @@ -58,7 +58,8 @@ Auto deploy script support Linux server side and masOS/Linux client side. $ bash -c "$(curl -s https://raw.githubusercontent.com/wweir/sower/master/deploy/install)" ``` -Then modify the configuration file as needed and set `127.0.0.1` as your first domain name server. In most situation, you just need to modify `/etc/resolv.conf`. +Then modify the configuration file as needed and set `127.0.0.1` as your first domain name server. +In most situation, you just need to modify `/etc/resolv.conf`. If you wanna uninstall sower, change `install` into `uninstall` and rerun the command. @@ -83,4 +84,4 @@ But the client is more troublesome and needs some understanding of the working m - [x] CI/CD - [ ] relay optimization - [ ] deploy script for all normal platform -- [ ] dns rule dynamic configuration \ No newline at end of file +- [x] dns rule intelligent suggestions \ No newline at end of file diff --git a/conf/conf.go b/conf/conf.go index df8b1d2..369a3d2 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -3,11 +3,11 @@ package conf import ( "context" "flag" - "net" "os" "os/exec" "path/filepath" "strconv" + "sync" "time" "github.com/golang/glog" @@ -27,24 +27,32 @@ var Conf = struct { DNSServer string `toml:"dns_server"` ClientIP string `toml:"client_ip"` - ClientIPNet net.IP `toml:"-"` ClearDNSCache string `toml:"clear_dns_cache"` BlockList []string `toml:"blocklist"` + WhiteList []string `toml:"whitelist"` Suggestions []string `toml:"suggestions"` Verbose int `toml:"verbose"` - - tree *toml.Tree // for suggestions }{} +var mu = &sync.Mutex{} + var OnRefreash = []func() error{ func() (err error) { - if Conf.tree, err = toml.LoadFile(Conf.ConfigFile); err != nil { - return err - } else if err = Conf.tree.Unmarshal(&Conf); err != nil { + mu.Lock() + defer mu.Unlock() + + f, err := os.OpenFile(Conf.ConfigFile, os.O_RDONLY, 0644) + if err != nil { return err } + defer f.Close() + + file := Conf.ConfigFile + if err = toml.NewDecoder(f).Decode(&Conf); err != nil { + return err + } + Conf.ConfigFile = file - Conf.ClientIPNet = net.ParseIP(Conf.ClientIP) return flag.Set("v", strconv.Itoa(Conf.Verbose)) }, func() error { @@ -75,10 +83,13 @@ func init() { } if _, err := os.Stat(Conf.ConfigFile); os.IsNotExist(err) { + glog.Warningln("no config file has been load:", Conf.ConfigFile) return } - if err := OnRefreash[0](); err != nil { - panic(err) + for i := range OnRefreash { + if err := OnRefreash[i](); err != nil { + glog.Fatalln(err) + } } watchConfigFile() } @@ -114,3 +125,27 @@ func watchConfigFile() { } }() } + +func AddSuggest(domain string) { + mu.Lock() + defer mu.Unlock() + + Conf.Suggestions = append(Conf.Suggestions, domain) + + // safe write + f, err := os.OpenFile(Conf.ConfigFile+"~", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + glog.Errorln(err) + return + } + defer f.Close() + + if err := toml.NewEncoder(f).ArraysWithOneElementPerLine(true).Encode(Conf); err != nil { + glog.Errorln(err) + return + } + + if err = os.Rename(Conf.ConfigFile+"~", Conf.ConfigFile); err != nil { + glog.Errorln(err) + } +} diff --git a/conf/sower.toml b/conf/sower.toml index 2632988..96f6d8f 100644 --- a/conf/sower.toml +++ b/conf/sower.toml @@ -9,82 +9,22 @@ client_ip="127.0.0.1" # clear_dns_cache="killall -HUP mDNSResponder" blocklist=[ "*.google.com", # google - "*.google.com.hk", - "*.google.ca", - "*.google.jp", - "*.google.tw", - "ocsp.pki.goog", - "g.co", "*.goo.gl", "*.googleusercontent.com", "*.googleapis.com", - "gmail.com", "*.youtube.com", # youtube "*.ytimg.com", "*.ggpht.com", "*.googlevideo.com", - "*.vimeo.com", "*.facebook.com", # facebook "*.xx.fbcdn.net", - "connect.facebook.net", "*.twitter.com", # twitter - "t.co", "*.twimg.com", - "www.linkedin.com", - "www.reddit.com", # reddit - "www.redditmedia.com", "*.blogspot.com", # blogspot - "www.blogger.com", - "www.feedburner.com", - "img1.blogblog.com", - "*.aws.amazon.com", # amazon - "m.media-amazon.com", - "*.awsstatic.com", - "*.s3.amazonaws.com", - "*.cloudfront.net", # atlassian - "synchrony-cdn.atlassian.com", - "avatar-cdn.atlassian.com", - "*.medium.com", - "*.prod.public.atl-paas.net", - "accounts-static.cdn.mozilla.net", # firefox - "*.services.mozilla.com", - "pocket-image-cache.com", # pocket - "*.golang.org", # golang - "*.googlesource.com", - "godoc.org", - "gist.github.com", # github "*.github.io", - "*.k8s.io", # k8s - "k8s.gcr.io", "*.wikipedia.org", # wikipeida "*.wikimedia.org", "*.wikisource.org", - "lookup-api.apple.com", - "lookup-api.apple.com.edgekey.net", - "e16991.b.akamaiedge.net", "*.web.telegram.org", # telegram - "core.telegram.org", - "telegram.me", - "t.me", - "*.slack.com", #slack - "*.slack-edge.com", - "js.driftt.com", - "cloud.feedly.com", # feedly - "static.grammarly.com", # grammarly - "denali-static.grammarly.com", - "apkpure.com", # apkpure - "download.apkpure.com", - "*.nytimes.com", # New York times - "*.nyt.com", - "*.akadns.net", # suggestions - "*.haxx.se", - "shadowsocks.org", - "t66y.com", - "ocsp.godaddy.com", - "*.qualtrics.com", - "*.starfieldtech.com", - "*.typekit.net", - "*.v2ray.com", - "homebrew.bintray.com", ] verbose=0 diff --git a/dns/dns.go b/dns/dns.go index de6d61f..7cd024d 100644 --- a/dns/dns.go +++ b/dns/dns.go @@ -1,18 +1,24 @@ package dns import ( + "io" "net" "strings" "time" "github.com/golang/glog" "github.com/miekg/dns" + mem "github.com/wweir/mem-go" "github.com/wweir/sower/conf" ) const colon = byte(':') -func StartDNS(dnsServer, listenIP string, ipNet net.IP) { +func StartDNS(dnsServer, listenIP string) { + ip := net.ParseIP(listenIP) + suggest := &intelliSuggest{listenIP, 2 * time.Second} + mem.DefaultCache = mem.New(time.Hour) + dns.HandleFunc(".", func(w dns.ResponseWriter, r *dns.Msg) { // *Msg r has an TSIG record and it was validated if r.IsTsig() != nil && w.TsigStatus() == nil { @@ -29,84 +35,80 @@ func StartDNS(dnsServer, listenIP string, ipNet net.IP) { domain = domain[:idx] } - if len(conf.Conf.BlockList) == 0 { - bestTry(w, r, domain, dnsServer, ipNet) - } else { - manual(w, r, domain, dnsServer, ipNet) - } + matchAndServe(w, r, domain, listenIP, dnsServer, ip, suggest) }) server := &dns.Server{Addr: listenIP + ":53", Net: "udp"} glog.Fatalln(server.ListenAndServe()) } -func bestTry(w dns.ResponseWriter, r *dns.Msg, domain, dnsServer string, ipNet net.IP) { - msg, _ := dns.Exchange(r, dnsServer+":53") - if msg == nil { - return - } - if len(msg.Answer) == 0 { // expose any response - w.WriteMsg(msg) - return - } +func matchAndServe(w dns.ResponseWriter, r *dns.Msg, domain, listenIP, dnsServer string, ipNet net.IP, suggest *intelliSuggest) { + inWriteList := whiteList.Match(domain) - var ip string - switch msg.Answer[0].(type) { - case *dns.A: - ip = msg.Answer[0].(*dns.A).A.String() - case *dns.AAAA: - ip = "[" + msg.Answer[0].(*dns.AAAA).AAAA.String() + "]" - default: - w.WriteMsg(msg) - return - } - - if _, err := net.DialTimeout("tcp", ip+":http", time.Second); err != nil { - glog.V(2).Infoln(ip+":80", err) - w.WriteMsg(localA(r, domain, ipNet)) - return - } - w.WriteMsg(msg) -} - -func manual(w dns.ResponseWriter, r *dns.Msg, domain, dnsServer string, ipNet net.IP) { - if !writeList.Match(domain) && - (blockList.Match(domain) || suggestList.Match(domain)) { + if !inWriteList && (blockList.Match(domain) || suggestList.Match(domain)) { glog.V(2).Infof("match %s suss", domain) w.WriteMsg(localA(r, domain, ipNet)) return } + if !inWriteList { + go mem.Remember(suggest, domain) + } + msg, err := dns.Exchange(r, dnsServer+":53") if msg == nil { // expose any response except nil glog.V(1).Infof("get dns of %s fail: %s", domain, err) return } w.WriteMsg(msg) +} - if conf.Conf.Verbose != 0 && len(msg.Answer) != 0 { - go func() { - _, err := net.DialTimeout("tcp", domain+":http", 2*time.Second) - if err == nil || !strings.Contains(err.Error(), "timeout") { - return - } +type intelliSuggest struct { + listenIP string + timeout time.Duration +} - _, err = net.DialTimeout("tcp", domain+":https", 3*time.Second) - if err == nil || !strings.Contains(err.Error(), "timeout") { - return - } +func (i *intelliSuggest) GetOne(domain interface{}) (interface{}, error) { + addr := strings.TrimSuffix(domain.(string), ".") - glog.V(1).Infof("SUGGEST check (%s) http(s) service: %s", domain, err) - }() + { // First: test direct connect + conn, err := net.DialTimeout("tcp", addr+":http", i.timeout) + if err == nil { + conn.Close() + return false, nil + } + glog.V(2).Infoln("first dial fail:", addr) } -} + { // Second: test remote connect + conn, err := net.DialTimeout("tcp", i.listenIP+":http", i.timeout/100) + if err != nil { + glog.V(1).Infoln("dial self service fail:", err) + return false, err + } + defer conn.Close() -func localA(r *dns.Msg, domain string, localIP net.IP) *dns.Msg { - m := new(dns.Msg) - m.SetReply(r) - m.Answer = []dns.RR{&dns.A{ - Hdr: dns.RR_Header{Name: domain, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 20}, - A: localIP, - }} - return m + conn.SetReadDeadline(time.Now().Add(2 * time.Second)) + if _, err = conn.Write([]byte("TRACE / HTTP/1.1\r\nHost: " + addr + "\r\n\r\n")); err != nil { + glog.V(1).Infoln("dial self service fail:", err) + return false, err + } + if _, err = conn.Read(make([]byte, 1)); err != nil && err != io.EOF { + return false, nil + } + glog.V(2).Infoln("remote dial succ:", addr) + } + { // Third: retest direct connect + conn, err := net.DialTimeout("tcp", addr+":http", i.timeout) + if err == nil { + whiteList.Add(addr) + conn.Close() + return false, nil + } + glog.V(2).Infoln("retry dial fail:", addr) + } + + // After three round test, most probably the addr is blocked + conf.AddSuggest(addr) + glog.Infof("added suggest domain: %s", addr) + return true, nil } diff --git a/dns/suffix_tree.go b/dns/suffix_tree.go index 989fee2..7c721e8 100644 --- a/dns/suffix_tree.go +++ b/dns/suffix_tree.go @@ -2,29 +2,36 @@ package dns import ( "strings" + "sync" ) type Node struct { - sep string - Node map[string]*Node + node + sep string + *sync.RWMutex +} +type node struct { + node map[string]*node } func NewNode(sep string) *Node { - return &Node{sep: sep, Node: map[string]*Node{}} + return &Node{node{node: map[string]*node{}}, sep, &sync.RWMutex{}} } func NewNodeFromRules(sep string, rules ...string) *Node { - node := NewNode(sep) + n := NewNode(sep) for i := range rules { - node.Add(rules[i]) + n.Add(rules[i]) } - return node + return n } func (n *Node) String() string { + n.RLock() + defer n.RUnlock() return n.string("") } -func (n *Node) string(prefix string) (out string) { - for key, val := range n.Node { +func (n *node) string(prefix string) (out string) { + for key, val := range n.node { out += prefix + key + "\n" + val.string(prefix+" ") } return @@ -34,20 +41,22 @@ func (n *Node) trim(item string) string { } func (n *Node) Add(item string) { + n.Lock() + defer n.Unlock() n.add(strings.Split(n.trim(item), n.sep)) } -func (n *Node) add(secs []string) { +func (n *node) add(secs []string) { length := len(secs) switch length { case 0: return case 1: - n.Node[secs[length-1]] = NewNode(n.sep) + n.node[secs[length-1]] = &node{node: map[string]*node{}} default: - subNode, ok := n.Node[secs[length-1]] + subNode, ok := n.node[secs[length-1]] if !ok { - subNode = NewNode(n.sep) - n.Node[secs[length-1]] = subNode + subNode = &node{node: map[string]*node{}} + n.node[secs[length-1]] = subNode } subNode.add(secs[:length-1]) } @@ -57,24 +66,24 @@ func (n *Node) Match(item string) bool { return n.matchSecs(strings.Split(n.trim(item), n.sep)) } -func (n *Node) matchSecs(secs []string) bool { +func (n *node) matchSecs(secs []string) bool { length := len(secs) if length == 0 { - switch len(n.Node) { + switch len(n.node) { case 0: return true case 1: - _, ok := n.Node["*"] + _, ok := n.node["*"] return ok default: return false } } - if n, ok := n.Node[secs[length-1]]; ok { + if n, ok := n.node[secs[length-1]]; ok { return n.matchSecs(secs[:length-1]) } - _, ok := n.Node["*"] + _, ok := n.node["*"] return ok } diff --git a/dns/util.go b/dns/util.go index dd4f6f8..56c8533 100644 --- a/dns/util.go +++ b/dns/util.go @@ -1,23 +1,28 @@ package dns import ( + "net" + "github.com/golang/glog" + "github.com/miekg/dns" "github.com/wweir/sower/conf" ) var ( blockList *Node + whiteList *Node suggestList *Node - writeList = NewNode(".") ) func init() { //first init blockList = loadRules("block", conf.Conf.BlockList) + whiteList = loadRules("white", conf.Conf.WhiteList) suggestList = loadRules("suggest", conf.Conf.BlockList) conf.OnRefreash = append(conf.OnRefreash, func() error { blockList = loadRules("block", conf.Conf.BlockList) + whiteList = loadRules("white", conf.Conf.WhiteList) suggestList = loadRules("suggest", conf.Conf.Suggestions) return nil }) @@ -28,3 +33,13 @@ func loadRules(name string, list []string) *Node { glog.V(2).Infof("load %s rule:\n%s", name, rule) return rule } + +func localA(r *dns.Msg, domain string, localIP net.IP) *dns.Msg { + m := new(dns.Msg) + m.SetReply(r) + m.Answer = []dns.RR{&dns.A{ + Hdr: dns.RR_Header{Name: domain, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 20}, + A: localIP, + }} + return m +} diff --git a/go.mod b/go.mod index 6a1a418..ce3da1d 100644 --- a/go.mod +++ b/go.mod @@ -7,24 +7,31 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b github.com/golang/mock v1.2.0 // indirect + github.com/guregu/null v3.4.0+incompatible // indirect github.com/hashicorp/golang-lru v0.5.0 // indirect github.com/klauspost/cpuid v1.2.0 // indirect github.com/klauspost/reedsolomon v1.8.0 // indirect + github.com/lib/pq v1.0.0 // indirect github.com/lucas-clemente/aes12 v0.0.0-20171027163421-cd47fb39b79f // indirect - github.com/lucas-clemente/quic-go v0.10.0 + github.com/lucas-clemente/quic-go v0.10.1 github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced // indirect github.com/miekg/dns v1.1.3 github.com/onsi/ginkgo v1.7.0 // indirect github.com/onsi/gomega v1.4.3 // indirect github.com/pelletier/go-toml v1.2.0 github.com/pkg/errors v0.8.1 + github.com/satori/go.uuid v1.2.0 // indirect + github.com/stretchr/testify v1.3.0 // indirect github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b // indirect github.com/tjfoc/gmsm v1.0.1 // indirect + 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/xtaci/kcp-go v5.0.7+incompatible - golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc - golang.org/x/net v0.0.0-20190110200230-915654e7eabc // indirect + golang.org/x/crypto v0.0.0-20190122013713-64072686203f + golang.org/x/net v0.0.0-20190119204137-ed066c81e75e // indirect golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect - golang.org/x/sys v0.0.0-20190114130336-2be517255631 // indirect + golang.org/x/sys v0.0.0-20190122071731-054c452bb702 // indirect + gopkg.in/yaml.v2 v2.2.2 // indirect ) diff --git a/go.sum b/go.sum index c45bde5..1b9e53c 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,7 @@ github.com/bifurcation/mint v0.0.0-20181105071958-a14404e9a861 h1:x17NvoJaphEzay github.com/bifurcation/mint v0.0.0-20181105071958-a14404e9a861/go.mod h1:zVt7zX3K/aDCk9Tj+VM7YymsX66ERvzCJzw8rFCX2JU= github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= @@ -14,6 +15,8 @@ github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/guregu/null v3.4.0+incompatible h1:a4mw37gBO7ypcBlTJeZGuMpSxxFTV9qFfFKgWxQSGaM= +github.com/guregu/null v3.4.0+incompatible/go.mod h1:ePGpQaN9cw0tj45IR5E5ehMvsFlLlQZAkkOXZurJ3NM= github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= @@ -22,10 +25,12 @@ github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/reedsolomon v1.8.0 h1:lvvOkvk64cE1EGbBIgFk7WSOOsI1GexpuLiT7zjab6g= github.com/klauspost/reedsolomon v1.8.0/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4= +github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lucas-clemente/aes12 v0.0.0-20171027163421-cd47fb39b79f h1:sSeNEkJrs+0F9TUau0CgWTTNEwF23HST3Eq0A+QIx+A= github.com/lucas-clemente/aes12 v0.0.0-20171027163421-cd47fb39b79f/go.mod h1:JpH9J1c9oX6otFSgdUHwUBUizmKlrMjxWnIAjff4m04= -github.com/lucas-clemente/quic-go v0.10.0 h1:xEF+pSHYAOcu+U10Meunf+DTtc8vhQDRqlA0BJ6hufc= -github.com/lucas-clemente/quic-go v0.10.0/go.mod h1:wuD+2XqEx8G9jtwx5ou2BEYBsE+whgQmlj0Vz/77PrY= +github.com/lucas-clemente/quic-go v0.10.1 h1:ipcMmYP9RT+b1YytOKGUY1qndxPGOczVEQkAVz3CZrs= +github.com/lucas-clemente/quic-go v0.10.1/go.mod h1:wuD+2XqEx8G9jtwx5ou2BEYBsE+whgQmlj0Vz/77PrY= github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced h1:zqEC1GJZFbGZA0tRyNZqRjep92K5fujFtFsu5ZW7Aug= github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced/go.mod h1:NCcRLrOTZbzhZvixZLlERbJtDtYsmMw8Jc4vS8Z0g58= github.com/miekg/dns v1.1.3 h1:1g0r1IvskvgL8rR+AcHzUA+oFmGcQlaIm4IqakufeMM= @@ -39,27 +44,38 @@ github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU= github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b h1:mnG1fcsIB1d/3vbkBak2MM0u+vhGhlQwpeimUi7QncM= github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4= github.com/tjfoc/gmsm v1.0.1 h1:R11HlqhXkDospckjZEihx9SW/2VW0RgdwrykyWMFOQU= github.com/tjfoc/gmsm v1.0.1/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc= +github.com/ulule/deepcopier v0.0.0-20171107155558-ca99b135e50f h1:QatZ4lsJBY3x1+Imst9g95+vUl7m52dqM9Pi4aSMW8w= +github.com/ulule/deepcopier v0.0.0-20171107155558-ca99b135e50f/go.mod h1:BNLmYJ8oMJPIPpNx5968jCyUhwEU1XT3YsuOqtbo5qo= 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/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-20190103213133-ff983b9c42bc h1:F5tKCVGp+MUAHhKp5MZtGqAlGX3+oCsiL1Q629FL90M= -golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190122013713-64072686203f h1:u1CmMhe3a44hy8VIgpInORnI01UVaUYheqR7x9BxT3c= +golang.org/x/crypto v0.0.0-20190122013713-64072686203f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190110200230-915654e7eabc h1:Yx9JGxI1SBhVLFjpAkWMaO1TF+xyqtHLjZpvQboJGiM= -golang.org/x/net v0.0.0-20190110200230-915654e7eabc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190119204137-ed066c81e75e h1:MDa3fSUp6MdYHouVmCCNz/zaH2a6CRcxY3VhT/K3C5Q= +golang.org/x/net v0.0.0-20190119204137-ed066c81e75e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190114130336-2be517255631 h1:g/5trXm6f9Tm+ochb21RlFNnF63lt+elB9hVBqtPu5Y= -golang.org/x/sys v0.0.0-20190114130336-2be517255631/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190122071731-054c452bb702 h1:Lk4tbZFnlyPgV+sLgTw5yGfzrlOn9kx4vSombi2FFlY= +golang.org/x/sys v0.0.0-20190122071731-054c452bb702/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= @@ -68,5 +84,6 @@ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/main.go b/main.go index 91abc4a..ad04e67 100644 --- a/main.go +++ b/main.go @@ -22,7 +22,7 @@ func main() { conf.Cipher, conf.Password, conf.HTTPProxy) } - go dns.StartDNS(conf.DNSServer, conf.ClientIP, conf.ClientIPNet) + go dns.StartDNS(conf.DNSServer, conf.ClientIP) proxy.StartClient(conf.NetType, conf.ServerAddr, conf.Cipher, conf.Password, conf.ClientIP) } } diff --git a/proxy/client.go b/proxy/client.go index e610aed..70e5197 100644 --- a/proxy/client.go +++ b/proxy/client.go @@ -2,6 +2,7 @@ package proxy import ( "net" + "strings" "github.com/golang/glog" "github.com/wweir/sower/proxy/kcp" @@ -31,18 +32,20 @@ func NewClient(netType string) Client { func StartClient(netType, server, cipher, password, listenIP string) { connCh := listenLocal(listenIP, []string{":80", ":443"}) client := NewClient(netType) - ips, err := net.LookupIP(server) - if err != nil || len(ips) == 0 { - glog.Fatalln(ips, err) + if idx := strings.Index(server, ":"); idx > 0 { + ips, err := net.LookupIP(server[:idx]) + if err != nil || len(ips) == 0 { + glog.Fatalln(err, ips) + } + server = ips[0].String() + server[idx:] } - serverAddr := ips[0].String() glog.Infoln("Client started.") for { conn := <-connCh glog.V(1).Infof("new conn from (%s) to (%s)", conn.RemoteAddr(), server) - rc, err := client.Dial(serverAddr) + rc, err := client.Dial(server) if err != nil { conn.Close() glog.Errorln(err) diff --git a/proxy/tcp/client.go b/proxy/tcp/client.go index bc882e9..f0be078 100644 --- a/proxy/tcp/client.go +++ b/proxy/tcp/client.go @@ -1,16 +1,22 @@ package tcp -import "net" +import ( + "net" + "time" +) type client struct { + DialTimeout time.Duration } func NewClient() *client { - return &client{} + return &client{ + DialTimeout: 5 * time.Second, + } } func (c *client) Dial(server string) (net.Conn, error) { - conn, err := net.Dial("tcp", server) + conn, err := net.DialTimeout("tcp", server, c.DialTimeout) if err != nil { return nil, err } diff --git a/proxy/util.go b/proxy/util.go index 0a0e5f0..9b9efda 100644 --- a/proxy/util.go +++ b/proxy/util.go @@ -34,9 +34,10 @@ func redirect(dst, src net.Conn, wg *sync.WaitGroup, exitFlag *int32) { } if atomic.CompareAndSwapInt32(exitFlag, 0, 1) { + // wakeup blocked goroutine now := time.Now() - src.SetReadDeadline(now) - dst.SetWriteDeadline(now) + src.SetDeadline(now) + dst.SetDeadline(now) } else { src.Close() dst.Close()