github.com/chwjbn/xclash@v0.2.0/adapter/outbound/socks5.go (about) 1 package outbound 2 3 import ( 4 "context" 5 "crypto/tls" 6 "errors" 7 "fmt" 8 "io" 9 "net" 10 "strconv" 11 12 "github.com/chwjbn/xclash/component/dialer" 13 C "github.com/chwjbn/xclash/constant" 14 "github.com/chwjbn/xclash/transport/socks5" 15 ) 16 17 type Socks5 struct { 18 *Base 19 user string 20 pass string 21 tls bool 22 skipCertVerify bool 23 tlsConfig *tls.Config 24 } 25 26 type Socks5Option struct { 27 BasicOption 28 Name string `proxy:"name"` 29 Server string `proxy:"server"` 30 Port int `proxy:"port"` 31 UserName string `proxy:"username,omitempty"` 32 Password string `proxy:"password,omitempty"` 33 TLS bool `proxy:"tls,omitempty"` 34 UDP bool `proxy:"udp,omitempty"` 35 SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` 36 } 37 38 // StreamConn implements C.ProxyAdapter 39 func (ss *Socks5) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { 40 if ss.tls { 41 cc := tls.Client(c, ss.tlsConfig) 42 err := cc.Handshake() 43 c = cc 44 if err != nil { 45 return nil, fmt.Errorf("%s connect error: %w", ss.addr, err) 46 } 47 } 48 49 var user *socks5.User 50 if ss.user != "" { 51 user = &socks5.User{ 52 Username: ss.user, 53 Password: ss.pass, 54 } 55 } 56 if _, err := socks5.ClientHandshake(c, serializesSocksAddr(metadata), socks5.CmdConnect, user); err != nil { 57 return nil, err 58 } 59 return c, nil 60 } 61 62 // DialContext implements C.ProxyAdapter 63 func (ss *Socks5) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { 64 c, err := dialer.DialContext(ctx, "tcp", ss.addr, ss.Base.DialOptions(opts...)...) 65 if err != nil { 66 return nil, fmt.Errorf("%s connect error: %w", ss.addr, err) 67 } 68 tcpKeepAlive(c) 69 70 defer safeConnClose(c, err) 71 72 c, err = ss.StreamConn(c, metadata) 73 if err != nil { 74 return nil, err 75 } 76 77 return NewConn(c, ss), nil 78 } 79 80 // ListenPacketContext implements C.ProxyAdapter 81 func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { 82 c, err := dialer.DialContext(ctx, "tcp", ss.addr, ss.Base.DialOptions(opts...)...) 83 if err != nil { 84 err = fmt.Errorf("%s connect error: %w", ss.addr, err) 85 return 86 } 87 88 if ss.tls { 89 cc := tls.Client(c, ss.tlsConfig) 90 err = cc.Handshake() 91 c = cc 92 } 93 94 defer safeConnClose(c, err) 95 96 tcpKeepAlive(c) 97 var user *socks5.User 98 if ss.user != "" { 99 user = &socks5.User{ 100 Username: ss.user, 101 Password: ss.pass, 102 } 103 } 104 105 bindAddr, err := socks5.ClientHandshake(c, serializesSocksAddr(metadata), socks5.CmdUDPAssociate, user) 106 if err != nil { 107 err = fmt.Errorf("client hanshake error: %w", err) 108 return 109 } 110 111 pc, err := dialer.ListenPacket(ctx, "udp", "", ss.Base.DialOptions(opts...)...) 112 if err != nil { 113 return 114 } 115 116 go func() { 117 io.Copy(io.Discard, c) 118 c.Close() 119 // A UDP association terminates when the TCP connection that the UDP 120 // ASSOCIATE request arrived on terminates. RFC1928 121 pc.Close() 122 }() 123 124 // Support unspecified UDP bind address. 125 bindUDPAddr := bindAddr.UDPAddr() 126 if bindUDPAddr == nil { 127 err = errors.New("invalid UDP bind address") 128 return 129 } else if bindUDPAddr.IP.IsUnspecified() { 130 serverAddr, err := resolveUDPAddr("udp", ss.Addr()) 131 if err != nil { 132 return nil, err 133 } 134 135 bindUDPAddr.IP = serverAddr.IP 136 } 137 138 return newPacketConn(&socksPacketConn{PacketConn: pc, rAddr: bindUDPAddr, tcpConn: c}, ss), nil 139 } 140 141 func NewSocks5(option Socks5Option) *Socks5 { 142 var tlsConfig *tls.Config 143 if option.TLS { 144 tlsConfig = &tls.Config{ 145 InsecureSkipVerify: option.SkipCertVerify, 146 ServerName: option.Server, 147 } 148 } 149 150 return &Socks5{ 151 Base: &Base{ 152 name: option.Name, 153 addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), 154 tp: C.Socks5, 155 udp: option.UDP, 156 iface: option.Interface, 157 }, 158 user: option.UserName, 159 pass: option.Password, 160 tls: option.TLS, 161 skipCertVerify: option.SkipCertVerify, 162 tlsConfig: tlsConfig, 163 } 164 } 165 166 type socksPacketConn struct { 167 net.PacketConn 168 rAddr net.Addr 169 tcpConn net.Conn 170 } 171 172 func (uc *socksPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { 173 packet, err := socks5.EncodeUDPPacket(socks5.ParseAddrToSocksAddr(addr), b) 174 if err != nil { 175 return 176 } 177 return uc.PacketConn.WriteTo(packet, uc.rAddr) 178 } 179 180 func (uc *socksPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { 181 n, _, e := uc.PacketConn.ReadFrom(b) 182 if e != nil { 183 return 0, nil, e 184 } 185 addr, payload, err := socks5.DecodeUDPPacket(b) 186 if err != nil { 187 return 0, nil, err 188 } 189 190 udpAddr := addr.UDPAddr() 191 if udpAddr == nil { 192 return 0, nil, errors.New("parse udp addr error") 193 } 194 195 // due to DecodeUDPPacket is mutable, record addr length 196 copy(b, payload) 197 return n - len(addr) - 3, udpAddr, nil 198 } 199 200 func (uc *socksPacketConn) Close() error { 201 uc.tcpConn.Close() 202 return uc.PacketConn.Close() 203 }