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