From 7686a4bdc8d837617f6f1af6a89399db2a8a4758 Mon Sep 17 00:00:00 2001 From: ginuerzh Date: Thu, 6 Dec 2018 19:57:36 +0800 Subject: [PATCH] add more detail info for routing --- bypass.go | 2 +- chain.go | 58 ++----- cmd/gost/{ => .config}/bypass.txt | 0 cmd/gost/{ => .config}/dns.txt | 0 cmd/gost/{ => .config}/hosts.txt | 0 cmd/gost/{ => .config}/peer.txt | 0 cmd/gost/.config/probe_resist.txt | 1 + cmd/gost/{ => .config}/secrets.txt | 0 cmd/gost/kcp.json | 21 --- cmd/gost/route.go | 8 +- cmd/gost/ssl/README.md | 48 ------ cmd/gost/ssl/localhost.crt | 23 --- cmd/gost/ssl/localhost.csr | 16 -- cmd/gost/ssl/localhost.key | 27 ---- cmd/gost/ssl/rootCA.crt | 27 ---- cmd/gost/ssl/rootCA.key | 51 ------ cmd/gost/ssl/rootCA.srl | 1 - handler.go | 8 + http.go | 226 ++++++++++++++------------ http2.go | 250 ++++++++++++++++++----------- node.go | 9 +- selector.go | 2 +- sni.go | 83 +++++++--- socks.go | 205 +++++++++++++++++------ ss.go | 73 ++++++--- ssh.go | 14 +- 26 files changed, 592 insertions(+), 561 deletions(-) rename cmd/gost/{ => .config}/bypass.txt (100%) rename cmd/gost/{ => .config}/dns.txt (100%) rename cmd/gost/{ => .config}/hosts.txt (100%) rename cmd/gost/{ => .config}/peer.txt (100%) create mode 100644 cmd/gost/.config/probe_resist.txt rename cmd/gost/{ => .config}/secrets.txt (100%) delete mode 100644 cmd/gost/kcp.json delete mode 100644 cmd/gost/ssl/README.md delete mode 100644 cmd/gost/ssl/localhost.crt delete mode 100644 cmd/gost/ssl/localhost.csr delete mode 100644 cmd/gost/ssl/localhost.key delete mode 100644 cmd/gost/ssl/rootCA.crt delete mode 100644 cmd/gost/ssl/rootCA.key delete mode 100644 cmd/gost/ssl/rootCA.srl diff --git a/bypass.go b/bypass.go index a64a5fc..067dff6 100644 --- a/bypass.go +++ b/bypass.go @@ -152,7 +152,7 @@ func NewBypassPatterns(reversed bool, patterns ...string) *Bypass { // Contains reports whether the bypass includes addr. func (bp *Bypass) Contains(addr string) bool { - if bp == nil { + if bp == nil || addr == "" { return false } // try to strip the port diff --git a/chain.go b/chain.go index dde827c..362d285 100644 --- a/chain.go +++ b/chain.go @@ -1,9 +1,7 @@ package gost import ( - "bytes" "errors" - "fmt" "net" "time" @@ -20,6 +18,7 @@ type Chain struct { isRoute bool Retries int nodeGroups []*NodeGroup + route []Node // nodes in the selected route } // NewChain creates a proxy chain with a list of proxy nodes. @@ -197,18 +196,14 @@ func (c *Chain) Conn(opts ...ChainOption) (conn net.Conn, err error) { continue } conn, err = route.getConn() - if err != nil { - log.Log(err) - continue + if err == nil { + break } - - break } return } // getConn obtains a connection to the last node of the chain. -// It does not handshake with the last node. func (c *Chain) getConn() (conn net.Conn, err error) { if c.IsEmpty() { err = ErrEmptyChain @@ -256,35 +251,7 @@ func (c *Chain) getConn() (conn net.Conn, err error) { } func (c *Chain) selectRoute() (route *Chain, err error) { - if c.IsEmpty() || c.isRoute { - return c, nil - } - - buf := bytes.Buffer{} - route = newRoute() - - for _, group := range c.nodeGroups { - node, err := group.Next() - if err != nil { - return nil, err - } - buf.WriteString(fmt.Sprintf("%s -> ", node.String())) - - if node.Client.Transporter.Multiplex() { - node.DialOptions = append(node.DialOptions, - ChainDialOption(route), - ) - route = newRoute() // cutoff the chain for multiplex. - } - - route.AddNode(node) - } - route.Retries = c.Retries - - if Debug { - log.Log("select route:", buf.String()) - } - return + return c.selectRouteFor("") } // selectRouteFor selects route with bypass testing. @@ -293,8 +260,8 @@ func (c *Chain) selectRouteFor(addr string) (route *Chain, err error) { return c, nil } - buf := bytes.Buffer{} route = newRoute() + var nl []Node for _, group := range c.nodeGroups { var node Node @@ -304,28 +271,21 @@ func (c *Chain) selectRouteFor(addr string) (route *Chain, err error) { } if node.Bypass.Contains(addr) { - if Debug { - buf.WriteString(fmt.Sprintf("[bypass]%s -> %s", node.String(), addr)) - log.Log("[route]", buf.String()) - } - return + break } - buf.WriteString(fmt.Sprintf("%s -> ", node.String())) - if node.Client.Transporter.Multiplex() { node.DialOptions = append(node.DialOptions, ChainDialOption(route), ) - route = newRoute() // cutoff the chain for multiplex. + route = newRoute() // cutoff the chain for multiplex node. } route.AddNode(node) + nl = append(nl, node) } - route.Retries = c.Retries - buf.WriteString(addr) - log.Log("[route]", buf.String()) + route.route = nl return } diff --git a/cmd/gost/bypass.txt b/cmd/gost/.config/bypass.txt similarity index 100% rename from cmd/gost/bypass.txt rename to cmd/gost/.config/bypass.txt diff --git a/cmd/gost/dns.txt b/cmd/gost/.config/dns.txt similarity index 100% rename from cmd/gost/dns.txt rename to cmd/gost/.config/dns.txt diff --git a/cmd/gost/hosts.txt b/cmd/gost/.config/hosts.txt similarity index 100% rename from cmd/gost/hosts.txt rename to cmd/gost/.config/hosts.txt diff --git a/cmd/gost/peer.txt b/cmd/gost/.config/peer.txt similarity index 100% rename from cmd/gost/peer.txt rename to cmd/gost/.config/peer.txt diff --git a/cmd/gost/.config/probe_resist.txt b/cmd/gost/.config/probe_resist.txt new file mode 100644 index 0000000..c57eff5 --- /dev/null +++ b/cmd/gost/.config/probe_resist.txt @@ -0,0 +1 @@ +Hello World! \ No newline at end of file diff --git a/cmd/gost/secrets.txt b/cmd/gost/.config/secrets.txt similarity index 100% rename from cmd/gost/secrets.txt rename to cmd/gost/.config/secrets.txt diff --git a/cmd/gost/kcp.json b/cmd/gost/kcp.json deleted file mode 100644 index 00a576a..0000000 --- a/cmd/gost/kcp.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "key": "it's a secrect", - "crypt": "aes", - "mode": "fast", - "mtu" : 1350, - "sndwnd": 1024, - "rcvwnd": 1024, - "datashard": 10, - "parityshard": 3, - "dscp": 0, - "nocomp": false, - "acknodelay": false, - "nodelay": 0, - "interval": 40, - "resend": 0, - "nc": 0, - "sockbuf": 4194304, - "keepalive": 10, - "snmplog": "", - "snmpperiod": 60 -} \ No newline at end of file diff --git a/cmd/gost/route.go b/cmd/gost/route.go index ecb7e5f..4858729 100644 --- a/cmd/gost/route.go +++ b/cmd/gost/route.go @@ -407,7 +407,8 @@ func (r *route) GenRouters() ([]router, error) { hosts := parseHosts(node.Get("hosts")) handler.Init( - gost.AddrHandlerOption(node.Addr), + // gost.AddrHandlerOption(node.Addr), + gost.AddrHandlerOption(ln.Addr().String()), gost.ChainHandlerOption(chain), gost.UsersHandlerOption(users...), gost.TLSConfigHandlerOption(tlsCfg), @@ -417,9 +418,10 @@ func (r *route) GenRouters() ([]router, error) { gost.BypassHandlerOption(node.Bypass), gost.ResolverHandlerOption(resolver), gost.HostsHandlerOption(hosts), - gost.RetryHandlerOption(node.GetInt("retry")), + gost.RetryHandlerOption(node.GetInt("retry")), // override the global retry option. gost.TimeoutHandlerOption(time.Duration(node.GetInt("timeout"))*time.Second), gost.ProbeResistHandlerOption(node.Get("probe_resist")), + gost.NodeHandlerOption(node), ) rt := router{ @@ -446,7 +448,7 @@ type router struct { } func (r *router) Serve() error { - log.Logf("[route] start %s on %s", r.node.String(), r.server.Addr()) + log.Logf("%s on %s", r.node.String(), r.server.Addr()) return r.server.Serve(r.handler) } diff --git a/cmd/gost/ssl/README.md b/cmd/gost/ssl/README.md deleted file mode 100644 index 6251d3d..0000000 --- a/cmd/gost/ssl/README.md +++ /dev/null @@ -1,48 +0,0 @@ -[//]: <> (https://gist.github.com/fntlnz/cf14feb5a46b2eda428e000157447309) - -# Create Root CA (Done once) - -## Create Root Key - -**Attention:** this is the key used to sign the certificate requests, anyone holding this can sign certificates on your behalf. So keep it in a safe place! - -```bash -openssl genrsa -des3 -out rootCA.key 4096 -``` - -If you want a non password protected key just remove the `-des3` option - - -## Create and self sign the Root Certificate - -```bash -openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.crt -``` - -Here we used our root key to create the root certificate that needs to be distributed in all the computers that have to trust us. - - -# Create a certificate (Done for each server) - -This procedure needs to be followed for each server/appliance that needs a trusted certificate from our CA - -## Create the certificate key - -``` -openssl genrsa -out mydomain.com.key 2048 -``` - -## Create the signing request - -**Important:** Please mind that while creating the signign request is important to specify the `Common Name` providing the IP address or URL for the service, otherwise the certificate -cannot be verified - -``` -openssl req -new -key mydomain.com.key -out mydomain.com.csr -``` - -## Generate the certificate using the `mydomain` csr and key along with the CA Root key - -``` -openssl x509 -req -in mydomain.com.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out mydomain.com.crt -days 500 -sha256 -``` diff --git a/cmd/gost/ssl/localhost.crt b/cmd/gost/ssl/localhost.crt deleted file mode 100644 index 9c33f21..0000000 --- a/cmd/gost/ssl/localhost.crt +++ /dev/null @@ -1,23 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDvjCCAaYCCQC0XjV3wljvnjANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls -b2NhbGhvc3QwHhcNMTgwNzA4MDQ1MzIyWhcNMTkxMTIwMDQ1MzIyWjAuMQswCQYD -VQQGEwJDTjELMAkGA1UEBwwCU0gxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJ -KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMOKgzWil/KjyRy2Axb3XlLB1nMwLFJC -pC6r8yb+1Kq/ldZghJZvymuFVjn+bihvJqvZiOv4KRtnM8gD55AhaQp6Ese5M9b+ -47HLB//SkfJsQREsmnrHfHxjmUQjhMy7jrpcf9OnDOXQ5zk3v6AWEIqMtAiZ99ku -AQvyJJ07+VpwZrMuzbSGfFBCKEbbqP7yKHjSUm3QDTpTiK4AnBmzlVeThUIA68oa -XZKQVXX/8U2i6H4eq5eNpyUsKSnnuK+cryHpAIK4vNMzw96vATTfEmuWASEzkHhW -3KtfXE0CIH0GsK5zueGDo9ygnO7hjtx60SWynlGf6c6edxPwNvEmTZcCAwEAATAN -BgkqhkiG9w0BAQsFAAOCAgEApLkdhnDzErgBljY6qRaR0JlouTpqJXwi5BRi7F1P -bx5ukrZAVSOsZ7ncEkZuxkIX+ktBVFBL8twkvMEl+sMQ24R+F+TrlHWN2xPR/pez -9V19hq26yMIlYLqSq3KZ0W9ZlT2ge+3sTvY+gAJhZ6nOz9WGRJ1mi+pN/ok678QX -KdOJXcePzYr5iKqMq/5cJ2sA1xYwVl+0xrvfRVTFkp4yR6wzGODtjquB+scZ9S64 -GWnFTjHAJvUKYxpeoLAt9lZHsESDqGq7hA4z1uVjhNEDJHKnXW4OhXxMB8Gk2hY8 -3k4zbnKsouNNW0a7jijCMpXOem/vgQF4GK5ecp0S+Ml/AunsPoi6rGgOCX8XXmti -6DfQhsxxgn1co/JKNxhgsnQftXFwKivh73JFctSh+bMLsewfXsvq+b0K3EuuV9bV -EttVCgbUaCDYdA6IDkqD2PRx9tsotne76r+cX+ah+NjnA6XN+XY2bJgV1UaiKTrP -moNHglw+xoUqOJ7FlGJcVC7uIFPhMviNkpSZh6WxX+OSS4fPO25kxxNpldql6I+3 -xb5XEHLpPCEI4PyK0rYnsjk764Loqff8YBMFRQSXIUz9ot5SgGs/FY1vsQap5OeD -Hw2usWhCvkSzr7kiXI+30BvJKK2r9GOAM7mtO9dfkM9MMKKnMzd+O2XE4r6PNLrg -Rds= ------END CERTIFICATE----- diff --git a/cmd/gost/ssl/localhost.csr b/cmd/gost/ssl/localhost.csr deleted file mode 100644 index 29f9f7d..0000000 --- a/cmd/gost/ssl/localhost.csr +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN CERTIFICATE REQUEST----- -MIICczCCAVsCAQAwLjELMAkGA1UEBhMCQ04xCzAJBgNVBAcMAlNIMRIwEAYDVQQD -DAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDDioM1 -opfyo8kctgMW915SwdZzMCxSQqQuq/Mm/tSqv5XWYISWb8prhVY5/m4obyar2Yjr -+CkbZzPIA+eQIWkKehLHuTPW/uOxywf/0pHybEERLJp6x3x8Y5lEI4TMu466XH/T -pwzl0Oc5N7+gFhCKjLQImffZLgEL8iSdO/lacGazLs20hnxQQihG26j+8ih40lJt -0A06U4iuAJwZs5VXk4VCAOvKGl2SkFV1//FNouh+HquXjaclLCkp57ivnK8h6QCC -uLzTM8PerwE03xJrlgEhM5B4VtyrX1xNAiB9BrCuc7nhg6PcoJzu4Y7cetElsp5R -n+nOnncT8DbxJk2XAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAr+AkYRAPulBU -B5HR3pAreYrf3Y2fvGLSNo4hvsJkmXJxDgMZnGsjVzW1IZLF8szn4v050y6Qm/Ne -qupabYP5zpj0vKkACYGJ2zadnowmwlTzwlxEOv27uQykC/IuRcjdloAD7ZwhNwmO -dLNjdiXn63GUeSL/JK0UHyXTqvpmiHq+6TAOdl3vmsRFCQDChRtViK2fwSeX2y87 -hLicSVQyNOe0gUx7IvE9B2QPNhdzaMVPYeN8I/cayNeUKhiWxEGKhwPAaievuSXJ -fUsz11XYBYW+kjFsTqkV1OjkG0mxvwaiq5W3CRx8365w71IMdKV5t5xhc0n0TXp7 -cT27XN7cdw== ------END CERTIFICATE REQUEST----- diff --git a/cmd/gost/ssl/localhost.key b/cmd/gost/ssl/localhost.key deleted file mode 100644 index be778fb..0000000 --- a/cmd/gost/ssl/localhost.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAw4qDNaKX8qPJHLYDFvdeUsHWczAsUkKkLqvzJv7Uqr+V1mCE -lm/Ka4VWOf5uKG8mq9mI6/gpG2czyAPnkCFpCnoSx7kz1v7jscsH/9KR8mxBESya -esd8fGOZRCOEzLuOulx/06cM5dDnOTe/oBYQioy0CJn32S4BC/IknTv5WnBmsy7N -tIZ8UEIoRtuo/vIoeNJSbdANOlOIrgCcGbOVV5OFQgDryhpdkpBVdf/xTaLofh6r -l42nJSwpKee4r5yvIekAgri80zPD3q8BNN8Sa5YBITOQeFbcq19cTQIgfQawrnO5 -4YOj3KCc7uGO3HrRJbKeUZ/pzp53E/A28SZNlwIDAQABAoIBAGx1pMeYMw3L2R5K -urX/aVsf1xI3My5Bdo3IpGsJx+4ZrEOnb4N96FnxMF2kiXd2B44kb/TqxepEOQ2F -VOi2D2xXP5l2WZGz+ZnBUuOL6ZX8g67B/cGCasMX/4gy51Mj6UvnSKOeMeI7GDW9 -fVWPR4eB+c4XkMju4ne8zKBGBs4pN4KoxTWSnZSM4p+q/Jb/DMa+kVhFfRjkqfWc -vCpDgHs1uMcHvPBNYO9flDaC2Jgk4cvV9mX0TolXAvaNo8aN0joM7WH3fvw7NCD9 -LCkqCmpjOxJIqJQT1twIkSy42q7VaFi7ApyIaMfXlmnj4UTlVTe5bBO+2AgwLYtC -cKgDMjECgYEA8JPm3Pc80EsYB6d4qp/Qmy2VrnlaxZwvaRwh63Pssqthg4SZkIp5 -yjXOT4MDlJdrEzMtATRZUXTCRxGFSs0tolNY2KQ2WvYRhISlN8UBkGuMEkRGLuct -p++qpPcSZJcox25kT82CKin1nQYb48k33JAOMUOWIBJO56G35sfPj28CgYEA0BOE -pa+FYj/WxZS79YT1ZbsajeuUKlNUtAIxKJ2cKSQyfFuPM+xZG3H+iroRfR8HCVai -2+Oz9/TlxZOPR7+P+2fpS8W2tT+Qkmiyz8QJAULd+Irw5XIdatkpVm343XxMx3Pa -2qtBmgj6RINvsWTWRotMqhcuDRirxqm1IIQhkFkCgYBLNmIhyOXpVODRW8k8xrQI -H6tBHc2EJD0qRlJQczCX9z6ISIdeCfzjfAjhENuos+IU4ZX7X2thLPikEVUzuou+ -yQHo0QXxUCbP4Exq8Bt6FDV5bIDonvvGGgamhlvouN1V5CxWSrCcD/wquEM15q2h -NiRJwJCJvE+Q2R1OeD9q3wKBgFWDkAJf7luAjQ3KoKy4pfnXOYSWCuCSOr94Hyfo -DmPCIpWFM4dNXRmwccIl0kYv2D54QppILJB9L2lRyZLdIZlbDUA802gN5aamLMbC -dEj2aC9bOsGxcnGVKi4BKEQub4eRD6LKuz1I70H1GpQ3MvDvEuTcfeqX9xDAclYY -t4qRAoGBAI6YSTs97DUe7Zwk7q+S3PBU5uct4Dwtmy2XWZdgHwl5aP8apSLciL5Y -PMkpcTMzkuC+QFaPZ8wFcI7GLg0bOs91hkrqscDKEg4nGB9fJkU82iOQZNL4Hv1u -wO7uIGa2kcpNtQOLNO88y45WFyrn5a+T6VhDmIuc+F+TU1ZzdYdH ------END RSA PRIVATE KEY----- diff --git a/cmd/gost/ssl/rootCA.crt b/cmd/gost/ssl/rootCA.crt deleted file mode 100644 index 030dd82..0000000 --- a/cmd/gost/ssl/rootCA.crt +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEpDCCAowCCQDwV08QFUCcSzANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls -b2NhbGhvc3QwHhcNMTgwNzA4MDQ1MzE3WhcNMjEwNDI3MDQ1MzE3WjAUMRIwEAYD -VQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDE -+71yX9vQY3l52C31e04ACvm2oNMLSCrLOXGewOTFpv9yXjinMC0Ab6Xa4yB4MPtd -ujWDSEq9gkKCkILoalVX7R4gtLDN+EdoVadBw/WHbrGB4sHnFOWwpUbjiiwwPSU5 -qXjcqIqR2sA1BgoAv6c1qHq7V4bgbEjtGL71KkDoQZSIgNyJlXe1mcUKqmqeAnro -0WfwrNXyYt66L3PmCy9MIpWRxf9sa8PDsgT3SQEN4BHb70Z8hNj6RXfVGDZcpfcI -iwbrL//YnK7waRKaKD4LoLOodE0cx3fowSWYvlwUAoYx8wFKOcdtM1zaUp+QDAug -xT5g5ghU150XuqhDU6Wq1An8dLgcDU1D4cxhLk/W8OXtIk4k7yny6eUJi2zHziMm -8jfHd3M6SwohUrE3LsQI5gpvu4sAVFLMkRxaWZ95XhsVMmIsE/L5FHwDfqid0dvx -bafOKT+fI3N3BaUPJlVHCNqSzSZIW59+ufnDwBV7SmJj4KMlvixEU+EFfPFdGiCA -Lr0dSG5+Scx1aClaMUeVccCljp2f99IEa9wI+xwMPDStkOmnhVuqG1aEogggQZkD -/5yh04wrn8EwYCAiasNNUXTV7AoqIt2bgeFbGo2Qr7LdsYuUmaWEzTm0KsHogkkg -Ibd3RPBLDr/WfWI13oHMdsz8jjbXG/D1AhrcdozYDQIDAQABMA0GCSqGSIb3DQEB -CwUAA4ICAQC1EeQqn2AwrS+UVc5fKRpHzV9ZMiDpxFMRLsDWBP5kNr1nSA72yYcR -WgxvdqG/rGRds6lvRbvIaWD0zeujPkR3iCpb1V5oRXQ6lWOlY44pZEwCdnDd2M8I -yQ7BLZCHHmlCN7a51n2o0D78HeILIeeTCQlKFDc5r51qrZbZR5DZmrp9jaZ+3eCg -LQ3Onfj0WEmQFuMFGQrbJ2oaCC1GvuZWEbRh+lrxjRKOyCaRQFTY4Efe8tIwMm6J -1iyMtqK7BxminQCfizQrstB67wMljydYeUf+wwbgkiKGYc9VGopckrO3lntzKycu -9l0BmlZYkmCFt3cv23BcqAbcLdyLXh3yASwVMXaLZ4iVSaslRm4uX+gbKFCBABLa -vqu7JQHfAPOeYj7zCrN12EHejPxdCjSImBeAbe56vax4uFGAodXxDGcepRItSzax -qPKJd8U/8e3JDn+wmZNKwD9UGLZPbiuYOg7X+EWhjki0J6ZjgLc8dMleeD2rO+j2 -P/Wgv1gMr6J1svUlqkNf1Ng9eSbl/nMhuOBVOGcPnK7+wCLxM7ByaR0QgeH6/9VO -4urq53/vspBC679BHsZx3gIhcg4VefmOM2cZnTRM4izPstq1JBQkbuvz+5XuT7Yj -5Fk1/xkapCUifntKYSoslkkbNHRYxAInqkc0txn3qNBI8GAQFksz5g== ------END CERTIFICATE----- diff --git a/cmd/gost/ssl/rootCA.key b/cmd/gost/ssl/rootCA.key deleted file mode 100644 index 60fd7fb..0000000 --- a/cmd/gost/ssl/rootCA.key +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKAIBAAKCAgEAxPu9cl/b0GN5edgt9XtOAAr5tqDTC0gqyzlxnsDkxab/cl44 -pzAtAG+l2uMgeDD7Xbo1g0hKvYJCgpCC6GpVV+0eILSwzfhHaFWnQcP1h26xgeLB -5xTlsKVG44osMD0lOal43KiKkdrANQYKAL+nNah6u1eG4GxI7Ri+9SpA6EGUiIDc -iZV3tZnFCqpqngJ66NFn8KzV8mLeui9z5gsvTCKVkcX/bGvDw7IE90kBDeAR2+9G -fITY+kV31Rg2XKX3CIsG6y//2Jyu8GkSmig+C6CzqHRNHMd36MElmL5cFAKGMfMB -SjnHbTNc2lKfkAwLoMU+YOYIVNedF7qoQ1OlqtQJ/HS4HA1NQ+HMYS5P1vDl7SJO -JO8p8unlCYtsx84jJvI3x3dzOksKIVKxNy7ECOYKb7uLAFRSzJEcWlmfeV4bFTJi -LBPy+RR8A36ondHb8W2nzik/nyNzdwWlDyZVRwjaks0mSFuffrn5w8AVe0piY+Cj -Jb4sRFPhBXzxXRoggC69HUhufknMdWgpWjFHlXHApY6dn/fSBGvcCPscDDw0rZDp -p4VbqhtWhKIIIEGZA/+codOMK5/BMGAgImrDTVF01ewKKiLdm4HhWxqNkK+y3bGL -lJmlhM05tCrB6IJJICG3d0TwSw6/1n1iNd6BzHbM/I421xvw9QIa3HaM2A0CAwEA -AQKCAgBXbeSH/0PxGjWwfuLnMfNM0ZJEHN2PBFj6GmTzsWnY0GZQvMEoc5mFuAhF -PsoKjrMCxsM5obyKoGYkzT9NKOT4QaY9nfVbdfc7t8ikx/USR29B1wN5LS1FWhY8 -p/c08e6zySR7y9K1KgJlhmiqLGZqynyu6gpTUbyMf49CAZ8Ndw4WCBvadRzM3ZM3 -SKxJtZAYBdm8WPocuwVgXe9zC0PS5wa7zMWxuaMKGNlbaGuvXOSQWYNPgSdM7chi -LHz0YjVi9VH80TEdU23SBtDa20Gup4UWH4iaXW47QH8PbG4x82zcfp7z8vEw5rsv -q7xmkvIWSXWGTJMmFQ0EmzRTray5fj38Oo2ZtwHvkJQ1WkiFNFiWFZXnoS3z6h5q -1lX6ZUhCoobUJRRlDYCwNDV6dMYKXK2NNeD1MPvzUoUIpoQnoxnNF+VYMMENax3e -YuEiT6xbBXzB/WE0bFVAtSPzf1vPVw+8MP5BhaH3lQb6XA89FiEZg+u95rNpf2SA -gFWvz0VZsGab+LwYhbYdicmKPRH+2Pzpt5MdWt8jyo066Lv0NP5xpH9IUv/u2RX7 -Ycw0Bu1HWKLoEzovoH6OEa0n1A7H+PNOhABzrLvbU8GMp4kEQpXACxR43KruxE7S -QgotUAb7teCP54yEHTVAe06YIaq4JPk5xqnmMVvaeuy5rvssAQKCAQEA5d4NUONV -/An+bAf31HicZfRH6Pf1N3JUdjYz2l1y60Pf7dzlDI2fjOWlccp24+efXLM1sMeK -GXQZsAnYJevZktQxodM67CsgEFgdGhH2s5Ey3Dp5bt3uS/SaFv4sT1x9awYdYtKp -6fGovjB1Qp/eMuZNJVl9RwegFVzzrrSMxucNzUuL4v4L911ypR4wz4s1ptqI31/U -56B1VRKjwZntqJaNO2Plt/yY0s+ganhzdKBynoOKzTpYHCqZhHdqvqfc1qC0W1xI -E/b3Nf0J+GqjjT7JDbWgqNty5ipDfCdIeems96U1Gu9oeKGDzvCgtImZNFMyHzLM -MhO0v6GA6zkuVQKCAQEA22Cls2AAUuugi2tdZR/krHokrUMPaKvi9RIQpqHpoKqL -E3rKX9aWyIMhktch7VsnDF52R8CMUhgc7PfL4wsWA5cCq26x4E57aJUH5mxc5va5 -n5Sxb3C99Ytr6GpCkG4Y3pzO93ihgfuW+mQREYLpFYd/c/1SH/e4Wx5nGKx6YocY -6b/AbxWcRMlihC2gK7JFgSZMqaL+wn68oKJ7j8RUN+ykZEzBXBWL2l4oKWm+qBDx -pOFSQODeQ0CQhPWovD4dVNmyrh5TcDUlJQ3+iU2hRkXe13mLTkGpSM53kwkrfVn+ -4SmVLEm5YhNcHG14A1yDqs6SY//8l5xfUeZyrPBK2QKCAQEAqIsRLm8SG9RkFWge -Qk8RNfxQQbSVu0r8PRTvHjyIx5Ij/e+KjpLFGvVDQtUWKXMquTi5tF4KlzE2qIn/ -T4bIKE2n+qS7vnC8eN9yryvevLlJFotVgIH/ePfnh9ZkPOhvGWsJXu1iIqPLe3Bi -ejBoJuAQTsN4BP3FVgSqtD20Px8pUo8DCbQGqCB/sCwb1AGZnDb+RvKoVBGmFnOt -WIX56TRCZ/qOdEIk9+W/FHIvDaObhziiLGqMMlLV73fz78l7Nm/s7lQSkXjyuEZJ -6jiepTEVEBVNsKH/dF4mz0CqdqFs7sPW1WIXMuQSlkh/PQDrMZ+Sz6daa5lhXWUY -9uAdZQKCAQBrDzRuYIhn7yPPRlsy0ai4X3dsstBfRZsh/Gnx2Ax64x+yJveCY+f7 -/LqyvZiKDDT3PVY92ALiwW/EWX2/1JYutFCSNxhJniNtu2U6l2GTOY8HCPq6puud -XCgSKWFIuOIcKax7avxuwchBc/o8cIWtgw25HkQo46ytkx2/FdU4JjQLRw/zZjl3 -/Eu+s8F58asnxvgcxTXM1yrYvdLNK4PqMutbI3YtqToyHEc/RqLLxFEZJPkOPm9Z -pLWinXx2OV35HbCsdpJDrTvuZHD2stLkx45j26YXT8X8iP4j3JLDvtq7KZ7qGSSG -b2pBWU77XPfIsL0SXkf3+VEvV+ZY7X+pAoIBABV6Mu5Yr4UxI+ZgsaKgcK8aKoyD -5GDshxkxs8R3K1i7eCF0mEjxkV25r11KX10qFvG+hPJqizKQDBOCQC2w3noqz42p -QVUeBNXpDVGoImD1/4DqUvQMivTwHWS+wSAi/wYAODJ6/bWP5Kil/7iDOwCPp0WD -mLd0ujjwkOw3Xksn2Gd01pXeiT4FZpkYnyh5ddWGf1TihRFATW5+vpi6t+6KX3LR -hwd9zi6soSwju/n986NUfGfeewBb6/fnh6hM/vfS2a0Blvk/7yM1k2P0uN+TzLYf -skhRay10UoMwtXak+q/DBzrrAbW3EwuIdV66H4dx1AV5NMq6kAAtfDXc728= ------END RSA PRIVATE KEY----- diff --git a/cmd/gost/ssl/rootCA.srl b/cmd/gost/ssl/rootCA.srl deleted file mode 100644 index 703d075..0000000 --- a/cmd/gost/ssl/rootCA.srl +++ /dev/null @@ -1 +0,0 @@ -B45E3577C258EF9E diff --git a/handler.go b/handler.go index cc9a6c1..c1fab92 100644 --- a/handler.go +++ b/handler.go @@ -33,6 +33,7 @@ type HandlerOptions struct { Resolver Resolver Hosts *Hosts ProbeResist string + Node Node } // HandlerOption allows a common way to set handler options. @@ -129,6 +130,13 @@ func ProbeResistHandlerOption(pr string) HandlerOption { } } +// NodeHandlerOption set the server node for server handler. +func NodeHandlerOption(node Node) HandlerOption { + return func(opts *HandlerOptions) { + opts.Node = node + } +} + type autoHandler struct { options *HandlerOptions } diff --git a/http.go b/http.go index e436f60..ccc29bd 100644 --- a/http.go +++ b/http.go @@ -2,6 +2,7 @@ package gost import ( "bufio" + "bytes" "encoding/base64" "fmt" "net" @@ -108,18 +109,34 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) { if req == nil { return } - if Debug { - dump, _ := httputil.DumpRequest(req, false) - log.Logf("[http] %s -> %s\n%s", conn.RemoteAddr(), req.Host, string(dump)) - } + host := req.Host // try to get the actual host. if v := req.Header.Get("Gost-Target"); v != "" { - if host, err := decodeServerName(v); err == nil { - req.Host = host + if h, err := decodeServerName(v); err == nil { + host = h } } + if _, port, _ := net.SplitHostPort(host); port == "" { + host = net.JoinHostPort(host, "80") + } + + u, _, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization")) + if u != "" { + u += "@" + } + log.Logf("[http] %s%s -> %s -> %s", + u, conn.RemoteAddr(), h.options.Node.String(), host) + + if Debug { + dump, _ := httputil.DumpRequest(req, false) + log.Logf("[http] %s -> %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(dump)) + } + + req.Host = host + req.Header.Del("Gost-Target") + resp := &http.Response{ ProtoMajor: 1, ProtoMinor: 1, @@ -127,96 +144,35 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) { } resp.Header.Add("Proxy-Agent", "gost/"+Version) - if !Can("tcp", req.Host, h.options.Whitelist, h.options.Blacklist) { + if !Can("tcp", host, h.options.Whitelist, h.options.Blacklist) { log.Logf("[http] %s - %s : Unauthorized to tcp connect to %s", - conn.RemoteAddr(), req.Host, req.Host) + conn.RemoteAddr(), conn.LocalAddr(), host) resp.StatusCode = http.StatusForbidden if Debug { dump, _ := httputil.DumpResponse(resp, false) - log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(dump)) + log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(dump)) } resp.Write(conn) return } - if h.options.Bypass.Contains(req.Host) { - log.Logf("[http] [bypass] %s", req.Host) + if h.options.Bypass.Contains(host) { resp.StatusCode = http.StatusForbidden + log.Logf("[http] %s - %s bypass %s", + conn.RemoteAddr(), conn.LocalAddr(), host) if Debug { dump, _ := httputil.DumpResponse(resp, false) - log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(dump)) + log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(dump)) } resp.Write(conn) return } - u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization")) - if Debug && (u != "" || p != "") { - log.Logf("[http] %s - %s : Authorization: '%s' '%s'", conn.RemoteAddr(), req.Host, u, p) - } - if !authenticate(u, p, h.options.Users...) { - // probing resistance is enabled - if ss := strings.SplitN(h.options.ProbeResist, ":", 2); len(ss) == 2 { - switch ss[0] { - case "code": - resp.StatusCode, _ = strconv.Atoi(ss[1]) - case "web": - url := ss[1] - if !strings.HasPrefix(url, "http") { - url = "http://" + url - } - if r, err := http.Get(url); err == nil { - resp = r - } - case "host": - cc, err := net.Dial("tcp", ss[1]) - if err == nil { - defer cc.Close() - - req.Write(cc) - log.Logf("[http] %s <-> %s : forward to %s", conn.LocalAddr(), req.Host, ss[1]) - transport(conn, cc) - log.Logf("[http] %s >-< %s : forward to %s", conn.LocalAddr(), req.Host, ss[1]) - return - } - case "file": - f, _ := os.Open(ss[1]) - if f != nil { - resp.StatusCode = http.StatusOK - if finfo, _ := f.Stat(); finfo != nil { - resp.ContentLength = finfo.Size() - } - resp.Body = f - } - } - } - - if resp.StatusCode == 0 { - log.Logf("[http] %s <- %s : proxy authentication required", conn.RemoteAddr(), req.Host) - resp.StatusCode = http.StatusProxyAuthRequired - resp.Header.Add("Proxy-Authenticate", "Basic realm=\"gost\"") - } else { - resp.Header = http.Header{} - resp.Header.Set("Server", "nginx/1.14.1") - resp.Header.Set("Date", time.Now().Format(http.TimeFormat)) - if resp.ContentLength > 0 { - resp.Header.Set("Content-Type", "text/html") - } - if resp.StatusCode == http.StatusOK { - resp.Header.Set("Connection", "keep-alive") - } - } - - if Debug { - dump, _ := httputil.DumpResponse(resp, false) - log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(dump)) - } - - resp.Write(conn) + if !h.authenticate(conn, req, resp) { return } @@ -225,7 +181,8 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) { if Debug { dump, _ := httputil.DumpResponse(resp, false) - log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(dump)) + log.Logf("[http] %s <- %s\n%s", + conn.RemoteAddr(), conn.LocalAddr(), string(dump)) } resp.Write(conn) @@ -234,11 +191,6 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) { req.Header.Del("Proxy-Authorization") - host := req.Host - if _, port, _ := net.SplitHostPort(host); port == "" { - host = net.JoinHostPort(req.Host, "80") - } - retries := 1 if h.options.Chain != nil && h.options.Chain.Retries > 0 { retries = h.options.Chain.Retries @@ -251,11 +203,22 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) { var cc net.Conn var route *Chain for i := 0; i < retries; i++ { - route, err = h.options.Chain.selectRouteFor(req.Host) + route, err = h.options.Chain.selectRouteFor(host) if err != nil { - log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err) + log.Logf("[http] %s -> %s : %s", + conn.RemoteAddr(), conn.LocalAddr(), err) continue } + + buf := bytes.Buffer{} + fmt.Fprintf(&buf, "%s -> %s -> ", + conn.RemoteAddr(), h.options.Node.String()) + for _, nd := range route.route { + fmt.Fprintf(&buf, "%d@%s -> ", nd.ID, nd.String()) + } + fmt.Fprintf(&buf, "%s", host) + log.Log("[route]", buf.String()) + // forward http request lastNode := route.LastNode() if req.Method != http.MethodConnect && lastNode.Protocol == "http" { @@ -263,12 +226,11 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) { if err == nil { return } - // log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err) + log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) continue } cc, err = route.Dial(host, - RetryChainOption(1), TimeoutChainOption(h.options.Timeout), HostsChainOption(h.options.Hosts), ResolverChainOption(h.options.Resolver), @@ -276,15 +238,15 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) { if err == nil { break } + log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) } if err != nil { - log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), host, err) resp.StatusCode = http.StatusServiceUnavailable if Debug { dump, _ := httputil.DumpResponse(resp, false) - log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), host, string(dump)) + log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(dump)) } resp.Write(conn) @@ -296,26 +258,94 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) { b := []byte("HTTP/1.1 200 Connection established\r\n" + "Proxy-Agent: gost/" + Version + "\r\n\r\n") if Debug { - log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), host, string(b)) + log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(b)) } conn.Write(b) } else { req.Header.Del("Proxy-Connection") if err = req.Write(cc); err != nil { - log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), host, err) + log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) return } } - var su string - if u != "" { - su = u + "@" + log.Logf("[http] %s <-> %s", conn.RemoteAddr(), host) + transport(conn, cc) + log.Logf("[http] %s >-< %s", conn.RemoteAddr(), host) +} + +func (h *httpHandler) authenticate(conn net.Conn, req *http.Request, resp *http.Response) (ok bool) { + u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization")) + if Debug && (u != "" || p != "") { + log.Logf("[http] %s -> %s : Authorization '%s' '%s'", + conn.RemoteAddr(), conn.LocalAddr(), u, p) + } + if authenticate(u, p, h.options.Users...) { + return true } - log.Logf("[http] %s%s <-> %s", su, cc.LocalAddr(), host) - transport(conn, cc) - log.Logf("[http] %s%s >-< %s", su, cc.LocalAddr(), host) + // probing resistance is enabled + if ss := strings.SplitN(h.options.ProbeResist, ":", 2); len(ss) == 2 { + switch ss[0] { + case "code": + resp.StatusCode, _ = strconv.Atoi(ss[1]) + case "web": + url := ss[1] + if !strings.HasPrefix(url, "http") { + url = "http://" + url + } + if r, err := http.Get(url); err == nil { + resp = r + } + case "host": + cc, err := net.Dial("tcp", ss[1]) + if err == nil { + defer cc.Close() + + req.Write(cc) + log.Logf("[http] %s <-> %s : forward to %s", + conn.RemoteAddr(), conn.LocalAddr(), ss[1]) + transport(conn, cc) + log.Logf("[http] %s >-< %s : forward to %s", + conn.RemoteAddr(), conn.LocalAddr(), ss[1]) + return + } + case "file": + f, _ := os.Open(ss[1]) + if f != nil { + resp.StatusCode = http.StatusOK + if finfo, _ := f.Stat(); finfo != nil { + resp.ContentLength = finfo.Size() + } + resp.Header.Set("Content-Type", "text/html") + resp.Body = f + } + } + } + + if resp.StatusCode == 0 { + log.Logf("[http] %s <- %s : proxy authentication required", + conn.RemoteAddr(), conn.LocalAddr()) + resp.StatusCode = http.StatusProxyAuthRequired + resp.Header.Add("Proxy-Authenticate", "Basic realm=\"gost\"") + } else { + resp.Header = http.Header{} + resp.Header.Set("Server", "nginx/1.14.1") + resp.Header.Set("Date", time.Now().Format(http.TimeFormat)) + if resp.StatusCode == http.StatusOK { + resp.Header.Set("Connection", "keep-alive") + } + } + + if Debug { + dump, _ := httputil.DumpResponse(resp, false) + log.Logf("[http] %s <- %s\n%s", + conn.RemoteAddr(), conn.LocalAddr(), string(dump)) + } + + resp.Write(conn) + return } func (h *httpHandler) forwardRequest(conn net.Conn, req *http.Request, route *Chain) error { @@ -324,9 +354,7 @@ func (h *httpHandler) forwardRequest(conn net.Conn, req *http.Request, route *Ch } lastNode := route.LastNode() - cc, err := route.Conn( - RetryChainOption(1), // we control the retry manually. - ) + cc, err := route.Conn() if err != nil { return err } @@ -346,7 +374,7 @@ func (h *httpHandler) forwardRequest(conn net.Conn, req *http.Request, route *Ch req.URL.Scheme = "http" // make sure that the URL is absolute } if err = req.WriteProxy(cc); err != nil { - log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err) + log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) return nil } cc.SetWriteDeadline(time.Time{}) diff --git a/http2.go b/http2.go index a33b57c..0f5c88b 100644 --- a/http2.go +++ b/http2.go @@ -6,6 +6,7 @@ import ( "crypto/tls" "encoding/base64" "errors" + "fmt" "io" "io/ioutil" "net" @@ -50,6 +51,7 @@ func (c *http2Connector) Connect(conn net.Conn, addr string, options ...ConnectO Host: cc.addr, ContentLength: -1, } + // TODO: use the standard CONNECT method. req.Header.Set("Gost-Target", addr) if c.User != nil { u := c.User.Username() @@ -110,8 +112,20 @@ func (tr *http2Transporter) Dial(addr string, options ...DialOption) (net.Conn, } tr.clientMutex.Lock() + defer tr.clientMutex.Unlock() + client, ok := tr.clients[addr] if !ok { + // NOTE: due to the dummy connection, HTTP2 node in a proxy chain can not be marked as dead. + // There is no real connection to the HTTP2 server at this moment. + // So we should try to connect the server. + conn, err := opts.Chain.Dial(addr) + if err != nil { + + return nil, err + } + conn.Close() + transport := http2.Transport{ TLSClientConfig: tr.tlsConfig, DialTLS: func(network, adr string, cfg *tls.Config) (net.Conn, error) { @@ -128,7 +142,6 @@ func (tr *http2Transporter) Dial(addr string, options ...DialOption) (net.Conn, } tr.clients[addr] = client } - tr.clientMutex.Unlock() return &http2ClientConn{ addr: addr, @@ -283,31 +296,40 @@ func (h *http2Handler) Handle(conn net.Conn) { } func (h *http2Handler) roundTrip(w http.ResponseWriter, r *http.Request) { - target := r.Header.Get("Gost-Target") - if target == "" { - target = r.Host + host := r.Header.Get("Gost-Target") + if host == "" { + host = r.Host } - if !strings.Contains(target, ":") { - target += ":80" + if _, port, _ := net.SplitHostPort(host); port == "" { + host = net.JoinHostPort(host, "80") } + laddr := h.options.Addr + u, _, _ := basicProxyAuth(r.Header.Get("Proxy-Authorization")) + if u != "" { + u += "@" + } + log.Logf("[http2] %s%s -> %s -> %s", + u, r.RemoteAddr, h.options.Node.String(), host) + if Debug { - log.Logf("[http2] %s %s - %s %s", r.Method, r.RemoteAddr, target, r.Proto) dump, _ := httputil.DumpRequest(r, false) - log.Log("[http2]", string(dump)) + log.Logf("[http2] %s - %s\n%s", r.RemoteAddr, laddr, string(dump)) } w.Header().Set("Proxy-Agent", "gost/"+Version) - if !Can("tcp", target, h.options.Whitelist, h.options.Blacklist) { - log.Logf("[http2] Unauthorized to tcp connect to %s", target) + if !Can("tcp", host, h.options.Whitelist, h.options.Blacklist) { + log.Logf("[http2] %s - %s : Unauthorized to tcp connect to %s", + r.RemoteAddr, laddr, host) w.WriteHeader(http.StatusForbidden) return } - if h.options.Bypass.Contains(target) { - log.Logf("[http2] [bypass] %s", target) + if h.options.Bypass.Contains(host) { + log.Logf("[http2] %s - %s bypass %s", + r.RemoteAddr, laddr, host) w.WriteHeader(http.StatusForbidden) return } @@ -319,86 +341,54 @@ func (h *http2Handler) roundTrip(w http.ResponseWriter, r *http.Request) { Body: ioutil.NopCloser(bytes.NewReader([]byte{})), } - u, p, _ := basicProxyAuth(r.Header.Get("Proxy-Authorization")) - if Debug && (u != "" || p != "") { - log.Logf("[http2] %s - %s : Authorization: '%s' '%s'", r.RemoteAddr, target, u, p) - } - if !authenticate(u, p, h.options.Users...) { - // probing resistance is enabled - if ss := strings.SplitN(h.options.ProbeResist, ":", 2); len(ss) == 2 { - switch ss[0] { - case "code": - resp.StatusCode, _ = strconv.Atoi(ss[1]) - case "web": - url := ss[1] - if !strings.HasPrefix(url, "http") { - url = "http://" + url - } - if r, err := http.Get(url); err == nil { - resp = r - } - case "host": - cc, err := net.Dial("tcp", ss[1]) - if err == nil { - defer cc.Close() - log.Logf("[http2] %s <-> %s : forward to %s", r.RemoteAddr, target, ss[1]) - if err := h.forwardRequest(w, r, cc); err != nil { - log.Logf("[http2] %s - %s : %s", r.RemoteAddr, target, err) - } - log.Logf("[http2] %s >-< %s : forward to %s", r.RemoteAddr, target, ss[1]) - return - } - case "file": - f, _ := os.Open(ss[1]) - if f != nil { - resp.StatusCode = http.StatusOK - if finfo, _ := f.Stat(); finfo != nil { - resp.ContentLength = finfo.Size() - } - resp.Body = f - } - } - } - - if resp.StatusCode == 0 { - log.Logf("[http2] %s <- %s : proxy authentication required", r.RemoteAddr, target) - resp.StatusCode = http.StatusProxyAuthRequired - resp.Header.Add("Proxy-Authenticate", "Basic realm=\"gost\"") - } else { - w.Header().Del("Proxy-Agent") - resp.Header = http.Header{} - resp.Header.Set("Server", "nginx/1.14.1") - resp.Header.Set("Date", time.Now().Format(http.TimeFormat)) - if resp.ContentLength > 0 { - resp.Header.Set("Content-Type", "text/html") - } - if resp.StatusCode == http.StatusOK { - resp.Header.Set("Connection", "keep-alive") - } - } - - if Debug { - dump, _ := httputil.DumpResponse(resp, false) - log.Logf("[http2] %s <- %s\n%s", r.RemoteAddr, target, string(dump)) - } - - h.writeResponse(w, resp) - resp.Body.Close() - + if !h.authenticate(w, r, resp) { return } + // delete the proxy related headers. r.Header.Del("Proxy-Authorization") r.Header.Del("Proxy-Connection") - cc, err := h.options.Chain.Dial(target, - RetryChainOption(h.options.Retries), - TimeoutChainOption(h.options.Timeout), - HostsChainOption(h.options.Hosts), - ResolverChainOption(h.options.Resolver), - ) + retries := 1 + if h.options.Chain != nil && h.options.Chain.Retries > 0 { + retries = h.options.Chain.Retries + } + if h.options.Retries > 0 { + retries = h.options.Retries + } + + var err error + var cc net.Conn + var route *Chain + for i := 0; i < retries; i++ { + route, err = h.options.Chain.selectRouteFor(host) + if err != nil { + log.Logf("[http2] %s -> %s : %s", + r.RemoteAddr, laddr, err) + continue + } + + buf := bytes.Buffer{} + fmt.Fprintf(&buf, "%s -> %s -> ", + r.RemoteAddr, h.options.Node.String()) + for _, nd := range route.route { + fmt.Fprintf(&buf, "%d@%s -> ", nd.ID, nd.String()) + } + fmt.Fprintf(&buf, "%s", host) + log.Log("[route]", buf.String()) + + cc, err = route.Dial(host, + TimeoutChainOption(h.options.Timeout), + HostsChainOption(h.options.Hosts), + ResolverChainOption(h.options.Resolver), + ) + if err == nil { + break + } + log.Logf("[http2] %s -> %s : %s", r.RemoteAddr, laddr, err) + } + if err != nil { - log.Logf("[http2] %s -> %s : %s", r.RemoteAddr, target, err) w.WriteHeader(http.StatusServiceUnavailable) return } @@ -415,29 +405,103 @@ func (h *http2Handler) roundTrip(w http.ResponseWriter, r *http.Request) { // we take over the underly connection conn, _, err := hj.Hijack() if err != nil { - log.Logf("[http2] %s -> %s : %s", r.RemoteAddr, target, err) + log.Logf("[http2] %s -> %s : %s", + r.RemoteAddr, laddr, err) w.WriteHeader(http.StatusInternalServerError) return } defer conn.Close() - log.Logf("[http2] %s <-> %s : downgrade to HTTP/1.1", r.RemoteAddr, target) + log.Logf("[http2] %s <-> %s : downgrade to HTTP/1.1", r.RemoteAddr, host) transport(conn, cc) - log.Logf("[http2] %s >-< %s", r.RemoteAddr, target) + log.Logf("[http2] %s >-< %s", r.RemoteAddr, host) return } - log.Logf("[http2] %s <-> %s", r.RemoteAddr, target) + log.Logf("[http2] %s <-> %s", r.RemoteAddr, host) transport(&readWriter{r: r.Body, w: flushWriter{w}}, cc) - log.Logf("[http2] %s >-< %s", r.RemoteAddr, target) + log.Logf("[http2] %s >-< %s", r.RemoteAddr, host) return } - log.Logf("[http2] %s <-> %s", r.RemoteAddr, target) + log.Logf("[http2] %s <-> %s", r.RemoteAddr, host) if err := h.forwardRequest(w, r, cc); err != nil { - log.Logf("[http2] %s - %s : %s", r.RemoteAddr, target, err) + log.Logf("[http2] %s - %s : %s", r.RemoteAddr, host, err) } - log.Logf("[http2] %s >-< %s", r.RemoteAddr, target) + log.Logf("[http2] %s >-< %s", r.RemoteAddr, host) +} + +func (h *http2Handler) authenticate(w http.ResponseWriter, r *http.Request, resp *http.Response) (ok bool) { + laddr := h.options.Addr + u, p, _ := basicProxyAuth(r.Header.Get("Proxy-Authorization")) + if Debug && (u != "" || p != "") { + log.Logf("[http2] %s - %s : Authorization '%s' '%s'", r.RemoteAddr, laddr, u, p) + } + if authenticate(u, p, h.options.Users...) { + return true + } + // probing resistance is enabled + if ss := strings.SplitN(h.options.ProbeResist, ":", 2); len(ss) == 2 { + switch ss[0] { + case "code": + resp.StatusCode, _ = strconv.Atoi(ss[1]) + case "web": + url := ss[1] + if !strings.HasPrefix(url, "http") { + url = "http://" + url + } + if r, err := http.Get(url); err == nil { + resp = r + } + case "host": + cc, err := net.Dial("tcp", ss[1]) + if err == nil { + defer cc.Close() + log.Logf("[http2] %s <-> %s : forward to %s", r.RemoteAddr, laddr, ss[1]) + if err := h.forwardRequest(w, r, cc); err != nil { + log.Logf("[http2] %s - %s : %s", r.RemoteAddr, laddr, err) + } + log.Logf("[http2] %s >-< %s : forward to %s", r.RemoteAddr, laddr, ss[1]) + return + } + case "file": + f, _ := os.Open(ss[1]) + if f != nil { + resp.StatusCode = http.StatusOK + if finfo, _ := f.Stat(); finfo != nil { + resp.ContentLength = finfo.Size() + } + resp.Body = f + } + } + } + + if resp.StatusCode == 0 { + log.Logf("[http2] %s <- %s : proxy authentication required", r.RemoteAddr, laddr) + resp.StatusCode = http.StatusProxyAuthRequired + resp.Header.Add("Proxy-Authenticate", "Basic realm=\"gost\"") + } else { + w.Header().Del("Proxy-Agent") + resp.Header = http.Header{} + resp.Header.Set("Server", "nginx/1.14.1") + resp.Header.Set("Date", time.Now().Format(http.TimeFormat)) + if resp.ContentLength > 0 { + resp.Header.Set("Content-Type", "text/html") + } + if resp.StatusCode == http.StatusOK { + resp.Header.Set("Connection", "keep-alive") + } + } + + if Debug { + dump, _ := httputil.DumpResponse(resp, false) + log.Logf("[http2] %s <- %s\n%s", r.RemoteAddr, laddr, string(dump)) + } + + h.writeResponse(w, resp) + resp.Body.Close() + + return } func (h *http2Handler) forwardRequest(w http.ResponseWriter, r *http.Request, rw io.ReadWriter) (err error) { diff --git a/node.go b/node.go index 6eb47b6..b412623 100644 --- a/node.go +++ b/node.go @@ -20,8 +20,8 @@ type Node struct { Host string Protocol string Transport string - Remote string // remote address, used by tcp/udp port forwarding - url string // raw url + Remote string // remote address, used by tcp/udp port forwarding + url *url.URL // raw url User *url.Userinfo Values url.Values DialOptions []DialOption @@ -55,10 +55,11 @@ func ParseNode(s string) (node Node, err error) { Values: u.Query(), User: u.User, marker: &failMarker{}, + url: u, } u.RawQuery = "" - node.url = u.String() + u.User = nil schemes := strings.Split(u.Scheme, "+") if len(schemes) == 1 { @@ -139,7 +140,7 @@ func (node *Node) GetInt(key string) int { } func (node Node) String() string { - return node.url + return node.url.String() } // NodeGroup is a group of nodes. diff --git a/selector.go b/selector.go index d4dcc7e..8913ac0 100644 --- a/selector.go +++ b/selector.go @@ -173,7 +173,7 @@ func (f *FailFilter) Filter(nodes []Node) []Node { nl := []Node{} for i := range nodes { marker := nodes[i].marker.Clone() - // log.Logf("%s: %d/%d %d/%d", nodes[i], marker.failCount, f.MaxFails, marker.failTime, f.FailTimeout) + // log.Logf("%s: %d/%d %v/%v", nodes[i], marker.failCount, f.MaxFails, marker.failTime, f.FailTimeout) if marker.failCount < uint32(f.MaxFails) || time.Since(time.Unix(marker.failTime, 0)) >= f.FailTimeout { nl = append(nl, nodes[i]) diff --git a/sni.go b/sni.go index ac7062a..5e46abd 100644 --- a/sni.go +++ b/sni.go @@ -8,6 +8,7 @@ import ( "encoding/base64" "encoding/binary" "errors" + "fmt" "hash/crc32" "io" "net" @@ -55,22 +56,23 @@ func (h *sniHandler) Init(options ...HandlerOption) { } func (h *sniHandler) Handle(conn net.Conn) { - br := bufio.NewReader(conn) + defer conn.Close() + br := bufio.NewReader(conn) hdr, err := br.Peek(dissector.RecordHeaderLen) if err != nil { - log.Log("[sni]", err) - conn.Close() + log.Logf("[sni] %s -> %s : %s", + conn.RemoteAddr(), conn.LocalAddr(), err) return } conn = &bufferdConn{br: br, Conn: conn} - defer conn.Close() if hdr[0] != dissector.Handshake { // We assume it is an HTTP request req, err := http.ReadRequest(bufio.NewReader(conn)) if err != nil { - log.Logf("[sni] %s - %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) + log.Logf("[sni] %s -> %s : %s", + conn.RemoteAddr(), conn.LocalAddr(), err) return } if !req.URL.IsAbs() { @@ -84,40 +86,79 @@ func (h *sniHandler) Handle(conn net.Conn) { b, host, err := readClientHelloRecord(conn, "", false) if err != nil { - log.Log("[sni]", err) + log.Logf("[sni] %s -> %s : %s", + conn.RemoteAddr(), conn.LocalAddr(), err) return } - addr := net.JoinHostPort(host, "443") + host = net.JoinHostPort(host, "443") - if !Can("tcp", addr, h.options.Whitelist, h.options.Blacklist) { - log.Logf("[sni] Unauthorized to tcp connect to %s", addr) + log.Logf("[ss] %s -> %s -> %s", + conn.RemoteAddr(), h.options.Node.String(), host) + + if !Can("tcp", host, h.options.Whitelist, h.options.Blacklist) { + log.Logf("[sni] %s -> %s : Unauthorized to tcp connect to %s", + conn.RemoteAddr(), conn.LocalAddr(), host) return } - if h.options.Bypass.Contains(addr) { - log.Log("[sni] [bypass]", addr) + if h.options.Bypass.Contains(host) { + log.Log("[sni] %s - %s bypass %s", + conn.RemoteAddr(), conn.LocalAddr(), host) return } - cc, err := h.options.Chain.Dial(addr, - RetryChainOption(h.options.Retries), - TimeoutChainOption(h.options.Timeout), - HostsChainOption(h.options.Hosts), - ResolverChainOption(h.options.Resolver), - ) + retries := 1 + if h.options.Chain != nil && h.options.Chain.Retries > 0 { + retries = h.options.Chain.Retries + } + if h.options.Retries > 0 { + retries = h.options.Retries + } + + var cc net.Conn + var route *Chain + for i := 0; i < retries; i++ { + route, err = h.options.Chain.selectRouteFor(host) + if err != nil { + log.Logf("[sni] %s -> %s : %s", + conn.RemoteAddr(), conn.LocalAddr(), err) + continue + } + + buf := bytes.Buffer{} + fmt.Fprintf(&buf, "%s -> %s -> ", + conn.RemoteAddr(), h.options.Node.String()) + for _, nd := range route.route { + fmt.Fprintf(&buf, "%d@%s -> ", nd.ID, nd.String()) + } + fmt.Fprintf(&buf, "%s", host) + log.Log("[route]", buf.String()) + + cc, err = route.Dial(host, + TimeoutChainOption(h.options.Timeout), + HostsChainOption(h.options.Hosts), + ResolverChainOption(h.options.Resolver), + ) + if err == nil { + break + } + log.Logf("[sni] %s -> %s : %s", + conn.RemoteAddr(), conn.LocalAddr(), err) + } + if err != nil { - log.Logf("[sni] %s -> %s : %s", conn.RemoteAddr(), addr, err) return } defer cc.Close() if _, err := cc.Write(b); err != nil { - log.Logf("[sni] %s -> %s : %s", conn.RemoteAddr(), addr, err) + log.Logf("[sni] %s -> %s : %s", + conn.RemoteAddr(), conn.LocalAddr(), err) } - log.Logf("[sni] %s <-> %s", cc.LocalAddr(), addr) + log.Logf("[sni] %s <-> %s", cc.LocalAddr(), host) transport(conn, cc) - log.Logf("[sni] %s >-< %s", cc.LocalAddr(), addr) + log.Logf("[sni] %s >-< %s", cc.LocalAddr(), host) } // sniSniffConn is a net.Conn that reads from r, fails on Writes, diff --git a/socks.go b/socks.go index 4d77608..c72f46c 100644 --- a/socks.go +++ b/socks.go @@ -386,12 +386,14 @@ func (h *socks5Handler) Handle(conn net.Conn) { conn = gosocks5.ServerConn(conn, h.selector) req, err := gosocks5.ReadRequest(conn) if err != nil { - log.Log("[socks5]", err) + log.Logf("[socks5] %s -> %s : %s", + conn.RemoteAddr(), conn.LocalAddr(), err) return } if Debug { - log.Logf("[socks5] %s - %s\n%s", conn.RemoteAddr(), req.Addr, req) + log.Logf("[socks5] %s -> %s\n%s", + conn.RemoteAddr(), conn.LocalAddr(), req) } switch req.Cmd { case gosocks5.CmdConnect: @@ -410,43 +412,86 @@ func (h *socks5Handler) Handle(conn net.Conn) { h.handleUDPTunnel(conn, req) default: - log.Log("[socks5] Unrecognized request:", req.Cmd) + log.Logf("[socks5] %s - %s : Unrecognized request: %d", + conn.RemoteAddr(), conn.LocalAddr(), req.Cmd) } } func (h *socks5Handler) handleConnect(conn net.Conn, req *gosocks5.Request) { - addr := req.Addr.String() - if !Can("tcp", addr, h.options.Whitelist, h.options.Blacklist) { - log.Logf("[socks5-connect] Unauthorized to tcp connect to %s", addr) + host := req.Addr.String() + + log.Logf("[socks5] %s -> %s -> %s", + conn.RemoteAddr(), h.options.Node.String(), host) + + if !Can("tcp", host, h.options.Whitelist, h.options.Blacklist) { + log.Logf("[socks5] %s - %s : Unauthorized to tcp connect to %s", + conn.RemoteAddr(), conn.LocalAddr(), host) rep := gosocks5.NewReply(gosocks5.NotAllowed, nil) rep.Write(conn) if Debug { - log.Logf("[socks5-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep) + log.Logf("[socks5] %s <- %s\n%s", + conn.RemoteAddr(), conn.LocalAddr(), rep) } return } - if h.options.Bypass.Contains(addr) { - log.Logf("[socks5-connect] [bypass] %s", addr) + if h.options.Bypass.Contains(host) { + log.Logf("[socks5] %s - %s : Bypass %s", + conn.RemoteAddr(), conn.LocalAddr(), host) rep := gosocks5.NewReply(gosocks5.NotAllowed, nil) rep.Write(conn) if Debug { - log.Logf("[socks5-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep) + log.Logf("[socks5] %s <- %s\n%s", + conn.RemoteAddr(), conn.LocalAddr(), rep) } return } - cc, err := h.options.Chain.Dial(addr, - RetryChainOption(h.options.Retries), - TimeoutChainOption(h.options.Timeout), - HostsChainOption(h.options.Hosts), - ResolverChainOption(h.options.Resolver), - ) + retries := 1 + if h.options.Chain != nil && h.options.Chain.Retries > 0 { + retries = h.options.Chain.Retries + } + if h.options.Retries > 0 { + retries = h.options.Retries + } + + var err error + var cc net.Conn + var route *Chain + for i := 0; i < retries; i++ { + route, err = h.options.Chain.selectRouteFor(host) + if err != nil { + log.Logf("[socks5] %s -> %s : %s", + conn.RemoteAddr(), conn.LocalAddr(), err) + continue + } + + buf := bytes.Buffer{} + fmt.Fprintf(&buf, "%s -> %s -> ", + conn.RemoteAddr(), h.options.Node.String()) + for _, nd := range route.route { + fmt.Fprintf(&buf, "%d@%s -> ", nd.ID, nd.String()) + } + fmt.Fprintf(&buf, "%s", host) + log.Log("[route]", buf.String()) + + cc, err = route.Dial(host, + TimeoutChainOption(h.options.Timeout), + HostsChainOption(h.options.Hosts), + ResolverChainOption(h.options.Resolver), + ) + if err == nil { + break + } + log.Logf("[socks5] %s -> %s : %s", + conn.RemoteAddr(), conn.LocalAddr(), err) + } + if err != nil { - log.Logf("[socks5-connect] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) rep := gosocks5.NewReply(gosocks5.HostUnreachable, nil) rep.Write(conn) if Debug { - log.Logf("[socks5-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep) + log.Logf("[socks5] %s <- %s\n%s", + conn.RemoteAddr(), conn.LocalAddr(), rep) } return } @@ -454,22 +499,29 @@ func (h *socks5Handler) handleConnect(conn net.Conn, req *gosocks5.Request) { rep := gosocks5.NewReply(gosocks5.Succeeded, nil) if err := rep.Write(conn); err != nil { - log.Logf("[socks5-connect] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err) + log.Logf("[socks5] %s <- %s : %s", + conn.RemoteAddr(), conn.LocalAddr(), err) return } if Debug { - log.Logf("[socks5-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep) + log.Logf("[socks5] %s <- %s\n%s", + conn.RemoteAddr(), conn.LocalAddr(), rep) } - log.Logf("[socks5-connect] %s <-> %s", conn.RemoteAddr(), req.Addr) + log.Logf("[socks5] %s <-> %s", conn.RemoteAddr(), host) transport(conn, cc) - log.Logf("[socks5-connect] %s >-< %s", conn.RemoteAddr(), req.Addr) + log.Logf("[socks5] %s >-< %s", conn.RemoteAddr(), host) } func (h *socks5Handler) handleBind(conn net.Conn, req *gosocks5.Request) { + addr := req.Addr.String() + + log.Logf("[socks5-bind] %s -> %s -> %s", + conn.RemoteAddr(), h.options.Node.String(), addr) + if h.options.Chain.IsEmpty() { - addr := req.Addr.String() if !Can("rtcp", addr, h.options.Whitelist, h.options.Blacklist) { - log.Logf("Unauthorized to tcp bind to %s", addr) + log.Logf("[socks5-bind] %s - %s : Unauthorized to tcp bind to %s", + conn.RemoteAddr(), conn.LocalAddr(), addr) return } h.bindOn(conn, addr) @@ -478,11 +530,13 @@ func (h *socks5Handler) handleBind(conn net.Conn, req *gosocks5.Request) { cc, err := h.options.Chain.Conn() if err != nil { - log.Logf("[socks5-bind] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err) + log.Logf("[socks5-bind] %s <- %s : %s", + conn.RemoteAddr(), conn.LocalAddr(), err) reply := gosocks5.NewReply(gosocks5.Failure, nil) reply.Write(conn) if Debug { - log.Logf("[socks5-bind] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, reply) + log.Logf("[socks5-bind] %s <- %s\n%s", + conn.RemoteAddr(), conn.LocalAddr(), reply) } return } @@ -492,16 +546,17 @@ func (h *socks5Handler) handleBind(conn net.Conn, req *gosocks5.Request) { // so we don't need to authenticate it, as it's as explicit as whitelisting defer cc.Close() req.Write(cc) - log.Logf("[socks5-bind] %s <-> %s", conn.RemoteAddr(), cc.RemoteAddr()) + log.Logf("[socks5-bind] %s <-> %s", conn.RemoteAddr(), addr) transport(conn, cc) - log.Logf("[socks5-bind] %s >-< %s", conn.RemoteAddr(), cc.RemoteAddr()) + log.Logf("[socks5-bind] %s >-< %s", conn.RemoteAddr(), addr) } func (h *socks5Handler) bindOn(conn net.Conn, addr string) { bindAddr, _ := net.ResolveTCPAddr("tcp", addr) ln, err := net.ListenTCP("tcp", bindAddr) // strict mode: if the port already in use, it will return error if err != nil { - log.Logf("[socks5-bind] %s -> %s : %s", conn.RemoteAddr(), addr, err) + log.Logf("[socks5-bind] %s -> %s : %s", + conn.RemoteAddr(), conn.LocalAddr(), err) gosocks5.NewReply(gosocks5.Failure, nil).Write(conn) return } @@ -511,14 +566,17 @@ func (h *socks5Handler) bindOn(conn net.Conn, addr string) { socksAddr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) reply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr) if err := reply.Write(conn); err != nil { - log.Logf("[socks5-bind] %s <- %s : %s", conn.RemoteAddr(), addr, err) + log.Logf("[socks5-bind] %s <- %s : %s", + conn.RemoteAddr(), conn.LocalAddr(), err) ln.Close() return } if Debug { - log.Logf("[socks5-bind] %s <- %s\n%s", conn.RemoteAddr(), addr, reply) + log.Logf("[socks5-bind] %s <- %s\n%s", + conn.RemoteAddr(), conn.LocalAddr(), reply) } - log.Logf("[socks5-bind] %s - %s BIND ON %s OK", conn.RemoteAddr(), addr, socksAddr) + log.Logf("[socks5-bind] %s - %s BIND ON %s OK", + conn.RemoteAddr(), conn.LocalAddr(), socksAddr) var pconn net.Conn accept := func() <-chan error { @@ -1142,17 +1200,18 @@ func (h *socks4Handler) Handle(conn net.Conn) { req, err := gosocks4.ReadRequest(conn) if err != nil { - log.Log("[socks4]", err) + log.Logf("[socks4] %s -> %s : %s", + conn.RemoteAddr(), conn.LocalAddr(), err) return } if Debug { - log.Logf("[socks4] %s -> %s\n%s", conn.RemoteAddr(), req.Addr, req) + log.Logf("[socks4] %s -> %s\n%s", + conn.RemoteAddr(), conn.LocalAddr(), req) } switch req.Cmd { case gosocks4.CmdConnect: - log.Logf("[socks4-connect] %s -> %s", conn.RemoteAddr(), req.Addr) h.handleConnect(conn, req) case gosocks4.CmdBind: @@ -1160,42 +1219,86 @@ func (h *socks4Handler) Handle(conn net.Conn) { h.handleBind(conn, req) default: - log.Logf("[socks4] Unrecognized request: %d", req.Cmd) + log.Logf("[socks4] %s - %s : Unrecognized request: %d", + conn.RemoteAddr(), conn.LocalAddr(), req.Cmd) } } func (h *socks4Handler) handleConnect(conn net.Conn, req *gosocks4.Request) { addr := req.Addr.String() + log.Logf("[socks4] %s -> %s -> %s", + conn.RemoteAddr(), h.options.Node.String(), addr) + if !Can("tcp", addr, h.options.Whitelist, h.options.Blacklist) { - log.Logf("[socks4-connect] Unauthorized to tcp connect to %s", addr) + log.Logf("[socks4] %s - %s : Unauthorized to tcp connect to %s", + conn.RemoteAddr(), conn.LocalAddr(), addr) rep := gosocks4.NewReply(gosocks4.Rejected, nil) rep.Write(conn) if Debug { - log.Logf("[socks4-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep) + log.Logf("[socks4] %s <- %s\n%s", + conn.RemoteAddr(), conn.LocalAddr(), rep) } return } if h.options.Bypass.Contains(addr) { - log.Log("[socks4-connect] [bypass]", addr) + log.Log("[socks4] %s - %s : Bypass %s", + conn.RemoteAddr(), conn.LocalAddr(), addr) rep := gosocks4.NewReply(gosocks4.Rejected, nil) rep.Write(conn) if Debug { - log.Logf("[socks4-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep) + log.Logf("[socks4] %s <- %s\n%s", + conn.RemoteAddr(), conn.LocalAddr(), rep) } return } - cc, err := h.options.Chain.Dial(addr, - RetryChainOption(h.options.Retries), - TimeoutChainOption(h.options.Timeout), - ) + retries := 1 + if h.options.Chain != nil && h.options.Chain.Retries > 0 { + retries = h.options.Chain.Retries + } + if h.options.Retries > 0 { + retries = h.options.Retries + } + + var err error + var cc net.Conn + var route *Chain + for i := 0; i < retries; i++ { + route, err = h.options.Chain.selectRouteFor(addr) + if err != nil { + log.Logf("[socks4] %s -> %s : %s", + conn.RemoteAddr(), conn.LocalAddr(), err) + continue + } + + buf := bytes.Buffer{} + fmt.Fprintf(&buf, "%s -> %s -> ", + conn.RemoteAddr(), h.options.Node.String()) + for _, nd := range route.route { + fmt.Fprintf(&buf, "%d@%s -> ", nd.ID, nd.String()) + } + fmt.Fprintf(&buf, "%s", addr) + log.Log("[route]", buf.String()) + + cc, err = route.Dial(addr, + TimeoutChainOption(h.options.Timeout), + HostsChainOption(h.options.Hosts), + ResolverChainOption(h.options.Resolver), + ) + if err == nil { + break + } + log.Logf("[socks4] %s -> %s : %s", + conn.RemoteAddr(), conn.LocalAddr(), err) + } + if err != nil { - log.Logf("[socks4-connect] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) rep := gosocks4.NewReply(gosocks4.Failed, nil) rep.Write(conn) if Debug { - log.Logf("[socks4-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep) + log.Logf("[socks4] %s <- %s\n%s", + conn.RemoteAddr(), conn.LocalAddr(), rep) } return } @@ -1203,16 +1306,18 @@ func (h *socks4Handler) handleConnect(conn net.Conn, req *gosocks4.Request) { rep := gosocks4.NewReply(gosocks4.Granted, nil) if err := rep.Write(conn); err != nil { - log.Logf("[socks4-connect] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err) + log.Logf("[socks4] %s <- %s : %s", + conn.RemoteAddr(), conn.LocalAddr(), err) return } if Debug { - log.Logf("[socks4-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep) + log.Logf("[socks4] %s <- %s\n%s", + conn.RemoteAddr(), conn.LocalAddr(), rep) } - log.Logf("[socks4-connect] %s <-> %s", conn.RemoteAddr(), req.Addr) + log.Logf("[socks4] %s <-> %s", conn.RemoteAddr(), addr) transport(conn, cc) - log.Logf("[socks4-connect] %s >-< %s", conn.RemoteAddr(), req.Addr) + log.Logf("[socks4] %s >-< %s", conn.RemoteAddr(), addr) } func (h *socks4Handler) handleBind(conn net.Conn, req *gosocks4.Request) { diff --git a/ss.go b/ss.go index 5138881..b6d8984 100644 --- a/ss.go +++ b/ss.go @@ -125,49 +125,84 @@ func (h *shadowHandler) Handle(conn net.Conn) { } cipher, err := ss.NewCipher(method, password) if err != nil { - log.Log("[ss]", err) + log.Logf("[ss] %s -> %s : %s", + conn.RemoteAddr(), conn.LocalAddr(), err) return } conn = &shadowConn{conn: ss.NewConn(conn, cipher)} - log.Logf("[ss] %s - %s", conn.RemoteAddr(), conn.LocalAddr()) - conn.SetReadDeadline(time.Now().Add(ReadTimeout)) - addr, err := h.getRequest(conn) + host, err := h.getRequest(conn) if err != nil { - log.Logf("[ss] %s - %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) + log.Logf("[ss] %s -> %s : %s", + conn.RemoteAddr(), conn.LocalAddr(), err) return } // clear timer conn.SetReadDeadline(time.Time{}) - log.Logf("[ss] %s -> %s", conn.RemoteAddr(), addr) + log.Logf("[ss] %s -> %s -> %s", + conn.RemoteAddr(), h.options.Node.String(), host) - if !Can("tcp", addr, h.options.Whitelist, h.options.Blacklist) { - log.Logf("[ss] Unauthorized to tcp connect to %s", addr) + if !Can("tcp", host, h.options.Whitelist, h.options.Blacklist) { + log.Logf("[ss] %s - %s : Unauthorized to tcp connect to %s", + conn.RemoteAddr(), conn.LocalAddr(), host) return } - if h.options.Bypass.Contains(addr) { - log.Logf("[ss] [bypass] %s", addr) + if h.options.Bypass.Contains(host) { + log.Logf("[ss] %s - %s : Bypass %s", + conn.RemoteAddr(), conn.LocalAddr(), host) return } - cc, err := h.options.Chain.Dial(addr, - RetryChainOption(h.options.Retries), - TimeoutChainOption(h.options.Timeout), - HostsChainOption(h.options.Hosts), - ResolverChainOption(h.options.Resolver), - ) + retries := 1 + if h.options.Chain != nil && h.options.Chain.Retries > 0 { + retries = h.options.Chain.Retries + } + if h.options.Retries > 0 { + retries = h.options.Retries + } + + var cc net.Conn + var route *Chain + for i := 0; i < retries; i++ { + route, err = h.options.Chain.selectRouteFor(host) + if err != nil { + log.Logf("[ss] %s -> %s : %s", + conn.RemoteAddr(), conn.LocalAddr(), err) + continue + } + + buf := bytes.Buffer{} + fmt.Fprintf(&buf, "%s -> %s -> ", + conn.RemoteAddr(), h.options.Node.String()) + for _, nd := range route.route { + fmt.Fprintf(&buf, "%d@%s -> ", nd.ID, nd.String()) + } + fmt.Fprintf(&buf, "%s", host) + log.Log("[route]", buf.String()) + + cc, err = route.Dial(host, + TimeoutChainOption(h.options.Timeout), + HostsChainOption(h.options.Hosts), + ResolverChainOption(h.options.Resolver), + ) + if err == nil { + break + } + log.Logf("[ss] %s -> %s : %s", + conn.RemoteAddr(), conn.LocalAddr(), err) + } + if err != nil { - log.Logf("[ss] %s -> %s : %s", conn.RemoteAddr(), addr, err) return } defer cc.Close() - log.Logf("[ss] %s <-> %s", conn.RemoteAddr(), addr) + log.Logf("[ss] %s <-> %s", conn.RemoteAddr(), host) transport(conn, cc) - log.Logf("[ss] %s >-< %s", conn.RemoteAddr(), addr) + log.Logf("[ss] %s >-< %s", conn.RemoteAddr(), host) } const ( diff --git a/ssh.go b/ssh.go index d649a7f..0c72848 100644 --- a/ssh.go +++ b/ssh.go @@ -444,15 +444,15 @@ func (h *sshForwardHandler) Init(options ...HandlerOption) { func (h *sshForwardHandler) Handle(conn net.Conn) { sshConn, chans, reqs, err := ssh.NewServerConn(conn, h.config) if err != nil { - log.Logf("[ssh-forward] %s -> %s : %s", conn.RemoteAddr(), h.options.Addr, err) + log.Logf("[ssh-forward] %s -> %s : %s", conn.RemoteAddr(), h.options.Node.Addr, err) conn.Close() return } defer sshConn.Close() - log.Logf("[ssh-forward] %s <-> %s", conn.RemoteAddr(), h.options.Addr) + log.Logf("[ssh-forward] %s <-> %s", conn.RemoteAddr(), h.options.Node.Addr) h.handleForward(sshConn, chans, reqs) - log.Logf("[ssh-forward] %s >-< %s", conn.RemoteAddr(), h.options.Addr) + log.Logf("[ssh-forward] %s >-< %s", conn.RemoteAddr(), h.options.Node.Addr) } func (h *sshForwardHandler) handleForward(conn ssh.Conn, chans <-chan ssh.NewChannel, reqs <-chan *ssh.Request) { @@ -506,7 +506,7 @@ func (h *sshForwardHandler) handleForward(conn ssh.Conn, chans <-chan ssh.NewCha func (h *sshForwardHandler) directPortForwardChannel(channel ssh.Channel, raddr string) { defer channel.Close() - log.Logf("[ssh-tcp] %s - %s", h.options.Addr, raddr) + log.Logf("[ssh-tcp] %s - %s", h.options.Node.Addr, raddr) if !Can("tcp", raddr, h.options.Whitelist, h.options.Blacklist) { log.Logf("[ssh-tcp] Unauthorized to tcp connect to %s", raddr) @@ -525,14 +525,14 @@ func (h *sshForwardHandler) directPortForwardChannel(channel ssh.Channel, raddr ResolverChainOption(h.options.Resolver), ) if err != nil { - log.Logf("[ssh-tcp] %s - %s : %s", h.options.Addr, raddr, err) + log.Logf("[ssh-tcp] %s - %s : %s", h.options.Node.Addr, raddr, err) return } defer conn.Close() - log.Logf("[ssh-tcp] %s <-> %s", h.options.Addr, raddr) + log.Logf("[ssh-tcp] %s <-> %s", h.options.Node.Addr, raddr) transport(conn, channel) - log.Logf("[ssh-tcp] %s >-< %s", h.options.Addr, raddr) + log.Logf("[ssh-tcp] %s >-< %s", h.options.Node.Addr, raddr) } // tcpipForward is structure for RFC 4254 7.1 "tcpip-forward" request