2 * Copyright (c) 2004, 2005 Apple Computer, 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> // for SCLog(), SCPrint()
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_checkResolverReachability(SCDynamicStoreRef
*storeP
,
59 SCNetworkConnectionFlags
*flags
,
61 const char * nodename
);
65 * checkResolverReachabilityByAddress()
67 * Given an IP address, determine whether a reverse DNS query can be issued
68 * using the current network configuration.
71 checkResolverReachabilityByAddress(SCDynamicStoreRef store
, struct sockaddr
*sa
)
73 SCNetworkConnectionFlags flags
;
80 * Ideally, we would have an API that given a local IP
81 * address would return the DNS server(s) that would field
82 * a given PTR query. Fortunately, we do have an SPI which
83 * which will provide this information given a "name" so we
84 * take the address, convert it into the inverse query name,
85 * and find out which servers should be consulted.
88 switch (sa
->sa_family
) {
94 struct sockaddr_in
*sin
= (struct sockaddr_in
*)sa
;
97 * build "PTR" query name
98 * NNN.NNN.NNN.NNN.in-addr.arpa.
100 rev
.s_addr
= sin
->sin_addr
.s_addr
;
101 (void) snprintf(ptr_name
, sizeof(ptr_name
), "%u.%u.%u.%u.in-addr.arpa.",
112 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)sa
;
113 int x
= sizeof(ptr_name
);
116 #define USE_NIBBLE_QUERY
117 #ifdef USE_NIBBLE_QUERY
119 * build IPv6 "nibble" PTR query name (RFC 1886, RFC 3152)
120 * 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.
122 for (i
= sizeof(sin6
->sin6_addr
) - 1; i
>= 0; i
--) {
123 n
= snprintf(&ptr_name
[s
], x
, "%x.%x.",
124 ( sin6
->sin6_addr
.s6_addr
[i
] & 0xf),
125 ((sin6
->sin6_addr
.s6_addr
[i
] >> 4) & 0xf));
126 if ((n
== -1) || (n
>= x
)) {
134 n
= snprintf(&ptr_name
[s
], x
, "ip6.arpa.");
135 if ((n
== -1) || (n
>= x
)) {
138 #else /* USE_NIBBLE_QUERY */
140 * build IPv6 "bit-string" PTR query name (RFC 2673)
141 * \[xNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN].ip6.arpa.
143 n
= snprintf(&ptr_name
[0], x
, "\\[x");
144 if ((n
== -1) || (n
>= x
)) {
150 for (i
= 0; i
< 16; i
++) {
151 n
= snprintf(&ptr_name
[s
], x
, "%2.2x", sin6
->sin6_addr
.s6_addr
[i
]);
152 if ((n
== -1) || (n
>= x
)) {
160 n
= snprintf(&ptr_name
[s
], x
, "].ip6.arpa.");
161 if ((n
== -1) || (n
>= x
)) {
164 #endif /* USE_NIBBLE_QUERY */
173 ok
= _SC_checkResolverReachability(&store
, &flags
, &haveDNS
, ptr_name
);
175 if (!(flags
& kSCNetworkFlagsReachable
) ||
176 (flags
& kSCNetworkFlagsConnectionRequired
)) {
177 // if not reachable *OR* connection required
188 #define HOSTNAME_NOTIFY_KEY "com.apple.system.hostname"
192 set_hostname(CFStringRef hostname
)
194 if (hostname
!= NULL
) {
195 char old_name
[MAXHOSTNAMELEN
];
196 char new_name
[MAXHOSTNAMELEN
];
198 if (gethostname(old_name
, sizeof(old_name
)) == -1) {
199 SCLog(TRUE
, LOG_ERR
, CFSTR("gethostname() failed: %s"), strerror(errno
));
203 if (_SC_cfstring_to_cstring(hostname
,
206 kCFStringEncodingUTF8
) == NULL
) {
207 SCLog(TRUE
, LOG_ERR
, CFSTR("could not convert [new] hostname"));
211 old_name
[sizeof(old_name
)-1] = '\0';
212 new_name
[sizeof(new_name
)-1] = '\0';
213 if (strcmp(old_name
, new_name
) != 0) {
214 if (sethostname(new_name
, strlen(new_name
)) == 0) {
217 SCLog(TRUE
, LOG_NOTICE
,
218 CFSTR("setting hostname to \"%s\""),
221 status
= notify_post(HOSTNAME_NOTIFY_KEY
);
222 if (status
!= NOTIFY_STATUS_OK
) {
224 CFSTR("notify_post(" HOSTNAME_NOTIFY_KEY
") failed: error=%lu"),
229 CFSTR("sethostname(%s, %d) failed: %s"),
241 #define HOSTCONFIG "/etc/hostconfig"
242 #define HOSTNAME_KEY "HOSTNAME="
243 #define AUTOMATIC "-AUTOMATIC-"
245 #define HOSTNAME_KEY_LEN (sizeof(HOSTNAME_KEY) - 1)
252 CFStringRef name
= NULL
;
254 f
= fopen(HOSTCONFIG
, "r");
259 while (fgets(buf
, sizeof(buf
), f
) != NULL
) {
267 if (buf
[n
-1] == '\n') {
268 /* the entire line fit in the buffer, remove the newline */
271 /* eat the remainder of the line */
274 } while ((n
!= '\n') && (n
!= EOF
));
277 // skip leading white space
279 while (isspace(*bp
)) {
283 // find "HOSTNAME=" key
284 if (strncmp(bp
, HOSTNAME_KEY
, HOSTNAME_KEY_LEN
) != 0) {
288 // get the hostname string
289 bp
+= HOSTNAME_KEY_LEN
;
294 while (*bp
!= '\0') {
307 str_quote
= !str_quote
;
318 } else if (!str_quote
&& (isspace(ch
) || (ch
== '#'))) {
334 // the shell won't parse this file so neither will we
338 if (strcmp(buf
, AUTOMATIC
) == 0) {
339 // skip "-AUTOMATIC-"
343 name
= CFStringCreateWithCString(NULL
, buf
, kCFStringEncodingUTF8
);
351 #ifndef kSCPropNetHostName
352 #define kSCPropNetHostName CFSTR("HostName")
357 copy_prefs_hostname(SCDynamicStoreRef store
)
359 CFDictionaryRef dict
;
361 CFStringRef name
= NULL
;
363 key
= SCDynamicStoreKeyCreateComputerName(NULL
);
364 dict
= SCDynamicStoreCopyValue(store
, key
);
369 if (!isA_CFDictionary(dict
)) {
373 name
= isA_CFString(CFDictionaryGetValue(dict
, kSCPropNetHostName
));
381 if (dict
!= NULL
) CFRelease(dict
);
388 copy_primary_service(SCDynamicStoreRef store
)
390 CFDictionaryRef dict
;
392 CFStringRef serviceID
= NULL
;
394 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
395 kSCDynamicStoreDomainState
,
397 dict
= SCDynamicStoreCopyValue(store
, key
);
401 if (isA_CFDictionary(dict
)) {
402 serviceID
= CFDictionaryGetValue(dict
, kSCDynamicStorePropNetPrimaryService
);
403 if (isA_CFString(serviceID
)) {
417 copy_primary_ip(SCDynamicStoreRef store
, CFStringRef serviceID
)
419 CFDictionaryRef dict
;
421 CFStringRef address
= NULL
;
423 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
424 kSCDynamicStoreDomainState
,
427 dict
= SCDynamicStoreCopyValue(store
, key
);
431 if (isA_CFDictionary(dict
)) {
432 CFArrayRef addresses
;
434 addresses
= CFDictionaryGetValue(dict
, kSCPropNetIPv4Addresses
);
435 if (isA_CFArray(addresses
) && (CFArrayGetCount(addresses
) > 0)) {
436 address
= CFArrayGetValueAtIndex(addresses
, 0);
437 if (isA_CFString(address
)) {
451 #define DHCP_OPTION_HOSTNAME 12
454 copy_dhcp_name(SCDynamicStoreRef store
, CFStringRef serviceID
)
456 CFDictionaryRef info
;
457 CFStringRef name
= NULL
;
459 info
= SCDynamicStoreCopyDHCPInfo(store
, serviceID
);
463 data
= DHCPInfoGetOptionData(info
, DHCP_OPTION_HOSTNAME
);
465 name
= CFStringCreateFromExternalRepresentation(NULL
, data
, kCFStringEncodingUTF8
);
476 reverseDNSComplete(int32_t status
, char *host
, char *serv
, void *context
)
478 struct timeval dnsQueryComplete
;
479 struct timeval dnsQueryElapsed
;
480 CFStringRef hostname
;
481 SCDynamicStoreRef store
= (SCDynamicStoreRef
)context
;
483 (void) gettimeofday(&dnsQueryComplete
, NULL
);
484 timersub(&dnsQueryComplete
, &dnsQueryStart
, &dnsQueryElapsed
);
485 SCLog(_verbose
, LOG_INFO
,
486 CFSTR("async DNS complete%s (query time = %d.%3.3d)"),
487 ((status
== 0) && (host
!= NULL
)) ? "" : ", host not found",
488 dnsQueryElapsed
.tv_sec
,
489 dnsQueryElapsed
.tv_usec
/ 1000);
491 // use reverse DNS name, if available
496 * if [reverse] DNS query was successful
499 hostname
= CFStringCreateWithCString(NULL
, host
, kCFStringEncodingUTF8
);
500 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (reverse DNS query) = %@"), hostname
);
501 set_hostname(hostname
);
509 * if no name available
517 SCLog(TRUE
, LOG_ERR
, CFSTR("getnameinfo() failed: %s"), gai_strerror(status
));
520 // get local (multicast DNS) name, if available
522 hostname
= SCDynamicStoreCopyLocalHostName(store
);
523 if (hostname
!= NULL
) {
524 CFMutableStringRef localName
;
526 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (multicast DNS) = %@"), hostname
);
527 localName
= CFStringCreateMutableCopy(NULL
, 0, hostname
);
528 CFStringAppend(localName
, CFSTR(".local"));
529 set_hostname(localName
);
530 CFRelease(localName
);
535 // use "localhost" if not other name is available
537 set_hostname(CFSTR("localhost"));
541 if (host
!= NULL
) free(host
);
542 if (serv
!= NULL
) free(serv
);
549 getnameinfo_async_handleCFReply(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
553 status
= getnameinfo_async_handle_reply(msg
);
554 if ((status
== 0) && dnsActive
) {
555 // if request has been re-queued
559 if (port
== dnsPort
) {
560 CFRunLoopSourceInvalidate(dnsRLS
);
572 start_dns_query(SCDynamicStoreRef store
, CFStringRef address
)
577 struct sockaddr_in sin
;
578 struct sockaddr_in6 sin6
;
580 if (_SC_cfstring_to_cstring(address
, addr
, sizeof(addr
), kCFStringEncodingASCII
) == NULL
) {
581 SCLog(TRUE
, LOG_ERR
, CFSTR("could not convert [primary] address"));
585 bzero(&sin
, sizeof(sin
));
586 sin
.sin_len
= sizeof(sin
);
587 sin
.sin_family
= AF_INET
;
589 bzero(&sin6
, sizeof(sin6
));
590 sin6
.sin6_len
= sizeof(sin6
);
591 sin6
.sin6_family
= AF_INET6
;
593 if (inet_aton(addr
, &sin
.sin_addr
) == 1) {
597 sa
= (struct sockaddr
*)&sin
;
598 } else if (inet_pton(AF_INET6
, addr
, &sin6
.sin6_addr
) == 1) {
604 p
= strchr(addr
, '%');
606 sin6
.sin6_scope_id
= if_nametoindex(p
+1);
609 sa
= (struct sockaddr
*)&sin6
;
614 ok
= checkResolverReachabilityByAddress(store
, sa
);
616 CFMachPortContext context
= { 0, (void *)store
, CFRetain
, CFRelease
, CFCopyDescription
};
620 (void) gettimeofday(&dnsQueryStart
, NULL
);
622 error
= getnameinfo_async_start(&port
,
633 dnsPort
= CFMachPortCreateWithPort(NULL
,
635 getnameinfo_async_handleCFReply
,
638 dnsRLS
= CFMachPortCreateRunLoopSource(NULL
, dnsPort
, 0);
639 CFRunLoopAddSource(CFRunLoopGetCurrent(), dnsRLS
, kCFRunLoopDefaultMode
);
649 update_hostname(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *info
)
651 CFStringRef address
= NULL
;
652 CFStringRef hostname
= NULL
;
653 CFStringRef serviceID
= NULL
;
655 // if active, cancel any in-progress attempt to resolve the primary IP address
657 if (dnsPort
!= NULL
) {
658 /* cancel the outstanding DNS query */
659 lu_async_call_cancel(CFMachPortGetPort(dnsPort
));
660 CFRunLoopSourceInvalidate(dnsRLS
);
667 // get static hostname, if available
669 hostname
= copy_static_name();
670 if (hostname
!= NULL
) {
671 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (static) = %@"), hostname
);
672 set_hostname(hostname
);
676 // get [prefs] hostname, if available
678 hostname
= copy_prefs_hostname(store
);
679 if (hostname
!= NULL
) {
680 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (prefs) = %@"), hostname
);
681 set_hostname(hostname
);
685 // get primary service ID
687 serviceID
= copy_primary_service(store
);
688 if (serviceID
== NULL
) {
692 // get DHCP provided name, if available
694 hostname
= copy_dhcp_name(store
, serviceID
);
695 if (hostname
!= NULL
) {
696 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (DHCP) = %@"), hostname
);
697 set_hostname(hostname
);
701 // get DNS name associated with primary IP, if available
703 address
= copy_primary_ip(store
, serviceID
);
704 if (address
!= NULL
) {
705 // start reverse DNS query using primary IP address
706 (void) start_dns_query(store
, address
);
712 // get local (multicast DNS) name, if available
714 hostname
= SCDynamicStoreCopyLocalHostName(store
);
715 if (hostname
!= NULL
) {
716 CFMutableStringRef localName
;
718 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (multicast DNS) = %@"), hostname
);
719 localName
= CFStringCreateMutableCopy(NULL
, 0, hostname
);
720 CFStringAppend(localName
, CFSTR(".local"));
721 set_hostname(localName
);
722 CFRelease(localName
);
726 // use "localhost" if not other name is available
728 set_hostname(CFSTR("localhost"));
732 if (address
) CFRelease(address
);
733 if (hostname
) CFRelease(hostname
);
734 if (serviceID
) CFRelease(serviceID
);
742 load_hostname(Boolean verbose
)
745 CFMutableArrayRef keys
= NULL
;
746 CFMutableArrayRef patterns
= NULL
;
752 /* initialize a few globals */
754 store
= SCDynamicStoreCreate(NULL
, CFSTR("set-hostname"), update_hostname
, NULL
);
757 CFSTR("SCDynamicStoreCreate() failed: %s"),
758 SCErrorString(SCError()));
762 /* establish notification keys and patterns */
764 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
765 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
767 /* ...watch for primary service / interface changes */
768 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
769 kSCDynamicStoreDomainState
,
771 CFArrayAppendValue(keys
, key
);
774 /* ...watch for DNS configuration changes */
775 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
776 kSCDynamicStoreDomainState
,
778 CFArrayAppendValue(keys
, key
);
781 /* ...watch for (per-service) DHCP option changes */
782 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
783 kSCDynamicStoreDomainState
,
786 CFArrayAppendValue(patterns
, key
);
789 /* ...watch for (BSD) hostname changes */
790 key
= SCDynamicStoreKeyCreateComputerName(NULL
);
791 CFArrayAppendValue(keys
, key
);
794 /* ...watch for local (multicast DNS) hostname changes */
795 key
= SCDynamicStoreKeyCreateHostNames(NULL
);
796 CFArrayAppendValue(keys
, key
);
799 /* register the keys/patterns */
800 if (!SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
)) {
802 CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s"),
803 SCErrorString(SCError()));
807 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
810 CFSTR("SCDynamicStoreCreateRunLoopSource() failed: %s"),
811 SCErrorString(SCError()));
814 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
822 if (keys
!= NULL
) CFRelease(keys
);
823 if (patterns
!= NULL
) CFRelease(patterns
);
824 if (store
!= NULL
) CFRelease(store
);
831 main(int argc
, char **argv
)
837 if ((argc
> 1) && (strcmp(argv
[1], "-d") == 0)) {
844 CFStringRef hostname
;
845 CFStringRef serviceID
;
846 SCDynamicStoreRef store
;
848 store
= SCDynamicStoreCreate(NULL
, CFSTR("set-hostname"), NULL
, NULL
);
850 SCPrint(TRUE
, stdout
,
851 CFSTR("SCDynamicStoreCreate() failed: %s\n"),
852 SCErrorString(SCError()));
856 // get static hostname
857 hostname
= copy_static_name();
858 if (hostname
!= NULL
) {
859 SCPrint(TRUE
, stdout
, CFSTR("hostname (static) = %@\n"), hostname
);
863 // get [prefs] hostname, if available
864 hostname
= copy_prefs_hostname(store
);
865 if (hostname
!= NULL
) {
866 SCPrint(TRUE
, stdout
, CFSTR("hostname (prefs) = %@\n"), hostname
);
870 // get primary service
871 serviceID
= copy_primary_service(store
);
872 if (serviceID
!= NULL
) {
873 SCPrint(TRUE
, stdout
, CFSTR("primary service ID = %@\n"), serviceID
);
875 SCPrint(TRUE
, stdout
, CFSTR("No primary service\n"));
879 if ((argc
== (2+1)) && (argv
[1][0] == 's')) {
880 if (serviceID
!= NULL
) CFRelease(serviceID
);
881 serviceID
= CFStringCreateWithCString(NULL
, argv
[2], kCFStringEncodingUTF8
);
882 SCPrint(TRUE
, stdout
, CFSTR("alternate service ID = %@\n"), serviceID
);
885 // get DHCP provided name
886 hostname
= copy_dhcp_name(store
, serviceID
);
887 if (hostname
!= NULL
) {
888 SCPrint(TRUE
, stdout
, CFSTR("hostname (DHCP) = %@\n"), hostname
);
892 // get primary IP address
893 address
= copy_primary_ip(store
, serviceID
);
894 if (address
!= NULL
) {
895 SCPrint(TRUE
, stdout
, CFSTR("primary address = %@\n"), address
);
897 if ((argc
== (2+1)) && (argv
[1][0] == 'a')) {
898 if (address
!= NULL
) CFRelease(address
);
899 address
= CFStringCreateWithCString(NULL
, argv
[2], kCFStringEncodingUTF8
);
900 SCPrint(TRUE
, stdout
, CFSTR("alternate primary address = %@\n"), address
);
903 // start reverse DNS query using primary IP address
904 start_dns_query(store
, address
);
908 CFRelease(serviceID
);
912 // get local (multicast DNS) name, if available
914 hostname
= SCDynamicStoreCopyLocalHostName(store
);
915 if (hostname
!= NULL
) {
916 CFMutableStringRef localName
;
918 SCPrint(TRUE
, stdout
, CFSTR("hostname (multicast DNS) = %@\n"), hostname
);
919 localName
= CFStringCreateMutableCopy(NULL
, 0, hostname
);
920 CFStringAppend(localName
, CFSTR(".local"));
921 CFRelease(localName
);
924 if (hostname
!= NULL
) CFRelease(hostname
);
926 update_hostname(store
, NULL
, NULL
);
935 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
937 load_hostname((argc
> 1) ? TRUE
: FALSE
);