diff --git a/conf/conf.go b/conf/conf.go index 30c942f..488b242 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -1,27 +1,36 @@ package conf import ( + "bufio" "flag" + "io" + "net/http" "os" "path/filepath" + "strings" "sync" toml "github.com/pelletier/go-toml" "github.com/wweir/sower/util" - "github.com/wweir/utils/log" + "github.com/wweir/util-go/log" + "golang.org/x/xerrors" ) type Client struct { Address string `toml:"address"` - HTTPProxy string `toml:"http_proxy"` - DNSServeIP string `toml:"dns_serve_ip"` DNSUpstream string `toml:"dns_upstream"` + Socks5Proxy string `toml:"socks5"` + HTTPProxy string `toml:"http_proxy"` PortForward map[string]string `toml:"port_forward"` Router struct { DetectLevel int `toml:"detect_level"` ProxyList []string `toml:"proxy_list"` + ProxyRefs []string `toml:"proxy_refs"` DirectList []string `toml:"direct_list"` + DirectRefs []string `toml:"direct_refs"` + BlockList []string `toml:"block_list"` + BlockRefs []string `toml:"block_refs"` } `toml:"router"` } type Server struct { @@ -59,7 +68,7 @@ func Init() (*Client, *Server, string) { flag.Parse() } - defer log.Infow("starting", "config", &conf) + defer log.Infow("starting", "version", version, "date", date, "config", &conf) if conf.file == "" { return &conf.Client, &conf.Server, conf.Password } @@ -76,16 +85,61 @@ func Init() (*Client, *Server, string) { var loadConfigFns = []struct { step string fn func() error -}{{"load_config", func() error { +}{{"parse file", func() error { f, err := os.OpenFile(conf.file, os.O_RDONLY, 0644) if err != nil { - return err + return xerrors.New(err.Error()) } defer f.Close() return toml.NewDecoder(f).Decode(&conf) +}}, {"load referenced rule", func() error { + for _, addr := range conf.Client.Router.BlockRefs { + lines, err := getRemoteRuleLines(addr) + if err != nil { + return err + } + conf.Client.Router.BlockList = append(conf.Client.Router.BlockList, lines...) + } + for _, addr := range conf.Client.Router.ProxyRefs { + lines, err := getRemoteRuleLines(addr) + if err != nil { + return err + } + conf.Client.Router.ProxyList = append(conf.Client.Router.ProxyList, lines...) + } + for _, addr := range conf.Client.Router.DirectRefs { + lines, err := getRemoteRuleLines(addr) + if err != nil { + return err + } + conf.Client.Router.DirectList = append(conf.Client.Router.DirectList, lines...) + } + + return nil }}} +func getRemoteRuleLines(addr string) ([]string, error) { + resp, err := http.Get(addr) + if err != nil { + return nil, xerrors.New(err.Error()) + } + defer resp.Body.Close() + + br := bufio.NewReader(resp.Body) + lines := []string{} + for { + line, _, err := br.ReadLine() + if err == io.EOF { + return lines, nil + } else if err != nil { + return nil, xerrors.New(err.Error()) + } + + lines = append(lines, "**."+strings.TrimSpace(string(line))) + } +} + // flushCh to avoid parallel persist var flushCh = make(chan struct{}) var flushOnce = sync.Once{} @@ -138,12 +192,5 @@ func flushConfDaemon() { continue } } - - // reload config - for i := range loadConfigFns { - if err := loadConfigFns[i].fn(); err != nil { - log.Errorw("flush config", "step", loadConfigFns[i].step, "err", err) - } - } } } diff --git a/conf/conf_darwin.go b/conf/conf_darwin.go index 05d20bb..7a26c3e 100644 --- a/conf/conf_darwin.go +++ b/conf/conf_darwin.go @@ -12,7 +12,7 @@ import ( "path/filepath" "time" - "github.com/wweir/utils/log" + "github.com/wweir/util-go/log" ) const svcPath = "/Library/LaunchDaemons/sower.plist" diff --git a/conf/conf_linux.go b/conf/conf_linux.go index 32863c0..936cfeb 100644 --- a/conf/conf_linux.go +++ b/conf/conf_linux.go @@ -12,7 +12,7 @@ import ( "path/filepath" "time" - "github.com/wweir/utils/log" + "github.com/wweir/util-go/log" ) const svcPath = "/etc/systemd/system/sower.service" @@ -66,13 +66,6 @@ func afterInitFlag() { case uninstallFlag: uninstall() default: - if conf.Client.DNSServeIP != "" { - log.Infow("setting system DNS", "DNS", conf.Client.DNSServeIP) - if err := execute(fmt.Sprintf("grep '%s' || sed -i '1inameserver %s' /etc/resolv.conf", - conf.Client.DNSServeIP, conf.Client.DNSServeIP)); err != nil { - log.Errorf("set DNS fail, please set DNS to %s manually", conf.Client.DNSServeIP) - } - } return } os.Exit(0) diff --git a/conf/conf_windows.go b/conf/conf_windows.go index 29e4294..c73718a 100644 --- a/conf/conf_windows.go +++ b/conf/conf_windows.go @@ -13,7 +13,7 @@ import ( "syscall" "time" - "github.com/wweir/utils/log" + "github.com/wweir/util-go/log" "golang.org/x/sys/windows" "golang.org/x/sys/windows/svc" "golang.org/x/sys/windows/svc/eventlog" diff --git a/conf/sower.toml b/conf/sower.toml index 0a85f0f..203ebf9 100644 --- a/conf/sower.toml +++ b/conf/sower.toml @@ -1,54 +1,65 @@ -password="" # sower password +password = "" # sower password [client] - address = "" # aa.bb.cc, socks5h://127.0.0.1:1080 - http_proxy=":8080" - dns_serve_ip="" # eg: 127.0.0.1. keep empty to disable DNS solution - dns_upstream="" # keep empty to set via dhcp, not effective in any environment +address = "" # aa.bb.cc, socks5h://127.0.0.1:1080 +dns_upstream = "" # keep empty to set via dhcp, not effective in any environment +http_proxy = "" +socks5 = "" - [client.port_forward] - # eg: ":2222"="aa.bb.cc:22" - - [client.router] - detect_level = 0 # [-4, 4], the bigger the harder to add - direct_list = [ - "**.in-addr.arpa", - "imap.*.*", - "imap.*.*.*", - "smtp.*.*", - "smtp.*.*.*", - "pop.*.*", - "pop.*.*.*", - "**.cn", - ] - proxy_list = [ - "**.google.*", - "**.goo.gl", - "**.googleusercontent.com", - "**.googleapis.com", - "*.googlesource.com", - "**.youtube.com", - "**.ytimg.com", - "**.ggpht.com", - "**.googlevideo.com", - "**.facebook.com", - "**.fbcdn.net", - "**.twitter.com", - "**.twimg.com", - "**.blogspot.com", - "**.appspot.com", - "**.wikipedia.org", - "*.cloudfront.net", - "**.amazon.com", - "**.amazonaws.com", - "*.githubusercontent.com", - "*.githubassets.com", - "*.github.*", - ] +[client.port_forward] +# eg: ":2222"="aa.bb.cc:22" +[client.router] +block_refs = [ + "https://cdn.jsdelivr.net/gh/pexcn/daily@gh-pages/adlist/adlist.txt", +] +detect_level = 0 # [-4, 4], the bigger the harder to add +direct_cird_refs = [ + "https://cdn.jsdelivr.net/gh/pexcn/daily@gh-pages/chnroute/chnroute.txt", +] +direct_list = [ + "**.in-addr.arpa", + "imap.*.*", + "imap.*.*.*", + "smtp.*.*", + "smtp.*.*.*", + "pop.*.*", + "pop.*.*.*", + "**.cn", +] +direct_refs = [ + "https://cdn.jsdelivr.net/gh/pexcn/daily@gh-pages/chinalist/chinalist.txt", +] +proxy_list = [ + "**.google.*", + "**.goo.gl", + "**.googleusercontent.com", + "**.googleapis.com", + "*.googlesource.com", + "**.youtube.com", + "**.ytimg.com", + "**.ggpht.com", + "**.googlevideo.com", + "**.facebook.com", + "**.fbcdn.net", + "**.twitter.com", + "**.twimg.com", + "**.blogspot.com", + "**.appspot.com", + "**.wikipedia.org", + "*.cloudfront.net", + "**.amazon.com", + "**.amazonaws.com", + "*.githubusercontent.com", + "*.githubassets.com", + "*.github.*", +] +proxy_refs = [ + "https://cdn.jsdelivr.net/gh/pexcn/daily@gh-pages/gfwlist/gfwlist.txt", +] [server] - cert_email = "" # eg: user@aa.bb.cc - cert_file = "" # eg: /etc/ssl/server.crt - key_file = "" # eg: /etc/ssl/server.key - upstream = "" # eg: 127.0.0.1:8080 \ No newline at end of file +cert_email = "" # eg: user@aa.bb.cc +cert_file = "" # eg: /etc/ssl/server.crt +key_file = "" # eg: /etc/ssl/server.key +upstream = "" # eg: 127.0.0.1:8080 diff --git a/go.mod b/go.mod index 8842ba1..513a061 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,12 @@ go 1.14 require ( github.com/krolaw/dhcp4 v0.0.0-20190909130307-a50d88189771 - github.com/libp2p/go-reuseport v0.0.1 - github.com/miekg/dns v1.1.29 - github.com/pelletier/go-toml v1.7.0 + github.com/libp2p/go-reuseport v0.0.2 + github.com/miekg/dns v1.1.30 + github.com/pelletier/go-toml v1.8.0 github.com/pkg/errors v0.9.1 - github.com/wweir/utils v0.0.0-20200401081845-fc35af3737dc - golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 - golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 + github.com/wweir/util-go/log v0.0.0-20200701032032-3cff7b4a46ea + github.com/wweir/util-go/mem v0.0.0-20200701032032-3cff7b4a46ea + golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 + golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 ) diff --git a/go.sum b/go.sum index 154e5c2..0e55d3f 100644 --- a/go.sum +++ b/go.sum @@ -1,64 +1,47 @@ -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 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/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/guregu/null v3.4.0+incompatible h1:a4mw37gBO7ypcBlTJeZGuMpSxxFTV9qFfFKgWxQSGaM= -github.com/guregu/null v3.4.0+incompatible/go.mod h1:ePGpQaN9cw0tj45IR5E5ehMvsFlLlQZAkkOXZurJ3NM= -github.com/influxdata/influxdb v1.7.9/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= -github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/krolaw/dhcp4 v0.0.0-20190909130307-a50d88189771 h1:t2c2B9g1ZVhMYduqmANSEGVD3/1WlsrEYNPtVoFlENk= github.com/krolaw/dhcp4 v0.0.0-20190909130307-a50d88189771/go.mod h1:0AqAH3ZogsCrvrtUpvc6EtVKbc3w6xwZhkvGLuqyi3o= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU= -github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/libp2p/go-reuseport v0.0.1 h1:7PhkfH73VXfPJYKQ6JwS5I/eVcoyYi9IMNGc6FWpFLw= -github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA= -github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/miekg/dns v1.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg= -github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/pelletier/go-toml v1.7.0 h1:7utD74fnzVc/cpcyy8sjrlFr5vYpypUixARcHIMIGuI= -github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= +github.com/libp2p/go-reuseport v0.0.2 h1:XSG94b1FJfGA01BUrT82imejHQyTxO4jEWqheyCXYvU= +github.com/libp2p/go-reuseport v0.0.2/go.mod h1:SPD+5RwGC7rcnzngoYC86GjPzjSywuQyMVAheVBD9nQ= +github.com/miekg/dns v1.1.30 h1:Qww6FseFn8PRfw07jueqIXqodm0JKiiKuK0DeXSqfyo= +github.com/miekg/dns v1.1.30/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMFtw= +github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.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/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -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/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/ulule/deepcopier v0.0.0-20200117111125-792cfb847af8 h1:iCslye9fWwp1ExDu06BcKM8/gjDRAlX9DBL+T8CjuWU= -github.com/ulule/deepcopier v0.0.0-20200117111125-792cfb847af8/go.mod h1:wUZg40sMUnY+1FU5F9rZwwCruLb8h1bHF8HzI09kgok= -github.com/wweir/utils v0.0.0-20200401081845-fc35af3737dc h1:XvNZju71btZ0/hammNDOu0Jqv7qi0kR0x9KaXYRKOAY= -github.com/wweir/utils v0.0.0-20200401081845-fc35af3737dc/go.mod h1:5PfQ9g8nWDRNkzu2gyjtFo/Xo6+s5ZCpulQADjvGz7M= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/ulule/deepcopier v0.0.0-20200430083143-45decc6639b6 h1:TtyC78WMafNW8QFfv3TeP3yWNDG+uxNkk9vOrnDu6JA= +github.com/ulule/deepcopier v0.0.0-20200430083143-45decc6639b6/go.mod h1:h8272+G2omSmi30fBXiZDMkmHuOgonplfKIKjQWzlfs= +github.com/wweir/util-go v0.0.0-20200701032032-3cff7b4a46ea h1:V/bIKmS5Bv4zMwQ5qE7ugJiHpcAVFnAxvxRbb7OCwug= +github.com/wweir/util-go/log v0.0.0-20200701032032-3cff7b4a46ea h1:IlO7d9R0I/l2syxJkx4/PURW9yVf4hy/Qf2IzdR4y38= +github.com/wweir/util-go/log v0.0.0-20200701032032-3cff7b4a46ea/go.mod h1:OObmMboiahxgJ1P3twG+AN8EAZ/wz14vstI4E5kaZzI= +github.com/wweir/util-go/mem v0.0.0-20200701032032-3cff7b4a46ea h1:nXgUp7CflDJnuR+zd43DHKxRA/hvuMkFtueSRLY7mEc= +github.com/wweir/util-go/mem v0.0.0-20200701032032-3cff7b4a46ea/go.mod h1:gp1gaDO1K4uV/B8NfrTjVim99Xq5xAmm1SHVamRsVW0= go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.14.1 h1:nYDKopTbvAPq/NrUVZwT15y2lpROBiLLyoRTbXOYWOo= -go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= +go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= +go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEBT6lE4eh+jOh886GHrn6V8= -golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= +golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg= +golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= @@ -67,31 +50,26 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/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-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 h1:opSr2sbRXk5X5/givKrrKj9HXxFpW2sdCiP8MJSKLQY= -golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/main.go b/main.go index 0025a84..d81aa43 100644 --- a/main.go +++ b/main.go @@ -19,13 +19,12 @@ func main() { server.CertFile, server.KeyFile, server.CertEmail) case client.Address != "": - route := &router.Route{ - ProxyAddress: client.Address, - ProxyPassword: password, - DetectLevel: client.Router.DetectLevel, - DirectList: client.Router.DirectList, - ProxyList: client.Router.ProxyList, - PersistFn: conf.PersistRule, + route := router.NewRoute(client.Address, password, client.Router.DetectLevel, + client.Router.BlockList, client.Router.ProxyList, client.Router.DirectList, + conf.PersistRule) + + if client.Socks5Proxy != "" { + go proxy.StartSocks5Proxy(client.Socks5Proxy, client.Address, []byte(password)) } if client.HTTPProxy != "" { @@ -33,15 +32,10 @@ func main() { []byte(password), route.GenProxyCheck(true)) } - enableDNSSolution := client.DNSServeIP != "" - if enableDNSSolution { - transport.SetDNS(nil, client.DNSUpstream) - go proxy.StartDNS(client.DNSServeIP, client.DNSUpstream, - route.GenProxyCheck(false)) - } + transport.SetDNS(nil, client.DNSUpstream) + go proxy.StartDNS(client.DNSUpstream, route.GenProxyCheck(false)) proxy.StartClient(client.Address, password, - client.DNSServeIP, enableDNSSolution, client.PortForward, route.GenProxyCheck(true)) default: diff --git a/proxy/dns.go b/proxy/dns.go index a595f15..96379f6 100644 --- a/proxy/dns.go +++ b/proxy/dns.go @@ -8,7 +8,7 @@ import ( "github.com/miekg/dns" "github.com/wweir/sower/dhcp" - "github.com/wweir/utils/log" + "github.com/wweir/util-go/log" ) type msgCache struct { @@ -18,12 +18,7 @@ type msgCache struct { var cache sync.Map -func StartDNS(redirectIP, relayServer string, shouldProxy func(string) bool) { - serveIP := net.ParseIP(redirectIP) - if redirectIP == "" || serveIP.String() != redirectIP { - log.Fatalw("invalid listen ip", "ip", redirectIP) - } - +func StartDNS(relayServer string, shouldProxy func(string) (bool, bool)) { var err error if relayServer, err = pickRelayAddr(relayServer); err != nil { log.Fatalw("pick upstream dns server", "err", err) @@ -47,8 +42,13 @@ func StartDNS(redirectIP, relayServer string, shouldProxy func(string) bool) { domain = domain[:idx] // trim port } - if shouldProxy(domain) { - w.WriteMsg(localA(r, domain, serveIP)) + if isblock, isproxy := shouldProxy(domain); isblock { + m := new(dns.Msg) + m.SetReply(r) + w.WriteMsg(m) + } else if isproxy { + host, _, _ := net.SplitHostPort(w.LocalAddr().String()) + w.WriteMsg(localA(r, domain, net.ParseIP(host))) } else if val, ok := cache.Load(domain); ok && val.(*msgCache).After(time.Now()) { msg := val.(*msgCache) @@ -82,7 +82,7 @@ func StartDNS(redirectIP, relayServer string, shouldProxy func(string) bool) { } }) - server := &dns.Server{Addr: net.JoinHostPort(redirectIP, "53"), Net: "udp"} + server := &dns.Server{Addr: ":53", Net: "udp"} log.Infow("start dns", "addr", server.Addr) log.Fatalw("dns serve fail", "err", server.ListenAndServe()) } diff --git a/proxy/http_proxy.go b/proxy/http_proxy.go index 14f0a05..a431541 100644 --- a/proxy/http_proxy.go +++ b/proxy/http_proxy.go @@ -10,20 +10,20 @@ import ( "github.com/wweir/sower/transport" "github.com/wweir/sower/util" - "github.com/wweir/utils/log" + "github.com/wweir/util-go/log" ) // StartHTTPProxy start http reverse proxy. // The httputil.ReverseProxy do not supply enough support for https request. func StartHTTPProxy(httpProxyAddr, serverAddr string, password []byte, - shouldProxy func(string) bool) { + shouldProxy func(string) (bool, bool)) { proxy := httputil.ReverseProxy{ Director: func(r *http.Request) {}, Transport: &http.Transport{ DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { return transport.Dial(serverAddr, func(host string) (string, []byte) { - if shouldProxy(host) { + if _, ok := shouldProxy(host); ok { return httpProxyAddr, password } return "", nil @@ -51,7 +51,7 @@ func StartHTTPProxy(httpProxyAddr, serverAddr string, password []byte, } func httpsProxy(w http.ResponseWriter, r *http.Request, - serverAddr string, password []byte, shouldProxy func(string) bool) { + serverAddr string, password []byte, shouldProxy func(string) (bool, bool)) { conn, _, err := w.(http.Hijacker).Hijack() if err != nil { @@ -68,7 +68,7 @@ func httpsProxy(w http.ResponseWriter, r *http.Request, target, _ := util.WithDefaultPort(r.Host, "443") rc, err := transport.Dial(target, func(host string) (string, []byte) { - if shouldProxy(host) { + if _, ok := shouldProxy(host); ok { return serverAddr, password } return "", nil diff --git a/proxy/proxy.go b/proxy/proxy.go index 1acccdf..291d3d1 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -6,17 +6,17 @@ import ( "net/http" "github.com/wweir/sower/transport" - "github.com/wweir/utils/log" + "github.com/wweir/util-go/log" "golang.org/x/crypto/acme/autocert" ) -func StartClient(serverAddr, password, serveIP string, enableDNS bool, - forwards map[string]string, shouldProxy func(string) bool) { +func StartClient(serverAddr, password string, + forwards map[string]string, shouldProxy func(string) (bool, bool)) { passwordData := []byte(password) relayToRemote := func(lnAddr, target string, parseFn func(net.Conn) (net.Conn, string, error), - shouldProxy func(string) bool) { + shouldProxy func(string) (bool, bool)) { ln, err := net.Listen("tcp", lnAddr) if err != nil { @@ -41,14 +41,13 @@ func StartClient(serverAddr, password, serveIP string, enableDNS bool, } rc, err := transport.Dial(target, func(domain string) (string, []byte) { - if shouldProxy(domain) { + if _, ok := shouldProxy(domain); ok { return serverAddr, passwordData } return "", nil }) if err != nil { - host, _, _ := net.SplitHostPort(target) - log.Warnw("dial", "addr", target, "proxy", shouldProxy(host), "err", err) + log.Warnw("dial", "addr", target, "err", err) return } defer rc.Close() @@ -59,15 +58,13 @@ func StartClient(serverAddr, password, serveIP string, enableDNS bool, } for from, to := range forwards { - go relayToRemote(from, to, nil, func(string) bool { return true }) + go relayToRemote(from, to, nil, func(string) (bool, bool) { return false, true }) } - if enableDNS { - go relayToRemote(net.JoinHostPort(serveIP, "http"), "", ParseHTTP, shouldProxy) - go relayToRemote(net.JoinHostPort(serveIP, "https"), "", ParseHTTPS, shouldProxy) - } + go relayToRemote(":http", "", ParseHTTP, shouldProxy) + go relayToRemote(":https", "", ParseHTTPS, shouldProxy) - log.Infow("start sower client", "dns solution", enableDNS, "forwards", forwards) + log.Infow("start sower client", "forwards", forwards) select {} } diff --git a/proxy/socks5.go b/proxy/socks5.go new file mode 100644 index 0000000..79b4f18 --- /dev/null +++ b/proxy/socks5.go @@ -0,0 +1,43 @@ +package proxy + +import ( + "net" + + "github.com/wweir/sower/transport" + "github.com/wweir/util-go/log" +) + +func StartSocks5Proxy(listenAddr, serverAddr string, password []byte) { + ln, err := net.Listen("tcp", listenAddr) + if err != nil { + log.Fatalw("socks5 proxy", "addr", listenAddr, "err", err) + } + + serveSocks5(ln, serverAddr, password) +} + +func serveSocks5(ln net.Listener, serverAddr string, password []byte) { + conn, err := ln.Accept() + if err != nil { + log.Fatalw("socks5 proxy", "err", err) + } + go serveSocks5(ln, serverAddr, password) + + tgtAddr, err := transport.ParseSocks5(conn) + if err != nil { + log.Errorw("socks5 proxy", "err", err) + return + } + + rc, err := transport.Dial(tgtAddr, func(host string) (string, []byte) { + return serverAddr, password + }) + if err != nil { + log.Errorw("socks5 proxy", "err", err) + return + } + + relay(conn, rc) + conn.Close() + rc.Close() +} diff --git a/router/router.go b/router/router.go index 755c808..e197a6d 100644 --- a/router/router.go +++ b/router/router.go @@ -9,8 +9,8 @@ import ( "github.com/wweir/sower/transport" "github.com/wweir/sower/util" - "github.com/wweir/utils/log" - "github.com/wweir/utils/mem" + "github.com/wweir/util-go/log" + "github.com/wweir/util-go/mem" ) // Route implement a router for each request @@ -18,27 +18,33 @@ type Route struct { once sync.Once cache *mem.Cache - ProxyAddress string - ProxyPassword string - password []byte + ProxyAddress string + password []byte DetectLevel int // dynamic detect proxy level - DirectList []string - directRule *util.Node - ProxyList []string + blockRule *util.Node proxyRule *util.Node + directRule *util.Node PersistFn func(string) } -// ShouldProxy check if the domain shoule request though proxy -func (r *Route) GenProxyCheck(sync bool) func(string) bool { - r.once.Do(func() { - r.cache = mem.New(4 * time.Hour) - r.password = []byte(r.ProxyPassword) - r.directRule = util.NewNodeFromRules(r.DirectList...) - r.proxyRule = util.NewNodeFromRules(r.ProxyList...) - }) +func NewRoute(address, password string, detectLevel int, + blocklist, proxylist, directlist []string, persist func(string)) *Route { + return &Route{ + cache: mem.New(4 * time.Hour), + ProxyAddress: address, + password: []byte(password), + DetectLevel: detectLevel, + blockRule: util.NewNodeFromRules(blocklist...), + proxyRule: util.NewNodeFromRules(proxylist...), + directRule: util.NewNodeFromRules(directlist...), + PersistFn: persist, + } +} + +// ShouldProxy check if the domain shoule request though proxy +func (r *Route) GenProxyCheck(sync bool) func(string) (bool, bool) { detect := func(domain string) bool { go r.cache.Remember(r, domain) return true @@ -53,21 +59,25 @@ func (r *Route) GenProxyCheck(sync bool) func(string) bool { } } - return func(domain string) bool { + return func(domain string) (bool, bool) { domain = strings.TrimSuffix(domain, ".") // break deadlook, for wildcard if sepCount := strings.Count(domain, "."); sepCount == 0 || sepCount >= 5 { - return false + return false, false + } + + if r.blockRule.Match(domain) { + return true, false } if r.proxyRule.Match(domain) { - return true + return false, true } if r.directRule.Match(domain) { - return false + return false, false } - return detect(domain) + return false, detect(domain) } } diff --git a/transport/socks5.go b/transport/socks5.go deleted file mode 100644 index e44e070..0000000 --- a/transport/socks5.go +++ /dev/null @@ -1,125 +0,0 @@ -package transport - -import ( - "encoding/binary" - "fmt" - "io" - "net" - "strings" -) - -func IsSocks5Schema(addr string) (string, bool) { - if strings.HasPrefix(addr, "socks5://") { - return strings.TrimPrefix(addr, "socks5://"), true - } - - if strings.HasPrefix(addr, "socks5h://") { - return strings.TrimPrefix(addr, "socks5h://"), true - } - - return addr, false -} - -func ToSocks5(c net.Conn, host string, port uint16) (net.Conn, error) { - return &conn{ - init: make(chan struct{}), - Conn: c, - domain: host, - port: port, - }, nil -} - -type conn struct { - init chan struct{} - domain string - port uint16 - net.Conn -} - -func (c *conn) Read(b []byte) (n int, err error) { - <-c.init - return c.Conn.Read(b) -} - -func (c *conn) Write(b []byte) (n int, err error) { - select { - case <-c.init: - return c.Conn.Write(b) - default: - } - - { - req := &authReq{ - VER: 5, - NMETHODS: 1, - METHODS: [1]byte{0}, // NO AUTHENTICATION REQUIRED - } - if err := binary.Write(c.Conn, binary.BigEndian, req); err != nil { - return 0, err - } - } - { - resp := &authResp{} - if err := binary.Read(c.Conn, binary.BigEndian, resp); err != nil { - return 0, err - } - } - { - portBuf := make([]byte, 2) - binary.BigEndian.PutUint16(portBuf, c.port) - req := &request{ - req: req{ - VER: 5, // socks5 - CMD: 1, // CONNECT - RSV: 0, // RESERVED - ATYP: 3, // DOMAINNAME - }, - DST_ADDR: append([]byte{byte(len(c.domain))}, []byte(c.domain)...), - DST_PORT: portBuf, - } - - if _, err := c.Conn.Write(req.Bytes()); err != nil { - return 0, err - } - } - { - resp := &response{} - if err := binary.Read(c.Conn, binary.BigEndian, &(resp.resp)); err != nil { - return 0, err - } - - switch resp.REP { - case 0x00: - default: - return 0, fmt.Errorf("socks5 handshake fail, return code: %d", resp.REP) - } - - switch resp.ATYP { - case 0x01: // IPv4 - resp.DST_ADDR = make([]byte, net.IPv4len) - if _, err := io.ReadFull(c.Conn, resp.DST_ADDR); err != nil { - return 0, err - } - case 0x03: // domain name - if _, err := io.ReadFull(c.Conn, resp.DST_ADDR[:1]); err != nil { - return 0, err - } - if _, err := io.ReadFull(c.Conn, resp.DST_ADDR[1:1+int(resp.DST_ADDR[0])]); err != nil { - return 0, err - } - case 0x04: - resp.DST_ADDR = make([]byte, net.IPv6len) - if _, err := io.ReadFull(c.Conn, resp.DST_ADDR); err != nil { - return 0, err - } - } - - resp.DST_PORT = make([]byte, 2) - if _, err := io.ReadFull(c.Conn, resp.DST_PORT); err != nil { - return 0, err - } - } - - close(c.init) - return c.Conn.Write(b) -} diff --git a/transport/socks5_client.go b/transport/socks5_client.go new file mode 100644 index 0000000..079a9b7 --- /dev/null +++ b/transport/socks5_client.go @@ -0,0 +1,124 @@ +package transport + +import ( + "encoding/binary" + "net" + "strings" + + "golang.org/x/xerrors" +) + +func IsSocks5Schema(addr string) (string, bool) { + if strings.HasPrefix(addr, "socks5://") { + return strings.TrimPrefix(addr, "socks5://"), true + } + + if strings.HasPrefix(addr, "socks5h://") { + return strings.TrimPrefix(addr, "socks5h://"), true + } + + return addr, false +} + +func ToSocks5(c net.Conn, host string, port uint16) (net.Conn, error) { + return &conn{ + init: make(chan struct{}), + Conn: c, + domain: host, + port: port, + }, nil +} + +type conn struct { + init chan struct{} + domain string + port uint16 + net.Conn +} + +func (c *conn) Read(b []byte) (n int, err error) { + select { + case <-c.init: + return c.Conn.Read(b) + default: + return 0, c.clientHandshake() + } +} + +func (c *conn) Write(b []byte) (n int, err error) { + select { + case <-c.init: + return c.Conn.Write(b) + default: + return 0, c.clientHandshake() + } +} + +func (c *conn) clientHandshake() error { + { + req := &authReq{ + VER: 5, + NMETHODS: 1, + METHODS: [1]byte{0}, // NO AUTHENTICATION REQUIRED + } + if err := binary.Write(c.Conn, binary.BigEndian, req); err != nil { + return xerrors.New(err.Error()) + } + } + { + resp := &authResp{} + if err := binary.Read(c.Conn, binary.BigEndian, resp); err != nil { + return xerrors.New(err.Error()) + } + } + { + reqHead := requestHead{ + VER: 5, // socks5 + CMD: 1, // CONNECT + RSV: 0, // RESERVED + ATYP: 3, // DOMAINNAME + } + + if err := binary.Write(c.Conn, binary.BigEndian, reqHead); err != nil { + return xerrors.New(err.Error()) + } + + buf := make([]byte, 0, 1 /*LEN*/ +len(c.domain)+2 /*PORT*/) + buf = append(buf, byte(len(c.domain))) + buf = append(buf, []byte(c.domain)...) + buf = append(buf, byte(c.port>>8), byte(c.port)) + if _, err := c.Conn.Write(buf); err != nil { + return xerrors.New(err.Error()) + } + } + { + head := replyHead{} + if err := binary.Read(c.Conn, binary.BigEndian, &head); err != nil { + return xerrors.New(err.Error()) + } + + switch head.REP { + case 0x00: + default: + return xerrors.Errorf("socks5 handshake fail, return code: %d", head.REP) + } + + var bindAddr addrType + switch head.ATYP { + case 0x01: // IPv4 + bindAddr = addrTypeIPv4{} + case 0x03: // domain name + bindAddr = addrTypeDomain{} + case 0x04: // IPv6 + bindAddr = addrTypeIPv6{} + default: + return xerrors.New("invalid connect type") + } + if err := bindAddr.Fullfill(c.Conn); err != nil { + return xerrors.New(err.Error()) + } + } + + close(c.init) + return nil +} diff --git a/transport/socks5_rfc.go b/transport/socks5_rfc.go index 6ae46f0..8ff3151 100644 --- a/transport/socks5_rfc.go +++ b/transport/socks5_rfc.go @@ -1,44 +1,115 @@ package transport +import ( + "encoding/binary" + "io" + "net" + "strconv" + + "golang.org/x/xerrors" +) + // https://tools.ietf.org/html/rfc1928 +// 1. client send auth request type authReq struct { VER byte NMETHODS byte METHODS [1]byte // 1 to 255, fix to no authentication } +// 2. server response auth request type authResp struct { VER byte METHOD byte } -type request struct { - req - DST_ADDR []byte // first byte is length - DST_PORT []byte // two bytes -} -type req struct { +// 3. client request with target address +type requestHead struct { VER byte CMD byte RSV byte ATYP byte } -func (r *request) Bytes() []byte { - out := []byte{r.VER, r.CMD, r.RSV, r.ATYP} - out = append(out, r.DST_ADDR...) - return append(out, r.DST_PORT...) -} - -type response struct { - resp - DST_ADDR []byte // first byte is length - DST_PORT []byte // two bytes -} -type resp struct { +// 4. server response with the address that assigned to connect to target address +type replyHead struct { VER byte REP byte RSV byte ATYP byte } + +type addrType interface { + Fullfill(r io.Reader) error + String() string +} + +// ATYP: +// 0x01 -> net.IPv4len +// 0x03 -> first byte is length +// 0x04 -> net.IPv6len +type addrTypeIPv4 struct { + DST_ADDR [4]byte + DST_PORT uint16 +} + +func (a addrTypeIPv4) Fullfill(r io.Reader) error { + return binary.Read(r, binary.BigEndian, &a) +} +func (a addrTypeIPv4) String() string { + return net.JoinHostPort( + net.IP(a.DST_ADDR[:]).String(), + strconv.FormatUint(uint64(a.DST_PORT), 10), + ) +} + +type addrTypeDomain struct { + DST_ADDR_LEN uint8 + DST_ADDR []byte + DST_PORT uint16 +} + +func (a addrTypeDomain) Fullfill(r io.Reader) error { + buf := make([]byte, 256) + // domain length + if _, err := io.ReadFull(r, buf[:1]); err != nil { + return xerrors.New(err.Error()) + } + a.DST_ADDR_LEN = uint8(buf[0]) + + // domain + if _, err := io.ReadFull(r, buf[:int(buf[0])]); err != nil { + return xerrors.New(err.Error()) + } + a.DST_ADDR = buf[:int(buf[0])] + + // port + if _, err := io.ReadFull(r, buf[:2]); err != nil { + return xerrors.New(err.Error()) + } + a.DST_PORT = binary.BigEndian.Uint16(buf[:2]) + + return nil +} +func (a addrTypeDomain) String() string { + return net.JoinHostPort( + net.IP(a.DST_ADDR[:]).String(), + strconv.FormatUint(uint64(a.DST_PORT), 10), + ) +} + +type addrTypeIPv6 struct { + DST_ADDR [16]byte + DST_PORT uint16 +} + +func (a addrTypeIPv6) Fullfill(r io.Reader) error { + return binary.Read(r, binary.BigEndian, &a) +} +func (a addrTypeIPv6) String() string { + return net.JoinHostPort( + net.IP(a.DST_ADDR[:]).String(), + strconv.FormatUint(uint64(a.DST_PORT), 10), + ) +} diff --git a/transport/socks5_server.go b/transport/socks5_server.go new file mode 100644 index 0000000..6166e8f --- /dev/null +++ b/transport/socks5_server.go @@ -0,0 +1,74 @@ +package transport + +import ( + "encoding/binary" + "net" + + "golang.org/x/xerrors" +) + +func ParseSocks5(conn net.Conn) (tgtaddr string, err error) { + { + authReq := new(authReq) + if err := binary.Read(conn, binary.BigEndian, authReq); err != nil { + return "", xerrors.New(err.Error()) + } + + if authReq.VER != 5 || // socks5 + authReq.NMETHODS != 1 || + authReq.METHODS[0] != 0 { // NO_AUTH + return "", xerrors.New("invalid socks5 auth method") + } + } + { + if err := binary.Write(conn, binary.BigEndian, &authResp{ + VER: 5, + METHOD: 1, + }); err != nil { + return "", xerrors.New(err.Error()) + } + } + { + head := &requestHead{} + if err := binary.Read(conn, binary.BigEndian, head); err != nil { + return "", xerrors.New(err.Error()) + } + if head.VER != 5 || + head.CMD != 1 { + return "", xerrors.New("invalid socks5 connect request") + } + + var addr addrType + switch head.ATYP { + case 0x01: // IPv4 + addr = addrTypeIPv4{} + case 0x03: // domain name + addr = addrTypeDomain{} + case 0x04: // IPv6 + addr = addrTypeIPv6{} + default: + return "", xerrors.New("invalid connect type") + } + if err := addr.Fullfill(conn); err != nil { + return "", xerrors.New(err.Error()) + } + tgtaddr = addr.String() + } + { + if err := binary.Write(conn, binary.BigEndian, &replyHead{ + VER: 5, + REP: 0, + RSV: 0, + ATYP: 1, + }); err != nil { + return "", xerrors.New(err.Error()) + } + + // FIXME: return the real address + if _, err := conn.Write(make([]byte, 6)); err != nil { + return "", xerrors.New(err.Error()) + } + } + + return tgtaddr, nil +} diff --git a/transport/util.go b/transport/util.go index eac0e9d..4142fb7 100644 --- a/transport/util.go +++ b/transport/util.go @@ -7,7 +7,7 @@ import ( "strconv" "github.com/wweir/sower/dhcp" - "github.com/wweir/utils/log" + "github.com/wweir/util-go/log" ) var ( diff --git a/util/suffix_tree.go b/util/suffix_tree.go index 2f7b84b..54e606b 100644 --- a/util/suffix_tree.go +++ b/util/suffix_tree.go @@ -49,7 +49,7 @@ func (n *node) add(secs []string) { case 0: return case 1: - n.node[secs[length-1]] = &node{node: map[string]*node{"": &node{}}} + n.node[secs[length-1]] = &node{node: map[string]*node{"": {}}} default: sec := secs[length-1] if sec == "**" {