]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/helper-main.c
mDNSResponder-765.20.4.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / helper-main.c
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2007-2012 Apple Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #define _FORTIFY_SOURCE 2
19
20 #include <CoreFoundation/CoreFoundation.h>
21 #include <sys/cdefs.h>
22 #include <sys/time.h>
23 #include <sys/types.h>
24 #include <mach/mach.h>
25 #include <mach/mach_error.h>
26 #include <servers/bootstrap.h>
27 #include <launch.h>
28 #include <pwd.h>
29 #include <pthread.h>
30 #include <stdarg.h>
31 #include <stdbool.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <time.h>
35 #include <unistd.h>
36 #include <Security/Security.h>
37 #include "helper.h"
38 #include "helper-server.h"
39 #include <xpc/private.h>
40
41 #if TARGET_OS_EMBEDDED
42 #define NO_SECURITYFRAMEWORK 1
43 #endif
44
45 #ifndef LAUNCH_JOBKEY_MACHSERVICES
46 #define LAUNCH_JOBKEY_MACHSERVICES "MachServices"
47 #define LAUNCH_DATA_MACHPORT 10
48 #define launch_data_get_machport launch_data_get_fd
49 #endif
50
51
52 int mDNSHelperLogEnabled = 0;
53 os_log_t log_handle = NULL;
54
55 static dispatch_queue_t xpc_queue = NULL;
56 static int opt_debug;
57 static pthread_t idletimer_thread;
58
59 unsigned long maxidle = 15;
60 unsigned long actualidle = 3600;
61
62 CFRunLoopRef gRunLoop = NULL;
63 CFRunLoopTimerRef gTimer = NULL;
64
65
66 static void handle_sigterm(int sig)
67 {
68 // os_log_debug(log_handle,"entry sig=%d", sig); Can't use syslog from within a signal handler
69 assert(sig == SIGTERM);
70 helper_exit();
71 }
72
73 static void initialize_logging(void)
74 {
75 log_handle = os_log_create("com.apple.mDNSResponderHelper", "INFO");
76
77 if (!log_handle)
78 {
79 // OS_LOG_DEFAULT is the default logging object, if you are not creating a custom subsystem/category
80 os_log_error(OS_LOG_DEFAULT, "Could NOT create log handle in mDNSResponderHelper");
81 }
82
83 }
84
85 static void initialize_id(void)
86 {
87 static char login[] = "_mdnsresponder";
88 struct passwd hardcode;
89 struct passwd *pwd = &hardcode; // getpwnam(login);
90 hardcode.pw_uid = 65;
91 hardcode.pw_gid = 65;
92
93 if (!pwd)
94 {
95 os_log(log_handle, "Could not find account name `%s'. I will only help root.", login);
96 return;
97 }
98 mDNSResponderUID = pwd->pw_uid;
99 mDNSResponderGID = pwd->pw_gid;
100 }
101
102 static void diediedie(CFRunLoopTimerRef timer, void *context)
103 {
104 os_log_info(log_handle, "entry %p %p %lu", timer, context, actualidle);
105
106 assert(gTimer == timer);
107 os_log_info(log_handle, "mDNSResponderHelper exiting after [%lu] seconds", actualidle);
108
109 if (actualidle)
110 helper_exit();
111 }
112
113 void pause_idle_timer(void)
114 {
115 os_log_debug(log_handle,"entry");
116 assert(gTimer);
117 assert(gRunLoop);
118 CFRunLoopRemoveTimer(gRunLoop, gTimer, kCFRunLoopDefaultMode);
119 }
120
121 void unpause_idle_timer(void)
122 {
123 os_log_debug(log_handle,"entry");
124 assert(gRunLoop);
125 assert(gTimer);
126 CFRunLoopAddTimer(gRunLoop, gTimer, kCFRunLoopDefaultMode);
127 }
128
129 void update_idle_timer(void)
130 {
131 os_log_debug(log_handle,"entry");
132 assert(gTimer);
133 CFRunLoopTimerSetNextFireDate(gTimer, CFAbsoluteTimeGetCurrent() + actualidle);
134 }
135
136 static void *idletimer(void *context)
137 {
138 os_log_debug(log_handle,"entry context=%p", context);
139 gRunLoop = CFRunLoopGetMain();
140
141 unpause_idle_timer();
142
143 for (;;)
144 {
145 // os_log_debug(log_handle,"Running CFRunLoop");
146 CFRunLoopRun();
147 sleep(1);
148 }
149
150 return NULL;
151 }
152
153 static int initialize_timer()
154 {
155 gTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + actualidle, actualidle, 0, 0, diediedie, NULL);
156 int err = 0;
157 os_log_info(log_handle, "mDNSResponderHelper initialize_timer() started");
158
159 if (0 != (err = pthread_create(&idletimer_thread, NULL, idletimer, NULL)))
160 os_log(log_handle, "Could not start idletimer thread: %s", strerror(err));
161
162 return err;
163 }
164
165 /*
166 Reads the user's program arguments for mDNSResponderHelper
167 For now we have only one option: mDNSHelperDebugLogging which is used to turn on mDNSResponderHelperLogging
168
169 To turn ON mDNSResponderHelper Verbose Logging,
170 1] sudo defaults write /Library/Preferences/com.apple.mDNSResponderHelper.plist mDNSHelperDebugLogging -bool YES
171 2] sudo reboot
172
173 To turn OFF mDNSResponderHelper Logging,
174 1] sudo defaults delete /Library/Preferences/com.apple.mDNSResponderHelper.plist
175
176 To view the current options set,
177 1] plutil -p /Library/Preferences/com.apple.mDNSResponderHelper.plist
178 OR
179 1] sudo defaults read /Library/Preferences/com.apple.mDNSResponderHelper.plist
180 */
181
182 static mDNSBool HelperPrefsGetValueBool(CFStringRef key, mDNSBool defaultVal)
183 {
184 CFBooleanRef boolean;
185 mDNSBool result = defaultVal;
186
187 boolean = CFPreferencesCopyAppValue(key, kmDNSHelperProgramArgs);
188 if (boolean)
189 {
190 if (CFGetTypeID(boolean) == CFBooleanGetTypeID())
191 result = CFBooleanGetValue(boolean) ? mDNStrue : mDNSfalse;
192 CFRelease(boolean);
193 }
194
195 return result;
196 }
197
198
199 // Verify Client's Entitlement
200 static mDNSBool check_entitlement(xpc_connection_t conn, const char *password)
201 {
202 mDNSBool entitled = mDNSfalse;
203 xpc_object_t ent = xpc_connection_copy_entitlement_value(conn, password);
204
205 if (ent)
206 {
207 if (xpc_get_type(ent) == XPC_TYPE_BOOL && xpc_bool_get_value(ent))
208 {
209 entitled = mDNStrue;
210 }
211 xpc_release(ent);
212 }
213 else
214 {
215 os_log(log_handle, "client entitlement is NULL");
216 }
217
218 if (!entitled)
219 os_log(log_handle, "entitlement check failed -> client is missing entitlement!");
220
221 return entitled;
222 }
223
224
225 static void handle_request(xpc_object_t req)
226 {
227 mDNSu32 helper_mode = 0;
228 int error_code = 0;
229
230 xpc_connection_t remote_conn = xpc_dictionary_get_remote_connection(req);
231 xpc_object_t response = xpc_dictionary_create_reply(req);
232
233 // switch here based on dictionary to handle different requests from mDNSResponder
234 if ((xpc_dictionary_get_uint64(req, kHelperMode)))
235 {
236 os_log_info(log_handle, "Getting mDNSResponder request mode");
237 helper_mode = (mDNSu32)(xpc_dictionary_get_uint64(req, kHelperMode));
238 }
239
240 switch (helper_mode)
241 {
242 case bpf_request:
243 {
244 os_log_info(log_handle, "Calling new RequestBPF()");
245 RequestBPF();
246 break;
247 }
248
249 case set_name:
250 {
251 const char *old_name;
252 const char *new_name;
253 int pref_key = 0;
254
255 pref_key = (int)(xpc_dictionary_get_uint64(req, kPrefsNameKey));
256 old_name = xpc_dictionary_get_string(req, kPrefsOldName);
257 new_name = xpc_dictionary_get_string(req, kPrefsNewName);
258
259 os_log_info(log_handle, "Calling new SetName() oldname: %s newname: %s key:%d", old_name, new_name, pref_key);
260 PreferencesSetName(pref_key, old_name, new_name);
261 break;
262 }
263
264 case p2p_packetfilter:
265 {
266 pfArray_t pfports;
267 pfArray_t pfprotocols;
268 const char *if_name;
269 uint32_t cmd;
270 uint32_t count;
271
272 cmd = xpc_dictionary_get_uint64(req, "pf_opcode");
273 if_name = xpc_dictionary_get_string(req, "pf_ifname");
274 count = xpc_dictionary_get_uint64(req, "pf_count");
275
276 pfports[0] = (uint16_t)xpc_dictionary_get_uint64(req, "pf_port0");
277 pfports[1] = (uint16_t)xpc_dictionary_get_uint64(req, "pf_port1");
278 pfports[2] = (uint16_t)xpc_dictionary_get_uint64(req, "pf_port2");
279 pfports[3] = (uint16_t)xpc_dictionary_get_uint64(req, "pf_port3");
280
281 pfprotocols[0] = (uint16_t)xpc_dictionary_get_uint64(req, "pf_protocol0");
282 pfprotocols[1] = (uint16_t)xpc_dictionary_get_uint64(req, "pf_protocol1");
283 pfprotocols[2] = (uint16_t)xpc_dictionary_get_uint64(req, "pf_protocol2");
284 pfprotocols[3] = (uint16_t)xpc_dictionary_get_uint64(req, "pf_protocol3");
285
286 os_log_info(log_handle,"Calling new PacketFilterControl()");
287 PacketFilterControl(cmd, if_name, count, pfports, pfprotocols);
288 break;
289 }
290
291 case user_notify:
292 {
293 const char *title;
294 const char *msg;
295 title = xpc_dictionary_get_string(req, "notify_title");
296 msg = xpc_dictionary_get_string(req, "notify_msg");
297
298 os_log_info(log_handle,"Calling new UserNotify() title:%s msg:%s", title, msg);
299 UserNotify(title, msg);
300 break;
301 }
302
303 case power_req:
304 {
305 int key, interval;
306 key = xpc_dictionary_get_uint64(req, "powerreq_key");
307 interval = xpc_dictionary_get_uint64(req, "powerreq_interval");
308
309 os_log_info(log_handle,"Calling new PowerRequest() key[%d] interval[%d]", key, interval);
310 PowerRequest(key, interval, &error_code);
311 break;
312 }
313
314 case send_wakepkt:
315 {
316 const char *ether_addr;
317 const char *ip_addr;
318 int iteration;
319 unsigned int if_id;
320
321 if_id = (unsigned int)xpc_dictionary_get_uint64(req, "interface_index");
322 ether_addr = xpc_dictionary_get_string(req, "ethernet_address");
323 ip_addr = xpc_dictionary_get_string(req, "ip_address");
324 iteration = (int)xpc_dictionary_get_uint64(req, "swp_iteration");
325
326 os_log_info(log_handle, "Calling new SendWakeupPacket() ether_addr[%s] ip_addr[%s] if_id[%d] iteration[%d]",
327 ether_addr, ip_addr, if_id, iteration);
328 SendWakeupPacket(if_id, ether_addr, ip_addr, iteration);
329 break;
330 }
331
332 case set_localaddr_cacheentry:
333 {
334 int if_index, family;
335
336 if_index = xpc_dictionary_get_uint64(req, "slace_ifindex");
337 family = xpc_dictionary_get_uint64(req, "slace_family");
338
339 const uint8_t* ip = xpc_dictionary_get_data(req, "slace_ip", NULL);
340 const uint8_t* eth = xpc_dictionary_get_data(req, "slace_eth", NULL);
341
342 os_log_info(log_handle, "Calling new SetLocalAddressCacheEntry() if_index[%d] family[%d] ", if_index, family);
343
344 SetLocalAddressCacheEntry(if_index, family, ip, eth, &error_code);
345
346 /*
347 static int v6addr_to_string(const v6addr_t addr, char *buf, size_t buflen)
348 {
349 if (NULL == inet_ntop(AF_INET6, addr, buf, buflen))
350 {
351 os_log(log_handle, "inet_ntop failed: %s", strerror(errno));
352 return -1;
353 }
354 else
355 {
356 return 0;
357 }
358 }
359
360 ethaddr_t eth = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 } ;
361 const uint8_t* slace_ip = NULL;
362 v6addr_t addr_ipv6;
363 size_t ip_len;
364 slace_ip = xpc_dictionary_get_data(req, "slace_ip", &ip_len);
365 if (slace_ip && (ip_len == sizeof(v6addr_t)))
366 {
367 os_log(log_handle, "mDNSResponderHelper: doing memcpy()");
368 memcpy(&addr_ipv6, slace_ip, ip_len);
369 }
370 char test_ipv6_str[46];
371 v6addr_to_string(addr_ipv6, test_ipv6_str, sizeof(test_ipv6_str));
372 os_log(log_handle, "mDNSResponderHelper: handle_request: set_localaddr_cacheentry: test_ipv6_str is %s", test_ipv6_str);
373 */
374
375 break;
376 }
377
378 case send_keepalive:
379 {
380 uint16_t lport, rport, win;
381 uint32_t seq, ack;
382
383 lport = xpc_dictionary_get_uint64(req, "send_keepalive_lport");
384 rport = xpc_dictionary_get_uint64(req, "send_keepalive_rport");
385 seq = xpc_dictionary_get_uint64(req, "send_keepalive_seq");
386 ack = xpc_dictionary_get_uint64(req, "send_keepalive_ack");
387 win = xpc_dictionary_get_uint64(req, "send_keepalive_win");
388
389 const uint8_t* sadd6 = xpc_dictionary_get_data(req, "send_keepalive_sadd", NULL);
390 const uint8_t* dadd6 = xpc_dictionary_get_data(req, "send_keepalive_dadd", NULL);
391
392 os_log_info(log_handle, "helper-main: handle_request: send_keepalive: lport is[%d] rport is[%d] seq is[%d] ack is[%d] win is[%d]",
393 lport, rport, seq, ack, win);
394
395 SendKeepalive(sadd6, dadd6, lport, rport, seq, ack, win);
396 break;
397 }
398
399 case retreive_tcpinfo:
400 {
401 uint16_t lport, rport;
402 int family;
403 uint32_t seq, ack;
404 uint16_t win;
405 int32_t intfid;
406
407 lport = xpc_dictionary_get_uint64(req, "retreive_tcpinfo_lport");
408 rport = xpc_dictionary_get_uint64(req, "retreive_tcpinfo_rport");
409 family = xpc_dictionary_get_uint64(req, "retreive_tcpinfo_family");
410
411 const uint8_t* laddr = xpc_dictionary_get_data(req, "retreive_tcpinfo_laddr", NULL);
412 const uint8_t* raddr = xpc_dictionary_get_data(req, "retreive_tcpinfo_raddr", NULL);
413
414 os_log_info(log_handle, "helper-main: handle_request: retreive_tcpinfo: lport is[%d] rport is[%d] family is [%d]",
415 lport, rport, family);
416
417 RetrieveTCPInfo(family, laddr, lport, raddr, rport, &seq, &ack, &win, &intfid, &error_code);
418
419 if (response)
420 {
421 xpc_dictionary_set_uint64(response, "retreive_tcpinfo_seq", seq);
422 xpc_dictionary_set_uint64(response, "retreive_tcpinfo_ack", ack);
423 xpc_dictionary_set_uint64(response, "retreive_tcpinfo_win", win);
424 xpc_dictionary_set_uint64(response, "retreive_tcpinfo_ifid", intfid);
425 }
426
427 os_log_info(log_handle, "helper-main: handle_request: retreive_tcpinfo: seq is[%d] ack is[%d] win is [%d] intfid is [%d]",
428 seq, ack, win, intfid);
429
430 break;
431 }
432
433 case autotunnel_setkeys:
434 {
435 uint16_t lport, rport;
436 int replace_del;
437 const char *fqdnstr;
438
439 lport = xpc_dictionary_get_uint64(req, "autotunnelsetkeys_lport");
440 rport = xpc_dictionary_get_uint64(req, "autotunnelsetkeys_rport");
441 replace_del = xpc_dictionary_get_uint64(req, "autotunnelsetkeys_repdel");
442
443 const uint8_t* local_inner = xpc_dictionary_get_data(req, "autotunnelsetkeys_localinner", NULL);
444 const uint8_t* local_outer = xpc_dictionary_get_data(req, "autotunnelsetkeys_localouter", NULL);
445 const uint8_t* remote_inner = xpc_dictionary_get_data(req, "autotunnelsetkeys_remoteinner", NULL);
446 const uint8_t* remote_outer = xpc_dictionary_get_data(req, "autotunnelsetkeys_remoteouter", NULL);
447
448 fqdnstr = xpc_dictionary_get_string(req, "autotunnelsetkeys_fqdnStr");
449
450 os_log_info(log_handle, "helper-main: handle_request: autotunnel_setkeys: lport is[%d] rport is[%d] replace_del is [%d]",
451 lport, rport, replace_del);
452
453
454 HelperAutoTunnelSetKeys(replace_del, local_inner, local_outer, lport, remote_inner, remote_outer, rport, fqdnstr, &error_code);
455
456 break;
457 }
458
459
460 case keychain_getsecrets:
461 {
462 unsigned int num_sec = 0;
463 unsigned long secrets = 0;
464 unsigned int sec_cnt = 0;
465
466 os_log_info(log_handle,"Calling new KeyChainGetSecrets()");
467
468 KeychainGetSecrets(&num_sec, &secrets, &sec_cnt, &error_code);
469
470 if (response)
471 {
472 xpc_dictionary_set_uint64(response, "keychain_num_secrets", num_sec);
473 xpc_dictionary_set_data(response, "keychain_secrets", (void *)secrets, sec_cnt);
474 xpc_dictionary_set_uint64(response, "keychain_secrets_count", sec_cnt);
475 }
476
477 os_log_info(log_handle,"helper-main: handle_request: keychain_getsecrets: num_secrets is %d, secrets is %lu, secrets_Cnt is %d",
478 num_sec, secrets, sec_cnt);
479
480 if (secrets)
481 vm_deallocate(mach_task_self(), secrets, sec_cnt);
482
483 break;
484 }
485
486 default:
487 {
488 os_log(log_handle, "handle_request: Unrecognized mode!");
489 error_code = kHelperErr_UndefinedMode;
490 break;
491 }
492 }
493
494 // Return Response Status back to the client (essentially ACKing the request)
495 if (response)
496 {
497 xpc_dictionary_set_uint64(response, kHelperReplyStatus, kHelperReply_ACK);
498 xpc_dictionary_set_int64(response, kHelperErrCode, error_code);
499 xpc_connection_send_message(remote_conn, response);
500 xpc_release(response);
501 }
502 else
503 {
504 os_log(log_handle, "handle_requests: Response Dictionary could not be created!");
505 return;
506 }
507
508 }
509
510 static void accept_client(xpc_connection_t conn)
511 {
512 int c_pid = xpc_connection_get_pid(conn);
513
514 if (!(check_entitlement(conn, kHelperService)))
515 {
516 os_log(log_handle, "accept_client: Helper Client PID[%d] is missing Entitlement. Cancelling connection", c_pid);
517 xpc_connection_cancel(conn);
518 return;
519 }
520
521 xpc_retain(conn);
522 xpc_connection_set_target_queue(conn, xpc_queue);
523 xpc_connection_set_event_handler(conn, ^(xpc_object_t req_msg)
524 {
525 xpc_type_t type = xpc_get_type(req_msg);
526
527 if (type == XPC_TYPE_DICTIONARY)
528 {
529 os_log_info(log_handle,"accept_client:conn:[%p] client[%d](mDNSResponder) requesting service", (void *) conn, c_pid);
530 handle_request(req_msg);
531 }
532 else // We hit this case ONLY if Client Terminated Connection OR Crashed
533 {
534 os_log(log_handle, "accept_client:conn:[%p] client[%d](mDNSResponder) teared down the connection (OR Crashed)", (void *) conn, c_pid);
535 // handle_termination();
536 xpc_release(conn);
537 }
538 });
539
540 xpc_connection_resume(conn);
541 }
542
543
544 static void init_helper_service(const char *service_name)
545 {
546
547 xpc_connection_t xpc_listener = xpc_connection_create_mach_service(service_name, NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER);
548 if (!xpc_listener || xpc_get_type(xpc_listener) != XPC_TYPE_CONNECTION)
549 {
550 os_log(log_handle, "init_helper_service: Error Creating XPC Listener for mDNSResponderHelperService !!");
551 return;
552 }
553
554 os_log_info(log_handle,"init_helper_service: XPC Listener for mDNSResponderHelperService Listening");
555
556 xpc_queue = dispatch_queue_create("com.apple.mDNSHelper.service_queue", NULL);
557
558 xpc_connection_set_event_handler(xpc_listener, ^(xpc_object_t eventmsg)
559 {
560 xpc_type_t type = xpc_get_type(eventmsg);
561
562 if (type == XPC_TYPE_CONNECTION)
563 {
564 os_log_info(log_handle,"init_helper_service: new mDNSResponderHelper Client %p", eventmsg);
565 accept_client(eventmsg);
566 }
567 else if (type == XPC_TYPE_ERROR) // Ideally, we would never hit these cases below
568 {
569 os_log(log_handle, "init_helper_service: XPCError: %s", xpc_dictionary_get_string(eventmsg, XPC_ERROR_KEY_DESCRIPTION));
570 return;
571 }
572 else
573 {
574 os_log(log_handle, "init_helper_service: Unknown EventMsg type");
575 return;
576 }
577 });
578
579 xpc_connection_resume(xpc_listener);
580 }
581
582
583 int main(int ac, char *av[])
584 {
585 char *p = NULL;
586 long n;
587 int ch;
588
589 while ((ch = getopt(ac, av, "dt:")) != -1)
590 {
591 switch (ch)
592 {
593 case 'd':
594 opt_debug = 1;
595 break;
596 case 't':
597 n = strtol(optarg, &p, 0);
598 if ('\0' == optarg[0] || '\0' != *p || n > LONG_MAX || n < 0)
599 {
600 fprintf(stderr, "Invalid idle timeout: %s\n", optarg);
601 exit(EXIT_FAILURE);
602 }
603 maxidle = n;
604 break;
605 case '?':
606 default:
607 fprintf(stderr, "Usage: mDNSResponderHelper [-d] [-t maxidle]\n");
608 exit(EXIT_FAILURE);
609 }
610 }
611 ac -= optind;
612 av += optind;
613 (void)ac; // Unused
614 (void)av; // Unused
615
616 initialize_logging();
617 initialize_id();
618
619 mDNSHelperLogEnabled = HelperPrefsGetValueBool(kPreferencesKey_mDNSHelperLog, mDNSHelperLogEnabled);
620
621 // Currently on Fuji/Whitetail releases we are keeping the logging always enabled.
622 // Hence mDNSHelperLogEnabled is set to true below by default.
623 mDNSHelperLogEnabled = 1;
624
625 os_log_info(log_handle,"mDNSResponderHelper Starting to run");
626
627 #ifndef NO_SECURITYFRAMEWORK
628 // We should normally be running as a system daemon. However, that might not be the case in some scenarios (e.g. debugging).
629 // Explicitly ensure that our Keychain operations utilize the system domain.
630 if (opt_debug)
631 SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
632 #endif
633
634 if (maxidle)
635 actualidle = maxidle;
636
637 signal(SIGTERM, handle_sigterm);
638
639 if (initialize_timer())
640 exit(EXIT_FAILURE);
641 for (n=0; n<100000; n++)
642 if (!gRunLoop)
643 usleep(100);
644
645 if (!gRunLoop)
646 {
647 os_log(log_handle, "gRunLoop not set after waiting");
648 exit(EXIT_FAILURE);
649 }
650
651 init_helper_service(kHelperService);
652 os_log_info(log_handle,"mDNSResponderHelper is now running");
653 dispatch_main();
654
655 }
656
657 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
658 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
659 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
660 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) # s
661 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
662
663 // For convenience when using the "strings" command, this is the last thing in the file
664 // The "@(#) " pattern is a special prefix the "what" command looks for
665 const char VersionString_SCCS[] = "@(#) mDNSResponderHelper " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
666
667 #if _BUILDING_XCODE_PROJECT_
668 // If the process crashes, then this string will be magically included in the automatically-generated crash log
669 const char *__crashreporter_info__ = VersionString_SCCS + 5;
670 asm (".desc ___crashreporter_info__, 0x10");
671 #endif