Browse Source

net/portmapper, go.mod: unfork our goupnp dependency

Updates #7436

Signed-off-by: Andrew Dunham <[email protected]>
Andrew Dunham 2 months ago
parent
commit
6aac87a84c

+ 6 - 6
cmd/k8s-operator/depaware.txt

@@ -55,6 +55,12 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
         github.com/google/gnostic-models/openapiv3                   from k8s.io/kube-openapi/pkg/handler3+
         github.com/google/uuid                                       from github.com/prometheus-community/pro-bing+
         github.com/hdevalence/ed25519consensus                       from tailscale.com/tka
+        github.com/huin/goupnp                                       from github.com/huin/goupnp/dcps/internetgateway2+
+        github.com/huin/goupnp/dcps/internetgateway2                 from tailscale.com/net/portmapper
+        github.com/huin/goupnp/httpu                                 from github.com/huin/goupnp+
+        github.com/huin/goupnp/scpd                                  from github.com/huin/goupnp
+        github.com/huin/goupnp/soap                                  from github.com/huin/goupnp+
+        github.com/huin/goupnp/ssdp                                  from github.com/huin/goupnp
         github.com/josharian/intern                                  from github.com/mailru/easyjson/jlexer
    L 💣 github.com/jsimonetti/rtnetlink                              from tailscale.com/net/netmon
    L    github.com/jsimonetti/rtnetlink/internal/unix                from github.com/jsimonetti/rtnetlink
@@ -103,12 +109,6 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
    W 💣 github.com/tailscale/go-winio/internal/socket                from github.com/tailscale/go-winio
    W    github.com/tailscale/go-winio/internal/stringbuffer          from github.com/tailscale/go-winio/internal/fs
    W    github.com/tailscale/go-winio/pkg/guid                       from github.com/tailscale/go-winio+
-        github.com/tailscale/goupnp                                  from github.com/tailscale/goupnp/dcps/internetgateway2+
-        github.com/tailscale/goupnp/dcps/internetgateway2            from tailscale.com/net/portmapper
-        github.com/tailscale/goupnp/httpu                            from github.com/tailscale/goupnp+
-        github.com/tailscale/goupnp/scpd                             from github.com/tailscale/goupnp
-        github.com/tailscale/goupnp/soap                             from github.com/tailscale/goupnp+
-        github.com/tailscale/goupnp/ssdp                             from github.com/tailscale/goupnp
         github.com/tailscale/hujson                                  from tailscale.com/ipn/conffile+
   LD    github.com/tailscale/peercred                                from tailscale.com/ipn/ipnauth
         github.com/tailscale/web-client-prebuilt                     from tailscale.com/client/web

+ 7 - 7
cmd/tailscale/depaware.txt

@@ -35,6 +35,12 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
         github.com/golang/groupcache/lru                             from tailscale.com/net/dnscache
   DW    github.com/google/uuid                                       from tailscale.com/clientupdate+
         github.com/hdevalence/ed25519consensus                       from tailscale.com/clientupdate/distsign+
+        github.com/huin/goupnp                                       from github.com/huin/goupnp/dcps/internetgateway2+
+        github.com/huin/goupnp/dcps/internetgateway2                 from tailscale.com/net/portmapper
+        github.com/huin/goupnp/httpu                                 from github.com/huin/goupnp+
+        github.com/huin/goupnp/scpd                                  from github.com/huin/goupnp
+        github.com/huin/goupnp/soap                                  from github.com/huin/goupnp+
+        github.com/huin/goupnp/ssdp                                  from github.com/huin/goupnp
    L 💣 github.com/jsimonetti/rtnetlink                              from tailscale.com/net/netmon
    L    github.com/jsimonetti/rtnetlink/internal/unix                from github.com/jsimonetti/rtnetlink
         github.com/kballard/go-shellquote                            from tailscale.com/cmd/tailscale/cli
@@ -55,12 +61,6 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
    W 💣 github.com/tailscale/go-winio/internal/socket                from github.com/tailscale/go-winio
    W    github.com/tailscale/go-winio/internal/stringbuffer          from github.com/tailscale/go-winio/internal/fs
    W    github.com/tailscale/go-winio/pkg/guid                       from github.com/tailscale/go-winio+
-        github.com/tailscale/goupnp                                  from github.com/tailscale/goupnp/dcps/internetgateway2+
-        github.com/tailscale/goupnp/dcps/internetgateway2            from tailscale.com/net/portmapper
-        github.com/tailscale/goupnp/httpu                            from github.com/tailscale/goupnp+
-        github.com/tailscale/goupnp/scpd                             from github.com/tailscale/goupnp
-        github.com/tailscale/goupnp/soap                             from github.com/tailscale/goupnp+
-        github.com/tailscale/goupnp/ssdp                             from github.com/tailscale/goupnp
         github.com/tailscale/hujson                                  from tailscale.com/ipn/conffile
         github.com/tailscale/web-client-prebuilt                     from tailscale.com/client/web
         github.com/toqueteos/webbrowser                              from tailscale.com/cmd/tailscale/cli+
@@ -463,7 +463,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
         path                                                         from archive/tar+
         path/filepath                                                from archive/tar+
         reflect                                                      from archive/tar+
-        regexp                                                       from github.com/tailscale/goupnp/httpu+
+        regexp                                                       from github.com/huin/goupnp/httpu+
         regexp/syntax                                                from regexp
         runtime                                                      from archive/tar+
         runtime/debug                                                from tailscale.com+

