mirror of
https://github.com/tailscale/tailscale.git
synced 2025-10-20 06:32:01 +00:00
Copy goupnp client into our repo
goupnp is an existing upnp client for go, which provides all the functionality we need, licensed under BSD-2-Clause, so we can copy it over and modify parts of it for our case. Specifically, we add contexts to all the methods so we can better handle timeouts, remove the dependency on large charsets, and (eventually) trim out extra components we don't need. Signed-off-by: julianknodt <julianknodt@gmail.com>
This commit is contained in:
@@ -12,11 +12,13 @@ import (
|
||||
"tailscale.com/net/netns"
|
||||
)
|
||||
|
||||
/*
|
||||
type ProbeResult struct {
|
||||
PCP bool
|
||||
PMP bool
|
||||
UPnP bool
|
||||
}
|
||||
*/
|
||||
|
||||
// Probe returns a summary of which port mapping services are
|
||||
// available on the network.
|
||||
|
@@ -5,56 +5,26 @@ package portmapper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/huin/goupnp/dcps/internetgateway2"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"inet.af/netaddr"
|
||||
"tailscale.com/net/upnp/dcps/internetgateway2"
|
||||
)
|
||||
|
||||
// probeUPnP returns true if there are any upnp clients, or false with an error if none can be
|
||||
// found.
|
||||
func probeUPnP(ctx context.Context) (bool, error) {
|
||||
wg := sync.WaitGroup{}
|
||||
any := make(chan bool)
|
||||
errChan := make(chan error)
|
||||
wg.Add(3)
|
||||
go func() {
|
||||
ip1Clients, _, err := internetgateway2.NewWANIPConnection1Clients()
|
||||
if len(ip1Clients) > 0 {
|
||||
any <- true
|
||||
}
|
||||
wg.Done()
|
||||
wg.Wait()
|
||||
errChan <- err
|
||||
}()
|
||||
go func() {
|
||||
ip2Clients, _, err := internetgateway2.NewWANIPConnection2Clients()
|
||||
if len(ip2Clients) > 0 {
|
||||
any <- true
|
||||
}
|
||||
wg.Done()
|
||||
wg.Wait()
|
||||
errChan <- err
|
||||
}()
|
||||
go func() {
|
||||
ppp1Clients, _, err := internetgateway2.NewWANPPPConnection1Clients()
|
||||
if len(ppp1Clients) > 0 {
|
||||
any <- true
|
||||
}
|
||||
wg.Done()
|
||||
wg.Wait()
|
||||
errChan <- err
|
||||
}()
|
||||
type upnpMapping struct {
|
||||
gw netaddr.IP
|
||||
external netaddr.IPPort
|
||||
internal netaddr.IPPort
|
||||
useUntil time.Time
|
||||
client upnpClient
|
||||
}
|
||||
|
||||
select {
|
||||
case <-any:
|
||||
return true, nil
|
||||
case err := <-errChan:
|
||||
// TODO probably want to take the non-nil of all the errors? Or something.
|
||||
return false, err
|
||||
case <-ctx.Done():
|
||||
return false, nil
|
||||
}
|
||||
func (u *upnpMapping) isCurrent() bool { return u.useUntil.After(time.Now()) }
|
||||
func (u *upnpMapping) validUntil() time.Time { return u.useUntil }
|
||||
func (u *upnpMapping) externalIPPort() netaddr.IPPort { return u.external }
|
||||
func (u *upnpMapping) release() {
|
||||
u.client.DeletePortMapping(context.Background(), "", u.external.Port(), "udp")
|
||||
}
|
||||
|
||||
type upnpClient interface {
|
||||
@@ -62,21 +32,61 @@ type upnpClient interface {
|
||||
// Implicitly assume that the calls for all these are uniform, which might be a dangerous
|
||||
// assumption.
|
||||
AddPortMapping(
|
||||
NewRemoteHost string,
|
||||
NewExternalPort uint16,
|
||||
NewProtocol string,
|
||||
NewInternalPort uint16,
|
||||
NewInternalClient string,
|
||||
NewEnabled bool,
|
||||
NewPortMappingDescription string,
|
||||
NewLeaseDuration uint32,
|
||||
ctx context.Context,
|
||||
newRemoteHost string,
|
||||
newExternalPort uint16,
|
||||
newProtocol string,
|
||||
newInternalPort uint16,
|
||||
newInternalClient string,
|
||||
newEnabled bool,
|
||||
newPortMappingDescription string,
|
||||
newLeaseDuration uint32,
|
||||
) (err error)
|
||||
|
||||
DeletePortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) error
|
||||
GetStatusInfo() (status string, lastErr string, uptime uint32, err error)
|
||||
DeletePortMapping(ctx context.Context, NewRemoteHost string, NewExternalPort uint16, NewProtocol string) error
|
||||
GetStatusInfo(ctx context.Context) (status string, lastErr string, uptime uint32, err error)
|
||||
|
||||
RequestTermination() error
|
||||
RequestConnection() error
|
||||
RequestTermination(ctx context.Context) error
|
||||
RequestConnection(ctx context.Context) error
|
||||
}
|
||||
|
||||
func AddAnyPortMapping(
|
||||
ctx context.Context,
|
||||
upnp upnpClient,
|
||||
newRemoteHost string,
|
||||
newExternalPort uint16,
|
||||
newProtocol string,
|
||||
newInternalPort uint16,
|
||||
newInternalClient string,
|
||||
newEnabled bool,
|
||||
newPortMappingDescription string,
|
||||
newLeaseDuration uint32,
|
||||
) (newPort uint16, err error) {
|
||||
if upnp, ok := upnp.(*internetgateway2.WANIPConnection2); ok {
|
||||
return upnp.AddAnyPortMapping(
|
||||
ctx,
|
||||
newRemoteHost,
|
||||
newExternalPort,
|
||||
newProtocol,
|
||||
newInternalPort,
|
||||
newInternalClient,
|
||||
newEnabled,
|
||||
newPortMappingDescription,
|
||||
newLeaseDuration,
|
||||
)
|
||||
}
|
||||
err = upnp.AddPortMapping(
|
||||
ctx,
|
||||
newRemoteHost,
|
||||
newExternalPort,
|
||||
newProtocol,
|
||||
newInternalPort,
|
||||
newInternalClient,
|
||||
newEnabled,
|
||||
newPortMappingDescription,
|
||||
newLeaseDuration,
|
||||
)
|
||||
return newInternalPort, err
|
||||
}
|
||||
|
||||
// getUPnPClients gets a client for interfacing with UPnP, ignoring the underlying protocol for
|
||||
|
Reference in New Issue
Block a user