1*10465441SEvalZero /**
2*10465441SEvalZero * @file
3*10465441SEvalZero * MDNS responder implementation
4*10465441SEvalZero *
5*10465441SEvalZero * @defgroup mdns MDNS
6*10465441SEvalZero * @ingroup apps
7*10465441SEvalZero *
8*10465441SEvalZero * RFC 6762 - Multicast DNS\n
9*10465441SEvalZero * RFC 6763 - DNS-Based Service Discovery\n
10*10465441SEvalZero *
11*10465441SEvalZero * @verbinclude mdns.txt
12*10465441SEvalZero *
13*10465441SEvalZero * Things left to implement:
14*10465441SEvalZero * -------------------------
15*10465441SEvalZero *
16*10465441SEvalZero * - Tiebreaking for simultaneous probing
17*10465441SEvalZero * - Sending goodbye messages (zero ttl) - shutdown, DHCP lease about to expire, DHCP turned off...
18*10465441SEvalZero * - Checking that source address of unicast requests are on the same network
19*10465441SEvalZero * - Limiting multicast responses to 1 per second per resource record
20*10465441SEvalZero * - Fragmenting replies if required
21*10465441SEvalZero * - Handling multi-packet known answers
22*10465441SEvalZero * - Individual known answer detection for all local IPv6 addresses
23*10465441SEvalZero * - Dynamic size of outgoing packet
24*10465441SEvalZero */
25*10465441SEvalZero
26*10465441SEvalZero /*
27*10465441SEvalZero * Copyright (c) 2015 Verisure Innovation AB
28*10465441SEvalZero * All rights reserved.
29*10465441SEvalZero *
30*10465441SEvalZero * Redistribution and use in source and binary forms, with or without modification,
31*10465441SEvalZero * are permitted provided that the following conditions are met:
32*10465441SEvalZero *
33*10465441SEvalZero * 1. Redistributions of source code must retain the above copyright notice,
34*10465441SEvalZero * this list of conditions and the following disclaimer.
35*10465441SEvalZero * 2. Redistributions in binary form must reproduce the above copyright notice,
36*10465441SEvalZero * this list of conditions and the following disclaimer in the documentation
37*10465441SEvalZero * and/or other materials provided with the distribution.
38*10465441SEvalZero * 3. The name of the author may not be used to endorse or promote products
39*10465441SEvalZero * derived from this software without specific prior written permission.
40*10465441SEvalZero *
41*10465441SEvalZero * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
42*10465441SEvalZero * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
43*10465441SEvalZero * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
44*10465441SEvalZero * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
45*10465441SEvalZero * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
46*10465441SEvalZero * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
47*10465441SEvalZero * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
48*10465441SEvalZero * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
49*10465441SEvalZero * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
50*10465441SEvalZero * OF SUCH DAMAGE.
51*10465441SEvalZero *
52*10465441SEvalZero * This file is part of the lwIP TCP/IP stack.
53*10465441SEvalZero *
54*10465441SEvalZero * Author: Erik Ekman <[email protected]>
55*10465441SEvalZero *
56*10465441SEvalZero */
57*10465441SEvalZero
58*10465441SEvalZero #include "lwip/apps/mdns.h"
59*10465441SEvalZero #include "lwip/apps/mdns_priv.h"
60*10465441SEvalZero #include "lwip/netif.h"
61*10465441SEvalZero #include "lwip/udp.h"
62*10465441SEvalZero #include "lwip/ip_addr.h"
63*10465441SEvalZero #include "lwip/mem.h"
64*10465441SEvalZero #include "lwip/prot/dns.h"
65*10465441SEvalZero #include "lwip/prot/iana.h"
66*10465441SEvalZero #include "lwip/timeouts.h"
67*10465441SEvalZero
68*10465441SEvalZero #include <string.h>
69*10465441SEvalZero
70*10465441SEvalZero #if LWIP_MDNS_RESPONDER
71*10465441SEvalZero
72*10465441SEvalZero #if (LWIP_IPV4 && !LWIP_IGMP)
73*10465441SEvalZero #error "If you want to use MDNS with IPv4, you have to define LWIP_IGMP=1 in your lwipopts.h"
74*10465441SEvalZero #endif
75*10465441SEvalZero #if (LWIP_IPV6 && !LWIP_IPV6_MLD)
76*10465441SEvalZero #error "If you want to use MDNS with IPv6, you have to define LWIP_IPV6_MLD=1 in your lwipopts.h"
77*10465441SEvalZero #endif
78*10465441SEvalZero #if (!LWIP_UDP)
79*10465441SEvalZero #error "If you want to use MDNS, you have to define LWIP_UDP=1 in your lwipopts.h"
80*10465441SEvalZero #endif
81*10465441SEvalZero
82*10465441SEvalZero #if LWIP_IPV4
83*10465441SEvalZero #include "lwip/igmp.h"
84*10465441SEvalZero /* IPv4 multicast group 224.0.0.251 */
85*10465441SEvalZero static const ip_addr_t v4group = DNS_MQUERY_IPV4_GROUP_INIT;
86*10465441SEvalZero #endif
87*10465441SEvalZero
88*10465441SEvalZero #if LWIP_IPV6
89*10465441SEvalZero #include "lwip/mld6.h"
90*10465441SEvalZero /* IPv6 multicast group FF02::FB */
91*10465441SEvalZero static const ip_addr_t v6group = DNS_MQUERY_IPV6_GROUP_INIT;
92*10465441SEvalZero #endif
93*10465441SEvalZero
94*10465441SEvalZero #define MDNS_TTL 255
95*10465441SEvalZero
96*10465441SEvalZero /* Stored offsets to beginning of domain names
97*10465441SEvalZero * Used for compression.
98*10465441SEvalZero */
99*10465441SEvalZero #define NUM_DOMAIN_OFFSETS 10
100*10465441SEvalZero #define DOMAIN_JUMP_SIZE 2
101*10465441SEvalZero #define DOMAIN_JUMP 0xc000
102*10465441SEvalZero
103*10465441SEvalZero static u8_t mdns_netif_client_id;
104*10465441SEvalZero static struct udp_pcb *mdns_pcb;
105*10465441SEvalZero #if MDNS_RESP_USENETIF_EXTCALLBACK
NETIF_DECLARE_EXT_CALLBACK(netif_callback)106*10465441SEvalZero NETIF_DECLARE_EXT_CALLBACK(netif_callback)
107*10465441SEvalZero #endif
108*10465441SEvalZero static mdns_name_result_cb_t mdns_name_result_cb;
109*10465441SEvalZero
110*10465441SEvalZero #define NETIF_TO_HOST(netif) (struct mdns_host*)(netif_get_client_data(netif, mdns_netif_client_id))
111*10465441SEvalZero
112*10465441SEvalZero #define TOPDOMAIN_LOCAL "local"
113*10465441SEvalZero
114*10465441SEvalZero #define REVERSE_PTR_TOPDOMAIN "arpa"
115*10465441SEvalZero #define REVERSE_PTR_V4_DOMAIN "in-addr"
116*10465441SEvalZero #define REVERSE_PTR_V6_DOMAIN "ip6"
117*10465441SEvalZero
118*10465441SEvalZero #define SRV_PRIORITY 0
119*10465441SEvalZero #define SRV_WEIGHT 0
120*10465441SEvalZero
121*10465441SEvalZero /* Payload size allocated for each outgoing UDP packet */
122*10465441SEvalZero #define OUTPACKET_SIZE 500
123*10465441SEvalZero
124*10465441SEvalZero /* Lookup from hostname -> IPv4 */
125*10465441SEvalZero #define REPLY_HOST_A 0x01
126*10465441SEvalZero /* Lookup from IPv4/v6 -> hostname */
127*10465441SEvalZero #define REPLY_HOST_PTR_V4 0x02
128*10465441SEvalZero /* Lookup from hostname -> IPv6 */
129*10465441SEvalZero #define REPLY_HOST_AAAA 0x04
130*10465441SEvalZero /* Lookup from hostname -> IPv6 */
131*10465441SEvalZero #define REPLY_HOST_PTR_V6 0x08
132*10465441SEvalZero
133*10465441SEvalZero /* Lookup for service types */
134*10465441SEvalZero #define REPLY_SERVICE_TYPE_PTR 0x10
135*10465441SEvalZero /* Lookup for instances of service */
136*10465441SEvalZero #define REPLY_SERVICE_NAME_PTR 0x20
137*10465441SEvalZero /* Lookup for location of service instance */
138*10465441SEvalZero #define REPLY_SERVICE_SRV 0x40
139*10465441SEvalZero /* Lookup for text info on service instance */
140*10465441SEvalZero #define REPLY_SERVICE_TXT 0x80
141*10465441SEvalZero
142*10465441SEvalZero #define MDNS_PROBE_DELAY_MS 250
143*10465441SEvalZero #define MDNS_PROBE_COUNT 3
144*10465441SEvalZero #ifdef LWIP_RAND
145*10465441SEvalZero /* first probe timeout SHOULD be random 0-250 ms*/
146*10465441SEvalZero #define MDNS_INITIAL_PROBE_DELAY_MS (LWIP_RAND() % MDNS_PROBE_DELAY_MS)
147*10465441SEvalZero #else
148*10465441SEvalZero #define MDNS_INITIAL_PROBE_DELAY_MS MDNS_PROBE_DELAY_MS
149*10465441SEvalZero #endif
150*10465441SEvalZero
151*10465441SEvalZero #define MDNS_PROBING_NOT_STARTED 0
152*10465441SEvalZero #define MDNS_PROBING_ONGOING 1
153*10465441SEvalZero #define MDNS_PROBING_COMPLETE 2
154*10465441SEvalZero
155*10465441SEvalZero static const char *dnssd_protos[] = {
156*10465441SEvalZero "_udp", /* DNSSD_PROTO_UDP */
157*10465441SEvalZero "_tcp", /* DNSSD_PROTO_TCP */
158*10465441SEvalZero };
159*10465441SEvalZero
160*10465441SEvalZero /** Description of a service */
161*10465441SEvalZero struct mdns_service {
162*10465441SEvalZero /** TXT record to answer with */
163*10465441SEvalZero struct mdns_domain txtdata;
164*10465441SEvalZero /** Name of service, like 'myweb' */
165*10465441SEvalZero char name[MDNS_LABEL_MAXLEN + 1];
166*10465441SEvalZero /** Type of service, like '_http' */
167*10465441SEvalZero char service[MDNS_LABEL_MAXLEN + 1];
168*10465441SEvalZero /** Callback function and userdata
169*10465441SEvalZero * to update txtdata buffer */
170*10465441SEvalZero service_get_txt_fn_t txt_fn;
171*10465441SEvalZero void *txt_userdata;
172*10465441SEvalZero /** TTL in seconds of SRV/TXT replies */
173*10465441SEvalZero u32_t dns_ttl;
174*10465441SEvalZero /** Protocol, TCP or UDP */
175*10465441SEvalZero u16_t proto;
176*10465441SEvalZero /** Port of the service */
177*10465441SEvalZero u16_t port;
178*10465441SEvalZero };
179*10465441SEvalZero
180*10465441SEvalZero /** Description of a host/netif */
181*10465441SEvalZero struct mdns_host {
182*10465441SEvalZero /** Hostname */
183*10465441SEvalZero char name[MDNS_LABEL_MAXLEN + 1];
184*10465441SEvalZero /** Pointer to services */
185*10465441SEvalZero struct mdns_service *services[MDNS_MAX_SERVICES];
186*10465441SEvalZero /** TTL in seconds of A/AAAA/PTR replies */
187*10465441SEvalZero u32_t dns_ttl;
188*10465441SEvalZero /** Number of probes sent for the current name */
189*10465441SEvalZero u8_t probes_sent;
190*10465441SEvalZero /** State in probing sequence */
191*10465441SEvalZero u8_t probing_state;
192*10465441SEvalZero };
193*10465441SEvalZero
194*10465441SEvalZero /** Information about received packet */
195*10465441SEvalZero struct mdns_packet {
196*10465441SEvalZero /** Sender IP/port */
197*10465441SEvalZero ip_addr_t source_addr;
198*10465441SEvalZero u16_t source_port;
199*10465441SEvalZero /** If packet was received unicast */
200*10465441SEvalZero u16_t recv_unicast;
201*10465441SEvalZero /** Netif that received the packet */
202*10465441SEvalZero struct netif *netif;
203*10465441SEvalZero /** Packet data */
204*10465441SEvalZero struct pbuf *pbuf;
205*10465441SEvalZero /** Current parsing offset in packet */
206*10465441SEvalZero u16_t parse_offset;
207*10465441SEvalZero /** Identifier. Used in legacy queries */
208*10465441SEvalZero u16_t tx_id;
209*10465441SEvalZero /** Number of questions in packet,
210*10465441SEvalZero * read from packet header */
211*10465441SEvalZero u16_t questions;
212*10465441SEvalZero /** Number of unparsed questions */
213*10465441SEvalZero u16_t questions_left;
214*10465441SEvalZero /** Number of answers in packet,
215*10465441SEvalZero * (sum of normal, authoritative and additional answers)
216*10465441SEvalZero * read from packet header */
217*10465441SEvalZero u16_t answers;
218*10465441SEvalZero /** Number of unparsed answers */
219*10465441SEvalZero u16_t answers_left;
220*10465441SEvalZero };
221*10465441SEvalZero
222*10465441SEvalZero /** Information about outgoing packet */
223*10465441SEvalZero struct mdns_outpacket {
224*10465441SEvalZero /** Netif to send the packet on */
225*10465441SEvalZero struct netif *netif;
226*10465441SEvalZero /** Packet data */
227*10465441SEvalZero struct pbuf *pbuf;
228*10465441SEvalZero /** Current write offset in packet */
229*10465441SEvalZero u16_t write_offset;
230*10465441SEvalZero /** Identifier. Used in legacy queries */
231*10465441SEvalZero u16_t tx_id;
232*10465441SEvalZero /** Destination IP/port if sent unicast */
233*10465441SEvalZero ip_addr_t dest_addr;
234*10465441SEvalZero u16_t dest_port;
235*10465441SEvalZero /** Number of questions written */
236*10465441SEvalZero u16_t questions;
237*10465441SEvalZero /** Number of normal answers written */
238*10465441SEvalZero u16_t answers;
239*10465441SEvalZero /** Number of authoritative answers written */
240*10465441SEvalZero u16_t authoritative;
241*10465441SEvalZero /** Number of additional answers written */
242*10465441SEvalZero u16_t additional;
243*10465441SEvalZero /** Offsets for written domain names in packet.
244*10465441SEvalZero * Used for compression */
245*10465441SEvalZero u16_t domain_offsets[NUM_DOMAIN_OFFSETS];
246*10465441SEvalZero /** If all answers in packet should set cache_flush bit */
247*10465441SEvalZero u8_t cache_flush;
248*10465441SEvalZero /** If reply should be sent unicast */
249*10465441SEvalZero u8_t unicast_reply;
250*10465441SEvalZero /** If legacy query. (tx_id needed, and write
251*10465441SEvalZero * question again in reply before answer) */
252*10465441SEvalZero u8_t legacy_query;
253*10465441SEvalZero /* Reply bitmask for host information */
254*10465441SEvalZero u8_t host_replies;
255*10465441SEvalZero /* Bitmask for which reverse IPv6 hosts to answer */
256*10465441SEvalZero u8_t host_reverse_v6_replies;
257*10465441SEvalZero /* Reply bitmask per service */
258*10465441SEvalZero u8_t serv_replies[MDNS_MAX_SERVICES];
259*10465441SEvalZero };
260*10465441SEvalZero
261*10465441SEvalZero /** Domain, type and class.
262*10465441SEvalZero * Shared between questions and answers */
263*10465441SEvalZero struct mdns_rr_info {
264*10465441SEvalZero struct mdns_domain domain;
265*10465441SEvalZero u16_t type;
266*10465441SEvalZero u16_t klass;
267*10465441SEvalZero };
268*10465441SEvalZero
269*10465441SEvalZero struct mdns_question {
270*10465441SEvalZero struct mdns_rr_info info;
271*10465441SEvalZero /** unicast reply requested */
272*10465441SEvalZero u16_t unicast;
273*10465441SEvalZero };
274*10465441SEvalZero
275*10465441SEvalZero struct mdns_answer {
276*10465441SEvalZero struct mdns_rr_info info;
277*10465441SEvalZero /** cache flush command bit */
278*10465441SEvalZero u16_t cache_flush;
279*10465441SEvalZero /* Validity time in seconds */
280*10465441SEvalZero u32_t ttl;
281*10465441SEvalZero /** Length of variable answer */
282*10465441SEvalZero u16_t rd_length;
283*10465441SEvalZero /** Offset of start of variable answer in packet */
284*10465441SEvalZero u16_t rd_offset;
285*10465441SEvalZero };
286*10465441SEvalZero
287*10465441SEvalZero static err_t mdns_send_outpacket(struct mdns_outpacket *outpkt, u8_t flags);
288*10465441SEvalZero static void mdns_probe(void* arg);
289*10465441SEvalZero
290*10465441SEvalZero static err_t
mdns_domain_add_label_base(struct mdns_domain * domain,u8_t len)291*10465441SEvalZero mdns_domain_add_label_base(struct mdns_domain *domain, u8_t len)
292*10465441SEvalZero {
293*10465441SEvalZero if (len > MDNS_LABEL_MAXLEN) {
294*10465441SEvalZero return ERR_VAL;
295*10465441SEvalZero }
296*10465441SEvalZero if (len > 0 && (1 + len + domain->length >= MDNS_DOMAIN_MAXLEN)) {
297*10465441SEvalZero return ERR_VAL;
298*10465441SEvalZero }
299*10465441SEvalZero /* Allow only zero marker on last byte */
300*10465441SEvalZero if (len == 0 && (1 + domain->length > MDNS_DOMAIN_MAXLEN)) {
301*10465441SEvalZero return ERR_VAL;
302*10465441SEvalZero }
303*10465441SEvalZero domain->name[domain->length] = len;
304*10465441SEvalZero domain->length++;
305*10465441SEvalZero return ERR_OK;
306*10465441SEvalZero }
307*10465441SEvalZero
308*10465441SEvalZero /**
309*10465441SEvalZero * Add a label part to a domain
310*10465441SEvalZero * @param domain The domain to add a label to
311*10465441SEvalZero * @param label The label to add, like <hostname>, 'local', 'com' or ''
312*10465441SEvalZero * @param len The length of the label
313*10465441SEvalZero * @return ERR_OK on success, an err_t otherwise if label too long
314*10465441SEvalZero */
315*10465441SEvalZero err_t
mdns_domain_add_label(struct mdns_domain * domain,const char * label,u8_t len)316*10465441SEvalZero mdns_domain_add_label(struct mdns_domain *domain, const char *label, u8_t len)
317*10465441SEvalZero {
318*10465441SEvalZero err_t err = mdns_domain_add_label_base(domain, len);
319*10465441SEvalZero if (err != ERR_OK) {
320*10465441SEvalZero return err;
321*10465441SEvalZero }
322*10465441SEvalZero if (len) {
323*10465441SEvalZero MEMCPY(&domain->name[domain->length], label, len);
324*10465441SEvalZero domain->length += len;
325*10465441SEvalZero }
326*10465441SEvalZero return ERR_OK;
327*10465441SEvalZero }
328*10465441SEvalZero
329*10465441SEvalZero /**
330*10465441SEvalZero * Add a label part to a domain (@see mdns_domain_add_label but copy directly from pbuf)
331*10465441SEvalZero */
332*10465441SEvalZero static err_t
mdns_domain_add_label_pbuf(struct mdns_domain * domain,const struct pbuf * p,u16_t offset,u8_t len)333*10465441SEvalZero mdns_domain_add_label_pbuf(struct mdns_domain *domain, const struct pbuf *p, u16_t offset, u8_t len)
334*10465441SEvalZero {
335*10465441SEvalZero err_t err = mdns_domain_add_label_base(domain, len);
336*10465441SEvalZero if (err != ERR_OK) {
337*10465441SEvalZero return err;
338*10465441SEvalZero }
339*10465441SEvalZero if (len) {
340*10465441SEvalZero if (pbuf_copy_partial(p, &domain->name[domain->length], len, offset) != len) {
341*10465441SEvalZero /* take back the ++ done before */
342*10465441SEvalZero domain->length--;
343*10465441SEvalZero return ERR_ARG;
344*10465441SEvalZero }
345*10465441SEvalZero domain->length += len;
346*10465441SEvalZero }
347*10465441SEvalZero return ERR_OK;
348*10465441SEvalZero }
349*10465441SEvalZero
350*10465441SEvalZero /**
351*10465441SEvalZero * Internal readname function with max 6 levels of recursion following jumps
352*10465441SEvalZero * while decompressing name
353*10465441SEvalZero */
354*10465441SEvalZero static u16_t
mdns_readname_loop(struct pbuf * p,u16_t offset,struct mdns_domain * domain,unsigned depth)355*10465441SEvalZero mdns_readname_loop(struct pbuf *p, u16_t offset, struct mdns_domain *domain, unsigned depth)
356*10465441SEvalZero {
357*10465441SEvalZero u8_t c;
358*10465441SEvalZero
359*10465441SEvalZero do {
360*10465441SEvalZero if (depth > 5) {
361*10465441SEvalZero /* Too many jumps */
362*10465441SEvalZero return MDNS_READNAME_ERROR;
363*10465441SEvalZero }
364*10465441SEvalZero
365*10465441SEvalZero c = pbuf_get_at(p, offset);
366*10465441SEvalZero offset++;
367*10465441SEvalZero
368*10465441SEvalZero /* is this a compressed label? */
369*10465441SEvalZero if ((c & 0xc0) == 0xc0) {
370*10465441SEvalZero u16_t jumpaddr;
371*10465441SEvalZero if (offset >= p->tot_len) {
372*10465441SEvalZero /* Make sure both jump bytes fit in the packet */
373*10465441SEvalZero return MDNS_READNAME_ERROR;
374*10465441SEvalZero }
375*10465441SEvalZero jumpaddr = (((c & 0x3f) << 8) | (pbuf_get_at(p, offset) & 0xff));
376*10465441SEvalZero offset++;
377*10465441SEvalZero if (jumpaddr >= SIZEOF_DNS_HDR && jumpaddr < p->tot_len) {
378*10465441SEvalZero u16_t res;
379*10465441SEvalZero /* Recursive call, maximum depth will be checked */
380*10465441SEvalZero res = mdns_readname_loop(p, jumpaddr, domain, depth + 1);
381*10465441SEvalZero /* Dont return offset since new bytes were not read (jumped to somewhere in packet) */
382*10465441SEvalZero if (res == MDNS_READNAME_ERROR) {
383*10465441SEvalZero return res;
384*10465441SEvalZero }
385*10465441SEvalZero } else {
386*10465441SEvalZero return MDNS_READNAME_ERROR;
387*10465441SEvalZero }
388*10465441SEvalZero break;
389*10465441SEvalZero }
390*10465441SEvalZero
391*10465441SEvalZero /* normal label */
392*10465441SEvalZero if (c <= MDNS_LABEL_MAXLEN) {
393*10465441SEvalZero err_t res;
394*10465441SEvalZero
395*10465441SEvalZero if (c + domain->length >= MDNS_DOMAIN_MAXLEN) {
396*10465441SEvalZero return MDNS_READNAME_ERROR;
397*10465441SEvalZero }
398*10465441SEvalZero res = mdns_domain_add_label_pbuf(domain, p, offset, c);
399*10465441SEvalZero if (res != ERR_OK) {
400*10465441SEvalZero return MDNS_READNAME_ERROR;
401*10465441SEvalZero }
402*10465441SEvalZero offset += c;
403*10465441SEvalZero } else {
404*10465441SEvalZero /* bad length byte */
405*10465441SEvalZero return MDNS_READNAME_ERROR;
406*10465441SEvalZero }
407*10465441SEvalZero } while (c != 0);
408*10465441SEvalZero
409*10465441SEvalZero return offset;
410*10465441SEvalZero }
411*10465441SEvalZero
412*10465441SEvalZero /**
413*10465441SEvalZero * Read possibly compressed domain name from packet buffer
414*10465441SEvalZero * @param p The packet
415*10465441SEvalZero * @param offset start position of domain name in packet
416*10465441SEvalZero * @param domain The domain name destination
417*10465441SEvalZero * @return The new offset after the domain, or MDNS_READNAME_ERROR
418*10465441SEvalZero * if reading failed
419*10465441SEvalZero */
420*10465441SEvalZero u16_t
mdns_readname(struct pbuf * p,u16_t offset,struct mdns_domain * domain)421*10465441SEvalZero mdns_readname(struct pbuf *p, u16_t offset, struct mdns_domain *domain)
422*10465441SEvalZero {
423*10465441SEvalZero memset(domain, 0, sizeof(struct mdns_domain));
424*10465441SEvalZero return mdns_readname_loop(p, offset, domain, 0);
425*10465441SEvalZero }
426*10465441SEvalZero
427*10465441SEvalZero /**
428*10465441SEvalZero * Print domain name to debug output
429*10465441SEvalZero * @param domain The domain name
430*10465441SEvalZero */
431*10465441SEvalZero static void
mdns_domain_debug_print(struct mdns_domain * domain)432*10465441SEvalZero mdns_domain_debug_print(struct mdns_domain *domain)
433*10465441SEvalZero {
434*10465441SEvalZero u8_t *src = domain->name;
435*10465441SEvalZero u8_t i;
436*10465441SEvalZero
437*10465441SEvalZero while (*src) {
438*10465441SEvalZero u8_t label_len = *src;
439*10465441SEvalZero src++;
440*10465441SEvalZero for (i = 0; i < label_len; i++) {
441*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("%c", src[i]));
442*10465441SEvalZero }
443*10465441SEvalZero src += label_len;
444*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("."));
445*10465441SEvalZero }
446*10465441SEvalZero }
447*10465441SEvalZero
448*10465441SEvalZero /**
449*10465441SEvalZero * Return 1 if contents of domains match (case-insensitive)
450*10465441SEvalZero * @param a Domain name to compare 1
451*10465441SEvalZero * @param b Domain name to compare 2
452*10465441SEvalZero * @return 1 if domains are equal ignoring case, 0 otherwise
453*10465441SEvalZero */
454*10465441SEvalZero int
mdns_domain_eq(struct mdns_domain * a,struct mdns_domain * b)455*10465441SEvalZero mdns_domain_eq(struct mdns_domain *a, struct mdns_domain *b)
456*10465441SEvalZero {
457*10465441SEvalZero u8_t *ptra, *ptrb;
458*10465441SEvalZero u8_t len;
459*10465441SEvalZero int res;
460*10465441SEvalZero
461*10465441SEvalZero if (a->length != b->length) {
462*10465441SEvalZero return 0;
463*10465441SEvalZero }
464*10465441SEvalZero
465*10465441SEvalZero ptra = a->name;
466*10465441SEvalZero ptrb = b->name;
467*10465441SEvalZero while (*ptra && *ptrb && ptra < &a->name[a->length]) {
468*10465441SEvalZero if (*ptra != *ptrb) {
469*10465441SEvalZero return 0;
470*10465441SEvalZero }
471*10465441SEvalZero len = *ptra;
472*10465441SEvalZero ptra++;
473*10465441SEvalZero ptrb++;
474*10465441SEvalZero res = lwip_strnicmp((char *) ptra, (char *) ptrb, len);
475*10465441SEvalZero if (res != 0) {
476*10465441SEvalZero return 0;
477*10465441SEvalZero }
478*10465441SEvalZero ptra += len;
479*10465441SEvalZero ptrb += len;
480*10465441SEvalZero }
481*10465441SEvalZero if (*ptra != *ptrb && ptra < &a->name[a->length]) {
482*10465441SEvalZero return 0;
483*10465441SEvalZero }
484*10465441SEvalZero return 1;
485*10465441SEvalZero }
486*10465441SEvalZero
487*10465441SEvalZero /**
488*10465441SEvalZero * Call user supplied function to setup TXT data
489*10465441SEvalZero * @param service The service to build TXT record for
490*10465441SEvalZero */
491*10465441SEvalZero static void
mdns_prepare_txtdata(struct mdns_service * service)492*10465441SEvalZero mdns_prepare_txtdata(struct mdns_service *service)
493*10465441SEvalZero {
494*10465441SEvalZero memset(&service->txtdata, 0, sizeof(struct mdns_domain));
495*10465441SEvalZero if (service->txt_fn) {
496*10465441SEvalZero service->txt_fn(service, service->txt_userdata);
497*10465441SEvalZero }
498*10465441SEvalZero }
499*10465441SEvalZero
500*10465441SEvalZero #if LWIP_IPV4
501*10465441SEvalZero /**
502*10465441SEvalZero * Build domain for reverse lookup of IPv4 address
503*10465441SEvalZero * like 12.0.168.192.in-addr.arpa. for 192.168.0.12
504*10465441SEvalZero * @param domain Where to write the domain name
505*10465441SEvalZero * @param addr Pointer to an IPv4 address to encode
506*10465441SEvalZero * @return ERR_OK if domain was written, an err_t otherwise
507*10465441SEvalZero */
508*10465441SEvalZero static err_t
mdns_build_reverse_v4_domain(struct mdns_domain * domain,const ip4_addr_t * addr)509*10465441SEvalZero mdns_build_reverse_v4_domain(struct mdns_domain *domain, const ip4_addr_t *addr)
510*10465441SEvalZero {
511*10465441SEvalZero int i;
512*10465441SEvalZero err_t res;
513*10465441SEvalZero const u8_t *ptr;
514*10465441SEvalZero
515*10465441SEvalZero LWIP_UNUSED_ARG(res);
516*10465441SEvalZero if (!domain || !addr) {
517*10465441SEvalZero return ERR_ARG;
518*10465441SEvalZero }
519*10465441SEvalZero memset(domain, 0, sizeof(struct mdns_domain));
520*10465441SEvalZero ptr = (const u8_t *) addr;
521*10465441SEvalZero for (i = sizeof(ip4_addr_t) - 1; i >= 0; i--) {
522*10465441SEvalZero char buf[4];
523*10465441SEvalZero u8_t val = ptr[i];
524*10465441SEvalZero
525*10465441SEvalZero lwip_itoa(buf, sizeof(buf), val);
526*10465441SEvalZero res = mdns_domain_add_label(domain, buf, (u8_t)strlen(buf));
527*10465441SEvalZero LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
528*10465441SEvalZero }
529*10465441SEvalZero res = mdns_domain_add_label(domain, REVERSE_PTR_V4_DOMAIN, (u8_t)(sizeof(REVERSE_PTR_V4_DOMAIN) - 1));
530*10465441SEvalZero LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
531*10465441SEvalZero res = mdns_domain_add_label(domain, REVERSE_PTR_TOPDOMAIN, (u8_t)(sizeof(REVERSE_PTR_TOPDOMAIN) - 1));
532*10465441SEvalZero LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
533*10465441SEvalZero res = mdns_domain_add_label(domain, NULL, 0);
534*10465441SEvalZero LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
535*10465441SEvalZero
536*10465441SEvalZero return ERR_OK;
537*10465441SEvalZero }
538*10465441SEvalZero #endif
539*10465441SEvalZero
540*10465441SEvalZero #if LWIP_IPV6
541*10465441SEvalZero /**
542*10465441SEvalZero * Build domain for reverse lookup of IP address
543*10465441SEvalZero * like b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. for 2001:db8::567:89ab
544*10465441SEvalZero * @param domain Where to write the domain name
545*10465441SEvalZero * @param addr Pointer to an IPv6 address to encode
546*10465441SEvalZero * @return ERR_OK if domain was written, an err_t otherwise
547*10465441SEvalZero */
548*10465441SEvalZero static err_t
mdns_build_reverse_v6_domain(struct mdns_domain * domain,const ip6_addr_t * addr)549*10465441SEvalZero mdns_build_reverse_v6_domain(struct mdns_domain *domain, const ip6_addr_t *addr)
550*10465441SEvalZero {
551*10465441SEvalZero int i;
552*10465441SEvalZero err_t res;
553*10465441SEvalZero const u8_t *ptr;
554*10465441SEvalZero LWIP_UNUSED_ARG(res);
555*10465441SEvalZero if (!domain || !addr) {
556*10465441SEvalZero return ERR_ARG;
557*10465441SEvalZero }
558*10465441SEvalZero memset(domain, 0, sizeof(struct mdns_domain));
559*10465441SEvalZero ptr = (const u8_t *) addr;
560*10465441SEvalZero for (i = sizeof(ip6_addr_p_t) - 1; i >= 0; i--) {
561*10465441SEvalZero char buf;
562*10465441SEvalZero u8_t byte = ptr[i];
563*10465441SEvalZero int j;
564*10465441SEvalZero for (j = 0; j < 2; j++) {
565*10465441SEvalZero if ((byte & 0x0F) < 0xA) {
566*10465441SEvalZero buf = '0' + (byte & 0x0F);
567*10465441SEvalZero } else {
568*10465441SEvalZero buf = 'a' + (byte & 0x0F) - 0xA;
569*10465441SEvalZero }
570*10465441SEvalZero res = mdns_domain_add_label(domain, &buf, sizeof(buf));
571*10465441SEvalZero LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
572*10465441SEvalZero byte >>= 4;
573*10465441SEvalZero }
574*10465441SEvalZero }
575*10465441SEvalZero res = mdns_domain_add_label(domain, REVERSE_PTR_V6_DOMAIN, (u8_t)(sizeof(REVERSE_PTR_V6_DOMAIN) - 1));
576*10465441SEvalZero LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
577*10465441SEvalZero res = mdns_domain_add_label(domain, REVERSE_PTR_TOPDOMAIN, (u8_t)(sizeof(REVERSE_PTR_TOPDOMAIN) - 1));
578*10465441SEvalZero LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
579*10465441SEvalZero res = mdns_domain_add_label(domain, NULL, 0);
580*10465441SEvalZero LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
581*10465441SEvalZero
582*10465441SEvalZero return ERR_OK;
583*10465441SEvalZero }
584*10465441SEvalZero #endif
585*10465441SEvalZero
586*10465441SEvalZero /* Add .local. to domain */
587*10465441SEvalZero static err_t
mdns_add_dotlocal(struct mdns_domain * domain)588*10465441SEvalZero mdns_add_dotlocal(struct mdns_domain *domain)
589*10465441SEvalZero {
590*10465441SEvalZero err_t res = mdns_domain_add_label(domain, TOPDOMAIN_LOCAL, (u8_t)(sizeof(TOPDOMAIN_LOCAL) - 1));
591*10465441SEvalZero LWIP_UNUSED_ARG(res);
592*10465441SEvalZero LWIP_ERROR("mdns_add_dotlocal: Failed to add label", (res == ERR_OK), return res);
593*10465441SEvalZero return mdns_domain_add_label(domain, NULL, 0);
594*10465441SEvalZero }
595*10465441SEvalZero
596*10465441SEvalZero /**
597*10465441SEvalZero * Build the <hostname>.local. domain name
598*10465441SEvalZero * @param domain Where to write the domain name
599*10465441SEvalZero * @param mdns TMDNS netif descriptor.
600*10465441SEvalZero * @return ERR_OK if domain <hostname>.local. was written, an err_t otherwise
601*10465441SEvalZero */
602*10465441SEvalZero static err_t
mdns_build_host_domain(struct mdns_domain * domain,struct mdns_host * mdns)603*10465441SEvalZero mdns_build_host_domain(struct mdns_domain *domain, struct mdns_host *mdns)
604*10465441SEvalZero {
605*10465441SEvalZero err_t res;
606*10465441SEvalZero LWIP_UNUSED_ARG(res);
607*10465441SEvalZero memset(domain, 0, sizeof(struct mdns_domain));
608*10465441SEvalZero LWIP_ERROR("mdns_build_host_domain: mdns != NULL", (mdns != NULL), return ERR_VAL);
609*10465441SEvalZero res = mdns_domain_add_label(domain, mdns->name, (u8_t)strlen(mdns->name));
610*10465441SEvalZero LWIP_ERROR("mdns_build_host_domain: Failed to add label", (res == ERR_OK), return res);
611*10465441SEvalZero return mdns_add_dotlocal(domain);
612*10465441SEvalZero }
613*10465441SEvalZero
614*10465441SEvalZero /**
615*10465441SEvalZero * Build the lookup-all-services special DNS-SD domain name
616*10465441SEvalZero * @param domain Where to write the domain name
617*10465441SEvalZero * @return ERR_OK if domain _services._dns-sd._udp.local. was written, an err_t otherwise
618*10465441SEvalZero */
619*10465441SEvalZero static err_t
mdns_build_dnssd_domain(struct mdns_domain * domain)620*10465441SEvalZero mdns_build_dnssd_domain(struct mdns_domain *domain)
621*10465441SEvalZero {
622*10465441SEvalZero err_t res;
623*10465441SEvalZero LWIP_UNUSED_ARG(res);
624*10465441SEvalZero memset(domain, 0, sizeof(struct mdns_domain));
625*10465441SEvalZero res = mdns_domain_add_label(domain, "_services", (u8_t)(sizeof("_services") - 1));
626*10465441SEvalZero LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
627*10465441SEvalZero res = mdns_domain_add_label(domain, "_dns-sd", (u8_t)(sizeof("_dns-sd") - 1));
628*10465441SEvalZero LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
629*10465441SEvalZero res = mdns_domain_add_label(domain, dnssd_protos[DNSSD_PROTO_UDP], (u8_t)strlen(dnssd_protos[DNSSD_PROTO_UDP]));
630*10465441SEvalZero LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
631*10465441SEvalZero return mdns_add_dotlocal(domain);
632*10465441SEvalZero }
633*10465441SEvalZero
634*10465441SEvalZero /**
635*10465441SEvalZero * Build domain name for a service
636*10465441SEvalZero * @param domain Where to write the domain name
637*10465441SEvalZero * @param service The service struct, containing service name, type and protocol
638*10465441SEvalZero * @param include_name Whether to include the service name in the domain
639*10465441SEvalZero * @return ERR_OK if domain was written. If service name is included,
640*10465441SEvalZero * <name>.<type>.<proto>.local. will be written, otherwise <type>.<proto>.local.
641*10465441SEvalZero * An err_t is returned on error.
642*10465441SEvalZero */
643*10465441SEvalZero static err_t
mdns_build_service_domain(struct mdns_domain * domain,struct mdns_service * service,int include_name)644*10465441SEvalZero mdns_build_service_domain(struct mdns_domain *domain, struct mdns_service *service, int include_name)
645*10465441SEvalZero {
646*10465441SEvalZero err_t res;
647*10465441SEvalZero LWIP_UNUSED_ARG(res);
648*10465441SEvalZero memset(domain, 0, sizeof(struct mdns_domain));
649*10465441SEvalZero if (include_name) {
650*10465441SEvalZero res = mdns_domain_add_label(domain, service->name, (u8_t)strlen(service->name));
651*10465441SEvalZero LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
652*10465441SEvalZero }
653*10465441SEvalZero res = mdns_domain_add_label(domain, service->service, (u8_t)strlen(service->service));
654*10465441SEvalZero LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
655*10465441SEvalZero res = mdns_domain_add_label(domain, dnssd_protos[service->proto], (u8_t)strlen(dnssd_protos[service->proto]));
656*10465441SEvalZero LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
657*10465441SEvalZero return mdns_add_dotlocal(domain);
658*10465441SEvalZero }
659*10465441SEvalZero
660*10465441SEvalZero /**
661*10465441SEvalZero * Check which replies we should send for a host/netif based on question
662*10465441SEvalZero * @param netif The network interface that received the question
663*10465441SEvalZero * @param rr Domain/type/class from a question
664*10465441SEvalZero * @param reverse_v6_reply Bitmask of which IPv6 addresses to send reverse PTRs for
665*10465441SEvalZero * if reply bit has REPLY_HOST_PTR_V6 set
666*10465441SEvalZero * @return Bitmask of which replies to send
667*10465441SEvalZero */
668*10465441SEvalZero static int
check_host(struct netif * netif,struct mdns_rr_info * rr,u8_t * reverse_v6_reply)669*10465441SEvalZero check_host(struct netif *netif, struct mdns_rr_info *rr, u8_t *reverse_v6_reply)
670*10465441SEvalZero {
671*10465441SEvalZero err_t res;
672*10465441SEvalZero int replies = 0;
673*10465441SEvalZero struct mdns_domain mydomain;
674*10465441SEvalZero
675*10465441SEvalZero LWIP_UNUSED_ARG(reverse_v6_reply); /* if ipv6 is disabled */
676*10465441SEvalZero
677*10465441SEvalZero if (rr->klass != DNS_RRCLASS_IN && rr->klass != DNS_RRCLASS_ANY) {
678*10465441SEvalZero /* Invalid class */
679*10465441SEvalZero return replies;
680*10465441SEvalZero }
681*10465441SEvalZero
682*10465441SEvalZero /* Handle PTR for our addresses */
683*10465441SEvalZero if (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY) {
684*10465441SEvalZero #if LWIP_IPV6
685*10465441SEvalZero int i;
686*10465441SEvalZero for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
687*10465441SEvalZero if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
688*10465441SEvalZero res = mdns_build_reverse_v6_domain(&mydomain, netif_ip6_addr(netif, i));
689*10465441SEvalZero if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
690*10465441SEvalZero replies |= REPLY_HOST_PTR_V6;
691*10465441SEvalZero /* Mark which addresses where requested */
692*10465441SEvalZero if (reverse_v6_reply) {
693*10465441SEvalZero *reverse_v6_reply |= (1 << i);
694*10465441SEvalZero }
695*10465441SEvalZero }
696*10465441SEvalZero }
697*10465441SEvalZero }
698*10465441SEvalZero #endif
699*10465441SEvalZero #if LWIP_IPV4
700*10465441SEvalZero if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
701*10465441SEvalZero res = mdns_build_reverse_v4_domain(&mydomain, netif_ip4_addr(netif));
702*10465441SEvalZero if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
703*10465441SEvalZero replies |= REPLY_HOST_PTR_V4;
704*10465441SEvalZero }
705*10465441SEvalZero }
706*10465441SEvalZero #endif
707*10465441SEvalZero }
708*10465441SEvalZero
709*10465441SEvalZero res = mdns_build_host_domain(&mydomain, NETIF_TO_HOST(netif));
710*10465441SEvalZero /* Handle requests for our hostname */
711*10465441SEvalZero if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
712*10465441SEvalZero /* TODO return NSEC if unsupported protocol requested */
713*10465441SEvalZero #if LWIP_IPV4
714*10465441SEvalZero if (!ip4_addr_isany_val(*netif_ip4_addr(netif))
715*10465441SEvalZero && (rr->type == DNS_RRTYPE_A || rr->type == DNS_RRTYPE_ANY)) {
716*10465441SEvalZero replies |= REPLY_HOST_A;
717*10465441SEvalZero }
718*10465441SEvalZero #endif
719*10465441SEvalZero #if LWIP_IPV6
720*10465441SEvalZero if (rr->type == DNS_RRTYPE_AAAA || rr->type == DNS_RRTYPE_ANY) {
721*10465441SEvalZero replies |= REPLY_HOST_AAAA;
722*10465441SEvalZero }
723*10465441SEvalZero #endif
724*10465441SEvalZero }
725*10465441SEvalZero
726*10465441SEvalZero return replies;
727*10465441SEvalZero }
728*10465441SEvalZero
729*10465441SEvalZero /**
730*10465441SEvalZero * Check which replies we should send for a service based on question
731*10465441SEvalZero * @param service A registered MDNS service
732*10465441SEvalZero * @param rr Domain/type/class from a question
733*10465441SEvalZero * @return Bitmask of which replies to send
734*10465441SEvalZero */
735*10465441SEvalZero static int
check_service(struct mdns_service * service,struct mdns_rr_info * rr)736*10465441SEvalZero check_service(struct mdns_service *service, struct mdns_rr_info *rr)
737*10465441SEvalZero {
738*10465441SEvalZero err_t res;
739*10465441SEvalZero int replies = 0;
740*10465441SEvalZero struct mdns_domain mydomain;
741*10465441SEvalZero
742*10465441SEvalZero if (rr->klass != DNS_RRCLASS_IN && rr->klass != DNS_RRCLASS_ANY) {
743*10465441SEvalZero /* Invalid class */
744*10465441SEvalZero return 0;
745*10465441SEvalZero }
746*10465441SEvalZero
747*10465441SEvalZero res = mdns_build_dnssd_domain(&mydomain);
748*10465441SEvalZero if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain) &&
749*10465441SEvalZero (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY)) {
750*10465441SEvalZero /* Request for all service types */
751*10465441SEvalZero replies |= REPLY_SERVICE_TYPE_PTR;
752*10465441SEvalZero }
753*10465441SEvalZero
754*10465441SEvalZero res = mdns_build_service_domain(&mydomain, service, 0);
755*10465441SEvalZero if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain) &&
756*10465441SEvalZero (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY)) {
757*10465441SEvalZero /* Request for the instance of my service */
758*10465441SEvalZero replies |= REPLY_SERVICE_NAME_PTR;
759*10465441SEvalZero }
760*10465441SEvalZero
761*10465441SEvalZero res = mdns_build_service_domain(&mydomain, service, 1);
762*10465441SEvalZero if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
763*10465441SEvalZero /* Request for info about my service */
764*10465441SEvalZero if (rr->type == DNS_RRTYPE_SRV || rr->type == DNS_RRTYPE_ANY) {
765*10465441SEvalZero replies |= REPLY_SERVICE_SRV;
766*10465441SEvalZero }
767*10465441SEvalZero if (rr->type == DNS_RRTYPE_TXT || rr->type == DNS_RRTYPE_ANY) {
768*10465441SEvalZero replies |= REPLY_SERVICE_TXT;
769*10465441SEvalZero }
770*10465441SEvalZero }
771*10465441SEvalZero
772*10465441SEvalZero return replies;
773*10465441SEvalZero }
774*10465441SEvalZero
775*10465441SEvalZero /**
776*10465441SEvalZero * Return bytes needed to write before jump for best result of compressing supplied domain
777*10465441SEvalZero * against domain in outpacket starting at specified offset.
778*10465441SEvalZero * If a match is found, offset is updated to where to jump to
779*10465441SEvalZero * @param pbuf Pointer to pbuf with the partially constructed DNS packet
780*10465441SEvalZero * @param offset Start position of a domain written earlier. If this location is suitable
781*10465441SEvalZero * for compression, the pointer is updated to where in the domain to jump to.
782*10465441SEvalZero * @param domain The domain to write
783*10465441SEvalZero * @return Number of bytes to write of the new domain before writing a jump to the offset.
784*10465441SEvalZero * If compression can not be done against this previous domain name, the full new
785*10465441SEvalZero * domain length is returned.
786*10465441SEvalZero */
787*10465441SEvalZero u16_t
mdns_compress_domain(struct pbuf * pbuf,u16_t * offset,struct mdns_domain * domain)788*10465441SEvalZero mdns_compress_domain(struct pbuf *pbuf, u16_t *offset, struct mdns_domain *domain)
789*10465441SEvalZero {
790*10465441SEvalZero struct mdns_domain target;
791*10465441SEvalZero u16_t target_end;
792*10465441SEvalZero u8_t target_len;
793*10465441SEvalZero u8_t writelen = 0;
794*10465441SEvalZero u8_t *ptr;
795*10465441SEvalZero if (pbuf == NULL) {
796*10465441SEvalZero return domain->length;
797*10465441SEvalZero }
798*10465441SEvalZero target_end = mdns_readname(pbuf, *offset, &target);
799*10465441SEvalZero if (target_end == MDNS_READNAME_ERROR) {
800*10465441SEvalZero return domain->length;
801*10465441SEvalZero }
802*10465441SEvalZero target_len = (u8_t)(target_end - *offset);
803*10465441SEvalZero ptr = domain->name;
804*10465441SEvalZero while (writelen < domain->length) {
805*10465441SEvalZero u8_t domainlen = (u8_t)(domain->length - writelen);
806*10465441SEvalZero u8_t labellen;
807*10465441SEvalZero if (domainlen <= target.length && domainlen > DOMAIN_JUMP_SIZE) {
808*10465441SEvalZero /* Compare domains if target is long enough, and we have enough left of the domain */
809*10465441SEvalZero u8_t targetpos = (u8_t)(target.length - domainlen);
810*10465441SEvalZero if ((targetpos + DOMAIN_JUMP_SIZE) >= target_len) {
811*10465441SEvalZero /* We are checking at or beyond a jump in the original, stop looking */
812*10465441SEvalZero break;
813*10465441SEvalZero }
814*10465441SEvalZero if (target.length >= domainlen &&
815*10465441SEvalZero memcmp(&domain->name[writelen], &target.name[targetpos], domainlen) == 0) {
816*10465441SEvalZero *offset += targetpos;
817*10465441SEvalZero return writelen;
818*10465441SEvalZero }
819*10465441SEvalZero }
820*10465441SEvalZero /* Skip to next label in domain */
821*10465441SEvalZero labellen = *ptr;
822*10465441SEvalZero writelen += 1 + labellen;
823*10465441SEvalZero ptr += 1 + labellen;
824*10465441SEvalZero }
825*10465441SEvalZero /* Nothing found */
826*10465441SEvalZero return domain->length;
827*10465441SEvalZero }
828*10465441SEvalZero
829*10465441SEvalZero /**
830*10465441SEvalZero * Write domain to outpacket. Compression will be attempted,
831*10465441SEvalZero * unless domain->skip_compression is set.
832*10465441SEvalZero * @param outpkt The outpacket to write to
833*10465441SEvalZero * @param domain The domain name to write
834*10465441SEvalZero * @return ERR_OK on success, an err_t otherwise
835*10465441SEvalZero */
836*10465441SEvalZero static err_t
mdns_write_domain(struct mdns_outpacket * outpkt,struct mdns_domain * domain)837*10465441SEvalZero mdns_write_domain(struct mdns_outpacket *outpkt, struct mdns_domain *domain)
838*10465441SEvalZero {
839*10465441SEvalZero int i;
840*10465441SEvalZero err_t res;
841*10465441SEvalZero u16_t writelen = domain->length;
842*10465441SEvalZero u16_t jump_offset = 0;
843*10465441SEvalZero u16_t jump;
844*10465441SEvalZero
845*10465441SEvalZero if (!domain->skip_compression) {
846*10465441SEvalZero for (i = 0; i < NUM_DOMAIN_OFFSETS; i++) {
847*10465441SEvalZero u16_t offset = outpkt->domain_offsets[i];
848*10465441SEvalZero if (offset) {
849*10465441SEvalZero u16_t len = mdns_compress_domain(outpkt->pbuf, &offset, domain);
850*10465441SEvalZero if (len < writelen) {
851*10465441SEvalZero writelen = len;
852*10465441SEvalZero jump_offset = offset;
853*10465441SEvalZero }
854*10465441SEvalZero }
855*10465441SEvalZero }
856*10465441SEvalZero }
857*10465441SEvalZero
858*10465441SEvalZero if (writelen) {
859*10465441SEvalZero /* Write uncompressed part of name */
860*10465441SEvalZero res = pbuf_take_at(outpkt->pbuf, domain->name, writelen, outpkt->write_offset);
861*10465441SEvalZero if (res != ERR_OK) {
862*10465441SEvalZero return res;
863*10465441SEvalZero }
864*10465441SEvalZero
865*10465441SEvalZero /* Store offset of this new domain */
866*10465441SEvalZero for (i = 0; i < NUM_DOMAIN_OFFSETS; i++) {
867*10465441SEvalZero if (outpkt->domain_offsets[i] == 0) {
868*10465441SEvalZero outpkt->domain_offsets[i] = outpkt->write_offset;
869*10465441SEvalZero break;
870*10465441SEvalZero }
871*10465441SEvalZero }
872*10465441SEvalZero
873*10465441SEvalZero outpkt->write_offset += writelen;
874*10465441SEvalZero }
875*10465441SEvalZero if (jump_offset) {
876*10465441SEvalZero /* Write jump */
877*10465441SEvalZero jump = lwip_htons(DOMAIN_JUMP | jump_offset);
878*10465441SEvalZero res = pbuf_take_at(outpkt->pbuf, &jump, DOMAIN_JUMP_SIZE, outpkt->write_offset);
879*10465441SEvalZero if (res != ERR_OK) {
880*10465441SEvalZero return res;
881*10465441SEvalZero }
882*10465441SEvalZero outpkt->write_offset += DOMAIN_JUMP_SIZE;
883*10465441SEvalZero }
884*10465441SEvalZero return ERR_OK;
885*10465441SEvalZero }
886*10465441SEvalZero
887*10465441SEvalZero /**
888*10465441SEvalZero * Write a question to an outpacket
889*10465441SEvalZero * A question contains domain, type and class. Since an answer also starts with these fields this function is also
890*10465441SEvalZero * called from mdns_add_answer().
891*10465441SEvalZero * @param outpkt The outpacket to write to
892*10465441SEvalZero * @param domain The domain name the answer is for
893*10465441SEvalZero * @param type The DNS type of the answer (like 'AAAA', 'SRV')
894*10465441SEvalZero * @param klass The DNS type of the answer (like 'IN')
895*10465441SEvalZero * @param unicast If highest bit in class should be set, to instruct the responder to
896*10465441SEvalZero * reply with a unicast packet
897*10465441SEvalZero * @return ERR_OK on success, an err_t otherwise
898*10465441SEvalZero */
899*10465441SEvalZero static err_t
mdns_add_question(struct mdns_outpacket * outpkt,struct mdns_domain * domain,u16_t type,u16_t klass,u16_t unicast)900*10465441SEvalZero mdns_add_question(struct mdns_outpacket *outpkt, struct mdns_domain *domain, u16_t type, u16_t klass, u16_t unicast)
901*10465441SEvalZero {
902*10465441SEvalZero u16_t question_len;
903*10465441SEvalZero u16_t field16;
904*10465441SEvalZero err_t res;
905*10465441SEvalZero
906*10465441SEvalZero if (!outpkt->pbuf) {
907*10465441SEvalZero /* If no pbuf is active, allocate one */
908*10465441SEvalZero outpkt->pbuf = pbuf_alloc(PBUF_TRANSPORT, OUTPACKET_SIZE, PBUF_RAM);
909*10465441SEvalZero if (!outpkt->pbuf) {
910*10465441SEvalZero return ERR_MEM;
911*10465441SEvalZero }
912*10465441SEvalZero outpkt->write_offset = SIZEOF_DNS_HDR;
913*10465441SEvalZero }
914*10465441SEvalZero
915*10465441SEvalZero /* Worst case calculation. Domain string might be compressed */
916*10465441SEvalZero question_len = domain->length + sizeof(type) + sizeof(klass);
917*10465441SEvalZero if (outpkt->write_offset + question_len > outpkt->pbuf->tot_len) {
918*10465441SEvalZero /* No space */
919*10465441SEvalZero return ERR_MEM;
920*10465441SEvalZero }
921*10465441SEvalZero
922*10465441SEvalZero /* Write name */
923*10465441SEvalZero res = mdns_write_domain(outpkt, domain);
924*10465441SEvalZero if (res != ERR_OK) {
925*10465441SEvalZero return res;
926*10465441SEvalZero }
927*10465441SEvalZero
928*10465441SEvalZero /* Write type */
929*10465441SEvalZero field16 = lwip_htons(type);
930*10465441SEvalZero res = pbuf_take_at(outpkt->pbuf, &field16, sizeof(field16), outpkt->write_offset);
931*10465441SEvalZero if (res != ERR_OK) {
932*10465441SEvalZero return res;
933*10465441SEvalZero }
934*10465441SEvalZero outpkt->write_offset += sizeof(field16);
935*10465441SEvalZero
936*10465441SEvalZero /* Write class */
937*10465441SEvalZero if (unicast) {
938*10465441SEvalZero klass |= 0x8000;
939*10465441SEvalZero }
940*10465441SEvalZero field16 = lwip_htons(klass);
941*10465441SEvalZero res = pbuf_take_at(outpkt->pbuf, &field16, sizeof(field16), outpkt->write_offset);
942*10465441SEvalZero if (res != ERR_OK) {
943*10465441SEvalZero return res;
944*10465441SEvalZero }
945*10465441SEvalZero outpkt->write_offset += sizeof(field16);
946*10465441SEvalZero
947*10465441SEvalZero return ERR_OK;
948*10465441SEvalZero }
949*10465441SEvalZero
950*10465441SEvalZero /**
951*10465441SEvalZero * Write answer to reply packet.
952*10465441SEvalZero * buf or answer_domain can be null. The rd_length written will be buf_length +
953*10465441SEvalZero * size of (compressed) domain. Most uses will need either buf or answer_domain,
954*10465441SEvalZero * special case is SRV that starts with 3 u16 and then a domain name.
955*10465441SEvalZero * @param reply The outpacket to write to
956*10465441SEvalZero * @param domain The domain name the answer is for
957*10465441SEvalZero * @param type The DNS type of the answer (like 'AAAA', 'SRV')
958*10465441SEvalZero * @param klass The DNS type of the answer (like 'IN')
959*10465441SEvalZero * @param cache_flush If highest bit in class should be set, to instruct receiver that
960*10465441SEvalZero * this reply replaces any earlier answer for this domain/type/class
961*10465441SEvalZero * @param ttl Validity time in seconds to send out for IP address data in DNS replies
962*10465441SEvalZero * @param buf Pointer to buffer of answer data
963*10465441SEvalZero * @param buf_length Length of variable data
964*10465441SEvalZero * @param answer_domain A domain to write after any buffer data as answer
965*10465441SEvalZero * @return ERR_OK on success, an err_t otherwise
966*10465441SEvalZero */
967*10465441SEvalZero static err_t
mdns_add_answer(struct mdns_outpacket * reply,struct mdns_domain * domain,u16_t type,u16_t klass,u16_t cache_flush,u32_t ttl,const u8_t * buf,size_t buf_length,struct mdns_domain * answer_domain)968*10465441SEvalZero mdns_add_answer(struct mdns_outpacket *reply, struct mdns_domain *domain, u16_t type, u16_t klass, u16_t cache_flush,
969*10465441SEvalZero u32_t ttl, const u8_t *buf, size_t buf_length, struct mdns_domain *answer_domain)
970*10465441SEvalZero {
971*10465441SEvalZero u16_t answer_len;
972*10465441SEvalZero u16_t field16;
973*10465441SEvalZero u16_t rdlen_offset;
974*10465441SEvalZero u16_t answer_offset;
975*10465441SEvalZero u32_t field32;
976*10465441SEvalZero err_t res;
977*10465441SEvalZero
978*10465441SEvalZero if (!reply->pbuf) {
979*10465441SEvalZero /* If no pbuf is active, allocate one */
980*10465441SEvalZero reply->pbuf = pbuf_alloc(PBUF_TRANSPORT, OUTPACKET_SIZE, PBUF_RAM);
981*10465441SEvalZero if (!reply->pbuf) {
982*10465441SEvalZero return ERR_MEM;
983*10465441SEvalZero }
984*10465441SEvalZero reply->write_offset = SIZEOF_DNS_HDR;
985*10465441SEvalZero }
986*10465441SEvalZero
987*10465441SEvalZero /* Worst case calculation. Domain strings might be compressed */
988*10465441SEvalZero answer_len = domain->length + sizeof(type) + sizeof(klass) + sizeof(ttl) + sizeof(field16)/*rd_length*/;
989*10465441SEvalZero if (buf) {
990*10465441SEvalZero answer_len += (u16_t)buf_length;
991*10465441SEvalZero }
992*10465441SEvalZero if (answer_domain) {
993*10465441SEvalZero answer_len += answer_domain->length;
994*10465441SEvalZero }
995*10465441SEvalZero if (reply->write_offset + answer_len > reply->pbuf->tot_len) {
996*10465441SEvalZero /* No space */
997*10465441SEvalZero return ERR_MEM;
998*10465441SEvalZero }
999*10465441SEvalZero
1000*10465441SEvalZero /* Answer starts with same data as question, then more fields */
1001*10465441SEvalZero mdns_add_question(reply, domain, type, klass, cache_flush);
1002*10465441SEvalZero
1003*10465441SEvalZero /* Write TTL */
1004*10465441SEvalZero field32 = lwip_htonl(ttl);
1005*10465441SEvalZero res = pbuf_take_at(reply->pbuf, &field32, sizeof(field32), reply->write_offset);
1006*10465441SEvalZero if (res != ERR_OK) {
1007*10465441SEvalZero return res;
1008*10465441SEvalZero }
1009*10465441SEvalZero reply->write_offset += sizeof(field32);
1010*10465441SEvalZero
1011*10465441SEvalZero /* Store offsets and skip forward to the data */
1012*10465441SEvalZero rdlen_offset = reply->write_offset;
1013*10465441SEvalZero reply->write_offset += sizeof(field16);
1014*10465441SEvalZero answer_offset = reply->write_offset;
1015*10465441SEvalZero
1016*10465441SEvalZero if (buf) {
1017*10465441SEvalZero /* Write static data */
1018*10465441SEvalZero res = pbuf_take_at(reply->pbuf, buf, (u16_t)buf_length, reply->write_offset);
1019*10465441SEvalZero if (res != ERR_OK) {
1020*10465441SEvalZero return res;
1021*10465441SEvalZero }
1022*10465441SEvalZero reply->write_offset += (u16_t)buf_length;
1023*10465441SEvalZero }
1024*10465441SEvalZero
1025*10465441SEvalZero if (answer_domain) {
1026*10465441SEvalZero /* Write name answer (compressed if possible) */
1027*10465441SEvalZero res = mdns_write_domain(reply, answer_domain);
1028*10465441SEvalZero if (res != ERR_OK) {
1029*10465441SEvalZero return res;
1030*10465441SEvalZero }
1031*10465441SEvalZero }
1032*10465441SEvalZero
1033*10465441SEvalZero /* Write rd_length after when we know the answer size */
1034*10465441SEvalZero field16 = lwip_htons(reply->write_offset - answer_offset);
1035*10465441SEvalZero res = pbuf_take_at(reply->pbuf, &field16, sizeof(field16), rdlen_offset);
1036*10465441SEvalZero
1037*10465441SEvalZero return res;
1038*10465441SEvalZero }
1039*10465441SEvalZero
1040*10465441SEvalZero /**
1041*10465441SEvalZero * Helper function for mdns_read_question/mdns_read_answer
1042*10465441SEvalZero * Reads a domain, type and class from the packet
1043*10465441SEvalZero * @param pkt The MDNS packet to read from. The parse_offset field will be
1044*10465441SEvalZero * incremented to point to the next unparsed byte.
1045*10465441SEvalZero * @param info The struct to fill with domain, type and class
1046*10465441SEvalZero * @return ERR_OK on success, an err_t otherwise
1047*10465441SEvalZero */
1048*10465441SEvalZero static err_t
mdns_read_rr_info(struct mdns_packet * pkt,struct mdns_rr_info * info)1049*10465441SEvalZero mdns_read_rr_info(struct mdns_packet *pkt, struct mdns_rr_info *info)
1050*10465441SEvalZero {
1051*10465441SEvalZero u16_t field16, copied;
1052*10465441SEvalZero pkt->parse_offset = mdns_readname(pkt->pbuf, pkt->parse_offset, &info->domain);
1053*10465441SEvalZero if (pkt->parse_offset == MDNS_READNAME_ERROR) {
1054*10465441SEvalZero return ERR_VAL;
1055*10465441SEvalZero }
1056*10465441SEvalZero
1057*10465441SEvalZero copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset);
1058*10465441SEvalZero if (copied != sizeof(field16)) {
1059*10465441SEvalZero return ERR_VAL;
1060*10465441SEvalZero }
1061*10465441SEvalZero pkt->parse_offset += copied;
1062*10465441SEvalZero info->type = lwip_ntohs(field16);
1063*10465441SEvalZero
1064*10465441SEvalZero copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset);
1065*10465441SEvalZero if (copied != sizeof(field16)) {
1066*10465441SEvalZero return ERR_VAL;
1067*10465441SEvalZero }
1068*10465441SEvalZero pkt->parse_offset += copied;
1069*10465441SEvalZero info->klass = lwip_ntohs(field16);
1070*10465441SEvalZero
1071*10465441SEvalZero return ERR_OK;
1072*10465441SEvalZero }
1073*10465441SEvalZero
1074*10465441SEvalZero /**
1075*10465441SEvalZero * Read a question from the packet.
1076*10465441SEvalZero * All questions have to be read before the answers.
1077*10465441SEvalZero * @param pkt The MDNS packet to read from. The questions_left field will be decremented
1078*10465441SEvalZero * and the parse_offset will be updated.
1079*10465441SEvalZero * @param question The struct to fill with question data
1080*10465441SEvalZero * @return ERR_OK on success, an err_t otherwise
1081*10465441SEvalZero */
1082*10465441SEvalZero static err_t
mdns_read_question(struct mdns_packet * pkt,struct mdns_question * question)1083*10465441SEvalZero mdns_read_question(struct mdns_packet *pkt, struct mdns_question *question)
1084*10465441SEvalZero {
1085*10465441SEvalZero /* Safety check */
1086*10465441SEvalZero if (pkt->pbuf->tot_len < pkt->parse_offset) {
1087*10465441SEvalZero return ERR_VAL;
1088*10465441SEvalZero }
1089*10465441SEvalZero
1090*10465441SEvalZero if (pkt->questions_left) {
1091*10465441SEvalZero err_t res;
1092*10465441SEvalZero pkt->questions_left--;
1093*10465441SEvalZero
1094*10465441SEvalZero memset(question, 0, sizeof(struct mdns_question));
1095*10465441SEvalZero res = mdns_read_rr_info(pkt, &question->info);
1096*10465441SEvalZero if (res != ERR_OK) {
1097*10465441SEvalZero return res;
1098*10465441SEvalZero }
1099*10465441SEvalZero
1100*10465441SEvalZero /* Extract unicast flag from class field */
1101*10465441SEvalZero question->unicast = question->info.klass & 0x8000;
1102*10465441SEvalZero question->info.klass &= 0x7FFF;
1103*10465441SEvalZero
1104*10465441SEvalZero return ERR_OK;
1105*10465441SEvalZero }
1106*10465441SEvalZero return ERR_VAL;
1107*10465441SEvalZero }
1108*10465441SEvalZero
1109*10465441SEvalZero /**
1110*10465441SEvalZero * Read an answer from the packet
1111*10465441SEvalZero * The variable length reply is not copied, its pbuf offset and length is stored instead.
1112*10465441SEvalZero * @param pkt The MDNS packet to read. The answers_left field will be decremented and
1113*10465441SEvalZero * the parse_offset will be updated.
1114*10465441SEvalZero * @param answer The struct to fill with answer data
1115*10465441SEvalZero * @return ERR_OK on success, an err_t otherwise
1116*10465441SEvalZero */
1117*10465441SEvalZero static err_t
mdns_read_answer(struct mdns_packet * pkt,struct mdns_answer * answer)1118*10465441SEvalZero mdns_read_answer(struct mdns_packet *pkt, struct mdns_answer *answer)
1119*10465441SEvalZero {
1120*10465441SEvalZero /* Read questions first */
1121*10465441SEvalZero if (pkt->questions_left) {
1122*10465441SEvalZero return ERR_VAL;
1123*10465441SEvalZero }
1124*10465441SEvalZero
1125*10465441SEvalZero /* Safety check */
1126*10465441SEvalZero if (pkt->pbuf->tot_len < pkt->parse_offset) {
1127*10465441SEvalZero return ERR_VAL;
1128*10465441SEvalZero }
1129*10465441SEvalZero
1130*10465441SEvalZero if (pkt->answers_left) {
1131*10465441SEvalZero u16_t copied, field16;
1132*10465441SEvalZero u32_t ttl;
1133*10465441SEvalZero err_t res;
1134*10465441SEvalZero pkt->answers_left--;
1135*10465441SEvalZero
1136*10465441SEvalZero memset(answer, 0, sizeof(struct mdns_answer));
1137*10465441SEvalZero res = mdns_read_rr_info(pkt, &answer->info);
1138*10465441SEvalZero if (res != ERR_OK) {
1139*10465441SEvalZero return res;
1140*10465441SEvalZero }
1141*10465441SEvalZero
1142*10465441SEvalZero /* Extract cache_flush flag from class field */
1143*10465441SEvalZero answer->cache_flush = answer->info.klass & 0x8000;
1144*10465441SEvalZero answer->info.klass &= 0x7FFF;
1145*10465441SEvalZero
1146*10465441SEvalZero copied = pbuf_copy_partial(pkt->pbuf, &ttl, sizeof(ttl), pkt->parse_offset);
1147*10465441SEvalZero if (copied != sizeof(ttl)) {
1148*10465441SEvalZero return ERR_VAL;
1149*10465441SEvalZero }
1150*10465441SEvalZero pkt->parse_offset += copied;
1151*10465441SEvalZero answer->ttl = lwip_ntohl(ttl);
1152*10465441SEvalZero
1153*10465441SEvalZero copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset);
1154*10465441SEvalZero if (copied != sizeof(field16)) {
1155*10465441SEvalZero return ERR_VAL;
1156*10465441SEvalZero }
1157*10465441SEvalZero pkt->parse_offset += copied;
1158*10465441SEvalZero answer->rd_length = lwip_ntohs(field16);
1159*10465441SEvalZero
1160*10465441SEvalZero answer->rd_offset = pkt->parse_offset;
1161*10465441SEvalZero pkt->parse_offset += answer->rd_length;
1162*10465441SEvalZero
1163*10465441SEvalZero return ERR_OK;
1164*10465441SEvalZero }
1165*10465441SEvalZero return ERR_VAL;
1166*10465441SEvalZero }
1167*10465441SEvalZero
1168*10465441SEvalZero #if LWIP_IPV4
1169*10465441SEvalZero /** Write an IPv4 address (A) RR to outpacket */
1170*10465441SEvalZero static err_t
mdns_add_a_answer(struct mdns_outpacket * reply,u16_t cache_flush,struct netif * netif)1171*10465441SEvalZero mdns_add_a_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif)
1172*10465441SEvalZero {
1173*10465441SEvalZero struct mdns_domain host;
1174*10465441SEvalZero mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
1175*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with A record\n"));
1176*10465441SEvalZero return mdns_add_answer(reply, &host, DNS_RRTYPE_A, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, (const u8_t *) netif_ip4_addr(netif), sizeof(ip4_addr_t), NULL);
1177*10465441SEvalZero }
1178*10465441SEvalZero
1179*10465441SEvalZero /** Write a 4.3.2.1.in-addr.arpa -> hostname.local PTR RR to outpacket */
1180*10465441SEvalZero static err_t
mdns_add_hostv4_ptr_answer(struct mdns_outpacket * reply,u16_t cache_flush,struct netif * netif)1181*10465441SEvalZero mdns_add_hostv4_ptr_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif)
1182*10465441SEvalZero {
1183*10465441SEvalZero struct mdns_domain host, revhost;
1184*10465441SEvalZero mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
1185*10465441SEvalZero mdns_build_reverse_v4_domain(&revhost, netif_ip4_addr(netif));
1186*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with v4 PTR record\n"));
1187*10465441SEvalZero return mdns_add_answer(reply, &revhost, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, NULL, 0, &host);
1188*10465441SEvalZero }
1189*10465441SEvalZero #endif
1190*10465441SEvalZero
1191*10465441SEvalZero #if LWIP_IPV6
1192*10465441SEvalZero /** Write an IPv6 address (AAAA) RR to outpacket */
1193*10465441SEvalZero static err_t
mdns_add_aaaa_answer(struct mdns_outpacket * reply,u16_t cache_flush,struct netif * netif,int addrindex)1194*10465441SEvalZero mdns_add_aaaa_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif, int addrindex)
1195*10465441SEvalZero {
1196*10465441SEvalZero struct mdns_domain host;
1197*10465441SEvalZero mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
1198*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with AAAA record\n"));
1199*10465441SEvalZero return mdns_add_answer(reply, &host, DNS_RRTYPE_AAAA, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, (const u8_t *) netif_ip6_addr(netif, addrindex), sizeof(ip6_addr_p_t), NULL);
1200*10465441SEvalZero }
1201*10465441SEvalZero
1202*10465441SEvalZero /** Write a x.y.z.ip6.arpa -> hostname.local PTR RR to outpacket */
1203*10465441SEvalZero static err_t
mdns_add_hostv6_ptr_answer(struct mdns_outpacket * reply,u16_t cache_flush,struct netif * netif,int addrindex)1204*10465441SEvalZero mdns_add_hostv6_ptr_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif, int addrindex)
1205*10465441SEvalZero {
1206*10465441SEvalZero struct mdns_domain host, revhost;
1207*10465441SEvalZero mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
1208*10465441SEvalZero mdns_build_reverse_v6_domain(&revhost, netif_ip6_addr(netif, addrindex));
1209*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with v6 PTR record\n"));
1210*10465441SEvalZero return mdns_add_answer(reply, &revhost, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, NULL, 0, &host);
1211*10465441SEvalZero }
1212*10465441SEvalZero #endif
1213*10465441SEvalZero
1214*10465441SEvalZero /** Write an all-services -> servicetype PTR RR to outpacket */
1215*10465441SEvalZero static err_t
mdns_add_servicetype_ptr_answer(struct mdns_outpacket * reply,struct mdns_service * service)1216*10465441SEvalZero mdns_add_servicetype_ptr_answer(struct mdns_outpacket *reply, struct mdns_service *service)
1217*10465441SEvalZero {
1218*10465441SEvalZero struct mdns_domain service_type, service_dnssd;
1219*10465441SEvalZero mdns_build_service_domain(&service_type, service, 0);
1220*10465441SEvalZero mdns_build_dnssd_domain(&service_dnssd);
1221*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with service type PTR record\n"));
1222*10465441SEvalZero return mdns_add_answer(reply, &service_dnssd, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, 0, service->dns_ttl, NULL, 0, &service_type);
1223*10465441SEvalZero }
1224*10465441SEvalZero
1225*10465441SEvalZero /** Write a servicetype -> servicename PTR RR to outpacket */
1226*10465441SEvalZero static err_t
mdns_add_servicename_ptr_answer(struct mdns_outpacket * reply,struct mdns_service * service)1227*10465441SEvalZero mdns_add_servicename_ptr_answer(struct mdns_outpacket *reply, struct mdns_service *service)
1228*10465441SEvalZero {
1229*10465441SEvalZero struct mdns_domain service_type, service_instance;
1230*10465441SEvalZero mdns_build_service_domain(&service_type, service, 0);
1231*10465441SEvalZero mdns_build_service_domain(&service_instance, service, 1);
1232*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with service name PTR record\n"));
1233*10465441SEvalZero return mdns_add_answer(reply, &service_type, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, 0, service->dns_ttl, NULL, 0, &service_instance);
1234*10465441SEvalZero }
1235*10465441SEvalZero
1236*10465441SEvalZero /** Write a SRV RR to outpacket */
1237*10465441SEvalZero static err_t
mdns_add_srv_answer(struct mdns_outpacket * reply,u16_t cache_flush,struct mdns_host * mdns,struct mdns_service * service)1238*10465441SEvalZero mdns_add_srv_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct mdns_host *mdns, struct mdns_service *service)
1239*10465441SEvalZero {
1240*10465441SEvalZero struct mdns_domain service_instance, srvhost;
1241*10465441SEvalZero u16_t srvdata[3];
1242*10465441SEvalZero mdns_build_service_domain(&service_instance, service, 1);
1243*10465441SEvalZero mdns_build_host_domain(&srvhost, mdns);
1244*10465441SEvalZero if (reply->legacy_query) {
1245*10465441SEvalZero /* RFC 6762 section 18.14:
1246*10465441SEvalZero * In legacy unicast responses generated to answer legacy queries,
1247*10465441SEvalZero * name compression MUST NOT be performed on SRV records.
1248*10465441SEvalZero */
1249*10465441SEvalZero srvhost.skip_compression = 1;
1250*10465441SEvalZero }
1251*10465441SEvalZero srvdata[0] = lwip_htons(SRV_PRIORITY);
1252*10465441SEvalZero srvdata[1] = lwip_htons(SRV_WEIGHT);
1253*10465441SEvalZero srvdata[2] = lwip_htons(service->port);
1254*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with SRV record\n"));
1255*10465441SEvalZero return mdns_add_answer(reply, &service_instance, DNS_RRTYPE_SRV, DNS_RRCLASS_IN, cache_flush, service->dns_ttl,
1256*10465441SEvalZero (const u8_t *) &srvdata, sizeof(srvdata), &srvhost);
1257*10465441SEvalZero }
1258*10465441SEvalZero
1259*10465441SEvalZero /** Write a TXT RR to outpacket */
1260*10465441SEvalZero static err_t
mdns_add_txt_answer(struct mdns_outpacket * reply,u16_t cache_flush,struct mdns_service * service)1261*10465441SEvalZero mdns_add_txt_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct mdns_service *service)
1262*10465441SEvalZero {
1263*10465441SEvalZero struct mdns_domain service_instance;
1264*10465441SEvalZero mdns_build_service_domain(&service_instance, service, 1);
1265*10465441SEvalZero mdns_prepare_txtdata(service);
1266*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with TXT record\n"));
1267*10465441SEvalZero return mdns_add_answer(reply, &service_instance, DNS_RRTYPE_TXT, DNS_RRCLASS_IN, cache_flush, service->dns_ttl,
1268*10465441SEvalZero (u8_t *) &service->txtdata.name, service->txtdata.length, NULL);
1269*10465441SEvalZero }
1270*10465441SEvalZero
1271*10465441SEvalZero /**
1272*10465441SEvalZero * Setup outpacket as a reply to the incoming packet
1273*10465441SEvalZero */
1274*10465441SEvalZero static void
mdns_init_outpacket(struct mdns_outpacket * out,struct mdns_packet * in)1275*10465441SEvalZero mdns_init_outpacket(struct mdns_outpacket *out, struct mdns_packet *in)
1276*10465441SEvalZero {
1277*10465441SEvalZero memset(out, 0, sizeof(struct mdns_outpacket));
1278*10465441SEvalZero out->cache_flush = 1;
1279*10465441SEvalZero out->netif = in->netif;
1280*10465441SEvalZero
1281*10465441SEvalZero /* Copy source IP/port to use when responding unicast, or to choose
1282*10465441SEvalZero * which pcb to use for multicast (IPv4/IPv6)
1283*10465441SEvalZero */
1284*10465441SEvalZero SMEMCPY(&out->dest_addr, &in->source_addr, sizeof(ip_addr_t));
1285*10465441SEvalZero out->dest_port = in->source_port;
1286*10465441SEvalZero
1287*10465441SEvalZero if (in->source_port != LWIP_IANA_PORT_MDNS) {
1288*10465441SEvalZero out->unicast_reply = 1;
1289*10465441SEvalZero out->cache_flush = 0;
1290*10465441SEvalZero if (in->questions == 1) {
1291*10465441SEvalZero out->legacy_query = 1;
1292*10465441SEvalZero out->tx_id = in->tx_id;
1293*10465441SEvalZero }
1294*10465441SEvalZero }
1295*10465441SEvalZero
1296*10465441SEvalZero if (in->recv_unicast) {
1297*10465441SEvalZero out->unicast_reply = 1;
1298*10465441SEvalZero }
1299*10465441SEvalZero }
1300*10465441SEvalZero
1301*10465441SEvalZero /**
1302*10465441SEvalZero * Send chosen answers as a reply
1303*10465441SEvalZero *
1304*10465441SEvalZero * Add all selected answers (first write will allocate pbuf)
1305*10465441SEvalZero * Add additional answers based on the selected answers
1306*10465441SEvalZero * Send the packet
1307*10465441SEvalZero */
1308*10465441SEvalZero static err_t
mdns_send_outpacket(struct mdns_outpacket * outpkt,u8_t flags)1309*10465441SEvalZero mdns_send_outpacket(struct mdns_outpacket *outpkt, u8_t flags)
1310*10465441SEvalZero {
1311*10465441SEvalZero struct mdns_service *service;
1312*10465441SEvalZero err_t res = ERR_ARG;
1313*10465441SEvalZero int i;
1314*10465441SEvalZero struct mdns_host *mdns = NETIF_TO_HOST(outpkt->netif);
1315*10465441SEvalZero u16_t answers = 0;
1316*10465441SEvalZero
1317*10465441SEvalZero /* Write answers to host questions */
1318*10465441SEvalZero #if LWIP_IPV4
1319*10465441SEvalZero if (outpkt->host_replies & REPLY_HOST_A) {
1320*10465441SEvalZero res = mdns_add_a_answer(outpkt, outpkt->cache_flush, outpkt->netif);
1321*10465441SEvalZero if (res != ERR_OK) {
1322*10465441SEvalZero goto cleanup;
1323*10465441SEvalZero }
1324*10465441SEvalZero answers++;
1325*10465441SEvalZero }
1326*10465441SEvalZero if (outpkt->host_replies & REPLY_HOST_PTR_V4) {
1327*10465441SEvalZero res = mdns_add_hostv4_ptr_answer(outpkt, outpkt->cache_flush, outpkt->netif);
1328*10465441SEvalZero if (res != ERR_OK) {
1329*10465441SEvalZero goto cleanup;
1330*10465441SEvalZero }
1331*10465441SEvalZero answers++;
1332*10465441SEvalZero }
1333*10465441SEvalZero #endif
1334*10465441SEvalZero #if LWIP_IPV6
1335*10465441SEvalZero if (outpkt->host_replies & REPLY_HOST_AAAA) {
1336*10465441SEvalZero int addrindex;
1337*10465441SEvalZero for (addrindex = 0; addrindex < LWIP_IPV6_NUM_ADDRESSES; addrindex++) {
1338*10465441SEvalZero if (ip6_addr_isvalid(netif_ip6_addr_state(outpkt->netif, addrindex))) {
1339*10465441SEvalZero res = mdns_add_aaaa_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex);
1340*10465441SEvalZero if (res != ERR_OK) {
1341*10465441SEvalZero goto cleanup;
1342*10465441SEvalZero }
1343*10465441SEvalZero answers++;
1344*10465441SEvalZero }
1345*10465441SEvalZero }
1346*10465441SEvalZero }
1347*10465441SEvalZero if (outpkt->host_replies & REPLY_HOST_PTR_V6) {
1348*10465441SEvalZero u8_t rev_addrs = outpkt->host_reverse_v6_replies;
1349*10465441SEvalZero int addrindex = 0;
1350*10465441SEvalZero while (rev_addrs) {
1351*10465441SEvalZero if (rev_addrs & 1) {
1352*10465441SEvalZero res = mdns_add_hostv6_ptr_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex);
1353*10465441SEvalZero if (res != ERR_OK) {
1354*10465441SEvalZero goto cleanup;
1355*10465441SEvalZero }
1356*10465441SEvalZero answers++;
1357*10465441SEvalZero }
1358*10465441SEvalZero addrindex++;
1359*10465441SEvalZero rev_addrs >>= 1;
1360*10465441SEvalZero }
1361*10465441SEvalZero }
1362*10465441SEvalZero #endif
1363*10465441SEvalZero
1364*10465441SEvalZero /* Write answers to service questions */
1365*10465441SEvalZero for (i = 0; i < MDNS_MAX_SERVICES; i++) {
1366*10465441SEvalZero service = mdns->services[i];
1367*10465441SEvalZero if (!service) {
1368*10465441SEvalZero continue;
1369*10465441SEvalZero }
1370*10465441SEvalZero
1371*10465441SEvalZero if (outpkt->serv_replies[i] & REPLY_SERVICE_TYPE_PTR) {
1372*10465441SEvalZero res = mdns_add_servicetype_ptr_answer(outpkt, service);
1373*10465441SEvalZero if (res != ERR_OK) {
1374*10465441SEvalZero goto cleanup;
1375*10465441SEvalZero }
1376*10465441SEvalZero answers++;
1377*10465441SEvalZero }
1378*10465441SEvalZero
1379*10465441SEvalZero if (outpkt->serv_replies[i] & REPLY_SERVICE_NAME_PTR) {
1380*10465441SEvalZero res = mdns_add_servicename_ptr_answer(outpkt, service);
1381*10465441SEvalZero if (res != ERR_OK) {
1382*10465441SEvalZero goto cleanup;
1383*10465441SEvalZero }
1384*10465441SEvalZero answers++;
1385*10465441SEvalZero }
1386*10465441SEvalZero
1387*10465441SEvalZero if (outpkt->serv_replies[i] & REPLY_SERVICE_SRV) {
1388*10465441SEvalZero res = mdns_add_srv_answer(outpkt, outpkt->cache_flush, mdns, service);
1389*10465441SEvalZero if (res != ERR_OK) {
1390*10465441SEvalZero goto cleanup;
1391*10465441SEvalZero }
1392*10465441SEvalZero answers++;
1393*10465441SEvalZero }
1394*10465441SEvalZero
1395*10465441SEvalZero if (outpkt->serv_replies[i] & REPLY_SERVICE_TXT) {
1396*10465441SEvalZero res = mdns_add_txt_answer(outpkt, outpkt->cache_flush, service);
1397*10465441SEvalZero if (res != ERR_OK) {
1398*10465441SEvalZero goto cleanup;
1399*10465441SEvalZero }
1400*10465441SEvalZero answers++;
1401*10465441SEvalZero }
1402*10465441SEvalZero }
1403*10465441SEvalZero
1404*10465441SEvalZero /* if this is a response, the data above is anwers, else this is a probe and the answers above goes into auth section */
1405*10465441SEvalZero if (flags & DNS_FLAG1_RESPONSE) {
1406*10465441SEvalZero outpkt->answers += answers;
1407*10465441SEvalZero } else {
1408*10465441SEvalZero outpkt->authoritative += answers;
1409*10465441SEvalZero }
1410*10465441SEvalZero
1411*10465441SEvalZero /* All answers written, add additional RRs */
1412*10465441SEvalZero for (i = 0; i < MDNS_MAX_SERVICES; i++) {
1413*10465441SEvalZero service = mdns->services[i];
1414*10465441SEvalZero if (!service) {
1415*10465441SEvalZero continue;
1416*10465441SEvalZero }
1417*10465441SEvalZero
1418*10465441SEvalZero if (outpkt->serv_replies[i] & REPLY_SERVICE_NAME_PTR) {
1419*10465441SEvalZero /* Our service instance requested, include SRV & TXT
1420*10465441SEvalZero * if they are already not requested. */
1421*10465441SEvalZero if (!(outpkt->serv_replies[i] & REPLY_SERVICE_SRV)) {
1422*10465441SEvalZero res = mdns_add_srv_answer(outpkt, outpkt->cache_flush, mdns, service);
1423*10465441SEvalZero if (res != ERR_OK) {
1424*10465441SEvalZero goto cleanup;
1425*10465441SEvalZero }
1426*10465441SEvalZero outpkt->additional++;
1427*10465441SEvalZero }
1428*10465441SEvalZero
1429*10465441SEvalZero if (!(outpkt->serv_replies[i] & REPLY_SERVICE_TXT)) {
1430*10465441SEvalZero res = mdns_add_txt_answer(outpkt, outpkt->cache_flush, service);
1431*10465441SEvalZero if (res != ERR_OK) {
1432*10465441SEvalZero goto cleanup;
1433*10465441SEvalZero }
1434*10465441SEvalZero outpkt->additional++;
1435*10465441SEvalZero }
1436*10465441SEvalZero }
1437*10465441SEvalZero
1438*10465441SEvalZero /* If service instance, SRV, record or an IP address is requested,
1439*10465441SEvalZero * supply all addresses for the host
1440*10465441SEvalZero */
1441*10465441SEvalZero if ((outpkt->serv_replies[i] & (REPLY_SERVICE_NAME_PTR | REPLY_SERVICE_SRV)) ||
1442*10465441SEvalZero (outpkt->host_replies & (REPLY_HOST_A | REPLY_HOST_AAAA))) {
1443*10465441SEvalZero #if LWIP_IPV6
1444*10465441SEvalZero if (!(outpkt->host_replies & REPLY_HOST_AAAA)) {
1445*10465441SEvalZero int addrindex;
1446*10465441SEvalZero for (addrindex = 0; addrindex < LWIP_IPV6_NUM_ADDRESSES; addrindex++) {
1447*10465441SEvalZero if (ip6_addr_isvalid(netif_ip6_addr_state(outpkt->netif, addrindex))) {
1448*10465441SEvalZero res = mdns_add_aaaa_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex);
1449*10465441SEvalZero if (res != ERR_OK) {
1450*10465441SEvalZero goto cleanup;
1451*10465441SEvalZero }
1452*10465441SEvalZero outpkt->additional++;
1453*10465441SEvalZero }
1454*10465441SEvalZero }
1455*10465441SEvalZero }
1456*10465441SEvalZero #endif
1457*10465441SEvalZero #if LWIP_IPV4
1458*10465441SEvalZero if (!(outpkt->host_replies & REPLY_HOST_A) &&
1459*10465441SEvalZero !ip4_addr_isany_val(*netif_ip4_addr(outpkt->netif))) {
1460*10465441SEvalZero res = mdns_add_a_answer(outpkt, outpkt->cache_flush, outpkt->netif);
1461*10465441SEvalZero if (res != ERR_OK) {
1462*10465441SEvalZero goto cleanup;
1463*10465441SEvalZero }
1464*10465441SEvalZero outpkt->additional++;
1465*10465441SEvalZero }
1466*10465441SEvalZero #endif
1467*10465441SEvalZero }
1468*10465441SEvalZero }
1469*10465441SEvalZero
1470*10465441SEvalZero if (outpkt->pbuf) {
1471*10465441SEvalZero const ip_addr_t *mcast_destaddr;
1472*10465441SEvalZero struct dns_hdr hdr;
1473*10465441SEvalZero
1474*10465441SEvalZero /* Write header */
1475*10465441SEvalZero memset(&hdr, 0, sizeof(hdr));
1476*10465441SEvalZero hdr.flags1 = flags;
1477*10465441SEvalZero hdr.numquestions = lwip_htons(outpkt->questions);
1478*10465441SEvalZero hdr.numanswers = lwip_htons(outpkt->answers);
1479*10465441SEvalZero hdr.numauthrr = lwip_htons(outpkt->authoritative);
1480*10465441SEvalZero hdr.numextrarr = lwip_htons(outpkt->additional);
1481*10465441SEvalZero hdr.id = lwip_htons(outpkt->tx_id);
1482*10465441SEvalZero pbuf_take(outpkt->pbuf, &hdr, sizeof(hdr));
1483*10465441SEvalZero
1484*10465441SEvalZero /* Shrink packet */
1485*10465441SEvalZero pbuf_realloc(outpkt->pbuf, outpkt->write_offset);
1486*10465441SEvalZero
1487*10465441SEvalZero if (IP_IS_V6_VAL(outpkt->dest_addr)) {
1488*10465441SEvalZero #if LWIP_IPV6
1489*10465441SEvalZero mcast_destaddr = &v6group;
1490*10465441SEvalZero #endif
1491*10465441SEvalZero } else {
1492*10465441SEvalZero #if LWIP_IPV4
1493*10465441SEvalZero mcast_destaddr = &v4group;
1494*10465441SEvalZero #endif
1495*10465441SEvalZero }
1496*10465441SEvalZero /* Send created packet */
1497*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Sending packet, len=%d, unicast=%d\n", outpkt->write_offset, outpkt->unicast_reply));
1498*10465441SEvalZero if (outpkt->unicast_reply) {
1499*10465441SEvalZero res = udp_sendto_if(mdns_pcb, outpkt->pbuf, &outpkt->dest_addr, outpkt->dest_port, outpkt->netif);
1500*10465441SEvalZero } else {
1501*10465441SEvalZero res = udp_sendto_if(mdns_pcb, outpkt->pbuf, mcast_destaddr, LWIP_IANA_PORT_MDNS, outpkt->netif);
1502*10465441SEvalZero }
1503*10465441SEvalZero }
1504*10465441SEvalZero
1505*10465441SEvalZero cleanup:
1506*10465441SEvalZero if (outpkt->pbuf) {
1507*10465441SEvalZero pbuf_free(outpkt->pbuf);
1508*10465441SEvalZero outpkt->pbuf = NULL;
1509*10465441SEvalZero }
1510*10465441SEvalZero return res;
1511*10465441SEvalZero }
1512*10465441SEvalZero
1513*10465441SEvalZero /**
1514*10465441SEvalZero * Send unsolicited answer containing all our known data
1515*10465441SEvalZero * @param netif The network interface to send on
1516*10465441SEvalZero * @param destination The target address to send to (usually multicast address)
1517*10465441SEvalZero */
1518*10465441SEvalZero static void
mdns_announce(struct netif * netif,const ip_addr_t * destination)1519*10465441SEvalZero mdns_announce(struct netif *netif, const ip_addr_t *destination)
1520*10465441SEvalZero {
1521*10465441SEvalZero struct mdns_outpacket announce;
1522*10465441SEvalZero int i;
1523*10465441SEvalZero struct mdns_host *mdns = NETIF_TO_HOST(netif);
1524*10465441SEvalZero
1525*10465441SEvalZero memset(&announce, 0, sizeof(announce));
1526*10465441SEvalZero announce.netif = netif;
1527*10465441SEvalZero announce.cache_flush = 1;
1528*10465441SEvalZero #if LWIP_IPV4
1529*10465441SEvalZero if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
1530*10465441SEvalZero announce.host_replies = REPLY_HOST_A | REPLY_HOST_PTR_V4;
1531*10465441SEvalZero }
1532*10465441SEvalZero #endif
1533*10465441SEvalZero #if LWIP_IPV6
1534*10465441SEvalZero for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
1535*10465441SEvalZero if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
1536*10465441SEvalZero announce.host_replies |= REPLY_HOST_AAAA | REPLY_HOST_PTR_V6;
1537*10465441SEvalZero announce.host_reverse_v6_replies |= (1 << i);
1538*10465441SEvalZero }
1539*10465441SEvalZero }
1540*10465441SEvalZero #endif
1541*10465441SEvalZero
1542*10465441SEvalZero for (i = 0; i < MDNS_MAX_SERVICES; i++) {
1543*10465441SEvalZero struct mdns_service *serv = mdns->services[i];
1544*10465441SEvalZero if (serv) {
1545*10465441SEvalZero announce.serv_replies[i] = REPLY_SERVICE_TYPE_PTR | REPLY_SERVICE_NAME_PTR |
1546*10465441SEvalZero REPLY_SERVICE_SRV | REPLY_SERVICE_TXT;
1547*10465441SEvalZero }
1548*10465441SEvalZero }
1549*10465441SEvalZero
1550*10465441SEvalZero announce.dest_port = LWIP_IANA_PORT_MDNS;
1551*10465441SEvalZero SMEMCPY(&announce.dest_addr, destination, sizeof(announce.dest_addr));
1552*10465441SEvalZero mdns_send_outpacket(&announce, DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE);
1553*10465441SEvalZero }
1554*10465441SEvalZero
1555*10465441SEvalZero /**
1556*10465441SEvalZero * Handle question MDNS packet
1557*10465441SEvalZero * 1. Parse all questions and set bits what answers to send
1558*10465441SEvalZero * 2. Clear pending answers if known answers are supplied
1559*10465441SEvalZero * 3. Put chosen answers in new packet and send as reply
1560*10465441SEvalZero */
1561*10465441SEvalZero static void
mdns_handle_question(struct mdns_packet * pkt)1562*10465441SEvalZero mdns_handle_question(struct mdns_packet *pkt)
1563*10465441SEvalZero {
1564*10465441SEvalZero struct mdns_service *service;
1565*10465441SEvalZero struct mdns_outpacket reply;
1566*10465441SEvalZero int replies = 0;
1567*10465441SEvalZero int i;
1568*10465441SEvalZero err_t res;
1569*10465441SEvalZero struct mdns_host *mdns = NETIF_TO_HOST(pkt->netif);
1570*10465441SEvalZero
1571*10465441SEvalZero if (mdns->probing_state != MDNS_PROBING_COMPLETE) {
1572*10465441SEvalZero /* Don't answer questions until we've verified our domains via probing */
1573*10465441SEvalZero /* @todo we should check incoming questions during probing for tiebreaking */
1574*10465441SEvalZero return;
1575*10465441SEvalZero }
1576*10465441SEvalZero
1577*10465441SEvalZero mdns_init_outpacket(&reply, pkt);
1578*10465441SEvalZero
1579*10465441SEvalZero while (pkt->questions_left) {
1580*10465441SEvalZero struct mdns_question q;
1581*10465441SEvalZero
1582*10465441SEvalZero res = mdns_read_question(pkt, &q);
1583*10465441SEvalZero if (res != ERR_OK) {
1584*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse question, skipping query packet\n"));
1585*10465441SEvalZero return;
1586*10465441SEvalZero }
1587*10465441SEvalZero
1588*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Query for domain "));
1589*10465441SEvalZero mdns_domain_debug_print(&q.info.domain);
1590*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", q.info.type, q.info.klass));
1591*10465441SEvalZero
1592*10465441SEvalZero if (q.unicast) {
1593*10465441SEvalZero /* Reply unicast if any question is unicast */
1594*10465441SEvalZero reply.unicast_reply = 1;
1595*10465441SEvalZero }
1596*10465441SEvalZero
1597*10465441SEvalZero reply.host_replies |= check_host(pkt->netif, &q.info, &reply.host_reverse_v6_replies);
1598*10465441SEvalZero replies |= reply.host_replies;
1599*10465441SEvalZero
1600*10465441SEvalZero for (i = 0; i < MDNS_MAX_SERVICES; i++) {
1601*10465441SEvalZero service = mdns->services[i];
1602*10465441SEvalZero if (!service) {
1603*10465441SEvalZero continue;
1604*10465441SEvalZero }
1605*10465441SEvalZero reply.serv_replies[i] |= check_service(service, &q.info);
1606*10465441SEvalZero replies |= reply.serv_replies[i];
1607*10465441SEvalZero }
1608*10465441SEvalZero
1609*10465441SEvalZero if (replies && reply.legacy_query) {
1610*10465441SEvalZero /* Add question to reply packet (legacy packet only has 1 question) */
1611*10465441SEvalZero res = mdns_add_question(&reply, &q.info.domain, q.info.type, q.info.klass, 0);
1612*10465441SEvalZero reply.questions = 1;
1613*10465441SEvalZero if (res != ERR_OK) {
1614*10465441SEvalZero goto cleanup;
1615*10465441SEvalZero }
1616*10465441SEvalZero }
1617*10465441SEvalZero }
1618*10465441SEvalZero
1619*10465441SEvalZero /* Handle known answers */
1620*10465441SEvalZero while (pkt->answers_left) {
1621*10465441SEvalZero struct mdns_answer ans;
1622*10465441SEvalZero u8_t rev_v6;
1623*10465441SEvalZero int match;
1624*10465441SEvalZero
1625*10465441SEvalZero res = mdns_read_answer(pkt, &ans);
1626*10465441SEvalZero if (res != ERR_OK) {
1627*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse answer, skipping query packet\n"));
1628*10465441SEvalZero goto cleanup;
1629*10465441SEvalZero }
1630*10465441SEvalZero
1631*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Known answer for domain "));
1632*10465441SEvalZero mdns_domain_debug_print(&ans.info.domain);
1633*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass));
1634*10465441SEvalZero
1635*10465441SEvalZero
1636*10465441SEvalZero if (ans.info.type == DNS_RRTYPE_ANY || ans.info.klass == DNS_RRCLASS_ANY) {
1637*10465441SEvalZero /* Skip known answers for ANY type & class */
1638*10465441SEvalZero continue;
1639*10465441SEvalZero }
1640*10465441SEvalZero
1641*10465441SEvalZero rev_v6 = 0;
1642*10465441SEvalZero match = reply.host_replies & check_host(pkt->netif, &ans.info, &rev_v6);
1643*10465441SEvalZero if (match && (ans.ttl > (mdns->dns_ttl / 2))) {
1644*10465441SEvalZero /* The RR in the known answer matches an RR we are planning to send,
1645*10465441SEvalZero * and the TTL is less than half gone.
1646*10465441SEvalZero * If the payload matches we should not send that answer.
1647*10465441SEvalZero */
1648*10465441SEvalZero if (ans.info.type == DNS_RRTYPE_PTR) {
1649*10465441SEvalZero /* Read domain and compare */
1650*10465441SEvalZero struct mdns_domain known_ans, my_ans;
1651*10465441SEvalZero u16_t len;
1652*10465441SEvalZero len = mdns_readname(pkt->pbuf, ans.rd_offset, &known_ans);
1653*10465441SEvalZero res = mdns_build_host_domain(&my_ans, mdns);
1654*10465441SEvalZero if (len != MDNS_READNAME_ERROR && res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) {
1655*10465441SEvalZero #if LWIP_IPV4
1656*10465441SEvalZero if (match & REPLY_HOST_PTR_V4) {
1657*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: v4 PTR\n"));
1658*10465441SEvalZero reply.host_replies &= ~REPLY_HOST_PTR_V4;
1659*10465441SEvalZero }
1660*10465441SEvalZero #endif
1661*10465441SEvalZero #if LWIP_IPV6
1662*10465441SEvalZero if (match & REPLY_HOST_PTR_V6) {
1663*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: v6 PTR\n"));
1664*10465441SEvalZero reply.host_reverse_v6_replies &= ~rev_v6;
1665*10465441SEvalZero if (reply.host_reverse_v6_replies == 0) {
1666*10465441SEvalZero reply.host_replies &= ~REPLY_HOST_PTR_V6;
1667*10465441SEvalZero }
1668*10465441SEvalZero }
1669*10465441SEvalZero #endif
1670*10465441SEvalZero }
1671*10465441SEvalZero } else if (match & REPLY_HOST_A) {
1672*10465441SEvalZero #if LWIP_IPV4
1673*10465441SEvalZero if (ans.rd_length == sizeof(ip4_addr_t) &&
1674*10465441SEvalZero pbuf_memcmp(pkt->pbuf, ans.rd_offset, netif_ip4_addr(pkt->netif), ans.rd_length) == 0) {
1675*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: A\n"));
1676*10465441SEvalZero reply.host_replies &= ~REPLY_HOST_A;
1677*10465441SEvalZero }
1678*10465441SEvalZero #endif
1679*10465441SEvalZero } else if (match & REPLY_HOST_AAAA) {
1680*10465441SEvalZero #if LWIP_IPV6
1681*10465441SEvalZero if (ans.rd_length == sizeof(ip6_addr_p_t) &&
1682*10465441SEvalZero /* TODO this clears all AAAA responses if first addr is set as known */
1683*10465441SEvalZero pbuf_memcmp(pkt->pbuf, ans.rd_offset, netif_ip6_addr(pkt->netif, 0), ans.rd_length) == 0) {
1684*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: AAAA\n"));
1685*10465441SEvalZero reply.host_replies &= ~REPLY_HOST_AAAA;
1686*10465441SEvalZero }
1687*10465441SEvalZero #endif
1688*10465441SEvalZero }
1689*10465441SEvalZero }
1690*10465441SEvalZero
1691*10465441SEvalZero for (i = 0; i < MDNS_MAX_SERVICES; i++) {
1692*10465441SEvalZero service = mdns->services[i];
1693*10465441SEvalZero if (!service) {
1694*10465441SEvalZero continue;
1695*10465441SEvalZero }
1696*10465441SEvalZero match = reply.serv_replies[i] & check_service(service, &ans.info);
1697*10465441SEvalZero if (match && (ans.ttl > (service->dns_ttl / 2))) {
1698*10465441SEvalZero /* The RR in the known answer matches an RR we are planning to send,
1699*10465441SEvalZero * and the TTL is less than half gone.
1700*10465441SEvalZero * If the payload matches we should not send that answer.
1701*10465441SEvalZero */
1702*10465441SEvalZero if (ans.info.type == DNS_RRTYPE_PTR) {
1703*10465441SEvalZero /* Read domain and compare */
1704*10465441SEvalZero struct mdns_domain known_ans, my_ans;
1705*10465441SEvalZero u16_t len;
1706*10465441SEvalZero len = mdns_readname(pkt->pbuf, ans.rd_offset, &known_ans);
1707*10465441SEvalZero if (len != MDNS_READNAME_ERROR) {
1708*10465441SEvalZero if (match & REPLY_SERVICE_TYPE_PTR) {
1709*10465441SEvalZero res = mdns_build_service_domain(&my_ans, service, 0);
1710*10465441SEvalZero if (res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) {
1711*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: service type PTR\n"));
1712*10465441SEvalZero reply.serv_replies[i] &= ~REPLY_SERVICE_TYPE_PTR;
1713*10465441SEvalZero }
1714*10465441SEvalZero }
1715*10465441SEvalZero if (match & REPLY_SERVICE_NAME_PTR) {
1716*10465441SEvalZero res = mdns_build_service_domain(&my_ans, service, 1);
1717*10465441SEvalZero if (res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) {
1718*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: service name PTR\n"));
1719*10465441SEvalZero reply.serv_replies[i] &= ~REPLY_SERVICE_NAME_PTR;
1720*10465441SEvalZero }
1721*10465441SEvalZero }
1722*10465441SEvalZero }
1723*10465441SEvalZero } else if (match & REPLY_SERVICE_SRV) {
1724*10465441SEvalZero /* Read and compare to my SRV record */
1725*10465441SEvalZero u16_t field16, len, read_pos;
1726*10465441SEvalZero struct mdns_domain known_ans, my_ans;
1727*10465441SEvalZero read_pos = ans.rd_offset;
1728*10465441SEvalZero do {
1729*10465441SEvalZero /* Check priority field */
1730*10465441SEvalZero len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos);
1731*10465441SEvalZero if (len != sizeof(field16) || lwip_ntohs(field16) != SRV_PRIORITY) {
1732*10465441SEvalZero break;
1733*10465441SEvalZero }
1734*10465441SEvalZero read_pos += len;
1735*10465441SEvalZero /* Check weight field */
1736*10465441SEvalZero len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos);
1737*10465441SEvalZero if (len != sizeof(field16) || lwip_ntohs(field16) != SRV_WEIGHT) {
1738*10465441SEvalZero break;
1739*10465441SEvalZero }
1740*10465441SEvalZero read_pos += len;
1741*10465441SEvalZero /* Check port field */
1742*10465441SEvalZero len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos);
1743*10465441SEvalZero if (len != sizeof(field16) || lwip_ntohs(field16) != service->port) {
1744*10465441SEvalZero break;
1745*10465441SEvalZero }
1746*10465441SEvalZero read_pos += len;
1747*10465441SEvalZero /* Check host field */
1748*10465441SEvalZero len = mdns_readname(pkt->pbuf, read_pos, &known_ans);
1749*10465441SEvalZero mdns_build_host_domain(&my_ans, mdns);
1750*10465441SEvalZero if (len == MDNS_READNAME_ERROR || !mdns_domain_eq(&known_ans, &my_ans)) {
1751*10465441SEvalZero break;
1752*10465441SEvalZero }
1753*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: SRV\n"));
1754*10465441SEvalZero reply.serv_replies[i] &= ~REPLY_SERVICE_SRV;
1755*10465441SEvalZero } while (0);
1756*10465441SEvalZero } else if (match & REPLY_SERVICE_TXT) {
1757*10465441SEvalZero mdns_prepare_txtdata(service);
1758*10465441SEvalZero if (service->txtdata.length == ans.rd_length &&
1759*10465441SEvalZero pbuf_memcmp(pkt->pbuf, ans.rd_offset, service->txtdata.name, ans.rd_length) == 0) {
1760*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: TXT\n"));
1761*10465441SEvalZero reply.serv_replies[i] &= ~REPLY_SERVICE_TXT;
1762*10465441SEvalZero }
1763*10465441SEvalZero }
1764*10465441SEvalZero }
1765*10465441SEvalZero }
1766*10465441SEvalZero }
1767*10465441SEvalZero
1768*10465441SEvalZero mdns_send_outpacket(&reply, DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE);
1769*10465441SEvalZero
1770*10465441SEvalZero cleanup:
1771*10465441SEvalZero if (reply.pbuf) {
1772*10465441SEvalZero /* This should only happen if we fail to alloc/write question for legacy query */
1773*10465441SEvalZero pbuf_free(reply.pbuf);
1774*10465441SEvalZero reply.pbuf = NULL;
1775*10465441SEvalZero }
1776*10465441SEvalZero }
1777*10465441SEvalZero
1778*10465441SEvalZero /**
1779*10465441SEvalZero * Handle response MDNS packet
1780*10465441SEvalZero * Only prints debug for now. Will need more code to do conflict resolution.
1781*10465441SEvalZero */
1782*10465441SEvalZero static void
mdns_handle_response(struct mdns_packet * pkt)1783*10465441SEvalZero mdns_handle_response(struct mdns_packet *pkt)
1784*10465441SEvalZero {
1785*10465441SEvalZero struct mdns_host* mdns = NETIF_TO_HOST(pkt->netif);
1786*10465441SEvalZero
1787*10465441SEvalZero /* Ignore all questions */
1788*10465441SEvalZero while (pkt->questions_left) {
1789*10465441SEvalZero struct mdns_question q;
1790*10465441SEvalZero err_t res;
1791*10465441SEvalZero
1792*10465441SEvalZero res = mdns_read_question(pkt, &q);
1793*10465441SEvalZero if (res != ERR_OK) {
1794*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse question, skipping response packet\n"));
1795*10465441SEvalZero return;
1796*10465441SEvalZero }
1797*10465441SEvalZero }
1798*10465441SEvalZero
1799*10465441SEvalZero while (pkt->answers_left) {
1800*10465441SEvalZero struct mdns_answer ans;
1801*10465441SEvalZero err_t res;
1802*10465441SEvalZero
1803*10465441SEvalZero res = mdns_read_answer(pkt, &ans);
1804*10465441SEvalZero if (res != ERR_OK) {
1805*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse answer, skipping response packet\n"));
1806*10465441SEvalZero return;
1807*10465441SEvalZero }
1808*10465441SEvalZero
1809*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Answer for domain "));
1810*10465441SEvalZero mdns_domain_debug_print(&ans.info.domain);
1811*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass));
1812*10465441SEvalZero
1813*10465441SEvalZero /*"Apparently conflicting Multicast DNS responses received *before* the first probe packet is sent MUST
1814*10465441SEvalZero be silently ignored" so drop answer if we haven't started probing yet*/
1815*10465441SEvalZero if ((mdns->probing_state == MDNS_PROBING_ONGOING) && (mdns->probes_sent > 0)) {
1816*10465441SEvalZero struct mdns_domain domain;
1817*10465441SEvalZero u8_t i;
1818*10465441SEvalZero u8_t conflict = 0;
1819*10465441SEvalZero
1820*10465441SEvalZero res = mdns_build_host_domain(&domain, mdns);
1821*10465441SEvalZero if (res == ERR_OK && mdns_domain_eq(&ans.info.domain, &domain)) {
1822*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Probe response matches host domain!"));
1823*10465441SEvalZero conflict = 1;
1824*10465441SEvalZero }
1825*10465441SEvalZero
1826*10465441SEvalZero for (i = 0; i < MDNS_MAX_SERVICES; i++) {
1827*10465441SEvalZero struct mdns_service* service = mdns->services[i];
1828*10465441SEvalZero if (!service) {
1829*10465441SEvalZero continue;
1830*10465441SEvalZero }
1831*10465441SEvalZero res = mdns_build_service_domain(&domain, service, 1);
1832*10465441SEvalZero if ((res == ERR_OK) && mdns_domain_eq(&ans.info.domain, &domain)) {
1833*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Probe response matches service domain!"));
1834*10465441SEvalZero conflict = 1;
1835*10465441SEvalZero }
1836*10465441SEvalZero }
1837*10465441SEvalZero
1838*10465441SEvalZero if (conflict != 0) {
1839*10465441SEvalZero sys_untimeout(mdns_probe, pkt->netif);
1840*10465441SEvalZero if (mdns_name_result_cb != NULL) {
1841*10465441SEvalZero mdns_name_result_cb(pkt->netif, MDNS_PROBING_CONFLICT);
1842*10465441SEvalZero }
1843*10465441SEvalZero }
1844*10465441SEvalZero }
1845*10465441SEvalZero }
1846*10465441SEvalZero }
1847*10465441SEvalZero
1848*10465441SEvalZero /**
1849*10465441SEvalZero * Receive input function for MDNS packets.
1850*10465441SEvalZero * Handles both IPv4 and IPv6 UDP pcbs.
1851*10465441SEvalZero */
1852*10465441SEvalZero static void
mdns_recv(void * arg,struct udp_pcb * pcb,struct pbuf * p,const ip_addr_t * addr,u16_t port)1853*10465441SEvalZero mdns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
1854*10465441SEvalZero {
1855*10465441SEvalZero struct dns_hdr hdr;
1856*10465441SEvalZero struct mdns_packet packet;
1857*10465441SEvalZero struct netif *recv_netif = ip_current_input_netif();
1858*10465441SEvalZero u16_t offset = 0;
1859*10465441SEvalZero
1860*10465441SEvalZero LWIP_UNUSED_ARG(arg);
1861*10465441SEvalZero LWIP_UNUSED_ARG(pcb);
1862*10465441SEvalZero
1863*10465441SEvalZero LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Received IPv%d MDNS packet, len %d\n", IP_IS_V6(addr) ? 6 : 4, p->tot_len));
1864*10465441SEvalZero
1865*10465441SEvalZero if (NETIF_TO_HOST(recv_netif) == NULL) {
1866*10465441SEvalZero /* From netif not configured for MDNS */
1867*10465441SEvalZero goto dealloc;
1868*10465441SEvalZero }
1869*10465441SEvalZero
1870*10465441SEvalZero if (pbuf_copy_partial(p, &hdr, SIZEOF_DNS_HDR, offset) < SIZEOF_DNS_HDR) {
1871*10465441SEvalZero /* Too small */
1872*10465441SEvalZero goto dealloc;
1873*10465441SEvalZero }
1874*10465441SEvalZero offset += SIZEOF_DNS_HDR;
1875*10465441SEvalZero
1876*10465441SEvalZero if (DNS_HDR_GET_OPCODE(&hdr)) {
1877*10465441SEvalZero /* Ignore non-standard queries in multicast packets (RFC 6762, section 18.3) */
1878*10465441SEvalZero goto dealloc;
1879*10465441SEvalZero }
1880*10465441SEvalZero
1881*10465441SEvalZero memset(&packet, 0, sizeof(packet));
1882*10465441SEvalZero SMEMCPY(&packet.source_addr, addr, sizeof(packet.source_addr));
1883*10465441SEvalZero packet.source_port = port;
1884*10465441SEvalZero packet.netif = recv_netif;
1885*10465441SEvalZero packet.pbuf = p;
1886*10465441SEvalZero packet.parse_offset = offset;
1887*10465441SEvalZero packet.tx_id = lwip_ntohs(hdr.id);
1888*10465441SEvalZero packet.questions = packet.questions_left = lwip_ntohs(hdr.numquestions);
1889*10465441SEvalZero packet.answers = packet.answers_left = lwip_ntohs(hdr.numanswers) + lwip_ntohs(hdr.numauthrr) + lwip_ntohs(hdr.numextrarr);
1890*10465441SEvalZero
1891*10465441SEvalZero #if LWIP_IPV6
1892*10465441SEvalZero if (IP_IS_V6(ip_current_dest_addr())) {
1893*10465441SEvalZero /* instead of having one 'v6group' per netif, just compare zoneless here */
1894*10465441SEvalZero if (!ip_addr_cmp_zoneless(ip_current_dest_addr(), &v6group)) {
1895*10465441SEvalZero packet.recv_unicast = 1;
1896*10465441SEvalZero }
1897*10465441SEvalZero }
1898*10465441SEvalZero #endif
1899*10465441SEvalZero #if LWIP_IPV4
1900*10465441SEvalZero if (!IP_IS_V6(ip_current_dest_addr())) {
1901*10465441SEvalZero if (!ip_addr_cmp(ip_current_dest_addr(), &v4group)) {
1902*10465441SEvalZero packet.recv_unicast = 1;
1903*10465441SEvalZero }
1904*10465441SEvalZero }
1905*10465441SEvalZero #endif
1906*10465441SEvalZero
1907*10465441SEvalZero if (hdr.flags1 & DNS_FLAG1_RESPONSE) {
1908*10465441SEvalZero mdns_handle_response(&packet);
1909*10465441SEvalZero } else {
1910*10465441SEvalZero mdns_handle_question(&packet);
1911*10465441SEvalZero }
1912*10465441SEvalZero
1913*10465441SEvalZero dealloc:
1914*10465441SEvalZero pbuf_free(p);
1915*10465441SEvalZero }
1916*10465441SEvalZero
1917*10465441SEvalZero #if LWIP_NETIF_EXT_STATUS_CALLBACK && MDNS_RESP_USENETIF_EXTCALLBACK
1918*10465441SEvalZero static void
mdns_netif_ext_status_callback(struct netif * netif,netif_nsc_reason_t reason,const netif_ext_callback_args_t * args)1919*10465441SEvalZero mdns_netif_ext_status_callback(struct netif *netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t *args)
1920*10465441SEvalZero {
1921*10465441SEvalZero LWIP_UNUSED_ARG(args);
1922*10465441SEvalZero
1923*10465441SEvalZero /* MDNS enabled on netif? */
1924*10465441SEvalZero if (NETIF_TO_HOST(netif) == NULL) {
1925*10465441SEvalZero return;
1926*10465441SEvalZero }
1927*10465441SEvalZero
1928*10465441SEvalZero if (reason & LWIP_NSC_STATUS_CHANGED) {
1929*10465441SEvalZero if (args->status_changed.state != 0) {
1930*10465441SEvalZero mdns_resp_restart(netif);
1931*10465441SEvalZero }
1932*10465441SEvalZero /* TODO: send goodbye message */
1933*10465441SEvalZero }
1934*10465441SEvalZero if (reason & LWIP_NSC_LINK_CHANGED) {
1935*10465441SEvalZero if (args->link_changed.state != 0) {
1936*10465441SEvalZero mdns_resp_restart(netif);
1937*10465441SEvalZero }
1938*10465441SEvalZero }
1939*10465441SEvalZero if (reason & (LWIP_NSC_IPV4_ADDRESS_CHANGED | LWIP_NSC_IPV4_GATEWAY_CHANGED |
1940*10465441SEvalZero LWIP_NSC_IPV4_NETMASK_CHANGED | LWIP_NSC_IPV4_SETTINGS_CHANGED |
1941*10465441SEvalZero LWIP_NSC_IPV6_SET | LWIP_NSC_IPV6_ADDR_STATE_CHANGED)) {
1942*10465441SEvalZero mdns_resp_announce(netif);
1943*10465441SEvalZero }
1944*10465441SEvalZero }
1945*10465441SEvalZero #endif /* LWIP_NETIF_EXT_STATUS_CALLBACK && MDNS_RESP_USENETIF_EXTCALLBACK */
1946*10465441SEvalZero
1947*10465441SEvalZero static err_t
mdns_send_probe(struct netif * netif,const ip_addr_t * destination)1948*10465441SEvalZero mdns_send_probe(struct netif* netif, const ip_addr_t *destination)
1949*10465441SEvalZero {
1950*10465441SEvalZero struct mdns_host* mdns;
1951*10465441SEvalZero struct mdns_outpacket pkt;
1952*10465441SEvalZero struct mdns_domain domain;
1953*10465441SEvalZero u8_t i;
1954*10465441SEvalZero err_t res;
1955*10465441SEvalZero
1956*10465441SEvalZero mdns = NETIF_TO_HOST(netif);
1957*10465441SEvalZero
1958*10465441SEvalZero memset(&pkt, 0, sizeof(pkt));
1959*10465441SEvalZero pkt.netif = netif;
1960*10465441SEvalZero
1961*10465441SEvalZero /* Add unicast questions with rtype ANY for all our desired records */
1962*10465441SEvalZero mdns_build_host_domain(&domain, mdns);
1963*10465441SEvalZero res = mdns_add_question(&pkt, &domain, DNS_RRTYPE_ANY, DNS_RRCLASS_IN, 1);
1964*10465441SEvalZero if (res != ERR_OK) {
1965*10465441SEvalZero goto cleanup;
1966*10465441SEvalZero }
1967*10465441SEvalZero pkt.questions++;
1968*10465441SEvalZero for (i = 0; i < MDNS_MAX_SERVICES; i++) {
1969*10465441SEvalZero struct mdns_service* service = mdns->services[i];
1970*10465441SEvalZero if (!service) {
1971*10465441SEvalZero continue;
1972*10465441SEvalZero }
1973*10465441SEvalZero mdns_build_service_domain(&domain, service, 1);
1974*10465441SEvalZero res = mdns_add_question(&pkt, &domain, DNS_RRTYPE_ANY, DNS_RRCLASS_IN, 1);
1975*10465441SEvalZero if (res != ERR_OK) {
1976*10465441SEvalZero goto cleanup;
1977*10465441SEvalZero }
1978*10465441SEvalZero pkt.questions++;
1979*10465441SEvalZero }
1980*10465441SEvalZero
1981*10465441SEvalZero /* Add answers to the questions above into the authority section for tiebreaking */
1982*10465441SEvalZero #if LWIP_IPV4
1983*10465441SEvalZero if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
1984*10465441SEvalZero pkt.host_replies = REPLY_HOST_A;
1985*10465441SEvalZero }
1986*10465441SEvalZero #endif
1987*10465441SEvalZero #if LWIP_IPV6
1988*10465441SEvalZero for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
1989*10465441SEvalZero if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
1990*10465441SEvalZero pkt.host_replies |= REPLY_HOST_AAAA;
1991*10465441SEvalZero }
1992*10465441SEvalZero }
1993*10465441SEvalZero #endif
1994*10465441SEvalZero
1995*10465441SEvalZero for (i = 0; i < MDNS_MAX_SERVICES; i++) {
1996*10465441SEvalZero struct mdns_service *serv = mdns->services[i];
1997*10465441SEvalZero if (serv) {
1998*10465441SEvalZero pkt.serv_replies[i] = REPLY_SERVICE_SRV | REPLY_SERVICE_TXT;
1999*10465441SEvalZero }
2000*10465441SEvalZero }
2001*10465441SEvalZero
2002*10465441SEvalZero pkt.tx_id = 0;
2003*10465441SEvalZero pkt.dest_port = LWIP_IANA_PORT_MDNS;
2004*10465441SEvalZero SMEMCPY(&pkt.dest_addr, destination, sizeof(pkt.dest_addr));
2005*10465441SEvalZero res = mdns_send_outpacket(&pkt, 0);
2006*10465441SEvalZero
2007*10465441SEvalZero cleanup:
2008*10465441SEvalZero if (pkt.pbuf) {
2009*10465441SEvalZero pbuf_free(pkt.pbuf);
2010*10465441SEvalZero pkt.pbuf = NULL;
2011*10465441SEvalZero }
2012*10465441SEvalZero return res;
2013*10465441SEvalZero }
2014*10465441SEvalZero
2015*10465441SEvalZero /**
2016*10465441SEvalZero * Timer callback for probing network.
2017*10465441SEvalZero */
2018*10465441SEvalZero static void
mdns_probe(void * arg)2019*10465441SEvalZero mdns_probe(void* arg)
2020*10465441SEvalZero {
2021*10465441SEvalZero struct netif *netif = (struct netif *)arg;
2022*10465441SEvalZero struct mdns_host* mdns = NETIF_TO_HOST(netif);
2023*10465441SEvalZero
2024*10465441SEvalZero if(mdns->probes_sent >= MDNS_PROBE_COUNT) {
2025*10465441SEvalZero /* probing successful, announce the new name */
2026*10465441SEvalZero mdns->probing_state = MDNS_PROBING_COMPLETE;
2027*10465441SEvalZero mdns_resp_announce(netif);
2028*10465441SEvalZero if (mdns_name_result_cb != NULL) {
2029*10465441SEvalZero mdns_name_result_cb(netif, MDNS_PROBING_SUCCESSFUL);
2030*10465441SEvalZero }
2031*10465441SEvalZero } else {
2032*10465441SEvalZero #if LWIP_IPV4
2033*10465441SEvalZero /*if ipv4 wait with probing until address is set*/
2034*10465441SEvalZero if (!ip4_addr_isany_val(*netif_ip4_addr(netif)) &&
2035*10465441SEvalZero mdns_send_probe(netif, IP4_ADDR_ANY) == ERR_OK)
2036*10465441SEvalZero #endif
2037*10465441SEvalZero {
2038*10465441SEvalZero #if LWIP_IPV6
2039*10465441SEvalZero if (mdns_send_probe(netif, IP6_ADDR_ANY) == ERR_OK)
2040*10465441SEvalZero #endif
2041*10465441SEvalZero {
2042*10465441SEvalZero mdns->probes_sent++;
2043*10465441SEvalZero }
2044*10465441SEvalZero }
2045*10465441SEvalZero sys_timeout(MDNS_PROBE_DELAY_MS, mdns_probe, netif);
2046*10465441SEvalZero }
2047*10465441SEvalZero }
2048*10465441SEvalZero
2049*10465441SEvalZero /**
2050*10465441SEvalZero * @ingroup mdns
2051*10465441SEvalZero * Activate MDNS responder for a network interface.
2052*10465441SEvalZero * @param netif The network interface to activate.
2053*10465441SEvalZero * @param hostname Name to use. Queries for <hostname>.local will be answered
2054*10465441SEvalZero * with the IP addresses of the netif. The hostname will be copied, the
2055*10465441SEvalZero * given pointer can be on the stack.
2056*10465441SEvalZero * @param dns_ttl Validity time in seconds to send out for IP address data in DNS replies
2057*10465441SEvalZero * @return ERR_OK if netif was added, an err_t otherwise
2058*10465441SEvalZero */
2059*10465441SEvalZero err_t
mdns_resp_add_netif(struct netif * netif,const char * hostname,u32_t dns_ttl)2060*10465441SEvalZero mdns_resp_add_netif(struct netif *netif, const char *hostname, u32_t dns_ttl)
2061*10465441SEvalZero {
2062*10465441SEvalZero err_t res;
2063*10465441SEvalZero struct mdns_host *mdns;
2064*10465441SEvalZero
2065*10465441SEvalZero LWIP_ASSERT_CORE_LOCKED();
2066*10465441SEvalZero LWIP_ERROR("mdns_resp_add_netif: netif != NULL", (netif != NULL), return ERR_VAL);
2067*10465441SEvalZero LWIP_ERROR("mdns_resp_add_netif: Hostname too long", (strlen(hostname) <= MDNS_LABEL_MAXLEN), return ERR_VAL);
2068*10465441SEvalZero
2069*10465441SEvalZero LWIP_ASSERT("mdns_resp_add_netif: Double add", NETIF_TO_HOST(netif) == NULL);
2070*10465441SEvalZero mdns = (struct mdns_host *) mem_calloc(1, sizeof(struct mdns_host));
2071*10465441SEvalZero LWIP_ERROR("mdns_resp_add_netif: Alloc failed", (mdns != NULL), return ERR_MEM);
2072*10465441SEvalZero
2073*10465441SEvalZero netif_set_client_data(netif, mdns_netif_client_id, mdns);
2074*10465441SEvalZero
2075*10465441SEvalZero MEMCPY(&mdns->name, hostname, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(hostname)));
2076*10465441SEvalZero mdns->dns_ttl = dns_ttl;
2077*10465441SEvalZero mdns->probes_sent = 0;
2078*10465441SEvalZero mdns->probing_state = MDNS_PROBING_NOT_STARTED;
2079*10465441SEvalZero
2080*10465441SEvalZero /* Join multicast groups */
2081*10465441SEvalZero #if LWIP_IPV4
2082*10465441SEvalZero res = igmp_joingroup_netif(netif, ip_2_ip4(&v4group));
2083*10465441SEvalZero if (res != ERR_OK) {
2084*10465441SEvalZero goto cleanup;
2085*10465441SEvalZero }
2086*10465441SEvalZero #endif
2087*10465441SEvalZero #if LWIP_IPV6
2088*10465441SEvalZero res = mld6_joingroup_netif(netif, ip_2_ip6(&v6group));
2089*10465441SEvalZero if (res != ERR_OK) {
2090*10465441SEvalZero goto cleanup;
2091*10465441SEvalZero }
2092*10465441SEvalZero #endif
2093*10465441SEvalZero
2094*10465441SEvalZero mdns_resp_restart(netif);
2095*10465441SEvalZero
2096*10465441SEvalZero return ERR_OK;
2097*10465441SEvalZero
2098*10465441SEvalZero cleanup:
2099*10465441SEvalZero mem_free(mdns);
2100*10465441SEvalZero netif_set_client_data(netif, mdns_netif_client_id, NULL);
2101*10465441SEvalZero return res;
2102*10465441SEvalZero }
2103*10465441SEvalZero
2104*10465441SEvalZero /**
2105*10465441SEvalZero * @ingroup mdns
2106*10465441SEvalZero * Stop responding to MDNS queries on this interface, leave multicast groups,
2107*10465441SEvalZero * and free the helper structure and any of its services.
2108*10465441SEvalZero * @param netif The network interface to remove.
2109*10465441SEvalZero * @return ERR_OK if netif was removed, an err_t otherwise
2110*10465441SEvalZero */
2111*10465441SEvalZero err_t
mdns_resp_remove_netif(struct netif * netif)2112*10465441SEvalZero mdns_resp_remove_netif(struct netif *netif)
2113*10465441SEvalZero {
2114*10465441SEvalZero int i;
2115*10465441SEvalZero struct mdns_host *mdns;
2116*10465441SEvalZero
2117*10465441SEvalZero LWIP_ASSERT_CORE_LOCKED();
2118*10465441SEvalZero LWIP_ASSERT("mdns_resp_remove_netif: Null pointer", netif);
2119*10465441SEvalZero mdns = NETIF_TO_HOST(netif);
2120*10465441SEvalZero LWIP_ERROR("mdns_resp_remove_netif: Not an active netif", (mdns != NULL), return ERR_VAL);
2121*10465441SEvalZero
2122*10465441SEvalZero if (mdns->probing_state == MDNS_PROBING_ONGOING) {
2123*10465441SEvalZero sys_untimeout(mdns_probe, netif);
2124*10465441SEvalZero }
2125*10465441SEvalZero
2126*10465441SEvalZero for (i = 0; i < MDNS_MAX_SERVICES; i++) {
2127*10465441SEvalZero struct mdns_service *service = mdns->services[i];
2128*10465441SEvalZero if (service) {
2129*10465441SEvalZero mem_free(service);
2130*10465441SEvalZero }
2131*10465441SEvalZero }
2132*10465441SEvalZero
2133*10465441SEvalZero /* Leave multicast groups */
2134*10465441SEvalZero #if LWIP_IPV4
2135*10465441SEvalZero igmp_leavegroup_netif(netif, ip_2_ip4(&v4group));
2136*10465441SEvalZero #endif
2137*10465441SEvalZero #if LWIP_IPV6
2138*10465441SEvalZero mld6_leavegroup_netif(netif, ip_2_ip6(&v6group));
2139*10465441SEvalZero #endif
2140*10465441SEvalZero
2141*10465441SEvalZero mem_free(mdns);
2142*10465441SEvalZero netif_set_client_data(netif, mdns_netif_client_id, NULL);
2143*10465441SEvalZero return ERR_OK;
2144*10465441SEvalZero }
2145*10465441SEvalZero
2146*10465441SEvalZero /**
2147*10465441SEvalZero * @ingroup mdns
2148*10465441SEvalZero * Update MDNS hostname for a network interface.
2149*10465441SEvalZero * @param netif The network interface to activate.
2150*10465441SEvalZero * @param hostname Name to use. Queries for <hostname>.local will be answered
2151*10465441SEvalZero * with the IP addresses of the netif. The hostname will be copied, the
2152*10465441SEvalZero * given pointer can be on the stack.
2153*10465441SEvalZero * @return ERR_OK if name could be set on netif, an err_t otherwise
2154*10465441SEvalZero */
2155*10465441SEvalZero err_t
mdns_resp_rename_netif(struct netif * netif,const char * hostname)2156*10465441SEvalZero mdns_resp_rename_netif(struct netif *netif, const char *hostname)
2157*10465441SEvalZero {
2158*10465441SEvalZero struct mdns_host *mdns;
2159*10465441SEvalZero size_t len;
2160*10465441SEvalZero
2161*10465441SEvalZero LWIP_ASSERT_CORE_LOCKED();
2162*10465441SEvalZero len = strlen(hostname);
2163*10465441SEvalZero LWIP_ERROR("mdns_resp_rename_netif: netif != NULL", (netif != NULL), return ERR_VAL);
2164*10465441SEvalZero LWIP_ERROR("mdns_resp_rename_netif: Hostname too long", (len <= MDNS_LABEL_MAXLEN), return ERR_VAL);
2165*10465441SEvalZero mdns = NETIF_TO_HOST(netif);
2166*10465441SEvalZero LWIP_ERROR("mdns_resp_rename_netif: Not an mdns netif", (mdns != NULL), return ERR_VAL);
2167*10465441SEvalZero
2168*10465441SEvalZero MEMCPY(&mdns->name, hostname, LWIP_MIN(MDNS_LABEL_MAXLEN, len));
2169*10465441SEvalZero mdns->name[len] = '\0'; /* null termination in case new name is shorter than previous */
2170*10465441SEvalZero
2171*10465441SEvalZero mdns_resp_restart(netif);
2172*10465441SEvalZero
2173*10465441SEvalZero return ERR_OK;
2174*10465441SEvalZero }
2175*10465441SEvalZero
2176*10465441SEvalZero /**
2177*10465441SEvalZero * @ingroup mdns
2178*10465441SEvalZero * Add a service to the selected network interface.
2179*10465441SEvalZero * @param netif The network interface to publish this service on
2180*10465441SEvalZero * @param name The name of the service
2181*10465441SEvalZero * @param service The service type, like "_http"
2182*10465441SEvalZero * @param proto The service protocol, DNSSD_PROTO_TCP for TCP ("_tcp") and DNSSD_PROTO_UDP
2183*10465441SEvalZero * for others ("_udp")
2184*10465441SEvalZero * @param port The port the service listens to
2185*10465441SEvalZero * @param dns_ttl Validity time in seconds to send out for service data in DNS replies
2186*10465441SEvalZero * @param txt_fn Callback to get TXT data. Will be called each time a TXT reply is created to
2187*10465441SEvalZero * allow dynamic replies.
2188*10465441SEvalZero * @param txt_data Userdata pointer for txt_fn
2189*10465441SEvalZero * @return service_id if the service was added to the netif, an err_t otherwise
2190*10465441SEvalZero */
2191*10465441SEvalZero s8_t
mdns_resp_add_service(struct netif * netif,const char * name,const char * service,enum mdns_sd_proto proto,u16_t port,u32_t dns_ttl,service_get_txt_fn_t txt_fn,void * txt_data)2192*10465441SEvalZero mdns_resp_add_service(struct netif *netif, const char *name, const char *service, enum mdns_sd_proto proto, u16_t port, u32_t dns_ttl, service_get_txt_fn_t txt_fn, void *txt_data)
2193*10465441SEvalZero {
2194*10465441SEvalZero s8_t i;
2195*10465441SEvalZero s8_t slot = -1;
2196*10465441SEvalZero struct mdns_service *srv;
2197*10465441SEvalZero struct mdns_host *mdns;
2198*10465441SEvalZero
2199*10465441SEvalZero LWIP_ASSERT_CORE_LOCKED();
2200*10465441SEvalZero LWIP_ASSERT("mdns_resp_add_service: netif != NULL", netif);
2201*10465441SEvalZero mdns = NETIF_TO_HOST(netif);
2202*10465441SEvalZero LWIP_ERROR("mdns_resp_add_service: Not an mdns netif", (mdns != NULL), return ERR_VAL);
2203*10465441SEvalZero
2204*10465441SEvalZero LWIP_ERROR("mdns_resp_add_service: Name too long", (strlen(name) <= MDNS_LABEL_MAXLEN), return ERR_VAL);
2205*10465441SEvalZero LWIP_ERROR("mdns_resp_add_service: Service too long", (strlen(service) <= MDNS_LABEL_MAXLEN), return ERR_VAL);
2206*10465441SEvalZero LWIP_ERROR("mdns_resp_add_service: Bad proto (need TCP or UDP)", (proto == DNSSD_PROTO_TCP || proto == DNSSD_PROTO_UDP), return ERR_VAL);
2207*10465441SEvalZero
2208*10465441SEvalZero for (i = 0; i < MDNS_MAX_SERVICES; i++) {
2209*10465441SEvalZero if (mdns->services[i] == NULL) {
2210*10465441SEvalZero slot = i;
2211*10465441SEvalZero break;
2212*10465441SEvalZero }
2213*10465441SEvalZero }
2214*10465441SEvalZero LWIP_ERROR("mdns_resp_add_service: Service list full (increase MDNS_MAX_SERVICES)", (slot >= 0), return ERR_MEM);
2215*10465441SEvalZero
2216*10465441SEvalZero srv = (struct mdns_service *)mem_calloc(1, sizeof(struct mdns_service));
2217*10465441SEvalZero LWIP_ERROR("mdns_resp_add_service: Alloc failed", (srv != NULL), return ERR_MEM);
2218*10465441SEvalZero
2219*10465441SEvalZero MEMCPY(&srv->name, name, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(name)));
2220*10465441SEvalZero MEMCPY(&srv->service, service, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(service)));
2221*10465441SEvalZero srv->txt_fn = txt_fn;
2222*10465441SEvalZero srv->txt_userdata = txt_data;
2223*10465441SEvalZero srv->proto = (u16_t)proto;
2224*10465441SEvalZero srv->port = port;
2225*10465441SEvalZero srv->dns_ttl = dns_ttl;
2226*10465441SEvalZero
2227*10465441SEvalZero mdns->services[slot] = srv;
2228*10465441SEvalZero
2229*10465441SEvalZero mdns_resp_restart(netif);
2230*10465441SEvalZero
2231*10465441SEvalZero return slot;
2232*10465441SEvalZero }
2233*10465441SEvalZero
2234*10465441SEvalZero /**
2235*10465441SEvalZero * @ingroup mdns
2236*10465441SEvalZero * Delete a service on the selected network interface.
2237*10465441SEvalZero * @param netif The network interface on which service should be removed
2238*10465441SEvalZero * @param slot The service slot number returned by mdns_resp_add_service
2239*10465441SEvalZero * @return ERR_OK if the service was removed from the netif, an err_t otherwise
2240*10465441SEvalZero */
2241*10465441SEvalZero err_t
mdns_resp_del_service(struct netif * netif,s8_t slot)2242*10465441SEvalZero mdns_resp_del_service(struct netif *netif, s8_t slot)
2243*10465441SEvalZero {
2244*10465441SEvalZero struct mdns_host *mdns;
2245*10465441SEvalZero struct mdns_service *srv;
2246*10465441SEvalZero LWIP_ASSERT("mdns_resp_del_service: netif != NULL", netif);
2247*10465441SEvalZero mdns = NETIF_TO_HOST(netif);
2248*10465441SEvalZero LWIP_ERROR("mdns_resp_del_service: Not an mdns netif", (mdns != NULL), return ERR_VAL);
2249*10465441SEvalZero LWIP_ERROR("mdns_resp_del_service: Invalid Service ID", (slot >= 0) && (slot < MDNS_MAX_SERVICES), return ERR_VAL);
2250*10465441SEvalZero LWIP_ERROR("mdns_resp_del_service: Invalid Service ID", (mdns->services[slot] != NULL), return ERR_VAL);
2251*10465441SEvalZero
2252*10465441SEvalZero srv = mdns->services[slot];
2253*10465441SEvalZero mdns->services[slot] = NULL;
2254*10465441SEvalZero mem_free(srv);
2255*10465441SEvalZero return ERR_OK;
2256*10465441SEvalZero }
2257*10465441SEvalZero
2258*10465441SEvalZero /**
2259*10465441SEvalZero * @ingroup mdns
2260*10465441SEvalZero * Update name for an MDNS service.
2261*10465441SEvalZero * @param netif The network interface to activate.
2262*10465441SEvalZero * @param slot The service slot number returned by mdns_resp_add_service
2263*10465441SEvalZero * @param name The new name for the service
2264*10465441SEvalZero * @return ERR_OK if name could be set on service, an err_t otherwise
2265*10465441SEvalZero */
2266*10465441SEvalZero err_t
mdns_resp_rename_service(struct netif * netif,s8_t slot,const char * name)2267*10465441SEvalZero mdns_resp_rename_service(struct netif *netif, s8_t slot, const char *name)
2268*10465441SEvalZero {
2269*10465441SEvalZero struct mdns_service *srv;
2270*10465441SEvalZero struct mdns_host *mdns;
2271*10465441SEvalZero size_t len;
2272*10465441SEvalZero
2273*10465441SEvalZero LWIP_ASSERT_CORE_LOCKED();
2274*10465441SEvalZero len = strlen(name);
2275*10465441SEvalZero LWIP_ASSERT("mdns_resp_rename_service: netif != NULL", netif);
2276*10465441SEvalZero mdns = NETIF_TO_HOST(netif);
2277*10465441SEvalZero LWIP_ERROR("mdns_resp_rename_service: Not an mdns netif", (mdns != NULL), return ERR_VAL);
2278*10465441SEvalZero LWIP_ERROR("mdns_resp_rename_service: Name too long", (len <= MDNS_LABEL_MAXLEN), return ERR_VAL);
2279*10465441SEvalZero LWIP_ERROR("mdns_resp_rename_service: Invalid Service ID", (slot >= 0) && (slot < MDNS_MAX_SERVICES), return ERR_VAL);
2280*10465441SEvalZero LWIP_ERROR("mdns_resp_rename_service: Invalid Service ID", (mdns->services[slot] != NULL), return ERR_VAL);
2281*10465441SEvalZero
2282*10465441SEvalZero srv = mdns->services[slot];
2283*10465441SEvalZero
2284*10465441SEvalZero MEMCPY(&srv->name, name, LWIP_MIN(MDNS_LABEL_MAXLEN, len));
2285*10465441SEvalZero srv->name[len] = '\0'; /* null termination in case new name is shorter than previous */
2286*10465441SEvalZero
2287*10465441SEvalZero mdns_resp_restart(netif);
2288*10465441SEvalZero
2289*10465441SEvalZero return ERR_OK;
2290*10465441SEvalZero }
2291*10465441SEvalZero
2292*10465441SEvalZero /**
2293*10465441SEvalZero * @ingroup mdns
2294*10465441SEvalZero * Call this function from inside the service_get_txt_fn_t callback to add text data.
2295*10465441SEvalZero * Buffer for TXT data is 256 bytes, and each field is prefixed with a length byte.
2296*10465441SEvalZero * @param service The service provided to the get_txt callback
2297*10465441SEvalZero * @param txt String to add to the TXT field.
2298*10465441SEvalZero * @param txt_len Length of string
2299*10465441SEvalZero * @return ERR_OK if the string was added to the reply, an err_t otherwise
2300*10465441SEvalZero */
2301*10465441SEvalZero err_t
mdns_resp_add_service_txtitem(struct mdns_service * service,const char * txt,u8_t txt_len)2302*10465441SEvalZero mdns_resp_add_service_txtitem(struct mdns_service *service, const char *txt, u8_t txt_len)
2303*10465441SEvalZero {
2304*10465441SEvalZero LWIP_ASSERT_CORE_LOCKED();
2305*10465441SEvalZero LWIP_ASSERT("mdns_resp_add_service_txtitem: service != NULL", service);
2306*10465441SEvalZero
2307*10465441SEvalZero /* Use a mdns_domain struct to store txt chunks since it is the same encoding */
2308*10465441SEvalZero return mdns_domain_add_label(&service->txtdata, txt, txt_len);
2309*10465441SEvalZero }
2310*10465441SEvalZero
2311*10465441SEvalZero /**
2312*10465441SEvalZero * @ingroup mdns
2313*10465441SEvalZero * Send unsolicited answer containing all our known data
2314*10465441SEvalZero * @param netif The network interface to send on
2315*10465441SEvalZero */
2316*10465441SEvalZero void
mdns_resp_announce(struct netif * netif)2317*10465441SEvalZero mdns_resp_announce(struct netif *netif)
2318*10465441SEvalZero {
2319*10465441SEvalZero struct mdns_host* mdns;
2320*10465441SEvalZero LWIP_ASSERT_CORE_LOCKED();
2321*10465441SEvalZero LWIP_ERROR("mdns_resp_announce: netif != NULL", (netif != NULL), return);
2322*10465441SEvalZero
2323*10465441SEvalZero mdns = NETIF_TO_HOST(netif);
2324*10465441SEvalZero if (mdns == NULL) {
2325*10465441SEvalZero return;
2326*10465441SEvalZero }
2327*10465441SEvalZero
2328*10465441SEvalZero if (mdns->probing_state == MDNS_PROBING_COMPLETE) {
2329*10465441SEvalZero /* Announce on IPv6 and IPv4 */
2330*10465441SEvalZero #if LWIP_IPV6
2331*10465441SEvalZero mdns_announce(netif, IP6_ADDR_ANY);
2332*10465441SEvalZero #endif
2333*10465441SEvalZero #if LWIP_IPV4
2334*10465441SEvalZero if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
2335*10465441SEvalZero mdns_announce(netif, IP4_ADDR_ANY);
2336*10465441SEvalZero }
2337*10465441SEvalZero #endif
2338*10465441SEvalZero } /* else: ip address changed while probing was ongoing? @todo reset counter to restart? */
2339*10465441SEvalZero }
2340*10465441SEvalZero
2341*10465441SEvalZero /** Register a callback function that is called if probing is completed successfully
2342*10465441SEvalZero * or with a conflict. */
2343*10465441SEvalZero void
mdns_resp_register_name_result_cb(mdns_name_result_cb_t cb)2344*10465441SEvalZero mdns_resp_register_name_result_cb(mdns_name_result_cb_t cb)
2345*10465441SEvalZero {
2346*10465441SEvalZero mdns_name_result_cb = cb;
2347*10465441SEvalZero }
2348*10465441SEvalZero
2349*10465441SEvalZero /**
2350*10465441SEvalZero * @ingroup mdns
2351*10465441SEvalZero * Restart mdns responder. Call this when cable is connected after being disconnected or
2352*10465441SEvalZero * administrative interface is set up after being down
2353*10465441SEvalZero * @param netif The network interface to send on
2354*10465441SEvalZero */
2355*10465441SEvalZero void
mdns_resp_restart(struct netif * netif)2356*10465441SEvalZero mdns_resp_restart(struct netif *netif)
2357*10465441SEvalZero {
2358*10465441SEvalZero struct mdns_host* mdns;
2359*10465441SEvalZero LWIP_ASSERT_CORE_LOCKED();
2360*10465441SEvalZero LWIP_ERROR("mdns_resp_restart: netif != NULL", (netif != NULL), return);
2361*10465441SEvalZero
2362*10465441SEvalZero mdns = NETIF_TO_HOST(netif);
2363*10465441SEvalZero if (mdns == NULL) {
2364*10465441SEvalZero return;
2365*10465441SEvalZero }
2366*10465441SEvalZero
2367*10465441SEvalZero if (mdns->probing_state == MDNS_PROBING_ONGOING) {
2368*10465441SEvalZero sys_untimeout(mdns_probe, netif);
2369*10465441SEvalZero }
2370*10465441SEvalZero /* @todo if we've failed 15 times within a 10 second period we MUST wait 5 seconds (or wait 5 seconds every time except first)*/
2371*10465441SEvalZero mdns->probes_sent = 0;
2372*10465441SEvalZero mdns->probing_state = MDNS_PROBING_ONGOING;
2373*10465441SEvalZero sys_timeout(MDNS_INITIAL_PROBE_DELAY_MS, mdns_probe, netif);
2374*10465441SEvalZero }
2375*10465441SEvalZero
2376*10465441SEvalZero /**
2377*10465441SEvalZero * @ingroup mdns
2378*10465441SEvalZero * Initiate MDNS responder. Will open UDP sockets on port 5353
2379*10465441SEvalZero */
2380*10465441SEvalZero void
mdns_resp_init(void)2381*10465441SEvalZero mdns_resp_init(void)
2382*10465441SEvalZero {
2383*10465441SEvalZero err_t res;
2384*10465441SEvalZero
2385*10465441SEvalZero /* LWIP_ASSERT_CORE_LOCKED(); is checked by udp_new() */
2386*10465441SEvalZero
2387*10465441SEvalZero mdns_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
2388*10465441SEvalZero LWIP_ASSERT("Failed to allocate pcb", mdns_pcb != NULL);
2389*10465441SEvalZero #if LWIP_MULTICAST_TX_OPTIONS
2390*10465441SEvalZero udp_set_multicast_ttl(mdns_pcb, MDNS_TTL);
2391*10465441SEvalZero #else
2392*10465441SEvalZero mdns_pcb->ttl = MDNS_TTL;
2393*10465441SEvalZero #endif
2394*10465441SEvalZero res = udp_bind(mdns_pcb, IP_ANY_TYPE, LWIP_IANA_PORT_MDNS);
2395*10465441SEvalZero LWIP_UNUSED_ARG(res); /* in case of LWIP_NOASSERT */
2396*10465441SEvalZero LWIP_ASSERT("Failed to bind pcb", res == ERR_OK);
2397*10465441SEvalZero udp_recv(mdns_pcb, mdns_recv, NULL);
2398*10465441SEvalZero
2399*10465441SEvalZero mdns_netif_client_id = netif_alloc_client_data_id();
2400*10465441SEvalZero
2401*10465441SEvalZero #if MDNS_RESP_USENETIF_EXTCALLBACK
2402*10465441SEvalZero /* register for netif events when started on first netif */
2403*10465441SEvalZero netif_add_ext_callback(&netif_callback, mdns_netif_ext_status_callback);
2404*10465441SEvalZero #endif
2405*10465441SEvalZero }
2406*10465441SEvalZero
2407*10465441SEvalZero #endif /* LWIP_MDNS_RESPONDER */
2408