From 0cdf31ddafc3da640786ebb3a70173a1d03dccb3 Mon Sep 17 00:00:00 2001 From: Dingjun Date: Sat, 20 Aug 2016 15:01:19 +0800 Subject: [PATCH 1/6] add optional Dial option the dial function provider a way to connect to server --- cmd/server/server.go | 6 ++++-- socks.go | 14 ++++++++++++-- socks4.go | 3 ++- socks5.go | 3 ++- 4 files changed, 20 insertions(+), 6 deletions(-) mode change 100755 => 100644 cmd/server/server.go mode change 100755 => 100644 socks.go mode change 100755 => 100644 socks4.go mode change 100755 => 100644 socks5.go diff --git a/cmd/server/server.go b/cmd/server/server.go old mode 100755 new mode 100644 index 249ec7f..527f3c8 --- a/cmd/server/server.go +++ b/cmd/server/server.go @@ -1,9 +1,10 @@ package main import ( - "github.com/fangdingjun/socks" + socks "github.com/fangdingjun/socks-go" "log" "net" + "time" ) func main() { @@ -18,7 +19,8 @@ func main() { continue } log.Printf("connected from %s", c.RemoteAddr()) - s := socks.SocksConn{ClientConn: c} + d := net.Dialer{Timeout: 10 * time.Second} + s := socks.SocksConn{ClientConn: c, Dial: d.Dial} go s.Serve() } } diff --git a/socks.go b/socks.go old mode 100755 new mode 100644 index 4c58e97..bba1b87 --- a/socks.go +++ b/socks.go @@ -4,6 +4,7 @@ import ( "io" "log" "net" + "time" ) const ( @@ -15,8 +16,11 @@ const ( addrTypeIPv6 = 0x04 ) +type dialFunc func(network, addr string) (net.Conn, error) + type SocksConn struct { ClientConn net.Conn + Dial dialFunc } func (s *SocksConn) Serve() { @@ -25,12 +29,18 @@ func (s *SocksConn) Serve() { // read version io.ReadFull(s.ClientConn, buf) + dial := s.Dial + if s.Dial == nil { + d := net.Dialer{Timeout: 10 * time.Second} + dial = d.Dial + } + switch buf[0] { case socks4Version: - s4 := socks4Conn{client_conn: s.ClientConn} + s4 := socks4Conn{client_conn: s.ClientConn, dial: dial} s4.Serve() case socks5Version: - s5 := socks5Conn{client_conn: s.ClientConn} + s5 := socks5Conn{client_conn: s.ClientConn, dial: dial} s5.Serve() default: log.Printf("error version %s", buf[0]) diff --git a/socks4.go b/socks4.go old mode 100755 new mode 100644 index 9b9da53..af54654 --- a/socks4.go +++ b/socks4.go @@ -11,6 +11,7 @@ import ( type socks4Conn struct { server_conn net.Conn client_conn net.Conn + dial dialFunc } func (s4 *socks4Conn) Serve() { @@ -92,7 +93,7 @@ func (s4 *socks4Conn) processRequest() error { log.Printf("connecting to %s", target) // connect to the target - s4.server_conn, err = net.Dial("tcp", target) + s4.server_conn, err = s4.dial("tcp", target) if err != nil { return err } diff --git a/socks5.go b/socks5.go old mode 100755 new mode 100644 index dd2a8f1..868c602 --- a/socks5.go +++ b/socks5.go @@ -13,6 +13,7 @@ type socks5Conn struct { //addr string client_conn net.Conn server_conn net.Conn + dial dialFunc } func (s5 *socks5Conn) Serve() { @@ -107,7 +108,7 @@ func (s5 *socks5Conn) processRequest() error { log.Printf("connecing to %s", target) // connect to the target - s5.server_conn, err = net.Dial("tcp", target) + s5.server_conn, err = s5.dial("tcp", target) if err != nil { return err } From cdfc17d24a7b56ac35121957a7dc825ee5e4a262 Mon Sep 17 00:00:00 2001 From: Dingjun Date: Sat, 20 Aug 2016 15:09:08 +0800 Subject: [PATCH 2/6] golint --- socks.go | 2 + socks4.go | 210 +++++++++++++++++++++++++++--------------------------- socks5.go | 32 ++++----- 3 files changed, 123 insertions(+), 121 deletions(-) diff --git a/socks.go b/socks.go index bba1b87..8aa20da 100644 --- a/socks.go +++ b/socks.go @@ -18,11 +18,13 @@ const ( type dialFunc func(network, addr string) (net.Conn, error) +// SocksConn present a client connection type SocksConn struct { ClientConn net.Conn Dial dialFunc } +// Serve serve the client func (s *SocksConn) Serve() { buf := make([]byte, 1) diff --git a/socks4.go b/socks4.go index af54654..97c1f96 100644 --- a/socks4.go +++ b/socks4.go @@ -1,105 +1,105 @@ -package socks - -import ( - "encoding/binary" - "fmt" - "io" - "log" - "net" -) - -type socks4Conn struct { - server_conn net.Conn - client_conn net.Conn - dial dialFunc -} - -func (s4 *socks4Conn) Serve() { - defer s4.Close() - if err := s4.processRequest(); err != nil { - log.Println(err) - return - } -} - -func (s4 *socks4Conn) Close() { - if s4.client_conn != nil { - s4.client_conn.Close() - } - if s4.server_conn != nil { - s4.server_conn.Close() - } -} - -func (s4 *socks4Conn) forward() { - go func() { - io.Copy(s4.client_conn, s4.server_conn) - }() - - io.Copy(s4.server_conn, s4.client_conn) -} - -func (s4 *socks4Conn) processRequest() error { - // version has already read out by socksConn.Serve() - // process command and target here - - buf := make([]byte, 128) - n, err := io.ReadAtLeast(s4.client_conn, buf, 8) - if err != nil { - return err - } - - // only support connect - if buf[0] != cmdConnect { - return fmt.Errorf("error command %s", buf[0]) - } - - port := binary.BigEndian.Uint16(buf[1:3]) - - ip := net.IP(buf[3:7]) - - // NULL-terminated user string - // jump to NULL character - var j int - for j = 7; j < n; j++ { - if buf[j] == 0x00 { - break - } - } - - host := ip.String() - - // socks4a - // 0.0.0.x - if ip[0] == 0x00 && ip[1] == 0x00 && ip[2] == 0x00 && ip[3] != 0x00 { - j++ - var i = j - - // jump to the end of hostname - for j = i; j < n; j++ { - if buf[j] == 0x00 { - break - } - } - host = string(buf[i:j]) - } - - target := net.JoinHostPort(host, fmt.Sprintf("%d", port)) - - // reply user with connect success - // if dial to target failed, user will receive connection reset - s4.client_conn.Write([]byte{0x00, 0x5a, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00}) - - log.Printf("connecting to %s", target) - - // connect to the target - s4.server_conn, err = s4.dial("tcp", target) - if err != nil { - return err - } - - // enter data exchange - s4.forward() - - return nil -} +package socks + +import ( + "encoding/binary" + "fmt" + "io" + "log" + "net" +) + +type socks4Conn struct { + serverConn net.Conn + clientConn net.Conn + dial dialFunc +} + +func (s4 *socks4Conn) Serve() { + defer s4.Close() + if err := s4.processRequest(); err != nil { + log.Println(err) + return + } +} + +func (s4 *socks4Conn) Close() { + if s4.clientConn != nil { + s4.clientConn.Close() + } + if s4.serverConn != nil { + s4.serverConn.Close() + } +} + +func (s4 *socks4Conn) forward() { + go func() { + io.Copy(s4.clientConn, s4.serverConn) + }() + + io.Copy(s4.serverConn, s4.clientConn) +} + +func (s4 *socks4Conn) processRequest() error { + // version has already read out by socksConn.Serve() + // process command and target here + + buf := make([]byte, 128) + n, err := io.ReadAtLeast(s4.clientConn, buf, 8) + if err != nil { + return err + } + + // only support connect + if buf[0] != cmdConnect { + return fmt.Errorf("error command %s", buf[0]) + } + + port := binary.BigEndian.Uint16(buf[1:3]) + + ip := net.IP(buf[3:7]) + + // NULL-terminated user string + // jump to NULL character + var j int + for j = 7; j < n; j++ { + if buf[j] == 0x00 { + break + } + } + + host := ip.String() + + // socks4a + // 0.0.0.x + if ip[0] == 0x00 && ip[1] == 0x00 && ip[2] == 0x00 && ip[3] != 0x00 { + j++ + var i = j + + // jump to the end of hostname + for j = i; j < n; j++ { + if buf[j] == 0x00 { + break + } + } + host = string(buf[i:j]) + } + + target := net.JoinHostPort(host, fmt.Sprintf("%d", port)) + + // reply user with connect success + // if dial to target failed, user will receive connection reset + s4.clientConn.Write([]byte{0x00, 0x5a, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00}) + + log.Printf("connecting to %s", target) + + // connect to the target + s4.serverConn, err = s4.dial("tcp", target) + if err != nil { + return err + } + + // enter data exchange + s4.forward() + + return nil +} diff --git a/socks5.go b/socks5.go index 868c602..f49776a 100644 --- a/socks5.go +++ b/socks5.go @@ -11,9 +11,9 @@ import ( type socks5Conn struct { //addr string - client_conn net.Conn - server_conn net.Conn - dial dialFunc + clientConn net.Conn + serverConn net.Conn + dial dialFunc } func (s5 *socks5Conn) Serve() { @@ -34,28 +34,28 @@ func (s5 *socks5Conn) handshake() error { // only process auth methods here buf := make([]byte, 258) - n, err := io.ReadAtLeast(s5.client_conn, buf, 1) + n, err := io.ReadAtLeast(s5.clientConn, buf, 1) if err != nil { return err } l := int(buf[0]) + 1 if n < l { - _, err := io.ReadFull(s5.client_conn, buf[n:l]) + _, err := io.ReadFull(s5.clientConn, buf[n:l]) if err != nil { return err } } // no auth required - s5.client_conn.Write([]byte{0x05, 0x00}) + s5.clientConn.Write([]byte{0x05, 0x00}) return nil } func (s5 *socks5Conn) processRequest() error { buf := make([]byte, 258) - n, err := io.ReadAtLeast(s5.client_conn, buf, 10) + n, err := io.ReadAtLeast(s5.clientConn, buf, 10) if err != nil { return err } @@ -83,7 +83,7 @@ func (s5 *socks5Conn) processRequest() error { msglen = 6 + hlen if n < msglen { - _, err := io.ReadFull(s5.client_conn, buf[n:msglen]) + _, err := io.ReadFull(s5.clientConn, buf[n:msglen]) if err != nil { return err } @@ -103,12 +103,12 @@ func (s5 *socks5Conn) processRequest() error { // reply user with connect success // if dial to target failed, user will receive connection reset - s5.client_conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01}) + s5.clientConn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01}) log.Printf("connecing to %s", target) // connect to the target - s5.server_conn, err = s5.dial("tcp", target) + s5.serverConn, err = s5.dial("tcp", target) if err != nil { return err } @@ -120,15 +120,15 @@ func (s5 *socks5Conn) processRequest() error { } func (s5 *socks5Conn) forward() { - go io.Copy(s5.client_conn, s5.server_conn) - io.Copy(s5.server_conn, s5.client_conn) + go io.Copy(s5.clientConn, s5.serverConn) + io.Copy(s5.serverConn, s5.clientConn) } func (s5 *socks5Conn) Close() { - if s5.server_conn != nil { - s5.server_conn.Close() + if s5.serverConn != nil { + s5.serverConn.Close() } - if s5.client_conn != nil { - s5.client_conn.Close() + if s5.clientConn != nil { + s5.clientConn.Close() } } From 811a67f289075e6e41c3f5093a322a389d190c71 Mon Sep 17 00:00:00 2001 From: Dingjun Date: Sat, 20 Aug 2016 15:12:25 +0800 Subject: [PATCH 3/6] fix field name typo --- socks.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/socks.go b/socks.go index 8aa20da..d02fb17 100644 --- a/socks.go +++ b/socks.go @@ -39,10 +39,10 @@ func (s *SocksConn) Serve() { switch buf[0] { case socks4Version: - s4 := socks4Conn{client_conn: s.ClientConn, dial: dial} + s4 := socks4Conn{clientConn: s.ClientConn, dial: dial} s4.Serve() case socks5Version: - s5 := socks5Conn{client_conn: s.ClientConn, dial: dial} + s5 := socks5Conn{clientConn: s.ClientConn, dial: dial} s5.Serve() default: log.Printf("error version %s", buf[0]) From 8a138fb3db302ace63f49189e735dd147ba79b4c Mon Sep 17 00:00:00 2001 From: Dingjun Date: Wed, 23 Nov 2016 15:23:01 +0800 Subject: [PATCH 4/6] minor changes add some comments fix some wrong format change the method of forward finish check --- .gitignore | 1 + README.md | 10 ++++---- examples/server/.gitignore | 1 + {cmd => examples}/server/README.md | 0 {cmd => examples}/server/server.go | 0 socks.go | 2 +- socks4.go | 21 +++++++++++++--- socks5.go | 39 ++++++++++++++++++++++++------ 8 files changed, 57 insertions(+), 17 deletions(-) create mode 100644 examples/server/.gitignore rename {cmd => examples}/server/README.md (100%) rename {cmd => examples}/server/server.go (100%) diff --git a/.gitignore b/.gitignore index 2ed562a..a62d708 100755 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.exe *~ *bak +*.swp diff --git a/README.md b/README.md index aa5f47e..c082f37 100755 --- a/README.md +++ b/README.md @@ -1,20 +1,20 @@ socks-go ======= -A socks server implement by golang, support socks4/4a, socks5. +A socks server implemented by golang, support socks4/4a, socks5. usage ==== Usage example: - import "github.com/fangdingjun/socks" + import socks "github.com/fangdingjun/socks-go" - fucn main(){ + func main(){ l, _ := net.Listen("tcp", ":1080") for { conn, _ := l.Accept() - s := socks.SocksConn{conn} - go s.Serve() + s := socks.SocksConn{ClientConn: conn, Dial: nil} // Dial is a function which dial to the upstream server + go s.Serve() // serve the socks request } } diff --git a/examples/server/.gitignore b/examples/server/.gitignore new file mode 100644 index 0000000..254defd --- /dev/null +++ b/examples/server/.gitignore @@ -0,0 +1 @@ +server diff --git a/cmd/server/README.md b/examples/server/README.md similarity index 100% rename from cmd/server/README.md rename to examples/server/README.md diff --git a/cmd/server/server.go b/examples/server/server.go similarity index 100% rename from cmd/server/server.go rename to examples/server/server.go diff --git a/socks.go b/socks.go index d02fb17..e150545 100644 --- a/socks.go +++ b/socks.go @@ -45,7 +45,7 @@ func (s *SocksConn) Serve() { s5 := socks5Conn{clientConn: s.ClientConn, dial: dial} s5.Serve() default: - log.Printf("error version %s", buf[0]) + log.Printf("error version %d", buf[0]) s.ClientConn.Close() } } diff --git a/socks4.go b/socks4.go index 97c1f96..4abbca6 100644 --- a/socks4.go +++ b/socks4.go @@ -16,6 +16,7 @@ type socks4Conn struct { func (s4 *socks4Conn) Serve() { defer s4.Close() + if err := s4.processRequest(); err != nil { log.Println(err) return @@ -26,17 +27,27 @@ func (s4 *socks4Conn) Close() { if s4.clientConn != nil { s4.clientConn.Close() } + if s4.serverConn != nil { s4.serverConn.Close() } } func (s4 *socks4Conn) forward() { + + c := make(chan int, 2) + go func() { io.Copy(s4.clientConn, s4.serverConn) + c <- 1 }() - io.Copy(s4.serverConn, s4.clientConn) + go func() { + io.Copy(s4.serverConn, s4.clientConn) + c <- 1 + }() + + <-c } func (s4 *socks4Conn) processRequest() error { @@ -44,18 +55,22 @@ func (s4 *socks4Conn) processRequest() error { // process command and target here buf := make([]byte, 128) + + // read header n, err := io.ReadAtLeast(s4.clientConn, buf, 8) if err != nil { return err } - // only support connect + // command only support connect if buf[0] != cmdConnect { return fmt.Errorf("error command %s", buf[0]) } + // get port port := binary.BigEndian.Uint16(buf[1:3]) + // get ip ip := net.IP(buf[3:7]) // NULL-terminated user string @@ -90,7 +105,7 @@ func (s4 *socks4Conn) processRequest() error { // if dial to target failed, user will receive connection reset s4.clientConn.Write([]byte{0x00, 0x5a, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00}) - log.Printf("connecting to %s", target) + //log.Printf("connecting to %s\r\n", target) // connect to the target s4.serverConn, err = s4.dial("tcp", target) diff --git a/socks5.go b/socks5.go index f49776a..4b67f03 100644 --- a/socks5.go +++ b/socks5.go @@ -18,6 +18,7 @@ type socks5Conn struct { func (s5 *socks5Conn) Serve() { defer s5.Close() + if err := s5.handshake(); err != nil { log.Println(err) return @@ -34,6 +35,8 @@ func (s5 *socks5Conn) handshake() error { // only process auth methods here buf := make([]byte, 258) + + // read auth methods n, err := io.ReadAtLeast(s5.clientConn, buf, 1) if err != nil { return err @@ -41,6 +44,7 @@ func (s5 *socks5Conn) handshake() error { l := int(buf[0]) + 1 if n < l { + // read remains data _, err := io.ReadFull(s5.clientConn, buf[n:l]) if err != nil { return err @@ -55,22 +59,26 @@ func (s5 *socks5Conn) handshake() error { func (s5 *socks5Conn) processRequest() error { buf := make([]byte, 258) + + // read header n, err := io.ReadAtLeast(s5.clientConn, buf, 10) if err != nil { return err } + if buf[0] != socks5Version { - return fmt.Errorf("error version %s", buf[0]) + return fmt.Errorf("error version %d", buf[0]) } - // only support connect + // command only support connect if buf[1] != cmdConnect { return fmt.Errorf("unsupported command %s", buf[1]) } - hlen := 0 - host := "" - msglen := 0 + hlen := 0 // target address length + host := "" // target address + msglen := 0 // header length + switch buf[3] { case addrTypeIPv4: hlen = 4 @@ -83,6 +91,7 @@ func (s5 *socks5Conn) processRequest() error { msglen = 6 + hlen if n < msglen { + // read remains header _, err := io.ReadFull(s5.clientConn, buf[n:msglen]) if err != nil { return err @@ -97,15 +106,17 @@ func (s5 *socks5Conn) processRequest() error { host = net.IP(addr).String() } + // get target port port := binary.BigEndian.Uint16(buf[msglen-2 : msglen]) + // target address target := net.JoinHostPort(host, fmt.Sprintf("%d", port)) // reply user with connect success // if dial to target failed, user will receive connection reset s5.clientConn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01}) - log.Printf("connecing to %s", target) + //log.Printf("connecing to %s\r\n", target) // connect to the target s5.serverConn, err = s5.dial("tcp", target) @@ -120,8 +131,20 @@ func (s5 *socks5Conn) processRequest() error { } func (s5 *socks5Conn) forward() { - go io.Copy(s5.clientConn, s5.serverConn) - io.Copy(s5.serverConn, s5.clientConn) + + c := make(chan int, 2) + + go func() { + io.Copy(s5.clientConn, s5.serverConn) + c <- 1 + }() + + go func() { + io.Copy(s5.serverConn, s5.clientConn) + c < 1 + }() + + <-c } func (s5 *socks5Conn) Close() { From a05b5b5ab96ada1720d13b8b576bd59e03601dd9 Mon Sep 17 00:00:00 2001 From: Dingjun Date: Wed, 23 Nov 2016 15:28:18 +0800 Subject: [PATCH 5/6] fix mis-typed dash --- socks5.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/socks5.go b/socks5.go index 4b67f03..cb1b858 100644 --- a/socks5.go +++ b/socks5.go @@ -141,7 +141,7 @@ func (s5 *socks5Conn) forward() { go func() { io.Copy(s5.serverConn, s5.clientConn) - c < 1 + c <- 1 }() <-c From f624794388dcd934a3dc94e0b240be39abad1420 Mon Sep 17 00:00:00 2001 From: Dingjun Date: Wed, 23 Nov 2016 16:48:29 +0800 Subject: [PATCH 6/6] add protocol data format --- README.md | 1 + socks4.go | 23 +++++++++++++++++++++++ socks5.go | 25 +++++++++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/README.md b/README.md index c082f37..21329bf 100755 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ socks-go A socks server implemented by golang, support socks4/4a, socks5. +Only support connect command now. usage ==== diff --git a/socks4.go b/socks4.go index 4abbca6..0eeda0d 100644 --- a/socks4.go +++ b/socks4.go @@ -8,6 +8,29 @@ import ( "net" ) +/* +socks4 protocol + +request +byte | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ... | + |0x04|cmd| port | ip | user\0 | + +reply +byte | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7| + |0x00|status| | | + + +socks4a protocol + +request +byte | 0 | 1 | 2 | 3 |4 | 5 | 6 | 7 | 8 | ... |... | + |0x04|cmd| port | 0.0.0.x | user\0 |domain\0| + +reply +byte | 0 | 1 | 2 | 3 | 4 | 5 | 6| 7 | + |0x00|staus| port | ip | + +*/ type socks4Conn struct { serverConn net.Conn clientConn net.Conn diff --git a/socks5.go b/socks5.go index cb1b858..d624a4d 100644 --- a/socks5.go +++ b/socks5.go @@ -9,6 +9,31 @@ import ( "encoding/binary" ) +/* +socks5 protocol + +initial + +byte | 0 | 1 | 2 | ...... | n | + |0x05|num auth| auth methods | + + +reply + +byte | 0 | 1 | + |0x05| auth| + + +request + +byte | 0 | 1 | 2 | 3 | 4 | .. | n-2 | n-1| n | + |0x05|cmd|0x00|addrtype| addr | port | + +response +byte |0 | 1 | 2 | 3 | 4 | .. | n-2 | n-1 | n | + |0x05|status|0x00|addrtype| addr | port | + +*/ type socks5Conn struct { //addr string clientConn net.Conn