]> git.saurik.com Git - apple/configd.git/blob - Plugins/IPMonitor/set-hostname.c
configd-699.1.5.tar.gz
[apple/configd.git] / Plugins / IPMonitor / set-hostname.c
1 /*
2 * Copyright (c) 2004-2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 #include <ctype.h>
24 #include <stdio.h>
25 #include <unistd.h>
26 #include <sys/param.h>
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <sys/time.h>
30 #include <net/if.h>
31 #include <net/if_dl.h>
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
34 #include <netdb_async.h>
35
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>
41
42 #include <notify.h>
43
44 #ifdef MAIN
45 #define my_log(__level, fmt, ...) SCPrint(TRUE, stdout, CFSTR(fmt "\n"), ## __VA_ARGS__)
46 #else // MAIN
47 #include "ip_plugin.h"
48 #endif // MAIN
49
50 static SCDynamicStoreRef store = NULL;
51 static CFRunLoopSourceRef rls = NULL;
52
53 static struct timeval ptrQueryStart;
54 static SCNetworkReachabilityRef ptrTarget = NULL;
55
56 static Boolean _verbose = FALSE;
57
58
59 #define HOSTNAME_NOTIFY_KEY "com.apple.system.hostname"
60
61 CFStringRef copy_dhcp_hostname(CFStringRef serviceID);
62
63 static void
64 set_hostname(CFStringRef hostname)
65 {
66 if (hostname != NULL) {
67 char old_name[MAXHOSTNAMELEN];
68 char new_name[MAXHOSTNAMELEN];
69
70 if (gethostname(old_name, sizeof(old_name)) == -1) {
71 my_log(LOG_ERR, "gethostname() failed: %s", strerror(errno));
72 old_name[0] = '\0';
73 }
74
75 if (_SC_cfstring_to_cstring(hostname,
76 new_name,
77 sizeof(new_name),
78 kCFStringEncodingUTF8) == NULL) {
79 my_log(LOG_ERR, "could not convert [new] hostname");
80 new_name[0] = '\0';
81 }
82
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) {
87 uint32_t status;
88
89 my_log(LOG_NOTICE,
90 "setting hostname to \"%s\"",
91 new_name);
92
93 status = notify_post(HOSTNAME_NOTIFY_KEY);
94 if (status != NOTIFY_STATUS_OK) {
95 my_log(LOG_ERR,
96 "notify_post(" HOSTNAME_NOTIFY_KEY ") failed: error=%u",
97 status);
98 }
99 } else {
100 my_log(LOG_ERR,
101 "sethostname(%s, %ld) failed: %s",
102 new_name,
103 strlen(new_name),
104 strerror(errno));
105 }
106 }
107 }
108
109 return;
110 }
111
112
113 static CFStringRef
114 copy_prefs_hostname(SCDynamicStoreRef store)
115 {
116 CFDictionaryRef dict;
117 CFStringRef key;
118 CFStringRef name = NULL;
119
120 key = SCDynamicStoreKeyCreateComputerName(NULL);
121 dict = SCDynamicStoreCopyValue(store, key);
122 CFRelease(key);
123 if (dict == NULL) {
124 goto done;
125 }
126 if (!isA_CFDictionary(dict)) {
127 goto done;
128 }
129
130 name = isA_CFString(CFDictionaryGetValue(dict, kSCPropSystemHostName));
131 if (name == NULL) {
132 goto done;
133 }
134 CFRetain(name);
135
136 done :
137
138 if (dict != NULL) CFRelease(dict);
139
140 return name;
141 }
142
143
144 static CFStringRef
145 copy_primary_service(SCDynamicStoreRef store)
146 {
147 CFDictionaryRef dict;
148 CFStringRef key;
149 CFStringRef serviceID = NULL;
150
151 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
152 kSCDynamicStoreDomainState,
153 kSCEntNetIPv4);
154 dict = SCDynamicStoreCopyValue(store, key);
155 CFRelease(key);
156
157 if (dict != NULL) {
158 if (isA_CFDictionary(dict)) {
159 serviceID = CFDictionaryGetValue(dict, kSCDynamicStorePropNetPrimaryService);
160 if (isA_CFString(serviceID)) {
161 CFRetain(serviceID);
162 } else {
163 serviceID = NULL;
164 }
165 }
166 CFRelease(dict);
167 }
168
169 return serviceID;
170 }
171
172
173 static CFStringRef
174 copy_primary_ip(SCDynamicStoreRef store, CFStringRef serviceID)
175 {
176 CFDictionaryRef dict;
177 CFStringRef key;
178 CFStringRef address = NULL;
179
180 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
181 kSCDynamicStoreDomainState,
182 serviceID,
183 kSCEntNetIPv4);
184 dict = SCDynamicStoreCopyValue(store, key);
185 CFRelease(key);
186
187 if (dict != NULL) {
188 if (isA_CFDictionary(dict)) {
189 CFArrayRef addresses;
190
191 addresses = CFDictionaryGetValue(dict, kSCPropNetIPv4Addresses);
192 if (isA_CFArray(addresses) && (CFArrayGetCount(addresses) > 0)) {
193 address = CFArrayGetValueAtIndex(addresses, 0);
194 if (isA_CFString(address)) {
195 CFRetain(address);
196 } else {
197 address = NULL;
198 }
199 }
200 }
201 CFRelease(dict);
202 }
203
204 return address;
205 }
206
207
208 static void
209 ptr_query_stop()
210 {
211 if (ptrTarget == NULL) {
212 return;
213 }
214
215 SCNetworkReachabilitySetCallback(ptrTarget, NULL, NULL);
216 SCNetworkReachabilityUnscheduleFromRunLoop(ptrTarget, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
217 CFRelease(ptrTarget);
218 ptrTarget = NULL;
219
220 return;
221 }
222
223
224 static void
225 ptr_query_callback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info)
226 {
227 CFStringRef hostname = NULL;
228 struct timeval ptrQueryComplete;
229 struct timeval ptrQueryElapsed;
230
231 (void) gettimeofday(&ptrQueryComplete, NULL);
232 timersub(&ptrQueryComplete, &ptrQueryStart, &ptrQueryElapsed);
233 if (_verbose) {
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);
238 }
239
240 // use reverse DNS name, if available
241
242 if (flags & kSCNetworkReachabilityFlagsReachable) {
243 int error_num;
244 CFArrayRef hosts;
245
246 /*
247 * if [reverse] DNS query was successful
248 */
249 hosts = SCNetworkReachabilityCopyResolvedAddress(target, &error_num);
250 if (hosts != NULL) {
251 if (CFArrayGetCount(hosts) > 0) {
252
253 hostname = CFArrayGetValueAtIndex(hosts, 0);
254 my_log(LOG_DEBUG, "hostname (reverse DNS query) = %@", hostname);
255 set_hostname(hostname);
256 }
257 CFRelease(hosts);
258
259 if (hostname != NULL) {
260 goto done;
261 }
262 }
263 }
264
265 // get local (multicast DNS) name, if available
266
267 hostname = SCDynamicStoreCopyLocalHostName(store);
268 if (hostname != NULL) {
269 CFMutableStringRef localName;
270
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);
277 CFRelease(hostname);
278 goto done;
279 }
280
281 // use "localhost" if not other name is available
282
283 set_hostname(CFSTR("localhost"));
284
285 done :
286
287 ptr_query_stop();
288
289 #ifdef MAIN
290 CFRunLoopStop(CFRunLoopGetCurrent());
291 #endif // MAIN
292
293 return;
294 }
295
296
297 static Boolean
298 ptr_query_start(CFStringRef address)
299 {
300 union {
301 struct sockaddr sa;
302 struct sockaddr_in sin;
303 struct sockaddr_in6 sin6;
304 } addr;
305 char buf[64];
306 CFDataRef data;
307 CFMutableDictionaryRef options;
308
309 if (_SC_cfstring_to_cstring(address, buf, sizeof(buf), kCFStringEncodingASCII) == NULL) {
310 my_log(LOG_ERR, "could not convert [primary] address string");
311 return FALSE;
312 }
313
314 if (_SC_string_to_sockaddr(buf, AF_UNSPEC, (void *)&addr, sizeof(addr)) == NULL) {
315 my_log(LOG_ERR, "could not convert [primary] address");
316 return FALSE;
317 }
318
319 options = CFDictionaryCreateMutable(NULL,
320 0,
321 &kCFTypeDictionaryKeyCallBacks,
322 &kCFTypeDictionaryValueCallBacks);
323 data = CFDataCreate(NULL, (const UInt8 *)&addr.sa, addr.sa.sa_len);
324 CFDictionarySetValue(options, kSCNetworkReachabilityOptionPTRAddress, data);
325 CFRelease(data);
326 ptrTarget = SCNetworkReachabilityCreateWithOptions(NULL, options);
327 CFRelease(options);
328 if (ptrTarget == NULL) {
329 my_log(LOG_ERR, "could not resolve [primary] address");
330 return FALSE;
331 }
332
333 (void) SCNetworkReachabilitySetCallback(ptrTarget, ptr_query_callback, NULL);
334 (void) SCNetworkReachabilityScheduleWithRunLoop(ptrTarget, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
335
336 return TRUE;
337 }
338
339
340 static void
341 update_hostname(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
342 {
343 CFStringRef address = NULL;
344 CFStringRef hostname = NULL;
345 CFStringRef serviceID = NULL;
346
347 // if active, cancel any in-progress attempt to resolve the primary IP address
348
349 if (ptrTarget != NULL) {
350 ptr_query_stop();
351 }
352
353 // get [prefs] hostname, if available
354
355 hostname = copy_prefs_hostname(store);
356 if (hostname != NULL) {
357 my_log(LOG_DEBUG, "hostname (prefs) = %@", hostname);
358 set_hostname(hostname);
359 goto done;
360 }
361
362 // get primary service ID
363
364 serviceID = copy_primary_service(store);
365 if (serviceID == NULL) {
366 goto mDNS;
367 }
368
369 // get DHCP provided name, if available
370
371 hostname = copy_dhcp_hostname(serviceID);
372 if (hostname != NULL) {
373 my_log(LOG_DEBUG, "hostname (DHCP) = %@", hostname);
374 set_hostname(hostname);
375 goto done;
376 }
377
378 // get DNS name associated with primary IP, if available
379
380 address = copy_primary_ip(store, serviceID);
381 if (address != NULL) {
382 Boolean ok;
383
384 // start reverse DNS query using primary IP address
385 ok = ptr_query_start(address);
386 if (ok) {
387 // if query started
388 goto done;
389 }
390 }
391
392 mDNS :
393
394 // get local (multicast DNS) name, if available
395
396 hostname = SCDynamicStoreCopyLocalHostName(store);
397 if (hostname != NULL) {
398 CFMutableStringRef localName;
399
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);
406 goto done;
407 }
408
409 // use "localhost" if not other name is available
410
411 set_hostname(CFSTR("localhost"));
412
413 done :
414
415 if (address) CFRelease(address);
416 if (hostname) CFRelease(hostname);
417 if (serviceID) CFRelease(serviceID);
418
419 return;
420 }
421
422
423 __private_extern__
424 void
425 load_hostname(Boolean verbose)
426 {
427 CFStringRef key;
428 CFMutableArrayRef keys = NULL;
429 CFMutableArrayRef patterns = NULL;
430
431 if (verbose) {
432 _verbose = TRUE;
433 }
434
435 /* initialize a few globals */
436
437 store = SCDynamicStoreCreate(NULL, CFSTR("set-hostname"), update_hostname, NULL);
438 if (store == NULL) {
439 my_log(LOG_ERR,
440 "SCDynamicStoreCreate() failed: %s",
441 SCErrorString(SCError()));
442 goto error;
443 }
444
445 /* establish notification keys and patterns */
446
447 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
448 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
449
450 /* ...watch for primary service / interface changes */
451 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
452 kSCDynamicStoreDomainState,
453 kSCEntNetIPv4);
454 CFArrayAppendValue(keys, key);
455 CFRelease(key);
456
457 /* ...watch for DNS configuration changes */
458 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
459 kSCDynamicStoreDomainState,
460 kSCEntNetDNS);
461 CFArrayAppendValue(keys, key);
462 CFRelease(key);
463
464 /* ...watch for (per-service) DHCP option changes */
465 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
466 kSCDynamicStoreDomainState,
467 kSCCompAnyRegex,
468 kSCEntNetDHCP);
469 CFArrayAppendValue(patterns, key);
470 CFRelease(key);
471
472 /* ...watch for (BSD) hostname changes */
473 key = SCDynamicStoreKeyCreateComputerName(NULL);
474 CFArrayAppendValue(keys, key);
475 CFRelease(key);
476
477 /* ...watch for local (multicast DNS) hostname changes */
478 key = SCDynamicStoreKeyCreateHostNames(NULL);
479 CFArrayAppendValue(keys, key);
480 CFRelease(key);
481
482 /* register the keys/patterns */
483 if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns)) {
484 my_log(LOG_ERR,
485 "SCDynamicStoreSetNotificationKeys() failed: %s",
486 SCErrorString(SCError()));
487 goto error;
488 }
489
490 rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
491 if (!rls) {
492 my_log(LOG_ERR,
493 "SCDynamicStoreCreateRunLoopSource() failed: %s",
494 SCErrorString(SCError()));
495 goto error;
496 }
497 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
498
499 CFRelease(keys);
500 CFRelease(patterns);
501 return;
502
503 error :
504
505 if (keys != NULL) CFRelease(keys);
506 if (patterns != NULL) CFRelease(patterns);
507 if (store != NULL) CFRelease(store);
508 return;
509 }
510
511
512 #ifdef MAIN
513 int
514 main(int argc, char **argv)
515 {
516
517 #ifdef DEBUG
518
519 _sc_log = FALSE;
520 if ((argc > 1) && (strcmp(argv[1], "-d") == 0)) {
521 _sc_verbose = TRUE;
522 _verbose = TRUE;
523 argv++;
524 argc--;
525 }
526
527 CFStringRef address;
528 CFStringRef hostname;
529 CFStringRef serviceID;
530 SCDynamicStoreRef store;
531
532 store = SCDynamicStoreCreate(NULL, CFSTR("set-hostname"), NULL, NULL);
533 if (store == NULL) {
534 SCPrint(TRUE, stdout,
535 CFSTR("SCDynamicStoreCreate() failed: %s\n"),
536 SCErrorString(SCError()));
537 exit(1);
538 }
539
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);
544 CFRelease(hostname);
545 }
546
547 // get local (multicast DNS) name, if available
548
549 hostname = SCDynamicStoreCopyLocalHostName(store);
550 if (hostname != NULL) {
551 SCPrint(TRUE, stdout, CFSTR("hostname (multicast DNS) = %@\n"), hostname);
552 CFRelease(hostname);
553 }
554
555 // get primary service
556 serviceID = copy_primary_service(store);
557 if (serviceID != NULL) {
558 SCPrint(TRUE, stdout, CFSTR("primary service ID = %@\n"), serviceID);
559 } else {
560 SCPrint(TRUE, stdout, CFSTR("No primary service\n"));
561 }
562
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);
567 }
568
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);
574 CFRelease(hostname);
575 }
576
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);
581
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);
586 }
587
588 // start reverse DNS query using primary IP address
589 (void) ptr_query_start(address);
590 CFRelease(address);
591 }
592
593 CFRelease(serviceID);
594 }
595
596 CFRelease(store);
597
598 CFRunLoopRun();
599
600 #else /* DEBUG */
601
602 _sc_log = FALSE;
603 _sc_verbose = (argc > 1) ? TRUE : FALSE;
604
605 load_hostname((argc > 1) ? TRUE : FALSE);
606 CFRunLoopRun();
607 /* not reached */
608
609 #endif /* DEBUG */
610
611 exit(0);
612 return 0;
613 }
614 #endif /* MAIN */