]> Cypherpunks repositories - gostls13.git/commitdiff
net: handle the network parameter properly in LookupPort
authorMateusz Poliwczak <mpoliwczak34@gmail.com>
Mon, 2 Oct 2023 16:24:26 +0000 (16:24 +0000)
committerGopher Robot <gobot@golang.org>
Mon, 2 Oct 2023 23:13:53 +0000 (23:13 +0000)
The cgo version (unix) is populating the GetAddrInfo hints
based on the network parameter, but windows not quite.

This change populates the hints the same way as the
cgo unix version does now.

This bug was spotted by Bryan in CL 530415.
https://go-review.googlesource.com/c/go/+/530415/comment/76640dc7_ed0409ca/

Change-Id: I6fc29b1e4cdc879123ab0f5a624b6f37c68c00ba
GitHub-Last-Rev: eaa616378b3fa9a5a72192f3d501c591804f45d8
GitHub-Pull-Request: golang/go#63284
Reviewed-on: https://go-review.googlesource.com/c/go/+/531635
Reviewed-by: Ian Lance Taylor <iant@google.com>
Run-TryBot: Mateusz Poliwczak <mpoliwczak34@gmail.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: David du Colombier <0intro@gmail.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
src/net/cgo_unix.go
src/net/lookup.go
src/net/lookup_plan9.go
src/net/lookup_test.go
src/net/lookup_windows.go

