func (s *ServerSession) Handshake(reader io.Reader, writer io.Writer) (*protocol.RequestHeader, error) { buffer := buf.NewLocal(512) request := new(protocol.RequestHeader) if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 2)); err != nil { return nil, errors.Base(err).Message("Socks|Server: Insufficient header.") } version := buffer.Byte(0) if version == socks4Version { if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 6)); err != nil { return nil, errors.Base(err).Message("Socks|Server: Insufficient header.") } port := v2net.PortFromBytes(buffer.BytesRange(2, 4)) address := v2net.IPAddress(buffer.BytesRange(4, 8)) _, err := readUntilNull(reader) // user id if err != nil { return nil, err } if address.IP()[0] == 0x00 { domain, err := readUntilNull(reader) if err != nil { return nil, errors.Base(err).Message("Socks|Server: Failed to read domain for socks 4a.") } address = v2net.DomainAddress(domain) } switch buffer.Byte(1) { case cmdTCPConnect: request.Command = protocol.RequestCommandTCP request.Address = address request.Port = port request.Version = socks4Version if err := writeSocks4Response(writer, socks4RequestGranted, v2net.AnyIP, v2net.Port(0)); err != nil { return nil, err } return request, nil default: writeSocks4Response(writer, socks4RequestRejected, v2net.AnyIP, v2net.Port(0)) return nil, errors.New("Socks|Server: Unsupported command: ", buffer.Byte(1)) } } if version == socks5Version { nMethod := int(buffer.Byte(1)) if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, nMethod)); err != nil { return nil, err } var expectedAuth byte = authNotRequired if s.config.AuthType == AuthType_PASSWORD { expectedAuth = authPassword } if !hasAuthMethod(expectedAuth, buffer.BytesRange(2, 2+nMethod)) { writeSocks5AuthenticationResponse(writer, authNoMatchingMethod) return nil, errors.New("Socks|Server: No matching auth method.") } if err := writeSocks5AuthenticationResponse(writer, expectedAuth); err != nil { return nil, err } if expectedAuth == authPassword { username, password, err := readUsernamePassword(reader) if err != nil { return nil, errors.Base(err).Message("Socks|Server: Failed to read username and password for authentication.") } if !s.config.HasAccount(username, password) { writeSocks5AuthenticationResponse(writer, 0xFF) return nil, errors.New("Socks|Server: Invalid username or password.") } if err := writeSocks5AuthenticationResponse(writer, 0x00); err != nil { return nil, err } } buffer.Clear() if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 4)); err != nil { return nil, err } cmd := buffer.Byte(1) if cmd == cmdTCPBind || (cmd == cmdUDPPort && !s.config.UdpEnabled) { writeSocks5Response(writer, statusCmdNotSupport, v2net.AnyIP, v2net.Port(0)) return nil, errors.New("Socks|Server: Unsupported command: ", cmd) } switch cmd { case cmdTCPConnect: request.Command = protocol.RequestCommandTCP case cmdUDPPort: request.Command = protocol.RequestCommandUDP } addrType := buffer.Byte(3) buffer.Clear() request.Version = socks5Version switch addrType { case addrTypeIPv4: if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 4)); err != nil { return nil, err } request.Address = v2net.IPAddress(buffer.Bytes()) case addrTypeIPv6: if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 16)); err != nil { return nil, err } request.Address = v2net.IPAddress(buffer.Bytes()) case addrTypeDomain: if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 1)); err != nil { return nil, err } domainLength := int(buffer.Byte(0)) if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, domainLength)); err != nil { return nil, err } request.Address = v2net.DomainAddress(string(buffer.BytesFrom(-domainLength))) default: return nil, errors.New("Socks|Server: Unknown address type: ", addrType) } if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 2)); err != nil { return nil, err } request.Port = v2net.PortFromBytes(buffer.BytesFrom(-2)) responseAddress := v2net.AnyIP responsePort := v2net.Port(1717) if request.Command == protocol.RequestCommandUDP { addr := s.config.Address.AsAddress() if addr == nil { addr = v2net.LocalHostIP } responseAddress = addr responsePort = s.port } if err := writeSocks5Response(writer, statusSuccess, responseAddress, responsePort); err != nil { return nil, err } return request, nil } return nil, errors.New("Socks|Server: Unknown Socks version: ", version) }