diff options
Diffstat (limited to 'src/pkg/net')
28 files changed, 872 insertions, 756 deletions
diff --git a/src/pkg/net/Makefile b/src/pkg/net/Makefile index 5472df392..536fe369d 100644 --- a/src/pkg/net/Makefile +++ b/src/pkg/net/Makefile @@ -7,6 +7,7 @@ include ../../Make.inc TARG=net GOFILES=\ dial.go\ + dnsclient.go\ dnsmsg.go\ fd_$(GOOS).go\ hosts.go\ @@ -14,7 +15,6 @@ GOFILES=\ ip.go\ ipsock.go\ iprawsock.go\ - lookup.go\ net.go\ parse.go\ pipe.go\ @@ -24,11 +24,12 @@ GOFILES=\ unixsock.go\ GOFILES_freebsd=\ - dnsclient.go\ + dnsclient_unix.go\ dnsconfig.go\ fd.go\ file.go\ interface_bsd.go\ + lookup_unix.go\ newpollserver.go\ port.go\ sendfile_stub.go\ @@ -39,11 +40,12 @@ CGOFILES_freebsd=\ cgo_unix.go\ GOFILES_darwin=\ - dnsclient.go\ + dnsclient_unix.go\ dnsconfig.go\ fd.go\ file.go\ interface_bsd.go\ + lookup_unix.go\ newpollserver.go\ port.go\ sendfile_stub.go\ @@ -54,11 +56,12 @@ CGOFILES_darwin=\ cgo_unix.go\ GOFILES_linux=\ - dnsclient.go\ + dnsclient_unix.go\ dnsconfig.go\ fd.go\ file.go\ interface_linux.go\ + lookup_unix.go\ newpollserver.go\ port.go\ sendfile_linux.go\ @@ -66,6 +69,7 @@ GOFILES_linux=\ GOFILES_plan9=\ interface_stub.go\ + lookup_unix.go\ sendfile_stub.go\ ifeq ($(GOARCH),arm) @@ -78,11 +82,10 @@ CGOFILES_linux=\ endif GOFILES_windows=\ - cgo_stub.go\ file_windows.go\ - interface_stub.go\ - resolv_windows.go\ - sendfile_stub.go\ + interface_windows.go\ + lookup_windows.go\ + sendfile_windows.go\ sock_windows.go\ GOFILES+=$(GOFILES_$(GOOS)) diff --git a/src/pkg/net/dial.go b/src/pkg/net/dial.go index ead775fe6..10c67dcc4 100644 --- a/src/pkg/net/dial.go +++ b/src/pkg/net/dial.go @@ -6,6 +6,28 @@ package net import "os" +func resolveNetAddr(op, net, addr string) (a Addr, err os.Error) { + if addr == "" { + return nil, &OpError{op, net, nil, errMissingAddress} + } + switch net { + case "tcp", "tcp4", "tcp6": + a, err = ResolveTCPAddr(net, addr) + case "udp", "udp4", "udp6": + a, err = ResolveUDPAddr(net, addr) + case "unix", "unixgram", "unixpacket": + a, err = ResolveUnixAddr(net, addr) + case "ip", "ip4", "ip6": + a, err = ResolveIPAddr(net, addr) + default: + err = UnknownNetworkError(net) + } + if err != nil { + return nil, &OpError{op, net + " " + addr, nil, err} + } + return +} + // Dial connects to the address addr on the network net. // // Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only), @@ -23,56 +45,26 @@ import "os" // Dial("tcp", "[de:ad:be:ef::ca:fe]:80") // func Dial(net, addr string) (c Conn, err os.Error) { - raddr := addr - if raddr == "" { - return nil, &OpError{"dial", net, nil, errMissingAddress} + addri, err := resolveNetAddr("dial", net, addr) + if err != nil { + return nil, err } - switch net { - case "tcp", "tcp4", "tcp6": - var ra *TCPAddr - if ra, err = ResolveTCPAddr(net, raddr); err != nil { - goto Error - } - c, err := DialTCP(net, nil, ra) - if err != nil { - return nil, err - } - return c, nil - case "udp", "udp4", "udp6": - var ra *UDPAddr - if ra, err = ResolveUDPAddr(net, raddr); err != nil { - goto Error - } - c, err := DialUDP(net, nil, ra) - if err != nil { - return nil, err - } - return c, nil - case "unix", "unixgram", "unixpacket": - var ra *UnixAddr - if ra, err = ResolveUnixAddr(net, raddr); err != nil { - goto Error - } + switch ra := addri.(type) { + case *TCPAddr: + c, err = DialTCP(net, nil, ra) + case *UDPAddr: + c, err = DialUDP(net, nil, ra) + case *UnixAddr: c, err = DialUnix(net, nil, ra) - if err != nil { - return nil, err - } - return c, nil - case "ip", "ip4", "ip6": - var ra *IPAddr - if ra, err = ResolveIPAddr(net, raddr); err != nil { - goto Error - } - c, err := DialIP(net, nil, ra) - if err != nil { - return nil, err - } - return c, nil - + case *IPAddr: + c, err = DialIP(net, nil, ra) + default: + err = UnknownNetworkError(net) + } + if err != nil { + return nil, &OpError{"dial", net + " " + addr, nil, err} } - err = UnknownNetworkError(net) -Error: - return nil, &OpError{"dial", net + " " + raddr, nil, err} + return } // Listen announces on the local network address laddr. diff --git a/src/pkg/net/dnsclient.go b/src/pkg/net/dnsclient.go index ae9ca8430..280b19453 100644 --- a/src/pkg/net/dnsclient.go +++ b/src/pkg/net/dnsclient.go @@ -2,16 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// DNS client: see RFC 1035. -// Has to be linked into package net for Dial. - -// TODO(rsc): -// Check periodically whether /etc/resolv.conf has changed. -// Could potentially handle many outstanding lookups faster. -// Could have a small cache. -// Random UDP source port (net.Dial should do that for us). -// Random request IDs. - package net import ( @@ -19,9 +9,6 @@ import ( "fmt" "os" "rand" - "sync" - "time" - "sort" ) // DNSError represents a DNS lookup error. @@ -49,54 +36,31 @@ func (e *DNSError) Temporary() bool { return e.IsTimeout } const noSuchHost = "no such host" -// Send a request on the connection and hope for a reply. -// Up to cfg.attempts attempts. -func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, os.Error) { - if len(name) >= 256 { - return nil, &DNSError{Error: "name too long", Name: name} - } - out := new(dnsMsg) - out.id = uint16(rand.Int()) ^ uint16(time.Nanoseconds()) - out.question = []dnsQuestion{ - {name, qtype, dnsClassINET}, - } - out.recursion_desired = true - msg, ok := out.Pack() - if !ok { - return nil, &DNSError{Error: "internal error - cannot pack message", Name: name} +// reverseaddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP +// address addr suitable for rDNS (PTR) record lookup or an error if it fails +// to parse the IP address. +func reverseaddr(addr string) (arpa string, err os.Error) { + ip := ParseIP(addr) + if ip == nil { + return "", &DNSError{Error: "unrecognized address", Name: addr} } - - for attempt := 0; attempt < cfg.attempts; attempt++ { - n, err := c.Write(msg) - if err != nil { - return nil, err - } - - c.SetReadTimeout(int64(cfg.timeout) * 1e9) // nanoseconds - - buf := make([]byte, 2000) // More than enough. - n, err = c.Read(buf) - if err != nil { - if e, ok := err.(Error); ok && e.Timeout() { - continue - } - return nil, err - } - buf = buf[0:n] - in := new(dnsMsg) - if !in.Unpack(buf) || in.id != out.id { - continue - } - return in, nil + if ip.To4() != nil { + return fmt.Sprintf("%d.%d.%d.%d.in-addr.arpa.", ip[15], ip[14], ip[13], ip[12]), nil } - var server string - if a := c.RemoteAddr(); a != nil { - server = a.String() + // Must be IPv6 + var buf bytes.Buffer + // Add it, in reverse, to the buffer + for i := len(ip) - 1; i >= 0; i-- { + s := fmt.Sprintf("%02x", ip[i]) + buf.WriteByte(s[1]) + buf.WriteByte('.') + buf.WriteByte(s[0]) + buf.WriteByte('.') } - return nil, &DNSError{Error: "no answer from server", Name: name, Server: server, IsTimeout: true} + // Append "ip6.arpa." and return (buf already has the final .) + return buf.String() + "ip6.arpa.", nil } - // Find answer for name in dns message. // On return, if err == nil, addrs != nil. func answer(name, server string, dns *dnsMsg, qtype uint16) (cname string, addrs []dnsRR, err os.Error) { @@ -150,63 +114,6 @@ Cname: return "", nil, &DNSError{Error: "too many redirects", Name: name, Server: server} } -// Do a lookup for a single name, which must be rooted -// (otherwise answer will not find the answers). -func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs []dnsRR, err os.Error) { - if len(cfg.servers) == 0 { - return "", nil, &DNSError{Error: "no DNS servers", Name: name} - } - for i := 0; i < len(cfg.servers); i++ { - // Calling Dial here is scary -- we have to be sure - // not to dial a name that will require a DNS lookup, - // or Dial will call back here to translate it. - // The DNS config parser has already checked that - // 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) - if cerr != nil { - err = cerr - continue - } - msg, merr := exchange(cfg, c, name, qtype) - c.Close() - if merr != nil { - err = merr - continue - } - cname, addrs, err = answer(name, server, msg, qtype) - if err == nil || err.(*DNSError).Error == noSuchHost { - break - } - } - return -} - -func convertRR_A(records []dnsRR) []IP { - addrs := make([]IP, len(records)) - for i, rr := range records { - a := rr.(*dnsRR_A).A - 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, rr := range records { - a := make(IP, 16) - copy(a, rr.(*dnsRR_AAAA).AAAA[:]) - addrs[i] = a - } - return addrs -} - -var cfg *dnsConfig -var dnserr os.Error - -func loadConfig() { cfg, dnserr = dnsReadConfig() } - func isDomainName(s string) bool { // See RFC 1035, RFC 3696. if len(s) == 0 { @@ -255,141 +162,6 @@ func isDomainName(s string) bool { return ok } -var onceLoadConfig sync.Once - -func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err os.Error) { - if !isDomainName(name) { - return name, nil, &DNSError{Error: "invalid domain name", Name: name} - } - onceLoadConfig.Do(loadConfig) - if dnserr != nil || cfg == nil { - err = dnserr - return - } - // If name is rooted (trailing dot) or has enough dots, - // try it by itself first. - rooted := len(name) > 0 && name[len(name)-1] == '.' - if rooted || count(name, '.') >= cfg.ndots { - rname := name - if !rooted { - rname += "." - } - // Can try as ordinary name. - cname, addrs, err = tryOneName(cfg, rname, qtype) - if err == nil { - return - } - } - if rooted { - return - } - - // Otherwise, try suffixes. - for i := 0; i < len(cfg.search); i++ { - rname := name + "." + cfg.search[i] - if rname[len(rname)-1] != '.' { - rname += "." - } - cname, addrs, err = tryOneName(cfg, rname, qtype) - if err == nil { - return - } - } - - // Last ditch effort: try unsuffixed. - rname := name - if !rooted { - rname += "." - } - cname, addrs, err = tryOneName(cfg, rname, qtype) - if err == nil { - return - } - return -} - -// goLookupHost is the native Go implementation of LookupHost. -// Used only if cgoLookupHost refuses to handle the request -// (that is, only if cgoLookupHost is the stub in cgo_stub.go). -// Normally we let cgo use the C library resolver instead of -// depending on our lookup code, so that Go and C get the same -// answers. -func goLookupHost(name string) (addrs []string, err os.Error) { - // Use entries from /etc/hosts if they match. - addrs = lookupStaticHost(name) - if len(addrs) > 0 { - return - } - onceLoadConfig.Do(loadConfig) - if dnserr != nil || cfg == nil { - err = dnserr - 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. -// Used only if cgoLookupIP refuses to handle the request -// (that is, only if cgoLookupIP is the stub in cgo_stub.go). -// Normally we let cgo use the C library resolver instead of -// depending on our lookup code, so that Go and C get the same -// answers. -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 -} - -// goLookupCNAME is the native Go implementation of LookupCNAME. -// Used only if cgoLookupCNAME refuses to handle the request -// (that is, only if cgoLookupCNAME is the stub in cgo_stub.go). -// Normally we let cgo use the C library resolver instead of -// depending on our lookup code, so that Go and C get the same -// answers. -func goLookupCNAME(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 - } - cname = rr[0].(*dnsRR_CNAME).Cname - return -} - // An SRV represents a single DNS SRV record. type SRV struct { Target string @@ -436,35 +208,6 @@ func shuffleSRVByWeight(addrs []*SRV) { } } -// LookupSRV tries to resolve an SRV query of the given service, -// protocol, and domain name, as specified in RFC 2782. In most cases -// the proto argument can be the same as the corresponding -// Addr.Network(). The returned records are sorted by priority -// and randomized by weight within a priority. -func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os.Error) { - target := "_" + service + "._" + proto + "." + name - var records []dnsRR - cname, records, err = lookup(target, dnsTypeSRV) - if err != nil { - return - } - addrs = make([]*SRV, len(records)) - for i, rr := range records { - r := rr.(*dnsRR_SRV) - addrs[i] = &SRV{r.Target, r.Port, r.Priority, r.Weight} - } - sort.Sort(byPriorityWeight(addrs)) - i := 0 - for j := 1; j < len(addrs); j++ { - if addrs[i].Priority != addrs[j].Priority { - shuffleSRVByWeight(addrs[i:j]) - i = j - } - } - shuffleSRVByWeight(addrs[i:len(addrs)]) - return -} - // An MX represents a single DNS MX record. type MX struct { Host string @@ -479,73 +222,3 @@ func (s byPref) Len() int { return len(s) } func (s byPref) Less(i, j int) bool { return s[i].Pref < s[j].Pref } func (s byPref) Swap(i, j int) { s[i], s[j] = s[j], s[i] } - -// LookupMX returns the DNS MX records for the given domain name sorted by preference. -func LookupMX(name string) (mx []*MX, err os.Error) { - _, rr, err := lookup(name, dnsTypeMX) - if err != nil { - return - } - mx = make([]*MX, len(rr)) - for i := range rr { - r := rr[i].(*dnsRR_MX) - mx[i] = &MX{r.Mx, r.Pref} - } - // Shuffle the records to match RFC 5321 when sorted - for i := range mx { - j := rand.Intn(i + 1) - mx[i], mx[j] = mx[j], mx[i] - } - sort.Sort(byPref(mx)) - return -} - -// reverseaddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP -// address addr suitable for rDNS (PTR) record lookup or an error if it fails -// to parse the IP address. -func reverseaddr(addr string) (arpa string, err os.Error) { - ip := ParseIP(addr) - if ip == nil { - return "", &DNSError{Error: "unrecognized address", Name: addr} - } - if ip.To4() != nil { - return fmt.Sprintf("%d.%d.%d.%d.in-addr.arpa.", ip[15], ip[14], ip[13], ip[12]), nil - } - // Must be IPv6 - var buf bytes.Buffer - // Add it, in reverse, to the buffer - for i := len(ip) - 1; i >= 0; i-- { - s := fmt.Sprintf("%02x", ip[i]) - buf.WriteByte(s[1]) - buf.WriteByte('.') - buf.WriteByte(s[0]) - buf.WriteByte('.') - } - // Append "ip6.arpa." and return (buf already has the final .) - return buf.String() + "ip6.arpa.", nil -} - -// LookupAddr performs a reverse lookup for the given address, returning a list -// of names mapping to that address. -func LookupAddr(addr string) (name []string, err os.Error) { - name = lookupStaticAddr(addr) - if len(name) > 0 { - return - } - var arpa string - arpa, err = reverseaddr(addr) - if err != nil { - return - } - var records []dnsRR - _, records, err = lookup(arpa, dnsTypePTR) - if err != nil { - return - } - name = make([]string, len(records)) - for i := range records { - r := records[i].(*dnsRR_PTR) - name[i] = r.Ptr - } - return -} diff --git a/src/pkg/net/dnsclient_unix.go b/src/pkg/net/dnsclient_unix.go new file mode 100644 index 000000000..7f3ef2878 --- /dev/null +++ b/src/pkg/net/dnsclient_unix.go @@ -0,0 +1,262 @@ +// 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. + +// DNS client: see RFC 1035. +// Has to be linked into package net for Dial. + +// TODO(rsc): +// Check periodically whether /etc/resolv.conf has changed. +// Could potentially handle many outstanding lookups faster. +// Could have a small cache. +// Random UDP source port (net.Dial should do that for us). +// Random request IDs. + +package net + +import ( + "os" + "rand" + "sync" + "time" +) + +// Send a request on the connection and hope for a reply. +// Up to cfg.attempts attempts. +func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, os.Error) { + if len(name) >= 256 { + return nil, &DNSError{Error: "name too long", Name: name} + } + out := new(dnsMsg) + out.id = uint16(rand.Int()) ^ uint16(time.Nanoseconds()) + out.question = []dnsQuestion{ + {name, qtype, dnsClassINET}, + } + out.recursion_desired = true + msg, ok := out.Pack() + if !ok { + return nil, &DNSError{Error: "internal error - cannot pack message", Name: name} + } + + for attempt := 0; attempt < cfg.attempts; attempt++ { + n, err := c.Write(msg) + if err != nil { + return nil, err + } + + c.SetReadTimeout(int64(cfg.timeout) * 1e9) // nanoseconds + + buf := make([]byte, 2000) // More than enough. + n, err = c.Read(buf) + if err != nil { + if e, ok := err.(Error); ok && e.Timeout() { + continue + } + return nil, err + } + buf = buf[0:n] + in := new(dnsMsg) + if !in.Unpack(buf) || in.id != out.id { + continue + } + return in, nil + } + var server string + if a := c.RemoteAddr(); a != nil { + server = a.String() + } + return nil, &DNSError{Error: "no answer from server", Name: name, Server: server, IsTimeout: true} +} + + +// Do a lookup for a single name, which must be rooted +// (otherwise answer will not find the answers). +func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs []dnsRR, err os.Error) { + if len(cfg.servers) == 0 { + return "", nil, &DNSError{Error: "no DNS servers", Name: name} + } + for i := 0; i < len(cfg.servers); i++ { + // Calling Dial here is scary -- we have to be sure + // not to dial a name that will require a DNS lookup, + // or Dial will call back here to translate it. + // The DNS config parser has already checked that + // 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) + if cerr != nil { + err = cerr + continue + } + msg, merr := exchange(cfg, c, name, qtype) + c.Close() + if merr != nil { + err = merr + continue + } + cname, addrs, err = answer(name, server, msg, qtype) + if err == nil || err.(*DNSError).Error == noSuchHost { + break + } + } + return +} + +func convertRR_A(records []dnsRR) []IP { + addrs := make([]IP, len(records)) + for i, rr := range records { + a := rr.(*dnsRR_A).A + 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, rr := range records { + a := make(IP, 16) + copy(a, rr.(*dnsRR_AAAA).AAAA[:]) + addrs[i] = a + } + return addrs +} + +var cfg *dnsConfig +var dnserr os.Error + +func loadConfig() { cfg, dnserr = dnsReadConfig() } + +var onceLoadConfig sync.Once + +func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err os.Error) { + if !isDomainName(name) { + return name, nil, &DNSError{Error: "invalid domain name", Name: name} + } + onceLoadConfig.Do(loadConfig) + if dnserr != nil || cfg == nil { + err = dnserr + return + } + // If name is rooted (trailing dot) or has enough dots, + // try it by itself first. + rooted := len(name) > 0 && name[len(name)-1] == '.' + if rooted || count(name, '.') >= cfg.ndots { + rname := name + if !rooted { + rname += "." + } + // Can try as ordinary name. + cname, addrs, err = tryOneName(cfg, rname, qtype) + if err == nil { + return + } + } + if rooted { + return + } + + // Otherwise, try suffixes. + for i := 0; i < len(cfg.search); i++ { + rname := name + "." + cfg.search[i] + if rname[len(rname)-1] != '.' { + rname += "." + } + cname, addrs, err = tryOneName(cfg, rname, qtype) + if err == nil { + return + } + } + + // Last ditch effort: try unsuffixed. + rname := name + if !rooted { + rname += "." + } + cname, addrs, err = tryOneName(cfg, rname, qtype) + if err == nil { + return + } + return +} + +// goLookupHost is the native Go implementation of LookupHost. +// Used only if cgoLookupHost refuses to handle the request +// (that is, only if cgoLookupHost is the stub in cgo_stub.go). +// Normally we let cgo use the C library resolver instead of +// depending on our lookup code, so that Go and C get the same +// answers. +func goLookupHost(name string) (addrs []string, err os.Error) { + // Use entries from /etc/hosts if they match. + addrs = lookupStaticHost(name) + if len(addrs) > 0 { + return + } + onceLoadConfig.Do(loadConfig) + if dnserr != nil || cfg == nil { + err = dnserr + 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. +// Used only if cgoLookupIP refuses to handle the request +// (that is, only if cgoLookupIP is the stub in cgo_stub.go). +// Normally we let cgo use the C library resolver instead of +// depending on our lookup code, so that Go and C get the same +// answers. +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 +} + +// goLookupCNAME is the native Go implementation of LookupCNAME. +// Used only if cgoLookupCNAME refuses to handle the request +// (that is, only if cgoLookupCNAME is the stub in cgo_stub.go). +// Normally we let cgo use the C library resolver instead of +// depending on our lookup code, so that Go and C get the same +// answers. +func goLookupCNAME(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 + } + cname = rr[0].(*dnsRR_CNAME).Cname + return +} diff --git a/src/pkg/net/dnsmsg.go b/src/pkg/net/dnsmsg.go index 0ba69a0ce..640973b13 100644 --- a/src/pkg/net/dnsmsg.go +++ b/src/pkg/net/dnsmsg.go @@ -93,7 +93,7 @@ const ( // DNS queries. type dnsQuestion struct { - Name string "domain-name" // "domain-name" specifies encoding; see packers below + Name string `net:"domain-name"` // `net:"domain-name"` specifies encoding; see packers below Qtype uint16 Qclass uint16 } @@ -102,7 +102,7 @@ type dnsQuestion struct { // There are many types of messages, // but they all share the same header. type dnsRR_Header struct { - Name string "domain-name" + Name string `net:"domain-name"` Rrtype uint16 Class uint16 Ttl uint32 @@ -121,7 +121,7 @@ type dnsRR interface { type dnsRR_CNAME struct { Hdr dnsRR_Header - Cname string "domain-name" + Cname string `net:"domain-name"` } func (rr *dnsRR_CNAME) Header() *dnsRR_Header { @@ -140,7 +140,7 @@ func (rr *dnsRR_HINFO) Header() *dnsRR_Header { type dnsRR_MB struct { Hdr dnsRR_Header - Mb string "domain-name" + Mb string `net:"domain-name"` } func (rr *dnsRR_MB) Header() *dnsRR_Header { @@ -149,7 +149,7 @@ func (rr *dnsRR_MB) Header() *dnsRR_Header { type dnsRR_MG struct { Hdr dnsRR_Header - Mg string "domain-name" + Mg string `net:"domain-name"` } func (rr *dnsRR_MG) Header() *dnsRR_Header { @@ -158,8 +158,8 @@ func (rr *dnsRR_MG) Header() *dnsRR_Header { type dnsRR_MINFO struct { Hdr dnsRR_Header - Rmail string "domain-name" - Email string "domain-name" + Rmail string `net:"domain-name"` + Email string `net:"domain-name"` } func (rr *dnsRR_MINFO) Header() *dnsRR_Header { @@ -168,7 +168,7 @@ func (rr *dnsRR_MINFO) Header() *dnsRR_Header { type dnsRR_MR struct { Hdr dnsRR_Header - Mr string "domain-name" + Mr string `net:"domain-name"` } func (rr *dnsRR_MR) Header() *dnsRR_Header { @@ -178,7 +178,7 @@ func (rr *dnsRR_MR) Header() *dnsRR_Header { type dnsRR_MX struct { Hdr dnsRR_Header Pref uint16 - Mx string "domain-name" + Mx string `net:"domain-name"` } func (rr *dnsRR_MX) Header() *dnsRR_Header { @@ -187,7 +187,7 @@ func (rr *dnsRR_MX) Header() *dnsRR_Header { type dnsRR_NS struct { Hdr dnsRR_Header - Ns string "domain-name" + Ns string `net:"domain-name"` } func (rr *dnsRR_NS) Header() *dnsRR_Header { @@ -196,7 +196,7 @@ func (rr *dnsRR_NS) Header() *dnsRR_Header { type dnsRR_PTR struct { Hdr dnsRR_Header - Ptr string "domain-name" + Ptr string `net:"domain-name"` } func (rr *dnsRR_PTR) Header() *dnsRR_Header { @@ -205,8 +205,8 @@ func (rr *dnsRR_PTR) Header() *dnsRR_Header { type dnsRR_SOA struct { Hdr dnsRR_Header - Ns string "domain-name" - Mbox string "domain-name" + Ns string `net:"domain-name"` + Mbox string `net:"domain-name"` Serial uint32 Refresh uint32 Retry uint32 @@ -232,7 +232,7 @@ type dnsRR_SRV struct { Priority uint16 Weight uint16 Port uint16 - Target string "domain-name" + Target string `net:"domain-name"` } func (rr *dnsRR_SRV) Header() *dnsRR_Header { @@ -241,7 +241,7 @@ func (rr *dnsRR_SRV) Header() *dnsRR_Header { type dnsRR_A struct { Hdr dnsRR_Header - A uint32 "ipv4" + A uint32 `net:"ipv4"` } func (rr *dnsRR_A) Header() *dnsRR_Header { @@ -250,7 +250,7 @@ func (rr *dnsRR_A) Header() *dnsRR_Header { type dnsRR_AAAA struct { Hdr dnsRR_Header - AAAA [16]byte "ipv6" + AAAA [16]byte `net:"ipv6"` } func (rr *dnsRR_AAAA) Header() *dnsRR_Header { @@ -394,7 +394,6 @@ func packStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok bool) f := val.Type().Field(i) switch fv := val.Field(i); fv.Kind() { default: - BadType: fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type) return len(msg), false case reflect.Struct: @@ -419,7 +418,8 @@ func packStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok bool) off += 4 case reflect.Array: if fv.Type().Elem().Kind() != reflect.Uint8 { - goto BadType + fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type) + return len(msg), false } n := fv.Len() if off+n > len(msg) { @@ -435,7 +435,7 @@ func packStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok bool) default: fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag) return len(msg), false - case "domain-name": + case `net:"domain-name"`: off, ok = packDomainName(s, msg, off) if !ok { return len(msg), false @@ -471,7 +471,6 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok boo f := val.Type().Field(i) switch fv := val.Field(i); fv.Kind() { default: - BadType: fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type) return len(msg), false case reflect.Struct: @@ -492,7 +491,8 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok boo off += 4 case reflect.Array: if fv.Type().Elem().Kind() != reflect.Uint8 { - goto BadType + fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type) + return len(msg), false } n := fv.Len() if off+n > len(msg) { @@ -506,7 +506,7 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok boo default: fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag) return len(msg), false - case "domain-name": + case `net:"domain-name"`: s, off, ok = unpackDomainName(msg, off) if !ok { return len(msg), false @@ -536,9 +536,9 @@ 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 -// and the "ipv6" tag on array variables, +// Doesn't care about the string tag `net:"domain-name"`, +// but does look for an `net:"ipv4"` tag on uint32 variables +// and the `net:"ipv6"` tag on array variables, // printing them as IP addresses. func printStructValue(val reflect.Value) string { s := "{" @@ -553,10 +553,10 @@ func printStructValue(val reflect.Value) string { fval := val.Field(i) if fv := fval; fv.Kind() == reflect.Struct { s += printStructValue(fv) - } else if fv := fval; (fv.Kind() == reflect.Uint || fv.Kind() == reflect.Uint8 || fv.Kind() == reflect.Uint16 || fv.Kind() == reflect.Uint32 || fv.Kind() == reflect.Uint64 || fv.Kind() == reflect.Uintptr) && f.Tag == "ipv4" { + } else if fv := fval; (fv.Kind() == reflect.Uint || fv.Kind() == reflect.Uint8 || fv.Kind() == reflect.Uint16 || fv.Kind() == reflect.Uint32 || fv.Kind() == reflect.Uint64 || fv.Kind() == reflect.Uintptr) && f.Tag == `net:"ipv4"` { i := fv.Uint() s += IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String() - } else if fv := fval; fv.Kind() == reflect.Array && f.Tag == "ipv6" { + } else if fv := fval; fv.Kind() == reflect.Array && f.Tag == `net:"ipv6"` { i := fv.Interface().([]byte) s += IP(i).String() } else { diff --git a/src/pkg/net/dnsmsg_test.go b/src/pkg/net/dnsmsg_test.go index 20c9f02b0..06152a01a 100644 --- a/src/pkg/net/dnsmsg_test.go +++ b/src/pkg/net/dnsmsg_test.go @@ -6,14 +6,10 @@ package net import ( "encoding/hex" - "runtime" "testing" ) func TestDNSParseSRVReply(t *testing.T) { - if runtime.GOOS == "windows" { - return - } data, err := hex.DecodeString(dnsSRVReply) if err != nil { t.Fatal(err) @@ -45,9 +41,6 @@ func TestDNSParseSRVReply(t *testing.T) { } func TestDNSParseCorruptSRVReply(t *testing.T) { - if runtime.GOOS == "windows" { - return - } data, err := hex.DecodeString(dnsSRVCorruptReply) if err != nil { t.Fatal(err) diff --git a/src/pkg/net/dnsname_test.go b/src/pkg/net/dnsname_test.go index 0c1a62518..70df693f7 100644 --- a/src/pkg/net/dnsname_test.go +++ b/src/pkg/net/dnsname_test.go @@ -6,7 +6,6 @@ package net import ( "testing" - "runtime" ) type testCase struct { @@ -55,9 +54,6 @@ func getTestCases(ch chan<- testCase) { } func TestDNSNames(t *testing.T) { - if runtime.GOOS == "windows" { - return - } ch := make(chan testCase) go getTestCases(ch) for tc := range ch { diff --git a/src/pkg/net/fd_darwin.go b/src/pkg/net/fd_darwin.go index 00a049bfd..7e3d549eb 100644 --- a/src/pkg/net/fd_darwin.go +++ b/src/pkg/net/fd_darwin.go @@ -56,7 +56,7 @@ func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, os.Error) { return false, os.NewSyscallError("kevent", e) } if n != 1 || (ev.Flags&syscall.EV_ERROR) == 0 || int(ev.Ident) != fd || int(ev.Filter) != kmode { - return false, os.ErrorString("kqueue phase error") + return false, os.NewError("kqueue phase error") } if ev.Data != 0 { return false, os.Errno(int(ev.Data)) diff --git a/src/pkg/net/fd_windows.go b/src/pkg/net/fd_windows.go index 9ed7801d2..41d06120a 100644 --- a/src/pkg/net/fd_windows.go +++ b/src/pkg/net/fd_windows.go @@ -29,8 +29,8 @@ func init() { } } -func closesocket(s int) (errno int) { - return syscall.Closesocket(int32(s)) +func closesocket(s syscall.Handle) (errno int) { + return syscall.Closesocket(s) } // Interface for all io operations. @@ -88,7 +88,7 @@ func (o *bufOp) Init(fd *netFD, buf []byte) { // iocp and send them to the correspondent waiting client // goroutine via channel supplied in the request. type resultSrv struct { - iocp int32 + iocp syscall.Handle } func (s *resultSrv) Run() { @@ -132,7 +132,7 @@ func (s *ioSrv) ProcessRemoteIO() { case o := <-s.submchan: o.Op().errnoc <- o.Submit() case o := <-s.canchan: - o.Op().errnoc <- syscall.CancelIo(uint32(o.Op().fd.sysfd)) + o.Op().errnoc <- syscall.CancelIo(syscall.Handle(o.Op().fd.sysfd)) } } } @@ -189,7 +189,7 @@ var onceStartServer sync.Once func startServer() { resultsrv = new(resultSrv) var errno int - resultsrv.iocp, errno = syscall.CreateIoCompletionPort(-1, 0, 0, 1) + resultsrv.iocp, errno = syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 1) if errno != 0 { panic("CreateIoCompletionPort failed " + syscall.Errstr(errno)) } @@ -209,7 +209,7 @@ type netFD struct { closing bool // immutable until Close - sysfd int + sysfd syscall.Handle family int proto int net string @@ -225,7 +225,7 @@ type netFD struct { wio sync.Mutex } -func allocFD(fd, family, proto int, net string) (f *netFD) { +func allocFD(fd syscall.Handle, family, proto int, net string) (f *netFD) { f = &netFD{ sysfd: fd, family: family, @@ -236,13 +236,13 @@ func allocFD(fd, family, proto int, net string) (f *netFD) { return f } -func newFD(fd, family, proto int, net string) (f *netFD, err os.Error) { +func newFD(fd syscall.Handle, family, proto int, net string) (f *netFD, err os.Error) { if initErr != nil { return nil, initErr } onceStartServer.Do(startServer) // Associate our socket with resultsrv.iocp. - if _, e := syscall.CreateIoCompletionPort(int32(fd), resultsrv.iocp, 0, 0); e != 0 { + if _, e := syscall.CreateIoCompletionPort(syscall.Handle(fd), resultsrv.iocp, 0, 0); e != 0 { return nil, os.Errno(e) } return allocFD(fd, family, proto, net), nil @@ -280,7 +280,7 @@ func (fd *netFD) decref() { // use the resultsrv for Close too. Sigh. syscall.SetNonblock(fd.sysfd, false) closesocket(fd.sysfd) - fd.sysfd = -1 + fd.sysfd = syscall.InvalidHandle // no need for a finalizer anymore runtime.SetFinalizer(fd, nil) } @@ -288,7 +288,7 @@ func (fd *netFD) decref() { } func (fd *netFD) Close() os.Error { - if fd == nil || fd.sysfd == -1 { + if fd == nil || fd.sysfd == syscall.InvalidHandle { return os.EINVAL } @@ -307,7 +307,7 @@ type readOp struct { func (o *readOp) Submit() (errno int) { var d, f uint32 - return syscall.WSARecv(uint32(o.fd.sysfd), &o.buf, 1, &d, &f, &o.o, nil) + return syscall.WSARecv(syscall.Handle(o.fd.sysfd), &o.buf, 1, &d, &f, &o.o, nil) } func (o *readOp) Name() string { @@ -322,7 +322,7 @@ func (fd *netFD) Read(buf []byte) (n int, err os.Error) { defer fd.rio.Unlock() fd.incref() defer fd.decref() - if fd.sysfd == -1 { + if fd.sysfd == syscall.InvalidHandle { return 0, os.EINVAL } var o readOp @@ -344,7 +344,7 @@ type readFromOp struct { func (o *readFromOp) Submit() (errno int) { var d, f uint32 l := int32(unsafe.Sizeof(o.rsa)) - return syscall.WSARecvFrom(uint32(o.fd.sysfd), &o.buf, 1, &d, &f, &o.rsa, &l, &o.o, nil) + return syscall.WSARecvFrom(o.fd.sysfd, &o.buf, 1, &d, &f, &o.rsa, &l, &o.o, nil) } func (o *readFromOp) Name() string { @@ -362,7 +362,7 @@ func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err os.Error) defer fd.rio.Unlock() fd.incref() defer fd.decref() - if fd.sysfd == -1 { + if fd.sysfd == syscall.InvalidHandle { return 0, nil, os.EINVAL } var o readFromOp @@ -380,7 +380,7 @@ type writeOp struct { func (o *writeOp) Submit() (errno int) { var d uint32 - return syscall.WSASend(uint32(o.fd.sysfd), &o.buf, 1, &d, 0, &o.o, nil) + return syscall.WSASend(o.fd.sysfd, &o.buf, 1, &d, 0, &o.o, nil) } func (o *writeOp) Name() string { @@ -395,7 +395,7 @@ func (fd *netFD) Write(buf []byte) (n int, err os.Error) { defer fd.wio.Unlock() fd.incref() defer fd.decref() - if fd.sysfd == -1 { + if fd.sysfd == syscall.InvalidHandle { return 0, os.EINVAL } var o writeOp @@ -412,7 +412,7 @@ type writeToOp struct { func (o *writeToOp) Submit() (errno int) { var d uint32 - return syscall.WSASendto(uint32(o.fd.sysfd), &o.buf, 1, &d, 0, o.sa, &o.o, nil) + return syscall.WSASendto(o.fd.sysfd, &o.buf, 1, &d, 0, o.sa, &o.o, nil) } func (o *writeToOp) Name() string { @@ -430,7 +430,7 @@ func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (n int, err os.Error) defer fd.wio.Unlock() fd.incref() defer fd.decref() - if fd.sysfd == -1 { + if fd.sysfd == syscall.InvalidHandle { return 0, os.EINVAL } var o writeToOp @@ -443,14 +443,14 @@ func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (n int, err os.Error) type acceptOp struct { anOp - newsock int + newsock syscall.Handle attrs [2]syscall.RawSockaddrAny // space for local and remote address only } func (o *acceptOp) Submit() (errno int) { var d uint32 l := uint32(unsafe.Sizeof(o.attrs[0])) - return syscall.AcceptEx(uint32(o.fd.sysfd), uint32(o.newsock), + return syscall.AcceptEx(o.fd.sysfd, o.newsock, (*byte)(unsafe.Pointer(&o.attrs[0])), 0, l, l, &d, &o.o) } @@ -459,7 +459,7 @@ func (o *acceptOp) Name() string { } func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os.Error) { - if fd == nil || fd.sysfd == -1 { + if fd == nil || fd.sysfd == syscall.InvalidHandle { return nil, os.EINVAL } fd.incref() @@ -478,7 +478,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os. // Associate our new socket with IOCP. onceStartServer.Do(startServer) - if _, e = syscall.CreateIoCompletionPort(int32(s), resultsrv.iocp, 0, 0); e != 0 { + if _, e = syscall.CreateIoCompletionPort(s, resultsrv.iocp, 0, 0); e != 0 { return nil, &OpError{"CreateIoCompletionPort", fd.net, fd.laddr, os.Errno(e)} } @@ -493,7 +493,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os. } // Inherit properties of the listening socket. - e = syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, fd.sysfd) + e = syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, int(fd.sysfd)) if e != 0 { closesocket(s) return nil, err diff --git a/src/pkg/net/hosts_test.go b/src/pkg/net/hosts_test.go index e5793eef2..1bd00541c 100644 --- a/src/pkg/net/hosts_test.go +++ b/src/pkg/net/hosts_test.go @@ -59,7 +59,7 @@ func TestLookupHost(t *testing.T) { // duplicate addresses (a common bug due to the way // getaddrinfo works). addrs, _ := LookupHost("localhost") - sort.SortStrings(addrs) + sort.Strings(addrs) for i := 0; i+1 < len(addrs); i++ { if addrs[i] == addrs[i+1] { t.Fatalf("LookupHost(\"localhost\") = %v, has duplicate addresses", addrs) diff --git a/src/pkg/net/interface.go b/src/pkg/net/interface.go index f622487ab..f6de36f64 100644 --- a/src/pkg/net/interface.go +++ b/src/pkg/net/interface.go @@ -34,7 +34,41 @@ type Interface struct { MTU int // maximum transmission unit Name string // e.g., "en0", "lo0", "eth0.100" HardwareAddr HardwareAddr // IEEE MAC-48, EUI-48 and EUI-64 form - rawFlags int + Flags Flags // e.g., FlagUp, FlagLoopback, FlagMulticast +} + +type Flags uint + +const ( + FlagUp Flags = 1 << iota // interface is up + FlagBroadcast // interface supports broadcast access capability + FlagLoopback // interface is a loopback interface + FlagPointToPoint // interface belongs to a point-to-point link + FlagMulticast // interface supports multicast access capability +) + +var flagNames = []string{ + "up", + "broadcast", + "loopback", + "pointtopoint", + "multicast", +} + +func (f Flags) String() string { + s := "" + for i, name := range flagNames { + if f&(1<<uint(i)) != 0 { + if s != "" { + s += "|" + } + s += name + } + } + if s == "" { + s = "0" + } + return s } // Addrs returns interface addresses for a specific interface. diff --git a/src/pkg/net/interface_bsd.go b/src/pkg/net/interface_bsd.go index 141b95b38..a4c3e71fe 100644 --- a/src/pkg/net/interface_bsd.go +++ b/src/pkg/net/interface_bsd.go @@ -12,49 +12,6 @@ import ( "unsafe" ) -// IsUp returns true if ifi is up. -func (ifi *Interface) IsUp() bool { - if ifi == nil { - return false - } - return ifi.rawFlags&syscall.IFF_UP != 0 -} - -// IsLoopback returns true if ifi is a loopback interface. -func (ifi *Interface) IsLoopback() bool { - if ifi == nil { - return false - } - return ifi.rawFlags&syscall.IFF_LOOPBACK != 0 -} - -// CanBroadcast returns true if ifi supports a broadcast access -// capability. -func (ifi *Interface) CanBroadcast() bool { - if ifi == nil { - return false - } - return ifi.rawFlags&syscall.IFF_BROADCAST != 0 -} - -// IsPointToPoint returns true if ifi belongs to a point-to-point -// link. -func (ifi *Interface) IsPointToPoint() bool { - if ifi == nil { - return false - } - return ifi.rawFlags&syscall.IFF_POINTOPOINT != 0 -} - -// CanMulticast returns true if ifi supports a multicast access -// capability. -func (ifi *Interface) CanMulticast() bool { - if ifi == nil { - return false - } - return ifi.rawFlags&syscall.IFF_MULTICAST != 0 -} - // If the ifindex is zero, interfaceTable returns mappings of all // network interfaces. Otheriwse it returns a mapping of a specific // interface. @@ -106,7 +63,7 @@ func newLink(m *syscall.InterfaceMessage) ([]Interface, os.Error) { // NOTE: SockaddrDatalink.Data is minimum work area, // can be larger. m.Data = m.Data[unsafe.Offsetof(v.Data):] - ifi := Interface{Index: int(m.Header.Index), rawFlags: int(m.Header.Flags)} + ifi := Interface{Index: int(m.Header.Index), Flags: linkFlags(m.Header.Flags)} var name [syscall.IFNAMSIZ]byte for i := 0; i < int(v.Nlen); i++ { name[i] = byte(m.Data[i]) @@ -125,6 +82,26 @@ func newLink(m *syscall.InterfaceMessage) ([]Interface, os.Error) { return ift, nil } +func linkFlags(rawFlags int32) Flags { + var f Flags + if rawFlags&syscall.IFF_UP != 0 { + f |= FlagUp + } + if rawFlags&syscall.IFF_BROADCAST != 0 { + f |= FlagBroadcast + } + if rawFlags&syscall.IFF_LOOPBACK != 0 { + f |= FlagLoopback + } + if rawFlags&syscall.IFF_POINTOPOINT != 0 { + f |= FlagPointToPoint + } + if rawFlags&syscall.IFF_MULTICAST != 0 { + f |= FlagMulticast + } + return f +} + // If the ifindex is zero, interfaceAddrTable returns addresses // for all network interfaces. Otherwise it returns addresses // for a specific interface. diff --git a/src/pkg/net/interface_linux.go b/src/pkg/net/interface_linux.go index 5c9657834..e869cd630 100644 --- a/src/pkg/net/interface_linux.go +++ b/src/pkg/net/interface_linux.go @@ -12,49 +12,6 @@ import ( "unsafe" ) -// IsUp returns true if ifi is up. -func (ifi *Interface) IsUp() bool { - if ifi == nil { - return false - } - return ifi.rawFlags&syscall.IFF_UP != 0 -} - -// IsLoopback returns true if ifi is a loopback interface. -func (ifi *Interface) IsLoopback() bool { - if ifi == nil { - return false - } - return ifi.rawFlags&syscall.IFF_LOOPBACK != 0 -} - -// CanBroadcast returns true if ifi supports a broadcast access -// capability. -func (ifi *Interface) CanBroadcast() bool { - if ifi == nil { - return false - } - return ifi.rawFlags&syscall.IFF_BROADCAST != 0 -} - -// IsPointToPoint returns true if ifi belongs to a point-to-point -// link. -func (ifi *Interface) IsPointToPoint() bool { - if ifi == nil { - return false - } - return ifi.rawFlags&syscall.IFF_POINTOPOINT != 0 -} - -// CanMulticast returns true if ifi supports a multicast access -// capability. -func (ifi *Interface) CanMulticast() bool { - if ifi == nil { - return false - } - return ifi.rawFlags&syscall.IFF_MULTICAST != 0 -} - // If the ifindex is zero, interfaceTable returns mappings of all // network interfaces. Otheriwse it returns a mapping of a specific // interface. @@ -98,7 +55,7 @@ done: } func newLink(attrs []syscall.NetlinkRouteAttr, ifim *syscall.IfInfomsg) Interface { - ifi := Interface{Index: int(ifim.Index), rawFlags: int(ifim.Flags)} + ifi := Interface{Index: int(ifim.Index), Flags: linkFlags(ifim.Flags)} for _, a := range attrs { switch a.Attr.Type { case syscall.IFLA_ADDRESS: @@ -112,7 +69,7 @@ func newLink(attrs []syscall.NetlinkRouteAttr, ifim *syscall.IfInfomsg) Interfac ifi.HardwareAddr = a.Value[:] } case syscall.IFLA_IFNAME: - ifi.Name = string(a.Value[:]) + ifi.Name = string(a.Value[:len(a.Value)-1]) case syscall.IFLA_MTU: ifi.MTU = int(uint32(a.Value[3])<<24 | uint32(a.Value[2])<<16 | uint32(a.Value[1])<<8 | uint32(a.Value[0])) } @@ -120,6 +77,26 @@ func newLink(attrs []syscall.NetlinkRouteAttr, ifim *syscall.IfInfomsg) Interfac return ifi } +func linkFlags(rawFlags uint32) Flags { + var f Flags + if rawFlags&syscall.IFF_UP != 0 { + f |= FlagUp + } + if rawFlags&syscall.IFF_BROADCAST != 0 { + f |= FlagBroadcast + } + if rawFlags&syscall.IFF_LOOPBACK != 0 { + f |= FlagLoopback + } + if rawFlags&syscall.IFF_POINTOPOINT != 0 { + f |= FlagPointToPoint + } + if rawFlags&syscall.IFF_MULTICAST != 0 { + f |= FlagMulticast + } + return f +} + // If the ifindex is zero, interfaceAddrTable returns addresses // for all network interfaces. Otherwise it returns addresses // for a specific interface. diff --git a/src/pkg/net/interface_stub.go b/src/pkg/net/interface_stub.go index feb871bb5..24a7431c5 100644 --- a/src/pkg/net/interface_stub.go +++ b/src/pkg/net/interface_stub.go @@ -8,34 +8,6 @@ package net import "os" -// IsUp returns true if ifi is up. -func (ifi *Interface) IsUp() bool { - return false -} - -// IsLoopback returns true if ifi is a loopback interface. -func (ifi *Interface) IsLoopback() bool { - return false -} - -// CanBroadcast returns true if ifi supports a broadcast access -// capability. -func (ifi *Interface) CanBroadcast() bool { - return false -} - -// IsPointToPoint returns true if ifi belongs to a point-to-point -// link. -func (ifi *Interface) IsPointToPoint() bool { - return false -} - -// CanMulticast returns true if ifi supports a multicast access -// capability. -func (ifi *Interface) CanMulticast() bool { - return false -} - // If the ifindex is zero, interfaceTable returns mappings of all // network interfaces. Otheriwse it returns a mapping of a specific // interface. diff --git a/src/pkg/net/interface_test.go b/src/pkg/net/interface_test.go index 938434623..ac523a049 100644 --- a/src/pkg/net/interface_test.go +++ b/src/pkg/net/interface_test.go @@ -19,30 +19,6 @@ func sameInterface(i, j *Interface) bool { return false } -func interfaceFlagsString(ifi *Interface) string { - fs := "<" - if ifi.IsUp() { - fs += "UP," - } - if ifi.CanBroadcast() { - fs += "BROADCAST," - } - if ifi.IsLoopback() { - fs += "LOOPBACK," - } - if ifi.IsPointToPoint() { - fs += "POINTOPOINT," - } - if ifi.CanMulticast() { - fs += "MULTICAST," - } - if len(fs) > 1 { - fs = fs[:len(fs)-1] - } - fs += ">" - return fs -} - func TestInterfaces(t *testing.T) { ift, err := Interfaces() if err != nil { @@ -69,11 +45,11 @@ func TestInterfaces(t *testing.T) { if err != nil { t.Fatalf("Interface.Addrs() failed: %v", err) } - t.Logf("%s: flags %s, ifindex %v, mtu %v\n", ifi.Name, interfaceFlagsString(&ifi), ifi.Index, ifi.MTU) + t.Logf("%q: flags %q, ifindex %v, mtu %v\n", ifi.Name, ifi.Flags.String(), ifi.Index, ifi.MTU) for _, ifa := range ifat { - t.Logf("\tinterface address %s\n", ifa.String()) + t.Logf("\tinterface address %q\n", ifa.String()) } - t.Logf("\thardware address %v", ifi.HardwareAddr.String()) + t.Logf("\thardware address %q", ifi.HardwareAddr.String()) } } @@ -85,6 +61,6 @@ func TestInterfaceAddrs(t *testing.T) { t.Logf("table: len/cap = %v/%v\n", len(ifat), cap(ifat)) for _, ifa := range ifat { - t.Logf("interface address %s\n", ifa.String()) + t.Logf("interface address %q\n", ifa.String()) } } diff --git a/src/pkg/net/interface_windows.go b/src/pkg/net/interface_windows.go new file mode 100644 index 000000000..198e4096f --- /dev/null +++ b/src/pkg/net/interface_windows.go @@ -0,0 +1,152 @@ +// 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. + +// Network interface identification for Windows + +package net + +import ( + "os" + "syscall" + "unsafe" +) + +func bytePtrToString(p *uint8) string { + a := (*[10000]uint8)(unsafe.Pointer(p)) + i := 0 + for a[i] != 0 { + i++ + } + return string(a[:i]) +} + +func getAdapterList() (*syscall.IpAdapterInfo, os.Error) { + b := make([]byte, 1000) + l := uint32(len(b)) + a := (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0])) + e := syscall.GetAdaptersInfo(a, &l) + if e == syscall.ERROR_BUFFER_OVERFLOW { + b = make([]byte, l) + a = (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0])) + e = syscall.GetAdaptersInfo(a, &l) + } + if e != 0 { + return nil, os.NewSyscallError("GetAdaptersInfo", e) + } + return a, nil +} + +func getInterfaceList() ([]syscall.InterfaceInfo, os.Error) { + s, e := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP) + if e != 0 { + return nil, os.NewSyscallError("Socket", e) + } + defer syscall.Closesocket(s) + + ii := [20]syscall.InterfaceInfo{} + ret := uint32(0) + size := uint32(unsafe.Sizeof(ii)) + e = syscall.WSAIoctl(s, syscall.SIO_GET_INTERFACE_LIST, nil, 0, (*byte)(unsafe.Pointer(&ii[0])), size, &ret, nil, 0) + if e != 0 { + return nil, os.NewSyscallError("WSAIoctl", e) + } + c := ret / uint32(unsafe.Sizeof(ii[0])) + return ii[:c-1], nil +} + + +// If the ifindex is zero, interfaceTable returns mappings of all +// network interfaces. Otheriwse it returns a mapping of a specific +// interface. +func interfaceTable(ifindex int) ([]Interface, os.Error) { + ai, e := getAdapterList() + if e != nil { + return nil, e + } + + ii, e := getInterfaceList() + if e != nil { + return nil, e + } + + var ift []Interface + for ; ai != nil; ai = ai.Next { + index := ai.Index + if ifindex == 0 || ifindex == int(index) { + var flags Flags + + row := syscall.MibIfRow{Index: index} + e := syscall.GetIfEntry(&row) + if e != 0 { + return nil, os.NewSyscallError("GetIfEntry", e) + } + + for _, ii := range ii { + ip := (*syscall.RawSockaddrInet4)(unsafe.Pointer(&ii.Address)).Addr + ipv4 := IPv4(ip[0], ip[1], ip[2], ip[3]) + ipl := &ai.IpAddressList + for ipl != nil { + ips := bytePtrToString(&ipl.IpAddress.String[0]) + if ipv4.Equal(parseIPv4(ips)) { + break + } + ipl = ipl.Next + } + if ipl == nil { + continue + } + if ii.Flags&syscall.IFF_UP != 0 { + flags |= FlagUp + } + if ii.Flags&syscall.IFF_LOOPBACK != 0 { + flags |= FlagLoopback + } + if ii.Flags&syscall.IFF_BROADCAST != 0 { + flags |= FlagBroadcast + } + if ii.Flags&syscall.IFF_POINTTOPOINT != 0 { + flags |= FlagPointToPoint + } + if ii.Flags&syscall.IFF_MULTICAST != 0 { + flags |= FlagMulticast + } + } + + name := bytePtrToString(&ai.AdapterName[0]) + + ifi := Interface{ + Index: int(index), + MTU: int(row.Mtu), + Name: name, + HardwareAddr: HardwareAddr(row.PhysAddr[:row.PhysAddrLen]), + Flags: flags} + ift = append(ift, ifi) + } + } + return ift, nil +} + +// If the ifindex is zero, interfaceAddrTable returns addresses +// for all network interfaces. Otherwise it returns addresses +// for a specific interface. +func interfaceAddrTable(ifindex int) ([]Addr, os.Error) { + ai, e := getAdapterList() + if e != nil { + return nil, e + } + + var ifat []Addr + for ; ai != nil; ai = ai.Next { + index := ai.Index + if ifindex == 0 || ifindex == int(index) { + ipl := &ai.IpAddressList + for ; ipl != nil; ipl = ipl.Next { + ifa := IPAddr{} + ifa.IP = parseIPv4(bytePtrToString(&ipl.IpAddress.String[0])) + ifat = append(ifat, ifa.toAddr()) + } + } + } + return ifat, nil +} diff --git a/src/pkg/net/iprawsock.go b/src/pkg/net/iprawsock.go index a811027b1..43047a78e 100644 --- a/src/pkg/net/iprawsock.go +++ b/src/pkg/net/iprawsock.go @@ -294,7 +294,7 @@ func splitNetProto(netProto string) (net string, proto int, err os.Error) { onceReadProtocols.Do(readProtocols) i := last(netProto, ':') if i < 0 { // no colon - return "", 0, os.ErrorString("no IP protocol specified") + return "", 0, os.NewError("no IP protocol specified") } net = netProto[0:i] protostr := netProto[i+1:] diff --git a/src/pkg/net/ipsock.go b/src/pkg/net/ipsock.go index 0b8c388f1..e831d9afc 100644 --- a/src/pkg/net/ipsock.go +++ b/src/pkg/net/ipsock.go @@ -26,28 +26,26 @@ import ( // boolean value is true, kernel supports IPv6 IPv4-mapping. func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { var probes = []struct { - s int la TCPAddr ok bool }{ // IPv6 communication capability - {-1, TCPAddr{IP: ParseIP("::1")}, false}, + {TCPAddr{IP: ParseIP("::1")}, false}, // IPv6 IPv4-mapped address communication capability - {-1, TCPAddr{IP: IPv4(127, 0, 0, 1)}, false}, + {TCPAddr{IP: IPv4(127, 0, 0, 1)}, false}, } - var errno int for i := range probes { - probes[i].s, errno = syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) + s, errno := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) if errno != 0 { continue } - defer closesocket(probes[i].s) + defer closesocket(s) sa, err := probes[i].la.toAddr().sockaddr(syscall.AF_INET6) if err != nil { continue } - errno = syscall.Bind(probes[i].s, sa) + errno = syscall.Bind(s, sa) if errno != 0 { continue } @@ -270,12 +268,16 @@ 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) { + var ( + addr IP + p, i int + ok bool + ) host, port, err := SplitHostPort(hostport) if err != nil { goto Error } - var addr IP if host != "" { // Try as an IP address. addr = ParseIP(host) @@ -302,7 +304,7 @@ func hostPortToIP(net, hostport string) (ip IP, iport int, err os.Error) { } } - p, i, ok := dtoi(port, 0) + p, i, ok = dtoi(port, 0) if !ok || i != len(port) { p, err = LookupPort(net, port) if err != nil { diff --git a/src/pkg/net/lookup.go b/src/pkg/net/lookup.go deleted file mode 100644 index eeb22a8ae..000000000 --- a/src/pkg/net/lookup.go +++ /dev/null @@ -1,50 +0,0 @@ -// 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 -} - -// 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) { - cname, err, ok := cgoLookupCNAME(name) - if !ok { - cname, err = goLookupCNAME(name) - } - return -} diff --git a/src/pkg/net/lookup_unix.go b/src/pkg/net/lookup_unix.go new file mode 100644 index 000000000..168d3fa6d --- /dev/null +++ b/src/pkg/net/lookup_unix.go @@ -0,0 +1,126 @@ +// 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" + "rand" + "sort" +) + +// 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 +} + +// 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) { + cname, err, ok := cgoLookupCNAME(name) + if !ok { + cname, err = goLookupCNAME(name) + } + return +} + +// LookupSRV tries to resolve an SRV query of the given service, +// protocol, and domain name, as specified in RFC 2782. In most cases +// the proto argument can be the same as the corresponding +// Addr.Network(). The returned records are sorted by priority +// and randomized by weight within a priority. +func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os.Error) { + target := "_" + service + "._" + proto + "." + name + var records []dnsRR + cname, records, err = lookup(target, dnsTypeSRV) + if err != nil { + return + } + addrs = make([]*SRV, len(records)) + for i, rr := range records { + r := rr.(*dnsRR_SRV) + addrs[i] = &SRV{r.Target, r.Port, r.Priority, r.Weight} + } + sort.Sort(byPriorityWeight(addrs)) + i := 0 + for j := 1; j < len(addrs); j++ { + if addrs[i].Priority != addrs[j].Priority { + shuffleSRVByWeight(addrs[i:j]) + i = j + } + } + shuffleSRVByWeight(addrs[i:len(addrs)]) + return +} + +// LookupMX returns the DNS MX records for the given domain name sorted by preference. +func LookupMX(name string) (mx []*MX, err os.Error) { + _, rr, err := lookup(name, dnsTypeMX) + if err != nil { + return + } + mx = make([]*MX, len(rr)) + for i := range rr { + r := rr[i].(*dnsRR_MX) + mx[i] = &MX{r.Mx, r.Pref} + } + // Shuffle the records to match RFC 5321 when sorted + for i := range mx { + j := rand.Intn(i + 1) + mx[i], mx[j] = mx[j], mx[i] + } + sort.Sort(byPref(mx)) + return +} + +// LookupAddr performs a reverse lookup for the given address, returning a list +// of names mapping to that address. +func LookupAddr(addr string) (name []string, err os.Error) { + name = lookupStaticAddr(addr) + if len(name) > 0 { + return + } + var arpa string + arpa, err = reverseaddr(addr) + if err != nil { + return + } + var records []dnsRR + _, records, err = lookup(arpa, dnsTypePTR) + if err != nil { + return + } + name = make([]string, len(records)) + for i := range records { + r := records[i].(*dnsRR_PTR) + name[i] = r.Ptr + } + return +} diff --git a/src/pkg/net/resolv_windows.go b/src/pkg/net/lookup_windows.go index f7c3f51be..16b37f56c 100644 --- a/src/pkg/net/resolv_windows.go +++ b/src/pkg/net/lookup_windows.go @@ -14,8 +14,8 @@ import ( var hostentLock sync.Mutex var serventLock sync.Mutex -func goLookupHost(name string) (addrs []string, err os.Error) { - ips, err := goLookupIP(name) +func LookupHost(name string) (addrs []string, err os.Error) { + ips, err := LookupIP(name) if err != nil { return } @@ -26,7 +26,7 @@ func goLookupHost(name string) (addrs []string, err os.Error) { return } -func goLookupIP(name string) (addrs []IP, err os.Error) { +func LookupIP(name string) (addrs []IP, err os.Error) { hostentLock.Lock() defer hostentLock.Unlock() h, e := syscall.GetHostByName(name) @@ -47,7 +47,23 @@ func goLookupIP(name string) (addrs []IP, err os.Error) { return addrs, nil } -func goLookupCNAME(name string) (cname string, err os.Error) { +func LookupPort(network, service string) (port int, err os.Error) { + switch network { + case "tcp4", "tcp6": + network = "tcp" + case "udp4", "udp6": + network = "udp" + } + serventLock.Lock() + defer serventLock.Unlock() + s, e := syscall.GetServByName(service, network) + if e != 0 { + return 0, os.NewSyscallError("GetServByName", e) + } + return int(syscall.Ntohs(s.Port)), nil +} + +func LookupCNAME(name string) (cname string, err os.Error) { var r *syscall.DNSRecord e := syscall.DnsQuery(name, syscall.DNS_TYPE_CNAME, 0, nil, &r, nil) if int(e) != 0 { @@ -61,13 +77,6 @@ func goLookupCNAME(name string) (cname string, err os.Error) { return } -type SRV struct { - Target string - Port uint16 - Priority uint16 - Weight uint16 -} - func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os.Error) { var r *syscall.DNSRecord target := "_" + service + "._" + proto + "." + name @@ -87,55 +96,12 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os. return name, addrs, nil } -func goLookupPort(network, service string) (port int, err os.Error) { - switch network { - case "tcp4", "tcp6": - network = "tcp" - case "udp4", "udp6": - network = "udp" - } - serventLock.Lock() - defer serventLock.Unlock() - s, e := syscall.GetServByName(service, network) - if e != 0 { - return 0, os.NewSyscallError("GetServByName", e) - } - return int(syscall.Ntohs(s.Port)), nil -} +// TODO(brainman): implement LookupMX and LookupAddr. -// TODO(brainman): Following code is only to get tests running. - -func isDomainName(s string) bool { - panic("unimplemented") -} - -func reverseaddr(addr string) (arpa string, err os.Error) { - panic("unimplemented") -} - -func answer(name, server string, dns *dnsMsg, qtype uint16) (cname string, addrs []dnsRR, err os.Error) { - panic("unimplemented") +func LookupMX(name string) (mx []*MX, err os.Error) { + return nil, os.NewSyscallError("LookupMX", syscall.EWINDOWS) } -// DNSError represents a DNS lookup error. -type DNSError struct { - Error string // description of the error - Name string // name looked for - Server string // server used - IsTimeout bool +func LookupAddr(addr string) (name []string, err os.Error) { + return nil, os.NewSyscallError("LookupAddr", syscall.EWINDOWS) } - -func (e *DNSError) String() string { - if e == nil { - return "<nil>" - } - s := "lookup " + e.Name - if e.Server != "" { - s += " on " + e.Server - } - s += ": " + e.Error - return s -} - -func (e *DNSError) Timeout() bool { return e.IsTimeout } -func (e *DNSError) Temporary() bool { return e.IsTimeout } diff --git a/src/pkg/net/net.go b/src/pkg/net/net.go index 51db10739..5c84d3434 100644 --- a/src/pkg/net/net.go +++ b/src/pkg/net/net.go @@ -115,7 +115,7 @@ type Listener interface { Addr() Addr } -var errMissingAddress = os.ErrorString("missing address") +var errMissingAddress = os.NewError("missing address") type OpError struct { Op string diff --git a/src/pkg/net/net_test.go b/src/pkg/net/net_test.go index f7eae56fe..698a84527 100644 --- a/src/pkg/net/net_test.go +++ b/src/pkg/net/net_test.go @@ -7,7 +7,6 @@ package net import ( "flag" "regexp" - "runtime" "testing" ) @@ -103,9 +102,6 @@ var revAddrTests = []struct { } func TestReverseAddress(t *testing.T) { - if runtime.GOOS == "windows" { - return - } for i, tt := range revAddrTests { a, e := reverseaddr(tt.Addr) if len(tt.ErrPrefix) > 0 && e == nil { diff --git a/src/pkg/net/newpollserver.go b/src/pkg/net/newpollserver.go index fff54dba7..427208701 100644 --- a/src/pkg/net/newpollserver.go +++ b/src/pkg/net/newpollserver.go @@ -18,12 +18,7 @@ func newPollServer() (s *pollServer, err os.Error) { } var e int if e = syscall.SetNonblock(s.pr.Fd(), true); e != 0 { - Errno: - err = &os.PathError{"setnonblock", s.pr.Name(), os.Errno(e)} - Error: - s.pr.Close() - s.pw.Close() - return nil, err + goto Errno } if e = syscall.SetNonblock(s.pw.Fd(), true); e != 0 { goto Errno @@ -38,4 +33,11 @@ func newPollServer() (s *pollServer, err os.Error) { s.pending = make(map[int]*netFD) go s.Run() return s, nil + +Errno: + err = &os.PathError{"setnonblock", s.pr.Name(), os.Errno(e)} +Error: + s.pr.Close() + s.pw.Close() + return nil, err } diff --git a/src/pkg/net/sendfile_windows.go b/src/pkg/net/sendfile_windows.go new file mode 100644 index 000000000..3772eee24 --- /dev/null +++ b/src/pkg/net/sendfile_windows.go @@ -0,0 +1,68 @@ +// 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 ( + "io" + "os" + "syscall" +) + +type sendfileOp struct { + anOp + src syscall.Handle // source + n uint32 +} + +func (o *sendfileOp) Submit() (errno int) { + return syscall.TransmitFile(o.fd.sysfd, o.src, o.n, 0, &o.o, nil, syscall.TF_WRITE_BEHIND) +} + +func (o *sendfileOp) Name() string { + return "TransmitFile" +} + +// sendFile copies the contents of r to c using the TransmitFile +// system call to minimize copies. +// +// if handled == true, sendFile returns the number of bytes copied and any +// non-EOF error. +// +// if handled == false, sendFile performed no work. +// +// Note that sendfile for windows does not suppport >2GB file. +func sendFile(c *netFD, r io.Reader) (written int64, err os.Error, handled bool) { + var n int64 = 0 // by default, copy until EOF + + lr, ok := r.(*io.LimitedReader) + if ok { + n, r = lr.N, lr.R + if n <= 0 { + return 0, nil, true + } + } + f, ok := r.(*os.File) + if !ok { + return 0, nil, false + } + + c.wio.Lock() + defer c.wio.Unlock() + c.incref() + defer c.decref() + + var o sendfileOp + o.Init(c) + o.n = uint32(n) + o.src = f.Fd() + done, err := iosrv.ExecIO(&o, 0) + if err != nil { + return 0, err, false + } + if lr != nil { + lr.N -= int64(done) + } + return int64(done), nil, true +} diff --git a/src/pkg/net/sock.go b/src/pkg/net/sock.go index eae7f3711..821716e43 100644 --- a/src/pkg/net/sock.go +++ b/src/pkg/net/sock.go @@ -50,8 +50,7 @@ func socket(net string, f, p, t int, la, ra syscall.Sockaddr, toAddr func(syscal if ra != nil { if err = fd.connect(ra); err != nil { - fd.sysfd = -1 - closesocket(s) + fd.Close() return nil, err } } @@ -65,25 +64,25 @@ func socket(net string, f, p, t int, la, ra syscall.Sockaddr, toAddr func(syscal return fd, nil } -func setsockoptInt(fd, level, opt int, value int) os.Error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, level, opt, value)) +func setsockoptInt(fd *netFD, level, opt int, value int) os.Error { + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, level, opt, value)) } -func setsockoptNsec(fd, level, opt int, nsec int64) os.Error { +func setsockoptNsec(fd *netFD, level, opt int, nsec int64) os.Error { var tv = syscall.NsecToTimeval(nsec) - return os.NewSyscallError("setsockopt", syscall.SetsockoptTimeval(fd, level, opt, &tv)) + return os.NewSyscallError("setsockopt", syscall.SetsockoptTimeval(fd.sysfd, level, opt, &tv)) } func setReadBuffer(fd *netFD, bytes int) os.Error { fd.incref() defer fd.decref() - return setsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes) + return setsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes) } func setWriteBuffer(fd *netFD, bytes int) os.Error { fd.incref() defer fd.decref() - return setsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes) + return setsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes) } func setReadTimeout(fd *netFD, nsec int64) os.Error { @@ -106,7 +105,7 @@ func setTimeout(fd *netFD, nsec int64) os.Error { func setReuseAddr(fd *netFD, reuse bool) os.Error { fd.incref() defer fd.decref() - return setsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, boolint(reuse)) + return setsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, boolint(reuse)) } func bindToDevice(fd *netFD, dev string) os.Error { @@ -117,19 +116,19 @@ func bindToDevice(fd *netFD, dev string) os.Error { func setDontRoute(fd *netFD, dontroute bool) os.Error { fd.incref() defer fd.decref() - return setsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_DONTROUTE, boolint(dontroute)) + return setsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_DONTROUTE, boolint(dontroute)) } func setKeepAlive(fd *netFD, keepalive bool) os.Error { fd.incref() defer fd.decref() - return setsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive)) + return setsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive)) } func setNoDelay(fd *netFD, noDelay bool) os.Error { fd.incref() defer fd.decref() - return setsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_NODELAY, boolint(noDelay)) + return setsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_NODELAY, boolint(noDelay)) } func setLinger(fd *netFD, sec int) os.Error { diff --git a/src/pkg/net/sock_windows.go b/src/pkg/net/sock_windows.go index e17c60b98..c6dbd0465 100644 --- a/src/pkg/net/sock_windows.go +++ b/src/pkg/net/sock_windows.go @@ -10,7 +10,7 @@ import ( "syscall" ) -func setKernelSpecificSockopt(s, f int) { +func setKernelSpecificSockopt(s syscall.Handle, f int) { // Allow reuse of recently-used addresses and ports. syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) diff --git a/src/pkg/net/udpsock.go b/src/pkg/net/udpsock.go index 5469acffa..94e249d62 100644 --- a/src/pkg/net/udpsock.go +++ b/src/pkg/net/udpsock.go @@ -281,7 +281,7 @@ func (c *UDPConn) BindToDevice(device string) os.Error { // Closing c does not affect f, and closing f does not affect c. func (c *UDPConn) File() (f *os.File, err os.Error) { return c.fd.dup() } -var errInvalidMulticast = os.ErrorString("invalid IPv4 multicast address") +var errInvalidMulticast = os.NewError("invalid IPv4 multicast address") // JoinGroup joins the IPv4 multicast group named by addr. // The UDPConn must use the "udp4" network. |