github.com/sagernet/sing@v0.2.6/protocol/socks/socks4/protocol.go (about)

     1  package socks4
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"io"
     7  	"net/netip"
     8  
     9  	"github.com/sagernet/sing/common"
    10  	"github.com/sagernet/sing/common/buf"
    11  	E "github.com/sagernet/sing/common/exceptions"
    12  	M "github.com/sagernet/sing/common/metadata"
    13  	"github.com/sagernet/sing/common/rw"
    14  )
    15  
    16  const (
    17  	Version byte = 4
    18  
    19  	CommandConnect byte = 1
    20  	CommandBind    byte = 2
    21  
    22  	ReplyCodeGranted                     byte = 90
    23  	ReplyCodeRejectedOrFailed            byte = 91
    24  	ReplyCodeCannotConnectToIdentd       byte = 92
    25  	ReplyCodeIdentdReportDifferentUserID byte = 93
    26  )
    27  
    28  type Request struct {
    29  	Command     byte
    30  	Destination M.Socksaddr
    31  	Username    string
    32  }
    33  
    34  func ReadRequest(reader io.Reader) (request Request, err error) {
    35  	version, err := rw.ReadByte(reader)
    36  	if err != nil {
    37  		return
    38  	}
    39  	if version != 4 {
    40  		err = E.New("excepted socks version 4, got ", version)
    41  		return
    42  	}
    43  	return ReadRequest0(reader)
    44  }
    45  
    46  func ReadRequest0(reader io.Reader) (request Request, err error) {
    47  	request.Command, err = rw.ReadByte(reader)
    48  	if err != nil {
    49  		return
    50  	}
    51  	err = binary.Read(reader, binary.BigEndian, &request.Destination.Port)
    52  	if err != nil {
    53  		return
    54  	}
    55  	var dstIP [4]byte
    56  	_, err = io.ReadFull(reader, dstIP[:])
    57  	if err != nil {
    58  		return
    59  	}
    60  	var readHostName bool
    61  	if dstIP[0] == 0 && dstIP[1] == 0 && dstIP[2] == 0 && dstIP[3] != 0 {
    62  		readHostName = true
    63  	} else {
    64  		request.Destination.Addr = netip.AddrFrom4(dstIP)
    65  	}
    66  	request.Username, err = readString(reader)
    67  	if readHostName {
    68  		request.Destination.Fqdn, err = readString(reader)
    69  		request.Destination = M.ParseSocksaddrHostPort(request.Destination.Fqdn, request.Destination.Port)
    70  	}
    71  	return
    72  }
    73  
    74  func WriteRequest(writer io.Writer, request Request) error {
    75  	var requestLen int
    76  	requestLen += 1 // version
    77  	requestLen += 1 // command
    78  	requestLen += 2 // port
    79  	requestLen += 4 // ip
    80  	requestLen += 1 // NUL
    81  	if !request.Destination.IsIPv4() {
    82  		requestLen += len(request.Destination.AddrString()) + 1
    83  	}
    84  	if request.Username != "" {
    85  		requestLen += len(request.Username)
    86  	}
    87  
    88  	_buffer := buf.StackNewSize(requestLen)
    89  	defer common.KeepAlive(_buffer)
    90  	buffer := common.Dup(_buffer)
    91  	defer buffer.Release()
    92  
    93  	common.Must(
    94  		buffer.WriteByte(Version),
    95  		buffer.WriteByte(request.Command),
    96  		binary.Write(buffer, binary.BigEndian, request.Destination.Port),
    97  	)
    98  	if request.Destination.IsIPv4() {
    99  		common.Must1(buffer.Write(request.Destination.Addr.AsSlice()))
   100  	} else {
   101  		// 0.0.0.X
   102  		common.Must(buffer.WriteZeroN(3))
   103  		common.Must(buffer.WriteByte(1))
   104  	}
   105  	if request.Username != "" {
   106  		common.Must1(buffer.WriteString(request.Username))
   107  	}
   108  	common.Must(buffer.WriteZero())
   109  	if !request.Destination.IsIPv4() {
   110  		common.Must1(buffer.WriteString(request.Destination.AddrString()))
   111  		common.Must(buffer.WriteZero())
   112  	}
   113  	return rw.WriteBytes(writer, buffer.Bytes())
   114  }
   115  
   116  type Response struct {
   117  	ReplyCode   byte
   118  	Destination M.Socksaddr
   119  }
   120  
   121  func ReadResponse(reader io.Reader) (response Response, err error) {
   122  	version, err := rw.ReadByte(reader)
   123  	if err != nil {
   124  		return
   125  	}
   126  	if version != 0 {
   127  		err = E.New("excepted socks4 response version 0, got ", version)
   128  		return
   129  	}
   130  	response.ReplyCode, err = rw.ReadByte(reader)
   131  	if err != nil {
   132  		return
   133  	}
   134  	err = binary.Read(reader, binary.BigEndian, &response.Destination.Port)
   135  	if err != nil {
   136  		return
   137  	}
   138  	var dstIP [4]byte
   139  	_, err = io.ReadFull(reader, dstIP[:])
   140  	if err != nil {
   141  		return
   142  	}
   143  	response.Destination.Addr = netip.AddrFrom4(dstIP)
   144  	return
   145  }
   146  
   147  func WriteResponse(writer io.Writer, response Response) error {
   148  	_buffer := buf.StackNewSize(8)
   149  	defer common.KeepAlive(_buffer)
   150  	buffer := common.Dup(_buffer)
   151  	defer buffer.Release()
   152  	common.Must(
   153  		buffer.WriteByte(0),
   154  		buffer.WriteByte(response.ReplyCode),
   155  		binary.Write(buffer, binary.BigEndian, response.Destination.Port),
   156  		common.Error(buffer.Write(response.Destination.Addr.AsSlice())),
   157  	)
   158  	return rw.WriteBytes(writer, buffer.Bytes())
   159  }
   160  
   161  func readString(reader io.Reader) (string, error) {
   162  	buffer := bytes.Buffer{}
   163  	for {
   164  		b, err := rw.ReadByte(reader)
   165  		if err != nil {
   166  			return "", err
   167  		}
   168  		if b == 0 {
   169  			break
   170  		}
   171  		buffer.WriteByte(b)
   172  	}
   173  	return buffer.String(), nil
   174  }