PowerShell und SOAP

Viele Dienste sind als "WebService" erreichbar. Das ist fast wie ein normaler HTTP-Abruf, nur dass die Daten nicht per GET sondern per POST gesendet werden und in der Anfrage auch eine XML-Struktur mit der genaueren Anforderung übermittelt wird. Auch die Exchange Webservices sind eine vergleichbare Schnittstelle, wobei es dort nun die ManagedAPI mit entsprechenden DLLs mit Modellen gibt.

Für die Demonstration mit PowerShell habe ich mit aber eine "einfachere" Plattform gesucht, anhand der die Funktion SOAP gezeigt werden kann. Ich nutze einfach meine Fritz!Box.

Einfacher SOAP-Abfrage mit WireShark

Die Fritz!Box wird sicher in vielen Heimanschlüssen stehen und wer mit PowerShell nicht viel am Hut hat aber dennoch seine Internetauslastung untersuchen will, kommt mit der kostenfreien Version von PRTG schneller zum Ziel. Der folgende Trace ist mit WireShark von einer PRTG-Probe ausgelesen worden.

POST /upnp/control/WANCommonIFC1 HTTP/1.1
Content-Type: text/xml; charset="utf-8"
HOST: 192.168.178.1:49000
Content-Length: 289
SOAPACTION: "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1#GetAddonInfos"

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

Zurück kommt dann auch eine HTML-Antwort, die aber als Nutzlast eine XML-Struktur enthält:

HTTP/1.1 200 OK
DATE: Tue, 24 Dec 2013 00:00:34 GMT
SERVER: FRITZ!Box Fon WLAN 7390 uPnP/1.0 AVM FRITZ!Box Fon WLAN 7390 84.06.01
CONNECTION: keep-alive
CONTENT-LENGTH: 983
CONTENT-TYPE: text/xml; charset="utf-8"
EXT:

<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Body>
<u:GetAddonInfosResponse xmlns:u="urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1">
<NewByteSendRate>62</NewByteSendRate>
<NewByteReceiveRate>115</NewByteReceiveRate>
<NewPacketSendRate>1</NewPacketSendRate>
<NewPacketReceiveRate>1</NewPacketReceiveRate>
<NewTotalBytesSent>228239281</NewTotalBytesSent>
<NewTotalBytesReceived>1588087349</NewTotalBytesReceived>
<NewAutoDisconnectTime>0</NewAutoDisconnectTime>
<NewIdleDisconnectTime>0</NewIdleDisconnectTime>
<NewDNSServer1>217.237.149.142</NewDNSServer1>
<NewDNSServer2>217.237.149.142</NewDNSServer2>
<NewVoipDNSServer1>0.0.0.0</NewVoipDNSServer1>
<NewVoipDNSServer2>0.0.0.0</NewVoipDNSServer2>
<NewUpnpControlEnabled>0</NewUpnpControlEnabled>
<NewRoutedBridgedModeBoth>0</NewRoutedBridgedModeBoth>
</u:GetAddonInfosResponse>
</s:Body>
</s:Envelope>

Sie sehen, dass auf die kleine Anfrage sehr viele interessante Daten kommen.

Weitere URLS

Welche Funktionen ein WebService anbietet, liefert das Manifest, die auch als "WSDL" (WebServiceDescriptionLanguage) -Dateien bezeichnet werden. Das sind XML-Beschreibungen, Sie sie einfach per HTTP-GET erreichen können. Die Fritz!Box bietet hier einige an- Wer eine Fritz!Box sein eigen nennt, kann ja die folgenden URLs einfach mal ansurfen:

Sie verraten welcher Service z.B.: welche Funktionen mit welchen Parametern anbietet. Interessant ist hier

Sie verweist auf die URL http://fritz.box:49000/igdicfgSCPD.xml, die unter anderem dann auch die Funktion "GetAddonInfos" beschreibt:

Wer ein Visual Studio oder eine andere IDE verwendet, kann oft direkt diese URLs angeben und die EntwicklungsUmgebung liefert dann entsprechend gleich die SyntaxUnterstützung mit. Das kann PowerShell so nicht.

