2 NICTA Public Software Licence
5 Copyright © 2004 National ICT Australia Ltd
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:
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
23 - The name of NICTA may not be used to endorse or promote products
24 derived from this Software without specific prior written permission.
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
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.
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:
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
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
66 NSSwitch Implementation of mDNS interface.
68 Andrew White (Andrew.White@nicta.com.au)
80 #include <sys/types.h>
82 #include <sys/socket.h>
84 #include <netinet/in.h>
86 #include <arpa/inet.h>
87 #define BIND_8_COMPAT 1
88 #include <arpa/nameser.h>
97 Count the number of dots in a name string.
100 count_dots (const char * name
);
104 Test whether a domain name is local.
107 1 if name ends with ".local" or ".local."
111 islocal (const char * name
);
115 Format an address structure as a string appropriate for DNS reverse (PTR)
116 lookup, based on address type.
120 Prefix length, in bits. When formatting, this will be rounded up
121 to the nearest appropriate size. If -1, assume maximum.
123 Output buffer. Must be long enough to hold largest possible
126 Pointer to (first character of) output buffer,
130 format_reverse_addr (int af
, const void * addr
, int prefixlen
, char * buf
);
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.
139 Prefix length, in bits. When formatting, this will be rounded up
140 to the nearest byte (8). If -1, assume 32.
142 Output buffer. Must be long enough to hold largest possible
143 output. For AF_INET, this is 29 characters (including null).
145 Pointer to (first character of) output buffer,
149 format_reverse_addr_in (
150 const struct in_addr
* addr
,
154 #define DNS_PTR_AF_INET_SIZE 29
157 Format an address structure as a string appropriate for DNS reverse (PTR)
158 lookup for AF_INET6. Output is in .ip6.int domain.
162 Prefix length, in bits. When formatting, this will be rounded up
163 to the nearest nibble (4). If -1, assume 128.
165 Output buffer. Must be long enough to hold largest possible
166 output. For AF_INET6, this is 72 characters (including null).
168 Pointer to (first character of) output buffer,
172 format_reverse_addr_in6 (
173 const struct in6_addr
* addr
,
177 #define DNS_PTR_AF_INET6_SIZE 72
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.
189 0 on failure (no match)
193 cmp_dns_suffix (const char * name
, const char * domain
);
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
202 typedef int ns_type_t
;
203 typedef int ns_class_t
;
206 Convert a DNS resource record (RR) code to an address family (AF) code.
210 resource record type (from nameser.h)
213 Appropriate AF code (from socket.h), or AF_UNSPEC if an appropriate
214 mapping couldn't be determined
217 rr_to_af (ns_type_t rrtype
);
221 Convert an address family (AF) code to a DNS resource record (RR) code.
225 address family code (from socket.h)
227 Appropriate RR code (from nameser.h), or ns_t_invalid if an appropriate
228 mapping couldn't be determined
235 Convert a string to an address family (case insensitive).
238 Matching AF code, or AF_UNSPEC if no match found.
241 str_to_af (const char * str
);
245 Convert a string to an ns_class_t (case insensitive).
248 Matching ns_class_t, or ns_c_invalid if no match found.
251 str_to_ns_class (const char * str
);
255 Convert a string to an ns_type_t (case insensitive).
258 Matching ns_type_t, or ns_t_invalid if no match found.
261 str_to_ns_type (const char * str
);
265 Convert an address family code to a string.
268 String representation of AF,
269 or NULL if address family unrecognised or invalid.
276 Convert an ns_class_t code to a string.
279 String representation of ns_class_t,
280 or NULL if ns_class_t unrecognised or invalid.
283 ns_class_to_str (ns_class_t in
);
287 Convert an ns_type_t code to a string.
290 String representation of ns_type_t,
291 or NULL if ns_type_t unrecognised or invalid.
294 ns_type_to_str (ns_type_t in
);
298 Convert DNS rdata in label format (RFC1034, RFC1035) to a name.
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
307 Rdata formatted as series of labels.
309 Length of rdata buffer.
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.
315 Number of characters available in name buffer, not including
319 Length of name buffer (not including trailing null).
321 A return of 0 implies the empty domain.
324 dns_rdata_to_name (const char * rdata
, int rdlen
, char * name
, int name_len
);
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.
336 #define DNS_LABEL_MAXLEN 63
337 // Maximum length of a single DNS label
338 #define DNS_NAME_MAXLEN 255
339 // Maximum length of a DNS name
344 typedef int errcode_t
;
345 // Used for 0 = success, non-zero = error code functions
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.
360 -1 error, check errno
363 config_is_mdns_suffix (const char * name
);
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
373 0 configuration ready
374 non-zero configuration error code
379 #define ENTNAME hostent
380 #define DATABASE "hosts"
386 #include <sys/types.h>
389 typedef enum nss_status nss_status
;
390 typedef struct hostent hostent
;
393 gethostbyname implementation
402 length of auxillary buffer
409 _nss_mdns_gethostbyname_r (
411 hostent
* result_buf
,
420 gethostbyname2 implementation
431 length of auxillary buffer
438 _nss_mdns_gethostbyname2_r (
441 hostent
* result_buf
,
450 gethostbyaddr implementation
453 address structure to look up
455 length of address structure
463 length of auxillary buffer
470 _nss_mdns_gethostbyaddr_r (
474 hostent
* result_buf
,
483 // Types and Constants
485 const int MDNS_VERBOSE
= 0;
486 // This enables verbose syslog messages
487 // If zero, only "imporant" messages will appear in syslog
489 #define k_hostname_maxlen 255
490 // As per RFC1034 and RFC1035
491 #define k_aliases_max 15
492 #define k_addrs_max 15
494 const int k_mdnsd_intfs_local
= 0;
495 // Tell mdnsd to perform lookups only using link-local interfaces.
497 Currently, this feature is buggy. 0 will actually cause mdnsd to
498 do what it thinks is best. Unfortunately, this is to lookup 'local'
499 addresses locally and remote addresses via the DNS. Thus, lookups
500 for non-"local" addresses via mdns will not work correctly.
502 Apple is currently modifying mdnsd to allow a special interface id
503 (expected value -2) to mean "always lookup locally". This constant
504 should be changed once the change is made.
510 typedef struct buf_header
512 char hostname
[k_hostname_maxlen
+ 1];
513 char * aliases
[k_aliases_max
+ 1];
514 char * addrs
[k_addrs_max
+ 1];
517 typedef struct result_map
522 buf_header_t
* header
;
527 // Index for addresses - grow from low end
528 // Index points to first empty space
530 // Index for aliases - grow from high end
531 // Index points to lowest entry
536 static const struct timeval
537 k_select_time
= { 0, 500000 };
538 // 0 seconds, 500 milliseconds
544 mdns_gethostbyname2 (
547 hostent
* result_buf
,
556 Lookup name using mDNS server
560 const char * fullname
,
562 result_map_t
* result
566 Lookup address using mDNS server
573 const char * addr_str
,
574 result_map_t
* result
579 Handle incoming MDNS events
582 handle_events (DNSServiceRef sdref
, result_map_t
* result
, const char * str
);
585 // Callback for mdns_lookup operations
586 //DNSServiceQueryRecordReply mdns_lookup_callback;
588 mdns_lookup_callback_t
591 DNSServiceFlags flags
,
592 uint32_t interface_index
,
593 DNSServiceErrorType error_code
,
594 const char *fullname
,
603 mdns_lookup_callback_t mdns_lookup_callback
;
608 result_map_t
* result
,
609 hostent
* result_buf
,
616 const char * fullname
,
617 result_map_t
* result
,
623 add_address_to_buffer (result_map_t
* result
, const void * data
, int len
);
625 add_alias_to_buffer (result_map_t
* result
, const char * data
, int len
);
627 add_hostname_len (result_map_t
* result
, const char * fullname
, int len
);
629 add_hostname_or_alias (result_map_t
* result
, const char * data
, int len
);
632 contains_address (result_map_t
* result
, const void * data
, int len
);
634 contains_alias (result_map_t
* result
, const char * data
);
639 result_map_t
* result
,
646 result_map_t
* result
,
653 // Error code functions
656 set_err (result_map_t
* result
, nss_status status
, int err
, int herr
);
658 static nss_status
set_err_notfound (result_map_t
* result
);
659 static nss_status
set_err_bad_hostname (result_map_t
* result
);
660 static nss_status
set_err_buf_too_small (result_map_t
* result
);
661 static nss_status
set_err_internal_resource_full (result_map_t
* result
);
662 static nss_status
set_err_system (result_map_t
* result
);
663 static nss_status
set_err_mdns_failed (result_map_t
* result
);
664 static nss_status
set_err_success (result_map_t
* result
);
675 _nss_mdns_gethostbyname_r (
677 hostent
* result_buf
,
686 "mdns: Called nss_mdns_gethostbyname with %s",
691 mdns_gethostbyname2 (
692 name
, AF_INET
, result_buf
, buf
, buflen
, errnop
, h_errnop
698 _nss_mdns_gethostbyname2_r (
701 hostent
* result_buf
,
710 "mdns: Called nss_mdns_gethostbyname2 with %s",
715 mdns_gethostbyname2 (
716 name
, af
, result_buf
, buf
, buflen
, errnop
, h_errnop
722 _nss_mdns_gethostbyaddr_r (
726 hostent
* result_buf
,
733 char addr_str
[NI_MAXHOST
+ 1];
737 if (inet_ntop (af
, addr
, addr_str
, NI_MAXHOST
) == NULL
)
739 const char * family
= af_to_str (af
);
746 "mdns: Couldn't covert address, family %d (%s) in nss_mdns_gethostbyaddr: %s",
752 // This address family never applicable to us, so return NOT_FOUND
755 *h_errnop
= HOST_NOT_FOUND
;
756 return NSS_STATUS_NOTFOUND
;
761 "mdns: Called nss_mdns_gethostbyaddr with %s",
767 err_status
= init_result (&result
, result_buf
, buf
, buflen
);
770 *errnop
= err_status
;
771 *h_errnop
= NETDB_INTERNAL
;
772 return NSS_STATUS_TRYAGAIN
;
775 if (is_applicable_addr (&result
, addr
, af
, addr_str
))
779 rv
= mdns_lookup_addr (addr
, len
, af
, addr_str
, &result
);
780 if (rv
== NSS_STATUS_SUCCESS
)
786 // Return current error status (defaults to NOT_FOUND)
788 *errnop
= result
.r_errno
;
789 *h_errnop
= result
.r_h_errno
;
790 return result
.status
;
798 mdns_gethostbyname2 (
801 hostent
* result_buf
,
808 char lookup_name
[k_hostname_maxlen
+ 1];
813 err_status
= init_result (&result
, result_buf
, buf
, buflen
);
816 *errnop
= err_status
;
817 *h_errnop
= NETDB_INTERNAL
;
818 return NSS_STATUS_TRYAGAIN
;
821 if (is_applicable_name (&result
, name
, lookup_name
))
828 "mdns: Local name: %s",
832 rv
= mdns_lookup_name (name
, af
, &result
);
833 if (rv
== NSS_STATUS_SUCCESS
)
839 // Return current error status (defaults to NOT_FOUND)
841 *errnop
= result
.r_errno
;
842 *h_errnop
= result
.r_h_errno
;
843 return result
.status
;
848 Lookup a fully qualified hostname using the default record type
849 for the specified address family.
853 Fully qualified hostname. If not fully qualified the code will
854 still 'work', but the lookup is unlikely to succeed.
856 Either AF_INET or AF_INET6. Other families are not supported.
858 Initialised 'result' data structure.
862 const char * fullname
,
864 result_map_t
* result
867 // Lookup using mDNS.
868 DNSServiceErrorType errcode
;
875 "mdns: Attempting lookup of %s",
883 result
->hostent
->h_length
= 4;
884 // Length of an A record
889 result
->hostent
->h_length
= 16;
890 // Length of an AAAA record
895 "mdns: Unsupported address family %d",
898 return set_err_bad_hostname (result
);
900 result
->hostent
->h_addrtype
= af
;
903 DNSServiceQueryRecord (
905 0, // reserved flags field
906 k_mdnsd_intfs_local
, // all local interfaces
907 fullname
, // full name to query for
908 rrtype
, // resource record type
909 C_IN
, // internet class records
910 mdns_lookup_callback
, // callback
911 result
// Context - result buffer
917 "mdns: Failed to initialise lookup, error %d",
920 return set_err_mdns_failed (result
);
923 status
= handle_events (sdref
, result
, fullname
);
924 DNSServiceRefDeallocate (sdref
);
930 Reverse (PTR) lookup for the specified address.
934 Either a struct in_addr or a struct in6_addr
938 Either AF_INET or AF_INET6. Other families are not supported.
941 Address in format suitable for PTR lookup.
942 AF_INET: a.b.c.d -> d.c.b.a.in-addr.arpa
943 AF_INET6: reverse nibble format, x.x.x...x.ip6.arpa
945 Initialised 'result' data structure.
952 const char * addr_str
,
953 result_map_t
* result
956 DNSServiceErrorType errcode
;
962 "mdns: Attempting lookup of %s",
966 result
->hostent
->h_addrtype
= af
;
967 result
->hostent
->h_length
= addr_len
;
969 // Query address becomes "address" in result.
970 if (! add_address_to_buffer (result
, addr
, addr_len
))
972 return result
->status
;
975 result
->hostent
->h_name
[0] = 0;
978 DNSServiceQueryRecord (
980 0, // reserved flags field
981 k_mdnsd_intfs_local
, // all local interfaces
982 addr_str
, // address string to query for
983 T_PTR
, // pointer RRs
984 C_IN
, // internet class records
985 mdns_lookup_callback
, // callback
986 result
// Context - result buffer
992 "mdns: Failed to initialise mdns lookup, error %d",
995 return set_err_mdns_failed (result
);
998 status
= handle_events (sdref
, result
, addr_str
);
999 DNSServiceRefDeallocate (sdref
);
1005 Wait on result of callback, and process it when it arrives.
1011 Initialised 'result' data structure.
1013 lookup string, used for status/error reporting.
1016 handle_events (DNSServiceRef sdref
, result_map_t
* result
, const char * str
)
1018 int dns_sd_fd
= DNSServiceRefSockFD(sdref
);
1019 int nfds
= dns_sd_fd
+ 1;
1024 while (! result
->done
)
1027 FD_SET(dns_sd_fd
, &readfds
);
1032 select (nfds
, &readfds
, (fd_set
*)NULL
, (fd_set
*)NULL
, &tv
);
1033 if (select_result
> 0)
1035 if (FD_ISSET(dns_sd_fd
, &readfds
))
1039 "mdns: Reply received for %s",
1042 DNSServiceProcessResult(sdref
);
1046 syslog (LOG_WARNING
,
1047 "mdns: Unexpected return from select on lookup of %s",
1054 // Terminate loop due to timer expiry
1057 "mdns: %s not found - timer expired",
1060 set_err_notfound (result
);
1065 return result
->status
;
1070 Examine incoming data and add to relevant fields in result structure.
1071 This routine is called from DNSServiceProcessResult where appropriate.
1074 mdns_lookup_callback
1076 DNSServiceRef sdref
,
1077 DNSServiceFlags flags
,
1078 uint32_t interface_index
,
1079 DNSServiceErrorType error_code
,
1080 const char *fullname
,
1089 // A single record is received
1091 result_map_t
* result
= (result_map_t
*) context
;
1093 (void)sdref
; // Unused
1094 (void)interface_index
; // Unused
1095 (void)ttl
; // Unused
1097 if (! (flags
& kDNSServiceFlagsMoreComing
) )
1102 if (error_code
== kDNSServiceErr_NoError
)
1104 ns_type_t expected_rr_type
=
1105 af_to_rr (result
->hostent
->h_addrtype
);
1107 // Idiot check class
1108 if (rrclass
!= C_IN
)
1110 syslog (LOG_WARNING
,
1111 "mdns: Received bad RR class: expected %d (%s),"
1112 " got %d (%s), RR type %d (%s)",
1114 ns_class_to_str (C_IN
),
1116 ns_class_to_str (rrclass
),
1118 ns_type_to_str (rrtype
)
1124 if (rrtype
== T_PTR
)
1126 if (callback_body_ptr (fullname
, result
, rdlen
, rdata
) < 0)
1129 else if (rrtype
== expected_rr_type
)
1132 add_hostname_or_alias (
1144 if (! add_address_to_buffer (result
, rdata
, rdlen
) )
1153 syslog (LOG_WARNING
,
1154 "mdns: Received bad RR type: expected %d (%s),"
1157 ns_type_to_str (expected_rr_type
),
1159 ns_type_to_str (rrtype
)
1164 if (result
->status
!= NSS_STATUS_SUCCESS
)
1165 set_err_success (result
);
1169 // For now, dump message to syslog and continue
1170 syslog (LOG_WARNING
,
1171 "mdns: callback returned error %d",
1179 const char * fullname
,
1180 result_map_t
* result
,
1185 char result_name
[k_hostname_maxlen
+ 1];
1188 // Fullname should be .in-addr.arpa or equivalent, which we're
1189 // not interested in. Ignore it.
1191 rv
= dns_rdata_to_name (rdata
, rdlen
, result_name
, k_hostname_maxlen
);
1194 const char * errmsg
;
1198 case DNS_RDATA_TO_NAME_BAD_FORMAT
:
1199 errmsg
= "mdns: PTR '%s' result badly formatted ('%s...')";
1202 case DNS_RDATA_TO_NAME_TOO_LONG
:
1203 errmsg
= "mdns: PTR '%s' result too long ('%s...')";
1206 case DNS_RDATA_TO_NAME_PTR
:
1207 errmsg
= "mdns: PTR '%s' result contained pointer ('%s...')";
1211 errmsg
= "mdns: PTR '%s' result conversion failed ('%s...')";
1214 syslog (LOG_WARNING
,
1226 "mdns: PTR '%s' resolved to '%s'",
1232 // Data should be a hostname
1234 add_hostname_or_alias (
1250 Add an address to the buffer.
1254 Result structure to write to
1256 Incoming address data buffer
1257 Must be 'int' aligned
1259 Length of data buffer (in bytes)
1260 Must match data alignment
1263 Pointer to start of newly written data,
1265 If address already exists in buffer, returns pointer to that instead.
1268 add_address_to_buffer (result_map_t
* result
, const void * data
, int len
)
1274 if ((temp
= contains_address (result
, data
, len
)))
1279 if (result
->addrs_count
>= k_addrs_max
)
1281 // Not enough addr slots
1282 set_err_internal_resource_full (result
);
1284 "mdns: Internal address buffer full; increase size"
1290 if (len
!= result
->hostent
->h_length
)
1292 syslog (LOG_WARNING
,
1293 "mdns: Unexpected rdata length for address. Expected %d, got %d",
1294 result
->hostent
->h_length
,
1297 // XXX And continue for now.
1300 new_addr
= result
->addr_idx
+ len
;
1302 if (new_addr
> result
->alias_idx
)
1305 set_err_buf_too_small (result
);
1308 "mdns: Ran out of buffer when adding address %d",
1309 result
->addrs_count
+ 1
1314 start
= result
->buffer
+ result
->addr_idx
;
1315 memcpy (start
, data
, len
);
1316 result
->addr_idx
= new_addr
;
1317 result
->header
->addrs
[result
->addrs_count
] = start
;
1318 result
->addrs_count
++;
1319 result
->header
->addrs
[result
->addrs_count
] = NULL
;
1326 contains_address (result_map_t
* result
, const void * data
, int len
)
1331 if (len
!= result
->hostent
->h_length
)
1333 syslog (LOG_WARNING
,
1334 "mdns: Unexpected rdata length for address. Expected %d, got %d",
1335 result
->hostent
->h_length
,
1338 // XXX And continue for now.
1341 for (i
= 0; result
->header
->addrs
[i
]; i
++)
1343 if (memcmp (result
->header
->addrs
[i
], data
, len
) == 0)
1345 return result
->header
->addrs
[i
];
1354 Add an alias to the buffer.
1358 Result structure to write to
1360 Incoming alias (null terminated)
1362 Length of data buffer (in bytes), including trailing null
1365 Pointer to start of newly written data,
1367 If alias already exists in buffer, returns pointer to that instead.
1370 add_alias_to_buffer (result_map_t
* result
, const char * data
, int len
)
1376 if ((temp
= contains_alias (result
, data
)))
1381 if (result
->aliases_count
>= k_aliases_max
)
1383 // Not enough alias slots
1384 set_err_internal_resource_full (result
);
1386 "mdns: Internal alias buffer full; increase size"
1391 new_alias
= result
->alias_idx
- len
;
1393 if (new_alias
< result
->addr_idx
)
1396 set_err_buf_too_small (result
);
1399 "mdns: Ran out of buffer when adding alias %d",
1400 result
->aliases_count
+ 1
1405 start
= result
->buffer
+ new_alias
;
1406 memcpy (start
, data
, len
);
1407 result
->alias_idx
= new_alias
;
1408 result
->header
->aliases
[result
->aliases_count
] = start
;
1409 result
->aliases_count
++;
1410 result
->header
->aliases
[result
->aliases_count
] = NULL
;
1417 contains_alias (result_map_t
* result
, const char * alias
)
1421 for (i
= 0; result
->header
->aliases
[i
]; i
++)
1423 if (strcmp (result
->header
->aliases
[i
], alias
) == 0)
1425 return result
->header
->aliases
[i
];
1434 Add fully qualified hostname to result.
1438 Result structure to write to
1440 Fully qualified hostname
1443 Pointer to start of hostname buffer,
1444 or NULL on error (usually hostname too long)
1448 add_hostname_len (result_map_t
* result
, const char * fullname
, int len
)
1450 if (len
>= k_hostname_maxlen
)
1452 set_err_bad_hostname (result
);
1453 syslog (LOG_WARNING
,
1454 "mdns: Hostname too long '%.*s': len %d, max %d",
1463 result
->hostent
->h_name
=
1464 strcpy (result
->header
->hostname
, fullname
);
1466 return result
->header
->hostname
;
1471 Add fully qualified name as hostname or alias.
1473 If hostname is not fully qualified this is not an error, but the data
1474 returned may be not what the application wanted.
1478 Result structure to write to
1480 Incoming alias (null terminated)
1482 Length of data buffer (in bytes), including trailing null
1485 Pointer to start of newly written data,
1487 If alias or hostname already exists, returns pointer to that instead.
1490 add_hostname_or_alias (result_map_t
* result
, const char * data
, int len
)
1492 char * hostname
= result
->hostent
->h_name
;
1496 if (strcmp (hostname
, data
) == 0)
1502 return add_alias_to_buffer (result
, data
, len
);
1507 return add_hostname_len (result
, data
, len
);
1514 result_map_t
* result
,
1515 hostent
* result_buf
,
1520 if (buflen
< sizeof (buf_header_t
))
1525 result
->hostent
= result_buf
;
1526 result
->header
= (buf_header_t
*) buf
;
1527 result
->header
->hostname
[0] = 0;
1528 result
->aliases_count
= 0;
1529 result
->header
->aliases
[0] = NULL
;
1530 result
->addrs_count
= 0;
1531 result
->header
->addrs
[0] = NULL
;
1532 result
->buffer
= buf
+ sizeof (buf_header_t
);
1533 result
->addr_idx
= 0;
1534 result
->alias_idx
= buflen
- sizeof (buf_header_t
);
1536 set_err_notfound (result
);
1538 // Point hostent to the right buffers
1539 result
->hostent
->h_name
= result
->header
->hostname
;
1540 result
->hostent
->h_aliases
= result
->header
->aliases
;
1541 result
->hostent
->h_addr_list
= result
->header
->addrs
;
1547 Set the status in the result.
1551 Result structure to update
1553 New nss_status value
1563 set_err (result_map_t
* result
, nss_status status
, int err
, int herr
)
1565 result
->status
= status
;
1566 result
->r_errno
= err
;
1567 result
->r_h_errno
= herr
;
1573 set_err_notfound (result_map_t
* result
)
1575 return set_err (result
, NSS_STATUS_NOTFOUND
, ENOENT
, HOST_NOT_FOUND
);
1579 set_err_bad_hostname (result_map_t
* result
)
1581 return set_err (result
, NSS_STATUS_TRYAGAIN
, ENOENT
, NO_RECOVERY
);
1585 set_err_buf_too_small (result_map_t
* result
)
1587 return set_err (result
, NSS_STATUS_TRYAGAIN
, ERANGE
, NETDB_INTERNAL
);
1591 set_err_internal_resource_full (result_map_t
* result
)
1593 return set_err (result
, NSS_STATUS_RETURN
, ERANGE
, NO_RECOVERY
);
1597 set_err_system (result_map_t
* result
)
1599 return set_err (result
, NSS_STATUS_UNAVAIL
, errno
, NETDB_INTERNAL
);
1603 set_err_mdns_failed (result_map_t
* result
)
1605 return set_err (result
, NSS_STATUS_TRYAGAIN
, EAGAIN
, TRY_AGAIN
);
1609 set_err_success (result_map_t
* result
)
1611 result
->status
= NSS_STATUS_SUCCESS
;
1612 return result
->status
;
1617 Test whether name is applicable for mdns to process, and if so copy into
1618 lookup_name buffer (if non-NULL).
1621 Pointer to name to lookup up, if applicable, or NULL otherwise.
1624 is_applicable_name (
1625 result_map_t
* result
,
1630 int match
= config_is_mdns_suffix (name
);
1635 strncpy (lookup_name
, name
, k_hostname_maxlen
+ 1);
1647 set_err_system (result
);
1654 Test whether address is applicable for mdns to process, and if so copy into
1655 addr_str buffer as an address suitable for ptr lookup.
1658 Pointer to name to lookup up, if applicable, or NULL otherwise.
1661 is_applicable_addr (
1662 result_map_t
* result
,
1670 if (! format_reverse_addr (af
, addr
, -1, addr_str
))
1674 "mdns: Failed to create reverse address"
1681 "mdns: Reverse address: %s",
1685 match
= config_is_mdns_suffix (addr_str
);
1694 set_err_system (result
);
1701 // Types and Constants
1703 const char * k_conf_file
= "/etc/nss_mdns.conf";
1704 #define CONF_LINE_SIZE 1024
1706 const char k_comment_char
= '#';
1708 const char * k_keyword_domain
= "domain";
1710 const char * k_default_domains
[] =
1713 "254.169.in-addr.arpa",
1717 // Always null terminated
1720 // Linked list of domains
1721 typedef struct domain_entry
1724 struct domain_entry
* next
;
1731 domain_entry_t
* domains
;
1734 const config_t k_empty_config
=
1740 // Context - tracks position in config file, used for error reporting
1743 const char * filename
;
1745 } config_file_context_t
;
1752 load_config (config_t
* conf
);
1755 process_config_line (
1758 config_file_context_t
* context
1762 get_next_word (char * input
, char **next
);
1765 default_config (config_t
* conf
);
1768 add_domain (config_t
* conf
, const char * domain
);
1771 contains_domain (const config_t
* conf
, const char * domain
);
1774 contains_domain_suffix (const config_t
* conf
, const char * addr
);
1780 static config_t
* g_config
= NULL
;
1781 // Configuration info
1783 pthread_mutex_t g_config_mutex
=
1784 #ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
1785 PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
;
1787 PTHREAD_MUTEX_INITIALIZER
;
1792 // Configuration functions
1796 Initialise the configuration from the config file.
1800 non-zero error code on failure
1808 Safe to test outside mutex.
1809 If non-zero, initialisation is complete and g_config can be
1810 safely used read-only. If zero, then we do proper mutex
1811 testing before initialisation.
1819 config_t
* temp_config
;
1822 presult
= pthread_mutex_lock (&g_config_mutex
);
1826 "mdns: Fatal mutex lock error in nss_mdns:init_config, %s:%d: %d: %s",
1827 __FILE__
, __LINE__
, presult
, strerror (presult
)
1832 // Test again now we have mutex, in case initialisation occurred while
1836 temp_config
= (config_t
*) malloc (sizeof (config_t
));
1839 // NOTE: This code will leak memory if initialisation fails
1840 // repeatedly. This should only happen in the case of a memory
1841 // error, so I'm not sure if it's a meaningful problem. - AW
1842 *temp_config
= k_empty_config
;
1843 errcode
= load_config (temp_config
);
1847 g_config
= temp_config
;
1853 "mdns: Can't allocate memory in nss_mdns:init_config, %s:%d",
1860 presult
= pthread_mutex_unlock (&g_config_mutex
);
1864 "mdns: Fatal mutex unlock error in nss_mdns:init_config, %s:%d: %d: %s",
1865 __FILE__
, __LINE__
, presult
, strerror (presult
)
1876 config_is_mdns_suffix (const char * name
)
1878 int errcode
= init_config ();
1881 return contains_domain_suffix (g_config
, name
);
1895 load_config (config_t
* conf
)
1898 char line
[CONF_LINE_SIZE
];
1899 config_file_context_t context
;
1901 context
.filename
= k_conf_file
;
1902 context
.linenum
= 0;
1905 cf
= fopen (context
.filename
, "r");
1909 "mdns: Couldn't open nss_mdns configuration file %s, using default.",
1912 return default_config (conf
);
1915 while (fgets (line
, CONF_LINE_SIZE
, cf
))
1919 errcode
= process_config_line (conf
, line
, &context
);
1922 // Critical error, give up
1932 Parse a line of the configuration file.
1933 For each keyword recognised, perform appropriate handling.
1934 If the keyword is not recognised, print a message to syslog
1938 0 success, or recoverable config file error
1939 non-zero serious system error, processing aborted
1942 process_config_line (
1945 config_file_context_t
* context
1951 word
= get_next_word (curr
, &curr
);
1952 if (! word
|| word
[0] == k_comment_char
)
1954 // Nothing interesting on this line
1958 if (strcmp (word
, k_keyword_domain
) == 0)
1960 word
= get_next_word (curr
, &curr
);
1963 int errcode
= add_domain (conf
, word
);
1966 // something badly wrong, bail
1970 if (get_next_word (curr
, NULL
))
1972 syslog (LOG_WARNING
,
1973 "%s, line %d: ignored extra text found after domain",
1981 syslog (LOG_WARNING
,
1982 "%s, line %d: no domain specified",
1990 syslog (LOG_WARNING
,
1991 "%s, line %d: unknown keyword %s - skipping",
2003 Get next word (whitespace separated) from input string.
2004 A null character is written into the first whitespace character following
2009 Input string. This string is modified by get_next_word.
2011 If non-NULL and the result is non-NULL, a pointer to the
2012 character following the end of the word (after the null)
2013 is written to 'next'.
2014 If no word is found, the original value is unchanged.
2015 If the word extended to the end of the string, 'next' points
2016 to the trailling NULL.
2017 It is safe to pass 'str' as 'input' and '&str' as 'next'.
2019 Pointer to the first non-whitespace character (and thus word) found.
2020 if no word is found, returns NULL.
2023 get_next_word (char * input
, char **next
)
2025 char * curr
= input
;
2028 while (isspace (*curr
))
2039 while (*curr
&& ! isspace (*curr
))
2064 default_config (config_t
* conf
)
2067 for (i
= 0; k_default_domains
[i
]; i
++)
2070 add_domain (conf
, k_default_domains
[i
]);
2073 // Something has gone (badly) wrong - let's bail
2083 add_domain (config_t
* conf
, const char * domain
)
2085 if (! contains_domain (conf
, domain
))
2087 domain_entry_t
* d
=
2088 (domain_entry_t
*) malloc (sizeof (domain_entry_t
));
2092 "mdns: Can't allocate memory in nss_mdns:init_config, %s:%d",
2098 d
->domain
= strdup (domain
);
2102 "mdns: Can't allocate memory in nss_mdns:init_config, %s:%d",
2108 d
->next
= conf
->domains
;
2117 contains_domain (const config_t
* conf
, const char * domain
)
2119 const domain_entry_t
* curr
= conf
->domains
;
2121 while (curr
!= NULL
)
2123 if (strcasecmp (curr
->domain
, domain
) == 0)
2136 contains_domain_suffix (const config_t
* conf
, const char * addr
)
2138 const domain_entry_t
* curr
= conf
->domains
;
2140 while (curr
!= NULL
)
2142 if (cmp_dns_suffix (addr
, curr
->domain
) > 0)
2154 // Types and Constants
2156 static const char * k_local_suffix
= "local";
2157 static const char k_dns_separator
= '.';
2159 static const int k_label_maxlen
= DNS_LABEL_MAXLEN
;
2160 // Label entries longer than this are actually pointers.
2166 const char * comment
;
2169 static const table_entry_t k_table_af
[] =
2171 { AF_UNSPEC
, NULL
, NULL
},
2172 { AF_LOCAL
, "LOCAL", NULL
},
2173 { AF_UNIX
, "UNIX", NULL
},
2174 { AF_INET
, "INET", NULL
},
2175 { AF_INET6
, "INET6", NULL
}
2177 static const int k_table_af_size
=
2178 sizeof (k_table_af
) / sizeof (* k_table_af
);
2180 static const char * k_table_ns_class
[] =
2185 static const int k_table_ns_class_size
=
2186 sizeof (k_table_ns_class
) / sizeof (* k_table_ns_class
);
2188 static const char * k_table_ns_type
[] =
2233 static const int k_table_ns_type_size
=
2234 sizeof (k_table_ns_type
) / sizeof (* k_table_ns_type
);
2241 simple_table_index (const char * table
[], int size
, const char * str
);
2244 table_index_name (const table_entry_t table
[], int size
, const char * str
);
2247 table_index_value (const table_entry_t table
[], int size
, int n
);
2258 count_dots (const char * name
)
2262 for (i
= 0; name
[i
]; i
++)
2264 if (name
[i
] == k_dns_separator
)
2273 islocal (const char * name
)
2275 return cmp_dns_suffix (name
, k_local_suffix
) > 0;
2280 rr_to_af (ns_type_t rrtype
)
2308 //return ns_t_invalid;
2315 str_to_af (const char * str
)
2318 table_index_name (k_table_af
, k_table_af_size
, str
);
2322 return k_table_af
[result
].value
;
2327 str_to_ns_class (const char * str
)
2330 simple_table_index (k_table_ns_class
, k_table_ns_class_size
, str
);
2335 str_to_ns_type (const char * str
)
2338 simple_table_index (k_table_ns_type
, k_table_ns_type_size
, str
);
2346 table_index_value (k_table_af
, k_table_af_size
, in
);
2350 return k_table_af
[result
].name
;
2355 ns_class_to_str (ns_class_t in
)
2357 if (in
< k_table_ns_class_size
)
2358 return k_table_ns_class
[in
];
2365 ns_type_to_str (ns_type_t in
)
2367 if (in
< k_table_ns_type_size
)
2368 return k_table_ns_type
[in
];
2375 format_reverse_addr_in (
2376 const struct in_addr
* addr
,
2384 const uint8_t * in_addr_a
= (uint8_t *) addr
;
2391 i
= (prefixlen
+ 7) / 8;
2392 // divide prefixlen into bytes, rounding up
2397 curr
+= sprintf (curr
, "%d.", in_addr_a
[i
]);
2399 sprintf (curr
, "in-addr.arpa");
2406 format_reverse_addr_in6 (
2407 const struct in6_addr
* addr
,
2415 const uint8_t * in_addr_a
= (uint8_t *) addr
;
2417 if (prefixlen
> 128)
2422 i
= (prefixlen
+ 3) / 4;
2423 // divide prefixlen into nibbles, rounding up
2425 // Special handling for first
2428 curr
+= sprintf (curr
, "%d.", (in_addr_a
[i
] >> 4) & 0x0F);
2431 // Convert i to bytes (divide by 2)
2438 val
= in_addr_a
[i
];
2439 curr
+= sprintf (curr
, "%x.%x.", val
& 0x0F, (val
>> 4) & 0x0F);
2441 sprintf (curr
, "ip6.arpa");
2448 format_reverse_addr (
2459 format_reverse_addr_in (
2460 (struct in_addr
*) addr
, prefixlen
, buf
2466 format_reverse_addr_in6 (
2467 (struct in6_addr
*) addr
, prefixlen
, buf
2478 cmp_dns_suffix (const char * name
, const char * domain
)
2480 const char * nametail
;
2481 const char * domaintail
;
2484 if (*name
== 0 || *name
== k_dns_separator
)
2486 // Name can't be empty or start with separator
2487 return CMP_DNS_SUFFIX_BAD_NAME
;
2492 return CMP_DNS_SUFFIX_SUCCESS
;
2496 if (*domain
== k_dns_separator
)
2498 // drop leading separator from domain
2500 if (*domain
== k_dns_separator
)
2502 return CMP_DNS_SUFFIX_BAD_DOMAIN
;
2506 // Find ends of strings
2507 for (nametail
= name
; *nametail
; nametail
++)
2509 for (domaintail
= domain
; *domaintail
; domaintail
++)
2512 // Shuffle back to last real character, and drop any trailing '.'
2513 // while we're at it.
2515 if (*nametail
== k_dns_separator
)
2518 if (*nametail
== k_dns_separator
)
2520 return CMP_DNS_SUFFIX_BAD_NAME
;
2524 if (*domaintail
== k_dns_separator
)
2527 if (*domaintail
== k_dns_separator
)
2529 return CMP_DNS_SUFFIX_BAD_DOMAIN
;
2536 && domaintail
>= domain
2537 && tolower(*nametail
) == tolower(*domaintail
))
2543 /* A successful finish will be one of the following:
2544 (leading and trailing . ignored)
2546 name : domain2.domain1
2547 domain: domain2.domain1
2550 name : domain3.domain2.domain1
2551 domain: domain2.domain1
2556 && (nametail
< name
|| *nametail
== k_dns_separator
)
2559 return CMP_DNS_SUFFIX_SUCCESS
;
2563 return CMP_DNS_SUFFIX_FAILURE
;
2569 dns_rdata_to_name (const char * rdata
, int rdlen
, char * name
, int name_len
)
2572 // Index into 'name'
2573 const char * rdata_curr
= rdata
;
2575 // drop any leading whitespace rubbish
2576 while (isspace (*rdata_curr
))
2579 if (rdata_curr
> rdata
+ rdlen
)
2581 return DNS_RDATA_TO_NAME_BAD_FORMAT
;
2586 In RDATA, a DNS name is stored as a series of labels.
2587 Each label consists of a length octet (max value 63)
2588 followed by the data for that label.
2589 The series is terminated with a length 0 octet.
2590 A length octet beginning with bits 11 is a pointer to
2591 somewhere else in the payload, but we don't support these
2592 since we don't have access to the entire payload.
2594 See RFC1034 section 3.1 and RFC1035 section 3.1.
2598 int term_len
= *rdata_curr
;
2604 // 0 length record terminates label
2606 else if (term_len
> k_label_maxlen
)
2609 return DNS_RDATA_TO_NAME_PTR
;
2611 else if (rdata_curr
+ term_len
> rdata
+ rdlen
)
2614 return DNS_RDATA_TO_NAME_BAD_FORMAT
;
2617 if (name_len
< i
+ term_len
+ 1)
2621 return DNS_RDATA_TO_NAME_TOO_LONG
;
2624 memcpy (name
+ i
, rdata_curr
, term_len
);
2627 rdata_curr
+= term_len
;
2629 name
[i
] = k_dns_separator
;
2642 Find the index of an string entry in a table. A case insenitive match
2643 is performed. If no entry is found, 0 is returned.
2648 Table entries may be NULL. NULL entries will never match.
2650 number of entries in table
2655 index of first matching entry, or 0 if no matches
2658 simple_table_index (const char * table
[], int size
, const char * str
)
2661 for (i
= 0; i
< size
; i
++)
2665 && (strcasecmp (table
[i
], str
) == 0)
2677 Find the index of a name in a table.
2681 array of table_entry_t records. The name field is compared
2682 (ignoring case) to the input string.
2684 number of entries in table
2689 index of first matching entry, or -1 if no matches
2692 table_index_name (const table_entry_t table
[], int size
, const char * str
)
2695 for (i
= 0; i
< size
; i
++)
2699 && (strcasecmp (table
[i
].name
, str
) == 0)
2711 Find the index of a value a table.
2715 array of table_entry_t records. The value field is compared to
2718 number of entries in table
2723 index of first matching entry, or -1 if no matches
2726 table_index_value (const table_entry_t table
[], int size
, int n
)
2729 for (i
= 0; i
< size
; i
++)
2731 if (table
[i
].value
== n
)