Forum > Networking and Web Programming

Anyone into mDNS?

(1/1)

bobby100:
Hi @all,

last month, I've completed a task to implement mDNS/Zeroconfig search into an app.
My target was to get the IP addresses of the PLCs in the network (I am working as an instrumentation and controlling engineer). The PLCs we're using, are responding to mDNS queries from the engineering software and I wanted to have this same functionality in my app.
The app is not open source, but I would like to share all my experience worth a month of internet search (not much info found in internet anyway) for someone like me, who didn't have any idea about how is mDNS working (and still has not the whole picture, just the part he needs). The app I wrote can also get and set the parameters of the network adapters (IP, Gateway etc.) and I would also like to share this code in another thread.

I have used two components from Indy 10 for mDNS/zeroconf/Avahi/Bonjour communication:
- IdIPMCastServer to send the mDNS query
- IdUDPServer to catch the answers

The basics of the multicast is that the IdIPMCastServer sends a query to the group with specifying the UDP port (Bound port in Indy terminology) where your app is waiting for the answers. After sending the query, our IdIPMCastServer needs to be deactivated.
Waiting for answers belongs to IdUDPServer, which should now be activated. Because of using the same port, you can't have them both active at the same time.
More instructions are in the code comments.


--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---procedure TfrmMain.mnuMulticastClick(Sender: TObject);var  //basic communication as captured by Wireshark  {00 00 - Transaction ID   00 00 - Standard query   00 01 - Question count   00 00 - Answer RRs count   00 00 - Authority RRs count   00 00 - Additional RRs count   xx xx - query string/question string   00 0c - Type PTR   00 01 - Class IN}  query: TIdBytes = ($00, $00, $00, $00, $00, $01, $00, $00, $00, $00,    $00, $00, $09, $5f, $73, $65, $72, $76, $69, $63, $65, $73, $07,    $5f, $64, $6e, $73, $2d, $73, $64, $04, $5f, $75, $64, $70, $05,    $6c, $6f, $63, $61, $6c, $00, $00, $0c, $00, $01);begin  //set CastServer  IdIPMCastServer.MulticastGroup := Id_IPMC_mDNS;  IdIPMCastServer.BoundIP := String_IP_Address; //Here goes the IP address of your network adapter  IdIPMCastServer.BoundPort := 49152; //my favorite port. Pick one that is not used by other apps in your network. Other devices will use this port to answer your query  IdIPMCastServer.Port := 5353; //this is standard   //set UDPServer  IdUDPServer.Bindings.Add;  IdUDPServer.Bindings[0].IP := String_IP_Address; //Here goes the IP address of your network adapter  IdUDPServer.Bindings[0].Port := 49152;   //send Query  IdIPMCastServer.Active := True;  IdIPMCastServer.Send(query);   //deactivate CastServer, activate UDPServer and deactivation timer for UDPServer  IdIPMCastServer.Active := False;  tmUDPdeactivate.Interval := IdIPMCastServer.TimeToLive * 1000;  tmUDPdeactivate.Enabled := True; //I use a timer to deactivate the IdUDPServer after 5 seconds of waiting for messages. You need this if you want to send another query with IdIPMCastServer because of shared port  IdUDPServer.Active := True;end;    
My IdUDPServer.OnUDPRead looks like this:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---procedure TfrmMain.IdUDPServerUDPRead(AThread: TIdUDPListenerThread;  const AData: TIdBytes; ABinding: TIdSocketHandle);begin  sgMachines.InsertRowWithValues(sgMachines.RowCount, [IPAddrToName(ABinding.PeerIP), ABinding.PeerIP]); ...end;I am just adding to a StringGrid the IP address of devices that responded to my query.
The responses from the devices are pretty much alike to the query-structure that I've sent to multicast.
Except for the PLCs that I wanted to search for, my code also finds my printers and NAS in my home network. It means - the query string is not specific for the PLCs, but it seems to be pretty much a universal query (anyone here with some more info about the query strings?).
This is the point where I am asking for help from more experienced programmers - let's make together a unit for this mDNS queries!

For the beginning, I have prepared the basic types:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---type   TmDNSQuestion = record    Query: string; //null terminated    QueryType: word;    QueryClass: word;  end;   TmDNSQuery = record    TransactionID: word;    Flags: word;    Questions: word;    AnswerRRs: word;    AuthorityRRs: word;    AdditionslRRs: word;    Q: array of TmDNSQuestion  end;   TmDNSAnswer = record    AnswerName: word;    AnswerType: word;    AnswerClass: word;    CacheFlush: word;    TimeToLive: word; //in seconds    DataLength: word; //for DomainName    DomainName: array of char; //Not null-terminated  end;   TmDNSResponse = record    TransactionID: word;    Flags: word;    Questions: word;    AnswerRRs: word;    AuthorityRRs: word;    AdditionslRRs: word;    Q: array of TmDNSQuestion;    A: array of TmDNSAnswer;  end;This is what I've got till now from reverse-engineering the Wireshark captures.
TmDNSAuthority and TmDNSAdditional are of unknown structure for me because I don't have any capture containing these.

Anyone interested?
Or anyone with some more knowledge, who may eventually find flaws or mistakes here?

I have my app done. I would like to gather here some info for the benefit of wider public.

MarkMLl:
Thanks for updating us. I don't think it's immediately useful to the sort of thing I do but I think everybody here appreciates the feedback.

MarkMLl

engkin:
In your records you cannot use dynamic types like String and array of byte. Typically you reserve enough memory for, let's say, your query. Then you use pointers and record pointer types to fill the memory with intended values.

Also, you need to take care of endianness. IIRC, network records are big endian. That's why, for instance, your third field in your query, Question count, is $00 $01, while 1 on Intel CPUs is $01 $00

For details, you can rely on DNS documentation/implementation as mDNS is based on DNS.

Navigation

[0] Message Index

Go to full version