5

There are few uPNP devices in my network. I am trying to send M-SEARCH request to the network and hope receiving some responses from it. This is what I am trying:

var sIP, sOut: string;
    iPort: Word;
    S: TStringBuilder;
begin
  S := TStringBuilder.Create;
  try
    S.Append('M-SEARCH * HTTP/1.1').AppendLine
     .Append('HOST: 239.255.255.250:1900').AppendLine
     .Append('MAN: "ssdp:discover"').AppendLine
     .Append('MX: 10').AppendLine
     .Append('ST: ssdp:all').AppendLine;

    IdUDPClient1.ReceiveTimeout := 3000;
    IdUDPClient1.Broadcast(S.ToString, 1900, '239.255.255.250');
    sOut := IdUDPClient1.ReceiveString(sIP, iPort);
    Memo1.Lines.Add(sIP);
    Memo1.Lines.Add(IntToStr(iPort));
    Memo1.Lines.Add(sOut);
  finally
    S.Free;
  end;
end;

I receive nothing from the UDP client. I use Wireshark to monitor network traffic and no message was send out from my host.

Any ideas? Thank you.

I found the answer finally:

uses
  System.SysUtils, IdUDPClient, IdStack;

var S: TStringBuilder;
    U: TIdUDPClient;
    iPeerPort: Word;
    sPeerIP, sResponse: string;
begin
  U := TIdUDPClient.Create(nil);
  S := TStringBuilder.Create;
  try
    S.Append('M-SEARCH * HTTP/1.1').AppendLine
     .Append('HOST: 239.255.255.250:1900').AppendLine
     .Append('MAN: "ssdp:discover"').AppendLine
     .Append('MX: 3').AppendLine
     .Append('ST: ssdp:all').AppendLine
     .AppendLine;

    U.BoundIP := GStack.LocalAddress;
    U.Send('239.255.255.250', 1900, S.ToString);

    U.ReceiveTimeout := 1000;
    repeat
      sResponse := U.ReceiveString(sPeerIP, iPeerPort);
      if iPeerPort <> 0 then begin
        WriteLn(Format('%s:%d', [sPeerIP, iPeerPort]));
        WriteLn(sResponse);
      end;
    until iPeerPort = 0;
    ReadLn;
  finally
    S.Free;
    U.Free;
  end;
end.
Chau Chee Yang
  • 18,422
  • 16
  • 68
  • 132

3 Answers3

6

Call AppendLine() twice at the end of the string builder. HTTP request headers are terminated by two CRLF pairs, but you are only appending one pair, so you are sending an incomplete request.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I add a new AppendLine() to stringbuilder object, but I didn't receive any response too. I also didn't notice the request send out from my host when monitor the traffic via WireShark. – Chau Chee Yang Jun 07 '12 at 00:58
  • Are you sure that `239.255.255.250` is the correct broadcast IP for your LAN? What local IP and subnet is assigned to your client's PC? – Remy Lebeau Jun 07 '12 at 01:10
  • If I broadcast to 239.255.255.255, I can see the traffic sent out from my host. But broadcast to 239.255.255.250, no traffic was detected. – Chau Chee Yang Jun 07 '12 at 01:11
  • I am not sure if 239.255.255.250 is the address I should broadcast. I am trying to send the m-search packet to 239.255.255.250 in order to receive some replies of available uPNP devices in my network. How to do it with IdUDPClient? Should I broadcast or send? – Chau Chee Yang Jun 07 '12 at 01:26
  • I found out the problem. I should set BoundIP to my adapter's IP. The uPNP Device Host Service doesn't listen to 0.0.0.0. It listen to host's adapter's IP only. – Chau Chee Yang Jun 07 '12 at 03:01
3

Quick and Dirty solution using TIdUDPServer (Indy 9).

Drop the TIdUDPServer component on the form, and using Object Inspector set Bindings to your local IP eg. 10.1.0.78:0, set BroadcastEnabled and Active to true. Drop a TMemo and TButton on the form.

Complete the OnClick and UDPRead Events as follows:

uses IdSocketHandle;
...
procedure TForm1.Button1Click(Sender: TObject);
begin
  IdUDPServer1.Send('239.255.255.250', 1900, 'M-SEARCH * HTTP/1.1' + #13#10 +
     'HOST: 239.255.255.250:1900' + #13#10 +
     'MAN: "ssdp:discover"'+ #13#10 +
     'MX: 3'+ #13#10 +
     'ST: ssdp:all'+ #13#10 +
     #13#10);
end;

procedure TForm1.IdUDPServer1UDPRead(Sender: TObject; AData: TStream; ABinding: TIdSocketHandle);
var 
   data: string;
begin

   setlength(data, Adata.Size - AData.Position); //No fragmentation :)        
   AData.ReadBuffer(data[1], length(data));

   memo1.Lines.Add('Read: ' + inttostr(AData.Position) + ' / ' + inttostr(AData.Size) + ' PeerIP: ' + ABinding.PeerIP);       
   memo1.Lines.Add(data);
end;

Save, Run and Bob's your uncle.

Noener
  • 31
  • 2
0

For multicast M-SEARCH, the message format is defined below. Values between * * are placeholders for actual values.

M-SEARCH * HTTP/1.1
HOST: 239.255.255.250:1900
MAN: "ssdp:discover"
MX: *seconds to delay response*
ST: *search target*
USER-AGENT: *OS/version UPnP/1.1 product/version*

So you need to fix ReceiveTimeout to

U.ReceiveTimeout := 3000;

It should be at least equal to delay in your request (MX:3) 3 sec = 3000 milliseconds