1/* 2 * Copyright (c) 2007-2011 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include <string.h> 25#include <stdio.h> 26#include <unistd.h> 27#include <syslog.h> 28 29#include <sys/types.h> 30#include <sys/socket.h> 31#include <ifaddrs.h> 32#include <netdb.h> 33 34#include <CoreFoundation/CoreFoundation.h> 35#include <SystemConfiguration/SystemConfiguration.h> 36 37#include "autofs.h" 38#include "automount.h" 39 40static int hosts_match(const char *host, size_t hostlen, const char *thishost); 41static int get_local_host_name(char *localhost, size_t localhost_len); 42static int convert_to_write_lock(void); 43static void get_my_host_names(void); 44static void free_hostinfo_list(void); 45 46/* 47 * XXX - is self_check() sufficient for this? It'll handle the "localhost" 48 * case (as 127.0.0.1 will be one of our addresses), and it should 49 * handle our primary host name, with or without qualifications. We 50 * need to call it anyway, to handle multi-homing and .local hostnames, 51 * and if we can just use self_check(), that avoids a gethostname() system 52 * call and some compares. 53 * 54 * One problem with self_check() is that it can be expensive if you're 55 * using it on a lot of names, as it looks up the host name and compares 56 * all the IP addresses for that host name with all of the IP addresses 57 * for the machine. If that's done for all the entries in the -fstab 58 * map, as would be the case for an "ls -l" done on /Network/Servers or 59 * if the Finder's looking at everything in /Network/Servers - as it would 60 * in column view when you're looking at anything under /Network/Servers - 61 * then, the first time that happens, it does a host name lookup for every 62 * server listed there, meaning it could do a DNS lookup for every such 63 * host. 64 * 65 * We use host_is_us() as a "fast path" check; instead of trying to look 66 * up the host name, we do a quick comparison against the result of 67 * gethostname() (the primary DNS host name) and against the result 68 * of SCDynamicStoreCopyLocalHostName() (the local host name) and 69 * "localhost", and then check whether it matches the result of a 70 * reverse lookup of any of our IP addresses. 71 * 72 * When we have a loopback file system, so we can use that for entries 73 * in -fstab that refer to us, rather than making those entries a symlink 74 * to /, we should be able to avoid this hack. 75 */ 76 77static u_int num_hostinfo; 78static struct hostent **hostinfo_list; 79static u_int num_host_names; 80static char **my_host_names; 81static int have_my_host_names; 82 83/* 84 * Read/write lock on the host name information. 85 */ 86static pthread_rwlock_t host_name_cache_lock = PTHREAD_RWLOCK_INITIALIZER; 87 88int 89host_is_us(const char *host, size_t hostlen) 90{ 91 int err; 92 static const char localhost[] = "localhost"; 93 static char ourhostname[MAXHOSTNAMELEN]; 94 static char ourlocalhostname[MAXHOSTNAMELEN]; 95 size_t ourlocalhostnamelen; 96 u_int i; 97 98 /* 99 * This is, by definition, us. 100 */ 101 if (hostlen == sizeof localhost - 1 && 102 strncasecmp(host, localhost, sizeof localhost - 1) == 0) 103 return (1); 104 105 /* 106 * Get our hostname, and compare the counted string we were 107 * handed with the host name - and the first component of 108 * the host name, if it has more than one component. 109 * 110 * For now, we call gethostname() every time, as that 111 * should be reasonably cheap and it avoids us having 112 * to catch notifications for the host name changing. 113 */ 114 if (gethostname(ourhostname, sizeof ourhostname) == 0) { 115 if (hosts_match(host, hostlen, ourhostname)) 116 return (1); 117 } 118 119 /* 120 * Try to get the local host name. If that works, compare the 121 * counted string we were handed with the host name. 122 * 123 * For now, we call get_local_host_name() every time, as 124 * that should be reasonably cheap (although, if it contacts 125 * configd, it's probably not as cheap as gethostname()) 126 * and it avoids us having to catch notifications for the 127 * local host name changing. 128 */ 129 if (get_local_host_name(ourlocalhostname, sizeof ourlocalhostname) 130 == 0) { 131 ourlocalhostnamelen = strlen(ourlocalhostname); 132 if (hostlen == ourlocalhostnamelen && 133 strncasecmp(host, ourlocalhostname, ourlocalhostnamelen) 134 == 0) 135 return (1); 136 } 137 138 /* 139 * Now we check against all the host names for this host. 140 * We cache those, as that's potentially a bit more 141 * expensive; we do flush that cache if we get a 142 * cache flush notification from automount, as it gets 143 * run with the "-c" flag whenever there's a network 144 * change, so that should be sufficient to catch changes 145 * in our IP addresses. 146 */ 147 148 /* 149 * Get a read lock, so the cache doesn't get modified out 150 * from under us. 151 */ 152 err = pthread_rwlock_rdlock(&host_name_cache_lock); 153 if (err != 0) { 154 pr_msg("Can't get read lock on host name cache: %s", 155 strerror(err)); 156 return (0); 157 } 158 159 /* 160 * OK, now get the names for all the IP addresses for this host. 161 */ 162 if (!have_my_host_names) { 163 /* 164 * We have to get the local host name; convert the read lock 165 * to a write lock, if we haven't done so already. 166 */ 167 if (!convert_to_write_lock()) { 168 /* convert_to_write_lock() released the read lock */ 169 return (0); 170 } 171 get_my_host_names(); 172 } 173 174 /* 175 * Check against all of those names. 176 */ 177 if (have_my_host_names) { 178 for (i = 0; i < num_host_names; i++) { 179 if (hosts_match(host, hostlen, my_host_names[i])) { 180 pthread_rwlock_unlock(&host_name_cache_lock); 181 return (1); 182 } 183 } 184 } 185 186 /* Not us. */ 187 pthread_rwlock_unlock(&host_name_cache_lock); 188 return (0); 189} 190 191static int 192hosts_match(const char *host, size_t hostlen, const char *matchhost) 193{ 194 size_t matchhost_len; 195 const char *p; 196 197 matchhost_len = strlen(matchhost); 198 if (hostlen == matchhost_len && 199 strncasecmp(host, matchhost, matchhost_len) == 0) 200 return (1); 201 202 /* 203 * Compare the counted string we were handed with the first 204 * component of the host name, if it has more than one component. 205 */ 206 p = strchr(matchhost, '.'); 207 if (p != NULL) { 208 matchhost_len = p - matchhost; 209 if (hostlen == matchhost_len && 210 strncasecmp(host, matchhost, matchhost_len) == 0) 211 return(1); 212 } 213 214 return (0); 215} 216 217static int 218get_local_host_name(char *ourlocalhostname, size_t ourlocalhostnamelen) 219{ 220 SCDynamicStoreRef store; 221 CFStringRef ourlocalhostname_CFString; 222 Boolean ret; 223 224 store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("automountd"), 225 NULL, NULL); 226 if (store == NULL) 227 return (-1); 228 229 ourlocalhostname_CFString = SCDynamicStoreCopyLocalHostName(store); 230 CFRelease(store); 231 if (ourlocalhostname_CFString == NULL) 232 return (-1); 233 234 ret = CFStringGetCString(ourlocalhostname_CFString, ourlocalhostname, 235 ourlocalhostnamelen, kCFStringEncodingUTF8); 236 CFRelease(ourlocalhostname_CFString); 237 if (!ret) 238 return (-1); 239 240 /* 241 * That won't have ".local" at the end; add it. 242 */ 243 if (strlcat(ourlocalhostname, ".local", ourlocalhostnamelen) 244 >= ourlocalhostnamelen) 245 return (-1); /* didn't fit in the buffer */ 246 return (0); 247} 248 249static int 250convert_to_write_lock(void) 251{ 252 int err; 253 254 pthread_rwlock_unlock(&host_name_cache_lock); 255 err = pthread_rwlock_wrlock(&host_name_cache_lock); 256 if (err != 0) { 257 pr_msg("Error attempting to get write lock on host name cache: %s", 258 strerror(err)); 259 return (0); 260 } 261 return (1); 262} 263 264static void 265get_my_host_names(void) 266{ 267 struct ifaddrs *ifaddrs, *ifaddr; 268 struct sockaddr *addr; 269 struct sockaddr_in *addr_in; 270#if 0 271 struct sockaddr_in6 *addr_in6; 272#endif 273 int error_num; 274 struct hostent **hostinfop; 275 struct hostent *hostinfo; 276 u_int i; 277 char **host_namep; 278 char **aliasp; 279 280 /* 281 * If we already have the list of host names, presumably 282 * that was fetched by another thread in between releasing 283 * the read lock on the list and getting the write lock; 284 * just return. (have_my_host_names is modified only 285 * when the write lock is held.) 286 */ 287 if (have_my_host_names) 288 return; 289 290 if (getifaddrs(&ifaddrs) == -1) { 291 pr_msg("getifaddrs failed: %s\n", strerror(errno)); 292 return; 293 } 294 295 /* 296 * What's the maximum number of hostinfo structures we'd have? 297 * (This counts all IPv4 and IPv6 addresses; we might not be 298 * able to get information for some of them, so we won't 299 * necessarily store hostinfo pointers for all of them.) 300 */ 301 num_hostinfo = 0; 302 for (ifaddr = ifaddrs; ifaddr != NULL; ifaddr = ifaddr->ifa_next) { 303 addr = ifaddr->ifa_addr; 304 switch (addr->sa_family) { 305 306 case AF_INET: 307 case AF_INET6: 308 num_hostinfo++; 309 break; 310 311 default: 312 break; 313 } 314 } 315 316 /* 317 * Allocate the array of hostinfo structures. 318 */ 319 hostinfo_list = malloc(num_hostinfo * sizeof *hostinfo_list); 320 if (hostinfo_list == NULL) { 321 freeifaddrs(ifaddrs); 322 pr_msg("Couldn't allocate array of hostinfo pointers\n"); 323 return; 324 } 325 326 /* 327 * Fill in the array of hostinfo pointers, and count how many 328 * hostinfo pointers and host names we have. 329 */ 330 hostinfop = hostinfo_list; 331 num_hostinfo = 0; 332 num_host_names = 0; 333 for (ifaddr = ifaddrs; ifaddr != NULL; ifaddr = ifaddr->ifa_next) { 334 addr = ifaddr->ifa_addr; 335 switch (addr->sa_family) { 336 337 case AF_INET: 338 addr_in = (struct sockaddr_in *)addr; 339 hostinfo = getipnodebyaddr(&addr_in->sin_addr, 340 sizeof addr_in->sin_addr, addr->sa_family, 341 &error_num); 342 break; 343 344#if 0 // until IPv6 reverse-DNS lookups are fixed - 8650817 345 case AF_INET6: 346 addr_in6 = (struct sockaddr_in6 *)addr; 347 hostinfo = getipnodebyaddr(&addr_in6->sin6_addr, 348 sizeof addr_in6->sin6_addr, addr->sa_family, 349 &error_num); 350 break; 351#endif 352 353 default: 354 hostinfo = NULL; 355 break; 356 } 357 if (hostinfo != NULL) { 358 *hostinfop++ = hostinfo; 359 num_hostinfo++; 360 num_host_names++; /* main name */ 361 for (aliasp = hostinfo->h_aliases; *aliasp != NULL; 362 aliasp++) 363 num_host_names++; /* alias */ 364 } 365 } 366 freeifaddrs(ifaddrs); 367 368 /* 369 * Allocate the array of host name pointers. 370 */ 371 my_host_names = malloc(num_host_names * sizeof *my_host_names); 372 if (my_host_names == NULL) { 373 free_hostinfo_list(); 374 pr_msg("Couldn't allocate array of host name pointers\n"); 375 return; 376 } 377 378 /* 379 * Fill in the array of host name pointers. 380 */ 381 host_namep = my_host_names; 382 for (i = 0; i < num_hostinfo; i++) { 383 hostinfo = hostinfo_list[i]; 384 *host_namep++ = hostinfo->h_name; 385 for (aliasp = hostinfo->h_aliases; *aliasp != NULL; aliasp++) 386 *host_namep++ = *aliasp; 387 } 388 389 have_my_host_names = 1; 390} 391 392static void 393free_hostinfo_list(void) 394{ 395 u_int i; 396 397 for (i = 0; i < num_hostinfo; i++) 398 freehostent(hostinfo_list[i]); 399 free(hostinfo_list); 400 hostinfo_list = NULL; 401 num_hostinfo = 0; 402} 403 404void 405flush_host_name_cache(void) 406{ 407 int err; 408 409 err = pthread_rwlock_wrlock(&host_name_cache_lock); 410 if (err != 0) { 411 pr_msg("Error attempting to get write lock on host name cache: %s", 412 strerror(err)); 413 return; 414 } 415 416 /* 417 * Discard the host information, if we have any. 418 */ 419 if (have_my_host_names) { 420 free_hostinfo_list(); 421 free(my_host_names); 422 my_host_names = NULL; 423 num_host_names = 0; 424 have_my_host_names = 0; 425 } 426 427 pthread_rwlock_unlock(&host_name_cache_lock); 428} 429