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