2 * Copyright (c) 2004-2010 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
!= MACH_PORT_NULL
, "*** set-hostname (after unscheduling)", mp
);
446 status
= getnameinfo_async_handle_reply(msg
);
447 __MACH_PORT_DEBUG(mp
!= MACH_PORT_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
= _SC_CFMachPortCreateWithPort("IPMonitor/set-hostname/re-queue",
460 getnameinfo_async_handleCFReply
,
462 rls
= CFMachPortCreateRunLoopSource(NULL
, dnsPort
, 0);
463 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
465 __MACH_PORT_DEBUG(mp
!= MACH_PORT_NULL
, "*** set-hostname (after rescheduling)", mp
);
473 start_dns_query(SCDynamicStoreRef store
, CFStringRef address
)
476 SCNetworkReachabilityFlags flags
;
480 struct sockaddr_in sin
;
481 struct sockaddr_in6 sin6
;
483 if (_SC_cfstring_to_cstring(address
, addr
, sizeof(addr
), kCFStringEncodingASCII
) == NULL
) {
484 SCLog(TRUE
, LOG_ERR
, CFSTR("could not convert [primary] address"));
488 bzero(&sin
, sizeof(sin
));
489 sin
.sin_len
= sizeof(sin
);
490 sin
.sin_family
= AF_INET
;
492 bzero(&sin6
, sizeof(sin6
));
493 sin6
.sin6_len
= sizeof(sin6
);
494 sin6
.sin6_family
= AF_INET6
;
496 if (inet_aton(addr
, &sin
.sin_addr
) == 1) {
500 sa
= (struct sockaddr
*)&sin
;
501 } else if (inet_pton(AF_INET6
, addr
, &sin6
.sin6_addr
) == 1) {
507 p
= strchr(addr
, '%');
509 sin6
.sin6_scope_id
= if_nametoindex(p
+ 1);
512 sa
= (struct sockaddr
*)&sin6
;
518 ok
= _SC_checkResolverReachabilityByAddress(&store
, &flags
, &haveDNS
, sa
);
520 if (!(flags
& kSCNetworkReachabilityFlagsReachable
) ||
521 (flags
& kSCNetworkReachabilityFlagsConnectionRequired
)) {
522 // if not reachable *OR* connection required
528 CFMachPortContext context
= { 0
532 , replyMPCopyDescription
536 CFRunLoopSourceRef rls
;
538 (void) gettimeofday(&dnsQueryStart
, NULL
);
540 error
= getnameinfo_async_start(&mp
,
543 NI_NAMEREQD
, // flags
549 __MACH_PORT_DEBUG(TRUE
, "*** set-hostname (after getnameinfo_async_start)", mp
);
552 dnsPort
= _SC_CFMachPortCreateWithPort("IPMonitor/set-hostname",
554 getnameinfo_async_handleCFReply
,
556 rls
= CFMachPortCreateRunLoopSource(NULL
, dnsPort
, 0);
557 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
559 __MACH_PORT_DEBUG(TRUE
, "*** set-hostname (after scheduling)", mp
);
569 update_hostname(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *info
)
571 CFStringRef address
= NULL
;
572 CFStringRef hostname
= NULL
;
573 CFStringRef serviceID
= NULL
;
575 // if active, cancel any in-progress attempt to resolve the primary IP address
577 if (dnsPort
!= NULL
) {
578 mach_port_t mp
= CFMachPortGetPort(dnsPort
);
580 /* cancel the outstanding DNS query */
581 CFMachPortInvalidate(dnsPort
);
585 getnameinfo_async_cancel(mp
);
588 // get static hostname, if available
590 hostname
= copy_static_name();
591 if (hostname
!= NULL
) {
592 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (static) = %@"), hostname
);
593 set_hostname(hostname
);
597 // get [prefs] hostname, if available
599 hostname
= copy_prefs_hostname(store
);
600 if (hostname
!= NULL
) {
601 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (prefs) = %@"), hostname
);
602 set_hostname(hostname
);
606 // get primary service ID
608 serviceID
= copy_primary_service(store
);
609 if (serviceID
== NULL
) {
613 // get DHCP provided name, if available
615 hostname
= copy_dhcp_name(store
, serviceID
);
616 if (hostname
!= NULL
) {
617 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (DHCP) = %@"), hostname
);
618 set_hostname(hostname
);
622 // get DNS name associated with primary IP, if available
624 address
= copy_primary_ip(store
, serviceID
);
625 if (address
!= NULL
) {
626 // start reverse DNS query using primary IP address
627 (void) start_dns_query(store
, address
);
633 // get local (multicast DNS) name, if available
635 hostname
= SCDynamicStoreCopyLocalHostName(store
);
636 if (hostname
!= NULL
) {
637 CFMutableStringRef localName
;
639 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (multicast DNS) = %@"), hostname
);
640 localName
= CFStringCreateMutableCopy(NULL
, 0, hostname
);
641 CFStringAppend(localName
, CFSTR(".local"));
642 set_hostname(localName
);
643 CFRelease(localName
);
647 // use "localhost" if not other name is available
649 set_hostname(CFSTR("localhost"));
653 if (address
) CFRelease(address
);
654 if (hostname
) CFRelease(hostname
);
655 if (serviceID
) CFRelease(serviceID
);
663 load_hostname(Boolean verbose
)
666 CFMutableArrayRef keys
= NULL
;
667 CFMutableArrayRef patterns
= NULL
;
673 /* initialize a few globals */
675 store
= SCDynamicStoreCreate(NULL
, CFSTR("set-hostname"), update_hostname
, NULL
);
678 CFSTR("SCDynamicStoreCreate() failed: %s"),
679 SCErrorString(SCError()));
683 /* establish notification keys and patterns */
685 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
686 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
688 /* ...watch for primary service / interface changes */
689 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
690 kSCDynamicStoreDomainState
,
692 CFArrayAppendValue(keys
, key
);
695 /* ...watch for DNS configuration changes */
696 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
697 kSCDynamicStoreDomainState
,
699 CFArrayAppendValue(keys
, key
);
702 /* ...watch for (per-service) DHCP option changes */
703 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
704 kSCDynamicStoreDomainState
,
707 CFArrayAppendValue(patterns
, key
);
710 /* ...watch for (BSD) hostname changes */
711 key
= SCDynamicStoreKeyCreateComputerName(NULL
);
712 CFArrayAppendValue(keys
, key
);
715 /* ...watch for local (multicast DNS) hostname changes */
716 key
= SCDynamicStoreKeyCreateHostNames(NULL
);
717 CFArrayAppendValue(keys
, key
);
720 /* register the keys/patterns */
721 if (!SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
)) {
723 CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s"),
724 SCErrorString(SCError()));
728 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
731 CFSTR("SCDynamicStoreCreateRunLoopSource() failed: %s"),
732 SCErrorString(SCError()));
735 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
743 if (keys
!= NULL
) CFRelease(keys
);
744 if (patterns
!= NULL
) CFRelease(patterns
);
745 if (store
!= NULL
) CFRelease(store
);
752 main(int argc
, char **argv
)
758 if ((argc
> 1) && (strcmp(argv
[1], "-d") == 0)) {
765 CFStringRef hostname
;
766 CFStringRef serviceID
;
767 SCDynamicStoreRef store
;
769 store
= SCDynamicStoreCreate(NULL
, CFSTR("set-hostname"), NULL
, NULL
);
771 SCPrint(TRUE
, stdout
,
772 CFSTR("SCDynamicStoreCreate() failed: %s\n"),
773 SCErrorString(SCError()));
777 // get static hostname
778 hostname
= copy_static_name();
779 if (hostname
!= NULL
) {
780 SCPrint(TRUE
, stdout
, CFSTR("hostname (static) = %@\n"), hostname
);
784 // get [prefs] hostname, if available
785 hostname
= copy_prefs_hostname(store
);
786 if (hostname
!= NULL
) {
787 SCPrint(TRUE
, stdout
, CFSTR("hostname (prefs) = %@\n"), hostname
);
791 // get primary service
792 serviceID
= copy_primary_service(store
);
793 if (serviceID
!= NULL
) {
794 SCPrint(TRUE
, stdout
, CFSTR("primary service ID = %@\n"), serviceID
);
796 SCPrint(TRUE
, stdout
, CFSTR("No primary service\n"));
800 if ((argc
== (2+1)) && (argv
[1][0] == 's')) {
801 if (serviceID
!= NULL
) CFRelease(serviceID
);
802 serviceID
= CFStringCreateWithCString(NULL
, argv
[2], kCFStringEncodingUTF8
);
803 SCPrint(TRUE
, stdout
, CFSTR("alternate service ID = %@\n"), serviceID
);
806 // get DHCP provided name
807 hostname
= copy_dhcp_name(store
, serviceID
);
808 if (hostname
!= NULL
) {
809 SCPrint(TRUE
, stdout
, CFSTR("hostname (DHCP) = %@\n"), hostname
);
813 // get primary IP address
814 address
= copy_primary_ip(store
, serviceID
);
815 if (address
!= NULL
) {
816 SCPrint(TRUE
, stdout
, CFSTR("primary address = %@\n"), address
);
818 if ((argc
== (2+1)) && (argv
[1][0] == 'a')) {
819 if (address
!= NULL
) CFRelease(address
);
820 address
= CFStringCreateWithCString(NULL
, argv
[2], kCFStringEncodingUTF8
);
821 SCPrint(TRUE
, stdout
, CFSTR("alternate primary address = %@\n"), address
);
824 // start reverse DNS query using primary IP address
825 start_dns_query(store
, address
);
829 CFRelease(serviceID
);
833 // get local (multicast DNS) name, if available
835 hostname
= SCDynamicStoreCopyLocalHostName(store
);
836 if (hostname
!= NULL
) {
837 CFMutableStringRef localName
;
839 SCPrint(TRUE
, stdout
, CFSTR("hostname (multicast DNS) = %@\n"), hostname
);
840 localName
= CFStringCreateMutableCopy(NULL
, 0, hostname
);
841 CFStringAppend(localName
, CFSTR(".local"));
842 CFRelease(localName
);
845 if (hostname
!= NULL
) CFRelease(hostname
);
847 update_hostname(store
, NULL
, NULL
);
856 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
858 load_hostname((argc
> 1) ? TRUE
: FALSE
);