+ 6 - 6
cmd/tailscaled/depaware.txt

@@ -125,6 +125,12 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
    L    github.com/google/nftables/xt                                from github.com/google/nftables/expr+
   DW    github.com/google/uuid                                       from tailscale.com/clientupdate+
         github.com/hdevalence/ed25519consensus                       from tailscale.com/clientupdate/distsign+
+        github.com/huin/goupnp                                       from github.com/huin/goupnp/dcps/internetgateway2+
+        github.com/huin/goupnp/dcps/internetgateway2                 from tailscale.com/net/portmapper
+        github.com/huin/goupnp/httpu                                 from github.com/huin/goupnp+
+        github.com/huin/goupnp/scpd                                  from github.com/huin/goupnp
+        github.com/huin/goupnp/soap                                  from github.com/huin/goupnp+
+        github.com/huin/goupnp/ssdp                                  from github.com/huin/goupnp
    L 💣 github.com/illarion/gonotify/v3                              from tailscale.com/feature/linuxdnsfight
    L    github.com/illarion/gonotify/v3/syscallf                     from github.com/illarion/gonotify/v3
    L    github.com/insomniacslk/dhcp/dhcpv4                          from tailscale.com/feature/tap
@@ -168,12 +174,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
    W 💣 github.com/tailscale/go-winio/internal/socket                from github.com/tailscale/go-winio
    W    github.com/tailscale/go-winio/internal/stringbuffer          from github.com/tailscale/go-winio/internal/fs
    W    github.com/tailscale/go-winio/pkg/guid                       from github.com/tailscale/go-winio+
-        github.com/tailscale/goupnp                                  from github.com/tailscale/goupnp/dcps/internetgateway2+
-        github.com/tailscale/goupnp/dcps/internetgateway2            from tailscale.com/net/portmapper
-        github.com/tailscale/goupnp/httpu                            from github.com/tailscale/goupnp+
-        github.com/tailscale/goupnp/scpd                             from github.com/tailscale/goupnp
-        github.com/tailscale/goupnp/soap                             from github.com/tailscale/goupnp+
-        github.com/tailscale/goupnp/ssdp                             from github.com/tailscale/goupnp
         github.com/tailscale/hujson                                  from tailscale.com/ipn/conffile
    L 💣 github.com/tailscale/netlink                                 from tailscale.com/net/routetable+
    L 💣 github.com/tailscale/netlink/nl                              from github.com/tailscale/netlink

+ 8 - 8
cmd/tsidp/depaware.txt

@@ -30,6 +30,12 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
         github.com/google/btree                                      from gvisor.dev/gvisor/pkg/tcpip/header+
    D    github.com/google/uuid                                       from github.com/prometheus-community/pro-bing
         github.com/hdevalence/ed25519consensus                       from tailscale.com/tka
+        github.com/huin/goupnp                                       from github.com/huin/goupnp/dcps/internetgateway2+
+        github.com/huin/goupnp/dcps/internetgateway2                 from tailscale.com/net/portmapper
+        github.com/huin/goupnp/httpu                                 from github.com/huin/goupnp+
+        github.com/huin/goupnp/scpd                                  from github.com/huin/goupnp
+        github.com/huin/goupnp/soap                                  from github.com/huin/goupnp+
+        github.com/huin/goupnp/ssdp                                  from github.com/huin/goupnp
    L 💣 github.com/jsimonetti/rtnetlink                              from tailscale.com/net/netmon
    L    github.com/jsimonetti/rtnetlink/internal/unix                from github.com/jsimonetti/rtnetlink
         github.com/klauspost/compress                                from github.com/klauspost/compress/zstd
@@ -53,12 +59,6 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
    W 💣 github.com/tailscale/go-winio/internal/socket                from github.com/tailscale/go-winio
    W    github.com/tailscale/go-winio/internal/stringbuffer          from github.com/tailscale/go-winio/internal/fs
    W    github.com/tailscale/go-winio/pkg/guid                       from github.com/tailscale/go-winio+
-        github.com/tailscale/goupnp                                  from github.com/tailscale/goupnp/dcps/internetgateway2+
-        github.com/tailscale/goupnp/dcps/internetgateway2            from tailscale.com/net/portmapper
-        github.com/tailscale/goupnp/httpu                            from github.com/tailscale/goupnp+
-        github.com/tailscale/goupnp/scpd                             from github.com/tailscale/goupnp
-        github.com/tailscale/goupnp/soap                             from github.com/tailscale/goupnp+
-        github.com/tailscale/goupnp/ssdp                             from github.com/tailscale/goupnp
         github.com/tailscale/hujson                                  from tailscale.com/ipn/conffile
   LD    github.com/tailscale/peercred                                from tailscale.com/ipn/ipnauth
         github.com/tailscale/web-client-prebuilt                     from tailscale.com/client/web
@@ -471,7 +471,7 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
         encoding/hex                                                 from crypto/x509+
         encoding/json                                                from expvar+
         encoding/pem                                                 from crypto/tls+
-        encoding/xml                                                 from github.com/tailscale/goupnp+
+        encoding/xml                                                 from github.com/huin/goupnp+
         errors                                                       from bufio+
         expvar                                                       from tailscale.com/health+
         flag                                                         from tailscale.com/cmd/tsidp+
