Jump to content
Sign in to follow this  
skyteddy

problem with UPnP/SSDP-discovery on Windows 7 with more then one network card

Recommended Posts

skyteddy

Hi all,

I need your help because I do not come on.

The purpose of my program is to collect all sent replies within 15 second of all devices in my network, after my SSDP-Discover via UPnP. Every 3 seconds, I will send again.

My program works on Windows XP, independent how many network cards are installed. In Windows 7 only, if only one network card is installed or active. This means, if all network cards are active, I get no UPnP-Responds. When I disable all network cards (vmware network adapter too!), except 1, my program works in Windows 7 too. the windows firewall is deactivated.

Now I'm not sure if I make a principal mistake or maybe it's a bug in AutoIt? to test several things, I wrote 2 functions:

The SSDPdiscover_V1 works as just described above. I use for UDPRecv the same port, which was used for sending.

In SSDPdiscover_V2 I've tried various UDPBinds to catch the incoming UPnP-packets, but nothing works :-( If you want to test this, you must adjust the call in main.

here is my program. Or you can use the attachment:

#region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Outfile=.\SSDPdiscover.exe
#AutoIt3Wrapper_Compression=4
#AutoIt3Wrapper_Res_Description=
#AutoIt3Wrapper_Res_Fileversion=0.0.0.1
#AutoIt3Wrapper_Res_LegalCopyright=(c) by Rainer Ullrich
#AutoIt3Wrapper_Res_Language=1031
://////=__=
://////=__=.=
#AutoIt3Wrapper_Run_After=del ".\SSDPdiscover*.au3.tbl"
#AutoIt3Wrapper_Run_After=del ".\SSDPdiscover*_Obfuscated.au3"
#AutoIt3Wrapper_Run_Obfuscator=y
#AutoIt3Wrapper_AU3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6
#Obfuscator_Parameters=/cs=1 /cn=1 /cf=1 /cv=1 /sf=1 /sv=1
#endregion ;**** Directives created by AutoIt3Wrapper_GUI ****
#include <Date.au3>
#include <Array.au3>
Opt("MustDeclareVars", 1)
; --- MainVar-Deklaration ---
#region MainVar-Deklaration
; Für SSDPdiscover
Global $MyCollectedResponses ; Array with the collected Responses
Global $MyCollectedIPs ; Array with the collected IPs
Global $MyTimeToSearch = 15 * 1000 ; time to search in ticks
Global $MySendInterval = 3 * 1000; each x ticks the ssdp-discover will be sent
Global $MyResultDatei = ".\ssdp-discover-result.txt"
Global $MyTemp, $MyError, $MyReturn
; MsgBox
Global Const $Mnormal = 0 + 262144
Global Const $Merror = 16 + 262144
Global Const $Mquestion = 32 + 262144
Global Const $Mattention = 48 + 262144
Global Const $Mresult = 64 + 262144
#endregion MainVar-Deklaration

; --- Main ---
#region Main
; -- SSDP Discxover via UPnP --
$MyReturn = SSDPdiscover_V1($MyCollectedResponses, $MyCollectedIPs, $MyTimeToSearch, $MySendInterval) ; change here ..._V1 or ..._V2   <-------------
$MyError = @error
If $MyError <> 0 Then
$MyTemp = "SSDPdiscover failed!" & @CRLF & @CRLF
Switch $MyError
  Case 0
   ; nix
  Case 1
   $MyTemp &= "UDPOpen-error"
  Case 2
   $MyTemp &= "UDPSend-error"
  Case 3
   $MyTemp &= "UDPRecv-error"
  Case 4
   $MyTemp &= "UDPBind-error"
EndSwitch
MsgBox($Merror, "error:", $MyTemp)
Exit
EndIf

; -- Result --
; delete file
If FileExists($MyResultDatei) Then
If Not FileDelete($MyResultDatei) Then
  MsgBox($Merror, "error:", 'the file "' & $MyResultDatei & '" could not be deletet!')
  Exit
EndIf
EndIf
; Responses
If IsArray($MyCollectedResponses) Then
$MyTemp = ''
For $e = 1 To $MyCollectedResponses[0]
  $MyTemp &= $MyCollectedResponses[$e] & @CRLF
Next
If FileWrite($MyResultDatei, $MyTemp) Then
  ; Quickhack un die Datei zu öffnen
  Run(@ComSpec & ' /c start "" "' & $MyResultDatei & '"', @WorkingDir, @SW_HIDE)
  MsgBox($Mresult, "result:", 'the result was writen in the file "' & $MyResultDatei & '"!')
Else
  MsgBox($Mresult, "result:", 'the file could not be written in the file "' & $MyResultDatei & '"!')
