2021-02-21 12:34:13 +00:00
|
|
|
# Guide
|
|
|
|
|
|
|
|
This is a quick guide with example code for common use cases that might be
|
|
|
|
helpful for people wanting a quick guide to common ways that people would
|
|
|
|
want to use this library.
|
|
|
|
|
|
|
|
## Internet Gateways
|
|
|
|
|
|
|
|
`goupnp/dcps/internetgateway1` and `goupnp/dcps/internetgateway2` implement
|
|
|
|
different version standards that allow clients to interact with devices like
|
|
|
|
home consumer routers, but you can probably get by with just
|
|
|
|
`internetgateway2`. Some very common use cases to talk to such devices are:
|
|
|
|
|
|
|
|
- Requesting the external Internet-facing IP address.
|
|
|
|
- Requesting a port be forwarded from the external (Internet-facing) interface
|
|
|
|
to a port on the LAN.
|
|
|
|
|
|
|
|
Different routers implement different standards, so you may have to request
|
|
|
|
multiple clients to find the one that your router needs. The most useful ones
|
|
|
|
for the purpose above can be requested with the following functions:
|
|
|
|
|
|
|
|
- `internetgateway2.NewWANIPConnection1Clients()`
|
|
|
|
- `internetgateway2.NewWANIPConnection2Clients()`
|
|
|
|
- `internetgateway2.NewWANPPPConnection1Clients()`
|
|
|
|
|
|
|
|
Fortunately, each of the clients returned by these functions provide the same
|
|
|
|
method signatures for the purposes listed above. So you could request multiple
|
|
|
|
clients, and whichever one you find, and return it from a function in a variable
|
|
|
|
of the common interface, e.g:
|
|
|
|
|
|
|
|
```go
|
|
|
|
type RouterClient interface {
|
|
|
|
AddPortMapping(
|
|
|
|
NewRemoteHost string,
|
|
|
|
NewExternalPort uint16,
|
|
|
|
NewProtocol string,
|
|
|
|
NewInternalPort uint16,
|
|
|
|
NewInternalClient string,
|
|
|
|
NewEnabled bool,
|
|
|
|
NewPortMappingDescription string,
|
|
|
|
NewLeaseDuration uint32,
|
|
|
|
) (err error)
|
|
|
|
|
2021-02-21 12:36:27 +00:00
|
|
|
GetExternalIPAddress() (
|
|
|
|
NewExternalIPAddress string,
|
|
|
|
err error,
|
|
|
|
)
|
2021-02-21 12:34:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func PickRouterClient(ctx context.Context) (RouterClient, error) {
|
|
|
|
tasks, _ := errgroup.WithContext(ctx)
|
2021-02-21 12:36:27 +00:00
|
|
|
// Request each type of client in parallel, and return what is found.
|
2021-02-21 12:34:13 +00:00
|
|
|
var ip1Clients []*internetgateway2.WANIPConnection1
|
|
|
|
tasks.Go(func() error {
|
|
|
|
var err error
|
|
|
|
ip1Clients, _, err = internetgateway2.NewWANIPConnection1Clients()
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
var ip2Clients []*internetgateway2.WANIPConnection2
|
|
|
|
tasks.Go(func() error {
|
|
|
|
var err error
|
|
|
|
ip2Clients, _, err = internetgateway2.NewWANIPConnection2Clients()
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
var ppp1Clients []*internetgateway2.WANPPPConnection1
|
|
|
|
tasks.Go(func() error {
|
|
|
|
var err error
|
|
|
|
ppp1Clients, _, err = internetgateway2.NewWANPPPConnection1Clients()
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
|
|
|
|
if err := tasks.Wait(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-02-21 12:36:27 +00:00
|
|
|
// Trivial handling for where we find exactly one device to talk to, you
|
|
|
|
// might want to provide more flexible handling than this if multiple
|
|
|
|
// devices are found.
|
2021-02-21 12:34:13 +00:00
|
|
|
switch {
|
|
|
|
case len(ip2Clients) == 1:
|
|
|
|
return ip2Clients[0], nil
|
|
|
|
case len(ip1Clients) == 1:
|
|
|
|
return ip1Clients[0], nil
|
|
|
|
case len(ppp1Clients) == 1:
|
|
|
|
return ppp1Clients[0], nil
|
|
|
|
default:
|
|
|
|
return nil, errors.New("multiple or no services found")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
You could then use this function to create a client, and both request the
|
|
|
|
external IP address and forward it to a port on your local network, e.g:
|
|
|
|
|
|
|
|
```go
|
|
|
|
func GetIPAndForwardPort(ctx context.Context) error {
|
2021-02-21 12:36:27 +00:00
|
|
|
client, err := PickRouterClient(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
externalIP, err := client.GetExternalIPAddress()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
fmt.Println("Our external IP address is: ", externalIP)
|
|
|
|
|
|
|
|
return client.AddPortMapping(
|
2021-02-21 12:34:13 +00:00
|
|
|
"",
|
2021-02-21 12:36:27 +00:00
|
|
|
// External port number to expose to Internet:
|
2021-02-21 12:34:13 +00:00
|
|
|
1234,
|
2021-02-21 12:36:27 +00:00
|
|
|
// Forward TCP (this could be "UDP" if we wanted that instead).
|
2021-02-21 12:34:13 +00:00
|
|
|
"TCP",
|
2021-02-21 12:36:27 +00:00
|
|
|
// Internal port number on the LAN to forward to.
|
|
|
|
// Some routers might not support this being different to the external
|
|
|
|
// port number.
|
2021-02-21 12:34:13 +00:00
|
|
|
1234,
|
2021-02-21 12:36:27 +00:00
|
|
|
// Internal address on the LAN we want to forward to.
|
2021-02-21 12:34:13 +00:00
|
|
|
"192.168.1.6",
|
2021-02-21 12:36:27 +00:00
|
|
|
// Enabled:
|
2021-02-21 12:34:13 +00:00
|
|
|
true,
|
2021-02-21 12:36:27 +00:00
|
|
|
// Informational description for the client requesting the port forwarding.
|
2021-02-21 12:34:13 +00:00
|
|
|
"MyProgramName",
|
2021-02-21 12:36:27 +00:00
|
|
|
// How long should the port forward last for in seconds.
|
|
|
|
// If you want to keep it open for longer and potentially across router
|
|
|
|
// resets, you might want to periodically request before this elapses.
|
2021-02-21 12:34:13 +00:00
|
|
|
3600,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
The code above is of course just a relatively trivial example that you can
|
|
|
|
tailor to your own use case.
|