huin/goupnp

Service requests against Fritz!Box devices fail

wichert opened this issue · 7 comments

I am trying a very simple test against a Fritz!Box device:

package main

import (
    "fmt"
    "log"
    "github.com/huin/goupnp/dcps/internetgateway1"
)


func main() {
    clients, _, err := internetgateway1.NewWANIPConnection1Clients ()
    if err != nil {
        log.Fatal(err)
    }
    for _, client := range clients {
        status, _, _, err := client.GetStatusInfo()
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println(status)
    }
}

but this always fails:

2014/06/05 11:30:25 goupnp: SOAP request got HTTP 500 Internal Server Error

I did a bit of on-wire sniffing to compare what goupnp does versus what miniupnpc does. This is a working request from miniupunc:

POST /igdupnp/control/WANIPConn1 HTTP/1.1
Host: 192.168.178.1:49000
User-Agent: Darwin/13.0.0, UPnP/1.0, MiniUPnPc/1.9
Content-Length: 271
Content-Type: text/xml
SOAPAction: "urn:schemas-upnp-org:service:WANIPConnection:1#GetStatusInfo"
Connection: Close
Cache-Control: no-cache
Pragma: no-cache

<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:GetStatusInfo xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1"></u:GetStatusInfo></s:Body></s:Envelope>

This is a failing request from goupnp:

POST /igdupnp/control/WANIPConn1 HTTP/1.1
Host: 192.168.178.1:49000
User-Agent: Go 1.1 package http
Content-Length: 154
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: urn:schemas-upnp-org:service:WANIPConnection:1#GetStatusInfo
Accept-Encoding: gzip

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body></s:Body></s:Envelope>

This shows several differences:

  1. miniupunc includes the <?xml version="1.0" encoding="utf-8"?> preamble.
  2. miniupunc includes an <u:GetStatusInfo xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1"> element in the SOAP body.

I suspect the latter part is the critical one.

I did a very quick hack by modifying encodeRequestAction:

func encodeRequestAction(inAction interface{}) ([]byte, error) {
    requestBuf := new(bytes.Buffer)
    requestBuf.WriteString(soapPrefix)
    requestBuf.WriteString("<u:GetStatusInfo xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\"></u:GetStatusInfo>")

That indeed fixed the 500 errors.

huin commented

You're quite right... I suspect this never worked, and I never tested things that actually required arguments to the action (ugh, lazy me - that's pretty bad). Thanks for reporting this.

I've pushed a fix. It's not pretty... but then, Go's support for controlling namespace prefixes is a pain, and a lot of UPnP servers seem to choke on most of the XML that Go's XML library emits (even though it's valid).

Let me know if that works for you!

huin commented

I realised shortly after writing a reply that while this code works for actions with no parameters, I'm not sure that I ever got around to testing actions with parameters. I'll need to do that... Probably setting up some basic unit tests around soap actions.

huin commented

I've added a test and confirmed a fix as working with parameters as well now. This should now be in a better state.

Thanks, this works correctly for me now.

huin commented

Excellent, thanks for the confirmation.