github.com/moqsien/xraycore@v1.8.5/transport/internet/dialer.go (about) 1 package internet 2 3 import ( 4 "context" 5 6 "github.com/moqsien/xraycore/common" 7 "github.com/moqsien/xraycore/common/dice" 8 "github.com/moqsien/xraycore/common/net" 9 "github.com/moqsien/xraycore/common/net/cnc" 10 "github.com/moqsien/xraycore/common/session" 11 "github.com/moqsien/xraycore/features/dns" 12 "github.com/moqsien/xraycore/features/outbound" 13 "github.com/moqsien/xraycore/transport" 14 "github.com/moqsien/xraycore/transport/internet/stat" 15 "github.com/moqsien/xraycore/transport/pipe" 16 ) 17 18 // Dialer is the interface for dialing outbound connections. 19 type Dialer interface { 20 // Dial dials a system connection to the given destination. 21 Dial(ctx context.Context, destination net.Destination) (stat.Connection, error) 22 23 // Address returns the address used by this Dialer. Maybe nil if not known. 24 Address() net.Address 25 } 26 27 // dialFunc is an interface to dial network connection to a specific destination. 28 type dialFunc func(ctx context.Context, dest net.Destination, streamSettings *MemoryStreamConfig) (stat.Connection, error) 29 30 var transportDialerCache = make(map[string]dialFunc) 31 32 // RegisterTransportDialer registers a Dialer with given name. 33 func RegisterTransportDialer(protocol string, dialer dialFunc) error { 34 if _, found := transportDialerCache[protocol]; found { 35 return newError(protocol, " dialer already registered").AtError() 36 } 37 transportDialerCache[protocol] = dialer 38 return nil 39 } 40 41 // Dial dials a internet connection towards the given destination. 42 func Dial(ctx context.Context, dest net.Destination, streamSettings *MemoryStreamConfig) (stat.Connection, error) { 43 if dest.Network == net.Network_TCP { 44 if streamSettings == nil { 45 s, err := ToMemoryStreamConfig(nil) 46 if err != nil { 47 return nil, newError("failed to create default stream settings").Base(err) 48 } 49 streamSettings = s 50 } 51 52 protocol := streamSettings.ProtocolName 53 dialer := transportDialerCache[protocol] 54 if dialer == nil { 55 return nil, newError(protocol, " dialer not registered").AtError() 56 } 57 return dialer(ctx, dest, streamSettings) 58 } 59 60 if dest.Network == net.Network_UDP { 61 udpDialer := transportDialerCache["udp"] 62 if udpDialer == nil { 63 return nil, newError("UDP dialer not registered").AtError() 64 } 65 return udpDialer(ctx, dest, streamSettings) 66 } 67 68 return nil, newError("unknown network ", dest.Network) 69 } 70 71 var ( 72 dnsClient dns.Client 73 obm outbound.Manager 74 ) 75 76 func lookupIP(domain string, strategy DomainStrategy, localAddr net.Address) ([]net.IP, error) { 77 if dnsClient == nil { 78 return nil, nil 79 } 80 81 option := dns.IPOption{ 82 IPv4Enable: true, 83 IPv6Enable: true, 84 FakeEnable: false, 85 } 86 87 switch { 88 case strategy == DomainStrategy_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()): 89 option = dns.IPOption{ 90 IPv4Enable: true, 91 IPv6Enable: false, 92 FakeEnable: false, 93 } 94 case strategy == DomainStrategy_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()): 95 option = dns.IPOption{ 96 IPv4Enable: false, 97 IPv6Enable: true, 98 FakeEnable: false, 99 } 100 case strategy == DomainStrategy_AS_IS: 101 return nil, nil 102 } 103 104 return dnsClient.LookupIP(domain, option) 105 } 106 107 func canLookupIP(ctx context.Context, dst net.Destination, sockopt *SocketConfig) bool { 108 if dst.Address.Family().IsIP() || dnsClient == nil { 109 return false 110 } 111 return sockopt.DomainStrategy != DomainStrategy_AS_IS 112 } 113 114 func redirect(ctx context.Context, dst net.Destination, obt string) net.Conn { 115 newError("redirecting request " + dst.String() + " to " + obt).WriteToLog(session.ExportIDToError(ctx)) 116 h := obm.GetHandler(obt) 117 ctx = session.ContextWithOutbound(ctx, &session.Outbound{Target: dst, Gateway: nil}) 118 if h != nil { 119 ur, uw := pipe.New(pipe.OptionsFromContext(ctx)...) 120 dr, dw := pipe.New(pipe.OptionsFromContext(ctx)...) 121 122 go h.Dispatch(ctx, &transport.Link{Reader: ur, Writer: dw}) 123 nc := cnc.NewConnection( 124 cnc.ConnectionInputMulti(uw), 125 cnc.ConnectionOutputMulti(dr), 126 cnc.ConnectionOnClose(common.ChainedClosable{uw, dw}), 127 ) 128 return nc 129 } 130 return nil 131 } 132 133 // DialSystem calls system dialer to create a network connection. 134 func DialSystem(ctx context.Context, dest net.Destination, sockopt *SocketConfig) (net.Conn, error) { 135 var src net.Address 136 if outbound := session.OutboundFromContext(ctx); outbound != nil { 137 src = outbound.Gateway 138 } 139 if sockopt == nil { 140 return effectiveSystemDialer.Dial(ctx, src, dest, sockopt) 141 } 142 143 if canLookupIP(ctx, dest, sockopt) { 144 ips, err := lookupIP(dest.Address.String(), sockopt.DomainStrategy, src) 145 if err == nil && len(ips) > 0 { 146 dest.Address = net.IPAddress(ips[dice.Roll(len(ips))]) 147 newError("replace destination with " + dest.String()).AtInfo().WriteToLog() 148 } else if err != nil { 149 newError("failed to resolve ip").Base(err).AtWarning().WriteToLog() 150 } 151 } 152 153 if obm != nil && len(sockopt.DialerProxy) > 0 { 154 nc := redirect(ctx, dest, sockopt.DialerProxy) 155 if nc != nil { 156 return nc, nil 157 } 158 } 159 160 return effectiveSystemDialer.Dial(ctx, src, dest, sockopt) 161 } 162 163 func InitSystemDialer(dc dns.Client, om outbound.Manager) { 164 dnsClient = dc 165 obm = om 166 }