diff --git a/utils/ss/util.go b/utils/ss/util.go index 45a3787..5a87120 100644 --- a/utils/ss/util.go +++ b/utils/ss/util.go @@ -1,7 +1,11 @@ package ss import ( + "encoding/binary" + "fmt" + "io" "net" + "strconv" "github.com/snail007/goproxy/utils" ) @@ -26,3 +30,53 @@ const ( lenDmBase = 2 // 1addrLen + 2port, plus addrLen lenHmacSha1 = 10 ) + +func GetRequest(conn *Conn) (host string, 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) + 255(max length address) + 2(port) + 10(hmac-sha1) + buf := make([]byte, 269) + // read till we get possible domain length field + if _, err = io.ReadFull(conn, buf[:idType+1]); err != nil { + return + } + + var reqStart, reqEnd int + addrType := buf[idType] + switch addrType & AddrMask { + case typeIPv4: + reqStart, reqEnd = idIP0, idIP0+lenIPv4 + case typeIPv6: + reqStart, reqEnd = idIP0, idIP0+lenIPv6 + case typeDm: + if _, err = io.ReadFull(conn, buf[idType+1:idDmLen+1]); err != nil { + return + } + reqStart, reqEnd = idDm0, idDm0+int(buf[idDmLen])+lenDmBase + default: + err = fmt.Errorf("addr type %d not supported", addrType&AddrMask) + return + } + + if _, err = io.ReadFull(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 addrType & AddrMask { + case typeIPv4: + host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String() + case typeIPv6: + host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String() + case typeDm: + host = string(buf[idDm0 : idDm0+int(buf[idDmLen])]) + } + // parse port + port := binary.BigEndian.Uint16(buf[reqEnd-2 : reqEnd]) + host = net.JoinHostPort(host, strconv.Itoa(int(port))) + + return +}