github.com/xmplusdev/xmcore@v1.8.11-0.20240412132628-5518b55526af/proxy/socks/protocol.go (about)

     1  package socks
     2  
     3  import (
     4  	"encoding/binary"
     5  	"io"
     6  
     7  	"github.com/xmplusdev/xmcore/common"
     8  	"github.com/xmplusdev/xmcore/common/buf"
     9  	"github.com/xmplusdev/xmcore/common/net"
    10  	"github.com/xmplusdev/xmcore/common/protocol"
    11  )
    12  
    13  const (
    14  	socks5Version = 0x05
    15  	socks4Version = 0x04
    16  
    17  	cmdTCPConnect    = 0x01
    18  	cmdTCPBind       = 0x02
    19  	cmdUDPAssociate  = 0x03
    20  	cmdTorResolve    = 0xF0
    21  	cmdTorResolvePTR = 0xF1
    22  
    23  	socks4RequestGranted  = 90
    24  	socks4RequestRejected = 91
    25  
    26  	authNotRequired = 0x00
    27  	// authGssAPI           = 0x01
    28  	authPassword         = 0x02
    29  	authNoMatchingMethod = 0xFF
    30  
    31  	statusSuccess       = 0x00
    32  	statusCmdNotSupport = 0x07
    33  )
    34  
    35  var addrParser = protocol.NewAddressParser(
    36  	protocol.AddressFamilyByte(0x01, net.AddressFamilyIPv4),
    37  	protocol.AddressFamilyByte(0x04, net.AddressFamilyIPv6),
    38  	protocol.AddressFamilyByte(0x03, net.AddressFamilyDomain),
    39  )
    40  
    41  type ServerSession struct {
    42  	config       *ServerConfig
    43  	address      net.Address
    44  	port         net.Port
    45  	localAddress net.Address
    46  }
    47  
    48  func (s *ServerSession) handshake4(cmd byte, reader io.Reader, writer io.Writer) (*protocol.RequestHeader, error) {
    49  	if s.config.AuthType == AuthType_PASSWORD {
    50  		writeSocks4Response(writer, socks4RequestRejected, net.AnyIP, net.Port(0))
    51  		return nil, newError("socks 4 is not allowed when auth is required.")
    52  	}
    53  
    54  	var port net.Port
    55  	var address net.Address
    56  
    57  	{
    58  		buffer := buf.StackNew()
    59  		if _, err := buffer.ReadFullFrom(reader, 6); err != nil {
    60  			buffer.Release()
    61  			return nil, newError("insufficient header").Base(err)
    62  		}
    63  		port = net.PortFromBytes(buffer.BytesRange(0, 2))
    64  		address = net.IPAddress(buffer.BytesRange(2, 6))
    65  		buffer.Release()
    66  	}
    67  
    68  	if _, err := ReadUntilNull(reader); /* user id */ err != nil {
    69  		return nil, err
    70  	}
    71  	if address.IP()[0] == 0x00 {
    72  		domain, err := ReadUntilNull(reader)
    73  		if err != nil {
    74  			return nil, newError("failed to read domain for socks 4a").Base(err)
    75  		}
    76  		address = net.DomainAddress(domain)
    77  	}
    78  
    79  	switch cmd {
    80  	case cmdTCPConnect:
    81  		request := &protocol.RequestHeader{
    82  			Command: protocol.RequestCommandTCP,
    83  			Address: address,
    84  			Port:    port,
    85  			Version: socks4Version,
    86  		}
    87  		if err := writeSocks4Response(writer, socks4RequestGranted, net.AnyIP, net.Port(0)); err != nil {
    88  			return nil, err
    89  		}
    90  		return request, nil
    91  	default:
    92  		writeSocks4Response(writer, socks4RequestRejected, net.AnyIP, net.Port(0))
    93  		return nil, newError("unsupported command: ", cmd)
    94  	}
    95  }
    96  
    97  func (s *ServerSession) auth5(nMethod byte, reader io.Reader, writer io.Writer) (username string, err error) {
    98  	buffer := buf.StackNew()
    99  	defer buffer.Release()
   100  
   101  	if _, err = buffer.ReadFullFrom(reader, int32(nMethod)); err != nil {
   102  		return "", newError("failed to read auth methods").Base(err)
   103  	}
   104  
   105  	var expectedAuth byte = authNotRequired
   106  	if s.config.AuthType == AuthType_PASSWORD {
   107  		expectedAuth = authPassword
   108  	}
   109  
   110  	if !hasAuthMethod(expectedAuth, buffer.BytesRange(0, int32(nMethod))) {
   111  		writeSocks5AuthenticationResponse(writer, socks5Version, authNoMatchingMethod)
   112  		return "", newError("no matching auth method")
   113  	}
   114  
   115  	if err := writeSocks5AuthenticationResponse(writer, socks5Version, expectedAuth); err != nil {
   116  		return "", newError("failed to write auth response").Base(err)
   117  	}
   118  
   119  	if expectedAuth == authPassword {
   120  		username, password, err := ReadUsernamePassword(reader)
   121  		if err != nil {
   122  			return "", newError("failed to read username and password for authentication").Base(err)
   123  		}
   124  
   125  		if !s.config.HasAccount(username, password) {
   126  			writeSocks5AuthenticationResponse(writer, 0x01, 0xFF)
   127  			return "", newError("invalid username or password")
   128  		}
   129  
   130  		if err := writeSocks5AuthenticationResponse(writer, 0x01, 0x00); err != nil {
   131  			return "", newError("failed to write auth response").Base(err)
   132  		}
   133  		return username, nil
   134  	}
   135  
   136  	return "", nil
   137  }
   138  
   139  func (s *ServerSession) handshake5(nMethod byte, reader io.Reader, writer io.Writer) (*protocol.RequestHeader, error) {
   140  	var (
   141  		username string
   142  		err      error
   143  	)
   144  	if username, err = s.auth5(nMethod, reader, writer); err != nil {
   145  		return nil, err
   146  	}
   147  
   148  	var cmd byte
   149  	{
   150  		buffer := buf.StackNew()
   151  		if _, err := buffer.ReadFullFrom(reader, 3); err != nil {
   152  			buffer.Release()
   153  			return nil, newError("failed to read request").Base(err)
   154  		}
   155  		cmd = buffer.Byte(1)
   156  		buffer.Release()
   157  	}
   158  
   159  	request := new(protocol.RequestHeader)
   160  	if username != "" {
   161  		request.User = &protocol.MemoryUser{Email: username}
   162  	}
   163  	switch cmd {
   164  	case cmdTCPConnect, cmdTorResolve, cmdTorResolvePTR:
   165  		// We don't have a solution for Tor case now. Simply treat it as connect command.
   166  		request.Command = protocol.RequestCommandTCP
   167  	case cmdUDPAssociate:
   168  		if !s.config.UdpEnabled {
   169  			writeSocks5Response(writer, statusCmdNotSupport, net.AnyIP, net.Port(0))
   170  			return nil, newError("UDP is not enabled.")
   171  		}
   172  		request.Command = protocol.RequestCommandUDP
   173  	case cmdTCPBind:
   174  		writeSocks5Response(writer, statusCmdNotSupport, net.AnyIP, net.Port(0))
   175  		return nil, newError("TCP bind is not supported.")
   176  	default:
   177  		writeSocks5Response(writer, statusCmdNotSupport, net.AnyIP, net.Port(0))
   178  		return nil, newError("unknown command ", cmd)
   179  	}
   180  
   181  	request.Version = socks5Version
   182  
   183  	addr, port, err := addrParser.ReadAddressPort(nil, reader)
   184  	if err != nil {
   185  		return nil, newError("failed to read address").Base(err)
   186  	}
   187  	request.Address = addr
   188  	request.Port = port
   189  
   190  	responseAddress := s.address
   191  	responsePort := s.port
   192  	//nolint:gocritic // Use if else chain for clarity
   193  	if request.Command == protocol.RequestCommandUDP {
   194  		if s.config.Address != nil {
   195  			// Use configured IP as remote address in the response to UDP Associate
   196  			responseAddress = s.config.Address.AsAddress()
   197  		} else {
   198  			// Use conn.LocalAddr() IP as remote address in the response by default
   199  			responseAddress = s.localAddress
   200  		}
   201  	}
   202  	if err := writeSocks5Response(writer, statusSuccess, responseAddress, responsePort); err != nil {
   203  		return nil, err
   204  	}
   205  
   206  	return request, nil
   207  }
   208  
   209  // Handshake performs a Socks4/4a/5 handshake.
   210  func (s *ServerSession) Handshake(reader io.Reader, writer io.Writer) (*protocol.RequestHeader, error) {
   211  	buffer := buf.StackNew()
   212  	if _, err := buffer.ReadFullFrom(reader, 2); err != nil {
   213  		buffer.Release()
   214  		return nil, newError("insufficient header").Base(err)
   215  	}
   216  
   217  	version := buffer.Byte(0)
   218  	cmd := buffer.Byte(1)
   219  	buffer.Release()
   220  
   221  	switch version {
   222  	case socks4Version:
   223  		return s.handshake4(cmd, reader, writer)
   224  	case socks5Version:
   225  		return s.handshake5(cmd, reader, writer)
   226  	default:
   227  		return nil, newError("unknown Socks version: ", version)
   228  	}
   229  }
   230  
   231  // ReadUsernamePassword reads Socks 5 username/password message from the given reader.
   232  // +----+------+----------+------+----------+
   233  // |VER | ULEN |  UNAME   | PLEN |  PASSWD  |
   234  // +----+------+----------+------+----------+
   235  // | 1  |  1   | 1 to 255 |  1   | 1 to 255 |
   236  // +----+------+----------+------+----------+
   237  func ReadUsernamePassword(reader io.Reader) (string, string, error) {
   238  	buffer := buf.StackNew()
   239  	defer buffer.Release()
   240  
   241  	if _, err := buffer.ReadFullFrom(reader, 2); err != nil {
   242  		return "", "", err
   243  	}
   244  	nUsername := int32(buffer.Byte(1))
   245  
   246  	buffer.Clear()
   247  	if _, err := buffer.ReadFullFrom(reader, nUsername); err != nil {
   248  		return "", "", err
   249  	}
   250  	username := buffer.String()
   251  
   252  	buffer.Clear()
   253  	if _, err := buffer.ReadFullFrom(reader, 1); err != nil {
   254  		return "", "", err
   255  	}
   256  	nPassword := int32(buffer.Byte(0))
   257  
   258  	buffer.Clear()
   259  	if _, err := buffer.ReadFullFrom(reader, nPassword); err != nil {
   260  		return "", "", err
   261  	}
   262  	password := buffer.String()
   263  	return username, password, nil
   264  }
   265  
   266  // ReadUntilNull reads content from given reader, until a null (0x00) byte.
   267  func ReadUntilNull(reader io.Reader) (string, error) {
   268  	b := buf.StackNew()
   269  	defer b.Release()
   270  
   271  	for {
   272  		_, err := b.ReadFullFrom(reader, 1)
   273  		if err != nil {
   274  			return "", err
   275  		}
   276  		if b.Byte(b.Len()-1) == 0x00 {
   277  			b.Resize(0, b.Len()-1)
   278  			return b.String(), nil
   279  		}
   280  		if b.IsFull() {
   281  			return "", newError("buffer overrun")
   282  		}
   283  	}
   284  }
   285  
   286  func hasAuthMethod(expectedAuth byte, authCandidates []byte) bool {
   287  	for _, a := range authCandidates {
   288  		if a == expectedAuth {
   289  			return true
   290  		}
   291  	}
   292  	return false
   293  }
   294  
   295  func writeSocks5AuthenticationResponse(writer io.Writer, version byte, auth byte) error {
   296  	return buf.WriteAllBytes(writer, []byte{version, auth}, nil)
   297  }
   298  
   299  func writeSocks5Response(writer io.Writer, errCode byte, address net.Address, port net.Port) error {
   300  	buffer := buf.New()
   301  	defer buffer.Release()
   302  
   303  	common.Must2(buffer.Write([]byte{socks5Version, errCode, 0x00 /* reserved */}))
   304  	if err := addrParser.WriteAddressPort(buffer, address, port); err != nil {
   305  		return err
   306  	}
   307  
   308  	return buf.WriteAllBytes(writer, buffer.Bytes(), nil)
   309  }
   310  
   311  func writeSocks4Response(writer io.Writer, errCode byte, address net.Address, port net.Port) error {
   312  	buffer := buf.StackNew()
   313  	defer buffer.Release()
   314  
   315  	common.Must(buffer.WriteByte(0x00))
   316  	common.Must(buffer.WriteByte(errCode))
   317  	portBytes := buffer.Extend(2)
   318  	binary.BigEndian.PutUint16(portBytes, port.Value())
   319  	common.Must2(buffer.Write(address.IP()))
   320  	return buf.WriteAllBytes(writer, buffer.Bytes(), nil)
   321  }
   322  
   323  func DecodeUDPPacket(packet *buf.Buffer) (*protocol.RequestHeader, error) {
   324  	if packet.Len() < 5 {
   325  		return nil, newError("insufficient length of packet.")
   326  	}
   327  	request := &protocol.RequestHeader{
   328  		Version: socks5Version,
   329  		Command: protocol.RequestCommandUDP,
   330  	}
   331  
   332  	// packet[0] and packet[1] are reserved
   333  	if packet.Byte(2) != 0 /* fragments */ {
   334  		return nil, newError("discarding fragmented payload.")
   335  	}
   336  
   337  	packet.Advance(3)
   338  
   339  	addr, port, err := addrParser.ReadAddressPort(nil, packet)
   340  	if err != nil {
   341  		return nil, newError("failed to read UDP header").Base(err)
   342  	}
   343  	request.Address = addr
   344  	request.Port = port
   345  	return request, nil
   346  }
   347  
   348  func EncodeUDPPacket(request *protocol.RequestHeader, data []byte) (*buf.Buffer, error) {
   349  	b := buf.New()
   350  	common.Must2(b.Write([]byte{0, 0, 0 /* Fragment */}))
   351  	if err := addrParser.WriteAddressPort(b, request.Address, request.Port); err != nil {
   352  		b.Release()
   353  		return nil, err
   354  	}
   355  	common.Must2(b.Write(data))
   356  	return b, nil
   357  }
   358  
   359  type UDPReader struct {
   360  	Reader io.Reader
   361  }
   362  
   363  func (r *UDPReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
   364  	buffer := buf.New()
   365  	_, err := buffer.ReadFrom(r.Reader)
   366  	if err != nil {
   367  		buffer.Release()
   368  		return nil, err
   369  	}
   370  	u, err := DecodeUDPPacket(buffer)
   371  	if err != nil {
   372  		buffer.Release()
   373  		return nil, err
   374  	}
   375  	dest := u.Destination()
   376  	buffer.UDP = &dest
   377  	return buf.MultiBuffer{buffer}, nil
   378  }
   379  
   380  type UDPWriter struct {
   381  	Writer  io.Writer
   382  	Request *protocol.RequestHeader
   383  }
   384  
   385  func (w *UDPWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
   386  	for {
   387  		mb2, b := buf.SplitFirst(mb)
   388  		mb = mb2
   389  		if b == nil {
   390  			break
   391  		}
   392  		request := w.Request
   393  		if b.UDP != nil {
   394  			request = &protocol.RequestHeader{
   395  				Address: b.UDP.Address,
   396  				Port:    b.UDP.Port,
   397  			}
   398  		}
   399  		packet, err := EncodeUDPPacket(request, b.Bytes())
   400  		b.Release()
   401  		if err != nil {
   402  			buf.ReleaseMulti(mb)
   403  			return err
   404  		}
   405  		_, err = w.Writer.Write(packet.Bytes())
   406  		packet.Release()
   407  		if err != nil {
   408  			buf.ReleaseMulti(mb)
   409  			return err
   410  		}
   411  	}
   412  	return nil
   413  }
   414  
   415  func ClientHandshake(request *protocol.RequestHeader, reader io.Reader, writer io.Writer) (*protocol.RequestHeader, error) {
   416  	authByte := byte(authNotRequired)
   417  	if request.User != nil {
   418  		authByte = byte(authPassword)
   419  	}
   420  
   421  	b := buf.New()
   422  	defer b.Release()
   423  
   424  	common.Must2(b.Write([]byte{socks5Version, 0x01, authByte}))
   425  	if err := buf.WriteAllBytes(writer, b.Bytes(), nil); err != nil {
   426  		return nil, err
   427  	}
   428  
   429  	b.Clear()
   430  	if _, err := b.ReadFullFrom(reader, 2); err != nil {
   431  		return nil, err
   432  	}
   433  
   434  	if b.Byte(0) != socks5Version {
   435  		return nil, newError("unexpected server version: ", b.Byte(0)).AtWarning()
   436  	}
   437  	if b.Byte(1) != authByte {
   438  		return nil, newError("auth method not supported.").AtWarning()
   439  	}
   440  
   441  	if authByte == authPassword {
   442  		b.Clear()
   443  		account := request.User.Account.(*Account)
   444  		common.Must(b.WriteByte(0x01))
   445  		common.Must(b.WriteByte(byte(len(account.Username))))
   446  		common.Must2(b.WriteString(account.Username))
   447  		common.Must(b.WriteByte(byte(len(account.Password))))
   448  		common.Must2(b.WriteString(account.Password))
   449  		if err := buf.WriteAllBytes(writer, b.Bytes(), nil); err != nil {
   450  			return nil, err
   451  		}
   452  
   453  		b.Clear()
   454  		if _, err := b.ReadFullFrom(reader, 2); err != nil {
   455  			return nil, err
   456  		}
   457  		if b.Byte(1) != 0x00 {
   458  			return nil, newError("server rejects account: ", b.Byte(1))
   459  		}
   460  	}
   461  
   462  	b.Clear()
   463  
   464  	command := byte(cmdTCPConnect)
   465  	if request.Command == protocol.RequestCommandUDP {
   466  		command = byte(cmdUDPAssociate)
   467  	}
   468  	common.Must2(b.Write([]byte{socks5Version, command, 0x00 /* reserved */}))
   469  	if request.Command == protocol.RequestCommandUDP {
   470  		common.Must2(b.Write([]byte{1, 0, 0, 0, 0, 0, 0 /* RFC 1928 */}))
   471  	} else {
   472  		if err := addrParser.WriteAddressPort(b, request.Address, request.Port); err != nil {
   473  			return nil, err
   474  		}
   475  	}
   476  
   477  	if err := buf.WriteAllBytes(writer, b.Bytes(), nil); err != nil {
   478  		return nil, err
   479  	}
   480  
   481  	b.Clear()
   482  	if _, err := b.ReadFullFrom(reader, 3); err != nil {
   483  		return nil, err
   484  	}
   485  
   486  	resp := b.Byte(1)
   487  	if resp != 0x00 {
   488  		return nil, newError("server rejects request: ", resp)
   489  	}
   490  
   491  	b.Clear()
   492  
   493  	address, port, err := addrParser.ReadAddressPort(b, reader)
   494  	if err != nil {
   495  		return nil, err
   496  	}
   497  
   498  	if request.Command == protocol.RequestCommandUDP {
   499  		udpRequest := &protocol.RequestHeader{
   500  			Version: socks5Version,
   501  			Command: protocol.RequestCommandUDP,
   502  			Address: address,
   503  			Port:    port,
   504  		}
   505  		return udpRequest, nil
   506  	}
   507  
   508  	return nil, nil
   509  }