#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <ifaddrs.h>
#include <netdb.h>
#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include "autofs.h"
#include "automount.h"
static int hosts_match(const char *host, size_t hostlen, const char *thishost);
static int get_local_host_name(char *localhost, size_t localhost_len);
static int convert_to_write_lock(void);
static void get_my_host_names(void);
static void free_hostinfo_list(void);
static u_int num_hostinfo;
static struct hostent **hostinfo_list;
static u_int num_host_names;
static char **my_host_names;
static int have_my_host_names;
static pthread_rwlock_t host_name_cache_lock = PTHREAD_RWLOCK_INITIALIZER;
int
host_is_us(const char *host, size_t hostlen)
{
int err;
static const char localhost[] = "localhost";
static char ourhostname[MAXHOSTNAMELEN];
static char ourlocalhostname[MAXHOSTNAMELEN];
size_t ourlocalhostnamelen;
u_int i;
if (hostlen == sizeof localhost - 1 &&
strncasecmp(host, localhost, sizeof localhost - 1) == 0)
return (1);
if (gethostname(ourhostname, sizeof ourhostname) == 0) {
if (hosts_match(host, hostlen, ourhostname))
return (1);
}
if (get_local_host_name(ourlocalhostname, sizeof ourlocalhostname)
== 0) {
ourlocalhostnamelen = strlen(ourlocalhostname);
if (hostlen == ourlocalhostnamelen &&
strncasecmp(host, ourlocalhostname, ourlocalhostnamelen)
== 0)
return (1);
}
err = pthread_rwlock_rdlock(&host_name_cache_lock);
if (err != 0) {
pr_msg("Can't get read lock on host name cache: %s",
strerror(err));
return (0);
}
if (!have_my_host_names) {
if (!convert_to_write_lock()) {
return (0);
}
get_my_host_names();
}
if (have_my_host_names) {
for (i = 0; i < num_host_names; i++) {
if (hosts_match(host, hostlen, my_host_names[i])) {
pthread_rwlock_unlock(&host_name_cache_lock);
return (1);
}
}
}
pthread_rwlock_unlock(&host_name_cache_lock);
return (0);
}
static int
hosts_match(const char *host, size_t hostlen, const char *matchhost)
{
size_t matchhost_len;
const char *p;
matchhost_len = strlen(matchhost);
if (hostlen == matchhost_len &&
strncasecmp(host, matchhost, matchhost_len) == 0)
return (1);
p = strchr(matchhost, '.');
if (p != NULL) {
matchhost_len = p - matchhost;
if (hostlen == matchhost_len &&
strncasecmp(host, matchhost, matchhost_len) == 0)
return(1);
}
return (0);
}
static int
get_local_host_name(char *ourlocalhostname, size_t ourlocalhostnamelen)
{
SCDynamicStoreRef store;
CFStringRef ourlocalhostname_CFString;
Boolean ret;
store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("automountd"),
NULL, NULL);
if (store == NULL)
return (-1);
ourlocalhostname_CFString = SCDynamicStoreCopyLocalHostName(store);
CFRelease(store);
if (ourlocalhostname_CFString == NULL)
return (-1);
ret = CFStringGetCString(ourlocalhostname_CFString, ourlocalhostname,
ourlocalhostnamelen, kCFStringEncodingUTF8);
CFRelease(ourlocalhostname_CFString);
if (!ret)
return (-1);
if (strlcat(ourlocalhostname, ".local", ourlocalhostnamelen)
>= ourlocalhostnamelen)
return (-1);
return (0);
}
static int
convert_to_write_lock(void)
{
int err;
pthread_rwlock_unlock(&host_name_cache_lock);
err = pthread_rwlock_wrlock(&host_name_cache_lock);
if (err != 0) {
pr_msg("Error attempting to get write lock on host name cache: %s",
strerror(err));
return (0);
}
return (1);
}
static void
get_my_host_names(void)
{
struct ifaddrs *ifaddrs, *ifaddr;
struct sockaddr *addr;
struct sockaddr_in *addr_in;
struct sockaddr_in6 *addr_in6;
int error_num;
struct hostent **hostinfop;
struct hostent *hostinfo;
u_int i;
char **host_namep;
char **aliasp;
if (have_my_host_names)
return;
if (getifaddrs(&ifaddrs) == -1) {
pr_msg("getifaddrs failed: %s\n", strerror(errno));
return;
}
num_hostinfo = 0;
for (ifaddr = ifaddrs; ifaddr != NULL; ifaddr = ifaddr->ifa_next) {
addr = ifaddr->ifa_addr;
switch (addr->sa_family) {
case AF_INET:
case AF_INET6:
num_hostinfo++;
break;
default:
break;
}
}
hostinfo_list = malloc(num_hostinfo * sizeof *hostinfo_list);
if (hostinfo_list == NULL) {
freeifaddrs(ifaddrs);
pr_msg("Couldn't allocate array of hostinfo pointers\n");
return;
}
hostinfop = hostinfo_list;
num_hostinfo = 0;
num_host_names = 0;
for (ifaddr = ifaddrs; ifaddr != NULL; ifaddr = ifaddr->ifa_next) {
addr = ifaddr->ifa_addr;
switch (addr->sa_family) {
case AF_INET:
addr_in = (struct sockaddr_in *)addr;
hostinfo = getipnodebyaddr(&addr_in->sin_addr,
sizeof addr_in->sin_addr, addr->sa_family,
&error_num);
break;
case AF_INET6:
addr_in6 = (struct sockaddr_in6 *)addr;
hostinfo = getipnodebyaddr(&addr_in6->sin6_addr,
sizeof addr_in6->sin6_addr, addr->sa_family,
&error_num);
break;
default:
hostinfo = NULL;
break;
}
if (hostinfo != NULL) {
*hostinfop++ = hostinfo;
num_hostinfo++;
num_host_names++;
for (aliasp = hostinfo->h_aliases; *aliasp != NULL;
aliasp++)
num_host_names++;
}
}
freeifaddrs(ifaddrs);
my_host_names = malloc(num_host_names * sizeof *my_host_names);
if (my_host_names == NULL) {
free_hostinfo_list();
pr_msg("Couldn't allocate array of host name pointers\n");
return;
}
host_namep = my_host_names;
for (i = 0; i < num_hostinfo; i++) {
hostinfo = hostinfo_list[i];
*host_namep++ = hostinfo->h_name;
for (aliasp = hostinfo->h_aliases; *aliasp != NULL; aliasp++)
*host_namep++ = *aliasp;
}
have_my_host_names = 1;
}
static void
free_hostinfo_list(void)
{
u_int i;
for (i = 0; i < num_hostinfo; i++)
freehostent(hostinfo_list[i]);
free(hostinfo_list);
hostinfo_list = NULL;
num_hostinfo = 0;
}
void
flush_host_name_cache(void)
{
int err;
err = pthread_rwlock_wrlock(&host_name_cache_lock);
if (err != 0) {
pr_msg("Error attempting to get write lock on host name cache: %s",
strerror(err));
return;
}
if (have_my_host_names) {
free_hostinfo_list();
free(my_host_names);
my_host_names = NULL;
num_host_names = 0;
have_my_host_names = 0;
}
pthread_rwlock_unlock(&host_name_cache_lock);
}