From 86667cb88e2a5afab5f8959ed41a7c5d9e324f92 Mon Sep 17 00:00:00 2001 From: John Beisley Date: Sun, 21 Feb 2021 12:34:13 +0000 Subject: [PATCH] Add a guide for using internet gateway clients. This attempts to help with #36. --- GUIDE.md | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 34 +++++++------- 2 files changed, 149 insertions(+), 18 deletions(-) create mode 100644 GUIDE.md diff --git a/GUIDE.md b/GUIDE.md new file mode 100644 index 0000000..040c9e2 --- /dev/null +++ b/GUIDE.md @@ -0,0 +1,133 @@ +# 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) + + GetExternalIPAddress() ( + NewExternalIPAddress string, + err error, + ) +} + +func PickRouterClient(ctx context.Context) (RouterClient, error) { + tasks, _ := errgroup.WithContext(ctx) + // Request each type of client in parallel, and return what is found. + 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 + } + + // 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. + 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 { + 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( + "", + // External port number to expose to Internet: + 1234, + // Forward TCP (this could be "UDP" if we wanted that instead). + "TCP", + // Internal port number on the LAN to forward to. + // Some routers might not support this being different to the external + // port number. + 1234, + // Internal address on the LAN we want to forward to. + "192.168.1.6", + // Enabled: + true, + // Informational description for the client requesting the port forwarding. + "MyProgramName", + // 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. + 3600, + ) +} +``` + +The code above is of course just a relatively trivial example that you can +tailor to your own use case. diff --git a/README.md b/README.md index 7c63903..facdd2b 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,38 @@ goupnp is a UPnP client library for Go -Installation ------------- +## Installation Run `go get -u github.com/huin/goupnp`. -Documentation -------------- +## Documentation + +See [GUIDE.md](GUIDE.md) for a quick start on the most common use case for this +library. Supported DCPs (you probably want to start with one of these): -* [![GoDoc](https://godoc.org/github.com/huin/goupnp?status.svg) av1](https://godoc.org/github.com/huin/goupnp/dcps/av1) - Client for UPnP Device Control Protocol MediaServer v1 and MediaRenderer v1. -* [![GoDoc](https://godoc.org/github.com/huin/goupnp?status.svg) internetgateway1](https://godoc.org/github.com/huin/goupnp/dcps/internetgateway1) - Client for UPnP Device Control Protocol Internet Gateway Device v1. -* [![GoDoc](https://godoc.org/github.com/huin/goupnp?status.svg) internetgateway2](https://godoc.org/github.com/huin/goupnp/dcps/internetgateway2) - Client for UPnP Device Control Protocol Internet Gateway Device v2. +- [![GoDoc](https://godoc.org/github.com/huin/goupnp?status.svg) av1](https://godoc.org/github.com/huin/goupnp/dcps/av1) - Client for UPnP Device Control Protocol MediaServer v1 and MediaRenderer v1. +- [![GoDoc](https://godoc.org/github.com/huin/goupnp?status.svg) internetgateway1](https://godoc.org/github.com/huin/goupnp/dcps/internetgateway1) - Client for UPnP Device Control Protocol Internet Gateway Device v1. +- [![GoDoc](https://godoc.org/github.com/huin/goupnp?status.svg) internetgateway2](https://godoc.org/github.com/huin/goupnp/dcps/internetgateway2) - Client for UPnP Device Control Protocol Internet Gateway Device v2. Core components: -* [![GoDoc](https://godoc.org/github.com/huin/goupnp?status.svg) (goupnp)](https://godoc.org/github.com/huin/goupnp) core library - contains datastructures and utilities typically used by the implemented DCPs. -* [![GoDoc](https://godoc.org/github.com/huin/goupnp?status.svg) httpu](https://godoc.org/github.com/huin/goupnp/httpu) HTTPU implementation, underlies SSDP. -* [![GoDoc](https://godoc.org/github.com/huin/goupnp?status.svg) ssdp](https://godoc.org/github.com/huin/goupnp/ssdp) SSDP client implementation (simple service discovery protocol) - used to discover UPnP services on a network. -* [![GoDoc](https://godoc.org/github.com/huin/goupnp?status.svg) soap](https://godoc.org/github.com/huin/goupnp/soap) SOAP client implementation (simple object access protocol) - used to communicate with discovered services. +- [![GoDoc](https://godoc.org/github.com/huin/goupnp?status.svg) (goupnp)](https://godoc.org/github.com/huin/goupnp) core library - contains datastructures and utilities typically used by the implemented DCPs. +- [![GoDoc](https://godoc.org/github.com/huin/goupnp?status.svg) httpu](https://godoc.org/github.com/huin/goupnp/httpu) HTTPU implementation, underlies SSDP. +- [![GoDoc](https://godoc.org/github.com/huin/goupnp?status.svg) ssdp](https://godoc.org/github.com/huin/goupnp/ssdp) SSDP client implementation (simple service discovery protocol) - used to discover UPnP services on a network. +- [![GoDoc](https://godoc.org/github.com/huin/goupnp?status.svg) soap](https://godoc.org/github.com/huin/goupnp/soap) SOAP client implementation (simple object access protocol) - used to communicate with discovered services. - -Regenerating dcps generated source code: ----------------------------------------- +## Regenerating dcps generated source code: 1. Build code generator: - `go get -u github.com/huin/goupnp/cmd/goupnpdcpgen` + `go get -u github.com/huin/goupnp/cmd/goupnpdcpgen` 2. Regenerate the code: - `go generate ./...` + `go generate ./...` -Supporting additional UPnP devices and services: ------------------------------------------------- +## Supporting additional UPnP devices and services: Supporting additional services is, in the trivial case, simply a matter of adding the service to the `dcpMetadata` whitelist in `cmd/goupnpdcpgen/metadata.go`,