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/types.h>
27 #include <sys/socket.h>
30 #include <net/if_dl.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <netdb_async.h>
35 #include <CoreFoundation/CoreFoundation.h>
36 #include <SystemConfiguration/SystemConfiguration.h>
37 #include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>
38 #include <SystemConfiguration/SCValidation.h>
39 #include <SystemConfiguration/SCPrivate.h> // for SCLog(), SCPrint()
44 static SCDynamicStoreRef store
= NULL
;
45 static CFRunLoopSourceRef rls
= NULL
;
47 static CFMachPortRef dnsPort
= NULL
;
48 static CFRunLoopSourceRef dnsRLS
= NULL
;
49 static struct timeval dnsQueryStart
;
51 static Boolean _verbose
= FALSE
;
54 /* SPI (from SCNetworkReachability.c) */
56 _SC_checkResolverReachability(SCDynamicStoreRef
*storeP
,
57 SCNetworkConnectionFlags
*flags
,
59 const char * nodename
);
63 * checkResolverReachabilityByAddress()
65 * Given an IP address, determine whether a reverse DNS query can be issued
66 * using the current network configuration.
69 checkResolverReachabilityByAddress(SCDynamicStoreRef store
, struct sockaddr
*sa
)
71 SCNetworkConnectionFlags flags
;
78 * Ideally, we would have an API that given a local IP
79 * address would return the DNS server(s) that would field
80 * a given PTR query. Fortunately, we do have an SPI which
81 * which will provide this information given a "name" so we
82 * take the address, convert it into the inverse query name,
83 * and find out which servers should be consulted.
86 switch (sa
->sa_family
) {
92 struct sockaddr_in
*sin
= (struct sockaddr_in
*)sa
;
95 * build "PTR" query name
96 * NNN.NNN.NNN.NNN.in-addr.arpa.
98 rev
.s_addr
= sin
->sin_addr
.s_addr
;
99 (void) snprintf(ptr_name
, sizeof(ptr_name
), "%u.%u.%u.%u.in-addr.arpa.",
110 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)sa
;
111 int x
= sizeof(ptr_name
);
114 #define USE_NIBBLE_QUERY
115 #ifdef USE_NIBBLE_QUERY
117 * build IPv6 "nibble" PTR query name (RFC 1886, RFC 3152)
118 * 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.
120 for (i
= sizeof(sin6
->sin6_addr
) - 1; i
>= 0; i
--) {
121 n
= snprintf(&ptr_name
[s
], x
, "%x.%x.",
122 ( sin6
->sin6_addr
.s6_addr
[i
] & 0xf),
123 ((sin6
->sin6_addr
.s6_addr
[i
] >> 4) & 0xf));
124 if ((n
== -1) || (n
>= x
)) {
132 n
= snprintf(&ptr_name
[s
], x
, "ip6.arpa.");
133 if ((n
== -1) || (n
>= x
)) {
136 #else /* USE_NIBBLE_QUERY */
138 * build IPv6 "bit-string" PTR query name (RFC 2673)
139 * \[xNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN].ip6.arpa.
141 n
= snprintf(&ptr_name
[0], x
, "\\[x");
142 if ((n
== -1) || (n
>= x
)) {
148 for (i
= 0; i
< 16; i
++) {
149 n
= snprintf(&ptr_name
[s
], x
, "%2.2x", sin6
->sin6_addr
.s6_addr
[i
]);
150 if ((n
== -1) || (n
>= x
)) {
158 n
= snprintf(&ptr_name
[s
], x
, "].ip6.arpa.");
159 if ((n
== -1) || (n
>= x
)) {
162 #endif /* USE_NIBBLE_QUERY */
171 ok
= _SC_checkResolverReachability(&store
, &flags
, &haveDNS
, ptr_name
);
173 if (!(flags
& kSCNetworkFlagsReachable
) ||
174 (flags
& kSCNetworkFlagsConnectionRequired
)) {
175 // if not reachable *OR* connection required
186 #define HOSTNAME_NOTIFY_KEY "com.apple.system.hostname"
190 set_hostname(CFStringRef hostname
)
192 if (hostname
!= NULL
) {
193 char old_name
[MAXHOSTNAMELEN
];
194 char new_name
[MAXHOSTNAMELEN
];
196 if (gethostname(old_name
, sizeof(old_name
)) == -1) {
197 SCLog(TRUE
, LOG_ERR
, CFSTR("gethostname() failed: %s"), strerror(errno
));
201 if (_SC_cfstring_to_cstring(hostname
,
204 kCFStringEncodingUTF8
) == NULL
) {
205 SCLog(TRUE
, LOG_ERR
, CFSTR("could not convert [new] hostname"));
209 old_name
[sizeof(old_name
)-1] = '\0';
210 new_name
[sizeof(new_name
)-1] = '\0';
211 if (strcmp(old_name
, new_name
) != 0) {
212 if (sethostname(new_name
, strlen(new_name
)) == 0) {
215 SCLog(TRUE
, LOG_NOTICE
,
216 CFSTR("setting hostname to \"%s\""),
219 status
= notify_post(HOSTNAME_NOTIFY_KEY
);
220 if (status
!= NOTIFY_STATUS_OK
) {
222 CFSTR("notify_post(" HOSTNAME_NOTIFY_KEY
") failed: error=%lu"),
227 CFSTR("sethostname(%s, %d) failed: %s"),
239 #define HOSTCONFIG "/etc/hostconfig"
240 #define HOSTNAME_KEY "HOSTNAME="
241 #define AUTOMATIC "-AUTOMATIC-"
243 #define HOSTNAME_KEY_LEN (sizeof(HOSTNAME_KEY) - 1)
250 CFStringRef name
= NULL
;
252 f
= fopen(HOSTCONFIG
, "r");
257 while (fgets(buf
, sizeof(buf
), f
) != NULL
) {
265 if (buf
[n
-1] == '\n') {
266 /* the entire line fit in the buffer, remove the newline */
269 /* eat the remainder of the line */
272 } while ((n
!= '\n') && (n
!= EOF
));
275 // skip leading white space
277 while (isspace(*bp
)) {
281 // find "HOSTNAME=" key
282 if (strncmp(bp
, HOSTNAME_KEY
, HOSTNAME_KEY_LEN
) != 0) {
286 // get the hostname string
287 bp
+= HOSTNAME_KEY_LEN
;
292 while (*bp
!= '\0') {
305 str_quote
= !str_quote
;
316 } else if (!str_quote
&& (isspace(ch
) || (ch
== '#'))) {
332 // the shell won't parse this file so neither will we
336 if (strcmp(buf
, AUTOMATIC
) == 0) {
337 // skip "-AUTOMATIC-"
341 name
= CFStringCreateWithCString(NULL
, buf
, kCFStringEncodingUTF8
);
349 #ifndef kSCPropNetHostName
350 #define kSCPropNetHostName CFSTR("HostName")
355 copy_prefs_hostname(SCDynamicStoreRef store
)
357 CFDictionaryRef dict
;
359 CFStringRef name
= NULL
;
361 key
= SCDynamicStoreKeyCreateComputerName(NULL
);
362 dict
= SCDynamicStoreCopyValue(store
, key
);
367 if (!isA_CFDictionary(dict
)) {
371 name
= isA_CFString(CFDictionaryGetValue(dict
, kSCPropNetHostName
));
379 if (dict
!= NULL
) CFRelease(dict
);
386 copy_primary_service(SCDynamicStoreRef store
)
388 CFDictionaryRef dict
;
390 CFStringRef serviceID
= NULL
;
392 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
393 kSCDynamicStoreDomainState
,
395 dict
= SCDynamicStoreCopyValue(store
, key
);
399 if (isA_CFDictionary(dict
)) {
400 serviceID
= CFDictionaryGetValue(dict
, kSCDynamicStorePropNetPrimaryService
);
401 if (isA_CFString(serviceID
)) {
415 copy_primary_ip(SCDynamicStoreRef store
, CFStringRef serviceID
)
417 CFDictionaryRef dict
;
419 CFStringRef address
= NULL
;
421 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
422 kSCDynamicStoreDomainState
,
425 dict
= SCDynamicStoreCopyValue(store
, key
);
429 if (isA_CFDictionary(dict
)) {
430 CFArrayRef addresses
;
432 addresses
= CFDictionaryGetValue(dict
, kSCPropNetIPv4Addresses
);
433 if (isA_CFArray(addresses
) && (CFArrayGetCount(addresses
) > 0)) {
434 address
= CFArrayGetValueAtIndex(addresses
, 0);
435 if (isA_CFString(address
)) {
449 #define DHCP_OPTION_HOSTNAME 12
452 copy_dhcp_name(SCDynamicStoreRef store
, CFStringRef serviceID
)
454 CFDictionaryRef info
;
455 CFStringRef name
= NULL
;
457 info
= SCDynamicStoreCopyDHCPInfo(store
, serviceID
);
461 data
= DHCPInfoGetOptionData(info
, DHCP_OPTION_HOSTNAME
);
463 name
= CFStringCreateFromExternalRepresentation(NULL
, data
, kCFStringEncodingUTF8
);
474 reverseDNSComplete(int32_t status
, char *host
, char *serv
, void *context
)
476 struct timeval dnsQueryComplete
;
477 struct timeval dnsQueryElapsed
;
478 CFStringRef hostname
;
479 SCDynamicStoreRef store
= (SCDynamicStoreRef
)context
;
481 (void) gettimeofday(&dnsQueryComplete
, NULL
);
482 timersub(&dnsQueryComplete
, &dnsQueryStart
, &dnsQueryElapsed
);
483 SCLog(_verbose
, LOG_INFO
,
484 CFSTR("async DNS complete%s (query time = %d.%3.3d)"),
485 ((status
== 0) && (host
!= NULL
)) ? "" : ", host not found",
486 dnsQueryElapsed
.tv_sec
,
487 dnsQueryElapsed
.tv_usec
/ 1000);
489 // use reverse DNS name, if available
494 * if [reverse] DNS query was successful
497 hostname
= CFStringCreateWithCString(NULL
, host
, kCFStringEncodingUTF8
);
498 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (reverse DNS query) = %@"), hostname
);
499 set_hostname(hostname
);
507 * if no name available
515 SCLog(TRUE
, LOG_ERR
, CFSTR("getnameinfo() failed: %s"), gai_strerror(status
));
518 // get local (multicast DNS) name, if available
520 hostname
= SCDynamicStoreCopyLocalHostName(store
);
521 if (hostname
!= NULL
) {
522 CFMutableStringRef localName
;
524 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (multicast DNS) = %@"), hostname
);
525 localName
= CFStringCreateMutableCopy(NULL
, 0, hostname
);
526 CFStringAppend(localName
, CFSTR(".local"));
527 set_hostname(localName
);
528 CFRelease(localName
);
533 // use "localhost" if not other name is available
535 set_hostname(CFSTR("localhost"));
539 if (host
!= NULL
) free(host
);
540 if (serv
!= NULL
) free(serv
);
546 getnameinfo_async_handleCFReply(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
548 getnameinfo_async_handle_reply(msg
);
550 if (port
== dnsPort
) {
562 start_dns_query(SCDynamicStoreRef store
, CFStringRef address
)
567 struct sockaddr_in sin
;
568 struct sockaddr_in6 sin6
;
570 if (_SC_cfstring_to_cstring(address
, addr
, sizeof(addr
), kCFStringEncodingASCII
) == NULL
) {
571 SCLog(TRUE
, LOG_ERR
, CFSTR("could not convert [primary] address"));
575 bzero(&sin
, sizeof(sin
));
576 sin
.sin_len
= sizeof(sin
);
577 sin
.sin_family
= AF_INET
;
579 bzero(&sin6
, sizeof(sin6
));
580 sin6
.sin6_len
= sizeof(sin6
);
581 sin6
.sin6_family
= AF_INET6
;
583 if (inet_aton(addr
, &sin
.sin_addr
) == 1) {
587 sa
= (struct sockaddr
*)&sin
;
588 } else if (inet_pton(AF_INET6
, addr
, &sin6
.sin6_addr
) == 1) {
594 p
= strchr(addr
, '%');
596 sin6
.sin6_scope_id
= if_nametoindex(p
+1);
599 sa
= (struct sockaddr
*)&sin6
;
604 ok
= checkResolverReachabilityByAddress(store
, sa
);
606 CFMachPortContext context
= { 0, (void *)store
, CFRetain
, CFRelease
, CFCopyDescription
};
610 if ((dnsPort
!= NULL
) && (dnsRLS
!= NULL
)) {
611 /* if we already have an active async DNS query */
612 lu_async_call_cancel(CFMachPortGetPort(dnsPort
));
619 (void) gettimeofday(&dnsQueryStart
, NULL
);
621 error
= getnameinfo_async_start(&port
,
631 dnsPort
= CFMachPortCreateWithPort(NULL
,
633 getnameinfo_async_handleCFReply
,
636 dnsRLS
= CFMachPortCreateRunLoopSource(NULL
, dnsPort
, 0);
637 CFRunLoopAddSource(CFRunLoopGetCurrent(), dnsRLS
, kCFRunLoopDefaultMode
);
647 update_hostname(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *info
)
649 CFStringRef address
= NULL
;
650 CFStringRef hostname
= NULL
;
651 CFStringRef serviceID
= NULL
;
653 // get static hostname, if available
655 hostname
= copy_static_name();
656 if (hostname
!= NULL
) {
657 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (static) = %@"), hostname
);
658 set_hostname(hostname
);
662 // get [prefs] hostname, if available
664 hostname
= copy_prefs_hostname(store
);
665 if (hostname
!= NULL
) {
666 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (prefs) = %@"), hostname
);
667 set_hostname(hostname
);
671 // get primary service ID
673 serviceID
= copy_primary_service(store
);
674 if (serviceID
== NULL
) {
678 // get DHCP provided name, if available
680 hostname
= copy_dhcp_name(store
, serviceID
);
681 if (hostname
!= NULL
) {
682 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (DHCP) = %@"), hostname
);
683 set_hostname(hostname
);
687 // get DNS name associated with primary IP, if available
689 address
= copy_primary_ip(store
, serviceID
);
690 if (address
!= NULL
) {
691 // start reverse DNS query using primary IP address
692 (void) start_dns_query(store
, address
);
698 // get local (multicast DNS) name, if available
700 hostname
= SCDynamicStoreCopyLocalHostName(store
);
701 if (hostname
!= NULL
) {
702 CFMutableStringRef localName
;
704 SCLog(TRUE
, LOG_INFO
, CFSTR("hostname (multicast DNS) = %@"), hostname
);
705 localName
= CFStringCreateMutableCopy(NULL
, 0, hostname
);
706 CFStringAppend(localName
, CFSTR(".local"));
707 set_hostname(localName
);
708 CFRelease(localName
);
712 // use "localhost" if not other name is available
714 set_hostname(CFSTR("localhost"));
718 if (address
) CFRelease(address
);
719 if (hostname
) CFRelease(hostname
);
720 if (serviceID
) CFRelease(serviceID
);
728 load_hostname(Boolean verbose
)
731 CFMutableArrayRef keys
= NULL
;
732 CFMutableArrayRef patterns
= NULL
;
738 /* initialize a few globals */
740 store
= SCDynamicStoreCreate(NULL
, CFSTR("set-hostname"), update_hostname
, NULL
);
743 CFSTR("SCDynamicStoreCreate() failed: %s"),
744 SCErrorString(SCError()));
748 /* establish notification keys and patterns */
750 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
751 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
753 /* ...watch for primary service / interface changes */
754 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
755 kSCDynamicStoreDomainState
,
757 CFArrayAppendValue(keys
, key
);
760 /* ...watch for DNS configuration changes */
761 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
762 kSCDynamicStoreDomainState
,
764 CFArrayAppendValue(keys
, key
);
767 /* ...watch for (per-service) DHCP option changes */
768 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
769 kSCDynamicStoreDomainState
,
772 CFArrayAppendValue(patterns
, key
);
775 /* ...watch for (BSD) hostname changes */
776 key
= SCDynamicStoreKeyCreateComputerName(NULL
);
777 CFArrayAppendValue(keys
, key
);
780 /* ...watch for local (multicast DNS) hostname changes */
781 key
= SCDynamicStoreKeyCreateHostNames(NULL
);
782 CFArrayAppendValue(keys
, key
);
785 /* register the keys/patterns */
786 if (!SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
)) {
788 CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s"),
789 SCErrorString(SCError()));
793 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
796 CFSTR("SCDynamicStoreCreateRunLoopSource() failed: %s"),
797 SCErrorString(SCError()));
800 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
808 if (keys
!= NULL
) CFRelease(keys
);
809 if (patterns
!= NULL
) CFRelease(patterns
);
810 if (store
!= NULL
) CFRelease(store
);
817 main(int argc
, char **argv
)
823 if ((argc
> 1) && (strcmp(argv
[1], "-d") == 0)) {
830 CFStringRef hostname
;
831 CFStringRef serviceID
;
832 SCDynamicStoreRef store
;
834 store
= SCDynamicStoreCreate(NULL
, CFSTR("set-hostname"), NULL
, NULL
);
836 SCPrint(TRUE
, stdout
,
837 CFSTR("SCDynamicStoreCreate() failed: %s\n"),
838 SCErrorString(SCError()));
842 // get static hostname
843 hostname
= copy_static_name();
844 if (hostname
!= NULL
) {
845 SCPrint(TRUE
, stdout
, CFSTR("hostname (static) = %@\n"), hostname
);
849 // get [prefs] hostname, if available
850 hostname
= copy_prefs_hostname(store
);
851 if (hostname
!= NULL
) {
852 SCPrint(TRUE
, stdout
, CFSTR("hostname (prefs) = %@\n"), hostname
);
856 // get primary service
857 serviceID
= copy_primary_service(store
);
858 if (serviceID
!= NULL
) {
859 SCPrint(TRUE
, stdout
, CFSTR("primary service ID = %@\n"), serviceID
);
861 SCPrint(TRUE
, stdout
, CFSTR("No primary service\n"));
865 if ((argc
== (2+1)) && (argv
[1][0] == 's')) {
866 if (serviceID
!= NULL
) CFRelease(serviceID
);
867 serviceID
= CFStringCreateWithCString(NULL
, argv
[2], kCFStringEncodingUTF8
);
868 SCPrint(TRUE
, stdout
, CFSTR("alternate service ID = %@\n"), serviceID
);
871 // get DHCP provided name
872 hostname
= copy_dhcp_name(store
, serviceID
);
873 if (hostname
!= NULL
) {
874 SCPrint(TRUE
, stdout
, CFSTR("hostname (DHCP) = %@\n"), hostname
);
878 // get primary IP address
879 address
= copy_primary_ip(store
, serviceID
);
880 if (address
!= NULL
) {
881 SCPrint(TRUE
, stdout
, CFSTR("primary address = %@\n"), address
);
883 if ((argc
== (2+1)) && (argv
[1][0] == 'a')) {
884 if (address
!= NULL
) CFRelease(address
);
885 address
= CFStringCreateWithCString(NULL
, argv
[2], kCFStringEncodingUTF8
);
886 SCPrint(TRUE
, stdout
, CFSTR("alternate primary address = %@\n"), address
);
889 // start reverse DNS query using primary IP address
890 start_dns_query(store
, address
);
894 CFRelease(serviceID
);
898 // get local (multicast DNS) name, if available
900 hostname
= SCDynamicStoreCopyLocalHostName(store
);
901 if (hostname
!= NULL
) {
902 CFMutableStringRef localName
;
904 SCPrint(TRUE
, stdout
, CFSTR("hostname (multicast DNS) = %@\n"), hostname
);
905 localName
= CFStringCreateMutableCopy(NULL
, 0, hostname
);
906 CFStringAppend(localName
, CFSTR(".local"));
907 CFRelease(localName
);
910 if (hostname
!= NULL
) CFRelease(hostname
);
912 update_hostname(store
, NULL
, NULL
);
921 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
923 load_hostname((argc
> 1) ? TRUE
: FALSE
);