diff options
Diffstat (limited to 'libgo/go/net')
59 files changed, 4768 insertions, 2081 deletions
diff --git a/libgo/go/net/dial.go b/libgo/go/net/dial.go index 16896b4269b..10c67dcc40a 100644 --- a/libgo/go/net/dial.go +++ b/libgo/go/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(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. @@ -139,12 +131,13 @@ func ListenPacket(net, laddr string) (c PacketConn, err os.Error) { return c, nil } - if i := last(net, ':'); i > 0 { - switch net[0:i] { + var rawnet string + if rawnet, _, err = splitNetProto(net); err != nil { + switch rawnet { case "ip", "ip4", "ip6": var la *IPAddr if laddr != "" { - if la, err = ResolveIPAddr(laddr); err != nil { + if la, err = ResolveIPAddr(rawnet, laddr); err != nil { return nil, err } } diff --git a/libgo/go/net/dialgoogle_test.go b/libgo/go/net/dialgoogle_test.go index e90c4f3f894..9ad1770dab7 100644 --- a/libgo/go/net/dialgoogle_test.go +++ b/libgo/go/net/dialgoogle_test.go @@ -105,14 +105,12 @@ func TestDialGoogleIPv4(t *testing.T) { doDial(t, "tcp", addr) if addr[0] != '[' { doDial(t, "tcp4", addr) - if !preferIPv4 { - // make sure preferIPv4 flag works. - preferIPv4 = true + if supportsIPv6 { + // make sure syscall.SocketDisableIPv6 flag works. syscall.SocketDisableIPv6 = true doDial(t, "tcp", addr) doDial(t, "tcp4", addr) syscall.SocketDisableIPv6 = false - preferIPv4 = false } } } @@ -132,7 +130,7 @@ func TestDialGoogleIPv6(t *testing.T) { return } // Only run tcp6 if the kernel will take it. - if !*ipv6 || !kernelSupportsIPv6() { + if !*ipv6 || !supportsIPv6 { return } diff --git a/libgo/go/net/dict/dict.go b/libgo/go/net/dict/dict.go index 42f6553ad33..b146ea2123c 100644 --- a/libgo/go/net/dict/dict.go +++ b/libgo/go/net/dict/dict.go @@ -7,7 +7,6 @@ package dict import ( - "container/vector" "net/textproto" "os" "strconv" @@ -144,7 +143,7 @@ func (c *Client) Define(dict, word string) ([]*Defn, os.Error) { // Fields are space separated unquoted words // or quoted with single or double quote. func fields(s string) ([]string, os.Error) { - var v vector.StringVector + var v []string i := 0 for { for i < len(s) && (s[i] == ' ' || s[i] == '\t') { @@ -170,7 +169,7 @@ func fields(s string) ([]string, os.Error) { break } } - v.Push(unquote(s[i+1 : j-1])) + v = append(v, unquote(s[i+1:j-1])) i = j } else { // atom @@ -180,7 +179,7 @@ func fields(s string) ([]string, os.Error) { break } } - v.Push(s[i:j]) + v = append(v, s[i:j]) i = j } if i < len(s) { diff --git a/libgo/go/net/dnsclient.go b/libgo/go/net/dnsclient.go index 3466003fab8..93c04f6b590 100644 --- a/libgo/go/net/dnsclient.go +++ b/libgo/go/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,8 +9,6 @@ import ( "fmt" "os" "rand" - "sync" - "time" "sort" ) @@ -49,54 +37,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 +115,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,166 +163,63 @@ 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 - } +// An SRV represents a single DNS SRV record. +type SRV struct { + Target string + Port uint16 + Priority uint16 + Weight uint16 +} - // 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 - } - } +// byPriorityWeight sorts SRV records by ascending priority and weight. +type byPriorityWeight []*SRV - // Last ditch effort: try unsuffixed. - rname := name - if !rooted { - rname += "." - } - cname, addrs, err = tryOneName(cfg, rname, qtype) - if err == nil { - return - } - return -} +func (s byPriorityWeight) Len() int { return len(s) } -// 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 -} +func (s byPriorityWeight) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -// 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 +func (s byPriorityWeight) Less(i, j int) bool { + return s[i].Priority < s[j].Priority || + (s[i].Priority == s[j].Priority && s[i].Weight < s[j].Weight) } -// 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 +// shuffleByWeight shuffles SRV records by weight using the algorithm +// described in RFC 2782. +func (addrs byPriorityWeight) shuffleByWeight() { + sum := 0 + for _, addr := range addrs { + sum += int(addr.Weight) } - _, rr, err := lookup(name, dnsTypeCNAME) - if err != nil { - return + for sum > 0 && len(addrs) > 1 { + s := 0 + n := rand.Intn(sum + 1) + for i := range addrs { + s += int(addrs[i].Weight) + if s >= n { + if i > 0 { + t := addrs[i] + copy(addrs[1:i+1], addrs[0:i]) + addrs[0] = t + } + break + } + } + sum -= int(addrs[0].Weight) + addrs = addrs[1:] } - cname = rr[0].(*dnsRR_CNAME).Cname - return } -// An SRV represents a single DNS SRV record. -type SRV struct { - Target string - Port uint16 - Priority uint16 - Weight uint16 -} - -// 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(). -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 reorders SRV records as specified in RFC 2782. +func (addrs byPriorityWeight) sort() { + sort.Sort(addrs) + i := 0 + for j := 1; j < len(addrs); j++ { + if addrs[i].Priority != addrs[j].Priority { + addrs[i:j].shuffleByWeight() + i = j + } } - return + addrs[i:].shuffleByWeight() } // An MX represents a single DNS MX record. @@ -432,72 +237,11 @@ 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 { +// sort reorders MX records as specified in RFC 5321. +func (s byPref) sort() { + for i := range s { 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 + s[i], s[j] = s[j], s[i] } - return + sort.Sort(s) } diff --git a/libgo/go/net/dnsclient_unix.go b/libgo/go/net/dnsclient_unix.go new file mode 100644 index 00000000000..f407b177830 --- /dev/null +++ b/libgo/go/net/dnsclient_unix.go @@ -0,0 +1,261 @@ +// 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/libgo/go/net/dnsconfig.go b/libgo/go/net/dnsconfig.go index 26f0e04e907..54e334342ad 100644 --- a/libgo/go/net/dnsconfig.go +++ b/libgo/go/net/dnsconfig.go @@ -30,7 +30,6 @@ func (e *DNSConfigError) String() string { func (e *DNSConfigError) Timeout() bool { return false } func (e *DNSConfigError) Temporary() bool { return false } - // See resolv.conf(5) on a Linux machine. // TODO(rsc): Supposed to call uname() and chop the beginning // of the host name to get the default search domain. diff --git a/libgo/go/net/dnsmsg.go b/libgo/go/net/dnsmsg.go index 731efe26a44..7595aa2ab59 100644 --- a/libgo/go/net/dnsmsg.go +++ b/libgo/go/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 @@ -117,12 +117,11 @@ type dnsRR interface { Header() *dnsRR_Header } - // Specific DNS RR formats for each query type. type dnsRR_CNAME struct { Hdr dnsRR_Header - Cname string "domain-name" + Cname string `net:"domain-name"` } func (rr *dnsRR_CNAME) Header() *dnsRR_Header { @@ -141,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 { @@ -150,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 { @@ -159,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 { @@ -169,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 { @@ -179,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 { @@ -188,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 { @@ -197,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 { @@ -206,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 @@ -233,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 { @@ -242,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 { @@ -251,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 { @@ -395,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: @@ -420,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) { @@ -436,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 @@ -472,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: @@ -493,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) { @@ -507,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 @@ -537,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 := "{" @@ -554,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 { @@ -636,7 +635,6 @@ type dnsMsg struct { extra []dnsRR } - func (dns *dnsMsg) Pack() (msg []byte, ok bool) { var dh dnsHeader diff --git a/libgo/go/net/dnsmsg_test.go b/libgo/go/net/dnsmsg_test.go index 20c9f02b0b4..06152a01a23 100644 --- a/libgo/go/net/dnsmsg_test.go +++ b/libgo/go/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/libgo/go/net/dnsname_test.go b/libgo/go/net/dnsname_test.go index 0c1a6251899..70df693f789 100644 --- a/libgo/go/net/dnsname_test.go +++ b/libgo/go/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/libgo/go/net/fd.go b/libgo/go/net/fd.go index cd1a21dc361..707dccaa421 100644 --- a/libgo/go/net/fd.go +++ b/libgo/go/net/fd.go @@ -585,20 +585,25 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os. fd.incref() defer fd.decref() + if fd.rdeadline_delta > 0 { + fd.rdeadline = pollserver.Now() + fd.rdeadline_delta + } else { + fd.rdeadline = 0 + } // See ../syscall/exec.go for description of ForkLock. // It is okay to hold the lock across syscall.Accept // because we have put fd.sysfd into non-blocking mode. syscall.ForkLock.RLock() var s, e int - var sa syscall.Sockaddr + var rsa syscall.Sockaddr for { if fd.closing { syscall.ForkLock.RUnlock() return nil, os.EINVAL } - s, sa, e = syscall.Accept(fd.sysfd) - if e != syscall.EAGAIN { + s, rsa, e = syscall.Accept(fd.sysfd) + if e != syscall.EAGAIN || fd.rdeadline < 0 { break } syscall.ForkLock.RUnlock() @@ -616,7 +621,8 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os. syscall.Close(s) return nil, err } - nfd.setAddr(fd.laddr, toAddr(sa)) + lsa, _ := syscall.Getsockname(nfd.sysfd) + nfd.setAddr(toAddr(lsa), toAddr(rsa)) return nfd, nil } diff --git a/libgo/go/net/fd_linux.go b/libgo/go/net/fd_linux.go index dcf65c014d5..70fc344b2a0 100644 --- a/libgo/go/net/fd_linux.go +++ b/libgo/go/net/fd_linux.go @@ -117,6 +117,17 @@ func (p *pollster) DelFD(fd int, mode int) { } else { p.StopWaiting(fd, writeFlags) } + + // Discard any queued up events. + i := 0 + for i < len(p.waitEvents) { + if fd == int(p.waitEvents[i].Fd) { + copy(p.waitEvents[i:], p.waitEvents[i+1:]) + p.waitEvents = p.waitEvents[:len(p.waitEvents)-1] + } else { + i++ + } + } } func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err os.Error) { diff --git a/libgo/go/net/fd_openbsd.go b/libgo/go/net/fd_openbsd.go new file mode 100644 index 00000000000..e50883e940b --- /dev/null +++ b/libgo/go/net/fd_openbsd.go @@ -0,0 +1,116 @@ +// 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. + +// Waiting for FDs via kqueue/kevent. + +package net + +import ( + "os" + "syscall" +) + +type pollster struct { + kq int + eventbuf [10]syscall.Kevent_t + events []syscall.Kevent_t + + // An event buffer for AddFD/DelFD. + // Must hold pollServer lock. + kbuf [1]syscall.Kevent_t +} + +func newpollster() (p *pollster, err os.Error) { + p = new(pollster) + var e int + if p.kq, e = syscall.Kqueue(); e != 0 { + return nil, os.NewSyscallError("kqueue", e) + } + p.events = p.eventbuf[0:0] + return p, nil +} + +func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, os.Error) { + // pollServer is locked. + + var kmode int + if mode == 'r' { + kmode = syscall.EVFILT_READ + } else { + kmode = syscall.EVFILT_WRITE + } + ev := &p.kbuf[0] + // EV_ADD - add event to kqueue list + // EV_ONESHOT - delete the event the first time it triggers + flags := syscall.EV_ADD + if !repeat { + flags |= syscall.EV_ONESHOT + } + syscall.SetKevent(ev, fd, kmode, flags) + + n, e := syscall.Kevent(p.kq, p.kbuf[:], nil, nil) + if e != 0 { + 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.NewSyscallError("kqueue phase error", e) + } + if ev.Data != 0 { + return false, os.Errno(int(ev.Data)) + } + return false, nil +} + +func (p *pollster) DelFD(fd int, mode int) { + // pollServer is locked. + + var kmode int + if mode == 'r' { + kmode = syscall.EVFILT_READ + } else { + kmode = syscall.EVFILT_WRITE + } + ev := &p.kbuf[0] + // EV_DELETE - delete event from kqueue list + syscall.SetKevent(ev, fd, kmode, syscall.EV_DELETE) + syscall.Kevent(p.kq, p.kbuf[:], nil, nil) +} + +func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err os.Error) { + var t *syscall.Timespec + for len(p.events) == 0 { + if nsec > 0 { + if t == nil { + t = new(syscall.Timespec) + } + *t = syscall.NsecToTimespec(nsec) + } + + s.Unlock() + nn, e := syscall.Kevent(p.kq, nil, p.eventbuf[:], t) + s.Lock() + + if e != 0 { + if e == syscall.EINTR { + continue + } + return -1, 0, os.NewSyscallError("kevent", e) + } + if nn == 0 { + return -1, 0, nil + } + p.events = p.eventbuf[0:nn] + } + ev := &p.events[0] + p.events = p.events[1:] + fd = int(ev.Ident) + if ev.Filter == syscall.EVFILT_READ { + mode = 'r' + } else { + mode = 'w' + } + return fd, mode, nil +} + +func (p *pollster) Close() os.Error { return os.NewSyscallError("close", syscall.Close(p.kq)) } diff --git a/libgo/go/net/fd_windows.go b/libgo/go/net/fd_windows.go index c2f736cc126..3757e143dca 100644 --- a/libgo/go/net/fd_windows.go +++ b/libgo/go/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. @@ -84,11 +84,11 @@ func (o *bufOp) Init(fd *netFD, buf []byte) { } } -// resultSrv will retreive all io completion results from +// resultSrv will retrieve all io completion results from // 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() { @@ -113,7 +113,6 @@ func (s *resultSrv) Run() { } } - // ioSrv executes net io requests. type ioSrv struct { submchan chan anOpIface // submit io requests @@ -132,7 +131,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)) } } } @@ -155,7 +154,7 @@ func (s *ioSrv) ExecIO(oi anOpIface, deadline_delta int64) (n int, err os.Error) case 0: // IO completed immediately, but we need to get our completion message anyway. case syscall.ERROR_IO_PENDING: - // IO started, and we have to wait for it's completion. + // IO started, and we have to wait for its completion. default: return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, os.Errno(e)} } @@ -189,7 +188,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 +208,7 @@ type netFD struct { closing bool // immutable until Close - sysfd int + sysfd syscall.Handle family int proto int net string @@ -225,7 +224,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 +235,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 @@ -273,14 +272,14 @@ func (fd *netFD) incref() { func (fd *netFD) decref() { fd.sysmu.Lock() fd.sysref-- - if fd.closing && fd.sysref == 0 && fd.sysfd >= 0 { + if fd.closing && fd.sysref == 0 && fd.sysfd != syscall.InvalidHandle { // In case the user has set linger, switch to blocking mode so // the close blocks. As long as this doesn't happen often, we // can handle the extra OS processes. Otherwise we'll need to // 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 +287,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 +306,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 +321,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 @@ -338,13 +337,13 @@ func (fd *netFD) Read(buf []byte) (n int, err os.Error) { type readFromOp struct { bufOp - rsa syscall.RawSockaddrAny + rsa syscall.RawSockaddrAny + rsan int32 } 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, &o.rsan, &o.o, nil) } func (o *readFromOp) Name() string { @@ -362,12 +361,16 @@ 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 o.Init(fd, buf) + o.rsan = int32(unsafe.Sizeof(o.rsa)) n, err = iosrv.ExecIO(&o, fd.rdeadline_delta) + if err != nil { + return 0, nil, err + } sa, _ = o.rsa.Sockaddr() return } @@ -380,7 +383,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 +398,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 +415,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 +433,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 +446,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 +462,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 +481,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 +496,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.Setsockopt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, (*byte)(unsafe.Pointer(&fd.sysfd)), int32(unsafe.Sizeof(fd.sysfd))) if e != 0 { closesocket(s) return nil, err @@ -513,7 +516,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os. return nfd, nil } -// Not implemeted functions. +// Unimplemented functions. func (fd *netFD) dup() (f *os.File, err os.Error) { // TODO: Implement this diff --git a/libgo/go/net/file_plan9.go b/libgo/go/net/file_plan9.go new file mode 100644 index 00000000000..a07e7433181 --- /dev/null +++ b/libgo/go/net/file_plan9.go @@ -0,0 +1,33 @@ +// 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" +) + +// FileConn returns a copy of the network connection corresponding to +// the open file f. It is the caller's responsibility to close f when +// finished. Closing c does not affect f, and closing f does not +// affect c. +func FileConn(f *os.File) (c Conn, err os.Error) { + return nil, os.EPLAN9 +} + +// FileListener returns a copy of the network listener corresponding +// to the open file f. It is the caller's responsibility to close l +// when finished. Closing c does not affect l, and closing l does not +// affect c. +func FileListener(f *os.File) (l Listener, err os.Error) { + return nil, os.EPLAN9 +} + +// FilePacketConn returns a copy of the packet network connection +// corresponding to the open file f. It is the caller's +// responsibility to close f when finished. Closing c does not affect +// f, and closing f does not affect c. +func FilePacketConn(f *os.File) (c PacketConn, err os.Error) { + return nil, os.EPLAN9 +} diff --git a/libgo/go/net/file_test.go b/libgo/go/net/file_test.go index 1ec05fdeea5..9a8c2dcbc4c 100644 --- a/libgo/go/net/file_test.go +++ b/libgo/go/net/file_test.go @@ -57,12 +57,12 @@ func testFileListener(t *testing.T, net, laddr string) { } func TestFileListener(t *testing.T) { - if runtime.GOOS == "windows" { + if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { return } testFileListener(t, "tcp", "127.0.0.1") testFileListener(t, "tcp", "127.0.0.1") - if kernelSupportsIPv6() { + if supportsIPv6 && supportsIPv4map { testFileListener(t, "tcp", "[::ffff:127.0.0.1]") testFileListener(t, "tcp", "127.0.0.1") testFileListener(t, "tcp", "[::ffff:127.0.0.1]") @@ -116,13 +116,15 @@ func testFilePacketConnDial(t *testing.T, net, raddr string) { } func TestFilePacketConn(t *testing.T) { - if runtime.GOOS == "windows" { + if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { return } testFilePacketConnListen(t, "udp", "127.0.0.1:0") testFilePacketConnDial(t, "udp", "127.0.0.1:12345") - if kernelSupportsIPv6() { + if supportsIPv6 { testFilePacketConnListen(t, "udp", "[::1]:0") + } + if supportsIPv6 && supportsIPv4map { testFilePacketConnDial(t, "udp", "[::ffff:127.0.0.1]:12345") } if syscall.OS == "linux" { diff --git a/libgo/go/net/hosts_test.go b/libgo/go/net/hosts_test.go index e5793eef2c7..1bd00541c6d 100644 --- a/libgo/go/net/hosts_test.go +++ b/libgo/go/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/libgo/go/net/interface.go b/libgo/go/net/interface.go new file mode 100644 index 00000000000..8a14cb23202 --- /dev/null +++ b/libgo/go/net/interface.go @@ -0,0 +1,132 @@ +// 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 + +package net + +import ( + "bytes" + "fmt" + "os" +) + +// A HardwareAddr represents a physical hardware address. +type HardwareAddr []byte + +func (a HardwareAddr) String() string { + var buf bytes.Buffer + for i, b := range a { + if i > 0 { + buf.WriteByte(':') + } + fmt.Fprintf(&buf, "%02x", b) + } + return buf.String() +} + +// Interface represents a mapping between network interface name +// and index. It also represents network interface facility +// information. +type Interface struct { + Index int // positive integer that starts at one, zero is never used + MTU int // maximum transmission unit + Name string // e.g., "en0", "lo0", "eth0.100" + HardwareAddr HardwareAddr // IEEE MAC-48, EUI-48 and EUI-64 form + 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. +func (ifi *Interface) Addrs() ([]Addr, os.Error) { + if ifi == nil { + return nil, os.NewError("net: invalid interface") + } + return interfaceAddrTable(ifi.Index) +} + +// MulticastAddrs returns multicast, joined group addresses for +// a specific interface. +func (ifi *Interface) MulticastAddrs() ([]Addr, os.Error) { + if ifi == nil { + return nil, os.NewError("net: invalid interface") + } + return interfaceMulticastAddrTable(ifi.Index) +} + +// Interfaces returns a list of the systems's network interfaces. +func Interfaces() ([]Interface, os.Error) { + return interfaceTable(0) +} + +// InterfaceAddrs returns a list of the system's network interface +// addresses. +func InterfaceAddrs() ([]Addr, os.Error) { + return interfaceAddrTable(0) +} + +// InterfaceByIndex returns the interface specified by index. +func InterfaceByIndex(index int) (*Interface, os.Error) { + if index <= 0 { + return nil, os.NewError("net: invalid interface index") + } + ift, err := interfaceTable(index) + if err != nil { + return nil, err + } + for _, ifi := range ift { + return &ifi, nil + } + return nil, os.NewError("net: no such interface") +} + +// InterfaceByName returns the interface specified by name. +func InterfaceByName(name string) (*Interface, os.Error) { + if name == "" { + return nil, os.NewError("net: invalid interface name") + } + ift, err := interfaceTable(0) + if err != nil { + return nil, err + } + for _, ifi := range ift { + if name == ifi.Name { + return &ifi, nil + } + } + return nil, os.NewError("net: no such interface") +} diff --git a/libgo/go/net/interface_bsd.go b/libgo/go/net/interface_bsd.go new file mode 100644 index 00000000000..2675f94b973 --- /dev/null +++ b/libgo/go/net/interface_bsd.go @@ -0,0 +1,171 @@ +// 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 BSD variants + +package net + +import ( + "os" + "syscall" + "unsafe" +) + +// 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) { + var ( + tab []byte + e int + msgs []syscall.RoutingMessage + ift []Interface + ) + + tab, e = syscall.RouteRIB(syscall.NET_RT_IFLIST, ifindex) + if e != 0 { + return nil, os.NewSyscallError("route rib", e) + } + + msgs, e = syscall.ParseRoutingMessage(tab) + if e != 0 { + return nil, os.NewSyscallError("route message", e) + } + + for _, m := range msgs { + switch v := m.(type) { + case *syscall.InterfaceMessage: + if ifindex == 0 || ifindex == int(v.Header.Index) { + ifi, err := newLink(v) + if err != nil { + return nil, err + } + ift = append(ift, ifi...) + } + } + } + + return ift, nil +} + +func newLink(m *syscall.InterfaceMessage) ([]Interface, os.Error) { + var ift []Interface + + sas, e := syscall.ParseRoutingSockaddr(m) + if e != 0 { + return nil, os.NewSyscallError("route sockaddr", e) + } + + for _, s := range sas { + switch v := s.(type) { + case *syscall.SockaddrDatalink: + // 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), Flags: linkFlags(m.Header.Flags)} + var name [syscall.IFNAMSIZ]byte + for i := 0; i < int(v.Nlen); i++ { + name[i] = byte(m.Data[i]) + } + ifi.Name = string(name[:v.Nlen]) + ifi.MTU = int(m.Header.Data.Mtu) + addr := make([]byte, v.Alen) + for i := 0; i < int(v.Alen); i++ { + addr[i] = byte(m.Data[int(v.Nlen)+i]) + } + ifi.HardwareAddr = addr[:v.Alen] + ift = append(ift, ifi) + } + } + + 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. +func interfaceAddrTable(ifindex int) ([]Addr, os.Error) { + var ( + tab []byte + e int + msgs []syscall.RoutingMessage + ifat []Addr + ) + + tab, e = syscall.RouteRIB(syscall.NET_RT_IFLIST, ifindex) + if e != 0 { + return nil, os.NewSyscallError("route rib", e) + } + + msgs, e = syscall.ParseRoutingMessage(tab) + if e != 0 { + return nil, os.NewSyscallError("route message", e) + } + + for _, m := range msgs { + switch v := m.(type) { + case *syscall.InterfaceAddrMessage: + if ifindex == 0 || ifindex == int(v.Header.Index) { + ifa, err := newAddr(v) + if err != nil { + return nil, err + } + ifat = append(ifat, ifa...) + } + } + } + + return ifat, nil +} + +func newAddr(m *syscall.InterfaceAddrMessage) ([]Addr, os.Error) { + var ifat []Addr + + sas, e := syscall.ParseRoutingSockaddr(m) + if e != 0 { + return nil, os.NewSyscallError("route sockaddr", e) + } + + for _, s := range sas { + + switch v := s.(type) { + case *syscall.SockaddrInet4: + ifa := &IPAddr{IP: IPv4(v.Addr[0], v.Addr[1], v.Addr[2], v.Addr[3])} + ifat = append(ifat, ifa.toAddr()) + case *syscall.SockaddrInet6: + ifa := &IPAddr{IP: make(IP, IPv6len)} + copy(ifa.IP, v.Addr[:]) + // NOTE: KAME based IPv6 protcol stack usually embeds + // the interface index in the interface-local or link- + // local address as the kernel-internal form. + if ifa.IP.IsLinkLocalUnicast() { + // remove embedded scope zone ID + ifa.IP[2], ifa.IP[3] = 0, 0 + } + ifat = append(ifat, ifa.toAddr()) + } + } + + return ifat, nil +} diff --git a/libgo/go/net/interface_darwin.go b/libgo/go/net/interface_darwin.go new file mode 100644 index 00000000000..a7b68ad7f77 --- /dev/null +++ b/libgo/go/net/interface_darwin.go @@ -0,0 +1,80 @@ +// 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 Darwin + +package net + +import ( + "os" + "syscall" +) + +// If the ifindex is zero, interfaceMulticastAddrTable returns +// addresses for all network interfaces. Otherwise it returns +// addresses for a specific interface. +func interfaceMulticastAddrTable(ifindex int) ([]Addr, os.Error) { + var ( + tab []byte + e int + msgs []syscall.RoutingMessage + ifmat []Addr + ) + + tab, e = syscall.RouteRIB(syscall.NET_RT_IFLIST2, ifindex) + if e != 0 { + return nil, os.NewSyscallError("route rib", e) + } + + msgs, e = syscall.ParseRoutingMessage(tab) + if e != 0 { + return nil, os.NewSyscallError("route message", e) + } + + for _, m := range msgs { + switch v := m.(type) { + case *syscall.InterfaceMulticastAddrMessage: + if ifindex == 0 || ifindex == int(v.Header.Index) { + ifma, err := newMulticastAddr(v) + if err != nil { + return nil, err + } + ifmat = append(ifmat, ifma...) + } + } + } + + return ifmat, nil +} + +func newMulticastAddr(m *syscall.InterfaceMulticastAddrMessage) ([]Addr, os.Error) { + var ifmat []Addr + + sas, e := syscall.ParseRoutingSockaddr(m) + if e != 0 { + return nil, os.NewSyscallError("route sockaddr", e) + } + + for _, s := range sas { + switch v := s.(type) { + case *syscall.SockaddrInet4: + ifma := &IPAddr{IP: IPv4(v.Addr[0], v.Addr[1], v.Addr[2], v.Addr[3])} + ifmat = append(ifmat, ifma.toAddr()) + case *syscall.SockaddrInet6: + ifma := &IPAddr{IP: make(IP, IPv6len)} + copy(ifma.IP, v.Addr[:]) + // NOTE: KAME based IPv6 protcol stack usually embeds + // the interface index in the interface-local or link- + // local address as the kernel-internal form. + if ifma.IP.IsInterfaceLocalMulticast() || + ifma.IP.IsLinkLocalMulticast() { + // remove embedded scope zone ID + ifma.IP[2], ifma.IP[3] = 0, 0 + } + ifmat = append(ifmat, ifma.toAddr()) + } + } + + return ifmat, nil +} diff --git a/libgo/go/net/interface_freebsd.go b/libgo/go/net/interface_freebsd.go new file mode 100644 index 00000000000..20f506b08bc --- /dev/null +++ b/libgo/go/net/interface_freebsd.go @@ -0,0 +1,80 @@ +// 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 FreeBSD + +package net + +import ( + "os" + "syscall" +) + +// If the ifindex is zero, interfaceMulticastAddrTable returns +// addresses for all network interfaces. Otherwise it returns +// addresses for a specific interface. +func interfaceMulticastAddrTable(ifindex int) ([]Addr, os.Error) { + var ( + tab []byte + e int + msgs []syscall.RoutingMessage + ifmat []Addr + ) + + tab, e = syscall.RouteRIB(syscall.NET_RT_IFMALIST, ifindex) + if e != 0 { + return nil, os.NewSyscallError("route rib", e) + } + + msgs, e = syscall.ParseRoutingMessage(tab) + if e != 0 { + return nil, os.NewSyscallError("route message", e) + } + + for _, m := range msgs { + switch v := m.(type) { + case *syscall.InterfaceMulticastAddrMessage: + if ifindex == 0 || ifindex == int(v.Header.Index) { + ifma, err := newMulticastAddr(v) + if err != nil { + return nil, err + } + ifmat = append(ifmat, ifma...) + } + } + } + + return ifmat, nil +} + +func newMulticastAddr(m *syscall.InterfaceMulticastAddrMessage) ([]Addr, os.Error) { + var ifmat []Addr + + sas, e := syscall.ParseRoutingSockaddr(m) + if e != 0 { + return nil, os.NewSyscallError("route sockaddr", e) + } + + for _, s := range sas { + switch v := s.(type) { + case *syscall.SockaddrInet4: + ifma := &IPAddr{IP: IPv4(v.Addr[0], v.Addr[1], v.Addr[2], v.Addr[3])} + ifmat = append(ifmat, ifma.toAddr()) + case *syscall.SockaddrInet6: + ifma := &IPAddr{IP: make(IP, IPv6len)} + copy(ifma.IP, v.Addr[:]) + // NOTE: KAME based IPv6 protcol stack usually embeds + // the interface index in the interface-local or link- + // local address as the kernel-internal form. + if ifma.IP.IsInterfaceLocalMulticast() || + ifma.IP.IsLinkLocalMulticast() { + // remove embedded scope zone ID + ifma.IP[2], ifma.IP[3] = 0, 0 + } + ifmat = append(ifmat, ifma.toAddr()) + } + } + + return ifmat, nil +} diff --git a/libgo/go/net/interface_linux.go b/libgo/go/net/interface_linux.go new file mode 100644 index 00000000000..3d2a0bb9f8a --- /dev/null +++ b/libgo/go/net/interface_linux.go @@ -0,0 +1,262 @@ +// 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 Linux + +package net + +import ( + "fmt" + "os" + "syscall" + "unsafe" +) + +// 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) { + var ( + ift []Interface + tab []byte + msgs []syscall.NetlinkMessage + e int + ) + + tab, e = syscall.NetlinkRIB(syscall.RTM_GETLINK, syscall.AF_UNSPEC) + if e != 0 { + return nil, os.NewSyscallError("netlink rib", e) + } + + msgs, e = syscall.ParseNetlinkMessage(tab) + if e != 0 { + return nil, os.NewSyscallError("netlink message", e) + } + + for _, m := range msgs { + switch m.Header.Type { + case syscall.NLMSG_DONE: + goto done + case syscall.RTM_NEWLINK: + ifim := (*syscall.IfInfomsg)(unsafe.Pointer(&m.Data[0])) + if ifindex == 0 || ifindex == int(ifim.Index) { + attrs, e := syscall.ParseNetlinkRouteAttr(&m) + if e != 0 { + return nil, os.NewSyscallError("netlink routeattr", e) + } + ifi := newLink(attrs, ifim) + ift = append(ift, ifi) + } + } + } + +done: + return ift, nil +} + +func newLink(attrs []syscall.NetlinkRouteAttr, ifim *syscall.IfInfomsg) Interface { + ifi := Interface{Index: int(ifim.Index), Flags: linkFlags(ifim.Flags)} + for _, a := range attrs { + switch a.Attr.Type { + case syscall.IFLA_ADDRESS: + var nonzero bool + for _, b := range a.Value { + if b != 0 { + nonzero = true + } + } + if nonzero { + ifi.HardwareAddr = a.Value[:] + } + case syscall.IFLA_IFNAME: + 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])) + } + } + 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. +func interfaceAddrTable(ifindex int) ([]Addr, os.Error) { + var ( + tab []byte + e int + err os.Error + ifat4 []Addr + ifat6 []Addr + msgs4 []syscall.NetlinkMessage + msgs6 []syscall.NetlinkMessage + ) + + tab, e = syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_INET) + if e != 0 { + return nil, os.NewSyscallError("netlink rib", e) + } + msgs4, e = syscall.ParseNetlinkMessage(tab) + if e != 0 { + return nil, os.NewSyscallError("netlink message", e) + } + ifat4, err = addrTable(msgs4, ifindex) + if err != nil { + return nil, err + } + + tab, e = syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_INET6) + if e != 0 { + return nil, os.NewSyscallError("netlink rib", e) + } + msgs6, e = syscall.ParseNetlinkMessage(tab) + if e != 0 { + return nil, os.NewSyscallError("netlink message", e) + } + ifat6, err = addrTable(msgs6, ifindex) + if err != nil { + return nil, err + } + + return append(ifat4, ifat6...), nil +} + +func addrTable(msgs []syscall.NetlinkMessage, ifindex int) ([]Addr, os.Error) { + var ifat []Addr + + for _, m := range msgs { + switch m.Header.Type { + case syscall.NLMSG_DONE: + goto done + case syscall.RTM_NEWADDR: + ifam := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0])) + if ifindex == 0 || ifindex == int(ifam.Index) { + attrs, e := syscall.ParseNetlinkRouteAttr(&m) + if e != 0 { + return nil, os.NewSyscallError("netlink routeattr", e) + } + ifat = append(ifat, newAddr(attrs, int(ifam.Family))...) + } + } + } + +done: + return ifat, nil +} + +func newAddr(attrs []syscall.NetlinkRouteAttr, family int) []Addr { + var ifat []Addr + + for _, a := range attrs { + switch a.Attr.Type { + case syscall.IFA_ADDRESS: + switch family { + case syscall.AF_INET: + ifa := &IPAddr{IP: IPv4(a.Value[0], a.Value[1], a.Value[2], a.Value[3])} + ifat = append(ifat, ifa.toAddr()) + case syscall.AF_INET6: + ifa := &IPAddr{IP: make(IP, IPv6len)} + copy(ifa.IP, a.Value[:]) + ifat = append(ifat, ifa.toAddr()) + } + } + } + + return ifat +} + +// If the ifindex is zero, interfaceMulticastAddrTable returns +// addresses for all network interfaces. Otherwise it returns +// addresses for a specific interface. +func interfaceMulticastAddrTable(ifindex int) ([]Addr, os.Error) { + var ( + ifi *Interface + err os.Error + ) + + if ifindex > 0 { + ifi, err = InterfaceByIndex(ifindex) + if err != nil { + return nil, err + } + } + + ifmat4 := parseProcNetIGMP(ifi) + ifmat6 := parseProcNetIGMP6(ifi) + + return append(ifmat4, ifmat6...), nil +} + +func parseProcNetIGMP(ifi *Interface) []Addr { + var ( + ifmat []Addr + name string + ) + + fd, err := open("/proc/net/igmp") + if err != nil { + return nil + } + defer fd.close() + + fd.readLine() // skip first line + b := make([]byte, IPv4len) + for l, ok := fd.readLine(); ok; l, ok = fd.readLine() { + f := getFields(l) + switch len(f) { + case 4: + if ifi == nil || name == ifi.Name { + fmt.Sscanf(f[0], "%08x", &b) + ifma := IPAddr{IP: IPv4(b[3], b[2], b[1], b[0])} + ifmat = append(ifmat, ifma.toAddr()) + } + case 5: + name = f[1] + } + } + + return ifmat +} + +func parseProcNetIGMP6(ifi *Interface) []Addr { + var ifmat []Addr + + fd, err := open("/proc/net/igmp6") + if err != nil { + return nil + } + defer fd.close() + + b := make([]byte, IPv6len) + for l, ok := fd.readLine(); ok; l, ok = fd.readLine() { + f := getFields(l) + if ifi == nil || f[1] == ifi.Name { + fmt.Sscanf(f[2], "%32x", &b) + ifma := IPAddr{IP: IP{b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]}} + ifmat = append(ifmat, ifma.toAddr()) + + } + } + + return ifmat +} diff --git a/libgo/go/net/interface_openbsd.go b/libgo/go/net/interface_openbsd.go new file mode 100644 index 00000000000..f18149393a5 --- /dev/null +++ b/libgo/go/net/interface_openbsd.go @@ -0,0 +1,16 @@ +// 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 OpenBSD + +package net + +import "os" + +// If the ifindex is zero, interfaceMulticastAddrTable returns +// addresses for all network interfaces. Otherwise it returns +// addresses for a specific interface. +func interfaceMulticastAddrTable(ifindex int) ([]Addr, os.Error) { + return nil, nil +} diff --git a/libgo/go/net/interface_stub.go b/libgo/go/net/interface_stub.go new file mode 100644 index 00000000000..950de6c5926 --- /dev/null +++ b/libgo/go/net/interface_stub.go @@ -0,0 +1,30 @@ +// 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 + +package net + +import "os" + +// 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) { + return nil, 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) { + return nil, nil +} + +// If the ifindex is zero, interfaceMulticastAddrTable returns +// addresses for all network interfaces. Otherwise it returns +// addresses for a specific interface. +func interfaceMulticastAddrTable(ifindex int) ([]Addr, os.Error) { + return nil, nil +} diff --git a/libgo/go/net/interface_test.go b/libgo/go/net/interface_test.go new file mode 100644 index 00000000000..0e4089abf8a --- /dev/null +++ b/libgo/go/net/interface_test.go @@ -0,0 +1,73 @@ +// 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 ( + "bytes" + "testing" +) + +func sameInterface(i, j *Interface) bool { + if i == nil || j == nil { + return false + } + if i.Index == j.Index && i.Name == j.Name && bytes.Equal(i.HardwareAddr, j.HardwareAddr) { + return true + } + return false +} + +func TestInterfaces(t *testing.T) { + ift, err := Interfaces() + if err != nil { + t.Fatalf("Interfaces() failed: %v", err) + } + t.Logf("table: len/cap = %v/%v\n", len(ift), cap(ift)) + + for _, ifi := range ift { + ifxi, err := InterfaceByIndex(ifi.Index) + if err != nil { + t.Fatalf("InterfaceByIndex(%#q) failed: %v", ifi.Index, err) + } + if !sameInterface(ifxi, &ifi) { + t.Fatalf("InterfaceByIndex(%#q) = %v, want %v", ifi.Index, *ifxi, ifi) + } + ifxn, err := InterfaceByName(ifi.Name) + if err != nil { + t.Fatalf("InterfaceByName(%#q) failed: %v", ifi.Name, err) + } + if !sameInterface(ifxn, &ifi) { + t.Fatalf("InterfaceByName(%#q) = %v, want %v", ifi.Name, *ifxn, ifi) + } + ifat, err := ifi.Addrs() + if err != nil { + t.Fatalf("Interface.Addrs() failed: %v", err) + } + ifmat, err := ifi.MulticastAddrs() + if err != nil { + t.Fatalf("Interface.MulticastAddrs() failed: %v", err) + } + 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 %q\n", ifa.String()) + } + for _, ifma := range ifmat { + t.Logf("\tjoined group address %q\n", ifma.String()) + } + t.Logf("\thardware address %q", ifi.HardwareAddr.String()) + } +} + +func TestInterfaceAddrs(t *testing.T) { + ifat, err := InterfaceAddrs() + if err != nil { + t.Fatalf("InterfaceAddrs() failed: %v", err) + } + t.Logf("table: len/cap = %v/%v\n", len(ifat), cap(ifat)) + + for _, ifa := range ifat { + t.Logf("interface address %q\n", ifa.String()) + } +} diff --git a/libgo/go/net/interface_windows.go b/libgo/go/net/interface_windows.go new file mode 100644 index 00000000000..7f5169c8797 --- /dev/null +++ b/libgo/go/net/interface_windows.go @@ -0,0 +1,158 @@ +// 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 +} + +// If the ifindex is zero, interfaceMulticastAddrTable returns +// addresses for all network interfaces. Otherwise it returns +// addresses for a specific interface. +func interfaceMulticastAddrTable(ifindex int) ([]Addr, os.Error) { + return nil, nil +} diff --git a/libgo/go/net/ip.go b/libgo/go/net/ip.go index 61b2c687e2f..b0e2c42053f 100644 --- a/libgo/go/net/ip.go +++ b/libgo/go/net/ip.go @@ -75,10 +75,71 @@ var ( // Well-known IPv6 addresses var ( - IPzero = make(IP, IPv6len) // all zeros - IPv6loopback = IP([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}) + IPv6zero = IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + IPv6unspecified = IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + IPv6loopback = IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1} + IPv6interfacelocalallnodes = IP{0xff, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01} + IPv6linklocalallnodes = IP{0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01} + IPv6linklocalallrouters = IP{0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02} ) +// IsUnspecified returns true if ip is an unspecified address. +func (ip IP) IsUnspecified() bool { + if ip.Equal(IPv4zero) || ip.Equal(IPv6unspecified) { + return true + } + return false +} + +// IsLoopback returns true if ip is a loopback address. +func (ip IP) IsLoopback() bool { + if ip4 := ip.To4(); ip4 != nil && ip4[0] == 127 { + return true + } + return ip.Equal(IPv6loopback) +} + +// IsMulticast returns true if ip is a multicast address. +func (ip IP) IsMulticast() bool { + if ip4 := ip.To4(); ip4 != nil && ip4[0]&0xf0 == 0xe0 { + return true + } + return ip[0] == 0xff +} + +// IsInterfaceLinkLocalMulticast returns true if ip is +// an interface-local multicast address. +func (ip IP) IsInterfaceLocalMulticast() bool { + return len(ip) == IPv6len && ip[0] == 0xff && ip[1]&0x0f == 0x01 +} + +// IsLinkLocalMulticast returns true if ip is a link-local +// multicast address. +func (ip IP) IsLinkLocalMulticast() bool { + if ip4 := ip.To4(); ip4 != nil && ip4[0] == 224 && ip4[1] == 0 && ip4[2] == 0 { + return true + } + return ip[0] == 0xff && ip[1]&0x0f == 0x02 +} + +// IsLinkLocalUnicast returns true if ip is a link-local +// unicast address. +func (ip IP) IsLinkLocalUnicast() bool { + if ip4 := ip.To4(); ip4 != nil && ip4[0] == 169 && ip4[1] == 254 { + return true + } + return ip[0] == 0xfe && ip[1]&0xc0 == 0x80 +} + +// IsGlobalUnicast returns true if ip is a global unicast +// address. +func (ip IP) IsGlobalUnicast() bool { + return !ip.IsUnspecified() && + !ip.IsLoopback() && + !ip.IsMulticast() && + !ip.IsLinkLocalUnicast() +} + // Is p all zeros? func isZeros(p IP) bool { for i := 0; i < len(p); i++ { diff --git a/libgo/go/net/ip_test.go b/libgo/go/net/ip_test.go index 2008953ef38..b189b10c4fd 100644 --- a/libgo/go/net/ip_test.go +++ b/libgo/go/net/ip_test.go @@ -9,6 +9,7 @@ import ( "reflect" "testing" "os" + "runtime" ) func isEqual(a, b []byte) bool { @@ -31,11 +32,7 @@ var parseiptests = []struct { {"abc", nil}, {"123:", nil}, {"::ffff:127.0.0.1", IPv4(127, 0, 0, 1)}, - {"2001:4860:0:2001::68", - IP{0x20, 0x01, 0x48, 0x60, 0, 0, 0x20, 0x01, - 0, 0, 0, 0, 0, 0, 0x00, 0x68, - }, - }, + {"2001:4860:0:2001::68", IP{0x20, 0x01, 0x48, 0x60, 0, 0, 0x20, 0x01, 0, 0, 0, 0, 0, 0, 0x00, 0x68}}, {"::ffff:4a7d:1363", IPv4(74, 125, 19, 99)}, } @@ -52,29 +49,21 @@ var ipstringtests = []struct { out string }{ // 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}, + {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0x1, 0x23, 0, 0x12, 0, 0x1}, "2001:db8::123:12:1"}, - {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0x1}, + {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1}, "2001:db8::1"}, - {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0x1, - 0, 0, 0, 0x1, 0, 0, 0, 0x1}, + {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1}, "2001:db8:0:1:0:1:0:1"}, - {IP{0x20, 0x1, 0xd, 0xb8, 0, 0x1, 0, 0, - 0, 0x1, 0, 0, 0, 0x1, 0, 0}, + {IP{0x20, 0x1, 0xd, 0xb8, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0}, "2001:db8:1:0:1:0:1:0"}, - {IP{0x20, 0x1, 0, 0, 0, 0, 0, 0, - 0, 0x1, 0, 0, 0, 0, 0, 0x1}, + {IP{0x20, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0x1}, "2001::1:0:0:1"}, - {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, - 0, 0x1, 0, 0, 0, 0, 0, 0}, + {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0}, "2001:db8:0:0:1::"}, - {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, - 0, 0x1, 0, 0, 0, 0, 0, 0x1}, + {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0x1}, "2001:db8::1:0:0:1"}, - {IP{0x20, 0x1, 0xD, 0xB8, 0, 0, 0, 0, - 0, 0xA, 0, 0xB, 0, 0xC, 0, 0xD}, + {IP{0x20, 0x1, 0xD, 0xB8, 0, 0, 0, 0, 0, 0xA, 0, 0xB, 0, 0xC, 0, 0xD}, "2001:db8::a:b:c:d"}, } @@ -143,3 +132,84 @@ func TestJoinHostPort(t *testing.T) { } } } + +var ipaftests = []struct { + in IP + af4 bool + af6 bool +}{ + {IPv4bcast, true, false}, + {IPv4allsys, true, false}, + {IPv4allrouter, true, false}, + {IPv4zero, true, false}, + {IPv4(224, 0, 0, 1), true, false}, + {IPv4(127, 0, 0, 1), true, false}, + {IPv4(240, 0, 0, 1), true, false}, + {IPv6unspecified, false, true}, + {IPv6loopback, false, true}, + {IPv6interfacelocalallnodes, false, true}, + {IPv6linklocalallnodes, false, true}, + {IPv6linklocalallrouters, false, true}, + {ParseIP("ff05::a:b:c:d"), false, true}, + {ParseIP("fe80::1:2:3:4"), false, true}, + {ParseIP("2001:db8::123:12:1"), false, true}, +} + +func TestIPAddrFamily(t *testing.T) { + for _, tt := range ipaftests { + if af := tt.in.To4() != nil; af != tt.af4 { + t.Errorf("verifying IPv4 address family for %#q = %v, want %v", tt.in, af, tt.af4) + } + if af := len(tt.in) == IPv6len && tt.in.To4() == nil; af != tt.af6 { + t.Errorf("verifying IPv6 address family for %#q = %v, want %v", tt.in, af, tt.af6) + } + } +} + +var ipscopetests = []struct { + scope func(IP) bool + in IP + ok bool +}{ + {IP.IsUnspecified, IPv4zero, true}, + {IP.IsUnspecified, IPv4(127, 0, 0, 1), false}, + {IP.IsUnspecified, IPv6unspecified, true}, + {IP.IsUnspecified, IPv6interfacelocalallnodes, false}, + {IP.IsLoopback, IPv4(127, 0, 0, 1), true}, + {IP.IsLoopback, IPv4(127, 255, 255, 254), true}, + {IP.IsLoopback, IPv4(128, 1, 2, 3), false}, + {IP.IsLoopback, IPv6loopback, true}, + {IP.IsLoopback, IPv6linklocalallrouters, false}, + {IP.IsMulticast, IPv4(224, 0, 0, 0), true}, + {IP.IsMulticast, IPv4(239, 0, 0, 0), true}, + {IP.IsMulticast, IPv4(240, 0, 0, 0), false}, + {IP.IsMulticast, IPv6linklocalallnodes, true}, + {IP.IsMulticast, IP{0xff, 0x05, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, true}, + {IP.IsMulticast, IP{0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false}, + {IP.IsLinkLocalMulticast, IPv4(224, 0, 0, 0), true}, + {IP.IsLinkLocalMulticast, IPv4(239, 0, 0, 0), false}, + {IP.IsLinkLocalMulticast, IPv6linklocalallrouters, true}, + {IP.IsLinkLocalMulticast, IP{0xff, 0x05, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false}, + {IP.IsLinkLocalUnicast, IPv4(169, 254, 0, 0), true}, + {IP.IsLinkLocalUnicast, IPv4(169, 255, 0, 0), false}, + {IP.IsLinkLocalUnicast, IP{0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, true}, + {IP.IsLinkLocalUnicast, IP{0xfe, 0xc0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false}, + {IP.IsGlobalUnicast, IPv4(240, 0, 0, 0), true}, + {IP.IsGlobalUnicast, IPv4(232, 0, 0, 0), false}, + {IP.IsGlobalUnicast, IPv4(169, 254, 0, 0), false}, + {IP.IsGlobalUnicast, IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0x1, 0x23, 0, 0x12, 0, 0x1}, true}, + {IP.IsGlobalUnicast, IP{0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false}, + {IP.IsGlobalUnicast, IP{0xff, 0x05, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false}, +} + +func name(f interface{}) string { + return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name() +} + +func TestIPAddrScope(t *testing.T) { + for _, tt := range ipscopetests { + if ok := tt.scope(tt.in); ok != tt.ok { + t.Errorf("%s(%#q) = %v, want %v", name(tt.scope), tt.in, ok, tt.ok) + } + } +} diff --git a/libgo/go/net/ipraw_test.go b/libgo/go/net/ipraw_test.go index 0c0b675f875..6894ce656dd 100644 --- a/libgo/go/net/ipraw_test.go +++ b/libgo/go/net/ipraw_test.go @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. - // TODO(cw): ListenPacket test, Read() test, ipv6 test & // Dial()/Listen() level tests @@ -75,15 +74,15 @@ func TestICMP(t *testing.T) { err os.Error ) if *srchost != "" { - laddr, err = ResolveIPAddr(*srchost) + laddr, err = ResolveIPAddr("ip4", *srchost) if err != nil { - t.Fatalf(`net.ResolveIPAddr("%v") = %v, %v`, *srchost, laddr, err) + t.Fatalf(`net.ResolveIPAddr("ip4", %v") = %v, %v`, *srchost, laddr, err) } } - raddr, err := ResolveIPAddr(*dsthost) + raddr, err := ResolveIPAddr("ip4", *dsthost) if err != nil { - t.Fatalf(`net.ResolveIPAddr("%v") = %v, %v`, *dsthost, raddr, err) + t.Fatalf(`net.ResolveIPAddr("ip4", %v") = %v, %v`, *dsthost, raddr, err) } c, err := ListenIP("ip4:icmp", laddr) diff --git a/libgo/go/net/iprawsock.go b/libgo/go/net/iprawsock.go index 5be6fe4e0b9..662b9f57bd3 100644 --- a/libgo/go/net/iprawsock.go +++ b/libgo/go/net/iprawsock.go @@ -8,22 +8,8 @@ package net import ( "os" - "sync" - "syscall" ) -var onceReadProtocols sync.Once - -func sockaddrToIP(sa syscall.Sockaddr) Addr { - switch sa := sa.(type) { - case *syscall.SockaddrInet4: - return &IPAddr{sa.Addr[0:]} - case *syscall.SockaddrInet6: - return &IPAddr{sa.Addr[0:]} - } - return nil -} - // IPAddr represents the address of a IP end point. type IPAddr struct { IP IP @@ -39,320 +25,45 @@ func (a *IPAddr) String() string { return a.IP.String() } -func (a *IPAddr) family() int { - if a == nil || len(a.IP) <= 4 { - return syscall.AF_INET - } - if ip := a.IP.To4(); ip != nil { - return syscall.AF_INET - } - return syscall.AF_INET6 -} - -func (a *IPAddr) sockaddr(family int) (syscall.Sockaddr, os.Error) { - return ipToSockaddr(family, a.IP, 0) -} - -func (a *IPAddr) toAddr() sockaddr { - if a == nil { // nil *IPAddr - return nil // nil interface - } - return a -} - // ResolveIPAddr parses addr as a IP address and resolves domain -// names to numeric addresses. A literal IPv6 host address must be +// names to numeric addresses on the network net, which must be +// "ip", "ip4" or "ip6". A literal IPv6 host address must be // enclosed in square brackets, as in "[::]". -func ResolveIPAddr(addr string) (*IPAddr, os.Error) { - ip, err := hostToIP(addr) +func ResolveIPAddr(net, addr string) (*IPAddr, os.Error) { + ip, err := hostToIP(net, addr) if err != nil { return nil, err } return &IPAddr{ip}, nil } -// IPConn is the implementation of the Conn and PacketConn -// interfaces for IP network connections. -type IPConn struct { - fd *netFD -} - -func newIPConn(fd *netFD) *IPConn { return &IPConn{fd} } - -func (c *IPConn) ok() bool { return c != nil && c.fd != nil } - -// Implementation of the Conn interface - see Conn for documentation. - -// Read implements the net.Conn Read method. -func (c *IPConn) Read(b []byte) (n int, err os.Error) { - n, _, err = c.ReadFrom(b) - return -} - -// Write implements the net.Conn Write method. -func (c *IPConn) Write(b []byte) (n int, err os.Error) { - if !c.ok() { - return 0, os.EINVAL - } - return c.fd.Write(b) -} - -// Close closes the IP connection. -func (c *IPConn) Close() os.Error { - if !c.ok() { - return os.EINVAL - } - err := c.fd.Close() - c.fd = nil - return err -} - -// LocalAddr returns the local network address. -func (c *IPConn) LocalAddr() Addr { - if !c.ok() { - return nil - } - return c.fd.laddr -} - -// RemoteAddr returns the remote network address, a *IPAddr. -func (c *IPConn) RemoteAddr() Addr { - if !c.ok() { - return nil - } - return c.fd.raddr -} - -// SetTimeout implements the net.Conn SetTimeout method. -func (c *IPConn) SetTimeout(nsec int64) os.Error { - if !c.ok() { - return os.EINVAL - } - return setTimeout(c.fd, nsec) -} - -// SetReadTimeout implements the net.Conn SetReadTimeout method. -func (c *IPConn) SetReadTimeout(nsec int64) os.Error { - if !c.ok() { - return os.EINVAL - } - return setReadTimeout(c.fd, nsec) -} - -// SetWriteTimeout implements the net.Conn SetWriteTimeout method. -func (c *IPConn) SetWriteTimeout(nsec int64) os.Error { - if !c.ok() { - return os.EINVAL - } - return setWriteTimeout(c.fd, nsec) -} - -// SetReadBuffer sets the size of the operating system's -// receive buffer associated with the connection. -func (c *IPConn) SetReadBuffer(bytes int) os.Error { - if !c.ok() { - return os.EINVAL - } - return setReadBuffer(c.fd, bytes) -} - -// SetWriteBuffer sets the size of the operating system's -// transmit buffer associated with the connection. -func (c *IPConn) SetWriteBuffer(bytes int) os.Error { - if !c.ok() { - return os.EINVAL - } - return setWriteBuffer(c.fd, bytes) -} - -// IP-specific methods. - -// ReadFromIP reads a IP packet from c, copying the payload into b. -// It returns the number of bytes copied into b and the return address -// that was on the packet. -// -// ReadFromIP can be made to time out and return an error with -// Timeout() == true after a fixed time limit; see SetTimeout and -// SetReadTimeout. -func (c *IPConn) ReadFromIP(b []byte) (n int, addr *IPAddr, err os.Error) { - if !c.ok() { - return 0, nil, os.EINVAL - } - // TODO(cw,rsc): consider using readv if we know the family - // type to avoid the header trim/copy - n, sa, err := c.fd.ReadFrom(b) - switch sa := sa.(type) { - case *syscall.SockaddrInet4: - addr = &IPAddr{sa.Addr[0:]} - if len(b) >= 4 { // discard ipv4 header - hsize := (int(b[0]) & 0xf) * 4 - copy(b, b[hsize:]) - n -= hsize - } - case *syscall.SockaddrInet6: - addr = &IPAddr{sa.Addr[0:]} - } - return -} - -// ReadFrom implements the net.PacketConn ReadFrom method. -func (c *IPConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) { - if !c.ok() { - return 0, nil, os.EINVAL - } - n, uaddr, err := c.ReadFromIP(b) - return n, uaddr.toAddr(), err -} - -// WriteToIP writes a IP packet to addr via c, copying the payload from b. -// -// WriteToIP can be made to time out and return -// an error with Timeout() == true after a fixed time limit; -// see SetTimeout and SetWriteTimeout. -// On packet-oriented connections, write timeouts are rare. -func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (n int, err os.Error) { - if !c.ok() { - return 0, os.EINVAL - } - sa, err1 := addr.sockaddr(c.fd.family) - if err1 != nil { - return 0, &OpError{Op: "write", Net: "ip", Addr: addr, Error: err1} - } - return c.fd.WriteTo(b, sa) -} - -// WriteTo implements the net.PacketConn WriteTo method. -func (c *IPConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) { - if !c.ok() { - return 0, os.EINVAL - } - a, ok := addr.(*IPAddr) - if !ok { - return 0, &OpError{"writeto", "ip", addr, os.EINVAL} - } - return c.WriteToIP(b, a) -} - // Convert "host" into IP address. -func hostToIP(host string) (ip IP, err os.Error) { +func hostToIP(net, host string) (ip IP, err os.Error) { var addr IP // Try as an IP address. addr = ParseIP(host) if addr == nil { + filter := anyaddr + if net != "" && net[len(net)-1] == '4' { + filter = ipv4only + } + if net != "" && net[len(net)-1] == '6' { + filter = ipv6only + } // Not an IP address. Try as a DNS name. addrs, err1 := LookupHost(host) if err1 != nil { err = err1 goto Error } - addr = firstSupportedAddr(anyaddr, addrs) + addr = firstFavoriteAddr(filter, addrs) if addr == nil { // should not happen - err = &AddrError{"LookupHost returned invalid address", addrs[0]} + err = &AddrError{"LookupHost returned no suitable address", addrs[0]} goto Error } } - return addr, nil - Error: return nil, err } - - -var protocols map[string]int - -func readProtocols() { - protocols = make(map[string]int) - if file, err := open("/etc/protocols"); err == nil { - for line, ok := file.readLine(); ok; line, ok = file.readLine() { - // tcp 6 TCP # transmission control protocol - if i := byteIndex(line, '#'); i >= 0 { - line = line[0:i] - } - f := getFields(line) - if len(f) < 2 { - continue - } - if proto, _, ok := dtoi(f[1], 0); ok { - protocols[f[0]] = proto - for _, alias := range f[2:] { - protocols[alias] = proto - } - } - } - file.close() - } -} - -func netProtoSplit(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") - } - net = netProto[0:i] - protostr := netProto[i+1:] - proto, i, ok := dtoi(protostr, 0) - if !ok || i != len(protostr) { - // lookup by name - proto, ok = protocols[protostr] - if ok { - return - } - } - return -} - -// DialIP connects to the remote address raddr on the network net, -// which must be "ip", "ip4", or "ip6". -func DialIP(netProto string, laddr, raddr *IPAddr) (c *IPConn, err os.Error) { - net, proto, err := netProtoSplit(netProto) - if err != nil { - return - } - switch net { - case "ip", "ip4", "ip6": - default: - return nil, UnknownNetworkError(net) - } - if raddr == nil { - return nil, &OpError{"dial", "ip", nil, errMissingAddress} - } - fd, e := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_RAW, proto, "dial", sockaddrToIP) - if e != nil { - return nil, e - } - return newIPConn(fd), nil -} - -// ListenIP listens for incoming IP packets addressed to the -// local address laddr. The returned connection c's ReadFrom -// and WriteTo methods can be used to receive and send IP -// packets with per-packet addressing. -func ListenIP(netProto string, laddr *IPAddr) (c *IPConn, err os.Error) { - net, proto, err := netProtoSplit(netProto) - if err != nil { - return - } - switch net { - case "ip", "ip4", "ip6": - default: - return nil, UnknownNetworkError(net) - } - fd, e := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_RAW, proto, "dial", sockaddrToIP) - if e != nil { - return nil, e - } - return newIPConn(fd), nil -} - -// BindToDevice binds an IPConn to a network interface. -func (c *IPConn) BindToDevice(device string) os.Error { - if !c.ok() { - return os.EINVAL - } - c.fd.incref() - defer c.fd.decref() - return os.NewSyscallError("setsockopt", syscall.BindToDevice(c.fd.sysfd, device)) -} diff --git a/libgo/go/net/iprawsock_plan9.go b/libgo/go/net/iprawsock_plan9.go new file mode 100644 index 00000000000..808e17974f8 --- /dev/null +++ b/libgo/go/net/iprawsock_plan9.go @@ -0,0 +1,99 @@ +// Copyright 2010 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. + +// (Raw) IP sockets stubs for Plan 9 + +package net + +import ( + "os" +) + +// IPConn is the implementation of the Conn and PacketConn +// interfaces for IP network connections. +type IPConn bool + +// Implementation of the Conn interface - see Conn for documentation. + +// Read implements the net.Conn Read method. +func (c *IPConn) Read(b []byte) (n int, err os.Error) { + return 0, os.EPLAN9 +} + +// Write implements the net.Conn Write method. +func (c *IPConn) Write(b []byte) (n int, err os.Error) { + return 0, os.EPLAN9 +} + +// Close closes the IP connection. +func (c *IPConn) Close() os.Error { + return os.EPLAN9 +} + +// LocalAddr returns the local network address. +func (c *IPConn) LocalAddr() Addr { + return nil +} + +// RemoteAddr returns the remote network address, a *IPAddr. +func (c *IPConn) RemoteAddr() Addr { + return nil +} + +// SetTimeout implements the net.Conn SetTimeout method. +func (c *IPConn) SetTimeout(nsec int64) os.Error { + return os.EPLAN9 +} + +// SetReadTimeout implements the net.Conn SetReadTimeout method. +func (c *IPConn) SetReadTimeout(nsec int64) os.Error { + return os.EPLAN9 +} + +// SetWriteTimeout implements the net.Conn SetWriteTimeout method. +func (c *IPConn) SetWriteTimeout(nsec int64) os.Error { + return os.EPLAN9 +} + +// IP-specific methods. + +// ReadFrom implements the net.PacketConn ReadFrom method. +func (c *IPConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) { + err = os.EPLAN9 + return +} + +// WriteToIP writes a IP packet to addr via c, copying the payload from b. +// +// WriteToIP can be made to time out and return +// an error with Timeout() == true after a fixed time limit; +// see SetTimeout and SetWriteTimeout. +// On packet-oriented connections, write timeouts are rare. +func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (n int, err os.Error) { + return 0, os.EPLAN9 +} + +// WriteTo implements the net.PacketConn WriteTo method. +func (c *IPConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) { + return 0, os.EPLAN9 +} + +func splitNetProto(netProto string) (net string, proto int, err os.Error) { + err = os.EPLAN9 + return +} + +// DialIP connects to the remote address raddr on the network net, +// which must be "ip", "ip4", or "ip6". +func DialIP(netProto string, laddr, raddr *IPAddr) (c *IPConn, err os.Error) { + return nil, os.EPLAN9 +} + +// ListenIP listens for incoming IP packets addressed to the +// local address laddr. The returned connection c's ReadFrom +// and WriteTo methods can be used to receive and send IP +// packets with per-packet addressing. +func ListenIP(netProto string, laddr *IPAddr) (c *IPConn, err os.Error) { + return nil, os.EPLAN9 +} diff --git a/libgo/go/net/iprawsock_posix.go b/libgo/go/net/iprawsock_posix.go new file mode 100644 index 00000000000..4e115180061 --- /dev/null +++ b/libgo/go/net/iprawsock_posix.go @@ -0,0 +1,305 @@ +// Copyright 2010 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. + +// (Raw) IP sockets + +package net + +import ( + "os" + "sync" + "syscall" +) + +var onceReadProtocols sync.Once + +func sockaddrToIP(sa syscall.Sockaddr) Addr { + switch sa := sa.(type) { + case *syscall.SockaddrInet4: + return &IPAddr{sa.Addr[0:]} + case *syscall.SockaddrInet6: + return &IPAddr{sa.Addr[0:]} + } + return nil +} + +func (a *IPAddr) family() int { + if a == nil || len(a.IP) <= 4 { + return syscall.AF_INET + } + if a.IP.To4() != nil { + return syscall.AF_INET + } + return syscall.AF_INET6 +} + +func (a *IPAddr) sockaddr(family int) (syscall.Sockaddr, os.Error) { + return ipToSockaddr(family, a.IP, 0) +} + +func (a *IPAddr) toAddr() sockaddr { + if a == nil { // nil *IPAddr + return nil // nil interface + } + return a +} + +// IPConn is the implementation of the Conn and PacketConn +// interfaces for IP network connections. +type IPConn struct { + fd *netFD +} + +func newIPConn(fd *netFD) *IPConn { return &IPConn{fd} } + +func (c *IPConn) ok() bool { return c != nil && c.fd != nil } + +// Implementation of the Conn interface - see Conn for documentation. + +// Read implements the net.Conn Read method. +func (c *IPConn) Read(b []byte) (n int, err os.Error) { + n, _, err = c.ReadFrom(b) + return +} + +// Write implements the net.Conn Write method. +func (c *IPConn) Write(b []byte) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + return c.fd.Write(b) +} + +// Close closes the IP connection. +func (c *IPConn) Close() os.Error { + if !c.ok() { + return os.EINVAL + } + err := c.fd.Close() + c.fd = nil + return err +} + +// LocalAddr returns the local network address. +func (c *IPConn) LocalAddr() Addr { + if !c.ok() { + return nil + } + return c.fd.laddr +} + +// RemoteAddr returns the remote network address, a *IPAddr. +func (c *IPConn) RemoteAddr() Addr { + if !c.ok() { + return nil + } + return c.fd.raddr +} + +// SetTimeout implements the net.Conn SetTimeout method. +func (c *IPConn) SetTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setTimeout(c.fd, nsec) +} + +// SetReadTimeout implements the net.Conn SetReadTimeout method. +func (c *IPConn) SetReadTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setReadTimeout(c.fd, nsec) +} + +// SetWriteTimeout implements the net.Conn SetWriteTimeout method. +func (c *IPConn) SetWriteTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setWriteTimeout(c.fd, nsec) +} + +// SetReadBuffer sets the size of the operating system's +// receive buffer associated with the connection. +func (c *IPConn) SetReadBuffer(bytes int) os.Error { + if !c.ok() { + return os.EINVAL + } + return setReadBuffer(c.fd, bytes) +} + +// SetWriteBuffer sets the size of the operating system's +// transmit buffer associated with the connection. +func (c *IPConn) SetWriteBuffer(bytes int) os.Error { + if !c.ok() { + return os.EINVAL + } + return setWriteBuffer(c.fd, bytes) +} + +// IP-specific methods. + +// ReadFromIP reads a IP packet from c, copying the payload into b. +// It returns the number of bytes copied into b and the return address +// that was on the packet. +// +// ReadFromIP can be made to time out and return an error with +// Timeout() == true after a fixed time limit; see SetTimeout and +// SetReadTimeout. +func (c *IPConn) ReadFromIP(b []byte) (n int, addr *IPAddr, err os.Error) { + if !c.ok() { + return 0, nil, os.EINVAL + } + // TODO(cw,rsc): consider using readv if we know the family + // type to avoid the header trim/copy + n, sa, err := c.fd.ReadFrom(b) + switch sa := sa.(type) { + case *syscall.SockaddrInet4: + addr = &IPAddr{sa.Addr[0:]} + if len(b) >= 4 { // discard ipv4 header + hsize := (int(b[0]) & 0xf) * 4 + copy(b, b[hsize:]) + n -= hsize + } + case *syscall.SockaddrInet6: + addr = &IPAddr{sa.Addr[0:]} + } + return +} + +// ReadFrom implements the net.PacketConn ReadFrom method. +func (c *IPConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) { + if !c.ok() { + return 0, nil, os.EINVAL + } + n, uaddr, err := c.ReadFromIP(b) + return n, uaddr.toAddr(), err +} + +// WriteToIP writes a IP packet to addr via c, copying the payload from b. +// +// WriteToIP can be made to time out and return +// an error with Timeout() == true after a fixed time limit; +// see SetTimeout and SetWriteTimeout. +// On packet-oriented connections, write timeouts are rare. +func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + sa, err1 := addr.sockaddr(c.fd.family) + if err1 != nil { + return 0, &OpError{Op: "write", Net: "ip", Addr: addr, Error: err1} + } + return c.fd.WriteTo(b, sa) +} + +// WriteTo implements the net.PacketConn WriteTo method. +func (c *IPConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + a, ok := addr.(*IPAddr) + if !ok { + return 0, &OpError{"writeto", "ip", addr, os.EINVAL} + } + return c.WriteToIP(b, a) +} + +var protocols map[string]int + +func readProtocols() { + protocols = make(map[string]int) + if file, err := open("/etc/protocols"); err == nil { + for line, ok := file.readLine(); ok; line, ok = file.readLine() { + // tcp 6 TCP # transmission control protocol + if i := byteIndex(line, '#'); i >= 0 { + line = line[0:i] + } + f := getFields(line) + if len(f) < 2 { + continue + } + if proto, _, ok := dtoi(f[1], 0); ok { + protocols[f[0]] = proto + for _, alias := range f[2:] { + protocols[alias] = proto + } + } + } + file.close() + } +} + +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.NewError("no IP protocol specified") + } + net = netProto[0:i] + protostr := netProto[i+1:] + proto, i, ok := dtoi(protostr, 0) + if !ok || i != len(protostr) { + // lookup by name + proto, ok = protocols[protostr] + if ok { + return + } + } + return +} + +// DialIP connects to the remote address raddr on the network net, +// which must be "ip", "ip4", or "ip6". +func DialIP(netProto string, laddr, raddr *IPAddr) (c *IPConn, err os.Error) { + net, proto, err := splitNetProto(netProto) + if err != nil { + return + } + switch net { + case "ip", "ip4", "ip6": + default: + return nil, UnknownNetworkError(net) + } + if raddr == nil { + return nil, &OpError{"dial", "ip", nil, errMissingAddress} + } + fd, e := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_RAW, proto, "dial", sockaddrToIP) + if e != nil { + return nil, e + } + return newIPConn(fd), nil +} + +// ListenIP listens for incoming IP packets addressed to the +// local address laddr. The returned connection c's ReadFrom +// and WriteTo methods can be used to receive and send IP +// packets with per-packet addressing. +func ListenIP(netProto string, laddr *IPAddr) (c *IPConn, err os.Error) { + net, proto, err := splitNetProto(netProto) + if err != nil { + return + } + switch net { + case "ip", "ip4", "ip6": + default: + return nil, UnknownNetworkError(net) + } + fd, e := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_RAW, proto, "dial", sockaddrToIP) + if e != nil { + return nil, e + } + return newIPConn(fd), nil +} + +// BindToDevice binds an IPConn to a network interface. +func (c *IPConn) BindToDevice(device string) os.Error { + if !c.ok() { + return os.EINVAL + } + c.fd.incref() + defer c.fd.decref() + return os.NewSyscallError("setsockopt", syscall.BindToDevice(c.fd.sysfd, device)) +} diff --git a/libgo/go/net/ipsock.go b/libgo/go/net/ipsock.go index e8bcac64603..4e2a5622b36 100644 --- a/libgo/go/net/ipsock.go +++ b/libgo/go/net/ipsock.go @@ -8,33 +8,27 @@ package net import ( "os" - "syscall" ) -// Should we try to use the IPv4 socket interface if we're -// only dealing with IPv4 sockets? As long as the host system -// understands IPv6, it's okay to pass IPv4 addresses to the IPv6 -// interface. That simplifies our code and is most general. -// Unfortunately, we need to run on kernels built without IPv6 support too. -// So probe the kernel to figure it out. -func kernelSupportsIPv6() bool { - s, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) - if err != 0 { - return false - } - defer closesocket(s) +var supportsIPv6, supportsIPv4map = probeIPv6Stack() - la := &TCPAddr{IP: IPv4(127, 0, 0, 1)} - sa, oserr := la.toAddr().sockaddr(syscall.AF_INET6) - if oserr != nil { - return false +func firstFavoriteAddr(filter func(IP) IP, addrs []string) (addr IP) { + if filter == anyaddr { + // We'll take any IP address, but since the dialing code + // does not yet try multiple addresses, prefer to use + // an IPv4 address if possible. This is especially relevant + // if localhost resolves to [ipv6-localhost, ipv4-localhost]. + // Too much code assumes localhost == ipv4-localhost. + addr = firstSupportedAddr(ipv4only, addrs) + if addr == nil { + addr = firstSupportedAddr(anyaddr, addrs) + } + } else { + addr = firstSupportedAddr(filter, addrs) } - - return syscall.Bind(s, sa) == 0 + return } -var preferIPv4 = !kernelSupportsIPv6() - func firstSupportedAddr(filter func(IP) IP, addrs []string) IP { for _, s := range addrs { if addr := filter(ParseIP(s)); addr != nil { @@ -44,98 +38,25 @@ func firstSupportedAddr(filter func(IP) IP, addrs []string) IP { return nil } -func anyaddr(x IP) IP { return x } +func anyaddr(x IP) IP { + if x4 := x.To4(); x4 != nil { + return x4 + } + if supportsIPv6 { + return x + } + return nil +} + func ipv4only(x IP) IP { return x.To4() } func ipv6only(x IP) IP { // Only return addresses that we can use // with the kernel's IPv6 addressing modes. - // If preferIPv4 is set, it means the IPv6 stack - // cannot take IPv4 addresses directly (we prefer - // to use the IPv4 stack) so reject IPv4 addresses. - if x.To4() != nil && preferIPv4 { - return nil - } - return x -} - -// TODO(rsc): if syscall.OS == "linux", we're supposd to read -// /proc/sys/net/core/somaxconn, -// to take advantage of kernels that have raised the limit. -func listenBacklog() int { return syscall.SOMAXCONN } - -// Internet sockets (TCP, UDP) - -// A sockaddr represents a TCP or UDP network address that can -// be converted into a syscall.Sockaddr. -type sockaddr interface { - Addr - sockaddr(family int) (syscall.Sockaddr, os.Error) - family() int -} - -func internetSocket(net string, laddr, raddr sockaddr, socktype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err os.Error) { - // Figure out IP version. - // If network has a suffix like "tcp4", obey it. - var oserr os.Error - family := syscall.AF_INET6 - switch net[len(net)-1] { - case '4': - family = syscall.AF_INET - case '6': - // nothing to do - default: - // Otherwise, guess. - // If the addresses are IPv4 and we prefer IPv4, use 4; else 6. - if preferIPv4 && - (laddr == nil || laddr.family() == syscall.AF_INET) && - (raddr == nil || raddr.family() == syscall.AF_INET) { - family = syscall.AF_INET - } + if len(x) == IPv6len && x.To4() == nil && supportsIPv6 { + return x } - - var la, ra syscall.Sockaddr - if laddr != nil { - if la, oserr = laddr.sockaddr(family); oserr != nil { - goto Error - } - } - if raddr != nil { - if ra, oserr = raddr.sockaddr(family); oserr != nil { - goto Error - } - } - fd, oserr = socket(net, family, socktype, proto, la, ra, toAddr) - if oserr != nil { - goto Error - } - return fd, nil - -Error: - addr := raddr - if mode == "listen" { - addr = laddr - } - return nil, &OpError{mode, net, addr, oserr} -} - -func getip(fd int, remote bool) (ip []byte, port int, ok bool) { - // No attempt at error reporting because - // there are no possible errors, and the - // caller won't report them anyway. - var sa syscall.Sockaddr - if remote { - sa, _ = syscall.Getpeername(fd) - } else { - sa, _ = syscall.Getsockname(fd) - } - switch sa := sa.(type) { - case *syscall.SockaddrInet4: - return sa.Addr[0:], sa.Port, true - case *syscall.SockaddrInet6: - return sa.Addr[0:], sa.Port, true - } - return + return nil } type InvalidAddrError string @@ -144,44 +65,6 @@ func (e InvalidAddrError) String() string { return string(e) } func (e InvalidAddrError) Timeout() bool { return false } func (e InvalidAddrError) Temporary() bool { return false } -func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) { - switch family { - case syscall.AF_INET: - if len(ip) == 0 { - ip = IPv4zero - } - if ip = ip.To4(); ip == nil { - return nil, InvalidAddrError("non-IPv4 address") - } - s := new(syscall.SockaddrInet4) - for i := 0; i < IPv4len; i++ { - s.Addr[i] = ip[i] - } - s.Port = port - return s, nil - case syscall.AF_INET6: - if len(ip) == 0 { - ip = IPzero - } - // IPv4 callers use 0.0.0.0 to mean "announce on any available address". - // In IPv6 mode, Linux treats that as meaning "announce on 0.0.0.0", - // which it refuses to do. Rewrite to the IPv6 all zeros. - if p4 := ip.To4(); p4 != nil && p4[0] == 0 && p4[1] == 0 && p4[2] == 0 && p4[3] == 0 { - ip = IPzero - } - if ip = ip.To16(); ip == nil { - return nil, InvalidAddrError("non-IPv6 address") - } - s := new(syscall.SockaddrInet6) - for i := 0; i < IPv6len; i++ { - s.Addr[i] = ip[i] - } - s.Port = port - return s, nil - } - return nil, InvalidAddrError("unexpected socket family") -} - // 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. @@ -220,20 +103,25 @@ 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) if addr == nil { filter := anyaddr - if len(net) >= 4 && net[3] == '4' { + if net != "" && net[len(net)-1] == '4' { filter = ipv4only - } else if len(net) >= 4 && net[3] == '6' { + } + if net != "" && net[len(net)-1] == '6' { filter = ipv6only } // Not an IP address. Try as a DNS name. @@ -242,28 +130,16 @@ func hostPortToIP(net, hostport string) (ip IP, iport int, err os.Error) { err = err1 goto Error } - if filter == anyaddr { - // We'll take any IP address, but since the dialing code - // does not yet try multiple addresses, prefer to use - // an IPv4 address if possible. This is especially relevant - // if localhost resolves to [ipv6-localhost, ipv4-localhost]. - // Too much code assumes localhost == ipv4-localhost. - addr = firstSupportedAddr(ipv4only, addrs) - if addr == nil { - addr = firstSupportedAddr(anyaddr, addrs) - } - } else { - addr = firstSupportedAddr(filter, addrs) - } + addr = firstFavoriteAddr(filter, addrs) if addr == nil { // should not happen - err = &AddrError{"LookupHost returned invalid address", addrs[0]} + err = &AddrError{"LookupHost returned no suitable address", addrs[0]} goto 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/libgo/go/net/ipsock_plan9.go b/libgo/go/net/ipsock_plan9.go new file mode 100644 index 00000000000..9e5da6d38a7 --- /dev/null +++ b/libgo/go/net/ipsock_plan9.go @@ -0,0 +1,305 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// IP sockets stubs for Plan 9 + +package net + +import ( + "os" +) + +// probeIPv6Stack returns two boolean values. If the first boolean value is +// true, kernel supports basic IPv6 functionality. If the second +// boolean value is true, kernel supports IPv6 IPv4-mapping. +func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { + return false, false +} + +// parsePlan9Addr parses address of the form [ip!]port (e.g. 127.0.0.1!80). +func parsePlan9Addr(s string) (ip IP, iport int, err os.Error) { + var ( + addr IP + p, i int + ok bool + ) + addr = IPv4zero // address contains port only + i = byteIndex(s, '!') + if i >= 0 { + addr = ParseIP(s[:i]) + if addr == nil { + err = os.NewError("net: parsing IP failed") + goto Error + } + } + p, _, ok = dtoi(s[i+1:], 0) + if !ok { + err = os.NewError("net: parsing port failed") + goto Error + } + if p < 0 || p > 0xFFFF { + err = &AddrError{"invalid port", string(p)} + goto Error + } + return addr, p, nil + +Error: + return nil, 0, err +} + +func readPlan9Addr(proto, filename string) (addr Addr, err os.Error) { + var buf [128]byte + + f, err := os.Open(filename) + if err != nil { + return + } + n, err := f.Read(buf[:]) + if err != nil { + return + } + ip, port, err := parsePlan9Addr(string(buf[:n])) + if err != nil { + return + } + switch proto { + case "tcp": + addr = &TCPAddr{ip, port} + case "udp": + addr = &UDPAddr{ip, port} + default: + return nil, os.NewError("unknown protocol " + proto) + } + return addr, nil +} + +type plan9Conn struct { + proto, name, dir string + ctl, data *os.File + laddr, raddr Addr +} + +func newPlan9Conn(proto, name string, ctl *os.File, laddr, raddr Addr) *plan9Conn { + return &plan9Conn{proto, name, "/net/" + proto + "/" + name, ctl, nil, laddr, raddr} +} + +func (c *plan9Conn) ok() bool { return c != nil && c.ctl != nil } + +// Implementation of the Conn interface - see Conn for documentation. + +// Read implements the net.Conn Read method. +func (c *plan9Conn) Read(b []byte) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + if c.data == nil { + c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0) + if err != nil { + return 0, err + } + } + n, err = c.data.Read(b) + if c.proto == "udp" && err == os.EOF { + n = 0 + err = nil + } + return +} + +// Write implements the net.Conn Write method. +func (c *plan9Conn) Write(b []byte) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + if c.data == nil { + c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0) + if err != nil { + return 0, err + } + } + return c.data.Write(b) +} + +// Close closes the connection. +func (c *plan9Conn) Close() os.Error { + if !c.ok() { + return os.EINVAL + } + err := c.ctl.Close() + if err != nil { + return err + } + if c.data != nil { + err = c.data.Close() + } + c.ctl = nil + c.data = nil + return err +} + +// LocalAddr returns the local network address. +func (c *plan9Conn) LocalAddr() Addr { + if !c.ok() { + return nil + } + return c.laddr +} + +// RemoteAddr returns the remote network address. +func (c *plan9Conn) RemoteAddr() Addr { + if !c.ok() { + return nil + } + return c.raddr +} + +// SetTimeout implements the net.Conn SetTimeout method. +func (c *plan9Conn) SetTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return os.EPLAN9 +} + +// SetReadTimeout implements the net.Conn SetReadTimeout method. +func (c *plan9Conn) SetReadTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return os.EPLAN9 +} + +// SetWriteTimeout implements the net.Conn SetWriteTimeout method. +func (c *plan9Conn) SetWriteTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return os.EPLAN9 +} + +func startPlan9(net string, addr Addr) (ctl *os.File, dest, proto, name string, err os.Error) { + var ( + ip IP + port int + ) + switch a := addr.(type) { + case *TCPAddr: + proto = "tcp" + ip = a.IP + port = a.Port + case *UDPAddr: + proto = "udp" + ip = a.IP + port = a.Port + default: + err = UnknownNetworkError(net) + return + } + + clone, dest, err := queryCS1(proto, ip, port) + if err != nil { + return + } + f, err := os.OpenFile(clone, os.O_RDWR, 0) + if err != nil { + return + } + var buf [16]byte + n, err := f.Read(buf[:]) + if err != nil { + return + } + return f, dest, proto, string(buf[:n]), nil +} + +func dialPlan9(net string, laddr, raddr Addr) (c *plan9Conn, err os.Error) { + f, dest, proto, name, err := startPlan9(net, raddr) + if err != nil { + return + } + _, err = f.WriteString("connect " + dest) + if err != nil { + return + } + laddr, err = readPlan9Addr(proto, "/net/"+proto+"/"+name+"/local") + if err != nil { + return + } + raddr, err = readPlan9Addr(proto, "/net/"+proto+"/"+name+"/remote") + if err != nil { + return + } + return newPlan9Conn(proto, name, f, laddr, raddr), nil +} + +type plan9Listener struct { + proto, name, dir string + ctl *os.File + laddr Addr +} + +func listenPlan9(net string, laddr Addr) (l *plan9Listener, err os.Error) { + f, dest, proto, name, err := startPlan9(net, laddr) + if err != nil { + return + } + _, err = f.WriteString("announce " + dest) + if err != nil { + return + } + laddr, err = readPlan9Addr(proto, "/net/"+proto+"/"+name+"/local") + if err != nil { + return + } + l = new(plan9Listener) + l.proto = proto + l.name = name + l.dir = "/net/" + proto + "/" + name + l.ctl = f + l.laddr = laddr + return l, nil +} + +func (l *plan9Listener) plan9Conn() *plan9Conn { + return newPlan9Conn(l.proto, l.name, l.ctl, l.laddr, nil) +} + +func (l *plan9Listener) acceptPlan9() (c *plan9Conn, err os.Error) { + f, err := os.Open(l.dir + "/listen") + if err != nil { + return + } + var buf [16]byte + n, err := f.Read(buf[:]) + if err != nil { + return + } + name := string(buf[:n]) + laddr, err := readPlan9Addr(l.proto, l.dir+"/local") + if err != nil { + return + } + raddr, err := readPlan9Addr(l.proto, l.dir+"/remote") + if err != nil { + return + } + return newPlan9Conn(l.proto, name, f, laddr, raddr), nil +} + +func (l *plan9Listener) Accept() (c Conn, err os.Error) { + c1, err := l.acceptPlan9() + if err != nil { + return + } + return c1, nil +} + +func (l *plan9Listener) Close() os.Error { + if l == nil || l.ctl == nil { + return os.EINVAL + } + return l.ctl.Close() +} + +func (l *plan9Listener) Addr() Addr { return l.laddr } diff --git a/libgo/go/net/ipsock_posix.go b/libgo/go/net/ipsock_posix.go new file mode 100644 index 00000000000..0c522fb7fbe --- /dev/null +++ b/libgo/go/net/ipsock_posix.go @@ -0,0 +1,174 @@ +// 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. + +package net + +import ( + "os" + "syscall" +) + +// Should we try to use the IPv4 socket interface if we're +// only dealing with IPv4 sockets? As long as the host system +// understands IPv6, it's okay to pass IPv4 addresses to the IPv6 +// interface. That simplifies our code and is most general. +// Unfortunately, we need to run on kernels built without IPv6 +// support too. So probe the kernel to figure it out. +// +// probeIPv6Stack probes both basic IPv6 capability and IPv6 IPv4- +// mapping capability which is controlled by IPV6_V6ONLY socket +// option and/or kernel state "net.inet6.ip6.v6only". +// It returns two boolean values. If the first boolean value is +// true, kernel supports basic IPv6 functionality. If the second +// boolean value is true, kernel supports IPv6 IPv4-mapping. +func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { + var probes = []struct { + la TCPAddr + ok bool + }{ + // IPv6 communication capability + {TCPAddr{IP: ParseIP("::1")}, false}, + // IPv6 IPv4-mapped address communication capability + {TCPAddr{IP: IPv4(127, 0, 0, 1)}, false}, + } + + for i := range probes { + s, errno := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) + if errno != 0 { + continue + } + defer closesocket(s) + sa, err := probes[i].la.toAddr().sockaddr(syscall.AF_INET6) + if err != nil { + continue + } + errno = syscall.Bind(s, sa) + if errno != 0 { + continue + } + probes[i].ok = true + } + + return probes[0].ok, probes[1].ok +} + +// favoriteAddrFamily returns the appropriate address family to +// the given net, raddr, laddr and mode. At first it figures +// address family out from the net. If mode indicates "listen" +// and laddr.(type).IP is nil, it assumes that the user wants to +// make a passive connection with wildcard address family, both +// INET and INET6, and wildcard address. Otherwise guess: if the +// addresses are IPv4 then returns INET, or else returns INET6. +func favoriteAddrFamily(net string, raddr, laddr sockaddr, mode string) int { + switch net[len(net)-1] { + case '4': + return syscall.AF_INET + case '6': + return syscall.AF_INET6 + } + + if mode == "listen" { + switch a := laddr.(type) { + case *TCPAddr: + if a.IP == nil && supportsIPv6 { + return syscall.AF_INET6 + } + case *UDPAddr: + if a.IP == nil && supportsIPv6 { + return syscall.AF_INET6 + } + case *IPAddr: + if a.IP == nil && supportsIPv6 { + return syscall.AF_INET6 + } + } + } + + if (laddr == nil || laddr.family() == syscall.AF_INET) && + (raddr == nil || raddr.family() == syscall.AF_INET) { + return syscall.AF_INET + } + return syscall.AF_INET6 +} + +// TODO(rsc): if syscall.OS == "linux", we're supposed to read +// /proc/sys/net/core/somaxconn, +// to take advantage of kernels that have raised the limit. +func listenBacklog() int { return syscall.SOMAXCONN } + +// Internet sockets (TCP, UDP) + +// A sockaddr represents a TCP or UDP network address that can +// be converted into a syscall.Sockaddr. +type sockaddr interface { + Addr + sockaddr(family int) (syscall.Sockaddr, os.Error) + family() int +} + +func internetSocket(net string, laddr, raddr sockaddr, socktype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err os.Error) { + var oserr os.Error + var la, ra syscall.Sockaddr + family := favoriteAddrFamily(net, raddr, laddr, mode) + if laddr != nil { + if la, oserr = laddr.sockaddr(family); oserr != nil { + goto Error + } + } + if raddr != nil { + if ra, oserr = raddr.sockaddr(family); oserr != nil { + goto Error + } + } + fd, oserr = socket(net, family, socktype, proto, la, ra, toAddr) + if oserr != nil { + goto Error + } + return fd, nil + +Error: + addr := raddr + if mode == "listen" { + addr = laddr + } + return nil, &OpError{mode, net, addr, oserr} +} + +func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) { + switch family { + case syscall.AF_INET: + if len(ip) == 0 { + ip = IPv4zero + } + if ip = ip.To4(); ip == nil { + return nil, InvalidAddrError("non-IPv4 address") + } + s := new(syscall.SockaddrInet4) + for i := 0; i < IPv4len; i++ { + s.Addr[i] = ip[i] + } + s.Port = port + return s, nil + case syscall.AF_INET6: + if len(ip) == 0 { + ip = IPv6zero + } + // IPv4 callers use 0.0.0.0 to mean "announce on any available address". + // In IPv6 mode, Linux treats that as meaning "announce on 0.0.0.0", + // which it refuses to do. Rewrite to the IPv6 all zeros. + if ip.Equal(IPv4zero) { + ip = IPv6zero + } + if ip = ip.To16(); ip == nil { + return nil, InvalidAddrError("non-IPv6 address") + } + s := new(syscall.SockaddrInet6) + for i := 0; i < IPv6len; i++ { + s.Addr[i] = ip[i] + } + s.Port = port + return s, nil + } + return nil, InvalidAddrError("unexpected socket family") +} diff --git a/libgo/go/net/lookup.go b/libgo/go/net/lookup.go deleted file mode 100644 index eeb22a8ae3d..00000000000 --- a/libgo/go/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/libgo/go/net/lookup_plan9.go b/libgo/go/net/lookup_plan9.go new file mode 100644 index 00000000000..37d6b8e315a --- /dev/null +++ b/libgo/go/net/lookup_plan9.go @@ -0,0 +1,226 @@ +// 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" +) + +func query(filename, query string, bufSize int) (res []string, err os.Error) { + file, err := os.OpenFile(filename, os.O_RDWR, 0) + if err != nil { + return + } + defer file.Close() + + _, err = file.WriteString(query) + if err != nil { + return + } + _, err = file.Seek(0, 0) + if err != nil { + return + } + buf := make([]byte, bufSize) + for { + n, _ := file.Read(buf) + if n <= 0 { + break + } + res = append(res, string(buf[:n])) + } + return +} + +func queryCS(net, host, service string) (res []string, err os.Error) { + switch net { + case "tcp4", "tcp6": + net = "tcp" + case "udp4", "udp6": + net = "udp" + } + if host == "" { + host = "*" + } + return query("/net/cs", net+"!"+host+"!"+service, 128) +} + +func queryCS1(net string, ip IP, port int) (clone, dest string, err os.Error) { + ips := "*" + if !ip.IsUnspecified() { + ips = ip.String() + } + lines, err := queryCS(net, ips, itoa(port)) + if err != nil { + return + } + f := getFields(lines[0]) + if len(f) < 2 { + return "", "", os.NewError("net: bad response from ndb/cs") + } + clone, dest = f[0], f[1] + return +} + +func queryDNS(addr string, typ string) (res []string, err os.Error) { + return query("/net/dns", addr+" "+typ, 1024) +} + +// 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) { + // Use /net/cs insead of /net/dns because cs knows about + // host names in local network (e.g. from /lib/ndb/local) + lines, err := queryCS("tcp", host, "1") + if err != nil { + return + } + for _, line := range lines { + f := getFields(line) + if len(f) < 2 { + continue + } + addr := f[1] + if i := byteIndex(addr, '!'); i >= 0 { + addr = addr[:i] // remove port + } + if ParseIP(addr) == nil { + continue + } + addrs = append(addrs, addr) + } + 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) (ips []IP, err os.Error) { + addrs, err := LookupHost(host) + if err != nil { + return + } + for _, addr := range addrs { + if ip := ParseIP(addr); ip != nil { + ips = append(ips, ip) + } + } + return +} + +// LookupPort looks up the port for the given network and service. +func LookupPort(network, service string) (port int, err os.Error) { + switch network { + case "tcp4", "tcp6": + network = "tcp" + case "udp4", "udp6": + network = "udp" + } + lines, err := queryCS(network, "127.0.0.1", service) + if err != nil { + return + } + unknownPortError := &AddrError{"unknown port", network + "/" + service} + if len(lines) == 0 { + return 0, unknownPortError + } + f := getFields(lines[0]) + if len(f) < 2 { + return 0, unknownPortError + } + s := f[1] + if i := byteIndex(s, '!'); i >= 0 { + s = s[i+1:] // remove address + } + if n, _, ok := dtoi(s, 0); ok { + return n, nil + } + return 0, unknownPortError +} + +// 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) { + lines, err := queryDNS(name, "cname") + if err != nil { + return + } + if len(lines) > 0 { + if f := getFields(lines[0]); len(f) >= 3 { + return f[2] + ".", nil + } + } + return "", os.NewError("net: bad response from ndb/dns") +} + +// 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 + lines, err := queryDNS(target, "srv") + if err != nil { + return + } + for _, line := range lines { + f := getFields(line) + if len(f) < 6 { + continue + } + port, _, portOk := dtoi(f[2], 0) + priority, _, priorityOk := dtoi(f[3], 0) + weight, _, weightOk := dtoi(f[4], 0) + if !(portOk && priorityOk && weightOk) { + continue + } + addrs = append(addrs, &SRV{f[5], uint16(port), uint16(priority), uint16(weight)}) + cname = f[0] + } + byPriorityWeight(addrs).sort() + return +} + +// LookupMX returns the DNS MX records for the given domain name sorted by preference. +func LookupMX(name string) (mx []*MX, err os.Error) { + lines, err := queryDNS(name, "mx") + if err != nil { + return + } + for _, line := range lines { + f := getFields(line) + if len(f) < 4 { + continue + } + if pref, _, ok := dtoi(f[2], 0); ok { + mx = append(mx, &MX{f[3], uint16(pref)}) + } + } + byPref(mx).sort() + 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) { + arpa, err := reverseaddr(addr) + if err != nil { + return + } + lines, err := queryDNS(arpa, "ptr") + if err != nil { + return + } + for _, line := range lines { + f := getFields(line) + if len(f) < 3 { + continue + } + name = append(name, f[2]) + } + return +} diff --git a/libgo/go/net/srv_test.go b/libgo/go/net/lookup_test.go index f1c7a0ab498..995ab03d090 100644 --- a/libgo/go/net/srv_test.go +++ b/libgo/go/net/lookup_test.go @@ -27,3 +27,31 @@ func TestGoogleSRV(t *testing.T) { t.Errorf("no results") } } + +func TestGmailMX(t *testing.T) { + if testing.Short() || avoidMacFirewall { + t.Logf("skipping test to avoid external network") + return + } + mx, err := LookupMX("gmail.com") + if err != nil { + t.Errorf("failed: %s", err) + } + if len(mx) == 0 { + t.Errorf("no results") + } +} + +func TestGoogleDNSAddr(t *testing.T) { + if testing.Short() || avoidMacFirewall { + t.Logf("skipping test to avoid external network") + return + } + names, err := LookupAddr("8.8.8.8") + if err != nil { + t.Errorf("failed: %s", err) + } + if len(names) == 0 { + t.Errorf("no results") + } +} diff --git a/libgo/go/net/lookup_unix.go b/libgo/go/net/lookup_unix.go new file mode 100644 index 00000000000..8f5e66212b3 --- /dev/null +++ b/libgo/go/net/lookup_unix.go @@ -0,0 +1,111 @@ +// 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 +} + +// 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} + } + byPriorityWeight(addrs).sort() + 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} + } + byPref(mx).sort() + 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/libgo/go/net/resolv_windows.go b/libgo/go/net/lookup_windows.go index f7c3f51bef1..fa3ad7c7f42 100644 --- a/libgo/go/net/resolv_windows.go +++ b/libgo/go/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 @@ -76,66 +85,46 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os. return "", nil, os.NewSyscallError("LookupSRV", int(e)) } defer syscall.DnsRecordListFree(r, 1) - addrs = make([]*SRV, 100) - i := 0 + addrs = make([]*SRV, 0, 10) for p := r; p != nil && p.Type == syscall.DNS_TYPE_SRV; p = p.Next { v := (*syscall.DNSSRVData)(unsafe.Pointer(&p.Data[0])) - addrs[i] = &SRV{syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Target))[:]), v.Port, v.Priority, v.Weight} - i++ + addrs = append(addrs, &SRV{syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Target))[:]), v.Port, v.Priority, v.Weight}) } - addrs = addrs[0:i] + byPriorityWeight(addrs).sort() 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" +func LookupMX(name string) (mx []*MX, err os.Error) { + var r *syscall.DNSRecord + e := syscall.DnsQuery(name, syscall.DNS_TYPE_MX, 0, nil, &r, nil) + if int(e) != 0 { + return nil, os.NewSyscallError("LookupMX", int(e)) } - serventLock.Lock() - defer serventLock.Unlock() - s, e := syscall.GetServByName(service, network) - if e != 0 { - return 0, os.NewSyscallError("GetServByName", e) + defer syscall.DnsRecordListFree(r, 1) + mx = make([]*MX, 0, 10) + for p := r; p != nil && p.Type == syscall.DNS_TYPE_MX; p = p.Next { + v := (*syscall.DNSMXData)(unsafe.Pointer(&p.Data[0])) + mx = append(mx, &MX{syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.NameExchange))[:]) + ".", v.Preference}) } - return int(syscall.Ntohs(s.Port)), nil -} - -// TODO(brainman): Following code is only to get tests running. - -func isDomainName(s string) bool { - panic("unimplemented") + byPref(mx).sort() + return mx, nil } -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") -} - -// 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 (e *DNSError) String() string { - if e == nil { - return "<nil>" +func LookupAddr(addr string) (name []string, err os.Error) { + arpa, err := reverseaddr(addr) + if err != nil { + return nil, err } - s := "lookup " + e.Name - if e.Server != "" { - s += " on " + e.Server + var r *syscall.DNSRecord + e := syscall.DnsQuery(arpa, syscall.DNS_TYPE_PTR, 0, nil, &r, nil) + if int(e) != 0 { + return nil, os.NewSyscallError("LookupAddr", int(e)) } - s += ": " + e.Error - return s + defer syscall.DnsRecordListFree(r, 1) + name = make([]string, 0, 10) + for p := r; p != nil && p.Type == syscall.DNS_TYPE_PTR; p = p.Next { + v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0])) + name = append(name, syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:])) + } + return name, nil } - -func (e *DNSError) Timeout() bool { return e.IsTimeout } -func (e *DNSError) Temporary() bool { return e.IsTimeout } diff --git a/libgo/go/net/net.go b/libgo/go/net/net.go index 51db1073954..5c84d34348a 100644 --- a/libgo/go/net/net.go +++ b/libgo/go/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/libgo/go/net/net_test.go b/libgo/go/net/net_test.go index da7928351ce..dc0d49d23ac 100644 --- a/libgo/go/net/net_test.go +++ b/libgo/go/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/libgo/go/net/newpollserver.go b/libgo/go/net/newpollserver.go index fff54dba71f..427208701b3 100644 --- a/libgo/go/net/newpollserver.go +++ b/libgo/go/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/libgo/go/net/parse_test.go b/libgo/go/net/parse_test.go index 226f354d300..8d51eba18c3 100644 --- a/libgo/go/net/parse_test.go +++ b/libgo/go/net/parse_test.go @@ -12,8 +12,8 @@ import ( ) func TestReadLine(t *testing.T) { - // /etc/services file does not exist on windows. - if runtime.GOOS == "windows" { + // /etc/services file does not exist on windows and Plan 9. + if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { return } filename := "/etc/services" // a nice big file diff --git a/libgo/go/net/sendfile_linux.go b/libgo/go/net/sendfile_linux.go new file mode 100644 index 00000000000..6a5a06c8c54 --- /dev/null +++ b/libgo/go/net/sendfile_linux.go @@ -0,0 +1,84 @@ +// 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" +) + +// maxSendfileSize is the largest chunk size we ask the kernel to copy +// at a time. +const maxSendfileSize int = 4 << 20 + +// sendFile copies the contents of r to c using the sendfile +// 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. +func sendFile(c *netFD, r io.Reader) (written int64, err os.Error, handled bool) { + var remain int64 = 1 << 62 // by default, copy until EOF + + lr, ok := r.(*io.LimitedReader) + if ok { + remain, r = lr.N, lr.R + if remain <= 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() + if c.wdeadline_delta > 0 { + // This is a little odd that we're setting the timeout + // for the entire file but Write has the same issue + // (if one slurps the whole file into memory and + // do one large Write). At least they're consistent. + c.wdeadline = pollserver.Now() + c.wdeadline_delta + } else { + c.wdeadline = 0 + } + + dst := c.sysfd + src := f.Fd() + for remain > 0 { + n := maxSendfileSize + if int64(n) > remain { + n = int(remain) + } + n, errno := syscall.Sendfile(dst, src, nil, n) + if n > 0 { + written += int64(n) + remain -= int64(n) + } + if n == 0 && errno == 0 { + break + } + if errno == syscall.EAGAIN && c.wdeadline >= 0 { + pollserver.WaitWrite(c) + continue + } + if errno != 0 { + // This includes syscall.ENOSYS (no kernel + // support) and syscall.EINVAL (fd types which + // don't implement sendfile together) + err = &OpError{"sendfile", c.net, c.raddr, os.Errno(errno)} + break + } + } + if lr != nil { + lr.N = remain + } + return written, err, written > 0 +} diff --git a/libgo/go/net/sendfile_stub.go b/libgo/go/net/sendfile_stub.go new file mode 100644 index 00000000000..43e8104e94c --- /dev/null +++ b/libgo/go/net/sendfile_stub.go @@ -0,0 +1,14 @@ +// 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" +) + +func sendFile(c *netFD, r io.Reader) (n int64, err os.Error, handled bool) { + return 0, nil, false +} diff --git a/libgo/go/net/sendfile_windows.go b/libgo/go/net/sendfile_windows.go new file mode 100644 index 00000000000..3772eee2490 --- /dev/null +++ b/libgo/go/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/libgo/go/net/server_test.go b/libgo/go/net/server_test.go index 075748b83b0..7d7f7fc01c4 100644 --- a/libgo/go/net/server_test.go +++ b/libgo/go/net/server_test.go @@ -92,15 +92,22 @@ func connect(t *testing.T, network, addr string, isEmpty bool) { } func doTest(t *testing.T, network, listenaddr, dialaddr string) { - t.Logf("Test %s %s %s\n", network, listenaddr, dialaddr) + t.Logf("Test %q %q %q\n", network, listenaddr, dialaddr) + switch listenaddr { + case "", "0.0.0.0", "[::]", "[::ffff:0.0.0.0]": + if testing.Short() || avoidMacFirewall { + t.Logf("skip wildcard listen during short test") + return + } + } listening := make(chan string) done := make(chan int) - if network == "tcp" { + if network == "tcp" || network == "tcp4" || network == "tcp6" { listenaddr += ":0" // any available port } go runServe(t, network, listenaddr, listening, done) addr := <-listening // wait for server to start - if network == "tcp" { + if network == "tcp" || network == "tcp4" || network == "tcp6" { dialaddr += addr[strings.LastIndex(addr, ":"):] } connect(t, network, dialaddr, false) @@ -108,16 +115,39 @@ func doTest(t *testing.T, network, listenaddr, dialaddr string) { } func TestTCPServer(t *testing.T) { + doTest(t, "tcp", "", "127.0.0.1") + doTest(t, "tcp", "0.0.0.0", "127.0.0.1") doTest(t, "tcp", "127.0.0.1", "127.0.0.1") - if kernelSupportsIPv6() { + doTest(t, "tcp4", "", "127.0.0.1") + doTest(t, "tcp4", "0.0.0.0", "127.0.0.1") + doTest(t, "tcp4", "127.0.0.1", "127.0.0.1") + if supportsIPv6 { + doTest(t, "tcp", "", "[::1]") + doTest(t, "tcp", "[::]", "[::1]") doTest(t, "tcp", "[::1]", "[::1]") + doTest(t, "tcp6", "", "[::1]") + doTest(t, "tcp6", "[::]", "[::1]") + doTest(t, "tcp6", "[::1]", "[::1]") + } + if supportsIPv6 && supportsIPv4map { + doTest(t, "tcp", "[::ffff:0.0.0.0]", "127.0.0.1") + doTest(t, "tcp", "[::]", "127.0.0.1") + doTest(t, "tcp4", "[::ffff:0.0.0.0]", "127.0.0.1") + doTest(t, "tcp6", "", "127.0.0.1") + doTest(t, "tcp6", "[::ffff:0.0.0.0]", "127.0.0.1") + doTest(t, "tcp6", "[::]", "127.0.0.1") doTest(t, "tcp", "127.0.0.1", "[::ffff:127.0.0.1]") + doTest(t, "tcp", "[::ffff:127.0.0.1]", "127.0.0.1") + doTest(t, "tcp4", "127.0.0.1", "[::ffff:127.0.0.1]") + doTest(t, "tcp4", "[::ffff:127.0.0.1]", "127.0.0.1") + doTest(t, "tcp6", "127.0.0.1", "[::ffff:127.0.0.1]") + doTest(t, "tcp6", "[::ffff:127.0.0.1]", "127.0.0.1") } } func TestUnixServer(t *testing.T) { - // "unix" sockets are not supported on windows. - if runtime.GOOS == "windows" { + // "unix" sockets are not supported on windows and Plan 9. + if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { return } os.Remove("/tmp/gotest.net") @@ -186,7 +216,7 @@ func TestUDPServer(t *testing.T) { for _, isEmpty := range []bool{false, true} { doTestPacket(t, "udp", "0.0.0.0", "127.0.0.1", isEmpty) doTestPacket(t, "udp", "", "127.0.0.1", isEmpty) - if kernelSupportsIPv6() { + if supportsIPv6 && supportsIPv4map { doTestPacket(t, "udp", "[::]", "[::ffff:127.0.0.1]", isEmpty) doTestPacket(t, "udp", "[::]", "127.0.0.1", isEmpty) doTestPacket(t, "udp", "0.0.0.0", "[::ffff:127.0.0.1]", isEmpty) @@ -195,8 +225,8 @@ func TestUDPServer(t *testing.T) { } func TestUnixDatagramServer(t *testing.T) { - // "unix" sockets are not supported on windows. - if runtime.GOOS == "windows" { + // "unix" sockets are not supported on windows and Plan 9. + if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { return } for _, isEmpty := range []bool{false} { diff --git a/libgo/go/net/sock.go b/libgo/go/net/sock.go index 21bd5f03e89..821716e43bd 100644 --- a/libgo/go/net/sock.go +++ b/libgo/go/net/sock.go @@ -7,6 +7,7 @@ package net import ( + "io" "os" "reflect" "syscall" @@ -49,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 } } @@ -64,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 { @@ -105,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 { @@ -116,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 { @@ -154,15 +154,13 @@ func (e *UnknownSocketError) String() string { return "unknown socket address type " + reflect.TypeOf(e.sa).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 - case *syscall.SockaddrInet6: - return JoinHostPort(IP(a.Addr[0:]).String(), itoa(a.Port)), nil - case *syscall.SockaddrUnix: - return a.Name, nil - } +type writerOnly struct { + io.Writer +} - return "", &UnknownSocketError{sa} +// Fallback implementation of io.ReaderFrom's ReadFrom, when sendfile isn't +// applicable. +func genericReadFrom(w io.Writer, r io.Reader) (n int64, err os.Error) { + // Use wrapper to hide existing r.ReadFrom from io.Copy. + return io.Copy(writerOnly{w}, r) } diff --git a/libgo/go/net/sock_windows.go b/libgo/go/net/sock_windows.go index e17c60b98b6..c6dbd046567 100644 --- a/libgo/go/net/sock_windows.go +++ b/libgo/go/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/libgo/go/net/tcpsock.go b/libgo/go/net/tcpsock.go index d9aa7cf19a5..f5c0a278107 100644 --- a/libgo/go/net/tcpsock.go +++ b/libgo/go/net/tcpsock.go @@ -8,19 +8,8 @@ package net import ( "os" - "syscall" ) -func sockaddrToTCP(sa syscall.Sockaddr) Addr { - switch sa := sa.(type) { - case *syscall.SockaddrInet4: - return &TCPAddr{sa.Addr[0:], sa.Port} - case *syscall.SockaddrInet6: - return &TCPAddr{sa.Addr[0:], sa.Port} - } - return nil -} - // TCPAddr represents the address of a TCP end point. type TCPAddr struct { IP IP @@ -37,258 +26,15 @@ func (a *TCPAddr) String() string { return JoinHostPort(a.IP.String(), itoa(a.Port)) } -func (a *TCPAddr) family() int { - if a == nil || len(a.IP) <= 4 { - return syscall.AF_INET - } - if ip := a.IP.To4(); ip != nil { - return syscall.AF_INET - } - return syscall.AF_INET6 -} - -func (a *TCPAddr) sockaddr(family int) (syscall.Sockaddr, os.Error) { - return ipToSockaddr(family, a.IP, a.Port) -} - -func (a *TCPAddr) toAddr() sockaddr { - if a == nil { // nil *TCPAddr - return nil // nil interface - } - return a -} - // ResolveTCPAddr parses addr as a TCP address of the form // host:port and resolves domain names or port names to -// numeric addresses. A literal IPv6 host address must be +// numeric addresses on the network net, which must be "tcp", +// "tcp4" or "tcp6". A literal IPv6 host address must be // enclosed in square brackets, as in "[::]:80". -func ResolveTCPAddr(network, addr string) (*TCPAddr, os.Error) { - ip, port, err := hostPortToIP(network, addr) +func ResolveTCPAddr(net, addr string) (*TCPAddr, os.Error) { + ip, port, err := hostPortToIP(net, addr) if err != nil { return nil, err } return &TCPAddr{ip, port}, nil } - -// TCPConn is an implementation of the Conn interface -// for TCP network connections. -type TCPConn struct { - fd *netFD -} - -func newTCPConn(fd *netFD) *TCPConn { - c := &TCPConn{fd} - c.SetNoDelay(true) - return c -} - -func (c *TCPConn) ok() bool { return c != nil && c.fd != nil } - -// Implementation of the Conn interface - see Conn for documentation. - -// Read implements the net.Conn Read method. -func (c *TCPConn) Read(b []byte) (n int, err os.Error) { - if !c.ok() { - return 0, os.EINVAL - } - return c.fd.Read(b) -} - -// Write implements the net.Conn Write method. -func (c *TCPConn) Write(b []byte) (n int, err os.Error) { - if !c.ok() { - return 0, os.EINVAL - } - return c.fd.Write(b) -} - -// Close closes the TCP connection. -func (c *TCPConn) Close() os.Error { - if !c.ok() { - return os.EINVAL - } - err := c.fd.Close() - c.fd = nil - return err -} - -// LocalAddr returns the local network address, a *TCPAddr. -func (c *TCPConn) LocalAddr() Addr { - if !c.ok() { - return nil - } - return c.fd.laddr -} - -// RemoteAddr returns the remote network address, a *TCPAddr. -func (c *TCPConn) RemoteAddr() Addr { - if !c.ok() { - return nil - } - return c.fd.raddr -} - -// SetTimeout implements the net.Conn SetTimeout method. -func (c *TCPConn) SetTimeout(nsec int64) os.Error { - if !c.ok() { - return os.EINVAL - } - return setTimeout(c.fd, nsec) -} - -// SetReadTimeout implements the net.Conn SetReadTimeout method. -func (c *TCPConn) SetReadTimeout(nsec int64) os.Error { - if !c.ok() { - return os.EINVAL - } - return setReadTimeout(c.fd, nsec) -} - -// SetWriteTimeout implements the net.Conn SetWriteTimeout method. -func (c *TCPConn) SetWriteTimeout(nsec int64) os.Error { - if !c.ok() { - return os.EINVAL - } - return setWriteTimeout(c.fd, nsec) -} - -// SetReadBuffer sets the size of the operating system's -// receive buffer associated with the connection. -func (c *TCPConn) SetReadBuffer(bytes int) os.Error { - if !c.ok() { - return os.EINVAL - } - return setReadBuffer(c.fd, bytes) -} - -// SetWriteBuffer sets the size of the operating system's -// transmit buffer associated with the connection. -func (c *TCPConn) SetWriteBuffer(bytes int) os.Error { - if !c.ok() { - return os.EINVAL - } - return setWriteBuffer(c.fd, bytes) -} - -// SetLinger sets the behavior of Close() on a connection -// which still has data waiting to be sent or to be acknowledged. -// -// If sec < 0 (the default), Close returns immediately and -// the operating system finishes sending the data in the background. -// -// If sec == 0, Close returns immediately and the operating system -// discards any unsent or unacknowledged data. -// -// If sec > 0, Close blocks for at most sec seconds waiting for -// data to be sent and acknowledged. -func (c *TCPConn) SetLinger(sec int) os.Error { - if !c.ok() { - return os.EINVAL - } - return setLinger(c.fd, sec) -} - -// SetKeepAlive sets whether the operating system should send -// keepalive messages on the connection. -func (c *TCPConn) SetKeepAlive(keepalive bool) os.Error { - if !c.ok() { - return os.EINVAL - } - return setKeepAlive(c.fd, keepalive) -} - -// SetNoDelay controls whether the operating system should delay -// packet transmission in hopes of sending fewer packets -// (Nagle's algorithm). The default is true (no delay), meaning -// that data is sent as soon as possible after a Write. -func (c *TCPConn) SetNoDelay(noDelay bool) os.Error { - if !c.ok() { - return os.EINVAL - } - return setNoDelay(c.fd, noDelay) -} - -// File returns a copy of the underlying os.File, set to blocking mode. -// It is the caller's responsibility to close f when finished. -// 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 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} - } - fd, e := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP) - if e != nil { - return nil, e - } - return newTCPConn(fd), nil -} - -// TCPListener is a TCP network listener. -// Clients should typically use variables of type Listener -// instead of assuming TCP. -type TCPListener struct { - fd *netFD -} - -// ListenTCP announces on the TCP address laddr and returns a TCP listener. -// Net must be "tcp", "tcp4", or "tcp6". -// If laddr has a port of 0, it means to listen on some available port. -// The caller can use l.Addr() to retrieve the chosen address. -func ListenTCP(net string, laddr *TCPAddr) (l *TCPListener, err os.Error) { - fd, err := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_STREAM, 0, "listen", sockaddrToTCP) - if err != nil { - return nil, err - } - errno := syscall.Listen(fd.sysfd, listenBacklog()) - if errno != 0 { - closesocket(fd.sysfd) - return nil, &OpError{"listen", "tcp", laddr, os.Errno(errno)} - } - l = new(TCPListener) - l.fd = fd - return l, nil -} - -// AcceptTCP accepts the next incoming call and returns the new connection -// and the remote address. -func (l *TCPListener) AcceptTCP() (c *TCPConn, err os.Error) { - if l == nil || l.fd == nil || l.fd.sysfd < 0 { - return nil, os.EINVAL - } - fd, err := l.fd.accept(sockaddrToTCP) - if err != nil { - return nil, err - } - return newTCPConn(fd), nil -} - -// Accept implements the Accept method in the Listener interface; -// it waits for the next call and returns a generic Conn. -func (l *TCPListener) Accept() (c Conn, err os.Error) { - c1, err := l.AcceptTCP() - if err != nil { - return nil, err - } - return c1, nil -} - -// Close stops listening on the TCP address. -// Already Accepted connections are not closed. -func (l *TCPListener) Close() os.Error { - if l == nil || l.fd == nil { - return os.EINVAL - } - return l.fd.Close() -} - -// Addr returns the listener's network address, a *TCPAddr. -func (l *TCPListener) Addr() Addr { return l.fd.laddr } - -// File returns a copy of the underlying os.File, set to blocking mode. -// It is the caller's responsibility to close f when finished. -// Closing c does not affect f, and closing f does not affect c. -func (l *TCPListener) File() (f *os.File, err os.Error) { return l.fd.dup() } diff --git a/libgo/go/net/tcpsock_plan9.go b/libgo/go/net/tcpsock_plan9.go new file mode 100644 index 00000000000..f4f6e9fee16 --- /dev/null +++ b/libgo/go/net/tcpsock_plan9.go @@ -0,0 +1,63 @@ +// 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. + +// TCP for Plan 9 + +package net + +import ( + "os" +) + +// TCPConn is an implementation of the Conn interface +// for TCP network connections. +type TCPConn struct { + plan9Conn +} + +// 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) { + switch net { + case "tcp", "tcp4", "tcp6": + default: + return nil, UnknownNetworkError(net) + } + if raddr == nil { + return nil, &OpError{"dial", "tcp", nil, errMissingAddress} + } + c1, err := dialPlan9(net, laddr, raddr) + if err != nil { + return + } + return &TCPConn{*c1}, nil +} + +// TCPListener is a TCP network listener. +// Clients should typically use variables of type Listener +// instead of assuming TCP. +type TCPListener struct { + plan9Listener +} + +// ListenTCP announces on the TCP address laddr and returns a TCP listener. +// Net must be "tcp", "tcp4", or "tcp6". +// If laddr has a port of 0, it means to listen on some available port. +// The caller can use l.Addr() to retrieve the chosen address. +func ListenTCP(net string, laddr *TCPAddr) (l *TCPListener, err os.Error) { + switch net { + case "tcp", "tcp4", "tcp6": + default: + return nil, UnknownNetworkError(net) + } + if laddr == nil { + return nil, &OpError{"listen", "tcp", nil, errMissingAddress} + } + l1, err := listenPlan9(net, laddr) + if err != nil { + return + } + return &TCPListener{*l1}, nil +} diff --git a/libgo/go/net/tcpsock_posix.go b/libgo/go/net/tcpsock_posix.go new file mode 100644 index 00000000000..5560301b40f --- /dev/null +++ b/libgo/go/net/tcpsock_posix.go @@ -0,0 +1,283 @@ +// 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. + +// TCP sockets + +package net + +import ( + "io" + "os" + "syscall" +) + +func sockaddrToTCP(sa syscall.Sockaddr) Addr { + switch sa := sa.(type) { + case *syscall.SockaddrInet4: + return &TCPAddr{sa.Addr[0:], sa.Port} + case *syscall.SockaddrInet6: + return &TCPAddr{sa.Addr[0:], sa.Port} + } + return nil +} + +func (a *TCPAddr) family() int { + if a == nil || len(a.IP) <= 4 { + return syscall.AF_INET + } + if a.IP.To4() != nil { + return syscall.AF_INET + } + return syscall.AF_INET6 +} + +func (a *TCPAddr) sockaddr(family int) (syscall.Sockaddr, os.Error) { + return ipToSockaddr(family, a.IP, a.Port) +} + +func (a *TCPAddr) toAddr() sockaddr { + if a == nil { // nil *TCPAddr + return nil // nil interface + } + return a +} + +// TCPConn is an implementation of the Conn interface +// for TCP network connections. +type TCPConn struct { + fd *netFD +} + +func newTCPConn(fd *netFD) *TCPConn { + c := &TCPConn{fd} + c.SetNoDelay(true) + return c +} + +func (c *TCPConn) ok() bool { return c != nil && c.fd != nil } + +// Implementation of the Conn interface - see Conn for documentation. + +// Read implements the net.Conn Read method. +func (c *TCPConn) Read(b []byte) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + return c.fd.Read(b) +} + +// ReadFrom implements the io.ReaderFrom ReadFrom method. +func (c *TCPConn) ReadFrom(r io.Reader) (int64, os.Error) { + if n, err, handled := sendFile(c.fd, r); handled { + return n, err + } + return genericReadFrom(c, r) +} + +// Write implements the net.Conn Write method. +func (c *TCPConn) Write(b []byte) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + return c.fd.Write(b) +} + +// Close closes the TCP connection. +func (c *TCPConn) Close() os.Error { + if !c.ok() { + return os.EINVAL + } + err := c.fd.Close() + c.fd = nil + return err +} + +// LocalAddr returns the local network address, a *TCPAddr. +func (c *TCPConn) LocalAddr() Addr { + if !c.ok() { + return nil + } + return c.fd.laddr +} + +// RemoteAddr returns the remote network address, a *TCPAddr. +func (c *TCPConn) RemoteAddr() Addr { + if !c.ok() { + return nil + } + return c.fd.raddr +} + +// SetTimeout implements the net.Conn SetTimeout method. +func (c *TCPConn) SetTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setTimeout(c.fd, nsec) +} + +// SetReadTimeout implements the net.Conn SetReadTimeout method. +func (c *TCPConn) SetReadTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setReadTimeout(c.fd, nsec) +} + +// SetWriteTimeout implements the net.Conn SetWriteTimeout method. +func (c *TCPConn) SetWriteTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setWriteTimeout(c.fd, nsec) +} + +// SetReadBuffer sets the size of the operating system's +// receive buffer associated with the connection. +func (c *TCPConn) SetReadBuffer(bytes int) os.Error { + if !c.ok() { + return os.EINVAL + } + return setReadBuffer(c.fd, bytes) +} + +// SetWriteBuffer sets the size of the operating system's +// transmit buffer associated with the connection. +func (c *TCPConn) SetWriteBuffer(bytes int) os.Error { + if !c.ok() { + return os.EINVAL + } + return setWriteBuffer(c.fd, bytes) +} + +// SetLinger sets the behavior of Close() on a connection +// which still has data waiting to be sent or to be acknowledged. +// +// If sec < 0 (the default), Close returns immediately and +// the operating system finishes sending the data in the background. +// +// If sec == 0, Close returns immediately and the operating system +// discards any unsent or unacknowledged data. +// +// If sec > 0, Close blocks for at most sec seconds waiting for +// data to be sent and acknowledged. +func (c *TCPConn) SetLinger(sec int) os.Error { + if !c.ok() { + return os.EINVAL + } + return setLinger(c.fd, sec) +} + +// SetKeepAlive sets whether the operating system should send +// keepalive messages on the connection. +func (c *TCPConn) SetKeepAlive(keepalive bool) os.Error { + if !c.ok() { + return os.EINVAL + } + return setKeepAlive(c.fd, keepalive) +} + +// SetNoDelay controls whether the operating system should delay +// packet transmission in hopes of sending fewer packets +// (Nagle's algorithm). The default is true (no delay), meaning +// that data is sent as soon as possible after a Write. +func (c *TCPConn) SetNoDelay(noDelay bool) os.Error { + if !c.ok() { + return os.EINVAL + } + return setNoDelay(c.fd, noDelay) +} + +// File returns a copy of the underlying os.File, set to blocking mode. +// It is the caller's responsibility to close f when finished. +// 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 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} + } + fd, e := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP) + if e != nil { + return nil, e + } + return newTCPConn(fd), nil +} + +// TCPListener is a TCP network listener. +// Clients should typically use variables of type Listener +// instead of assuming TCP. +type TCPListener struct { + fd *netFD +} + +// ListenTCP announces on the TCP address laddr and returns a TCP listener. +// Net must be "tcp", "tcp4", or "tcp6". +// If laddr has a port of 0, it means to listen on some available port. +// The caller can use l.Addr() to retrieve the chosen address. +func ListenTCP(net string, laddr *TCPAddr) (l *TCPListener, err os.Error) { + fd, err := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_STREAM, 0, "listen", sockaddrToTCP) + if err != nil { + return nil, err + } + errno := syscall.Listen(fd.sysfd, listenBacklog()) + if errno != 0 { + closesocket(fd.sysfd) + return nil, &OpError{"listen", "tcp", laddr, os.Errno(errno)} + } + l = new(TCPListener) + l.fd = fd + return l, nil +} + +// AcceptTCP accepts the next incoming call and returns the new connection +// and the remote address. +func (l *TCPListener) AcceptTCP() (c *TCPConn, err os.Error) { + if l == nil || l.fd == nil || l.fd.sysfd < 0 { + return nil, os.EINVAL + } + fd, err := l.fd.accept(sockaddrToTCP) + if err != nil { + return nil, err + } + return newTCPConn(fd), nil +} + +// Accept implements the Accept method in the Listener interface; +// it waits for the next call and returns a generic Conn. +func (l *TCPListener) Accept() (c Conn, err os.Error) { + c1, err := l.AcceptTCP() + if err != nil { + return nil, err + } + return c1, nil +} + +// Close stops listening on the TCP address. +// Already Accepted connections are not closed. +func (l *TCPListener) Close() os.Error { + if l == nil || l.fd == nil { + return os.EINVAL + } + return l.fd.Close() +} + +// Addr returns the listener's network address, a *TCPAddr. +func (l *TCPListener) Addr() Addr { return l.fd.laddr } + +// SetTimeout sets the deadline associated with the listener +func (l *TCPListener) SetTimeout(nsec int64) os.Error { + if l == nil || l.fd == nil { + return os.EINVAL + } + return setTimeout(l.fd, nsec) +} + +// File returns a copy of the underlying os.File, set to blocking mode. +// It is the caller's responsibility to close f when finished. +// Closing c does not affect f, and closing f does not affect c. +func (l *TCPListener) File() (f *os.File, err os.Error) { return l.fd.dup() } diff --git a/libgo/go/net/textproto/reader.go b/libgo/go/net/textproto/reader.go index ac1278689a4..ce0ddc73f84 100644 --- a/libgo/go/net/textproto/reader.go +++ b/libgo/go/net/textproto/reader.go @@ -7,7 +7,6 @@ package textproto import ( "bufio" "bytes" - "container/vector" "io" "io/ioutil" "os" @@ -33,22 +32,25 @@ func NewReader(r *bufio.Reader) *Reader { // ReadLine reads a single line from r, // eliding the final \n or \r\n from the returned string. func (r *Reader) ReadLine() (string, os.Error) { - line, err := r.ReadLineBytes() + line, err := r.readLineSlice() return string(line), err } // ReadLineBytes is like ReadLine but returns a []byte instead of a string. func (r *Reader) ReadLineBytes() ([]byte, os.Error) { - r.closeDot() - line, err := r.R.ReadBytes('\n') - n := len(line) - if n > 0 && line[n-1] == '\n' { - n-- - if n > 0 && line[n-1] == '\r' { - n-- - } + line, err := r.readLineSlice() + if line != nil { + buf := make([]byte, len(line)) + copy(buf, line) + line = buf } - return line[0:n], err + return line, err +} + +func (r *Reader) readLineSlice() ([]byte, os.Error) { + r.closeDot() + line, _, err := r.R.ReadLine() + return line, err } // ReadContinuedLine reads a possibly continued line from r, @@ -71,7 +73,7 @@ func (r *Reader) ReadLineBytes() ([]byte, os.Error) { // A line consisting of only white space is never continued. // func (r *Reader) ReadContinuedLine() (string, os.Error) { - line, err := r.ReadContinuedLineBytes() + line, err := r.readContinuedLineSlice() return string(line), err } @@ -92,8 +94,18 @@ func trim(s []byte) []byte { // ReadContinuedLineBytes is like ReadContinuedLine but // returns a []byte instead of a string. func (r *Reader) ReadContinuedLineBytes() ([]byte, os.Error) { + line, err := r.readContinuedLineSlice() + if line != nil { + buf := make([]byte, len(line)) + copy(buf, line) + line = buf + } + return line, err +} + +func (r *Reader) readContinuedLineSlice() ([]byte, os.Error) { // Read the first line. - line, err := r.ReadLineBytes() + line, err := r.readLineSlice() if err != nil { return line, err } @@ -102,6 +114,13 @@ func (r *Reader) ReadContinuedLineBytes() ([]byte, os.Error) { } line = trim(line) + copied := false + if r.R.Buffered() < 1 { + // ReadByte will flush the buffer; make a copy of the slice. + copied = true + line = append([]byte(nil), line...) + } + // Look for a continuation line. c, err := r.R.ReadByte() if err != nil { @@ -114,6 +133,11 @@ func (r *Reader) ReadContinuedLineBytes() ([]byte, os.Error) { return line, nil } + if !copied { + // The next readLineSlice will invalidate the previous one. + line = append(make([]byte, 0, len(line)*2), line...) + } + // Read continuation lines. for { // Consume leading spaces; one already gone. @@ -128,7 +152,7 @@ func (r *Reader) ReadContinuedLineBytes() ([]byte, os.Error) { } } var cont []byte - cont, err = r.ReadLineBytes() + cont, err = r.readLineSlice() cont = trim(cont) line = append(line, ' ') line = append(line, cont...) @@ -237,7 +261,7 @@ func (r *Reader) ReadResponse(expectCode int) (code int, message string, err os. // to a method on r. // // Dot encoding is a common framing used for data blocks -// in text protcols like SMTP. The data consists of a sequence +// in text protocols such as SMTP. The data consists of a sequence // of lines, each of which ends in "\r\n". The sequence itself // ends at a line containing just a dot: ".\r\n". Lines beginning // with a dot are escaped with an additional dot to avoid @@ -375,7 +399,7 @@ func (r *Reader) ReadDotLines() ([]string, os.Error) { // We could use ReadDotBytes and then Split it, // but reading a line at a time avoids needing a // large contiguous block of memory and is simpler. - var v vector.StringVector + var v []string var err os.Error for { var line string @@ -394,7 +418,7 @@ func (r *Reader) ReadDotLines() ([]string, os.Error) { } line = line[1:] } - v.Push(line) + v = append(v, line) } return v, err } @@ -422,7 +446,7 @@ func (r *Reader) ReadDotLines() ([]string, os.Error) { func (r *Reader) ReadMIMEHeader() (MIMEHeader, os.Error) { m := make(MIMEHeader) for { - kv, err := r.ReadContinuedLineBytes() + kv, err := r.readContinuedLineSlice() if len(kv) == 0 { return m, err } @@ -441,9 +465,7 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, os.Error) { } value := string(kv[i:]) - v := vector.StringVector(m[key]) - v.Push(value) - m[key] = v + m[key] = append(m[key], value) if err != nil { return m, err diff --git a/libgo/go/net/udpsock.go b/libgo/go/net/udpsock.go index 67684471b72..3dfa71675f9 100644 --- a/libgo/go/net/udpsock.go +++ b/libgo/go/net/udpsock.go @@ -8,19 +8,8 @@ package net import ( "os" - "syscall" ) -func sockaddrToUDP(sa syscall.Sockaddr) Addr { - switch sa := sa.(type) { - case *syscall.SockaddrInet4: - return &UDPAddr{sa.Addr[0:], sa.Port} - case *syscall.SockaddrInet6: - return &UDPAddr{sa.Addr[0:], sa.Port} - } - return nil -} - // UDPAddr represents the address of a UDP end point. type UDPAddr struct { IP IP @@ -37,286 +26,15 @@ func (a *UDPAddr) String() string { return JoinHostPort(a.IP.String(), itoa(a.Port)) } -func (a *UDPAddr) family() int { - if a == nil || len(a.IP) <= 4 { - return syscall.AF_INET - } - if ip := a.IP.To4(); ip != nil { - return syscall.AF_INET - } - return syscall.AF_INET6 -} - -func (a *UDPAddr) sockaddr(family int) (syscall.Sockaddr, os.Error) { - return ipToSockaddr(family, a.IP, a.Port) -} - -func (a *UDPAddr) toAddr() sockaddr { - if a == nil { // nil *UDPAddr - return nil // nil interface - } - return a -} - // ResolveUDPAddr parses addr as a UDP address of the form // host:port and resolves domain names or port names to -// numeric addresses. A literal IPv6 host address must be +// numeric addresses on the network net, which must be "udp", +// "udp4" or "udp6". A literal IPv6 host address must be // enclosed in square brackets, as in "[::]:80". -func ResolveUDPAddr(network, addr string) (*UDPAddr, os.Error) { - ip, port, err := hostPortToIP(network, addr) +func ResolveUDPAddr(net, addr string) (*UDPAddr, os.Error) { + ip, port, err := hostPortToIP(net, addr) if err != nil { return nil, err } return &UDPAddr{ip, port}, nil } - -// UDPConn is the implementation of the Conn and PacketConn -// interfaces for UDP network connections. -type UDPConn struct { - fd *netFD -} - -func newUDPConn(fd *netFD) *UDPConn { return &UDPConn{fd} } - -func (c *UDPConn) ok() bool { return c != nil && c.fd != nil } - -// Implementation of the Conn interface - see Conn for documentation. - -// Read implements the net.Conn Read method. -func (c *UDPConn) Read(b []byte) (n int, err os.Error) { - if !c.ok() { - return 0, os.EINVAL - } - return c.fd.Read(b) -} - -// Write implements the net.Conn Write method. -func (c *UDPConn) Write(b []byte) (n int, err os.Error) { - if !c.ok() { - return 0, os.EINVAL - } - return c.fd.Write(b) -} - -// Close closes the UDP connection. -func (c *UDPConn) Close() os.Error { - if !c.ok() { - return os.EINVAL - } - err := c.fd.Close() - c.fd = nil - return err -} - -// LocalAddr returns the local network address. -func (c *UDPConn) LocalAddr() Addr { - if !c.ok() { - return nil - } - return c.fd.laddr -} - -// RemoteAddr returns the remote network address, a *UDPAddr. -func (c *UDPConn) RemoteAddr() Addr { - if !c.ok() { - return nil - } - return c.fd.raddr -} - -// SetTimeout implements the net.Conn SetTimeout method. -func (c *UDPConn) SetTimeout(nsec int64) os.Error { - if !c.ok() { - return os.EINVAL - } - return setTimeout(c.fd, nsec) -} - -// SetReadTimeout implements the net.Conn SetReadTimeout method. -func (c *UDPConn) SetReadTimeout(nsec int64) os.Error { - if !c.ok() { - return os.EINVAL - } - return setReadTimeout(c.fd, nsec) -} - -// SetWriteTimeout implements the net.Conn SetWriteTimeout method. -func (c *UDPConn) SetWriteTimeout(nsec int64) os.Error { - if !c.ok() { - return os.EINVAL - } - return setWriteTimeout(c.fd, nsec) -} - -// SetReadBuffer sets the size of the operating system's -// receive buffer associated with the connection. -func (c *UDPConn) SetReadBuffer(bytes int) os.Error { - if !c.ok() { - return os.EINVAL - } - return setReadBuffer(c.fd, bytes) -} - -// SetWriteBuffer sets the size of the operating system's -// transmit buffer associated with the connection. -func (c *UDPConn) SetWriteBuffer(bytes int) os.Error { - if !c.ok() { - return os.EINVAL - } - return setWriteBuffer(c.fd, bytes) -} - -// UDP-specific methods. - -// ReadFromUDP reads a UDP packet from c, copying the payload into b. -// It returns the number of bytes copied into b and the return address -// that was on the packet. -// -// ReadFromUDP can be made to time out and return an error with Timeout() == true -// after a fixed time limit; see SetTimeout and SetReadTimeout. -func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err os.Error) { - if !c.ok() { - return 0, nil, os.EINVAL - } - n, sa, err := c.fd.ReadFrom(b) - switch sa := sa.(type) { - case *syscall.SockaddrInet4: - addr = &UDPAddr{sa.Addr[0:], sa.Port} - case *syscall.SockaddrInet6: - addr = &UDPAddr{sa.Addr[0:], sa.Port} - } - return -} - -// ReadFrom implements the net.PacketConn ReadFrom method. -func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) { - if !c.ok() { - return 0, nil, os.EINVAL - } - n, uaddr, err := c.ReadFromUDP(b) - return n, uaddr.toAddr(), err -} - -// WriteToUDP writes a UDP packet to addr via c, copying the payload from b. -// -// WriteToUDP can be made to time out and return -// an error with Timeout() == true after a fixed time limit; -// see SetTimeout and SetWriteTimeout. -// On packet-oriented connections, write timeouts are rare. -func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err os.Error) { - if !c.ok() { - return 0, os.EINVAL - } - sa, err1 := addr.sockaddr(c.fd.family) - if err1 != nil { - return 0, &OpError{Op: "write", Net: "udp", Addr: addr, Error: err1} - } - return c.fd.WriteTo(b, sa) -} - -// WriteTo implements the net.PacketConn WriteTo method. -func (c *UDPConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) { - if !c.ok() { - return 0, os.EINVAL - } - a, ok := addr.(*UDPAddr) - if !ok { - return 0, &OpError{"writeto", "udp", addr, os.EINVAL} - } - return c.WriteToUDP(b, a) -} - -// DialUDP connects to the remote address raddr on the network net, -// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is used -// as the local address for the connection. -func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err os.Error) { - switch net { - case "udp", "udp4", "udp6": - default: - return nil, UnknownNetworkError(net) - } - if raddr == nil { - return nil, &OpError{"dial", "udp", nil, errMissingAddress} - } - fd, e := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP) - if e != nil { - return nil, e - } - return newUDPConn(fd), nil -} - -// ListenUDP listens for incoming UDP packets addressed to the -// local address laddr. The returned connection c's ReadFrom -// and WriteTo methods can be used to receive and send UDP -// packets with per-packet addressing. -func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err os.Error) { - switch net { - case "udp", "udp4", "udp6": - default: - return nil, UnknownNetworkError(net) - } - if laddr == nil { - return nil, &OpError{"listen", "udp", nil, errMissingAddress} - } - fd, e := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP) - if e != nil { - return nil, e - } - return newUDPConn(fd), nil -} - -// BindToDevice binds a UDPConn to a network interface. -func (c *UDPConn) BindToDevice(device string) os.Error { - if !c.ok() { - return os.EINVAL - } - c.fd.incref() - defer c.fd.decref() - return os.NewSyscallError("setsockopt", syscall.BindToDevice(c.fd.sysfd, device)) -} - -// File returns a copy of the underlying os.File, set to blocking mode. -// It is the caller's responsibility to close f when finished. -// 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") - -// JoinGroup joins the IPv4 multicast group named by addr. -// The UDPConn must use the "udp4" network. -func (c *UDPConn) JoinGroup(addr IP) os.Error { - if !c.ok() { - return os.EINVAL - } - ip := addr.To4() - if ip == nil { - return &OpError{"joingroup", "udp", &IPAddr{ip}, errInvalidMulticast} - } - mreq := &syscall.IpMreq{ - Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}, - } - err := os.NewSyscallError("setsockopt", syscall.SetsockoptIpMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq)) - if err != nil { - return &OpError{"joingroup", "udp", &IPAddr{ip}, err} - } - return nil -} - -// LeaveGroup exits the IPv4 multicast group named by addr. -func (c *UDPConn) LeaveGroup(addr IP) os.Error { - if !c.ok() { - return os.EINVAL - } - ip := addr.To4() - if ip == nil { - return &OpError{"leavegroup", "udp", &IPAddr{ip}, errInvalidMulticast} - } - mreq := &syscall.IpMreq{ - Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}, - } - err := os.NewSyscallError("setsockopt", syscall.SetsockoptIpMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_DROP_MEMBERSHIP, mreq)) - if err != nil { - return &OpError{"leavegroup", "udp", &IPAddr{ip}, err} - } - return nil -} diff --git a/libgo/go/net/udpsock_plan9.go b/libgo/go/net/udpsock_plan9.go new file mode 100644 index 00000000000..bb7196041a4 --- /dev/null +++ b/libgo/go/net/udpsock_plan9.go @@ -0,0 +1,187 @@ +// 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. + +// UDP for Plan 9 + +package net + +import ( + "os" +) + +// UDPConn is the implementation of the Conn and PacketConn +// interfaces for UDP network connections. +type UDPConn struct { + plan9Conn +} + +// UDP-specific methods. + +// ReadFromUDP reads a UDP packet from c, copying the payload into b. +// It returns the number of bytes copied into b and the return address +// that was on the packet. +// +// ReadFromUDP can be made to time out and return an error with Timeout() == true +// after a fixed time limit; see SetTimeout and SetReadTimeout. +func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err os.Error) { + if !c.ok() { + return 0, nil, os.EINVAL + } + if c.data == nil { + c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0) + if err != nil { + return 0, nil, err + } + } + buf := make([]byte, udpHeaderSize+len(b)) + m, err := c.data.Read(buf) + if err != nil { + return + } + if m < udpHeaderSize { + return 0, nil, os.NewError("short read reading UDP header") + } + buf = buf[:m] + + h, buf := unmarshalUDPHeader(buf) + n = copy(b, buf) + return n, &UDPAddr{h.raddr, int(h.rport)}, nil +} + +// ReadFrom implements the net.PacketConn ReadFrom method. +func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) { + if !c.ok() { + return 0, nil, os.EINVAL + } + return c.ReadFromUDP(b) +} + +// WriteToUDP writes a UDP packet to addr via c, copying the payload from b. +// +// WriteToUDP can be made to time out and return +// an error with Timeout() == true after a fixed time limit; +// see SetTimeout and SetWriteTimeout. +// On packet-oriented connections, write timeouts are rare. +func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + if c.data == nil { + c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0) + if err != nil { + return 0, err + } + } + h := new(udpHeader) + h.raddr = addr.IP.To16() + h.laddr = c.laddr.(*UDPAddr).IP.To16() + h.ifcaddr = IPv6zero // ignored (receive only) + h.rport = uint16(addr.Port) + h.lport = uint16(c.laddr.(*UDPAddr).Port) + + buf := make([]byte, udpHeaderSize+len(b)) + i := copy(buf, h.Bytes()) + copy(buf[i:], b) + return c.data.Write(buf) +} + +// WriteTo implements the net.PacketConn WriteTo method. +func (c *UDPConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + a, ok := addr.(*UDPAddr) + if !ok { + return 0, &OpError{"writeto", "udp", addr, os.EINVAL} + } + return c.WriteToUDP(b, a) +} + +// DialUDP connects to the remote address raddr on the network net, +// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is used +// as the local address for the connection. +func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err os.Error) { + switch net { + case "udp", "udp4", "udp6": + default: + return nil, UnknownNetworkError(net) + } + if raddr == nil { + return nil, &OpError{"dial", "udp", nil, errMissingAddress} + } + c1, err := dialPlan9(net, laddr, raddr) + if err != nil { + return + } + return &UDPConn{*c1}, nil +} + +const udpHeaderSize = 16*3 + 2*2 + +type udpHeader struct { + raddr, laddr, ifcaddr IP + rport, lport uint16 +} + +func (h *udpHeader) Bytes() []byte { + b := make([]byte, udpHeaderSize) + i := 0 + i += copy(b[i:i+16], h.raddr) + i += copy(b[i:i+16], h.laddr) + i += copy(b[i:i+16], h.ifcaddr) + b[i], b[i+1], i = byte(h.rport>>8), byte(h.rport), i+2 + b[i], b[i+1], i = byte(h.lport>>8), byte(h.lport), i+2 + return b +} + +func unmarshalUDPHeader(b []byte) (*udpHeader, []byte) { + h := new(udpHeader) + h.raddr, b = IP(b[:16]), b[16:] + h.laddr, b = IP(b[:16]), b[16:] + h.ifcaddr, b = IP(b[:16]), b[16:] + h.rport, b = uint16(b[0])<<8|uint16(b[1]), b[2:] + h.lport, b = uint16(b[0])<<8|uint16(b[1]), b[2:] + return h, b +} + +// ListenUDP listens for incoming UDP packets addressed to the +// local address laddr. The returned connection c's ReadFrom +// and WriteTo methods can be used to receive and send UDP +// packets with per-packet addressing. +func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err os.Error) { + switch net { + case "udp", "udp4", "udp6": + default: + return nil, UnknownNetworkError(net) + } + if laddr == nil { + return nil, &OpError{"listen", "udp", nil, errMissingAddress} + } + l, err := listenPlan9(net, laddr) + if err != nil { + return + } + _, err = l.ctl.WriteString("headers") + if err != nil { + return + } + return &UDPConn{*l.plan9Conn()}, nil +} + +// JoinGroup joins the IPv4 multicast group named by addr. +// The UDPConn must use the "udp4" network. +func (c *UDPConn) JoinGroup(addr IP) os.Error { + if !c.ok() { + return os.EINVAL + } + return os.EPLAN9 +} + +// LeaveGroup exits the IPv4 multicast group named by addr. +func (c *UDPConn) LeaveGroup(addr IP) os.Error { + if !c.ok() { + return os.EINVAL + } + return os.EPLAN9 +} diff --git a/libgo/go/net/udpsock_posix.go b/libgo/go/net/udpsock_posix.go new file mode 100644 index 00000000000..d4ea056f3c7 --- /dev/null +++ b/libgo/go/net/udpsock_posix.go @@ -0,0 +1,294 @@ +// 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. + +// UDP sockets + +package net + +import ( + "os" + "syscall" +) + +func sockaddrToUDP(sa syscall.Sockaddr) Addr { + switch sa := sa.(type) { + case *syscall.SockaddrInet4: + return &UDPAddr{sa.Addr[0:], sa.Port} + case *syscall.SockaddrInet6: + return &UDPAddr{sa.Addr[0:], sa.Port} + } + return nil +} + +func (a *UDPAddr) family() int { + if a == nil || len(a.IP) <= 4 { + return syscall.AF_INET + } + if a.IP.To4() != nil { + return syscall.AF_INET + } + return syscall.AF_INET6 +} + +func (a *UDPAddr) sockaddr(family int) (syscall.Sockaddr, os.Error) { + return ipToSockaddr(family, a.IP, a.Port) +} + +func (a *UDPAddr) toAddr() sockaddr { + if a == nil { // nil *UDPAddr + return nil // nil interface + } + return a +} + +// UDPConn is the implementation of the Conn and PacketConn +// interfaces for UDP network connections. +type UDPConn struct { + fd *netFD +} + +func newUDPConn(fd *netFD) *UDPConn { return &UDPConn{fd} } + +func (c *UDPConn) ok() bool { return c != nil && c.fd != nil } + +// Implementation of the Conn interface - see Conn for documentation. + +// Read implements the net.Conn Read method. +func (c *UDPConn) Read(b []byte) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + return c.fd.Read(b) +} + +// Write implements the net.Conn Write method. +func (c *UDPConn) Write(b []byte) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + return c.fd.Write(b) +} + +// Close closes the UDP connection. +func (c *UDPConn) Close() os.Error { + if !c.ok() { + return os.EINVAL + } + err := c.fd.Close() + c.fd = nil + return err +} + +// LocalAddr returns the local network address. +func (c *UDPConn) LocalAddr() Addr { + if !c.ok() { + return nil + } + return c.fd.laddr +} + +// RemoteAddr returns the remote network address, a *UDPAddr. +func (c *UDPConn) RemoteAddr() Addr { + if !c.ok() { + return nil + } + return c.fd.raddr +} + +// SetTimeout implements the net.Conn SetTimeout method. +func (c *UDPConn) SetTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setTimeout(c.fd, nsec) +} + +// SetReadTimeout implements the net.Conn SetReadTimeout method. +func (c *UDPConn) SetReadTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setReadTimeout(c.fd, nsec) +} + +// SetWriteTimeout implements the net.Conn SetWriteTimeout method. +func (c *UDPConn) SetWriteTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setWriteTimeout(c.fd, nsec) +} + +// SetReadBuffer sets the size of the operating system's +// receive buffer associated with the connection. +func (c *UDPConn) SetReadBuffer(bytes int) os.Error { + if !c.ok() { + return os.EINVAL + } + return setReadBuffer(c.fd, bytes) +} + +// SetWriteBuffer sets the size of the operating system's +// transmit buffer associated with the connection. +func (c *UDPConn) SetWriteBuffer(bytes int) os.Error { + if !c.ok() { + return os.EINVAL + } + return setWriteBuffer(c.fd, bytes) +} + +// UDP-specific methods. + +// ReadFromUDP reads a UDP packet from c, copying the payload into b. +// It returns the number of bytes copied into b and the return address +// that was on the packet. +// +// ReadFromUDP can be made to time out and return an error with Timeout() == true +// after a fixed time limit; see SetTimeout and SetReadTimeout. +func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err os.Error) { + if !c.ok() { + return 0, nil, os.EINVAL + } + n, sa, err := c.fd.ReadFrom(b) + switch sa := sa.(type) { + case *syscall.SockaddrInet4: + addr = &UDPAddr{sa.Addr[0:], sa.Port} + case *syscall.SockaddrInet6: + addr = &UDPAddr{sa.Addr[0:], sa.Port} + } + return +} + +// ReadFrom implements the net.PacketConn ReadFrom method. +func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) { + if !c.ok() { + return 0, nil, os.EINVAL + } + n, uaddr, err := c.ReadFromUDP(b) + return n, uaddr.toAddr(), err +} + +// WriteToUDP writes a UDP packet to addr via c, copying the payload from b. +// +// WriteToUDP can be made to time out and return +// an error with Timeout() == true after a fixed time limit; +// see SetTimeout and SetWriteTimeout. +// On packet-oriented connections, write timeouts are rare. +func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + sa, err1 := addr.sockaddr(c.fd.family) + if err1 != nil { + return 0, &OpError{Op: "write", Net: "udp", Addr: addr, Error: err1} + } + return c.fd.WriteTo(b, sa) +} + +// WriteTo implements the net.PacketConn WriteTo method. +func (c *UDPConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + a, ok := addr.(*UDPAddr) + if !ok { + return 0, &OpError{"writeto", "udp", addr, os.EINVAL} + } + return c.WriteToUDP(b, a) +} + +// DialUDP connects to the remote address raddr on the network net, +// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is used +// as the local address for the connection. +func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err os.Error) { + switch net { + case "udp", "udp4", "udp6": + default: + return nil, UnknownNetworkError(net) + } + if raddr == nil { + return nil, &OpError{"dial", "udp", nil, errMissingAddress} + } + fd, e := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP) + if e != nil { + return nil, e + } + return newUDPConn(fd), nil +} + +// ListenUDP listens for incoming UDP packets addressed to the +// local address laddr. The returned connection c's ReadFrom +// and WriteTo methods can be used to receive and send UDP +// packets with per-packet addressing. +func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err os.Error) { + switch net { + case "udp", "udp4", "udp6": + default: + return nil, UnknownNetworkError(net) + } + if laddr == nil { + return nil, &OpError{"listen", "udp", nil, errMissingAddress} + } + fd, e := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP) + if e != nil { + return nil, e + } + return newUDPConn(fd), nil +} + +// BindToDevice binds a UDPConn to a network interface. +func (c *UDPConn) BindToDevice(device string) os.Error { + if !c.ok() { + return os.EINVAL + } + c.fd.incref() + defer c.fd.decref() + return os.NewSyscallError("setsockopt", syscall.BindToDevice(c.fd.sysfd, device)) +} + +// File returns a copy of the underlying os.File, set to blocking mode. +// It is the caller's responsibility to close f when finished. +// 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.NewError("invalid IPv4 multicast address") + +// JoinGroup joins the IPv4 multicast group named by addr. +// The UDPConn must use the "udp4" network. +func (c *UDPConn) JoinGroup(addr IP) os.Error { + if !c.ok() { + return os.EINVAL + } + ip := addr.To4() + if ip == nil { + return &OpError{"joingroup", "udp", &IPAddr{ip}, errInvalidMulticast} + } + mreq := &syscall.IPMreq{ + Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}, + } + err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq)) + if err != nil { + return &OpError{"joingroup", "udp", &IPAddr{ip}, err} + } + return nil +} + +// LeaveGroup exits the IPv4 multicast group named by addr. +func (c *UDPConn) LeaveGroup(addr IP) os.Error { + if !c.ok() { + return os.EINVAL + } + ip := addr.To4() + if ip == nil { + return &OpError{"leavegroup", "udp", &IPAddr{ip}, errInvalidMulticast} + } + mreq := &syscall.IPMreq{ + Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}, + } + err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_DROP_MEMBERSHIP, mreq)) + if err != nil { + return &OpError{"leavegroup", "udp", &IPAddr{ip}, err} + } + return nil +} diff --git a/libgo/go/net/unixsock.go b/libgo/go/net/unixsock.go index 8c26a7bafd5..d5040f9a297 100644 --- a/libgo/go/net/unixsock.go +++ b/libgo/go/net/unixsock.go @@ -8,109 +8,14 @@ package net import ( "os" - "syscall" ) -func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err os.Error) { - var proto int - switch net { - default: - return nil, UnknownNetworkError(net) - case "unix": - proto = syscall.SOCK_STREAM - case "unixgram": - proto = syscall.SOCK_DGRAM - case "unixpacket": - proto = syscall.SOCK_SEQPACKET - } - - var la, ra syscall.Sockaddr - switch mode { - default: - panic("unixSocket mode " + mode) - - case "dial": - if laddr != nil { - la = &syscall.SockaddrUnix{Name: laddr.Name} - } - if raddr != nil { - ra = &syscall.SockaddrUnix{Name: raddr.Name} - } else if proto != syscall.SOCK_DGRAM || laddr == nil { - return nil, &OpError{Op: mode, Net: net, Error: errMissingAddress} - } - - case "listen": - if laddr == nil { - return nil, &OpError{mode, net, nil, errMissingAddress} - } - la = &syscall.SockaddrUnix{Name: laddr.Name} - if raddr != nil { - return nil, &OpError{Op: mode, Net: net, Addr: raddr, Error: &AddrError{Error: "unexpected remote address", Addr: raddr.String()}} - } - } - - f := sockaddrToUnix - if proto == syscall.SOCK_DGRAM { - f = sockaddrToUnixgram - } else if proto == syscall.SOCK_SEQPACKET { - f = sockaddrToUnixpacket - } - - fd, oserr := socket(net, syscall.AF_UNIX, proto, 0, la, ra, f) - if oserr != nil { - goto Error - } - return fd, nil - -Error: - addr := raddr - if mode == "listen" { - addr = laddr - } - return nil, &OpError{Op: mode, Net: net, Addr: addr, Error: oserr} -} - // UnixAddr represents the address of a Unix domain socket end point. type UnixAddr struct { Name string Net string } -func sockaddrToUnix(sa syscall.Sockaddr) Addr { - if s, ok := sa.(*syscall.SockaddrUnix); ok { - return &UnixAddr{s.Name, "unix"} - } - return nil -} - -func sockaddrToUnixgram(sa syscall.Sockaddr) Addr { - if s, ok := sa.(*syscall.SockaddrUnix); ok { - return &UnixAddr{s.Name, "unixgram"} - } - return nil -} - -func sockaddrToUnixpacket(sa syscall.Sockaddr) Addr { - if s, ok := sa.(*syscall.SockaddrUnix); ok { - return &UnixAddr{s.Name, "unixpacket"} - } - return nil -} - -func protoToNet(proto int) string { - switch proto { - case syscall.SOCK_STREAM: - return "unix" - case syscall.SOCK_SEQPACKET: - return "unixpacket" - case syscall.SOCK_DGRAM: - return "unixgram" - default: - panic("protoToNet unknown protocol") - } - return "" -} - // Network returns the address's network name, "unix" or "unixgram". func (a *UnixAddr) Network() string { return a.Net @@ -143,307 +48,3 @@ func ResolveUnixAddr(net, addr string) (*UnixAddr, os.Error) { } return &UnixAddr{addr, net}, nil } - -// UnixConn is an implementation of the Conn interface -// for connections to Unix domain sockets. -type UnixConn struct { - fd *netFD -} - -func newUnixConn(fd *netFD) *UnixConn { return &UnixConn{fd} } - -func (c *UnixConn) ok() bool { return c != nil && c.fd != nil } - -// Implementation of the Conn interface - see Conn for documentation. - -// Read implements the net.Conn Read method. -func (c *UnixConn) Read(b []byte) (n int, err os.Error) { - if !c.ok() { - return 0, os.EINVAL - } - return c.fd.Read(b) -} - -// Write implements the net.Conn Write method. -func (c *UnixConn) Write(b []byte) (n int, err os.Error) { - if !c.ok() { - return 0, os.EINVAL - } - return c.fd.Write(b) -} - -// Close closes the Unix domain connection. -func (c *UnixConn) Close() os.Error { - if !c.ok() { - return os.EINVAL - } - err := c.fd.Close() - c.fd = nil - return err -} - -// LocalAddr returns the local network address, a *UnixAddr. -// Unlike in other protocols, LocalAddr is usually nil for dialed connections. -func (c *UnixConn) LocalAddr() Addr { - if !c.ok() { - return nil - } - return c.fd.laddr -} - -// RemoteAddr returns the remote network address, a *UnixAddr. -// Unlike in other protocols, RemoteAddr is usually nil for connections -// accepted by a listener. -func (c *UnixConn) RemoteAddr() Addr { - if !c.ok() { - return nil - } - return c.fd.raddr -} - -// SetTimeout implements the net.Conn SetTimeout method. -func (c *UnixConn) SetTimeout(nsec int64) os.Error { - if !c.ok() { - return os.EINVAL - } - return setTimeout(c.fd, nsec) -} - -// SetReadTimeout implements the net.Conn SetReadTimeout method. -func (c *UnixConn) SetReadTimeout(nsec int64) os.Error { - if !c.ok() { - return os.EINVAL - } - return setReadTimeout(c.fd, nsec) -} - -// SetWriteTimeout implements the net.Conn SetWriteTimeout method. -func (c *UnixConn) SetWriteTimeout(nsec int64) os.Error { - if !c.ok() { - return os.EINVAL - } - return setWriteTimeout(c.fd, nsec) -} - -// SetReadBuffer sets the size of the operating system's -// receive buffer associated with the connection. -func (c *UnixConn) SetReadBuffer(bytes int) os.Error { - if !c.ok() { - return os.EINVAL - } - return setReadBuffer(c.fd, bytes) -} - -// SetWriteBuffer sets the size of the operating system's -// transmit buffer associated with the connection. -func (c *UnixConn) SetWriteBuffer(bytes int) os.Error { - if !c.ok() { - return os.EINVAL - } - return setWriteBuffer(c.fd, bytes) -} - -// ReadFromUnix reads a packet from c, copying the payload into b. -// It returns the number of bytes copied into b and the return address -// that was on the packet. -// -// ReadFromUnix can be made to time out and return -// an error with Timeout() == true after a fixed time limit; -// see SetTimeout and SetReadTimeout. -func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err os.Error) { - if !c.ok() { - return 0, nil, os.EINVAL - } - n, sa, err := c.fd.ReadFrom(b) - switch sa := sa.(type) { - case *syscall.SockaddrUnix: - addr = &UnixAddr{sa.Name, protoToNet(c.fd.proto)} - } - return -} - -// ReadFrom implements the net.PacketConn ReadFrom method. -func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) { - if !c.ok() { - return 0, nil, os.EINVAL - } - n, uaddr, err := c.ReadFromUnix(b) - return n, uaddr.toAddr(), err -} - -// WriteToUnix writes a packet to addr via c, copying the payload from b. -// -// WriteToUnix can be made to time out and return -// an error with Timeout() == true after a fixed time limit; -// see SetTimeout and SetWriteTimeout. -// On packet-oriented connections, write timeouts are rare. -func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err os.Error) { - if !c.ok() { - return 0, os.EINVAL - } - if addr.Net != protoToNet(c.fd.proto) { - return 0, os.EAFNOSUPPORT - } - sa := &syscall.SockaddrUnix{Name: addr.Name} - return c.fd.WriteTo(b, sa) -} - -// WriteTo implements the net.PacketConn WriteTo method. -func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) { - if !c.ok() { - return 0, os.EINVAL - } - a, ok := addr.(*UnixAddr) - if !ok { - return 0, &OpError{"writeto", "unix", addr, os.EINVAL} - } - return c.WriteToUnix(b, a) -} - -func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err os.Error) { - if !c.ok() { - return 0, 0, 0, nil, os.EINVAL - } - n, oobn, flags, sa, err := c.fd.ReadMsg(b, oob) - switch sa := sa.(type) { - case *syscall.SockaddrUnix: - addr = &UnixAddr{sa.Name, protoToNet(c.fd.proto)} - } - return -} - -func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err os.Error) { - if !c.ok() { - return 0, 0, os.EINVAL - } - if addr != nil { - if addr.Net != protoToNet(c.fd.proto) { - return 0, 0, os.EAFNOSUPPORT - } - sa := &syscall.SockaddrUnix{Name: addr.Name} - return c.fd.WriteMsg(b, oob, sa) - } - return c.fd.WriteMsg(b, oob, nil) -} - -// File returns a copy of the underlying os.File, set to blocking mode. -// It is the caller's responsibility to close f when finished. -// Closing c does not affect f, and closing f does not affect c. -func (c *UnixConn) File() (f *os.File, err os.Error) { return c.fd.dup() } - -// DialUnix connects to the remote address raddr on the network net, -// which must be "unix" or "unixgram". If laddr is not nil, it is used -// as the local address for the connection. -func DialUnix(net string, laddr, raddr *UnixAddr) (c *UnixConn, err os.Error) { - fd, e := unixSocket(net, laddr, raddr, "dial") - if e != nil { - return nil, e - } - return newUnixConn(fd), nil -} - -// UnixListener is a Unix domain socket listener. -// Clients should typically use variables of type Listener -// instead of assuming Unix domain sockets. -type UnixListener struct { - fd *netFD - path string -} - -// ListenUnix announces on the Unix domain socket laddr and returns a Unix listener. -// Net must be "unix" (stream sockets). -func ListenUnix(net string, laddr *UnixAddr) (l *UnixListener, err os.Error) { - if net != "unix" && net != "unixgram" && net != "unixpacket" { - return nil, UnknownNetworkError(net) - } - if laddr != nil { - laddr = &UnixAddr{laddr.Name, net} // make our own copy - } - fd, err := unixSocket(net, laddr, nil, "listen") - if err != nil { - return nil, err - } - e1 := syscall.Listen(fd.sysfd, 8) // listenBacklog()); - if e1 != 0 { - closesocket(fd.sysfd) - return nil, &OpError{Op: "listen", Net: "unix", Addr: laddr, Error: os.Errno(e1)} - } - return &UnixListener{fd, laddr.Name}, nil -} - -// AcceptUnix accepts the next incoming call and returns the new connection -// and the remote address. -func (l *UnixListener) AcceptUnix() (c *UnixConn, err os.Error) { - if l == nil || l.fd == nil { - return nil, os.EINVAL - } - fd, e := l.fd.accept(sockaddrToUnix) - if e != nil { - return nil, e - } - c = newUnixConn(fd) - return c, nil -} - -// Accept implements the Accept method in the Listener interface; -// it waits for the next call and returns a generic Conn. -func (l *UnixListener) Accept() (c Conn, err os.Error) { - c1, err := l.AcceptUnix() - if err != nil { - return nil, err - } - return c1, nil -} - -// Close stops listening on the Unix address. -// Already accepted connections are not closed. -func (l *UnixListener) Close() os.Error { - if l == nil || l.fd == nil { - return os.EINVAL - } - - // The operating system doesn't clean up - // the file that announcing created, so - // we have to clean it up ourselves. - // There's a race here--we can't know for - // sure whether someone else has come along - // and replaced our socket name already-- - // but this sequence (remove then close) - // is at least compatible with the auto-remove - // sequence in ListenUnix. It's only non-Go - // programs that can mess us up. - if l.path[0] != '@' { - syscall.Unlink(l.path) - } - err := l.fd.Close() - l.fd = nil - return err -} - -// Addr returns the listener's network address. -func (l *UnixListener) Addr() Addr { return l.fd.laddr } - -// File returns a copy of the underlying os.File, set to blocking mode. -// It is the caller's responsibility to close f when finished. -// Closing c does not affect f, and closing f does not affect c. -func (l *UnixListener) File() (f *os.File, err os.Error) { return l.fd.dup() } - -// ListenUnixgram listens for incoming Unix datagram packets addressed to the -// local address laddr. The returned connection c's ReadFrom -// and WriteTo methods can be used to receive and send UDP -// packets with per-packet addressing. The network net must be "unixgram". -func ListenUnixgram(net string, laddr *UnixAddr) (c *UDPConn, err os.Error) { - switch net { - case "unixgram": - default: - return nil, UnknownNetworkError(net) - } - if laddr == nil { - return nil, &OpError{"listen", "unixgram", nil, errMissingAddress} - } - fd, e := unixSocket(net, laddr, nil, "listen") - if e != nil { - return nil, e - } - return newUDPConn(fd), nil -} diff --git a/libgo/go/net/unixsock_plan9.go b/libgo/go/net/unixsock_plan9.go new file mode 100644 index 00000000000..7e212df8a3b --- /dev/null +++ b/libgo/go/net/unixsock_plan9.go @@ -0,0 +1,105 @@ +// 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. + +// Unix domain sockets stubs for Plan 9 + +package net + +import ( + "os" +) + +// UnixConn is an implementation of the Conn interface +// for connections to Unix domain sockets. +type UnixConn bool + +// Implementation of the Conn interface - see Conn for documentation. + +// Read implements the net.Conn Read method. +func (c *UnixConn) Read(b []byte) (n int, err os.Error) { + return 0, os.EPLAN9 +} + +// Write implements the net.Conn Write method. +func (c *UnixConn) Write(b []byte) (n int, err os.Error) { + return 0, os.EPLAN9 +} + +// Close closes the Unix domain connection. +func (c *UnixConn) Close() os.Error { + return os.EPLAN9 +} + +// LocalAddr returns the local network address, a *UnixAddr. +// Unlike in other protocols, LocalAddr is usually nil for dialed connections. +func (c *UnixConn) LocalAddr() Addr { + return nil +} + +// RemoteAddr returns the remote network address, a *UnixAddr. +// Unlike in other protocols, RemoteAddr is usually nil for connections +// accepted by a listener. +func (c *UnixConn) RemoteAddr() Addr { + return nil +} + +// SetTimeout implements the net.Conn SetTimeout method. +func (c *UnixConn) SetTimeout(nsec int64) os.Error { + return os.EPLAN9 +} + +// SetReadTimeout implements the net.Conn SetReadTimeout method. +func (c *UnixConn) SetReadTimeout(nsec int64) os.Error { + return os.EPLAN9 +} + +// SetWriteTimeout implements the net.Conn SetWriteTimeout method. +func (c *UnixConn) SetWriteTimeout(nsec int64) os.Error { + return os.EPLAN9 +} + +// ReadFrom implements the net.PacketConn ReadFrom method. +func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) { + err = os.EPLAN9 + return +} + +// WriteTo implements the net.PacketConn WriteTo method. +func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) { + err = os.EPLAN9 + return +} + +// DialUnix connects to the remote address raddr on the network net, +// which must be "unix" or "unixgram". If laddr is not nil, it is used +// as the local address for the connection. +func DialUnix(net string, laddr, raddr *UnixAddr) (c *UnixConn, err os.Error) { + return nil, os.EPLAN9 +} + +// UnixListener is a Unix domain socket listener. +// Clients should typically use variables of type Listener +// instead of assuming Unix domain sockets. +type UnixListener bool + +// ListenUnix announces on the Unix domain socket laddr and returns a Unix listener. +// Net must be "unix" (stream sockets). +func ListenUnix(net string, laddr *UnixAddr) (l *UnixListener, err os.Error) { + return nil, os.EPLAN9 +} + +// Accept implements the Accept method in the Listener interface; +// it waits for the next call and returns a generic Conn. +func (l *UnixListener) Accept() (c Conn, err os.Error) { + return nil, os.EPLAN9 +} + +// Close stops listening on the Unix address. +// Already accepted connections are not closed. +func (l *UnixListener) Close() os.Error { + return os.EPLAN9 +} + +// Addr returns the listener's network address. +func (l *UnixListener) Addr() Addr { return nil } diff --git a/libgo/go/net/unixsock_posix.go b/libgo/go/net/unixsock_posix.go new file mode 100644 index 00000000000..38c6fe9eb1e --- /dev/null +++ b/libgo/go/net/unixsock_posix.go @@ -0,0 +1,418 @@ +// 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. + +// Unix domain sockets + +package net + +import ( + "os" + "syscall" +) + +func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err os.Error) { + var proto int + switch net { + default: + return nil, UnknownNetworkError(net) + case "unix": + proto = syscall.SOCK_STREAM + case "unixgram": + proto = syscall.SOCK_DGRAM + case "unixpacket": + proto = syscall.SOCK_SEQPACKET + } + + var la, ra syscall.Sockaddr + switch mode { + default: + panic("unixSocket mode " + mode) + + case "dial": + if laddr != nil { + la = &syscall.SockaddrUnix{Name: laddr.Name} + } + if raddr != nil { + ra = &syscall.SockaddrUnix{Name: raddr.Name} + } else if proto != syscall.SOCK_DGRAM || laddr == nil { + return nil, &OpError{Op: mode, Net: net, Error: errMissingAddress} + } + + case "listen": + if laddr == nil { + return nil, &OpError{mode, net, nil, errMissingAddress} + } + la = &syscall.SockaddrUnix{Name: laddr.Name} + if raddr != nil { + return nil, &OpError{Op: mode, Net: net, Addr: raddr, Error: &AddrError{Error: "unexpected remote address", Addr: raddr.String()}} + } + } + + f := sockaddrToUnix + if proto == syscall.SOCK_DGRAM { + f = sockaddrToUnixgram + } else if proto == syscall.SOCK_SEQPACKET { + f = sockaddrToUnixpacket + } + + fd, oserr := socket(net, syscall.AF_UNIX, proto, 0, la, ra, f) + if oserr != nil { + goto Error + } + return fd, nil + +Error: + addr := raddr + if mode == "listen" { + addr = laddr + } + return nil, &OpError{Op: mode, Net: net, Addr: addr, Error: oserr} +} + +func sockaddrToUnix(sa syscall.Sockaddr) Addr { + if s, ok := sa.(*syscall.SockaddrUnix); ok { + return &UnixAddr{s.Name, "unix"} + } + return nil +} + +func sockaddrToUnixgram(sa syscall.Sockaddr) Addr { + if s, ok := sa.(*syscall.SockaddrUnix); ok { + return &UnixAddr{s.Name, "unixgram"} + } + return nil +} + +func sockaddrToUnixpacket(sa syscall.Sockaddr) Addr { + if s, ok := sa.(*syscall.SockaddrUnix); ok { + return &UnixAddr{s.Name, "unixpacket"} + } + return nil +} + +func protoToNet(proto int) string { + switch proto { + case syscall.SOCK_STREAM: + return "unix" + case syscall.SOCK_SEQPACKET: + return "unixpacket" + case syscall.SOCK_DGRAM: + return "unixgram" + default: + panic("protoToNet unknown protocol") + } + return "" +} + +// UnixConn is an implementation of the Conn interface +// for connections to Unix domain sockets. +type UnixConn struct { + fd *netFD +} + +func newUnixConn(fd *netFD) *UnixConn { return &UnixConn{fd} } + +func (c *UnixConn) ok() bool { return c != nil && c.fd != nil } + +// Implementation of the Conn interface - see Conn for documentation. + +// Read implements the net.Conn Read method. +func (c *UnixConn) Read(b []byte) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + return c.fd.Read(b) +} + +// Write implements the net.Conn Write method. +func (c *UnixConn) Write(b []byte) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + return c.fd.Write(b) +} + +// Close closes the Unix domain connection. +func (c *UnixConn) Close() os.Error { + if !c.ok() { + return os.EINVAL + } + err := c.fd.Close() + c.fd = nil + return err +} + +// LocalAddr returns the local network address, a *UnixAddr. +// Unlike in other protocols, LocalAddr is usually nil for dialed connections. +func (c *UnixConn) LocalAddr() Addr { + if !c.ok() { + return nil + } + return c.fd.laddr +} + +// RemoteAddr returns the remote network address, a *UnixAddr. +// Unlike in other protocols, RemoteAddr is usually nil for connections +// accepted by a listener. +func (c *UnixConn) RemoteAddr() Addr { + if !c.ok() { + return nil + } + return c.fd.raddr +} + +// SetTimeout implements the net.Conn SetTimeout method. +func (c *UnixConn) SetTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setTimeout(c.fd, nsec) +} + +// SetReadTimeout implements the net.Conn SetReadTimeout method. +func (c *UnixConn) SetReadTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setReadTimeout(c.fd, nsec) +} + +// SetWriteTimeout implements the net.Conn SetWriteTimeout method. +func (c *UnixConn) SetWriteTimeout(nsec int64) os.Error { + if !c.ok() { + return os.EINVAL + } + return setWriteTimeout(c.fd, nsec) +} + +// SetReadBuffer sets the size of the operating system's +// receive buffer associated with the connection. +func (c *UnixConn) SetReadBuffer(bytes int) os.Error { + if !c.ok() { + return os.EINVAL + } + return setReadBuffer(c.fd, bytes) +} + +// SetWriteBuffer sets the size of the operating system's +// transmit buffer associated with the connection. +func (c *UnixConn) SetWriteBuffer(bytes int) os.Error { + if !c.ok() { + return os.EINVAL + } + return setWriteBuffer(c.fd, bytes) +} + +// ReadFromUnix reads a packet from c, copying the payload into b. +// It returns the number of bytes copied into b and the return address +// that was on the packet. +// +// ReadFromUnix can be made to time out and return +// an error with Timeout() == true after a fixed time limit; +// see SetTimeout and SetReadTimeout. +func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err os.Error) { + if !c.ok() { + return 0, nil, os.EINVAL + } + n, sa, err := c.fd.ReadFrom(b) + switch sa := sa.(type) { + case *syscall.SockaddrUnix: + addr = &UnixAddr{sa.Name, protoToNet(c.fd.proto)} + } + return +} + +// ReadFrom implements the net.PacketConn ReadFrom method. +func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) { + if !c.ok() { + return 0, nil, os.EINVAL + } + n, uaddr, err := c.ReadFromUnix(b) + return n, uaddr.toAddr(), err +} + +// WriteToUnix writes a packet to addr via c, copying the payload from b. +// +// WriteToUnix can be made to time out and return +// an error with Timeout() == true after a fixed time limit; +// see SetTimeout and SetWriteTimeout. +// On packet-oriented connections, write timeouts are rare. +func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + if addr.Net != protoToNet(c.fd.proto) { + return 0, os.EAFNOSUPPORT + } + sa := &syscall.SockaddrUnix{Name: addr.Name} + return c.fd.WriteTo(b, sa) +} + +// WriteTo implements the net.PacketConn WriteTo method. +func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) { + if !c.ok() { + return 0, os.EINVAL + } + a, ok := addr.(*UnixAddr) + if !ok { + return 0, &OpError{"writeto", "unix", addr, os.EINVAL} + } + return c.WriteToUnix(b, a) +} + +func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err os.Error) { + if !c.ok() { + return 0, 0, 0, nil, os.EINVAL + } + n, oobn, flags, sa, err := c.fd.ReadMsg(b, oob) + switch sa := sa.(type) { + case *syscall.SockaddrUnix: + addr = &UnixAddr{sa.Name, protoToNet(c.fd.proto)} + } + return +} + +func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err os.Error) { + if !c.ok() { + return 0, 0, os.EINVAL + } + if addr != nil { + if addr.Net != protoToNet(c.fd.proto) { + return 0, 0, os.EAFNOSUPPORT + } + sa := &syscall.SockaddrUnix{Name: addr.Name} + return c.fd.WriteMsg(b, oob, sa) + } + return c.fd.WriteMsg(b, oob, nil) +} + +// File returns a copy of the underlying os.File, set to blocking mode. +// It is the caller's responsibility to close f when finished. +// Closing c does not affect f, and closing f does not affect c. +func (c *UnixConn) File() (f *os.File, err os.Error) { return c.fd.dup() } + +// DialUnix connects to the remote address raddr on the network net, +// which must be "unix" or "unixgram". If laddr is not nil, it is used +// as the local address for the connection. +func DialUnix(net string, laddr, raddr *UnixAddr) (c *UnixConn, err os.Error) { + fd, e := unixSocket(net, laddr, raddr, "dial") + if e != nil { + return nil, e + } + return newUnixConn(fd), nil +} + +// UnixListener is a Unix domain socket listener. +// Clients should typically use variables of type Listener +// instead of assuming Unix domain sockets. +type UnixListener struct { + fd *netFD + path string +} + +// ListenUnix announces on the Unix domain socket laddr and returns a Unix listener. +// Net must be "unix" (stream sockets). +func ListenUnix(net string, laddr *UnixAddr) (l *UnixListener, err os.Error) { + if net != "unix" && net != "unixgram" && net != "unixpacket" { + return nil, UnknownNetworkError(net) + } + if laddr != nil { + laddr = &UnixAddr{laddr.Name, net} // make our own copy + } + fd, err := unixSocket(net, laddr, nil, "listen") + if err != nil { + return nil, err + } + e1 := syscall.Listen(fd.sysfd, 8) // listenBacklog()); + if e1 != 0 { + closesocket(fd.sysfd) + return nil, &OpError{Op: "listen", Net: "unix", Addr: laddr, Error: os.Errno(e1)} + } + return &UnixListener{fd, laddr.Name}, nil +} + +// AcceptUnix accepts the next incoming call and returns the new connection +// and the remote address. +func (l *UnixListener) AcceptUnix() (c *UnixConn, err os.Error) { + if l == nil || l.fd == nil { + return nil, os.EINVAL + } + fd, e := l.fd.accept(sockaddrToUnix) + if e != nil { + return nil, e + } + c = newUnixConn(fd) + return c, nil +} + +// Accept implements the Accept method in the Listener interface; +// it waits for the next call and returns a generic Conn. +func (l *UnixListener) Accept() (c Conn, err os.Error) { + c1, err := l.AcceptUnix() + if err != nil { + return nil, err + } + return c1, nil +} + +// Close stops listening on the Unix address. +// Already accepted connections are not closed. +func (l *UnixListener) Close() os.Error { + if l == nil || l.fd == nil { + return os.EINVAL + } + + // The operating system doesn't clean up + // the file that announcing created, so + // we have to clean it up ourselves. + // There's a race here--we can't know for + // sure whether someone else has come along + // and replaced our socket name already-- + // but this sequence (remove then close) + // is at least compatible with the auto-remove + // sequence in ListenUnix. It's only non-Go + // programs that can mess us up. + if l.path[0] != '@' { + syscall.Unlink(l.path) + } + err := l.fd.Close() + l.fd = nil + return err +} + +// Addr returns the listener's network address. +func (l *UnixListener) Addr() Addr { return l.fd.laddr } + +// SetTimeout sets the deadline associated wuth the listener +func (l *UnixListener) SetTimeout(nsec int64) (err os.Error) { + if l == nil || l.fd == nil { + return os.EINVAL + } + return setTimeout(l.fd, nsec) +} + +// File returns a copy of the underlying os.File, set to blocking mode. +// It is the caller's responsibility to close f when finished. +// Closing c does not affect f, and closing f does not affect c. +func (l *UnixListener) File() (f *os.File, err os.Error) { return l.fd.dup() } + +// ListenUnixgram listens for incoming Unix datagram packets addressed to the +// local address laddr. The returned connection c's ReadFrom +// and WriteTo methods can be used to receive and send UDP +// packets with per-packet addressing. The network net must be "unixgram". +func ListenUnixgram(net string, laddr *UnixAddr) (c *UDPConn, err os.Error) { + switch net { + case "unixgram": + default: + return nil, UnknownNetworkError(net) + } + if laddr == nil { + return nil, &OpError{"listen", "unixgram", nil, errMissingAddress} + } + fd, e := unixSocket(net, laddr, nil, "listen") + if e != nil { + return nil, e + } + return newUDPConn(fd), nil +} |