]> Cypherpunks repositories - gostls13.git/commitdiff
net: drop laddr from Dial, cname from LookupHost; new functions
authorRuss Cox <rsc@golang.org>
Tue, 29 Mar 2011 03:28:42 +0000 (23:28 -0400)
committerRuss Cox <rsc@golang.org>
Tue, 29 Mar 2011 03:28:42 +0000 (23:28 -0400)
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

22 files changed:
src/pkg/crypto/tls/handshake_client_test.go
src/pkg/crypto/tls/tls.go
src/pkg/net/Makefile
src/pkg/net/cgo_stub.go [new file with mode: 0644]
src/pkg/net/dial.go
src/pkg/net/dialgoogle_test.go
src/pkg/net/dnsclient.go
src/pkg/net/dnsmsg.go
src/pkg/net/hosts_test.go
src/pkg/net/ip.go
src/pkg/net/ip_test.go
src/pkg/net/iprawsock.go
src/pkg/net/ipsock.go
src/pkg/net/lookup.go [new file with mode: 0644]
src/pkg/net/net_test.go
src/pkg/net/port.go
src/pkg/net/server_test.go
src/pkg/net/sock.go
src/pkg/net/tcpsock.go
src/pkg/net/textproto/textproto.go
src/pkg/net/timeout_test.go
src/pkg/net/udpsock.go

