github.com/igoogolx/clash@v1.19.8/listener/socks/tcp.go (about) 1 package socks 2 3 import ( 4 "io" 5 "net" 6 7 "github.com/igoogolx/clash/adapter/inbound" 8 N "github.com/igoogolx/clash/common/net" 9 C "github.com/igoogolx/clash/constant" 10 authStore "github.com/igoogolx/clash/listener/auth" 11 "github.com/igoogolx/clash/transport/socks4" 12 "github.com/igoogolx/clash/transport/socks5" 13 ) 14 15 type Listener struct { 16 listener net.Listener 17 addr string 18 closed bool 19 } 20 21 // RawAddress implements C.Listener 22 func (l *Listener) RawAddress() string { 23 return l.addr 24 } 25 26 // Address implements C.Listener 27 func (l *Listener) Address() string { 28 return l.listener.Addr().String() 29 } 30 31 // Close implements C.Listener 32 func (l *Listener) Close() error { 33 l.closed = true 34 return l.listener.Close() 35 } 36 37 func New(addr string, in chan<- C.ConnContext) (C.Listener, error) { 38 l, err := net.Listen("tcp", addr) 39 if err != nil { 40 return nil, err 41 } 42 43 sl := &Listener{ 44 listener: l, 45 addr: addr, 46 } 47 go func() { 48 for { 49 c, err := l.Accept() 50 if err != nil { 51 if sl.closed { 52 break 53 } 54 continue 55 } 56 go handleSocks(c, in) 57 } 58 }() 59 60 return sl, nil 61 } 62 63 func handleSocks(conn net.Conn, in chan<- C.ConnContext) { 64 conn.(*net.TCPConn).SetKeepAlive(true) 65 bufConn := N.NewBufferedConn(conn) 66 head, err := bufConn.Peek(1) 67 if err != nil { 68 conn.Close() 69 return 70 } 71 72 switch head[0] { 73 case socks4.Version: 74 HandleSocks4(bufConn, in) 75 case socks5.Version: 76 HandleSocks5(bufConn, in) 77 default: 78 conn.Close() 79 } 80 } 81 82 func HandleSocks4(conn net.Conn, in chan<- C.ConnContext) { 83 addr, _, err := socks4.ServerHandshake(conn, authStore.Authenticator()) 84 if err != nil { 85 conn.Close() 86 return 87 } 88 in <- inbound.NewSocket(socks5.ParseAddr(addr), conn, C.SOCKS4) 89 } 90 91 func HandleSocks5(conn net.Conn, in chan<- C.ConnContext) { 92 target, command, err := socks5.ServerHandshake(conn, authStore.Authenticator()) 93 if err != nil { 94 conn.Close() 95 return 96 } 97 if command == socks5.CmdUDPAssociate { 98 defer conn.Close() 99 io.Copy(io.Discard, conn) 100 return 101 } 102 in <- inbound.NewSocket(target, conn, C.SOCKS5) 103 }