EndIf
Else
MsgBox(0, "result:", "no responses via UPnP!")
EndIf
; IP
If IsArray($MyCollectedIPs) Then
_ArrayDisplay($MyCollectedIPs, "Collectes IPs")
Else
MsgBox($Mresult, "result:", "no IP-addresses found!")
EndIf
Exit
#endregion Main
; --- Funktionsdefinitionen ---
#region Funktionsdefinitionen
; Function that performs a UPnP ssdp-discover and writes the results into an array.
; In addition, the IP addresses of the devices are written in a second array.
; V1: Receive-Socket = Send-Socket. Works on XP (independed how many networkcards are installed) and Win 7 only, when all network cards, exclude 1, are disabled in the device manager
; Error:
;   0  =  kein Error
;   1  =  UDPOpen-error
;   2  =  UDPSend-error
;   3  =  UDPRecv-error
;   4  =  UDPBind-error
Func SSDPdiscover_V1(ByRef $ResponsesArray, ByRef $IPArray, $TicksToSearch = 10000, $SendIntervalInTicks = 1000)
; --- UPnP-Kommando ---
Local Const $UPnPcmd = _
   'M-SEARCH * HTTP/1.1' & @CRLF & _
   'ST:upnp:rootdevice' & @CRLF & _
   'MX: 10' & @CRLF & _
   'MAN: "ssdp:discover"' & @CRLF & _
   'HOST: 239.255.255.250:1900' & @CRLF & _
   @CRLF
Local $UPNPsendSocket, $UPNPreceiveSocket
Local $SendCounter = 0, $ReceiveCounter = 0
Local $ReceiveData, $NewIP
Local $StartTimeoutTimer, $UsedTimeoutTicks ; Timeout-Timer
Local $StartSendTimer, $UsedSendTicks ; Send-Timer
Local $OldRemainSeconds = -99, $RemainSeconds
Local $return = 1, $error = 0
; Arrays "löschen"
$ResponsesArray = ""
$IPArray = ""
; UPnP-Kommando ausgeben
ConsoleWrite(@CRLF)
ConsoleWrite("UPnP-Kommando:" & @CRLF)
ConsoleWrite($UPnPcmd & @CRLF)
; UDP starten
UDPStartup()
; - Sender -
;   $array[1] contains the real socket, $array[2] contains the specified IP address and $array[3] contains the port
$UPNPsendSocket = UDPOpen("239.255.255.250", 1900)
ConsoleWrite("SendSocket: real socket: " & $UPNPsendSocket[1] & ", IP-address: " & $UPNPsendSocket[2] & ", port: " & $UPNPsendSocket[3] & @CRLF & @CRLF)
; _ArrayDisplay($UPNPsendSocket)
If $UPNPsendSocket[0] == -1 Or $UPNPsendSocket[0] == 0 Then ; documentation is somewhat vague
  ; Error 1
  Return SetError(1, 0, 0)
