net: support IPv6 scoped addressing zone

This CL provides IPv6 scoped addressing zone support as defined
in RFC 4007 for internet protocol family connection setups.

Follwoing types and functions allow a literal IPv6 address with
zone identifer as theirs parameter.

pkg net, func Dial(string, string) (Conn, error)
pkg net, func DialOpt(string, ...DialOption) (Conn, error)
pkg net, func DialTimeout(string, string, time.Duration) (Conn, error)
pkg net, func Listen(string, string) (Listener, error)
pkg net, func ListenPacket(string, string) (PacketConn, error)
pkg net, func ResolveIPAddr(string, string) (*IPAddr, error)
pkg net, func ResolveTCPAddr(string, string) (*TCPAddr, error)
pkg net, func ResolveUDPAddr(string, string) (*UDPAddr, error)
pkg net, type IPAddr struct, Zone string
pkg net, type TCPAddr struct, Zone string
pkg net, type UDPAddr struct, Zone string

Also follwoing methods return a literal IPv6 address with zone
identifier string if possible.

pkg net, method (*IPAddr) String() string
pkg net, method (*TCPAddr) String() string
pkg net, method (*UDPAddr) String() string

Fixes #4234.
Fixes #4501.
Update #5081.

R=rsc, iant
CC=golang-dev
https://golang.org/cl/6816116
This commit is contained in:
Mikio Hara 2013-03-23 09:57:40 +09:00
parent c90850277b
commit aa0dda767a
11 changed files with 395 additions and 105 deletions

View file

@ -431,6 +431,9 @@ func (n *IPNet) Contains(ip IP) bool {
return true
}
// Network returns the address's network name, "ip+net".
func (n *IPNet) Network() string { return "ip+net" }
// String returns the CIDR notation of n like "192.168.100.1/24"
// or "2001:DB8::/48" as defined in RFC 4632 and RFC 4291.
// If the mask is not in the canonical form, it returns the
@ -449,9 +452,6 @@ func (n *IPNet) String() string {
return nn.String() + "/" + itod(uint(l))
}
// Network returns the address's network name, "ip+net".
func (n *IPNet) Network() string { return "ip+net" }
// Parse IPv4 address (d.d.d.d).
func parseIPv4(s string) IP {
var p [IPv4len]byte
@ -483,26 +483,26 @@ func parseIPv4(s string) IP {
return IPv4(p[0], p[1], p[2], p[3])
}
// Parse IPv6 address. Many forms.
// The basic form is a sequence of eight colon-separated
// 16-bit hex numbers separated by colons,
// as in 0123:4567:89ab:cdef:0123:4567:89ab:cdef.
// Two exceptions:
// * A run of zeros can be replaced with "::".
// * The last 32 bits can be in IPv4 form.
// Thus, ::ffff:1.2.3.4 is the IPv4 address 1.2.3.4.
func parseIPv6(s string) IP {
p := make(IP, IPv6len)
// parseIPv6 parses s as a literal IPv6 address described in RFC 4291
// and RFC 5952. It can also parse a literal scoped IPv6 address with
// zone identifier which is described in RFC 4007 when zoneAllowed is
// true.
func parseIPv6(s string, zoneAllowed bool) (ip IP, zone string) {
ip = make(IP, IPv6len)
ellipsis := -1 // position of ellipsis in p
i := 0 // index in string s
if zoneAllowed {
s, zone = splitHostZone(s)
}
// Might have leading ellipsis
if len(s) >= 2 && s[0] == ':' && s[1] == ':' {
ellipsis = 0
i = 2
// Might be only ellipsis
if i == len(s) {
return p
return ip, zone
}
}
@ -512,35 +512,35 @@ func parseIPv6(s string) IP {
// Hex number.
n, i1, ok := xtoi(s, i)
if !ok || n > 0xFFFF {
return nil
return nil, zone
}
// If followed by dot, might be in trailing IPv4.
if i1 < len(s) && s[i1] == '.' {
if ellipsis < 0 && j != IPv6len-IPv4len {
// Not the right place.
return nil
return nil, zone
}
if j+IPv4len > IPv6len {
// Not enough room.
return nil
return nil, zone
}
p4 := parseIPv4(s[i:])
if p4 == nil {
return nil
ip4 := parseIPv4(s[i:])
if ip4 == nil {
return nil, zone
}
p[j] = p4[12]
p[j+1] = p4[13]
p[j+2] = p4[14]
p[j+3] = p4[15]
ip[j] = ip4[12]
ip[j+1] = ip4[13]
ip[j+2] = ip4[14]
ip[j+3] = ip4[15]
i = len(s)
j += IPv4len
break
}
// Save this 16-bit chunk.
p[j] = byte(n >> 8)
p[j+1] = byte(n)
ip[j] = byte(n >> 8)
ip[j+1] = byte(n)
j += 2
// Stop at end of string.
@ -551,14 +551,14 @@ func parseIPv6(s string) IP {
// Otherwise must be followed by colon and more.
if s[i] != ':' || i+1 == len(s) {
return nil
return nil, zone
}
i++
// Look for ellipsis.
if s[i] == ':' {
if ellipsis >= 0 { // already have one
return nil
return nil, zone
}
ellipsis = j
if i++; i == len(s) { // can be at end
@ -569,23 +569,23 @@ func parseIPv6(s string) IP {
// Must have used entire string.
if i != len(s) {
return nil
return nil, zone
}
// If didn't parse enough, expand ellipsis.
if j < IPv6len {
if ellipsis < 0 {
return nil
return nil, zone
}
n := IPv6len - j
for k := j - 1; k >= ellipsis; k-- {
p[k+n] = p[k]
ip[k+n] = ip[k]
}
for k := ellipsis + n - 1; k >= ellipsis; k-- {
p[k] = 0
ip[k] = 0
}
}
return p
return ip, zone
}
// A ParseError represents a malformed text string and the type of string that was expected.
@ -598,26 +598,17 @@ func (e *ParseError) Error() string {
return "invalid " + e.Type + ": " + e.Text
}
func parseIP(s string) IP {
if p := parseIPv4(s); p != nil {
return p
}
if p := parseIPv6(s); p != nil {
return p
}
return nil
}
// ParseIP parses s as an IP address, returning the result.
// The string s can be in dotted decimal ("74.125.19.99")
// or IPv6 ("2001:4860:0:2001::68") form.
// If s is not a valid textual representation of an IP address,
// ParseIP returns nil.
func ParseIP(s string) IP {
if p := parseIPv4(s); p != nil {
return p
if ip := parseIPv4(s); ip != nil {
return ip
}
return parseIPv6(s)
ip, _ := parseIPv6(s, false)
return ip
}
// ParseCIDR parses s as a CIDR notation IP address and mask,
@ -632,15 +623,15 @@ func ParseCIDR(s string) (IP, *IPNet, error) {
if i < 0 {
return nil, nil, &ParseError{"CIDR address", s}
}
ipstr, maskstr := s[:i], s[i+1:]
addr, mask := s[:i], s[i+1:]
iplen := IPv4len
ip := parseIPv4(ipstr)
ip := parseIPv4(addr)
if ip == nil {
iplen = IPv6len
ip = parseIPv6(ipstr)
ip, _ = parseIPv6(addr, false)
}
n, i, ok := dtoi(maskstr, 0)
if ip == nil || !ok || i != len(maskstr) || n < 0 || n > 8*iplen {
n, i, ok := dtoi(mask, 0)
if ip == nil || !ok || i != len(mask) || n < 0 || n > 8*iplen {
return nil, nil, &ParseError{"CIDR address", s}
}
m := CIDRMask(n, 8*iplen)