Browse Source

socks5 module

刘河 5 years ago
parent
commit
eed3efd18f
1 changed files with 171 additions and 0 deletions
  1. 171 0
      server/socks5/socks5_request_handle.go

+ 171 - 0
server/socks5/socks5_request_handle.go

@@ -0,0 +1,171 @@
+package socks5
+
+import (
+	"context"
+	"encoding/binary"
+	"errors"
+	"github.com/cnlh/nps/core"
+	"io"
+	"net"
+	"strconv"
+)
+
+type Request struct {
+	clientConn net.Conn
+	ctx        context.Context
+}
+
+const (
+	ipV4            = 1
+	domainName      = 3
+	ipV6            = 4
+	connectMethod   = 1
+	bindMethod      = 2
+	associateMethod = 3
+	// The maximum packet size of any udp Associate packet, based on ethernet's max size,
+	// minus the IP and UDP headerrequest. IPv4 has a 20 byte header, UDP adds an
+	// additional 4 byterequest.  This is a total overhead of 24 byterequest.  Ethernet's
+	// max packet size is 1500 bytes,  1500 - 24 = 1476.
+	maxUDPPacketSize     = 1476
+	commandNotSupported  = 7
+	addrTypeNotSupported = 8
+)
+
+func (request *Request) GetConfigName() []*core.Config {
+	c := make([]*core.Config, 0)
+	c = append(c, &core.Config{ConfigName: "socks5_check_request", Description: "need check the permission?"})
+	c = append(c, &core.Config{ConfigName: "socks5_request_username", Description: "auth username"})
+	c = append(c, &core.Config{ConfigName: "socks5_request_password", Description: "auth password"})
+	return nil
+}
+
+func (request *Request) GetStage() core.Stage {
+	return core.STAGE_RUN
+}
+
+func (request *Request) Start(ctx context.Context, config map[string]string) error {
+	return nil
+}
+func (request *Request) End(ctx context.Context, config map[string]string) error {
+	return nil
+}
+
+func (request *Request) Run(ctx context.Context, config map[string]string) error {
+	clientCtxConn := ctx.Value("clientConn")
+	if clientCtxConn == nil {
+		return errors.New("the client request.clientConnection is not exist")
+	}
+	request.clientConn = clientCtxConn.(net.Conn)
+	request.ctx = ctx
+
+	/*
+		The SOCKS request is formed as follows:
+		+----+-----+-------+------+----------+----------+
+		|VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
+		+----+-----+-------+------+----------+----------+
+		| 1  |  1  | X'00' |  1   | Variable |    2     |
+		+----+-----+-------+------+----------+----------+
+	*/
+	header := make([]byte, 3)
+
+	_, err := io.ReadFull(request.clientConn, header)
+
+	if err != nil {
+		return errors.New("illegal request" + err.Error())
+	}
+
+	switch header[1] {
+	case connectMethod:
+		context.WithValue(request.ctx, "socks5_target_type", "tcp")
+		return request.doConnect()
+	case bindMethod:
+		return request.handleBind()
+	case associateMethod:
+		context.WithValue(request.ctx, "socks5_target_type", "udp")
+		return request.handleUDP()
+	default:
+		request.sendReply(commandNotSupported)
+		return errors.New("command not supported")
+	}
+	return nil
+}
+
+func (request *Request) sendReply(rep uint8) error {
+	reply := []byte{
+		5,
+		rep,
+		0,
+		1,
+	}
+
+	localAddr := request.clientConn.LocalAddr().String()
+	localHost, localPort, _ := net.SplitHostPort(localAddr)
+	ipBytes := net.ParseIP(localHost).To4()
+	nPort, _ := strconv.Atoi(localPort)
+	reply = append(reply, ipBytes...)
+	portBytes := make([]byte, 2)
+	binary.BigEndian.PutUint16(portBytes, uint16(nPort))
+	reply = append(reply, portBytes...)
+	_, err := request.clientConn.Write(reply)
+	return err
+}
+
+//do conn
+func (request *Request) doConnect() error {
+	addrType := make([]byte, 1)
+	request.clientConn.Read(addrType)
+
+	var host string
+	switch addrType[0] {
+	case ipV4:
+		ipv4 := make(net.IP, net.IPv4len)
+		request.clientConn.Read(ipv4)
+		host = ipv4.String()
+	case ipV6:
+		ipv6 := make(net.IP, net.IPv6len)
+		request.clientConn.Read(ipv6)
+		host = ipv6.String()
+	case domainName:
+		var domainLen uint8
+		binary.Read(request.clientConn, binary.BigEndian, &domainLen)
+		domain := make([]byte, domainLen)
+		request.clientConn.Read(domain)
+		host = string(domain)
+	default:
+		request.sendReply(addrTypeNotSupported)
+		return errors.New("target address type is not support")
+	}
+
+	var port uint16
+	binary.Read(request.clientConn, binary.BigEndian, &port)
+
+	context.WithValue(request.ctx, "socks5_target_host", host)
+	context.WithValue(request.ctx, "socks5_target_port", port)
+	return nil
+}
+
+// passive mode
+func (request *Request) handleBind() error {
+	return nil
+}
+
+//udp
+func (request *Request) handleUDP() error {
+	/*
+	   +----+------+------+----------+----------+----------+
+	   |RSV | FRAG | ATYP | DST.ADDR | DST.PORT |   DATA   |
+	   +----+------+------+----------+----------+----------+
+	   | 2  |  1   |  1   | Variable |    2     | Variable |
+	   +----+------+------+----------+----------+----------+
+	*/
+	buf := make([]byte, 3)
+	request.clientConn.Read(buf)
+	// relay udp datagram silently, without any notification to the requesting client
+	if buf[2] != 0 {
+		// does not support fragmentation, drop it
+		dummy := make([]byte, maxUDPPacketSize)
+		request.clientConn.Read(dummy)
+		return errors.New("does not support fragmentation, drop")
+	}
+	return request.doConnect()
+}