From e34f77ba0e4a6220c96010eaedc685b322323e14 Mon Sep 17 00:00:00 2001 From: Caleb Callaway <enlightened.despot@gmail.com> Date: Sat, 18 Oct 2014 10:20:57 -0700 Subject: [PATCH] Enable portmapping for individual UPnP services --- internal/upnp/upnp.go | 73 +++++++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 24 deletions(-) diff --git a/internal/upnp/upnp.go b/internal/upnp/upnp.go index a6780bce2..5d2d529c1 100644 --- a/internal/upnp/upnp.go +++ b/internal/upnp/upnp.go @@ -472,22 +472,12 @@ func soapRequest(url, device, function, message string) ([]byte, error) { return resp, nil } -// Add a port mapping to the specified InternetGatewayDevice. +// Add a port mapping to all relevant services on the specified InternetGatewayDevice. +// Port mapping will fail and return an error if action is fails for _any_ of the relevant services. +// For this reason, it is generally better to configure port mapping for each individual service instead. func (n *IGD) AddPortMapping(protocol Protocol, externalPort, internalPort int, description string, timeout int) error { for _, service := range n.services { - tpl := `<u:AddPortMapping xmlns:u="%s"> - <NewRemoteHost></NewRemoteHost> - <NewExternalPort>%d</NewExternalPort> - <NewProtocol>%s</NewProtocol> - <NewInternalPort>%d</NewInternalPort> - <NewInternalClient>%s</NewInternalClient> - <NewEnabled>1</NewEnabled> - <NewPortMappingDescription>%s</NewPortMappingDescription> - <NewLeaseDuration>%d</NewLeaseDuration> - </u:AddPortMapping>` - body := fmt.Sprintf(tpl, service.serviceURN, externalPort, protocol, internalPort, n.localIPAddress, description, timeout) - - _, err := soapRequest(service.serviceURL, service.serviceURN, "AddPortMapping", body) + err := service.AddPortMapping(n.localIPAddress, protocol, externalPort, internalPort, description, timeout) if err != nil { return err } @@ -495,17 +485,12 @@ func (n *IGD) AddPortMapping(protocol Protocol, externalPort, internalPort int, return nil } -// Delete a port mapping from the specified InternetGatewayDevice. -func (n *IGD) DeletePortMapping(protocol Protocol, externalPort int) (err error) { +// Delete a port mapping from all relevant services on the specified InternetGatewayDevice. +// Port mapping will fail and return an error if action is fails for _any_ of the relevant services. +// For this reason, it is generally better to configure port mapping for each individual service instead. +func (n *IGD) DeletePortMapping(protocol Protocol, externalPort int) error { for _, service := range n.services { - tpl := `<u:DeletePortMapping xmlns:u="%s"> - <NewRemoteHost></NewRemoteHost> - <NewExternalPort>%d</NewExternalPort> - <NewProtocol>%s</NewProtocol> - </u:DeletePortMapping>` - body := fmt.Sprintf(tpl, service.serviceURN, externalPort, protocol) - - _, err := soapRequest(service.serviceURL, service.serviceURN, "DeletePortMapping", body) + err := service.DeletePortMapping(protocol, externalPort) if err != nil { return err } @@ -547,6 +532,46 @@ type getExternalIPAddressResponse struct { NewExternalIPAddress string `xml:"NewExternalIPAddress"` } +// Add a port mapping to the specified IGD service. +func (s *IGDService) AddPortMapping(localIPAddress string, protocol Protocol, externalPort, internalPort int, description string, timeout int) error { + tpl := `<u:AddPortMapping xmlns:u="%s"> + <NewRemoteHost></NewRemoteHost> + <NewExternalPort>%d</NewExternalPort> + <NewProtocol>%s</NewProtocol> + <NewInternalPort>%d</NewInternalPort> + <NewInternalClient>%s</NewInternalClient> + <NewEnabled>1</NewEnabled> + <NewPortMappingDescription>%s</NewPortMappingDescription> + <NewLeaseDuration>%d</NewLeaseDuration> + </u:AddPortMapping>` + body := fmt.Sprintf(tpl, s.serviceURN, externalPort, protocol, internalPort, localIPAddress, description, timeout) + + _, err := soapRequest(s.serviceURL, s.serviceURN, "AddPortMapping", body) + if err != nil { + return err + } + + return nil +} + +// Delete a port mapping from the specified IGD service. +func (s *IGDService) DeletePortMapping(protocol Protocol, externalPort int) error { + tpl := `<u:DeletePortMapping xmlns:u="%s"> + <NewRemoteHost></NewRemoteHost> + <NewExternalPort>%d</NewExternalPort> + <NewProtocol>%s</NewProtocol> + </u:DeletePortMapping>` + body := fmt.Sprintf(tpl, s.serviceURN, externalPort, protocol) + + _, err := soapRequest(s.serviceURL, s.serviceURN, "DeletePortMapping", body) + + if err != nil { + return err + } + + return nil +} + // Query the IGD service for its external IP address. // Returns nil if the external IP address is invalid or undefined, along with any relevant errors func (s *IGDService) GetExternalIPAddress() (net.IP, error) { -- GitLab