ipsock.go 3.48 KB
Newer Older
1 2 3 4 5 6 7 8
// Copyright 2009 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// IP sockets

package net

9
import "time"
10

11
var supportsIPv6, supportsIPv4map = probeIPv6Stack()
12

Russ Cox's avatar
Russ Cox committed
13
func firstFavoriteAddr(filter func(IP) IP, addrs []string) (addr IP) {
Russ Cox's avatar
Russ Cox committed
14
	if filter == nil {
Russ Cox's avatar
Russ Cox committed
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
		// We'll take any IP address, but since the dialing code
		// does not yet try multiple addresses, prefer to use
		// an IPv4 address if possible.  This is especially relevant
		// if localhost resolves to [ipv6-localhost, ipv4-localhost].
		// Too much code assumes localhost == ipv4-localhost.
		addr = firstSupportedAddr(ipv4only, addrs)
		if addr == nil {
			addr = firstSupportedAddr(anyaddr, addrs)
		}
	} else {
		addr = firstSupportedAddr(filter, addrs)
	}
	return
}

30
func firstSupportedAddr(filter func(IP) IP, addrs []string) IP {
31
	for _, s := range addrs {
32 33
		if addr := filter(ParseIP(s)); addr != nil {
			return addr
34 35
		}
	}
36 37 38
	return nil
}

39 40 41 42 43 44 45 46 47 48
func anyaddr(x IP) IP {
	if x4 := x.To4(); x4 != nil {
		return x4
	}
	if supportsIPv6 {
		return x
	}
	return nil
}

49 50 51 52 53
func ipv4only(x IP) IP { return x.To4() }

func ipv6only(x IP) IP {
	// Only return addresses that we can use
	// with the kernel's IPv6 addressing modes.
54 55
	if len(x) == IPv6len && x.To4() == nil && supportsIPv6 {
		return x
56
	}
57
	return nil
58 59
}

60 61
type InvalidAddrError string

62
func (e InvalidAddrError) Error() string   { return string(e) }
63 64 65
func (e InvalidAddrError) Timeout() bool   { return false }
func (e InvalidAddrError) Temporary() bool { return false }

66 67 68
// SplitHostPort splits a network address of the form
// "host:port" or "[host]:port" into host and port.
// The latter form must be used when host contains a colon.
69
func SplitHostPort(hostport string) (host, port string, err error) {
70
	// The port starts after the last colon.
71
	i := last(hostport, ':')
72
	if i < 0 {
73 74
		err = &AddrError{"missing port in address", hostport}
		return
75 76
	}

77
	host, port = hostport[0:i], hostport[i+1:]
78 79 80

	// Can put brackets around host ...
	if len(host) > 0 && host[0] == '[' && host[len(host)-1] == ']' {
81
		host = host[1 : len(host)-1]
82 83 84
	} else {
		// ... but if there are no brackets, no colons.
		if byteIndex(host, ':') >= 0 {
85 86
			err = &AddrError{"too many colons in address", hostport}
			return
87 88
		}
	}
89
	return
90 91
}

92 93 94
// JoinHostPort combines host and port into a network address
// of the form "host:port" or, if host contains a colon, "[host]:port".
func JoinHostPort(host, port string) string {
95 96
	// If host has colons, have to bracket it.
	if byteIndex(host, ':') >= 0 {
97
		return "[" + host + "]:" + port
98
	}
99
	return host + ":" + port
100 101 102
}

// Convert "host:port" into IP address and port.
103
func hostPortToIP(net, hostport string, deadline time.Time) (ip IP, iport int, err error) {
104
	host, port, err := SplitHostPort(hostport)
105
	if err != nil {
106
		return nil, 0, err
107 108
	}

109
	var addr IP
Russ Cox's avatar
Russ Cox committed
110 111
	if host != "" {
		// Try as an IP address.
112
		addr = ParseIP(host)
113
		if addr == nil {
Russ Cox's avatar
Russ Cox committed
114
			var filter func(IP) IP
115
			if net != "" && net[len(net)-1] == '4' {
116
				filter = ipv4only
117 118
			}
			if net != "" && net[len(net)-1] == '6' {
119 120
				filter = ipv6only
			}
Russ Cox's avatar
Russ Cox committed
121
			// Not an IP address.  Try as a DNS name.
122
			addrs, err := lookupHostDeadline(host, deadline)
123 124
			if err != nil {
				return nil, 0, err
Russ Cox's avatar
Russ Cox committed
125
			}
Russ Cox's avatar
Russ Cox committed
126
			addr = firstFavoriteAddr(filter, addrs)
Russ Cox's avatar
Russ Cox committed
127 128
			if addr == nil {
				// should not happen
129
				return nil, 0, &AddrError{"LookupHost returned no suitable address", addrs[0]}
Russ Cox's avatar
Russ Cox committed
130
			}
131 132 133
		}
	}

134 135 136
	p, err := parsePort(net, port)
	if err != nil {
		return nil, 0, err
137 138
	}

139
	return addr, p, nil
140
}