]> Cypherpunks repositories - gostls13.git/commitdiff
net: make LookupPort and lookupProtocol work on nacl
authorBrad Fitzpatrick <bradfitz@golang.org>
Fri, 9 Sep 2016 22:51:11 +0000 (22:51 +0000)
committerBrad Fitzpatrick <bradfitz@golang.org>
Sun, 11 Sep 2016 04:19:25 +0000 (04:19 +0000)
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 <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Minux Ma <minux@golang.org>
src/net/cgo_unix.go
src/net/lookup.go
src/net/lookup_nacl.go [moved from src/net/lookup_stub.go with 94% similarity]
src/net/lookup_test.go
src/net/lookup_unix.go
src/net/port_unix.go

index 5a1eed843751a45ab72de2365337132f3729f4ba..56d34b6d0356bfd7f08560f91634e9a3b7089d03 100644 (file)
@@ -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)
index c169e9e902c2654e01ac6490aed6dc2224e3ded0..12ea3022ef8077675325073adf79edcb17a340b2 100644 (file)
@@ -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.
similarity index 94%
rename from src/net/lookup_stub.go
rename to src/net/lookup_nacl.go
index bd096b39652a55e38a7d1191d350c17c982ee647..48c0d1938ecf1020f61cf4eae8b6fad04cb29f20 100644 (file)
@@ -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) {
index b3aeb85afbc1fa22a42717695def94d0b4c9e150..5de9f39b08a2c060a6a4e01e67d21d187094a721 100644 (file)
@@ -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)
+               }
+       }
+
+}
index be0ae9aefa556fc24955c239997fb656477e824d..fe84a6420808709675b883849190882c1bdab78f 100644 (file)
@@ -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) {
index a8cb0199a0d17f2ac5aa0a6de6567e732ec4fdb2..4e0478194e57bb4e650f99dfd2a96694d06c7da9 100644 (file)
@@ -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)
 }