#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <pthread.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define BIND_8_COMPAT 1
#include <arpa/nameser.h>
#include <dns_sd.h>
int
count_dots (const char * name);
int
islocal (const char * name);
char *
format_reverse_addr (int af, const void * addr, int prefixlen, char * buf);
char *
format_reverse_addr_in (
const struct in_addr * addr,
int prefixlen,
char * buf
);
#define DNS_PTR_AF_INET_SIZE 29
char *
format_reverse_addr_in6 (
const struct in6_addr * addr,
int prefixlen,
char * buf
);
#define DNS_PTR_AF_INET6_SIZE 72
int
cmp_dns_suffix (const char * name, const char * domain);
enum
{
CMP_DNS_SUFFIX_SUCCESS = 1,
CMP_DNS_SUFFIX_FAILURE = 0,
CMP_DNS_SUFFIX_BAD_NAME = 1,
CMP_DNS_SUFFIX_BAD_DOMAIN = -2
};
typedef int ns_type_t;
typedef int ns_class_t;
int
rr_to_af (ns_type_t rrtype);
ns_type_t
af_to_rr (int af);
int
str_to_af (const char * str);
ns_class_t
str_to_ns_class (const char * str);
ns_type_t
str_to_ns_type (const char * str);
const char *
af_to_str (int in);
const char *
ns_class_to_str (ns_class_t in);
const char *
ns_type_to_str (ns_type_t in);
int
dns_rdata_to_name (const char * rdata, int rdlen, char * name, int name_len);
enum
{
DNS_RDATA_TO_NAME_BAD_FORMAT = -1,
DNS_RDATA_TO_NAME_TOO_LONG = -2,
DNS_RDATA_TO_NAME_PTR = -3,
};
#define DNS_LABEL_MAXLEN 63
#define DNS_NAME_MAXLEN 255
typedef int errcode_t;
int
config_is_mdns_suffix (const char * name);
errcode_t
init_config ();
#define ENTNAME hostent
#define DATABASE "hosts"
#include <nss.h>
#include <netdb.h>
#include <sys/types.h>
typedef enum nss_status nss_status;
typedef struct hostent hostent;
nss_status
_nss_mdns_gethostbyname_r (
const char *name,
hostent * result_buf,
char *buf,
size_t buflen,
int *errnop,
int *h_errnop
);
nss_status
_nss_mdns_gethostbyname2_r (
const char *name,
int af,
hostent * result_buf,
char *buf,
size_t buflen,
int *errnop,
int *h_errnop
);
nss_status
_nss_mdns_gethostbyaddr_r (
const void *addr,
socklen_t len,
int af,
hostent * result_buf,
char *buf,
size_t buflen,
int *errnop,
int *h_errnop
);
const int MDNS_VERBOSE = 0;
#define k_hostname_maxlen 255
#define k_aliases_max 15
#define k_addrs_max 15
const int k_mdnsd_intfs_local = 0;
typedef struct buf_header
{
char hostname [k_hostname_maxlen + 1];
char * aliases [k_aliases_max + 1];
char * addrs [k_addrs_max + 1];
} buf_header_t;
typedef struct result_map
{
int done;
nss_status status;
hostent * hostent;
buf_header_t * header;
int aliases_count;
int addrs_count;
char * buffer;
int addr_idx;
int alias_idx;
int r_errno;
int r_h_errno;
} result_map_t;
static const struct timeval
k_select_time = { 0, 500000 };
static nss_status
mdns_gethostbyname2 (
const char *name,
int af,
hostent * result_buf,
char *buf,
size_t buflen,
int *errnop,
int *h_errnop
);
static nss_status
mdns_lookup_name (
const char * fullname,
int af,
result_map_t * result
);
static nss_status
mdns_lookup_addr (
const void * addr,
socklen_t len,
int af,
const char * addr_str,
result_map_t * result
);
static nss_status
handle_events (DNSServiceRef sdref, result_map_t * result, const char * str);
typedef void
mdns_lookup_callback_t
(
DNSServiceRef sdref,
DNSServiceFlags flags,
uint32_t interface_index,
DNSServiceErrorType error_code,
const char *fullname,
uint16_t rrtype,
uint16_t rrclass,
uint16_t rdlen,
const void *rdata,
uint32_t ttl,
void *context
);
mdns_lookup_callback_t mdns_lookup_callback;
static int
init_result (
result_map_t * result,
hostent * result_buf,
char * buf,
size_t buflen
);
static int
callback_body_ptr (
const char * fullname,
result_map_t * result,
int rdlen,
const void * rdata
);
static void *
add_address_to_buffer (result_map_t * result, const void * data, int len);
static char *
add_alias_to_buffer (result_map_t * result, const char * data, int len);
static char *
add_hostname_len (result_map_t * result, const char * fullname, int len);
static char *
add_hostname_or_alias (result_map_t * result, const char * data, int len);
static void *
contains_address (result_map_t * result, const void * data, int len);
static char *
contains_alias (result_map_t * result, const char * data);
static const char *
is_applicable_name (
result_map_t * result,
const char * name,
char * lookup_name
);
static const char *
is_applicable_addr (
result_map_t * result,
const void * addr,
int af,
char * addr_str
);
static nss_status
set_err (result_map_t * result, nss_status status, int err, int herr);
static nss_status set_err_notfound (result_map_t * result);
static nss_status set_err_bad_hostname (result_map_t * result);
static nss_status set_err_buf_too_small (result_map_t * result);
static nss_status set_err_internal_resource_full (result_map_t * result);
static nss_status set_err_system (result_map_t * result);
static nss_status set_err_mdns_failed (result_map_t * result);
static nss_status set_err_success (result_map_t * result);
nss_status
_nss_mdns_gethostbyname_r (
const char *name,
hostent * result_buf,
char *buf,
size_t buflen,
int *errnop,
int *h_errnop
)
{
if (MDNS_VERBOSE)
syslog (LOG_DEBUG,
"mdns: Called nss_mdns_gethostbyname with %s",
name
);
return
mdns_gethostbyname2 (
name, AF_INET, result_buf, buf, buflen, errnop, h_errnop
);
}
nss_status
_nss_mdns_gethostbyname2_r (
const char *name,
int af,
hostent * result_buf,
char *buf,
size_t buflen,
int *errnop,
int *h_errnop
)
{
if (MDNS_VERBOSE)
syslog (LOG_DEBUG,
"mdns: Called nss_mdns_gethostbyname2 with %s",
name
);
return
mdns_gethostbyname2 (
name, af, result_buf, buf, buflen, errnop, h_errnop
);
}
nss_status
_nss_mdns_gethostbyaddr_r (
const void *addr,
socklen_t len,
int af,
hostent * result_buf,
char *buf,
size_t buflen,
int *errnop,
int *h_errnop
)
{
char addr_str [NI_MAXHOST + 1];
result_map_t result;
int err_status;
if (inet_ntop (af, addr, addr_str, NI_MAXHOST) == NULL)
{
const char * family = af_to_str (af);
if (family == NULL)
{
family = "Unknown";
}
syslog (LOG_WARNING,
"mdns: Couldn't covert address, family %d (%s) in nss_mdns_gethostbyaddr: %s",
af,
family,
strerror (errno)
);
*errnop = ENOENT;
*h_errnop = HOST_NOT_FOUND;
return NSS_STATUS_NOTFOUND;
}
if (MDNS_VERBOSE)
{
syslog (LOG_DEBUG,
"mdns: Called nss_mdns_gethostbyaddr with %s",
addr_str
);
}
err_status = init_result (&result, result_buf, buf, buflen);
if (err_status)
{
*errnop = err_status;
*h_errnop = NETDB_INTERNAL;
return NSS_STATUS_TRYAGAIN;
}
if (is_applicable_addr (&result, addr, af, addr_str))
{
nss_status rv;
rv = mdns_lookup_addr (addr, len, af, addr_str, &result);
if (rv == NSS_STATUS_SUCCESS)
{
return rv;
}
}
*errnop = result.r_errno;
*h_errnop = result.r_h_errno;
return result.status;
}
nss_status
mdns_gethostbyname2 (
const char *name,
int af,
hostent * result_buf,
char *buf,
size_t buflen,
int *errnop,
int *h_errnop
)
{
char lookup_name [k_hostname_maxlen + 1];
result_map_t result;
int err_status;
err_status = init_result (&result, result_buf, buf, buflen);
if (err_status)
{
*errnop = err_status;
*h_errnop = NETDB_INTERNAL;
return NSS_STATUS_TRYAGAIN;
}
if (is_applicable_name (&result, name, lookup_name))
{
nss_status rv;
if (MDNS_VERBOSE)
syslog (LOG_DEBUG,
"mdns: Local name: %s",
name
);
rv = mdns_lookup_name (name, af, &result);
if (rv == NSS_STATUS_SUCCESS)
{
return rv;
}
}
*errnop = result.r_errno;
*h_errnop = result.r_h_errno;
return result.status;
}
static nss_status
mdns_lookup_name (
const char * fullname,
int af,
result_map_t * result
)
{
DNSServiceErrorType errcode;
DNSServiceRef sdref;
ns_type_t rrtype;
if (MDNS_VERBOSE)
syslog (LOG_DEBUG,
"mdns: Attempting lookup of %s",
fullname
);
switch (af)
{
case AF_INET:
rrtype = T_A;
result->hostent->h_length = 4;
break;
case AF_INET6:
rrtype = T_AAAA;
result->hostent->h_length = 16;
break;
default:
syslog (LOG_WARNING,
"mdns: Unsupported address family %d",
af
);
return set_err_bad_hostname (result);
}
result->hostent->h_addrtype = af;
errcode =
DNSServiceQueryRecord (
&sdref,
0, k_mdnsd_intfs_local, fullname, rrtype, C_IN, mdns_lookup_callback, result );
if (errcode)
{
syslog (LOG_WARNING,
"mdns: Failed to initialise lookup, error %d",
errcode
);
return set_err_mdns_failed (result);
}
return handle_events (sdref, result, fullname);
}
static nss_status
mdns_lookup_addr (
const void * addr,
socklen_t addr_len,
int af,
const char * addr_str,
result_map_t * result
)
{
DNSServiceErrorType errcode;
DNSServiceRef sdref;
if (MDNS_VERBOSE)
syslog (LOG_DEBUG,
"mdns: Attempting lookup of %s",
addr_str
);
result->hostent->h_addrtype = af;
result->hostent->h_length = addr_len;
if (! add_address_to_buffer (result, addr, addr_len))
{
return result->status;
}
result->hostent->h_name [0] = 0;
errcode =
DNSServiceQueryRecord (
&sdref,
0, k_mdnsd_intfs_local, addr_str, T_PTR, C_IN, mdns_lookup_callback, result );
if (errcode)
{
syslog (LOG_WARNING,
"mdns: Failed to initialise mdns lookup, error %d",
errcode
);
return set_err_mdns_failed (result);
}
return handle_events (sdref, result, addr_str);
}
static nss_status
handle_events (DNSServiceRef sdref, result_map_t * result, const char * str)
{
int dns_sd_fd = DNSServiceRefSockFD(sdref);
int nfds = dns_sd_fd + 1;
fd_set readfds;
struct timeval tv;
int select_result;
while (! result->done)
{
FD_ZERO(&readfds);
FD_SET(dns_sd_fd, &readfds);
tv = k_select_time;
select_result =
select (nfds, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv);
if (select_result > 0)
{
if (FD_ISSET(dns_sd_fd, &readfds))
{
if (MDNS_VERBOSE)
syslog (LOG_DEBUG,
"mdns: Reply received for %s",
str
);
DNSServiceProcessResult(sdref);
}
else
{
syslog (LOG_WARNING,
"mdns: Unexpected return from select on lookup of %s",
str
);
}
}
else
{
if (MDNS_VERBOSE)
syslog (LOG_DEBUG,
"mdns: %s not found - timer expired",
str
);
set_err_notfound (result);
break;
}
}
return result->status;
}
void
mdns_lookup_callback
(
DNSServiceRef sdref,
DNSServiceFlags flags,
uint32_t interface_index,
DNSServiceErrorType error_code,
const char *fullname,
uint16_t rrtype,
uint16_t rrclass,
uint16_t rdlen,
const void *rdata,
uint32_t ttl,
void *context
)
{
result_map_t * result = (result_map_t *) context;
(void)sdref; (void)interface_index; (void)ttl;
if (! (flags & kDNSServiceFlagsMoreComing) )
{
result->done = 1;
}
if (error_code == kDNSServiceErr_NoError)
{
ns_type_t expected_rr_type =
af_to_rr (result->hostent->h_addrtype);
if (rrclass != C_IN)
{
syslog (LOG_WARNING,
"mdns: Received bad RR class: expected %d (%s),"
" got %d (%s), RR type %d (%s)",
C_IN,
ns_class_to_str (C_IN),
rrclass,
ns_class_to_str (rrclass),
rrtype,
ns_type_to_str (rrtype)
);
return;
}
if (rrtype == T_PTR)
{
if (callback_body_ptr (fullname, result, rdlen, rdata) < 0)
return;
}
else if (rrtype == expected_rr_type)
{
if (!
add_hostname_or_alias (
result,
fullname,
strlen (fullname)
)
)
{
result->done = 1;
return;
}
if (! add_address_to_buffer (result, rdata, rdlen) )
{
result->done = 1;
return;
}
}
else
{
syslog (LOG_WARNING,
"mdns: Received bad RR type: expected %d (%s),"
" got %d (%s)",
expected_rr_type,
ns_type_to_str (expected_rr_type),
rrtype,
ns_type_to_str (rrtype)
);
return;
}
if (result->status != NSS_STATUS_SUCCESS)
set_err_success (result);
}
else
{
syslog (LOG_WARNING,
"mdns: callback returned error %d",
error_code
);
}
}
static int
callback_body_ptr (
const char * fullname,
result_map_t * result,
int rdlen,
const void * rdata
)
{
char result_name [k_hostname_maxlen + 1];
int rv;
rv = dns_rdata_to_name (rdata, rdlen, result_name, k_hostname_maxlen);
if (rv < 0)
{
const char * errmsg;
switch (rv)
{
case DNS_RDATA_TO_NAME_BAD_FORMAT:
errmsg = "mdns: PTR '%s' result badly formatted ('%s...')";
break;
case DNS_RDATA_TO_NAME_TOO_LONG:
errmsg = "mdns: PTR '%s' result too long ('%s...')";
break;
case DNS_RDATA_TO_NAME_PTR:
errmsg = "mdns: PTR '%s' result contained pointer ('%s...')";
break;
default:
errmsg = "mdns: PTR '%s' result conversion failed ('%s...')";
}
syslog (LOG_WARNING,
errmsg,
fullname,
result_name
);
return -1;
}
if (MDNS_VERBOSE)
{
syslog (LOG_DEBUG,
"mdns: PTR '%s' resolved to '%s'",
fullname,
result_name
);
}
if (!
add_hostname_or_alias (
result,
result_name,
rv
)
)
{
result->done = 1;
return -1;
}
return 0;
}
static void *
add_address_to_buffer (result_map_t * result, const void * data, int len)
{
int new_addr;
void * start;
void * temp;
if ((temp = contains_address (result, data, len)))
{
return temp;
}
if (result->addrs_count >= k_addrs_max)
{
set_err_internal_resource_full (result);
syslog (LOG_ERR,
"mdns: Internal address buffer full; increase size"
);
return NULL;
}
if (len != result->hostent->h_length)
{
syslog (LOG_WARNING,
"mdns: Unexpected rdata length for address. Expected %d, got %d",
result->hostent->h_length,
len
);
}
new_addr = result->addr_idx + len;
if (new_addr > result->alias_idx)
{
set_err_buf_too_small (result);
if (MDNS_VERBOSE)
syslog (LOG_DEBUG,
"mdns: Ran out of buffer when adding address %d",
result->addrs_count + 1
);
return NULL;
}
start = result->buffer + result->addr_idx;
memcpy (start, data, len);
result->addr_idx = new_addr;
result->header->addrs [result->addrs_count] = start;
result->addrs_count ++;
result->header->addrs [result->addrs_count] = NULL;
return start;
}
static void *
contains_address (result_map_t * result, const void * data, int len)
{
int i;
if (len != result->hostent->h_length)
{
syslog (LOG_WARNING,
"mdns: Unexpected rdata length for address. Expected %d, got %d",
result->hostent->h_length,
len
);
}
for (i = 0; result->header->addrs [i]; i++)
{
if (memcmp (result->header->addrs [i], data, len) == 0)
{
return result->header->addrs [i];
}
}
return NULL;
}
static char *
add_alias_to_buffer (result_map_t * result, const char * data, int len)
{
int new_alias;
char * start;
char * temp;
if ((temp = contains_alias (result, data)))
{
return temp;
}
if (result->aliases_count >= k_aliases_max)
{
set_err_internal_resource_full (result);
syslog (LOG_ERR,
"mdns: Internal alias buffer full; increase size"
);
return NULL;
}
new_alias = result->alias_idx - len;
if (new_alias < result->addr_idx)
{
set_err_buf_too_small (result);
if (MDNS_VERBOSE)
syslog (LOG_DEBUG,
"mdns: Ran out of buffer when adding alias %d",
result->aliases_count + 1
);
return NULL;
}
start = result->buffer + new_alias;
memcpy (start, data, len);
result->alias_idx = new_alias;
result->header->aliases [result->aliases_count] = start;
result->aliases_count ++;
result->header->aliases [result->aliases_count] = NULL;
return start;
}
static char *
contains_alias (result_map_t * result, const char * alias)
{
int i;
for (i = 0; result->header->aliases [i]; i++)
{
if (strcmp (result->header->aliases [i], alias) == 0)
{
return result->header->aliases [i];
}
}
return NULL;
}
static char *
add_hostname_len (result_map_t * result, const char * fullname, int len)
{
if (len >= k_hostname_maxlen)
{
set_err_bad_hostname (result);
syslog (LOG_WARNING,
"mdns: Hostname too long '%.*s': len %d, max %d",
len,
fullname,
len,
k_hostname_maxlen
);
return NULL;
}
result->hostent->h_name =
strcpy (result->header->hostname, fullname);
return result->header->hostname;
}
static char *
add_hostname_or_alias (result_map_t * result, const char * data, int len)
{
char * hostname = result->hostent->h_name;
if (*hostname)
{
if (strcmp (hostname, data) == 0)
{
return hostname;
}
else
{
return add_alias_to_buffer (result, data, len);
}
}
else
{
return add_hostname_len (result, data, len);
}
}
static int
init_result (
result_map_t * result,
hostent * result_buf,
char * buf,
size_t buflen
)
{
if (buflen < sizeof (buf_header_t))
{
return ERANGE;
}
result->hostent = result_buf;
result->header = (buf_header_t *) buf;
result->header->hostname[0] = 0;
result->aliases_count = 0;
result->header->aliases[0] = NULL;
result->addrs_count = 0;
result->header->addrs[0] = NULL;
result->buffer = buf + sizeof (buf_header_t);
result->addr_idx = 0;
result->alias_idx = buflen - sizeof (buf_header_t);
result->done = 0;
set_err_notfound (result);
result->hostent->h_name = result->header->hostname;
result->hostent->h_aliases = result->header->aliases;
result->hostent->h_addr_list = result->header->addrs;
return 0;
}
static nss_status
set_err (result_map_t * result, nss_status status, int err, int herr)
{
result->status = status;
result->r_errno = err;
result->r_h_errno = herr;
return status;
}
static nss_status
set_err_notfound (result_map_t * result)
{
return set_err (result, NSS_STATUS_NOTFOUND, ENOENT, HOST_NOT_FOUND);
}
static nss_status
set_err_bad_hostname (result_map_t * result)
{
return set_err (result, NSS_STATUS_TRYAGAIN, ENOENT, NO_RECOVERY);
}
static nss_status
set_err_buf_too_small (result_map_t * result)
{
return set_err (result, NSS_STATUS_TRYAGAIN, ERANGE, NETDB_INTERNAL);
}
static nss_status
set_err_internal_resource_full (result_map_t * result)
{
return set_err (result, NSS_STATUS_RETURN, ERANGE, NO_RECOVERY);
}
static nss_status
set_err_system (result_map_t * result)
{
return set_err (result, NSS_STATUS_UNAVAIL, errno, NETDB_INTERNAL);
}
static nss_status
set_err_mdns_failed (result_map_t * result)
{
return set_err (result, NSS_STATUS_TRYAGAIN, EAGAIN, TRY_AGAIN);
}
static nss_status
set_err_success (result_map_t * result)
{
result->status = NSS_STATUS_SUCCESS;
return result->status;
}
static const char *
is_applicable_name (
result_map_t * result,
const char * name,
char * lookup_name
)
{
int match = config_is_mdns_suffix (name);
if (match > 0)
{
if (lookup_name)
{
strncpy (lookup_name, name, k_hostname_maxlen + 1);
return lookup_name;
}
else
{
return name;
}
}
else
{
if (match < 0)
{
set_err_system (result);
}
return NULL;
}
}
static const char *
is_applicable_addr (
result_map_t * result,
const void * addr,
int af,
char * addr_str
)
{
int match;
if (! format_reverse_addr (af, addr, -1, addr_str))
{
if (MDNS_VERBOSE)
syslog (LOG_DEBUG,
"mdns: Failed to create reverse address"
);
return NULL;
}
if (MDNS_VERBOSE)
syslog (LOG_DEBUG,
"mdns: Reverse address: %s",
addr_str
);
match = config_is_mdns_suffix (addr_str);
if (match > 0)
{
return addr_str;
}
else
{
if (match < 0)
{
set_err_system (result);
}
return NULL;
}
}
const char * k_conf_file = "/etc/nss_mdns.conf";
#define CONF_LINE_SIZE 1024
const char k_comment_char = '#';
const char * k_keyword_domain = "domain";
const char * k_default_domains [] =
{
"local",
"254.169.in-addr.arpa",
"0.8.e.f.ip6.int",
"0.8.e.f.ip6.arpa",
NULL
};
typedef struct domain_entry
{
char * domain;
struct domain_entry * next;
} domain_entry_t;
typedef struct
{
domain_entry_t * domains;
} config_t;
const config_t k_empty_config =
{
NULL
};
typedef struct
{
const char * filename;
int linenum;
} config_file_context_t;
static errcode_t
load_config (config_t * conf);
static errcode_t
process_config_line (
config_t * conf,
char * line,
config_file_context_t * context
);
static char *
get_next_word (char * input, char **next);
static errcode_t
default_config (config_t * conf);
static errcode_t
add_domain (config_t * conf, const char * domain);
static int
contains_domain (const config_t * conf, const char * domain);
static int
contains_domain_suffix (const config_t * conf, const char * addr);
static config_t * g_config = NULL;
pthread_mutex_t g_config_mutex =
#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
#else
PTHREAD_MUTEX_INITIALIZER;
#endif
errcode_t
init_config ()
{
if (g_config)
{
return 0;
}
else
{
int errcode = -1;
int presult;
config_t * temp_config;
presult = pthread_mutex_lock (&g_config_mutex);
if (presult)
{
syslog (LOG_ERR,
"mdns: Fatal mutex lock error in nss_mdns:init_config, %s:%d: %d: %s",
__FILE__, __LINE__, presult, strerror (presult)
);
return presult;
}
if (! g_config)
{
temp_config = (config_t *) malloc (sizeof (config_t));
if (temp_config)
{
*temp_config = k_empty_config;
errcode = load_config (temp_config);
if (! errcode)
{
g_config = temp_config;
}
}
else
{
syslog (LOG_ERR,
"mdns: Can't allocate memory in nss_mdns:init_config, %s:%d",
__FILE__, __LINE__
);
errcode = errno;
}
}
presult = pthread_mutex_unlock (&g_config_mutex);
if (presult)
{
syslog (LOG_ERR,
"mdns: Fatal mutex unlock error in nss_mdns:init_config, %s:%d: %d: %s",
__FILE__, __LINE__, presult, strerror (presult)
);
errcode = presult;
}
return errcode;
}
}
int
config_is_mdns_suffix (const char * name)
{
int errcode = init_config ();
if (! errcode)
{
return contains_domain_suffix (g_config, name);
}
else
{
errno = errcode;
return -1;
}
}
static errcode_t
load_config (config_t * conf)
{
FILE * cf;
char line [CONF_LINE_SIZE];
config_file_context_t context;
context.filename = k_conf_file;
context.linenum = 0;
cf = fopen (context.filename, "r");
if (! cf)
{
syslog (LOG_INFO,
"mdns: Couldn't open nss_mdns configuration file %s, using default.",
context.filename
);
return default_config (conf);
}
while (fgets (line, CONF_LINE_SIZE, cf))
{
int errcode;
context.linenum++;
errcode = process_config_line (conf, line, &context);
if (errcode)
{
return errcode;
}
}
return 0;
}
static errcode_t
process_config_line (
config_t * conf,
char * line,
config_file_context_t * context
)
{
char * curr = line;
char * word;
word = get_next_word (curr, &curr);
if (! word || word [0] == k_comment_char)
{
return 0;
}
if (strcmp (word, k_keyword_domain) == 0)
{
word = get_next_word (curr, &curr);
if (word)
{
int errcode = add_domain (conf, word);
if (errcode)
{
return errcode;
}
if (get_next_word (curr, NULL))
{
syslog (LOG_WARNING,
"%s, line %d: ignored extra text found after domain",
context->filename,
context->linenum
);
}
}
else
{
syslog (LOG_WARNING,
"%s, line %d: no domain specified",
context->filename,
context->linenum
);
}
}
else
{
syslog (LOG_WARNING,
"%s, line %d: unknown keyword %s - skipping",
context->filename,
context->linenum,
word
);
}
return 0;
}
static char *
get_next_word (char * input, char **next)
{
char * curr = input;
char * result;
while (isspace (*curr))
{
curr ++;
}
if (*curr == 0)
{
return NULL;
}
result = curr;
while (*curr && ! isspace (*curr))
{
curr++;
}
if (*curr)
{
*curr = 0;
if (next)
{
*next = curr+1;
}
}
else
{
if (next)
{
*next = curr;
}
}
return result;
}
static errcode_t
default_config (config_t * conf)
{
int i;
for (i = 0; k_default_domains [i]; i++)
{
int errcode =
add_domain (conf, k_default_domains [i]);
if (errcode)
{
return errcode;
}
}
return 0;
}
static errcode_t
add_domain (config_t * conf, const char * domain)
{
if (! contains_domain (conf, domain))
{
domain_entry_t * d =
(domain_entry_t *) malloc (sizeof (domain_entry_t));
if (! d)
{
syslog (LOG_ERR,
"mdns: Can't allocate memory in nss_mdns:init_config, %s:%d",
__FILE__, __LINE__
);
return ENOMEM;
}
d->domain = strdup (domain);
if (! d->domain)
{
syslog (LOG_ERR,
"mdns: Can't allocate memory in nss_mdns:init_config, %s:%d",
__FILE__, __LINE__
);
free (d);
return ENOMEM;
}
d->next = conf->domains;
conf->domains = d;
}
return 0;
}
static int
contains_domain (const config_t * conf, const char * domain)
{
const domain_entry_t * curr = conf->domains;
while (curr != NULL)
{
if (strcasecmp (curr->domain, domain) == 0)
{
return 1;
}
curr = curr->next;
}
return 0;
}
static int
contains_domain_suffix (const config_t * conf, const char * addr)
{
const domain_entry_t * curr = conf->domains;
while (curr != NULL)
{
if (cmp_dns_suffix (addr, curr->domain) > 0)
{
return 1;
}
curr = curr->next;
}
return 0;
}
static const char * k_local_suffix = "local";
static const char k_dns_separator = '.';
static const int k_label_maxlen = DNS_LABEL_MAXLEN;
typedef struct
{
int value;
const char * name;
const char * comment;
} table_entry_t;
static const table_entry_t k_table_af [] =
{
{ AF_UNSPEC, NULL, NULL },
{ AF_LOCAL, "LOCAL", NULL },
{ AF_UNIX, "UNIX", NULL },
{ AF_INET, "INET", NULL },
{ AF_INET6, "INET6", NULL }
};
static const int k_table_af_size =
sizeof (k_table_af) / sizeof (* k_table_af);
static const char * k_table_ns_class [] =
{
NULL,
"IN"
};
static const int k_table_ns_class_size =
sizeof (k_table_ns_class) / sizeof (* k_table_ns_class);
static const char * k_table_ns_type [] =
{
NULL,
"A",
"NS",
"MD",
"MF",
"CNAME",
"SOA",
"MB",
"MG",
"MR",
"NULL",
"WKS",
"PTR",
"HINFO",
"MINFO",
"MX",
"TXT",
"RP",
"AFSDB",
"X25",
"ISDN",
"RT",
"NSAP",
NULL,
"SIG",
"KEY",
"PX",
"GPOS",
"AAAA",
"LOC",
"NXT",
"EID",
"NIMLOC",
"SRV",
"ATMA",
"NAPTR",
"KX",
"CERT",
"A6",
"DNAME",
"SINK",
"OPT"
};
static const int k_table_ns_type_size =
sizeof (k_table_ns_type) / sizeof (* k_table_ns_type);
static int
simple_table_index (const char * table [], int size, const char * str);
static int
table_index_name (const table_entry_t table [], int size, const char * str);
static int
table_index_value (const table_entry_t table [], int size, int n);
int
count_dots (const char * name)
{
int count = 0;
int i;
for (i = 0; name[i]; i++)
{
if (name [i] == k_dns_separator)
count++;
}
return count;
}
int
islocal (const char * name)
{
return cmp_dns_suffix (name, k_local_suffix) > 0;
}
int
rr_to_af (ns_type_t rrtype)
{
switch (rrtype)
{
case T_A:
return AF_INET;
case T_AAAA:
return AF_INET6;
default:
return AF_UNSPEC;
}
}
ns_type_t
af_to_rr (int af)
{
switch (af)
{
case AF_INET:
return T_A;
case AF_INET6:
return T_AAAA;
default:
return 0;
}
}
int
str_to_af (const char * str)
{
int result =
table_index_name (k_table_af, k_table_af_size, str);
if (result < 0)
result = 0;
return k_table_af [result].value;
}
ns_class_t
str_to_ns_class (const char * str)
{
return (ns_class_t)
simple_table_index (k_table_ns_class, k_table_ns_class_size, str);
}
ns_type_t
str_to_ns_type (const char * str)
{
return (ns_type_t)
simple_table_index (k_table_ns_type, k_table_ns_type_size, str);
}
const char *
af_to_str (int in)
{
int result =
table_index_value (k_table_af, k_table_af_size, in);
if (result < 0)
result = 0;
return k_table_af [result].name;
}
const char *
ns_class_to_str (ns_class_t in)
{
if (in < k_table_ns_class_size)
return k_table_ns_class [in];
else
return NULL;
}
const char *
ns_type_to_str (ns_type_t in)
{
if (in < k_table_ns_type_size)
return k_table_ns_type [in];
else
return NULL;
}
char *
format_reverse_addr_in (
const struct in_addr * addr,
int prefixlen,
char * buf
)
{
char * curr = buf;
int i;
const uint8_t * in_addr_a = (uint8_t *) addr;
if (prefixlen > 32)
return NULL;
if (prefixlen < 0)
prefixlen = 32;
i = (prefixlen + 7) / 8;
while (i > 0)
{
i--;
curr += sprintf (curr, "%d.", in_addr_a [i]);
}
sprintf (curr, "in-addr.arpa");
return buf;
}
char *
format_reverse_addr_in6 (
const struct in6_addr * addr,
int prefixlen,
char * buf
)
{
char * curr = buf;
int i;
const uint8_t * in_addr_a = (uint8_t *) addr;
if (prefixlen > 128)
return NULL;
if (prefixlen < 0)
prefixlen = 128;
i = (prefixlen + 3) / 4;
if (i / 2)
{
curr += sprintf (curr, "%d.", (in_addr_a [i] >> 4) & 0x0F);
}
i >>= 1;
while (i > 0)
{
uint8_t val;
i--;
val = in_addr_a [i];
curr += sprintf (curr, "%x.%x.", val & 0x0F, (val >> 4) & 0x0F);
}
sprintf (curr, "ip6.arpa");
return buf;
}
char *
format_reverse_addr (
int af,
const void * addr,
int prefixlen,
char * buf
)
{
switch (af)
{
case AF_INET:
return
format_reverse_addr_in (
(struct in_addr *) addr, prefixlen, buf
);
break;
case AF_INET6:
return
format_reverse_addr_in6 (
(struct in6_addr *) addr, prefixlen, buf
);
break;
default:
return NULL;
}
}
int
cmp_dns_suffix (const char * name, const char * domain)
{
const char * nametail;
const char * domaintail;
if (*name == 0 || *name == k_dns_separator)
{
return CMP_DNS_SUFFIX_BAD_NAME;
}
if (*domain == 0)
{
return CMP_DNS_SUFFIX_SUCCESS;
}
if (*domain == k_dns_separator)
{
domain++;
if (*domain == k_dns_separator)
{
return CMP_DNS_SUFFIX_BAD_DOMAIN;
}
}
for (nametail = name; *nametail; nametail++)
;
for (domaintail = domain; *domaintail; domaintail++)
;
nametail --;
if (*nametail == k_dns_separator)
{
nametail --;
if (*nametail == k_dns_separator)
{
return CMP_DNS_SUFFIX_BAD_NAME;
}
}
domaintail --;
if (*domaintail == k_dns_separator)
{
domaintail --;
if (*domaintail == k_dns_separator)
{
return CMP_DNS_SUFFIX_BAD_DOMAIN;
}
}
while (
nametail >= name
&& domaintail >= domain
&& tolower(*nametail) == tolower(*domaintail))
{
nametail--;
domaintail--;
}
if (
domaintail < domain
&& (nametail < name || *nametail == k_dns_separator)
)
{
return CMP_DNS_SUFFIX_SUCCESS;
}
else
{
return CMP_DNS_SUFFIX_FAILURE;
}
}
int
dns_rdata_to_name (const char * rdata, int rdlen, char * name, int name_len)
{
int i = 0;
const char * rdata_curr = rdata;
while (isspace (*rdata_curr))
{
rdata_curr ++;
if (rdata_curr > rdata + rdlen)
{
return DNS_RDATA_TO_NAME_BAD_FORMAT;
}
}
while (1)
{
int term_len = *rdata_curr;
rdata_curr++;
if (term_len == 0)
{
break;
}
else if (term_len > k_label_maxlen)
{
name [i] = 0;
return DNS_RDATA_TO_NAME_PTR;
}
else if (rdata_curr + term_len > rdata + rdlen)
{
name [i] = 0;
return DNS_RDATA_TO_NAME_BAD_FORMAT;
}
if (name_len < i + term_len + 1)
{
name [i] = 0;
return DNS_RDATA_TO_NAME_TOO_LONG;
}
memcpy (name + i, rdata_curr, term_len);
i += term_len;
rdata_curr += term_len;
name [i] = k_dns_separator;
i++;
}
name [i] = 0;
return i;
}
static int
simple_table_index (const char * table [], int size, const char * str)
{
int i;
for (i = 0; i < size; i++)
{
if (
table [i]
&& (strcasecmp (table [i], str) == 0)
)
{
return i;
}
}
return 0;
}
static int
table_index_name (const table_entry_t table [], int size, const char * str)
{
int i;
for (i = 0; i < size; i++)
{
if (
table [i].name
&& (strcasecmp (table [i].name, str) == 0)
)
{
return i;
}
}
return -1;
}
static int
table_index_value (const table_entry_t table [], int size, int n)
{
int i;
for (i = 0; i < size; i++)
{
if (table [i].value == n)
{
return i;
}
}
return -1;
}