EndIf
; - Empfänger -
; Socket muß der gleiche sein, sonst geht es nicht. Allerdings funktioniert es nicht auf Win 7-Rechnern mit mehreren Netzwerkkarten :-(
$UPNPreceiveSocket = $UPNPsendSocket
; Timer setzen
$StartTimeoutTimer = TimerInit() ; Timeout-Timer
$StartSendTimer = -99 ; Notlösung, kann man schöner programmieren
While 1
  ; SenderPause berechnen
  If $StartSendTimer = -99 Then
   ; Timer wurde bisher noch nicht gesetzt, daher UsedTicks setzen
   $UsedSendTicks = $SendIntervalInTicks + 10
  Else
   ; Berechnen
   $UsedSendTicks = TimerDiff($StartSendTimer)
  EndIf
  ; Senden
  If $UsedSendTicks > $SendIntervalInTicks Then
   $SendCounter += 1
   ConsoleWrite("UPnP Send Count Nr " & $SendCounter & @CRLF & @CRLF)
   UDPSend($UPNPsendSocket, $UPnPcmd)
   If @error <> 0 Then
    $error = 2
    $return = 0
    ExitLoop
   EndIf
   ; reset timer
   $StartSendTimer = TimerInit()
  EndIf
  ; kurze Pause (nach dem Senden)
  Sleep(100)
  ; Empfangen
  $ReceiveData = UDPRecv($UPNPreceiveSocket, 1024)
  If @error <> 0 Then
   $error = 3
   $return = 0
   ExitLoop
  EndIf
  If $ReceiveData <> "" Then
   $ReceiveCounter += 1
   ConsoleWrite("-------------------- Received Response " & $ReceiveCounter & ":" & " --------------------" & @CRLF)
   ConsoleWrite($ReceiveData & @CRLF)
   ; Wenn neue Responds, dann hinzugügen
   If AddItemToArray($ResponsesArray, $ReceiveData, 1) > 0 Then
    ConsoleWrite("-> hinzugefügt" & @CRLF & @CRLF)
   Else
    ConsoleWrite("-> bereits vorhanden" & @CRLF & @CRLF)
   EndIf
   ; IP extrahieren und sammeln
   $NewIP = FetchIPfromUPNPdata($ReceiveData)
   If $NewIP <> "" Then AddItemToArray($IPArray, $NewIP, 1)
  EndIf
  ; Verbrauchte Zeit ermitteln
  $UsedTimeoutTicks = TimerDiff($StartTimeoutTimer)
  ; Wenn die Zeit verstrichen ist, dann raus
  If $UsedTimeoutTicks >= $TicksToSearch Then
   ExitLoop
  Else
   ; Restsekunden berechnen und als TrayTip ausgeben
   $RemainSeconds = Ceiling(($TicksToSearch - $UsedTimeoutTicks) / 1000)
   If $RemainSeconds <> $OldRemainSeconds Then
    TrayTip("SSDP-Discover", "Search for devices via UPnP (" & $RemainSeconds & " seconds)... ", 5, 1)
    $OldRemainSeconds = $RemainSeconds
   EndIf
  EndIf
WEnd
; TrayTip schliessen
TrayTip("", "", 0)
; Socket schliessen
UDPCloseSocket($UPNPsendSocket)
UDPCloseSocket($UPNPreceiveSocket)
; UDP beenden
UDPShutdown()
; Sortieren
If IsArray($IPArray) Then _ArraySort($IPArray, 0, 1)
If IsArray($ResponsesArray) Then _ArraySort($ResponsesArray, 0, 1)
; Wenn error vorhanden, dann error zurückliefern
If $error > 0 Then
  ; error zurückliefern
  Return SetError($error, 0, $return)
Else
  ; Normaler Return
  Return $return
EndIf
EndFunc   ;==>SSDPdiscover_V1

; Function that performs a UPnP ssdp-discover and writes the results into an array.
; In addition, the IP addresses of the devices are written in a second array.
; V2: Receive-Socket and Send-Socket are differ. I try many different ways, but nothing works :-(
; Error:
;   0  =  kein Error
;   1  =  UDPOpen-error
;   2  =  UDPSend-error
;   3  =  UDPRecv-error
;   4  =  UDPBind-error
Func SSDPdiscover_V2(ByRef $ResponsesArray, ByRef $IPArray, $TicksToSearch = 10000, $SendIntervalInTicks = 1000)
; --- UPnP-Kommando ---
Local Const $UPnPcmd = _
   'M-SEARCH * HTTP/1.1' & @CRLF & _
   'ST:upnp:rootdevice' & @CRLF & _
   'MX: 10' & @CRLF & _
   'MAN: "ssdp:discover"' & @CRLF & _
   'HOST: 239.255.255.250:1900' & @CRLF & _
   @CRLF
Local $UPNPsendSocket, $UPNPreceiveSocket
Local $SendCounter = 0, $ReceiveCounter = 0
Local $ReceiveData, $NewIP
Local $StartTimeoutTimer, $UsedTimeoutTicks ; Timeout-Timer
Local $StartSendTimer, $UsedSendTicks ; Send-Timer
Local $OldRemainSeconds = -99, $RemainSeconds
Local $return = 1, $error = 0
; Arrays "löschen"
$ResponsesArray = ""
$IPArray = ""
; UPnP-Kommando ausgeben
ConsoleWrite(@CRLF)
ConsoleWrite("UPnP-Kommando:" & @CRLF)
ConsoleWrite($UPnPcmd & @CRLF)
; UDP starten
UDPStartup()
; - Sender -
;   $array[1] contains the real socket, $array[2] contains the specified IP address and $array[3] contains the port
$UPNPsendSocket = UDPOpen("239.255.255.250", 1900)
ConsoleWrite("SendSocket: real socket: " & $UPNPsendSocket[1] & ", IP-address: " & $UPNPsendSocket[2] & ", port: " & $UPNPsendSocket[3] & @CRLF & @CRLF)
; _ArrayDisplay($UPNPsendSocket)
If $UPNPsendSocket[0] == -1 Or $UPNPsendSocket[0] == 0 Then ; documentation is somewhat vague
  ; Error 1
  Return SetError(1, 0, 0)
EndIf
; - Empfänger -
If @IPAddress1 <> "0.0.0.0" Then ConsoleWrite(@IPAddress1 & @CRLF)
If @IPAddress2 <> "0.0.0.0" Then ConsoleWrite(@IPAddress2 & @CRLF)
If @IPAddress3 <> "0.0.0.0" Then ConsoleWrite(@IPAddress3 & @CRLF)
If @IPAddress4 <> "0.0.0.0" Then ConsoleWrite(@IPAddress4 & @CRLF)
; $UPNPreceiveSocket = UDPBind("127.0.0.1", 1900)
; $UPNPreceiveSocket = UDPBind(@IPAddress1, 1900)
; $UPNPreceiveSocket = UDPBind("127.0.0.1", $UPNPsendSocket[1])
; $UPNPreceiveSocket = UDPBind(@IPAddress1, $UPNPsendSocket[1])
; $UPNPreceiveSocket = UDPBind("239.255.255.250", 1900)
$UPNPreceiveSocket = UDPBind(@IPAddress1, 1900)
ConsoleWrite("ReceiveSocket: real socket: " & $UPNPreceiveSocket[1] & ", IP-address: " & $UPNPreceiveSocket[2] & ", port: " & $UPNPreceiveSocket[3] & @CRLF & @CRLF)
; _ArrayDisplay($UPNPsendSocket)
If $UPNPreceiveSocket[0] == -1 Or $UPNPreceiveSocket[0] == -0 Then ; documentation is somewhat vague
  ; Error 4
  Return SetError(4, 0, 0)
EndIf
; Timer setzen
$StartTimeoutTimer = TimerInit() ; Timeout-Timer
$StartSendTimer = -99 ; temporary solution, could be better :)
While 1
  ; SenderPause berechnen
  If $StartSendTimer = -99 Then
   ; Timer wurde bisher noch nicht gesetzt, daher UsedTicks setzen
   $UsedSendTicks = $SendIntervalInTicks + 10
  Else
   ; Berechnen
   $UsedSendTicks = TimerDiff($StartSendTimer)
  EndIf
  ; Senden
  If $UsedSendTicks > $SendIntervalInTicks Then
   $SendCounter += 1
   ConsoleWrite("UPnP Send Count Nr " & $SendCounter & @CRLF & @CRLF)
   UDPSend($UPNPsendSocket, $UPnPcmd)
   If @error <> 0 Then
    $error = 2
    $return = 0
    ExitLoop
   EndIf
   ; reset timer
   $StartSendTimer = TimerInit()
  EndIf
  ; kurze Pause (nach dem Senden)
  Sleep(100)
  ; Empfangen
  $ReceiveData = UDPRecv($UPNPreceiveSocket, 1024)
  If @error <> 0 Then
   $error = 3
   $return = 0
   ExitLoop
  EndIf
  If $ReceiveData <> "" Then
   $ReceiveCounter += 1
   ConsoleWrite("-------------------- Received Response " & $ReceiveCounter & ":" & " --------------------" & @CRLF)
   ConsoleWrite($ReceiveData & @CRLF)
   ; Wenn neue Responds, dann hinzugügen
   If AddItemToArray($ResponsesArray, $ReceiveData, 1) > 0 Then
    ConsoleWrite("-> hinzugefügt" & @CRLF & @CRLF)
   Else
    ConsoleWrite("-> bereits vorhanden" & @CRLF & @CRLF)
   EndIf
   ; IP extrahieren und sammeln
   $NewIP = FetchIPfromUPNPdata($ReceiveData)
   If $NewIP <> "" Then AddItemToArray($IPArray, $NewIP, 1)
  EndIf
  ; Verbrauchte Zeit ermitteln
  $UsedTimeoutTicks = TimerDiff($StartTimeoutTimer)
  ; Wenn die Zeit verstrichen ist, dann raus
  If $UsedTimeoutTicks >= $TicksToSearch Then
   ExitLoop
  Else
   ; Restsekunden berechnen und als TrayTip ausgeben
   $RemainSeconds = Ceiling(($TicksToSearch - $UsedTimeoutTicks) / 1000)
   If $RemainSeconds <> $OldRemainSeconds Then
    TrayTip("SSDP-Discover", "Search for devices via UPnP (" & $RemainSeconds & " seconds)... ", 5, 1)
    $OldRemainSeconds = $RemainSeconds
   EndIf
  EndIf
WEnd
; TrayTip schliessen
TrayTip("", "", 0)
; Socket schliessen
UDPCloseSocket($UPNPsendSocket)
UDPCloseSocket($UPNPreceiveSocket)
; UDP beenden
UDPShutdown()
; Sortieren
If IsArray($IPArray) Then _ArraySort($IPArray, 0, 1)
If IsArray($ResponsesArray) Then _ArraySort($ResponsesArray, 0, 1)
; Wenn error vorhanden, dann error zurückliefern
If $error > 0 Then
  ; error zurückliefern
  Return SetError($error, 0, $return)
Else
  ; Normaler Return
  Return $return
EndIf
EndFunc   ;==>SSDPdiscover_V2
; Funktion, die aus den ReceivedPacket die IP-Adresse extrahiert
Func FetchIPfromUPNPdata($Data)
#cs Beispiel:

  HTTP/1.1 200 OK
  LOCATION: http://192.168.5.1:49000/igddesc.xml
  SERVER: WLAN_VDSL_Ullrich UPnP/1.0 AVM FRITZ!Box Fon WLAN 7270 54.04.74
  CACHE-CONTROL: max-age=1800
  EXT:
  ST: upnp:rootdevice
  USN: uuid:75802409-bccb-40e7-8e6c-001F3F56E239::upnp:rootdevice
#ce
; --- Einfach mit RegExp die IP rausfiltern ---
; Local $IP = StringRegExp($Data, "LOCATION: http://\d+\.\d+\.\d+\.\d+:49", 1)
Local $IP = StringRegExp($Data, "\d+\.\d+\.\d+\.\d+", 1)
; _ArrayDisplay($IP)
; Scheun ob es ein Array ist, wenn nicht raus
If Not IsArray($IP) Then Return ""
; Ich bauche erstes Element aus dem RegExp-Ergebnis
$IP = $IP[0]
; überprüfen auf Gültigkeit
If Not _IsIPv4($IP) Then Return ""
Return $IP
EndFunc   ;==>FetchIPfromUPNPdata

; Funktion, die überprüft, ob es eine gültige IP4-IP ist
Func _IsIPv4($S_IP)
If StringRegExp($S_IP, "\A(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])\z") Then Return (1)
Return (0)
EndFunc   ;==>_IsIPv4

; Funktion, die ein Item an das Array (mit Zelle) hinzufügt und dabei den Counter in Zelle 0 um eins erhöht
; Ist das Array noch leer, wird es angelegt. Es wird der Counter zurückgeliefert
; Wenn $OnlyIfNew = 1, dann wird vorher geschaut, ob es schon im Array vorhanden ist
Func AddItemToArray(ByRef $ArrayWithCounterCell, $value, $OnlyIfNew = 0) ; Fügt zum Array ein Item hinzu und erhöht den Wert in Zelle 0. Ist das Array leer, wird eines erzeugt
; Schauen, ob es ein Array ist. Wenn nicht, dann wird es angelegt und der Wert hinzugefügt
If IsArray($ArrayWithCounterCell) Then
  ; es ist ein Array
  ; ggf. schauen, ob es bereits enthalten ist
  If $OnlyIfNew == 1 Then
   ; Raus, wenn es bereist enthalten ist, also größer als 0
   If _ArraySearch($ArrayWithCounterCell, $value, 1) > 0 Then
    Return 0
   EndIf
  EndIf
  ; Element hinzufügen
  Local $ret = _ArrayAdd($ArrayWithCounterCell, $value)
  ; wenn ungleich -1 dann den Counter erhöhen
  If $ret <> -1 Then
   ; um 1 erhöhren in Zelle 0
   Local $Count = $ArrayWithCounterCell[0] + 1
   $ArrayWithCounterCell[0] = $Count
   ; Index, also Count zurückliefern
   Return $Count
  Else
   Return -1
  EndIf
Else
  ; Es ist kein Array, daher erzeugen und Wert hinzufügen
  Dim $ArrayWithCounterCell[2]
  $ArrayWithCounterCell[0] = 1
  $ArrayWithCounterCell[1] = $value
  Return 1
EndIf
EndFunc   ;==>AddItemToArray
#endregion Funktionsdefinitionen

Yes, I know, there is also a but my final goal is, to collect the "Locations", and the Obj-methode doesn't provide this information.

Does anyone have any idea what I'm doing wrong?

Many thanks and best regards

skyteddy

SSDPdiscover_V3_english.au3

Share this post


Link to post
Share on other sites
skyteddy

Hi all,

I've just noticed with dismay, on Windows XP I receive only answers from devices, they are connected on the first network card.

I don't receive anything from the devices, they are connected on the second network cards.

When I want to "read" this devices, I must disable the first network card in Device Manager.

What do I wrong in principle or what is the matter?

many thanks in advance

skyteddy

Share this post


Link to post
Share on other sites
skyteddy

Is there anybody who can helps me?

many thanks

skyteddy

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×