From 456fba2651cfb0cb67e44b8354668a0b3f5f5182 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 30 Mar 2012 21:27:11 +0000 Subject: libgo: Update to weekly.2012-03-13. From-SVN: r186023 --- libgo/go/net/dial_test.go | 19 +- libgo/go/net/dnsclient.go | 20 +- libgo/go/net/dnsmsg.go | 350 +++++++++++++++-------- libgo/go/net/dnsmsg_test.go | 13 + libgo/go/net/fd_linux.go | 3 +- libgo/go/net/file_test.go | 112 ++++++-- libgo/go/net/http/client_test.go | 6 +- libgo/go/net/http/request.go | 6 +- libgo/go/net/http/request_test.go | 19 ++ libgo/go/net/http/server.go | 2 +- libgo/go/net/http/transport.go | 2 +- libgo/go/net/http/transport_test.go | 26 ++ libgo/go/net/interface.go | 2 +- libgo/go/net/interface_linux.go | 10 +- libgo/go/net/iprawsock_posix.go | 7 + libgo/go/net/ipsock_posix.go | 82 +++--- libgo/go/net/mac.go | 20 +- libgo/go/net/mac_test.go | 16 +- libgo/go/net/mail/message.go | 3 +- libgo/go/net/multicast_test.go | 8 + libgo/go/net/net.go | 17 +- libgo/go/net/net_test.go | 1 + libgo/go/net/parse_test.go | 4 +- libgo/go/net/rpc/client.go | 3 +- libgo/go/net/server_test.go | 547 +++++++++++++++++++++++++----------- libgo/go/net/sock.go | 4 +- libgo/go/net/sockopt.go | 3 +- libgo/go/net/sockopt_bsd.go | 13 +- libgo/go/net/sockopt_linux.go | 13 +- libgo/go/net/sockopt_windows.go | 13 +- libgo/go/net/tcpsock_posix.go | 10 +- libgo/go/net/timeout_test.go | 51 ++-- libgo/go/net/udp_test.go | 4 +- libgo/go/net/udpsock_posix.go | 7 + libgo/go/net/unicast_test.go | 521 ++++++++++++++++++++++++++++++---- libgo/go/net/unixsock_posix.go | 14 +- 36 files changed, 1482 insertions(+), 469 deletions(-) (limited to 'libgo/go/net') diff --git a/libgo/go/net/dial_test.go b/libgo/go/net/dial_test.go index 5f5aea1..7212087 100644 --- a/libgo/go/net/dial_test.go +++ b/libgo/go/net/dial_test.go @@ -6,6 +6,7 @@ package net import ( "flag" + "fmt" "regexp" "runtime" "testing" @@ -32,7 +33,7 @@ func TestDialTimeout(t *testing.T) { numConns := listenerBacklog + 10 // TODO(bradfitz): It's hard to test this in a portable - // way. This is unforunate, but works for now. + // way. This is unfortunate, but works for now. switch runtime.GOOS { case "linux": // The kernel will start accepting TCP connections before userspace @@ -44,13 +45,25 @@ func TestDialTimeout(t *testing.T) { errc <- err }() } - case "darwin": + case "darwin", "windows": // At least OS X 10.7 seems to accept any number of // connections, ignoring listen's backlog, so resort // to connecting to a hopefully-dead 127/8 address. // Same for windows. + // + // Use an IANA reserved port (49151) instead of 80, because + // on our 386 builder, this Dial succeeds, connecting + // to an IIS web server somewhere. The data center + // or VM or firewall must be stealing the TCP connection. + // + // IANA Service Name and Transport Protocol Port Number Registry + // go func() { - _, err := DialTimeout("tcp", "127.0.71.111:80", 200*time.Millisecond) + c, err := DialTimeout("tcp", "127.0.71.111:49151", 200*time.Millisecond) + if err == nil { + err = fmt.Errorf("unexpected: connected to %s!", c.RemoteAddr()) + c.Close() + } errc <- err }() default: diff --git a/libgo/go/net/dnsclient.go b/libgo/go/net/dnsclient.go index f4ed8b8..e69cb31 100644 --- a/libgo/go/net/dnsclient.go +++ b/libgo/go/net/dnsclient.go @@ -5,8 +5,6 @@ package net import ( - "bytes" - "fmt" "math/rand" "sort" ) @@ -45,20 +43,22 @@ func reverseaddr(addr string) (arpa string, err error) { return "", &DNSError{Err: "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 + return itoa(int(ip[15])) + "." + itoa(int(ip[14])) + "." + itoa(int(ip[13])) + "." + + itoa(int(ip[12])) + ".in-addr.arpa.", nil } // Must be IPv6 - var buf bytes.Buffer + buf := make([]byte, 0, len(ip)*4+len("ip6.arpa.")) // 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('.') + v := ip[i] + buf = append(buf, hexDigit[v&0xF]) + buf = append(buf, '.') + buf = append(buf, hexDigit[v>>4]) + buf = append(buf, '.') } // Append "ip6.arpa." and return (buf already has the final .) - return buf.String() + "ip6.arpa.", nil + buf = append(buf, "ip6.arpa."...) + return string(buf), nil } // Find answer for name in dns message. diff --git a/libgo/go/net/dnsmsg.go b/libgo/go/net/dnsmsg.go index 97c5062..b6ebe11 100644 --- a/libgo/go/net/dnsmsg.go +++ b/libgo/go/net/dnsmsg.go @@ -7,11 +7,10 @@ // This is intended to support name resolution during Dial. // It doesn't have to be blazing fast. // -// Rather than write the usual handful of routines to pack and -// unpack every message that can appear on the wire, we use -// reflection to write a generic pack/unpack for structs and then -// use it. Thus, if in the future we need to define new message -// structs, no new pack/unpack/printing code needs to be written. +// Each message structure has a Walk method that is used by +// a generic pack/unpack routine. Thus, if in the future we need +// to define new message structs, no new pack/unpack/printing code +// needs to be written. // // The first half of this file defines the DNS message formats. // The second half implements the conversion to and from wire format. @@ -23,12 +22,6 @@ package net -import ( - "fmt" - "os" - "reflect" -) - // Packet formats // Wire constants. @@ -75,6 +68,20 @@ const ( dnsRcodeRefused = 5 ) +// A dnsStruct describes how to iterate over its fields to emulate +// reflective marshalling. +type dnsStruct interface { + // Walk iterates over fields of a structure and calls f + // with a reference to that field, the name of the field + // and a tag ("", "domain", "ipv4", "ipv6") specifying + // particular encodings. Possible concrete types + // for v are *uint16, *uint32, *string, or []byte, and + // *int, *bool in the case of dnsMsgHdr. + // Whenever f returns false, Walk must stop and return + // false, and otherwise return true. + Walk(f func(v interface{}, name, tag string) (ok bool)) (ok bool) +} + // The wire format for the DNS packet header. type dnsHeader struct { Id uint16 @@ -82,6 +89,15 @@ type dnsHeader struct { Qdcount, Ancount, Nscount, Arcount uint16 } +func (h *dnsHeader) Walk(f func(v interface{}, name, tag string) bool) bool { + return f(&h.Id, "Id", "") && + f(&h.Bits, "Bits", "") && + f(&h.Qdcount, "Qdcount", "") && + f(&h.Ancount, "Ancount", "") && + f(&h.Nscount, "Nscount", "") && + f(&h.Arcount, "Arcount", "") +} + const ( // dnsHeader.Bits _QR = 1 << 15 // query/response (response=1) @@ -98,6 +114,12 @@ type dnsQuestion struct { Qclass uint16 } +func (q *dnsQuestion) Walk(f func(v interface{}, name, tag string) bool) bool { + return f(&q.Name, "Name", "domain") && + f(&q.Qtype, "Qtype", "") && + f(&q.Qclass, "Qclass", "") +} + // DNS responses (resource records). // There are many types of messages, // but they all share the same header. @@ -113,7 +135,16 @@ func (h *dnsRR_Header) Header() *dnsRR_Header { return h } +func (h *dnsRR_Header) Walk(f func(v interface{}, name, tag string) bool) bool { + return f(&h.Name, "Name", "domain") && + f(&h.Rrtype, "Rrtype", "") && + f(&h.Class, "Class", "") && + f(&h.Ttl, "Ttl", "") && + f(&h.Rdlength, "Rdlength", "") +} + type dnsRR interface { + dnsStruct Header() *dnsRR_Header } @@ -128,6 +159,10 @@ func (rr *dnsRR_CNAME) Header() *dnsRR_Header { return &rr.Hdr } +func (rr *dnsRR_CNAME) Walk(f func(v interface{}, name, tag string) bool) bool { + return rr.Hdr.Walk(f) && f(&rr.Cname, "Cname", "domain") +} + type dnsRR_HINFO struct { Hdr dnsRR_Header Cpu string @@ -138,6 +173,10 @@ func (rr *dnsRR_HINFO) Header() *dnsRR_Header { return &rr.Hdr } +func (rr *dnsRR_HINFO) Walk(f func(v interface{}, name, tag string) bool) bool { + return rr.Hdr.Walk(f) && f(&rr.Cpu, "Cpu", "") && f(&rr.Os, "Os", "") +} + type dnsRR_MB struct { Hdr dnsRR_Header Mb string `net:"domain-name"` @@ -147,6 +186,10 @@ func (rr *dnsRR_MB) Header() *dnsRR_Header { return &rr.Hdr } +func (rr *dnsRR_MB) Walk(f func(v interface{}, name, tag string) bool) bool { + return rr.Hdr.Walk(f) && f(&rr.Mb, "Mb", "domain") +} + type dnsRR_MG struct { Hdr dnsRR_Header Mg string `net:"domain-name"` @@ -156,6 +199,10 @@ func (rr *dnsRR_MG) Header() *dnsRR_Header { return &rr.Hdr } +func (rr *dnsRR_MG) Walk(f func(v interface{}, name, tag string) bool) bool { + return rr.Hdr.Walk(f) && f(&rr.Mg, "Mg", "domain") +} + type dnsRR_MINFO struct { Hdr dnsRR_Header Rmail string `net:"domain-name"` @@ -166,6 +213,10 @@ func (rr *dnsRR_MINFO) Header() *dnsRR_Header { return &rr.Hdr } +func (rr *dnsRR_MINFO) Walk(f func(v interface{}, name, tag string) bool) bool { + return rr.Hdr.Walk(f) && f(&rr.Rmail, "Rmail", "domain") && f(&rr.Email, "Email", "domain") +} + type dnsRR_MR struct { Hdr dnsRR_Header Mr string `net:"domain-name"` @@ -175,6 +226,10 @@ func (rr *dnsRR_MR) Header() *dnsRR_Header { return &rr.Hdr } +func (rr *dnsRR_MR) Walk(f func(v interface{}, name, tag string) bool) bool { + return rr.Hdr.Walk(f) && f(&rr.Mr, "Mr", "domain") +} + type dnsRR_MX struct { Hdr dnsRR_Header Pref uint16 @@ -185,6 +240,10 @@ func (rr *dnsRR_MX) Header() *dnsRR_Header { return &rr.Hdr } +func (rr *dnsRR_MX) Walk(f func(v interface{}, name, tag string) bool) bool { + return rr.Hdr.Walk(f) && f(&rr.Pref, "Pref", "") && f(&rr.Mx, "Mx", "domain") +} + type dnsRR_NS struct { Hdr dnsRR_Header Ns string `net:"domain-name"` @@ -194,6 +253,10 @@ func (rr *dnsRR_NS) Header() *dnsRR_Header { return &rr.Hdr } +func (rr *dnsRR_NS) Walk(f func(v interface{}, name, tag string) bool) bool { + return rr.Hdr.Walk(f) && f(&rr.Ns, "Ns", "domain") +} + type dnsRR_PTR struct { Hdr dnsRR_Header Ptr string `net:"domain-name"` @@ -203,6 +266,10 @@ func (rr *dnsRR_PTR) Header() *dnsRR_Header { return &rr.Hdr } +func (rr *dnsRR_PTR) Walk(f func(v interface{}, name, tag string) bool) bool { + return rr.Hdr.Walk(f) && f(&rr.Ptr, "Ptr", "domain") +} + type dnsRR_SOA struct { Hdr dnsRR_Header Ns string `net:"domain-name"` @@ -218,6 +285,17 @@ func (rr *dnsRR_SOA) Header() *dnsRR_Header { return &rr.Hdr } +func (rr *dnsRR_SOA) Walk(f func(v interface{}, name, tag string) bool) bool { + return rr.Hdr.Walk(f) && + f(&rr.Ns, "Ns", "domain") && + f(&rr.Mbox, "Mbox", "domain") && + f(&rr.Serial, "Serial", "") && + f(&rr.Refresh, "Refresh", "") && + f(&rr.Retry, "Retry", "") && + f(&rr.Expire, "Expire", "") && + f(&rr.Minttl, "Minttl", "") +} + type dnsRR_TXT struct { Hdr dnsRR_Header Txt string // not domain name @@ -227,6 +305,10 @@ func (rr *dnsRR_TXT) Header() *dnsRR_Header { return &rr.Hdr } +func (rr *dnsRR_TXT) Walk(f func(v interface{}, name, tag string) bool) bool { + return rr.Hdr.Walk(f) && f(&rr.Txt, "Txt", "") +} + type dnsRR_SRV struct { Hdr dnsRR_Header Priority uint16 @@ -239,6 +321,14 @@ func (rr *dnsRR_SRV) Header() *dnsRR_Header { return &rr.Hdr } +func (rr *dnsRR_SRV) Walk(f func(v interface{}, name, tag string) bool) bool { + return rr.Hdr.Walk(f) && + f(&rr.Priority, "Priority", "") && + f(&rr.Weight, "Weight", "") && + f(&rr.Port, "Port", "") && + f(&rr.Target, "Target", "domain") +} + type dnsRR_A struct { Hdr dnsRR_Header A uint32 `net:"ipv4"` @@ -248,6 +338,10 @@ func (rr *dnsRR_A) Header() *dnsRR_Header { return &rr.Hdr } +func (rr *dnsRR_A) Walk(f func(v interface{}, name, tag string) bool) bool { + return rr.Hdr.Walk(f) && f(&rr.A, "A", "ipv4") +} + type dnsRR_AAAA struct { Hdr dnsRR_Header AAAA [16]byte `net:"ipv6"` @@ -257,6 +351,10 @@ func (rr *dnsRR_AAAA) Header() *dnsRR_Header { return &rr.Hdr } +func (rr *dnsRR_AAAA) Walk(f func(v interface{}, name, tag string) bool) bool { + return rr.Hdr.Walk(f) && f(rr.AAAA[:], "AAAA", "ipv6") +} + // Packing and unpacking. // // All the packers and unpackers take a (msg []byte, off int) @@ -386,134 +484,107 @@ Loop: return s, off1, true } -// TODO(rsc): Move into generic library? -// Pack a reflect.StructValue into msg. Struct members can only be uint16, uint32, string, -// [n]byte, and other (often anonymous) structs. -func packStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok bool) { - for i := 0; i < val.NumField(); i++ { - f := val.Type().Field(i) - switch fv := val.Field(i); fv.Kind() { +// packStruct packs a structure into msg at specified offset off, and +// returns off1 such that msg[off:off1] is the encoded data. +func packStruct(any dnsStruct, msg []byte, off int) (off1 int, ok bool) { + ok = any.Walk(func(field interface{}, name, tag string) bool { + switch fv := field.(type) { default: - fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type) - return len(msg), false - case reflect.Struct: - off, ok = packStructValue(fv, msg, off) - case reflect.Uint16: + println("net: dns: unknown packing type") + return false + case *uint16: + i := *fv if off+2 > len(msg) { - return len(msg), false + return false } - i := fv.Uint() msg[off] = byte(i >> 8) msg[off+1] = byte(i) off += 2 - case reflect.Uint32: - if off+4 > len(msg) { - return len(msg), false - } - i := fv.Uint() + case *uint32: + i := *fv msg[off] = byte(i >> 24) msg[off+1] = byte(i >> 16) msg[off+2] = byte(i >> 8) msg[off+3] = byte(i) off += 4 - case reflect.Array: - if fv.Type().Elem().Kind() != reflect.Uint8 { - fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type) - return len(msg), false - } - n := fv.Len() + case []byte: + n := len(fv) if off+n > len(msg) { - return len(msg), false + return false } - reflect.Copy(reflect.ValueOf(msg[off:off+n]), fv) + copy(msg[off:off+n], fv) off += n - case reflect.String: - // There are multiple string encodings. - // The tag distinguishes ordinary strings from domain names. - s := fv.String() - switch f.Tag { + case *string: + s := *fv + switch tag { default: - fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag) - return len(msg), false - case `net:"domain-name"`: + println("net: dns: unknown string tag", tag) + return false + case "domain": off, ok = packDomainName(s, msg, off) if !ok { - return len(msg), false + return false } case "": // Counted string: 1 byte length. if len(s) > 255 || off+1+len(s) > len(msg) { - return len(msg), false + return false } msg[off] = byte(len(s)) off++ off += copy(msg[off:], s) } } + return true + }) + if !ok { + return len(msg), false } return off, true } -func structValue(any interface{}) reflect.Value { - return reflect.ValueOf(any).Elem() -} - -func packStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) { - off, ok = packStructValue(structValue(any), msg, off) - return off, ok -} - -// TODO(rsc): Move into generic library? -// Unpack a reflect.StructValue from msg. -// Same restrictions as packStructValue. -func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok bool) { - for i := 0; i < val.NumField(); i++ { - f := val.Type().Field(i) - switch fv := val.Field(i); fv.Kind() { +// unpackStruct decodes msg[off:] into the given structure, and +// returns off1 such that msg[off:off1] is the encoded data. +func unpackStruct(any dnsStruct, msg []byte, off int) (off1 int, ok bool) { + ok = any.Walk(func(field interface{}, name, tag string) bool { + switch fv := field.(type) { default: - fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type) - return len(msg), false - case reflect.Struct: - off, ok = unpackStructValue(fv, msg, off) - case reflect.Uint16: + println("net: dns: unknown packing type") + return false + case *uint16: if off+2 > len(msg) { - return len(msg), false + return false } - i := uint16(msg[off])<<8 | uint16(msg[off+1]) - fv.SetUint(uint64(i)) + *fv = uint16(msg[off])<<8 | uint16(msg[off+1]) off += 2 - case reflect.Uint32: + case *uint32: if off+4 > len(msg) { - return len(msg), false + return false } - i := uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | uint32(msg[off+2])<<8 | uint32(msg[off+3]) - fv.SetUint(uint64(i)) + *fv = uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | + uint32(msg[off+2])<<8 | uint32(msg[off+3]) off += 4 - case reflect.Array: - if fv.Type().Elem().Kind() != reflect.Uint8 { - fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type) - return len(msg), false - } - n := fv.Len() + case []byte: + n := len(fv) if off+n > len(msg) { - return len(msg), false + return false } - reflect.Copy(fv, reflect.ValueOf(msg[off:off+n])) + copy(fv, msg[off:off+n]) off += n - case reflect.String: + case *string: var s string - switch f.Tag { + switch tag { default: - fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag) - return len(msg), false - case `net:"domain-name"`: + println("net: dns: unknown string tag", tag) + return false + case "domain": s, off, ok = unpackDomainName(msg, off) if !ok { - return len(msg), false + return false } case "": if off >= len(msg) || off+1+int(msg[off]) > len(msg) { - return len(msg), false + return false } n := int(msg[off]) off++ @@ -524,51 +595,77 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok boo off += n s = string(b) } - fv.SetString(s) + *fv = s } + return true + }) + if !ok { + return len(msg), false } return off, true } -func unpackStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) { - off, ok = unpackStructValue(structValue(any), msg, off) - return off, ok -} - -// Generic struct printer. -// 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 { +// Generic struct printer. Prints fields with tag "ipv4" or "ipv6" +// as IP addresses. +func printStruct(any dnsStruct) string { s := "{" - for i := 0; i < val.NumField(); i++ { - if i > 0 { + i := 0 + any.Walk(func(val interface{}, name, tag string) bool { + i++ + if i > 1 { s += ", " } - f := val.Type().Field(i) - if !f.Anonymous { - s += f.Name + "=" - } - 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 == `net:"ipv4"` { - i := fv.Uint() + s += name + "=" + switch tag { + case "ipv4": + i := val.(uint32) s += IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String() - } else if fv := fval; fv.Kind() == reflect.Array && f.Tag == `net:"ipv6"` { - i := fv.Interface().([]byte) + case "ipv6": + i := val.([]byte) s += IP(i).String() - } else { - s += fmt.Sprint(fval.Interface()) + default: + var i int64 + switch v := val.(type) { + default: + // can't really happen. + s += "" + return true + case *string: + s += *v + return true + case []byte: + s += string(v) + return true + case *bool: + if *v { + s += "true" + } else { + s += "false" + } + return true + case *int: + i = int64(*v) + case *uint: + i = int64(*v) + case *uint8: + i = int64(*v) + case *uint16: + i = int64(*v) + case *uint32: + i = int64(*v) + case *uint64: + i = int64(*v) + case *uintptr: + i = int64(*v) + } + s += itoa(int(i)) } - } + return true + }) s += "}" return s } -func printStruct(any interface{}) string { return printStructValue(structValue(any)) } - // Resource record packer. func packRR(rr dnsRR, msg []byte, off int) (off2 int, ok bool) { var off1 int @@ -627,6 +724,17 @@ type dnsMsgHdr struct { rcode int } +func (h *dnsMsgHdr) Walk(f func(v interface{}, name, tag string) bool) bool { + return f(&h.id, "id", "") && + f(&h.response, "response", "") && + f(&h.opcode, "opcode", "") && + f(&h.authoritative, "authoritative", "") && + f(&h.truncated, "truncated", "") && + f(&h.recursion_desired, "recursion_desired", "") && + f(&h.recursion_available, "recursion_available", "") && + f(&h.rcode, "rcode", "") +} + type dnsMsg struct { dnsMsgHdr question []dnsQuestion diff --git a/libgo/go/net/dnsmsg_test.go b/libgo/go/net/dnsmsg_test.go index 06152a0..c39dbdb 100644 --- a/libgo/go/net/dnsmsg_test.go +++ b/libgo/go/net/dnsmsg_test.go @@ -6,6 +6,7 @@ package net import ( "encoding/hex" + "reflect" "testing" ) @@ -19,6 +20,7 @@ func TestDNSParseSRVReply(t *testing.T) { if !ok { t.Fatalf("unpacking packet failed") } + msg.String() // exercise this code path if g, e := len(msg.answer), 5; g != e { t.Errorf("len(msg.answer) = %d; want %d", g, e) } @@ -38,6 +40,16 @@ func TestDNSParseSRVReply(t *testing.T) { t.Errorf("len(addrs) = %d; want %d", g, e) t.Logf("addrs = %#v", addrs) } + // repack and unpack. + data2, ok := msg.Pack() + msg2 := new(dnsMsg) + msg2.Unpack(data2) + switch { + case !ok: + t.Errorf("failed to repack message") + case !reflect.DeepEqual(msg, msg2): + t.Errorf("repacked message differs from original") + } } func TestDNSParseCorruptSRVReply(t *testing.T) { @@ -50,6 +62,7 @@ func TestDNSParseCorruptSRVReply(t *testing.T) { if !ok { t.Fatalf("unpacking packet failed") } + msg.String() // exercise this code path if g, e := len(msg.answer), 5; g != e { t.Errorf("len(msg.answer) = %d; want %d", g, e) } diff --git a/libgo/go/net/fd_linux.go b/libgo/go/net/fd_linux.go index a1d62ac..085e423 100644 --- a/libgo/go/net/fd_linux.go +++ b/libgo/go/net/fd_linux.go @@ -84,7 +84,8 @@ func (p *pollster) StopWaiting(fd int, bits uint) { events, already := p.events[fd] if !already { - print("Epoll unexpected fd=", fd, "\n") + // The fd returned by the kernel may have been + // cancelled already; return silently. return } diff --git a/libgo/go/net/file_test.go b/libgo/go/net/file_test.go index 868388e..95c0b66 100644 --- a/libgo/go/net/file_test.go +++ b/libgo/go/net/file_test.go @@ -27,7 +27,8 @@ type connFile interface { } func testFileListener(t *testing.T, net, laddr string) { - if net == "tcp" { + switch net { + case "tcp", "tcp4", "tcp6": laddr += ":0" // any available port } l, err := Listen(net, laddr) @@ -55,20 +56,52 @@ func testFileListener(t *testing.T, net, laddr string) { } } +var fileListenerTests = []struct { + net string + laddr string + ipv6 bool // test with underlying AF_INET6 socket + linux bool // test with abstract unix domain socket, a Linux-ism +}{ + {net: "tcp", laddr: ""}, + {net: "tcp", laddr: "0.0.0.0"}, + {net: "tcp", laddr: "[::ffff:0.0.0.0]"}, + {net: "tcp", laddr: "[::]", ipv6: true}, + + {net: "tcp", laddr: "127.0.0.1"}, + {net: "tcp", laddr: "[::ffff:127.0.0.1]"}, + {net: "tcp", laddr: "[::1]", ipv6: true}, + + {net: "tcp4", laddr: ""}, + {net: "tcp4", laddr: "0.0.0.0"}, + {net: "tcp4", laddr: "[::ffff:0.0.0.0]"}, + + {net: "tcp4", laddr: "127.0.0.1"}, + {net: "tcp4", laddr: "[::ffff:127.0.0.1]"}, + + {net: "tcp6", laddr: "", ipv6: true}, + {net: "tcp6", laddr: "[::]", ipv6: true}, + + {net: "tcp6", laddr: "[::1]", ipv6: true}, + + {net: "unix", laddr: "@gotest/net", linux: true}, + {net: "unixpacket", laddr: "@gotest/net", linux: true}, +} + func TestFileListener(t *testing.T) { - if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { + switch runtime.GOOS { + case "plan9", "windows": + t.Logf("skipping test on %q", runtime.GOOS) return } - testFileListener(t, "tcp", "127.0.0.1") - testFileListener(t, "tcp", "127.0.0.1") - 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]") - } - if runtime.GOOS == "linux" { - testFileListener(t, "unix", "@gotest/net") - testFileListener(t, "unixpacket", "@gotest/net") + + for _, tt := range fileListenerTests { + if skipServerTest(tt.net, "unix", tt.laddr, tt.ipv6, false, tt.linux) { + continue + } + if skipServerTest(tt.net, "unixpacket", tt.laddr, tt.ipv6, false, tt.linux) { + continue + } + testFileListener(t, tt.net, tt.laddr) } } @@ -98,9 +131,13 @@ func testFilePacketConn(t *testing.T, pcf packetConnFile, listen bool) { } func testFilePacketConnListen(t *testing.T, net, laddr string) { + switch net { + case "udp", "udp4", "udp6": + laddr += ":0" // any available port + } l, err := ListenPacket(net, laddr) if err != nil { - t.Fatalf("Listen failed: %v", err) + t.Fatalf("ListenPacket failed: %v", err) } testFilePacketConn(t, l.(packetConnFile), true) if err := l.Close(); err != nil { @@ -109,6 +146,10 @@ func testFilePacketConnListen(t *testing.T, net, laddr string) { } func testFilePacketConnDial(t *testing.T, net, raddr string) { + switch net { + case "udp", "udp4", "udp6": + raddr += ":12345" + } c, err := Dial(net, raddr) if err != nil { t.Fatalf("Dial failed: %v", err) @@ -119,19 +160,42 @@ func testFilePacketConnDial(t *testing.T, net, raddr string) { } } +var filePacketConnTests = []struct { + net string + addr string + ipv6 bool // test with underlying AF_INET6 socket + linux bool // test with abstract unix domain socket, a Linux-ism +}{ + {net: "udp", addr: "127.0.0.1"}, + {net: "udp", addr: "[::ffff:127.0.0.1]"}, + {net: "udp", addr: "[::1]", ipv6: true}, + + {net: "udp4", addr: "127.0.0.1"}, + {net: "udp4", addr: "[::ffff:127.0.0.1]"}, + + {net: "udp6", addr: "[::1]", ipv6: true}, + + {net: "unixgram", addr: "@gotest3/net", linux: true}, +} + func TestFilePacketConn(t *testing.T) { - if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { + switch runtime.GOOS { + case "plan9", "windows": + t.Logf("skipping test on %q", runtime.GOOS) return } - testFilePacketConnListen(t, "udp", "127.0.0.1:0") - testFilePacketConnDial(t, "udp", "127.0.0.1:12345") - if supportsIPv6 { - testFilePacketConnListen(t, "udp", "[::1]:0") - } - if supportsIPv6 && supportsIPv4map { - testFilePacketConnDial(t, "udp", "[::ffff:127.0.0.1]:12345") - } - if runtime.GOOS == "linux" { - testFilePacketConnListen(t, "unixgram", "@gotest1/net") + + for _, tt := range filePacketConnTests { + if skipServerTest(tt.net, "unixgram", tt.addr, tt.ipv6, false, tt.linux) { + continue + } + testFilePacketConnListen(t, tt.net, tt.addr) + switch tt.addr { + case "", "0.0.0.0", "[::ffff:0.0.0.0]", "[::]": + default: + if tt.net != "unixgram" { + testFilePacketConnDial(t, tt.net, tt.addr) + } + } } } diff --git a/libgo/go/net/http/client_test.go b/libgo/go/net/http/client_test.go index aa0bf4b..e00b62e 100644 --- a/libgo/go/net/http/client_test.go +++ b/libgo/go/net/http/client_test.go @@ -238,9 +238,9 @@ func TestRedirects(t *testing.T) { } var expectedCookies = []*Cookie{ - &Cookie{Name: "ChocolateChip", Value: "tasty"}, - &Cookie{Name: "First", Value: "Hit"}, - &Cookie{Name: "Second", Value: "Hit"}, + {Name: "ChocolateChip", Value: "tasty"}, + {Name: "First", Value: "Hit"}, + {Name: "Second", Value: "Hit"}, } var echoCookiesRedirectHandler = HandlerFunc(func(w ResponseWriter, r *Request) { diff --git a/libgo/go/net/http/request.go b/libgo/go/net/http/request.go index 5277657..f5bc6eb 100644 --- a/libgo/go/net/http/request.go +++ b/libgo/go/net/http/request.go @@ -455,11 +455,13 @@ func ReadRequest(b *bufio.Reader) (req *Request, err error) { // First line: GET /index.html HTTP/1.0 var s string if s, err = tp.ReadLine(); err != nil { + return nil, err + } + defer func() { if err == io.EOF { err = io.ErrUnexpectedEOF } - return nil, err - } + }() var f []string if f = strings.SplitN(s, " ", 3); len(f) < 3 { diff --git a/libgo/go/net/http/request_test.go b/libgo/go/net/http/request_test.go index 7a3556d..6e00b9b 100644 --- a/libgo/go/net/http/request_test.go +++ b/libgo/go/net/http/request_test.go @@ -5,6 +5,7 @@ package http_test import ( + "bufio" "bytes" "fmt" "io" @@ -177,6 +178,24 @@ func TestRequestMultipartCallOrder(t *testing.T) { } } +var readRequestErrorTests = []struct { + in string + err error +}{ + {"GET / HTTP/1.1\r\nheader:foo\r\n\r\n", nil}, + {"GET / HTTP/1.1\r\nheader:foo\r\n", io.ErrUnexpectedEOF}, + {"", io.EOF}, +} + +func TestReadRequestErrors(t *testing.T) { + for i, tt := range readRequestErrorTests { + _, err := ReadRequest(bufio.NewReader(strings.NewReader(tt.in))) + if err != tt.err { + t.Errorf("%d. got error = %v; want %v", i, err, tt.err) + } + } +} + func testMissingFile(t *testing.T, req *Request) { f, fh, err := req.FormFile("missing") if f != nil { diff --git a/libgo/go/net/http/server.go b/libgo/go/net/http/server.go index fa0df54..228ac40 100644 --- a/libgo/go/net/http/server.go +++ b/libgo/go/net/http/server.go @@ -601,7 +601,7 @@ func (c *conn) serve() { // while they're still writing their // request. Undefined behavior. msg = "413 Request Entity Too Large" - } else if err == io.ErrUnexpectedEOF { + } else if err == io.EOF { break // Don't reply } else if neterr, ok := err.(net.Error); ok && neterr.Timeout() { break // Don't reply diff --git a/libgo/go/net/http/transport.go b/libgo/go/net/http/transport.go index 09579f8..0249759 100644 --- a/libgo/go/net/http/transport.go +++ b/libgo/go/net/http/transport.go @@ -196,7 +196,7 @@ func (t *Transport) CloseIdleConnections() { pconn.close() } } - t.idleConn = nil + t.idleConn = make(map[string][]*persistConn) } // diff --git a/libgo/go/net/http/transport_test.go b/libgo/go/net/http/transport_test.go index cbb3884..a9e401d 100644 --- a/libgo/go/net/http/transport_test.go +++ b/libgo/go/net/http/transport_test.go @@ -698,6 +698,32 @@ func TestTransportPersistConnLeak(t *testing.T) { } } +// This used to crash; http://golang.org/issue/3266 +func TestTransportIdleConnCrash(t *testing.T) { + tr := &Transport{} + c := &Client{Transport: tr} + + unblockCh := make(chan bool, 1) + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + <-unblockCh + tr.CloseIdleConnections() + })) + defer ts.Close() + + didreq := make(chan bool) + go func() { + res, err := c.Get(ts.URL) + if err != nil { + t.Error(err) + } else { + res.Body.Close() // returns idle conn + } + didreq <- true + }() + unblockCh <- true + <-didreq +} + type fooProto struct{} func (fooProto) RoundTrip(req *Request) (*Response, error) { diff --git a/libgo/go/net/interface.go b/libgo/go/net/interface.go index f25d046..ee23570 100644 --- a/libgo/go/net/interface.go +++ b/libgo/go/net/interface.go @@ -78,7 +78,7 @@ func (ifi *Interface) MulticastAddrs() ([]Addr, error) { return interfaceMulticastAddrTable(ifi.Index) } -// Interfaces returns a list of the systems's network interfaces. +// Interfaces returns a list of the system's network interfaces. func Interfaces() ([]Interface, error) { return interfaceTable(0) } diff --git a/libgo/go/net/interface_linux.go b/libgo/go/net/interface_linux.go index 15c2f37..8c9c304 100644 --- a/libgo/go/net/interface_linux.go +++ b/libgo/go/net/interface_linux.go @@ -7,7 +7,6 @@ package net import ( - "fmt" "os" "syscall" "unsafe" @@ -194,7 +193,9 @@ func parseProcNetIGMP(path string, ifi *Interface) []Addr { name = f[1] case len(f[0]) == 8: if ifi == nil || name == ifi.Name { - fmt.Sscanf(f[0], "%08x", &b) + for i := 0; i+1 < len(f[0]); i += 2 { + b[i/2], _ = xtoi2(f[0][i:i+2], 0) + } ifma := IPAddr{IP: IPv4(b[3], b[2], b[1], b[0])} ifmat = append(ifmat, ifma.toAddr()) } @@ -218,10 +219,11 @@ func parseProcNetIGMP6(path string, ifi *Interface) []Addr { continue } if ifi == nil || f[1] == ifi.Name { - fmt.Sscanf(f[2], "%32x", &b) + for i := 0; i+1 < len(f[2]); i += 2 { + b[i/2], _ = xtoi2(f[2][i:i+2], 0) + } 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/iprawsock_posix.go b/libgo/go/net/iprawsock_posix.go index 9caa869..6bbe67c 100644 --- a/libgo/go/net/iprawsock_posix.go +++ b/libgo/go/net/iprawsock_posix.go @@ -34,6 +34,13 @@ func (a *IPAddr) family() int { return syscall.AF_INET6 } +func (a *IPAddr) isWildcard() bool { + if a == nil || a.IP == nil { + return true + } + return a.IP.IsUnspecified() +} + func (a *IPAddr) sockaddr(family int) (syscall.Sockaddr, error) { return ipToSockaddr(family, a.IP, 0) } diff --git a/libgo/go/net/ipsock_posix.go b/libgo/go/net/ipsock_posix.go index 4841057..ed31319 100644 --- a/libgo/go/net/ipsock_posix.go +++ b/libgo/go/net/ipsock_posix.go @@ -38,6 +38,7 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { continue } defer closesocket(s) + syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0) sa, err := probes[i].la.toAddr().sockaddr(syscall.AF_INET6) if err != nil { continue @@ -55,58 +56,75 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { // favoriteAddrFamily returns the appropriate address family to // the given net, laddr, raddr 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, laddr, raddr sockaddr, mode string) int { +// and laddr is a wildcard, it assumes that the user wants to +// make a passive connection with a wildcard address family, both +// AF_INET and AF_INET6, and a wildcard address like following: +// +// 1. A wild-wild listen, "tcp" + "" +// If the platform supports both IPv6 and IPv6 IPv4-mapping +// capabilities, we assume that the user want to listen on +// both IPv4 and IPv6 wildcard address over an AF_INET6 +// socket with IPV6_V6ONLY=0. Otherwise we prefer an IPv4 +// wildcard address listen over an AF_INET socket. +// +// 2. A wild-ipv4wild listen, "tcp" + "0.0.0.0" +// Same as 1. +// +// 3. A wild-ipv6wild listen, "tcp" + "[::]" +// Almost same as 1 but we prefer an IPv6 wildcard address +// listen over an AF_INET6 socket with IPV6_V6ONLY=0 when +// the platform supports IPv6 capability but not IPv6 IPv4- +// mapping capability. +// +// 4. A ipv4-ipv4wild listen, "tcp4" + "" or "0.0.0.0" +// We use an IPv4 (AF_INET) wildcard address listen. +// +// 5. A ipv6-ipv6wild listen, "tcp6" + "" or "[::]" +// We use an IPv6 (AF_INET6, IPV6_V6ONLY=1) wildcard address +// listen. +// +// Otherwise guess: if the addresses are IPv4 then returns AF_INET, +// or else returns AF_INET6. It also returns a boolean value what +// designates IPV6_V6ONLY option. +// +// Note that OpenBSD allows neither "net.inet6.ip6.v6only=1" change +// nor IPPROTO_IPV6 level IPV6_V6ONLY socket option setting. +func favoriteAddrFamily(net string, laddr, raddr sockaddr, mode string) (family int, ipv6only bool) { switch net[len(net)-1] { case '4': - return syscall.AF_INET + return syscall.AF_INET, false case '6': - return syscall.AF_INET6 + return syscall.AF_INET6, true } - if mode == "listen" { - // Note that OpenBSD allows neither "net.inet6.ip6.v6only" - // change nor IPPROTO_IPV6 level IPV6_V6ONLY socket option - // setting. - switch a := laddr.(type) { - case *TCPAddr: - if a.IP == nil && supportsIPv6 && supportsIPv4map { - return syscall.AF_INET6 - } - case *UDPAddr: - if a.IP == nil && supportsIPv6 && supportsIPv4map { - return syscall.AF_INET6 - } - case *IPAddr: - if a.IP == nil && supportsIPv6 && supportsIPv4map { - return syscall.AF_INET6 - } + if mode == "listen" && laddr.isWildcard() { + if supportsIPv4map { + return syscall.AF_INET6, false } + return laddr.family(), false } if (laddr == nil || laddr.family() == syscall.AF_INET) && (raddr == nil || raddr.family() == syscall.AF_INET) { - return syscall.AF_INET + return syscall.AF_INET, false } - return syscall.AF_INET6 + return syscall.AF_INET6, false } -// Internet sockets (TCP, UDP) +// Internet sockets (TCP, UDP, IP) -// A sockaddr represents a TCP or UDP network address that can +// A sockaddr represents a TCP, UDP or IP network address that can // be converted into a syscall.Sockaddr. type sockaddr interface { Addr - sockaddr(family int) (syscall.Sockaddr, error) family() int + isWildcard() bool + sockaddr(family int) (syscall.Sockaddr, error) } func internetSocket(net string, laddr, raddr sockaddr, sotype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) { var la, ra syscall.Sockaddr - family := favoriteAddrFamily(net, laddr, raddr, mode) + family, ipv6only := favoriteAddrFamily(net, laddr, raddr, mode) if laddr != nil { if la, err = laddr.sockaddr(family); err != nil { goto Error @@ -117,7 +135,7 @@ func internetSocket(net string, laddr, raddr sockaddr, sotype, proto int, mode s goto Error } } - fd, err = socket(net, family, sotype, proto, la, ra, toAddr) + fd, err = socket(net, family, sotype, proto, ipv6only, la, ra, toAddr) if err != nil { goto Error } @@ -152,7 +170,7 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, error) { } // 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. + // which it refuses to do. Rewrite to the IPv6 unspecified address. if ip.Equal(IPv4zero) { ip = IPv6zero } diff --git a/libgo/go/net/mac.go b/libgo/go/net/mac.go index e0637d0..d616b1f 100644 --- a/libgo/go/net/mac.go +++ b/libgo/go/net/mac.go @@ -6,24 +6,26 @@ package net -import ( - "bytes" - "errors" - "fmt" -) +import "errors" + +const hexDigit = "0123456789abcdef" // A HardwareAddr represents a physical hardware address. type HardwareAddr []byte func (a HardwareAddr) String() string { - var buf bytes.Buffer + if len(a) == 0 { + return "" + } + buf := make([]byte, 0, len(a)*3-1) for i, b := range a { if i > 0 { - buf.WriteByte(':') + buf = append(buf, ':') } - fmt.Fprintf(&buf, "%02x", b) + buf = append(buf, hexDigit[b>>4]) + buf = append(buf, hexDigit[b&0xF]) } - return buf.String() + return string(buf) } // ParseMAC parses s as an IEEE 802 MAC-48, EUI-48, or EUI-64 using one of the diff --git a/libgo/go/net/mac_test.go b/libgo/go/net/mac_test.go index 3837e740..8f9dc66 100644 --- a/libgo/go/net/mac_test.go +++ b/libgo/go/net/mac_test.go @@ -43,12 +43,24 @@ func match(err error, s string) bool { return err != nil && strings.Contains(err.Error(), s) } -func TestParseMAC(t *testing.T) { - for _, tt := range mactests { +func TestMACParseString(t *testing.T) { + for i, tt := range mactests { out, err := ParseMAC(tt.in) if !reflect.DeepEqual(out, tt.out) || !match(err, tt.err) { t.Errorf("ParseMAC(%q) = %v, %v, want %v, %v", tt.in, out, err, tt.out, tt.err) } + if tt.err == "" { + // Verify that serialization works too, and that it round-trips. + s := out.String() + out2, err := ParseMAC(s) + if err != nil { + t.Errorf("%d. ParseMAC(%q) = %v", i, s, err) + continue + } + if !reflect.DeepEqual(out2, out) { + t.Errorf("%d. ParseMAC(%q) = %v, want %v", i, s, out2, out) + } + } } } diff --git a/libgo/go/net/mail/message.go b/libgo/go/net/mail/message.go index bf22c71..0917bbe 100644 --- a/libgo/go/net/mail/message.go +++ b/libgo/go/net/mail/message.go @@ -394,8 +394,7 @@ func (p *addrParser) consumeAtom(dot bool) (atom string, err error) { i := 1 for ; i < p.len() && isAtext((*p)[i], dot); i++ { } - // TODO(dsymonds): Remove the []byte() conversion here when 6g doesn't need it. - atom, *p = string([]byte((*p)[:i])), (*p)[i:] + atom, *p = string((*p)[:i]), (*p)[i:] return atom, nil } diff --git a/libgo/go/net/multicast_test.go b/libgo/go/net/multicast_test.go index 1d760c2..67261b1 100644 --- a/libgo/go/net/multicast_test.go +++ b/libgo/go/net/multicast_test.go @@ -47,9 +47,11 @@ var multicastListenerTests = []struct { func TestMulticastListener(t *testing.T) { switch runtime.GOOS { case "netbsd", "openbsd", "plan9", "windows": + t.Logf("skipping test on %q", runtime.GOOS) return case "linux": if runtime.GOARCH == "arm" || runtime.GOARCH == "alpha" { + t.Logf("skipping test on %q/%q", runtime.GOOS, runtime.GOARCH) return } } @@ -86,7 +88,13 @@ func TestMulticastListener(t *testing.T) { func TestSimpleMulticastListener(t *testing.T) { switch runtime.GOOS { case "plan9": + t.Logf("skipping test on %q", runtime.GOOS) return + case "windows": + if testing.Short() || !*testExternal { + t.Logf("skipping test on windows to avoid firewall") + return + } } for _, tt := range multicastListenerTests { diff --git a/libgo/go/net/net.go b/libgo/go/net/net.go index bf242ff..9ebcdbe 100644 --- a/libgo/go/net/net.go +++ b/libgo/go/net/net.go @@ -54,6 +54,8 @@ type Addr interface { } // Conn is a generic stream-oriented network connection. +// +// Multiple goroutines may invoke methods on a Conn simultaneously. type Conn interface { // Read reads data from the connection. // Read can be made to time out and return a Error with Timeout() == true @@ -66,6 +68,7 @@ type Conn interface { Write(b []byte) (n int, err error) // Close closes the connection. + // Any blocked Read or Write operations will be unblocked and return errors. Close() error // LocalAddr returns the local network address. @@ -89,11 +92,11 @@ type Conn interface { // A zero value for t means I/O operations will not time out. SetDeadline(t time.Time) error - // SetReadDeadline sets the deadline for Read calls. + // SetReadDeadline sets the deadline for future Read calls. // A zero value for t means Read will not time out. SetReadDeadline(t time.Time) error - // SetWriteDeadline sets the deadline for Write calls. + // SetWriteDeadline sets the deadline for future Write calls. // Even if write times out, it may return n > 0, indicating that // some of the data was successfully written. // A zero value for t means Write will not time out. @@ -108,6 +111,8 @@ type Error interface { } // PacketConn is a generic packet-oriented network connection. +// +// Multiple goroutines may invoke methods on a PacketConn simultaneously. type PacketConn interface { // ReadFrom reads a packet from the connection, // copying the payload into b. It returns the number of @@ -126,6 +131,7 @@ type PacketConn interface { WriteTo(b []byte, addr Addr) (n int, err error) // Close closes the connection. + // Any blocked ReadFrom or WriteTo operations will be unblocked and return errors. Close() error // LocalAddr returns the local network address. @@ -135,13 +141,13 @@ type PacketConn interface { // with the connection. SetDeadline(t time.Time) error - // SetReadDeadline sets the deadline for all Read calls to return. + // SetReadDeadline sets the deadline for future Read calls. // If the deadline is reached, Read will fail with a timeout // (see type Error) instead of blocking. // A zero value for t means Read will not time out. SetReadDeadline(t time.Time) error - // SetWriteDeadline sets the deadline for all Write calls to return. + // SetWriteDeadline sets the deadline for future Write calls. // If the deadline is reached, Write will fail with a timeout // (see type Error) instead of blocking. // A zero value for t means Write will not time out. @@ -151,11 +157,14 @@ type PacketConn interface { } // A Listener is a generic network listener for stream-oriented protocols. +// +// Multiple goroutines may invoke methods on a Listener simultaneously. type Listener interface { // Accept waits for and returns the next connection to the listener. Accept() (c Conn, err error) // Close closes the listener. + // Any blocked Accept operations will be unblocked and return errors. Close() error // Addr returns the listener's network address. diff --git a/libgo/go/net/net_test.go b/libgo/go/net/net_test.go index c1a90de..fd145e1 100644 --- a/libgo/go/net/net_test.go +++ b/libgo/go/net/net_test.go @@ -13,6 +13,7 @@ import ( func TestShutdown(t *testing.T) { if runtime.GOOS == "plan9" { + t.Logf("skipping test on %q", runtime.GOOS) return } l, err := Listen("tcp", "127.0.0.1:0") diff --git a/libgo/go/net/parse_test.go b/libgo/go/net/parse_test.go index dfbaba4..30fda45 100644 --- a/libgo/go/net/parse_test.go +++ b/libgo/go/net/parse_test.go @@ -13,7 +13,9 @@ import ( func TestReadLine(t *testing.T) { // /etc/services file does not exist on windows and Plan 9. - if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { + switch runtime.GOOS { + case "plan9", "windows": + t.Logf("skipping test on %q", runtime.GOOS) return } filename := "/etc/services" // a nice big file diff --git a/libgo/go/net/rpc/client.go b/libgo/go/net/rpc/client.go index f7abf21..db2da8e 100644 --- a/libgo/go/net/rpc/client.go +++ b/libgo/go/net/rpc/client.go @@ -36,7 +36,8 @@ type Call struct { // Client represents an RPC Client. // There may be multiple outstanding Calls associated -// with a single Client. +// with a single Client, and a Client may be used by +// multiple goroutines simultaneously. type Client struct { mutex sync.Mutex // protects pending, seq, request sending sync.Mutex diff --git a/libgo/go/net/server_test.go b/libgo/go/net/server_test.go index b986216..158b947 100644 --- a/libgo/go/net/server_test.go +++ b/libgo/go/net/server_test.go @@ -9,234 +9,461 @@ import ( "io" "os" "runtime" - "strings" "testing" "time" ) -// Do not test empty datagrams by default. -// It causes unexplained timeouts on some systems, -// including Snow Leopard. I think that the kernel -// doesn't quite expect them. -var testUDP = flag.Bool("udp", false, "whether to test UDP datagrams") +func skipServerTest(net, unixsotype, addr string, ipv6, ipv4map, linuxonly bool) bool { + switch runtime.GOOS { + case "linux": + case "plan9", "windows": + // "unix" sockets are not supported on Windows and Plan 9. + if net == unixsotype { + return true + } + default: + if net == unixsotype && linuxonly { + return true + } + } + switch addr { + case "", "0.0.0.0", "[::ffff:0.0.0.0]", "[::]": + if testing.Short() || !*testExternal { + return true + } + } + if ipv6 && !supportsIPv6 { + return true + } + if ipv4map && !supportsIPv4map { + return true + } + return false +} -func runEcho(fd io.ReadWriter, done chan<- int) { - var buf [1024]byte +var streamConnServerTests = []struct { + snet string // server side + saddr string + cnet string // client side + caddr string + ipv6 bool // test with underlying AF_INET6 socket + ipv4map bool // test with IPv6 IPv4-mapping functionality + empty bool // test with empty data + linux bool // test with abstract unix domain socket, a Linux-ism +}{ + {snet: "tcp", saddr: "", cnet: "tcp", caddr: "127.0.0.1"}, + {snet: "tcp", saddr: "0.0.0.0", cnet: "tcp", caddr: "127.0.0.1"}, + {snet: "tcp", saddr: "[::ffff:0.0.0.0]", cnet: "tcp", caddr: "127.0.0.1"}, + {snet: "tcp", saddr: "[::]", cnet: "tcp", caddr: "[::1]", ipv6: true}, - for { - n, err := fd.Read(buf[0:]) - if err != nil || n == 0 || string(buf[:n]) == "END" { - break + {snet: "tcp", saddr: "", cnet: "tcp", caddr: "[::1]", ipv4map: true}, + {snet: "tcp", saddr: "0.0.0.0", cnet: "tcp", caddr: "[::1]", ipv4map: true}, + {snet: "tcp", saddr: "[::ffff:0.0.0.0]", cnet: "tcp", caddr: "[::1]", ipv4map: true}, + {snet: "tcp", saddr: "[::]", cnet: "tcp", caddr: "127.0.0.1", ipv4map: true}, + + {snet: "tcp", saddr: "", cnet: "tcp4", caddr: "127.0.0.1"}, + {snet: "tcp", saddr: "0.0.0.0", cnet: "tcp4", caddr: "127.0.0.1"}, + {snet: "tcp", saddr: "[::ffff:0.0.0.0]", cnet: "tcp4", caddr: "127.0.0.1"}, + {snet: "tcp", saddr: "[::]", cnet: "tcp6", caddr: "[::1]", ipv6: true}, + + {snet: "tcp", saddr: "", cnet: "tcp6", caddr: "[::1]", ipv4map: true}, + {snet: "tcp", saddr: "0.0.0.0", cnet: "tcp6", caddr: "[::1]", ipv4map: true}, + {snet: "tcp", saddr: "[::ffff:0.0.0.0]", cnet: "tcp6", caddr: "[::1]", ipv4map: true}, + {snet: "tcp", saddr: "[::]", cnet: "tcp4", caddr: "127.0.0.1", ipv4map: true}, + + {snet: "tcp", saddr: "127.0.0.1", cnet: "tcp", caddr: "127.0.0.1"}, + {snet: "tcp", saddr: "[::ffff:127.0.0.1]", cnet: "tcp", caddr: "127.0.0.1"}, + {snet: "tcp", saddr: "[::1]", cnet: "tcp", caddr: "[::1]", ipv6: true}, + + {snet: "tcp4", saddr: "", cnet: "tcp4", caddr: "127.0.0.1"}, + {snet: "tcp4", saddr: "0.0.0.0", cnet: "tcp4", caddr: "127.0.0.1"}, + {snet: "tcp4", saddr: "[::ffff:0.0.0.0]", cnet: "tcp4", caddr: "127.0.0.1"}, + + {snet: "tcp4", saddr: "127.0.0.1", cnet: "tcp4", caddr: "127.0.0.1"}, + + {snet: "tcp6", saddr: "", cnet: "tcp6", caddr: "[::1]", ipv6: true}, + {snet: "tcp6", saddr: "[::]", cnet: "tcp6", caddr: "[::1]", ipv6: true}, + + {snet: "tcp6", saddr: "[::1]", cnet: "tcp6", caddr: "[::1]", ipv6: true}, + + {snet: "unix", saddr: "/tmp/gotest1.net", cnet: "unix", caddr: "/tmp/gotest1.net.local"}, + {snet: "unix", saddr: "@gotest2/net", cnet: "unix", caddr: "@gotest2/net.local", linux: true}, +} + +func TestStreamConnServer(t *testing.T) { + for _, tt := range streamConnServerTests { + if skipServerTest(tt.snet, "unix", tt.saddr, tt.ipv6, tt.ipv4map, tt.linux) { + continue + } + + listening := make(chan string) + done := make(chan int) + switch tt.snet { + case "tcp", "tcp4", "tcp6": + tt.saddr += ":0" + case "unix": + os.Remove(tt.saddr) + os.Remove(tt.caddr) + } + + go runStreamConnServer(t, tt.snet, tt.saddr, listening, done) + taddr := <-listening // wait for server to start + + switch tt.cnet { + case "tcp", "tcp4", "tcp6": + _, port, err := SplitHostPort(taddr) + if err != nil { + t.Errorf("SplitHostPort(%q) failed: %v", taddr, err) + return + } + taddr = tt.caddr + ":" + port + } + + runStreamConnClient(t, tt.cnet, taddr, tt.empty) + <-done // make sure server stopped + + switch tt.snet { + case "unix": + os.Remove(tt.saddr) + os.Remove(tt.caddr) + } + } +} + +var seqpacketConnServerTests = []struct { + net string + saddr string // server address + caddr string // client address + empty bool // test with empty data +}{ + {net: "unixpacket", saddr: "/tmp/gotest3.net", caddr: "/tmp/gotest3.net.local"}, + {net: "unixpacket", saddr: "@gotest4/net", caddr: "@gotest4/net.local"}, +} + +func TestSeqpacketConnServer(t *testing.T) { + if runtime.GOOS != "linux" { + t.Logf("skipping test on %q", runtime.GOOS) + return + } + + for _, tt := range seqpacketConnServerTests { + listening := make(chan string) + done := make(chan int) + switch tt.net { + case "unixpacket": + os.Remove(tt.saddr) + os.Remove(tt.caddr) + } + + go runStreamConnServer(t, tt.net, tt.saddr, listening, done) + taddr := <-listening // wait for server to start + + runStreamConnClient(t, tt.net, taddr, tt.empty) + <-done // make sure server stopped + + switch tt.net { + case "unixpacket": + os.Remove(tt.saddr) + os.Remove(tt.caddr) } - fd.Write(buf[0:n]) } - done <- 1 } -func runServe(t *testing.T, network, addr string, listening chan<- string, done chan<- int) { - l, err := Listen(network, addr) +func runStreamConnServer(t *testing.T, net, laddr string, listening chan<- string, done chan<- int) { + l, err := Listen(net, laddr) if err != nil { - t.Fatalf("net.Listen(%q, %q) = _, %v", network, addr, err) + t.Errorf("Listen(%q, %q) failed: %v", net, laddr, err) + listening <- "" + done <- 1 + return } + defer l.Close() listening <- l.Addr().String() + echo := func(rw io.ReadWriter, done chan<- int) { + buf := make([]byte, 1024) + for { + n, err := rw.Read(buf[0:]) + if err != nil || n == 0 || string(buf[:n]) == "END" { + break + } + rw.Write(buf[0:n]) + } + done <- 1 + } + +run: for { - fd, err := l.Accept() + c, err := l.Accept() if err != nil { - break + continue run } echodone := make(chan int) - go runEcho(fd, echodone) - <-echodone // make sure Echo stops - l.Close() + go echo(c, echodone) + <-echodone // make sure echo stopped + c.Close() + break run } done <- 1 } -func connect(t *testing.T, network, addr string, isEmpty bool) { - var fd Conn - var err error - if network == "unixgram" { - fd, err = DialUnix(network, &UnixAddr{addr + ".local", network}, &UnixAddr{addr, network}) - } else { - fd, err = Dial(network, addr) - } +func runStreamConnClient(t *testing.T, net, taddr string, isEmpty bool) { + c, err := Dial(net, taddr) if err != nil { - t.Fatalf("net.Dial(%q, %q) = _, %v", network, addr, err) + t.Errorf("Dial(%q, %q) failed: %v", net, taddr, err) + return } - fd.SetReadDeadline(time.Now().Add(1 * time.Second)) + defer c.Close() + c.SetReadDeadline(time.Now().Add(1 * time.Second)) - var b []byte + var wb []byte if !isEmpty { - b = []byte("hello, world\n") + wb = []byte("StreamConnClient by Dial\n") } - var b1 [100]byte - - n, err1 := fd.Write(b) - if n != len(b) { - t.Fatalf("fd.Write(%q) = %d, %v", b, n, err1) + if n, err := c.Write(wb); err != nil || n != len(wb) { + t.Errorf("Write failed: %v, %v; want %v, ", n, err, len(wb)) + return } - n, err1 = fd.Read(b1[0:]) - if n != len(b) || err1 != nil { - t.Fatalf("fd.Read() = %d, %v (want %d, nil)", n, err1, len(b)) + rb := make([]byte, 1024) + if n, err := c.Read(rb[0:]); err != nil || n != len(wb) { + t.Errorf("Read failed: %v, %v; want %v, ", n, err, len(wb)) + return } // Send explicit ending for unixpacket. // Older Linux kernels do not stop reads on close. - if network == "unixpacket" { - fd.Write([]byte("END")) + switch net { + case "unixpacket": + c.Write([]byte("END")) } - - fd.Close() } -func doTest(t *testing.T, network, listenaddr, dialaddr string) { - t.Logf("Test %q %q %q", network, listenaddr, dialaddr) - switch listenaddr { - case "", "0.0.0.0", "[::]", "[::ffff:0.0.0.0]": - if testing.Short() || !*testExternal { - t.Logf("skip wildcard listen during short test") - return - } - } - listening := make(chan string) - done := make(chan int) - 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" || network == "tcp4" || network == "tcp6" { - dialaddr += addr[strings.LastIndex(addr, ":"):] - } - connect(t, network, dialaddr, false) - <-done // make sure server stopped -} +// Do not test empty datagrams by default. +// It causes unexplained timeouts on some systems, +// including Snow Leopard. I think that the kernel +// doesn't quite expect them. +var testDatagram = flag.Bool("datagram", false, "whether to test udp and unixgram") -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") - 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]", "[::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") - } +var datagramPacketConnServerTests = []struct { + snet string // server side + saddr string + cnet string // client side + caddr string + ipv6 bool // test with underlying AF_INET6 socket + ipv4map bool // test with IPv6 IPv4-mapping functionality + dial bool // test with Dial or DialUnix + empty bool // test with empty data + linux bool // test with abstract unix domain socket, a Linux-ism +}{ + {snet: "udp", saddr: "", cnet: "udp", caddr: "127.0.0.1"}, + {snet: "udp", saddr: "0.0.0.0", cnet: "udp", caddr: "127.0.0.1"}, + {snet: "udp", saddr: "[::ffff:0.0.0.0]", cnet: "udp", caddr: "127.0.0.1"}, + {snet: "udp", saddr: "[::]", cnet: "udp", caddr: "[::1]", ipv6: true}, + + {snet: "udp", saddr: "", cnet: "udp", caddr: "[::1]", ipv4map: true}, + {snet: "udp", saddr: "0.0.0.0", cnet: "udp", caddr: "[::1]", ipv4map: true}, + {snet: "udp", saddr: "[::ffff:0.0.0.0]", cnet: "udp", caddr: "[::1]", ipv4map: true}, + {snet: "udp", saddr: "[::]", cnet: "udp", caddr: "127.0.0.1", ipv4map: true}, + + {snet: "udp", saddr: "", cnet: "udp4", caddr: "127.0.0.1"}, + {snet: "udp", saddr: "0.0.0.0", cnet: "udp4", caddr: "127.0.0.1"}, + {snet: "udp", saddr: "[::ffff:0.0.0.0]", cnet: "udp4", caddr: "127.0.0.1"}, + {snet: "udp", saddr: "[::]", cnet: "udp6", caddr: "[::1]", ipv6: true}, + + {snet: "udp", saddr: "", cnet: "udp6", caddr: "[::1]", ipv4map: true}, + {snet: "udp", saddr: "0.0.0.0", cnet: "udp6", caddr: "[::1]", ipv4map: true}, + {snet: "udp", saddr: "[::ffff:0.0.0.0]", cnet: "udp6", caddr: "[::1]", ipv4map: true}, + {snet: "udp", saddr: "[::]", cnet: "udp4", caddr: "127.0.0.1", ipv4map: true}, + + {snet: "udp", saddr: "127.0.0.1", cnet: "udp", caddr: "127.0.0.1"}, + {snet: "udp", saddr: "[::ffff:127.0.0.1]", cnet: "udp", caddr: "127.0.0.1"}, + {snet: "udp", saddr: "[::1]", cnet: "udp", caddr: "[::1]", ipv6: true}, + + {snet: "udp4", saddr: "", cnet: "udp4", caddr: "127.0.0.1"}, + {snet: "udp4", saddr: "0.0.0.0", cnet: "udp4", caddr: "127.0.0.1"}, + {snet: "udp4", saddr: "[::ffff:0.0.0.0]", cnet: "udp4", caddr: "127.0.0.1"}, + + {snet: "udp4", saddr: "127.0.0.1", cnet: "udp4", caddr: "127.0.0.1"}, + + {snet: "udp6", saddr: "", cnet: "udp6", caddr: "[::1]", ipv6: true}, + {snet: "udp6", saddr: "[::]", cnet: "udp6", caddr: "[::1]", ipv6: true}, + + {snet: "udp6", saddr: "[::1]", cnet: "udp6", caddr: "[::1]", ipv6: true}, + + {snet: "udp", saddr: "127.0.0.1", cnet: "udp", caddr: "127.0.0.1", dial: true}, + {snet: "udp", saddr: "127.0.0.1", cnet: "udp", caddr: "127.0.0.1", empty: true}, + {snet: "udp", saddr: "127.0.0.1", cnet: "udp", caddr: "127.0.0.1", dial: true, empty: true}, + + {snet: "udp", saddr: "[::1]", cnet: "udp", caddr: "[::1]", ipv6: true, dial: true}, + {snet: "udp", saddr: "[::1]", cnet: "udp", caddr: "[::1]", ipv6: true, empty: true}, + {snet: "udp", saddr: "[::1]", cnet: "udp", caddr: "[::1]", ipv6: true, dial: true, empty: true}, + + {snet: "unixgram", saddr: "/tmp/gotest5.net", cnet: "unixgram", caddr: "/tmp/gotest5.net.local"}, + {snet: "unixgram", saddr: "/tmp/gotest5.net", cnet: "unixgram", caddr: "/tmp/gotest5.net.local", dial: true}, + {snet: "unixgram", saddr: "/tmp/gotest5.net", cnet: "unixgram", caddr: "/tmp/gotest5.net.local", empty: true}, + {snet: "unixgram", saddr: "/tmp/gotest5.net", cnet: "unixgram", caddr: "/tmp/gotest5.net.local", dial: true, empty: true}, + + {snet: "unixgram", saddr: "@gotest6/net", cnet: "unixgram", caddr: "@gotest6/net.local", linux: true}, } -func TestUnixServer(t *testing.T) { - // "unix" sockets are not supported on windows and Plan 9. - if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { +func TestDatagramPacketConnServer(t *testing.T) { + if !*testDatagram { return } - os.Remove("/tmp/gotest.net") - doTest(t, "unix", "/tmp/gotest.net", "/tmp/gotest.net") - os.Remove("/tmp/gotest.net") - if runtime.GOOS == "linux" { - doTest(t, "unixpacket", "/tmp/gotest.net", "/tmp/gotest.net") - os.Remove("/tmp/gotest.net") - // Test abstract unix domain socket, a Linux-ism - doTest(t, "unix", "@gotest/net", "@gotest/net") - doTest(t, "unixpacket", "@gotest/net", "@gotest/net") + + for _, tt := range datagramPacketConnServerTests { + if skipServerTest(tt.snet, "unixgram", tt.saddr, tt.ipv6, tt.ipv4map, tt.linux) { + continue + } + + listening := make(chan string) + done := make(chan int) + switch tt.snet { + case "udp", "udp4", "udp6": + tt.saddr += ":0" + case "unixgram": + os.Remove(tt.saddr) + os.Remove(tt.caddr) + } + + go runDatagramPacketConnServer(t, tt.snet, tt.saddr, listening, done) + taddr := <-listening // wait for server to start + + switch tt.cnet { + case "udp", "udp4", "udp6": + _, port, err := SplitHostPort(taddr) + if err != nil { + t.Errorf("SplitHostPort(%q) failed: %v", taddr, err) + return + } + taddr = tt.caddr + ":" + port + tt.caddr += ":0" + } + if tt.dial { + runDatagramConnClient(t, tt.cnet, tt.caddr, taddr, tt.empty) + } else { + runDatagramPacketConnClient(t, tt.cnet, tt.caddr, taddr, tt.empty) + } + <-done // tell server to stop + <-done // make sure server stopped + + switch tt.snet { + case "unixgram": + os.Remove(tt.saddr) + os.Remove(tt.caddr) + } } } -func runPacket(t *testing.T, network, addr string, listening chan<- string, done chan<- int) { - c, err := ListenPacket(network, addr) +func runDatagramPacketConnServer(t *testing.T, net, laddr string, listening chan<- string, done chan<- int) { + c, err := ListenPacket(net, laddr) if err != nil { - t.Fatalf("net.ListenPacket(%q, %q) = _, %v", network, addr, err) + t.Errorf("ListenPacket(%q, %q) failed: %v", net, laddr, err) + listening <- "" + done <- 1 + return } + defer c.Close() listening <- c.LocalAddr().String() - var buf [1000]byte -Run: + + buf := make([]byte, 1024) +run: for { c.SetReadDeadline(time.Now().Add(10 * time.Millisecond)) - n, addr, err := c.ReadFrom(buf[0:]) - if e, ok := err.(Error); ok && e.Timeout() { + n, ra, err := c.ReadFrom(buf[0:]) + if nerr, ok := err.(Error); ok && nerr.Timeout() { select { case done <- 1: - break Run + break run default: - continue Run + continue run } } if err != nil { - break + break run } - if _, err = c.WriteTo(buf[0:n], addr); err != nil { - t.Fatalf("WriteTo %v: %v", addr, err) + if _, err = c.WriteTo(buf[0:n], ra); err != nil { + t.Errorf("WriteTo(%v) failed: %v", ra, err) + break run } } - c.Close() done <- 1 } -func doTestPacket(t *testing.T, network, listenaddr, dialaddr string, isEmpty bool) { - t.Logf("TestPacket %q %q %q", network, listenaddr, dialaddr) - listening := make(chan string) - done := make(chan int) - if network == "udp" { - listenaddr += ":0" // any available port - } - go runPacket(t, network, listenaddr, listening, done) - addr := <-listening // wait for server to start - if network == "udp" { - dialaddr += addr[strings.LastIndex(addr, ":"):] - } - connect(t, network, dialaddr, isEmpty) - <-done // tell server to stop - <-done // wait for stop -} +func runDatagramConnClient(t *testing.T, net, laddr, taddr string, isEmpty bool) { + var c Conn + var err error + switch net { + case "udp", "udp4", "udp6": + c, err = Dial(net, taddr) + if err != nil { + t.Errorf("Dial(%q, %q) failed: %v", net, taddr, err) + return + } + case "unixgram": + c, err = DialUnix(net, &UnixAddr{laddr, net}, &UnixAddr{taddr, net}) + if err != nil { + t.Errorf("DialUnix(%q, {%q, %q}) failed: %v", net, laddr, taddr, err) + return + } + } + defer c.Close() + c.SetReadDeadline(time.Now().Add(1 * time.Second)) -func TestUDPServer(t *testing.T) { - if !*testUDP { + var wb []byte + if !isEmpty { + wb = []byte("DatagramConnClient by Dial\n") + } + if n, err := c.Write(wb[0:]); err != nil || n != len(wb) { + t.Errorf("Write failed: %v, %v; want %v, ", n, err, len(wb)) return } - 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 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) - } + + rb := make([]byte, 1024) + if n, err := c.Read(rb[0:]); err != nil || n != len(wb) { + t.Errorf("Read failed: %v, %v; want %v, ", n, err, len(wb)) + return } } -func TestUnixDatagramServer(t *testing.T) { - // "unix" sockets are not supported on windows and Plan 9. - if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { +func runDatagramPacketConnClient(t *testing.T, net, laddr, taddr string, isEmpty bool) { + var ra Addr + var err error + switch net { + case "udp", "udp4", "udp6": + ra, err = ResolveUDPAddr(net, taddr) + if err != nil { + t.Errorf("ResolveUDPAddr(%q, %q) failed: %v", net, taddr, err) + return + } + case "unixgram": + ra, err = ResolveUnixAddr(net, taddr) + if err != nil { + t.Errorf("ResolveUxixAddr(%q, %q) failed: %v", net, taddr, err) + return + } + } + c, err := ListenPacket(net, laddr) + if err != nil { + t.Errorf("ListenPacket(%q, %q) faild: %v", net, laddr, err) return } - for _, isEmpty := range []bool{false} { - os.Remove("/tmp/gotest1.net") - os.Remove("/tmp/gotest1.net.local") - doTestPacket(t, "unixgram", "/tmp/gotest1.net", "/tmp/gotest1.net", isEmpty) - os.Remove("/tmp/gotest1.net") - os.Remove("/tmp/gotest1.net.local") - if runtime.GOOS == "linux" { - // Test abstract unix domain socket, a Linux-ism - doTestPacket(t, "unixgram", "@gotest1/net", "@gotest1/net", isEmpty) - } + defer c.Close() + c.SetReadDeadline(time.Now().Add(1 * time.Second)) + + var wb []byte + if !isEmpty { + wb = []byte("DatagramPacketConnClient by ListenPacket\n") + } + if n, err := c.WriteTo(wb[0:], ra); err != nil || n != len(wb) { + t.Errorf("WriteTo(%v) failed: %v, %v; want %v, ", ra, n, err, len(wb)) + return + } + + rb := make([]byte, 1024) + if n, _, err := c.ReadFrom(rb[0:]); err != nil || n != len(wb) { + t.Errorf("ReadFrom failed: %v, %v; want %v, ", n, err, len(wb)) + return } } diff --git a/libgo/go/net/sock.go b/libgo/go/net/sock.go index dc139f0..3ae1605 100644 --- a/libgo/go/net/sock.go +++ b/libgo/go/net/sock.go @@ -16,7 +16,7 @@ import ( var listenerBacklog = maxListenerBacklog() // Generic socket creation. -func socket(net string, f, t, p int, la, ra syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) { +func socket(net string, f, t, p int, ipv6only bool, la, ra syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) { // See ../syscall/exec.go for description of ForkLock. syscall.ForkLock.RLock() s, err := syscall.Socket(f, t, p) @@ -27,7 +27,7 @@ func socket(net string, f, t, p int, la, ra syscall.Sockaddr, toAddr func(syscal syscall.CloseOnExec(s) syscall.ForkLock.RUnlock() - err = setDefaultSockopts(s, f, t) + err = setDefaultSockopts(s, f, t, ipv6only) if err != nil { closesocket(s) return nil, err diff --git a/libgo/go/net/sockopt.go b/libgo/go/net/sockopt.go index 0a051d7..0cd1926 100644 --- a/libgo/go/net/sockopt.go +++ b/libgo/go/net/sockopt.go @@ -9,7 +9,6 @@ package net import ( - "bytes" "os" "syscall" "time" @@ -98,7 +97,7 @@ func setIPv4MreqToInterface(mreq *syscall.IPMreq, ifi *Interface) error { } } done: - if bytes.Equal(mreq.Multiaddr[:], IPv4zero.To4()) { + if bytesEqual(mreq.Multiaddr[:], IPv4zero.To4()) { return errNoSuchMulticastInterface } return nil diff --git a/libgo/go/net/sockopt_bsd.go b/libgo/go/net/sockopt_bsd.go index 79e0e57..fff65f3 100644 --- a/libgo/go/net/sockopt_bsd.go +++ b/libgo/go/net/sockopt_bsd.go @@ -13,12 +13,17 @@ import ( "syscall" ) -func setDefaultSockopts(s, f, t int) error { +func setDefaultSockopts(s, f, t int, ipv6only bool) error { switch f { case syscall.AF_INET6: - // Allow both IP versions even if the OS default is otherwise. - // Note that some operating systems never admit this option. - syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0) + if ipv6only { + syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 1) + } else { + // Allow both IP versions even if the OS default + // is otherwise. Note that some operating systems + // never admit this option. + syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0) + } } // Allow broadcast. err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1) diff --git a/libgo/go/net/sockopt_linux.go b/libgo/go/net/sockopt_linux.go index 7509c29..0f47538 100644 --- a/libgo/go/net/sockopt_linux.go +++ b/libgo/go/net/sockopt_linux.go @@ -11,12 +11,17 @@ import ( "syscall" ) -func setDefaultSockopts(s, f, t int) error { +func setDefaultSockopts(s, f, t int, ipv6only bool) error { switch f { case syscall.AF_INET6: - // Allow both IP versions even if the OS default is otherwise. - // Note that some operating systems never admit this option. - syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0) + if ipv6only { + syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 1) + } else { + // Allow both IP versions even if the OS default + // is otherwise. Note that some operating systems + // never admit this option. + syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0) + } } // Allow broadcast. err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1) diff --git a/libgo/go/net/sockopt_windows.go b/libgo/go/net/sockopt_windows.go index b18af67..509b596 100644 --- a/libgo/go/net/sockopt_windows.go +++ b/libgo/go/net/sockopt_windows.go @@ -11,12 +11,17 @@ import ( "syscall" ) -func setDefaultSockopts(s syscall.Handle, f, t int) error { +func setDefaultSockopts(s syscall.Handle, f, t int, ipv6only bool) error { switch f { case syscall.AF_INET6: - // Allow both IP versions even if the OS default is otherwise. - // Note that some operating systems never admit this option. - syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0) + if ipv6only { + syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 1) + } else { + // Allow both IP versions even if the OS default + // is otherwise. Note that some operating systems + // never admit this option. + syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0) + } } // Allow broadcast. syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1) diff --git a/libgo/go/net/tcpsock_posix.go b/libgo/go/net/tcpsock_posix.go index e05bc10..15f8efd 100644 --- a/libgo/go/net/tcpsock_posix.go +++ b/libgo/go/net/tcpsock_posix.go @@ -9,7 +9,6 @@ package net import ( - "fmt" "io" "os" "syscall" @@ -30,7 +29,7 @@ func sockaddrToTCP(sa syscall.Sockaddr) Addr { default: if sa != nil { // Diagnose when we will turn a non-nil sockaddr into a nil. - panic(fmt.Sprintf("unexpected type in sockaddrToTCP: %T", sa)) + panic("unexpected type in sockaddrToTCP") } } return nil @@ -46,6 +45,13 @@ func (a *TCPAddr) family() int { return syscall.AF_INET6 } +func (a *TCPAddr) isWildcard() bool { + if a == nil || a.IP == nil { + return true + } + return a.IP.IsUnspecified() +} + func (a *TCPAddr) sockaddr(family int) (syscall.Sockaddr, error) { return ipToSockaddr(family, a.IP, a.Port) } diff --git a/libgo/go/net/timeout_test.go b/libgo/go/net/timeout_test.go index ef350f0..672fb72 100644 --- a/libgo/go/net/timeout_test.go +++ b/libgo/go/net/timeout_test.go @@ -11,13 +11,13 @@ import ( "time" ) -func testTimeout(t *testing.T, network, addr string, readFrom bool) { - fd, err := Dial(network, addr) +func testTimeout(t *testing.T, net, addr string, readFrom bool) { + c, err := Dial(net, addr) if err != nil { - t.Errorf("dial %s %s failed: %v", network, addr, err) + t.Errorf("Dial(%q, %q) failed: %v", net, addr, err) return } - defer fd.Close() + defer c.Close() what := "Read" if readFrom { what = "ReadFrom" @@ -26,22 +26,22 @@ func testTimeout(t *testing.T, network, addr string, readFrom bool) { errc := make(chan error, 1) go func() { t0 := time.Now() - fd.SetReadDeadline(time.Now().Add(100 * time.Millisecond)) + c.SetReadDeadline(time.Now().Add(100 * time.Millisecond)) var b [100]byte var n int - var err1 error + var err error if readFrom { - n, _, err1 = fd.(PacketConn).ReadFrom(b[0:]) + n, _, err = c.(PacketConn).ReadFrom(b[0:]) } else { - n, err1 = fd.Read(b[0:]) + n, err = c.Read(b[0:]) } t1 := time.Now() - if n != 0 || err1 == nil || !err1.(Error).Timeout() { - errc <- fmt.Errorf("fd.%s on %s %s did not return 0, timeout: %v, %v", what, network, addr, n, err1) + if n != 0 || err == nil || !err.(Error).Timeout() { + errc <- fmt.Errorf("%s(%q, %q) did not return 0, timeout: %v, %v", what, net, addr, n, err) return } if dt := t1.Sub(t0); dt < 50*time.Millisecond || !testing.Short() && dt > 250*time.Millisecond { - errc <- fmt.Errorf("fd.%s on %s %s took %s, expected 0.1s", what, network, addr, dt) + errc <- fmt.Errorf("%s(%q, %q) took %s, expected 0.1s", what, net, addr, dt) return } errc <- nil @@ -52,26 +52,39 @@ func testTimeout(t *testing.T, network, addr string, readFrom bool) { t.Error(err) } case <-time.After(1 * time.Second): - t.Errorf("%s on %s %s took over 1 second, expected 0.1s", what, network, addr) + t.Errorf("%s(%q, %q) took over 1 second, expected 0.1s", what, net, addr) } } func TestTimeoutUDP(t *testing.T) { - if runtime.GOOS == "plan9" { + switch runtime.GOOS { + case "plan9": + t.Logf("skipping test on %q", runtime.GOOS) return } - testTimeout(t, "udp", "127.0.0.1:53", false) - testTimeout(t, "udp", "127.0.0.1:53", true) + + // set up a listener that won't talk back + listening := make(chan string) + done := make(chan int) + go runDatagramPacketConnServer(t, "udp", "127.0.0.1:0", listening, done) + addr := <-listening + + testTimeout(t, "udp", addr, false) + testTimeout(t, "udp", addr, true) + <-done } func TestTimeoutTCP(t *testing.T) { - if runtime.GOOS == "plan9" { + switch runtime.GOOS { + case "plan9": + t.Logf("skipping test on %q", runtime.GOOS) return } + // set up a listener that won't talk back listening := make(chan string) done := make(chan int) - go runServe(t, "tcp", "127.0.0.1:0", listening, done) + go runStreamConnServer(t, "tcp", "127.0.0.1:0", listening, done) addr := <-listening testTimeout(t, "tcp", addr, false) @@ -79,7 +92,9 @@ func TestTimeoutTCP(t *testing.T) { } func TestDeadlineReset(t *testing.T) { - if runtime.GOOS == "plan9" { + switch runtime.GOOS { + case "plan9": + t.Logf("skipping test on %q", runtime.GOOS) return } ln, err := Listen("tcp", "127.0.0.1:0") diff --git a/libgo/go/net/udp_test.go b/libgo/go/net/udp_test.go index ea5fad4..f80d3b5 100644 --- a/libgo/go/net/udp_test.go +++ b/libgo/go/net/udp_test.go @@ -10,7 +10,9 @@ import ( ) func TestWriteToUDP(t *testing.T) { - if runtime.GOOS == "plan9" { + switch runtime.GOOS { + case "plan9": + t.Logf("skipping test on %q", runtime.GOOS) return } diff --git a/libgo/go/net/udpsock_posix.go b/libgo/go/net/udpsock_posix.go index 1f99dc5..9e820e1 100644 --- a/libgo/go/net/udpsock_posix.go +++ b/libgo/go/net/udpsock_posix.go @@ -37,6 +37,13 @@ func (a *UDPAddr) family() int { return syscall.AF_INET6 } +func (a *UDPAddr) isWildcard() bool { + if a == nil || a.IP == nil { + return true + } + return a.IP.IsUnspecified() +} + func (a *UDPAddr) sockaddr(family int) (syscall.Sockaddr, error) { return ipToSockaddr(family, a.IP, a.Port) } diff --git a/libgo/go/net/unicast_test.go b/libgo/go/net/unicast_test.go index 297276d..a23bc5a 100644 --- a/libgo/go/net/unicast_test.go +++ b/libgo/go/net/unicast_test.go @@ -7,81 +7,481 @@ package net import ( "io" "runtime" + "syscall" "testing" ) -var unicastTests = []struct { - net string - laddr string - ipv6 bool - packet bool +var listenerTests = []struct { + net string + laddr string + ipv6 bool // test with underlying AF_INET6 socket + wildcard bool // test with wildcard address }{ - {net: "tcp4", laddr: "127.0.0.1:0"}, - {net: "tcp4", laddr: "previous"}, - {net: "tcp6", laddr: "[::1]:0", ipv6: true}, - {net: "tcp6", laddr: "previous", ipv6: true}, - {net: "udp4", laddr: "127.0.0.1:0", packet: true}, - {net: "udp6", laddr: "[::1]:0", ipv6: true, packet: true}, + {net: "tcp", laddr: "", wildcard: true}, + {net: "tcp", laddr: "0.0.0.0", wildcard: true}, + {net: "tcp", laddr: "[::ffff:0.0.0.0]", wildcard: true}, + {net: "tcp", laddr: "[::]", ipv6: true, wildcard: true}, + + {net: "tcp", laddr: "127.0.0.1"}, + {net: "tcp", laddr: "[::ffff:127.0.0.1]"}, + {net: "tcp", laddr: "[::1]", ipv6: true}, + + {net: "tcp4", laddr: "", wildcard: true}, + {net: "tcp4", laddr: "0.0.0.0", wildcard: true}, + {net: "tcp4", laddr: "[::ffff:0.0.0.0]", wildcard: true}, + + {net: "tcp4", laddr: "127.0.0.1"}, + {net: "tcp4", laddr: "[::ffff:127.0.0.1]"}, + + {net: "tcp6", laddr: "", ipv6: true, wildcard: true}, + {net: "tcp6", laddr: "[::]", ipv6: true, wildcard: true}, + + {net: "tcp6", laddr: "[::1]", ipv6: true}, } -func TestUnicastTCPAndUDP(t *testing.T) { - if runtime.GOOS == "plan9" || runtime.GOOS == "windows" { +// TestTCPListener tests both single and double listen to a test +// listener with same address family, same listening address and +// same port. +func TestTCPListener(t *testing.T) { + switch runtime.GOOS { + case "plan9", "windows": + t.Logf("skipping test on %q", runtime.GOOS) return } - prevladdr := "" - for _, tt := range unicastTests { + for _, tt := range listenerTests { + if tt.wildcard && (testing.Short() || !*testExternal) { + continue + } if tt.ipv6 && !supportsIPv6 { continue } - var ( - fd *netFD - closer io.Closer - ) - if !tt.packet { - if tt.laddr == "previous" { - tt.laddr = prevladdr + l1, port := usableListenPort(t, tt.net, tt.laddr) + checkFirstListener(t, tt.net, tt.laddr+":"+port, l1) + l2, err := Listen(tt.net, tt.laddr+":"+port) + checkSecondListener(t, tt.net, tt.laddr+":"+port, err, l2) + fd := l1.(*TCPListener).fd + switch fd.family { + case syscall.AF_INET: + testIPv4UnicastSocketOptions(t, fd) + case syscall.AF_INET6: + testIPv6UnicastSocketOptions(t, fd) + } + l1.(io.Closer).Close() + } +} + +// TestUDPListener tests both single and double listen to a test +// listener with same address family, same listening address and +// same port. +func TestUDPListener(t *testing.T) { + switch runtime.GOOS { + case "plan9", "windows": + t.Logf("skipping test on %q", runtime.GOOS) + return + } + + toudpnet := func(net string) string { + switch net { + case "tcp": + return "udp" + case "tcp4": + return "udp4" + case "tcp6": + return "udp6" + } + return "" + } + + for _, tt := range listenerTests { + if tt.wildcard && (testing.Short() || !*testExternal) { + continue + } + if tt.ipv6 && !supportsIPv6 { + continue + } + tt.net = toudpnet(tt.net) + l1, port := usableListenPacketPort(t, tt.net, tt.laddr) + checkFirstListener(t, tt.net, tt.laddr+":"+port, l1) + l2, err := ListenPacket(tt.net, tt.laddr+":"+port) + checkSecondListener(t, tt.net, tt.laddr+":"+port, err, l2) + fd := l1.(*UDPConn).fd + switch fd.family { + case syscall.AF_INET: + testIPv4UnicastSocketOptions(t, fd) + case syscall.AF_INET6: + testIPv6UnicastSocketOptions(t, fd) + } + l1.(io.Closer).Close() + } +} + +func TestSimpleTCPListener(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Logf("skipping test on %q", runtime.GOOS) + return + } + + for _, tt := range listenerTests { + if tt.wildcard && (testing.Short() || !*testExternal) { + continue + } + if tt.ipv6 { + continue + } + l1, port := usableListenPort(t, tt.net, tt.laddr) + checkFirstListener(t, tt.net, tt.laddr+":"+port, l1) + l2, err := Listen(tt.net, tt.laddr+":"+port) + checkSecondListener(t, tt.net, tt.laddr+":"+port, err, l2) + l1.(io.Closer).Close() + } +} + +func TestSimpleUDPListener(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Logf("skipping test on %q", runtime.GOOS) + return + } + + toudpnet := func(net string) string { + switch net { + case "tcp": + return "udp" + case "tcp4": + return "udp4" + case "tcp6": + return "udp6" + } + return "" + } + + for _, tt := range listenerTests { + if tt.wildcard && (testing.Short() || !*testExternal) { + continue + } + if tt.ipv6 { + continue + } + tt.net = toudpnet(tt.net) + l1, port := usableListenPacketPort(t, tt.net, tt.laddr) + checkFirstListener(t, tt.net, tt.laddr+":"+port, l1) + l2, err := ListenPacket(tt.net, tt.laddr+":"+port) + checkSecondListener(t, tt.net, tt.laddr+":"+port, err, l2) + l1.(io.Closer).Close() + } +} + +var dualStackListenerTests = []struct { + net1 string // first listener + laddr1 string + net2 string // second listener + laddr2 string + wildcard bool // test with wildcard address + xerr error // expected error value, nil or other +}{ + // Test cases and expected results for the attemping 2nd listen on the same port + // 1st listen 2nd listen darwin freebsd linux openbsd + // ------------------------------------------------------------------------------------ + // "tcp" "" "tcp" "" - - - - + // "tcp" "" "tcp" "0.0.0.0" - - - - + // "tcp" "0.0.0.0" "tcp" "" - - - - + // ------------------------------------------------------------------------------------ + // "tcp" "" "tcp" "[::]" - - - ok + // "tcp" "[::]" "tcp" "" - - - ok + // "tcp" "0.0.0.0" "tcp" "[::]" - - - ok + // "tcp" "[::]" "tcp" "0.0.0.0" - - - ok + // "tcp" "[::ffff:0.0.0.0]" "tcp" "[::]" - - - ok + // "tcp" "[::]" "tcp" "[::ffff:0.0.0.0]" - - - ok + // ------------------------------------------------------------------------------------ + // "tcp4" "" "tcp6" "" ok ok ok ok + // "tcp6" "" "tcp4" "" ok ok ok ok + // "tcp4" "0.0.0.0" "tcp6" "[::]" ok ok ok ok + // "tcp6" "[::]" "tcp4" "0.0.0.0" ok ok ok ok + // ------------------------------------------------------------------------------------ + // "tcp" "127.0.0.1" "tcp" "[::1]" ok ok ok ok + // "tcp" "[::1]" "tcp" "127.0.0.1" ok ok ok ok + // "tcp4" "127.0.0.1" "tcp6" "[::1]" ok ok ok ok + // "tcp6" "[::1]" "tcp4" "127.0.0.1" ok ok ok ok + // + // Platform default configurations: + // darwin, kernel version 11.3.0 + // net.inet6.ip6.v6only=0 (overridable by sysctl or IPV6_V6ONLY option) + // freebsd, kernel version 8.2 + // net.inet6.ip6.v6only=1 (overridable by sysctl or IPV6_V6ONLY option) + // linux, kernel version 3.0.0 + // net.ipv6.bindv6only=0 (overridable by sysctl or IPV6_V6ONLY option) + // openbsd, kernel version 5.0 + // net.inet6.ip6.v6only=1 (overriding is prohibited) + + {net1: "tcp", laddr1: "", net2: "tcp", laddr2: "", wildcard: true, xerr: syscall.EADDRINUSE}, + {net1: "tcp", laddr1: "", net2: "tcp", laddr2: "0.0.0.0", wildcard: true, xerr: syscall.EADDRINUSE}, + {net1: "tcp", laddr1: "0.0.0.0", net2: "tcp", laddr2: "", wildcard: true, xerr: syscall.EADDRINUSE}, + + {net1: "tcp", laddr1: "", net2: "tcp", laddr2: "[::]", wildcard: true, xerr: syscall.EADDRINUSE}, + {net1: "tcp", laddr1: "[::]", net2: "tcp", laddr2: "", wildcard: true, xerr: syscall.EADDRINUSE}, + {net1: "tcp", laddr1: "0.0.0.0", net2: "tcp", laddr2: "[::]", wildcard: true, xerr: syscall.EADDRINUSE}, + {net1: "tcp", laddr1: "[::]", net2: "tcp", laddr2: "0.0.0.0", wildcard: true, xerr: syscall.EADDRINUSE}, + {net1: "tcp", laddr1: "[::ffff:0.0.0.0]", net2: "tcp", laddr2: "[::]", wildcard: true, xerr: syscall.EADDRINUSE}, + {net1: "tcp", laddr1: "[::]", net2: "tcp", laddr2: "[::ffff:0.0.0.0]", wildcard: true, xerr: syscall.EADDRINUSE}, + + {net1: "tcp4", laddr1: "", net2: "tcp6", laddr2: "", wildcard: true}, + {net1: "tcp6", laddr1: "", net2: "tcp4", laddr2: "", wildcard: true}, + {net1: "tcp4", laddr1: "0.0.0.0", net2: "tcp6", laddr2: "[::]", wildcard: true}, + {net1: "tcp6", laddr1: "[::]", net2: "tcp4", laddr2: "0.0.0.0", wildcard: true}, + + {net1: "tcp", laddr1: "127.0.0.1", net2: "tcp", laddr2: "[::1]"}, + {net1: "tcp", laddr1: "[::1]", net2: "tcp", laddr2: "127.0.0.1"}, + {net1: "tcp4", laddr1: "127.0.0.1", net2: "tcp6", laddr2: "[::1]"}, + {net1: "tcp6", laddr1: "[::1]", net2: "tcp4", laddr2: "127.0.0.1"}, +} + +// TestDualStackTCPListener tests both single and double listen +// to a test listener with various address families, differnet +// listening address and same port. +func TestDualStackTCPListener(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Logf("skipping test on %q", runtime.GOOS) + return + } + if !supportsIPv6 { + return + } + + for _, tt := range dualStackListenerTests { + if tt.wildcard && (testing.Short() || !*testExternal) { + continue + } + switch runtime.GOOS { + case "openbsd": + if tt.wildcard && differentWildcardAddr(tt.laddr1, tt.laddr2) { + tt.xerr = nil + } + } + l1, port := usableListenPort(t, tt.net1, tt.laddr1) + laddr := tt.laddr1 + ":" + port + checkFirstListener(t, tt.net1, laddr, l1) + laddr = tt.laddr2 + ":" + port + l2, err := Listen(tt.net2, laddr) + checkDualStackSecondListener(t, tt.net2, laddr, tt.xerr, err, l2) + l1.Close() + } +} + +// TestDualStackUDPListener tests both single and double listen +// to a test listener with various address families, differnet +// listening address and same port. +func TestDualStackUDPListener(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Logf("skipping test on %q", runtime.GOOS) + return + } + if !supportsIPv6 { + return + } + + toudpnet := func(net string) string { + switch net { + case "tcp": + return "udp" + case "tcp4": + return "udp4" + case "tcp6": + return "udp6" + } + return "" + } + + for _, tt := range dualStackListenerTests { + if tt.wildcard && (testing.Short() || !*testExternal) { + continue + } + tt.net1 = toudpnet(tt.net1) + tt.net2 = toudpnet(tt.net2) + switch runtime.GOOS { + case "openbsd": + if tt.wildcard && differentWildcardAddr(tt.laddr1, tt.laddr2) { + tt.xerr = nil } - l, err := Listen(tt.net, tt.laddr) - if err != nil { - t.Fatalf("Listen failed: %v", err) + } + l1, port := usableListenPacketPort(t, tt.net1, tt.laddr1) + laddr := tt.laddr1 + ":" + port + checkFirstListener(t, tt.net1, laddr, l1) + laddr = tt.laddr2 + ":" + port + l2, err := ListenPacket(tt.net2, laddr) + checkDualStackSecondListener(t, tt.net2, laddr, tt.xerr, err, l2) + l1.Close() + } +} + +func usableListenPort(t *testing.T, net, laddr string) (l Listener, port string) { + var nladdr string + var err error + switch net { + default: + panic("usableListenPort net=" + net) + case "tcp", "tcp4", "tcp6": + l, err = Listen(net, laddr+":0") + if err != nil { + t.Fatalf("Probe Listen(%q, %q) failed: %v", net, laddr, err) + } + nladdr = l.(*TCPListener).Addr().String() + } + _, port, err = SplitHostPort(nladdr) + if err != nil { + t.Fatalf("SplitHostPort failed: %v", err) + } + return l, port +} + +func usableListenPacketPort(t *testing.T, net, laddr string) (l PacketConn, port string) { + var nladdr string + var err error + switch net { + default: + panic("usableListenPacketPort net=" + net) + case "udp", "udp4", "udp6": + l, err = ListenPacket(net, laddr+":0") + if err != nil { + t.Fatalf("Probe ListenPacket(%q, %q) failed: %v", net, laddr, err) + } + nladdr = l.(*UDPConn).LocalAddr().String() + } + _, port, err = SplitHostPort(nladdr) + if err != nil { + t.Fatalf("SplitHostPort failed: %v", err) + } + return l, port +} + +func differentWildcardAddr(i, j string) bool { + if (i == "" || i == "0.0.0.0" || i == "::ffff:0.0.0.0") && (j == "" || j == "0.0.0.0" || j == "::ffff:0.0.0.0") { + return false + } + if i == "[::]" && j == "[::]" { + return false + } + return true +} + +func checkFirstListener(t *testing.T, net, laddr string, l interface{}) { + switch net { + case "tcp": + fd := l.(*TCPListener).fd + checkDualStackAddrFamily(t, net, laddr, fd) + case "tcp4": + fd := l.(*TCPListener).fd + if fd.family != syscall.AF_INET { + t.Fatalf("First Listen(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET) + } + case "tcp6": + fd := l.(*TCPListener).fd + if fd.family != syscall.AF_INET6 { + t.Fatalf("First Listen(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET6) + } + case "udp": + fd := l.(*UDPConn).fd + checkDualStackAddrFamily(t, net, laddr, fd) + case "udp4": + fd := l.(*UDPConn).fd + if fd.family != syscall.AF_INET { + t.Fatalf("First ListenPacket(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET) + } + case "udp6": + fd := l.(*UDPConn).fd + if fd.family != syscall.AF_INET6 { + t.Fatalf("First ListenPacket(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET6) + } + default: + t.Fatalf("Unexpected network: %q", net) + } +} + +func checkSecondListener(t *testing.T, net, laddr string, err error, l interface{}) { + switch net { + case "tcp", "tcp4", "tcp6": + if err == nil { + l.(*TCPListener).Close() + t.Fatalf("Second Listen(%q, %q) should fail", net, laddr) + } + case "udp", "udp4", "udp6": + if err == nil { + l.(*UDPConn).Close() + t.Fatalf("Second ListenPacket(%q, %q) should fail", net, laddr) + } + default: + t.Fatalf("Unexpected network: %q", net) + } +} + +func checkDualStackSecondListener(t *testing.T, net, laddr string, xerr, err error, l interface{}) { + switch net { + case "tcp", "tcp4", "tcp6": + if xerr == nil && err != nil || xerr != nil && err == nil { + t.Fatalf("Second Listen(%q, %q) returns %v, expected %v", net, laddr, err, xerr) + } + l.(*TCPListener).Close() + case "udp", "udp4", "udp6": + if xerr == nil && err != nil || xerr != nil && err == nil { + t.Fatalf("Second ListenPacket(%q, %q) returns %v, expected %v", net, laddr, err, xerr) + } + l.(*UDPConn).Close() + default: + t.Fatalf("Unexpected network: %q", net) + } +} + +func checkDualStackAddrFamily(t *testing.T, net, laddr string, fd *netFD) { + switch a := fd.laddr.(type) { + case *TCPAddr: + // If a node under test supports both IPv6 capability + // and IPv6 IPv4-mapping capability, we can assume + // that the node listens on a wildcard address with an + // AF_INET6 socket. + if supportsIPv4map && fd.laddr.(*TCPAddr).isWildcard() { + if fd.family != syscall.AF_INET6 { + t.Fatalf("Listen(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET6) } - prevladdr = l.Addr().String() - closer = l - fd = l.(*TCPListener).fd } else { - c, err := ListenPacket(tt.net, tt.laddr) - if err != nil { - t.Fatalf("ListenPacket failed: %v", err) + if fd.family != a.family() { + t.Fatalf("Listen(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, a.family()) } - closer = c - fd = c.(*UDPConn).fd } - if !tt.ipv6 { - testIPv4UnicastSocketOptions(t, fd) + case *UDPAddr: + // If a node under test supports both IPv6 capability + // and IPv6 IPv4-mapping capability, we can assume + // that the node listens on a wildcard address with an + // AF_INET6 socket. + if supportsIPv4map && fd.laddr.(*UDPAddr).isWildcard() { + if fd.family != syscall.AF_INET6 { + t.Fatalf("ListenPacket(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET6) + } } else { - testIPv6UnicastSocketOptions(t, fd) + if fd.family != a.family() { + t.Fatalf("ListenPacket(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, a.family()) + } } - closer.Close() + default: + t.Fatalf("Unexpected protocol address type: %T", a) } } func testIPv4UnicastSocketOptions(t *testing.T, fd *netFD) { - tos, err := ipv4TOS(fd) + _, err := ipv4TOS(fd) if err != nil { t.Fatalf("ipv4TOS failed: %v", err) } - t.Logf("IPv4 TOS: %v", tos) err = setIPv4TOS(fd, 1) if err != nil { t.Fatalf("setIPv4TOS failed: %v", err) } - - ttl, err := ipv4TTL(fd) + _, err = ipv4TTL(fd) if err != nil { t.Fatalf("ipv4TTL failed: %v", err) } - t.Logf("IPv4 TTL: %v", ttl) err = setIPv4TTL(fd, 1) if err != nil { t.Fatalf("setIPv4TTL failed: %v", err) @@ -89,23 +489,50 @@ func testIPv4UnicastSocketOptions(t *testing.T, fd *netFD) { } func testIPv6UnicastSocketOptions(t *testing.T, fd *netFD) { - tos, err := ipv6TrafficClass(fd) + _, err := ipv6TrafficClass(fd) if err != nil { t.Fatalf("ipv6TrafficClass failed: %v", err) } - t.Logf("IPv6 TrafficClass: %v", tos) err = setIPv6TrafficClass(fd, 1) if err != nil { t.Fatalf("setIPv6TrafficClass failed: %v", err) } - - hoplim, err := ipv6HopLimit(fd) + _, err = ipv6HopLimit(fd) if err != nil { t.Fatalf("ipv6HopLimit failed: %v", err) } - t.Logf("IPv6 HopLimit: %v", hoplim) err = setIPv6HopLimit(fd, 1) if err != nil { t.Fatalf("setIPv6HopLimit failed: %v", err) } } + +var prohibitionaryDialArgTests = []struct { + net string + addr string +}{ + {"tcp6", "127.0.0.1"}, + {"tcp6", "[::ffff:127.0.0.1]"}, +} + +func TestProhibitionaryDialArgs(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Logf("skipping test on %q", runtime.GOOS) + return + } + // This test requires both IPv6 and IPv6 IPv4-mapping functionality. + if !supportsIPv4map || testing.Short() || !*testExternal { + return + } + + l, port := usableListenPort(t, "tcp", "[::]") + defer l.Close() + + for _, tt := range prohibitionaryDialArgTests { + _, err := Dial(tt.net, tt.addr+":"+port) + if err == nil { + t.Fatalf("Dial(%q, %q) should fail", tt.net, tt.addr) + } + } +} diff --git a/libgo/go/net/unixsock_posix.go b/libgo/go/net/unixsock_posix.go index 3a94cf5..37a2b1e 100644 --- a/libgo/go/net/unixsock_posix.go +++ b/libgo/go/net/unixsock_posix.go @@ -59,7 +59,7 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err f = sockaddrToUnixpacket } - fd, err = socket(net, syscall.AF_UNIX, sotype, 0, la, ra, f) + fd, err = socket(net, syscall.AF_UNIX, sotype, 0, false, la, ra, f) if err != nil { goto Error } @@ -208,8 +208,8 @@ func (c *UnixConn) SetWriteBuffer(bytes int) error { } // 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. +// It returns the number of bytes copied into b and the source address +// of the packet. // // ReadFromUnix can be made to time out and return // an error with Timeout() == true after a fixed time limit; @@ -264,6 +264,11 @@ func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err error) { return c.WriteToUnix(b, a) } +// ReadMsgUnix reads a packet from c, copying the payload into b +// and the associated out-of-band data into oob. +// It returns the number of bytes copied into b, the number of +// bytes copied into oob, the flags that were set on the packet, +// and the source address of the packet. func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err error) { if !c.ok() { return 0, 0, 0, nil, syscall.EINVAL @@ -276,6 +281,9 @@ func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAdd return } +// WriteMsgUnix writes a packet to addr via c, copying the payload from b +// and the associated out-of-band data from oob. It returns the number +// of payload and out-of-band bytes written. func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err error) { if !c.ok() { return 0, 0, syscall.EINVAL -- cgit v1.1