2 * Copyright (c) 2004-2009 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>
44 static SCDynamicStoreRef store
= NULL
;
45 static CFRunLoopSourceRef rls
= NULL
;
47 static Boolean dnsActive
= FALSE
;
48 static CFMachPortRef dnsPort
= NULL
;
49 static struct timeval dnsQueryStart
;
51 static Boolean _verbose
= FALSE
;
54 #define HOSTNAME_NOTIFY_KEY "com.apple.system.hostname"
58 set_hostname(CFStringRef hostname
)
60 if (hostname
!= NULL
) {
61 char old_name
[MAXHOSTNAMELEN
];
62 char new_name
[MAXHOSTNAMELEN
];
64 if (gethostname(old_name
, sizeof(old_name
)) == -1) {
65 SCLog(TRUE
, LOG_ERR
, CFSTR("gethostname() failed: %s"), strerror(errno
));
69 if (_SC_cfstring_to_cstring(hostname
,
72 kCFStringEncodingUTF8
) == NULL
) {
73 SCLog(TRUE
, LOG_ERR
, CFSTR("could not convert [new] hostname"));
77 old_name
[sizeof(old_name
)-1] = '\0';
78 new_name
[sizeof(new_name
)-1] = '\0';
79 if (strcmp(old_name
, new_name
) != 0) {
80 if (sethostname(new_name
, strlen(new_name
)) == 0) {
83 SCLog(TRUE
, LOG_NOTICE
,
84 CFSTR("setting hostname to \"%s\""),
87 status
= notify_post(HOSTNAME_NOTIFY_KEY
);
88 if (status
!= NOTIFY_STATUS_OK
) {
90 CFSTR("notify_post(" HOSTNAME_NOTIFY_KEY
") failed: error=%lu"),
95 CFSTR("sethostname(%s, %d) failed: %s"),
107 #define HOSTCONFIG "/etc/hostconfig"
108 #define HOSTNAME_KEY "HOSTNAME="
109 #define AUTOMATIC "-AUTOMATIC-"
111 #define HOSTNAME_KEY_LEN (sizeof(HOSTNAME_KEY) - 1)
118 CFStringRef name
= NULL
;
120 f
= fopen(HOSTCONFIG
, "r");
125 while (fgets(buf
, sizeof(buf
), f
) != NULL
) {
133 if (buf
[n
-1] == '\n') {
134 /* the entire line fit in the buffer, remove the newline */
137 /* eat the remainder of the line */
140 } while ((n
!= '\n') && (n
!= EOF
));
143 // skip leading white space
145 while (isspace(*bp
)) {
149 // find "HOSTNAME=" key
150 if (strncmp(bp
, HOSTNAME_KEY
, HOSTNAME_KEY_LEN
) != 0) {
154 // get the hostname string
155 bp
+= HOSTNAME_KEY_LEN
;
160 while (*bp
!= '\0') {
173 str_quote
= !str_quote
;
184 } else if (!str_quote
&& (isspace(ch
) || (ch
== '#'))) {
200 // the shell won't parse this file so neither will we
204 if (strcmp(buf
, AUTOMATIC
) == 0) {
205 // skip "-AUTOMATIC-"
209 name
= CFStringCreateWithCString(NULL
, buf
, kCFStringEncodingUTF8
);
218 copy_prefs_hostname(SCDynamicStoreRef store
)
220 CFDictionaryRef dict
;
222 CFStringRef name
= NULL
;
224 key
= SCDynamicStoreKeyCreateComputerName(NULL
);
225 dict
= SCDynamicStoreCopyValue(store
, key
);
230 if (!isA_CFDictionary(dict
)) {
234 name
= isA_CFString(CFDictionaryGetValue(dict
, kSCPropSystemHostName
));
242 if (dict
!= NULL
) CFRelease(dict
);
249 copy_primary_service(SCDynamicStoreRef store
)
251 CFDictionaryRef dict
;
253 CFStringRef serviceID
= NULL
;
255 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
256 kSCDynamicStoreDomainState
,
258 dict
= SCDynamicStoreCopyValue(store
, key
);
262 if (isA_CFDictionary(dict
)) {
263 serviceID
= CFDictionaryGetValue(dict
, kSCDynamicStorePropNetPrimaryService
);
264 if (isA_CFString(serviceID
)) {
278 copy_primary_ip(SCDynamicStoreRef store
, CFStringRef serviceID
)
280 CFDictionaryRef dict
;
282 CFStringRef address
= NULL
;
284 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
285 kSCDynamicStoreDomainState
,
288 dict
= SCDynamicStoreCopyValue(store
, key
);
292 if (isA_CFDictionary(dict
)) {
293 CFArrayRef addresses
;
295 addresses
= CFDictionaryGetValue(dict
, kSCPropNetIPv4Addresses
);
296 if (isA_CFArray(addresses
) && (CFArrayGetCount(addresses
) > 0)) {
297 address
= CFArrayGetValueAtIndex(addresses
, 0);
298 if (isA_CFString(address
)) {
312 #define DHCP_OPTION_HOSTNAME 12
315 copy_dhcp_name(SCDynamicStoreRef store
, CFStringRef serviceID
)
317 CFDictionaryRef info
;
318 CFStringRef name
= NULL
;
320 info
= SCDynamicStoreCopyDHCPInfo(store
, serviceID
);
324 data
= DHCPInfoGetOptionData(info
, DHCP_OPTION_HOSTNAME
);
326 name
= CFStringCreateFromExternalRepresentation(NULL
, data
, kCFStringEncodingUTF8
);
337 reverseDNSComplete(int32_t status
, char *host
, char *serv
, void *context
)
339 struct timeval dnsQueryComplete
;
340 struct timeval dnsQueryElapsed
;
341 CFStringRef hostname
;
342 SCDynamicStoreRef store
= (SCDynamicStoreRef
)context
;
344 (void) gettimeofday(&dnsQueryComplete
, NULL
);
345 timersub(&dnsQueryComplete
, &dnsQueryStart
, &dnsQueryElapsed
);
346 SCLog(_verbose
, LOG_INFO
,
347 CFSTR("async DNS complete%s (query time = %d.%3.3d)"),
348 ((status
== 0) && (host
!= NULL
)) ? "" : ", host not found",
349 dnsQueryElapsed
.tv_sec
,
350 dnsQueryElapsed
.tv_usec
/ 1000);
352 // use reverse DNS name, if available
357 * if [reverse] DNS query was successful
360 hostname
= CFStringCreateWithCString(NULL
, host
, kCFStringEncodingUTF8
);
361 if (hostname
!= NULL
) {
362 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (reverse DNS query) = %@"), hostname
);
363 set_hostname(hostname
);
371 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
375 * if no name available
383 SCLog(TRUE
, LOG_ERR
, CFSTR("getnameinfo() failed: %s"), gai_strerror(status
));
386 // get local (multicast DNS) name, if available
388 hostname
= SCDynamicStoreCopyLocalHostName(store
);
389 if (hostname
!= NULL
) {
390 CFMutableStringRef localName
;
392 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (multicast DNS) = %@"), hostname
);
393 localName
= CFStringCreateMutableCopy(NULL
, 0, hostname
);
394 CFStringAppend(localName
, CFSTR(".local"));
395 set_hostname(localName
);
396 CFRelease(localName
);
401 // use "localhost" if not other name is available
403 set_hostname(CFSTR("localhost"));
407 if (host
!= NULL
) free(host
);
408 if (serv
!= NULL
) free(serv
);
415 replyMPCopyDescription(const void *info
)
417 SCDynamicStoreRef store
= (SCDynamicStoreRef
)info
;
419 return CFStringCreateWithFormat(NULL
,
421 CFSTR("<getnameinfo_async_start reply MP> {store = %p}"),
427 getnameinfo_async_handleCFReply(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
429 mach_port_t mp
= MACH_PORT_NULL
;
432 if (port
!= dnsPort
) {
433 // we've received a callback on the async DNS port but since the
434 // associated CFMachPort doesn't match than the request must have
435 // already been cancelled.
436 SCLog(TRUE
, LOG_ERR
, CFSTR("getnameinfo_async_handleCFReply(): port != dnsPort"));
440 mp
= CFMachPortGetPort(port
);
441 CFMachPortInvalidate(dnsPort
);
444 __MACH_PORT_DEBUG(mp
!= NULL
, "*** set-hostname (after unscheduling)", mp
);
446 status
= getnameinfo_async_handle_reply(msg
);
447 __MACH_PORT_DEBUG(mp
!= NULL
, "*** set-hostname (after getnameinfo_async_handle_reply)", mp
);
448 if ((status
== 0) && dnsActive
&& (mp
!= MACH_PORT_NULL
)) {
449 CFMachPortContext context
= { 0
453 , replyMPCopyDescription
455 CFRunLoopSourceRef rls
;
457 // if request has been re-queued
458 dnsPort
= CFMachPortCreateWithPort(NULL
,
460 getnameinfo_async_handleCFReply
,
463 rls
= CFMachPortCreateRunLoopSource(NULL
, dnsPort
, 0);
464 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
466 __MACH_PORT_DEBUG(mp
!= NULL
, "*** set-hostname (after rescheduling)", mp
);
474 start_dns_query(SCDynamicStoreRef store
, CFStringRef address
)
477 SCNetworkReachabilityFlags flags
;
481 struct sockaddr_in sin
;
482 struct sockaddr_in6 sin6
;
484 if (_SC_cfstring_to_cstring(address
, addr
, sizeof(addr
), kCFStringEncodingASCII
) == NULL
) {
485 SCLog(TRUE
, LOG_ERR
, CFSTR("could not convert [primary] address"));
489 bzero(&sin
, sizeof(sin
));
490 sin
.sin_len
= sizeof(sin
);
491 sin
.sin_family
= AF_INET
;
493 bzero(&sin6
, sizeof(sin6
));
494 sin6
.sin6_len
= sizeof(sin6
);
495 sin6
.sin6_family
= AF_INET6
;
497 if (inet_aton(addr
, &sin
.sin_addr
) == 1) {
501 sa
= (struct sockaddr
*)&sin
;
502 } else if (inet_pton(AF_INET6
, addr
, &sin6
.sin6_addr
) == 1) {
508 p
= strchr(addr
, '%');
510 sin6
.sin6_scope_id
= if_nametoindex(p
+ 1);
513 sa
= (struct sockaddr
*)&sin6
;
519 ok
= _SC_checkResolverReachabilityByAddress(&store
, &flags
, &haveDNS
, sa
);
521 if (!(flags
& kSCNetworkReachabilityFlagsReachable
) ||
522 (flags
& kSCNetworkReachabilityFlagsConnectionRequired
)) {
523 // if not reachable *OR* connection required
529 CFMachPortContext context
= { 0
533 , replyMPCopyDescription
537 CFRunLoopSourceRef rls
;
539 (void) gettimeofday(&dnsQueryStart
, NULL
);
541 error
= getnameinfo_async_start(&mp
,
544 NI_NAMEREQD
, // flags
550 __MACH_PORT_DEBUG(TRUE
, "*** set-hostname (after getnameinfo_async_start)", mp
);
553 dnsPort
= CFMachPortCreateWithPort(NULL
,
555 getnameinfo_async_handleCFReply
,
558 rls
= CFMachPortCreateRunLoopSource(NULL
, dnsPort
, 0);
559 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
561 __MACH_PORT_DEBUG(TRUE
, "*** set-hostname (after scheduling)", mp
);
571 update_hostname(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *info
)
573 CFStringRef address
= NULL
;
574 CFStringRef hostname
= NULL
;
575 CFStringRef serviceID
= NULL
;
577 // if active, cancel any in-progress attempt to resolve the primary IP address
579 if (dnsPort
!= NULL
) {
580 mach_port_t mp
= CFMachPortGetPort(dnsPort
);
582 /* cancel the outstanding DNS query */
583 CFMachPortInvalidate(dnsPort
);
587 getnameinfo_async_cancel(mp
);
590 // get static hostname, if available
592 hostname
= copy_static_name();
593 if (hostname
!= NULL
) {
594 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (static) = %@"), hostname
);
595 set_hostname(hostname
);
599 // get [prefs] hostname, if available
601 hostname
= copy_prefs_hostname(store
);
602 if (hostname
!= NULL
) {
603 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (prefs) = %@"), hostname
);
604 set_hostname(hostname
);
608 // get primary service ID
610 serviceID
= copy_primary_service(store
);
611 if (serviceID
== NULL
) {
615 // get DHCP provided name, if available
617 hostname
= copy_dhcp_name(store
, serviceID
);
618 if (hostname
!= NULL
) {
619 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (DHCP) = %@"), hostname
);
620 set_hostname(hostname
);
624 // get DNS name associated with primary IP, if available
626 address
= copy_primary_ip(store
, serviceID
);
627 if (address
!= NULL
) {
628 // start reverse DNS query using primary IP address
629 (void) start_dns_query(store
, address
);
635 // get local (multicast DNS) name, if available
637 hostname
= SCDynamicStoreCopyLocalHostName(store
);
638 if (hostname
!= NULL
) {
639 CFMutableStringRef localName
;
641 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (multicast DNS) = %@"), hostname
);
642 localName
= CFStringCreateMutableCopy(NULL
, 0, hostname
);
643 CFStringAppend(localName
, CFSTR(".local"));
644 set_hostname(localName
);
645 CFRelease(localName
);
649 // use "localhost" if not other name is available
651 set_hostname(CFSTR("localhost"));
655 if (address
) CFRelease(address
);
656 if (hostname
) CFRelease(hostname
);
657 if (serviceID
) CFRelease(serviceID
);
665 load_hostname(Boolean verbose
)
668 CFMutableArrayRef keys
= NULL
;
669 CFMutableArrayRef patterns
= NULL
;
675 /* initialize a few globals */
677 store
= SCDynamicStoreCreate(NULL
, CFSTR("set-hostname"), update_hostname
, NULL
);
680 CFSTR("SCDynamicStoreCreate() failed: %s"),
681 SCErrorString(SCError()));
685 /* establish notification keys and patterns */
687 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
688 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
690 /* ...watch for primary service / interface changes */
691 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
692 kSCDynamicStoreDomainState
,
694 CFArrayAppendValue(keys
, key
);
697 /* ...watch for DNS configuration changes */
698 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
699 kSCDynamicStoreDomainState
,
701 CFArrayAppendValue(keys
, key
);
704 /* ...watch for (per-service) DHCP option changes */
705 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
706 kSCDynamicStoreDomainState
,
709 CFArrayAppendValue(patterns
, key
);
712 /* ...watch for (BSD) hostname changes */
713 key
= SCDynamicStoreKeyCreateComputerName(NULL
);
714 CFArrayAppendValue(keys
, key
);
717 /* ...watch for local (multicast DNS) hostname changes */
718 key
= SCDynamicStoreKeyCreateHostNames(NULL
);
719 CFArrayAppendValue(keys
, key
);
722 /* register the keys/patterns */
723 if (!SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
)) {
725 CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s"),
726 SCErrorString(SCError()));
730 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
733 CFSTR("SCDynamicStoreCreateRunLoopSource() failed: %s"),
734 SCErrorString(SCError()));
737 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
745 if (keys
!= NULL
) CFRelease(keys
);
746 if (patterns
!= NULL
) CFRelease(patterns
);
747 if (store
!= NULL
) CFRelease(store
);
754 main(int argc
, char **argv
)
760 if ((argc
> 1) && (strcmp(argv
[1], "-d") == 0)) {
767 CFStringRef hostname
;
768 CFStringRef serviceID
;
769 SCDynamicStoreRef store
;
771 store
= SCDynamicStoreCreate(NULL
, CFSTR("set-hostname"), NULL
, NULL
);
773 SCPrint(TRUE
, stdout
,
774 CFSTR("SCDynamicStoreCreate() failed: %s\n"),
775 SCErrorString(SCError()));
779 // get static hostname
780 hostname
= copy_static_name();
781 if (hostname
!= NULL
) {
782 SCPrint(TRUE
, stdout
, CFSTR("hostname (static) = %@\n"), hostname
);
786 // get [prefs] hostname, if available
787 hostname
= copy_prefs_hostname(store
);
788 if (hostname
!= NULL
) {
789 SCPrint(TRUE
, stdout
, CFSTR("hostname (prefs) = %@\n"), hostname
);
793 // get primary service
794 serviceID
= copy_primary_service(store
);
795 if (serviceID
!= NULL
) {
796 SCPrint(TRUE
, stdout
, CFSTR("primary service ID = %@\n"), serviceID
);
798 SCPrint(TRUE
, stdout
, CFSTR("No primary service\n"));
802 if ((argc
== (2+1)) && (argv
[1][0] == 's')) {
803 if (serviceID
!= NULL
) CFRelease(serviceID
);
804 serviceID
= CFStringCreateWithCString(NULL
, argv
[2], kCFStringEncodingUTF8
);
805 SCPrint(TRUE
, stdout
, CFSTR("alternate service ID = %@\n"), serviceID
);
808 // get DHCP provided name
809 hostname
= copy_dhcp_name(store
, serviceID
);
810 if (hostname
!= NULL
) {
811 SCPrint(TRUE
, stdout
, CFSTR("hostname (DHCP) = %@\n"), hostname
);
815 // get primary IP address
816 address
= copy_primary_ip(store
, serviceID
);
817 if (address
!= NULL
) {
818 SCPrint(TRUE
, stdout
, CFSTR("primary address = %@\n"), address
);
820 if ((argc
== (2+1)) && (argv
[1][0] == 'a')) {
821 if (address
!= NULL
) CFRelease(address
);
822 address
= CFStringCreateWithCString(NULL
, argv
[2], kCFStringEncodingUTF8
);
823 SCPrint(TRUE
, stdout
, CFSTR("alternate primary address = %@\n"), address
);
826 // start reverse DNS query using primary IP address
827 start_dns_query(store
, address
);
831 CFRelease(serviceID
);
835 // get local (multicast DNS) name, if available
837 hostname
= SCDynamicStoreCopyLocalHostName(store
);
838 if (hostname
!= NULL
) {
839 CFMutableStringRef localName
;
841 SCPrint(TRUE
, stdout
, CFSTR("hostname (multicast DNS) = %@\n"), hostname
);
842 localName
= CFStringCreateMutableCopy(NULL
, 0, hostname
);
843 CFStringAppend(localName
, CFSTR(".local"));
844 CFRelease(localName
);
847 if (hostname
!= NULL
) CFRelease(hostname
);
849 update_hostname(store
, NULL
, NULL
);
858 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
860 load_hostname((argc
> 1) ? TRUE
: FALSE
);