From: Brad Fitzpatrick Date: Fri, 9 Sep 2016 22:51:11 +0000 (+0000) Subject: net: make LookupPort and lookupProtocol work on nacl X-Git-Tag: go1.8beta1~1394 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=74190735be5e722a0a977d056204307c2468cf21;p=gostls13.git net: make LookupPort and lookupProtocol work on nacl Also, flesh out the baked-in /etc/services table for LookupPort a bit. This services map moves from a unix-specific file to a portable file where nacl can use it. Also, remove the duplicated entries in the protocol map in different cases, and just canonicalize the input before looking in the map. Now it handles any case, including MiXeD cAse. In the process, add a test that service names for LookupPort are case insensitive. They were on Windows, but not cgo. Now there's a test and they're case insensitive in all 3+ paths. Maybe it breaks plan9. We'll see. Fixes #17045 Change-Id: Idce7d68703f371727c7505cda03a32bd842298cd Reviewed-on: https://go-review.googlesource.com/28951 Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot Reviewed-by: Minux Ma --- diff --git a/src/net/cgo_unix.go b/src/net/cgo_unix.go index 5a1eed8437..56d34b6d03 100644 --- a/src/net/cgo_unix.go +++ b/src/net/cgo_unix.go @@ -96,6 +96,11 @@ func cgoLookupPort(ctx context.Context, network, service string) (port int, err func cgoLookupServicePort(hints *C.struct_addrinfo, network, service string) (port int, err error) { s := C.CString(service) + // Lowercase the service name in the C-allocated memory. + for i := 0; i < len(service); i++ { + bp := (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(s)) + uintptr(i))) + *bp = lowerASCII(*bp) + } var res *C.struct_addrinfo defer C.free(unsafe.Pointer(s)) gerrno, err := C.getaddrinfo(nil, s, hints, &res) diff --git a/src/net/lookup.go b/src/net/lookup.go index c169e9e902..12ea3022ef 100644 --- a/src/net/lookup.go +++ b/src/net/lookup.go @@ -15,12 +15,73 @@ import ( // protocol numbers. // // See http://www.iana.org/assignments/protocol-numbers +// +// On Unix, this map is augmented by readProtocols via lookupProtocol. var protocols = map[string]int{ - "icmp": 1, "ICMP": 1, - "igmp": 2, "IGMP": 2, - "tcp": 6, "TCP": 6, - "udp": 17, "UDP": 17, - "ipv6-icmp": 58, "IPV6-ICMP": 58, "IPv6-ICMP": 58, + "icmp": 1, + "igmp": 2, + "tcp": 6, + "udp": 17, + "ipv6-icmp": 58, +} + +// services contains minimal mappings between services names and port +// numbers for platforms that don't have a complete list of port numbers +// (some Solaris distros, nacl, etc). +// On Unix, this map is augmented by readServices via goLookupPort. +var services = map[string]map[string]int{ + "udp": { + "domain": 53, + }, + "tcp": { + "ftp": 21, + "ftps": 990, + "gopher": 70, // ʕ◔ϖ◔ʔ + "http": 80, + "https": 443, + "imap2": 143, + "imap3": 220, + "imaps": 993, + "pop3": 110, + "pop3s": 995, + "smtp": 25, + "ssh": 22, + "telnet": 23, + }, +} + +const maxProtoLength = len("RSVP-E2E-IGNORE") + 10 // with room to grow + +func lookupProtocolMap(name string) (int, error) { + var lowerProtocol [maxProtoLength]byte + n := copy(lowerProtocol[:], name) + lowerASCIIBytes(lowerProtocol[:n]) + proto, found := protocols[string(lowerProtocol[:n])] + if !found || n != len(name) { + return 0, &AddrError{Err: "unknown IP protocol specified", Addr: name} + } + return proto, nil +} + +const maxServiceLength = len("mobility-header") + 10 // with room to grow + +func lookupPortMap(network, service string) (port int, error error) { + switch network { + case "tcp4", "tcp6": + network = "tcp" + case "udp4", "udp6": + network = "udp" + } + + if m, ok := services[network]; ok { + var lowerService [maxServiceLength]byte + n := copy(lowerService[:], service) + lowerASCIIBytes(lowerService[:n]) + if port, ok := m[string(lowerService[:n])]; ok && n == len(service) { + return port, nil + } + } + return 0, &AddrError{Err: "unknown port", Addr: network + "/" + service} } // LookupHost looks up the given host using the local resolver. diff --git a/src/net/lookup_stub.go b/src/net/lookup_nacl.go similarity index 94% rename from src/net/lookup_stub.go rename to src/net/lookup_nacl.go index bd096b3965..48c0d1938e 100644 --- a/src/net/lookup_stub.go +++ b/src/net/lookup_nacl.go @@ -12,7 +12,7 @@ import ( ) func lookupProtocol(ctx context.Context, name string) (proto int, err error) { - return 0, syscall.ENOPROTOOPT + return lookupProtocolMap(name) } func lookupHost(ctx context.Context, host string) (addrs []string, err error) { @@ -24,7 +24,7 @@ func lookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) { } func lookupPort(ctx context.Context, network, service string) (port int, err error) { - return 0, syscall.ENOPROTOOPT + return goLookupPort(network, service) } func lookupCNAME(ctx context.Context, name string) (cname string, err error) { diff --git a/src/net/lookup_test.go b/src/net/lookup_test.go index b3aeb85afb..5de9f39b08 100644 --- a/src/net/lookup_test.go +++ b/src/net/lookup_test.go @@ -616,7 +616,7 @@ func srvString(srvs []*SRV) string { func TestLookupPort(t *testing.T) { // See http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml // - // Please be careful about adding new mappings for testings. + // Please be careful about adding new test cases. // There are platforms having incomplete mappings for // restricted resource access and security reasons. type test struct { @@ -648,8 +648,6 @@ func TestLookupPort(t *testing.T) { } switch runtime.GOOS { - case "nacl": - t.Skipf("not supported on %s", runtime.GOOS) case "android": if netGo { t.Skipf("not supported on %s without cgo; see golang.org/issues/14576", runtime.GOOS) @@ -670,3 +668,52 @@ func TestLookupPort(t *testing.T) { } } } + +// Like TestLookupPort but with minimal tests that should always pass +// because the answers are baked-in to the net package. +func TestLookupPort_Minimal(t *testing.T) { + type test struct { + network string + name string + port int + } + var tests = []test{ + {"tcp", "http", 80}, + {"tcp", "HTTP", 80}, // case shouldn't matter + {"tcp", "https", 443}, + {"tcp", "ssh", 22}, + {"tcp", "gopher", 70}, + {"tcp4", "http", 80}, + {"tcp6", "http", 80}, + } + + for _, tt := range tests { + port, err := LookupPort(tt.network, tt.name) + if port != tt.port || err != nil { + t.Errorf("LookupPort(%q, %q) = %d, %v; want %d, error=nil", tt.network, tt.name, port, err, tt.port) + } + } +} + +func TestLookupProtocol_Minimal(t *testing.T) { + type test struct { + name string + want int + } + var tests = []test{ + {"tcp", 6}, + {"TcP", 6}, // case shouldn't matter + {"icmp", 1}, + {"igmp", 2}, + {"udp", 17}, + {"ipv6-icmp", 58}, + } + + for _, tt := range tests { + got, err := lookupProtocol(context.Background(), tt.name) + if got != tt.want || err != nil { + t.Errorf("LookupProtocol(%q) = %d, %v; want %d, error=nil", tt.name, got, err, tt.want) + } + } + +} diff --git a/src/net/lookup_unix.go b/src/net/lookup_unix.go index be0ae9aefa..fe84a64208 100644 --- a/src/net/lookup_unix.go +++ b/src/net/lookup_unix.go @@ -45,11 +45,7 @@ func readProtocols() { // returns correspondent protocol number. func lookupProtocol(_ context.Context, name string) (int, error) { onceReadProtocols.Do(readProtocols) - proto, found := protocols[name] - if !found { - return 0, &AddrError{Err: "unknown IP protocol specified", Addr: name} - } - return proto, nil + return lookupProtocolMap(name) } func lookupHost(ctx context.Context, host string) (addrs []string, err error) { diff --git a/src/net/port_unix.go b/src/net/port_unix.go index a8cb0199a0..4e0478194e 100644 --- a/src/net/port_unix.go +++ b/src/net/port_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd linux netbsd openbsd solaris +// +build darwin dragonfly freebsd linux netbsd openbsd solaris nacl // Read system port mappings from /etc/services @@ -10,12 +10,6 @@ package net import "sync" -// services contains minimal mappings between services names and port -// numbers for platforms that don't have a complete list of port numbers -// (some Solaris distros). -var services = map[string]map[string]int{ - "tcp": {"http": 80}, -} var servicesError error var onceReadServices sync.Once @@ -27,7 +21,7 @@ func readServices() { for line, ok := file.readLine(); ok; line, ok = file.readLine() { // "http 80/tcp www www-http # World Wide Web HTTP" if i := byteIndex(line, '#'); i >= 0 { - line = line[0:i] + line = line[:i] } f := getFields(line) if len(f) < 2 { @@ -56,18 +50,5 @@ func readServices() { // goLookupPort is the native Go implementation of LookupPort. func goLookupPort(network, service string) (port int, err error) { onceReadServices.Do(readServices) - - switch network { - case "tcp4", "tcp6": - network = "tcp" - case "udp4", "udp6": - network = "udp" - } - - if m, ok := services[network]; ok { - if port, ok = m[service]; ok { - return - } - } - return 0, &AddrError{Err: "unknown port", Addr: network + "/" + service} + return lookupPortMap(network, service) }