From 03c85668435e4e90fa9d1d6b169a9269695391d8 Mon Sep 17 00:00:00 2001 From: ryan Date: Fri, 3 May 2019 10:27:54 +0800 Subject: [PATCH 1/6] Move case statement of ping-stop up --- cmd/shadowsocks-server/server.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index 28099981..3e906c0f 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -542,12 +542,12 @@ func managerDaemon(conn *net.UDPConn) { res = handleAddPort(bytes.Trim(data[4:], "\x00\r\n ")) case strings.HasPrefix(command, "remove:"): res = handleRemovePort(bytes.Trim(data[7:], "\x00\r\n ")) - case strings.HasPrefix(command, "ping"): - conn.WriteToUDP(handlePing(), remote) - reportconnSet[remote.String()] = remote // append the host into the report list case strings.HasPrefix(command, "ping-stop"): // add the stop ping command conn.WriteToUDP(handlePing(), remote) delete(reportconnSet, remote.String()) + case strings.HasPrefix(command, "ping"): + conn.WriteToUDP(handlePing(), remote) + reportconnSet[remote.String()] = remote // append the host into the report list } if len(res) == 0 { continue From f6898272b3fbe16344d706eafef7365216857c0d Mon Sep 17 00:00:00 2001 From: ryan Date: Fri, 3 May 2019 10:59:25 +0800 Subject: [PATCH 2/6] Support http get via socks proxy --- cmd/shadowsocks-httpget/httpget.go | 135 ++++++++++++++++++++++++++--- 1 file changed, 121 insertions(+), 14 deletions(-) diff --git a/cmd/shadowsocks-httpget/httpget.go b/cmd/shadowsocks-httpget/httpget.go index 2e54833d..a11c0bfa 100644 --- a/cmd/shadowsocks-httpget/httpget.go +++ b/cmd/shadowsocks-httpget/httpget.go @@ -1,6 +1,7 @@ package main import ( + "errors" "flag" "fmt" ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" @@ -57,7 +58,11 @@ func get(connid int, uri, serverAddr string, rawAddr []byte, cipher *ss.Cipher, }() tr := &http.Transport{ Dial: func(_, _ string) (net.Conn, error) { - return ss.DialWithRawAddr(rawAddr, serverAddr, cipher.Copy()) + if cipher != nil { + return ss.DialWithRawAddr(rawAddr, serverAddr, cipher.Copy()) + } + + return dialSocks5(string(rawAddr), serverAddr) }, } @@ -76,9 +81,94 @@ func get(connid int, uri, serverAddr string, rawAddr []byte, cipher *ss.Cipher, } } +func dialSocks5(targetAddr, proxy string) (conn net.Conn, err error) { + readAll := func(conn net.Conn) (resp []byte, err error) { + resp = make([]byte, 1024) + Timeout := 5 * time.Second + if err := conn.SetReadDeadline(time.Now().Add(Timeout)); err != nil { + return nil, err + } + n, err := conn.Read(resp) + resp = resp[:n] + return + } + sendReceive := func(conn net.Conn, req []byte) (resp []byte, err error) { + Timeout := 5 * time.Second + if err := conn.SetWriteDeadline(time.Now().Add(Timeout)); err != nil { + return nil, err + } + _, err = conn.Write(req) + if err != nil { + return + } + resp, err = readAll(conn) + return + } + + conn, err = net.Dial("tcp", proxy) + if err != nil { + return + } + + // version identifier/method selection request + req := []byte{ + 5, // version number + 1, // number of methods + 0, // method 0: no authentication (only anonymous access supported for now) + } + resp, err := sendReceive(conn, req) + if err != nil { + return + } else if len(resp) != 2 { + err = errors.New("server does not respond properly") + return + } else if resp[0] != 5 { + err = errors.New("server does not support Socks 5") + return + } else if resp[1] != 0 { // no auth + err = errors.New("socks method negotiation failed") + return + } + + // detail request + host, portStr, err := net.SplitHostPort(targetAddr) + if err != nil { + return nil, err + } + portInt, err := strconv.ParseUint(portStr, 10, 16) + if err != nil { + return nil, err + } + port := uint16(portInt) + + req = []byte{ + 5, // version number + 1, // connect command + 0, // reserved, must be zero + 3, // address type, 3 means domain name + byte(len(host)), // address length + } + req = append(req, []byte(host)...) + req = append(req, []byte{ + byte(port >> 8), // higher byte of destination port + byte(port), // lower byte of destination port (big endian) + }...) + resp, err = sendReceive(conn, req) + if err != nil { + return + } else if len(resp) != 10 { + err = errors.New("server does not respond properly") + } else if resp[1] != 0 { + err = errors.New("can't complete SOCKS5 connection") + } + + return +} + func main() { - flag.StringVar(&config.server, "s", "127.0.0.1", "server:port") - flag.IntVar(&config.port, "p", 0, "server:port") + server := flag.String("s", "127.0.0.1", "server:port") + proxy := flag.String("ss", "127.0.0.1", "proxy:port") + flag.IntVar(&config.port, "p", 0, "port") flag.IntVar(&config.core, "core", 1, "number of CPU cores to use") flag.StringVar(&config.passwd, "k", "", "password") flag.StringVar(&config.method, "m", "", "encryption method, use empty string or rc4") @@ -89,8 +179,17 @@ func main() { flag.Parse() - if config.server == "" || config.port == 0 || config.passwd == "" || len(flag.Args()) != 1 { - fmt.Printf("Usage: %s -s -p -k \n", os.Args[0]) + config.server = "127.0.0.1" + var connectProxy bool + if *server != config.server { + config.server = *server + } else { + config.server = *proxy + connectProxy = true + } + + if config.port == 0 || !connectProxy && config.passwd == "" || len(flag.Args()) != 1 { + fmt.Printf("Usage: %s -s[s] -p -k \n", os.Args[0]) os.Exit(1) } @@ -104,11 +203,6 @@ func main() { uri = "http://" + uri } - cipher, err := ss.NewCipher(config.method, config.passwd) - if err != nil { - fmt.Println("Error creating cipher:", err) - os.Exit(1) - } serverAddr := net.JoinHostPort(config.server, strconv.Itoa(config.port)) parsedURL, err := url.Parse(uri) @@ -122,10 +216,23 @@ func main() { } else { host = parsedURL.Host } - // fmt.Println(host) - rawAddr, err := ss.RawAddr(host) - if err != nil { - panic("Error getting raw address.") + + rawAddr := []byte(host) + var cipher *ss.Cipher + if !connectProxy { + if config.method == "" { + config.method = "aes-256-cfb" + } + + cipher, err = ss.NewCipher(config.method, config.passwd) + if err != nil { + fmt.Println("Error creating cipher:", err) + os.Exit(1) + } + rawAddr, err = ss.RawAddr(host) + if err != nil { + panic("Error getting raw address.") + } } done := make(chan []time.Duration) From 35674254497907aaa4c0e11fe41a815cecf2ef0a Mon Sep 17 00:00:00 2001 From: ryan Date: Fri, 3 May 2019 22:59:55 +0800 Subject: [PATCH 3/6] Add `ss-redir` into shadowsocks-local --- .gitignore | 1 + cmd/shadowsocks-local/guess.go | 138 +++++++++++++++++++++++++++++++++ cmd/shadowsocks-local/local.go | 128 ++++++++++++++++++++++++------ 3 files changed, 243 insertions(+), 24 deletions(-) create mode 100644 cmd/shadowsocks-local/guess.go diff --git a/.gitignore b/.gitignore index a53134e0..7eca66c3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ script/http bin .idea +.vscode/ diff --git a/cmd/shadowsocks-local/guess.go b/cmd/shadowsocks-local/guess.go new file mode 100644 index 00000000..df76c7f6 --- /dev/null +++ b/cmd/shadowsocks-local/guess.go @@ -0,0 +1,138 @@ +package main + +import ( + "strings" +) + +const ( + DefaultPortForHttp = 80 + DefaultPortForTls = 443 + + SERVER_NAME_LEN = 256 + TLS_HEADER_LEN = 5 + TLS_HANDSHAKE_CONTENT_TYPE = 0x16 + TLS_HANDSHAKE_TYPE_CLIENT_HELLO = 0x01 +) + +func parseHttpHeader(buf string) string { + for _, l := range strings.Split(buf, "\r\n") { + if strings.HasPrefix(l, "Host:") { + return strings.TrimSpace(l[5:]) + } + } + + return "" +} + +func parseTlsHeader(buf string) string { + slen := len(buf) + if slen < TLS_HEADER_LEN { + return "" + } + + if buf[0] != TLS_HANDSHAKE_CONTENT_TYPE { + return "" + } + + tlsVersionMajor, tlsVersionMinor := buf[1], buf[2] + if tlsVersionMajor < 3 { + return "" + } + + l := int(uint(buf[3])<<8 + uint(buf[4]) + TLS_HEADER_LEN) + if slen < l { + return "" + } + + buf = buf[:l] + slen = len(buf) + pos := TLS_HEADER_LEN + if slen < pos+1 { + return "" + } + + if buf[pos] != TLS_HANDSHAKE_TYPE_CLIENT_HELLO { + return "" + } + + /* Skip past fixed length records: + * 1 Handshake Type + * 3 Length + * 2 Version (again) + * 32 Random + * to Session ID Length + */ + pos += 38 + + if pos+1 > slen { + return "" + } + pos += int(1 + uint(buf[pos])) + + if pos+2 > slen { + return "" + } + pos += int(2 + uint(buf[pos])<<8 + uint(buf[pos+1])) + + if pos+1 > slen { + return "" + } + pos += int(1 + uint(buf[pos])) + + if pos == slen && tlsVersionMajor == 3 && tlsVersionMinor == 0 { + return "" + } + + if pos+2 > slen { + return "" + } + l = int(uint(buf[pos])<<8 + uint(buf[pos+1])) + pos += 2 + if pos+l > slen { + return "" + } + + return parseExtensions(buf[pos : pos+l]) +} + +func parseExtensions(buf string) string { + var pos, l int + slen := len(buf) + + for pos+4 <= slen { + l = int(uint(buf[pos+2])<<8 + uint(buf[pos+3])) + if buf[pos] == 0x00 && buf[pos+1] == 0x00 { + if pos+4+l > slen { + return "" + } + + return parseServerNameExtension(buf[pos+4 : pos+4+l]) + } + pos += 4 + l + } + + return "" +} + +func parseServerNameExtension(buf string) string { + var l int + slen := len(buf) + pos := 2 + + for pos+3 < slen { + l = int(uint(buf[pos+1])<<8 + uint(buf[pos+2])) + if pos+3+l > slen { + return "" + } + + switch buf[pos] { + case 0x00: + return buf[pos+3 : pos+3+l] + default: + } + pos += 3 + l + } + + return "" +} + diff --git a/cmd/shadowsocks-local/local.go b/cmd/shadowsocks-local/local.go index 604bf9f7..668e9a25 100644 --- a/cmd/shadowsocks-local/local.go +++ b/cmd/shadowsocks-local/local.go @@ -15,14 +15,17 @@ import ( "path" "strconv" "strings" + "syscall" "time" ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" ) -var debug ss.DebugLog +const SO_ORIGINAL_DST = 80 var ( + debug ss.DebugLog + errAddrType = errors.New("socks addr type not supported") errVer = errors.New("socks version not supported") errMethod = errors.New("socks only support 1 method now") @@ -246,6 +249,7 @@ func connectToServer(serverId int, rawaddr []byte, addr string) (remote *ss.Conn } return nil, err } + debug.Printf("connected to %s via %s\n", addr, se.server) servers.failCnt[serverId] = 0 return @@ -280,7 +284,7 @@ func createServerConn(rawaddr []byte, addr string) (remote *ss.Conn, err error) return nil, err } -func handleConnection(conn net.Conn) { +func handleConnection(conn net.Conn, redir bool) { if debug { debug.Printf("socks connect from %s\n", conn.RemoteAddr().String()) } @@ -291,23 +295,37 @@ func handleConnection(conn net.Conn) { } }() - var err error = nil - if err = handShake(conn); err != nil { - log.Println("socks handshake:", err) - return - } - rawaddr, addr, err := getRequest(conn) - if err != nil { - log.Println("error getting request:", err) - return - } - // Sending connection established message immediately to client. - // This some round trip time for creating socks connection with the client. - // But if connection failed, the client will get connection reset error. - _, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x43}) - if err != nil { - debug.Println("send connection confirmation:", err) - return + var rawaddr, buf []byte + var addr string + var n int + if !redir { + var err error = nil + if err = handShake(conn); err != nil { + log.Println("socks handshake:", err) + return + } + rawaddr, addr, err = getRequest(conn) + if err != nil { + log.Println("error getting request:", err) + return + } + // Sending connection established message immediately to client. + // This some round trip time for creating socks connection with the client. + // But if connection failed, the client will get connection reset error. + _, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x43}) + if err != nil { + debug.Println("send connection confirmation:", err) + return + } + } else { + var err error + buf = ss.GetLeakyBuf() + n, err = conn.Read(buf) + if err != nil { + println(err.Error()) + } + + rawaddr, addr = getDestAddr(&conn, string(buf[:n])) } remote, err := createServerConn(rawaddr, addr) @@ -323,13 +341,72 @@ func handleConnection(conn net.Conn) { } }() - go ss.PipeThenClose(conn, remote, nil) - ss.PipeThenClose(remote, conn, nil) + go ss.PipeThenClose(conn, remote, nil, buf, n) + ss.PipeThenClose(remote, conn, nil, nil, 0) closed = true debug.Println("closed connection to", addr) } -func run(listenAddr string) { +func getDestAddr(conn *net.Conn, buf string) (rawaddr []byte, addr string) { + tcpConn := (*conn).(*net.TCPConn) + // connection => file, will make a copy + tcpConnFile, err := tcpConn.File() + if err != nil { + panic(err) + } else { + tcpConn.Close() + } + + defer func() { + // file => connection + (*conn), err = net.FileConn(tcpConnFile) + if err != nil { + panic(err) + } + tcpConnFile.Close() + }() + + fd := int(tcpConnFile.Fd()) + req, err := syscall.GetsockoptIPv6Mreq(fd, syscall.IPPROTO_IP, SO_ORIGINAL_DST) + if err != nil { + _, err := syscall.GetsockoptIPMreq(fd, syscall.SOL_IP, SO_ORIGINAL_DST) + if err != nil { + println(err.Error()) + } + // TODO(me): I don't where the port is saved. + return nil, "" + } + + ip := net.IPv4(req.Multiaddr[4], req.Multiaddr[5], req.Multiaddr[6], req.Multiaddr[7]) + port := uint16(req.Multiaddr[2])<<8 + uint16(req.Multiaddr[3]) + dstaddr, err := net.ResolveTCPAddr("tcp4", fmt.Sprintf("%s:%d", ip.String(), port)) + if err != nil { + return nil, "" + } + addr = dstaddr.String() + var s string + if dstaddr.Port == DefaultPortForHttp { + s = parseHttpHeader(buf) + } else if dstaddr.Port == DefaultPortForTls { + s = parseTlsHeader(buf) + } + + if s != "" { + addr = s + rawaddr = append(rawaddr, byte(3)) + rawaddr = append(rawaddr, byte(len(s))) + rawaddr = append(rawaddr, []byte(s)...) + rawaddr = append(rawaddr, req.Multiaddr[2:4]...) + return + } + + rawaddr = append(rawaddr, byte(1)) + rawaddr = append(rawaddr, req.Multiaddr[4:8]...) + rawaddr = append(rawaddr, req.Multiaddr[2:4]...) + return +} + +func run(listenAddr string, redir bool) { ln, err := net.Listen("tcp", listenAddr) if err != nil { log.Fatal(err) @@ -341,7 +418,7 @@ func run(listenAddr string) { log.Println("accept:", err) continue } - go handleConnection(conn) + go handleConnection(conn, redir) } } @@ -407,8 +484,10 @@ func main() { var configFile, cmdServer, cmdURI string var cmdConfig ss.Config var printVer bool + var redirect bool flag.BoolVar(&printVer, "version", false, "print version") + flag.BoolVar(&redirect, "redirect", false, "Redirect normal request to socks server.") flag.StringVar(&configFile, "c", "config.json", "specify config file") flag.StringVar(&cmdServer, "s", "", "server address") flag.StringVar(&cmdConfig.LocalAddress, "b", "", "local address, listen only to this address if specified") @@ -477,5 +556,6 @@ func main() { } parseServerConfig(config) - run(config.LocalAddress + ":" + strconv.Itoa(config.LocalPort)) + run(config.LocalAddress+":"+strconv.Itoa(config.LocalPort), redirect) } + From 8e38949f7463d0955d276eadbda9cf91fddd2db1 Mon Sep 17 00:00:00 2001 From: ryan Date: Fri, 3 May 2019 22:59:55 +0800 Subject: [PATCH 4/6] Add `ss-redir` into shadowsocks-local --- .gitignore | 1 + cmd/shadowsocks-local/guess.go | 138 +++++++++++++++++++++++++++++++++ cmd/shadowsocks-local/local.go | 130 +++++++++++++++++++++++++------ shadowsocks/leakybuf.go | 5 ++ shadowsocks/pipe.go | 20 +++-- 5 files changed, 262 insertions(+), 32 deletions(-) create mode 100644 cmd/shadowsocks-local/guess.go diff --git a/.gitignore b/.gitignore index a53134e0..7eca66c3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ script/http bin .idea +.vscode/ diff --git a/cmd/shadowsocks-local/guess.go b/cmd/shadowsocks-local/guess.go new file mode 100644 index 00000000..df76c7f6 --- /dev/null +++ b/cmd/shadowsocks-local/guess.go @@ -0,0 +1,138 @@ +package main + +import ( + "strings" +) + +const ( + DefaultPortForHttp = 80 + DefaultPortForTls = 443 + + SERVER_NAME_LEN = 256 + TLS_HEADER_LEN = 5 + TLS_HANDSHAKE_CONTENT_TYPE = 0x16 + TLS_HANDSHAKE_TYPE_CLIENT_HELLO = 0x01 +) + +func parseHttpHeader(buf string) string { + for _, l := range strings.Split(buf, "\r\n") { + if strings.HasPrefix(l, "Host:") { + return strings.TrimSpace(l[5:]) + } + } + + return "" +} + +func parseTlsHeader(buf string) string { + slen := len(buf) + if slen < TLS_HEADER_LEN { + return "" + } + + if buf[0] != TLS_HANDSHAKE_CONTENT_TYPE { + return "" + } + + tlsVersionMajor, tlsVersionMinor := buf[1], buf[2] + if tlsVersionMajor < 3 { + return "" + } + + l := int(uint(buf[3])<<8 + uint(buf[4]) + TLS_HEADER_LEN) + if slen < l { + return "" + } + + buf = buf[:l] + slen = len(buf) + pos := TLS_HEADER_LEN + if slen < pos+1 { + return "" + } + + if buf[pos] != TLS_HANDSHAKE_TYPE_CLIENT_HELLO { + return "" + } + + /* Skip past fixed length records: + * 1 Handshake Type + * 3 Length + * 2 Version (again) + * 32 Random + * to Session ID Length + */ + pos += 38 + + if pos+1 > slen { + return "" + } + pos += int(1 + uint(buf[pos])) + + if pos+2 > slen { + return "" + } + pos += int(2 + uint(buf[pos])<<8 + uint(buf[pos+1])) + + if pos+1 > slen { + return "" + } + pos += int(1 + uint(buf[pos])) + + if pos == slen && tlsVersionMajor == 3 && tlsVersionMinor == 0 { + return "" + } + + if pos+2 > slen { + return "" + } + l = int(uint(buf[pos])<<8 + uint(buf[pos+1])) + pos += 2 + if pos+l > slen { + return "" + } + + return parseExtensions(buf[pos : pos+l]) +} + +func parseExtensions(buf string) string { + var pos, l int + slen := len(buf) + + for pos+4 <= slen { + l = int(uint(buf[pos+2])<<8 + uint(buf[pos+3])) + if buf[pos] == 0x00 && buf[pos+1] == 0x00 { + if pos+4+l > slen { + return "" + } + + return parseServerNameExtension(buf[pos+4 : pos+4+l]) + } + pos += 4 + l + } + + return "" +} + +func parseServerNameExtension(buf string) string { + var l int + slen := len(buf) + pos := 2 + + for pos+3 < slen { + l = int(uint(buf[pos+1])<<8 + uint(buf[pos+2])) + if pos+3+l > slen { + return "" + } + + switch buf[pos] { + case 0x00: + return buf[pos+3 : pos+3+l] + default: + } + pos += 3 + l + } + + return "" +} + diff --git a/cmd/shadowsocks-local/local.go b/cmd/shadowsocks-local/local.go index 604bf9f7..a62de543 100644 --- a/cmd/shadowsocks-local/local.go +++ b/cmd/shadowsocks-local/local.go @@ -15,14 +15,17 @@ import ( "path" "strconv" "strings" + "syscall" "time" - ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" + ss "github.com/bonafideyan/shadowsocks-go/shadowsocks" ) -var debug ss.DebugLog +const SO_ORIGINAL_DST = 80 var ( + debug ss.DebugLog + errAddrType = errors.New("socks addr type not supported") errVer = errors.New("socks version not supported") errMethod = errors.New("socks only support 1 method now") @@ -246,6 +249,7 @@ func connectToServer(serverId int, rawaddr []byte, addr string) (remote *ss.Conn } return nil, err } + debug.Printf("connected to %s via %s\n", addr, se.server) servers.failCnt[serverId] = 0 return @@ -280,7 +284,7 @@ func createServerConn(rawaddr []byte, addr string) (remote *ss.Conn, err error) return nil, err } -func handleConnection(conn net.Conn) { +func handleConnection(conn net.Conn, redir bool) { if debug { debug.Printf("socks connect from %s\n", conn.RemoteAddr().String()) } @@ -291,23 +295,37 @@ func handleConnection(conn net.Conn) { } }() - var err error = nil - if err = handShake(conn); err != nil { - log.Println("socks handshake:", err) - return - } - rawaddr, addr, err := getRequest(conn) - if err != nil { - log.Println("error getting request:", err) - return - } - // Sending connection established message immediately to client. - // This some round trip time for creating socks connection with the client. - // But if connection failed, the client will get connection reset error. - _, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x43}) - if err != nil { - debug.Println("send connection confirmation:", err) - return + var rawaddr, buf []byte + var addr string + var n int + if !redir { + var err error = nil + if err = handShake(conn); err != nil { + log.Println("socks handshake:", err) + return + } + rawaddr, addr, err = getRequest(conn) + if err != nil { + log.Println("error getting request:", err) + return + } + // Sending connection established message immediately to client. + // This some round trip time for creating socks connection with the client. + // But if connection failed, the client will get connection reset error. + _, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x43}) + if err != nil { + debug.Println("send connection confirmation:", err) + return + } + } else { + var err error + buf = ss.GetLeakyBuf() + n, err = conn.Read(buf) + if err != nil { + println(err.Error()) + } + + rawaddr, addr = getDestAddr(&conn, string(buf[:n])) } remote, err := createServerConn(rawaddr, addr) @@ -323,13 +341,72 @@ func handleConnection(conn net.Conn) { } }() - go ss.PipeThenClose(conn, remote, nil) - ss.PipeThenClose(remote, conn, nil) + go ss.PipeThenClose(conn, remote, nil, buf, n) + ss.PipeThenClose(remote, conn, nil, nil, 0) closed = true debug.Println("closed connection to", addr) } -func run(listenAddr string) { +func getDestAddr(conn *net.Conn, buf string) (rawaddr []byte, addr string) { + tcpConn := (*conn).(*net.TCPConn) + // connection => file, will make a copy + tcpConnFile, err := tcpConn.File() + if err != nil { + panic(err) + } else { + tcpConn.Close() + } + + defer func() { + // file => connection + (*conn), err = net.FileConn(tcpConnFile) + if err != nil { + panic(err) + } + tcpConnFile.Close() + }() + + fd := int(tcpConnFile.Fd()) + req, err := syscall.GetsockoptIPv6Mreq(fd, syscall.IPPROTO_IP, SO_ORIGINAL_DST) + if err != nil { + _, err := syscall.GetsockoptIPMreq(fd, syscall.SOL_IP, SO_ORIGINAL_DST) + if err != nil { + println(err.Error()) + } + // TODO(me): I don't where the port is saved. + return nil, "" + } + + ip := net.IPv4(req.Multiaddr[4], req.Multiaddr[5], req.Multiaddr[6], req.Multiaddr[7]) + port := uint16(req.Multiaddr[2])<<8 + uint16(req.Multiaddr[3]) + dstaddr, err := net.ResolveTCPAddr("tcp4", fmt.Sprintf("%s:%d", ip.String(), port)) + if err != nil { + return nil, "" + } + addr = dstaddr.String() + var s string + if dstaddr.Port == DefaultPortForHttp { + s = parseHttpHeader(buf) + } else if dstaddr.Port == DefaultPortForTls { + s = parseTlsHeader(buf) + } + + if s != "" { + addr = s + rawaddr = append(rawaddr, byte(3)) + rawaddr = append(rawaddr, byte(len(s))) + rawaddr = append(rawaddr, []byte(s)...) + rawaddr = append(rawaddr, req.Multiaddr[2:4]...) + return + } + + rawaddr = append(rawaddr, byte(1)) + rawaddr = append(rawaddr, req.Multiaddr[4:8]...) + rawaddr = append(rawaddr, req.Multiaddr[2:4]...) + return +} + +func run(listenAddr string, redir bool) { ln, err := net.Listen("tcp", listenAddr) if err != nil { log.Fatal(err) @@ -341,7 +418,7 @@ func run(listenAddr string) { log.Println("accept:", err) continue } - go handleConnection(conn) + go handleConnection(conn, redir) } } @@ -407,8 +484,10 @@ func main() { var configFile, cmdServer, cmdURI string var cmdConfig ss.Config var printVer bool + var redirect bool flag.BoolVar(&printVer, "version", false, "print version") + flag.BoolVar(&redirect, "redirect", false, "Redirect normal request to socks server.") flag.StringVar(&configFile, "c", "config.json", "specify config file") flag.StringVar(&cmdServer, "s", "", "server address") flag.StringVar(&cmdConfig.LocalAddress, "b", "", "local address, listen only to this address if specified") @@ -477,5 +556,6 @@ func main() { } parseServerConfig(config) - run(config.LocalAddress + ":" + strconv.Itoa(config.LocalPort)) + run(config.LocalAddress+":"+strconv.Itoa(config.LocalPort), redirect) } + diff --git a/shadowsocks/leakybuf.go b/shadowsocks/leakybuf.go index b6922eb9..7600a3ea 100644 --- a/shadowsocks/leakybuf.go +++ b/shadowsocks/leakybuf.go @@ -20,6 +20,11 @@ func NewLeakyBuf(n, bufSize int) *LeakyBuf { } } +// GetLeakyBuf is invoked outside of the package. +func GetLeakyBuf() []byte { + return leakyBuf.Get() +} + // Get returns a buffer from the leaky buffer or create a new buffer. func (lb *LeakyBuf) Get() (b []byte) { select { diff --git a/shadowsocks/pipe.go b/shadowsocks/pipe.go index 51f52006..c474b4a9 100644 --- a/shadowsocks/pipe.go +++ b/shadowsocks/pipe.go @@ -12,16 +12,15 @@ func SetReadTimeout(c net.Conn) { } // PipeThenClose copies data from src to dst, closes dst when done. -func PipeThenClose(src, dst net.Conn, addTraffic func(int)) { +func PipeThenClose(src, dst net.Conn, addTraffic func(int), buf []byte, n int) { defer dst.Close() - buf := leakyBuf.Get() + if buf == nil { + buf = leakyBuf.Get() + } defer leakyBuf.Put(buf) + + var err error for { - SetReadTimeout(src) - n, err := src.Read(buf) - if addTraffic != nil { - addTraffic(n) - } // read may return EOF with n > 0 // should always process n > 0 bytes before handling error if n > 0 { @@ -31,6 +30,13 @@ func PipeThenClose(src, dst net.Conn, addTraffic func(int)) { break } } + + SetReadTimeout(src) + n, err = src.Read(buf) + if addTraffic != nil { + addTraffic(n) + } + if err != nil { // Always "use of closed network connection", but no easy way to // identify this specific error. So just leave the error along for now. From 74f938b4b3814c809e8a2c1cbeb4c9abad38b36f Mon Sep 17 00:00:00 2001 From: ryan Date: Sat, 4 May 2019 09:22:07 +0800 Subject: [PATCH 5/6] Replace read-and-write with io.Copy --- shadowsocks/pipe.go | 36 +++++++----------------------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/shadowsocks/pipe.go b/shadowsocks/pipe.go index c474b4a9..c0a0b733 100644 --- a/shadowsocks/pipe.go +++ b/shadowsocks/pipe.go @@ -1,6 +1,7 @@ package shadowsocks import ( + "io" "net" "time" ) @@ -14,40 +15,17 @@ func SetReadTimeout(c net.Conn) { // PipeThenClose copies data from src to dst, closes dst when done. func PipeThenClose(src, dst net.Conn, addTraffic func(int), buf []byte, n int) { defer dst.Close() - if buf == nil { - buf = leakyBuf.Get() - } - defer leakyBuf.Put(buf) - - var err error - for { - // read may return EOF with n > 0 - // should always process n > 0 bytes before handling error + if buf != nil { if n > 0 { - // Note: avoid overwrite err returned by Read. if _, err := dst.Write(buf[0:n]); err != nil { Debug.Println("write:", err) - break + leakyBuf.Put(buf) + return } } - - SetReadTimeout(src) - n, err = src.Read(buf) - if addTraffic != nil { - addTraffic(n) - } - - if err != nil { - // Always "use of closed network connection", but no easy way to - // identify this specific error. So just leave the error along for now. - // More info here: https://code.google.com/p/go/issues/detail?id=4373 - /* - if bool(Debug) && err != io.EOF { - Debug.Println("read:", err) - } - */ - break - } + leakyBuf.Put(buf) } + + io.Copy(src, dst) return } From 20f6d66ea08d8eb6d6692da2f0a262cc9196e95d Mon Sep 17 00:00:00 2001 From: dalao Date: Sun, 29 Sep 2019 07:35:46 +0800 Subject: [PATCH 6/6] Add go.mod --- go.mod | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 go.mod diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..1da1d8a2 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/bonafideyan/shadowsocks-go + +go 1.13