diff --git a/README.md b/README.md index a97189b..80e60db 100644 --- a/README.md +++ b/README.md @@ -650,6 +650,8 @@ Then access to the local 8080 port is access to the proxy port 38080 on the VPS, ### How to use the source code? use command cd to enter your go SRC directory and then +mkdir snail007 +cd snail007 execute `git clone https://github.com/snail007/goproxy.git ./proxy` Direct compilation: `go build` execution: `go run *.go` diff --git a/README_ZH.md b/README_ZH.md index 9643244..31dc5f8 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -656,8 +656,9 @@ KCP协议需要-B参数设置一个密码用于加密解密数据 - 欢迎加群反馈... ### 如何使用源码? -建议go1.8,不保证>=1.9能用. -cd进入你的go src目录,然后git clone https://github.com/snail007/goproxy.git ./proxy 即可. +建议go1.8.5,不保证>=1.9能用. +cd进入你的go src目录,新建文件夹snail007, +cd进入snail007,然后git clone https://github.com/snail007/goproxy.git ./proxy 即可. 编译直接:go build 运行: go run *.go utils是工具包,service是具体的每个服务类. diff --git a/config.go b/config.go index 9c35d85..b2de361 100755 --- a/config.go +++ b/config.go @@ -76,6 +76,8 @@ func initConfig() (err error) { httpArgs.AuthURLTimeout = http.Flag("auth-timeout", "access 'auth-url' timeout milliseconds").Default("3000").Int() httpArgs.AuthURLOkCode = http.Flag("auth-code", "access 'auth-url' success http code").Default("204").Int() httpArgs.AuthURLRetry = http.Flag("auth-retry", "access 'auth-url' fail and retry count").Default("1").Int() + httpArgs.DNSAddress = http.Flag("dns-address", "if set this, proxy will use this dns for resolve doamin").Short('q').Default("").String() + httpArgs.DNSTTL = http.Flag("dns-ttl", "caching seconds of dns query result").Short('e').Default("300").Int() //########tcp######### tcp := app.Command("tcp", "proxy on tcp mode") @@ -182,6 +184,8 @@ func initConfig() (err error) { socksArgs.AuthURLTimeout = socks.Flag("auth-timeout", "access 'auth-url' timeout milliseconds").Default("3000").Int() socksArgs.AuthURLOkCode = socks.Flag("auth-code", "access 'auth-url' success http code").Default("204").Int() socksArgs.AuthURLRetry = socks.Flag("auth-retry", "access 'auth-url' fail and retry count").Default("0").Int() + socksArgs.DNSAddress = socks.Flag("dns-address", "if set this, proxy will use this dns for resolve doamin").Short('q').Default("").String() + socksArgs.DNSTTL = socks.Flag("dns-ttl", "caching seconds of dns query result").Short('e').Default("300").Int() //parse args serviceName := kingpin.MustParse(app.Parse(os.Args[1:])) diff --git a/services/args.go b/services/args.go index 3a845f6..0482a0c 100644 --- a/services/args.go +++ b/services/args.go @@ -138,6 +138,8 @@ type HTTPArgs struct { KCPMethod *string KCPKey *string LocalIPS *[]string + DNSAddress *string + DNSTTL *int } type UDPArgs struct { Parent *string @@ -182,6 +184,8 @@ type SocksArgs struct { UDPParent *string UDPLocal *string LocalIPS *[]string + DNSAddress *string + DNSTTL *int } func (a *TCPArgs) Protocol() string { diff --git a/services/http.go b/services/http.go index 7ff67c2..cd3f0cc 100644 --- a/services/http.go +++ b/services/http.go @@ -16,12 +16,13 @@ import ( ) type HTTP struct { - outPool utils.OutPool - cfg HTTPArgs - checker utils.Checker - basicAuth utils.BasicAuth - sshClient *ssh.Client - lockChn chan bool + outPool utils.OutPool + cfg HTTPArgs + checker utils.Checker + basicAuth utils.BasicAuth + sshClient *ssh.Client + lockChn chan bool + domainResolver utils.DomainResolver } func NewHTTP() Service { @@ -74,6 +75,9 @@ func (s *HTTP) InitService() { if *s.cfg.Parent != "" { s.checker = utils.NewChecker(*s.cfg.HTTPTimeout, int64(*s.cfg.Interval), *s.cfg.Blocked, *s.cfg.Direct) } + if *s.cfg.DNSAddress != "" { + (*s).domainResolver = utils.NewDomainResolver(*s.cfg.DNSAddress, *s.cfg.DNSTTL) + } if *s.cfg.ParentType == "ssh" { err := s.ConnectSSH() if err != nil { @@ -82,7 +86,7 @@ func (s *HTTP) InitService() { go func() { //循环检查ssh网络连通性 for { - conn, err := utils.ConnectHost(*s.cfg.Parent, *s.cfg.Timeout*2) + conn, err := utils.ConnectHost(s.domainResolver.MustResolve(*s.cfg.Parent), *s.cfg.Timeout*2) if err == nil { _, err = conn.Write([]byte{0}) } @@ -211,7 +215,7 @@ func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *ut } } } else { - outConn, err = utils.ConnectHost(address, *s.cfg.Timeout) + outConn, err = utils.ConnectHost(s.domainResolver.MustResolve(address), *s.cfg.Timeout) } tryCount++ if err == nil || tryCount > maxTryCount { @@ -304,7 +308,7 @@ func (s *HTTP) ConnectSSH() (err error) { if s.sshClient != nil { s.sshClient.Close() } - s.sshClient, err = ssh.Dial("tcp", *s.cfg.Parent, &config) + s.sshClient, err = ssh.Dial("tcp", s.domainResolver.MustResolve(*s.cfg.Parent), &config) <-s.lockChn return } @@ -318,7 +322,7 @@ func (s *HTTP) InitOutConnPool() { *s.cfg.KCPMethod, *s.cfg.KCPKey, s.cfg.CertBytes, s.cfg.KeyBytes, - *s.cfg.Parent, + s.domainResolver.MustResolve(*s.cfg.Parent), *s.cfg.Timeout, *s.cfg.PoolSize, *s.cfg.PoolSize*2, diff --git a/services/socks.go b/services/socks.go index 7f74410..e1d13ed 100644 --- a/services/socks.go +++ b/services/socks.go @@ -6,10 +6,10 @@ import ( "io/ioutil" "log" "net" + "runtime/debug" "snail007/proxy/utils" "snail007/proxy/utils/aes" "snail007/proxy/utils/socks" - "runtime/debug" "strings" "time" @@ -17,12 +17,13 @@ import ( ) type Socks struct { - cfg SocksArgs - checker utils.Checker - basicAuth utils.BasicAuth - sshClient *ssh.Client - lockChn chan bool - udpSC utils.ServerChannel + cfg SocksArgs + checker utils.Checker + basicAuth utils.BasicAuth + sshClient *ssh.Client + lockChn chan bool + udpSC utils.ServerChannel + domainResolver utils.DomainResolver } func NewSocks() Service { @@ -77,6 +78,9 @@ func (s *Socks) CheckArgs() { } func (s *Socks) InitService() { s.InitBasicAuth() + if *s.cfg.DNSAddress != "" { + (*s).domainResolver = utils.NewDomainResolver(*s.cfg.DNSAddress, *s.cfg.DNSTTL) + } s.checker = utils.NewChecker(*s.cfg.Timeout, int64(*s.cfg.Interval), *s.cfg.Blocked, *s.cfg.Direct) if *s.cfg.ParentType == "ssh" { err := s.ConnectSSH() diff --git a/utils/functions.go b/utils/functions.go index ffb9e1b..80a16ab 100755 --- a/utils/functions.go +++ b/utils/functions.go @@ -431,7 +431,7 @@ func GetKCPBlock(method, key string) (block kcp.BlockCrypt) { } return } -func HttpGet(URL string, timeout int) (body []byte, code int, err error) { +func HttpGet(URL string, timeout int, host ...string) (body []byte, code int, err error) { var tr *http.Transport var client *http.Client conf := &tls.Config{ @@ -445,7 +445,16 @@ func HttpGet(URL string, timeout int) (body []byte, code int, err error) { client = &http.Client{Timeout: time.Millisecond * time.Duration(timeout), Transport: tr} } defer tr.CloseIdleConnections() - resp, err := client.Get(URL) + + //resp, err := client.Get(URL) + req, err := http.NewRequest("GET", URL, nil) + if err != nil { + return + } + if len(host) == 1 { + req.Host = host[0] + } + resp, err := client.Do(req) if err != nil { return } diff --git a/utils/structs.go b/utils/structs.go index ab9191d..50b7415 100644 --- a/utils/structs.go +++ b/utils/structs.go @@ -15,6 +15,8 @@ import ( "strings" "sync" "time" + + "src/github.com/miekg/dns" ) type Checker struct { @@ -815,3 +817,91 @@ func (c *ClientKeyRouter) GetKey() string { } } + +type DomainResolver struct { + ttl int + dnsAddrress string + data ConcurrentMap +} +type DomainResolverItem struct { + ip string + domain string + expiredAt int64 +} + +func NewDomainResolver(dnsAddrress string, ttl int) DomainResolver { + + return DomainResolver{ + ttl: ttl, + dnsAddrress: dnsAddrress, + data: NewConcurrentMap(), + } +} +func (a *DomainResolver) MustResolve(address string) (ip string) { + ip, _ = a.Resolve(address) + return +} +func (a *DomainResolver) Resolve(address string) (ip string, err error) { + domain := address + port := "" + defer func() { + if port != "" { + ip = net.JoinHostPort(ip, port) + } + }() + if strings.Contains(domain, ":") { + domain, port, err = net.SplitHostPort(domain) + if err != nil { + return + } + } + item, ok := a.data.Get(domain) + if ok { + if (*item.(*DomainResolverItem)).expiredAt > time.Now().Unix() { + ip = (*item.(*DomainResolverItem)).ip + return + } + + } else { + item = &DomainResolverItem{ + domain: domain, + } + a.data.Set(domain, item) + } + + if net.ParseIP(domain) != nil { + return domain, nil + } + c := new(dns.Client) + m := new(dns.Msg) + m.SetQuestion(dns.Fqdn(domain), dns.TypeA) + m.RecursionDesired = true + r, _, err := c.Exchange(m, a.dnsAddrress) + if r == nil { + return + } + + if r.Rcode != dns.RcodeSuccess { + err = fmt.Errorf(" *** invalid answer name %s after A query for %s", domain, a.dnsAddrress) + return + } + for _, answer := range r.Answer { + if answer.Header().Rrtype == dns.TypeA { + info := strings.Fields(answer.String()) + if len(info) >= 5 { + ip = info[4] + _item := item.(*DomainResolverItem) + (*_item).expiredAt = time.Now().Unix() + int64(a.ttl) + (*_item).ip = ip + } + return + } + } + return +} +func (a *DomainResolver) PrintData() { + for k, item := range a.data.Items() { + d := item.(*DomainResolverItem) + fmt.Printf("%s:ip[%s],domain[%s],expired at[%d]\n", k, (*d).ip, (*d).domain, (*d).expiredAt) + } +}