2 * Copyright (c) 2004-2007 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 #include <sys/param.h>
27 #include <sys/types.h>
28 #include <sys/socket.h>
31 #include <net/if_dl.h>
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
34 #include <netdb_async.h>
36 #include <CoreFoundation/CoreFoundation.h>
37 #include <SystemConfiguration/SystemConfiguration.h>
38 #include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>
39 #include <SystemConfiguration/SCValidation.h>
40 #include <SystemConfiguration/SCPrivate.h>
45 static SCDynamicStoreRef store
= NULL
;
46 static CFRunLoopSourceRef rls
= NULL
;
48 static Boolean dnsActive
= FALSE
;
49 static CFMachPortRef dnsPort
= NULL
;
50 static CFRunLoopSourceRef dnsRLS
= NULL
;
51 static struct timeval dnsQueryStart
;
53 static Boolean _verbose
= FALSE
;
56 /* SPI (from SCNetworkReachability.c) */
58 _SC_checkResolverReachabilityByAddress(SCDynamicStoreRef
*storeP
,
59 SCNetworkConnectionFlags
*flags
,
64 #define HOSTNAME_NOTIFY_KEY "com.apple.system.hostname"
68 set_hostname(CFStringRef hostname
)
70 if (hostname
!= NULL
) {
71 char old_name
[MAXHOSTNAMELEN
];
72 char new_name
[MAXHOSTNAMELEN
];
74 if (gethostname(old_name
, sizeof(old_name
)) == -1) {
75 SCLog(TRUE
, LOG_ERR
, CFSTR("gethostname() failed: %s"), strerror(errno
));
79 if (_SC_cfstring_to_cstring(hostname
,
82 kCFStringEncodingUTF8
) == NULL
) {
83 SCLog(TRUE
, LOG_ERR
, CFSTR("could not convert [new] hostname"));
87 old_name
[sizeof(old_name
)-1] = '\0';
88 new_name
[sizeof(new_name
)-1] = '\0';
89 if (strcmp(old_name
, new_name
) != 0) {
90 if (sethostname(new_name
, strlen(new_name
)) == 0) {
93 SCLog(TRUE
, LOG_NOTICE
,
94 CFSTR("setting hostname to \"%s\""),
97 status
= notify_post(HOSTNAME_NOTIFY_KEY
);
98 if (status
!= NOTIFY_STATUS_OK
) {
100 CFSTR("notify_post(" HOSTNAME_NOTIFY_KEY
") failed: error=%lu"),
105 CFSTR("sethostname(%s, %d) failed: %s"),
117 #define HOSTCONFIG "/etc/hostconfig"
118 #define HOSTNAME_KEY "HOSTNAME="
119 #define AUTOMATIC "-AUTOMATIC-"
121 #define HOSTNAME_KEY_LEN (sizeof(HOSTNAME_KEY) - 1)
128 CFStringRef name
= NULL
;
130 f
= fopen(HOSTCONFIG
, "r");
135 while (fgets(buf
, sizeof(buf
), f
) != NULL
) {
143 if (buf
[n
-1] == '\n') {
144 /* the entire line fit in the buffer, remove the newline */
147 /* eat the remainder of the line */
150 } while ((n
!= '\n') && (n
!= EOF
));
153 // skip leading white space
155 while (isspace(*bp
)) {
159 // find "HOSTNAME=" key
160 if (strncmp(bp
, HOSTNAME_KEY
, HOSTNAME_KEY_LEN
) != 0) {
164 // get the hostname string
165 bp
+= HOSTNAME_KEY_LEN
;
170 while (*bp
!= '\0') {
183 str_quote
= !str_quote
;
194 } else if (!str_quote
&& (isspace(ch
) || (ch
== '#'))) {
210 // the shell won't parse this file so neither will we
214 if (strcmp(buf
, AUTOMATIC
) == 0) {
215 // skip "-AUTOMATIC-"
219 name
= CFStringCreateWithCString(NULL
, buf
, kCFStringEncodingUTF8
);
228 copy_prefs_hostname(SCDynamicStoreRef store
)
230 CFDictionaryRef dict
;
232 CFStringRef name
= NULL
;
234 key
= SCDynamicStoreKeyCreateComputerName(NULL
);
235 dict
= SCDynamicStoreCopyValue(store
, key
);
240 if (!isA_CFDictionary(dict
)) {
244 name
= isA_CFString(CFDictionaryGetValue(dict
, kSCPropSystemHostName
));
252 if (dict
!= NULL
) CFRelease(dict
);
259 copy_primary_service(SCDynamicStoreRef store
)
261 CFDictionaryRef dict
;
263 CFStringRef serviceID
= NULL
;
265 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
266 kSCDynamicStoreDomainState
,
268 dict
= SCDynamicStoreCopyValue(store
, key
);
272 if (isA_CFDictionary(dict
)) {
273 serviceID
= CFDictionaryGetValue(dict
, kSCDynamicStorePropNetPrimaryService
);
274 if (isA_CFString(serviceID
)) {
288 copy_primary_ip(SCDynamicStoreRef store
, CFStringRef serviceID
)
290 CFDictionaryRef dict
;
292 CFStringRef address
= NULL
;
294 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
295 kSCDynamicStoreDomainState
,
298 dict
= SCDynamicStoreCopyValue(store
, key
);
302 if (isA_CFDictionary(dict
)) {
303 CFArrayRef addresses
;
305 addresses
= CFDictionaryGetValue(dict
, kSCPropNetIPv4Addresses
);
306 if (isA_CFArray(addresses
) && (CFArrayGetCount(addresses
) > 0)) {
307 address
= CFArrayGetValueAtIndex(addresses
, 0);
308 if (isA_CFString(address
)) {
322 #define DHCP_OPTION_HOSTNAME 12
325 copy_dhcp_name(SCDynamicStoreRef store
, CFStringRef serviceID
)
327 CFDictionaryRef info
;
328 CFStringRef name
= NULL
;
330 info
= SCDynamicStoreCopyDHCPInfo(store
, serviceID
);
334 data
= DHCPInfoGetOptionData(info
, DHCP_OPTION_HOSTNAME
);
336 name
= CFStringCreateFromExternalRepresentation(NULL
, data
, kCFStringEncodingUTF8
);
347 reverseDNSComplete(int32_t status
, char *host
, char *serv
, void *context
)
349 struct timeval dnsQueryComplete
;
350 struct timeval dnsQueryElapsed
;
351 CFStringRef hostname
;
352 SCDynamicStoreRef store
= (SCDynamicStoreRef
)context
;
354 (void) gettimeofday(&dnsQueryComplete
, NULL
);
355 timersub(&dnsQueryComplete
, &dnsQueryStart
, &dnsQueryElapsed
);
356 SCLog(_verbose
, LOG_INFO
,
357 CFSTR("async DNS complete%s (query time = %d.%3.3d)"),
358 ((status
== 0) && (host
!= NULL
)) ? "" : ", host not found",
359 dnsQueryElapsed
.tv_sec
,
360 dnsQueryElapsed
.tv_usec
/ 1000);
362 // use reverse DNS name, if available
367 * if [reverse] DNS query was successful
370 hostname
= CFStringCreateWithCString(NULL
, host
, kCFStringEncodingUTF8
);
371 if (hostname
!= NULL
) {
372 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (reverse DNS query) = %@"), hostname
);
373 set_hostname(hostname
);
381 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
385 * if no name available
393 SCLog(TRUE
, LOG_ERR
, CFSTR("getnameinfo() failed: %s"), gai_strerror(status
));
396 // get local (multicast DNS) name, if available
398 hostname
= SCDynamicStoreCopyLocalHostName(store
);
399 if (hostname
!= NULL
) {
400 CFMutableStringRef localName
;
402 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (multicast DNS) = %@"), hostname
);
403 localName
= CFStringCreateMutableCopy(NULL
, 0, hostname
);
404 CFStringAppend(localName
, CFSTR(".local"));
405 set_hostname(localName
);
406 CFRelease(localName
);
411 // use "localhost" if not other name is available
413 set_hostname(CFSTR("localhost"));
417 if (host
!= NULL
) free(host
);
418 if (serv
!= NULL
) free(serv
);
425 getnameinfo_async_handleCFReply(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
429 status
= getnameinfo_async_handle_reply(msg
);
430 if ((status
== 0) && dnsActive
) {
431 // if request has been re-queued
435 if (port
== dnsPort
) {
436 CFRunLoopSourceInvalidate(dnsRLS
);
448 replyMPCopyDescription(const void *info
)
450 SCDynamicStoreRef store
= (SCDynamicStoreRef
)info
;
452 return CFStringCreateWithFormat(NULL
,
454 CFSTR("<getnameinfo_async_start reply MP> {store = %p}"),
460 start_dns_query(SCDynamicStoreRef store
, CFStringRef address
)
463 SCNetworkConnectionFlags flags
;
467 struct sockaddr_in sin
;
468 struct sockaddr_in6 sin6
;
470 if (_SC_cfstring_to_cstring(address
, addr
, sizeof(addr
), kCFStringEncodingASCII
) == NULL
) {
471 SCLog(TRUE
, LOG_ERR
, CFSTR("could not convert [primary] address"));
475 bzero(&sin
, sizeof(sin
));
476 sin
.sin_len
= sizeof(sin
);
477 sin
.sin_family
= AF_INET
;
479 bzero(&sin6
, sizeof(sin6
));
480 sin6
.sin6_len
= sizeof(sin6
);
481 sin6
.sin6_family
= AF_INET6
;
483 if (inet_aton(addr
, &sin
.sin_addr
) == 1) {
487 sa
= (struct sockaddr
*)&sin
;
488 } else if (inet_pton(AF_INET6
, addr
, &sin6
.sin6_addr
) == 1) {
494 p
= strchr(addr
, '%');
496 sin6
.sin6_scope_id
= if_nametoindex(p
+ 1);
499 sa
= (struct sockaddr
*)&sin6
;
505 ok
= _SC_checkResolverReachabilityByAddress(&store
, &flags
, &haveDNS
, sa
);
507 if (!(flags
& kSCNetworkFlagsReachable
) ||
508 (flags
& kSCNetworkFlagsConnectionRequired
)) {
509 // if not reachable *OR* connection required
515 CFMachPortContext context
= { 0
519 , replyMPCopyDescription
524 (void) gettimeofday(&dnsQueryStart
, NULL
);
526 error
= getnameinfo_async_start(&port
,
529 NI_NAMEREQD
, // flags
537 dnsPort
= CFMachPortCreateWithPort(NULL
,
539 getnameinfo_async_handleCFReply
,
542 dnsRLS
= CFMachPortCreateRunLoopSource(NULL
, dnsPort
, 0);
543 CFRunLoopAddSource(CFRunLoopGetCurrent(), dnsRLS
, kCFRunLoopDefaultMode
);
553 update_hostname(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *info
)
555 CFStringRef address
= NULL
;
556 CFStringRef hostname
= NULL
;
557 CFStringRef serviceID
= NULL
;
559 // if active, cancel any in-progress attempt to resolve the primary IP address
561 if (dnsPort
!= NULL
) {
562 /* cancel the outstanding DNS query */
563 getnameinfo_async_cancel(CFMachPortGetPort(dnsPort
));
564 CFRunLoopSourceInvalidate(dnsRLS
);
571 // get static hostname, if available
573 hostname
= copy_static_name();
574 if (hostname
!= NULL
) {
575 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (static) = %@"), hostname
);
576 set_hostname(hostname
);
580 // get [prefs] hostname, if available
582 hostname
= copy_prefs_hostname(store
);
583 if (hostname
!= NULL
) {
584 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (prefs) = %@"), hostname
);
585 set_hostname(hostname
);
589 // get primary service ID
591 serviceID
= copy_primary_service(store
);
592 if (serviceID
== NULL
) {
596 // get DHCP provided name, if available
598 hostname
= copy_dhcp_name(store
, serviceID
);
599 if (hostname
!= NULL
) {
600 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (DHCP) = %@"), hostname
);
601 set_hostname(hostname
);
605 // get DNS name associated with primary IP, if available
607 address
= copy_primary_ip(store
, serviceID
);
608 if (address
!= NULL
) {
609 // start reverse DNS query using primary IP address
610 (void) start_dns_query(store
, address
);
616 // get local (multicast DNS) name, if available
618 hostname
= SCDynamicStoreCopyLocalHostName(store
);
619 if (hostname
!= NULL
) {
620 CFMutableStringRef localName
;
622 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (multicast DNS) = %@"), hostname
);
623 localName
= CFStringCreateMutableCopy(NULL
, 0, hostname
);
624 CFStringAppend(localName
, CFSTR(".local"));
625 set_hostname(localName
);
626 CFRelease(localName
);
630 // use "localhost" if not other name is available
632 set_hostname(CFSTR("localhost"));
636 if (address
) CFRelease(address
);
637 if (hostname
) CFRelease(hostname
);
638 if (serviceID
) CFRelease(serviceID
);
646 load_hostname(Boolean verbose
)
649 CFMutableArrayRef keys
= NULL
;
650 CFMutableArrayRef patterns
= NULL
;
656 /* initialize a few globals */
658 store
= SCDynamicStoreCreate(NULL
, CFSTR("set-hostname"), update_hostname
, NULL
);
661 CFSTR("SCDynamicStoreCreate() failed: %s"),
662 SCErrorString(SCError()));
666 /* establish notification keys and patterns */
668 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
669 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
671 /* ...watch for primary service / interface changes */
672 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
673 kSCDynamicStoreDomainState
,
675 CFArrayAppendValue(keys
, key
);
678 /* ...watch for DNS configuration changes */
679 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
680 kSCDynamicStoreDomainState
,
682 CFArrayAppendValue(keys
, key
);
685 /* ...watch for (per-service) DHCP option changes */
686 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
687 kSCDynamicStoreDomainState
,
690 CFArrayAppendValue(patterns
, key
);
693 /* ...watch for (BSD) hostname changes */
694 key
= SCDynamicStoreKeyCreateComputerName(NULL
);
695 CFArrayAppendValue(keys
, key
);
698 /* ...watch for local (multicast DNS) hostname changes */
699 key
= SCDynamicStoreKeyCreateHostNames(NULL
);
700 CFArrayAppendValue(keys
, key
);
703 /* register the keys/patterns */
704 if (!SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
)) {
706 CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s"),
707 SCErrorString(SCError()));
711 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
714 CFSTR("SCDynamicStoreCreateRunLoopSource() failed: %s"),
715 SCErrorString(SCError()));
718 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
726 if (keys
!= NULL
) CFRelease(keys
);
727 if (patterns
!= NULL
) CFRelease(patterns
);
728 if (store
!= NULL
) CFRelease(store
);
735 main(int argc
, char **argv
)
741 if ((argc
> 1) && (strcmp(argv
[1], "-d") == 0)) {
748 CFStringRef hostname
;
749 CFStringRef serviceID
;
750 SCDynamicStoreRef store
;
752 store
= SCDynamicStoreCreate(NULL
, CFSTR("set-hostname"), NULL
, NULL
);
754 SCPrint(TRUE
, stdout
,
755 CFSTR("SCDynamicStoreCreate() failed: %s\n"),
756 SCErrorString(SCError()));
760 // get static hostname
761 hostname
= copy_static_name();
762 if (hostname
!= NULL
) {
763 SCPrint(TRUE
, stdout
, CFSTR("hostname (static) = %@\n"), hostname
);
767 // get [prefs] hostname, if available
768 hostname
= copy_prefs_hostname(store
);
769 if (hostname
!= NULL
) {
770 SCPrint(TRUE
, stdout
, CFSTR("hostname (prefs) = %@\n"), hostname
);
774 // get primary service
775 serviceID
= copy_primary_service(store
);
776 if (serviceID
!= NULL
) {
777 SCPrint(TRUE
, stdout
, CFSTR("primary service ID = %@\n"), serviceID
);
779 SCPrint(TRUE
, stdout
, CFSTR("No primary service\n"));
783 if ((argc
== (2+1)) && (argv
[1][0] == 's')) {
784 if (serviceID
!= NULL
) CFRelease(serviceID
);
785 serviceID
= CFStringCreateWithCString(NULL
, argv
[2], kCFStringEncodingUTF8
);
786 SCPrint(TRUE
, stdout
, CFSTR("alternate service ID = %@\n"), serviceID
);
789 // get DHCP provided name
790 hostname
= copy_dhcp_name(store
, serviceID
);
791 if (hostname
!= NULL
) {
792 SCPrint(TRUE
, stdout
, CFSTR("hostname (DHCP) = %@\n"), hostname
);
796 // get primary IP address
797 address
= copy_primary_ip(store
, serviceID
);
798 if (address
!= NULL
) {
799 SCPrint(TRUE
, stdout
, CFSTR("primary address = %@\n"), address
);
801 if ((argc
== (2+1)) && (argv
[1][0] == 'a')) {
802 if (address
!= NULL
) CFRelease(address
);
803 address
= CFStringCreateWithCString(NULL
, argv
[2], kCFStringEncodingUTF8
);
804 SCPrint(TRUE
, stdout
, CFSTR("alternate primary address = %@\n"), address
);
807 // start reverse DNS query using primary IP address
808 start_dns_query(store
, address
);
812 CFRelease(serviceID
);
816 // get local (multicast DNS) name, if available
818 hostname
= SCDynamicStoreCopyLocalHostName(store
);
819 if (hostname
!= NULL
) {
820 CFMutableStringRef localName
;
822 SCPrint(TRUE
, stdout
, CFSTR("hostname (multicast DNS) = %@\n"), hostname
);
823 localName
= CFStringCreateMutableCopy(NULL
, 0, hostname
);
824 CFStringAppend(localName
, CFSTR(".local"));
825 CFRelease(localName
);
828 if (hostname
!= NULL
) CFRelease(hostname
);
830 update_hostname(store
, NULL
, NULL
);
839 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
841 load_hostname((argc
> 1) ? TRUE
: FALSE
);