* gcc.dg/guality/guality.exp: Skip on AIX.
[official-gcc.git] / libgo / go / net / ipsock.go
blobd930595879c8432d94c4144ffe8ca83cd615f836
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
7 package net
9 import "time"
11 var supportsIPv6, supportsIPv4map bool
13 func init() {
14 sysInit()
15 supportsIPv6, supportsIPv4map = probeIPv6Stack()
18 func firstFavoriteAddr(filter func(IP) IP, addrs []string) (addr IP) {
19 if filter == nil {
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)
26 if addr == nil {
27 addr = firstSupportedAddr(anyaddr, addrs)
29 } else {
30 addr = firstSupportedAddr(filter, addrs)
32 return
35 func firstSupportedAddr(filter func(IP) IP, addrs []string) IP {
36 for _, s := range addrs {
37 if addr := filter(ParseIP(s)); addr != nil {
38 return addr
41 return nil
44 func anyaddr(x IP) IP {
45 if x4 := x.To4(); x4 != nil {
46 return x4
48 if supportsIPv6 {
49 return x
51 return 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 {
60 return x
62 return nil
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) {
77 j, k := 0, 0
79 // The port starts after the last colon.
80 i := last(hostport, ':')
81 if i < 0 {
82 goto missingPort
85 if hostport[0] == '[' {
86 // Expect the first ']' just before the last ':'.
87 end := byteIndex(hostport, ']')
88 if end < 0 {
89 err = &AddrError{"missing ']' in address", hostport}
90 return
92 switch end + 1 {
93 case len(hostport):
94 // There can't be a ':' behind the ']' now.
95 goto missingPort
96 case i:
97 // The expected result.
98 default:
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] == ':' {
102 goto tooManyColons
104 goto missingPort
106 host = hostport[1:end]
107 j, k = 1, end+1 // there can't be a '[' resp. ']' before these positions
108 } else {
109 host = hostport[:i]
110 if byteIndex(host, ':') >= 0 {
111 goto tooManyColons
113 if byteIndex(host, '%') >= 0 {
114 goto missingBrackets
117 if byteIndex(hostport[j:], '[') >= 0 {
118 err = &AddrError{"unexpected '[' in address", hostport}
119 return
121 if byteIndex(hostport[k:], ']') >= 0 {
122 err = &AddrError{"unexpected ']' in address", hostport}
123 return
126 port = hostport[i+1:]
127 return
129 missingPort:
130 err = &AddrError{"missing port in address", hostport}
131 return
133 tooManyColons:
134 err = &AddrError{"too many colons in address", hostport}
135 return
137 missingBrackets:
138 err = &AddrError{"missing brackets in address", hostport}
139 return
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:]
147 } else {
148 host = s
150 return
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,
155 // "[host]:port".
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) {
165 var (
166 err error
167 host, port, zone string
168 portnum int
170 switch net {
171 case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6":
172 if addr != "" {
173 if host, port, err = SplitHostPort(addr); err != nil {
174 return nil, err
176 if portnum, err = parsePort(net, port); err != nil {
177 return nil, err
180 case "ip", "ip4", "ip6":
181 if addr != "" {
182 host = addr
184 default:
185 return nil, UnknownNetworkError(net)
187 inetaddr := func(net string, ip IP, port int, zone string) Addr {
188 switch net {
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}
196 return nil
198 if host == "" {
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)
211 if err != nil {
212 return nil, err
214 var filter func(IP) IP
215 if net != "" && net[len(net)-1] == '4' {
216 filter = ipv4only
218 if net != "" && net[len(net)-1] == '6' || zone != "" {
219 filter = ipv6only
221 ip := firstFavoriteAddr(filter, addrs)
222 if ip == nil {
223 // should not happen
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 {
230 if zone == 0 {
231 return ""
233 if ifi, err := InterfaceByIndex(zone); err == nil {
234 return ifi.Name
236 return itod(uint(zone))
239 func zoneToInt(zone string) int {
240 if zone == "" {
241 return 0
243 if ifi, err := InterfaceByName(zone); err == nil {
244 return ifi.Index
246 n, _, _ := dtoi(zone, 0)
247 return n