2 * Copyright (c) 2004-2012 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"
56 CFStringRef
copy_dhcp_hostname(CFStringRef serviceID
);
59 set_hostname(CFStringRef hostname
)
61 if (hostname
!= NULL
) {
62 char old_name
[MAXHOSTNAMELEN
];
63 char new_name
[MAXHOSTNAMELEN
];
65 if (gethostname(old_name
, sizeof(old_name
)) == -1) {
66 SCLog(TRUE
, LOG_ERR
, CFSTR("gethostname() failed: %s"), strerror(errno
));
70 if (_SC_cfstring_to_cstring(hostname
,
73 kCFStringEncodingUTF8
) == NULL
) {
74 SCLog(TRUE
, LOG_ERR
, CFSTR("could not convert [new] hostname"));
78 old_name
[sizeof(old_name
)-1] = '\0';
79 new_name
[sizeof(new_name
)-1] = '\0';
80 if (strcmp(old_name
, new_name
) != 0) {
81 if (sethostname(new_name
, strlen(new_name
)) == 0) {
84 SCLog(TRUE
, LOG_NOTICE
,
85 CFSTR("setting hostname to \"%s\""),
88 status
= notify_post(HOSTNAME_NOTIFY_KEY
);
89 if (status
!= NOTIFY_STATUS_OK
) {
91 CFSTR("notify_post(" HOSTNAME_NOTIFY_KEY
") failed: error=%lu"),
96 CFSTR("sethostname(%s, %d) failed: %s"),
108 #define HOSTCONFIG "/etc/hostconfig"
109 #define HOSTNAME_KEY "HOSTNAME="
110 #define AUTOMATIC "-AUTOMATIC-"
112 #define HOSTNAME_KEY_LEN (sizeof(HOSTNAME_KEY) - 1)
119 CFStringRef name
= NULL
;
121 f
= fopen(HOSTCONFIG
, "r");
126 while (fgets(buf
, sizeof(buf
), f
) != NULL
) {
134 if (buf
[n
-1] == '\n') {
135 /* the entire line fit in the buffer, remove the newline */
138 /* eat the remainder of the line */
141 } while ((n
!= '\n') && (n
!= EOF
));
144 // skip leading white space
146 while (isspace(*bp
)) {
150 // find "HOSTNAME=" key
151 if (strncmp(bp
, HOSTNAME_KEY
, HOSTNAME_KEY_LEN
) != 0) {
155 // get the hostname string
156 bp
+= HOSTNAME_KEY_LEN
;
161 while (*bp
!= '\0') {
174 str_quote
= !str_quote
;
185 } else if (!str_quote
&& (isspace(ch
) || (ch
== '#'))) {
201 // the shell won't parse this file so neither will we
205 if (strcmp(buf
, AUTOMATIC
) == 0) {
206 // skip "-AUTOMATIC-"
210 name
= CFStringCreateWithCString(NULL
, buf
, kCFStringEncodingUTF8
);
219 copy_prefs_hostname(SCDynamicStoreRef store
)
221 CFDictionaryRef dict
;
223 CFStringRef name
= NULL
;
225 key
= SCDynamicStoreKeyCreateComputerName(NULL
);
226 dict
= SCDynamicStoreCopyValue(store
, key
);
231 if (!isA_CFDictionary(dict
)) {
235 name
= isA_CFString(CFDictionaryGetValue(dict
, kSCPropSystemHostName
));
243 if (dict
!= NULL
) CFRelease(dict
);
250 copy_primary_service(SCDynamicStoreRef store
)
252 CFDictionaryRef dict
;
254 CFStringRef serviceID
= NULL
;
256 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
257 kSCDynamicStoreDomainState
,
259 dict
= SCDynamicStoreCopyValue(store
, key
);
263 if (isA_CFDictionary(dict
)) {
264 serviceID
= CFDictionaryGetValue(dict
, kSCDynamicStorePropNetPrimaryService
);
265 if (isA_CFString(serviceID
)) {
279 copy_primary_ip(SCDynamicStoreRef store
, CFStringRef serviceID
)
281 CFDictionaryRef dict
;
283 CFStringRef address
= NULL
;
285 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
286 kSCDynamicStoreDomainState
,
289 dict
= SCDynamicStoreCopyValue(store
, key
);
293 if (isA_CFDictionary(dict
)) {
294 CFArrayRef addresses
;
296 addresses
= CFDictionaryGetValue(dict
, kSCPropNetIPv4Addresses
);
297 if (isA_CFArray(addresses
) && (CFArrayGetCount(addresses
) > 0)) {
298 address
= CFArrayGetValueAtIndex(addresses
, 0);
299 if (isA_CFString(address
)) {
313 reverseDNSComplete(int32_t status
, char *host
, char *serv
, void *context
)
315 struct timeval dnsQueryComplete
;
316 struct timeval dnsQueryElapsed
;
317 CFStringRef hostname
;
318 SCDynamicStoreRef store
= (SCDynamicStoreRef
)context
;
320 (void) gettimeofday(&dnsQueryComplete
, NULL
);
321 timersub(&dnsQueryComplete
, &dnsQueryStart
, &dnsQueryElapsed
);
322 SCLog(_verbose
, LOG_INFO
,
323 CFSTR("async DNS complete%s (query time = %d.%3.3d)"),
324 ((status
== 0) && (host
!= NULL
)) ? "" : ", host not found",
325 dnsQueryElapsed
.tv_sec
,
326 dnsQueryElapsed
.tv_usec
/ 1000);
328 // use reverse DNS name, if available
333 * if [reverse] DNS query was successful
336 hostname
= CFStringCreateWithCString(NULL
, host
, kCFStringEncodingUTF8
);
337 if (hostname
!= NULL
) {
338 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (reverse DNS query) = %@"), hostname
);
339 set_hostname(hostname
);
347 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
351 * if no name available
359 SCLog(TRUE
, LOG_ERR
, CFSTR("getnameinfo() failed: %s"), gai_strerror(status
));
362 // get local (multicast DNS) name, if available
364 hostname
= SCDynamicStoreCopyLocalHostName(store
);
365 if (hostname
!= NULL
) {
366 CFMutableStringRef localName
;
368 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (multicast DNS) = %@"), hostname
);
369 localName
= CFStringCreateMutableCopy(NULL
, 0, hostname
);
370 CFStringAppend(localName
, CFSTR(".local"));
371 set_hostname(localName
);
372 CFRelease(localName
);
377 // use "localhost" if not other name is available
379 set_hostname(CFSTR("localhost"));
383 if (host
!= NULL
) free(host
);
384 if (serv
!= NULL
) free(serv
);
391 replyMPCopyDescription(const void *info
)
393 SCDynamicStoreRef store
= (SCDynamicStoreRef
)info
;
395 return CFStringCreateWithFormat(NULL
,
397 CFSTR("<getnameinfo_async_start reply MP> {store = %p}"),
403 getnameinfo_async_handleCFReply(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
405 mach_port_t mp
= MACH_PORT_NULL
;
408 if (port
!= dnsPort
) {
409 // we've received a callback on the async DNS port but since the
410 // associated CFMachPort doesn't match than the request must have
411 // already been cancelled.
412 SCLog(TRUE
, LOG_ERR
, CFSTR("getnameinfo_async_handleCFReply(): port != dnsPort"));
416 mp
= CFMachPortGetPort(port
);
417 CFMachPortInvalidate(dnsPort
);
420 __MACH_PORT_DEBUG(mp
!= MACH_PORT_NULL
, "*** set-hostname (after unscheduling)", mp
);
422 status
= getnameinfo_async_handle_reply(msg
);
423 __MACH_PORT_DEBUG(mp
!= MACH_PORT_NULL
, "*** set-hostname (after getnameinfo_async_handle_reply)", mp
);
424 if ((status
== 0) && dnsActive
&& (mp
!= MACH_PORT_NULL
)) {
425 CFMachPortContext context
= { 0
429 , replyMPCopyDescription
431 CFRunLoopSourceRef rls
;
433 // if request has been re-queued
434 dnsPort
= _SC_CFMachPortCreateWithPort("IPMonitor/set-hostname/re-queue",
436 getnameinfo_async_handleCFReply
,
438 rls
= CFMachPortCreateRunLoopSource(NULL
, dnsPort
, 0);
439 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
441 __MACH_PORT_DEBUG(mp
!= MACH_PORT_NULL
, "*** set-hostname (after rescheduling)", mp
);
449 start_dns_query(SCDynamicStoreRef store
, CFStringRef address
)
453 struct sockaddr_in sin
;
454 struct sockaddr_in6 sin6
;
457 SCNetworkReachabilityFlags flags
;
461 if (_SC_cfstring_to_cstring(address
, buf
, sizeof(buf
), kCFStringEncodingASCII
) == NULL
) {
462 SCLog(TRUE
, LOG_ERR
, CFSTR("could not convert [primary] address"));
466 if (_SC_string_to_sockaddr(buf
, AF_UNSPEC
, (void *)&addr
, sizeof(addr
)) == NULL
) {
467 /* if not an IP[v6] address */
468 SCLog(TRUE
, LOG_ERR
, CFSTR("could not parse [primary] address"));
472 ok
= _SC_checkResolverReachabilityByAddress(&store
, &flags
, &haveDNS
, &addr
.sa
);
474 if (!(flags
& kSCNetworkReachabilityFlagsReachable
) ||
475 (flags
& kSCNetworkReachabilityFlagsConnectionRequired
)) {
476 // if not reachable *OR* connection required
482 CFMachPortContext context
= { 0
486 , replyMPCopyDescription
490 CFRunLoopSourceRef rls
;
492 (void) gettimeofday(&dnsQueryStart
, NULL
);
494 error
= getnameinfo_async_start(&mp
,
497 NI_NAMEREQD
, // flags
503 __MACH_PORT_DEBUG(TRUE
, "*** set-hostname (after getnameinfo_async_start)", mp
);
506 dnsPort
= _SC_CFMachPortCreateWithPort("IPMonitor/set-hostname",
508 getnameinfo_async_handleCFReply
,
510 rls
= CFMachPortCreateRunLoopSource(NULL
, dnsPort
, 0);
511 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
513 __MACH_PORT_DEBUG(TRUE
, "*** set-hostname (after scheduling)", mp
);
523 update_hostname(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *info
)
525 CFStringRef address
= NULL
;
526 CFStringRef hostname
= NULL
;
527 CFStringRef serviceID
= NULL
;
529 // if active, cancel any in-progress attempt to resolve the primary IP address
531 if (dnsPort
!= NULL
) {
532 mach_port_t mp
= CFMachPortGetPort(dnsPort
);
534 /* cancel the outstanding DNS query */
535 CFMachPortInvalidate(dnsPort
);
539 getnameinfo_async_cancel(mp
);
542 // get static hostname, if available
544 hostname
= copy_static_name();
545 if (hostname
!= NULL
) {
546 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (static) = %@"), hostname
);
547 set_hostname(hostname
);
551 // get [prefs] hostname, if available
553 hostname
= copy_prefs_hostname(store
);
554 if (hostname
!= NULL
) {
555 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (prefs) = %@"), hostname
);
556 set_hostname(hostname
);
560 // get primary service ID
562 serviceID
= copy_primary_service(store
);
563 if (serviceID
== NULL
) {
567 // get DHCP provided name, if available
569 hostname
= copy_dhcp_hostname(serviceID
);
570 if (hostname
!= NULL
) {
571 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (DHCP) = %@"), hostname
);
572 set_hostname(hostname
);
576 // get DNS name associated with primary IP, if available
578 address
= copy_primary_ip(store
, serviceID
);
579 if (address
!= NULL
) {
580 // start reverse DNS query using primary IP address
581 (void) start_dns_query(store
, address
);
587 // get local (multicast DNS) name, if available
589 hostname
= SCDynamicStoreCopyLocalHostName(store
);
590 if (hostname
!= NULL
) {
591 CFMutableStringRef localName
;
593 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (multicast DNS) = %@"), hostname
);
594 localName
= CFStringCreateMutableCopy(NULL
, 0, hostname
);
595 CFStringAppend(localName
, CFSTR(".local"));
596 set_hostname(localName
);
597 CFRelease(localName
);
601 // use "localhost" if not other name is available
603 set_hostname(CFSTR("localhost"));
607 if (address
) CFRelease(address
);
608 if (hostname
) CFRelease(hostname
);
609 if (serviceID
) CFRelease(serviceID
);
617 load_hostname(Boolean verbose
)
620 CFMutableArrayRef keys
= NULL
;
621 CFMutableArrayRef patterns
= NULL
;
627 /* initialize a few globals */
629 store
= SCDynamicStoreCreate(NULL
, CFSTR("set-hostname"), update_hostname
, NULL
);
632 CFSTR("SCDynamicStoreCreate() failed: %s"),
633 SCErrorString(SCError()));
637 /* establish notification keys and patterns */
639 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
640 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
642 /* ...watch for primary service / interface changes */
643 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
644 kSCDynamicStoreDomainState
,
646 CFArrayAppendValue(keys
, key
);
649 /* ...watch for DNS configuration changes */
650 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
651 kSCDynamicStoreDomainState
,
653 CFArrayAppendValue(keys
, key
);
656 /* ...watch for (per-service) DHCP option changes */
657 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
658 kSCDynamicStoreDomainState
,
661 CFArrayAppendValue(patterns
, key
);
664 /* ...watch for (BSD) hostname changes */
665 key
= SCDynamicStoreKeyCreateComputerName(NULL
);
666 CFArrayAppendValue(keys
, key
);
669 /* ...watch for local (multicast DNS) hostname changes */
670 key
= SCDynamicStoreKeyCreateHostNames(NULL
);
671 CFArrayAppendValue(keys
, key
);
674 /* register the keys/patterns */
675 if (!SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
)) {
677 CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s"),
678 SCErrorString(SCError()));
682 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
685 CFSTR("SCDynamicStoreCreateRunLoopSource() failed: %s"),
686 SCErrorString(SCError()));
689 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
697 if (keys
!= NULL
) CFRelease(keys
);
698 if (patterns
!= NULL
) CFRelease(patterns
);
699 if (store
!= NULL
) CFRelease(store
);
706 main(int argc
, char **argv
)
712 if ((argc
> 1) && (strcmp(argv
[1], "-d") == 0)) {
719 CFStringRef hostname
;
720 CFStringRef serviceID
;
721 SCDynamicStoreRef store
;
723 store
= SCDynamicStoreCreate(NULL
, CFSTR("set-hostname"), NULL
, NULL
);
725 SCPrint(TRUE
, stdout
,
726 CFSTR("SCDynamicStoreCreate() failed: %s\n"),
727 SCErrorString(SCError()));
731 // get static hostname
732 hostname
= copy_static_name();
733 if (hostname
!= NULL
) {
734 SCPrint(TRUE
, stdout
, CFSTR("hostname (static) = %@\n"), hostname
);
738 // get [prefs] hostname, if available
739 hostname
= copy_prefs_hostname(store
);
740 if (hostname
!= NULL
) {
741 SCPrint(TRUE
, stdout
, CFSTR("hostname (prefs) = %@\n"), hostname
);
745 // get primary service
746 serviceID
= copy_primary_service(store
);
747 if (serviceID
!= NULL
) {
748 SCPrint(TRUE
, stdout
, CFSTR("primary service ID = %@\n"), serviceID
);
750 SCPrint(TRUE
, stdout
, CFSTR("No primary service\n"));
754 if ((argc
== (2+1)) && (argv
[1][0] == 's')) {
755 if (serviceID
!= NULL
) CFRelease(serviceID
);
756 serviceID
= CFStringCreateWithCString(NULL
, argv
[2], kCFStringEncodingUTF8
);
757 SCPrint(TRUE
, stdout
, CFSTR("alternate service ID = %@\n"), serviceID
);
760 // get DHCP provided name
761 hostname
= copy_dhcp_name(store
, serviceID
);
762 if (hostname
!= NULL
) {
763 SCPrint(TRUE
, stdout
, CFSTR("hostname (DHCP) = %@\n"), hostname
);
767 // get primary IP address
768 address
= copy_primary_ip(store
, serviceID
);
769 if (address
!= NULL
) {
770 SCPrint(TRUE
, stdout
, CFSTR("primary address = %@\n"), address
);
772 if ((argc
== (2+1)) && (argv
[1][0] == 'a')) {
773 if (address
!= NULL
) CFRelease(address
);
774 address
= CFStringCreateWithCString(NULL
, argv
[2], kCFStringEncodingUTF8
);
775 SCPrint(TRUE
, stdout
, CFSTR("alternate primary address = %@\n"), address
);
778 // start reverse DNS query using primary IP address
779 start_dns_query(store
, address
);
783 CFRelease(serviceID
);
787 // get local (multicast DNS) name, if available
789 hostname
= SCDynamicStoreCopyLocalHostName(store
);
790 if (hostname
!= NULL
) {
791 CFMutableStringRef localName
;
793 SCPrint(TRUE
, stdout
, CFSTR("hostname (multicast DNS) = %@\n"), hostname
);
794 localName
= CFStringCreateMutableCopy(NULL
, 0, hostname
);
795 CFStringAppend(localName
, CFSTR(".local"));
796 CFRelease(localName
);
799 if (hostname
!= NULL
) CFRelease(hostname
);
801 update_hostname(store
, NULL
, NULL
);
810 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
812 load_hostname((argc
> 1) ? TRUE
: FALSE
);