index 2a7d1ec3fafae369a8bb336ac439ff56032dcd11..0a783d08a9f15b9278ac47a3333706cf41f3c933 100644 (file)
@@ -80,7 +80,7 @@ func cgoLookupHost(ctx context.Context, name string) (hosts []string, err error)
 func cgoLookupPort(ctx context.Context, network, service string) (port int, err error) {
        var hints _C_struct_addrinfo
        switch network {
-       case "": // no hints
+       case "ip": // no hints
        case "tcp", "tcp4", "tcp6":
                *_C_ai_socktype(&hints) = _C_SOCK_STREAM
                *_C_ai_protocol(&hints) = _C_IPPROTO_TCP
index 28532075d4f7fc37acd7a0d643dc741d859b1642..15165970b6f38f46523daab9a8dc12d1daad4499 100644 (file)
@@ -41,19 +41,20 @@ var services = map[string]map[string]int{
                "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,
+               "ftp":         21,
+               "ftps":        990,
+               "gopher":      70, // ʕ◔ϖ◔ʔ
+               "http":        80,
+               "https":       443,
+               "imap2":       143,
+               "imap3":       220,
+               "imaps":       993,
+               "pop3":        110,
+               "pop3s":       995,
+               "smtp":        25,
+               "submissions": 465,
+               "ssh":         22,
+               "telnet":      23,
        },
 }
 
@@ -83,12 +84,20 @@ const maxPortBufSize = len("mobility-header") + 10
 
 func lookupPortMap(network, service string) (port int, error error) {
        switch network {
-       case "tcp4", "tcp6":
-               network = "tcp"
-       case "udp4", "udp6":
-               network = "udp"
+       case "ip": // no hints
+               if p, err := lookupPortMapWithNetwork("tcp", "ip", service); err == nil {
+                       return p, nil
+               }
+               return lookupPortMapWithNetwork("udp", "ip", service)
+       case "tcp", "tcp4", "tcp6":
+               return lookupPortMapWithNetwork("tcp", "tcp", service)
+       case "udp", "udp4", "udp6":
+               return lookupPortMapWithNetwork("udp", "udp", service)
        }
+       return 0, &DNSError{Err: "unknown network", Name: network + "/" + service}
+}
 
+func lookupPortMapWithNetwork(network, errNetwork, service string) (port int, error error) {
        if m, ok := services[network]; ok {
                var lowerService [maxPortBufSize]byte
                n := copy(lowerService[:], service)
@@ -96,9 +105,9 @@ func lookupPortMap(network, service string) (port int, error error) {
                if port, ok := m[string(lowerService[:n])]; ok && n == len(service) {
                        return port, nil
                }
-               return 0, &DNSError{Err: "unknown port", Name: network + "/" + service, IsNotFound: true}
+               return 0, &DNSError{Err: "unknown port", Name: errNetwork + "/" + service, IsNotFound: true}
        }
-       return 0, &DNSError{Err: "unknown network", Name: network + "/" + service}
+       return 0, &DNSError{Err: "unknown network", Name: errNetwork + "/" + service}
 }
 
 // ipVersion returns the provided network's IP version: '4', '6' or 0
@@ -415,11 +424,13 @@ func LookupPort(network, service string) (port int, err error) {
 }
 
 // LookupPort looks up the port for the given network and service.
+//
+// The network must be one of "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6" or "ip".
 func (r *Resolver) LookupPort(ctx context.Context, network, service string) (port int, err error) {
        port, needsLookup := parsePort(service)
        if needsLookup {
                switch network {
-               case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6":
+               case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6", "ip":
                case "": // a hint wildcard for Go 1.0 undocumented behavior
                        network = "ip"
                default:
index c9b4da951c96f9e99da66d394ab0a19a2954f755..9d2c4cda5bf53d73bb23745e90c82be16cadf20a 100644 (file)
@@ -203,26 +203,36 @@ func (r *Resolver) lookupIP(ctx context.Context, network, host string) (addrs []
        return
 }
 
-func (*Resolver) lookupPort(ctx context.Context, network, service string) (port int, err error) {
+func (*Resolver) lookupPort(ctx context.Context, network, service string) (port int, err error) {
        switch network {
-       case "tcp4", "tcp6":
-               network = "tcp"
-       case "udp4", "udp6":
-               network = "udp"
+       case "ip": // no hints
+               if p, err := r.lookupPortWithNetwork(ctx, "tcp", "ip", service); err == nil {
+                       return p, nil
+               }
+               return r.lookupPortWithNetwork(ctx, "udp", "ip", service)
+       case "tcp", "tcp4", "tcp6":
+               return r.lookupPortWithNetwork(ctx, "tcp", "tcp", service)
+       case "udp", "udp4", "udp6":
+               return r.lookupPortWithNetwork(ctx, "udp", "udp", service)
+       default:
+               return 0, &DNSError{Err: "unknown network", Name: network + "/" + service}
        }
+}
+
+func (*Resolver) lookupPortWithNetwork(ctx context.Context, network, errNetwork, service string) (port int, err error) {
        lines, err := queryCS(ctx, network, "127.0.0.1", toLower(service))
        if err != nil {
                if stringsHasSuffix(err.Error(), "can't translate service") {
-                       return 0, &DNSError{Err: "unknown port", Name: network + "/" + service, IsNotFound: true}
+                       return 0, &DNSError{Err: "unknown port", Name: errNetwork + "/" + service, IsNotFound: true}
                }
                return
        }
        if len(lines) == 0 {
-               return 0, &DNSError{Err: "unknown port", Name: network + "/" + service, IsNotFound: true}
+               return 0, &DNSError{Err: "unknown port", Name: errNetwork + "/" + service, IsNotFound: true}
        }
        f := getFields(lines[0])
        if len(f) < 2 {
-               return 0, &DNSError{Err: "unknown port", Name: network + "/" + service, IsNotFound: true}
+               return 0, &DNSError{Err: "unknown port", Name: errNetwork + "/" + service, IsNotFound: true}
        }
        s := f[1]
        if i := bytealg.IndexByteString(s, '!'); i >= 0 {
@@ -231,7 +241,7 @@ func (*Resolver) lookupPort(ctx context.Context, network, service string) (port
        if n, _, ok := dtoi(s); ok {
                return n, nil
        }
-       return 0, &DNSError{Err: "unknown port", Name: network + "/" + service, IsNotFound: true}
+       return 0, &DNSError{Err: "unknown port", Name: errNetwork + "/" + service, IsNotFound: true}
 }
 
 func (r *Resolver) lookupCNAME(ctx context.Context, name string) (cname string, err error) {
index ab68d75836e454584360bbef07c62109c11bfb52..1e222763bd0d0df71bcc61863b410c3a2daeb129 100644 (file)
@@ -1462,9 +1462,65 @@ func testLookupNoData(t *testing.T, prefix string) {
 }
 
 func TestLookupPortNotFound(t *testing.T) {
-       _, err := LookupPort("udp", "_-unknown-service-")
-       var dnsErr *DNSError
-       if !errors.As(err, &dnsErr) || !dnsErr.IsNotFound {
-               t.Fatalf("unexpected error: %v", err)
+       allResolvers(t, func(t *testing.T) {
+               _, err := LookupPort("udp", "_-unknown-service-")
+               var dnsErr *DNSError
+               if !errors.As(err, &dnsErr) || !dnsErr.IsNotFound {
+                       t.Fatalf("unexpected error: %v", err)
+               }
+       })
+}
+
+// submissions service is only available through a tcp network, see:
+// https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=submissions
+var tcpOnlyService = func() string {
+       // plan9 does not have submissions service defined in the service database.
+       if runtime.GOOS == "plan9" {
+               return "https"
        }
+       return "submissions"
+}()
+
+func TestLookupPortDifferentNetwork(t *testing.T) {
+       allResolvers(t, func(t *testing.T) {
+               _, err := LookupPort("udp", tcpOnlyService)
+               var dnsErr *DNSError
+               if !errors.As(err, &dnsErr) || !dnsErr.IsNotFound {
+                       t.Fatalf("unexpected error: %v", err)
+               }
+       })
+}
+
+func TestLookupPortEmptyNetworkString(t *testing.T) {
+       allResolvers(t, func(t *testing.T) {
+               _, err := LookupPort("", tcpOnlyService)
+               if err != nil {
+                       t.Fatalf("unexpected error: %v", err)
+               }
+       })
+}
+
+func TestLookupPortIPNetworkString(t *testing.T) {
+       allResolvers(t, func(t *testing.T) {
+               _, err := LookupPort("ip", tcpOnlyService)
+               if err != nil {
+                       t.Fatalf("unexpected error: %v", err)
+               }
+       })
+}
+
+func allResolvers(t *testing.T, f func(t *testing.T)) {
+       t.Run("default resolver", f)
+       t.Run("forced go resolver", func(t *testing.T) {
+               if fixup := forceGoDNS(); fixup != nil {
+                       defer fixup()
+                       f(t)
+               }
+       })
+       t.Run("forced cgo resolver", func(t *testing.T) {
+               if fixup := forceCgoDNS(); fixup != nil {
+                       defer fixup()
+                       f(t)
+               }
+       })
 }
index ce0df7ddd880888d580487a66e21654d05cfad1b..b6ef6da716edecadb9e1c7dffd3d9e1a328c7dda 100644 (file)
@@ -200,18 +200,28 @@ func (r *Resolver) lookupPort(ctx context.Context, network, service string) (int
        // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
        acquireThread()
        defer releaseThread()
-       var stype int32
+
+       var hints syscall.AddrinfoW
+
        switch network {
-       case "tcp4", "tcp6":
-               stype = syscall.SOCK_STREAM
-       case "udp4", "udp6":
-               stype = syscall.SOCK_DGRAM
+       case "ip": // no hints
+       case "tcp", "tcp4", "tcp6":
+               hints.Socktype = syscall.SOCK_STREAM
+               hints.Protocol = syscall.IPPROTO_TCP
+       case "udp", "udp4", "udp6":
+               hints.Socktype = syscall.SOCK_DGRAM
+               hints.Protocol = syscall.IPPROTO_UDP
+       default:
+               return 0, &DNSError{Err: "unknown network", Name: network + "/" + service}
        }
-       hints := syscall.AddrinfoW{
-               Family:   syscall.AF_UNSPEC,
-               Socktype: stype,
-               Protocol: syscall.IPPROTO_IP,
+
+       switch ipVersion(network) {
+       case '4':
+               hints.Family = syscall.AF_INET
+       case '6':
+               hints.Family = syscall.AF_INET6
        }
+
        var result *syscall.AddrinfoW
        e := syscall.GetAddrInfoW(nil, syscall.StringToUTF16Ptr(service), &hints, &result)
        if e != nil {