mdns_client.c
Go to the documentation of this file.
1 /**
2  * @file mdns_client.c
3  * @brief mDNS client (Multicast DNS)
4  *
5  * @section License
6  *
7  * Copyright (C) 2010-2018 Oryx Embedded SARL. All rights reserved.
8  *
9  * This file is part of CycloneTCP Open.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24  *
25  * @author Oryx Embedded SARL (www.oryx-embedded.com)
26  * @version 1.9.0
27  **/
28 
29 //Switch to the appropriate trace level
30 #define TRACE_LEVEL MDNS_TRACE_LEVEL
31 
32 //Dependencies
33 #include "core/net.h"
34 #include "ipv6/ipv6.h"
35 #include "ipv6/ipv6_misc.h"
36 #include "mdns/mdns_client.h"
37 #include "mdns/mdns_responder.h"
38 #include "mdns/mdns_common.h"
39 #include "dns/dns_debug.h"
40 #include "debug.h"
41 
42 //Check TCP/IP stack configuration
43 #if (MDNS_CLIENT_SUPPORT == ENABLED)
44 
45 
46 /**
47  * @brief Resolve a host name using mDNS
48  * @param[in] interface Underlying network interface
49  * @param[in] name Name of the host to be resolved
50  * @param[in] type Host type (IPv4 or IPv6)
51  * @param[out] ipAddr IP address corresponding to the specified host name
52  **/
53 
56 {
57  error_t error;
58  DnsCacheEntry *entry;
59 
60 #if (NET_RTOS_SUPPORT == ENABLED)
61  systime_t delay;
62 
63  //Debug message
64  TRACE_INFO("Resolving host name %s (mDNS resolver)...\r\n", name);
65 #endif
66 
67  //Get exclusive access
69 
70  //Search the DNS cache for the specified host name
71  entry = dnsFindEntry(interface, name, type, HOST_NAME_RESOLVER_MDNS);
72 
73  //Check whether a matching entry has been found
74  if(entry)
75  {
76  //Host name already resolved?
77  if(entry->state == DNS_STATE_RESOLVED ||
78  entry->state == DNS_STATE_PERMANENT)
79  {
80  //Return the corresponding IP address
81  *ipAddr = entry->ipAddr;
82  //Successful host name resolution
83  error = NO_ERROR;
84  }
85  else
86  {
87  //Host name resolution is in progress...
88  error = ERROR_IN_PROGRESS;
89  }
90  }
91  else
92  {
93  //If no entry exists, then create a new one
94  entry = dnsCreateEntry();
95 
96  //Record the host name whose IP address is unknown
97  strcpy(entry->name, name);
98 
99  //Initialize DNS cache entry
100  entry->type = type;
102  entry->interface = interface;
103 
104  //Initialize retransmission counter
106  //Send mDNS query
107  error = mdnsClientSendQuery(entry);
108 
109  //mDNS message successfully sent?
110  if(!error)
111  {
112  //Save the time at which the query message was sent
113  entry->timestamp = osGetSystemTime();
114  //Set timeout value
117  //Decrement retransmission counter
118  entry->retransmitCount--;
119 
120  //Switch state
121  entry->state = DNS_STATE_IN_PROGRESS;
122  //Host name resolution is in progress
123  error = ERROR_IN_PROGRESS;
124  }
125  }
126 
127  //Release exclusive access
129 
130 #if (NET_RTOS_SUPPORT == ENABLED)
131  //Set default polling interval
133 
134  //Wait the host name resolution to complete
135  while(error == ERROR_IN_PROGRESS)
136  {
137  //Wait until the next polling period
138  osDelayTask(delay);
139 
140  //Get exclusive access
142 
143  //Search the DNS cache for the specified host name
144  entry = dnsFindEntry(interface, name, type, HOST_NAME_RESOLVER_MDNS);
145 
146  //Check whether a matching entry has been found
147  if(entry)
148  {
149  //Host name successfully resolved?
150  if(entry->state == DNS_STATE_RESOLVED)
151  {
152  //Return the corresponding IP address
153  *ipAddr = entry->ipAddr;
154  //Successful host name resolution
155  error = NO_ERROR;
156  }
157  }
158  else
159  {
160  //Host name resolution failed
161  error = ERROR_FAILURE;
162  }
163 
164  //Release exclusive access
166 
167  //Backoff support for less aggressive polling
168  delay = MIN(delay * 2, DNS_CACHE_MAX_POLLING_INTERVAL);
169  }
170 
171  //Check status code
172  if(error)
173  {
174  //Failed to resolve host name
175  TRACE_INFO("Host name resolution failed!\r\n");
176  }
177  else
178  {
179  //Successful host name resolution
180  TRACE_INFO("Host name resolved to %s...\r\n", ipAddrToString(ipAddr, NULL));
181  }
182 #endif
183 
184  //Return status code
185  return error;
186 }
187 
188 
189 /**
190  * @brief Send a mDNS query message
191  * @param[in] entry Pointer to a valid DNS cache entry
192  * @return Error code
193  **/
194 
196 {
197  error_t error;
198  DnsQuestion *dnsQuestion;
200 
201  //Create an empty mDNS query message
202  error = mdnsCreateMessage(&message, FALSE);
203  //Any error to report?
204  if(error)
205  return error;
206 
207  //Encode the host name using the DNS name notation
208  message.length += dnsEncodeName(entry->name, message.dnsHeader->questions);
209 
210  //Point to the corresponding question structure
211  dnsQuestion = DNS_GET_QUESTION(message.dnsHeader, message.length);
212 
213 #if (IPV4_SUPPORT == ENABLED)
214  //An IPv4 address is expected?
215  if(entry->type == HOST_TYPE_IPV4)
216  {
217  //Fill in question structure
218  dnsQuestion->qtype = HTONS(DNS_RR_TYPE_A);
219  dnsQuestion->qclass = HTONS(DNS_RR_CLASS_IN);
220  }
221 #endif
222 #if (IPV6_SUPPORT == ENABLED)
223  //An IPv6 address is expected?
224  if(entry->type == HOST_TYPE_IPV6)
225  {
226  //Fill in question structure
227  dnsQuestion->qtype = HTONS(DNS_RR_TYPE_AAAA);
228  dnsQuestion->qclass = HTONS(DNS_RR_CLASS_IN);
229  }
230 #endif
231 
232  //Update the length of the mDNS query message
233  message.length += sizeof(DnsQuestion);
234  //Number of questions in the Question Section
235  message.dnsHeader->qdcount = 1;
236 
237  //Send mDNS message
238  error = mdnsSendMessage(entry->interface, &message, NULL, MDNS_PORT);
239 
240  //Free previously allocated memory
242 
243  //Return status code
244  return error;
245 }
246 
247 
248 /**
249  * @brief Parse a resource record from the Answer Section
250  * @param[in] interface Underlying network interface
251  * @param[in] message Pointer to the mDNS message
252  * @param[in] offset Offset to first byte of the resource record
253  * @param[in] record Pointer to the resource record
254  **/
255 
257  const MdnsMessage *message, size_t offset, const DnsResourceRecord *record)
258 {
259  uint_t i;
260  uint16_t rclass;
261  DnsCacheEntry *entry;
262 
263  //Loop through DNS cache entries
264  for(i = 0; i < DNS_CACHE_SIZE; i++)
265  {
266  //Point to the current entry
267  entry = &dnsCache[i];
268 
269  //mDNS name resolution in progress?
270  if(entry->state == DNS_STATE_IN_PROGRESS &&
272  {
273  //Compare resource record name
274  if(!dnsCompareName(message->dnsHeader, message->length, offset, entry->name, 0))
275  {
276  //Convert the class to host byte order
277  rclass = ntohs(record->rclass);
278  //Discard Cache Flush flag
280 
281  //Check the class of the resource record
282  if(rclass == DNS_RR_CLASS_IN)
283  {
284 #if (IPV4_SUPPORT == ENABLED)
285  //IPv4 address expected?
286  if(entry->type == HOST_TYPE_IPV4)
287  {
288  //A resource record found?
289  if(ntohs(record->rtype) == DNS_RR_TYPE_A)
290  {
291  //Verify the length of the data field
292  if(ntohs(record->rdlength) == sizeof(Ipv4Addr))
293  {
294  //Copy the IPv4 address
295  entry->ipAddr.length = sizeof(Ipv4Addr);
296  ipv4CopyAddr(&entry->ipAddr.ipv4Addr, record->rdata);
297 
298  //Save current time
299  entry->timestamp = osGetSystemTime();
300  //Save TTL value
301  entry->timeout = ntohl(record->ttl) * 1000;
302  //Limit the lifetime of the mDNS cache entries
303  entry->timeout = MIN(entry->timeout, MDNS_MAX_LIFETIME);
304 
305  //Host name successfully resolved
306  entry->state = DNS_STATE_RESOLVED;
307  }
308  }
309  }
310 #endif
311 #if (IPV6_SUPPORT == ENABLED)
312  //IPv6 address expected?
313  if(entry->type == HOST_TYPE_IPV6)
314  {
315  //AAAA resource record found?
316  if(ntohs(record->rtype) == DNS_RR_TYPE_AAAA)
317  {
318  //Verify the length of the data field
319  if(ntohs(record->rdlength) == sizeof(Ipv6Addr))
320  {
321  //Copy the IPv6 address
322  entry->ipAddr.length = sizeof(Ipv6Addr);
323  ipv6CopyAddr(&entry->ipAddr.ipv6Addr, record->rdata);
324 
325  //Save current time
326  entry->timestamp = osGetSystemTime();
327  //Save TTL value
328  entry->timeout = ntohl(record->ttl) * 1000;
329  //Limit the lifetime of the mDNS cache entries
330  entry->timeout = MIN(entry->timeout, MDNS_MAX_LIFETIME);
331 
332  //Host name successfully resolved
333  entry->state = DNS_STATE_RESOLVED;
334  }
335  }
336  }
337 #endif
338  }
339  }
340  }
341  }
342 }
343 
344 #endif
#define DNS_GET_QUESTION(message, offset)
Definition: dns_common.h:61
uint32_t Ipv4Addr
IPv4 network address.
Definition: ipv4.h:232
__start_packed struct @151 DnsQuestion
Question format.
uint32_t systime_t
Definition: compiler_port.h:44
IPv6 address.
Definition: dns_common.h:131
DnsCacheEntry dnsCache[DNS_CACHE_SIZE]
Definition: dns_cache.c:49
mDNS responder (Multicast DNS)
#define ipv6CopyAddr(destIpAddr, srcIpAddr)
Definition: ipv6.h:115
char char_t
Definition: compiler_port.h:41
#define MDNS_PORT
Definition: mdns_common.h:51
HostnameResolver protocol
Name resolution protocol.
Definition: dns_cache.h:98
Ipv4Addr ipv4Addr
Definition: ip.h:63
systime_t osGetSystemTime(void)
Retrieve system time.
#define MDNS_RCLASS_CACHE_FLUSH
Definition: mdns_common.h:60
TCP/IP stack core.
Debugging facilities.
Internet.
Definition: dns_common.h:108
Generic error code.
Definition: error.h:43
uint8_t message[]
Definition: chap.h:150
IpAddr ipAddr
IP address.
Definition: dns_cache.h:104
mDNS message
Definition: mdns_common.h:75
HostType type
IPv4 or IPv6 host?
Definition: dns_cache.h:97
IP network address.
Definition: ip.h:57
__start_packed struct @183 Ipv6Addr
IPv6 network address.
systime_t timeout
Retransmission timeout.
Definition: dns_cache.h:106
char_t * ipAddrToString(const IpAddr *ipAddr, char_t *str)
Convert a binary IP address to a string representation.
Definition: ip.c:685
error_t mdnsSendMessage(NetInterface *interface, const MdnsMessage *message, const IpAddr *destIpAddr, uint_t destPort)
Send mDNS message.
Definition: mdns_common.c:452
char_t type
#define MDNS_CLIENT_INIT_TIMEOUT
Definition: mdns_client.h:55
#define HTONS(value)
Definition: cpu_endian.h:388
systime_t timestamp
Time stamp to manage entry lifetime.
Definition: dns_cache.h:105
uint8_t ipAddr[4]
Definition: mib_common.h:185
Data logging functions for debugging purpose (DNS)
#define ntohl(value)
Definition: cpu_endian.h:397
#define MDNS_CLIENT_MAX_RETRIES
Definition: mdns_client.h:48
char_t name[DNS_MAX_NAME_LEN+1]
Domain name.
Definition: dns_cache.h:103
DnsCacheEntry * dnsFindEntry(NetInterface *interface, const char_t *name, HostType type, HostnameResolver protocol)
Search the DNS cache for a given domain name.
Definition: dns_cache.c:181
#define ntohs(value)
Definition: cpu_endian.h:396
DNS cache entry.
Definition: dns_cache.h:94
uint16_t rclass
Definition: dns_common.h:202
HostType
Host types.
Definition: socket.h:168
char_t name[]
error_t mdnsClientSendQuery(DnsCacheEntry *entry)
Send a mDNS query message.
Definition: mdns_client.c:195
void osDelayTask(systime_t delay)
Delay routine.
int_t dnsCompareName(const DnsHeader *message, size_t length, size_t pos, const char_t *name, uint_t level)
Compare domain names.
Definition: dns_common.c:238
DnsCacheEntry * dnsCreateEntry(void)
Create a new entry in the DNS cache.
Definition: dns_cache.c:99
#define MIN(a, b)
Definition: os_port.h:60
error_t mdnsClientResolve(NetInterface *interface, const char_t *name, HostType type, IpAddr *ipAddr)
Resolve a host name using mDNS.
Definition: mdns_client.c:54
NetInterface * interface
Underlying network interface.
Definition: dns_cache.h:99
#define DNS_CACHE_MAX_POLLING_INTERVAL
Definition: dns_cache.h:66
#define DNS_CACHE_INIT_POLLING_INTERVAL
Definition: dns_cache.h:59
Helper functions for IPv6.
#define TRACE_INFO(...)
Definition: debug.h:86
IPv6 (Internet Protocol Version 6)
systime_t maxTimeout
Maximum retransmission timeout.
Definition: dns_cache.h:107
Success.
Definition: error.h:42
error_t
Error codes.
Definition: error.h:40
size_t dnsEncodeName(const char_t *src, uint8_t *dest)
Encode a domain name using the DNS name notation.
Definition: dns_common.c:54
size_t length
Definition: ip.h:59
unsigned int uint_t
Definition: compiler_port.h:43
void osReleaseMutex(OsMutex *mutex)
Release ownership of the specified mutex object.
Ipv6Addr ipv6Addr
Definition: ip.h:66
mDNS client (Multicast DNS)
#define NetInterface
Definition: net.h:34
void mdnsClientParseAnRecord(NetInterface *interface, const MdnsMessage *message, size_t offset, const DnsResourceRecord *record)
Parse a resource record from the Answer Section.
Definition: mdns_client.c:256
__start_packed struct @152 DnsResourceRecord
Resource record format.
#define MDNS_MAX_LIFETIME
Definition: mdns_client.h:69
Definitions common to mDNS client and mDNS responder.
OsMutex netMutex
Definition: net.c:70
DnsState state
Entry state.
Definition: dns_cache.h:96
error_t mdnsCreateMessage(MdnsMessage *message, bool_t queryResponse)
Create an empty mDNS message.
Definition: mdns_common.c:352
uint_t retransmitCount
Retransmission counter.
Definition: dns_cache.h:108
#define FALSE
Definition: os_port.h:44
#define MDNS_CLIENT_MAX_TIMEOUT
Definition: mdns_client.h:62
Host address.
Definition: dns_common.h:121
void mdnsDeleteMessage(MdnsMessage *message)
release a mDNS message
Definition: mdns_common.c:428
void osAcquireMutex(OsMutex *mutex)
Acquire ownership of the specified mutex object.
#define ipv4CopyAddr(destIpAddr, srcIpAddr)
Definition: ipv4.h:133
#define DNS_CACHE_SIZE
Definition: dns_cache.h:45