index fd1f145cfc4e37754131282a9c80f9639dff1513..3f91c7acf1f7a19c1cbb6dc4cd3f5122db4fd76c 100644 (file)
@@ -50,7 +50,7 @@ func TestRunClient(t *testing.T) {
 
        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 {
                t.Fatal(err)
        }
index e8290d728dd80153835ef5f8b178f5917436a00e..f66449c822535f15839a2c376f4256552615a752 100644 (file)
@@ -87,8 +87,9 @@ func Listen(network, laddr string, config *Config) (*Listener, os.Error) {
 // Dial interprets a nil configuration as equivalent to
 // the zero configuration; see the documentation of Config
 // for the defaults.
-func Dial(network, laddr, raddr string, config *Config) (*Conn, os.Error) {
-       c, err := net.Dial(network, laddr, raddr)
+func Dial(network, addr string, config *Config) (*Conn, os.Error) {
+       raddr := addr
+       c, err := net.Dial(network, raddr)
        if err != nil {
                return nil, err
        }
index 3f48907446f85383394601ee51c7ba41a764dac5..7ce6502798ad7901cf55e985855d5034ab11c97b 100644 (file)
@@ -6,6 +6,7 @@ include ../../Make.inc
 
 TARG=net
 GOFILES=\
+       cgo_stub.go\
        dial.go\
        dnsmsg.go\
        fd_$(GOOS).go\
@@ -13,6 +14,7 @@ GOFILES=\
        ip.go\
        ipsock.go\
        iprawsock.go\
+       lookup.go\
        net.go\
        parse.go\
        pipe.go\
diff --git a/src/pkg/net/cgo_stub.go b/src/pkg/net/cgo_stub.go
new file mode 100644 (file)
index 0000000..e28f662
--- /dev/null
@@ -0,0 +1,21 @@
+// 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
+}
index 1cf8e79159e2243b62445c830cf8051641f70fd6..66cb09b19bbb6b0266df22a9683d28aa13d39348 100644 (file)
@@ -6,9 +6,7 @@ package net
 
 import "os"
 
-// Dial connects to the remote address raddr on the network net.
-// If the string laddr is not empty, it is used as the local address
-// for the connection.
+// Dial connects to the address addr on the network net.
 //
 // Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only),
 // "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4"
@@ -16,79 +14,56 @@ import "os"
 //
 // For IP networks, addresses have the form host:port.  If host is
 // a literal IPv6 address, it must be enclosed in square brackets.
+// The functions JoinHostPort and SplitHostPort manipulate 
+// addresses in this form.
 //
 // Examples:
-//     Dial("tcp", "", "12.34.56.78:80")
-//     Dial("tcp", "", "google.com:80")
-//     Dial("tcp", "", "[de:ad:be:ef::ca:fe]:80")
-//     Dial("tcp", "127.0.0.1:123", "127.0.0.1:88")
+//     Dial("tcp", "12.34.56.78:80")
+//     Dial("tcp", "google.com:80")
+//     Dial("tcp", "[de:ad:be:ef::ca:fe]:80")
 //
-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 {
        case "tcp", "tcp4", "tcp6":
-               var la, ra *TCPAddr
-               if laddr != "" {
-                       if la, err = ResolveTCPAddr(laddr); err != nil {
-                               goto Error
-                       }
-               }
-               if raddr != "" {
-                       if ra, err = ResolveTCPAddr(raddr); err != nil {
-                               goto Error
-                       }
+               var ra *TCPAddr
+               if ra, err = ResolveTCPAddr(raddr); err != nil {
+                       goto Error
                }
-               c, err := DialTCP(net, la, ra)
+               c, err := DialTCP(net, nil, ra)
                if err != nil {
                        return nil, err
                }
                return c, nil
        case "udp", "udp4", "udp6":
-               var la, ra *UDPAddr
-               if laddr != "" {
-                       if la, err = ResolveUDPAddr(laddr); err != nil {
-                               goto Error
-                       }
-               }
-               if raddr != "" {
-                       if ra, err = ResolveUDPAddr(raddr); err != nil {
-                               goto Error
-                       }
+               var ra *UDPAddr
+               if ra, err = ResolveUDPAddr(raddr); err != nil {
+                       goto Error
                }
-               c, err := DialUDP(net, la, ra)
+               c, err := DialUDP(net, nil, ra)
                if err != nil {
                        return nil, err
                }
                return c, nil
        case "unix", "unixgram", "unixpacket":
-               var la, ra *UnixAddr
-               if raddr != "" {
-                       if ra, err = ResolveUnixAddr(net, raddr); err != nil {
-                               goto Error
-                       }
-               }
-               if laddr != "" {
-                       if la, err = ResolveUnixAddr(net, laddr); err != nil {
-                               goto Error
-                       }
+               var ra *UnixAddr
+               if ra, err = ResolveUnixAddr(net, raddr); err != nil {
+                       goto Error
                }
-               c, err = DialUnix(net, la, ra)
+               c, err = DialUnix(net, nil, ra)
                if err != nil {
                        return nil, err
                }
                return c, nil
        case "ip", "ip4", "ip6":
-               var la, ra *IPAddr
-               if laddr != "" {
-                       if la, err = ResolveIPAddr(laddr); err != nil {
-                               goto Error
-                       }
-               }
-               if raddr != "" {
-                       if ra, err = ResolveIPAddr(raddr); err != nil {
-                               goto Error
-                       }
+               var ra *IPAddr
+               if ra, err = ResolveIPAddr(raddr); err != nil {
+                       goto Error
                }
-               c, err := DialIP(net, la, ra)
+               c, err := DialIP(net, nil, ra)
                if err != nil {
                        return nil, err
                }
index a432800cfe2cb84236b07ab854f2baa10757ba26..9a9c02ebd717f874151c98dad145fe81832458af 100644 (file)
@@ -32,7 +32,7 @@ func fetchGoogle(t *testing.T, fd Conn, network, addr string) {
 }
 
 func doDial(t *testing.T, network, addr string) {
-       fd, err := Dial(network, "", addr)
+       fd, err := Dial(network, addr)
        if err != nil {
                t.Errorf("Dial(%q, %q, %q) = _, %v", network, "", addr, err)
                return
@@ -55,6 +55,13 @@ var googleaddrs = []string{
        "[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) {
        // If no ipv6 tunnel, don't try the last address.
        if !*ipv6 {
@@ -64,14 +71,14 @@ func TestDialGoogle(t *testing.T) {
        // Insert an actual IP address for google.com
        // into the table.
 
-       _, addrs, err := LookupHost("www.google.com")
+       addrs, err := LookupIP("www.google.com")
        if err != nil {
                t.Fatalf("lookup www.google.com: %v", err)
        }
        if len(addrs) == 0 {
                t.Fatalf("no addresses for www.google.com")
        }
-       ip := ParseIP(addrs[0]).To4()
+       ip := addrs[0].To4()
 
        for i, s := range googleaddrs {
                if strings.Contains(s, "%") {
index 3252dd454068e9c3e9e45e5e18d26d6faad12935..32cea6125eb05276a366f74ed6959a4bc03aa6e5 100644 (file)
@@ -159,7 +159,7 @@ func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs
                // all the cfg.servers[i] are IP addresses, which
                // Dial will use without a DNS lookup.
                server := cfg.servers[i] + ":53"
-               c, cerr := Dial("udp", "", server)
+               c, cerr := Dial("udp", server)
                if cerr != nil {
                        err = cerr
                        continue
@@ -178,12 +178,23 @@ func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs
        return
 }
 
-func convertRR_A(records []dnsRR) []string {
-       addrs := make([]string, len(records))
+func convertRR_A(records []dnsRR) []IP {
+       addrs := make([]IP, len(records))
        for i := 0; i < len(records); i++ {
                rr := records[i]
                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
 }
@@ -294,10 +305,8 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err os.Erro
        return
 }
 
-// LookupHost looks for name using the local hosts file and DNS resolver.
-// It returns the canonical name for the host and an array of that
-// host's addresses.
-func LookupHost(name string) (cname string, addrs []string, err os.Error) {
+// goLookupHost is the native Go implementation of LookupHost.
+func goLookupHost(name string) (addrs []string, err os.Error) {
        onceLoadConfig.Do(loadConfig)
        if dnserr != nil || cfg == nil {
                err = dnserr
@@ -306,18 +315,69 @@ func LookupHost(name string) (cname string, addrs []string, err os.Error) {
        // Use entries from /etc/hosts if they match.
        addrs = lookupStaticHost(name)
        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
        }
        var records []dnsRR
+       var cname string
        cname, records, err = lookup(name, dnsTypeA)
        if err != nil {
                return
        }
        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
 }
 
+// An SRV represents a single DNS SRV record.
 type SRV struct {
        Target   string
        Port     uint16
@@ -344,11 +404,13 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os.
        return
 }
 
+// An MX represents a single DNS MX record.
 type MX struct {
        Host string
        Pref uint16
 }
 
+// LookupMX returns the DNS MX records associated with name.
 func LookupMX(name string) (entries []*MX, err os.Error) {
        var records []dnsRR
        _, records, err = lookup(name, dnsTypeMX)
index dc195caf8015e5adb2f7e62faa4d89976d13889d..5209c1a06a542a9ecb95caf0e0efd697a89cfd0c 100644 (file)
@@ -50,6 +50,7 @@ const (
        dnsTypeMINFO = 14
        dnsTypeMX    = 15
        dnsTypeTXT   = 16
+       dnsTypeAAAA  = 28
        dnsTypeSRV   = 33
 
        // valid dnsQuestion.qtype only
@@ -244,8 +245,18 @@ type dnsRR_A struct {
        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.
 //
@@ -270,6 +281,7 @@ var rr_mk = map[int]func() dnsRR{
        dnsTypeTXT:   func() dnsRR { return new(dnsRR_TXT) },
        dnsTypeSRV:   func() dnsRR { return new(dnsRR_SRV) },
        dnsTypeA:     func() dnsRR { return new(dnsRR_A) },
+       dnsTypeAAAA:  func() dnsRR { return new(dnsRR_AAAA) },
 }
 
 // Pack a domain name s into msg[off:].
@@ -377,7 +389,7 @@ Loop:
 
 // TODO(rsc): Move into generic library?
 // 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) {
        for i := 0; i < val.NumField(); i++ {
                f := val.Type().(*reflect.StructType).Field(i)
@@ -410,6 +422,16 @@ func packStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, o
                                msg[off+3] = byte(i)
                                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:
                        // There are multiple string encodings.
                        // The tag distinguishes ordinary strings from domain names.
@@ -478,6 +500,16 @@ func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int,
                                fv.Set(uint64(i))
                                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:
                        var s string
                        switch f.Tag {
@@ -515,7 +547,8 @@ func unpackStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) {
 
 // Generic struct printer.
 // 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.
 func printStructValue(val *reflect.StructValue) string {
        s := "{"
@@ -533,6 +566,9 @@ func printStructValue(val *reflect.StructValue) string {
                } else if fv, ok := fval.(*reflect.UintValue); ok && f.Tag == "ipv4" {
                        i := fv.Get()
                        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 {
                        s += fmt.Sprint(fval.Interface())
                }
index 84cd92e376cf81f5a9b6a71c86e9feac869e11f5..470e35f7863801ca6b415164e237f36caf14afbe 100644 (file)
@@ -13,7 +13,6 @@ type hostTest struct {
        ips  []IP
 }
 
-
 var hosttests = []hostTest{
        {"odin", []IP{
                IPv4(127, 0, 0, 2),
index 1904af0d6ad784556a8f2cdd1594a1dea532adcf..12bb6f351a1e914a4f76faecf37cc98c9afdf307 100644 (file)
@@ -474,13 +474,13 @@ func parseIPv6(s string) IP {
        return p
 }
 
-// A SyntaxError represents a malformed text string and the type of string that was expected.
-type SyntaxError struct {
+// A ParseError represents a malformed text string and the type of string that was expected.
+type ParseError struct {
        Type string
        Text string
 }
 
-func (e *SyntaxError) String() string {
+func (e *ParseError) String() string {
        return "invalid " + e.Type + ": " + e.Text
 }
 
@@ -507,33 +507,46 @@ func ParseIP(s string) IP {
 }
 
 // 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) {
        i := byteIndex(s, '/')
        if i < 0 {
-               return nil, nil, &SyntaxError{"CIDR address", s}
+               return nil, nil, &ParseError{"CIDR address", s}
        }
        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)
-       if ip == nil || !ok || i != len(maskstr) || nn < 0 || nn > 8*len(ip) {
-               return nil, nil, &SyntaxError{"CIDR address", s}
+       if ip == nil || !ok || i != len(maskstr) || nn < 0 || nn > 8*iplen {
+               return nil, nil, &ParseError{"CIDR address", s}
        }
        n := uint(nn)
-       if len(ip) == 4 {
+       if iplen == 4 {
                v4mask := ^uint32(0xffffffff >> n)
-               mask = IPMask(IPv4(byte(v4mask>>24), byte(v4mask>>16), byte(v4mask>>8), byte(v4mask)))
-               return ip, mask, nil
-       }
-       mask = make(IPMask, 16)
-       for i := 0; i < 16; i++ {
-               if n >= 8 {
-                       mask[i] = 0xff
-                       n -= 8
-                       continue
+               mask = IPv4Mask(byte(v4mask>>24), byte(v4mask>>16), byte(v4mask>>8), byte(v4mask))
+       } else {
+               mask = make(IPMask, 16)
+               for i := 0; i < 16; i++ {
+                       if n >= 8 {
+                               mask[i] = 0xff
+                               n -= 8
+                               continue
+                       }
+                       mask[i] = ^byte(0xff >> n)
+                       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}
                }
-               mask[i] = ^byte(0xff >> n)
-               n = 0
        }
        return ip, mask, nil
 }
index e29c3021da9e82942869e5d9d028929480407e84..f1a4716d227b4bc200af719f0fa8a0c77833c806 100644 (file)
@@ -5,30 +5,26 @@
 package net
 
 import (
+       "bytes"
+       "reflect"
        "testing"
+       "os"
 )
 
-func isEqual(a, b IP) bool {
+func isEqual(a, b []byte) bool {
        if a == nil && b == nil {
                return true
        }
-       if a == nil || b == nil || len(a) != len(b) {
+       if a == nil || b == nil {
                return false
        }
-       for i := 0; i < len(a); i++ {
-               if a[i] != b[i] {
-                       return false
-               }
-       }
-       return true
+       return bytes.Equal(a, b)
 }
 
-type parseIPTest struct {
+var parseiptests = []struct {
        in  string
        out IP
-}
-
-var parseiptests = []parseIPTest{
+}{
        {"127.0.1.2", IPv4(127, 0, 1, 2)},
        {"127.0.0.1", IPv4(127, 0, 0, 1)},
        {"127.0.0.256", nil},
@@ -43,20 +39,17 @@ var parseiptests = []parseIPTest{
 }
 
 func TestParseIP(t *testing.T) {
-       for i := 0; i < len(parseiptests); i++ {
-               tt := parseiptests[i]
+       for _, tt := range parseiptests {
                if out := ParseIP(tt.in); !isEqual(out, tt.out) {
                        t.Errorf("ParseIP(%#q) = %v, want %v", tt.in, out, tt.out)
                }
        }
 }
 
-type ipStringTest struct {
+var ipstringtests = []struct {
        in  IP
        out string
-}
-
-var ipstringtests = []ipStringTest{
+}{
        // cf. RFC 5952 (A Recommendation for IPv6 Address Text Representation)
        {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0,
                0, 0, 0x1, 0x23, 0, 0x12, 0, 0x1},
@@ -85,10 +78,67 @@ var ipstringtests = []ipStringTest{
 }
 
 func TestIPString(t *testing.T) {
-       for i := 0; i < len(ipstringtests); i++ {
-               tt := ipstringtests[i]
+       for _, tt := range ipstringtests {
                if out := tt.in.String(); 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)
+               }
+       }
+}
index 81a918ce5cb710909e10aa5623e59430258e3302..60433303ae1b09fe62698bd2f7e163b7548b04a3 100644 (file)
@@ -240,7 +240,7 @@ func hostToIP(host string) (ip IP, err os.Error) {
        addr = ParseIP(host)
        if addr == nil {
                // Not an IP address.  Try as a DNS name.
-               _, addrs, err1 := LookupHost(host)
+               addrs, err1 := LookupHost(host)
                if err1 != nil {
                        err = err1
                        goto Error
index ae4204b48aa48b1bf092cb81621afff554e476d5..80bc3eea5da138cb421fbe6c3542cd58c1c3072f 100644 (file)
@@ -170,9 +170,10 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) {
        return nil, InvalidAddrError("unexpected socket family")
 }
 
-// Split "host:port" into "host" and "port".
-// Host cannot contain colons unless it is bracketed.
-func splitHostPort(hostport string) (host, port string, err os.Error) {
+// 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.
+func SplitHostPort(hostport string) (host, port string, err os.Error) {
        // The port starts after the last colon.
        i := last(hostport, ':')
        if i < 0 {
@@ -195,9 +196,9 @@ func splitHostPort(hostport string) (host, port string, err os.Error) {
        return
 }
 
-// Join "host" and "port" into "host:port".
-// If host contains colons, will join into "[host]:port".
-func joinHostPort(host, port string) string {
+// 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 {
        // If host has colons, have to bracket it.
        if byteIndex(host, ':') >= 0 {
                return "[" + host + "]:" + port
@@ -207,7 +208,7 @@ func joinHostPort(host, port string) string {
 
 // Convert "host:port" into IP address and port.
 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 {
                goto Error
        }
@@ -218,7 +219,7 @@ func hostPortToIP(net, hostport string) (ip IP, iport int, err os.Error) {
                addr = ParseIP(host)
                if addr == nil {
                        // Not an IP address.  Try as a DNS name.
-                       _, addrs, err1 := LookupHost(host)
+                       addrs, err1 := LookupHost(host)
                        if err1 != nil {
                                err = err1
                                goto Error
diff --git a/src/pkg/net/lookup.go b/src/pkg/net/lookup.go
new file mode 100644 (file)
index 0000000..7b2185e
--- /dev/null
@@ -0,0 +1,38 @@
+// 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
+}
index 1e6e99eec7556e3f7ee6fd9c8fbb476bc0bed091..f7eae56fea001ac65d80826a5950963aded45aa4 100644 (file)
@@ -15,50 +15,49 @@ var runErrorTest = flag.Bool("run_error_test", false, "let TestDialError check f
 
 type DialErrorTest struct {
        Net     string
-       Laddr   string
        Raddr   string
        Pattern string
 }
 
 var dialErrorTests = []DialErrorTest{
        {
-               "datakit", "", "mh/astro/r70",
+               "datakit", "mh/astro/r70",
                "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/☺",
        },
        {
-               "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 (.*)",
        },
        {
-               "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 (.*)",
        },
        {
-               "tcp", "", "no-such-name:80",
+               "tcp", "no-such-name:80",
                `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",
        },
        {
-               "unix", "", "/etc/file-not-found",
+               "unix", "/etc/file-not-found",
                "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)",
        },
        {
-               "unixpacket", "", "/etc/file-not-found",
+               "unixpacket", "/etc/file-not-found",
                "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)",
        },
 }
@@ -69,7 +68,7 @@ func TestDialError(t *testing.T) {
                return
        }
        for i, tt := range dialErrorTests {
-               c, e := Dial(tt.Net, tt.Laddr, tt.Raddr)
+               c, e := Dial(tt.Net, tt.Raddr)
                if c != nil {
                        c.Close()
                }
index 7d25058b29cd7e6572be7573156ab8d62eb6b198..8f8327a3733d573994a0e53d760e36aacadbb17d 100644 (file)
@@ -50,8 +50,8 @@ func readServices() {
        file.close()
 }
 
-// LookupPort looks up the port for the given network and service.
-func LookupPort(network, service string) (port int, err os.Error) {
+// goLookupPort is the native Go implementation of LookupPort.
+func goLookupPort(network, service string) (port int, err os.Error) {
        onceReadServices.Do(readServices)
 
        switch network {
index 3dda500e585608d7765f7efc76b79ee02f5883d1..37695a068d15297b9e8646902a50fc7559627408 100644 (file)
@@ -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) {
-       var laddr string
+       var fd Conn
+       var err os.Error
        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 {
-               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
 
index 8ad3548add41cd34757ac0aec0a99cf595623d53..26816264c323175ceac49f6ebf2b5b47d9483706 100644 (file)
@@ -167,9 +167,9 @@ func (e *UnknownSocketError) String() string {
 func sockaddrToString(sa syscall.Sockaddr) (name string, err os.Error) {
        switch a := sa.(type) {
        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:
-               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:
                return a.Name, nil
        }
index a4bca11bb48830e606de4be7b0a7e0d114bc7196..b484be20b463dd7af28617f40e2d5d2479588c72 100644 (file)
@@ -34,7 +34,7 @@ func (a *TCPAddr) String() string {
        if a == nil {
                return "<nil>"
        }
-       return joinHostPort(a.IP.String(), itoa(a.Port))
+       return JoinHostPort(a.IP.String(), itoa(a.Port))
 }
 
 func (a *TCPAddr) family() int {
@@ -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.
 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
-// and returns a TCPConn structure.
+// DialTCP connects to the remote address raddr on the network net,
+// 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) {
        if raddr == nil {
                return nil, &OpError{"dial", "tcp", nil, errMissingAddress}
index f62009c523b766939913a57b248b53bde0f84237..fbfad9d61ce4519ef28e66c1045f0c946c792bbe 100644 (file)
@@ -78,7 +78,7 @@ func (c *Conn) Close() os.Error {
 // Dial connects to the given address on the given network using net.Dial
 // and then returns a new Conn for the connection.
 func Dial(network, addr string) (*Conn, os.Error) {
-       c, err := net.Dial(network, "", addr)
+       c, err := net.Dial(network, addr)
        if err != nil {
                return nil, err
        }
index 09a257dc817fca387cb2a16dca49818a39b146e0..0dbab5846a6885ec13a130b6c5c5402e7a42b13f 100644 (file)
@@ -11,7 +11,7 @@ import (
 )
 
 func testTimeout(t *testing.T, network, addr string, readFrom bool) {
-       fd, err := Dial(network, "", addr)
+       fd, err := Dial(network, addr)
        if err != nil {
                t.Errorf("dial %s %s failed: %v", network, addr, err)
                return
index f9274493e602a39173cf40b0c4240342887ba632..44d618dab08c91daa08504813f5c8a48fe4b4749 100644 (file)
@@ -34,7 +34,7 @@ func (a *UDPAddr) String() string {
        if a == nil {
                return "<nil>"
        }
-       return joinHostPort(a.IP.String(), itoa(a.Port))
+       return JoinHostPort(a.IP.String(), itoa(a.Port))
 }
 
 func (a *UDPAddr) family() int {