From 518ca462c6152a3faef5a1715cc8b84fa0ac1d9e Mon Sep 17 00:00:00 2001 From: "rui.zheng" Date: Wed, 5 Oct 2016 14:53:15 +0800 Subject: [PATCH] fix shadowsocks --- .gitignore | 2 + README.md | 3 +- chain.go | 49 ++- cmd/gost/conn.go | 645 ------------------------------ cmd/gost/http.go | 308 -------------- cmd/gost/main.go | 76 ---- cmd/gost/socks.go | 553 ------------------------- cmd/gost/ss.go | 123 ------ cmd/gost/tls.go | 74 ---- cmd/gost/udp.go | 170 -------- cmd/gost/util.go | 193 --------- cmd/gost/ws.go | 141 ------- conn.go | 20 +- cmd/gost/forward.go => forward.go | 350 ++++++++++++---- gost.go | 2 +- http.go | 22 +- node.go | 24 +- server.go | 28 +- socks.go | 1 + ss.go | 255 ++++++++---- 20 files changed, 553 insertions(+), 2486 deletions(-) delete mode 100644 cmd/gost/conn.go delete mode 100644 cmd/gost/http.go delete mode 100644 cmd/gost/main.go delete mode 100644 cmd/gost/socks.go delete mode 100644 cmd/gost/ss.go delete mode 100644 cmd/gost/tls.go delete mode 100644 cmd/gost/udp.go delete mode 100644 cmd/gost/util.go delete mode 100644 cmd/gost/ws.go rename cmd/gost/forward.go => forward.go (50%) diff --git a/.gitignore b/.gitignore index d701402..7840d7e 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ _testmain.go *.exe *.test + +*.bak diff --git a/README.md b/README.md index cf31a10..5a8bd2e 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,9 @@ gost - GO Simple Tunnel * 兼容标准http(s)/socks5代理协议 * socks5代理支持tls协商加密 * Tunnel UDP over TCP -* 兼容shadowsocks协议 +* 兼容shadowsocks协议,支持OTA(OTA功能需2.2及以上版本) * 支持端口转发(2.1及以上版本) +* 支持HTTP2.0(2.2及以上版本) 二进制文件下载:https://github.com/ginuerzh/gost/releases diff --git a/chain.go b/chain.go index 2b83c56..e12ba85 100644 --- a/chain.go +++ b/chain.go @@ -2,6 +2,7 @@ package gost import ( "crypto/tls" + "encoding/base64" "errors" "github.com/golang/glog" "golang.org/x/net/http2" @@ -114,7 +115,22 @@ func (c *ProxyChain) GetConn() (net.Conn, error) { if c.Http2Enabled() { nodes = nodes[c.http2NodeIndex+1:] if len(nodes) == 0 { - return c.getHttp2Conn() + header := make(http.Header) + header.Set("Proxy-Switch", "gost") // Flag header to indicate server to switch to HTTP2 transport mode + conn, err := c.getHttp2Conn(header) + if err != nil { + return nil, err + } + http2Node := c.nodes[c.http2NodeIndex] + if http2Node.Protocol == "" { + http2Node.Protocol = "socks5" // assume it as socks5 protocol + } + pc := NewProxyConn(conn, http2Node) + if err := pc.Handshake(); err != nil { + conn.Close() + return nil, err + } + return pc, nil } } return c.travelNodes(nodes...) @@ -183,17 +199,21 @@ func (c *ProxyChain) travelNodes(nodes ...ProxyNode) (conn *ProxyConn, err error } // Initialize an HTTP2 transport if HTTP2 is enabled. -func (c *ProxyChain) getHttp2Conn() (net.Conn, error) { +func (c *ProxyChain) getHttp2Conn(header http.Header) (net.Conn, error) { if !c.Http2Enabled() { return nil, errors.New("HTTP2 not enabled") } http2Node := c.nodes[c.http2NodeIndex] pr, pw := io.Pipe() + if header == nil { + header = make(http.Header) + } + req := http.Request{ Method: http.MethodConnect, URL: &url.URL{Scheme: "https", Host: http2Node.Addr}, - Header: make(http.Header), + Header: header, Proto: "HTTP/2.0", ProtoMajor: 2, ProtoMinor: 0, @@ -201,7 +221,6 @@ func (c *ProxyChain) getHttp2Conn() (net.Conn, error) { Host: http2Node.Addr, ContentLength: -1, } - req.Header.Set("Proxy-Switch", "gost") // Flag header to indicate server to switch to HTTP2 transport mode if glog.V(LDEBUG) { dump, _ := httputil.DumpRequest(&req, false) glog.Infoln(string(dump)) @@ -214,19 +233,23 @@ func (c *ProxyChain) getHttp2Conn() (net.Conn, error) { resp.Body.Close() return nil, errors.New(resp.Status) } - return &http2Conn{r: resp.Body, w: pw}, nil + conn := &http2Conn{r: resp.Body, w: pw} + conn.remoteAddr, _ = net.ResolveTCPAddr("tcp", http2Node.Addr) + return conn, nil } // Use HTTP2 as transport to connect target addr func (c *ProxyChain) http2Connect(addr string) (net.Conn, error) { - conn, err := c.getHttp2Conn() - if err != nil { - return nil, err + if !c.Http2Enabled() { + return nil, errors.New("HTTP2 not enabled") } - pc := NewProxyConn(conn, c.nodes[c.http2NodeIndex]) - if err = pc.Connect(addr); err != nil { - pc.Close() - return nil, err + http2Node := c.nodes[c.http2NodeIndex] + + header := make(http.Header) + header.Set("Gost-Target", addr) // Flag header to indicate the address that server connected to + if http2Node.User != nil { + header.Set("Proxy-Authorization", + "Basic "+base64.StdEncoding.EncodeToString([]byte(http2Node.User.String()))) } - return conn, nil + return c.getHttp2Conn(header) } diff --git a/cmd/gost/conn.go b/cmd/gost/conn.go deleted file mode 100644 index 94bd755..0000000 --- a/cmd/gost/conn.go +++ /dev/null @@ -1,645 +0,0 @@ -package main - -import ( - "bufio" - "bytes" - "crypto/tls" - "encoding/base64" - "errors" - "github.com/ginuerzh/gosocks5" - "github.com/golang/glog" - "github.com/shadowsocks/shadowsocks-go/shadowsocks" - "io" - "io/ioutil" - "net" - "net/http" - "net/http/httputil" - "net/url" - "strconv" - "strings" - "sync" - //"sync/atomic" - "golang.org/x/net/http2" - "time" -) - -var ( - connCounter int32 -) - -var ( - // tcp buffer pool - tcpPool = sync.Pool{ - New: func() interface{} { - return make([]byte, 32*1024) - }, - } - // udp buffer pool - udpPool = sync.Pool{ - New: func() interface{} { - return make([]byte, 32*1024) - }, - } -) - -func listenAndServe(arg Args) error { - var ln net.Listener - var err error - - switch arg.Transport { - case "ws": // websocket connection - return NewWs(arg).ListenAndServe() - case "wss": // websocket security connection - return NewWs(arg).listenAndServeTLS() - case "tls": // tls connection - ln, err = tls.Listen("tcp", arg.Addr, - &tls.Config{Certificates: []tls.Certificate{arg.Cert}}) - case "http2": // http2 connetction - return listenAndServeHttp2(arg, http.HandlerFunc(handlerHttp2Request)) - case "tcp": // Local TCP port forwarding - return listenAndServeTcpForward(arg) - case "udp": // Local UDP port forwarding - return listenAndServeUdpForward(arg) - case "rtcp": // Remote TCP port forwarding - return serveRTcpForward(arg) - case "rudp": // Remote UDP port forwarding - return serveRUdpForward(arg) - default: - ln, err = net.Listen("tcp", arg.Addr) - } - - if err != nil { - return err - } - - defer ln.Close() - - for { - conn, err := ln.Accept() - if err != nil { - glog.V(LWARNING).Infoln(err) - continue - } - - setKeepAlive(conn, keepAliveTime) - - go handleConn(conn, arg) - } -} - -func listenAndServeHttp2(arg Args, handler http.Handler) error { - srv := http.Server{ - Addr: arg.Addr, - Handler: handler, - TLSConfig: &tls.Config{ - Certificates: []tls.Certificate{arg.Cert}, - }, - } - http2.ConfigureServer(&srv, nil) - return srv.ListenAndServeTLS("", "") -} - -func listenAndServeTcpForward(arg Args) error { - raddr, err := net.ResolveTCPAddr("tcp", arg.Remote) - if err != nil { - return err - } - - ln, err := net.Listen("tcp", arg.Addr) - if err != nil { - return err - } - defer ln.Close() - - for { - conn, err := ln.Accept() - if err != nil { - glog.V(LWARNING).Infoln(err) - continue - } - setKeepAlive(conn, keepAliveTime) - - go handleTcpForward(conn, raddr) - } -} - -func listenAndServeUdpForward(arg Args) error { - laddr, err := net.ResolveUDPAddr("udp", arg.Addr) - if err != nil { - return err - } - - raddr, err := net.ResolveUDPAddr("udp", arg.Remote) - if err != nil { - return err - } - - conn, err := net.ListenUDP("udp", laddr) - if err != nil { - glog.V(LWARNING).Infof("[udp] %s -> %s : %s", laddr, raddr, err) - return err - } - defer conn.Close() - - if len(forwardArgs) == 0 { - for { - b := udpPool.Get().([]byte) - - n, addr, err := conn.ReadFromUDP(b) - if err != nil { - glog.V(LWARNING).Infof("[udp] %s -> %s : %s", laddr, raddr, err) - continue - } - go func() { - handleUdpForwardLocal(conn, addr, raddr, b[:n]) - udpPool.Put(b) - }() - } - } - - rChan, wChan := make(chan *gosocks5.UDPDatagram, 32), make(chan *gosocks5.UDPDatagram, 32) - - go func() { - for { - b := make([]byte, 32*1024) - n, addr, err := conn.ReadFromUDP(b) - if err != nil { - glog.V(LWARNING).Infof("[udp] %s -> %s : %s", laddr, raddr, err) - return - } - - select { - case rChan <- gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(uint16(n), 0, ToSocksAddr(addr)), b[:n]): - default: - // glog.V(LWARNING).Infof("[udp-connect] %s -> %s : rbuf is full", laddr, raddr) - } - } - }() - - go func() { - for { - dgram := <-wChan - addr, err := net.ResolveUDPAddr("udp", dgram.Header.Addr.String()) - if err != nil { - glog.V(LWARNING).Infof("[udp] %s <- %s : %s", laddr, raddr, err) - continue // drop silently - } - if _, err = conn.WriteToUDP(dgram.Data, addr); err != nil { - glog.V(LWARNING).Infof("[udp] %s <- %s : %s", laddr, raddr, err) - return - } - } - }() - - for { - handleUdpForwardTunnel(laddr, raddr, rChan, wChan) - } -} - -func serveRTcpForward(arg Args) error { - if len(forwardArgs) == 0 { - return errors.New("rtcp: at least one -F must be assigned") - } - - laddr, err := net.ResolveTCPAddr("tcp", arg.Addr) - if err != nil { - return err - } - raddr, err := net.ResolveTCPAddr("tcp", arg.Remote) - if err != nil { - return err - } - - retry := 0 - for { - conn, _, err := forwardChain(forwardArgs...) - if err != nil { - glog.V(LWARNING).Infof("[rtcp] %s - %s : %s", arg.Addr, arg.Remote, err) - time.Sleep((1 << uint(retry)) * time.Second) - if retry < 5 { - retry++ - } - continue - } - retry = 0 - - if err := connectRTcpForward(conn, laddr, raddr); err != nil { - conn.Close() - time.Sleep(6 * time.Second) - } - } -} - -func serveRUdpForward(arg Args) error { - if len(forwardArgs) == 0 { - return errors.New("rudp: at least one -F must be assigned") - } - - laddr, err := net.ResolveUDPAddr("udp", arg.Addr) - if err != nil { - return err - } - raddr, err := net.ResolveUDPAddr("udp", arg.Remote) - if err != nil { - return err - } - - retry := 0 - for { - conn, _, err := forwardChain(forwardArgs...) - if err != nil { - glog.V(LWARNING).Infof("[rudp] %s - %s : %s", arg.Addr, arg.Remote, err) - time.Sleep((1 << uint(retry)) * time.Second) - if retry < 5 { - retry++ - } - continue - } - retry = 0 - - if err := connectRUdpForward(conn, laddr, raddr); err != nil { - conn.Close() - time.Sleep(6 * time.Second) - } - } -} - -func handleConn(conn net.Conn, arg Args) { - defer conn.Close() - - // socks5 server supported methods - selector := &serverSelector{ - methods: []uint8{ - gosocks5.MethodNoAuth, - gosocks5.MethodUserPass, - MethodTLS, - MethodTLSAuth, - }, - user: arg.User, - cert: arg.Cert, - } - - switch arg.Protocol { - case "ss": // shadowsocks - handleShadow(conn, arg) - return - case "http": - req, err := http.ReadRequest(bufio.NewReader(conn)) - if err != nil { - glog.V(LWARNING).Infoln("[http]", err) - return - } - handleHttpRequest(req, conn, arg) - return - case "socks", "socks5": - conn = gosocks5.ServerConn(conn, selector) - req, err := gosocks5.ReadRequest(conn) - if err != nil { - glog.V(LWARNING).Infoln("[socks5]", err) - return - } - handleSocks5Request(req, conn) - return - } - - // http or socks5 - - //b := make([]byte, 16*1024) - b := tcpPool.Get().([]byte) - defer tcpPool.Put(b) - - n, err := io.ReadAtLeast(conn, b, 2) - if err != nil { - glog.V(LWARNING).Infoln("[client]", err) - return - } - - if b[0] == gosocks5.Ver5 { - mn := int(b[1]) // methods count - length := 2 + mn - if n < length { - if _, err := io.ReadFull(conn, b[n:length]); err != nil { - glog.V(LWARNING).Infoln("[socks5]", err) - return - } - } - methods := b[2 : 2+mn] - method := selector.Select(methods...) - if _, err := conn.Write([]byte{gosocks5.Ver5, method}); err != nil { - glog.V(LWARNING).Infoln("[socks5] select:", err) - return - } - c, err := selector.OnSelected(method, conn) - if err != nil { - glog.V(LWARNING).Infoln("[socks5] onselected:", err) - return - } - conn = c - - req, err := gosocks5.ReadRequest(conn) - if err != nil { - glog.V(LWARNING).Infoln("[socks5] request:", err) - return - } - handleSocks5Request(req, conn) - return - } - - req, err := http.ReadRequest(bufio.NewReader(newReqReader(b[:n], conn))) - if err != nil { - glog.V(LWARNING).Infoln("[http]", err) - return - } - handleHttpRequest(req, conn, arg) -} - -type reqReader struct { - b []byte - r io.Reader -} - -func newReqReader(b []byte, r io.Reader) *reqReader { - return &reqReader{ - b: b, - r: r, - } -} - -func (r *reqReader) Read(p []byte) (n int, err error) { - if len(r.b) == 0 { - return r.r.Read(p) - } - n = copy(p, r.b) - r.b = r.b[n:] - - return -} - -func connect(addr string, prot string, chain ...Args) (conn net.Conn, err error) { - if !strings.Contains(addr, ":") { - addr += ":80" - } - - if enabled, h2host := http2Enabled(); enabled { - return connectHttp2(http2Client, h2host, addr, prot) - } - - if len(chain) == 0 { - return net.DialTimeout("tcp", addr, time.Second*90) - } - - var end Args - conn, end, err = forwardChain(chain...) - if err != nil { - return nil, err - } - if err := establish(conn, addr, end); err != nil { - conn.Close() - return nil, err - } - return conn, nil -} - -func http2Enabled() (enabled bool, host string) { - length := len(forwardArgs) - if http2Client == nil || length == 0 || forwardArgs[length-1].Transport != "http2" { - return - } - return true, forwardArgs[length-1].Addr -} - -func connectHttp2(client *http.Client, host, target string, prot string) (net.Conn, error) { - pr, pw := io.Pipe() - req := http.Request{ - Method: http.MethodConnect, - URL: &url.URL{Scheme: "https", Host: host}, - Header: make(http.Header), - Proto: "HTTP/2.0", - ProtoMajor: 2, - ProtoMinor: 0, - Body: ioutil.NopCloser(pr), - Host: host, - ContentLength: -1, - } - req.Header.Set("gost-target", target) - if prot != "" { - req.Header.Set("gost-protocol", prot) - } - - if glog.V(LDEBUG) { - dump, _ := httputil.DumpRequest(&req, false) - glog.Infoln(string(dump)) - } - resp, err := client.Do(&req) - if err != nil { - return nil, err - } - if resp.StatusCode != http.StatusOK { - resp.Body.Close() - return nil, errors.New(resp.Status) - } - conn := &Http2ClientConn{r: resp.Body, w: pw} - conn.remoteAddr, _ = net.ResolveTCPAddr("tcp", target) - return conn, nil -} - -// establish connection throughout the forward chain -func forwardChain(chain ...Args) (conn net.Conn, end Args, err error) { - defer func() { - if err != nil && conn != nil { - conn.Close() - conn = nil - } - }() - - end = chain[0] - if conn, err = net.DialTimeout("tcp", end.Addr, time.Second*90); err != nil { - return - } - - setKeepAlive(conn, keepAliveTime) - - c, err := forward(conn, end) - if err != nil { - return - } - conn = c - - chain = chain[1:] - for _, arg := range chain { - if err = establish(conn, arg.Addr, end); err != nil { - goto exit - } - - c, err = forward(conn, arg) - if err != nil { - goto exit - } - conn = c - end = arg - } - -exit: - return -} - -func forward(conn net.Conn, arg Args) (net.Conn, error) { - var err error - if glog.V(LINFO) { - proto := arg.Protocol - trans := arg.Transport - if proto == "" { - proto = "http" // default is http - } - if trans == "" { // default is tcp - trans = "tcp" - } - glog.V(LDEBUG).Infof("forward: %s/%s %s", proto, trans, arg.Addr) - } - - var tlsUsed bool - - switch arg.Transport { - case "ws": // websocket connection - conn, err = wsClient("ws", conn, arg.Addr) - if err != nil { - return nil, err - } - case "wss": // websocket security - tlsUsed = true - conn, err = wsClient("wss", conn, arg.Addr) - if err != nil { - return nil, err - } - case "tls": // tls connection - tlsUsed = true - conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true}) - // conn = tls.Client(conn, &tls.Config{ServerName: "ice139.com"}) - case "tcp": - fallthrough - default: - } - - switch arg.Protocol { - case "socks", "socks5": - selector := &clientSelector{ - methods: []uint8{ - gosocks5.MethodNoAuth, - gosocks5.MethodUserPass, - //MethodTLS, - }, - user: arg.User, - } - - if !tlsUsed { // if transport is not security, enable security socks5 - selector.methods = append(selector.methods, MethodTLS) - } - - c := gosocks5.ClientConn(conn, selector) - if err := c.Handleshake(); err != nil { - return nil, err - } - conn = c - case "ss": // shadowsocks - if arg.User != nil { - method := arg.User.Username() - password, _ := arg.User.Password() - cipher, err := shadowsocks.NewCipher(method, password) - if err != nil { - return nil, err - } - conn = shadowsocks.NewConn(conn, cipher) - } - case "http": - fallthrough - default: - } - - return conn, nil -} - -func establish(conn net.Conn, addr string, arg Args) error { - switch arg.Protocol { - case "ss": // shadowsocks - host, port, err := net.SplitHostPort(addr) - if err != nil { - return err - } - p, _ := strconv.Atoi(port) - req := gosocks5.NewRequest(gosocks5.CmdConnect, &gosocks5.Addr{ - Type: gosocks5.AddrDomain, - Host: host, - Port: uint16(p), - }) - buf := bytes.Buffer{} - if err := req.Write(&buf); err != nil { - return err - } - b := buf.Bytes() - if _, err := conn.Write(b[3:]); err != nil { - return err - } - glog.V(LDEBUG).Infoln(req) - case "socks", "socks5": - host, port, err := net.SplitHostPort(addr) - if err != nil { - return err - } - p, _ := strconv.Atoi(port) - req := gosocks5.NewRequest(gosocks5.CmdConnect, &gosocks5.Addr{ - Type: gosocks5.AddrDomain, - Host: host, - Port: uint16(p), - }) - if err := req.Write(conn); err != nil { - return err - } - glog.V(LDEBUG).Infoln(req) - - rep, err := gosocks5.ReadReply(conn) - if err != nil { - return err - } - glog.V(LDEBUG).Infoln(rep) - if rep.Rep != gosocks5.Succeeded { - return errors.New("Service unavailable") - } - case "http": - fallthrough - default: - req := &http.Request{ - Method: "CONNECT", - URL: &url.URL{Host: addr}, - Host: addr, - ProtoMajor: 1, - ProtoMinor: 1, - Header: make(http.Header), - } - req.Header.Set("Proxy-Connection", "keep-alive") - if arg.User != nil { - req.Header.Set("Proxy-Authorization", - "Basic "+base64.StdEncoding.EncodeToString([]byte(arg.User.String()))) - } - if err := req.Write(conn); err != nil { - return err - } - if glog.V(LDEBUG) { - dump, _ := httputil.DumpRequest(req, false) - glog.Infoln(string(dump)) - } - - resp, err := http.ReadResponse(bufio.NewReader(conn), req) - if err != nil { - return err - } - if glog.V(LDEBUG) { - dump, _ := httputil.DumpResponse(resp, false) - glog.Infoln(string(dump)) - } - if resp.StatusCode != http.StatusOK { - return errors.New(resp.Status) - } - } - - return nil -} diff --git a/cmd/gost/http.go b/cmd/gost/http.go deleted file mode 100644 index fb4dbb8..0000000 --- a/cmd/gost/http.go +++ /dev/null @@ -1,308 +0,0 @@ -package main - -import ( - "bufio" - "crypto/tls" - "encoding/base64" - "github.com/golang/glog" - "golang.org/x/net/http2" - "io" - "net" - "net/http" - "net/http/httputil" - "strings" - "time" -) - -var ( - http2Client *http.Client -) - -func handleHttpRequest(req *http.Request, conn net.Conn, arg Args) { - glog.V(LINFO).Infof("[http] %s %s - %s %s", req.Method, conn.RemoteAddr(), req.Host, req.Proto) - - if glog.V(LDEBUG) { - dump, _ := httputil.DumpRequest(req, false) - glog.Infoln(string(dump)) - } - - var username, password string - if arg.User != nil { - username = arg.User.Username() - password, _ = arg.User.Password() - } - - u, p, _ := basicAuth(req.Header.Get("Proxy-Authorization")) - req.Header.Del("Proxy-Authorization") - if (username != "" && u != username) || (password != "" && p != password) { - resp := "HTTP/1.1 407 Proxy Authentication Required\r\n" + - "Proxy-Authenticate: Basic realm=\"gost\"\r\n" + - "Proxy-Agent: gost/" + Version + "\r\n\r\n" - - if _, err := conn.Write([]byte(resp)); err != nil { - glog.V(LWARNING).Infof("[http] %s <- %s : %s", conn.RemoteAddr(), req.Host, err) - } - glog.V(LDEBUG).Infof("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, resp) - - glog.V(LWARNING).Infof("[http] %s <- %s : proxy authentication required", conn.RemoteAddr(), req.Host) - return - } - - if len(forwardArgs) > 0 { - last := forwardArgs[len(forwardArgs)-1] - if last.Protocol == "http" || last.Protocol == "" { - forwardHttpRequest(req, conn, arg) - return - } - } - - c, err := connect(req.Host, "http", forwardArgs...) - if err != nil { - glog.V(LWARNING).Infof("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err) - - b := []byte("HTTP/1.1 503 Service unavailable\r\n" + - "Proxy-Agent: gost/" + Version + "\r\n\r\n") - glog.V(LDEBUG).Infof("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b)) - conn.Write(b) - return - } - defer c.Close() - - if req.Method == http.MethodConnect { - b := []byte("HTTP/1.1 200 Connection established\r\n" + - "Proxy-Agent: gost/" + Version + "\r\n\r\n") - glog.V(LDEBUG).Infof("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b)) - conn.Write(b) - } else { - req.Header.Del("Proxy-Connection") - req.Header.Set("Connection", "Keep-Alive") - - if err = req.Write(c); err != nil { - glog.V(LWARNING).Infof("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err) - return - } - } - - glog.V(LINFO).Infof("[http] %s <-> %s", conn.RemoteAddr(), req.Host) - Transport(conn, c) - glog.V(LINFO).Infof("[http] %s >-< %s", conn.RemoteAddr(), req.Host) -} - -func forwardHttpRequest(req *http.Request, conn net.Conn, arg Args) { - last := forwardArgs[len(forwardArgs)-1] - c, _, err := forwardChain(forwardArgs...) - if err != nil { - glog.V(LWARNING).Infof("[http] %s -> %s : %s", conn.RemoteAddr(), last.Addr, err) - - b := []byte("HTTP/1.1 503 Service unavailable\r\n" + - "Proxy-Agent: gost/" + Version + "\r\n\r\n") - glog.V(LDEBUG).Infof("[http] %s <- %s\n%s", conn.RemoteAddr(), last.Addr, string(b)) - conn.Write(b) - return - } - defer c.Close() - - if last.User != nil { - req.Header.Set("Proxy-Authorization", - "Basic "+base64.StdEncoding.EncodeToString([]byte(last.User.String()))) - } - - if err = req.Write(c); err != nil { - glog.V(LWARNING).Infof("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err) - return - } - glog.V(LINFO).Infof("[http] %s <-> %s", conn.RemoteAddr(), req.Host) - Transport(conn, c) - glog.V(LINFO).Infof("[http] %s >-< %s", conn.RemoteAddr(), req.Host) - return -} - -type Http2ClientConn struct { - r io.Reader - w io.Writer - localAddr net.Addr - remoteAddr net.Addr -} - -func (c *Http2ClientConn) Read(b []byte) (n int, err error) { - return c.r.Read(b) -} - -func (c *Http2ClientConn) Write(b []byte) (n int, err error) { - return c.w.Write(b) -} - -func (c *Http2ClientConn) Close() error { - if rc, ok := c.r.(io.ReadCloser); ok { - return rc.Close() - } - return nil -} - -func (c *Http2ClientConn) LocalAddr() net.Addr { - return c.localAddr -} - -func (c *Http2ClientConn) RemoteAddr() net.Addr { - return c.remoteAddr -} - -func (c *Http2ClientConn) SetDeadline(t time.Time) error { - return nil -} - -func (c *Http2ClientConn) SetReadDeadline(t time.Time) error { - return nil -} - -func (c *Http2ClientConn) SetWriteDeadline(t time.Time) error { - return nil -} - -// init http2 client with target http2 proxy server addr, and forward chain chain -func initHttp2Client(host string, chain ...Args) { - tr := http2.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: true, - }, - DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) { - // replace the default dialer with our forward chain. - conn, err := connect(host, "http2", chain...) - if err != nil { - return conn, err - } - return tls.Client(conn, cfg), nil - }, - } - http2Client = &http.Client{Transport: &tr} -} - -func handlerHttp2Request(w http.ResponseWriter, req *http.Request) { - target := req.Header.Get("gost-target") - if target == "" { - target = req.Host - } - - glog.V(LINFO).Infof("[http2] %s %s - %s %s", req.Method, req.RemoteAddr, target, req.Proto) - if glog.V(LDEBUG) { - dump, _ := httputil.DumpRequest(req, false) - glog.Infoln(string(dump)) - } - - c, err := connect(target, req.Header.Get("gost-protocol"), forwardArgs...) - if err != nil { - glog.V(LWARNING).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err) - w.Header().Set("Proxy-Agent", "gost/"+Version) - w.WriteHeader(http.StatusServiceUnavailable) - if fw, ok := w.(http.Flusher); ok { - fw.Flush() - } - return - } - defer c.Close() - - glog.V(LINFO).Infof("[http2] %s <-> %s", req.RemoteAddr, target) - errc := make(chan error, 2) - - if req.Method == http.MethodConnect { - w.Header().Set("Proxy-Agent", "gost/"+Version) - w.WriteHeader(http.StatusOK) - if fw, ok := w.(http.Flusher); ok { - fw.Flush() - } - - // compatible with HTTP 1.x - if hj, ok := w.(http.Hijacker); ok && req.ProtoMajor == 1 { - // we take over the underly connection - conn, _, err := hj.Hijack() - if err != nil { - glog.V(LWARNING).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err) - return - } - defer conn.Close() - - go Pipe(conn, c, errc) - go Pipe(c, conn, errc) - } else { - go Pipe(req.Body, c, errc) - go Pipe(c, flushWriter{w}, errc) - } - - select { - case <-errc: - // glog.V(LWARNING).Infoln("exit", err) - } - } else { - req.Header.Set("Connection", "Keep-Alive") - if err = req.Write(c); err != nil { - glog.V(LWARNING).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err) - return - } - - resp, err := http.ReadResponse(bufio.NewReader(c), req) - if err != nil { - glog.V(LWARNING).Infoln(err) - return - } - defer resp.Body.Close() - - for k, v := range resp.Header { - for _, vv := range v { - w.Header().Add(k, vv) - } - } - w.WriteHeader(resp.StatusCode) - if fw, ok := w.(http.Flusher); ok { - fw.Flush() - } - if _, err := io.Copy(flushWriter{w}, resp.Body); err != nil { - glog.V(LWARNING).Infof("[http2] %s <- %s : %s", req.RemoteAddr, target, err) - } - } - - glog.V(LINFO).Infof("[http2] %s >-< %s", req.RemoteAddr, target) -} - -func handleHttp2Transport(w http.ResponseWriter, req *http.Request) { - glog.V(LINFO).Infof("[http2] %s - %s", req.RemoteAddr, req.Host) - if glog.V(LDEBUG) { - dump, _ := httputil.DumpRequest(req, false) - glog.Infoln(string(dump)) - } -} - -func basicAuth(authInfo string) (username, password string, ok bool) { - if authInfo == "" { - return - } - - if !strings.HasPrefix(authInfo, "Basic ") { - return - } - c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(authInfo, "Basic ")) - if err != nil { - return - } - cs := string(c) - s := strings.IndexByte(cs, ':') - if s < 0 { - return - } - - return cs[:s], cs[s+1:], true -} - -type flushWriter struct { - w io.Writer -} - -func (fw flushWriter) Write(p []byte) (n int, err error) { - n, err = fw.w.Write(p) - if err != nil { - glog.V(LWARNING).Infoln("flush writer:", err) - } - if f, ok := fw.w.(http.Flusher); ok { - f.Flush() - } - return -} diff --git a/cmd/gost/main.go b/cmd/gost/main.go deleted file mode 100644 index 0834db5..0000000 --- a/cmd/gost/main.go +++ /dev/null @@ -1,76 +0,0 @@ -// main -package main - -import ( - "flag" - "fmt" - "github.com/golang/glog" - "golang.org/x/net/http2" - "os" - "runtime" - "sync" -) - -const ( - LFATAL = iota - LERROR - LWARNING - LINFO - LDEBUG - LVDEBUG // verbose debug -) - -const ( - Version = "2.2-dev-http2" -) - -var ( - listenAddr, forwardAddr strSlice - pv bool // print version - - listenArgs []Args - forwardArgs []Args -) - -func init() { - flag.Var(&listenAddr, "L", "listen address, can listen on multiple ports") - flag.Var(&forwardAddr, "F", "forward address, can make a forward chain") - flag.BoolVar(&pv, "V", false, "print version") - flag.Parse() - - if glog.V(LVDEBUG) { - http2.VerboseLogs = true - } -} - -func main() { - defer glog.Flush() - - if flag.NFlag() == 0 { - flag.PrintDefaults() - return - } - if pv { - fmt.Fprintf(os.Stderr, "gost %s (%s)\n", Version, runtime.Version()) - return - } - - listenArgs = parseArgs(listenAddr) - if len(listenArgs) == 0 { - fmt.Fprintln(os.Stderr, "no listen address, please specify at least one -L parameter") - return - } - - forwardArgs = parseArgs(forwardAddr) - processForwardChain(forwardArgs...) - - var wg sync.WaitGroup - for _, args := range listenArgs { - wg.Add(1) - go func(arg Args) { - defer wg.Done() - glog.V(LERROR).Infoln(listenAndServe(arg)) - }(args) - } - wg.Wait() -} diff --git a/cmd/gost/socks.go b/cmd/gost/socks.go deleted file mode 100644 index d304930..0000000 --- a/cmd/gost/socks.go +++ /dev/null @@ -1,553 +0,0 @@ -package main - -import ( - //"bytes" - "crypto/tls" - "errors" - "github.com/ginuerzh/gosocks5" - "github.com/golang/glog" - //"os/exec" - //"io" - //"io/ioutil" - "net" - "net/url" - "strconv" - "time" -) - -const ( - MethodTLS uint8 = 0x80 // extended method for tls - MethodTLSAuth uint8 = 0x82 // extended method for tls+auth -) - -const ( - CmdUdpConnect uint8 = 0xF1 // extended method for udp local port forwarding - CmdUdpTun uint8 = 0xF3 // extended method for udp over tcp -) - -type clientSelector struct { - methods []uint8 - user *url.Userinfo -} - -func (selector *clientSelector) Methods() []uint8 { - return selector.methods -} - -func (selector *clientSelector) Select(methods ...uint8) (method uint8) { - return -} - -func (selector *clientSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) { - switch method { - case MethodTLS: - conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true}) - - case gosocks5.MethodUserPass, MethodTLSAuth: - if method == MethodTLSAuth { - conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true}) - } - - var username, password string - if selector.user != nil { - username = selector.user.Username() - password, _ = selector.user.Password() - } - - req := gosocks5.NewUserPassRequest(gosocks5.UserPassVer, username, password) - if err := req.Write(conn); err != nil { - glog.V(LWARNING).Infoln("socks5 auth:", err) - return nil, err - } - glog.V(LDEBUG).Infoln(req) - - resp, err := gosocks5.ReadUserPassResponse(conn) - if err != nil { - glog.V(LWARNING).Infoln("socks5 auth:", err) - return nil, err - } - glog.V(LDEBUG).Infoln(resp) - - if resp.Status != gosocks5.Succeeded { - return nil, gosocks5.ErrAuthFailure - } - case gosocks5.MethodNoAcceptable: - return nil, gosocks5.ErrBadMethod - } - - return conn, nil -} - -type serverSelector struct { - methods []uint8 - user *url.Userinfo - cert tls.Certificate -} - -func (selector *serverSelector) Methods() []uint8 { - return selector.methods -} - -func (selector *serverSelector) Select(methods ...uint8) (method uint8) { - glog.V(LDEBUG).Infof("%d %d %v", gosocks5.Ver5, len(methods), methods) - - method = gosocks5.MethodNoAuth - for _, m := range methods { - if m == MethodTLS { - method = m - break - } - } - - // when user/pass is set, auth is mandatory - if selector.user != nil { - if method == gosocks5.MethodNoAuth { - method = gosocks5.MethodUserPass - } - if method == MethodTLS { - method = MethodTLSAuth - } - } - - return -} - -func (selector *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) { - glog.V(LDEBUG).Infof("%d %d", gosocks5.Ver5, method) - - switch method { - case MethodTLS: - conn = tls.Server(conn, &tls.Config{Certificates: []tls.Certificate{selector.cert}}) - - case gosocks5.MethodUserPass, MethodTLSAuth: - if method == MethodTLSAuth { - conn = tls.Server(conn, &tls.Config{Certificates: []tls.Certificate{selector.cert}}) - } - - req, err := gosocks5.ReadUserPassRequest(conn) - if err != nil { - glog.V(LWARNING).Infoln("[socks5-auth]", err) - return nil, err - } - glog.V(LDEBUG).Infoln("[socks5]", req.String()) - - var username, password string - if selector.user != nil { - username = selector.user.Username() - password, _ = selector.user.Password() - } - - if (username != "" && req.Username != username) || (password != "" && req.Password != password) { - resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Failure) - if err := resp.Write(conn); err != nil { - glog.V(LWARNING).Infoln("[socks5-auth]", err) - return nil, err - } - glog.V(LDEBUG).Infoln("[socks5]", resp) - glog.V(LWARNING).Infoln("[socks5-auth] proxy authentication required") - - return nil, gosocks5.ErrAuthFailure - } - - resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Succeeded) - if err := resp.Write(conn); err != nil { - glog.V(LWARNING).Infoln("[socks5-auth]", err) - return nil, err - } - glog.V(LDEBUG).Infoln(resp) - - case gosocks5.MethodNoAcceptable: - return nil, gosocks5.ErrBadMethod - } - - return conn, nil -} - -func handleSocks5Request(req *gosocks5.Request, conn net.Conn) { - glog.V(LDEBUG).Infof("[socks5] %s -> %s\n%s", conn.RemoteAddr(), req.Addr, req) - - switch req.Cmd { - case gosocks5.CmdConnect: - glog.V(LINFO).Infof("[socks5-connect] %s - %s", conn.RemoteAddr(), req.Addr) - - tconn, err := connect(req.Addr.String(), "socks5", forwardArgs...) - if err != nil { - glog.V(LWARNING).Infof("[socks5-connect] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) - rep := gosocks5.NewReply(gosocks5.HostUnreachable, nil) - if err := rep.Write(conn); err != nil { - glog.V(LWARNING).Infof("[socks5-connect] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err) - } else { - glog.V(LDEBUG).Infof("[socks5-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep) - } - return - } - defer tconn.Close() - - rep := gosocks5.NewReply(gosocks5.Succeeded, nil) - if err := rep.Write(conn); err != nil { - glog.V(LWARNING).Infof("[socks5-connect] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err) - return - } - glog.V(LDEBUG).Infof("[socks5-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep) - - glog.V(LINFO).Infof("[socks5-connect] %s <-> %s", conn.RemoteAddr(), req.Addr) - Transport(conn, tconn) - glog.V(LINFO).Infof("[socks5-connect] %s >-< %s", conn.RemoteAddr(), req.Addr) - - case gosocks5.CmdBind: - glog.V(LINFO).Infof("[socks5-bind] %s - %s", conn.RemoteAddr(), req.Addr) - - reply, fconn, err := socks5Bind(req, conn) - if reply != nil { - if err := reply.Write(conn); err != nil { - glog.V(LWARNING).Infof("[socks5-bind] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err) - if fconn != nil { - fconn.Close() - } - return - } - glog.V(LDEBUG).Infof("[socks5-bind] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, reply) - } - - if err != nil { - glog.V(LWARNING).Infof("[socks5-bind] %s - %s : %s", conn.RemoteAddr(), req.Addr, err) - return - } - defer fconn.Close() - - glog.V(LINFO).Infof("[socks5-bind] %s <-> %s", conn.RemoteAddr(), fconn.RemoteAddr()) - Transport(conn, fconn) - glog.V(LINFO).Infof("[socks5-bind] %s >-< %s", conn.RemoteAddr(), fconn.RemoteAddr()) - - case CmdUdpConnect: - glog.V(LINFO).Infof("[udp] %s - %s", conn.RemoteAddr(), req.Addr) - udpConnect(req, conn) - - case gosocks5.CmdUdp: - glog.V(LINFO).Infof("[socks5-udp] %s - %s", conn.RemoteAddr(), req.Addr) - socks5UDP(req, conn) - - case CmdUdpTun: - glog.V(LINFO).Infof("[socks5-udp] %s - %s", conn.RemoteAddr(), req.Addr) - if err := socks5TunnelUDP(req, conn); err != nil { - glog.V(LWARNING).Infof("[socks5-udp] %s - %s : %s", conn.RemoteAddr(), req.Addr, err) - rep := gosocks5.NewReply(gosocks5.Failure, nil) - if err := rep.Write(conn); err != nil { - glog.V(LWARNING).Infof("[socks5-udp] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err) - } else { - glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep) - } - return - } - - default: - glog.V(LWARNING).Infoln("[socks5] Unrecognized request:", req.Cmd) - } -} - -func udpConnect(req *gosocks5.Request, conn net.Conn) error { - if len(forwardArgs) > 0 { // direct forwarding - fconn, _, err := forwardChain(forwardArgs...) - if err != nil { - glog.V(LINFO).Infof("[udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) - gosocks5.NewReply(gosocks5.Failure, nil).Write(conn) - return err - } - defer fconn.Close() - - if err := req.Write(fconn); err != nil { - glog.V(LINFO).Infof("[udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) - gosocks5.NewReply(gosocks5.Failure, nil).Write(conn) - return err - } - - glog.V(LINFO).Infof("[udp] %s <-> %s", conn.RemoteAddr(), req.Addr) - err = Transport(conn, fconn) - glog.V(LINFO).Infof("[udp] %s >-< %s", conn.RemoteAddr(), req.Addr) - return err - } - - raddr, err := net.ResolveUDPAddr("udp", req.Addr.String()) - if err != nil { - glog.V(LINFO).Infof("[udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) - gosocks5.NewReply(gosocks5.Failure, nil).Write(conn) - return err - } - - if err := gosocks5.NewReply(gosocks5.Succeeded, nil).Write(conn); err != nil { - glog.V(LINFO).Infof("[udp] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err) - return err - } - - glog.V(LINFO).Infof("[udp] %s <-> %s", conn.RemoteAddr(), raddr) - defer glog.V(LINFO).Infof("[udp] %s >-< %s", conn.RemoteAddr(), raddr) - - for { - dgram, err := gosocks5.ReadUDPDatagram(conn) - if err != nil { - glog.V(LWARNING).Infof("[udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) - return err - } - - go func() { - b := udpPool.Get().([]byte) - defer udpPool.Put(b) - - relay, err := net.DialUDP("udp", nil, raddr) - if err != nil { - glog.V(LWARNING).Infof("[udp] %s -> %s : %s", conn.RemoteAddr(), raddr, err) - return - } - defer relay.Close() - - if _, err := relay.Write(dgram.Data); err != nil { - glog.V(LWARNING).Infof("[udp] %s -> %s : %s", conn.RemoteAddr(), raddr, err) - return - } - glog.V(LDEBUG).Infof("[udp-tun] %s >>> %s length: %d", conn.RemoteAddr(), raddr, len(dgram.Data)) - - relay.SetReadDeadline(time.Now().Add(time.Second * 60)) - n, err := relay.Read(b) - if err != nil { - glog.V(LWARNING).Infof("[udp] %s <- %s : %s", conn.RemoteAddr(), raddr, err) - return - } - relay.SetReadDeadline(time.Time{}) - - glog.V(LDEBUG).Infof("[udp-tun] %s <<< %s length: %d", conn.RemoteAddr(), raddr, n) - - conn.SetWriteDeadline(time.Now().Add(time.Second * 90)) - if err := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(uint16(n), 0, dgram.Header.Addr), b[:n]).Write(conn); err != nil { - glog.V(LWARNING).Infof("[udp] %s <- %s : %s", conn.RemoteAddr(), raddr, err) - return - } - conn.SetWriteDeadline(time.Time{}) - }() - } -} - -func socks5UDP(req *gosocks5.Request, conn net.Conn) error { - bindAddr, _ := net.ResolveUDPAddr("udp", req.Addr.String()) - relay, err := net.ListenUDP("udp", bindAddr) // udp associate, strict mode: if the port already in use, it will return error - if err != nil { - glog.V(LWARNING).Infof("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) - - rep := gosocks5.NewReply(gosocks5.Failure, nil) - if err := rep.Write(conn); err != nil { - glog.V(LWARNING).Infof("[socks5-udp] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err) - } else { - glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep) - } - return err - } - defer relay.Close() - - addr := ToSocksAddr(relay.LocalAddr()) - addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) - rep := gosocks5.NewReply(gosocks5.Succeeded, addr) - if err := rep.Write(conn); err != nil { - return err - } - glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep) - - glog.V(LINFO).Infof("[socks5-udp] %s - %s BIND ON %s OK", conn.RemoteAddr(), req.Addr, addr) - - if len(forwardArgs) > 0 { // client -> tunnel, tunnel udp over tcp - tun, _, err := forwardChain(forwardArgs...) - if err != nil { - glog.V(LWARNING).Infof("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) - return err - } - defer tun.Close() - - tun.SetWriteDeadline(time.Now().Add(time.Second * 90)) - if err := gosocks5.NewRequest(CmdUdpTun, nil).Write(tun); err != nil { - glog.V(LWARNING).Infoln("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) - return err - } - tun.SetWriteDeadline(time.Time{}) - - tun.SetReadDeadline(time.Now().Add(time.Second * 90)) - rep, err := gosocks5.ReadReply(tun) - if err != nil { - glog.V(LWARNING).Infoln("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) - return err - } - if rep.Rep != gosocks5.Succeeded { - return errors.New("udp associate error") - } - tun.SetReadDeadline(time.Time{}) - - glog.V(LINFO).Infof("[socks5-udp] %s <-> %s", conn.RemoteAddr(), req.Addr) - go tunnelUDP(relay, tun, true) - } else { // standard socks5 udp relay - peer, err := net.ListenUDP("udp", nil) - if err != nil { - glog.V(LWARNING).Infof("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) - return err - } - defer peer.Close() - - glog.V(LINFO).Infof("[socks5-udp] %s <-> %s", conn.RemoteAddr(), req.Addr) - go transportUDP(relay, peer) - } - - b := tcpPool.Get().([]byte) - defer tcpPool.Put(b) - for { - _, err := conn.Read(b) // discard any data from tcp connection - if err != nil { - break // client disconnected - } - } - glog.V(LINFO).Infof("[socks5-udp] %s >-< %s", conn.RemoteAddr(), req.Addr) - return nil -} - -func socks5TunnelUDP(req *gosocks5.Request, conn net.Conn) error { - if len(forwardArgs) > 0 { // tunnel -> tunnel, direct forwarding - tun, _, err := forwardChain(forwardArgs...) - if err != nil { - return err - } - defer tun.Close() - - if err := req.Write(tun); err != nil { - return err - } - - glog.V(LINFO).Infof("[socks5-udp] %s <-> %s[tun]", conn.RemoteAddr(), tun.RemoteAddr()) - Transport(conn, tun) - glog.V(LINFO).Infof("[socks5-udp] %s >-< %s[tun]", conn.RemoteAddr(), tun.RemoteAddr()) - } else { // tunnel -> remote, handle tunnel udp request - bindAddr, _ := net.ResolveUDPAddr("udp", req.Addr.String()) - uconn, err := net.ListenUDP("udp", bindAddr) - if err != nil { - return err - } - defer uconn.Close() - - addr := ToSocksAddr(uconn.LocalAddr()) - addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) - rep := gosocks5.NewReply(gosocks5.Succeeded, addr) - if err := rep.Write(conn); err != nil { - return nil - } - glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), uconn.LocalAddr(), rep) - - glog.V(LINFO).Infof("[socks5-udp] %s <-> %s", conn.RemoteAddr(), uconn.LocalAddr()) - tunnelUDP(uconn, conn, false) - glog.V(LINFO).Infof("[socks5-udp] %s >-< %s", conn.RemoteAddr(), uconn.LocalAddr()) - } - return nil -} - -func socks5Bind(req *gosocks5.Request, conn net.Conn) (*gosocks5.Reply, net.Conn, error) { - if len(forwardArgs) > 0 { - fconn, _, err := forwardChain(forwardArgs...) - if err != nil { - return gosocks5.NewReply(gosocks5.Failure, nil), nil, err - } - - if err := req.Write(fconn); err != nil { - fconn.Close() - return gosocks5.NewReply(gosocks5.Failure, nil), nil, err - } - - return nil, fconn, nil - } - - bindAddr, _ := net.ResolveTCPAddr("tcp", req.Addr.String()) - ln, err := net.ListenTCP("tcp", bindAddr) // strict mode: if the port already in use, it will return error - if err != nil { - return gosocks5.NewReply(gosocks5.Failure, nil), nil, err - } - - addr := ToSocksAddr(ln.Addr()) - // Issue: may not reachable when host has multi-interface - addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) - rep := gosocks5.NewReply(gosocks5.Succeeded, addr) - if err := rep.Write(conn); err != nil { - glog.V(LWARNING).Infof("[socks5-bind] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err) - ln.Close() - return nil, nil, err - } - glog.V(LDEBUG).Infof("[socks5-bind] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep) - glog.V(LINFO).Infof("[socks5-bind] %s - %s BIND ON %s OK", conn.RemoteAddr(), req.Addr, addr) - - lnChan := make(chan net.Conn, 1) - go func() { - defer close(lnChan) - c, err := ln.AcceptTCP() - if err != nil { - return - } - lnChan <- c - }() - - peerChan := make(chan error, 1) - go func() { - defer close(peerChan) - b := tcpPool.Get().([]byte) - defer tcpPool.Put(b) - _, err := conn.Read(b) - if err != nil { - if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() { - return - } - peerChan <- err - } - }() - - var pconn net.Conn - - for { - select { - case c := <-lnChan: - ln.Close() // only accept one peer - if c == nil { - return gosocks5.NewReply(gosocks5.Failure, nil), nil, errors.New("[socks5-bind] accept error") - } - pconn = c - lnChan = nil - ln = nil - conn.SetReadDeadline(time.Now()) // timeout right now ,so we can break out of blocking - case err := <-peerChan: - if err != nil || pconn == nil { - if ln != nil { - ln.Close() - } - if pconn != nil { - pconn.Close() - } - if err == nil { - err = errors.New("Oops, some mysterious error!") - } - return nil, nil, err - } - goto out - } - } - -out: - conn.SetReadDeadline(time.Time{}) - - glog.V(LINFO).Infof("[socks5-bind] %s <- %s PEER %s ACCEPTED", conn.RemoteAddr(), addr, pconn.RemoteAddr()) - rep = gosocks5.NewReply(gosocks5.Succeeded, ToSocksAddr(pconn.RemoteAddr())) - return rep, pconn, nil -} - -func ToSocksAddr(addr net.Addr) *gosocks5.Addr { - host := "0.0.0.0" - port := 0 - if addr != nil { - h, p, _ := net.SplitHostPort(addr.String()) - host = h - port, _ = strconv.Atoi(p) - } - return &gosocks5.Addr{ - Type: gosocks5.AddrIPv4, - Host: host, - Port: uint16(port), - } -} diff --git a/cmd/gost/ss.go b/cmd/gost/ss.go deleted file mode 100644 index 2ed3f34..0000000 --- a/cmd/gost/ss.go +++ /dev/null @@ -1,123 +0,0 @@ -package main - -import ( - "encoding/binary" - "fmt" - "github.com/ginuerzh/gosocks5" - "github.com/golang/glog" - "github.com/shadowsocks/shadowsocks-go/shadowsocks" - "io" - "net" -) - -func handleShadow(conn net.Conn, arg Args) { - glog.V(LINFO).Infof("[ss] %s -> %s", conn.RemoteAddr(), conn.LocalAddr()) - - if arg.User != nil { - method := arg.User.Username() - password, _ := arg.User.Password() - cipher, err := shadowsocks.NewCipher(method, password) - if err != nil { - glog.V(LWARNING).Infof("[ss] %s - %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) - return - } - conn = shadowsocks.NewConn(conn, cipher) - } - - addr, extra, err := getShadowRequest(conn) - if err != nil { - glog.V(LWARNING).Infof("[ss] %s - %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) - return - } - glog.V(LINFO).Infof("[ss] %s -> %s", conn.RemoteAddr(), addr.String()) - - sconn, err := connect(addr.String(), "ss", forwardArgs...) - if err != nil { - glog.V(LWARNING).Infof("[ss] %s -> %s : %s", conn.RemoteAddr(), addr.String(), err) - return - } - defer sconn.Close() - - if extra != nil { - if _, err := sconn.Write(extra); err != nil { - glog.V(LWARNING).Infof("[ss] %s - %s : %s", conn.RemoteAddr(), addr.String(), err) - return - } - } - - glog.V(LINFO).Infof("[ss] %s <-> %s", conn.RemoteAddr(), addr.String()) - Transport(conn, sconn) - glog.V(LINFO).Infof("[ss] %s >-< %s", conn.RemoteAddr(), addr.String()) -} - -func getShadowRequest(conn net.Conn) (addr *gosocks5.Addr, extra []byte, err error) { - const ( - idType = 0 // address type index - idIP0 = 1 // ip addres start index - idDmLen = 1 // domain address length index - idDm0 = 2 // domain address start index - - typeIPv4 = 1 // type is ipv4 address - typeDm = 3 // type is domain address - typeIPv6 = 4 // type is ipv6 address - - lenIPv4 = 1 + net.IPv4len + 2 // 1addrType + ipv4 + 2port - lenIPv6 = 1 + net.IPv6len + 2 // 1addrType + ipv6 + 2port - lenDmBase = 1 + 1 + 2 // 1addrType + 1addrLen + 2port, plus addrLen - ) - - // buf size should at least have the same size with the largest possible - // request size (when addrType is 3, domain name has at most 256 bytes) - // 1(addrType) + 1(lenByte) + 256(max length address) + 2(port) - buf := make([]byte, 1024) - - var n int - // read till we get possible domain length field - //shadowsocks.SetReadTimeout(conn) - if n, err = io.ReadAtLeast(conn, buf, idDmLen+1); err != nil { - return - } - - addr = &gosocks5.Addr{ - Type: buf[idType], - } - - reqLen := -1 - switch buf[idType] { - case typeIPv4: - reqLen = lenIPv4 - case typeIPv6: - reqLen = lenIPv6 - case typeDm: - reqLen = int(buf[idDmLen]) + lenDmBase - default: - err = fmt.Errorf("addr type %d not supported", buf[idType]) - return - } - - if n < reqLen { // rare case - //ss.SetReadTimeout(conn) - if _, err = io.ReadFull(conn, buf[n:reqLen]); err != nil { - return - } - } else if n > reqLen { - // it's possible to read more than just the request head - extra = buf[reqLen:n] - } - - // Return string for typeIP is not most efficient, but browsers (Chrome, - // Safari, Firefox) all seems using typeDm exclusively. So this is not a - // big problem. - switch buf[idType] { - case typeIPv4: - addr.Host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String() - case typeIPv6: - addr.Host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String() - case typeDm: - addr.Host = string(buf[idDm0 : idDm0+buf[idDmLen]]) - } - // parse port - addr.Port = binary.BigEndian.Uint16(buf[reqLen-2 : reqLen]) - - return -} diff --git a/cmd/gost/tls.go b/cmd/gost/tls.go deleted file mode 100644 index 083df8e..0000000 --- a/cmd/gost/tls.go +++ /dev/null @@ -1,74 +0,0 @@ -package main - -import ( - "crypto/tls" - "github.com/golang/glog" -) - -const ( - certFile = "cert.pem" - keyFile = "key.pem" - // This is the default cert file for convenience, providing your own cert is recommended. - rawCert = `-----BEGIN CERTIFICATE----- -MIIC5jCCAdCgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD -bzAeFw0xNDAzMTcwNjIwNTFaFw0xNTAzMTcwNjIwNTFaMBIxEDAOBgNVBAoTB0Fj -bWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDccNO1xmd4lWSf -d/0/QS3E93cYIWHw831i/IKxigdRD/XMZonLdEHywW6lOiXazaP8e6CqPGSmnl0x -5k/3dvGCMj2JCVxM6+z7NpL+AiwvXmvkj/TOciCgwqssCwYS2CiVwjfazRjx1ZUJ -VDC5qiyRsfktQ2fVHrpnJGVSRagmiQgwGWBilVG9B8QvRtpQKN/GQGq17oIQm8aK -kOdPt93g93ojMIg7YJpgDgOirvVz/hDn7YD4ryrtPos9CMafFkJprymKpRHyvz7P -8a3+OkuPjFjPnwOHQ5u1U3+8vC44vfb1ExWzDLoT8Xp8Gndx39k0f7MVOol3GnYu -MN/dvNUdAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIAoDATBgNVHSUEDDAKBggrBgEF -BQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDALBgkqhkiG -9w0BAQUDggEBAIG8CJqvTIgJnNOK+i5/IUc/3yF/mSCWuG8qP+Fmo2t6T0PVOtc0 -8wiWH5iWtCAhjn0MRY9l/hIjWm6gUZGHCGuEgsOPpJDYGoNLjH9Xwokm4y3LFNRK -UBrrrDbKRNibApBHCapPf6gC5sXcjOwx7P2/kiHDgY7YH47jfcRhtAPNsM4gjsEO -RmwENY+hRUFHIRfQTyalqND+x6PWhRo3K6hpHs4DQEYPq4P2kFPqUqSBymH+Ny5/ -BcQ3wdMNmC6Bm/oiL1QV0M+/InOsAgQk/EDd0kmoU1ZT2lYHQduGmP099bOlHNpS -uqO3vXF3q8SPPr/A9TqSs7BKkBQbe0+cdsA= ------END CERTIFICATE-----` - - rawKey = `-----BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEA3HDTtcZneJVkn3f9P0EtxPd3GCFh8PN9YvyCsYoHUQ/1zGaJ -y3RB8sFupTol2s2j/Hugqjxkpp5dMeZP93bxgjI9iQlcTOvs+zaS/gIsL15r5I/0 -znIgoMKrLAsGEtgolcI32s0Y8dWVCVQwuaoskbH5LUNn1R66ZyRlUkWoJokIMBlg -YpVRvQfEL0baUCjfxkBqte6CEJvGipDnT7fd4Pd6IzCIO2CaYA4Doq71c/4Q5+2A -+K8q7T6LPQjGnxZCaa8piqUR8r8+z/Gt/jpLj4xYz58Dh0ObtVN/vLwuOL329RMV -swy6E/F6fBp3cd/ZNH+zFTqJdxp2LjDf3bzVHQIDAQABAoIBAHal26147nQ+pHwY -jxwers3XDCjWvup7g79lfcqlKi79UiUEA6KYHm7UogMYewt7p4nb2KwH+XycvDiB -aAUf5flXpTs+6IkWauUDiLZi4PlV7uiEexUq5FjirlL0U/6MjbudX4bK4WQ4uxDc -WaV07Kw2iJFOOHLDKT0en9JaX5jtJNc4ZnE9efFoQ5jfypPWtRw65G1rULEg6nvc -GDh+1ce+4foCkpLRC9c24xAwJONZG6x3UqrSS9qfAsb73nWRQrTfUcO3nhoN8VvL -kL9skn1+S06NyUN0KoEtyRBp+RcpXSsBWAo6qZmo/WqhB/gjzWrxVwn20+yJSm35 -ZsMc6QECgYEA8GS+Mp9xfB2szWHz6YTOO1Uu4lHM1ccZMwS1G+dL0KO3uGAiPdvp -woVot6v6w88t7onXsLo5pgz7SYug0CpkF3K/MRd1Ar4lH7PK7IBQ6rFr9ppVxDbx -AEWRswUoPbKCr7W6HU8LbQHDavsDlEIwc6+DiwnL4BzlKjb7RpgQEz0CgYEA6sB5 -uHvx3Y5FDcGk1n73leQSAcq14l3ZLNpjrs8msoREDil/j5WmuSN58/7PGMiMgHEi -1vLm3H796JmvGr9OBvspOjHyk07ui2/We/j9Hoxm1VWhyi8HkLNDj70HKalTTFMz -RHO4O+0xCva+h9mKZrRMVktXr2jjdFn/0MYIZ2ECgYAIIsC1IeRLWQ3CHbCNlKsO -IwHlMvOFwKk/qsceXKOaOhA7szU1dr3gkXdL0Aw6mEZrrkqYdpUA46uVf54/rU+Z -445I8QxKvXiwK/uQKX+TkdGflPWWIG3jnnch4ejMvb/ihnn4B/bRB6A/fKNQXzUY -lTYUfI5j1VaEKTwz1W2l2QKBgByFCcSp+jZqhGUpc3dDsZyaOr3Q/Mvlju7uEVI5 -hIAHpaT60a6GBd1UPAqymEJwivFHzW3D0NxU6VAK68UaHMaoWNfjHY9b9YsnKS2i -kE3XzN56Ks+/avHfdYPO+UHMenw5V28nh+hv5pdoZrlmanQTz3pkaOC8o3WNQZEB -nh/BAoGBAMY5z2f1pmMhrvtPDSlEVjgjELbaInxFaxPLR4Pdyzn83gtIIU14+R8X -2LPs6PPwrNjWnIgrUSVXncIFL3pa45B+Mx1pYCpOAB1+nCZjIBQmpeo4Y0dwA/XH -85EthKPvoszm+OPbyI16OcePV5ocX7lupRYuAo0pek7bomhmHWHz ------END RSA PRIVATE KEY-----` -) - -func init() { - var err error - if tlsCert, err = tls.LoadX509KeyPair(certFile, keyFile); err != nil { - glog.V(LWARNING).Infoln(err) - - tlsCert, err = tls.X509KeyPair([]byte(rawCert), []byte(rawKey)) - if err != nil { - glog.Infoln(err) - } - } -} - -var ( - tlsCert tls.Certificate -) diff --git a/cmd/gost/udp.go b/cmd/gost/udp.go deleted file mode 100644 index f751211..0000000 --- a/cmd/gost/udp.go +++ /dev/null @@ -1,170 +0,0 @@ -package main - -import ( - "bytes" - "github.com/ginuerzh/gosocks5" - "github.com/golang/glog" - "net" - //"time" -) - -func transportUDP(relay, peer *net.UDPConn) (err error) { - rChan := make(chan error, 1) - wChan := make(chan error, 1) - - var clientAddr *net.UDPAddr - - go func() { - b := udpPool.Get().([]byte) - defer udpPool.Put(b) - - for { - n, laddr, err := relay.ReadFromUDP(b) - if err != nil { - rChan <- err - return - } - if clientAddr == nil { - clientAddr = laddr - } - dgram, err := gosocks5.ReadUDPDatagram(bytes.NewReader(b[:n])) - if err != nil { - rChan <- err - return - } - - raddr, err := net.ResolveUDPAddr("udp", dgram.Header.Addr.String()) - if err != nil { - continue // drop silently - } - if _, err := peer.WriteToUDP(dgram.Data, raddr); err != nil { - rChan <- err - return - } - glog.V(LDEBUG).Infof("[socks5-udp] %s >>> %s length: %d", relay.LocalAddr(), raddr, len(dgram.Data)) - } - }() - - go func() { - b := udpPool.Get().([]byte) - defer udpPool.Put(b) - - for { - n, raddr, err := peer.ReadFrom(b) - if err != nil { - wChan <- err - return - } - if clientAddr == nil { - continue - } - buf := bytes.Buffer{} - dgram := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(0, 0, ToSocksAddr(raddr)), b[:n]) - dgram.Write(&buf) - if _, err := relay.WriteToUDP(buf.Bytes(), clientAddr); err != nil { - wChan <- err - return - } - glog.V(LDEBUG).Infof("[socks5-udp] %s <<< %s length: %d", relay.LocalAddr(), raddr, len(dgram.Data)) - } - }() - - select { - case err = <-wChan: - //log.Println("w exit", err) - case err = <-rChan: - //log.Println("r exit", err) - } - - return -} - -func tunnelUDP(conn *net.UDPConn, tun net.Conn, client bool) (err error) { - rChan := make(chan error, 1) - wChan := make(chan error, 1) - - var clientAddr *net.UDPAddr - - go func() { - b := udpPool.Get().([]byte) - defer udpPool.Put(b) - - for { - n, addr, err := conn.ReadFromUDP(b) - if err != nil { - rChan <- err - return - } - - var dgram *gosocks5.UDPDatagram - if client { // pipe from relay to tunnel - dgram, err = gosocks5.ReadUDPDatagram(bytes.NewReader(b[:n])) - if err != nil { - rChan <- err - return - } - if clientAddr == nil { - clientAddr = addr - } - dgram.Header.Rsv = uint16(len(dgram.Data)) - if err := dgram.Write(tun); err != nil { - rChan <- err - return - } - glog.V(LDEBUG).Infof("[udp-tun] %s >>> %s length: %d", conn.LocalAddr(), dgram.Header.Addr, len(dgram.Data)) - } else { // pipe from peer to tunnel - dgram = gosocks5.NewUDPDatagram( - gosocks5.NewUDPHeader(uint16(n), 0, ToSocksAddr(addr)), b[:n]) - if err := dgram.Write(tun); err != nil { - rChan <- err - return - } - glog.V(LDEBUG).Infof("[udp-tun] %s <<< %s length: %d", tun.RemoteAddr(), dgram.Header.Addr, len(dgram.Data)) - } - } - }() - - go func() { - for { - dgram, err := gosocks5.ReadUDPDatagram(tun) - if err != nil { - wChan <- err - return - } - - if client { // pipe from tunnel to relay - if clientAddr == nil { - continue - } - dgram.Header.Rsv = 0 - - buf := bytes.Buffer{} - dgram.Write(&buf) - if _, err := conn.WriteToUDP(buf.Bytes(), clientAddr); err != nil { - wChan <- err - return - } - glog.V(LDEBUG).Infof("[udp-tun] %s <<< %s length: %d", conn.LocalAddr(), dgram.Header.Addr, len(dgram.Data)) - } else { // pipe from tunnel to peer - addr, err := net.ResolveUDPAddr("udp", dgram.Header.Addr.String()) - if err != nil { - continue // drop silently - } - if _, err := conn.WriteToUDP(dgram.Data, addr); err != nil { - wChan <- err - return - } - glog.V(LDEBUG).Infof("[udp-tun] %s >>> %s length: %d", tun.RemoteAddr(), addr, len(dgram.Data)) - } - } - }() - - select { - case err = <-wChan: - //log.Println("w exit", err) - case err = <-rChan: - //log.Println("r exit", err) - } - - return -} diff --git a/cmd/gost/util.go b/cmd/gost/util.go deleted file mode 100644 index 023ca37..0000000 --- a/cmd/gost/util.go +++ /dev/null @@ -1,193 +0,0 @@ -package main - -import ( - "crypto/tls" - "errors" - "fmt" - "github.com/golang/glog" - "io" - "net" - "net/url" - "strings" - "time" -) - -const ( - keepAliveTime = 180 * time.Second -) - -type strSlice []string - -func (ss *strSlice) String() string { - return fmt.Sprintf("%s", *ss) -} -func (ss *strSlice) Set(value string) error { - *ss = append(*ss, value) - return nil -} - -// admin:123456@localhost:8080 -type Args struct { - Addr string // host:port - Protocol string // protocol: http/http2/socks5/ss - Transport string // transport: ws/wss/tls/tcp/udp/rtcp/rudp - Remote string // remote address, used by tcp/udp port forwarding - User *url.Userinfo // authentication for proxy - Cert tls.Certificate // tls certificate -} - -func (args Args) String() string { - var authUser, authPass string - if args.User != nil { - authUser = args.User.Username() - authPass, _ = args.User.Password() - } - return fmt.Sprintf("host: %s, protocol: %s, transport: %s, remote: %s, auth: %s/%s", - args.Addr, args.Protocol, args.Transport, args.Remote, authUser, authPass) -} - -func parseArgs(ss []string) (args []Args) { - for _, s := range ss { - if !strings.Contains(s, "://") { - s = "auto://" + s - } - u, err := url.Parse(s) - if err != nil { - glog.V(LWARNING).Infoln(err) - continue - } - - arg := Args{ - Addr: u.Host, - User: u.User, - Cert: tlsCert, - } - - schemes := strings.Split(u.Scheme, "+") - if len(schemes) == 1 { - arg.Protocol = schemes[0] - arg.Transport = schemes[0] - } - if len(schemes) == 2 { - arg.Protocol = schemes[0] - arg.Transport = schemes[1] - } - - switch arg.Transport { - case "ws", "wss", "tls": - case "https": - arg.Protocol = "http" - arg.Transport = "tls" - case "http2": - arg.Protocol = "http2" - case "tcp", "udp": // started from v2.1, tcp and udp are for local port forwarding - arg.Remote = strings.Trim(u.EscapedPath(), "/") - case "rtcp", "rudp": // started from v2.1, rtcp and rudp are for remote port forwarding - arg.Remote = strings.Trim(u.EscapedPath(), "/") - default: - arg.Transport = "" - } - - switch arg.Protocol { - case "http", "http2", "socks", "socks5", "ss": - default: - arg.Protocol = "" - } - - args = append(args, arg) - } - - return -} - -func processForwardChain(chain ...Args) { - glog.V(LINFO).Infoln(chain) - if len(chain) == 0 { - return - } - length := len(chain) - c, last := chain[:length-1], chain[length-1] - - // http2 restrict: only last proxy can enable http2 - for i, _ := range c { - if c[i].Transport == "http2" { - c[i].Transport = "" - } - } - if last.Transport == "http2" { - initHttp2Client(last.Addr, c...) - } -} - -// Based on io.Copy, but the io.ErrShortWrite is ignored (mainly for websocket) -func Copy(dst io.Writer, src io.Reader) (written int64, err error) { - // b := make([]byte, 32*1024) - b := tcpPool.Get().([]byte) - defer tcpPool.Put(b) - - for { - nr, er := src.Read(b) - //log.Println("cp r", nr, er) - if nr > 0 { - nw, ew := dst.Write(b[:nr]) - //log.Println("cp w", nw, ew) - if nw > 0 { - written += int64(nw) - } - if ew != nil { - err = ew - break - } - /* - if nr != nw { - err = io.ErrShortWrite - break - } - */ - } - if er == io.EOF { - break - } - if er != nil { - err = er - break - } - } - return -} - -func Pipe(src io.Reader, dst io.Writer, ch chan<- error) { - _, err := Copy(dst, src) - ch <- err -} - -func Transport(conn, conn2 net.Conn) (err error) { - rChan := make(chan error, 1) - wChan := make(chan error, 1) - - go Pipe(conn, conn2, wChan) - go Pipe(conn2, conn, rChan) - - select { - case err = <-wChan: - //log.Println("w exit", err) - case err = <-rChan: - //log.Println("r exit", err) - } - - return -} - -func setKeepAlive(conn net.Conn, d time.Duration) error { - c, ok := conn.(*net.TCPConn) - if !ok { - return errors.New("Not a TCP connection") - } - if err := c.SetKeepAlive(true); err != nil { - return err - } - if err := c.SetKeepAlivePeriod(d); err != nil { - return err - } - return nil -} diff --git a/cmd/gost/ws.go b/cmd/gost/ws.go deleted file mode 100644 index e549723..0000000 --- a/cmd/gost/ws.go +++ /dev/null @@ -1,141 +0,0 @@ -package main - -import ( - //"github.com/ginuerzh/gosocks5" - "crypto/tls" - "github.com/golang/glog" - "github.com/gorilla/websocket" - "net" - "net/http" - "net/http/httputil" - "net/url" - "time" -) - -type wsConn struct { - conn *websocket.Conn - rb []byte -} - -func wsClient(scheme string, conn net.Conn, host string) (*wsConn, error) { - dialer := websocket.Dialer{ - ReadBufferSize: 4096, - WriteBufferSize: 4096, - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - HandshakeTimeout: time.Second * 90, - NetDial: func(net, addr string) (net.Conn, error) { - return conn, nil - }, - } - u := url.URL{Scheme: scheme, Host: host, Path: "/ws"} - c, resp, err := dialer.Dial(u.String(), nil) - if err != nil { - return nil, err - } - resp.Body.Close() - - return &wsConn{conn: c}, nil -} - -func wsServer(conn *websocket.Conn) *wsConn { - return &wsConn{ - conn: conn, - } -} - -func (c *wsConn) Read(b []byte) (n int, err error) { - if len(c.rb) == 0 { - _, c.rb, err = c.conn.ReadMessage() - } - n = copy(b, c.rb) - c.rb = c.rb[n:] - - //log.Println("ws r:", n) - - return -} - -func (c *wsConn) Write(b []byte) (n int, err error) { - err = c.conn.WriteMessage(websocket.BinaryMessage, b) - n = len(b) - //log.Println("ws w:", n) - - return -} - -func (c *wsConn) Close() error { - return c.conn.Close() -} - -func (c *wsConn) LocalAddr() net.Addr { - return c.conn.LocalAddr() -} - -func (c *wsConn) RemoteAddr() net.Addr { - return c.conn.RemoteAddr() -} - -func (conn *wsConn) SetDeadline(t time.Time) error { - if err := conn.SetReadDeadline(t); err != nil { - return err - } - return conn.SetWriteDeadline(t) -} -func (c *wsConn) SetReadDeadline(t time.Time) error { - return c.conn.SetReadDeadline(t) -} - -func (c *wsConn) SetWriteDeadline(t time.Time) error { - return c.conn.SetWriteDeadline(t) -} - -type ws struct { - upgrader websocket.Upgrader - arg Args -} - -func NewWs(arg Args) *ws { - return &ws{ - arg: arg, - upgrader: websocket.Upgrader{ - ReadBufferSize: 1024, - WriteBufferSize: 1024, - CheckOrigin: func(r *http.Request) bool { return true }, - }, - } -} - -func (s *ws) handle(w http.ResponseWriter, r *http.Request) { - glog.V(LINFO).Infof("[ws] %s - %s", r.RemoteAddr, s.arg.Addr) - if glog.V(LDEBUG) { - dump, err := httputil.DumpRequest(r, false) - if err != nil { - glog.V(LWARNING).Infof("[ws] %s - %s : %s", r.RemoteAddr, s.arg.Addr, err) - } else { - glog.V(LDEBUG).Infof("[ws] %s - %s\n%s", r.RemoteAddr, s.arg.Addr, string(dump)) - } - } - conn, err := s.upgrader.Upgrade(w, r, nil) - if err != nil { - glog.V(LERROR).Infoln(err) - return - } - handleConn(wsServer(conn), s.arg) -} - -func (s *ws) ListenAndServe() error { - sm := http.NewServeMux() - sm.HandleFunc("/ws", s.handle) - return http.ListenAndServe(s.arg.Addr, sm) -} - -func (s *ws) listenAndServeTLS() error { - sm := http.NewServeMux() - sm.HandleFunc("/ws", s.handle) - server := &http.Server{ - Addr: s.arg.Addr, - TLSConfig: &tls.Config{Certificates: []tls.Certificate{s.arg.Cert}}, - Handler: sm, - } - return server.ListenAndServeTLS("", "") -} diff --git a/conn.go b/conn.go index 76eacde..83ed375 100644 --- a/conn.go +++ b/conn.go @@ -73,13 +73,15 @@ func (c *ProxyConn) handshake() error { return err } c.conn = conn - case "tls", "http2": // tls connection + case "tls": // tls connection tlsUsed = true cfg := &tls.Config{ InsecureSkipVerify: c.Node.insecureSkipVerify(), ServerName: c.Node.serverName, } c.conn = tls.Client(c.conn, cfg) + case "http2": + tlsUsed = true default: } @@ -115,9 +117,9 @@ func (c *ProxyConn) handshake() error { if err != nil { return err } - c.conn = shadowsocks.NewConn(c.conn, cipher) + c.conn = &shadowConn{conn: shadowsocks.NewConn(c.conn, cipher)} } - case "http", "http2": + case "http": fallthrough default: } @@ -150,7 +152,7 @@ func (c *ProxyConn) Connect(addr string) error { return err } - glog.V(LDEBUG).Infoln(req) + glog.V(LDEBUG).Infoln("[ss]", req) case "socks", "socks5": host, port, err := net.SplitHostPort(addr) if err != nil { @@ -165,17 +167,17 @@ func (c *ProxyConn) Connect(addr string) error { if err := req.Write(c); err != nil { return err } - glog.V(LDEBUG).Infoln(req) + glog.V(LDEBUG).Infoln("[socks5]", req) - rep, err := gosocks5.ReadReply(c) + reply, err := gosocks5.ReadReply(c) if err != nil { return err } - glog.V(LDEBUG).Infoln(rep) - if rep.Rep != gosocks5.Succeeded { + glog.V(LDEBUG).Infoln("[socks5]", reply) + if reply.Rep != gosocks5.Succeeded { return errors.New("Service unavailable") } - case "http", "http2": + case "http": fallthrough default: req := &http.Request{ diff --git a/cmd/gost/forward.go b/forward.go similarity index 50% rename from cmd/gost/forward.go rename to forward.go index a9af8f8..7a001cc 100644 --- a/cmd/gost/forward.go +++ b/forward.go @@ -1,4 +1,4 @@ -package main +package gost import ( "errors" @@ -9,23 +9,139 @@ import ( "time" ) -func handleTcpForward(conn net.Conn, raddr net.Addr) { +type TcpForwardServer struct { + Base *ProxyServer + Handler func(conn net.Conn, raddr net.Addr) +} + +func NewTcpForwardServer(base *ProxyServer) *TcpForwardServer { + return &TcpForwardServer{Base: base} +} + +func (s *TcpForwardServer) ListenAndServe() error { + raddr, err := net.ResolveTCPAddr("tcp", s.Base.Node.Remote) + if err != nil { + return err + } + + ln, err := net.Listen("tcp", s.Base.Node.Addr) + if err != nil { + return err + } + defer ln.Close() + + if s.Handler == nil { + s.Handler = s.handleTcpForward + } + + for { + conn, err := ln.Accept() + if err != nil { + glog.V(LWARNING).Infoln(err) + continue + } + setKeepAlive(conn, KeepAliveTime) + + go s.Handler(conn, raddr) + } +} + +func (s *TcpForwardServer) handleTcpForward(conn net.Conn, raddr net.Addr) { defer conn.Close() glog.V(LINFO).Infof("[tcp] %s - %s", conn.RemoteAddr(), raddr) - c, err := connect(raddr.String(), "", forwardArgs...) + cc, err := s.Base.Chain.Dial(raddr.String()) if err != nil { glog.V(LWARNING).Infof("[tcp] %s -> %s : %s", conn.RemoteAddr(), raddr, err) return } - defer c.Close() + defer cc.Close() glog.V(LINFO).Infof("[tcp] %s <-> %s", conn.RemoteAddr(), raddr) - Transport(conn, c) + s.Base.transport(conn, cc) glog.V(LINFO).Infof("[tcp] %s >-< %s", conn.RemoteAddr(), raddr) } -func handleUdpForwardLocal(conn *net.UDPConn, laddr, raddr *net.UDPAddr, data []byte) { +type UdpForwardServer struct { + Base *ProxyServer +} + +func NewUdpForwardServer(base *ProxyServer) *UdpForwardServer { + return &UdpForwardServer{Base: base} +} + +func (s *UdpForwardServer) ListenAndServe() error { + laddr, err := net.ResolveUDPAddr("udp", s.Base.Node.Addr) + if err != nil { + return err + } + + raddr, err := net.ResolveUDPAddr("udp", s.Base.Node.Remote) + if err != nil { + return err + } + + conn, err := net.ListenUDP("udp", laddr) + if err != nil { + glog.V(LWARNING).Infof("[udp] %s -> %s : %s", laddr, raddr, err) + return err + } + defer conn.Close() + + if len(s.Base.Chain.nodes) == 0 { + for { + b := make([]byte, MediumBufferSize) + n, addr, err := conn.ReadFromUDP(b) + if err != nil { + glog.V(LWARNING).Infof("[udp] %s -> %s : %s", laddr, raddr, err) + continue + } + go func() { + s.handleUdpForwardLocal(conn, addr, raddr, b[:n]) + }() + } + } + + rChan, wChan := make(chan *gosocks5.UDPDatagram, 32), make(chan *gosocks5.UDPDatagram, 32) + + go func() { + for { + b := make([]byte, MediumBufferSize) + n, addr, err := conn.ReadFromUDP(b) + if err != nil { + glog.V(LWARNING).Infof("[udp] %s -> %s : %s", laddr, raddr, err) + return + } + + select { + case rChan <- gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(uint16(n), 0, ToSocksAddr(addr)), b[:n]): + default: + // glog.V(LWARNING).Infof("[udp-connect] %s -> %s : rbuf is full", laddr, raddr) + } + } + }() + + go func() { + for { + dgram := <-wChan + addr, err := net.ResolveUDPAddr("udp", dgram.Header.Addr.String()) + if err != nil { + glog.V(LWARNING).Infof("[udp] %s <- %s : %s", laddr, raddr, err) + continue // drop silently + } + if _, err = conn.WriteToUDP(dgram.Data, addr); err != nil { + glog.V(LWARNING).Infof("[udp] %s <- %s : %s", laddr, raddr, err) + return + } + } + }() + + for { + s.handleUdpForwardTunnel(laddr, raddr, rChan, wChan) + } +} + +func (s *UdpForwardServer) handleUdpForwardLocal(conn *net.UDPConn, laddr, raddr *net.UDPAddr, data []byte) { lconn, err := net.ListenUDP("udp", nil) if err != nil { glog.V(LWARNING).Infof("[udp] %s -> %s : %s", laddr, raddr, err) @@ -39,9 +155,8 @@ func handleUdpForwardLocal(conn *net.UDPConn, laddr, raddr *net.UDPAddr, data [] } glog.V(LDEBUG).Infof("[udp] %s >>> %s length %d", laddr, raddr, len(data)) - b := udpPool.Get().([]byte) - defer udpPool.Put(b) - lconn.SetReadDeadline(time.Now().Add(time.Second * 60)) + b := make([]byte, MediumBufferSize) + lconn.SetReadDeadline(time.Now().Add(ReadTimeout)) n, addr, err := lconn.ReadFromUDP(b) if err != nil { glog.V(LWARNING).Infof("[udp] %s <- %s : %s", laddr, raddr, err) @@ -55,20 +170,85 @@ func handleUdpForwardLocal(conn *net.UDPConn, laddr, raddr *net.UDPAddr, data [] return } -func prepareUdpConnectTunnel(addr net.Addr) (net.Conn, error) { - conn, _, err := forwardChain(forwardArgs...) +func (s *UdpForwardServer) handleUdpForwardTunnel(laddr, raddr *net.UDPAddr, rChan, wChan chan *gosocks5.UDPDatagram) { + var cc net.Conn + var err error + retry := 0 + + for { + cc, err = s.prepareUdpConnectTunnel(raddr) + if err != nil { + glog.V(LWARNING).Infof("[udp] %s -> %s : %s", laddr, raddr, err) + time.Sleep((1 << uint(retry)) * time.Second) + if retry < 5 { + retry++ + } + continue + } + break + } + defer cc.Close() + + glog.V(LINFO).Infof("[udp] %s <-> %s", laddr, raddr) + + rExit := make(chan interface{}) + errc := make(chan error, 2) + + go func() { + for { + select { + case dgram := <-rChan: + if err := dgram.Write(cc); err != nil { + glog.V(LWARNING).Infof("[udp] %s -> %s : %s", laddr, raddr, err) + errc <- err + return + } + glog.V(LDEBUG).Infof("[udp-tun] %s >>> %s length: %d", laddr, raddr, len(dgram.Data)) + case <-rExit: + // glog.V(LDEBUG).Infof("[udp-connect] %s -> %s : exited", laddr, raddr) + return + } + } + }() + go func() { + for { + dgram, err := gosocks5.ReadUDPDatagram(cc) + if err != nil { + glog.V(LWARNING).Infof("[udp] %s <- %s : %s", laddr, raddr, err) + close(rExit) + errc <- err + return + } + + select { + case wChan <- dgram: + glog.V(LDEBUG).Infof("[udp-tun] %s <<< %s length: %d", laddr, raddr, len(dgram.Data)) + default: + } + } + }() + + select { + case <-errc: + //log.Println("w exit", err) + } + glog.V(LINFO).Infof("[udp] %s >-< %s", laddr, raddr) +} + +func (s *UdpForwardServer) prepareUdpConnectTunnel(addr net.Addr) (net.Conn, error) { + conn, err := s.Base.Chain.GetConn() if err != nil { return nil, err } - conn.SetWriteDeadline(time.Now().Add(time.Second * 90)) + conn.SetWriteDeadline(time.Now().Add(WriteTimeout)) if err = gosocks5.NewRequest(CmdUdpConnect, ToSocksAddr(addr)).Write(conn); err != nil { conn.Close() return nil, err } conn.SetWriteDeadline(time.Time{}) - conn.SetReadDeadline(time.Now().Add(90 * time.Second)) + conn.SetReadDeadline(time.Now().Add(ReadTimeout)) reply, err := gosocks5.ReadReply(conn) if err != nil { conn.Close() @@ -84,73 +264,49 @@ func prepareUdpConnectTunnel(addr net.Addr) (net.Conn, error) { return conn, nil } -func handleUdpForwardTunnel(laddr, raddr *net.UDPAddr, rChan, wChan chan *gosocks5.UDPDatagram) { - var tun net.Conn - var err error +type RTcpForwardServer struct { + Base *ProxyServer +} + +func NewRTcpForwardServer(base *ProxyServer) *RTcpForwardServer { + return &RTcpForwardServer{Base: base} +} + +func (s *RTcpForwardServer) Serve() error { + if len(s.Base.Chain.nodes) == 0 { + return errors.New("rtcp: at least one -F must be assigned") + } + + laddr, err := net.ResolveTCPAddr("tcp", s.Base.Node.Addr) + if err != nil { + return err + } + raddr, err := net.ResolveTCPAddr("tcp", s.Base.Node.Remote) + if err != nil { + return err + } + retry := 0 for { - tun, err = prepareUdpConnectTunnel(raddr) + conn, err := s.Base.Chain.GetConn() if err != nil { - glog.V(LWARNING).Infof("[udp] %s -> %s : %s", laddr, raddr, err) + glog.V(LWARNING).Infof("[rtcp] %s - %s : %s", laddr, raddr, err) time.Sleep((1 << uint(retry)) * time.Second) if retry < 5 { retry++ } continue } - break - } - defer tun.Close() + retry = 0 - glog.V(LINFO).Infof("[udp] %s <-> %s", laddr, raddr) - - rExit := make(chan interface{}) - rErr, wErr := make(chan error, 1), make(chan error, 1) - - go func() { - for { - select { - case dgram := <-rChan: - if err := dgram.Write(tun); err != nil { - glog.V(LWARNING).Infof("[udp] %s -> %s : %s", laddr, raddr, err) - rErr <- err - return - } - glog.V(LDEBUG).Infof("[udp-tun] %s >>> %s length: %d", laddr, raddr, len(dgram.Data)) - case <-rExit: - // glog.V(LDEBUG).Infof("[udp-connect] %s -> %s : exited", laddr, raddr) - return - } + if err := s.connectRTcpForward(conn, laddr, raddr); err != nil { + conn.Close() + time.Sleep(6 * time.Second) } - }() - go func() { - for { - dgram, err := gosocks5.ReadUDPDatagram(tun) - if err != nil { - glog.V(LWARNING).Infof("[udp] %s <- %s : %s", laddr, raddr, err) - close(rExit) - wErr <- err - return - } - - select { - case wChan <- dgram: - glog.V(LDEBUG).Infof("[udp-tun] %s <<< %s length: %d", laddr, raddr, len(dgram.Data)) - default: - } - } - }() - - select { - case <-rErr: - //log.Println("w exit", err) - case <-wErr: - //log.Println("r exit", err) } - glog.V(LINFO).Infof("[udp] %s >-< %s", laddr, raddr) } -func connectRTcpForward(conn net.Conn, laddr, raddr net.Addr) error { +func (s *RTcpForwardServer) connectRTcpForward(conn net.Conn, laddr, raddr net.Addr) error { glog.V(LINFO).Infof("[rtcp] %s - %s", laddr, raddr) req := gosocks5.NewRequest(gosocks5.CmdBind, ToSocksAddr(laddr)) @@ -160,7 +316,7 @@ func connectRTcpForward(conn net.Conn, laddr, raddr net.Addr) error { } // first reply, bind status - conn.SetReadDeadline(time.Now().Add(90 * time.Second)) + conn.SetReadDeadline(time.Now().Add(ReadTimeout)) rep, err := gosocks5.ReadReply(conn) if err != nil { glog.V(LWARNING).Infof("[rtcp] %s -> %s : %s", laddr, raddr, err) @@ -197,25 +353,67 @@ func connectRTcpForward(conn net.Conn, laddr, raddr net.Addr) error { defer lconn.Close() glog.V(LINFO).Infof("[rtcp] %s <-> %s", rep.Addr, lconn.RemoteAddr()) - Transport(lconn, conn) + s.Base.transport(lconn, conn) glog.V(LINFO).Infof("[rtcp] %s >-< %s", rep.Addr, lconn.RemoteAddr()) }() return nil } -func connectRUdpForward(conn net.Conn, laddr, raddr *net.UDPAddr) error { +type RUdpForwardServer struct { + Base *ProxyServer +} + +func NewRUdpForwardServer(base *ProxyServer) *RUdpForwardServer { + return &RUdpForwardServer{Base: base} +} + +func (s *RUdpForwardServer) Serve() error { + if len(s.Base.Chain.nodes) == 0 { + return errors.New("rudp: at least one -F must be assigned") + } + + laddr, err := net.ResolveUDPAddr("udp", s.Base.Node.Addr) + if err != nil { + return err + } + raddr, err := net.ResolveUDPAddr("udp", s.Base.Node.Remote) + if err != nil { + return err + } + + retry := 0 + for { + conn, err := s.Base.Chain.GetConn() + if err != nil { + glog.V(LWARNING).Infof("[rudp] %s - %s : %s", laddr, raddr, err) + time.Sleep((1 << uint(retry)) * time.Second) + if retry < 5 { + retry++ + } + continue + } + retry = 0 + + if err := s.connectRUdpForward(conn, laddr, raddr); err != nil { + conn.Close() + time.Sleep(6 * time.Second) + } + } +} + +func (s *RUdpForwardServer) connectRUdpForward(conn net.Conn, laddr, raddr *net.UDPAddr) error { glog.V(LINFO).Infof("[rudp] %s - %s", laddr, raddr) req := gosocks5.NewRequest(CmdUdpTun, ToSocksAddr(laddr)) - conn.SetWriteDeadline(time.Now().Add(time.Second * 90)) + conn.SetWriteDeadline(time.Now().Add(WriteTimeout)) if err := req.Write(conn); err != nil { glog.V(LWARNING).Infof("[rudp] %s -> %s : %s", laddr, raddr, err) return err } conn.SetWriteDeadline(time.Time{}) - conn.SetReadDeadline(time.Now().Add(90 * time.Second)) + conn.SetReadDeadline(time.Now().Add(ReadTimeout)) rep, err := gosocks5.ReadReply(conn) if err != nil { glog.V(LWARNING).Infof("[rudp] %s <- %s : %s", laddr, raddr, err) @@ -225,7 +423,7 @@ func connectRUdpForward(conn net.Conn, laddr, raddr *net.UDPAddr) error { if rep.Rep != gosocks5.Succeeded { glog.V(LWARNING).Infof("[rudp] %s <- %s : bind on %s failure", laddr, raddr, laddr) - return errors.New(fmt.Sprintf("Bind on %s failure", laddr)) + return errors.New(fmt.Sprintf("bind on %s failure", laddr)) } glog.V(LINFO).Infof("[rudp] %s - %s BIND ON %s OK", laddr, raddr, rep.Addr) @@ -238,8 +436,7 @@ func connectRUdpForward(conn net.Conn, laddr, raddr *net.UDPAddr) error { } go func() { - b := udpPool.Get().([]byte) - defer udpPool.Put(b) + b := make([]byte, MediumBufferSize) relay, err := net.DialUDP("udp", nil, raddr) if err != nil { @@ -254,7 +451,7 @@ func connectRUdpForward(conn net.Conn, laddr, raddr *net.UDPAddr) error { } glog.V(LDEBUG).Infof("[rudp] %s >>> %s length: %d", laddr, raddr, len(dgram.Data)) - relay.SetReadDeadline(time.Now().Add(time.Second * 60)) + relay.SetReadDeadline(time.Now().Add(ReadTimeout)) n, err := relay.Read(b) if err != nil { glog.V(LWARNING).Infof("[rudp] %s <- %s : %s", laddr, raddr, err) @@ -264,7 +461,7 @@ func connectRUdpForward(conn net.Conn, laddr, raddr *net.UDPAddr) error { glog.V(LDEBUG).Infof("[rudp] %s <<< %s length: %d", laddr, raddr, n) - conn.SetWriteDeadline(time.Now().Add(time.Second * 90)) + conn.SetWriteDeadline(time.Now().Add(WriteTimeout)) if err := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(uint16(n), 0, dgram.Header.Addr), b[:n]).Write(conn); err != nil { glog.V(LWARNING).Infof("[rudp] %s <- %s : %s", laddr, raddr, err) return @@ -272,5 +469,4 @@ func connectRUdpForward(conn net.Conn, laddr, raddr *net.UDPAddr) error { conn.SetWriteDeadline(time.Time{}) }() } - } diff --git a/gost.go b/gost.go index 664176f..2b5e557 100644 --- a/gost.go +++ b/gost.go @@ -33,7 +33,7 @@ var ( var ( SmallBufferSize = 1 * 1024 // 1KB small buffer MediumBufferSize = 8 * 1024 // 8KB medium buffer - LargeBufferSize = 16 * 1024 // 16KB large buffer + LargeBufferSize = 32 * 1024 // 32KB large buffer ) var ( diff --git a/http.go b/http.go index efd71ff..c7b3989 100644 --- a/http.go +++ b/http.go @@ -148,6 +148,7 @@ func (s *Http2Server) HandleRequest(w http.ResponseWriter, req *http.Request) { glog.V(LINFO).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err) return } + glog.V(LINFO).Infof("[http2] %s - %s : switch to HTTP2 transport mode OK", req.RemoteAddr, target) s.Base.handleConn(conn) return } @@ -257,13 +258,18 @@ func (s *Http2Server) Upgrade(w http.ResponseWriter, r *http.Request) (net.Conn, fw.Flush() } - return &http2Conn{r: r.Body, w: flushWriter{w}}, nil + conn := &http2Conn{r: r.Body, w: flushWriter{w}} + conn.remoteAddr, _ = net.ResolveTCPAddr("tcp", r.RemoteAddr) + conn.localAddr, _ = net.ResolveTCPAddr("tcp", r.Host) + return conn, nil } // HTTP2 client connection, wrapped up just like a net.Conn type http2Conn struct { - r io.Reader - w io.Writer + r io.Reader + w io.Writer + remoteAddr net.Addr + localAddr net.Addr } func (c *http2Conn) Read(b []byte) (n int, err error) { @@ -282,23 +288,23 @@ func (c *http2Conn) Close() error { } func (c *http2Conn) LocalAddr() net.Addr { - return nil + return c.localAddr } func (c *http2Conn) RemoteAddr() net.Addr { - return nil + return c.remoteAddr } func (c *http2Conn) SetDeadline(t time.Time) error { - return nil + return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")} } func (c *http2Conn) SetReadDeadline(t time.Time) error { - return nil + return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")} } func (c *http2Conn) SetWriteDeadline(t time.Time) error { - return nil + return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")} } type flushWriter struct { diff --git a/node.go b/node.go index 14c70dd..35f224e 100644 --- a/node.go +++ b/node.go @@ -56,12 +56,10 @@ func ParseProxyNode(s string) (node ProxyNode, err error) { } switch node.Transport { - case "ws", "wss", "tls": + case "ws", "wss", "tls", "http2": case "https": node.Protocol = "http" node.Transport = "tls" - case "http2": - node.Protocol = "http2" case "tcp", "udp": // started from v2.1, tcp and udp are for local port forwarding node.Remote = strings.Trim(u.EscapedPath(), "/") case "rtcp", "rudp": // started from v2.1, rtcp and rudp are for remote port forwarding @@ -71,7 +69,7 @@ func ParseProxyNode(s string) (node ProxyNode, err error) { } switch node.Protocol { - case "http", "socks", "socks5", "ss", "http2": + case "http", "socks", "socks5", "ss": default: node.Protocol = "" } @@ -84,19 +82,21 @@ func (node *ProxyNode) Get(key string) string { return node.values.Get(key) } +func (node *ProxyNode) getBool(key string) bool { + s := node.Get(key) + if b, _ := strconv.ParseBool(s); b { + return b + } + n, _ := strconv.Atoi(s) + return n > 0 +} + func (node *ProxyNode) Set(key, value string) { node.values.Set(key, value) } func (node *ProxyNode) insecureSkipVerify() bool { - s := node.Get("secure") - if secure, _ := strconv.ParseBool(s); secure { - return !secure - } - if n, _ := strconv.Atoi(s); n > 0 { - return false - } - return true + return !node.getBool("secure") } func (node *ProxyNode) certFile() string { diff --git a/server.go b/server.go index d4e5fa4..dd2e727 100644 --- a/server.go +++ b/server.go @@ -5,6 +5,7 @@ import ( "crypto/tls" "github.com/ginuerzh/gosocks5" "github.com/golang/glog" + ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" "io" "net" "net/http" @@ -15,6 +16,7 @@ type ProxyServer struct { Chain *ProxyChain TLSConfig *tls.Config selector *serverSelector + cipher *ss.Cipher } func NewProxyServer(node ProxyNode, chain *ProxyChain, config *tls.Config) *ProxyServer { @@ -24,6 +26,17 @@ func NewProxyServer(node ProxyNode, chain *ProxyChain, config *tls.Config) *Prox if config == nil { config = &tls.Config{} } + + var cipher *ss.Cipher + if node.Protocol == "ss" && node.User != nil { + var err error + method := node.User.Username() + password, _ := node.User.Password() + cipher, err = ss.NewCipher(method, password) + if err != nil { + glog.Fatal(err) + } + } return &ProxyServer{ Node: node, Chain: chain, @@ -39,6 +52,7 @@ func NewProxyServer(node ProxyNode, chain *ProxyChain, config *tls.Config) *Prox user: node.User, tlsConfig: config, }, + cipher: cipher, } } @@ -59,13 +73,13 @@ func (s *ProxyServer) Serve() error { server.Handler = http.HandlerFunc(server.HandleRequest) return server.ListenAndServeTLS(s.TLSConfig) case "tcp": // Local TCP port forwarding - // return listenAndServeTcpForward(arg) + return NewTcpForwardServer(s).ListenAndServe() case "udp": // Local UDP port forwarding - // return listenAndServeUdpForward(arg) + return NewUdpForwardServer(s).ListenAndServe() case "rtcp": // Remote TCP port forwarding - // return serveRTcpForward(arg) + return NewRTcpForwardServer(s).Serve() case "rudp": // Remote UDP port forwarding - // return serveRUdpForward(arg) + return NewRUdpForwardServer(s).Serve() default: ln, err = net.Listen("tcp", node.Addr) } @@ -94,7 +108,9 @@ func (s *ProxyServer) handleConn(conn net.Conn) { switch s.Node.Protocol { case "ss": // shadowsocks - NewShadowServer(conn, s).Serve() + server := NewShadowServer(ss.NewConn(conn, s.cipher.Copy()), s) + server.OTA = s.Node.getBool("ota") + server.Serve() return case "http": req, err := http.ReadRequest(bufio.NewReader(conn)) @@ -166,7 +182,7 @@ func (s *ProxyServer) handleConn(conn net.Conn) { NewHttpServer(conn, s).HandleRequest(req) } -func (s *ProxyServer) transport(conn1, conn2 net.Conn) (err error) { +func (_ *ProxyServer) transport(conn1, conn2 net.Conn) (err error) { errc := make(chan error, 2) go func() { diff --git a/socks.go b/socks.go index a476f1b..038e9d0 100644 --- a/socks.go +++ b/socks.go @@ -467,6 +467,7 @@ func (s *Socks5Server) bind(addr string) (net.Conn, error) { pconn = c lnChan = nil ln = nil + // TODO: implement deadline s.conn.SetReadDeadline(time.Now()) // timeout right now ,so we can break out of blocking case err := <-peerChan: if err != nil || pconn == nil { diff --git a/ss.go b/ss.go index 7c4d786..2d3abe1 100644 --- a/ss.go +++ b/ss.go @@ -1,134 +1,237 @@ package gost import ( + "bytes" "encoding/binary" + "errors" "fmt" - "github.com/ginuerzh/gosocks5" "github.com/golang/glog" - "github.com/shadowsocks/shadowsocks-go/shadowsocks" + ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" "io" "net" + "strconv" + "time" +) + +const ( + idType = 0 // address type index + idIP0 = 1 // ip addres start index + idDmLen = 1 // domain address length index + idDm0 = 2 // domain address start index + + typeIPv4 = 1 // type is ipv4 address + typeDm = 3 // type is domain address + typeIPv6 = 4 // type is ipv6 address + + lenIPv4 = net.IPv4len + 2 // ipv4 + 2port + lenIPv6 = net.IPv6len + 2 // ipv6 + 2port + lenDmBase = 2 // 1addrLen + 2port, plus addrLen + lenHmacSha1 = 10 ) type ShadowServer struct { - conn net.Conn + conn *ss.Conn Base *ProxyServer + OTA bool // one time auth } -func NewShadowServer(conn net.Conn, base *ProxyServer) *ShadowServer { +func NewShadowServer(conn *ss.Conn, base *ProxyServer) *ShadowServer { return &ShadowServer{conn: conn, Base: base} } func (s *ShadowServer) Serve() { - glog.V(LINFO).Infof("[ss] %s -> %s", s.conn.RemoteAddr(), s.conn.LocalAddr()) + glog.V(LINFO).Infof("[ss] %s - %s", s.conn.RemoteAddr(), s.conn.LocalAddr()) - var conn net.Conn - - if s.Base.Node.User != nil { - method := s.Base.Node.User.Username() - password, _ := s.Base.Node.User.Password() - cipher, err := shadowsocks.NewCipher(method, password) - if err != nil { - glog.V(LWARNING).Infof("[ss] %s - %s : %s", s.conn.RemoteAddr(), s.conn.LocalAddr(), err) - return - } - conn = shadowsocks.NewConn(s.conn, cipher) - } - - addr, extra, err := getShadowRequest(conn) + addr, ota, err := s.getRequest() if err != nil { - glog.V(LWARNING).Infof("[ss] %s - %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) + glog.V(LWARNING).Infof("[ss] %s - %s : %s", s.conn.RemoteAddr(), s.conn.LocalAddr(), err) return } - glog.V(LINFO).Infof("[ss] %s -> %s", conn.RemoteAddr(), addr.String()) + glog.V(LINFO).Infof("[ss] %s -> %s, ota: %v", s.conn.RemoteAddr(), addr, ota) - cc, err := s.Base.Chain.Dial(addr.String()) + cc, err := s.Base.Chain.Dial(addr) if err != nil { - glog.V(LWARNING).Infof("[ss] %s -> %s : %s", conn.RemoteAddr(), addr.String(), err) + glog.V(LWARNING).Infof("[ss] %s -> %s : %s", s.conn.RemoteAddr(), addr, err) return } defer cc.Close() - if extra != nil { - if _, err := cc.Write(extra); err != nil { - glog.V(LWARNING).Infof("[ss] %s - %s : %s", conn.RemoteAddr(), addr.String(), err) - return - } + glog.V(LINFO).Infof("[ss] %s <-> %s", s.conn.RemoteAddr(), addr) + if ota { + s.transportOTA(s.conn, cc) + } else { + s.Base.transport(&shadowConn{conn: s.conn}, cc) } - - glog.V(LINFO).Infof("[ss] %s <-> %s", conn.RemoteAddr(), addr.String()) - s.Base.transport(conn, cc) - glog.V(LINFO).Infof("[ss] %s >-< %s", conn.RemoteAddr(), addr.String()) + glog.V(LINFO).Infof("[ss] %s >-< %s", s.conn.RemoteAddr(), addr) } -func getShadowRequest(conn net.Conn) (addr *gosocks5.Addr, extra []byte, err error) { - const ( - idType = 0 // address type index - idIP0 = 1 // ip addres start index - idDmLen = 1 // domain address length index - idDm0 = 2 // domain address start index - - typeIPv4 = 1 // type is ipv4 address - typeDm = 3 // type is domain address - typeIPv6 = 4 // type is ipv6 address - - lenIPv4 = 1 + net.IPv4len + 2 // 1addrType + ipv4 + 2port - lenIPv6 = 1 + net.IPv6len + 2 // 1addrType + ipv6 + 2port - lenDmBase = 1 + 1 + 2 // 1addrType + 1addrLen + 2port, plus addrLen - ) - +// This func are copied from shadowsocks library with some modification. +func (s *ShadowServer) getRequest() (host string, ota bool, err error) { // buf size should at least have the same size with the largest possible // request size (when addrType is 3, domain name has at most 256 bytes) // 1(addrType) + 1(lenByte) + 256(max length address) + 2(port) buf := make([]byte, SmallBufferSize) - var n int // read till we get possible domain length field - //shadowsocks.SetReadTimeout(conn) - if n, err = io.ReadAtLeast(conn, buf, idDmLen+1); err != nil { + s.conn.SetReadDeadline(time.Now().Add(ReadTimeout)) + if _, err = io.ReadFull(s.conn, buf[:idType+1]); err != nil { return } - addr = &gosocks5.Addr{ - Type: buf[idType], - } - - reqLen := -1 - switch buf[idType] { + var reqStart, reqEnd int + addrType := buf[idType] + switch addrType & ss.AddrMask { case typeIPv4: - reqLen = lenIPv4 + reqStart, reqEnd = idIP0, idIP0+lenIPv4 case typeIPv6: - reqLen = lenIPv6 + reqStart, reqEnd = idIP0, idIP0+lenIPv6 case typeDm: - reqLen = int(buf[idDmLen]) + lenDmBase - default: - err = fmt.Errorf("addr type %d not supported", buf[idType]) - return - } - - if n < reqLen { // rare case - //ss.SetReadTimeout(conn) - if _, err = io.ReadFull(conn, buf[n:reqLen]); err != nil { + if _, err = io.ReadFull(s.conn, buf[idType+1:idDmLen+1]); err != nil { return } - } else if n > reqLen { - // it's possible to read more than just the request head - extra = buf[reqLen:n] + reqStart, reqEnd = idDm0, int(idDm0+buf[idDmLen]+lenDmBase) + default: + err = fmt.Errorf("addr type %d not supported", addrType&ss.AddrMask) + return + } + + if _, err = io.ReadFull(s.conn, buf[reqStart:reqEnd]); err != nil { + return } // Return string for typeIP is not most efficient, but browsers (Chrome, // Safari, Firefox) all seems using typeDm exclusively. So this is not a // big problem. - switch buf[idType] { + switch addrType & ss.AddrMask { case typeIPv4: - addr.Host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String() + host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String() case typeIPv6: - addr.Host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String() + host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String() case typeDm: - addr.Host = string(buf[idDm0 : idDm0+buf[idDmLen]]) + host = string(buf[idDm0 : idDm0+buf[idDmLen]]) } // parse port - addr.Port = binary.BigEndian.Uint16(buf[reqLen-2 : reqLen]) + port := binary.BigEndian.Uint16(buf[reqEnd-2 : reqEnd]) + host = net.JoinHostPort(host, strconv.Itoa(int(port))) + // if specified one time auth enabled, we should verify this + if s.OTA || addrType&ss.OneTimeAuthMask > 0 { + ota = true + if _, err = io.ReadFull(s.conn, buf[reqEnd:reqEnd+lenHmacSha1]); err != nil { + return + } + iv := s.conn.GetIv() + key := s.conn.GetKey() + actualHmacSha1Buf := ss.HmacSha1(append(iv, key...), buf[:reqEnd]) + if !bytes.Equal(buf[reqEnd:reqEnd+lenHmacSha1], actualHmacSha1Buf) { + err = fmt.Errorf("verify one time auth failed, iv=%v key=%v data=%v", iv, key, buf[:reqEnd]) + return + } + } + return +} + +const ( + dataLenLen = 2 + hmacSha1Len = 10 + idxData0 = dataLenLen + hmacSha1Len +) + +// copyOta copies data from src to dst with ota verification. +// +// This func are copied from shadowsocks library with some modification. +func (s *ShadowServer) copyOta(dst net.Conn, src *ss.Conn) (int64, error) { + // sometimes it have to fill large block + buf := make([]byte, LargeBufferSize) + for { + src.SetReadDeadline(time.Now().Add(ReadTimeout)) + if n, err := io.ReadFull(src, buf[:dataLenLen+hmacSha1Len]); err != nil { + return int64(n), err + } + src.SetReadDeadline(time.Time{}) + + dataLen := binary.BigEndian.Uint16(buf[:dataLenLen]) + expectedHmacSha1 := buf[dataLenLen:idxData0] + + var dataBuf []byte + if len(buf) < int(idxData0+dataLen) { + dataBuf = make([]byte, dataLen) + } else { + dataBuf = buf[idxData0 : idxData0+dataLen] + } + if n, err := io.ReadFull(src, dataBuf); err != nil { + return int64(n), err + } + chunkIdBytes := make([]byte, 4) + chunkId := src.GetAndIncrChunkId() + binary.BigEndian.PutUint32(chunkIdBytes, chunkId) + actualHmacSha1 := ss.HmacSha1(append(src.GetIv(), chunkIdBytes...), dataBuf) + if !bytes.Equal(expectedHmacSha1, actualHmacSha1) { + return 0, errors.New("ota error: mismatch") + } + + if n, err := dst.Write(dataBuf); err != nil { + return int64(n), err + } + } +} + +func (s *ShadowServer) transportOTA(sc *ss.Conn, cc net.Conn) (err error) { + errc := make(chan error, 2) + + go func() { + _, err := io.Copy(&shadowConn{conn: sc}, cc) + errc <- err + }() + + go func() { + _, err := s.copyOta(cc, sc) + errc <- err + }() + + select { + case err = <-errc: + //glog.V(LWARNING).Infoln("transport exit", err) + } return } + +// Due to in/out byte length is inconsistent of the shadowsocks.Conn.Write, +// we wrap around it to make io.Copy happy +type shadowConn struct { + conn *ss.Conn +} + +func (c *shadowConn) Read(b []byte) (n int, err error) { + return c.conn.Read(b) +} + +func (c *shadowConn) Write(b []byte) (n int, err error) { + n = len(b) // force byte length consistent + _, err = c.conn.Write(b) + return +} + +func (c *shadowConn) Close() error { + return c.conn.Close() +} + +func (c *shadowConn) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} + +func (c *shadowConn) RemoteAddr() net.Addr { + return c.conn.RemoteAddr() +} + +func (c *shadowConn) SetDeadline(t time.Time) error { + return c.conn.SetDeadline(t) +} + +func (c *shadowConn) SetReadDeadline(t time.Time) error { + return c.conn.SetReadDeadline(t) +} + +func (c *shadowConn) SetWriteDeadline(t time.Time) error { + return c.conn.SetWriteDeadline(t) +}