github.com/chwjbn/xclash@v0.2.0/listener/socks/tcp.go (about) 1 package socks 2 3 import ( 4 "io" 5 "net" 6 7 "github.com/chwjbn/xclash/adapter/inbound" 8 N "github.com/chwjbn/xclash/common/net" 9 C "github.com/chwjbn/xclash/constant" 10 authStore "github.com/chwjbn/xclash/listener/auth" 11 "github.com/chwjbn/xclash/transport/socks4" 12 "github.com/chwjbn/xclash/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) (*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 bufConn := N.NewBufferedConn(conn) 65 head, err := bufConn.Peek(1) 66 if err != nil { 67 conn.Close() 68 return 69 } 70 71 switch head[0] { 72 case socks4.Version: 73 HandleSocks4(bufConn, in) 74 case socks5.Version: 75 HandleSocks5(bufConn, in) 76 default: 77 conn.Close() 78 } 79 } 80 81 func HandleSocks4(conn net.Conn, in chan<- C.ConnContext) { 82 addr, _, err := socks4.ServerHandshake(conn, authStore.Authenticator()) 83 if err != nil { 84 conn.Close() 85 return 86 } 87 if c, ok := conn.(*net.TCPConn); ok { 88 c.SetKeepAlive(true) 89 } 90 in <- inbound.NewSocket(socks5.ParseAddr(addr), conn, C.SOCKS4) 91 } 92 93 func HandleSocks5(conn net.Conn, in chan<- C.ConnContext) { 94 authUser, target, command, err := socks5.ServerHandshake(conn, authStore.Authenticator()) 95 if err != nil { 96 conn.Close() 97 return 98 } 99 if c, ok := conn.(*net.TCPConn); ok { 100 c.SetKeepAlive(true) 101 } 102 if command == socks5.CmdUDPAssociate { 103 defer conn.Close() 104 io.Copy(io.Discard, conn) 105 return 106 } 107 in <- inbound.NewSocketWithAuth(target, conn, C.SOCKS5, authUser) 108 }