SOAP mit PowerShell

Hier müssen wir also schon selbst die XML-Anfrage zusammen bauen und absenden.

In der XML-Anfrage müssen "Doppelpunkte" enthalten sein, die aber in der PowerShell mit einem "konvertiert werden. Fehler: "Das ':'-Zeichen, hexadezimaler Wert 0x3A, darf nicht in einem Namen enthalten sein." abgelehnt werden.

Das folgende funktioniert also nicht.

[xml]$SOAPRequest='<?xml version="1.0" encoding="utf-8"?>
   <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
      <s:Body>
         <u:urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1 xmlns:u=GetAddonInfos />
   </s:Body>
</s:Envelope>'

Der XML-Knoten mit "<u:urn:schemas.." hat für den PowerShell XML-Validator einfach zu viele Doppelpunkte. Da die XML so aber korrekt ist, habe ich sie dann als String übergeben:

param (
	[String] $URL = "http://192.168.178.1:49000/upnp/control/WANCommonIFC1"
)


write-host "SOAP Request vorbereiten an Ziel: $URL"
$soapWebRequest = [System.Net.WebRequest]::Create($URL) 
$soapWebRequest.Headers.Add("SOAPAction",'"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1#GetAddonInfos"')
$soapWebRequest.ContentType = 'text/xml;charset="utf-8"'
$soapWebRequest.Accept      = "text/xml" 
$soapWebRequest.Method      = "POST" 

write-host "SOAP Request XML in den RequestStream schreiben"
$requestStream = $soapWebRequest.GetRequestStream() 
[string]$SOAPRequest='
   <?xml version="1.0" encoding="utf-8"?>
   <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
      <soap:Body>
         <u:urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1 xmlns:u=GetAddonInfos />
      </soap:Body>
   </soap:Envelope>'
$SOAPRequestbytearray = ([system.Text.Encoding]::ASCII).GetBytes($SOAPRequest)
$requestStream.write($SOAPRequestbytearray,0,$SOAPRequestbytearray.count)
$requestStream.Close() 

write-host "Sende Request"
$resp = $soapWebRequest.GetResponse() 
write-host "Verarbeite Ergebnisse"
$responseStream = $resp.GetResponseStream() 
$soapReader = [System.IO.StreamReader]($responseStream) 
$ReturnXml = [Xml] $soapReader.ReadToEnd() 
$responseStream.Close() 

write-host "Ergebnisse"
$ReturnXml.Envelope.Body.GetAddonInfosResponse
write-host "Ende"

Die Ausgabe ist dann natürlich erst mal unspektakulär

PS C:\group\Technik\Skripte\PowerShell-samples> .\pssoapfritz.ps1
SOAP Request vorbereiten an Ziel: http://192.168.178.1:49000/upnp/control/WANCom
monIFC1
SOAP Request XML in den RequestStream schreiben
Sende Request
Verarbeite Ergebnisse
Ergebnisse u                        : urn:schemas-upnp-org:service:WANCommonInterfaceConfi
                           g:1
NewByteSendRate          : 10452
NewByteReceiveRate       : 1453440
NewPacketSendRate        : 201
NewPacketReceiveRate     : 960
NewTotalBytesSent        : 680528281
NewTotalBytesReceived    : 1258271956
NewAutoDisconnectTime    : 0
NewIdleDisconnectTime    : 1
NewDNSServer1            : 217.237.149.142
NewDNSServer2            : 217.237.149.142
NewVoipDNSServer1        : 0.0.0.0
NewVoipDNSServer2        : 0.0.0.0
NewUpnpControlEnabled    : 0
NewRoutedBridgedModeBoth : 0

Ende

Damit hätte ich also auch per PowerShell meinen ersten SOAP-Request erfolgreich gegen die Fritz-Box getestet. Der Sinn dahinter ist recht einfach: Eine Fritz!Box habe ich, sie erwartet keine Authentifizierung und die Daten sind durchaus interessant. Solche Requests kann ich natürlich nun erweitern um z.B. Exchange WebServices oder die Lync UCWA anzusprechen.

Weitere Links