1 /*
2 NICTA Public Software Licence
3 Version 1.0
4 
5 Copyright © 2004 National ICT Australia Ltd
6 
7 All rights reserved.
8 
9 By this licence, National ICT Australia Ltd (NICTA) grants permission,
10 free of charge, to any person who obtains a copy of this software
11 and any associated documentation files ("the Software") to use and
12 deal with the Software in source code and binary forms without
13 restriction, with or without modification, and to permit persons
14 to whom the Software is furnished to do so, provided that the
15 following conditions are met:
16 
17 - Redistributions of source code must retain the above copyright
18   notice, this list of conditions and the following disclaimers.
19 - Redistributions in binary form must reproduce the above copyright
20   notice, this list of conditions and the following disclaimers in
21   the documentation and/or other materials provided with the
22   distribution.
23 - The name of NICTA may not be used to endorse or promote products
24   derived from this Software without specific prior written permission.
25 
26 EXCEPT AS EXPRESSLY STATED IN THIS LICENCE AND TO THE FULL EXTENT
27 PERMITTED BY APPLICABLE LAW, THE SOFTWARE IS PROVIDED "AS-IS" AND
28 NICTA MAKES NO REPRESENTATIONS, WARRANTIES OR CONDITIONS OF ANY
29 KIND, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY
30 REPRESENTATIONS, WARRANTIES OR CONDITIONS REGARDING THE CONTENTS
31 OR ACCURACY OF THE SOFTWARE, OR OF TITLE, MERCHANTABILITY, FITNESS
32 FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, THE ABSENCE OF LATENT
33 OR OTHER DEFECTS, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR
34 NOT DISCOVERABLE.
35 
36 TO THE FULL EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL
37 NICTA BE LIABLE ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
38 NEGLIGENCE) FOR ANY LOSS OR DAMAGE WHATSOEVER, INCLUDING (WITHOUT
39 LIMITATION) LOSS OF PRODUCTION OR OPERATION TIME, LOSS, DAMAGE OR
40 CORRUPTION OF DATA OR RECORDS; OR LOSS OF ANTICIPATED SAVINGS,
41 OPPORTUNITY, REVENUE, PROFIT OR GOODWILL, OR OTHER ECONOMIC LOSS;
42 OR ANY SPECIAL, INCIDENTAL, INDIRECT, CONSEQUENTIAL, PUNITIVE OR
43 EXEMPLARY DAMAGES ARISING OUT OF OR IN CONNECTION WITH THIS LICENCE,
44 THE SOFTWARE OR THE USE OF THE SOFTWARE, EVEN IF NICTA HAS BEEN
45 ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
46 
47 If applicable legislation implies warranties or conditions, or
48 imposes obligations or liability on NICTA in respect of the Software
49 that cannot be wholly or partly excluded, restricted or modified,
50 NICTA's liability is limited, to the full extent permitted by the
51 applicable legislation, at its option, to:
52 
53 a. in the case of goods, any one or more of the following:
54   i.   the replacement of the goods or the supply of equivalent goods;
55   ii.  the repair of the goods;
56   iii. the payment of the cost of replacing the goods or of acquiring
57        equivalent goods;
58   iv.  the payment of the cost of having the goods repaired; or
59 b. in the case of services:
60   i.   the supplying of the services again; or
61   ii.  the payment of the cost of having the services supplied
62        again.
63  */
64 
65 /*
66 	NSSwitch Implementation of mDNS interface.
67 
68 	Andrew White (Andrew.White@nicta.com.au)
69 	May 2004
70  */
71 
72 #include <stdlib.h>
73 #include <stdio.h>
74 #include <string.h>
75 #include <errno.h>
76 #include <syslog.h>
77 #include <pthread.h>
78 #include <ctype.h>
79 
80 #include <sys/types.h>
81 #include <sys/time.h>
82 #include <sys/socket.h>
83 
84 #include <netinet/in.h>
85 
86 #include <arpa/inet.h>
87 #define BIND_8_COMPAT 1
88 #include <arpa/nameser.h>
89 
90 #include <dns_sd.h>
91 
92 
93 //----------
94 // Public functions
95 
96 /*
97 	Count the number of dots in a name string.
98  */
99 int
100 count_dots (const char * name);
101 
102 
103 /*
104 	Test whether a domain name is local.
105 
106 	Returns
107 		1 if name ends with ".local" or ".local."
108 		0 otherwise
109  */
110 int
111 islocal (const char * name);
112 
113 
114 /*
115 	Format an address structure as a string appropriate for DNS reverse (PTR)
116 	lookup, based on address type.
117 
118 	Parameters
119 		prefixlen
120 			Prefix length, in bits.  When formatting, this will be rounded up
121 			to the nearest appropriate size.  If -1, assume maximum.
122 		buf
123 			Output buffer.  Must be long enough to hold largest possible
124 			output.
125 	Returns
126 		Pointer to (first character of) output buffer,
127 		or NULL on error.
128  */
129 char *
130 format_reverse_addr (int af, const void * addr, int prefixlen, char * buf);
131 
132 
133 /*
134 	Format an address structure as a string appropriate for DNS reverse (PTR)
135 	lookup for AF_INET.  Output is in .in-addr.arpa domain.
136 
137 	Parameters
138 		prefixlen
139 			Prefix length, in bits.  When formatting, this will be rounded up
140 			to the nearest byte (8).  If -1, assume 32.
141 		buf
142 			Output buffer.  Must be long enough to hold largest possible
143 			output.  For AF_INET, this is 29 characters (including null).
144 	Returns
145 		Pointer to (first character of) output buffer,
146 		or NULL on error.
147  */
148 char *
149 format_reverse_addr_in (
150 	const struct in_addr * addr,
151 	int prefixlen,
152 	char * buf
153 );
154 #define DNS_PTR_AF_INET_SIZE 29
155 
156 /*
157 	Format an address structure as a string appropriate for DNS reverse (PTR)
158 	lookup for AF_INET6.  Output is in .ip6.arpa domain.
159 
160 	Parameters
161 		prefixlen
162 			Prefix length, in bits.  When formatting, this will be rounded up
163 			to the nearest nibble (4).  If -1, assume 128.
164 		buf
165 			Output buffer.  Must be long enough to hold largest possible
166 			output.  For AF_INET6, this is 72 characters (including null).
167 	Returns
168 		Pointer to (first character of) output buffer,
169 		or NULL on error.
170  */
171 char *
172 format_reverse_addr_in6 (
173 	const struct in6_addr * addr,
174 	int prefixlen,
175 	char * buf
176 );
177 #define DNS_PTR_AF_INET6_SIZE 72
178 
179 
180 /*
181 	Compare whether the given dns name has the given domain suffix.
182 	A single leading '.' on the name or leading or trailing '.' on the
183 	domain is ignored for the purposes of the comparison.
184 	Multiple leading or trailing '.'s are an error.  Other DNS syntax
185 	errors are not checked for.  The comparison is case insensitive.
186 
187 	Returns
188 		1 on success (match)
189 		0 on failure (no match)
190 		< 0 on error
191  */
192 int
193 cmp_dns_suffix (const char * name, const char * domain);
194 enum
195 {
196 	CMP_DNS_SUFFIX_SUCCESS = 1,
197 	CMP_DNS_SUFFIX_FAILURE = 0,
198 	CMP_DNS_SUFFIX_BAD_NAME = 1,
199 	CMP_DNS_SUFFIX_BAD_DOMAIN = -2
200 };
201 
202 typedef int ns_type_t;
203 typedef int ns_class_t;
204 
205 /*
206 	Convert a DNS resource record (RR) code to an address family (AF) code.
207 
208 	Parameters
209 		rrtype
210 			resource record type (from nameser.h)
211 
212 	Returns
213 		Appropriate AF code (from socket.h), or AF_UNSPEC if an appropriate
214 		mapping couldn't be determined
215  */
216 int
217 rr_to_af (ns_type_t rrtype);
218 
219 
220 /*
221 	Convert an address family (AF) code to a DNS resource record (RR) code.
222 
223 	Parameters
224 		int
225 			address family code (from socket.h)
226 	Returns
227 		Appropriate RR code (from nameser.h), or ns_t_invalid if an appropriate
228 		mapping couldn't be determined
229  */
230 ns_type_t
231 af_to_rr (int af);
232 
233 
234 /*
235 	Convert a string to an address family (case insensitive).
236 
237 	Returns
238 		Matching AF code, or AF_UNSPEC if no match found.
239  */
240 int
241 str_to_af (const char * str);
242 
243 
244 /*
245 	Convert a string to an ns_class_t (case insensitive).
246 
247 	Returns
248 		Matching ns_class_t, or ns_c_invalid if no match found.
249  */
250 ns_class_t
251 str_to_ns_class (const char * str);
252 
253 
254 /*
255 	Convert a string to an ns_type_t (case insensitive).
256 
257 	Returns
258 		Matching ns_type_t, or ns_t_invalid if no match found.
259  */
260 ns_type_t
261 str_to_ns_type (const char * str);
262 
263 
264 /*
265 	Convert an address family code to a string.
266 
267 	Returns
268 		String representation of AF,
269 		or NULL if address family unrecognised or invalid.
270  */
271 const char *
272 af_to_str (int in);
273 
274 
275 /*
276 	Convert an ns_class_t code to a string.
277 
278 	Returns
279 		String representation of ns_class_t,
280 		or NULL if ns_class_t unrecognised or invalid.
281  */
282 const char *
283 ns_class_to_str (ns_class_t in);
284 
285 
286 /*
287 	Convert an ns_type_t code to a string.
288 
289 	Returns
290 		String representation of ns_type_t,
291 		or NULL if ns_type_t unrecognised or invalid.
292  */
293 const char *
294 ns_type_to_str (ns_type_t in);
295 
296 
297 /*
298 	Convert DNS rdata in label format (RFC1034, RFC1035) to a name.
299 
300 	On error, partial data is written to name (as much as was successfully
301 	processed) and an error code is returned.  Errors include a name too
302 	long for the buffer and a pointer in the label (which cannot be
303 	resolved).
304 
305 	Parameters
306 		rdata
307 			Rdata formatted as series of labels.
308 		rdlen
309 			Length of rdata buffer.
310 		name
311 			Buffer to store fully qualified result in.
312 			By RFC1034 section 3.1, a 255 character buffer (256 characters
313 			including null) is long enough for any legal name.
314 		name_len
315 			Number of characters available in name buffer, not including
316 			trailing null.
317 
318 	Returns
319 		Length of name buffer (not including trailing null).
320 		< 0 on error.
321 		A return of 0 implies the empty domain.
322  */
323 int
324 dns_rdata_to_name (const char * rdata, int rdlen, char * name, int name_len);
325 enum
326 {
327 	DNS_RDATA_TO_NAME_BAD_FORMAT = -1,
328 		// Format is broken.  Usually because we ran out of data
329 		// (according to rdata) before the labels said we should.
330 	DNS_RDATA_TO_NAME_TOO_LONG = -2,
331 		// The converted rdata is longer than the name buffer.
332 	DNS_RDATA_TO_NAME_PTR = -3,
333 		// The rdata contains a pointer.
334 };
335 
336 #define DNS_LABEL_MAXLEN 63
337 	// Maximum length of a single DNS label
338 #define DNS_NAME_MAXLEN 256
339 	// Maximum length of a DNS name
340 
341 //----------
342 // Public types
343 
344 typedef int errcode_t;
345 	// Used for 0 = success, non-zero = error code functions
346 
347 
348 //----------
349 // Public functions
350 
351 /*
352 	Test whether a domain name is in a domain covered by nss_mdns.
353 	The name is assumed to be fully qualified (trailing dot optional);
354 	unqualified names will be processed but may return unusual results
355 	if the unqualified prefix happens to match a domain suffix.
356 
357 	Returns
358 		 1 success
359 		 0 failure
360 		-1 error, check errno
361  */
362 int
363 config_is_mdns_suffix (const char * name);
364 
365 
366 /*
367 	Loads all relevant data from configuration file.  Other code should
368 	rarely need to call this function, since all other public configuration
369 	functions do so implicitly.  Once loaded, configuration info doesn't
370 	change.
371 
372 	Returns
373 		0 configuration ready
374 		non-zero configuration error code
375  */
376 errcode_t
377 init_config ();
378 
379 #define ENTNAME  hostent
380 #define DATABASE "hosts"
381 
382 #include <nss.h>
383 	// For nss_status
384 #include <netdb.h>
385 	// For hostent
386 #include <sys/types.h>
387 	// For size_t
388 
389 typedef enum nss_status nss_status;
390 typedef struct hostent hostent;
391 
392 /*
393 gethostbyname implementation
394 
395 	name:
396 		name to look up
397 	result_buf:
398 		resulting entry
399 	buf:
400 		auxillary buffer
401 	buflen:
402 		length of auxillary buffer
403 	errnop:
404 		pointer to errno
405 	h_errnop:
406 		pointer to h_errno
407  */
408 nss_status
409 _nss_mdns_gethostbyname_r (
410 	const char *name,
411 	hostent * result_buf,
412 	char *buf,
413 	size_t buflen,
414 	int *errnop,
415 	int *h_errnop
416 );
417 
418 
419 /*
420 gethostbyname2 implementation
421 
422 	name:
423 		name to look up
424 	af:
425 		address family
426 	result_buf:
427 		resulting entry
428 	buf:
429 		auxillary buffer
430 	buflen:
431 		length of auxillary buffer
432 	errnop:
433 		pointer to errno
434 	h_errnop:
435 		pointer to h_errno
436  */
437 nss_status
438 _nss_mdns_gethostbyname2_r (
439 	const char *name,
440 	int af,
441 	hostent * result_buf,
442 	char *buf,
443 	size_t buflen,
444 	int *errnop,
445 	int *h_errnop
446 );
447 
448 
449 /*
450 gethostbyaddr implementation
451 
452 	addr:
453 		address structure to look up
454 	len:
455 		length of address structure
456 	af:
457 		address family
458 	result_buf:
459 		resulting entry
460 	buf:
461 		auxillary buffer
462 	buflen:
463 		length of auxillary buffer
464 	errnop:
465 		pointer to errno
466 	h_errnop:
467 		pointer to h_errno
468  */
469 nss_status
470 _nss_mdns_gethostbyaddr_r (
471 	const void *addr,
472 	socklen_t len,
473 	int af,
474 	hostent * result_buf,
475 	char *buf,
476 	size_t buflen,
477 	int *errnop,
478 	int *h_errnop
479 );
480 
481 
482 //----------
483 // Types and Constants
484 
485 const int MDNS_VERBOSE = 0;
486 	// This enables verbose syslog messages
487 	// If zero, only "imporant" messages will appear in syslog
488 
489 #define k_hostname_maxlen 256
490 	// As per RFC1034 and RFC1035
491 #define k_aliases_max 15
492 #define k_addrs_max 15
493 
494 typedef struct buf_header
495 {
496 	char hostname [k_hostname_maxlen + 1];
497 	char * aliases [k_aliases_max + 1];
498 	char * addrs [k_addrs_max + 1];
499 } buf_header_t;
500 
501 typedef struct result_map
502 {
503 	int done;
504 	nss_status status;
505 	hostent * hostent;
506 	buf_header_t * header;
507 	int aliases_count;
508 	int addrs_count;
509 	char * buffer;
510 	int addr_idx;
511 		// Index for addresses - grow from low end
512 		// Index points to first empty space
513 	int alias_idx;
514 		// Index for aliases - grow from high end
515 		// Index points to lowest entry
516 	int r_errno;
517 	int r_h_errno;
518 } result_map_t;
519 
520 static const struct timeval
521 	k_select_time = { 0, 500000 };
522 		// 0 seconds, 500 milliseconds
523 
524 //----------
525 // Local prototypes
526 
527 static nss_status
528 mdns_gethostbyname2 (
529 	const char *name,
530 	int af,
531 	hostent * result_buf,
532 	char *buf,
533 	size_t buflen,
534 	int *errnop,
535 	int *h_errnop
536 );
537 
538 
539 /*
540 	Lookup name using mDNS server
541  */
542 static nss_status
543 mdns_lookup_name (
544 	const char * fullname,
545 	int af,
546 	result_map_t * result
547 );
548 
549 /*
550 	Lookup address using mDNS server
551  */
552 static nss_status
553 mdns_lookup_addr (
554 	const void * addr,
555 	socklen_t len,
556 	int af,
557 	const char * addr_str,
558 	result_map_t * result
559 );
560 
561 
562 /*
563 	Handle incoming MDNS events
564  */
565 static nss_status
566 handle_events (DNSServiceRef sdref, result_map_t * result, const char * str);
567 
568 
569 // Callback for mdns_lookup operations
570 //DNSServiceQueryRecordReply mdns_lookup_callback;
571 typedef void
572 mdns_lookup_callback_t
573 (
574 	DNSServiceRef		sdref,
575 	DNSServiceFlags		flags,
576 	uint32_t			interface_index,
577 	DNSServiceErrorType	error_code,
578 	const char			*fullname,
579 	uint16_t			rrtype,
580 	uint16_t			rrclass,
581 	uint16_t			rdlen,
582 	const void			*rdata,
583 	uint32_t			ttl,
584 	void				*context
585 );
586 
587 mdns_lookup_callback_t mdns_lookup_callback;
588 
589 
590 static int
591 init_result (
592 	result_map_t * result,
593 	hostent * result_buf,
594 	char * buf,
595 	size_t buflen
596 );
597 
598 static int
599 callback_body_ptr (
600 	const char * fullname,
601 	result_map_t * result,
602 	int rdlen,
603 	const void * rdata
604 );
605 
606 static void *
607 add_address_to_buffer (result_map_t * result, const void * data, int len);
608 static char *
609 add_alias_to_buffer (result_map_t * result, const char * data, int len);
610 static char *
611 add_hostname_len (result_map_t * result, const char * fullname, int len);
612 static char *
613 add_hostname_or_alias (result_map_t * result, const char * data, int len);
614 
615 static void *
616 contains_address (result_map_t * result, const void * data, int len);
617 static char *
618 contains_alias (result_map_t * result, const char * data);
619 
620 
621 static const char *
622 is_applicable_name (
623 	result_map_t * result,
624 	const char * name,
625 	char * lookup_name
626 );
627 
628 static const char *
629 is_applicable_addr (
630 	result_map_t * result,
631 	const void * addr,
632 	int af,
633 	char * addr_str
634 );
635 
636 
637 // Error code functions
638 
639 static nss_status
640 set_err (result_map_t * result, nss_status status, int err, int herr);
641 
642 static nss_status set_err_notfound (result_map_t * result);
643 static nss_status set_err_bad_hostname (result_map_t * result);
644 static nss_status set_err_buf_too_small (result_map_t * result);
645 static nss_status set_err_internal_resource_full (result_map_t * result);
646 static nss_status set_err_system (result_map_t * result);
647 static nss_status set_err_mdns_failed (result_map_t * result);
648 static nss_status set_err_success (result_map_t * result);
649 
650 
651 //----------
652 // Global variables
653 
654 
655 //----------
656 // NSS functions
657 
658 nss_status
_nss_mdns_gethostbyname_r(const char * name,hostent * result_buf,char * buf,size_t buflen,int * errnop,int * h_errnop)659 _nss_mdns_gethostbyname_r (
660 	const char *name,
661 	hostent * result_buf,
662 	char *buf,
663 	size_t buflen,
664 	int *errnop,
665 	int *h_errnop
666 )
667 {
668 	if (MDNS_VERBOSE)
669 		syslog (LOG_DEBUG,
670 			"mdns: Called nss_mdns_gethostbyname with %s",
671 			name
672 		);
673 
674 	return
675 		mdns_gethostbyname2 (
676 			name, AF_INET, result_buf, buf, buflen, errnop, h_errnop
677 		);
678 }
679 
680 
681 nss_status
_nss_mdns_gethostbyname2_r(const char * name,int af,hostent * result_buf,char * buf,size_t buflen,int * errnop,int * h_errnop)682 _nss_mdns_gethostbyname2_r (
683 	const char *name,
684 	int af,
685 	hostent * result_buf,
686 	char *buf,
687 	size_t buflen,
688 	int *errnop,
689 	int *h_errnop
690 )
691 {
692 	if (MDNS_VERBOSE)
693 		syslog (LOG_DEBUG,
694 			"mdns: Called nss_mdns_gethostbyname2 with %s",
695 			name
696 		);
697 
698 	return
699 		mdns_gethostbyname2 (
700 			name, af, result_buf, buf, buflen, errnop, h_errnop
701 		);
702 }
703 
704 
705 nss_status
_nss_mdns_gethostbyaddr_r(const void * addr,socklen_t len,int af,hostent * result_buf,char * buf,size_t buflen,int * errnop,int * h_errnop)706 _nss_mdns_gethostbyaddr_r (
707 	const void *addr,
708 	socklen_t len,
709 	int af,
710 	hostent * result_buf,
711 	char *buf,
712 	size_t buflen,
713 	int *errnop,
714 	int *h_errnop
715 )
716 {
717 	char addr_str [NI_MAXHOST + 1];
718 	result_map_t result;
719 	int err_status;
720 
721 	if (inet_ntop (af, addr, addr_str, NI_MAXHOST) == NULL)
722 	{
723 		const char * family = af_to_str (af);
724 		if (family == NULL)
725 		{
726 			family = "Unknown";
727 		}
728 
729 		syslog (LOG_WARNING,
730 			"mdns: Couldn't covert address, family %d (%s) in nss_mdns_gethostbyaddr: %s",
731 			af,
732 			family,
733 			strerror (errno)
734 		);
735 
736 		// This address family never applicable to us, so return NOT_FOUND
737 
738 		*errnop = ENOENT;
739 		*h_errnop = HOST_NOT_FOUND;
740 		return NSS_STATUS_NOTFOUND;
741 	}
742 	if (MDNS_VERBOSE)
743 	{
744 		syslog (LOG_DEBUG,
745 			"mdns: Called nss_mdns_gethostbyaddr with %s",
746 			addr_str
747 		);
748 	}
749 
750 	// Initialise result
751 	err_status = init_result (&result, result_buf, buf, buflen);
752 	if (err_status)
753 	{
754 		*errnop = err_status;
755 		*h_errnop = NETDB_INTERNAL;
756 		return NSS_STATUS_TRYAGAIN;
757 	}
758 
759 	if (is_applicable_addr (&result, addr, af, addr_str))
760 	{
761 		nss_status rv;
762 
763 		rv = mdns_lookup_addr (addr, len, af, addr_str, &result);
764 		if (rv == NSS_STATUS_SUCCESS)
765 		{
766 			return rv;
767 		}
768 	}
769 
770 	// Return current error status (defaults to NOT_FOUND)
771 
772 	*errnop = result.r_errno;
773 	*h_errnop = result.r_h_errno;
774 	return result.status;
775 }
776 
777 
778 //----------
779 // Local functions
780 
781 nss_status
mdns_gethostbyname2(const char * name,int af,hostent * result_buf,char * buf,size_t buflen,int * errnop,int * h_errnop)782 mdns_gethostbyname2 (
783 	const char *name,
784 	int af,
785 	hostent * result_buf,
786 	char *buf,
787 	size_t buflen,
788 	int *errnop,
789 	int *h_errnop
790 )
791 {
792 	char lookup_name [k_hostname_maxlen + 1];
793 	result_map_t result;
794 	int err_status;
795 
796 	// Initialise result
797 	err_status = init_result (&result, result_buf, buf, buflen);
798 	if (err_status)
799 	{
800 		*errnop = err_status;
801 		*h_errnop = NETDB_INTERNAL;
802 		return NSS_STATUS_TRYAGAIN;
803 	}
804 
805 	if (is_applicable_name (&result, name, lookup_name))
806 	{
807 		// Try using mdns
808 		nss_status rv;
809 
810 		if (MDNS_VERBOSE)
811 			syslog (LOG_DEBUG,
812 				"mdns: Local name: %s",
813 				name
814 			);
815 
816 		rv = mdns_lookup_name (name, af, &result);
817 		if (rv == NSS_STATUS_SUCCESS)
818 		{
819 			return rv;
820 		}
821 	}
822 
823 	// Return current error status (defaults to NOT_FOUND)
824 
825 	*errnop = result.r_errno;
826 	*h_errnop = result.r_h_errno;
827 	return result.status;
828 }
829 
830 
831 /*
832 	Lookup a fully qualified hostname using the default record type
833 	for the specified address family.
834 
835 	Parameters
836 		fullname
837 			Fully qualified hostname.  If not fully qualified the code will
838 			still 'work', but the lookup is unlikely to succeed.
839 		af
840 			Either AF_INET or AF_INET6.  Other families are not supported.
841 		result
842 			Initialised 'result' data structure.
843  */
844 static nss_status
mdns_lookup_name(const char * fullname,int af,result_map_t * result)845 mdns_lookup_name (
846 	const char * fullname,
847 	int af,
848 	result_map_t * result
849 )
850 {
851 	// Lookup using mDNS.
852 	DNSServiceErrorType errcode;
853 	DNSServiceRef sdref;
854 	ns_type_t rrtype;
855 	nss_status status;
856 
857 	if (MDNS_VERBOSE)
858 		syslog (LOG_DEBUG,
859 			"mdns: Attempting lookup of %s",
860 			fullname
861 		);
862 
863 	switch (af)
864 	{
865 	  case AF_INET:
866 		rrtype = kDNSServiceType_A;
867 		result->hostent->h_length = 4;
868 			// Length of an A record
869 		break;
870 
871 	  case AF_INET6:
872 		rrtype = kDNSServiceType_AAAA;
873 		result->hostent->h_length = 16;
874 			// Length of an AAAA record
875 		break;
876 
877 	  default:
878 		syslog (LOG_WARNING,
879 			"mdns: Unsupported address family %d",
880 			af
881 		);
882 		return set_err_bad_hostname (result);
883 	}
884 	result->hostent->h_addrtype = af;
885 
886 	errcode =
887 		DNSServiceQueryRecord (
888 			&sdref,
889 			kDNSServiceFlagsForceMulticast,		// force multicast query
890 			kDNSServiceInterfaceIndexAny,	// all interfaces
891 			fullname,	// full name to query for
892 			rrtype,		// resource record type
893 			kDNSServiceClass_IN,	// internet class records
894 			mdns_lookup_callback,	// callback
895 			result		// Context - result buffer
896 		);
897 
898 	if (errcode)
899 	{
900 		syslog (LOG_WARNING,
901 			"mdns: Failed to initialise lookup, error %d",
902 			errcode
903 		);
904 		return set_err_mdns_failed (result);
905 	}
906 
907 	status = handle_events (sdref, result, fullname);
908 	DNSServiceRefDeallocate (sdref);
909 	return status;
910 }
911 
912 
913 /*
914 	Reverse (PTR) lookup for the specified address.
915 
916 	Parameters
917 		addr
918 			Either a struct in_addr or a struct in6_addr
919 		addr_len
920 			size of the address
921 		af
922 			Either AF_INET or AF_INET6.  Other families are not supported.
923 			Must match addr
924 		addr_str
925 			Address in format suitable for PTR lookup.
926 			AF_INET: a.b.c.d -> d.c.b.a.in-addr.arpa
927 			AF_INET6: reverse nibble format, x.x.x...x.ip6.arpa
928 		result
929 			Initialised 'result' data structure.
930  */
931 static nss_status
mdns_lookup_addr(const void * addr,socklen_t addr_len,int af,const char * addr_str,result_map_t * result)932 mdns_lookup_addr (
933 	const void * addr,
934 	socklen_t addr_len,
935 	int af,
936 	const char * addr_str,
937 	result_map_t * result
938 )
939 {
940 	DNSServiceErrorType errcode;
941 	DNSServiceRef sdref;
942 	nss_status status;
943 
944 	if (MDNS_VERBOSE)
945 		syslog (LOG_DEBUG,
946 			"mdns: Attempting lookup of %s",
947 			addr_str
948 		);
949 
950 	result->hostent->h_addrtype = af;
951 	result->hostent->h_length = addr_len;
952 
953 	// Query address becomes "address" in result.
954 	if (! add_address_to_buffer (result, addr, addr_len))
955 	{
956 		return result->status;
957 	}
958 
959 	result->hostent->h_name [0] = 0;
960 
961 	errcode =
962 		DNSServiceQueryRecord (
963 			&sdref,
964 			kDNSServiceFlagsForceMulticast,		// force multicast query
965 			kDNSServiceInterfaceIndexAny,	// all interfaces
966 			addr_str,	// address string to query for
967 			kDNSServiceType_PTR,	// pointer RRs
968 			kDNSServiceClass_IN,	// internet class records
969 			mdns_lookup_callback,	// callback
970 			result		// Context - result buffer
971 		);
972 
973 	if (errcode)
974 	{
975 		syslog (LOG_WARNING,
976 			"mdns: Failed to initialise mdns lookup, error %d",
977 			errcode
978 		);
979 		return set_err_mdns_failed (result);
980 	}
981 
982 	status = handle_events (sdref, result, addr_str);
983 	DNSServiceRefDeallocate (sdref);
984 	return status;
985 }
986 
987 
988 /*
989 	Wait on result of callback, and process it when it arrives.
990 
991 	Parameters
992 		sdref
993 			dns-sd reference
994 		result
995 			Initialised 'result' data structure.
996 		str
997 			lookup string, used for status/error reporting.
998  */
999 static nss_status
handle_events(DNSServiceRef sdref,result_map_t * result,const char * str)1000 handle_events (DNSServiceRef sdref, result_map_t * result, const char * str)
1001 {
1002 	int dns_sd_fd = DNSServiceRefSockFD(sdref);
1003 	int nfds = dns_sd_fd + 1;
1004 	fd_set readfds;
1005 	struct timeval tv;
1006 	int select_result;
1007 
1008 	while (! result->done)
1009 	{
1010 		FD_ZERO(&readfds);
1011 		FD_SET(dns_sd_fd, &readfds);
1012 
1013 		tv = k_select_time;
1014 
1015 		select_result =
1016 			select (nfds, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv);
1017 		if (select_result > 0)
1018 		{
1019 			if (FD_ISSET(dns_sd_fd, &readfds))
1020 			{
1021 				if (MDNS_VERBOSE)
1022 					syslog (LOG_DEBUG,
1023 						"mdns: Reply received for %s",
1024 						str
1025 					);
1026 				DNSServiceProcessResult(sdref);
1027 			}
1028 			else
1029 			{
1030 				syslog (LOG_WARNING,
1031 					"mdns: Unexpected return from select on lookup of %s",
1032 					str
1033 				);
1034 			}
1035 		}
1036 		else
1037 		{
1038 			// Terminate loop due to timer expiry
1039 			if (MDNS_VERBOSE)
1040 				syslog (LOG_DEBUG,
1041 					"mdns: %s not found - timer expired",
1042 					str
1043 				);
1044 			set_err_notfound (result);
1045 			break;
1046 		}
1047 	}
1048 
1049 	return result->status;
1050 }
1051 
1052 
1053 /*
1054 	Examine incoming data and add to relevant fields in result structure.
1055 	This routine is called from DNSServiceProcessResult where appropriate.
1056  */
1057 void
mdns_lookup_callback(DNSServiceRef sdref,DNSServiceFlags flags,uint32_t interface_index,DNSServiceErrorType error_code,const char * fullname,uint16_t rrtype,uint16_t rrclass,uint16_t rdlen,const void * rdata,uint32_t ttl,void * context)1058 mdns_lookup_callback
1059 (
1060 	DNSServiceRef		sdref,
1061 	DNSServiceFlags		flags,
1062 	uint32_t			interface_index,
1063 	DNSServiceErrorType	error_code,
1064 	const char			*fullname,
1065 	uint16_t			rrtype,
1066 	uint16_t			rrclass,
1067 	uint16_t			rdlen,
1068 	const void			*rdata,
1069 	uint32_t			ttl,
1070 	void				*context
1071 )
1072 {
1073 	// A single record is received
1074 
1075 	result_map_t * result = (result_map_t *) context;
1076 
1077 	(void)sdref; // Unused
1078 	(void)interface_index; // Unused
1079 	(void)ttl; // Unused
1080 
1081 	if (! (flags & kDNSServiceFlagsMoreComing) )
1082 	{
1083 		result->done = 1;
1084 	}
1085 
1086 	if (error_code == kDNSServiceErr_NoError)
1087 	{
1088 		ns_type_t expected_rr_type =
1089 			af_to_rr (result->hostent->h_addrtype);
1090 
1091 		// Idiot check class
1092 		if (rrclass != C_IN)
1093 		{
1094 			syslog (LOG_WARNING,
1095 				"mdns: Received bad RR class: expected %d (%s),"
1096 				" got %d (%s), RR type %d (%s)",
1097 				C_IN,
1098 				ns_class_to_str (C_IN),
1099 				rrclass,
1100 				ns_class_to_str (rrclass),
1101 				rrtype,
1102 				ns_type_to_str (rrtype)
1103 			);
1104 			return;
1105 		}
1106 
1107 		// If a PTR
1108 		if (rrtype == kDNSServiceType_PTR)
1109 		{
1110 			if (callback_body_ptr (fullname, result, rdlen, rdata) < 0)
1111 				return;
1112 		}
1113 		else if (rrtype == expected_rr_type)
1114 		{
1115 			if (!
1116 				add_hostname_or_alias (
1117 					result,
1118 					fullname,
1119 					strlen (fullname)
1120 				)
1121 			)
1122 			{
1123 				result->done = 1;
1124 				return;
1125 					// Abort on error
1126 			}
1127 
1128 			if (! add_address_to_buffer (result, rdata, rdlen) )
1129 			{
1130 				result->done = 1;
1131 				return;
1132 					// Abort on error
1133 			}
1134 		}
1135 		else
1136 		{
1137 			syslog (LOG_WARNING,
1138 				"mdns: Received bad RR type: expected %d (%s),"
1139 				" got %d (%s)",
1140 				expected_rr_type,
1141 				ns_type_to_str (expected_rr_type),
1142 				rrtype,
1143 				ns_type_to_str (rrtype)
1144 			);
1145 			return;
1146 		}
1147 
1148 		if (result->status != NSS_STATUS_SUCCESS)
1149 			set_err_success (result);
1150 	}
1151 	else
1152 	{
1153 		// For now, dump message to syslog and continue
1154 		syslog (LOG_WARNING,
1155 			"mdns: callback returned error %d",
1156 			error_code
1157 		);
1158 	}
1159 }
1160 
1161 static int
callback_body_ptr(const char * fullname,result_map_t * result,int rdlen,const void * rdata)1162 callback_body_ptr (
1163 	const char * fullname,
1164 	result_map_t * result,
1165 	int rdlen,
1166 	const void * rdata
1167 )
1168 {
1169 	char result_name [k_hostname_maxlen + 1];
1170 	int rv;
1171 
1172 	// Fullname should be .in-addr.arpa or equivalent, which we're
1173 	// not interested in.  Ignore it.
1174 
1175 	rv = dns_rdata_to_name (rdata, rdlen, result_name, k_hostname_maxlen);
1176 	if (rv < 0)
1177 	{
1178 		const char * errmsg;
1179 
1180 		switch (rv)
1181 		{
1182 		  case DNS_RDATA_TO_NAME_BAD_FORMAT:
1183 			errmsg = "mdns: PTR '%s' result badly formatted ('%s...')";
1184 			break;
1185 
1186 		  case DNS_RDATA_TO_NAME_TOO_LONG:
1187 			errmsg = "mdns: PTR '%s' result too long ('%s...')";
1188 			break;
1189 
1190 		  case DNS_RDATA_TO_NAME_PTR:
1191 			errmsg = "mdns: PTR '%s' result contained pointer ('%s...')";
1192 			break;
1193 
1194 		  default:
1195 			errmsg = "mdns: PTR '%s' result conversion failed ('%s...')";
1196 		}
1197 
1198 		syslog (LOG_WARNING,
1199 			errmsg,
1200 			fullname,
1201 			result_name
1202 		);
1203 
1204 		return -1;
1205 	}
1206 
1207 	if (MDNS_VERBOSE)
1208 	{
1209 		syslog (LOG_DEBUG,
1210 			"mdns: PTR '%s' resolved to '%s'",
1211 			fullname,
1212 			result_name
1213 		);
1214 	}
1215 
1216 	// Data should be a hostname
1217 	if (!
1218 		add_hostname_or_alias (
1219 			result,
1220 			result_name,
1221 			rv
1222 		)
1223 	)
1224 	{
1225 		result->done = 1;
1226 		return -1;
1227 	}
1228 
1229 	return 0;
1230 }
1231 
1232 
1233 /*
1234 	Add an address to the buffer.
1235 
1236 	Parameter
1237 		result
1238 			Result structure to write to
1239 		data
1240 			Incoming address data buffer
1241 			Must be 'int' aligned
1242 		len
1243 			Length of data buffer (in bytes)
1244 			Must match data alignment
1245 
1246 	Result
1247 		Pointer to start of newly written data,
1248 		or NULL on error.
1249 		If address already exists in buffer, returns pointer to that instead.
1250  */
1251 static void *
add_address_to_buffer(result_map_t * result,const void * data,int len)1252 add_address_to_buffer (result_map_t * result, const void * data, int len)
1253 {
1254 	int new_addr;
1255 	void * start;
1256 	void * temp;
1257 
1258 	if ((temp = contains_address (result, data, len)))
1259 	{
1260 		return temp;
1261 	}
1262 
1263 	if (result->addrs_count >= k_addrs_max)
1264 	{
1265 		// Not enough addr slots
1266 		set_err_internal_resource_full (result);
1267 		syslog (LOG_ERR,
1268 			"mdns: Internal address buffer full; increase size"
1269 		);
1270 		return NULL;
1271 	}
1272 
1273 	// Idiot check
1274 	if (len != result->hostent->h_length)
1275 	{
1276 		syslog (LOG_WARNING,
1277 			"mdns: Unexpected rdata length for address.  Expected %d, got %d",
1278 			result->hostent->h_length,
1279 			len
1280 		);
1281 		// XXX And continue for now.
1282 	}
1283 
1284 	new_addr = result->addr_idx + len;
1285 
1286 	if (new_addr > result->alias_idx)
1287 	{
1288 		// Not enough room
1289 		set_err_buf_too_small (result);
1290 		if (MDNS_VERBOSE)
1291 			syslog (LOG_DEBUG,
1292 				"mdns: Ran out of buffer when adding address %d",
1293 				result->addrs_count + 1
1294 			);
1295 		return NULL;
1296 	}
1297 
1298 	start = result->buffer + result->addr_idx;
1299 	memcpy (start, data, len);
1300 	result->addr_idx = new_addr;
1301 	result->header->addrs [result->addrs_count] = start;
1302 	result->addrs_count ++;
1303 	result->header->addrs [result->addrs_count] = NULL;
1304 
1305 	return start;
1306 }
1307 
1308 
1309 static void *
contains_address(result_map_t * result,const void * data,int len)1310 contains_address (result_map_t * result, const void * data, int len)
1311 {
1312 	int i;
1313 
1314 	// Idiot check
1315 	if (len != result->hostent->h_length)
1316 	{
1317 		syslog (LOG_WARNING,
1318 			"mdns: Unexpected rdata length for address.  Expected %d, got %d",
1319 			result->hostent->h_length,
1320 			len
1321 		);
1322 		// XXX And continue for now.
1323 	}
1324 
1325 	for (i = 0; result->header->addrs [i]; i++)
1326 	{
1327 		if (memcmp (result->header->addrs [i], data, len) == 0)
1328 		{
1329 			return result->header->addrs [i];
1330 		}
1331 	}
1332 
1333 	return NULL;
1334 }
1335 
1336 
1337 /*
1338 	Add an alias to the buffer.
1339 
1340 	Parameter
1341 		result
1342 			Result structure to write to
1343 		data
1344 			Incoming alias (null terminated)
1345 		len
1346 			Length of data buffer (in bytes), including trailing null
1347 
1348 	Result
1349 		Pointer to start of newly written data,
1350 		or NULL on error
1351 		If alias already exists in buffer, returns pointer to that instead.
1352  */
1353 static char *
add_alias_to_buffer(result_map_t * result,const char * data,int len)1354 add_alias_to_buffer (result_map_t * result, const char * data, int len)
1355 {
1356 	int new_alias;
1357 	char * start;
1358 	char * temp;
1359 
1360 	if ((temp = contains_alias (result, data)))
1361 	{
1362 		return temp;
1363 	}
1364 
1365 	if (result->aliases_count >= k_aliases_max)
1366 	{
1367 		// Not enough alias slots
1368 		set_err_internal_resource_full (result);
1369 		syslog (LOG_ERR,
1370 			"mdns: Internal alias buffer full; increase size"
1371 		);
1372 		return NULL;
1373 	}
1374 
1375 	new_alias = result->alias_idx - len;
1376 
1377 	if (new_alias < result->addr_idx)
1378 	{
1379 		// Not enough room
1380 		set_err_buf_too_small (result);
1381 		if (MDNS_VERBOSE)
1382 			syslog (LOG_DEBUG,
1383 				"mdns: Ran out of buffer when adding alias %d",
1384 				result->aliases_count + 1
1385 			);
1386 		return NULL;
1387 	}
1388 
1389 	start = result->buffer + new_alias;
1390 	memcpy (start, data, len);
1391 	result->alias_idx = new_alias;
1392 	result->header->aliases [result->aliases_count] = start;
1393 	result->aliases_count ++;
1394 	result->header->aliases [result->aliases_count] = NULL;
1395 
1396 	return start;
1397 }
1398 
1399 
1400 static char *
contains_alias(result_map_t * result,const char * alias)1401 contains_alias (result_map_t * result, const char * alias)
1402 {
1403 	int i;
1404 
1405 	for (i = 0; result->header->aliases [i]; i++)
1406 	{
1407 		if (strcmp (result->header->aliases [i], alias) == 0)
1408 		{
1409 			return result->header->aliases [i];
1410 		}
1411 	}
1412 
1413 	return NULL;
1414 }
1415 
1416 
1417 /*
1418 	Add fully qualified hostname to result.
1419 
1420 	Parameter
1421 		result
1422 			Result structure to write to
1423 		fullname
1424 			Fully qualified hostname
1425 
1426 	Result
1427 		Pointer to start of hostname buffer,
1428 		or NULL on error (usually hostname too long)
1429  */
1430 
1431 static char *
add_hostname_len(result_map_t * result,const char * fullname,int len)1432 add_hostname_len (result_map_t * result, const char * fullname, int len)
1433 {
1434 	if (len >= k_hostname_maxlen)
1435 	{
1436 		set_err_bad_hostname (result);
1437 		syslog (LOG_WARNING,
1438 			"mdns: Hostname too long '%.*s': len %d, max %d",
1439 			len,
1440 			fullname,
1441 			len,
1442 			k_hostname_maxlen
1443 		);
1444 		return NULL;
1445 	}
1446 
1447 	result->hostent->h_name =
1448 		strcpy (result->header->hostname, fullname);
1449 
1450 	return result->header->hostname;
1451 }
1452 
1453 
1454 /*
1455 	Add fully qualified name as hostname or alias.
1456 
1457 	If hostname is not fully qualified this is not an error, but the data
1458 	returned may be not what the application wanted.
1459 
1460 	Parameter
1461 		result
1462 			Result structure to write to
1463 		data
1464 			Incoming alias (null terminated)
1465 		len
1466 			Length of data buffer (in bytes), including trailing null
1467 
1468 	Result
1469 		Pointer to start of newly written data,
1470 		or NULL on error
1471 		If alias or hostname already exists, returns pointer to that instead.
1472  */
1473 static char *
add_hostname_or_alias(result_map_t * result,const char * data,int len)1474 add_hostname_or_alias (result_map_t * result, const char * data, int len)
1475 {
1476 	char * hostname = result->hostent->h_name;
1477 
1478 	if (*hostname)
1479 	{
1480 		if (strcmp (hostname, data) == 0)
1481 		{
1482 			return hostname;
1483 		}
1484 		else
1485 		{
1486 			return add_alias_to_buffer (result, data, len);
1487 		}
1488 	}
1489 	else
1490 	{
1491 		return add_hostname_len (result, data, len);
1492 	}
1493 }
1494 
1495 
1496 static int
init_result(result_map_t * result,hostent * result_buf,char * buf,size_t buflen)1497 init_result (
1498 	result_map_t * result,
1499 	hostent * result_buf,
1500 	char * buf,
1501 	size_t buflen
1502 )
1503 {
1504 	if (buflen < sizeof (buf_header_t))
1505 	{
1506 		return ERANGE;
1507 	}
1508 
1509 	result->hostent = result_buf;
1510 	result->header = (buf_header_t *) buf;
1511 	result->header->hostname[0] = 0;
1512 	result->aliases_count = 0;
1513 	result->header->aliases[0] = NULL;
1514 	result->addrs_count = 0;
1515 	result->header->addrs[0] = NULL;
1516 	result->buffer = buf + sizeof (buf_header_t);
1517 	result->addr_idx = 0;
1518 	result->alias_idx = buflen - sizeof (buf_header_t);
1519 	result->done = 0;
1520 	set_err_notfound (result);
1521 
1522 	// Point hostent to the right buffers
1523 	result->hostent->h_name = result->header->hostname;
1524 	result->hostent->h_aliases = result->header->aliases;
1525 	result->hostent->h_addr_list = result->header->addrs;
1526 
1527 	return 0;
1528 }
1529 
1530 /*
1531 	Set the status in the result.
1532 
1533 	Parameters
1534 		result
1535 			Result structure to update
1536 		status
1537 			New nss_status value
1538 		err
1539 			New errno value
1540 		herr
1541 			New h_errno value
1542 
1543 	Returns
1544 		New status value
1545  */
1546 static nss_status
set_err(result_map_t * result,nss_status status,int err,int herr)1547 set_err (result_map_t * result, nss_status status, int err, int herr)
1548 {
1549 	result->status = status;
1550 	result->r_errno = err;
1551 	result->r_h_errno = herr;
1552 
1553 	return status;
1554 }
1555 
1556 static nss_status
set_err_notfound(result_map_t * result)1557 set_err_notfound (result_map_t * result)
1558 {
1559 	return set_err (result, NSS_STATUS_NOTFOUND, ENOENT, HOST_NOT_FOUND);
1560 }
1561 
1562 static nss_status
set_err_bad_hostname(result_map_t * result)1563 set_err_bad_hostname (result_map_t * result)
1564 {
1565 	return set_err (result, NSS_STATUS_TRYAGAIN, ENOENT, NO_RECOVERY);
1566 }
1567 
1568 static nss_status
set_err_buf_too_small(result_map_t * result)1569 set_err_buf_too_small (result_map_t * result)
1570 {
1571 	return set_err (result, NSS_STATUS_TRYAGAIN, ERANGE, NETDB_INTERNAL);
1572 }
1573 
1574 static nss_status
set_err_internal_resource_full(result_map_t * result)1575 set_err_internal_resource_full (result_map_t * result)
1576 {
1577 	return set_err (result, NSS_STATUS_RETURN, ERANGE, NO_RECOVERY);
1578 }
1579 
1580 static nss_status
set_err_system(result_map_t * result)1581 set_err_system (result_map_t * result)
1582 {
1583 	return set_err (result, NSS_STATUS_UNAVAIL, errno, NETDB_INTERNAL);
1584 }
1585 
1586 static nss_status
set_err_mdns_failed(result_map_t * result)1587 set_err_mdns_failed (result_map_t * result)
1588 {
1589 	return set_err (result, NSS_STATUS_TRYAGAIN, EAGAIN, TRY_AGAIN);
1590 }
1591 
1592 static nss_status
set_err_success(result_map_t * result)1593 set_err_success (result_map_t * result)
1594 {
1595 	result->status = NSS_STATUS_SUCCESS;
1596 	return result->status;
1597 }
1598 
1599 
1600 /*
1601 	Test whether name is applicable for mdns to process, and if so copy into
1602 	lookup_name buffer (if non-NULL).
1603 
1604 	Returns
1605 		Pointer to name to lookup up, if applicable, or NULL otherwise.
1606  */
1607 static const char *
is_applicable_name(result_map_t * result,const char * name,char * lookup_name)1608 is_applicable_name (
1609 	result_map_t * result,
1610 	const char * name,
1611 	char * lookup_name
1612 )
1613 {
1614 	int match = config_is_mdns_suffix (name);
1615 	if (match > 0)
1616 	{
1617 		if (lookup_name)
1618 		{
1619 			strncpy (lookup_name, name, k_hostname_maxlen + 1);
1620 			return lookup_name;
1621 		}
1622 		else
1623 		{
1624 			return name;
1625 		}
1626 	}
1627 	else
1628 	{
1629 		if (match < 0)
1630 		{
1631 			set_err_system (result);
1632 		}
1633 		return NULL;
1634 	}
1635 }
1636 
1637 /*
1638 	Test whether address is applicable for mdns to process, and if so copy into
1639 	addr_str buffer as an address suitable for ptr lookup.
1640 
1641 	Returns
1642 		Pointer to name to lookup up, if applicable, or NULL otherwise.
1643  */
1644 static const char *
is_applicable_addr(result_map_t * result,const void * addr,int af,char * addr_str)1645 is_applicable_addr (
1646 	result_map_t * result,
1647 	const void * addr,
1648 	int af,
1649 	char * addr_str
1650 )
1651 {
1652 	int match;
1653 
1654 	if (! format_reverse_addr (af, addr, -1, addr_str))
1655 	{
1656 		if (MDNS_VERBOSE)
1657 			syslog (LOG_DEBUG,
1658 				"mdns: Failed to create reverse address"
1659 			);
1660 		return NULL;
1661 	}
1662 
1663 	if (MDNS_VERBOSE)
1664 		syslog (LOG_DEBUG,
1665 			"mdns: Reverse address: %s",
1666 			addr_str
1667 		);
1668 
1669 	match = config_is_mdns_suffix (addr_str);
1670 	if (match > 0)
1671 	{
1672 		return addr_str;
1673 	}
1674 	else
1675 	{
1676 		if (match < 0)
1677 		{
1678 			set_err_system (result);
1679 		}
1680 		return NULL;
1681 	}
1682 }
1683 
1684 //----------
1685 // Types and Constants
1686 
1687 const char * k_conf_file = "/etc/nss_mdns.conf";
1688 #define CONF_LINE_SIZE 1024
1689 
1690 const char k_comment_char = '#';
1691 
1692 const char * k_keyword_domain = "domain";
1693 
1694 const char * k_default_domains [] =
1695 	{
1696 		"local",
1697 		"254.169.in-addr.arpa",
1698 		"8.e.f.ip6.int",
1699 		"8.e.f.ip6.arpa",
1700 		"9.e.f.ip6.int",
1701 		"9.e.f.ip6.arpa",
1702 		"a.e.f.ip6.int",
1703 		"a.e.f.ip6.arpa",
1704 		"b.e.f.ip6.int",
1705 		"b.e.f.ip6.arpa",
1706 		NULL
1707 			// Always null terminated
1708 	};
1709 
1710 // Linked list of domains
1711 typedef struct domain_entry
1712 {
1713 	char * domain;
1714 	struct domain_entry * next;
1715 } domain_entry_t;
1716 
1717 
1718 // Config
1719 typedef struct
1720 {
1721 	domain_entry_t * domains;
1722 } config_t;
1723 
1724 const config_t k_empty_config =
1725 	{
1726 		NULL
1727 	};
1728 
1729 
1730 // Context - tracks position in config file, used for error reporting
1731 typedef struct
1732 {
1733 	const char * filename;
1734 	int linenum;
1735 } config_file_context_t;
1736 
1737 
1738 //----------
1739 // Local prototypes
1740 
1741 static errcode_t
1742 load_config (config_t * conf);
1743 
1744 static errcode_t
1745 process_config_line (
1746 	config_t * conf,
1747 	char * line,
1748 	config_file_context_t * context
1749 );
1750 
1751 static char *
1752 get_next_word (char * input, char **next);
1753 
1754 static errcode_t
1755 default_config (config_t * conf);
1756 
1757 static errcode_t
1758 add_domain (config_t * conf, const char * domain);
1759 
1760 static int
1761 contains_domain (const config_t * conf, const char * domain);
1762 
1763 static int
1764 contains_domain_suffix (const config_t * conf, const char * addr);
1765 
1766 
1767 //----------
1768 // Global variables
1769 
1770 static config_t * g_config = NULL;
1771 	// Configuration info
1772 
1773 pthread_mutex_t g_config_mutex =
1774 #ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
1775 	PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
1776 #else
1777 	PTHREAD_MUTEX_INITIALIZER;
1778 #endif
1779 
1780 
1781 //----------
1782 // Configuration functions
1783 
1784 
1785 /*
1786 	Initialise the configuration from the config file.
1787 
1788 	Returns
1789 		0 success
1790 		non-zero error code on failure
1791  */
1792 errcode_t
init_config()1793 init_config ()
1794 {
1795 	if (g_config)
1796 	{
1797 		/*
1798 			Safe to test outside mutex.
1799 			If non-zero, initialisation is complete and g_config can be
1800 			safely used read-only.  If zero, then we do proper mutex
1801 			testing before initialisation.
1802 		 */
1803 		return 0;
1804 	}
1805 	else
1806 	{
1807 		int errcode = -1;
1808 		int presult;
1809 		config_t * temp_config;
1810 
1811 		// Acquire mutex
1812 		presult = pthread_mutex_lock (&g_config_mutex);
1813 		if (presult)
1814 		{
1815 			syslog (LOG_ERR,
1816 				"mdns: Fatal mutex lock error in nss_mdns:init_config, %s:%d: %d: %s",
1817 				__FILE__, __LINE__, presult, strerror (presult)
1818 			);
1819 			return presult;
1820 		}
1821 
1822 		// Test again now we have mutex, in case initialisation occurred while
1823 		// we were waiting
1824 		if (! g_config)
1825 		{
1826 			temp_config = (config_t *) malloc (sizeof (config_t));
1827 			if (temp_config)
1828 			{
1829 				// Note: This code will leak memory if initialisation fails
1830 				// repeatedly.  This should only happen in the case of a memory
1831 				// error, so I'm not sure if it's a meaningful problem. - AW
1832 				*temp_config = k_empty_config;
1833 				errcode = load_config (temp_config);
1834 
1835 				if (! errcode)
1836 				{
1837 					g_config = temp_config;
1838 				}
1839 			}
1840 			else
1841 			{
1842 				syslog (LOG_ERR,
1843 					"mdns: Can't allocate memory in nss_mdns:init_config, %s:%d",
1844 					__FILE__, __LINE__
1845 				);
1846 				errcode = errno;
1847 			}
1848 		}
1849 
1850 		presult = pthread_mutex_unlock (&g_config_mutex);
1851 		if (presult)
1852 		{
1853 			syslog (LOG_ERR,
1854 				"mdns: Fatal mutex unlock error in nss_mdns:init_config, %s:%d: %d: %s",
1855 				__FILE__, __LINE__, presult, strerror (presult)
1856 			);
1857 			errcode = presult;
1858 		}
1859 
1860 		return errcode;
1861 	}
1862 }
1863 
1864 
1865 int
config_is_mdns_suffix(const char * name)1866 config_is_mdns_suffix (const char * name)
1867 {
1868 	int errcode = init_config ();
1869 	if (! errcode)
1870 	{
1871 		return contains_domain_suffix (g_config, name);
1872 	}
1873 	else
1874 	{
1875 		errno = errcode;
1876 		return -1;
1877 	}
1878 }
1879 
1880 
1881 //----------
1882 // Local functions
1883 
1884 static errcode_t
load_config(config_t * conf)1885 load_config (config_t * conf)
1886 {
1887 	FILE * cf;
1888 	char line [CONF_LINE_SIZE];
1889 	config_file_context_t context;
1890 
1891 	context.filename = k_conf_file;
1892 	context.linenum = 0;
1893 
1894 
1895 	cf = fopen (context.filename, "r");
1896 	if (! cf)
1897 	{
1898 		syslog (LOG_INFO,
1899 			"mdns: Couldn't open nss_mdns configuration file %s, using default.",
1900 			context.filename
1901 		);
1902 		return default_config (conf);
1903 	}
1904 
1905 	while (fgets (line, CONF_LINE_SIZE, cf))
1906 	{
1907 		int errcode;
1908 		context.linenum++;
1909 		errcode = process_config_line (conf, line, &context);
1910 		if (errcode)
1911 		{
1912 			// Critical error, give up
1913 			fclose(cf);
1914 			return errcode;
1915 		}
1916 	}
1917 
1918 	fclose (cf);
1919 
1920 	return 0;
1921 }
1922 
1923 
1924 /*
1925 	Parse a line of the configuration file.
1926 	For each keyword recognised, perform appropriate handling.
1927 	If the keyword is not recognised, print a message to syslog
1928 	and continue.
1929 
1930 	Returns
1931 		0 success, or recoverable config file error
1932 		non-zero serious system error, processing aborted
1933  */
1934 static errcode_t
process_config_line(config_t * conf,char * line,config_file_context_t * context)1935 process_config_line (
1936 	config_t * conf,
1937 	char * line,
1938 	config_file_context_t * context
1939 )
1940 {
1941 	char * curr = line;
1942 	char * word;
1943 
1944 	word = get_next_word (curr, &curr);
1945 	if (! word || word [0] == k_comment_char)
1946 	{
1947 		// Nothing interesting on this line
1948 		return 0;
1949 	}
1950 
1951 	if (strcmp (word, k_keyword_domain) == 0)
1952 	{
1953 		word = get_next_word (curr, &curr);
1954 		if (word)
1955 		{
1956 			int errcode = add_domain (conf, word);
1957 			if (errcode)
1958 			{
1959 				// something badly wrong, bail
1960 				return errcode;
1961 			}
1962 
1963 			if (get_next_word (curr, NULL))
1964 			{
1965 				syslog (LOG_WARNING,
1966 					"%s, line %d: ignored extra text found after domain",
1967 					context->filename,
1968 					context->linenum
1969 				);
1970 			}
1971 		}
1972 		else
1973 		{
1974 			syslog (LOG_WARNING,
1975 				"%s, line %d: no domain specified",
1976 				context->filename,
1977 				context->linenum
1978 			);
1979 		}
1980 	}
1981 	else
1982 	{
1983 		syslog (LOG_WARNING,
1984 			"%s, line %d: unknown keyword %s - skipping",
1985 			context->filename,
1986 			context->linenum,
1987 			word
1988 		);
1989 	}
1990 
1991 	return 0;
1992 }
1993 
1994 
1995 /*
1996 	Get next word (whitespace separated) from input string.
1997 	A null character is written into the first whitespace character following
1998 	the word.
1999 
2000 	Parameters
2001 		input
2002 			Input string.  This string is modified by get_next_word.
2003 		next
2004 			If non-NULL and the result is non-NULL, a pointer to the
2005 			character following the end of the word (after the null)
2006 			is written to 'next'.
2007 			If no word is found, the original value is unchanged.
2008 			If the word extended to the end of the string, 'next' points
2009 			to the trailling NULL.
2010 			It is safe to pass 'str' as 'input' and '&str' as 'next'.
2011 	Returns
2012 		Pointer to the first non-whitespace character (and thus word) found.
2013 		if no word is found, returns NULL.
2014  */
2015 static char *
get_next_word(char * input,char ** next)2016 get_next_word (char * input, char **next)
2017 {
2018 	char * curr = input;
2019 	char * result;
2020 
2021 	while (isspace (*curr))
2022 	{
2023 		curr ++;
2024 	}
2025 
2026 	if (*curr == 0)
2027 	{
2028 		return NULL;
2029 	}
2030 
2031 	result = curr;
2032 	while (*curr && ! isspace (*curr))
2033 	{
2034 		curr++;
2035 	}
2036 	if (*curr)
2037 	{
2038 		*curr = 0;
2039 		if (next)
2040 		{
2041 			*next = curr+1;
2042 		}
2043 	}
2044 	else
2045 	{
2046 		if (next)
2047 		{
2048 			*next = curr;
2049 		}
2050 	}
2051 
2052 	return result;
2053 }
2054 
2055 
2056 static errcode_t
default_config(config_t * conf)2057 default_config (config_t * conf)
2058 {
2059 	int i;
2060 	for (i = 0; k_default_domains [i]; i++)
2061 	{
2062 		int errcode =
2063 			add_domain (conf, k_default_domains [i]);
2064 		if (errcode)
2065 		{
2066 			// Something has gone (badly) wrong - let's bail
2067 			return errcode;
2068 		}
2069 	}
2070 
2071 	return 0;
2072 }
2073 
2074 
2075 static errcode_t
add_domain(config_t * conf,const char * domain)2076 add_domain (config_t * conf, const char * domain)
2077 {
2078 	if (! contains_domain (conf, domain))
2079 	{
2080 		domain_entry_t * d =
2081 			(domain_entry_t *) malloc (sizeof (domain_entry_t));
2082 		if (! d)
2083 		{
2084 			syslog (LOG_ERR,
2085 				"mdns: Can't allocate memory in nss_mdns:init_config, %s:%d",
2086 				__FILE__, __LINE__
2087 			);
2088 			return ENOMEM;
2089 		}
2090 
2091 		d->domain = strdup (domain);
2092 		if (! d->domain)
2093 		{
2094 			syslog (LOG_ERR,
2095 				"mdns: Can't allocate memory in nss_mdns:init_config, %s:%d",
2096 				__FILE__, __LINE__
2097 			);
2098 			free (d);
2099 			return ENOMEM;
2100 		}
2101 		d->next = conf->domains;
2102 		conf->domains = d;
2103 	}
2104 
2105 	return 0;
2106 }
2107 
2108 
2109 static int
contains_domain(const config_t * conf,const char * domain)2110 contains_domain (const config_t * conf, const char * domain)
2111 {
2112 	const domain_entry_t * curr = conf->domains;
2113 
2114 	while (curr != NULL)
2115 	{
2116 		if (strcasecmp (curr->domain, domain) == 0)
2117 		{
2118 			return 1;
2119 		}
2120 
2121 		curr = curr->next;
2122 	}
2123 
2124 	return 0;
2125 }
2126 
2127 
2128 static int
contains_domain_suffix(const config_t * conf,const char * addr)2129 contains_domain_suffix (const config_t * conf, const char * addr)
2130 {
2131 	const domain_entry_t * curr = conf->domains;
2132 
2133 	while (curr != NULL)
2134 	{
2135 		if (cmp_dns_suffix (addr, curr->domain) > 0)
2136 		{
2137 			return 1;
2138 		}
2139 
2140 		curr = curr->next;
2141 	}
2142 
2143 	return 0;
2144 }
2145 
2146 //----------
2147 // Types and Constants
2148 
2149 static const char * k_local_suffix = "local";
2150 static const char k_dns_separator = '.';
2151 
2152 static const int k_label_maxlen = DNS_LABEL_MAXLEN;
2153 	// Label entries longer than this are actually pointers.
2154 
2155 typedef struct
2156 {
2157 	int value;
2158 	const char * name;
2159 	const char * comment;
2160 } table_entry_t;
2161 
2162 static const table_entry_t k_table_af [] =
2163 	{
2164 		{ AF_UNSPEC, NULL, NULL },
2165 		{ AF_LOCAL, "LOCAL", NULL },
2166 		{ AF_UNIX, "UNIX", NULL },
2167 		{ AF_INET, "INET", NULL },
2168 		{ AF_INET6, "INET6", NULL }
2169 	};
2170 static const int k_table_af_size =
2171 	sizeof (k_table_af) / sizeof (* k_table_af);
2172 
2173 static const char * k_table_ns_class [] =
2174 	{
2175 		NULL,
2176 		"IN"
2177 	};
2178 static const int k_table_ns_class_size =
2179 	sizeof (k_table_ns_class) / sizeof (* k_table_ns_class);
2180 
2181 static const char * k_table_ns_type [] =
2182 	{
2183 		NULL,
2184 		"A",
2185 		"NS",
2186 		"MD",
2187 		"MF",
2188 		"CNAME",
2189 		"SOA",
2190 		"MB",
2191 		"MG",
2192 		"MR",
2193 		"NULL",
2194 		"WKS",
2195 		"PTR",
2196 		"HINFO",
2197 		"MINFO",
2198 		"MX",
2199 		"TXT",
2200 		"RP",
2201 		"AFSDB",
2202 		"X25",
2203 		"ISDN",
2204 		"RT",
2205 		"NSAP",
2206 		NULL,
2207 		"SIG",
2208 		"KEY",
2209 		"PX",
2210 		"GPOS",
2211 		"AAAA",
2212 		"LOC",
2213 		"NXT",
2214 		"EID",
2215 		"NIMLOC",
2216 		"SRV",
2217 		"ATMA",
2218 		"NAPTR",
2219 		"KX",
2220 		"CERT",
2221 		"A6",
2222 		"DNAME",
2223 		"SINK",
2224 		"OPT"
2225 	};
2226 static const int k_table_ns_type_size =
2227 	sizeof (k_table_ns_type) / sizeof (* k_table_ns_type);
2228 
2229 
2230 //----------
2231 // Local prototypes
2232 
2233 static int
2234 simple_table_index (const char * table [], int size, const char * str);
2235 
2236 static int
2237 table_index_name (const table_entry_t table [], int size, const char * str);
2238 
2239 static int
2240 table_index_value (const table_entry_t table [], int size, int n);
2241 
2242 
2243 //----------
2244 // Global variables
2245 
2246 
2247 //----------
2248 // Util functions
2249 
2250 int
count_dots(const char * name)2251 count_dots (const char * name)
2252 {
2253 	int count = 0;
2254 	int i;
2255 	for (i = 0; name[i]; i++)
2256 	{
2257 		if (name [i] == k_dns_separator)
2258 			count++;
2259 	}
2260 
2261 	return count;
2262 }
2263 
2264 
2265 int
islocal(const char * name)2266 islocal (const char * name)
2267 {
2268 	return cmp_dns_suffix (name, k_local_suffix) > 0;
2269 }
2270 
2271 
2272 int
rr_to_af(ns_type_t rrtype)2273 rr_to_af (ns_type_t rrtype)
2274 {
2275 	switch (rrtype)
2276 	{
2277 	  case kDNSServiceType_A:
2278 		return AF_INET;
2279 
2280 	  case kDNSServiceType_AAAA:
2281 		return AF_INET6;
2282 
2283 	  default:
2284 		return AF_UNSPEC;
2285 	}
2286 }
2287 
2288 
2289 ns_type_t
af_to_rr(int af)2290 af_to_rr (int af)
2291 {
2292 	switch (af)
2293 	{
2294 	  case AF_INET:
2295 		return kDNSServiceType_A;
2296 
2297 	  case AF_INET6:
2298 		return kDNSServiceType_AAAA;
2299 
2300 	  default:
2301 		//return ns_t_invalid;
2302 		return 0;
2303 	}
2304 }
2305 
2306 
2307 int
str_to_af(const char * str)2308 str_to_af (const char * str)
2309 {
2310 	int result =
2311 		table_index_name (k_table_af, k_table_af_size, str);
2312 	if (result < 0)
2313 		result = 0;
2314 
2315 	return k_table_af [result].value;
2316 }
2317 
2318 
2319 ns_class_t
str_to_ns_class(const char * str)2320 str_to_ns_class (const char * str)
2321 {
2322 	return (ns_class_t)
2323 		simple_table_index (k_table_ns_class, k_table_ns_class_size, str);
2324 }
2325 
2326 
2327 ns_type_t
str_to_ns_type(const char * str)2328 str_to_ns_type (const char * str)
2329 {
2330 	return (ns_type_t)
2331 		simple_table_index (k_table_ns_type, k_table_ns_type_size, str);
2332 }
2333 
2334 
2335 const char *
af_to_str(int in)2336 af_to_str (int in)
2337 {
2338 	int result =
2339 		table_index_value (k_table_af, k_table_af_size, in);
2340 	if (result < 0)
2341 		result = 0;
2342 
2343 	return k_table_af [result].name;
2344 }
2345 
2346 
2347 const char *
ns_class_to_str(ns_class_t in)2348 ns_class_to_str (ns_class_t in)
2349 {
2350 	if (in < k_table_ns_class_size)
2351 		return k_table_ns_class [in];
2352 	else
2353 		return NULL;
2354 }
2355 
2356 
2357 const char *
ns_type_to_str(ns_type_t in)2358 ns_type_to_str (ns_type_t in)
2359 {
2360 	if (in < k_table_ns_type_size)
2361 		return k_table_ns_type [in];
2362 	else
2363 		return NULL;
2364 }
2365 
2366 
2367 char *
format_reverse_addr_in(const struct in_addr * addr,int prefixlen,char * buf)2368 format_reverse_addr_in (
2369 	const struct in_addr * addr,
2370 	int prefixlen,
2371 	char * buf
2372 )
2373 {
2374 	char * curr = buf;
2375 	int i;
2376 
2377 	const uint8_t * in_addr_a = (uint8_t *) addr;
2378 
2379 	if (prefixlen > 32)
2380 		return NULL;
2381 	if (prefixlen < 0)
2382 		prefixlen = 32;
2383 
2384 	i = (prefixlen + 7) / 8;
2385 		// divide prefixlen into bytes, rounding up
2386 
2387 	while (i > 0)
2388 	{
2389 		i--;
2390 		curr += sprintf (curr, "%d.", in_addr_a [i]);
2391 	}
2392 	sprintf (curr, "in-addr.arpa");
2393 
2394 	return buf;
2395 }
2396 
2397 
2398 char *
format_reverse_addr_in6(const struct in6_addr * addr,int prefixlen,char * buf)2399 format_reverse_addr_in6 (
2400 	const struct in6_addr * addr,
2401 	int prefixlen,
2402 	char * buf
2403 )
2404 {
2405 	char * curr = buf;
2406 	int i;
2407 
2408 	const uint8_t * in_addr_a = (uint8_t *) addr;
2409 
2410 	if (prefixlen > 128)
2411 		return NULL;
2412 	if (prefixlen < 0)
2413 		prefixlen = 128;
2414 
2415 	i = (prefixlen + 3) / 4;
2416 		// divide prefixlen into nibbles, rounding up
2417 
2418 	// Special handling for first
2419 	if (i % 2)
2420 	{
2421 		curr += sprintf (curr, "%d.", (in_addr_a [i/2] >> 4) & 0x0F);
2422 	}
2423 	i >>= 1;
2424 		// Convert i to bytes (divide by 2)
2425 
2426 	while (i > 0)
2427 	{
2428 		uint8_t val;
2429 
2430 		i--;
2431 		val = in_addr_a [i];
2432 		curr += sprintf (curr, "%x.%x.", val & 0x0F, (val >> 4) & 0x0F);
2433 	}
2434 	sprintf (curr, "ip6.arpa");
2435 
2436 	return buf;
2437 }
2438 
2439 
2440 char *
format_reverse_addr(int af,const void * addr,int prefixlen,char * buf)2441 format_reverse_addr (
2442 	int af,
2443 	const void * addr,
2444 	int prefixlen,
2445 	char * buf
2446 )
2447 {
2448 	switch (af)
2449 	{
2450 	  case AF_INET:
2451 		return
2452 			format_reverse_addr_in (
2453 				(struct in_addr *) addr, prefixlen, buf
2454 			);
2455 		break;
2456 
2457 	  case AF_INET6:
2458 		return
2459 			format_reverse_addr_in6 (
2460 				(struct in6_addr *) addr, prefixlen, buf
2461 			);
2462 		break;
2463 
2464 	  default:
2465 		return NULL;
2466 	}
2467 }
2468 
2469 
2470 int
cmp_dns_suffix(const char * name,const char * domain)2471 cmp_dns_suffix (const char * name, const char * domain)
2472 {
2473 	const char * nametail;
2474 	const char * domaintail;
2475 
2476 	// Idiot checks
2477 	if (*name == 0 || *name == k_dns_separator)
2478 	{
2479 		// Name can't be empty or start with separator
2480 		return CMP_DNS_SUFFIX_BAD_NAME;
2481 	}
2482 
2483 	if (*domain == 0)
2484 	{
2485 		return CMP_DNS_SUFFIX_SUCCESS;
2486 			// trivially true
2487 	}
2488 
2489 	if (*domain == k_dns_separator)
2490 	{
2491 		// drop leading separator from domain
2492 		domain++;
2493 		if (*domain == k_dns_separator)
2494 		{
2495 			return CMP_DNS_SUFFIX_BAD_DOMAIN;
2496 		}
2497 	}
2498 
2499 	// Find ends of strings
2500 	for (nametail = name; *nametail; nametail++)
2501 		;
2502 	for (domaintail = domain; *domaintail; domaintail++)
2503 		;
2504 
2505 	// Shuffle back to last real character, and drop any trailing '.'
2506 	// while we're at it.
2507 	nametail --;
2508 	if (*nametail == k_dns_separator)
2509 	{
2510 		nametail --;
2511 		if (*nametail == k_dns_separator)
2512 		{
2513 			return CMP_DNS_SUFFIX_BAD_NAME;
2514 		}
2515 	}
2516 	domaintail --;
2517 	if (*domaintail == k_dns_separator)
2518 	{
2519 		domaintail --;
2520 		if (*domaintail == k_dns_separator)
2521 		{
2522 			return CMP_DNS_SUFFIX_BAD_DOMAIN;
2523 		}
2524 	}
2525 
2526 	// Compare.
2527 	while (
2528 		nametail >= name
2529 		&& domaintail >= domain
2530 		&& tolower(*nametail) == tolower(*domaintail))
2531 	{
2532 		nametail--;
2533 		domaintail--;
2534 	}
2535 
2536 	/* A successful finish will be one of the following:
2537 		(leading and trailing . ignored)
2538 
2539 		name  :  domain2.domain1
2540 		domain:  domain2.domain1
2541 		        ^
2542 
2543 		name  : domain3.domain2.domain1
2544 		domain:         domain2.domain1
2545 		               ^
2546 	 */
2547 	if (
2548 		domaintail < domain
2549 		&& (nametail < name || *nametail == k_dns_separator)
2550 	)
2551 	{
2552 		return CMP_DNS_SUFFIX_SUCCESS;
2553 	}
2554 	else
2555 	{
2556 		return CMP_DNS_SUFFIX_FAILURE;
2557 	}
2558 }
2559 
2560 
2561 int
dns_rdata_to_name(const char * rdata,int rdlen,char * name,int name_len)2562 dns_rdata_to_name (const char * rdata, int rdlen, char * name, int name_len)
2563 {
2564 	int i = 0;
2565 		// Index into 'name'
2566 	const char * rdata_curr = rdata;
2567 
2568 	if (rdlen == 0) return DNS_RDATA_TO_NAME_BAD_FORMAT;
2569 
2570 	/*
2571 		In RDATA, a DNS name is stored as a series of labels.
2572 		Each label consists of a length octet (max value 63)
2573 		followed by the data for that label.
2574 		The series is terminated with a length 0 octet.
2575 		A length octet beginning with bits 11 is a pointer to
2576 		somewhere else in the payload, but we don't support these
2577 		since we don't have access to the entire payload.
2578 
2579 		See RFC1034 section 3.1 and RFC1035 section 3.1.
2580 	 */
2581 	while (1)
2582 	{
2583 		int term_len = *rdata_curr;
2584 		rdata_curr++;
2585 
2586 		if (term_len == 0)
2587 		{
2588 			break;
2589 				// 0 length record terminates label
2590 		}
2591 		else if (term_len > k_label_maxlen)
2592 		{
2593 			name [i] = 0;
2594 			return DNS_RDATA_TO_NAME_PTR;
2595 		}
2596 		else if (rdata_curr + term_len > rdata + rdlen)
2597 		{
2598 			name [i] = 0;
2599 			return DNS_RDATA_TO_NAME_BAD_FORMAT;
2600 		}
2601 
2602 		if (name_len < i + term_len + 1)
2603 			// +1 is separator
2604 		{
2605 			name [i] = 0;
2606 			return DNS_RDATA_TO_NAME_TOO_LONG;
2607 		}
2608 
2609 		memcpy (name + i, rdata_curr, term_len);
2610 
2611 		i += term_len;
2612 		rdata_curr += term_len;
2613 
2614 		name [i] = k_dns_separator;
2615 		i++;
2616 	}
2617 
2618 	name [i] = 0;
2619 	return i;
2620 }
2621 
2622 
2623 //----------
2624 // Local functions
2625 
2626 /*
2627 	Find the index of an string entry in a table.  A case insenitive match
2628 	is performed.  If no entry is found, 0 is returned.
2629 
2630 	Parameters
2631 		table
2632 			Lookup table
2633 			Table entries may be NULL.  NULL entries will never match.
2634 		size
2635 			number of entries in table
2636 		str
2637 			lookup string
2638 
2639 	Result
2640 		index of first matching entry, or 0 if no matches
2641  */
2642 static int
simple_table_index(const char * table[],int size,const char * str)2643 simple_table_index (const char * table [], int size, const char * str)
2644 {
2645 	int i;
2646 	for (i = 0; i < size; i++)
2647 	{
2648 		if (
2649 			table [i]
2650 			&& (strcasecmp (table [i], str) == 0)
2651 		)
2652 		{
2653 			return i;
2654 		}
2655 	}
2656 
2657 	return 0;
2658 }
2659 
2660 
2661 /*
2662 	Find the index of a name in a table.
2663 
2664 	Parameters
2665 		table
2666 			array of table_entry_t records.  The name field is compared
2667 			(ignoring case) to the input string.
2668 		size
2669 			number of entries in table
2670 		str
2671 			lookup string
2672 
2673 	Result
2674 		index of first matching entry, or -1 if no matches
2675  */
2676 static int
table_index_name(const table_entry_t table[],int size,const char * str)2677 table_index_name (const table_entry_t table [], int size, const char * str)
2678 {
2679 	int i;
2680 	for (i = 0; i < size; i++)
2681 	{
2682 		if (
2683 			table [i].name
2684 			&& (strcasecmp (table [i].name, str) == 0)
2685 		)
2686 		{
2687 			return i;
2688 		}
2689 	}
2690 
2691 	return -1;
2692 }
2693 
2694 
2695 /*
2696 	Find the index of a value a table.
2697 
2698 	Parameters
2699 		table
2700 			array of table_entry_t records.  The value field is compared to
2701 			the input value
2702 		size
2703 			number of entries in table
2704 		n
2705 			lookup value
2706 
2707 	Result
2708 		index of first matching entry, or -1 if no matches
2709  */
2710 static int
table_index_value(const table_entry_t table[],int size,int n)2711 table_index_value (const table_entry_t table [], int size, int n)
2712 {
2713 	int i;
2714 	for (i = 0; i < size; i++)
2715 	{
2716 		if (table [i].value == n)
2717 		{
2718 			return i;
2719 		}
2720 	}
2721 
2722 	return -1;
2723 }
2724