Multicast DNS (mDNS)
Table of Contents >> Application Reference
The Treck mDNS/DNS-SD product provides a subset of the Multicast DNS and DNS-Based Service Discovery specifications defined in RFC 6762 and RFC 6763.
Treck mDNS is designed for embedded devices with simple needs:
- Must identify itself by name and IP address.
- Must identify the services it provides by name, type and port number.
- Must communicate the information to local workstations using a widely accepted name query/response mechanism.
- Has no need to query other devices or manage the mDNS information associated with other devices.
Contents
User Interface
The following functions provide support for mDNS.
- tfMdnsStart()
- Configure mDNS and prepare a new object with the necessary resources (socket, timer, DNS message names). Sets a callback function for notification of mDNS events, conditions and state transitions. A new mDNS descriptor handle is returned for use with the functions described below.
- tfMdnsStop()
- Signal shutdown. The mDNS descriptor handle remains valid until tfMdnsExecute() returns TM_ESHUTDOWN.
- tfMdnsExecute()
- Run the mDNS state machine for the descriptor returned by tfMdnsStart(). The mDNS descriptor handle becomes invalid when tfMdnsExecute() returns TM_ESHUTDOWN and the user must stop using it.
- tfMdnsSetData()
- Associate a user data pointer with the mDNS handle.
- tfMdnsGetData()
- Return the user data pointer set by tfMdnsSetData(), or NULL if not set.
- tfMdnsGetState()
- Get the current state of the mDNS descriptor (TM_MDNS_S_...).
- tfMdnsGetLastError()
- Get the last Treck error code recorded in the mDNS descriptor.
- tfMdnsGetLastNameMatch()
- Get the last domain name match for a received mDNS message. Use this information to identify which name failed during its probe phase.
Using Treck mDNS
Treck mDNS is sold as a separate product. If you're not sure you have the product and want to check, open file treck-tcpip-*-options.txt and look for the Multicast DNS option ("ON" = installed).
Include Treck mDNS by adding source/trmdns.c to your build. In library form, the mDNS code will be included if your application calls any mDNS functions, excluded otherwise. You can define the following compile time macro to expressly exclude the mDNS code without removing the trmdns object from your build. By default, the following macro is undefined (see include/trsystem.h).
#define TM_DISABLE_MDNS
| Important design considerations |
- Reentrancy: Treck mDNS does not use any lock or mutex to guard the descriptor. Function tfMdnsExecute() controls the mDNS and must be called from one thread, only (the mDNS Thread). A global list of all active mDNS servers provides a means for validation. Treck uses a lock in this case to guard against race conditions when adding to the list.
- Asynchronous Events: Some events that trigger the user's callback function can occur from threads running concurrently with the mDNS Thread thread. To avoid reentrancy problems and race conditions, do not call Treck functions that use the mDNS handle unless you know what you are doing. Be aware that some events can occur during and after descriptor deletion. With that in mind, you can call atomic, read-only functions like tfMdnsGetData().
Configuring mDNS
Call tfMdnsStart() to configure mDNS in your product.
int tfMdnsStart(ttMdnsServerPtrPtr mdRefPtr, ttConstMdnsConfigPtr scfg, ttMdnsUserFunc user);
Declare a variable of type ttMdnsServerPtr and pass by reference via parameter mdRefPtr. The handle returned is used by all other API functions.
Specify configuration information via parameter scfg, types ttMdnsConfig and ttMdnsConfigService[].
/* mDNS server configuration (per mDNS descriptor handle). * . Host name must be unique on the physical link, e.g. "Motor Control 2". * . Interface handle and IPv4 multihome index must be specified * (see tfConfigInterface, tfGetIpAddress and tfSetMcastInterface). * . List of services is optional. If provided, each service instance must * be unique on the physical link, e.g. "Printer at Mary's Desk". * . Flags must be zero (not currently used). */ typedef struct tsMdnsConfig { ttUserConstCharPtr mcHostName; ttUserInterface mcInterface; int mcMultihome; ttMdnsConstConfigServicePtr mcServiceArr; int mcServiceCount; unsigned int mcFlags; } ttMdnsConfig; typedef ttMdnsConfig TM_FAR *ttMdnsConfigPtr; typedef ttMdnsConfigPtr TM_FAR *ttMdnsConfigPtrPtr; typedef const ttMdnsConfig TM_FAR *ttConstMdnsConfigPtr;
Create an array of structures (see below) for the services you are providing. Specify a NULL array pointer and count of zero in the structure above, if you aren't providing any.
/* mDNS service configuration (DNS SRV record). * Service Instance Name ([RFC6763].4.1): <Instance> . <Service> . <Domain> * <Instance>: user-friendly name, e.g. "Web server at Alpha Station". * <Service>: type of service, e.g. "_http._tcp". * <Domain>: always ".local" for mDNS. */ typedef struct tsMdnsConfigService { ttUserConstCharPtr mcsName; /* <Instance> part of [RFC6763].4.1 */ ttUserConstCharPtr mcsType; /* <Service> part of [RFC6763].4.1 */ unsigned int mcsPort; /* Port number (UDP/TCP) of the service */ } ttMdnsConfigService; typedef ttMdnsConfigService TM_FAR *ttMdnsConfigServicePtr; typedef const ttMdnsConfigService TM_FAR *ttMdnsConstConfigServicePtr;
These structures are defined in include/trsocket.h.
Create a callback function of the form void user_callback(ttMdnsServerPtr mdnsPtr, int eventId) {} and pass the function pointer via parameter user. Event identifiers are as follows.
- TM_MDNS_V_SOCKET
- Socket receive (asynchronous socket callback). Signal your mDNS thread to call tfMdnsExecute() to process the event. This is a notification from tfRecvInterface() or tfRecvScatInterface().
- TM_MDNS_V_TIMER
- Timer expired (asynchronous timer callback). Signal your mDNS thread to call tfMdnsExecute() to process the event. This is a notification from tfTimerExecute().
- TM_MDNS_V_USER
- User-initiated action (asynchronous to mDNS processing). Signal your mDNS thread to call tfMdnsExecute() to process the event. This is a notification from tfMdnsStop().
- TM_MDNS_V_PROBE
- State change informational event. The mDNS is sending probe messages to detect if the configured names are already in use by another host on the link. This is a notification from tfMdnsExecute().
- TM_MDNS_V_ANNOUNCE
- State change informational event. The mDNS is sending announce messages to inform of name ownership after a successful probe phase. This is a notification from tfMdnsExecute().
- TM_MDNS_V_UP
- State change informational event. The mDNS will passively respond to queries, indefinitely. This is a notification from tfMdnsExecute().
- TM_MDNS_V_RXDATA
- Received message is available, temporarily. This is a notification from tfMdnsExecute().
- TM_MDNS_V_FAIL
- State change informational event. The mDNS remains in the fail state after receiving a response to a probe message. The user must shutdown the mDNS and reconfigure with different names. This is a notification from tfMdnsExecute().
- TM_MDNS_V_DOWN
- State change informational event. The mDNS object is about to be deleted. The user must not use the mDNS handle after returning from the callback. This is a notification from tfMdnsExecute().
Examples
A device with two network services. The name of this unit is Sensor #7. The unit is configured with a Web server and Telnet server named Web server at Sensor #7 and Telnet server at Sensor #7, respectively. Note that the names must not conflict with names used by other mDNS units on the link.
/* Global data */ ttUserInterface ifHandle; ttMdnsServerPtr mdnsHandle; int nameConflict; /* The mDNS thread */ int RunMdnsService() { ttMdnsConfig mdnsConfig; ttMdnsConfigService mdnsService[2]; int errCode; /* Set the mDNS configuration data */ memset(&mdnsConfig, 0, sizeof mdnsConfig); mdnsConfig.mcHostName = "Sensor #7"; mdnsConfig.mcInterface = ifHandle; mdnsConfig.mcMultihome = 0; mdnsConfig.mcServiceArr = mdnsService; mdnsConfig.mcServiceCount = 2; mdnsService[0].mcsName = "Web server at Sensor #7"; mdnsService[0].mcsType = "_http._tcp"; mdnsService[0].mcsPort = 80; mdnsService[1].mcsName = "Telnet server at Sensor #7"; mdnsService[1].mcsType = "_telnet._tcp"; mdnsService[1].mcsPort = 23; /* Create the mDNS descriptor */ errCode = tfMdnsStart(&mdnsHandle, &mdnsConfig, ServiceMdnsCallback); if (errCode != TM_ENOERROR) { fprintf(stderr, "[ERROR] %s:%d: %d (%s)\n", __FILE__, __LINE__, errCode, tfStrError(errCode)); return FAIL; } /* Run the mDNS state machine */ while ((errCode = tfMdnsExecute(mdnsHandle)) == TM_ENOERROR) { os_event_wait(osEventHandle); /* O/S specific */ if (nameConflict) { /* Destroy the descriptor when a duplicate name was found on the link */ fprintf(stderr, "[ERROR] %s:%d: the chosen names are not unique\n", __FILE__, __LINE__); goto FailExit; } } if (errCode != TM_ESHUTDOWN) { /* Destroy the descriptor on error */ fprintf(stderr, "[ERROR] %s:%d: %d (%s)\n", __FILE__, __LINE__, errCode, tfStrError(errCode)); FailExit: tfMdnsStop(mdnsHandle); do errCode = tfMdnsExecute(mdnsHandle); while (errCode != TM_ESHUTDOWN); return FAIL; } /* Externally stopped (see StopMdnsService() below) and successfully deleted */ return SUCCESS; } /* Callback from the mDNS state machine or asynchronous mDNS sources */ void ServiceMdnsCallback(ttMdnsServerPtr mdnsHandle, int eventId) { switch (eventId) { /* Asynchronous events for the mDNS state machine to process */ case TM_MDNS_V_SOCKET: /* mDNS socket receive */ case TM_MDNS_V_TIMER: /* mDNS timer expired */ case TM_MDNS_V_USER: /* user called tfMdnsStop() */ os_event_signal(osEventHandle); /* O/S specific */ break; /* One of the names is in use by another host */ case TM_MDNS_V_FAIL: /* Signal the main loop to stop mDNS and restart with different names */ nameConflict = 1; os_event_signal(osEventHandle); /* O/S specific */ break; /* The descriptor is about to be deleted */ case TM_MDNS_V_DOWN: mdnsHandle = NULL; break; default: ; } } /* Call this function from any context when you want to shutdown the mDNS */ void StopMdnsService() { tfMdnsStop(mdnsHandle); }
A device with a host name only (same as above but no services defined):
. . . /* Set the mDNS configuration data */ . . . mdnsConfig.mcServiceArr = NULL; mdnsConfig.mcServiceCount = 0; . . .