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.2
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 Process LLMNR query message
47  * @param[in] interface Underlying network interface
48  * @param[in] pseudoHeader UDP pseudo header
49  * @param[in] udpHeader UDP header
50  * @param[in] message Pointer to the LLMNR query message
51  * @param[in] length Length of the message
52  **/
53 
54 void llmnrProcessQuery(NetInterface *interface, const IpPseudoHeader *pseudoHeader,
55  const UdpHeader *udpHeader, const LlmnrHeader *message, size_t length)
56 {
57  size_t n;
58  size_t offset;
59  uint16_t destPort;
61  DnsQuestion *question;
62 
63 #if (IPV4_SUPPORT == ENABLED)
64  //IPv4 query received?
65  if(pseudoHeader->length == sizeof(Ipv4PseudoHeader))
66  {
67  //Unicast UDP queries must be silently discarded (refer to RFC 4795,
68  //section 2.4)
69  if(!ipv4IsMulticastAddr(pseudoHeader->ipv4Data.destAddr))
70  return;
71 
72  //A responder responds to a multicast query by sending a unicast UDP
73  //response to the sender (refer to RFC 4795, section 2)
74  destIpAddr.length = sizeof(Ipv4Addr);
75  destIpAddr.ipv4Addr = pseudoHeader->ipv4Data.srcAddr;
76  }
77  else
78 #endif
79 #if (IPV6_SUPPORT == ENABLED)
80  //IPv6 query received?
81  if(pseudoHeader->length == sizeof(Ipv6PseudoHeader))
82  {
83  //Unicast UDP queries must be silently discarded (refer to RFC 4795,
84  //section 2.4)
85  if(!ipv6IsMulticastAddr(&pseudoHeader->ipv6Data.destAddr))
86  return;
87 
88  //A responder responds to a multicast query by sending a unicast UDP
89  //response to the sender (refer to RFC 4795, section 2)
90  destIpAddr.length = sizeof(Ipv6Addr);
91  destIpAddr.ipv6Addr = pseudoHeader->ipv6Data.srcAddr;
92  }
93  else
94 #endif
95  //Invalid query received?
96  {
97  //Discard the LLMNR query message
98  return;
99  }
100 
101  //LLMNR responders must silently discard LLMNR queries with QDCOUNT not
102  //equal to one (refer to RFC 4795, section 2.1.1)
103  if(ntohs(message->qdcount) != 1)
104  return;
105 
106  //LLMNR responders must silently discard LLMNR queries with ANCOUNT or
107  //NSCOUNT not equal to zero
108  if(ntohs(message->ancount) != 0 || ntohs(message->nscount) != 0)
109  return;
110 
111  //Point to the first question
112  offset = sizeof(LlmnrHeader);
113 
114  //Parse resource record name
115  n = dnsParseName((DnsHeader *) message, length, offset, NULL, 0);
116  //Invalid name?
117  if(n == 0)
118  return;
119 
120  //Malformed LLMNR message?
121  if((n + sizeof(DnsQuestion)) > length)
122  return;
123 
124  //Point to the corresponding entry
125  question = DNS_GET_QUESTION(message, n);
126 
127  //Check the class of the query
128  if(ntohs(question->qclass) == DNS_RR_CLASS_IN ||
129  ntohs(question->qclass) == DNS_RR_CLASS_ANY)
130  {
131  //Responders must respond to LLMNR queries for names and addresses for
132  //which they are authoritative
133  if(!dnsCompareName((DnsHeader *) message, length, offset,
134  interface->hostname, 0))
135  {
136  //Responders must direct responses to the port from which the query
137  //was sent
138  destPort = ntohs(udpHeader->srcPort);
139 
140  //Send LLMNR response
141  llmnrSendResponse(interface, &destIpAddr, destPort, message->id,
142  ntohs(question->qtype), ntohs(question->qclass));
143  }
144  }
145 }
146 
147 
148 /**
149  * @brief Send LLMNR response message
150  * @param[in] interface Underlying network interface
151  * @param[in] destIpAddr Destination IP address
152  * @param[in] destPort destination port
153  * @param[in] id 16-bit identifier to be used when sending LLMNR query
154  * @param[in] qtype Resource record type
155  * @param[in] qclass Resource record class
156  **/
157 
159  uint16_t destPort, uint16_t id, uint16_t qtype, uint16_t qclass)
160 {
161  error_t error;
162  size_t length;
163  size_t offset;
164  NetBuffer *buffer;
166  DnsQuestion *question;
167  DnsResourceRecord *record;
168 
169  //Initialize status code
170  error = NO_ERROR;
171 
172  //Allocate a memory buffer to hold the LLMNR response message
173  buffer = udpAllocBuffer(DNS_MESSAGE_MAX_SIZE, &offset);
174  //Failed to allocate buffer?
175  if(buffer == NULL)
176  return ERROR_OUT_OF_MEMORY;
177 
178  //Point to the LLMNR header
179  message = netBufferAt(buffer, offset);
180 
181  //Take the identifier from the query message
182  message->id = id;
183 
184  //Format LLMNR response header
185  message->qr = 1;
186  message->opcode = DNS_OPCODE_QUERY;
187  message->c = 0;
188  message->tc = 0;
189  message->t = 0;
190  message->z = 0;
191  message->rcode = DNS_RCODE_NO_ERROR;
192  message->qdcount = HTONS(1);
193  message->ancount = 0;
194  message->nscount = 0;
195  message->arcount = 0;
196 
197  //Set the length of the LLMNR response message
198  length = sizeof(DnsHeader);
199 
200  //Encode the requested host name using the DNS name notation
201  length += dnsEncodeName(interface->hostname,
202  (uint8_t *) message + length);
203 
204  //Point to the corresponding entry
205  question = DNS_GET_QUESTION(message, length);
206 
207  //Fill in resource record
208  question->qtype = htons(qtype);
209  question->qclass = htons(qclass);
210 
211  //Update the length of the response message
212  length += sizeof(DnsQuestion);
213 
214 #if (IPV4_SUPPORT == ENABLED)
215  //A resource record requested?
217  {
218  //Valid IPv4 host address?
219  if(interface->ipv4Context.addrList[0].state == IPV4_ADDR_STATE_VALID)
220  {
221  //Encode the host name using the DNS name notation
222  length += dnsEncodeName(interface->hostname,
223  (uint8_t *) message + length);
224 
225  //Point to the corresponding resource record
227 
228  //Fill in resource record
229  record->rtype = HTONS(DNS_RR_TYPE_A);
230  record->rclass = HTONS(DNS_RR_CLASS_IN);
232  record->rdlength = HTONS(sizeof(Ipv4Addr));
233 
234  //Copy IPv4 address
235  ipv4CopyAddr(record->rdata, &interface->ipv4Context.addrList[0].addr);
236 
237  //Number of resource records in the answer section
238  message->ancount++;
239 
240  //Update the length of the response message
241  length += sizeof(DnsResourceRecord) + sizeof(Ipv4Addr);
242  }
243  }
244 #endif
245 
246 #if (IPV6_SUPPORT == ENABLED)
247  //AAAA resource record requested?
249  {
250  //Valid IPv6 link-local address?
252  {
253  //Encode the host name using the DNS name notation
254  length += dnsEncodeName(interface->hostname,
255  (uint8_t *) message + length);
256 
257  //Point to the corresponding resource record
259 
260  //Fill in resource record
261  record->rtype = HTONS(DNS_RR_TYPE_AAAA);
262  record->rclass = HTONS(DNS_RR_CLASS_IN);
264  record->rdlength = HTONS(sizeof(Ipv6Addr));
265 
266  //Copy IPv6 address
267  ipv6CopyAddr(record->rdata, &interface->ipv6Context.addrList[0].addr);
268 
269  //Number of resource records in the answer section
270  message->ancount++;
271 
272  //Update the length of the response message
273  length += sizeof(DnsResourceRecord) + sizeof(Ipv6Addr);
274  }
275  }
276 #endif
277 
278  //Valid LLMNR response?
279  if(message->ancount > 0)
280  {
281  //The ANCOUNT field specifies the number of resource records in the
282  //answer section
283  message->ancount = htons(message->ancount);
284 
285  //Adjust the length of the multi-part buffer
286  netBufferSetLength(buffer, offset + length);
287 
288  //Debug message
289  TRACE_INFO("Sending LLMNR message (%" PRIuSIZE " bytes)...\r\n", length);
290  //Dump message
292 
293  //For UDP responses, the Hop Limit field in the IPv6 header and the TTL
294  //field in the IPV4 header MAY be set to any value. However, it is
295  //recommended that the value 255 be used for compatibility with early
296  //implementations (refer to RFC 4795, section 2.5)
297  error = udpSendDatagramEx(interface, NULL, LLMNR_PORT, destIpAddr,
298  destPort, buffer, offset, LLMNR_DEFAULT_IP_TTL);
299  }
300 
301  //Free previously allocated memory
302  netBufferFree(buffer);
303 
304  //Return status code
305  return error;
306 }
307 
308 #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
__start_packed struct @151 DnsQuestion
Question format.
#define DNS_GET_RESOURCE_RECORD(message, offset)
Definition: dns_common.h:64
IPv6 address.
Definition: dns_common.h:133
uint16_t destPort
Definition: tcp.h:303
#define ipv6CopyAddr(destIpAddr, srcIpAddr)
Definition: ipv6.h:117
#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
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:59
__start_packed struct @183 Ipv6Addr
IPv6 network address.
#define htons(value)
Definition: cpu_endian.h:392
#define HTONL(value)
Definition: cpu_endian.h:391
#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, uint8_t ttl)
Send a UDP datagram (raw interface)
Definition: udp.c:452
uint16_t qclass
Definition: dns_common.h:193
Data logging functions for debugging purpose (DNS)
void llmnrProcessQuery(NetInterface *interface, const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader, const LlmnrHeader *message, size_t length)
Process LLMNR query message.
#define LLMNR_PORT
Definition: llmnr_common.h:46
__start_packed struct @150 DnsHeader
DNS message header.
#define ntohs(value)
Definition: cpu_endian.h:398
size_t length
Definition: ip.h:80
A request for all records.
Definition: dns_common.h:141
void * netBufferAt(const NetBuffer *buffer, size_t offset)
Returns a pointer to the data at the specified position.
Definition: net_mem.c:413
__start_packed struct @126 UdpHeader
UDP header.
#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
LLMNR responder (Link-Local Multicast Name Resolution)
uint16_t id
Definition: dns_common.h:158
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:684
#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
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
__start_packed struct @208 LlmnrHeader
LLMNR message header.
#define ipv4IsMulticastAddr(ipAddr)
Definition: ipv4.h:158
IP pseudo header.
Definition: ip.h:78
Definitions common to LLMNR client and responder.
__start_packed struct @152 DnsResourceRecord
Resource record format.
Ipv6PseudoHeader ipv6Data
Definition: ip.h:87
Ipv4PseudoHeader ipv4Data
Definition: ip.h:84
uint8_t length
Definition: dtls_misc.h:142
uint8_t n
Host address.
Definition: dns_common.h:123
#define ipv4CopyAddr(destIpAddr, srcIpAddr)
Definition: ipv4.h:142