llmnr_responder.c
Go to the documentation of this file.
1 /**
2  * @file llmnr_responder.c
3  * @brief LLMNR responder (Link-Local Multicast Name Resolution)
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2019 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 1.9.4
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL LLMNR_TRACE_LEVEL
33 
34 //Dependencies
35 #include "core/net.h"
36 #include "llmnr/llmnr_responder.h"
37 #include "llmnr/llmnr_common.h"
38 #include "dns/dns_debug.h"
39 #include "debug.h"
40 
41 //Check TCP/IP stack configuration
42 #if (LLMNR_RESPONDER_SUPPORT == ENABLED)
43 
44 
45 /**
46  * @brief LLMNR responder initialization
47  * @param[in] interface Underlying network interface
48  * @return Error code
49  **/
50 
52 {
53  error_t error;
54 
55 #if (IPV4_SUPPORT == ENABLED)
56  //Join the LLMNR IPv4 multicast group
58  //Any error to report?
59  if(error)
60  return error;
61 #endif
62 
63 #if (IPV6_SUPPORT == ENABLED)
64  //Join the LLMNR IPv6 multicast group
66  //Any error to report?
67  if(error)
68  return error;
69 #endif
70 
71  //LLMNR responders must listen on UDP port 5355
73  NULL);
74  //Any error to report?
75  if(error)
76  return error;
77 
78  //Successful initialization
79  return NO_ERROR;
80 }
81 
82 
83 /**
84  * @brief Process LLMNR query message
85  * @param[in] interface Underlying network interface
86  * @param[in] pseudoHeader UDP pseudo header
87  * @param[in] udpHeader UDP header
88  * @param[in] buffer Multi-part buffer containing the incoming LLMNR message
89  * @param[in] offset Offset to the first byte of the LLMNR message
90  * @param[in] param Callback function parameter (not used)
91  **/
92 
93 void llmnrProcessQuery(NetInterface *interface, const IpPseudoHeader *pseudoHeader,
94  const UdpHeader *udpHeader, const NetBuffer *buffer, size_t offset, void *param)
95 {
96  size_t n;
97  size_t pos;
98  size_t length;
99  uint16_t destPort;
102  DnsQuestion *question;
103 
104  //Retrieve the length of the LLMNR message
105  length = netBufferGetLength(buffer) - offset;
106 
107  //Ensure the LLMNR message is valid
108  if(length < sizeof(LlmnrHeader))
109  return;
111  return;
112 
113  //Point to the LLMNR message header
114  message = netBufferAt(buffer, offset);
115  //Sanity check
116  if(message == NULL)
117  return;
118 
119  //Debug message
120  TRACE_INFO("LLMNR message received (%" PRIuSIZE " bytes)...\r\n", length);
121  //Dump message
123 
124 #if (IPV4_SUPPORT == ENABLED)
125  //IPv4 query received?
126  if(pseudoHeader->length == sizeof(Ipv4PseudoHeader))
127  {
128  //Unicast UDP queries must be silently discarded (refer to RFC 4795,
129  //section 2.4)
130  if(!ipv4IsMulticastAddr(pseudoHeader->ipv4Data.destAddr))
131  return;
132 
133  //A responder responds to a multicast query by sending a unicast UDP
134  //response to the sender (refer to RFC 4795, section 2)
135  destIpAddr.length = sizeof(Ipv4Addr);
136  destIpAddr.ipv4Addr = pseudoHeader->ipv4Data.srcAddr;
137  }
138  else
139 #endif
140 #if (IPV6_SUPPORT == ENABLED)
141  //IPv6 query received?
142  if(pseudoHeader->length == sizeof(Ipv6PseudoHeader))
143  {
144  //Unicast UDP queries must be silently discarded (refer to RFC 4795,
145  //section 2.4)
146  if(!ipv6IsMulticastAddr(&pseudoHeader->ipv6Data.destAddr))
147  return;
148 
149  //A responder responds to a multicast query by sending a unicast UDP
150  //response to the sender (refer to RFC 4795, section 2)
151  destIpAddr.length = sizeof(Ipv6Addr);
152  destIpAddr.ipv6Addr = pseudoHeader->ipv6Data.srcAddr;
153  }
154  else
155 #endif
156  //Invalid query received?
157  {
158  //Discard the LLMNR query message
159  return;
160  }
161 
162  //Discard LLMNR responses received on port 5355
163  if(message->qr)
164  return;
165 
166  //LLMNR messages received with an opcode other than zero must be silently
167  //ignored
168  if(message->opcode != DNS_OPCODE_QUERY)
169  return;
170 
171  //LLMNR messages received with non-zero response codes must be silently
172  //ignored
173  if(message->rcode != DNS_RCODE_NO_ERROR)
174  return;
175 
176  //LLMNR responders must silently discard LLMNR queries with QDCOUNT not
177  //equal to one (refer to RFC 4795, section 2.1.1)
178  if(ntohs(message->qdcount) != 1)
179  return;
180 
181  //LLMNR responders must silently discard LLMNR queries with ANCOUNT or
182  //NSCOUNT not equal to zero
183  if(ntohs(message->ancount) != 0 || ntohs(message->nscount) != 0)
184  return;
185 
186  //Point to the first question
187  pos = sizeof(LlmnrHeader);
188 
189  //Parse resource record name
190  n = dnsParseName((DnsHeader *) message, length, pos, NULL, 0);
191  //Invalid name?
192  if(n == 0)
193  return;
194 
195  //Malformed LLMNR message?
196  if((n + sizeof(DnsQuestion)) > length)
197  return;
198 
199  //Point to the corresponding entry
200  question = DNS_GET_QUESTION(message, n);
201 
202  //Check the class of the query
203  if(ntohs(question->qclass) == DNS_RR_CLASS_IN ||
204  ntohs(question->qclass) == DNS_RR_CLASS_ANY)
205  {
206  //Responders must respond to LLMNR queries for names and addresses for
207  //which they are authoritative
208  if(!dnsCompareName((DnsHeader *) message, length, pos,
209  interface->hostname, 0))
210  {
211  //Responders must direct responses to the port from which the query
212  //was sent
213  destPort = ntohs(udpHeader->srcPort);
214 
215  //Send LLMNR response
216  llmnrSendResponse(interface, &destIpAddr, destPort, message->id,
217  ntohs(question->qtype), ntohs(question->qclass));
218  }
219  }
220 }
221 
222 
223 /**
224  * @brief Send LLMNR response message
225  * @param[in] interface Underlying network interface
226  * @param[in] destIpAddr Destination IP address
227  * @param[in] destPort destination port
228  * @param[in] id 16-bit identifier to be used when sending LLMNR query
229  * @param[in] qtype Resource record type
230  * @param[in] qclass Resource record class
231  **/
232 
234  uint16_t destPort, uint16_t id, uint16_t qtype, uint16_t qclass)
235 {
236  error_t error;
237  size_t length;
238  size_t offset;
239  NetBuffer *buffer;
241  DnsQuestion *question;
242  DnsResourceRecord *record;
243 
244  //Initialize status code
245  error = NO_ERROR;
246 
247  //Allocate a memory buffer to hold the LLMNR response message
248  buffer = udpAllocBuffer(DNS_MESSAGE_MAX_SIZE, &offset);
249  //Failed to allocate buffer?
250  if(buffer == NULL)
251  return ERROR_OUT_OF_MEMORY;
252 
253  //Point to the LLMNR header
254  message = netBufferAt(buffer, offset);
255 
256  //Take the identifier from the query message
257  message->id = id;
258 
259  //Format LLMNR response header
260  message->qr = 1;
261  message->opcode = DNS_OPCODE_QUERY;
262  message->c = 0;
263  message->tc = 0;
264  message->t = 0;
265  message->z = 0;
266  message->rcode = DNS_RCODE_NO_ERROR;
267  message->qdcount = HTONS(1);
268  message->ancount = 0;
269  message->nscount = 0;
270  message->arcount = 0;
271 
272  //Set the length of the LLMNR response message
273  length = sizeof(DnsHeader);
274 
275  //Encode the requested host name using the DNS name notation
276  length += dnsEncodeName(interface->hostname,
277  (uint8_t *) message + length);
278 
279  //Point to the corresponding entry
280  question = DNS_GET_QUESTION(message, length);
281 
282  //Fill in resource record
283  question->qtype = htons(qtype);
284  question->qclass = htons(qclass);
285 
286  //Update the length of the response message
287  length += sizeof(DnsQuestion);
288 
289 #if (IPV4_SUPPORT == ENABLED)
290  //A resource record requested?
292  {
293  //Valid IPv4 host address?
294  if(interface->ipv4Context.addrList[0].state == IPV4_ADDR_STATE_VALID)
295  {
296  //Encode the host name using the DNS name notation
297  length += dnsEncodeName(interface->hostname,
298  (uint8_t *) message + length);
299 
300  //Point to the corresponding resource record
302 
303  //Fill in resource record
304  record->rtype = HTONS(DNS_RR_TYPE_A);
305  record->rclass = HTONS(DNS_RR_CLASS_IN);
307  record->rdlength = HTONS(sizeof(Ipv4Addr));
308 
309  //Copy IPv4 address
310  ipv4CopyAddr(record->rdata, &interface->ipv4Context.addrList[0].addr);
311 
312  //Number of resource records in the answer section
313  message->ancount++;
314 
315  //Update the length of the response message
316  length += sizeof(DnsResourceRecord) + sizeof(Ipv4Addr);
317  }
318  }
319 #endif
320 
321 #if (IPV6_SUPPORT == ENABLED)
322  //AAAA resource record requested?
324  {
325  //Valid IPv6 link-local address?
327  {
328  //Encode the host name using the DNS name notation
329  length += dnsEncodeName(interface->hostname,
330  (uint8_t *) message + length);
331 
332  //Point to the corresponding resource record
334 
335  //Fill in resource record
336  record->rtype = HTONS(DNS_RR_TYPE_AAAA);
337  record->rclass = HTONS(DNS_RR_CLASS_IN);
339  record->rdlength = HTONS(sizeof(Ipv6Addr));
340 
341  //Copy IPv6 address
342  ipv6CopyAddr(record->rdata, &interface->ipv6Context.addrList[0].addr);
343 
344  //Number of resource records in the answer section
345  message->ancount++;
346 
347  //Update the length of the response message
348  length += sizeof(DnsResourceRecord) + sizeof(Ipv6Addr);
349  }
350  }
351 #endif
352 
353  //Valid LLMNR response?
354  if(message->ancount > 0)
355  {
356  //The ANCOUNT field specifies the number of resource records in the
357  //answer section
358  message->ancount = htons(message->ancount);
359 
360  //Adjust the length of the multi-part buffer
361  netBufferSetLength(buffer, offset + length);
362 
363  //Debug message
364  TRACE_INFO("Sending LLMNR message (%" PRIuSIZE " bytes)...\r\n", length);
365  //Dump message
367 
368  //For UDP responses, the Hop Limit field in the IPv6 header and the TTL
369  //field in the IPV4 header MAY be set to any value. However, it is
370  //recommended that the value 255 be used for compatibility with early
371  //implementations (refer to RFC 4795, section 2.5)
372  error = udpSendDatagramEx(interface, NULL, LLMNR_PORT, destIpAddr,
374  }
375 
376  //Free previously allocated memory
377  netBufferFree(buffer);
378 
379  //Return status code
380  return error;
381 }
382 
383 #endif
An address assigned to an interface whose use is unrestricted.
Definition: ipv4.h:187
#define DNS_GET_QUESTION(message, offset)
Definition: dns_common.h:63
uint32_t Ipv4Addr
IPv4 network address.
Definition: ipv4.h:235
#define DNS_GET_RESOURCE_RECORD(message, offset)
Definition: dns_common.h:64
__start_packed struct @147 DnsQuestion
Question format.
IPv6 address.
Definition: dns_common.h:133
uint16_t destPort
Definition: tcp.h:303
#define ipv6CopyAddr(destIpAddr, srcIpAddr)
Definition: ipv6.h:117
__start_packed struct @146 DnsHeader
DNS message header.
#define ipv6IsMulticastAddr(ipAddr)
Definition: ipv6.h:133
#define ipv6GetLinkLocalAddrState(interface)
Definition: ipv6.h:141
TCP/IP stack core.
error_t llmnrSendResponse(NetInterface *interface, const IpAddr *destIpAddr, uint16_t destPort, uint16_t id, uint16_t qtype, uint16_t qclass)
Send LLMNR response message.
void netBufferFree(NetBuffer *buffer)
Dispose a multi-part buffer.
Definition: net_mem.c:282
Debugging facilities.
Internet.
Definition: dns_common.h:110
size_t netBufferGetLength(const NetBuffer *buffer)
Get the actual length of a multi-part buffer.
Definition: net_mem.c:297
uint16_t qtype
Definition: dns_common.h:192
uint8_t message[]
Definition: chap.h:152
error_t netBufferSetLength(NetBuffer *buffer, size_t length)
Adjust the length of a multi-part buffer.
Definition: net_mem.c:320
IP network address.
Definition: ip.h:71
#define htons(value)
Definition: cpu_endian.h:392
#define HTONL(value)
Definition: cpu_endian.h:391
__start_packed struct @122 UdpHeader
UDP header.
#define Ipv4PseudoHeader
Definition: ipv4.h:39
#define LLMNR_DEFAULT_IP_TTL
Definition: llmnr_common.h:48
#define HTONS(value)
Definition: cpu_endian.h:390
error_t udpSendDatagramEx(NetInterface *interface, const IpAddr *srcIpAddr, uint16_t srcPort, const IpAddr *destIpAddr, uint16_t destPort, NetBuffer *buffer, size_t offset, uint_t flags)
Send a UDP datagram (raw interface)
Definition: udp.c:457
uint16_t qclass
Definition: dns_common.h:193
Data logging functions for debugging purpose (DNS)
#define LLMNR_PORT
Definition: llmnr_common.h:46
#define ntohs(value)
Definition: cpu_endian.h:398
size_t length
Definition: ip.h:92
A request for all records.
Definition: dns_common.h:141
error_t ipv4JoinMulticastGroup(NetInterface *interface, Ipv4Addr groupAddr)
Join the specified host group.
Definition: ipv4.c:1194
void * netBufferAt(const NetBuffer *buffer, size_t offset)
Returns a pointer to the data at the specified position.
Definition: net_mem.c:413
error_t udpAttachRxCallback(NetInterface *interface, uint16_t port, UdpRxCallback callback, void *param)
Register user callback.
Definition: udp.c:758
#define LLMNR_DEFAULT_RESOURCE_RECORD_TTL
Definition: llmnr_common.h:40
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:240
#define Ipv6PseudoHeader
Definition: ipv6.h:42
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:88
error_t llmnrResponderInit(NetInterface *interface)
LLMNR responder initialization.
LLMNR responder (Link-Local Multicast Name Resolution)
#define LLMNR_IPV4_MULTICAST_ADDR
Definition: llmnr_common.h:51
uint16_t id
Definition: dns_common.h:158
__start_packed struct @204 LlmnrHeader
LLMNR message header.
An address assigned to an interface whose use is unrestricted.
Definition: ipv6.h:173
void dnsDumpMessage(const DnsHeader *message, size_t length)
Dump DNS message for debugging purpose.
Definition: dns_debug.c:52
NetBuffer * udpAllocBuffer(size_t length, size_t *offset)
Allocate a buffer to hold a UDP packet.
Definition: udp.c:691
#define TRACE_INFO(...)
Definition: debug.h:94
size_t dnsParseName(const DnsHeader *message, size_t length, size_t pos, char_t *dest, uint_t level)
Decode a domain name that uses the DNS name encoding.
Definition: dns_common.c:130
Success.
Definition: error.h:44
#define DNS_MESSAGE_MAX_SIZE
Definition: dns_common.h:45
__start_packed struct @148 DnsResourceRecord
Resource record format.
__start_packed struct @179 Ipv6Addr
IPv6 network address.
error_t
Error codes.
Definition: error.h:42
Ipv4Addr destIpAddr
Definition: ipcp.h:78
size_t dnsEncodeName(const char_t *src, uint8_t *dest)
Encode a domain name using the DNS name notation.
Definition: dns_common.c:56
Any class.
Definition: dns_common.h:113
#define PRIuSIZE
Definition: compiler_port.h:78
#define NetInterface
Definition: net.h:36
#define ipv4IsMulticastAddr(ipAddr)
Definition: ipv4.h:158
IP pseudo header.
Definition: ip.h:90
Definitions common to LLMNR client and responder.
Ipv6PseudoHeader ipv6Data
Definition: ip.h:99
const Ipv6Addr LLMNR_IPV6_MULTICAST_ADDR
Definition: llmnr_common.c:45
Ipv4PseudoHeader ipv4Data
Definition: ip.h:96
void llmnrProcessQuery(NetInterface *interface, const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader, const NetBuffer *buffer, size_t offset, void *param)
Process LLMNR query message.
uint8_t length
Definition: dtls_misc.h:142
uint8_t n
error_t ipv6JoinMulticastGroup(NetInterface *interface, const Ipv6Addr *groupAddr)
Join an IPv6 multicast group.
Definition: ipv6.c:1928
Host address.
Definition: dns_common.h:123
#define ipv4CopyAddr(destIpAddr, srcIpAddr)
Definition: ipv4.h:142