github.com/codingeasygo/util@v0.0.0-20231206062002-1ce2f004b7d9/proxy/socks/socks.go (about)

     1  package socks
     2  
     3  import (
     4  	"encoding/binary"
     5  	"fmt"
     6  	"io"
     7  	"net"
     8  	"strconv"
     9  	"strings"
    10  	"sync"
    11  
    12  	"github.com/codingeasygo/util/xdebug"
    13  	"github.com/codingeasygo/util/xio"
    14  )
    15  
    16  // Codable is interface for get current code
    17  type Codable interface {
    18  	Code() byte
    19  }
    20  
    21  // Server is an implementation of socks5 proxy
    22  type Server struct {
    23  	BufferSize int
    24  	listners   map[net.Listener]string
    25  	waiter     sync.WaitGroup
    26  	Dialer     xio.PiperDialer
    27  }
    28  
    29  // NewServer will return new Server
    30  func NewServer() (socks *Server) {
    31  	socks = &Server{
    32  		BufferSize: 32 * 1024,
    33  		listners:   map[net.Listener]string{},
    34  		waiter:     sync.WaitGroup{},
    35  		Dialer:     xio.PiperDialerF(xio.DialNetPiper),
    36  	}
    37  	return
    38  }
    39  
    40  func (s *Server) loopAccept(l net.Listener) {
    41  	for {
    42  		conn, err := l.Accept()
    43  		if err != nil {
    44  			break
    45  		}
    46  		go s.ProcConn(conn)
    47  	}
    48  	s.waiter.Done()
    49  }
    50  
    51  // Run will listen tcp on address and sync accept to ProcConn
    52  func (s *Server) Run(addr string) (err error) {
    53  	listener, err := net.Listen("tcp", addr)
    54  	if err == nil {
    55  		s.listners[listener] = addr
    56  		InfoLog("Server listen http proxy on %v", addr)
    57  		s.waiter.Add(1)
    58  		s.loopAccept(listener)
    59  	}
    60  	return
    61  }
    62  
    63  // Start proxy listener
    64  func (s *Server) Start(network, addr string) (listener net.Listener, err error) {
    65  	listener, err = net.Listen(network, addr)
    66  	if err == nil {
    67  		s.listners[listener] = addr
    68  		InfoLog("Server listen socks5 proxy on %v", addr)
    69  		s.waiter.Add(1)
    70  		go s.loopAccept(listener)
    71  	}
    72  	return
    73  }
    74  
    75  // Stop will stop listener and wait loop stop
    76  func (s *Server) Stop() (err error) {
    77  	for listener, addr := range s.listners {
    78  		err = listener.Close()
    79  		delete(s.listners, listener)
    80  		InfoLog("Server socks5 proxy listener on %v is stopped by %v", addr, err)
    81  	}
    82  	s.waiter.Wait()
    83  	return
    84  }
    85  
    86  // ProcConn will process connecton as socket protocol
    87  func (s *Server) ProcConn(conn io.ReadWriteCloser) (err error) {
    88  	// DebugLog("Server proxy socks connection on %v from %v", xio.LocalAddr(conn), xio.RemoteAddr(conn))
    89  	defer func() {
    90  		if perr := recover(); perr != nil {
    91  			ErrorLog("Server socks proxy conn on %v from %v is panic with %v, callstakc is \n%v", xio.LocalAddr(conn), xio.RemoteAddr(conn), perr, xdebug.CallStack())
    92  		}
    93  		if err != xio.ErrAsyncRunning {
    94  			DebugLog("Server socks proxy connection on %v from %v is done with %v", xio.LocalAddr(conn), xio.RemoteAddr(conn), err)
    95  			conn.Close()
    96  		}
    97  	}()
    98  	buf := make([]byte, 1024*4)
    99  	//
   100  	//Procedure method
   101  	err = xio.FullBuffer(conn, buf, 2, nil)
   102  	if err != nil {
   103  		return
   104  	}
   105  	if buf[0] != 0x05 {
   106  		err = fmt.Errorf("only ver 0x05 is supported, but %x", buf[0])
   107  		return
   108  	}
   109  	err = xio.FullBuffer(conn, buf[2:], uint32(buf[1]), nil)
   110  	if err != nil {
   111  		return
   112  	}
   113  	_, err = conn.Write([]byte{0x05, 0x00})
   114  	if err != nil {
   115  		return
   116  	}
   117  	//
   118  	//Procedure request
   119  	err = xio.FullBuffer(conn, buf, 5, nil)
   120  	if err != nil {
   121  		return
   122  	}
   123  	if buf[0] != 0x05 {
   124  		err = fmt.Errorf("only ver 0x05 is supported, but %x", buf[0])
   125  		return
   126  	}
   127  	var uri string
   128  	switch buf[3] {
   129  	case 0x01:
   130  		err = xio.FullBuffer(conn, buf[5:], 5, nil)
   131  		if err == nil {
   132  			remote := fmt.Sprintf("%v.%v.%v.%v", buf[4], buf[5], buf[6], buf[7])
   133  			port := uint16(buf[8])*256 + uint16(buf[9])
   134  			uri = fmt.Sprintf("tcp://%v:%v", remote, port)
   135  		}
   136  	case 0x03:
   137  		err = xio.FullBuffer(conn, buf[5:], uint32(buf[4]+2), nil)
   138  		if err == nil {
   139  			remote := string(buf[5 : buf[4]+5])
   140  			port := uint16(buf[buf[4]+5])*256 + uint16(buf[buf[4]+6])
   141  			uri = fmt.Sprintf("tcp://%v:%v", remote, port)
   142  		}
   143  	default:
   144  		err = xio.FullBuffer(conn, buf[5:], uint32(buf[4]+2), nil)
   145  		if err == nil {
   146  			uri = string(buf[5 : buf[4]+5])
   147  		}
   148  	}
   149  	DebugLog("Server socks proxy start dial to %v on %v from %v", uri, xio.LocalAddr(conn), xio.RemoteAddr(conn))
   150  	raw, err := s.Dialer.DialPiper(uri, s.BufferSize)
   151  	if err != nil {
   152  		buf[0], buf[1], buf[2], buf[3] = 0x05, 0x04, 0x00, 0x01
   153  		buf[4], buf[5], buf[6], buf[7] = 0x00, 0x00, 0x00, 0x00
   154  		buf[8], buf[9] = 0x00, 0x00
   155  		n := 0
   156  		if cerr, ok := err.(Codable); ok {
   157  			buf[1] = cerr.Code()
   158  			n = 10
   159  		} else {
   160  			buf[1] = 0x10
   161  			message := err.Error()
   162  			if len(message) > 2048 {
   163  				message = message[:2048]
   164  			}
   165  			binary.BigEndian.PutUint16(buf[10:12], uint16(len(message)))
   166  			n = 12 + copy(buf[12:], []byte(message))
   167  		}
   168  		conn.Write(buf[:n])
   169  		DebugLog("Server socks proxy dial to %v on %v fail with %v", uri, xio.RemoteAddr(conn), err)
   170  		return
   171  	}
   172  	buf[0], buf[1], buf[2], buf[3] = 0x05, 0x00, 0x00, 0x01
   173  	buf[4], buf[5], buf[6], buf[7] = 0x00, 0x00, 0x00, 0x00
   174  	buf[8], buf[9] = 0x00, 0x00
   175  	_, err = conn.Write(buf[:10])
   176  	if err != nil {
   177  		raw.Close()
   178  		return
   179  	}
   180  	err = raw.PipeConn(conn, uri)
   181  	if err != xio.ErrAsyncRunning {
   182  		raw.Close()
   183  	}
   184  	return
   185  }
   186  
   187  // Dial will dial connection by proxy server
   188  func Dial(proxy, uri string) (conn net.Conn, err error) {
   189  	conn, err = DialType(proxy, 0x03, uri)
   190  	return
   191  }
   192  
   193  // DialType wil dial connection by proxy server and uri type
   194  func DialType(proxy string, uriType byte, uri string) (conn net.Conn, err error) {
   195  	proxyNetwork, proxyAddr := "", ""
   196  	if strings.HasPrefix(proxy, "socks5://") {
   197  		proxyNetwork = "tcp"
   198  		proxyAddr = strings.TrimPrefix(proxy, "socks5://")
   199  	} else if strings.HasPrefix(proxy, "tcp://") {
   200  		proxyNetwork = "tcp"
   201  		proxyAddr = strings.TrimPrefix(proxy, "tcp://")
   202  	} else if strings.HasPrefix(proxy, "unix://") {
   203  		proxyNetwork = "unix"
   204  		proxyAddr = strings.TrimPrefix(proxy, "unix://")
   205  	} else {
   206  		proxyNetwork = "tcp"
   207  		proxyAddr = proxy
   208  	}
   209  	conn, err = net.Dial(proxyNetwork, proxyAddr)
   210  	if err != nil {
   211  		return
   212  	}
   213  	conn.Write([]byte{0x05, 0x01, 0x00})
   214  	buf := make([]byte, 1024*64)
   215  	err = xio.FullBuffer(conn, buf, 2, nil)
   216  	if err != nil {
   217  		conn.Close()
   218  		return
   219  	}
   220  	if buf[0] != 0x05 || buf[1] != 0x00 {
   221  		err = fmt.Errorf("unsupported %x", buf)
   222  		conn.Close()
   223  		return
   224  	}
   225  	var host string
   226  	var port int
   227  	if uriType == 0x03 {
   228  		var p string
   229  		host, p, _ = net.SplitHostPort(uri)
   230  		port, _ = strconv.Atoi(p)
   231  	} else {
   232  		host = uri
   233  	}
   234  	blen := len(host) + 7
   235  	buf[0], buf[1], buf[2] = 0x05, 0x01, 0x00
   236  	buf[3], buf[4] = uriType, byte(len(host))
   237  	copy(buf[5:], []byte(host))
   238  	buf[blen-2] = byte(port / 256)
   239  	buf[blen-1] = byte(port % 256)
   240  	conn.Write(buf[:blen])
   241  	err = xio.FullBuffer(conn, buf, 5, nil)
   242  	if err != nil {
   243  		conn.Close()
   244  		return
   245  	}
   246  	n := 0
   247  	switch buf[3] {
   248  	case 0x01:
   249  		n = 10
   250  		err = xio.FullBuffer(conn, buf[5:], 5, nil)
   251  	case 0x03:
   252  		n = 5 + int(buf[4]) + 2
   253  		err = xio.FullBuffer(conn, buf[5:], uint32(buf[4])+2, nil)
   254  	case 0x04:
   255  		n = 5 + int(buf[4]) + 17
   256  		err = xio.FullBuffer(conn, buf[5:], 17, nil)
   257  	default:
   258  		err = fmt.Errorf("reply address type is not supported:%v", buf[3])
   259  	}
   260  	if err != nil {
   261  		conn.Close()
   262  		return
   263  	}
   264  	if buf[1] == 0x10 {
   265  		err = xio.FullBuffer(conn, buf[n:], 2, nil)
   266  		messageLen := 0
   267  		if err == nil {
   268  			messageLen = int(binary.BigEndian.Uint16(buf[n : n+2]))
   269  			err = xio.FullBuffer(conn, buf[n+2:], uint32(messageLen), nil)
   270  		}
   271  		if err == nil {
   272  			err = fmt.Errorf("%v", string(buf[n+2:n+2+messageLen]))
   273  		}
   274  		return
   275  	}
   276  	if buf[1] != 0x00 {
   277  		conn.Close()
   278  		err = fmt.Errorf("socks server response code(%x)", buf[1])
   279  		return
   280  	}
   281  	return
   282  }