github.com/Asutorufa/yuhaiin@v0.3.6-0.20240502055049-7984da7023a0/pkg/net/proxy/socks5/client.go (about)

     1  package socks5
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"net"
     9  
    10  	"github.com/Asutorufa/yuhaiin/pkg/net/netapi"
    11  	"github.com/Asutorufa/yuhaiin/pkg/net/proxy/simple"
    12  	"github.com/Asutorufa/yuhaiin/pkg/net/proxy/socks5/tools"
    13  	"github.com/Asutorufa/yuhaiin/pkg/net/proxy/yuubinsya"
    14  	"github.com/Asutorufa/yuhaiin/pkg/protos/node/point"
    15  	"github.com/Asutorufa/yuhaiin/pkg/protos/node/protocol"
    16  	"github.com/Asutorufa/yuhaiin/pkg/protos/statistic"
    17  	"github.com/Asutorufa/yuhaiin/pkg/utils/pool"
    18  	"github.com/Asutorufa/yuhaiin/pkg/utils/relay"
    19  	"github.com/Asutorufa/yuhaiin/pkg/utils/yerror"
    20  )
    21  
    22  func Dial(host, port, user, password string) netapi.Proxy {
    23  	addr, err := netapi.ParseAddress(statistic.Type_tcp, net.JoinHostPort(host, port))
    24  	if err != nil {
    25  		return netapi.NewErrProxy(err)
    26  	}
    27  	p, _ := NewClient(&protocol.Protocol_Socks5{
    28  		Socks5: &protocol.Socks5{
    29  			Hostname: host,
    30  			User:     user,
    31  			Password: password,
    32  		}})(yerror.Must(simple.NewClient(&protocol.Protocol_Simple{
    33  		Simple: &protocol.Simple{
    34  			Host: addr.Hostname(),
    35  			Port: int32(addr.Port().Port()),
    36  		},
    37  	})(nil)))
    38  	return p
    39  }
    40  
    41  // https://tools.ietf.org/html/rfc1928
    42  // Client socks5 Client
    43  type Client struct {
    44  	username string
    45  	password string
    46  
    47  	hostname string
    48  	netapi.EmptyDispatch
    49  	dialer netapi.Proxy
    50  }
    51  
    52  func init() {
    53  	point.RegisterProtocol(NewClient)
    54  }
    55  
    56  // New returns a new Socks5 client
    57  func NewClient(config *protocol.Protocol_Socks5) point.WrapProxy {
    58  	return func(dialer netapi.Proxy) (netapi.Proxy, error) {
    59  		return &Client{
    60  			dialer:   dialer,
    61  			username: config.Socks5.User,
    62  			password: config.Socks5.Password,
    63  			hostname: config.Socks5.Hostname,
    64  		}, nil
    65  	}
    66  }
    67  
    68  func (s *Client) Conn(ctx context.Context, host netapi.Address) (net.Conn, error) {
    69  	conn, err := s.dialer.Conn(ctx, host)
    70  	if err != nil {
    71  		return nil, fmt.Errorf("dial failed: %w", err)
    72  	}
    73  
    74  	err = s.handshake1(conn)
    75  	if err != nil {
    76  		conn.Close()
    77  		return nil, fmt.Errorf("first hand failed: %w", err)
    78  	}
    79  
    80  	_, err = s.handshake2(ctx, conn, tools.Connect, host)
    81  	if err != nil {
    82  		conn.Close()
    83  		return nil, fmt.Errorf("second hand failed: %w", err)
    84  	}
    85  
    86  	return conn, nil
    87  }
    88  
    89  func (s *Client) handshake1(conn net.Conn) error {
    90  	_, err := conn.Write([]byte{0x05, 0x02, tools.NoAuthenticationRequired, tools.UserAndPassword})
    91  	if err != nil {
    92  		return fmt.Errorf("write sock5 header failed: %w", err)
    93  	}
    94  
    95  	header := make([]byte, 2)
    96  	_, err = io.ReadFull(conn, header)
    97  	if err != nil {
    98  		return fmt.Errorf("read header failed: %w", err)
    99  	}
   100  
   101  	if header[0] != 0x05 {
   102  		return errors.New("unknown socks5 version")
   103  	}
   104  
   105  	switch header[1] {
   106  	case tools.NoAuthenticationRequired:
   107  		return nil
   108  	case tools.UserAndPassword: // username and password
   109  		req := pool.GetBytesWriter(pool.DefaultSize)
   110  		defer req.Free()
   111  
   112  		req.WriteByte(0x01)
   113  		req.WriteByte(byte(len(s.username)))
   114  		req.WriteString(s.username)
   115  		req.WriteByte(byte(len(s.password)))
   116  		req.WriteString(s.password)
   117  
   118  		_, err = conn.Write(req.Bytes())
   119  		if err != nil {
   120  			return fmt.Errorf("write auth data failed: %w", err)
   121  		}
   122  
   123  		_, err = io.ReadFull(conn, header)
   124  		if err != nil {
   125  			return fmt.Errorf("read auth data failed: %w", err)
   126  		}
   127  		if header[1] == 0x01 {
   128  			return errors.New("username or password not correct,socks5 handshake failed")
   129  		}
   130  	}
   131  
   132  	return fmt.Errorf("unsupported Authentication methods: %d", header[1])
   133  }
   134  
   135  func (s *Client) handshake2(ctx context.Context, conn net.Conn, cmd tools.CMD, address netapi.Address) (target netapi.Address, err error) {
   136  	req := pool.GetBytesWriter(pool.DefaultSize)
   137  	defer req.Free()
   138  
   139  	req.WriteByte(0x05)
   140  	req.WriteByte(byte(cmd))
   141  	req.WriteByte(0x00)
   142  	tools.EncodeAddr(address, req)
   143  
   144  	if _, err = conn.Write(req.Bytes()); err != nil {
   145  		return nil, err
   146  	}
   147  
   148  	header := make([]byte, 3)
   149  	if _, err := io.ReadFull(conn, header); err != nil {
   150  		return nil, err
   151  	}
   152  
   153  	if header[0] != 0x05 || header[1] != tools.Succeeded {
   154  		return nil, fmt.Errorf("socks5 second handshake failed, data: %v", header[:2])
   155  	}
   156  
   157  	add, err := tools.ResolveAddr(conn)
   158  	if err != nil {
   159  		return nil, fmt.Errorf("resolve addr failed: %w", err)
   160  	}
   161  
   162  	addr := add.Address(statistic.Type_tcp)
   163  
   164  	if addr.Type() == netapi.IP && yerror.Must(addr.IP(ctx)).IsUnspecified() {
   165  		addr = netapi.ParseAddressPort(statistic.Type_tcp, s.hostname, addr.Port())
   166  	}
   167  
   168  	return addr, nil
   169  }
   170  
   171  func (s *Client) PacketConn(ctx context.Context, host netapi.Address) (net.PacketConn, error) {
   172  	conn, err := s.dialer.Conn(ctx, host)
   173  	if err != nil {
   174  		return nil, fmt.Errorf("dial tcp failed: %w", err)
   175  	}
   176  
   177  	err = s.handshake1(conn)
   178  	if err != nil {
   179  		conn.Close()
   180  		return nil, fmt.Errorf("first hand failed: %w", err)
   181  	}
   182  
   183  	addr, err := s.handshake2(ctx, conn, tools.Udp, host)
   184  	if err != nil {
   185  		conn.Close()
   186  		return nil, fmt.Errorf("second hand failed: %w", err)
   187  	}
   188  
   189  	pc, err := s.dialer.PacketConn(context.WithValue(ctx, simple.PacketDirectKey{}, true), addr)
   190  	if err != nil {
   191  		conn.Close()
   192  		return nil, fmt.Errorf("listen udp failed: %w", err)
   193  	}
   194  
   195  	pc = yuubinsya.NewAuthPacketConn(pc).WithTcpConn(conn).WithTarget(addr).WithPrefix(true)
   196  
   197  	go func() {
   198  		_, _ = relay.Copy(io.Discard, conn)
   199  		pc.Close()
   200  	}()
   201  
   202  	return pc, nil
   203  }
   204  
   205  // The client connects to the server, and sends a version
   206  // identifier/method selection message:
   207  
   208  // 				+----+----------+----------+
   209  // 				|VER | NMETHODS | METHODS  |
   210  // 				+----+----------+----------+
   211  // 				| 1  |    1     | 1 to 255 |
   212  // 				+----+----------+----------+
   213  
   214  // The VER field is set to X'05' for this version of the protocol.  The
   215  // NMETHODS field contains the number of method identifier octets that
   216  // appear in the METHODS field.
   217  
   218  // The server selects from one of the methods given in METHODS, and
   219  // sends a METHOD selection message:
   220  
   221  // 					  +----+--------+
   222  // 					  |VER | METHOD |
   223  // 					  +----+--------+
   224  // 					  | 1  |   1    |
   225  // 					  +----+--------+
   226  
   227  // If the selected METHOD is X'FF', none of the methods listed by the
   228  // client are acceptable, and the client MUST close the connection.
   229  
   230  // The values currently defined for METHOD are:
   231  
   232  // 	   o  X'00' NO AUTHENTICATION REQUIRED
   233  // 	   o  X'01' GSSAPI
   234  // 	   o  X'02' USERNAME/PASSWORD
   235  // 	   o  X'03' to X'7F' IANA ASSIGNED
   236  // 	   o  X'80' to X'FE' RESERVED FOR PRIVATE METHODS
   237  // 	   o  X'FF' NO ACCEPTABLE METHODS
   238  // The client and server then enter a method-specific sub-negotiation.
   239  
   240  // second handshake
   241  
   242  // 	Once the method-dependent subnegotiation has completed, the client
   243  // 	sends the request details.  If the negotiated method includes
   244  // 	encapsulation for purposes of integrity checking and/or
   245  // 	confidentiality, these requests MUST be encapsulated in the method-
   246  // 	dependent encapsulation.
   247  
   248  // 	The SOCKS request is formed as follows:
   249  
   250  // 		 +----+-----+-------+------+----------+----------+
   251  // 		 |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
   252  // 		 +----+-----+-------+------+----------+----------+
   253  // 		 | 1  |  1  | X'00' |  1   | Variable |    2     |
   254  // 		 +----+-----+-------+------+----------+----------+
   255  
   256  // 	  Where:
   257  
   258  // 		   o  VER    protocol version: X'05'
   259  // 		   o  CMD
   260  // 			  o  CONNECT X'01'
   261  // 			  o  BIND X'02'
   262  // 			  o  UDP ASSOCIATE X'03'
   263  // 		   o  RSV    RESERVED
   264  // 		   o  ATYP   address type of following address
   265  // 			  o  IP V4 address: X'01'
   266  // 			  o  DOMAINNAME: X'03'
   267  // 			  o  IP V6 address: X'04'
   268  // 		   o  DST.ADDR       desired destination address
   269  // 		   o  DST.PORT desired destination port in network octet
   270  // 			  order
   271  
   272  // 	The SOCKS server will typically evaluate the request based on source
   273  // 	and destination addresses, and return one or more reply messages, as
   274  // 	appropriate for the request type.
   275  
   276  //  5.  Addressing
   277  
   278  // 	In an address field (DST.ADDR, BND.ADDR), the ATYP field specifies
   279  // 	the type of address contained within the field:
   280  
   281  // 		   o  X'01'
   282  
   283  // 	the address is a version-4 IP address, with a length of 4 octets
   284  
   285  // 		   o  X'03'
   286  
   287  // 	the address field contains a fully-qualified domain name.  The first
   288  // 	octet of the address field contains the number of octets of name that
   289  // 	follow, there is no terminating NUL octet.
   290  
   291  // 		   o  X'04'
   292  
   293  // 	the address is a version-6 IP address, with a length of 16 octets.
   294  
   295  // 6.  Replies
   296  // The SOCKS request information is sent by the client as soon as it has
   297  // established a connection to the SOCKS server, and completed the
   298  // authentication negotiations.  The server evaluates the request, and
   299  // returns a reply formed as follows:
   300  
   301  // 	 +----+-----+-------+------+----------+----------+
   302  // 	 |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
   303  // 	 +----+-----+-------+------+----------+----------+
   304  // 	 | 1  |  1  | X'00' |  1   | Variable |    2     |
   305  // 	 +----+-----+-------+------+----------+----------+
   306  
   307  //   Where:
   308  
   309  // 	   o  VER    protocol version: X'05'
   310  // 	   o  REP    Reply field:
   311  // 		  o  X'00' succeeded
   312  // 		  o  X'01' general SOCKS server failure
   313  // 		  o  X'02' connection not allowed by ruleset
   314  // 		  o  X'03' Network unreachable
   315  // 		  o  X'04' Host unreachable
   316  // 		  o  X'05' Connection refused
   317  // 		  o  X'06' TTL expired
   318  // 		  o  X'07' Command not supported
   319  // 		  o  X'08' Address type not supported
   320  // 		  o  X'09' to X'FF' unassigned
   321  // 	   o  RSV    RESERVED
   322  // 	   o  ATYP   address type of following address
   323  // 		  o  IP V4 address: X'01'
   324  // 		  o  DOMAINNAME: X'03'
   325  // 		  o  IP V6 address: X'04'
   326  // 	   o  BND.ADDR       server bound address
   327  // 	   o  BND.PORT       server bound port in network octet order
   328  
   329  // Fields marked RESERVED (RSV) must be set to X'00'.
   330  
   331  // If the chosen method includes encapsulation for purposes of
   332  // authentication, integrity and/or confidentiality, the replies are
   333  // encapsulated in the method-dependent encapsulation.
   334  
   335  // CONNECT
   336  
   337  // In the reply to a CONNECT, BND.PORT contains the port number that the
   338  // server assigned to connect to the target host, while BND.ADDR
   339  // contains the associated IP address.  The supplied BND.ADDR is often
   340  // different from the IP address that the client uses to reach the SOCKS
   341  // server, since such servers are often multi-homed.  It is expected
   342  // that the SOCKS server will use DST.ADDR and DST.PORT, and the
   343  // client-side source address and port in evaluating the CONNECT
   344  // request.
   345  
   346  // BIND
   347  
   348  // The BIND request is used in protocols which require the client to
   349  // accept connections from the server.  FTP is a well-known example,
   350  // which uses the primary client-to-server connection for commands and
   351  // status reports, but may use a server-to-client connection for
   352  // transferring data on demand (e.g. LS, GET, PUT).
   353  
   354  // It is expected that the client side of an application protocol will
   355  // use the BIND request only to establish secondary connections after a
   356  // primary connection is established using CONNECT.  In is expected that
   357  // a SOCKS server will use DST.ADDR and DST.PORT in evaluating the BIND
   358  // request.
   359  
   360  // Two replies are sent from the SOCKS server to the client during a
   361  // BIND operation.  The first is sent after the server creates and binds
   362  // a new socket.  The BND.PORT field contains the port number that the
   363  // SOCKS server assigned to listen for an incoming connection.  The
   364  // BND.ADDR field contains the associated IP address.  The client will
   365  // typically use these pieces of information to notify (via the primary
   366  // or control connection) the application server of the rendezvous
   367  // address.  The second reply occurs only after the anticipated incoming
   368  // connection succeeds or fails.
   369  
   370  // In the second reply, the BND.PORT and BND.ADDR fields contain the
   371  // address and port number of the connecting host.
   372  
   373  // UDP ASSOCIATE
   374  
   375  // The UDP ASSOCIATE request is used to establish an association within
   376  // the UDP relay process to handle UDP datagrams.  The DST.ADDR and
   377  // DST.PORT fields contain the address and port that the client expects
   378  // to use to send UDP datagrams on for the association.  The server MAY
   379  // use this information to limit access to the association.  If the
   380  // client is not in possession of the information at the time of the UDP
   381  // ASSOCIATE, the client MUST use a port number and address of all
   382  // zeros.
   383  
   384  // A UDP association terminates when the TCP connection that the UDP
   385  // ASSOCIATE request arrived on terminates.
   386  
   387  // In the reply to a UDP ASSOCIATE request, the BND.PORT and BND.ADDR
   388  // fields indicate the port number/address where the client MUST send
   389  // UDP request messages to be relayed.
   390  
   391  // Reply Processing
   392  
   393  // When a reply (REP value other than X'00') indicates a failure, the
   394  // SOCKS server MUST terminate the TCP connection shortly after sending
   395  // the reply.  This must be no more than 10 seconds after detecting the
   396  // condition that caused a failure.
   397  
   398  // If the reply code (REP value of X'00') indicates a success, and the
   399  // request was either a BIND or a CONNECT, the client may now start
   400  // passing data.  If the selected authentication method supports
   401  // encapsulation for the purposes of integrity, authentication and/or
   402  // confidentiality, the data are encapsulated using the method-dependent
   403  // encapsulation.  Similarly, when data arrives at the SOCKS server for
   404  // the client, the server MUST encapsulate the data as appropriate for
   405  // the authentication method in use.