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 */
99int
100count_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 */
110int
111islocal (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 */
129char *
130format_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 */
148char *
149format_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 */
171char *
172format_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 */
192int
193cmp_dns_suffix (const char * name, const char * domain);
194enum
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
202typedef int ns_type_t;
203typedef 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 */
216int
217rr_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 */
230ns_type_t
231af_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 */
240int
241str_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 */
250ns_class_t
251str_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 */
260ns_type_t
261str_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 */
271const char *
272af_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 */
282const char *
283ns_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 */
293const char *
294ns_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 */
323static int
324dns_rdata_to_name (const unsigned char * rdata, int rdlen, char * name, unsigned int name_len);
325enum
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
344typedef 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 */
362int
363config_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 */
376errcode_t
377init_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
389typedef enum nss_status nss_status;
390typedef 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 */
408nss_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 */
437nss_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 */
469nss_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
485const 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
494typedef 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
501typedef 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
520static const struct timeval
521k_select_time = { 0, 500000 };
522// 0 seconds, 500 milliseconds
523
524//----------
525// Local prototypes
526
527static nss_status
528mdns_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 */
542static nss_status
543mdns_lookup_name (
544    const char * fullname,
545    int af,
546    result_map_t * result
547    );
548
549/*
550    Lookup address using mDNS server
551 */
552static nss_status
553mdns_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 */
565static nss_status
566handle_events (DNSServiceRef sdref, result_map_t * result, const char * str);
567
568
569// Callback for mdns_lookup operations
570//DNSServiceQueryRecordReply mdns_lookup_callback;
571typedef void
572mdns_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
587mdns_lookup_callback_t mdns_lookup_callback;
588
589
590static int
591init_result (
592    result_map_t * result,
593    hostent * result_buf,
594    char * buf,
595    size_t buflen
596    );
597
598static int
599callback_body_ptr (
600    const char * fullname,
601    result_map_t * result,
602    int rdlen,
603    const void * rdata
604    );
605
606static void *
607add_address_to_buffer (result_map_t * result, const void * data, int len);
608static char *
609add_alias_to_buffer (result_map_t * result, const char * data, int len);
610static char *
611add_hostname_len (result_map_t * result, const char * fullname, int len);
612static char *
613add_hostname_or_alias (result_map_t * result, const char * data, int len);
614
615static void *
616contains_address (result_map_t * result, const void * data, int len);
617static char *
618contains_alias (result_map_t * result, const char * data);
619
620
621static const char *
622is_applicable_name (
623    result_map_t * result,
624    const char * name,
625    char * lookup_name
626    );
627
628static const char *
629is_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
639static nss_status
640set_err (result_map_t * result, nss_status status, int err, int herr);
641
642static nss_status set_err_notfound (result_map_t * result);
643static nss_status set_err_bad_hostname (result_map_t * result);
644static nss_status set_err_buf_too_small (result_map_t * result);
645static nss_status set_err_internal_resource_full (result_map_t * result);
646static nss_status set_err_system (result_map_t * result);
647static nss_status set_err_mdns_failed (result_map_t * result);
648static nss_status set_err_success (result_map_t * result);
649
650
651//----------
652// Global variables
653
654
655//----------
656// NSS functions
657
658nss_status
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
681nss_status
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
705nss_status
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
781nss_status
782mdns_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 */
844static nss_status
845mdns_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 */
931static nss_status
932mdns_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 */
999static nss_status
1000handle_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 */
1057void
1058mdns_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
1161static int
1162callback_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 */
1251static void *
1252add_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
1309static void *
1310contains_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 */
1353static char *
1354add_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
1400static char *
1401contains_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
1431static char *
1432add_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 */
1473static char *
1474add_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
1496static int
1497init_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 */
1546static nss_status
1547set_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
1556static nss_status
1557set_err_notfound (result_map_t * result)
1558{
1559    return set_err (result, NSS_STATUS_NOTFOUND, ENOENT, HOST_NOT_FOUND);
1560}
1561
1562static nss_status
1563set_err_bad_hostname (result_map_t * result)
1564{
1565    return set_err (result, NSS_STATUS_TRYAGAIN, ENOENT, NO_RECOVERY);
1566}
1567
1568static nss_status
1569set_err_buf_too_small (result_map_t * result)
1570{
1571    return set_err (result, NSS_STATUS_TRYAGAIN, ERANGE, NETDB_INTERNAL);
1572}
1573
1574static nss_status
1575set_err_internal_resource_full (result_map_t * result)
1576{
1577    return set_err (result, NSS_STATUS_RETURN, ERANGE, NO_RECOVERY);
1578}
1579
1580static nss_status
1581set_err_system (result_map_t * result)
1582{
1583    return set_err (result, NSS_STATUS_UNAVAIL, errno, NETDB_INTERNAL);
1584}
1585
1586static nss_status
1587set_err_mdns_failed (result_map_t * result)
1588{
1589    return set_err (result, NSS_STATUS_TRYAGAIN, EAGAIN, TRY_AGAIN);
1590}
1591
1592static nss_status
1593set_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 */
1607static const char *
1608is_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 */
1644static const char *
1645is_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
1687const char * k_conf_file = "/etc/nss_mdns.conf";
1688#define CONF_LINE_SIZE 1024
1689
1690const char k_comment_char = '#';
1691
1692const char * k_keyword_domain = "domain";
1693
1694const 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
1711typedef struct domain_entry
1712{
1713    char * domain;
1714    struct domain_entry * next;
1715} domain_entry_t;
1716
1717
1718// Config
1719typedef struct
1720{
1721    domain_entry_t * domains;
1722} config_t;
1723
1724const config_t k_empty_config =
1725{
1726    NULL
1727};
1728
1729
1730// Context - tracks position in config file, used for error reporting
1731typedef struct
1732{
1733    const char * filename;
1734    int linenum;
1735} config_file_context_t;
1736
1737
1738//----------
1739// Local prototypes
1740
1741static errcode_t
1742load_config (config_t * conf);
1743
1744static errcode_t
1745process_config_line (
1746    config_t * conf,
1747    char * line,
1748    config_file_context_t * context
1749    );
1750
1751static char *
1752get_next_word (char * input, char **next);
1753
1754static errcode_t
1755default_config (config_t * conf);
1756
1757static errcode_t
1758add_domain (config_t * conf, const char * domain);
1759
1760static int
1761contains_domain (const config_t * conf, const char * domain);
1762
1763static int
1764contains_domain_suffix (const config_t * conf, const char * addr);
1765
1766
1767//----------
1768// Global variables
1769
1770static config_t * g_config = NULL;
1771// Configuration info
1772
1773pthread_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 */
1792errcode_t
1793init_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
1865int
1866config_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
1884static errcode_t
1885load_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 */
1934static errcode_t
1935process_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 */
2015static char *
2016get_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
2056static errcode_t
2057default_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
2075static errcode_t
2076add_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
2109static int
2110contains_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
2128static int
2129contains_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
2149static const char * k_local_suffix = "local";
2150static const char k_dns_separator = '.';
2151
2152static const unsigned int k_label_maxlen = DNS_LABEL_MAXLEN;
2153// Label entries longer than this are actually pointers.
2154
2155typedef struct
2156{
2157    int value;
2158    const char * name;
2159    const char * comment;
2160} table_entry_t;
2161
2162static 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};
2170static const int k_table_af_size =
2171    sizeof (k_table_af) / sizeof (*k_table_af);
2172
2173static const char * k_table_ns_class [] =
2174{
2175    NULL,
2176    "IN"
2177};
2178static const int k_table_ns_class_size =
2179    sizeof (k_table_ns_class) / sizeof (*k_table_ns_class);
2180
2181static 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};
2226static 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
2233static int
2234simple_table_index (const char * table [], int size, const char * str);
2235
2236static int
2237table_index_name (const table_entry_t table [], int size, const char * str);
2238
2239static int
2240table_index_value (const table_entry_t table [], int size, int n);
2241
2242
2243//----------
2244// Global variables
2245
2246
2247//----------
2248// Util functions
2249
2250int
2251count_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
2265int
2266islocal (const char * name)
2267{
2268    return cmp_dns_suffix (name, k_local_suffix) > 0;
2269}
2270
2271
2272int
2273rr_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
2289ns_type_t
2290af_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
2307int
2308str_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
2319ns_class_t
2320str_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
2327ns_type_t
2328str_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
2335const char *
2336af_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
2347const char *
2348ns_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
2357const char *
2358ns_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
2367char *
2368format_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
2398char *
2399format_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
2440char *
2441format_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
2470int
2471cmp_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
2561static int
2562dns_rdata_to_name (const unsigned char * rdata, int rdlen, char * name, unsigned int name_len)
2563{
2564    int i = 0;
2565    // Index into 'name'
2566    const unsigned 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        unsigned 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 */
2642static int
2643simple_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 */
2676static int
2677table_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 */
2710static int
2711table_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