1 /**
2  * @file
3  * MDNS responder implementation - domain related functionalities
4  */
5 
6 /*
7  * Copyright (c) 2015 Verisure Innovation AB
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without modification,
11  * are permitted provided that the following conditions are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright notice,
14  *    this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright notice,
16  *    this list of conditions and the following disclaimer in the documentation
17  *    and/or other materials provided with the distribution.
18  * 3. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
22  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
24  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
26  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
29  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
30  * OF SUCH DAMAGE.
31  *
32  * This file is part of the lwIP TCP/IP stack.
33  *
34  * Author: Erik Ekman <erik@kryo.se>
35  * Author: Jasper Verschueren <jasper.verschueren@apart-audio.com>
36  *
37  */
38 
39 #include "lwip/apps/mdns.h"
40 #include "lwip/apps/mdns_domain.h"
41 #include "lwip/apps/mdns_priv.h"
42 #include "lwip/prot/dns.h"
43 
44 #include <string.h>
45 
46 #if LWIP_IPV6
47 #include "lwip/prot/ip6.h"
48 #endif
49 
50 #if LWIP_MDNS_RESPONDER
51 
52 /* Stored offsets to beginning of domain names
53  * Used for compression.
54  */
55 #define DOMAIN_JUMP_SIZE 2
56 #define DOMAIN_JUMP 0xc000
57 
58 #define TOPDOMAIN_LOCAL "local"
59 
60 #define REVERSE_PTR_TOPDOMAIN "arpa"
61 #define REVERSE_PTR_V4_DOMAIN "in-addr"
62 #define REVERSE_PTR_V6_DOMAIN "ip6"
63 
64 static const char *dnssd_protos[] = {
65   "_udp", /* DNSSD_PROTO_UDP */
66   "_tcp", /* DNSSD_PROTO_TCP */
67 };
68 
69 /* forward declarations (function prototypes)*/
70 static err_t mdns_domain_add_label_base(struct mdns_domain *domain, u8_t len);
71 static err_t mdns_domain_add_label_pbuf(struct mdns_domain *domain,
72                                         const struct pbuf *p, u16_t offset,
73                                         u8_t len);
74 static u16_t mdns_readname_loop(struct pbuf *p, u16_t offset,
75                                 struct mdns_domain *domain, unsigned depth);
76 static err_t mdns_add_dotlocal(struct mdns_domain *domain);
77 
78 
79 static err_t
mdns_domain_add_label_base(struct mdns_domain * domain,u8_t len)80 mdns_domain_add_label_base(struct mdns_domain *domain, u8_t len)
81 {
82   if (len > MDNS_LABEL_MAXLEN) {
83     return ERR_VAL;
84   }
85   if (len > 0 && (1 + len + domain->length >= MDNS_DOMAIN_MAXLEN)) {
86     return ERR_VAL;
87   }
88   /* Allow only zero marker on last byte */
89   if (len == 0 && (1 + domain->length > MDNS_DOMAIN_MAXLEN)) {
90     return ERR_VAL;
91   }
92   domain->name[domain->length] = len;
93   domain->length++;
94   return ERR_OK;
95 }
96 
97 /**
98  * Add a label part to a domain
99  * @param domain The domain to add a label to
100  * @param label The label to add, like &lt;hostname&gt;, 'local', 'com' or ''
101  * @param len The length of the label
102  * @return ERR_OK on success, an err_t otherwise if label too long
103  */
104 err_t
mdns_domain_add_label(struct mdns_domain * domain,const char * label,u8_t len)105 mdns_domain_add_label(struct mdns_domain *domain, const char *label, u8_t len)
106 {
107   err_t err = mdns_domain_add_label_base(domain, len);
108   if (err != ERR_OK) {
109     return err;
110   }
111   if (len) {
112     MEMCPY(&domain->name[domain->length], label, len);
113     domain->length += len;
114   }
115   return ERR_OK;
116 }
117 
118 /**
119  * Add a label part to a domain (@see mdns_domain_add_label but copy directly from pbuf)
120  */
121 static err_t
mdns_domain_add_label_pbuf(struct mdns_domain * domain,const struct pbuf * p,u16_t offset,u8_t len)122 mdns_domain_add_label_pbuf(struct mdns_domain *domain, const struct pbuf *p, u16_t offset, u8_t len)
123 {
124   err_t err = mdns_domain_add_label_base(domain, len);
125   if (err != ERR_OK) {
126     return err;
127   }
128   if (len) {
129     if (pbuf_copy_partial(p, &domain->name[domain->length], len, offset) != len) {
130       /* take back the ++ done before */
131       domain->length--;
132       return ERR_ARG;
133     }
134     domain->length += len;
135   }
136   return ERR_OK;
137 }
138 
139 /**
140  * Add a partial domain to a domain
141  * @param domain The domain to add a label to
142  * @param source The domain to add, like &lt;\\x09_services\\007_dns-sd\\000&gt;
143  * @return ERR_OK on success, an err_t otherwise if label too long
144  */
145 err_t
mdns_domain_add_domain(struct mdns_domain * domain,struct mdns_domain * source)146 mdns_domain_add_domain(struct mdns_domain *domain, struct mdns_domain *source)
147 {
148   u16_t len = source->length;
149   if (len > 0 && (1 + len + domain->length >= MDNS_DOMAIN_MAXLEN)) {
150     return ERR_VAL;
151   }
152   /* Allow only zero marker on last byte */
153   if (len == 0 && (1 + domain->length > MDNS_DOMAIN_MAXLEN)) {
154     return ERR_VAL;
155   }
156   if (len) {
157     /* Copy partial domain */
158     MEMCPY(&domain->name[domain->length], source->name, len);
159     domain->length += len;
160   } else {
161     /* Add zero marker */
162     domain->name[domain->length] = 0;
163     domain->length++;
164   }
165   return ERR_OK;
166 }
167 
168 /**
169  * Add a string domain to a domain
170  * @param domain The domain to add a label to
171  * @param source The string to add, like &lt;_services._dns-sd&gt;
172  * @return ERR_OK on success, an err_t otherwise if label too long
173  */
174 err_t
mdns_domain_add_string(struct mdns_domain * domain,const char * source)175 mdns_domain_add_string(struct mdns_domain *domain, const char *source)
176 {
177   u8_t *len = &domain->name[domain->length];
178   u8_t *end = &domain->name[MDNS_DOMAIN_MAXLEN];
179   u8_t *start = len + 1;
180   *len = 0;
181   while (*source && start < end) {
182       if (*source == '.') {
183         len = start++;
184         *len = 0;
185         source++;
186       } else {
187         *start++ = *source++;
188         *len = *len + 1;
189       }
190   }
191   if (start == end) {
192       return ERR_VAL;
193   }
194   domain->length = (u16_t)(start - &domain->name[0]);
195   return ERR_OK;
196 }
197 
198 
199 /**
200  * Internal readname function with max 6 levels of recursion following jumps
201  * while decompressing name
202  */
203 static u16_t
mdns_readname_loop(struct pbuf * p,u16_t offset,struct mdns_domain * domain,unsigned depth)204 mdns_readname_loop(struct pbuf *p, u16_t offset, struct mdns_domain *domain, unsigned depth)
205 {
206   u8_t c;
207 
208   do {
209     if (depth > 5) {
210       /* Too many jumps */
211       return MDNS_READNAME_ERROR;
212     }
213 
214     c = pbuf_get_at(p, offset);
215     offset++;
216 
217     /* is this a compressed label? */
218     if ((c & 0xc0) == 0xc0) {
219       u16_t jumpaddr;
220       if (offset >= p->tot_len) {
221         /* Make sure both jump bytes fit in the packet */
222         return MDNS_READNAME_ERROR;
223       }
224       jumpaddr = (((c & 0x3f) << 8) | (pbuf_get_at(p, offset) & 0xff));
225       offset++;
226       if (jumpaddr >= SIZEOF_DNS_HDR && jumpaddr < p->tot_len) {
227         u16_t res;
228         /* Recursive call, maximum depth will be checked */
229         res = mdns_readname_loop(p, jumpaddr, domain, depth + 1);
230         /* Don't return offset since new bytes were not read (jumped to somewhere in packet) */
231         if (res == MDNS_READNAME_ERROR) {
232           return res;
233         }
234       } else {
235         return MDNS_READNAME_ERROR;
236       }
237       break;
238     }
239 
240     /* normal label */
241     if (c <= MDNS_LABEL_MAXLEN) {
242       err_t res;
243 
244       if (c + domain->length >= MDNS_DOMAIN_MAXLEN) {
245         return MDNS_READNAME_ERROR;
246       }
247       res = mdns_domain_add_label_pbuf(domain, p, offset, c);
248       if (res != ERR_OK) {
249         return MDNS_READNAME_ERROR;
250       }
251       offset += c;
252     } else {
253       /* bad length byte */
254       return MDNS_READNAME_ERROR;
255     }
256   } while (c != 0);
257 
258   return offset;
259 }
260 
261 /**
262  * Read possibly compressed domain name from packet buffer
263  * @param p The packet
264  * @param offset start position of domain name in packet
265  * @param domain The domain name destination
266  * @return The new offset after the domain, or MDNS_READNAME_ERROR
267  *         if reading failed
268  */
269 u16_t
mdns_readname(struct pbuf * p,u16_t offset,struct mdns_domain * domain)270 mdns_readname(struct pbuf *p, u16_t offset, struct mdns_domain *domain)
271 {
272   memset(domain, 0, sizeof(struct mdns_domain));
273   return mdns_readname_loop(p, offset, domain, 0);
274 }
275 
276 /**
277  * Print domain name to debug output
278  * @param domain The domain name
279  */
280 void
mdns_domain_debug_print(struct mdns_domain * domain)281 mdns_domain_debug_print(struct mdns_domain *domain)
282 {
283   u8_t *src = domain->name;
284   u8_t i;
285 
286   while (*src) {
287     u8_t label_len = *src;
288     src++;
289     for (i = 0; i < label_len; i++) {
290       LWIP_DEBUGF(MDNS_DEBUG, ("%c", src[i]));
291     }
292     src += label_len;
293     LWIP_DEBUGF(MDNS_DEBUG, ("."));
294   }
295 }
296 
297 /**
298  * Return 1 if contents of domains match (case-insensitive)
299  * @param a Domain name to compare 1
300  * @param b Domain name to compare 2
301  * @return 1 if domains are equal ignoring case, 0 otherwise
302  */
303 int
mdns_domain_eq(struct mdns_domain * a,struct mdns_domain * b)304 mdns_domain_eq(struct mdns_domain *a, struct mdns_domain *b)
305 {
306   u8_t *ptra, *ptrb;
307   u8_t len;
308   int res;
309 
310   if (a->length != b->length) {
311     return 0;
312   }
313 
314   ptra = a->name;
315   ptrb = b->name;
316   while (*ptra && *ptrb && ptra < &a->name[a->length]) {
317     if (*ptra != *ptrb) {
318       return 0;
319     }
320     len = *ptra;
321     ptra++;
322     ptrb++;
323     res = lwip_strnicmp((char *) ptra, (char *) ptrb, len);
324     if (res != 0) {
325       return 0;
326     }
327     ptra += len;
328     ptrb += len;
329   }
330   if (*ptra != *ptrb && ptra < &a->name[a->length]) {
331     return 0;
332   }
333   return 1;
334 }
335 
336 #if LWIP_IPV4
337 /**
338  * Build domain for reverse lookup of IPv4 address
339  * like 12.0.168.192.in-addr.arpa. for 192.168.0.12
340  * @param domain Where to write the domain name
341  * @param addr Pointer to an IPv4 address to encode
342  * @return ERR_OK if domain was written, an err_t otherwise
343  */
344 err_t
mdns_build_reverse_v4_domain(struct mdns_domain * domain,const ip4_addr_t * addr)345 mdns_build_reverse_v4_domain(struct mdns_domain *domain, const ip4_addr_t *addr)
346 {
347   int i;
348   err_t res;
349   const u8_t *ptr;
350 
351   LWIP_UNUSED_ARG(res);
352   if (!domain || !addr) {
353     return ERR_ARG;
354   }
355   memset(domain, 0, sizeof(struct mdns_domain));
356   ptr = (const u8_t *) addr;
357   for (i = sizeof(ip4_addr_t) - 1; i >= 0; i--) {
358     char buf[4];
359     u8_t val = ptr[i];
360 
361     lwip_itoa(buf, sizeof(buf), val);
362     res = mdns_domain_add_label(domain, buf, (u8_t)strlen(buf));
363     LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
364   }
365   res = mdns_domain_add_label(domain, REVERSE_PTR_V4_DOMAIN, (u8_t)(sizeof(REVERSE_PTR_V4_DOMAIN) - 1));
366   LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
367   res = mdns_domain_add_label(domain, REVERSE_PTR_TOPDOMAIN, (u8_t)(sizeof(REVERSE_PTR_TOPDOMAIN) - 1));
368   LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
369   res = mdns_domain_add_label(domain, NULL, 0);
370   LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
371 
372   return ERR_OK;
373 }
374 #endif
375 
376 #if LWIP_IPV6
377 /**
378  * Build domain for reverse lookup of IP address
379  * 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
380  * @param domain Where to write the domain name
381  * @param addr Pointer to an IPv6 address to encode
382  * @return ERR_OK if domain was written, an err_t otherwise
383  */
384 err_t
mdns_build_reverse_v6_domain(struct mdns_domain * domain,const ip6_addr_t * addr)385 mdns_build_reverse_v6_domain(struct mdns_domain *domain, const ip6_addr_t *addr)
386 {
387   int i;
388   err_t res;
389   const u8_t *ptr;
390   LWIP_UNUSED_ARG(res);
391   if (!domain || !addr) {
392     return ERR_ARG;
393   }
394   memset(domain, 0, sizeof(struct mdns_domain));
395   ptr = (const u8_t *) addr;
396   for (i = sizeof(ip6_addr_p_t) - 1; i >= 0; i--) {
397     char buf;
398     u8_t byte = ptr[i];
399     int j;
400     for (j = 0; j < 2; j++) {
401       if ((byte & 0x0F) < 0xA) {
402         buf = '0' + (byte & 0x0F);
403       } else {
404         buf = 'a' + (byte & 0x0F) - 0xA;
405       }
406       res = mdns_domain_add_label(domain, &buf, sizeof(buf));
407       LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
408       byte >>= 4;
409     }
410   }
411   res = mdns_domain_add_label(domain, REVERSE_PTR_V6_DOMAIN, (u8_t)(sizeof(REVERSE_PTR_V6_DOMAIN) - 1));
412   LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
413   res = mdns_domain_add_label(domain, REVERSE_PTR_TOPDOMAIN, (u8_t)(sizeof(REVERSE_PTR_TOPDOMAIN) - 1));
414   LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
415   res = mdns_domain_add_label(domain, NULL, 0);
416   LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
417 
418   return ERR_OK;
419 }
420 #endif
421 
422 /* Add .local. to domain */
423 static err_t
mdns_add_dotlocal(struct mdns_domain * domain)424 mdns_add_dotlocal(struct mdns_domain *domain)
425 {
426   err_t res = mdns_domain_add_label(domain, TOPDOMAIN_LOCAL, (u8_t)(sizeof(TOPDOMAIN_LOCAL) - 1));
427   LWIP_UNUSED_ARG(res);
428   LWIP_ERROR("mdns_add_dotlocal: Failed to add label", (res == ERR_OK), return res);
429   return mdns_domain_add_label(domain, NULL, 0);
430 }
431 
432 /**
433  * Build the \<hostname\>.local. domain name
434  * @param domain Where to write the domain name
435  * @param mdns TMDNS netif descriptor.
436  * @return ERR_OK if domain \<hostname\>.local. was written, an err_t otherwise
437  */
438 err_t
mdns_build_host_domain(struct mdns_domain * domain,struct mdns_host * mdns)439 mdns_build_host_domain(struct mdns_domain *domain, struct mdns_host *mdns)
440 {
441   err_t res;
442   LWIP_UNUSED_ARG(res);
443   memset(domain, 0, sizeof(struct mdns_domain));
444   LWIP_ERROR("mdns_build_host_domain: mdns != NULL", (mdns != NULL), return ERR_VAL);
445   res = mdns_domain_add_label(domain, mdns->name, (u8_t)strlen(mdns->name));
446   LWIP_ERROR("mdns_build_host_domain: Failed to add label", (res == ERR_OK), return res);
447   return mdns_add_dotlocal(domain);
448 }
449 
450 /**
451  * Build the lookup-all-services special DNS-SD domain name
452  * @param domain Where to write the domain name
453  * @return ERR_OK if domain _services._dns-sd._udp.local. was written, an err_t otherwise
454  */
455 err_t
mdns_build_dnssd_domain(struct mdns_domain * domain)456 mdns_build_dnssd_domain(struct mdns_domain *domain)
457 {
458   err_t res;
459   LWIP_UNUSED_ARG(res);
460   memset(domain, 0, sizeof(struct mdns_domain));
461   res = mdns_domain_add_label(domain, "_services", (u8_t)(sizeof("_services") - 1));
462   LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
463   res = mdns_domain_add_label(domain, "_dns-sd", (u8_t)(sizeof("_dns-sd") - 1));
464   LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
465   res = mdns_domain_add_label(domain, dnssd_protos[DNSSD_PROTO_UDP], (u8_t)strlen(dnssd_protos[DNSSD_PROTO_UDP]));
466   LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
467   return mdns_add_dotlocal(domain);
468 }
469 
470 /**
471  * Build domain name for a service
472  * @param domain Where to write the domain name
473  * @param service The service struct, containing service name, type and protocol
474  * @param include_name Whether to include the service name in the domain
475  * @return ERR_OK if domain was written. If service name is included,
476  *         \<name\>.\<type\>.\<proto\>.local. will be written, otherwise \<type\>.\<proto\>.local.
477  *         An err_t is returned on error.
478  */
479 err_t
mdns_build_service_domain(struct mdns_domain * domain,struct mdns_service * service,int include_name)480 mdns_build_service_domain(struct mdns_domain *domain, struct mdns_service *service, int include_name)
481 {
482   err_t res;
483   LWIP_UNUSED_ARG(res);
484   memset(domain, 0, sizeof(struct mdns_domain));
485   if (include_name) {
486     res = mdns_domain_add_label(domain, service->name, (u8_t)strlen(service->name));
487     LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
488   }
489   res = mdns_domain_add_label(domain, service->service, (u8_t)strlen(service->service));
490   LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
491   res = mdns_domain_add_label(domain, dnssd_protos[service->proto], (u8_t)strlen(dnssd_protos[service->proto]));
492   LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
493   return mdns_add_dotlocal(domain);
494 }
495 
496 #if LWIP_MDNS_SEARCH
497 /**
498  * Build domain name for a request
499  * @param domain Where to write the domain name
500  * @param request The request struct, containing service name, type and protocol
501  * @param include_name Whether to include the service name in the domain
502  * @return ERR_OK if domain was written. If service name is included,
503  *         \<name\>.\<type\>.\<proto\>.local. will be written, otherwise \<type\>.\<proto\>.local.
504  *         An err_t is returned on error.
505  */
506 err_t
mdns_build_request_domain(struct mdns_domain * domain,struct mdns_request * request,int include_name)507 mdns_build_request_domain(struct mdns_domain *domain, struct mdns_request *request, int include_name)
508 {
509   err_t res;
510   memset(domain, 0, sizeof(struct mdns_domain));
511   if (include_name) {
512     res = mdns_domain_add_label(domain, request->name, (u8_t)strlen(request->name));
513     LWIP_ERROR("mdns_build_request_domain: Failed to add label", (res == ERR_OK), return res);
514   }
515   res = mdns_domain_add_domain(domain, &request->service);
516   LWIP_ERROR("mdns_build_request_domain: Failed to add domain", (res == ERR_OK), return res);
517   res = mdns_domain_add_label(domain, dnssd_protos[request->proto], (u8_t)strlen(dnssd_protos[request->proto]));
518   LWIP_ERROR("mdns_build_request_domain: Failed to add label", (res == ERR_OK), return res);
519   return mdns_add_dotlocal(domain);
520 }
521 #endif
522 
523 /**
524  * Return bytes needed to write before jump for best result of compressing supplied domain
525  * against domain in outpacket starting at specified offset.
526  * If a match is found, offset is updated to where to jump to
527  * @param pbuf Pointer to pbuf with the partially constructed DNS packet
528  * @param offset Start position of a domain written earlier. If this location is suitable
529  *               for compression, the pointer is updated to where in the domain to jump to.
530  * @param domain The domain to write
531  * @return Number of bytes to write of the new domain before writing a jump to the offset.
532  *         If compression can not be done against this previous domain name, the full new
533  *         domain length is returned.
534  */
535 u16_t
mdns_compress_domain(struct pbuf * pbuf,u16_t * offset,struct mdns_domain * domain)536 mdns_compress_domain(struct pbuf *pbuf, u16_t *offset, struct mdns_domain *domain)
537 {
538   struct mdns_domain target;
539   u16_t target_end;
540   u8_t target_len;
541   u8_t writelen = 0;
542   u8_t *ptr;
543   if (pbuf == NULL) {
544     return domain->length;
545   }
546   target_end = mdns_readname(pbuf, *offset, &target);
547   if (target_end == MDNS_READNAME_ERROR) {
548     return domain->length;
549   }
550   target_len = (u8_t)(target_end - *offset);
551   ptr = domain->name;
552   while (writelen < domain->length) {
553     u8_t domainlen = (u8_t)(domain->length - writelen);
554     u8_t labellen;
555     if (domainlen <= target.length && domainlen > DOMAIN_JUMP_SIZE) {
556       /* Compare domains if target is long enough, and we have enough left of the domain */
557       u8_t targetpos = (u8_t)(target.length - domainlen);
558       if ((targetpos + DOMAIN_JUMP_SIZE) >= target_len) {
559         /* We are checking at or beyond a jump in the original, stop looking */
560         break;
561       }
562       if (target.length >= domainlen &&
563           memcmp(&domain->name[writelen], &target.name[targetpos], domainlen) == 0) {
564         *offset += targetpos;
565         return writelen;
566       }
567     }
568     /* Skip to next label in domain */
569     labellen = *ptr;
570     writelen += 1 + labellen;
571     ptr += 1 + labellen;
572   }
573   /* Nothing found */
574   return domain->length;
575 }
576 
577 /**
578  * Write domain to outpacket. Compression will be attempted,
579  * unless domain->skip_compression is set.
580  * @param outpkt The outpacket to write to
581  * @param domain The domain name to write
582  * @return ERR_OK on success, an err_t otherwise
583  */
584 err_t
mdns_write_domain(struct mdns_outpacket * outpkt,struct mdns_domain * domain)585 mdns_write_domain(struct mdns_outpacket *outpkt, struct mdns_domain *domain)
586 {
587   int i;
588   err_t res;
589   u16_t writelen = domain->length;
590   u16_t jump_offset = 0;
591   u16_t jump;
592 
593   if (!domain->skip_compression) {
594     for (i = 0; i < NUM_DOMAIN_OFFSETS; i++) {
595       u16_t offset = outpkt->domain_offsets[i];
596       if (offset) {
597         u16_t len = mdns_compress_domain(outpkt->pbuf, &offset, domain);
598         if (len < writelen) {
599           writelen = len;
600           jump_offset = offset;
601         }
602       }
603     }
604   }
605 
606   if (writelen) {
607     /* Write uncompressed part of name */
608     res = pbuf_take_at(outpkt->pbuf, domain->name, writelen, outpkt->write_offset);
609     if (res != ERR_OK) {
610       return res;
611     }
612 
613     /* Store offset of this new domain */
614     for (i = 0; i < NUM_DOMAIN_OFFSETS; i++) {
615       if (outpkt->domain_offsets[i] == 0) {
616         outpkt->domain_offsets[i] = outpkt->write_offset;
617         break;
618       }
619     }
620 
621     outpkt->write_offset += writelen;
622   }
623   if (jump_offset) {
624     /* Write jump */
625     jump = lwip_htons(DOMAIN_JUMP | jump_offset);
626     res = pbuf_take_at(outpkt->pbuf, &jump, DOMAIN_JUMP_SIZE, outpkt->write_offset);
627     if (res != ERR_OK) {
628       return res;
629     }
630     outpkt->write_offset += DOMAIN_JUMP_SIZE;
631   }
632   return ERR_OK;
633 }
634 
635 #endif /* LWIP_MDNS_RESPONDER */
636