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>
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)
// 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.
)
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) {
}
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) {
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 {
}
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)
}
}
}
+
+// 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)
+ }
+ }
+
+}
// 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) {
// 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
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
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 {
// 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)
}