Commit 74190735 authored by Brad Fitzpatrick's avatar Brad Fitzpatrick

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 <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarMinux Ma <minux@golang.org>
parent f7fce1e2
......@@ -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)
......
......@@ -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.
......
......@@ -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) {
......
......@@ -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)
}
}
}
......@@ -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) {
......
......@@ -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)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment