1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Internet protocol family sockets
11 var supportsIPv6
, supportsIPv4map
bool
15 supportsIPv6
, supportsIPv4map
= probeIPv6Stack()
18 func firstFavoriteAddr(filter
func(IP
) IP
, addrs
[]string) (addr IP
) {
20 // We'll take any IP address, but since the dialing code
21 // does not yet try multiple addresses, prefer to use
22 // an IPv4 address if possible. This is especially relevant
23 // if localhost resolves to [ipv6-localhost, ipv4-localhost].
24 // Too much code assumes localhost == ipv4-localhost.
25 addr
= firstSupportedAddr(ipv4only
, addrs
)
27 addr
= firstSupportedAddr(anyaddr
, addrs
)
30 addr
= firstSupportedAddr(filter
, addrs
)
35 func firstSupportedAddr(filter
func(IP
) IP
, addrs
[]string) IP
{
36 for _
, s
:= range addrs
{
37 if addr
:= filter(ParseIP(s
)); addr
!= nil {
44 func anyaddr(x IP
) IP
{
45 if x4
:= x
.To4(); x4
!= nil {
54 func ipv4only(x IP
) IP
{ return x
.To4() }
56 func ipv6only(x IP
) IP
{
57 // Only return addresses that we can use
58 // with the kernel's IPv6 addressing modes.
59 if len(x
) == IPv6len
&& x
.To4() == nil && supportsIPv6
{
65 type InvalidAddrError
string
67 func (e InvalidAddrError
) Error() string { return string(e
) }
68 func (e InvalidAddrError
) Timeout() bool { return false }
69 func (e InvalidAddrError
) Temporary() bool { return false }
71 // SplitHostPort splits a network address of the form "host:port",
72 // "[host]:port" or "[ipv6-host%zone]:port" into host or
73 // ipv6-host%zone and port. A literal address or host name for IPv6
74 // must be enclosed in square brackets, as in "[::1]:80",
75 // "[ipv6-host]:http" or "[ipv6-host%zone]:80".
76 func SplitHostPort(hostport
string) (host
, port
string, err error
) {
79 // The port starts after the last colon.
80 i
:= last(hostport
, ':')
85 if hostport
[0] == '[' {
86 // Expect the first ']' just before the last ':'.
87 end
:= byteIndex(hostport
, ']')
89 err
= &AddrError
{"missing ']' in address", hostport
}
94 // There can't be a ':' behind the ']' now.
97 // The expected result.
99 // Either ']' isn't followed by a colon, or it is
100 // followed by a colon that is not the last one.
101 if hostport
[end
+1] == ':' {
106 host
= hostport
[1:end
]
107 j
, k
= 1, end
+1 // there can't be a '[' resp. ']' before these positions
110 if byteIndex(host
, ':') >= 0 {
113 if byteIndex(host
, '%') >= 0 {
117 if byteIndex(hostport
[j
:], '[') >= 0 {
118 err
= &AddrError
{"unexpected '[' in address", hostport
}
121 if byteIndex(hostport
[k
:], ']') >= 0 {
122 err
= &AddrError
{"unexpected ']' in address", hostport
}
126 port
= hostport
[i
+1:]
130 err
= &AddrError
{"missing port in address", hostport
}
134 err
= &AddrError
{"too many colons in address", hostport
}
138 err
= &AddrError
{"missing brackets in address", hostport
}
142 func splitHostZone(s
string) (host
, zone
string) {
143 // The IPv6 scoped addressing zone identifer starts after the
144 // last percent sign.
145 if i
:= last(s
, '%'); i
> 0 {
146 host
, zone
= s
[:i
], s
[i
+1:]
153 // JoinHostPort combines host and port into a network address of the
154 // form "host:port" or, if host contains a colon or a percent sign,
156 func JoinHostPort(host
, port
string) string {
157 // If host has colons or a percent sign, have to bracket it.
158 if byteIndex(host
, ':') >= 0 ||
byteIndex(host
, '%') >= 0 {
159 return "[" + host
+ "]:" + port
161 return host
+ ":" + port
164 func resolveInternetAddr(net
, addr
string, deadline time
.Time
) (Addr
, error
) {
167 host
, port
, zone
string
171 case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6":
173 if host
, port
, err
= SplitHostPort(addr
); err
!= nil {
176 if portnum
, err
= parsePort(net
, port
); err
!= nil {
180 case "ip", "ip4", "ip6":
185 return nil, UnknownNetworkError(net
)
187 inetaddr
:= func(net
string, ip IP
, port
int, zone
string) Addr
{
189 case "tcp", "tcp4", "tcp6":
190 return &TCPAddr
{IP
: ip
, Port
: port
, Zone
: zone
}
191 case "udp", "udp4", "udp6":
192 return &UDPAddr
{IP
: ip
, Port
: port
, Zone
: zone
}
193 case "ip", "ip4", "ip6":
194 return &IPAddr
{IP
: ip
, Zone
: zone
}
199 return inetaddr(net
, nil, portnum
, zone
), nil
201 // Try as an IP address.
202 if ip
:= parseIPv4(host
); ip
!= nil {
203 return inetaddr(net
, ip
, portnum
, zone
), nil
205 if ip
, zone
:= parseIPv6(host
, true); ip
!= nil {
206 return inetaddr(net
, ip
, portnum
, zone
), nil
208 // Try as a domain name.
209 host
, zone
= splitHostZone(host
)
210 addrs
, err
:= lookupHostDeadline(host
, deadline
)
214 var filter
func(IP
) IP
215 if net
!= "" && net
[len(net
)-1] == '4' {
218 if net
!= "" && net
[len(net
)-1] == '6' || zone
!= "" {
221 ip
:= firstFavoriteAddr(filter
, addrs
)
224 return nil, &AddrError
{"LookupHost returned no suitable address", addrs
[0]}
226 return inetaddr(net
, ip
, portnum
, zone
), nil
229 func zoneToString(zone
int) string {
233 if ifi
, err
:= InterfaceByIndex(zone
); err
== nil {
236 return itod(uint(zone
))
239 func zoneToInt(zone
string) int {
243 if ifi
, err
:= InterfaceByName(zone
); err
== nil {
246 n
, _
, _
:= dtoi(zone
, 0)