@@ -26,87 +26,70 @@ var (
26
26
supportsIPv4map bool
27
27
)
28
28
29
- // A netaddr represents a network endpoint address or a list of
30
- // network endpoint addresses.
31
- type netaddr interface {
32
- // toAddr returns the address represented in Addr interface.
33
- // It returns a nil interface when the address is nil.
34
- toAddr () Addr
35
- }
36
-
37
29
// An addrList represents a list of network endpoint addresses.
38
- type addrList []netaddr
30
+ type addrList []Addr
39
31
40
- func (al addrList ) toAddr () Addr {
41
- switch len (al ) {
42
- case 0 :
43
- return nil
44
- case 1 :
45
- return al [0 ].toAddr ()
46
- default :
47
- // For now, we'll roughly pick first one without
48
- // considering dealing with any preferences such as
49
- // DNS TTL, transport path quality, network routing
50
- // information.
51
- return al [0 ].toAddr ()
32
+ // isIPv4 returns true if the Addr contains an IPv4 address.
33
+ func isIPv4 (addr Addr ) bool {
34
+ switch addr := addr .(type ) {
35
+ case * TCPAddr :
36
+ return addr .IP .To4 () != nil
37
+ case * UDPAddr :
38
+ return addr .IP .To4 () != nil
39
+ case * IPAddr :
40
+ return addr .IP .To4 () != nil
52
41
}
42
+ return false
53
43
}
54
44
55
- var errNoSuitableAddress = errors .New ("no suitable address found" )
56
-
57
- // firstFavoriteAddr returns an address or a list of addresses that
58
- // implement the netaddr interface. Known filters are nil, ipv4only
59
- // and ipv6only. It returns any address when filter is nil. The result
60
- // contains at least one address when error is nil.
61
- func firstFavoriteAddr (filter func (IPAddr ) bool , ips []IPAddr , inetaddr func (IPAddr ) netaddr ) (netaddr , error ) {
62
- if filter != nil {
63
- return firstSupportedAddr (filter , ips , inetaddr )
64
- }
65
- var (
66
- ipv4 , ipv6 , swap bool
67
- list addrList
68
- )
69
- for _ , ip := range ips {
70
- // We'll take any IP address, but since the dialing
71
- // code does not yet try multiple addresses
72
- // effectively, prefer to use an IPv4 address if
73
- // possible. This is especially relevant if localhost
74
- // resolves to [ipv6-localhost, ipv4-localhost]. Too
75
- // much code assumes localhost == ipv4-localhost.
76
- if ipv4only (ip ) && ! ipv4 {
77
- list = append (list , inetaddr (ip ))
78
- ipv4 = true
79
- if ipv6 {
80
- swap = true
81
- }
82
- } else if ipv6only (ip ) && ! ipv6 {
83
- list = append (list , inetaddr (ip ))
84
- ipv6 = true
85
- }
86
- if ipv4 && ipv6 {
87
- if swap {
88
- list [0 ], list [1 ] = list [1 ], list [0 ]
89
- }
90
- break
45
+ // first returns the first address which satisfies strategy, or if
46
+ // none do, then the first address of any kind.
47
+ func (addrs addrList ) first (strategy func (Addr ) bool ) Addr {
48
+ for _ , addr := range addrs {
49
+ if strategy (addr ) {
50
+ return addr
91
51
}
92
52
}
93
- switch len (list ) {
94
- case 0 :
95
- return nil , errNoSuitableAddress
96
- case 1 :
97
- return list [0 ], nil
98
- default :
99
- return list , nil
53
+ return addrs [0 ]
54
+ }
55
+
56
+ // partition divides an address list into two categories, using a
57
+ // strategy function to assign a boolean label to each address.
58
+ // The first address, and any with a matching label, are returned as
59
+ // primaries, while addresses with the opposite label are returned
60
+ // as fallbacks. For non-empty inputs, primaries is guaranteed to be
61
+ // non-empty.
62
+ func (addrs addrList ) partition (strategy func (Addr ) bool ) (primaries , fallbacks addrList ) {
63
+ var primaryLabel bool
64
+ for i , addr := range addrs {
65
+ label := strategy (addr )
66
+ if i == 0 || label == primaryLabel {
67
+ primaryLabel = label
68
+ primaries = append (primaries , addr )
69
+ } else {
70
+ fallbacks = append (fallbacks , addr )
71
+ }
100
72
}
73
+ return
101
74
}
102
75
103
- func firstSupportedAddr (filter func (IPAddr ) bool , ips []IPAddr , inetaddr func (IPAddr ) netaddr ) (netaddr , error ) {
76
+ var errNoSuitableAddress = errors .New ("no suitable address found" )
77
+
78
+ // filterAddrList applies a filter to a list of IP addresses,
79
+ // yielding a list of Addr objects. Known filters are nil, ipv4only,
80
+ // and ipv6only. It returns every address when the filter is nil.
81
+ // The result contains at least one address when error is nil.
82
+ func filterAddrList (filter func (IPAddr ) bool , ips []IPAddr , inetaddr func (IPAddr ) Addr ) (addrList , error ) {
83
+ var addrs addrList
104
84
for _ , ip := range ips {
105
- if filter (ip ) {
106
- return inetaddr (ip ), nil
85
+ if filter == nil || filter (ip ) {
86
+ addrs = append ( addrs , inetaddr (ip ))
107
87
}
108
88
}
109
- return nil , errNoSuitableAddress
89
+ if len (addrs ) == 0 {
90
+ return nil , errNoSuitableAddress
91
+ }
92
+ return addrs , nil
110
93
}
111
94
112
95
// ipv4only reports whether the kernel supports IPv4 addressing mode
@@ -214,13 +197,11 @@ func JoinHostPort(host, port string) string {
214
197
return host + ":" + port
215
198
}
216
199
217
- // resolveInternetAddr resolves addr that is either a literal IP
218
- // address or a DNS name and returns an internet protocol family
219
- // address. It returns a list that contains a pair of different
220
- // address family addresses when addr is a DNS name and the name has
221
- // multiple address family records. The result contains at least one
222
- // address when error is nil.
223
- func resolveInternetAddr (net , addr string , deadline time.Time ) (netaddr , error ) {
200
+ // internetAddrList resolves addr, which may be a literal IP
201
+ // address or a DNS name, and returns a list of internet protocol
202
+ // family addresses. The result contains at least one address when
203
+ // error is nil.
204
+ func internetAddrList (net , addr string , deadline time.Time ) (addrList , error ) {
224
205
var (
225
206
err error
226
207
host , port string
@@ -243,7 +224,7 @@ func resolveInternetAddr(net, addr string, deadline time.Time) (netaddr, error)
243
224
default :
244
225
return nil , UnknownNetworkError (net )
245
226
}
246
- inetaddr := func (ip IPAddr ) netaddr {
227
+ inetaddr := func (ip IPAddr ) Addr {
247
228
switch net {
248
229
case "tcp" , "tcp4" , "tcp6" :
249
230
return & TCPAddr {IP : ip .IP , Port : portnum , Zone : ip .Zone }
@@ -256,16 +237,16 @@ func resolveInternetAddr(net, addr string, deadline time.Time) (netaddr, error)
256
237
}
257
238
}
258
239
if host == "" {
259
- return inetaddr (IPAddr {}), nil
240
+ return addrList { inetaddr (IPAddr {})} , nil
260
241
}
261
242
// Try as a literal IP address.
262
243
var ip IP
263
244
if ip = parseIPv4 (host ); ip != nil {
264
- return inetaddr (IPAddr {IP : ip }), nil
245
+ return addrList { inetaddr (IPAddr {IP : ip })} , nil
265
246
}
266
247
var zone string
267
248
if ip , zone = parseIPv6 (host , true ); ip != nil {
268
- return inetaddr (IPAddr {IP : ip , Zone : zone }), nil
249
+ return addrList { inetaddr (IPAddr {IP : ip , Zone : zone })} , nil
269
250
}
270
251
// Try as a DNS name.
271
252
ips , err := lookupIPDeadline (host , deadline )
@@ -279,7 +260,7 @@ func resolveInternetAddr(net, addr string, deadline time.Time) (netaddr, error)
279
260
if net != "" && net [len (net )- 1 ] == '6' {
280
261
filter = ipv6only
281
262
}
282
- return firstFavoriteAddr (filter , ips , inetaddr )
263
+ return filterAddrList (filter , ips , inetaddr )
283
264
}
284
265
285
266
func zoneToString (zone int ) string {
0 commit comments