diff --git a/Makefile b/Makefile index d8381e2..c50fa38 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,6 @@ PREFIX := shadowsocks LOCAL := $(GOBIN)/$(PREFIX)-local SERVER := $(GOBIN)/$(PREFIX)-server -TEST := $(GOBIN)/$(PREFIX)-test # TODO define the install package path for use in clean and detect whether # package need re-build @@ -23,6 +22,5 @@ $(SERVER): shadowsocks/*.go cmd/$(PREFIX)-server/*.go cd shadowsocks; go install cd cmd/$(PREFIX)-server; go install -$(TEST): shadowsocks/*.go cmd/$(PREFIX)-test/*.go - cd shadowsocks; go install - cd cmd/$(PREFIX)-test; go install +test: + cd shadowsocks; go test diff --git a/cmd/shadowsocks-local/local.go b/cmd/shadowsocks-local/local.go index 5c2558b..d09fe38 100644 --- a/cmd/shadowsocks-local/local.go +++ b/cmd/shadowsocks-local/local.go @@ -1,101 +1,207 @@ package main import ( - "fmt" + "bytes" + "encoding/binary" + "errors" + "flag" + ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" + "io" "log" "net" - "github.com/shadowsocks/shadowsocks-go/shadowsocks" + "strconv" ) -func handleConnection(conn net.Conn, server string) { - log.Printf("socks connect from %s\n", conn.RemoteAddr().String()) - b := make([]byte, 262) - var err error = nil - var hasError = false +var debug ss.DebugLog + +var ( + errAddr = errors.New("socks addr type not supported") + errVer = errors.New("socks version not supported") + errMethod = errors.New("socks only support 1 method now") + errAuth = errors.New("socks authentication not required") + errCmd = errors.New("socks command not supported") +) + +func handShake(conn net.Conn) (err error) { + const ( + idVer = 0 + idNmethod = 1 + ) + // version identification and method selection message in theory can have + // at most 256 methods, plus version and nmethod field in total 258 bytes + // the current rfc defines only 3 authentication methods (plus 2 reserved) + + buf := make([]byte, 258-2, 258-2) // reuse the buf to read nmethod field + + if _, err = io.ReadFull(conn, buf[:2]); err != nil { + return + } + if buf[idVer] != 5 { + return errVer + } + nmethod := buf[idNmethod] + if _, err = io.ReadFull(conn, buf[:nmethod]); err != nil { + return + } + // version 5, no authentication required + _, err = conn.Write([]byte{5, 0}) + return +} + +func getRequest(conn net.Conn) (rawaddr []byte, extra []byte, host string, err error) { + const ( + idVer = 0 + idCmd = 1 + idType = 3 // address type index + idIP0 = 4 // ip addres start index + idDmLen = 4 // domain address length index + idDm0 = 5 // domain address start index + + typeIP = 1 // type is ip address + typeDm = 3 // type is domain address + + lenIP = 3 + 1 + 4 + 2 // 3(ver+cmd+rsv) + 1addrType + 4ip + 2port + lenDmBase = 3 + 1 + 1 + 2 // 3 + 1addrType + 1addrLen + 2port, plus addrLen + ) + // refer to getRequest in server.go for why set buffer size to 263 + buf := make([]byte, 263, 263) + cur := 0 // current location in buf + reqLen := 0 + for { - var _ int - buf := make([]byte, 4096) - _, err = conn.Read(b) - if err != nil { - hasError = true - break + var n int + // usually need to read only once + if n, err = conn.Read(buf[cur:]); err != nil { + // debug.Println("read request error:", err) + return } - conn.Write([]byte{0x05, 0x00}) - _, err = conn.Read(buf) - mode := buf[1] - if mode != 1 { - hasError = true - log.Println("mode != 1") - break + cur += n + if cur < idType+1 { // read till we get addr type + continue } - var addr string - addrType := buf[3] - var addrToSend []byte - if addrType == 1 { - var addrIp net.IP = make(net.IP, 4) - copy(addrIp, buf[4:8]) - addr = addrIp.String() - addrToSend = buf[3:10] - } else if addrType == 3 { - addrLen := buf[4] - addr = string(buf[5 : 5+addrLen]) - addrToSend = buf[3 : 5+addrLen+2] - } else { - hasError = true - log.Println("unsurpported addr type") - break + // check version and cmd + if buf[idVer] != 5 { + err = errVer + return } - log.Println("connecting ", addr) - conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x43}) - - remote, err := shadowsocks.DialWithAddrBuf(addrToSend, server) - if err != nil { - hasError = true - break + if buf[idCmd] != 1 { + err = errCmd + return } - c := make(chan int, 2) - go shadowsocks.Pipe(conn, remote, c) - go shadowsocks.Pipe(remote, conn, c) - <-c // close the other connection whenever one connection is closed - log.Println("closing") - err = conn.Close() - err1 := remote.Close() - if err == nil { - err = err1 + // TODO following code is copied from server.go, fix code duplication? + if buf[idType] == typeIP { + if cur >= lenIP { + // debug.Println("ip request complete, cur:", cur) + reqLen = lenIP + break + } + } else if buf[idType] == typeDm { + if cur < idDmLen+1 { // read until we get address length byte + continue + } + if cur >= lenDmBase+int(buf[idDmLen]) { + // debug.Println("domain request complete, cur:", cur) + reqLen = lenDmBase + int(buf[idDmLen]) + break + } + } else { + err = errAddr + return } - break + // debug.Println("request not complete, cur:", cur) } - if err != nil || hasError { - if err != nil { - log.Println("error ", err) - } - err = conn.Close() - if err != nil { - log.Println("close:", err) + + rawaddr = buf[idType:reqLen] + if cur > reqLen { + extra = buf[reqLen:cur] + // debug.Println("extra:", string(extra)) + } + + if debug { + if buf[idType] == typeIP { + addrIp := make(net.IP, 4) + copy(addrIp, buf[idIP0:idIP0+4]) + host = addrIp.String() + } else if buf[idType] == typeDm { + host = string(buf[idDm0 : idDm0+buf[idDmLen]]) } + var port int16 + sb := bytes.NewBuffer(buf[reqLen-2 : reqLen]) + binary.Read(sb, binary.BigEndian, &port) + host += ":" + strconv.Itoa(int(port)) + } + return +} + +func handleConnection(conn net.Conn, server string, encTbl *ss.EncryptTable) { + if debug { + debug.Printf("socks connect from %s\n", conn.RemoteAddr().String()) + } + defer conn.Close() + + var err error = nil + if err = handShake(conn); err != nil { + log.Println("socks handshack:", err) return } + rawaddr, extra, addr, err := getRequest(conn) + if err != nil { + log.Println("error getting request:", err) + return + } + // TODO should send error code to client if connect to server failed + _, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x43}) + if err != nil { + debug.Println("send connection confirmation:", err) + return + } + + debug.Println("connecting to", addr) + remote, err := ss.DialWithRawAddr(rawaddr, server, encTbl) + if err != nil { + log.Println("error connect to shadowsocks server:", err) + return + } + defer remote.Close() + if extra != nil { + debug.Println("writing extra content to remote, len", len(extra)) + if _, err = remote.Write(extra); err != nil { + debug.Println("write request extra error:", err) + return + } + } + c := make(chan byte, 2) + go ss.Pipe(conn, remote, c) + go ss.Pipe(remote, conn, c) + <-c // close the other connection whenever one connection is closed + debug.Println("closing") } -func run(port int, server string) { - ln, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) +func run(port, password, server string) { + ln, err := net.Listen("tcp", ":"+port) if err != nil { log.Fatal(err) } - log.Printf("starting server at port %d ...\n", port) + encTbl := ss.GetTable(password) + log.Printf("starting server at port %v ...\n", port) for { conn, err := ln.Accept() if err != nil { log.Println("accept:", err) continue } - go handleConnection(conn, server) + go handleConnection(conn, server, encTbl) } } func main() { - config := shadowsocks.ParseConfig() - shadowsocks.InitTable(config.Password) - run(config.LocalPort, fmt.Sprintf("%s:%d", config.Server, config.ServerPort)) + var configFile string + flag.StringVar(&configFile, "c", "config.json", "specify config file") + flag.Parse() + + config := ss.ParseConfig(configFile) + debug = ss.Debug + run(strconv.Itoa(config.LocalPort), config.Password, + config.Server+":"+strconv.Itoa(config.ServerPort)) } diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index a3a96c5..b28d5a9 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -3,115 +3,188 @@ package main import ( "bytes" "encoding/binary" - "fmt" + "errors" + "flag" + ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" "log" "net" - "github.com/shadowsocks/shadowsocks-go/shadowsocks" + "strconv" + "sync/atomic" + "time" ) -func handleConnection(conn shadowsocks.Conn) { - log.Printf("socks connect from %s\n", conn.RemoteAddr().String()) - var err error = nil - var hasError = false +var debug ss.DebugLog + +var errAddr = errors.New("addr type not supported") + +func getRequest(conn *ss.Conn) (host string, 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 + + typeIP = 1 // type is ip address + typeDm = 3 // type is domain address + + lenIP = 1 + 4 + 2 // 1addrType + 4ip + 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, 260, 260) + cur := 0 // current location in buf + + // first read the complete request, may read extra bytes for { - var _ int - buf := make([]byte, 1) - _, err = conn.Read(buf) - if err != nil { - hasError = true - break + // hopefully, we should only need one read to get the complete request + // this read normally will read just the request, no extra data + ss.SetReadTimeout(conn) + var n int + if n, err = conn.Read(buf[cur:]); err != nil { + // debug.Println("read request error:", err) + return } - addrType := buf[0] - var addr string - var port int16 - if addrType == 1 { - buf = make([]byte, 6) - _, err = conn.Read(buf) - if err != nil { - hasError = true + cur += n + if buf[idType] == typeIP { + if cur >= lenIP { + // debug.Println("ip request complete, cur:", cur) break } - var addrIp net.IP = make(net.IP, 4) - copy(addrIp, buf[0:4]) - addr = addrIp.String() - sb := bytes.NewBuffer(buf[4:6]) - binary.Read(sb, binary.BigEndian, &port) - } else if addrType == 3 { - _, err = conn.Read(buf) - if err != nil { - hasError = true - break + } else if buf[idType] == typeDm { + if cur < idDmLen+1 { // read until we get address length byte + continue } - addrLen := buf[0] - buf = make([]byte, addrLen+2) - _, err = conn.Read(buf) - if err != nil { - hasError = true + if cur >= lenDmBase+int(buf[idDmLen]) { + // debug.Println("domain request complete, cur:", cur) break } - sb := bytes.NewBuffer(buf[0:addrLen]) - addr = sb.String() - sb = bytes.NewBuffer(buf[addrLen : addrLen+2]) - binary.Read(sb, binary.BigEndian, &port) } else { - hasError = true - log.Println("unsurpported addr type") - break - } - log.Println("connecting ", addr) - var remote net.Conn - remote, err = net.Dial("tcp", fmt.Sprintf("%s:%d", addr, port)) - if err != nil { - hasError = true - break + err = errAddr + return } - if err != nil { - hasError = true - break - } - c := make(chan int, 2) - go shadowsocks.Pipe(conn, remote, c) - go shadowsocks.Pipe(remote, conn, c) - <-c // close the other connection whenever one connection is closed - log.Println("closing") - err = conn.Close() - err1 := remote.Close() - if err == nil { - err = err1 - } - break + // debug.Println("request not complete, cur:", cur) } - if err != nil || hasError { - if err != nil { - log.Println("error ", err) - } - err = conn.Close() - if err != nil { - log.Println("close:", err) - } + + reqLen := lenIP // default to IP request length + if buf[idType] == typeIP { + addrIp := make(net.IP, 4) + copy(addrIp, buf[idIP0:idIP0+4]) + host = addrIp.String() + } else if buf[idType] == typeDm { + reqLen = lenDmBase + int(buf[idDmLen]) + host = string(buf[idDm0 : idDm0+buf[idDmLen]]) + } + var port int16 + sb := bytes.NewBuffer(buf[reqLen-2 : reqLen]) + binary.Read(sb, binary.BigEndian, &port) + + // debug.Println("requesting:", host, "header len", reqLen) + host += ":" + strconv.Itoa(int(port)) + if cur > reqLen { + extra = buf[reqLen:cur] + // debug.Println("extra:", string(extra)) + } + return +} + +func handleConnection(conn *ss.Conn) { + if debug { + // function arguments are always evaluated, so surround debug + // statement with if statement + debug.Printf("socks connect from %s\n", conn.RemoteAddr().String()) + } + defer conn.Close() + + host, extra, err := getRequest(conn) + if err != nil { + log.Println("error getting request:", err) return } + debug.Println("connecting", host) + remote, err := net.Dial("tcp", host) + if err != nil { + debug.Println("error connecting to:", host, err) + return + } + defer remote.Close() + // write extra bytes read from + if extra != nil { + debug.Println("writing extra content to remote, len", len(extra)) + if _, err = remote.Write(extra); err != nil { + debug.Println("write request extra error:", err) + return + } + } + debug.Println("piping", host) + c := make(chan byte, 2) + go ss.Pipe(conn, remote, c) + go ss.Pipe(remote, conn, c) + <-c // close the other connection whenever one connection is closed + debug.Println("closing", host) + return +} + +// Add a encrypt table cache to save memory and startup time in case of many +// same password. +// If startup time becomes an issue, save the encrypt table on disk. +var tableCache = map[string]*ss.EncryptTable{} +var tableGetCnt int32 +func getTable(password string) (tbl *ss.EncryptTable) { + tbl, ok := tableCache[password] + if ok { + debug.Println("table cache hit for password:", password) + return + } + tbl = ss.GetTable(password) + tableCache[password] = tbl + return } -func run(port int) { - ln, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) +func run(port, password string) { + ln, err := net.Listen("tcp", ":"+port) if err != nil { log.Fatal(err) } - log.Printf("starting server at port %d ...\n", port) + encTbl := getTable(password) + atomic.AddInt32(&tableGetCnt, 1) + log.Printf("starting server at port %v ...\n", port) for { conn, err := ln.Accept() if err != nil { log.Println("accept:", err) continue } - go handleConnection(shadowsocks.Conn{conn}) + go handleConnection(ss.NewConn(conn, encTbl)) } } func main() { - config := shadowsocks.ParseConfig() - shadowsocks.InitTable(config.Password) - run(config.ServerPort) + var configFile string + flag.StringVar(&configFile, "c", "config.json", "specify config file") + flag.Parse() + + config := ss.ParseConfig(configFile) + debug = ss.Debug + if len(config.PortPassword) == 0 { + run(strconv.Itoa(config.ServerPort), config.Password) + } else { + if config.ServerPort != 0 { + log.Println("ignoring server_port and password option, only uses port_password") + } + for port, password := range config.PortPassword { + go run(port, password) + } + // Wait all ports have get it's encryption table + for int(tableGetCnt) != len(config.PortPassword) { + time.Sleep(1 * time.Second) + } + log.Println("all ports ready") + tableCache = nil // release memory + c := make(chan byte) + <-c // block forever + } } diff --git a/cmd/shadowsocks-test/test.go b/cmd/shadowsocks-test/test.go deleted file mode 100644 index d2b1d09..0000000 --- a/cmd/shadowsocks-test/test.go +++ /dev/null @@ -1,53 +0,0 @@ -package main - -import ( - "github.com/shadowsocks/shadowsocks-go/shadowsocks" - "log" -) - -func test1() bool { - target1 := []byte{60, 53, 84, 138, 217, 94, 88, 23, 39, 242, 219, 35, 12, 157, 165, 181, 255, 143, 83, 247, 162, 16, 31, 209, 190, 171, 115, 65, 38, 41, 21, 245, 236, 46, 121, 62, 166, 233, 44, 154, 153, 145, 230, 49, 128, 216, 173, 29, 241, 119, 64, 229, 194, 103, 131, 110, 26, 197, 218, 59, 204, 56, 27, 34, 141, 221, 149, 239, 192, 195, 24, 155, 170, 183, 11, 254, 213, 37, 137, 226, 75, 203, 55, 19, 72, 248, 22, 129, 33, 175, 178, 10, 198, 71, 77, 36, 113, 167, 48, 2, 117, 140, 142, 66, 199, 232, 243, 32, 123, 54, 51, 82, 57, 177, 87, 251, 150, 196, 133, 5, 253, 130, 8, 184, 14, 152, 231, 3, 186, 159, 76, 89, 228, 205, 156, 96, 163, 146, 18, 91, 132, 85, 80, 109, 172, 176, 105, 13, 50, 235, 127, 0, 189, 95, 98, 136, 250, 200, 108, 179, 211, 214, 106, 168, 78, 79, 74, 210, 30, 73, 201, 151, 208, 114, 101, 174, 92, 52, 120, 240, 15, 169, 220, 182, 81, 224, 43, 185, 40, 99, 180, 17, 212, 158, 42, 90, 9, 191, 45, 6, 25, 4, 222, 67, 126, 1, 116, 124, 206, 69, 61, 7, 68, 97, 202, 63, 244, 20, 28, 58, 93, 134, 104, 144, 227, 147, 102, 118, 135, 148, 47, 238, 86, 112, 122, 70, 107, 215, 100, 139, 223, 225, 164, 237, 111, 125, 207, 160, 187, 246, 234, 161, 188, 193, 249, 252 } - target2 := []byte{ 151, 205, 99, 127, 201, 119, 199, 211, 122, 196, 91, 74, 12, 147, 124, 180, 21, 191, 138, 83, 217, 30, 86, 7, 70, 200, 56, 62, 218, 47, 168, 22, 107, 88, 63, 11, 95, 77, 28, 8, 188, 29, 194, 186, 38, 198, 33, 230, 98, 43, 148, 110, 177, 1, 109, 82, 61, 112, 219, 59, 0, 210, 35, 215, 50, 27, 103, 203, 212, 209, 235, 93, 84, 169, 166, 80, 130, 94, 164, 165, 142, 184, 111, 18, 2, 141, 232, 114, 6, 131, 195, 139, 176, 220, 5, 153, 135, 213, 154, 189, 238, 174, 226, 53, 222, 146, 162, 236, 158, 143, 55, 244, 233, 96, 173, 26, 206, 100, 227, 49, 178, 34, 234, 108, 207, 245, 204, 150, 44, 87, 121, 54, 140, 118, 221, 228, 155, 78, 3, 239, 101, 64, 102, 17, 223, 41, 137, 225, 229, 66, 116, 171, 125, 40, 39, 71, 134, 13, 193, 129, 247, 251, 20, 136, 242, 14, 36, 97, 163, 181, 72, 25, 144, 46, 175, 89, 145, 113, 90, 159, 190, 15, 183, 73, 123, 187, 128, 248, 252, 152, 24, 197, 68, 253, 52, 69, 117, 57, 92, 104, 157, 170, 214, 81, 60, 133, 208, 246, 172, 23, 167, 160, 192, 76, 161, 237, 45, 4, 58, 10, 182, 65, 202, 240, 185, 241, 79, 224, 132, 51, 42, 126, 105, 37, 250, 149, 32, 243, 231, 67, 179, 48, 9, 106, 216, 31, 249, 19, 85, 254, 156, 115, 255, 120, 75, 16 } - normal := true - encyrptTable, decryptTable := shadowsocks.GetTable("foobar!") - for i := 0; i < 256; i++ { - if target1[i] != encyrptTable[i] { - normal = false - break - } - if target2[i] != decryptTable[i] { - normal = false - break - } - } - return normal -} - -func test2() bool { - target1 := []byte{124, 30, 170, 247, 27, 127, 224, 59, 13, 22, 196, 76, 72, 154, 32, 209, 4, 2, 131, 62, 101, 51, 230, 9, 166, 11, 99 , 80, 208, 112, 36, 248, 81, 102, 130, 88, 218, 38, 168, 15, 241, 228, 167, 117, 158, 41, 10, 180, 194, 50, 204, 243, 246, 251, 29, 198, 219, 210, 195, 21, 54, 91, 203, 221, 70, 57, 183, 17, 147, 49, 133, 65, 77, 55, 202, 122, 162, 169, 188, 200, 190, 125, 63, 244, 96, 31, 107, 106, 74, 143, 116, 148, 78, 46, 1, 137, 150, 110, 181, 56, 95, 139, 58, 3, 231, 66, 165, 142, 242, 43, 192, 157, 89, 175, 109, 220, 128, 0, 178, 42, 255, 20, 214, 185, 83, 160, 253, 7, 23, 92, 111, 153, 26, 226, 33, 176, 144, 18, 216, 212, 28, 151, 71, 206, 222, 182, 8, 174, 205, 201, 152, 240, 155, 108, 223, 104, 239, 98, 164, 211, 184, 34, 193, 14, 114, 187, 40, 254, 12, 67, 93, 217, 6, 94, 16, 19, 82 , 86, 245, 24, 197, 134, 132, 138, 229, 121, 5, 235, 238, 85, 47, 103, 113, 179, 69, 250, 45, 135, 156, 25, 61, 75, 44, 146, 189, 84, 207, 172, 119, 53, 123, 186, 120, 171, 68, 227, 145, 136, 100, 90, 48, 79, 159, 149, 39, 213, 236, 126, 52, 60, 225, 199, 105, 73, 233, 252, 118, 215, 35, 115, 64, 37, 97, 129, 161, 177, 87, 237, 141, 173, 191, 163, 140, 234, 232, 249 } - target2 := []byte{ 117, 94, 17, 103, 16, 186, 172, 127, 146, 23, 46, 25, 168, 8, 163, 39, 174, 67, 137, 175, 121, 59, 9, 128, 179, 199 , 132, 4, 140, 54, 1, 85, 14, 134, 161, 238, 30, 241, 37, 224, 166, 45, 119, 109, 202, 196, 93, 190, 220, 69, 49 , 21, 228, 209, 60, 73, 99, 65, 102, 7, 229, 200, 19, 82, 240, 71, 105, 169, 214, 194, 64, 142, 12, 233, 88, 201 , 11, 72, 92, 221, 27, 32, 176, 124, 205, 189, 177, 246, 35, 112, 219, 61, 129, 170, 173, 100, 84, 242, 157, 26, 218, 20, 33, 191, 155, 232, 87, 86, 153, 114, 97, 130, 29, 192, 164, 239, 90, 43, 236, 208, 212, 185, 75, 210, 0, 81, 227, 5, 116, 243, 34, 18, 182, 70, 181, 197, 217, 95, 183, 101, 252, 248, 107, 89, 136, 216, 203, 68, 91, 223, 96, 141, 150, 131, 13, 152, 198, 111, 44, 222, 125, 244, 76, 251, 158, 106, 24, 42, 38, 77, 2, 213, 207, 249, 147, 113, 135, 245, 118, 193, 47, 98, 145, 66, 160, 123, 211, 165, 78, 204, 80, 250, 110, 162, 48, 58, 10, 180, 55, 231, 79, 149, 74, 62, 50, 148, 143, 206, 28, 15, 57, 159, 139, 225, 122, 237, 138, 171, 36, 56, 115, 63, 144, 154, 6, 230, 133, 215, 41, 184, 22, 104, 254, 234, 253, 187, 226, 247, 188, 156, 151, 40, 108, 51, 83, 178, 52, 3, 31, 255, 195, 53, 235, 126, 167, 120 } - normal := true - encyrptTable, decryptTable := shadowsocks.GetTable("barfoo!") - for i := 0; i < 256; i++ { - if target1[i] != encyrptTable[i] { - normal = false - break - } - if target2[i] != decryptTable[i] { - normal = false - break - } - } - return normal -} - -func main() { - - if test1() && test2() { - - log.Println("test passed") - } else { - log.Println("test failed") - } - -} diff --git a/config.json b/config.json index 0548439..02a39cc 100644 --- a/config.json +++ b/config.json @@ -3,5 +3,10 @@ "server_port":8388, "local_port":1080, "password":"barfoo!", - "timeout":60 -} \ No newline at end of file + "port_password": { + "8388": "barfoo!", + "8387": "foobar!" + }, + "timeout":60, + "debug":true +} diff --git a/shadowsocks/config.go b/shadowsocks/config.go index 5ba4b0c..21b70be 100644 --- a/shadowsocks/config.go +++ b/shadowsocks/config.go @@ -9,33 +9,39 @@ package shadowsocks import ( "encoding/json" - "os" + "io/ioutil" "log" + "os" + "time" ) + type Config struct { - Server string `json:"server"` - ServerPort int `json:"server_port"` - LocalPort int `json:"local_port"` - Password string `json:"password"` + Server string `json:"server"` + ServerPort int `json:"server_port"` + LocalPort int `json:"local_port"` + Password string `json:"password"` + PortPassword map[string]string `json:"port_password"` + Timeout int `json:"timeout"` + Debug bool `json:"debug"` } -func ParseConfig() Config { - file, err := os.Open("config.json") // For read access. +var readTimeout time.Duration + +func ParseConfig(path string) *Config { + file, err := os.Open(path) // For read access. if err != nil { - log.Fatal("error opening config file config.json:", err) + log.Fatal("error opening config file:", err) } - data := make([]byte, 4096) - count, err := file.Read(data) + defer file.Close() + data, err := ioutil.ReadAll(file) if err != nil { - log.Fatal("error reading config:", err) - } - if count == 4096 { - log.Fatal("config file is too large") + log.Fatalln("error reading config:", err) } var config Config - err = json.Unmarshal(data[0:count], &config) - if err != nil { - log.Fatal("can not parse config:",err) + if err = json.Unmarshal(data, &config); err != nil { + log.Fatalln("can not parse config:", err) } - return config + Debug = DebugLog(config.Debug) + readTimeout = time.Duration(config.Timeout) * time.Second + return &config } diff --git a/shadowsocks/config_test.go b/shadowsocks/config_test.go new file mode 100644 index 0000000..7e48e3e --- /dev/null +++ b/shadowsocks/config_test.go @@ -0,0 +1,30 @@ +package shadowsocks + +import ( + "testing" +) + +func TestParseConfig1Password(t *testing.T) { + config := ParseConfig("testdata/config-one-passwd.json") + + if config.Password != "barfoo!" { + t.Error("wrong password from config") + } + if config.Debug != true { + t.Error("debug option wrong") + } + if config.Timeout != 60 { + t.Error("tiemout wrong") + } +} + +func TestParseConfigMultiPassword(t *testing.T) { + config := ParseConfig("testdata/config-multi-passwd.json") + + if config.Password != "barfoo!" { + t.Error("wrong password from config") + } + if config.PortPassword["8387"] != "foobar" { + t.Error("wrong multiple password for port 8387") + } +} diff --git a/shadowsocks/conn.go b/shadowsocks/conn.go index 0fcce98..8f94391 100644 --- a/shadowsocks/conn.go +++ b/shadowsocks/conn.go @@ -8,20 +8,16 @@ import ( "strings" ) -var ( - encTable []byte - decTable []byte -) - type Conn struct { net.Conn + *EncryptTable } -func InitTable(passwd string) { - encTable, decTable = GetTable(passwd) +func NewConn(cn net.Conn, encTbl *EncryptTable) *Conn { + return &Conn{cn, encTbl} } -func addrBufFromString(addr string) (buf []byte, err error) { +func rawAddr(addr string) (buf []byte, err error) { arr := strings.Split(addr, ":") if len(arr) != 2 { return nil, errors.New( @@ -37,51 +33,50 @@ func addrBufFromString(addr string) (buf []byte, err error) { hostLen := len(host) l := 1 + 1 + hostLen + 2 // addrType + lenByte + address + port buf = make([]byte, l, l) - buf[0] = 3 - buf[1] = byte(hostLen) + buf[0] = 3 // 3 means the address is domain name + buf[1] = byte(hostLen) // host address length followed by host address copy(buf[2:], host) - buf[2+hostLen] = byte(port >> 8 & 0xFF) + buf[2+hostLen] = byte(port >> 8 & 0xFF) // the next 2 bytes are port buf[2+hostLen+1] = byte(port) & 0xFF return } -// Export this for use by local.go and server.go -func DialWithAddrBuf(addrBuf []byte, server string) (c Conn, err error) { - if encTable == nil { - panic("shadowsocks internal error, must call InitTable first.") - } +// This is intended for use by users implementing a local socks proxy. +// rawaddr shoud contain part of the data in socks request, starting from the +// ATYP field. (Refer to rfc1928 for more information.) +func DialWithRawAddr(rawaddr []byte, server string, encTbl *EncryptTable) (c *Conn, err error) { conn, err := net.Dial("tcp", server) if err != nil { return } - c = Conn{conn} - if _, err = c.Write(addrBuf); err != nil { + c = NewConn(conn, encTbl) + if _, err = c.Write(rawaddr); err != nil { c.Close() - return + return nil, err } return } // addr should be in the form of host:port -func Dial(addr string, server string) (c Conn, err error) { - addrBuf, err := addrBufFromString(addr) +func Dial(addr, server string, encTbl *EncryptTable) (c *Conn, err error) { + ra, err := rawAddr(addr) if err != nil { return } - return DialWithAddrBuf(addrBuf, server) + return DialWithRawAddr(ra, server, encTbl) } func (c Conn) Read(b []byte) (n int, err error) { buf := make([]byte, len(b), len(b)) n, err = c.Conn.Read(buf) if n > 0 { - Encrypt2(decTable, buf[0:n], b[0:n]) + encrypt2(c.decTbl, buf[0:n], b[0:n]) } return } func (c Conn) Write(b []byte) (n int, err error) { - buf := Encrypt(encTable, b) + buf := encrypt(c.encTbl, b) n, err = c.Conn.Write(buf) return } diff --git a/shadowsocks/encrypt.go b/shadowsocks/encrypt.go index f56a971..c7dcda8 100644 --- a/shadowsocks/encrypt.go +++ b/shadowsocks/encrypt.go @@ -7,10 +7,18 @@ import ( "io" ) -func GetTable(key string) (encryptTable []byte, decryptTable []byte) { - encryptTable = make([]byte, 256) - decryptTable = make([]byte, 256) - table := make([]uint64, 256) +type EncryptTable struct { + encTbl []byte + decTbl []byte +} + +func GetTable(key string) (tbl *EncryptTable) { + const tbl_size = 256 + tbl = &EncryptTable{ + make([]byte, tbl_size, tbl_size), + make([]byte, tbl_size, tbl_size), + } + table := make([]uint64, tbl_size, tbl_size) h := md5.New() io.WriteString(h, key) @@ -21,7 +29,7 @@ func GetTable(key string) (encryptTable []byte, decryptTable []byte) { buf := bytes.NewBuffer(s) binary.Read(buf, binary.LittleEndian, &a) var i uint64 - for i = 0; i < 256; i++ { + for i = 0; i < tbl_size; i++ { table[i] = i } for i = 1; i < 1024; i++ { @@ -29,24 +37,23 @@ func GetTable(key string) (encryptTable []byte, decryptTable []byte) { return int64(a%uint64(x+i) - a%uint64(y+i)) }) } - for i = 0; i < 256; i++ { - encryptTable[i] = byte(table[i]) + for i = 0; i < tbl_size; i++ { + tbl.encTbl[i] = byte(table[i]) } - for i = 0; i < 256; i++ { - decryptTable[encryptTable[i]] = byte(i) + for i = 0; i < tbl_size; i++ { + tbl.decTbl[tbl.encTbl[i]] = byte(i) } - return } -func Encrypt2(table []byte, buf, result []byte) { +func encrypt2(table []byte, buf, result []byte) { for i := 0; i < len(buf); i++ { result[i] = table[buf[i]] } } -func Encrypt(table []byte, buf []byte) []byte { +func encrypt(table []byte, buf []byte) []byte { var result = make([]byte, len(buf), len(buf)) - Encrypt2(table, buf, result) + encrypt2(table, buf, result) return result } diff --git a/shadowsocks/encrypt_test.go b/shadowsocks/encrypt_test.go new file mode 100644 index 0000000..7758493 --- /dev/null +++ b/shadowsocks/encrypt_test.go @@ -0,0 +1,32 @@ +package shadowsocks + +import ( + "testing" +) + +const tbl_size = 256 + +func checkTable(t *testing.T, tbl *EncryptTable, encTarget, decTarget []byte, msg string) { + for i := 0; i < tbl_size; i++ { + if encTarget[i] != tbl.encTbl[i] { + t.Fatalf("%s: encrypt table error at index %d\n", msg, i) + } + if decTarget[i] != tbl.decTbl[i] { + t.Fatalf("%s: decrypt table error at index %d\n", msg, i) + } + } +} + +func TestEncrypt1(t *testing.T) { + enc := []byte{60, 53, 84, 138, 217, 94, 88, 23, 39, 242, 219, 35, 12, 157, 165, 181, 255, 143, 83, 247, 162, 16, 31, 209, 190, 171, 115, 65, 38, 41, 21, 245, 236, 46, 121, 62, 166, 233, 44, 154, 153, 145, 230, 49, 128, 216, 173, 29, 241, 119, 64, 229, 194, 103, 131, 110, 26, 197, 218, 59, 204, 56, 27, 34, 141, 221, 149, 239, 192, 195, 24, 155, 170, 183, 11, 254, 213, 37, 137, 226, 75, 203, 55, 19, 72, 248, 22, 129, 33, 175, 178, 10, 198, 71, 77, 36, 113, 167, 48, 2, 117, 140, 142, 66, 199, 232, 243, 32, 123, 54, 51, 82, 57, 177, 87, 251, 150, 196, 133, 5, 253, 130, 8, 184, 14, 152, 231, 3, 186, 159, 76, 89, 228, 205, 156, 96, 163, 146, 18, 91, 132, 85, 80, 109, 172, 176, 105, 13, 50, 235, 127, 0, 189, 95, 98, 136, 250, 200, 108, 179, 211, 214, 106, 168, 78, 79, 74, 210, 30, 73, 201, 151, 208, 114, 101, 174, 92, 52, 120, 240, 15, 169, 220, 182, 81, 224, 43, 185, 40, 99, 180, 17, 212, 158, 42, 90, 9, 191, 45, 6, 25, 4, 222, 67, 126, 1, 116, 124, 206, 69, 61, 7, 68, 97, 202, 63, 244, 20, 28, 58, 93, 134, 104, 144, 227, 147, 102, 118, 135, 148, 47, 238, 86, 112, 122, 70, 107, 215, 100, 139, 223, 225, 164, 237, 111, 125, 207, 160, 187, 246, 234, 161, 188, 193, 249, 252} + dec := []byte{151, 205, 99, 127, 201, 119, 199, 211, 122, 196, 91, 74, 12, 147, 124, 180, 21, 191, 138, 83, 217, 30, 86, 7, 70, 200, 56, 62, 218, 47, 168, 22, 107, 88, 63, 11, 95, 77, 28, 8, 188, 29, 194, 186, 38, 198, 33, 230, 98, 43, 148, 110, 177, 1, 109, 82, 61, 112, 219, 59, 0, 210, 35, 215, 50, 27, 103, 203, 212, 209, 235, 93, 84, 169, 166, 80, 130, 94, 164, 165, 142, 184, 111, 18, 2, 141, 232, 114, 6, 131, 195, 139, 176, 220, 5, 153, 135, 213, 154, 189, 238, 174, 226, 53, 222, 146, 162, 236, 158, 143, 55, 244, 233, 96, 173, 26, 206, 100, 227, 49, 178, 34, 234, 108, 207, 245, 204, 150, 44, 87, 121, 54, 140, 118, 221, 228, 155, 78, 3, 239, 101, 64, 102, 17, 223, 41, 137, 225, 229, 66, 116, 171, 125, 40, 39, 71, 134, 13, 193, 129, 247, 251, 20, 136, 242, 14, 36, 97, 163, 181, 72, 25, 144, 46, 175, 89, 145, 113, 90, 159, 190, 15, 183, 73, 123, 187, 128, 248, 252, 152, 24, 197, 68, 253, 52, 69, 117, 57, 92, 104, 157, 170, 214, 81, 60, 133, 208, 246, 172, 23, 167, 160, 192, 76, 161, 237, 45, 4, 58, 10, 182, 65, 202, 240, 185, 241, 79, 224, 132, 51, 42, 126, 105, 37, 250, 149, 32, 243, 231, 67, 179, 48, 9, 106, 216, 31, 249, 19, 85, 254, 156, 115, 255, 120, 75, 16} + tbl := GetTable("foobar!") + checkTable(t, tbl, enc, dec, "Error for password foobar!") +} + +func TestEncrypt2(t *testing.T) { + enc := []byte{124, 30, 170, 247, 27, 127, 224, 59, 13, 22, 196, 76, 72, 154, 32, 209, 4, 2, 131, 62, 101, 51, 230, 9, 166, 11, 99, 80, 208, 112, 36, 248, 81, 102, 130, 88, 218, 38, 168, 15, 241, 228, 167, 117, 158, 41, 10, 180, 194, 50, 204, 243, 246, 251, 29, 198, 219, 210, 195, 21, 54, 91, 203, 221, 70, 57, 183, 17, 147, 49, 133, 65, 77, 55, 202, 122, 162, 169, 188, 200, 190, 125, 63, 244, 96, 31, 107, 106, 74, 143, 116, 148, 78, 46, 1, 137, 150, 110, 181, 56, 95, 139, 58, 3, 231, 66, 165, 142, 242, 43, 192, 157, 89, 175, 109, 220, 128, 0, 178, 42, 255, 20, 214, 185, 83, 160, 253, 7, 23, 92, 111, 153, 26, 226, 33, 176, 144, 18, 216, 212, 28, 151, 71, 206, 222, 182, 8, 174, 205, 201, 152, 240, 155, 108, 223, 104, 239, 98, 164, 211, 184, 34, 193, 14, 114, 187, 40, 254, 12, 67, 93, 217, 6, 94, 16, 19, 82, 86, 245, 24, 197, 134, 132, 138, 229, 121, 5, 235, 238, 85, 47, 103, 113, 179, 69, 250, 45, 135, 156, 25, 61, 75, 44, 146, 189, 84, 207, 172, 119, 53, 123, 186, 120, 171, 68, 227, 145, 136, 100, 90, 48, 79, 159, 149, 39, 213, 236, 126, 52, 60, 225, 199, 105, 73, 233, 252, 118, 215, 35, 115, 64, 37, 97, 129, 161, 177, 87, 237, 141, 173, 191, 163, 140, 234, 232, 249} + dec := []byte{117, 94, 17, 103, 16, 186, 172, 127, 146, 23, 46, 25, 168, 8, 163, 39, 174, 67, 137, 175, 121, 59, 9, 128, 179, 199, 132, 4, 140, 54, 1, 85, 14, 134, 161, 238, 30, 241, 37, 224, 166, 45, 119, 109, 202, 196, 93, 190, 220, 69, 49, 21, 228, 209, 60, 73, 99, 65, 102, 7, 229, 200, 19, 82, 240, 71, 105, 169, 214, 194, 64, 142, 12, 233, 88, 201, 11, 72, 92, 221, 27, 32, 176, 124, 205, 189, 177, 246, 35, 112, 219, 61, 129, 170, 173, 100, 84, 242, 157, 26, 218, 20, 33, 191, 155, 232, 87, 86, 153, 114, 97, 130, 29, 192, 164, 239, 90, 43, 236, 208, 212, 185, 75, 210, 0, 81, 227, 5, 116, 243, 34, 18, 182, 70, 181, 197, 217, 95, 183, 101, 252, 248, 107, 89, 136, 216, 203, 68, 91, 223, 96, 141, 150, 131, 13, 152, 198, 111, 44, 222, 125, 244, 76, 251, 158, 106, 24, 42, 38, 77, 2, 213, 207, 249, 147, 113, 135, 245, 118, 193, 47, 98, 145, 66, 160, 123, 211, 165, 78, 204, 80, 250, 110, 162, 48, 58, 10, 180, 55, 231, 79, 149, 74, 62, 50, 148, 143, 206, 28, 15, 57, 159, 139, 225, 122, 237, 138, 171, 36, 56, 115, 63, 144, 154, 6, 230, 133, 215, 41, 184, 22, 104, 254, 234, 253, 187, 226, 247, 188, 156, 151, 40, 108, 51, 83, 178, 52, 3, 31, 255, 195, 53, 235, 126, 167, 120} + tbl := GetTable("barfoo!") + checkTable(t, tbl, enc, dec, "Error for password barfoo!") +} diff --git a/shadowsocks/log.go b/shadowsocks/log.go new file mode 100644 index 0000000..5b1c960 --- /dev/null +++ b/shadowsocks/log.go @@ -0,0 +1,24 @@ +package shadowsocks + +import ( + "log" + "os" +) + +type DebugLog bool + +var Debug DebugLog + +var dbgLog = log.New(os.Stdout, "[DEBUG] ", log.Ltime) + +func (d DebugLog) Printf(format string, args ...interface{}) { + if d { + dbgLog.Printf(format, args...) + } +} + +func (d DebugLog) Println(args ...interface{}) { + if d { + dbgLog.Println(args...) + } +} diff --git a/shadowsocks/pipe.go b/shadowsocks/pipe.go index 3355ff4..963d196 100644 --- a/shadowsocks/pipe.go +++ b/shadowsocks/pipe.go @@ -1,29 +1,39 @@ package shadowsocks import ( - "log" + "io" "net" + "time" ) -func Pipe(src net.Conn, dst net.Conn, end chan int) { +func SetReadTimeout(c net.Conn) { + c.SetReadDeadline(time.Now().Add(readTimeout)) +} + +func Pipe(src, dst net.Conn, end chan byte) { + // Should not use io.Copy here. + // io.Copy will try to use the ReadFrom interface of TCPConn, but the src + // here is not a regular file, so sendfile is not applicable. + // io.Copy will fallback to the normal copy after discovering this, + // introducing unnecessary overhead. buf := make([]byte, 4096) for { - num, err := src.Read(buf) - if err == nil { - _, err := dst.Write(buf[0:num]) - if err != nil { - log.Println("write:", err) - end <- 1 - return + SetReadTimeout(src) + n, err := src.Read(buf) + // read may return EOF with n > 0 + // should always process n > 0 bytes before handling error + if n > 0 { + if _, err = dst.Write(buf[0:n]); err != nil { + Debug.Println("write:", err) + break } - } else { - log.Println("read:", err) - end <- 1 - return } - if num == 0 { - end <- 1 - return + if err != nil { + if err != io.EOF { + Debug.Println("read:", err) + } + break } } + end <- 1 } diff --git a/shadowsocks/testdata/config-multi-passwd.json b/shadowsocks/testdata/config-multi-passwd.json new file mode 100644 index 0000000..2d2aa3a --- /dev/null +++ b/shadowsocks/testdata/config-multi-passwd.json @@ -0,0 +1,11 @@ +{ + "server":"127.0.0.1", + "server_port":8388, + "local_port":1081, + "password":"barfoo!", + "port_password": { + "8387": "foobar", + "8389": "!foobar" + }, + "timeout":60 +} diff --git a/shadowsocks/testdata/config-one-passwd.json b/shadowsocks/testdata/config-one-passwd.json new file mode 100644 index 0000000..4888e49 --- /dev/null +++ b/shadowsocks/testdata/config-one-passwd.json @@ -0,0 +1,8 @@ +{ + "server":"127.0.0.1", + "server_port":8388, + "local_port":1081, + "password":"barfoo!", + "timeout":60, + "debug": true +} diff --git a/testscript/curl.sh b/testscript/curl.sh new file mode 100755 index 0000000..63b02e0 --- /dev/null +++ b/testscript/curl.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +if [ $# != 3 ]; then + echo "$0 " + exit 1 +fi + +socks=$1 +n=$2 +url=$3 + +for i in `seq 1 $n`; do + curl -s --socks5 $socks $url >/dev/null + if [ $(($i % 1000)) -eq 0 ]; then + echo "finished $i request" + fi +done +