good morning!!!!

Skip to content
Snippets Groups Projects
Commit 663d4e0a authored by Felix Lange's avatar Felix Lange
Browse files

p2p/nat: add test for UPnP auto discovery via SSDP

The test listens for multicast UDP packets on the default interface
because I couldn't get it to work reliably on loopback without massive
changes to goupnp. This means that the test might fail when there is a
UPnP-enabled router attached on that interface. I checked that locally
by looping the test and it passes reliably because the local SSDP server
always responds faster.
parent 983f5a71
No related branches found
No related tags found
No related merge requests found
package nat
import (
"fmt"
"io"
"net"
"net/http"
"strings"
"testing"
"github.com/huin/goupnp/httpu"
)
func TestUPNP_DDWRT(t *testing.T) {
dev := &fakeIGD{
t: t,
ssdpResp: "HTTP/1.1 200 OK\r\n" +
"Cache-Control: max-age=300\r\n" +
"Date: Sun, 10 May 2015 10:05:33 GMT\r\n" +
"Ext: \r\n" +
"Location: http://{{listenAddr}}/InternetGatewayDevice.xml\r\n" +
"Server: POSIX UPnP/1.0 DD-WRT Linux/V24\r\n" +
"ST: urn:schemas-upnp-org:device:WANConnectionDevice:1\r\n" +
"USN: uuid:CB2471CC-CF2E-9795-8D9C-E87B34C16800::urn:schemas-upnp-org:device:WANConnectionDevice:1\r\n" +
"\r\n",
httpResps: map[string]string{
"GET /InternetGatewayDevice.xml": `
<?xml version="1.0"?>
<root xmlns="urn:schemas-upnp-org:device-1-0">
<specVersion>
<major>1</major>
<minor>0</minor>
</specVersion>
<device>
<deviceType>urn:schemas-upnp-org:device:InternetGatewayDevice:1</deviceType>
<manufacturer>DD-WRT</manufacturer>
<manufacturerURL>http://www.dd-wrt.com</manufacturerURL>
<modelDescription>Gateway</modelDescription>
<friendlyName>Asus RT-N16:DD-WRT</friendlyName>
<modelName>Asus RT-N16</modelName>
<modelNumber>V24</modelNumber>
<serialNumber>0000001</serialNumber>
<modelURL>http://www.dd-wrt.com</modelURL>
<UDN>uuid:A13AB4C3-3A14-E386-DE6A-EFEA923A06FE</UDN>
<serviceList>
<service>
<serviceType>urn:schemas-upnp-org:service:Layer3Forwarding:1</serviceType>
<serviceId>urn:upnp-org:serviceId:L3Forwarding1</serviceId>
<SCPDURL>/x_layer3forwarding.xml</SCPDURL>
<controlURL>/control?Layer3Forwarding</controlURL>
<eventSubURL>/event?Layer3Forwarding</eventSubURL>
</service>
</serviceList>
<deviceList>
<device>
<deviceType>urn:schemas-upnp-org:device:WANDevice:1</deviceType>
<friendlyName>WANDevice</friendlyName>
<manufacturer>DD-WRT</manufacturer>
<manufacturerURL>http://www.dd-wrt.com</manufacturerURL>
<modelDescription>Gateway</modelDescription>
<modelName>router</modelName>
<modelURL>http://www.dd-wrt.com</modelURL>
<UDN>uuid:48FD569B-F9A9-96AE-4EE6-EB403D3DB91A</UDN>
<serviceList>
<service>
<serviceType>urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1</serviceType>
<serviceId>urn:upnp-org:serviceId:WANCommonIFC1</serviceId>
<SCPDURL>/x_wancommoninterfaceconfig.xml</SCPDURL>
<controlURL>/control?WANCommonInterfaceConfig</controlURL>
<eventSubURL>/event?WANCommonInterfaceConfig</eventSubURL>
</service>
</serviceList>
<deviceList>
<device>
<deviceType>urn:schemas-upnp-org:device:WANConnectionDevice:1</deviceType>
<friendlyName>WAN Connection Device</friendlyName>
<manufacturer>DD-WRT</manufacturer>
<manufacturerURL>http://www.dd-wrt.com</manufacturerURL>
<modelDescription>Gateway</modelDescription>
<modelName>router</modelName>
<modelURL>http://www.dd-wrt.com</modelURL>
<UDN>uuid:CB2471CC-CF2E-9795-8D9C-E87B34C16800</UDN>
<serviceList>
<service>
<serviceType>urn:schemas-upnp-org:service:WANIPConnection:1</serviceType>
<serviceId>urn:upnp-org:serviceId:WANIPConn1</serviceId>
<SCPDURL>/x_wanipconnection.xml</SCPDURL>
<controlURL>/control?WANIPConnection</controlURL>
<eventSubURL>/event?WANIPConnection</eventSubURL>
</service>
</serviceList>
</device>
</deviceList>
</device>
<device>
<deviceType>urn:schemas-upnp-org:device:LANDevice:1</deviceType>
<friendlyName>LANDevice</friendlyName>
<manufacturer>DD-WRT</manufacturer>
<manufacturerURL>http://www.dd-wrt.com</manufacturerURL>
<modelDescription>Gateway</modelDescription>
<modelName>router</modelName>
<modelURL>http://www.dd-wrt.com</modelURL>
<UDN>uuid:04021998-3B35-2BDB-7B3C-99DA4435DA09</UDN>
<serviceList>
<service>
<serviceType>urn:schemas-upnp-org:service:LANHostConfigManagement:1</serviceType>
<serviceId>urn:upnp-org:serviceId:LANHostCfg1</serviceId>
<SCPDURL>/x_lanhostconfigmanagement.xml</SCPDURL>
<controlURL>/control?LANHostConfigManagement</controlURL>
<eventSubURL>/event?LANHostConfigManagement</eventSubURL>
</service>
</serviceList>
</device>
</deviceList>
<presentationURL>http://{{listenAddr}}</presentationURL>
</device>
</root>
`,
// The response to our GetNATRSIPStatus call. This
// particular implementation has a bug where the elements
// inside u:GetNATRSIPStatusResponse are not properly
// namespaced.
"POST /control?WANIPConnection": `
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Body>
<u:GetNATRSIPStatusResponse xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1">
<NewRSIPAvailable>0</NewRSIPAvailable>
<NewNATEnabled>1</NewNATEnabled>
</u:GetNATRSIPStatusResponse>
</s:Body>
</s:Envelope>
`,
},
}
if err := dev.listen(); err != nil {
t.Skipf("cannot listen: %v", err)
}
dev.serve()
defer dev.close()
// Attempt to discover the fake device.
discovered := discoverUPnP()
if discovered == nil {
t.Fatalf("not discovered")
}
upnp, _ := discovered.(*upnp)
if upnp.service != "IGDv1-IP1" {
t.Errorf("upnp.service mismatch: got %q, want %q", upnp.service, "IGDv1-IP1")
}
wantURL := "http://" + dev.listener.Addr().String() + "/InternetGatewayDevice.xml"
if upnp.dev.URLBaseStr != wantURL {
t.Errorf("upnp.dev.URLBaseStr mismatch: got %q, want %q", upnp.dev.URLBaseStr, wantURL)
}
}
// fakeIGD presents itself as a discoverable UPnP device which sends
// canned responses to HTTPU and HTTP requests.
type fakeIGD struct {
t *testing.T // for logging
listener net.Listener
mcastListener *net.UDPConn
// This should be a complete HTTP response (including headers).
// It is sent as the response to any sspd packet. Any occurrence
// of "{{listenAddr}}" is replaced with the actual TCP listen
// address of the HTTP server.
ssdpResp string
// This one should contain XML payloads for all requests
// performed. The keys contain method and path, e.g. "GET /foo/bar".
// As with ssdpResp, "{{listenAddr}}" is replaced with the TCP
// listen address.
httpResps map[string]string
}
// httpu.Handler
func (dev *fakeIGD) ServeMessage(r *http.Request) {
dev.t.Logf(`HTTPU request %s %s`, r.Method, r.RequestURI)
conn, err := net.Dial("udp4", r.RemoteAddr)
if err != nil {
fmt.Printf("reply Dial error: %v", err)
return
}
defer conn.Close()
io.WriteString(conn, dev.replaceListenAddr(dev.ssdpResp))
}
// http.Handler
func (dev *fakeIGD) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if resp, ok := dev.httpResps[r.Method+" "+r.RequestURI]; ok {
dev.t.Logf(`HTTP request "%s %s" --> %d`, r.Method, r.RequestURI, 200)
io.WriteString(w, dev.replaceListenAddr(resp))
} else {
dev.t.Logf(`HTTP request "%s %s" --> %d`, r.Method, r.RequestURI, 404)
w.WriteHeader(http.StatusNotFound)
}
}
func (dev *fakeIGD) replaceListenAddr(resp string) string {
return strings.Replace(resp, "{{listenAddr}}", dev.listener.Addr().String(), -1)
}
func (dev *fakeIGD) listen() (err error) {
if dev.listener, err = net.Listen("tcp", "127.0.0.1:0"); err != nil {
return err
}
laddr := &net.UDPAddr{IP: net.ParseIP("239.255.255.250"), Port: 1900}
if dev.mcastListener, err = net.ListenMulticastUDP("udp", nil, laddr); err != nil {
dev.listener.Close()
return err
}
return nil
}
func (dev *fakeIGD) serve() {
go httpu.Serve(dev.mcastListener, dev)
go http.Serve(dev.listener, dev)
}
func (dev *fakeIGD) close() {
dev.mcastListener.Close()
dev.listener.Close()
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment