Commit 41f93a43 authored by Russ Cox's avatar Russ Cox

net: drop laddr from Dial, cname from LookupHost; new functions

Drop laddr argument from Dial.

Drop cname return from LookupHost.

Add LookupIP, LookupCNAME, ParseCIDR, IP.Equal.
Export SplitHostPort, JoinHostPort.
Add AAAA (IPv6) support to host lookups.

Preparations for implementing some of the
lookups using cgo.

ParseCIDR and IP.Equal are logically new in this CL
but accidentally snuck into an earlier CL about unused
labels that was in the same client.

In crypto/tls, drop laddr from Dial to match net.

R=golang-dev, dsymonds, adg, rh
CC=golang-dev
https://golang.org/cl/4244055
parent 188a17db
...@@ -50,7 +50,7 @@ func TestRunClient(t *testing.T) { ...@@ -50,7 +50,7 @@ func TestRunClient(t *testing.T) {
testConfig.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA} testConfig.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA}
conn, err := Dial("tcp", "", "127.0.0.1:10443", testConfig) conn, err := Dial("tcp", "127.0.0.1:10443", testConfig)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
......
...@@ -87,8 +87,9 @@ func Listen(network, laddr string, config *Config) (*Listener, os.Error) { ...@@ -87,8 +87,9 @@ func Listen(network, laddr string, config *Config) (*Listener, os.Error) {
// Dial interprets a nil configuration as equivalent to // Dial interprets a nil configuration as equivalent to
// the zero configuration; see the documentation of Config // the zero configuration; see the documentation of Config
// for the defaults. // for the defaults.
func Dial(network, laddr, raddr string, config *Config) (*Conn, os.Error) { func Dial(network, addr string, config *Config) (*Conn, os.Error) {
c, err := net.Dial(network, laddr, raddr) raddr := addr
c, err := net.Dial(network, raddr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -6,6 +6,7 @@ include ../../Make.inc ...@@ -6,6 +6,7 @@ include ../../Make.inc
TARG=net TARG=net
GOFILES=\ GOFILES=\
cgo_stub.go\
dial.go\ dial.go\
dnsmsg.go\ dnsmsg.go\
fd_$(GOOS).go\ fd_$(GOOS).go\
...@@ -13,6 +14,7 @@ GOFILES=\ ...@@ -13,6 +14,7 @@ GOFILES=\
ip.go\ ip.go\
ipsock.go\ ipsock.go\
iprawsock.go\ iprawsock.go\
lookup.go\
net.go\ net.go\
parse.go\ parse.go\
pipe.go\ pipe.go\
......
// Copyright 2011 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.
// Stub cgo routines for systems that do not use cgo to do network lookups.
package net
import "os"
func cgoLookupHost(name string) (addrs []string, err os.Error, completed bool) {
return nil, nil, false
}
func cgoLookupPort(network, service string) (port int, err os.Error, completed bool) {
return 0, nil, false
}
func cgoLookupIP(name string) (addrs []IP, err os.Error, completed bool) {
return nil, nil, false
}
...@@ -6,9 +6,7 @@ package net ...@@ -6,9 +6,7 @@ package net
import "os" import "os"
// Dial connects to the remote address raddr on the network net. // Dial connects to the address addr on the network net.
// If the string laddr is not empty, it is used as the local address
// for the connection.
// //
// Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only), // Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only),
// "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4" // "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4"
...@@ -16,79 +14,56 @@ import "os" ...@@ -16,79 +14,56 @@ import "os"
// //
// For IP networks, addresses have the form host:port. If host is // For IP networks, addresses have the form host:port. If host is
// a literal IPv6 address, it must be enclosed in square brackets. // a literal IPv6 address, it must be enclosed in square brackets.
// The functions JoinHostPort and SplitHostPort manipulate
// addresses in this form.
// //
// Examples: // Examples:
// Dial("tcp", "", "12.34.56.78:80") // Dial("tcp", "12.34.56.78:80")
// Dial("tcp", "", "google.com:80") // Dial("tcp", "google.com:80")
// Dial("tcp", "", "[de:ad:be:ef::ca:fe]:80") // Dial("tcp", "[de:ad:be:ef::ca:fe]:80")
// Dial("tcp", "127.0.0.1:123", "127.0.0.1:88")
// //
func Dial(net, laddr, raddr string) (c Conn, err os.Error) { func Dial(net, addr string) (c Conn, err os.Error) {
raddr := addr
if raddr == "" {
return nil, &OpError{"dial", net, nil, errMissingAddress}
}
switch net { switch net {
case "tcp", "tcp4", "tcp6": case "tcp", "tcp4", "tcp6":
var la, ra *TCPAddr var ra *TCPAddr
if laddr != "" {
if la, err = ResolveTCPAddr(laddr); err != nil {
goto Error
}
}
if raddr != "" {
if ra, err = ResolveTCPAddr(raddr); err != nil { if ra, err = ResolveTCPAddr(raddr); err != nil {
goto Error goto Error
} }
} c, err := DialTCP(net, nil, ra)
c, err := DialTCP(net, la, ra)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return c, nil return c, nil
case "udp", "udp4", "udp6": case "udp", "udp4", "udp6":
var la, ra *UDPAddr var ra *UDPAddr
if laddr != "" {
if la, err = ResolveUDPAddr(laddr); err != nil {
goto Error
}
}
if raddr != "" {
if ra, err = ResolveUDPAddr(raddr); err != nil { if ra, err = ResolveUDPAddr(raddr); err != nil {
goto Error goto Error
} }
} c, err := DialUDP(net, nil, ra)
c, err := DialUDP(net, la, ra)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return c, nil return c, nil
case "unix", "unixgram", "unixpacket": case "unix", "unixgram", "unixpacket":
var la, ra *UnixAddr var ra *UnixAddr
if raddr != "" {
if ra, err = ResolveUnixAddr(net, raddr); err != nil { if ra, err = ResolveUnixAddr(net, raddr); err != nil {
goto Error goto Error
} }
} c, err = DialUnix(net, nil, ra)
if laddr != "" {
if la, err = ResolveUnixAddr(net, laddr); err != nil {
goto Error
}
}
c, err = DialUnix(net, la, ra)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return c, nil return c, nil
case "ip", "ip4", "ip6": case "ip", "ip4", "ip6":
var la, ra *IPAddr var ra *IPAddr
if laddr != "" {
if la, err = ResolveIPAddr(laddr); err != nil {
goto Error
}
}
if raddr != "" {
if ra, err = ResolveIPAddr(raddr); err != nil { if ra, err = ResolveIPAddr(raddr); err != nil {
goto Error goto Error
} }
} c, err := DialIP(net, nil, ra)
c, err := DialIP(net, la, ra)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -32,7 +32,7 @@ func fetchGoogle(t *testing.T, fd Conn, network, addr string) { ...@@ -32,7 +32,7 @@ func fetchGoogle(t *testing.T, fd Conn, network, addr string) {
} }
func doDial(t *testing.T, network, addr string) { func doDial(t *testing.T, network, addr string) {
fd, err := Dial(network, "", addr) fd, err := Dial(network, addr)
if err != nil { if err != nil {
t.Errorf("Dial(%q, %q, %q) = _, %v", network, "", addr, err) t.Errorf("Dial(%q, %q, %q) = _, %v", network, "", addr, err)
return return
...@@ -55,6 +55,13 @@ var googleaddrs = []string{ ...@@ -55,6 +55,13 @@ var googleaddrs = []string{
"[2001:4860:0:2001::68]:80", // ipv6.google.com; removed if ipv6 flag not set "[2001:4860:0:2001::68]:80", // ipv6.google.com; removed if ipv6 flag not set
} }
func TestLookupCNAME(t *testing.T) {
cname, err := LookupCNAME("www.google.com")
if cname != "www.l.google.com." || err != nil {
t.Errorf(`LookupCNAME("www.google.com.") = %q, %v, want "www.l.google.com.", nil`, cname, err)
}
}
func TestDialGoogle(t *testing.T) { func TestDialGoogle(t *testing.T) {
// If no ipv6 tunnel, don't try the last address. // If no ipv6 tunnel, don't try the last address.
if !*ipv6 { if !*ipv6 {
...@@ -64,14 +71,14 @@ func TestDialGoogle(t *testing.T) { ...@@ -64,14 +71,14 @@ func TestDialGoogle(t *testing.T) {
// Insert an actual IP address for google.com // Insert an actual IP address for google.com
// into the table. // into the table.
_, addrs, err := LookupHost("www.google.com") addrs, err := LookupIP("www.google.com")
if err != nil { if err != nil {
t.Fatalf("lookup www.google.com: %v", err) t.Fatalf("lookup www.google.com: %v", err)
} }
if len(addrs) == 0 { if len(addrs) == 0 {
t.Fatalf("no addresses for www.google.com") t.Fatalf("no addresses for www.google.com")
} }
ip := ParseIP(addrs[0]).To4() ip := addrs[0].To4()
for i, s := range googleaddrs { for i, s := range googleaddrs {
if strings.Contains(s, "%") { if strings.Contains(s, "%") {
......
...@@ -159,7 +159,7 @@ func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs ...@@ -159,7 +159,7 @@ func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs
// all the cfg.servers[i] are IP addresses, which // all the cfg.servers[i] are IP addresses, which
// Dial will use without a DNS lookup. // Dial will use without a DNS lookup.
server := cfg.servers[i] + ":53" server := cfg.servers[i] + ":53"
c, cerr := Dial("udp", "", server) c, cerr := Dial("udp", server)
if cerr != nil { if cerr != nil {
err = cerr err = cerr
continue continue
...@@ -178,12 +178,23 @@ func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs ...@@ -178,12 +178,23 @@ func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs
return return
} }
func convertRR_A(records []dnsRR) []string { func convertRR_A(records []dnsRR) []IP {
addrs := make([]string, len(records)) addrs := make([]IP, len(records))
for i := 0; i < len(records); i++ { for i := 0; i < len(records); i++ {
rr := records[i] rr := records[i]
a := rr.(*dnsRR_A).A a := rr.(*dnsRR_A).A
addrs[i] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a)).String() addrs[i] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a))
}
return addrs
}
func convertRR_AAAA(records []dnsRR) []IP {
addrs := make([]IP, len(records))
for i := 0; i < len(records); i++ {
rr := records[i]
a := make(IP, 16)
copy(a, rr.(*dnsRR_AAAA).AAAA[:])
addrs[i] = a
} }
return addrs return addrs
} }
...@@ -294,10 +305,8 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err os.Erro ...@@ -294,10 +305,8 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err os.Erro
return return
} }
// LookupHost looks for name using the local hosts file and DNS resolver. // goLookupHost is the native Go implementation of LookupHost.
// It returns the canonical name for the host and an array of that func goLookupHost(name string) (addrs []string, err os.Error) {
// host's addresses.
func LookupHost(name string) (cname string, addrs []string, err os.Error) {
onceLoadConfig.Do(loadConfig) onceLoadConfig.Do(loadConfig)
if dnserr != nil || cfg == nil { if dnserr != nil || cfg == nil {
err = dnserr err = dnserr
...@@ -306,18 +315,69 @@ func LookupHost(name string) (cname string, addrs []string, err os.Error) { ...@@ -306,18 +315,69 @@ func LookupHost(name string) (cname string, addrs []string, err os.Error) {
// Use entries from /etc/hosts if they match. // Use entries from /etc/hosts if they match.
addrs = lookupStaticHost(name) addrs = lookupStaticHost(name)
if len(addrs) > 0 { if len(addrs) > 0 {
cname = name return
}
ips, err := goLookupIP(name)
if err != nil {
return
}
addrs = make([]string, 0, len(ips))
for _, ip := range ips {
addrs = append(addrs, ip.String())
}
return
}
// goLookupIP is the native Go implementation of LookupIP.
func goLookupIP(name string) (addrs []IP, err os.Error) {
onceLoadConfig.Do(loadConfig)
if dnserr != nil || cfg == nil {
err = dnserr
return return
} }
var records []dnsRR var records []dnsRR
var cname string
cname, records, err = lookup(name, dnsTypeA) cname, records, err = lookup(name, dnsTypeA)
if err != nil { if err != nil {
return return
} }
addrs = convertRR_A(records) addrs = convertRR_A(records)
if cname != "" {
name = cname
}
_, records, err = lookup(name, dnsTypeAAAA)
if err != nil && len(addrs) > 0 {
// Ignore error because A lookup succeeded.
err = nil
}
if err != nil {
return
}
addrs = append(addrs, convertRR_AAAA(records)...)
return
}
// LookupCNAME returns the canonical DNS host for the given name.
// Callers that do not care about the canonical name can call
// LookupHost or LookupIP directly; both take care of resolving
// the canonical name as part of the lookup.
func LookupCNAME(name string) (cname string, err os.Error) {
onceLoadConfig.Do(loadConfig)
if dnserr != nil || cfg == nil {
err = dnserr
return
}
_, rr, err := lookup(name, dnsTypeCNAME)
if err != nil {
return
}
if len(rr) >= 0 {
cname = rr[0].(*dnsRR_CNAME).Cname
}
return return
} }
// An SRV represents a single DNS SRV record.
type SRV struct { type SRV struct {
Target string Target string
Port uint16 Port uint16
...@@ -344,11 +404,13 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os. ...@@ -344,11 +404,13 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os.
return return
} }
// An MX represents a single DNS MX record.
type MX struct { type MX struct {
Host string Host string
Pref uint16 Pref uint16
} }
// LookupMX returns the DNS MX records associated with name.
func LookupMX(name string) (entries []*MX, err os.Error) { func LookupMX(name string) (entries []*MX, err os.Error) {
var records []dnsRR var records []dnsRR
_, records, err = lookup(name, dnsTypeMX) _, records, err = lookup(name, dnsTypeMX)
......
...@@ -50,6 +50,7 @@ const ( ...@@ -50,6 +50,7 @@ const (
dnsTypeMINFO = 14 dnsTypeMINFO = 14
dnsTypeMX = 15 dnsTypeMX = 15
dnsTypeTXT = 16 dnsTypeTXT = 16
dnsTypeAAAA = 28
dnsTypeSRV = 33 dnsTypeSRV = 33
// valid dnsQuestion.qtype only // valid dnsQuestion.qtype only
...@@ -244,8 +245,18 @@ type dnsRR_A struct { ...@@ -244,8 +245,18 @@ type dnsRR_A struct {
A uint32 "ipv4" A uint32 "ipv4"
} }
func (rr *dnsRR_A) Header() *dnsRR_Header { return &rr.Hdr } func (rr *dnsRR_A) Header() *dnsRR_Header {
return &rr.Hdr
}
type dnsRR_AAAA struct {
Hdr dnsRR_Header
AAAA [16]byte "ipv6"
}
func (rr *dnsRR_AAAA) Header() *dnsRR_Header {
return &rr.Hdr
}
// Packing and unpacking. // Packing and unpacking.
// //
...@@ -270,6 +281,7 @@ var rr_mk = map[int]func() dnsRR{ ...@@ -270,6 +281,7 @@ var rr_mk = map[int]func() dnsRR{
dnsTypeTXT: func() dnsRR { return new(dnsRR_TXT) }, dnsTypeTXT: func() dnsRR { return new(dnsRR_TXT) },
dnsTypeSRV: func() dnsRR { return new(dnsRR_SRV) }, dnsTypeSRV: func() dnsRR { return new(dnsRR_SRV) },
dnsTypeA: func() dnsRR { return new(dnsRR_A) }, dnsTypeA: func() dnsRR { return new(dnsRR_A) },
dnsTypeAAAA: func() dnsRR { return new(dnsRR_AAAA) },
} }
// Pack a domain name s into msg[off:]. // Pack a domain name s into msg[off:].
...@@ -377,7 +389,7 @@ Loop: ...@@ -377,7 +389,7 @@ Loop:
// TODO(rsc): Move into generic library? // TODO(rsc): Move into generic library?
// Pack a reflect.StructValue into msg. Struct members can only be uint16, uint32, string, // Pack a reflect.StructValue into msg. Struct members can only be uint16, uint32, string,
// and other (often anonymous) structs. // [n]byte, and other (often anonymous) structs.
func packStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, ok bool) { func packStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, ok bool) {
for i := 0; i < val.NumField(); i++ { for i := 0; i < val.NumField(); i++ {
f := val.Type().(*reflect.StructType).Field(i) f := val.Type().(*reflect.StructType).Field(i)
...@@ -410,6 +422,16 @@ func packStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, o ...@@ -410,6 +422,16 @@ func packStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, o
msg[off+3] = byte(i) msg[off+3] = byte(i)
off += 4 off += 4
} }
case *reflect.ArrayValue:
if fv.Type().(*reflect.ArrayType).Elem().Kind() != reflect.Uint8 {
goto BadType
}
n := fv.Len()
if off+n > len(msg) {
return len(msg), false
}
reflect.Copy(reflect.NewValue(msg[off:off+n]).(*reflect.SliceValue), fv)
off += n
case *reflect.StringValue: case *reflect.StringValue:
// There are multiple string encodings. // There are multiple string encodings.
// The tag distinguishes ordinary strings from domain names. // The tag distinguishes ordinary strings from domain names.
...@@ -478,6 +500,16 @@ func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, ...@@ -478,6 +500,16 @@ func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int,
fv.Set(uint64(i)) fv.Set(uint64(i))
off += 4 off += 4
} }
case *reflect.ArrayValue:
if fv.Type().(*reflect.ArrayType).Elem().Kind() != reflect.Uint8 {
goto BadType
}
n := fv.Len()
if off+n > len(msg) {
return len(msg), false
}
reflect.Copy(fv, reflect.NewValue(msg[off:off+n]).(*reflect.SliceValue))
off += n
case *reflect.StringValue: case *reflect.StringValue:
var s string var s string
switch f.Tag { switch f.Tag {
...@@ -515,7 +547,8 @@ func unpackStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) { ...@@ -515,7 +547,8 @@ func unpackStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) {
// Generic struct printer. // Generic struct printer.
// Doesn't care about the string tag "domain-name", // Doesn't care about the string tag "domain-name",
// but does look for an "ipv4" tag on uint32 variables, // but does look for an "ipv4" tag on uint32 variables
// and the "ipv6" tag on array variables,
// printing them as IP addresses. // printing them as IP addresses.
func printStructValue(val *reflect.StructValue) string { func printStructValue(val *reflect.StructValue) string {
s := "{" s := "{"
...@@ -533,6 +566,9 @@ func printStructValue(val *reflect.StructValue) string { ...@@ -533,6 +566,9 @@ func printStructValue(val *reflect.StructValue) string {
} else if fv, ok := fval.(*reflect.UintValue); ok && f.Tag == "ipv4" { } else if fv, ok := fval.(*reflect.UintValue); ok && f.Tag == "ipv4" {
i := fv.Get() i := fv.Get()
s += IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String() s += IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String()
} else if fv, ok := fval.(*reflect.ArrayValue); ok && f.Tag == "ipv6" {
i := fv.Interface().([]byte)
s += IP(i).String()
} else { } else {
s += fmt.Sprint(fval.Interface()) s += fmt.Sprint(fval.Interface())
} }
......
...@@ -13,7 +13,6 @@ type hostTest struct { ...@@ -13,7 +13,6 @@ type hostTest struct {
ips []IP ips []IP
} }
var hosttests = []hostTest{ var hosttests = []hostTest{
{"odin", []IP{ {"odin", []IP{
IPv4(127, 0, 0, 2), IPv4(127, 0, 0, 2),
......
...@@ -474,13 +474,13 @@ func parseIPv6(s string) IP { ...@@ -474,13 +474,13 @@ func parseIPv6(s string) IP {
return p return p
} }
// A SyntaxError represents a malformed text string and the type of string that was expected. // A ParseError represents a malformed text string and the type of string that was expected.
type SyntaxError struct { type ParseError struct {
Type string Type string
Text string Text string
} }
func (e *SyntaxError) String() string { func (e *ParseError) String() string {
return "invalid " + e.Type + ": " + e.Text return "invalid " + e.Type + ": " + e.Text
} }
...@@ -507,24 +507,29 @@ func ParseIP(s string) IP { ...@@ -507,24 +507,29 @@ func ParseIP(s string) IP {
} }
// ParseCIDR parses s as a CIDR notation IP address and mask, // ParseCIDR parses s as a CIDR notation IP address and mask,
// like "192.168.100.1/24" or "2001:DB8::/48". // like "192.168.100.1/24", "2001:DB8::/48", as defined in
// RFC 4632 and RFC 4291.
func ParseCIDR(s string) (ip IP, mask IPMask, err os.Error) { func ParseCIDR(s string) (ip IP, mask IPMask, err os.Error) {
i := byteIndex(s, '/') i := byteIndex(s, '/')
if i < 0 { if i < 0 {
return nil, nil, &SyntaxError{"CIDR address", s} return nil, nil, &ParseError{"CIDR address", s}
} }
ipstr, maskstr := s[:i], s[i+1:] ipstr, maskstr := s[:i], s[i+1:]
ip = ParseIP(ipstr) iplen := 4
ip = parseIPv4(ipstr)
if ip == nil {
iplen = 16
ip = parseIPv6(ipstr)
}
nn, i, ok := dtoi(maskstr, 0) nn, i, ok := dtoi(maskstr, 0)
if ip == nil || !ok || i != len(maskstr) || nn < 0 || nn > 8*len(ip) { if ip == nil || !ok || i != len(maskstr) || nn < 0 || nn > 8*iplen {
return nil, nil, &SyntaxError{"CIDR address", s} return nil, nil, &ParseError{"CIDR address", s}
} }
n := uint(nn) n := uint(nn)
if len(ip) == 4 { if iplen == 4 {
v4mask := ^uint32(0xffffffff >> n) v4mask := ^uint32(0xffffffff >> n)
mask = IPMask(IPv4(byte(v4mask>>24), byte(v4mask>>16), byte(v4mask>>8), byte(v4mask))) mask = IPv4Mask(byte(v4mask>>24), byte(v4mask>>16), byte(v4mask>>8), byte(v4mask))
return ip, mask, nil } else {
}
mask = make(IPMask, 16) mask = make(IPMask, 16)
for i := 0; i < 16; i++ { for i := 0; i < 16; i++ {
if n >= 8 { if n >= 8 {
...@@ -534,6 +539,14 @@ func ParseCIDR(s string) (ip IP, mask IPMask, err os.Error) { ...@@ -534,6 +539,14 @@ func ParseCIDR(s string) (ip IP, mask IPMask, err os.Error) {
} }
mask[i] = ^byte(0xff >> n) mask[i] = ^byte(0xff >> n)
n = 0 n = 0
}
}
// address must not have any bits not in mask
for i := range ip {
if ip[i]&^mask[i] != 0 {
return nil, nil, &ParseError{"CIDR address", s}
}
} }
return ip, mask, nil return ip, mask, nil
} }
...@@ -5,30 +5,26 @@ ...@@ -5,30 +5,26 @@
package net package net
import ( import (
"bytes"
"reflect"
"testing" "testing"
"os"
) )
func isEqual(a, b IP) bool { func isEqual(a, b []byte) bool {
if a == nil && b == nil { if a == nil && b == nil {
return true return true
} }
if a == nil || b == nil || len(a) != len(b) { if a == nil || b == nil {
return false return false
} }
for i := 0; i < len(a); i++ { return bytes.Equal(a, b)
if a[i] != b[i] {
return false
}
}
return true
} }
type parseIPTest struct { var parseiptests = []struct {
in string in string
out IP out IP
} }{
var parseiptests = []parseIPTest{
{"127.0.1.2", IPv4(127, 0, 1, 2)}, {"127.0.1.2", IPv4(127, 0, 1, 2)},
{"127.0.0.1", IPv4(127, 0, 0, 1)}, {"127.0.0.1", IPv4(127, 0, 0, 1)},
{"127.0.0.256", nil}, {"127.0.0.256", nil},
...@@ -43,20 +39,17 @@ var parseiptests = []parseIPTest{ ...@@ -43,20 +39,17 @@ var parseiptests = []parseIPTest{
} }
func TestParseIP(t *testing.T) { func TestParseIP(t *testing.T) {
for i := 0; i < len(parseiptests); i++ { for _, tt := range parseiptests {
tt := parseiptests[i]
if out := ParseIP(tt.in); !isEqual(out, tt.out) { if out := ParseIP(tt.in); !isEqual(out, tt.out) {
t.Errorf("ParseIP(%#q) = %v, want %v", tt.in, out, tt.out) t.Errorf("ParseIP(%#q) = %v, want %v", tt.in, out, tt.out)
} }
} }
} }
type ipStringTest struct { var ipstringtests = []struct {
in IP in IP
out string out string
} }{
var ipstringtests = []ipStringTest{
// cf. RFC 5952 (A Recommendation for IPv6 Address Text Representation) // cf. RFC 5952 (A Recommendation for IPv6 Address Text Representation)
{IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0,
0, 0, 0x1, 0x23, 0, 0x12, 0, 0x1}, 0, 0, 0x1, 0x23, 0, 0x12, 0, 0x1},
...@@ -85,10 +78,67 @@ var ipstringtests = []ipStringTest{ ...@@ -85,10 +78,67 @@ var ipstringtests = []ipStringTest{
} }
func TestIPString(t *testing.T) { func TestIPString(t *testing.T) {
for i := 0; i < len(ipstringtests); i++ { for _, tt := range ipstringtests {
tt := ipstringtests[i]
if out := tt.in.String(); out != tt.out { if out := tt.in.String(); out != tt.out {
t.Errorf("IP.String(%v) = %#q, want %#q", tt.in, out, tt.out) t.Errorf("IP.String(%v) = %#q, want %#q", tt.in, out, tt.out)
} }
} }
} }
var parsecidrtests = []struct {
in string
ip IP
mask IPMask
err os.Error
}{
{"135.104.0.0/32", IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 255), nil},
{"0.0.0.0/24", IPv4(0, 0, 0, 0), IPv4Mask(255, 255, 255, 0), nil},
{"135.104.0.0/24", IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 0), nil},
{"135.104.0.1/32", IPv4(135, 104, 0, 1), IPv4Mask(255, 255, 255, 255), nil},
{"135.104.0.1/24", nil, nil, &ParseError{"CIDR address", "135.104.0.1/24"}},
{"::1/128", ParseIP("::1"), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")), nil},
{"abcd:2345::/127", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe")), nil},
{"abcd:2345::/65", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff:8000::")), nil},
{"abcd:2345::/64", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff::")), nil},
{"abcd:2345::/63", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:fffe::")), nil},
{"abcd:2345::/33", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:8000::")), nil},
{"abcd:2345::/32", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff::")), nil},
{"abcd:2344::/31", ParseIP("abcd:2344::"), IPMask(ParseIP("ffff:fffe::")), nil},
{"abcd:2300::/24", ParseIP("abcd:2300::"), IPMask(ParseIP("ffff:ff00::")), nil},
{"abcd:2345::/24", nil, nil, &ParseError{"CIDR address", "abcd:2345::/24"}},
{"2001:DB8::/48", ParseIP("2001:DB8::"), IPMask(ParseIP("ffff:ffff:ffff::")), nil},
}
func TestParseCIDR(t *testing.T) {
for _, tt := range parsecidrtests {
if ip, mask, err := ParseCIDR(tt.in); !isEqual(ip, tt.ip) || !isEqual(mask, tt.mask) || !reflect.DeepEqual(err, tt.err) {
t.Errorf("ParseCIDR(%q) = %v, %v, %v; want %v, %v, %v", tt.in, ip, mask, err, tt.ip, tt.mask, tt.err)
}
}
}
var splitjointests = []struct {
Host string
Port string
Join string
}{
{"www.google.com", "80", "www.google.com:80"},
{"127.0.0.1", "1234", "127.0.0.1:1234"},
{"::1", "80", "[::1]:80"},
}
func TestSplitHostPort(t *testing.T) {
for _, tt := range splitjointests {
if host, port, err := SplitHostPort(tt.Join); host != tt.Host || port != tt.Port || err != nil {
t.Errorf("SplitHostPort(%q) = %q, %q, %v; want %q, %q, nil", tt.Join, host, port, err, tt.Host, tt.Port)
}
}
}
func TestJoinHostPort(t *testing.T) {
for _, tt := range splitjointests {
if join := JoinHostPort(tt.Host, tt.Port); join != tt.Join {
t.Errorf("JoinHostPort(%q, %q) = %q; want %q", tt.Host, tt.Port, join, tt.Join)
}
}
}
...@@ -240,7 +240,7 @@ func hostToIP(host string) (ip IP, err os.Error) { ...@@ -240,7 +240,7 @@ func hostToIP(host string) (ip IP, err os.Error) {
addr = ParseIP(host) addr = ParseIP(host)
if addr == nil { if addr == nil {
// Not an IP address. Try as a DNS name. // Not an IP address. Try as a DNS name.
_, addrs, err1 := LookupHost(host) addrs, err1 := LookupHost(host)
if err1 != nil { if err1 != nil {
err = err1 err = err1
goto Error goto Error
......
...@@ -170,9 +170,10 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) { ...@@ -170,9 +170,10 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) {
return nil, InvalidAddrError("unexpected socket family") return nil, InvalidAddrError("unexpected socket family")
} }
// Split "host:port" into "host" and "port". // SplitHostPort splits a network address of the form
// Host cannot contain colons unless it is bracketed. // "host:port" or "[host]:port" into host and port.
func splitHostPort(hostport string) (host, port string, err os.Error) { // The latter form must be used when host contains a colon.
func SplitHostPort(hostport string) (host, port string, err os.Error) {
// The port starts after the last colon. // The port starts after the last colon.
i := last(hostport, ':') i := last(hostport, ':')
if i < 0 { if i < 0 {
...@@ -195,9 +196,9 @@ func splitHostPort(hostport string) (host, port string, err os.Error) { ...@@ -195,9 +196,9 @@ func splitHostPort(hostport string) (host, port string, err os.Error) {
return return
} }
// Join "host" and "port" into "host:port". // JoinHostPort combines host and port into a network address
// If host contains colons, will join into "[host]:port". // of the form "host:port" or, if host contains a colon, "[host]:port".
func joinHostPort(host, port string) string { func JoinHostPort(host, port string) string {
// If host has colons, have to bracket it. // If host has colons, have to bracket it.
if byteIndex(host, ':') >= 0 { if byteIndex(host, ':') >= 0 {
return "[" + host + "]:" + port return "[" + host + "]:" + port
...@@ -207,7 +208,7 @@ func joinHostPort(host, port string) string { ...@@ -207,7 +208,7 @@ func joinHostPort(host, port string) string {
// Convert "host:port" into IP address and port. // Convert "host:port" into IP address and port.
func hostPortToIP(net, hostport string) (ip IP, iport int, err os.Error) { func hostPortToIP(net, hostport string) (ip IP, iport int, err os.Error) {
host, port, err := splitHostPort(hostport) host, port, err := SplitHostPort(hostport)
if err != nil { if err != nil {
goto Error goto Error
} }
...@@ -218,7 +219,7 @@ func hostPortToIP(net, hostport string) (ip IP, iport int, err os.Error) { ...@@ -218,7 +219,7 @@ func hostPortToIP(net, hostport string) (ip IP, iport int, err os.Error) {
addr = ParseIP(host) addr = ParseIP(host)
if addr == nil { if addr == nil {
// Not an IP address. Try as a DNS name. // Not an IP address. Try as a DNS name.
_, addrs, err1 := LookupHost(host) addrs, err1 := LookupHost(host)
if err1 != nil { if err1 != nil {
err = err1 err = err1
goto Error goto Error
......
// Copyright 2011 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.
package net
import (
"os"
)
// LookupHost looks up the given host using the local resolver.
// It returns an array of that host's addresses.
func LookupHost(host string) (addrs []string, err os.Error) {
addrs, err, ok := cgoLookupHost(host)
if !ok {
addrs, err = goLookupHost(host)
}
return
}
// LookupIP looks up host using the local resolver.
// It returns an array of that host's IPv4 and IPv6 addresses.
func LookupIP(host string) (addrs []IP, err os.Error) {
addrs, err, ok := cgoLookupIP(host)
if !ok {
addrs, err = goLookupIP(host)
}
return
}
// LookupPort looks up the port for the given network and service.
func LookupPort(network, service string) (port int, err os.Error) {
port, err, ok := cgoLookupPort(network, service)
if !ok {
port, err = goLookupPort(network, service)
}
return
}
...@@ -15,50 +15,49 @@ var runErrorTest = flag.Bool("run_error_test", false, "let TestDialError check f ...@@ -15,50 +15,49 @@ var runErrorTest = flag.Bool("run_error_test", false, "let TestDialError check f
type DialErrorTest struct { type DialErrorTest struct {
Net string Net string
Laddr string
Raddr string Raddr string
Pattern string Pattern string
} }
var dialErrorTests = []DialErrorTest{ var dialErrorTests = []DialErrorTest{
{ {
"datakit", "", "mh/astro/r70", "datakit", "mh/astro/r70",
"dial datakit mh/astro/r70: unknown network datakit", "dial datakit mh/astro/r70: unknown network datakit",
}, },
{ {
"tcp", "", "127.0.0.1:☺", "tcp", "127.0.0.1:☺",
"dial tcp 127.0.0.1:☺: unknown port tcp/☺", "dial tcp 127.0.0.1:☺: unknown port tcp/☺",
}, },
{ {
"tcp", "", "no-such-name.google.com.:80", "tcp", "no-such-name.google.com.:80",
"dial tcp no-such-name.google.com.:80: lookup no-such-name.google.com.( on .*)?: no (.*)", "dial tcp no-such-name.google.com.:80: lookup no-such-name.google.com.( on .*)?: no (.*)",
}, },
{ {
"tcp", "", "no-such-name.no-such-top-level-domain.:80", "tcp", "no-such-name.no-such-top-level-domain.:80",
"dial tcp no-such-name.no-such-top-level-domain.:80: lookup no-such-name.no-such-top-level-domain.( on .*)?: no (.*)", "dial tcp no-such-name.no-such-top-level-domain.:80: lookup no-such-name.no-such-top-level-domain.( on .*)?: no (.*)",
}, },
{ {
"tcp", "", "no-such-name:80", "tcp", "no-such-name:80",
`dial tcp no-such-name:80: lookup no-such-name\.(.*\.)?( on .*)?: no (.*)`, `dial tcp no-such-name:80: lookup no-such-name\.(.*\.)?( on .*)?: no (.*)`,
}, },
{ {
"tcp", "", "mh/astro/r70:http", "tcp", "mh/astro/r70:http",
"dial tcp mh/astro/r70:http: lookup mh/astro/r70: invalid domain name", "dial tcp mh/astro/r70:http: lookup mh/astro/r70: invalid domain name",
}, },
{ {
"unix", "", "/etc/file-not-found", "unix", "/etc/file-not-found",
"dial unix /etc/file-not-found: no such file or directory", "dial unix /etc/file-not-found: no such file or directory",
}, },
{ {
"unix", "", "/etc/", "unix", "/etc/",
"dial unix /etc/: (permission denied|socket operation on non-socket|connection refused)", "dial unix /etc/: (permission denied|socket operation on non-socket|connection refused)",
}, },
{ {
"unixpacket", "", "/etc/file-not-found", "unixpacket", "/etc/file-not-found",
"dial unixpacket /etc/file-not-found: no such file or directory", "dial unixpacket /etc/file-not-found: no such file or directory",
}, },
{ {
"unixpacket", "", "/etc/", "unixpacket", "/etc/",
"dial unixpacket /etc/: (permission denied|socket operation on non-socket|connection refused)", "dial unixpacket /etc/: (permission denied|socket operation on non-socket|connection refused)",
}, },
} }
...@@ -69,7 +68,7 @@ func TestDialError(t *testing.T) { ...@@ -69,7 +68,7 @@ func TestDialError(t *testing.T) {
return return
} }
for i, tt := range dialErrorTests { for i, tt := range dialErrorTests {
c, e := Dial(tt.Net, tt.Laddr, tt.Raddr) c, e := Dial(tt.Net, tt.Raddr)
if c != nil { if c != nil {
c.Close() c.Close()
} }
......
...@@ -50,8 +50,8 @@ func readServices() { ...@@ -50,8 +50,8 @@ func readServices() {
file.close() file.close()
} }
// LookupPort looks up the port for the given network and service. // goLookupPort is the native Go implementation of LookupPort.
func LookupPort(network, service string) (port int, err os.Error) { func goLookupPort(network, service string) (port int, err os.Error) {
onceReadServices.Do(readServices) onceReadServices.Do(readServices)
switch network { switch network {
......
...@@ -54,13 +54,15 @@ func runServe(t *testing.T, network, addr string, listening chan<- string, done ...@@ -54,13 +54,15 @@ func runServe(t *testing.T, network, addr string, listening chan<- string, done
} }
func connect(t *testing.T, network, addr string, isEmpty bool) { func connect(t *testing.T, network, addr string, isEmpty bool) {
var laddr string var fd Conn
var err os.Error
if network == "unixgram" { if network == "unixgram" {
laddr = addr + ".local" fd, err = DialUnix(network, &UnixAddr{addr + ".local", network}, &UnixAddr{addr, network})
} else {
fd, err = Dial(network, addr)
} }
fd, err := Dial(network, laddr, addr)
if err != nil { if err != nil {
t.Fatalf("net.Dial(%q, %q, %q) = _, %v", network, laddr, addr, err) t.Fatalf("net.Dial(%q, %q) = _, %v", network, addr, err)
} }
fd.SetReadTimeout(1e9) // 1s fd.SetReadTimeout(1e9) // 1s
......
...@@ -167,9 +167,9 @@ func (e *UnknownSocketError) String() string { ...@@ -167,9 +167,9 @@ func (e *UnknownSocketError) String() string {
func sockaddrToString(sa syscall.Sockaddr) (name string, err os.Error) { func sockaddrToString(sa syscall.Sockaddr) (name string, err os.Error) {
switch a := sa.(type) { switch a := sa.(type) {
case *syscall.SockaddrInet4: case *syscall.SockaddrInet4:
return joinHostPort(IP(a.Addr[0:]).String(), itoa(a.Port)), nil return JoinHostPort(IP(a.Addr[0:]).String(), itoa(a.Port)), nil
case *syscall.SockaddrInet6: case *syscall.SockaddrInet6:
return joinHostPort(IP(a.Addr[0:]).String(), itoa(a.Port)), nil return JoinHostPort(IP(a.Addr[0:]).String(), itoa(a.Port)), nil
case *syscall.SockaddrUnix: case *syscall.SockaddrUnix:
return a.Name, nil return a.Name, nil
} }
......
...@@ -34,7 +34,7 @@ func (a *TCPAddr) String() string { ...@@ -34,7 +34,7 @@ func (a *TCPAddr) String() string {
if a == nil { if a == nil {
return "<nil>" return "<nil>"
} }
return joinHostPort(a.IP.String(), itoa(a.Port)) return JoinHostPort(a.IP.String(), itoa(a.Port))
} }
func (a *TCPAddr) family() int { func (a *TCPAddr) family() int {
...@@ -213,8 +213,9 @@ func (c *TCPConn) SetNoDelay(noDelay bool) os.Error { ...@@ -213,8 +213,9 @@ func (c *TCPConn) SetNoDelay(noDelay bool) os.Error {
// Closing c does not affect f, and closing f does not affect c. // Closing c does not affect f, and closing f does not affect c.
func (c *TCPConn) File() (f *os.File, err os.Error) { return c.fd.dup() } func (c *TCPConn) File() (f *os.File, err os.Error) { return c.fd.dup() }
// DialTCP is like Dial but can only connect to TCP networks // DialTCP connects to the remote address raddr on the network net,
// and returns a TCPConn structure. // which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used
// as the local address for the connection.
func DialTCP(net string, laddr, raddr *TCPAddr) (c *TCPConn, err os.Error) { func DialTCP(net string, laddr, raddr *TCPAddr) (c *TCPConn, err os.Error) {
if raddr == nil { if raddr == nil {
return nil, &OpError{"dial", "tcp", nil, errMissingAddress} return nil, &OpError{"dial", "tcp", nil, errMissingAddress}
......
...@@ -78,7 +78,7 @@ func (c *Conn) Close() os.Error { ...@@ -78,7 +78,7 @@ func (c *Conn) Close() os.Error {
// Dial connects to the given address on the given network using net.Dial // Dial connects to the given address on the given network using net.Dial
// and then returns a new Conn for the connection. // and then returns a new Conn for the connection.
func Dial(network, addr string) (*Conn, os.Error) { func Dial(network, addr string) (*Conn, os.Error) {
c, err := net.Dial(network, "", addr) c, err := net.Dial(network, addr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -11,7 +11,7 @@ import ( ...@@ -11,7 +11,7 @@ import (
) )
func testTimeout(t *testing.T, network, addr string, readFrom bool) { func testTimeout(t *testing.T, network, addr string, readFrom bool) {
fd, err := Dial(network, "", addr) fd, err := Dial(network, addr)
if err != nil { if err != nil {
t.Errorf("dial %s %s failed: %v", network, addr, err) t.Errorf("dial %s %s failed: %v", network, addr, err)
return return
......
...@@ -34,7 +34,7 @@ func (a *UDPAddr) String() string { ...@@ -34,7 +34,7 @@ func (a *UDPAddr) String() string {
if a == nil { if a == nil {
return "<nil>" return "<nil>"
} }
return joinHostPort(a.IP.String(), itoa(a.Port)) return JoinHostPort(a.IP.String(), itoa(a.Port))
} }
func (a *UDPAddr) family() int { func (a *UDPAddr) family() int {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment