2 * Copyright (c) 2004-2014 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>
45 #define my_log(__level, fmt, ...) SCPrint(TRUE, stdout, CFSTR(fmt "\n"), ## __VA_ARGS__)
47 #include "ip_plugin.h"
50 static SCDynamicStoreRef store
= NULL
;
51 static CFRunLoopSourceRef rls
= NULL
;
53 static struct timeval ptrQueryStart
;
54 static SCNetworkReachabilityRef ptrTarget
= NULL
;
56 static Boolean _verbose
= FALSE
;
59 #define HOSTNAME_NOTIFY_KEY "com.apple.system.hostname"
61 CFStringRef
copy_dhcp_hostname(CFStringRef serviceID
);
64 set_hostname(CFStringRef hostname
)
66 if (hostname
!= NULL
) {
67 char old_name
[MAXHOSTNAMELEN
];
68 char new_name
[MAXHOSTNAMELEN
];
70 if (gethostname(old_name
, sizeof(old_name
)) == -1) {
71 my_log(LOG_ERR
, "gethostname() failed: %s", strerror(errno
));
75 if (_SC_cfstring_to_cstring(hostname
,
78 kCFStringEncodingUTF8
) == NULL
) {
79 my_log(LOG_ERR
, "could not convert [new] hostname");
83 old_name
[sizeof(old_name
)-1] = '\0';
84 new_name
[sizeof(new_name
)-1] = '\0';
85 if (strcmp(old_name
, new_name
) != 0) {
86 if (sethostname(new_name
, (int)strlen(new_name
)) == 0) {
90 "setting hostname to \"%s\"",
93 status
= notify_post(HOSTNAME_NOTIFY_KEY
);
94 if (status
!= NOTIFY_STATUS_OK
) {
96 "notify_post(" HOSTNAME_NOTIFY_KEY
") failed: error=%u",
101 "sethostname(%s, %ld) failed: %s",
114 copy_prefs_hostname(SCDynamicStoreRef store
)
116 CFDictionaryRef dict
;
118 CFStringRef name
= NULL
;
120 key
= SCDynamicStoreKeyCreateComputerName(NULL
);
121 dict
= SCDynamicStoreCopyValue(store
, key
);
126 if (!isA_CFDictionary(dict
)) {
130 name
= isA_CFString(CFDictionaryGetValue(dict
, kSCPropSystemHostName
));
138 if (dict
!= NULL
) CFRelease(dict
);
145 copy_primary_service(SCDynamicStoreRef store
)
147 CFDictionaryRef dict
;
149 CFStringRef serviceID
= NULL
;
151 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
152 kSCDynamicStoreDomainState
,
154 dict
= SCDynamicStoreCopyValue(store
, key
);
158 if (isA_CFDictionary(dict
)) {
159 serviceID
= CFDictionaryGetValue(dict
, kSCDynamicStorePropNetPrimaryService
);
160 if (isA_CFString(serviceID
)) {
174 copy_primary_ip(SCDynamicStoreRef store
, CFStringRef serviceID
)
176 CFDictionaryRef dict
;
178 CFStringRef address
= NULL
;
180 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
181 kSCDynamicStoreDomainState
,
184 dict
= SCDynamicStoreCopyValue(store
, key
);
188 if (isA_CFDictionary(dict
)) {
189 CFArrayRef addresses
;
191 addresses
= CFDictionaryGetValue(dict
, kSCPropNetIPv4Addresses
);
192 if (isA_CFArray(addresses
) && (CFArrayGetCount(addresses
) > 0)) {
193 address
= CFArrayGetValueAtIndex(addresses
, 0);
194 if (isA_CFString(address
)) {
211 if (ptrTarget
== NULL
) {
215 SCNetworkReachabilitySetCallback(ptrTarget
, NULL
, NULL
);
216 SCNetworkReachabilityUnscheduleFromRunLoop(ptrTarget
, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode
);
217 CFRelease(ptrTarget
);
225 ptr_query_callback(SCNetworkReachabilityRef target
, SCNetworkReachabilityFlags flags
, void *info
)
227 CFStringRef hostname
= NULL
;
228 struct timeval ptrQueryComplete
;
229 struct timeval ptrQueryElapsed
;
231 (void) gettimeofday(&ptrQueryComplete
, NULL
);
232 timersub(&ptrQueryComplete
, &ptrQueryStart
, &ptrQueryElapsed
);
234 my_log(LOG_DEBUG
, "ptr query complete%s (query time = %ld.%3.3d)",
235 (flags
& kSCNetworkReachabilityFlagsReachable
) ? "" : ", host not found",
236 ptrQueryElapsed
.tv_sec
,
237 ptrQueryElapsed
.tv_usec
/ 1000);
240 // use reverse DNS name, if available
242 if (flags
& kSCNetworkReachabilityFlagsReachable
) {
247 * if [reverse] DNS query was successful
249 hosts
= SCNetworkReachabilityCopyResolvedAddress(target
, &error_num
);
251 if (CFArrayGetCount(hosts
) > 0) {
253 hostname
= CFArrayGetValueAtIndex(hosts
, 0);
254 my_log(LOG_DEBUG
, "hostname (reverse DNS query) = %@", hostname
);
255 set_hostname(hostname
);
259 if (hostname
!= NULL
) {
265 // get local (multicast DNS) name, if available
267 hostname
= SCDynamicStoreCopyLocalHostName(store
);
268 if (hostname
!= NULL
) {
269 CFMutableStringRef localName
;
271 my_log(LOG_DEBUG
, "hostname (multicast DNS) = %@", hostname
);
272 localName
= CFStringCreateMutableCopy(NULL
, 0, hostname
);
273 assert(localName
!= NULL
);
274 CFStringAppend(localName
, CFSTR(".local"));
275 set_hostname(localName
);
276 CFRelease(localName
);
281 // use "localhost" if not other name is available
283 set_hostname(CFSTR("localhost"));
290 CFRunLoopStop(CFRunLoopGetCurrent());
298 ptr_query_start(CFStringRef address
)
302 struct sockaddr_in sin
;
303 struct sockaddr_in6 sin6
;
307 CFMutableDictionaryRef options
;
309 if (_SC_cfstring_to_cstring(address
, buf
, sizeof(buf
), kCFStringEncodingASCII
) == NULL
) {
310 my_log(LOG_ERR
, "could not convert [primary] address string");
314 if (_SC_string_to_sockaddr(buf
, AF_UNSPEC
, (void *)&addr
, sizeof(addr
)) == NULL
) {
315 my_log(LOG_ERR
, "could not convert [primary] address");
319 options
= CFDictionaryCreateMutable(NULL
,
321 &kCFTypeDictionaryKeyCallBacks
,
322 &kCFTypeDictionaryValueCallBacks
);
323 data
= CFDataCreate(NULL
, (const UInt8
*)&addr
.sa
, addr
.sa
.sa_len
);
324 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionPTRAddress
, data
);
326 ptrTarget
= SCNetworkReachabilityCreateWithOptions(NULL
, options
);
328 if (ptrTarget
== NULL
) {
329 my_log(LOG_ERR
, "could not resolve [primary] address");
333 (void) SCNetworkReachabilitySetCallback(ptrTarget
, ptr_query_callback
, NULL
);
334 (void) SCNetworkReachabilityScheduleWithRunLoop(ptrTarget
, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode
);
341 update_hostname(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *info
)
343 CFStringRef address
= NULL
;
344 CFStringRef hostname
= NULL
;
345 CFStringRef serviceID
= NULL
;
347 // if active, cancel any in-progress attempt to resolve the primary IP address
349 if (ptrTarget
!= NULL
) {
353 // get [prefs] hostname, if available
355 hostname
= copy_prefs_hostname(store
);
356 if (hostname
!= NULL
) {
357 my_log(LOG_DEBUG
, "hostname (prefs) = %@", hostname
);
358 set_hostname(hostname
);
362 // get primary service ID
364 serviceID
= copy_primary_service(store
);
365 if (serviceID
== NULL
) {
369 // get DHCP provided name, if available
371 hostname
= copy_dhcp_hostname(serviceID
);
372 if (hostname
!= NULL
) {
373 my_log(LOG_DEBUG
, "hostname (DHCP) = %@", hostname
);
374 set_hostname(hostname
);
378 // get DNS name associated with primary IP, if available
380 address
= copy_primary_ip(store
, serviceID
);
381 if (address
!= NULL
) {
384 // start reverse DNS query using primary IP address
385 ok
= ptr_query_start(address
);
394 // get local (multicast DNS) name, if available
396 hostname
= SCDynamicStoreCopyLocalHostName(store
);
397 if (hostname
!= NULL
) {
398 CFMutableStringRef localName
;
400 my_log(LOG_DEBUG
, "hostname (multicast DNS) = %@", hostname
);
401 localName
= CFStringCreateMutableCopy(NULL
, 0, hostname
);
402 assert(localName
!= NULL
);
403 CFStringAppend(localName
, CFSTR(".local"));
404 set_hostname(localName
);
405 CFRelease(localName
);
409 // use "localhost" if not other name is available
411 set_hostname(CFSTR("localhost"));
415 if (address
) CFRelease(address
);
416 if (hostname
) CFRelease(hostname
);
417 if (serviceID
) CFRelease(serviceID
);
425 load_hostname(Boolean verbose
)
428 CFMutableArrayRef keys
= NULL
;
429 CFMutableArrayRef patterns
= NULL
;
435 /* initialize a few globals */
437 store
= SCDynamicStoreCreate(NULL
, CFSTR("set-hostname"), update_hostname
, NULL
);
440 "SCDynamicStoreCreate() failed: %s",
441 SCErrorString(SCError()));
445 /* establish notification keys and patterns */
447 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
448 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
450 /* ...watch for primary service / interface changes */
451 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
452 kSCDynamicStoreDomainState
,
454 CFArrayAppendValue(keys
, key
);
457 /* ...watch for DNS configuration changes */
458 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
459 kSCDynamicStoreDomainState
,
461 CFArrayAppendValue(keys
, key
);
464 /* ...watch for (per-service) DHCP option changes */
465 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
466 kSCDynamicStoreDomainState
,
469 CFArrayAppendValue(patterns
, key
);
472 /* ...watch for (BSD) hostname changes */
473 key
= SCDynamicStoreKeyCreateComputerName(NULL
);
474 CFArrayAppendValue(keys
, key
);
477 /* ...watch for local (multicast DNS) hostname changes */
478 key
= SCDynamicStoreKeyCreateHostNames(NULL
);
479 CFArrayAppendValue(keys
, key
);
482 /* register the keys/patterns */
483 if (!SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
)) {
485 "SCDynamicStoreSetNotificationKeys() failed: %s",
486 SCErrorString(SCError()));
490 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
493 "SCDynamicStoreCreateRunLoopSource() failed: %s",
494 SCErrorString(SCError()));
497 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
505 if (keys
!= NULL
) CFRelease(keys
);
506 if (patterns
!= NULL
) CFRelease(patterns
);
507 if (store
!= NULL
) CFRelease(store
);
514 main(int argc
, char **argv
)
520 if ((argc
> 1) && (strcmp(argv
[1], "-d") == 0)) {
528 CFStringRef hostname
;
529 CFStringRef serviceID
;
530 SCDynamicStoreRef store
;
532 store
= SCDynamicStoreCreate(NULL
, CFSTR("set-hostname"), NULL
, NULL
);
534 SCPrint(TRUE
, stdout
,
535 CFSTR("SCDynamicStoreCreate() failed: %s\n"),
536 SCErrorString(SCError()));
540 // get [prefs] hostname, if available
541 hostname
= copy_prefs_hostname(store
);
542 if (hostname
!= NULL
) {
543 SCPrint(TRUE
, stdout
, CFSTR("hostname (prefs) = %@\n"), hostname
);
547 // get local (multicast DNS) name, if available
549 hostname
= SCDynamicStoreCopyLocalHostName(store
);
550 if (hostname
!= NULL
) {
551 SCPrint(TRUE
, stdout
, CFSTR("hostname (multicast DNS) = %@\n"), hostname
);
555 // get primary service
556 serviceID
= copy_primary_service(store
);
557 if (serviceID
!= NULL
) {
558 SCPrint(TRUE
, stdout
, CFSTR("primary service ID = %@\n"), serviceID
);
560 SCPrint(TRUE
, stdout
, CFSTR("No primary service\n"));
563 if ((argc
== (2+1)) && (argv
[1][0] == 's')) {
564 if (serviceID
!= NULL
) CFRelease(serviceID
);
565 serviceID
= CFStringCreateWithCString(NULL
, argv
[2], kCFStringEncodingUTF8
);
566 SCPrint(TRUE
, stdout
, CFSTR("alternate service ID = %@\n"), serviceID
);
569 if (serviceID
!= NULL
) {
570 // get DHCP provided name
571 hostname
= copy_dhcp_hostname(serviceID
);
572 if (hostname
!= NULL
) {
573 SCPrint(TRUE
, stdout
, CFSTR("hostname (DHCP) = %@\n"), hostname
);
577 // get primary IP address
578 address
= copy_primary_ip(store
, serviceID
);
579 if (address
!= NULL
) {
580 SCPrint(TRUE
, stdout
, CFSTR("primary address = %@\n"), address
);
582 if ((argc
== (2+1)) && (argv
[1][0] == 'a')) {
583 if (address
!= NULL
) CFRelease(address
);
584 address
= CFStringCreateWithCString(NULL
, argv
[2], kCFStringEncodingUTF8
);
585 SCPrint(TRUE
, stdout
, CFSTR("alternate primary address = %@\n"), address
);
588 // start reverse DNS query using primary IP address
589 (void) ptr_query_start(address
);
593 CFRelease(serviceID
);
603 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
605 load_hostname((argc
> 1) ? TRUE
: FALSE
);