@@ -562,7 +562,7 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
         path                                                         from debug/dwarf+
         path/filepath                                                from crypto/x509+
         reflect                                                      from crypto/x509+
-        regexp                                                       from github.com/tailscale/goupnp/httpu+
+        regexp                                                       from github.com/huin/goupnp/httpu+
         regexp/syntax                                                from regexp
         runtime                                                      from crypto/internal/fips140+
         runtime/debug                                                from github.com/coder/websocket/internal/xsync+

+ 1 - 1
flake.nix

@@ -151,4 +151,4 @@
     });
   };
 }
-# nix-direnv cache busting line: sha256-PVE4oEwcoTbXbPJnBcVgBeXITWlnkhBf+UT5nqSeANM=
+# nix-direnv cache busting line: sha256-knSIes9pFVkVfK5hcBG9BSR1ueH+yPpx4hv/UsyaW2M=

+ 1 - 1
go.mod

@@ -56,6 +56,7 @@ require (
 	github.com/hashicorp/raft v1.7.2
 	github.com/hashicorp/raft-boltdb/v2 v2.3.1
 	github.com/hdevalence/ed25519consensus v0.2.0
+	github.com/huin/goupnp v1.3.0
 	github.com/illarion/gonotify/v3 v3.0.2
 	github.com/inetaf/tcpproxy v0.0.0-20250203165043-ded522cbd03f
 	github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2
@@ -86,7 +87,6 @@ require (
 	github.com/tailscale/depaware v0.0.0-20251001183927-9c2ad255ef3f
 	github.com/tailscale/goexpect v0.0.0-20210902213824-6e8c725cea41
 	github.com/tailscale/golang-x-crypto v0.0.0-20250404221719-a5573b049869
-	github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05
 	github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a
 	github.com/tailscale/mkctr v0.0.0-20260107121656-ea857e3e500b
 	github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7

+ 1 - 1
go.mod.sri

@@ -1 +1 @@
-sha256-PVE4oEwcoTbXbPJnBcVgBeXITWlnkhBf+UT5nqSeANM=
+sha256-knSIes9pFVkVfK5hcBG9BSR1ueH+yPpx4hv/UsyaW2M=

+ 2 - 2
go.sum

@@ -670,6 +670,8 @@ github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI
 github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
 github.com/hugelgupf/vmtest v0.0.0-20240216064925-0561770280a1 h1:jWoR2Yqg8tzM0v6LAiP7i1bikZJu3gxpgvu3g1Lw+a0=
 github.com/hugelgupf/vmtest v0.0.0-20240216064925-0561770280a1/go.mod h1:B63hDJMhTupLWCHwopAyEo7wRFowx9kOc8m8j1sfOqE=
+github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
+github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
 github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 github.com/illarion/gonotify/v3 v3.0.2 h1:O7S6vcopHexutmpObkeWsnzMJt/r1hONIEogeVNmJMk=
 github.com/illarion/gonotify/v3 v3.0.2/go.mod h1:HWGPdPe817GfvY3w7cx6zkbzNZfi3QjcBm/wgVvEL1U=
@@ -1090,8 +1092,6 @@ github.com/tailscale/goexpect v0.0.0-20210902213824-6e8c725cea41 h1:/V2rCMMWcsjY
 github.com/tailscale/goexpect v0.0.0-20210902213824-6e8c725cea41/go.mod h1:/roCdA6gg6lQyw/Oz6gIIGu3ggJKYhF+WC/AQReE5XQ=
 github.com/tailscale/golang-x-crypto v0.0.0-20250404221719-a5573b049869 h1:SRL6irQkKGQKKLzvQP/ke/2ZuB7Py5+XuqtOgSj+iMM=
 github.com/tailscale/golang-x-crypto v0.0.0-20250404221719-a5573b049869/go.mod h1:ikbF+YT089eInTp9f2vmvy4+ZVnW5hzX1q2WknxSprQ=
-github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 h1:4chzWmimtJPxRs2O36yuGRW3f9SYV+bMTTvMBI0EKio=
-github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05/go.mod h1:PdCqy9JzfWMJf1H5UJW2ip33/d4YkoKN0r67yKH1mG8=
 github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29XwJucQo73FrleVK6t4kYz4NVhp34Yw=
 github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8=
 github.com/tailscale/mkctr v0.0.0-20260107121656-ea857e3e500b h1:QKqCnmp0qHWUHySySKjpuhZANzRn7XrTVZWUuUgJ3lQ=

+ 1 - 1
licenses/android.md

@@ -27,7 +27,7 @@ Client][].  See also the dependencies in the [Tailscale CLI][].
  - [github.com/mdlayher/socket](https://pkg.go.dev/github.com/mdlayher/socket) ([MIT](https://github.com/mdlayher/socket/blob/v0.5.0/LICENSE.md))
  - [github.com/pierrec/lz4/v4](https://pkg.go.dev/github.com/pierrec/lz4/v4) ([BSD-3-Clause](https://github.com/pierrec/lz4/blob/v4.1.21/LICENSE))
  - [github.com/pires/go-proxyproto](https://pkg.go.dev/github.com/pires/go-proxyproto) ([Apache-2.0](https://github.com/pires/go-proxyproto/blob/v0.8.1/LICENSE))
- - [github.com/tailscale/goupnp](https://pkg.go.dev/github.com/tailscale/goupnp) ([BSD-2-Clause](https://github.com/tailscale/goupnp/blob/c64d0f06ea05/LICENSE))
+ - [github.com/huin/goupnp](https://pkg.go.dev/github.com/huin/goupnp) ([BSD-2-Clause](https://github.com/huin/goupnp/blob/v1.3.0/LICENSE))
  - [github.com/tailscale/peercred](https://pkg.go.dev/github.com/tailscale/peercred) ([BSD-3-Clause](https://github.com/tailscale/peercred/blob/35a0c7bd7edc/LICENSE))
  - [github.com/tailscale/tailscale-android/libtailscale](https://pkg.go.dev/github.com/tailscale/tailscale-android/libtailscale) ([BSD-3-Clause](https://github.com/tailscale/tailscale-android/blob/HEAD/LICENSE))
  - [github.com/tailscale/wireguard-go](https://pkg.go.dev/github.com/tailscale/wireguard-go) ([MIT](https://github.com/tailscale/wireguard-go/blob/1d0488a3d7da/LICENSE))

+ 1 - 1
licenses/apple.md

@@ -59,7 +59,7 @@ See also the dependencies in the [Tailscale CLI][].
  - [github.com/pires/go-proxyproto](https://pkg.go.dev/github.com/pires/go-proxyproto) ([Apache-2.0](https://github.com/pires/go-proxyproto/blob/v0.8.1/LICENSE))
  - [github.com/prometheus-community/pro-bing](https://pkg.go.dev/github.com/prometheus-community/pro-bing) ([MIT](https://github.com/prometheus-community/pro-bing/blob/v0.4.0/LICENSE))
  - [github.com/safchain/ethtool](https://pkg.go.dev/github.com/safchain/ethtool) ([Apache-2.0](https://github.com/safchain/ethtool/blob/v0.3.0/LICENSE))
- - [github.com/tailscale/goupnp](https://pkg.go.dev/github.com/tailscale/goupnp) ([BSD-2-Clause](https://github.com/tailscale/goupnp/blob/c64d0f06ea05/LICENSE))
+ - [github.com/huin/goupnp](https://pkg.go.dev/github.com/huin/goupnp) ([BSD-2-Clause](https://github.com/huin/goupnp/blob/v1.3.0/LICENSE))
  - [github.com/tailscale/netlink](https://pkg.go.dev/github.com/tailscale/netlink) ([Apache-2.0](https://github.com/tailscale/netlink/blob/4d49adab4de7/LICENSE))
  - [github.com/tailscale/peercred](https://pkg.go.dev/github.com/tailscale/peercred) ([BSD-3-Clause](https://github.com/tailscale/peercred/blob/35a0c7bd7edc/LICENSE))
  - [github.com/tailscale/wireguard-go](https://pkg.go.dev/github.com/tailscale/wireguard-go) ([MIT](https://github.com/tailscale/wireguard-go/blob/1d0488a3d7da/LICENSE))

+ 26 - 26
net/portmapper/legacy_upnp.go

@@ -10,8 +10,8 @@ package portmapper
 import (
 	"context"
 
-	"github.com/tailscale/goupnp"
-	"github.com/tailscale/goupnp/soap"
+	"github.com/huin/goupnp"
+	"github.com/huin/goupnp/soap"
 )
 
 const (
@@ -32,8 +32,8 @@ type legacyWANPPPConnection1 struct {
 	goupnp.ServiceClient
 }
 
-// AddPortMapping implements upnpClient
-func (client *legacyWANPPPConnection1) AddPortMapping(
+// AddPortMappingCtx implements upnpClient
+func (client *legacyWANPPPConnection1) AddPortMappingCtx(
 	ctx context.Context,
 	NewRemoteHost string,
 	NewExternalPort uint16,
@@ -85,11 +85,11 @@ func (client *legacyWANPPPConnection1) AddPortMapping(
 	response := any(nil)
 
 	// Perform the SOAP call.
-	return client.SOAPClient.PerformAction(ctx, urn_LegacyWANPPPConnection_1, "AddPortMapping", request, response)
+	return client.SOAPClient.PerformActionCtx(ctx, urn_LegacyWANPPPConnection_1, "AddPortMapping", request, response)
 }
 
-// DeletePortMapping implements upnpClient
-func (client *legacyWANPPPConnection1) DeletePortMapping(ctx context.Context, NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (err error) {
+// DeletePortMappingCtx implements upnpClient
+func (client *legacyWANPPPConnection1) DeletePortMappingCtx(ctx context.Context, NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (err error) {
 	// Request structure.
 	request := &struct {
 		NewRemoteHost   string
@@ -110,11 +110,11 @@ func (client *legacyWANPPPConnection1) DeletePortMapping(ctx context.Context, Ne
 	response := any(nil)
 
 	// Perform the SOAP call.
-	return client.SOAPClient.PerformAction(ctx, urn_LegacyWANPPPConnection_1, "DeletePortMapping", request, response)
+	return client.SOAPClient.PerformActionCtx(ctx, urn_LegacyWANPPPConnection_1, "DeletePortMapping", request, response)
 }
 
-// GetExternalIPAddress implements upnpClient
-func (client *legacyWANPPPConnection1) GetExternalIPAddress(ctx context.Context) (NewExternalIPAddress string, err error) {
+// GetExternalIPAddressCtx implements upnpClient
+func (client *legacyWANPPPConnection1) GetExternalIPAddressCtx(ctx context.Context) (NewExternalIPAddress string, err error) {
 	// Request structure.
 	request := any(nil)
 
@@ -124,7 +124,7 @@ func (client *legacyWANPPPConnection1) GetExternalIPAddress(ctx context.Context)
 	}{}
 
 	// Perform the SOAP call.
-	if err = client.SOAPClient.PerformAction(ctx, urn_LegacyWANPPPConnection_1, "GetExternalIPAddress", request, response); err != nil {
+	if err = client.SOAPClient.PerformActionCtx(ctx, urn_LegacyWANPPPConnection_1, "GetExternalIPAddress", request, response); err != nil {
 		return
 	}
 
@@ -134,8 +134,8 @@ func (client *legacyWANPPPConnection1) GetExternalIPAddress(ctx context.Context)
 	return
 }
 
-// GetStatusInfo implements upnpClient
-func (client *legacyWANPPPConnection1) GetStatusInfo(ctx context.Context) (NewConnectionStatus string, NewLastConnectionError string, NewUptime uint32, err error) {
+// GetStatusInfoCtx implements upnpClient
+func (client *legacyWANPPPConnection1) GetStatusInfoCtx(ctx context.Context) (NewConnectionStatus string, NewLastConnectionError string, NewUptime uint32, err error) {
 	// Request structure.
 	request := any(nil)
 
@@ -147,7 +147,7 @@ func (client *legacyWANPPPConnection1) GetStatusInfo(ctx context.Context) (NewCo
 	}{}
 
 	// Perform the SOAP call.
-	if err = client.SOAPClient.PerformAction(ctx, urn_LegacyWANPPPConnection_1, "GetStatusInfo", request, response); err != nil {
+	if err = client.SOAPClient.PerformActionCtx(ctx, urn_LegacyWANPPPConnection_1, "GetStatusInfo", request, response); err != nil {
 		return
 	}
 
@@ -171,8 +171,8 @@ type legacyWANIPConnection1 struct {
 	goupnp.ServiceClient
 }
 
-// AddPortMapping implements upnpClient
-func (client *legacyWANIPConnection1) AddPortMapping(
+// AddPortMappingCtx implements upnpClient
+func (client *legacyWANIPConnection1) AddPortMappingCtx(
 	ctx context.Context,
 	NewRemoteHost string,
 	NewExternalPort uint16,
@@ -224,11 +224,11 @@ func (client *legacyWANIPConnection1) AddPortMapping(
 	response := any(nil)
 
 	// Perform the SOAP call.
-	return client.SOAPClient.PerformAction(ctx, urn_LegacyWANIPConnection_1, "AddPortMapping", request, response)
+	return client.SOAPClient.PerformActionCtx(ctx, urn_LegacyWANIPConnection_1, "AddPortMapping", request, response)
 }
 
-// DeletePortMapping implements upnpClient
-func (client *legacyWANIPConnection1) DeletePortMapping(ctx context.Context, NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (err error) {
+// DeletePortMappingCtx implements upnpClient
+func (client *legacyWANIPConnection1) DeletePortMappingCtx(ctx context.Context, NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (err error) {
 	// Request structure.
 	request := &struct {
 		NewRemoteHost   string
@@ -249,11 +249,11 @@ func (client *legacyWANIPConnection1) DeletePortMapping(ctx context.Context, New
 	response := any(nil)
 
 	// Perform the SOAP call.
-	return client.SOAPClient.PerformAction(ctx, urn_LegacyWANIPConnection_1, "DeletePortMapping", request, response)
+	return client.SOAPClient.PerformActionCtx(ctx, urn_LegacyWANIPConnection_1, "DeletePortMapping", request, response)
 }
 
-// GetExternalIPAddress implements upnpClient
-func (client *legacyWANIPConnection1) GetExternalIPAddress(ctx context.Context) (NewExternalIPAddress string, err error) {
+// GetExternalIPAddressCtx implements upnpClient
+func (client *legacyWANIPConnection1) GetExternalIPAddressCtx(ctx context.Context) (NewExternalIPAddress string, err error) {
 	// Request structure.
 	request := any(nil)
 
@@ -263,7 +263,7 @@ func (client *legacyWANIPConnection1) GetExternalIPAddress(ctx context.Context)
 	}{}
 
 	// Perform the SOAP call.
-	if err = client.SOAPClient.PerformAction(ctx, urn_LegacyWANIPConnection_1, "GetExternalIPAddress", request, response); err != nil {
+	if err = client.SOAPClient.PerformActionCtx(ctx, urn_LegacyWANIPConnection_1, "GetExternalIPAddress", request, response); err != nil {
 		return
 	}
 
@@ -273,8 +273,8 @@ func (client *legacyWANIPConnection1) GetExternalIPAddress(ctx context.Context)
 	return
 }
 
-// GetStatusInfo implements upnpClient
-func (client *legacyWANIPConnection1) GetStatusInfo(ctx context.Context) (NewConnectionStatus string, NewLastConnectionError string, NewUptime uint32, err error) {
+// GetStatusInfoCtx implements upnpClient
+func (client *legacyWANIPConnection1) GetStatusInfoCtx(ctx context.Context) (NewConnectionStatus string, NewLastConnectionError string, NewUptime uint32, err error) {
 	// Request structure.
 	request := any(nil)
 
@@ -286,7 +286,7 @@ func (client *legacyWANIPConnection1) GetStatusInfo(ctx context.Context) (NewCon
 	}{}
 
 	// Perform the SOAP call.
-	if err = client.SOAPClient.PerformAction(ctx, urn_LegacyWANIPConnection_1, "GetStatusInfo", request, response); err != nil {
+	if err = client.SOAPClient.PerformActionCtx(ctx, urn_LegacyWANIPConnection_1, "GetStatusInfo", request, response); err != nil {
 		return
 	}
 

+ 3 - 3
net/portmapper/select_test.go

@@ -11,8 +11,8 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/tailscale/goupnp"
-	"github.com/tailscale/goupnp/dcps/internetgateway2"
+	"github.com/huin/goupnp"
+	"github.com/huin/goupnp/dcps/internetgateway2"
 )
 
 // NOTE: this is in a distinct file because the various string constants are
@@ -168,7 +168,7 @@ func TestSelectBestService(t *testing.T) {
 
 			// Ensure that we're using the HTTP client that talks to our test IGD server
 			ctx := context.Background()
-			ctx = goupnp.WithHTTPClient(ctx, c.upnpHTTPClientLocked())
+			ctx = upnpHTTPClientKey.WithValue(ctx, c.upnpHTTPClientLocked())
 
 			loc := mustParseURL(igd.ts.URL)
 			rootDev := mustParseRootDev(t, rootDesc, loc)

+ 56 - 22
net/portmapper/upnp.go

@@ -25,15 +25,47 @@ import (
 	"sync/atomic"
 	"time"
 
-	"github.com/tailscale/goupnp"
-	"github.com/tailscale/goupnp/dcps/internetgateway2"
-	"github.com/tailscale/goupnp/soap"
+	"github.com/huin/goupnp"
+	"github.com/huin/goupnp/dcps/internetgateway2"
+	"github.com/huin/goupnp/soap"
 	"tailscale.com/envknob"
 	"tailscale.com/net/netns"
 	"tailscale.com/types/logger"
+	"tailscale.com/util/ctxkey"
 	"tailscale.com/util/mak"
 )
 
+// upnpHTTPClientKey is a context key for storing an HTTP client to use
+// for UPnP requests. This allows us to use a custom HTTP client (with custom
+// dialer, timeouts, etc.) while using the upstream goupnp library which only
+// supports a global HTTPClientDefault.
+var upnpHTTPClientKey = ctxkey.New[*http.Client]("portmapper.upnpHTTPClient", nil)
+
+// delegatingRoundTripper implements http.RoundTripper by delegating to
+// the HTTP client stored in the request's context. This allows us to use
+// per-request HTTP client configuration with the upstream goupnp library.
+type delegatingRoundTripper struct {
+	inner *http.Client
+}
+
+func (d delegatingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
+	if c := upnpHTTPClientKey.Value(req.Context()); c != nil {
+		return c.Transport.RoundTrip(req)
+	}
+	return d.inner.Do(req)
+}
+
+func init() {
+	// The upstream goupnp library uses a global HTTP client for all
+	// requests, while we want to be able to use a per-Client
+	// [http.Client]. We replace its global HTTP client with one that
+	// delegates to the HTTP client stored in the request's context.
+	old := goupnp.HTTPClientDefault
+	goupnp.HTTPClientDefault = &http.Client{
+		Transport: delegatingRoundTripper{old},
+	}
+}
+
 // References:
 //
 // WANIP Connection v2: http://upnp.org/specs/gw/UPnP-gw-WANIPConnection-v2-Service.pdf
@@ -79,14 +111,17 @@ func (u *upnpMapping) MappingDebug() string {
 		u.loc)
 }
 func (u *upnpMapping) Release(ctx context.Context) {
-	u.client.DeletePortMapping(ctx, "", u.external.Port(), upnpProtocolUDP)
+	u.client.DeletePortMappingCtx(ctx, "", u.external.Port(), upnpProtocolUDP)
 }
 
 // upnpClient is an interface over the multiple different clients exported by goupnp,
 // exposing the functions we need for portmapping. Those clients are auto-generated from XML-specs,
 // which is why they're not very idiomatic.
+//
+// The method names use the *Ctx suffix to match the upstream goupnp library's convention
+// for context-aware methods.
 type upnpClient interface {
-	AddPortMapping(
+	AddPortMappingCtx(
 		ctx context.Context,
 
 		// remoteHost is the remote device sending packets to this device, in the format of x.x.x.x.
@@ -119,9 +154,9 @@ type upnpClient interface {
 		leaseDurationSec uint32,
 	) error
 
-	DeletePortMapping(ctx context.Context, remoteHost string, externalPort uint16, protocol string) error
-	GetExternalIPAddress(ctx context.Context) (externalIPAddress string, err error)
-	GetStatusInfo(ctx context.Context) (status string, lastConnError string, uptime uint32, err error)
+	DeletePortMappingCtx(ctx context.Context, remoteHost string, externalPort uint16, protocol string) error
+	GetExternalIPAddressCtx(ctx context.Context) (externalIPAddress string, err error)
+	GetStatusInfoCtx(ctx context.Context) (status string, lastConnError string, uptime uint32, err error)
 }
 
 // tsPortMappingDesc gets sent to UPnP clients as a human-readable label for the portmapping.
@@ -171,7 +206,7 @@ func addAnyPortMapping(
 	// First off, try using AddAnyPortMapping; if there's a conflict, the
 	// router will pick another port and return it.
 	if upnp, ok := upnp.(*internetgateway2.WANIPConnection2); ok {
-		return upnp.AddAnyPortMapping(
+		return upnp.AddAnyPortMappingCtx(
 			ctx,
 			"",
 			externalPort,
@@ -186,7 +221,7 @@ func addAnyPortMapping(
 
 	// Fall back to using AddPortMapping, which requests a mapping to/from
 	// a specific external port.
-	err = upnp.AddPortMapping(
+	err = upnp.AddPortMappingCtx(
 		ctx,
 		"",
 		externalPort,
@@ -244,7 +279,7 @@ func getUPnPRootDevice(ctx context.Context, logf logger.Logf, debug DebugKnobs,
 	defer cancel()
 
 	// This part does a network fetch.
-	root, err := goupnp.DeviceByURL(ctx, u)
+	root, err := goupnp.DeviceByURLCtx(ctx, u)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -257,8 +292,7 @@ func getUPnPRootDevice(ctx context.Context, logf logger.Logf, debug DebugKnobs,
 //
 // loc is the parsed location that was used to fetch the given RootDevice.
 //
-// The provided ctx is not retained in the returned upnpClient, but
-// its associated HTTP client is (if set via goupnp.WithHTTPClient).
+// The provided ctx is not retained in the returned upnpClient.
 func selectBestService(ctx context.Context, logf logger.Logf, root *goupnp.RootDevice, loc *url.URL) (client upnpClient, err error) {
 	method := "none"
 	defer func() {
@@ -274,9 +308,9 @@ func selectBestService(ctx context.Context, logf logger.Logf, root *goupnp.RootD
 	// First, get all available clients from the device, and append to our
 	// list of possible clients. Order matters here; we want to prefer
 	// WANIPConnection2 over WANIPConnection1 or WANPPPConnection.
-	wanIP2, _ := internetgateway2.NewWANIPConnection2ClientsFromRootDevice(ctx, root, loc)
-	wanIP1, _ := internetgateway2.NewWANIPConnection1ClientsFromRootDevice(ctx, root, loc)
-	wanPPP, _ := internetgateway2.NewWANPPPConnection1ClientsFromRootDevice(ctx, root, loc)
+	wanIP2, _ := internetgateway2.NewWANIPConnection2ClientsFromRootDevice(root, loc)
+	wanIP1, _ := internetgateway2.NewWANIPConnection1ClientsFromRootDevice(root, loc)
+	wanPPP, _ := internetgateway2.NewWANPPPConnection1ClientsFromRootDevice(root, loc)
 
 	var clients []upnpClient
 	for _, v := range wanIP2 {
@@ -291,12 +325,12 @@ func selectBestService(ctx context.Context, logf logger.Logf, root *goupnp.RootD
 
 	// These are legacy services that were deprecated in 2015, but are
 	// still in use by older devices; try them just in case.
-	legacyClients, _ := goupnp.NewServiceClientsFromRootDevice(ctx, root, loc, urn_LegacyWANPPPConnection_1)
+	legacyClients, _ := goupnp.NewServiceClientsFromRootDevice(root, loc, urn_LegacyWANPPPConnection_1)
 	metricUPnPSelectLegacy.Add(int64(len(legacyClients)))
 	for _, client := range legacyClients {
 		clients = append(clients, &legacyWANPPPConnection1{client})
 	}
-	legacyClients, _ = goupnp.NewServiceClientsFromRootDevice(ctx, root, loc, urn_LegacyWANIPConnection_1)
+	legacyClients, _ = goupnp.NewServiceClientsFromRootDevice(root, loc, urn_LegacyWANIPConnection_1)
 	metricUPnPSelectLegacy.Add(int64(len(legacyClients)))
 	for _, client := range legacyClients {
 		clients = append(clients, &legacyWANIPConnection1{client})
@@ -346,7 +380,7 @@ func selectBestService(ctx context.Context, logf logger.Logf, root *goupnp.RootD
 		}
 
 		// Check if the device has an external IP address.
-		extIP, err := svc.GetExternalIPAddress(ctx)
+		extIP, err := svc.GetExternalIPAddressCtx(ctx)
 		if err != nil {
 			continue
 		}
@@ -399,7 +433,7 @@ func selectBestService(ctx context.Context, logf logger.Logf, root *goupnp.RootD
 // serviceIsConnected returns whether a given UPnP service is connected, based
 // on the NewConnectionStatus field returned from GetStatusInfo.
 func serviceIsConnected(ctx context.Context, logf logger.Logf, svc upnpClient) bool {
-	status, _ /* NewLastConnectionError */, _ /* NewUptime */, err := svc.GetStatusInfo(ctx)
+	status, _ /* NewLastConnectionError */, _ /* NewUptime */, err := svc.GetStatusInfoCtx(ctx)
 	if err != nil {
 		return false
 	}
@@ -454,7 +488,7 @@ func (c *Client) getUPnPPortMapping(
 	c.mu.Lock()
 	oldMapping, ok := c.mapping.(*upnpMapping)
 	metas := c.uPnPMetas
-	ctx = goupnp.WithHTTPClient(ctx, c.upnpHTTPClientLocked())
+	ctx = upnpHTTPClientKey.WithValue(ctx, c.upnpHTTPClientLocked())
 	c.mu.Unlock()
 
 	// Wrapper for a uPnPDiscoResponse with an optional existing root
@@ -629,7 +663,7 @@ func (c *Client) tryUPnPPortmapWithDevice(
 	}
 
 	// TODO cache this ip somewhere?
-	extIP, err := client.GetExternalIPAddress(ctx)
+	extIP, err := client.GetExternalIPAddressCtx(ctx)
 	c.vlogf("client.GetExternalIPAddress: %v, %v", extIP, err)
 	if err != nil {
 		return netip.AddrPort{}, nil, err

+ 1 - 1
shell.nix

@@ -16,4 +16,4 @@
 ) {
   src =  ./.;
 }).shellNix
-# nix-direnv cache busting line: sha256-PVE4oEwcoTbXbPJnBcVgBeXITWlnkhBf+UT5nqSeANM=
+# nix-direnv cache busting line: sha256-knSIes9pFVkVfK5hcBG9BSR1ueH+yPpx4hv/UsyaW2M=

+ 8 - 8
tsnet/depaware.txt

@@ -30,6 +30,12 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
         github.com/google/btree                                      from gvisor.dev/gvisor/pkg/tcpip/header+
   DI    github.com/google/uuid                                       from github.com/prometheus-community/pro-bing
         github.com/hdevalence/ed25519consensus                       from tailscale.com/tka
+        github.com/huin/goupnp                                       from github.com/huin/goupnp/dcps/internetgateway2+
+        github.com/huin/goupnp/dcps/internetgateway2                 from tailscale.com/net/portmapper
+        github.com/huin/goupnp/httpu                                 from github.com/huin/goupnp+
+        github.com/huin/goupnp/scpd                                  from github.com/huin/goupnp
+        github.com/huin/goupnp/soap                                  from github.com/huin/goupnp+
+        github.com/huin/goupnp/ssdp                                  from github.com/huin/goupnp
    L 💣 github.com/jsimonetti/rtnetlink                              from tailscale.com/net/netmon
    L    github.com/jsimonetti/rtnetlink/internal/unix                from github.com/jsimonetti/rtnetlink
         github.com/klauspost/compress                                from github.com/klauspost/compress/zstd
@@ -53,12 +59,6 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
    W 💣 github.com/tailscale/go-winio/internal/socket                from github.com/tailscale/go-winio
    W    github.com/tailscale/go-winio/internal/stringbuffer          from github.com/tailscale/go-winio/internal/fs
    W    github.com/tailscale/go-winio/pkg/guid                       from github.com/tailscale/go-winio+
-        github.com/tailscale/goupnp                                  from github.com/tailscale/goupnp/dcps/internetgateway2+
-        github.com/tailscale/goupnp/dcps/internetgateway2            from tailscale.com/net/portmapper
-        github.com/tailscale/goupnp/httpu                            from github.com/tailscale/goupnp+
-        github.com/tailscale/goupnp/scpd                             from github.com/tailscale/goupnp
-        github.com/tailscale/goupnp/soap                             from github.com/tailscale/goupnp+
-        github.com/tailscale/goupnp/ssdp                             from github.com/tailscale/goupnp
  LDW    github.com/tailscale/hujson                                  from tailscale.com/ipn/conffile
  LDAI    github.com/tailscale/peercred                                from tailscale.com/ipn/ipnauth
  LDW    github.com/tailscale/web-client-prebuilt                     from tailscale.com/client/web
@@ -464,7 +464,7 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
         encoding/hex                                                 from crypto/x509+
         encoding/json                                                from expvar+
         encoding/pem                                                 from crypto/tls+
-        encoding/xml                                                 from github.com/tailscale/goupnp+
+        encoding/xml                                                 from github.com/huin/goupnp+
         errors                                                       from bufio+
         expvar                                                       from tailscale.com/health+
         flag                                                         from tailscale.com/util/testenv
@@ -554,7 +554,7 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
         path                                                         from debug/dwarf+
         path/filepath                                                from crypto/x509+
         reflect                                                      from crypto/x509+
-        regexp                                                       from github.com/tailscale/goupnp/httpu+
+        regexp                                                       from github.com/huin/goupnp/httpu+
         regexp/syntax                                                from regexp
         runtime                                                      from crypto/internal/fips140+
         runtime/debug                                                from github.com/coder/websocket/internal/xsync+

+ 6 - 6
tstest/jsdeps/jsdeps_test.go

@@ -14,12 +14,12 @@ func TestDeps(t *testing.T) {
 		GOOS:   "js",
 		GOARCH: "wasm",
 		BadDeps: map[string]string{
-			"testing":                     "do not use testing package in production code",
-			"runtime/pprof":               "bloat",
-			"golang.org/x/net/http2/h2c":  "bloat",
-			"net/http/pprof":              "bloat",
-			"golang.org/x/net/proxy":      "bloat",
-			"github.com/tailscale/goupnp": "bloat, which can't work anyway in wasm",
+			"testing":                    "do not use testing package in production code",
+			"runtime/pprof":              "bloat",
+			"golang.org/x/net/http2/h2c": "bloat",
+			"net/http/pprof":             "bloat",
+			"golang.org/x/net/proxy":     "bloat",
+			"github.com/huin/goupnp":     "bloat, which can't work anyway in wasm",
 		},
 	}.Check(t)
 }