diff --git a/cmd/discoverigd/discoverigd.go b/cmd/discoverigd/discoverigd.go index 4bcd98c..6a69448 100644 --- a/cmd/discoverigd/discoverigd.go +++ b/cmd/discoverigd/discoverigd.go @@ -19,16 +19,7 @@ func (i indentLevel) String() string { func displayDevice(indent indentLevel, device *goupnp.Device) { fmt.Println(indent.String(), device) for _, srv := range device.Services { - fmt.Println((indent + 1).String(), srv, srv.SCPDURL.URL.String(), srv.ControlURL.URL.String()) - fmt.Println(goupnp.ServiceTypeWANPPPConnection, srv.ServiceType) - if srv.ServiceType == goupnp.ServiceTypeWANPPPConnection { - results, err := goupnp.PerformSoapAction(goupnp.ServiceTypeWANPPPConnection, "GetExternalIPAddress", &srv.ControlURL.URL, nil) - if err != nil { - fmt.Println("Error calling GetExternalIPAddress:", err) - } else { - fmt.Println(results) - } - } + fmt.Printf("%sService %s\n", indent+1, srv.ServiceType) } for i := range device.Devices { displayDevice(indent+1, &device.Devices[i]) @@ -43,9 +34,26 @@ func main() { for _, maybeRootDevice := range results { if maybeRootDevice.Err != nil { fmt.Println(maybeRootDevice.Err) - } else { - displayDevice(0, &maybeRootDevice.Root.Device) + continue } + + device := &maybeRootDevice.Root.Device + + displayDevice(0, device) + wanPPPSrvs := device.FindService(goupnp.ServiceTypeWANPPPConnection) + if len(wanPPPSrvs) < 1 { + fmt.Printf("Could not find expected service on device %s\n", device.FriendlyName) + continue + } else if len(wanPPPSrvs) > 1 { + fmt.Printf("Got more than one expected service on device %s\n", device.FriendlyName) + } + srv := wanPPPSrvs[0] + results, err := goupnp.PerformSoapAction(goupnp.ServiceTypeWANPPPConnection, "GetExternalIPAddress", &srv.ControlURL.URL, nil) + if err != nil { + fmt.Printf("Failed to GetExternalIPAddress from %s: %v\n", device.FriendlyName, err) + continue + } + fmt.Printf("Got GetExternalIPAddress result from %s: %v\n", device.FriendlyName, results) } } } diff --git a/device.go b/device.go index 13a4951..272accf 100644 --- a/device.go +++ b/device.go @@ -17,7 +17,7 @@ const ( // description" in // http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf type RootDevice struct { - Name xml.Name `xml:"root` + XMLName xml.Name `xml:"root"` SpecVersion SpecVersion `xml:"specVersion"` URLBase url.URL `xml:"-"` URLBaseStr string `xml:"URLBase"` @@ -55,6 +55,32 @@ type Device struct { PresentationURL URLField `xml:"presentationURL"` } +func (device *Device) VisitDevices(visitor func(*Device)) { + visitor(device) + for i := range device.Devices { + device.Devices[i].VisitDevices(visitor) + } +} + +func (device *Device) VisitServices(visitor func(*Service)) { + device.VisitDevices(func(d *Device) { + for i := range device.Services { + visitor(&d.Services[i]) + } + }) +} + +func (device *Device) FindService(serviceType string) []*Service { + var services []*Service + device.VisitServices(func(s *Service) { + fmt.Println(s.ServiceType) + if s.ServiceType == serviceType { + services = append(services, s) + } + }) + return services +} + func (device *Device) SetURLBase(urlBase *url.URL) { device.ManufacturerURL.SetURLBase(urlBase) device.ModelURL.SetURLBase(urlBase) diff --git a/scpd.go b/scpd.go index 00a9de4..df291f8 100644 --- a/scpd.go +++ b/scpd.go @@ -12,7 +12,7 @@ const ( // description" in // http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf type SCPD struct { - Name xml.Name `xml:"scpd"` + XMLName xml.Name `xml:"scpd"` ConfigId string `xml:"configId,attr"` SpecVersion SpecVersion `xml:"specVersion"` Actions []Action `xml:"actionList>action"` diff --git a/soap.go b/soap.go index 80d7963..668f51f 100644 --- a/soap.go +++ b/soap.go @@ -74,6 +74,10 @@ func PerformSoapAction(actionNamespace, actionName string, url *url.URL, argumen return nil, err } + if responseEnv.Body.Fault != nil { + return nil, responseEnv.Body.Fault + } + results := make([]NameValue, len(responseEnv.Body.Action.Arguments)) for i, soapArg := range responseEnv.Body.Action.Arguments { results[i] = NameValue{ @@ -92,9 +96,20 @@ type SoapEnvelope struct { } type SoapBody struct { + Fault *SoapFault `xml:"Fault"` Action SoapAction `xml:",any"` } +type SoapFault struct { + FaultCode string `xml:"faultcode"` + FaultString string `xml:"faultstring"` + Detail string `xml:"detail"` +} + +func (err *SoapFault) Error() string { + return fmt.Sprintf("SOAP fault: %s", err.FaultString) +} + type SoapAction struct { XMLName xml.Name Arguments []SoapArgument `xml:",any"`