X-Git-Url: https://git.saurik.com/apple/configd.git/blobdiff_plain/4c5e92e2493bdfbbce40e998f3b607c72c47af2c..dbf6a266c384fc8b55e00a396eebe5cb62e21547:/Plugins/IPMonitor/set-hostname.c?ds=sidebyside diff --git a/Plugins/IPMonitor/set-hostname.c b/Plugins/IPMonitor/set-hostname.c new file mode 100644 index 0000000..b5408f9 --- /dev/null +++ b/Plugins/IPMonitor/set-hostname.c @@ -0,0 +1,932 @@ +/* + * Copyright (c) 2004-2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include // for SCLog(), SCPrint() + +#include + + +static SCDynamicStoreRef store = NULL; +static CFRunLoopSourceRef rls = NULL; + +static CFMachPortRef dnsPort = NULL; +static CFRunLoopSourceRef dnsRLS = NULL; +static struct timeval dnsQueryStart; + +static Boolean _verbose = FALSE; + + +/* SPI (from SCNetworkReachability.c) */ +Boolean +_SC_checkResolverReachability(SCDynamicStoreRef *storeP, + SCNetworkConnectionFlags *flags, + Boolean *haveDNS, + const char * nodename); + + +/* + * checkResolverReachabilityByAddress() + * + * Given an IP address, determine whether a reverse DNS query can be issued + * using the current network configuration. + */ +static Boolean +checkResolverReachabilityByAddress(SCDynamicStoreRef store, struct sockaddr *sa) +{ + SCNetworkConnectionFlags flags; + Boolean haveDNS; + int i; + Boolean ok = FALSE; + char ptr_name[128]; + + /* + * Ideally, we would have an API that given a local IP + * address would return the DNS server(s) that would field + * a given PTR query. Fortunately, we do have an SPI which + * which will provide this information given a "name" so we + * take the address, convert it into the inverse query name, + * and find out which servers should be consulted. + */ + + switch (sa->sa_family) { + case AF_INET : { + union { + in_addr_t s_addr; + unsigned char b[4]; + } rev; + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + + /* + * build "PTR" query name + * NNN.NNN.NNN.NNN.in-addr.arpa. + */ + rev.s_addr = sin->sin_addr.s_addr; + (void) snprintf(ptr_name, sizeof(ptr_name), "%u.%u.%u.%u.in-addr.arpa.", + rev.b[3], + rev.b[2], + rev.b[1], + rev.b[0]); + + break; + } + + case AF_INET6 : { + int s = 0; + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; + int x = sizeof(ptr_name); + int n; + +#define USE_NIBBLE_QUERY +#ifdef USE_NIBBLE_QUERY + /* + * build IPv6 "nibble" PTR query name (RFC 1886, RFC 3152) + * N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.ip6.arpa. + */ + for (i = sizeof(sin6->sin6_addr) - 1; i >= 0; i--) { + n = snprintf(&ptr_name[s], x, "%x.%x.", + ( sin6->sin6_addr.s6_addr[i] & 0xf), + ((sin6->sin6_addr.s6_addr[i] >> 4) & 0xf)); + if ((n == -1) || (n >= x)) { + goto done; + } + + s += n; + x -= n; + } + + n = snprintf(&ptr_name[s], x, "ip6.arpa."); + if ((n == -1) || (n >= x)) { + goto done; + } +#else /* USE_NIBBLE_QUERY */ + /* + * build IPv6 "bit-string" PTR query name (RFC 2673) + * \[xNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN].ip6.arpa. + */ + n = snprintf(&ptr_name[0], x, "\\[x"); + if ((n == -1) || (n >= x)) { + goto done; + } + + s += n; + x -= n; + for (i = 0; i < 16; i++) { + n = snprintf(&ptr_name[s], x, "%2.2x", sin6->sin6_addr.s6_addr[i]); + if ((n == -1) || (n >= x)) { + goto done; + } + + s += n; + x -= n; + } + + n = snprintf(&ptr_name[s], x, "].ip6.arpa."); + if ((n == -1) || (n >= x)) { + goto done; + } +#endif /* USE_NIBBLE_QUERY */ + + break; + } + + default : + goto done; + } + + ok = _SC_checkResolverReachability(&store, &flags, &haveDNS, ptr_name); + if (ok) { + if (!(flags & kSCNetworkFlagsReachable) || + (flags & kSCNetworkFlagsConnectionRequired)) { + // if not reachable *OR* connection required + ok = FALSE; + } + } + + done : + + return ok; +} + + +#define HOSTNAME_NOTIFY_KEY "com.apple.system.hostname" + + +static void +set_hostname(CFStringRef hostname) +{ + if (hostname != NULL) { + char old_name[MAXHOSTNAMELEN]; + char new_name[MAXHOSTNAMELEN]; + + if (gethostname(old_name, sizeof(old_name)) == -1) { + SCLog(TRUE, LOG_ERR, CFSTR("gethostname() failed: %s"), strerror(errno)); + old_name[0] = '\0'; + } + + if (_SC_cfstring_to_cstring(hostname, + new_name, + sizeof(new_name), + kCFStringEncodingUTF8) == NULL) { + SCLog(TRUE, LOG_ERR, CFSTR("could not convert [new] hostname")); + new_name[0] = '\0'; + } + + old_name[sizeof(old_name)-1] = '\0'; + new_name[sizeof(new_name)-1] = '\0'; + if (strcmp(old_name, new_name) != 0) { + if (sethostname(new_name, strlen(new_name)) == 0) { + uint32_t status; + + SCLog(TRUE, LOG_NOTICE, + CFSTR("setting hostname to \"%s\""), + new_name); + + status = notify_post(HOSTNAME_NOTIFY_KEY); + if (status != NOTIFY_STATUS_OK) { + SCLog(TRUE, LOG_ERR, + CFSTR("notify_post(" HOSTNAME_NOTIFY_KEY ") failed: error=%lu"), + status); + } + } else { + SCLog(TRUE, LOG_ERR, + CFSTR("sethostname(%s, %d) failed: %s"), + new_name, + strlen(new_name), + strerror(errno)); + } + } + } + + return; +} + + +#define HOSTCONFIG "/etc/hostconfig" +#define HOSTNAME_KEY "HOSTNAME=" +#define AUTOMATIC "-AUTOMATIC-" + +#define HOSTNAME_KEY_LEN (sizeof(HOSTNAME_KEY) - 1) + +static CFStringRef +copy_static_name() +{ + FILE * f; + char buf[256]; + CFStringRef name = NULL; + + f = fopen(HOSTCONFIG, "r"); + if (f == NULL) { + return NULL; + } + + while (fgets(buf, sizeof(buf), f) != NULL) { + char * bp; + int n; + char * np; + Boolean str_escape; + Boolean str_quote; + + n = strlen(buf); + if (buf[n-1] == '\n') { + /* the entire line fit in the buffer, remove the newline */ + buf[n-1] = '\0'; + } else { + /* eat the remainder of the line */ + do { + n = fgetc(f); + } while ((n != '\n') && (n != EOF)); + } + + // skip leading white space + bp = &buf[0]; + while (isspace(*bp)) { + bp++; + } + + // find "HOSTNAME=" key + if (strncmp(bp, HOSTNAME_KEY, HOSTNAME_KEY_LEN) != 0) { + continue; // if not + } + + // get the hostname string + bp += HOSTNAME_KEY_LEN; + str_escape = FALSE; + str_quote = FALSE; + + np = &buf[0]; + while (*bp != '\0') { + char ch = *bp; + + switch (ch) { + case '\\' : + if (!str_escape) { + str_escape = TRUE; + bp++; + continue; + } + break; + case '"' : + if (!str_escape) { + str_quote = !str_quote; + bp++; + continue; + } + break; + default : + break; + } + + if (str_escape) { + str_escape = FALSE; + } else if (!str_quote && (isspace(ch) || (ch == '#'))) { + break; + } + + *np++ = ch; + bp++; + } + + *np = '\0'; + + if (name != NULL) { + CFRelease(name); + name = NULL; + } + + if (str_quote) { + // the shell won't parse this file so neither will we + break; + } + + if (strcmp(buf, AUTOMATIC) == 0) { + // skip "-AUTOMATIC-" + continue; + } + + name = CFStringCreateWithCString(NULL, buf, kCFStringEncodingUTF8); + } + + (void) fclose(f); + return name; +} + + +#ifndef kSCPropNetHostName +#define kSCPropNetHostName CFSTR("HostName") +#endif + + +static CFStringRef +copy_prefs_hostname(SCDynamicStoreRef store) +{ + CFDictionaryRef dict; + CFStringRef key; + CFStringRef name = NULL; + + key = SCDynamicStoreKeyCreateComputerName(NULL); + dict = SCDynamicStoreCopyValue(store, key); + CFRelease(key); + if (dict == NULL) { + goto done; + } + if (!isA_CFDictionary(dict)) { + goto done; + } + + name = isA_CFString(CFDictionaryGetValue(dict, kSCPropNetHostName)); + if (name == NULL) { + goto done; + } + CFRetain(name); + + done : + + if (dict != NULL) CFRelease(dict); + + return name; +} + + +static CFStringRef +copy_primary_service(SCDynamicStoreRef store) +{ + CFDictionaryRef dict; + CFStringRef key; + CFStringRef serviceID = NULL; + + key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, + kSCDynamicStoreDomainState, + kSCEntNetIPv4); + dict = SCDynamicStoreCopyValue(store, key); + CFRelease(key); + + if (dict != NULL) { + if (isA_CFDictionary(dict)) { + serviceID = CFDictionaryGetValue(dict, kSCDynamicStorePropNetPrimaryService); + if (isA_CFString(serviceID)) { + CFRetain(serviceID); + } else { + serviceID = NULL; + } + } + CFRelease(dict); + } + + return serviceID; +} + + +static CFStringRef +copy_primary_ip(SCDynamicStoreRef store, CFStringRef serviceID) +{ + CFDictionaryRef dict; + CFStringRef key; + CFStringRef address = NULL; + + key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, + kSCDynamicStoreDomainState, + serviceID, + kSCEntNetIPv4); + dict = SCDynamicStoreCopyValue(store, key); + CFRelease(key); + + if (dict != NULL) { + if (isA_CFDictionary(dict)) { + CFArrayRef addresses; + + addresses = CFDictionaryGetValue(dict, kSCPropNetIPv4Addresses); + if (isA_CFArray(addresses) && (CFArrayGetCount(addresses) > 0)) { + address = CFArrayGetValueAtIndex(addresses, 0); + if (isA_CFString(address)) { + CFRetain(address); + } else { + address = NULL; + } + } + } + CFRelease(dict); + } + + return address; +} + + +#define DHCP_OPTION_HOSTNAME 12 + +static CFStringRef +copy_dhcp_name(SCDynamicStoreRef store, CFStringRef serviceID) +{ + CFDictionaryRef info; + CFStringRef name = NULL; + + info = SCDynamicStoreCopyDHCPInfo(store, serviceID); + if (info != NULL) { + CFDataRef data; + + data = DHCPInfoGetOptionData(info, DHCP_OPTION_HOSTNAME); + if (data != NULL) { + name = CFStringCreateFromExternalRepresentation(NULL, data, kCFStringEncodingUTF8); + } + + CFRelease(info); + } + + return name; +} + + +static void +reverseDNSComplete(int32_t status, char *host, char *serv, void *context) +{ + struct timeval dnsQueryComplete; + struct timeval dnsQueryElapsed; + CFStringRef hostname; + SCDynamicStoreRef store = (SCDynamicStoreRef)context; + + (void) gettimeofday(&dnsQueryComplete, NULL); + timersub(&dnsQueryComplete, &dnsQueryStart, &dnsQueryElapsed); + SCLog(_verbose, LOG_INFO, + CFSTR("async DNS complete%s (query time = %d.%3.3d)"), + ((status == 0) && (host != NULL)) ? "" : ", host not found", + dnsQueryElapsed.tv_sec, + dnsQueryElapsed.tv_usec / 1000); + + // use reverse DNS name, if available + + switch (status) { + case 0 : + /* + * if [reverse] DNS query was successful + */ + if (host != NULL) { + hostname = CFStringCreateWithCString(NULL, host, kCFStringEncodingUTF8); + SCLog(TRUE, LOG_INFO, CFSTR("hostname (reverse DNS query) = %@"), hostname); + set_hostname(hostname); + CFRelease(hostname); + goto done; + } + break; + + case EAI_NONAME : + /* + * if no name available + */ + break; + + default : + /* + * Hmmmm... + */ + SCLog(TRUE, LOG_ERR, CFSTR("getnameinfo() failed: %s"), gai_strerror(status)); + } + + // get local (multicast DNS) name, if available + + hostname = SCDynamicStoreCopyLocalHostName(store); + if (hostname != NULL) { + CFMutableStringRef localName; + + SCLog(TRUE, LOG_INFO, CFSTR("hostname (multicast DNS) = %@"), hostname); + localName = CFStringCreateMutableCopy(NULL, 0, hostname); + CFStringAppend(localName, CFSTR(".local")); + set_hostname(localName); + CFRelease(localName); + CFRelease(hostname); + goto done; + } + + // use "localhost" if not other name is available + + set_hostname(CFSTR("localhost")); + + done : + + if (host != NULL) free(host); + if (serv != NULL) free(serv); + return; +} + + +static void +getnameinfo_async_handleCFReply(CFMachPortRef port, void *msg, CFIndex size, void *info) +{ + getnameinfo_async_handle_reply(msg); + + if (port == dnsPort) { + CFRelease(dnsRLS); + dnsRLS = NULL; + CFRelease(dnsPort); + dnsPort = NULL; + } + + return; +} + + +static void +start_dns_query(SCDynamicStoreRef store, CFStringRef address) +{ + char addr[64]; + Boolean ok; + struct sockaddr *sa; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + + if (_SC_cfstring_to_cstring(address, addr, sizeof(addr), kCFStringEncodingASCII) == NULL) { + SCLog(TRUE, LOG_ERR, CFSTR("could not convert [primary] address")); + return; + } + + bzero(&sin, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + + bzero(&sin6, sizeof(sin6)); + sin6.sin6_len = sizeof(sin6); + sin6.sin6_family = AF_INET6; + + if (inet_aton(addr, &sin.sin_addr) == 1) { + /* + * if IPv4 address + */ + sa = (struct sockaddr *)&sin; + } else if (inet_pton(AF_INET6, addr, &sin6.sin6_addr) == 1) { + /* + * if IPv6 address + */ + char *p; + + p = strchr(addr, '%'); + if (p != NULL) { + sin6.sin6_scope_id = if_nametoindex(p+1); + } + + sa = (struct sockaddr *)&sin6; + } else { + goto done; + } + + ok = checkResolverReachabilityByAddress(store, sa); + if (ok) { + CFMachPortContext context = { 0, (void *)store, CFRetain, CFRelease, CFCopyDescription }; + mach_port_t port; + int32_t error; + + if ((dnsPort != NULL) && (dnsRLS != NULL)) { + /* if we already have an active async DNS query */ + lu_async_call_cancel(CFMachPortGetPort(dnsPort)); + CFRelease(dnsRLS); + dnsRLS = NULL; + CFRelease(dnsPort); + dnsPort = NULL; + } + + (void) gettimeofday(&dnsQueryStart, NULL); + + error = getnameinfo_async_start(&port, + sa, + sa->sa_len, + 0, // flags + reverseDNSComplete, + NULL); + if (error != 0) { + goto done; + } + + dnsPort = CFMachPortCreateWithPort(NULL, + port, + getnameinfo_async_handleCFReply, + &context, + NULL); + dnsRLS = CFMachPortCreateRunLoopSource(NULL, dnsPort, 0); + CFRunLoopAddSource(CFRunLoopGetCurrent(), dnsRLS, kCFRunLoopDefaultMode); + } + + done : + + return; +} + + +static void +update_hostname(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info) +{ + CFStringRef address = NULL; + CFStringRef hostname = NULL; + CFStringRef serviceID = NULL; + + // get static hostname, if available + + hostname = copy_static_name(); + if (hostname != NULL) { + SCLog(TRUE, LOG_INFO, CFSTR("hostname (static) = %@"), hostname); + set_hostname(hostname); + goto done; + } + + // get [prefs] hostname, if available + + hostname = copy_prefs_hostname(store); + if (hostname != NULL) { + SCLog(TRUE, LOG_INFO, CFSTR("hostname (prefs) = %@"), hostname); + set_hostname(hostname); + goto done; + } + + // get primary service ID + + serviceID = copy_primary_service(store); + if (serviceID == NULL) { + goto mDNS; + } + + // get DHCP provided name, if available + + hostname = copy_dhcp_name(store, serviceID); + if (hostname != NULL) { + SCLog(TRUE, LOG_INFO, CFSTR("hostname (DHCP) = %@"), hostname); + set_hostname(hostname); + goto done; + } + + // get DNS name associated with primary IP, if available + + address = copy_primary_ip(store, serviceID); + if (address != NULL) { + // start reverse DNS query using primary IP address + (void) start_dns_query(store, address); + goto done; + } + + mDNS : + + // get local (multicast DNS) name, if available + + hostname = SCDynamicStoreCopyLocalHostName(store); + if (hostname != NULL) { + CFMutableStringRef localName; + + SCLog(TRUE, LOG_INFO, CFSTR("hostname (multicast DNS) = %@"), hostname); + localName = CFStringCreateMutableCopy(NULL, 0, hostname); + CFStringAppend(localName, CFSTR(".local")); + set_hostname(localName); + CFRelease(localName); + goto done; + } + + // use "localhost" if not other name is available + + set_hostname(CFSTR("localhost")); + + done : + + if (address) CFRelease(address); + if (hostname) CFRelease(hostname); + if (serviceID) CFRelease(serviceID); + + return; +} + + +__private_extern__ +void +load_hostname(Boolean verbose) +{ + CFStringRef key; + CFMutableArrayRef keys = NULL; + CFMutableArrayRef patterns = NULL; + + if (verbose) { + _verbose = TRUE; + } + + /* initialize a few globals */ + + store = SCDynamicStoreCreate(NULL, CFSTR("set-hostname"), update_hostname, NULL); + if (store == NULL) { + SCLog(TRUE, LOG_ERR, + CFSTR("SCDynamicStoreCreate() failed: %s"), + SCErrorString(SCError())); + goto error; + } + + /* establish notification keys and patterns */ + + keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + + /* ...watch for primary service / interface changes */ + key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, + kSCDynamicStoreDomainState, + kSCEntNetIPv4); + CFArrayAppendValue(keys, key); + CFRelease(key); + + /* ...watch for DNS configuration changes */ + key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, + kSCDynamicStoreDomainState, + kSCEntNetDNS); + CFArrayAppendValue(keys, key); + CFRelease(key); + + /* ...watch for (per-service) DHCP option changes */ + key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, + kSCDynamicStoreDomainState, + kSCCompAnyRegex, + kSCEntNetDHCP); + CFArrayAppendValue(patterns, key); + CFRelease(key); + + /* ...watch for (BSD) hostname changes */ + key = SCDynamicStoreKeyCreateComputerName(NULL); + CFArrayAppendValue(keys, key); + CFRelease(key); + + /* ...watch for local (multicast DNS) hostname changes */ + key = SCDynamicStoreKeyCreateHostNames(NULL); + CFArrayAppendValue(keys, key); + CFRelease(key); + + /* register the keys/patterns */ + if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns)) { + SCLog(TRUE, LOG_ERR, + CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s"), + SCErrorString(SCError())); + goto error; + } + + rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); + if (!rls) { + SCLog(TRUE, LOG_ERR, + CFSTR("SCDynamicStoreCreateRunLoopSource() failed: %s"), + SCErrorString(SCError())); + goto error; + } + CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); + + CFRelease(keys); + CFRelease(patterns); + return; + + error : + + if (keys != NULL) CFRelease(keys); + if (patterns != NULL) CFRelease(patterns); + if (store != NULL) CFRelease(store); + return; +} + + +#ifdef MAIN +int +main(int argc, char **argv) +{ + +#ifdef DEBUG + + _sc_log = FALSE; + if ((argc > 1) && (strcmp(argv[1], "-d") == 0)) { + _sc_verbose = TRUE; + argv++; + argc--; + } + + CFStringRef address; + CFStringRef hostname; + CFStringRef serviceID; + SCDynamicStoreRef store; + + store = SCDynamicStoreCreate(NULL, CFSTR("set-hostname"), NULL, NULL); + if (store == NULL) { + SCPrint(TRUE, stdout, + CFSTR("SCDynamicStoreCreate() failed: %s\n"), + SCErrorString(SCError())); + exit(1); + } + + // get static hostname + hostname = copy_static_name(); + if (hostname != NULL) { + SCPrint(TRUE, stdout, CFSTR("hostname (static) = %@\n"), hostname); + CFRelease(hostname); + } + + // get [prefs] hostname, if available + hostname = copy_prefs_hostname(store); + if (hostname != NULL) { + SCPrint(TRUE, stdout, CFSTR("hostname (prefs) = %@\n"), hostname); + CFRelease(hostname); + } + + // get primary service + serviceID = copy_primary_service(store); + if (serviceID != NULL) { + SCPrint(TRUE, stdout, CFSTR("primary service ID = %@\n"), serviceID); + } else { + SCPrint(TRUE, stdout, CFSTR("No primary service\n")); + goto mDNS; + } + + if ((argc == (2+1)) && (argv[1][0] == 's')) { + if (serviceID != NULL) CFRelease(serviceID); + serviceID = CFStringCreateWithCString(NULL, argv[2], kCFStringEncodingUTF8); + SCPrint(TRUE, stdout, CFSTR("alternate service ID = %@\n"), serviceID); + } + + // get DHCP provided name + hostname = copy_dhcp_name(store, serviceID); + if (hostname != NULL) { + SCPrint(TRUE, stdout, CFSTR("hostname (DHCP) = %@\n"), hostname); + CFRelease(hostname); + } + + // get primary IP address + address = copy_primary_ip(store, serviceID); + if (address != NULL) { + SCPrint(TRUE, stdout, CFSTR("primary address = %@\n"), address); + + if ((argc == (2+1)) && (argv[1][0] == 'a')) { + if (address != NULL) CFRelease(address); + address = CFStringCreateWithCString(NULL, argv[2], kCFStringEncodingUTF8); + SCPrint(TRUE, stdout, CFSTR("alternate primary address = %@\n"), address); + } + + // start reverse DNS query using primary IP address + start_dns_query(store, address); + CFRelease(address); + } + + CFRelease(serviceID); + + mDNS : + + // get local (multicast DNS) name, if available + + hostname = SCDynamicStoreCopyLocalHostName(store); + if (hostname != NULL) { + CFMutableStringRef localName; + + SCPrint(TRUE, stdout, CFSTR("hostname (multicast DNS) = %@\n"), hostname); + localName = CFStringCreateMutableCopy(NULL, 0, hostname); + CFStringAppend(localName, CFSTR(".local")); + CFRelease(localName); + } + + if (hostname != NULL) CFRelease(hostname); + + update_hostname(store, NULL, NULL); + + CFRelease(store); + + CFRunLoopRun(); + +#else /* DEBUG */ + + _sc_log = FALSE; + _sc_verbose = (argc > 1) ? TRUE : FALSE; + + load_hostname((argc > 1) ? TRUE : FALSE); + CFRunLoopRun(); + /* not reached */ + +#endif /* DEBUG */ + + exit(0); + return 0; +} +#endif /* MAIN */