1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2007 Apple Inc. All rights reserved.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 Change History (most recent first):
19 $Log: helper-main.c,v $
20 Revision 1.28 2009/04/11 00:20:08 jessic2
21 <rdar://problem/4426780> Daemon: Should be able to turn on LogOperation dynamically
23 Revision 1.27 2009/03/09 19:00:26 mcguire
24 <rdar://problem/6660098> temporarily don't use getpwnam
26 Revision 1.26 2009/03/05 23:08:12 cheshire
27 <rdar://problem/6648751> mDNSResponderHelper deadlocked — Can't use syslog from within a signal handler
29 Revision 1.25 2009/02/06 03:06:49 mcguire
30 <rdar://problem/5858533> Adopt vproc_transaction API in mDNSResponder
32 Revision 1.24 2009/01/28 17:20:46 mcguire
33 changed incorrect notice level log to debug
35 Revision 1.23 2009/01/28 03:17:19 mcguire
36 <rdar://problem/5858535> helper: Adopt vproc_transaction API
38 Revision 1.22 2008/12/19 01:56:47 mcguire
39 <rdar://problem/6181947> crashes in mDNSResponderHelper
41 Revision 1.21 2008/09/15 23:52:30 cheshire
42 <rdar://problem/6218902> mDNSResponder-177 fails to compile on Linux with .desc pseudo-op
43 Made __crashreporter_info__ symbol conditional, so we only use it for OS X build
45 Revision 1.20 2008/08/13 23:11:35 mcguire
46 <rdar://problem/5858535> handle SIGTERM in mDNSResponderHelper
48 Revision 1.19 2008/08/13 23:04:06 mcguire
49 <rdar://problem/5858535> handle SIGTERM in mDNSResponderHelper
50 Preparation: rename message function, as it will no longer be called only on idle exit
52 Revision 1.18 2008/08/13 22:56:32 mcguire
53 <rdar://problem/5858535> handle SIGTERM in mDNSResponderHelper
54 Preparation: store mach port in global variable so we can write to it from a signal handler
56 Revision 1.17 2008/07/24 01:04:04 mcguire
57 <rdar://problem/6003721> helper spawned every 10s
59 Revision 1.16 2008/07/01 01:40:01 mcguire
60 <rdar://problem/5823010> 64-bit fixes
62 Revision 1.15 2008/03/13 20:55:16 mcguire
63 <rdar://problem/5769316> fix deprecated warnings/errors
64 Additional cleanup: use a conditional macro instead of lots of #if
66 Revision 1.14 2008/03/12 23:02:59 mcguire
67 <rdar://problem/5769316> fix deprecated warnings/errors
69 Revision 1.13 2007/09/21 16:13:14 cheshire
70 Additional Tiger compatibility fix: After bootstrap_check_in, we need to give
71 ourselves a Mach "send" right to the port, otherwise our ten-second idle timeout
72 mechanism is not able to send the "mDNSIdleExit" message to itself
74 Revision 1.12 2007/09/20 22:26:20 cheshire
75 Add necessary bootstrap_check_in() in Tiger compatibility code (not used on Leopard)
77 Revision 1.11 2007/09/18 19:09:02 cheshire
78 <rdar://problem/5489549> mDNSResponderHelper (and other binaries) missing SCCS version strings
80 Revision 1.10 2007/09/09 02:21:17 mcguire
81 <rdar://problem/5469345> Leopard Server9A547(Insatll):mDNSResponderHelper crashing
83 Revision 1.9 2007/09/07 22:44:03 mcguire
84 <rdar://problem/5448420> Move CFUserNotification code to mDNSResponderHelper
86 Revision 1.8 2007/09/07 22:24:36 vazquez
87 <rdar://problem/5466301> Need to stop spewing mDNSResponderHelper logs
89 Revision 1.7 2007/08/31 18:09:32 cheshire
90 <rdar://problem/5434050> Restore ability to run mDNSResponder on Tiger
92 Revision 1.6 2007/08/31 17:45:13 cheshire
93 Allow maxidle time of zero, meaning "run indefinitely"
95 Revision 1.5 2007/08/31 00:09:54 cheshire
96 Deleted extraneous whitespace (shortened code from 260 lines to 160)
98 Revision 1.4 2007/08/28 00:33:04 jgraessley
99 <rdar://problem/5423932> Selective compilation options
101 Revision 1.3 2007/08/23 23:21:24 cheshire
102 Tiger compatibility: Use old bootstrap_register() instead of Leopard-only bootstrap_register2()
104 Revision 1.2 2007/08/23 21:36:17 cheshire
105 Made code layout style consistent with existing project style; added $Log header
107 Revision 1.1 2007/08/08 22:34:58 mcguire
108 <rdar://problem/5197869> Security: Run mDNSResponder as user id mdnsresponder instead of root
111 #define _FORTIFY_SOURCE 2
112 #include <CoreFoundation/CoreFoundation.h>
113 #include <sys/cdefs.h>
114 #include <sys/time.h>
115 #include <sys/types.h>
116 #include <mach/mach.h>
117 #include <mach/mach_error.h>
118 #include <servers/bootstrap.h>
129 #include <Security/Security.h>
131 #include "helper-server.h"
132 #include "helpermsg.h"
133 #include "helpermsgServer.h"
134 #include "safe_vproc.h"
136 #if TARGET_OS_EMBEDDED
137 #include <bootstrap_priv.h>
138 #define NO_SECURITYFRAMEWORK 1
140 #define bootstrap_register(A,B,C) bootstrap_register2((A),(B),(C),0)
143 #ifndef LAUNCH_JOBKEY_MACHSERVICES
144 #define LAUNCH_JOBKEY_MACHSERVICES "MachServices"
145 #define LAUNCH_DATA_MACHPORT 10
146 #define launch_data_get_machport launch_data_get_fd
151 union __RequestUnion__proxy_helper_subsystem req
;
152 union __ReplyUnion__proxy_helper_subsystem rep
;
155 #ifdef VPROC_HAS_TRANSACTIONS
156 typedef struct __transaction_s
158 struct __transaction_s
* next
;
159 vproc_transaction_t vt
;
162 static transaction_t
* transactions
= NULL
;
165 static const mach_msg_size_t MAX_MSG_SIZE
= sizeof(union max_msg_size
) + MAX_TRAILER_SIZE
;
166 static aslclient logclient
= NULL
;
167 static int opt_debug
;
168 static pthread_t idletimer_thread
;
170 unsigned long maxidle
= 15;
171 unsigned long actualidle
= 3600;
173 CFRunLoopRef gRunLoop
= NULL
;
174 CFRunLoopTimerRef gTimer
= NULL
;
176 mach_port_t gPort
= MACH_PORT_NULL
;
178 static void helplogv(int level
, const char *fmt
, va_list ap
)
180 if (NULL
== logclient
) { vfprintf(stderr
, fmt
, ap
); fflush(stderr
); }
181 else asl_vlog(logclient
, NULL
, level
, fmt
, ap
);
184 void helplog(int level
, const char *fmt
, ...)
188 helplogv(level
, fmt
, ap
);
193 void LogMsgWithLevel(mDNSLogLevel_t logLevel
, const char *fmt
, ...)
198 // safe_vproc only calls LogMsg, so assume logLevel maps to ASL_LEVEL_ERR
199 helplog(ASL_LEVEL_ERR
, fmt
, ap
);
203 static void handle_sigterm(int sig
)
205 // debug("entry sig=%d", sig); Can't use syslog from within a signal handler
206 assert(sig
== SIGTERM
);
207 (void)proxy_mDNSExit(gPort
);
210 static void initialize_logging(void)
212 logclient
= asl_open(NULL
, kmDNSHelperServiceName
, (opt_debug
? ASL_OPT_STDERR
: 0));
213 if (NULL
== logclient
) { fprintf(stderr
, "Could not initialize ASL logging.\n"); fflush(stderr
); return; }
214 if (opt_debug
) asl_set_filter(logclient
, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG
));
217 static void initialize_id(void)
219 static char login
[] = "_mdnsresponder";
220 struct passwd hardcode
;
221 struct passwd
*pwd
= &hardcode
; // getpwnam(login);
222 hardcode
.pw_uid
= 65;
223 hardcode
.pw_gid
= 65;
225 if (!pwd
) { helplog(ASL_LEVEL_ERR
, "Could not find account name `%s'. I will only help root.", login
); return; }
226 mDNSResponderUID
= pwd
->pw_uid
;
227 mDNSResponderGID
= pwd
->pw_gid
;
230 static void diediedie(CFRunLoopTimerRef timer
, void *context
)
232 debug("entry %p %p %d", timer
, context
, maxidle
);
233 assert(gTimer
== timer
);
235 (void)proxy_mDNSExit(gPort
);
238 void pause_idle_timer(void)
243 CFRunLoopRemoveTimer(gRunLoop
, gTimer
, kCFRunLoopDefaultMode
);
246 void unpause_idle_timer(void)
251 CFRunLoopAddTimer(gRunLoop
, gTimer
, kCFRunLoopDefaultMode
);
254 void update_idle_timer(void)
258 CFRunLoopTimerSetNextFireDate(gTimer
, CFAbsoluteTimeGetCurrent() + actualidle
);
261 static void *idletimer(void *context
)
263 debug("entry context=%p", context
);
264 gRunLoop
= CFRunLoopGetCurrent();
266 unpause_idle_timer();
270 debug("Running CFRunLoop");
278 static int initialize_timer()
280 gTimer
= CFRunLoopTimerCreate(kCFAllocatorDefault
, CFAbsoluteTimeGetCurrent() + actualidle
, actualidle
, 0, 0, diediedie
, NULL
);
284 if (0 != (err
= pthread_create(&idletimer_thread
, NULL
, idletimer
, NULL
)))
285 helplog(ASL_LEVEL_ERR
, "Could not start idletimer thread: %s", strerror(err
));
290 static mach_port_t
checkin(char *service_name
)
292 kern_return_t kr
= KERN_SUCCESS
;
293 mach_port_t port
= MACH_PORT_NULL
;
294 launch_data_t msg
= NULL
, reply
= NULL
, datum
= NULL
;
296 if (NULL
== (msg
= launch_data_new_string(LAUNCH_KEY_CHECKIN
)))
297 { helplog(ASL_LEVEL_ERR
, "Could not create checkin message for launchd."); goto fin
; }
298 if (NULL
== (reply
= launch_msg(msg
)))
299 { helplog(ASL_LEVEL_ERR
, "Could not message launchd."); goto fin
; }
300 if (LAUNCH_DATA_ERRNO
== launch_data_get_type(reply
))
302 if (launch_data_get_errno(reply
) == EACCES
) { launch_data_free(msg
); launch_data_free(reply
); return(MACH_PORT_NULL
); }
303 helplog(ASL_LEVEL_ERR
, "Launchd checkin failed: %s.", strerror(launch_data_get_errno(reply
))); goto fin
;
305 if (NULL
== (datum
= launch_data_dict_lookup(reply
, LAUNCH_JOBKEY_MACHSERVICES
)) || LAUNCH_DATA_DICTIONARY
!= launch_data_get_type(datum
))
306 { helplog(ASL_LEVEL_ERR
, "Launchd reply does not contain %s dictionary.", LAUNCH_JOBKEY_MACHSERVICES
); goto fin
; }
307 if (NULL
== (datum
= launch_data_dict_lookup(datum
, service_name
)) || LAUNCH_DATA_MACHPORT
!= launch_data_get_type(datum
))
308 { helplog(ASL_LEVEL_ERR
, "Launchd reply does not contain %s Mach port.", service_name
); goto fin
; }
309 if (MACH_PORT_NULL
== (port
= launch_data_get_machport(datum
)))
310 { helplog(ASL_LEVEL_ERR
, "Launchd gave me a null Mach port."); goto fin
; }
311 if (KERN_SUCCESS
!= (kr
= mach_port_insert_right(mach_task_self(), port
, port
, MACH_MSG_TYPE_MAKE_SEND
)))
312 { helplog(ASL_LEVEL_ERR
, "mach_port_insert_right: %s", mach_error_string(kr
)); goto fin
; }
315 if (NULL
!= msg
) launch_data_free(msg
);
316 if (NULL
!= reply
) launch_data_free(reply
);
317 if (MACH_PORT_NULL
== port
) exit(EXIT_FAILURE
);
321 static mach_port_t
register_service(const char *service_name
)
323 mach_port_t port
= MACH_PORT_NULL
;
326 if (KERN_SUCCESS
== (kr
= bootstrap_check_in(bootstrap_port
, (char *)service_name
, &port
)))
328 if (KERN_SUCCESS
!= (kr
= mach_port_insert_right(mach_task_self(), port
, port
, MACH_MSG_TYPE_MAKE_SEND
)))
329 helplog(ASL_LEVEL_ERR
, "mach_port_insert_right: %s", mach_error_string(kr
));
333 if (KERN_SUCCESS
!= (kr
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE
, &port
)))
334 { helplog(ASL_LEVEL_ERR
, "mach_port_allocate: %s", mach_error_string(kr
)); goto error
; }
335 if (KERN_SUCCESS
!= (kr
= mach_port_insert_right(mach_task_self(), port
, port
, MACH_MSG_TYPE_MAKE_SEND
)))
336 { helplog(ASL_LEVEL_ERR
, "mach_port_insert_right: %s", mach_error_string(kr
)); goto error
; }
338 // XXX bootstrap_register does not modify its second argument, but the prototype does not include const.
339 if (KERN_SUCCESS
!= (kr
= bootstrap_register(bootstrap_port
, (char *)service_name
, port
)))
340 { helplog(ASL_LEVEL_ERR
, "bootstrap_register failed: %s", mach_error_string(kr
)); goto error
; }
344 if (MACH_PORT_NULL
!= port
) mach_port_deallocate(mach_task_self(), port
);
345 return MACH_PORT_NULL
;
348 int main(int ac
, char *av
[])
351 kern_return_t kr
= KERN_FAILURE
;
354 mach_msg_header_t hdr
;
356 while ((ch
= getopt(ac
, av
, "dt:")) != -1)
359 case 'd': opt_debug
= 1; break;
361 n
= strtol(optarg
, &p
, 0);
362 if ('\0' == optarg
[0] || '\0' != *p
|| n
> LONG_MAX
|| n
< 0)
363 { fprintf(stderr
, "Invalid idle timeout: %s\n", optarg
); exit(EXIT_FAILURE
); }
368 fprintf(stderr
, "Usage: mDNSResponderHelper [-d] [-t maxidle]\n");
374 initialize_logging();
375 helplog(ASL_LEVEL_INFO
, "Starting");
378 #ifndef NO_SECURITYFRAMEWORK
379 // We should normally be running as a system daemon. However, that might not be the case in some scenarios (e.g. debugging).
380 // Explicitly ensure that our Keychain operations utilize the system domain.
381 SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem
);
383 gPort
= checkin(kmDNSHelperServiceName
);
386 helplog(ASL_LEVEL_ERR
, "Launchd provided no launchdata; will open Mach port explicitly");
387 gPort
= register_service(kmDNSHelperServiceName
);
390 if (maxidle
) actualidle
= maxidle
;
392 signal(SIGTERM
, handle_sigterm
);
394 if (initialize_timer()) exit(EXIT_FAILURE
);
395 for (n
=0; n
<100000; n
++) if (!gRunLoop
) usleep(100);
398 helplog(ASL_LEVEL_ERR
, "gRunLoop not set after waiting");
405 hdr
.msgh_local_port
= gPort
;
406 hdr
.msgh_remote_port
= MACH_PORT_NULL
;
407 hdr
.msgh_size
= sizeof(hdr
);
409 kr
= mach_msg(&hdr
, MACH_RCV_LARGE
| MACH_RCV_MSG
, 0, hdr
.msgh_size
, gPort
, 0, 0);
410 if (MACH_RCV_TOO_LARGE
!= kr
) helplog(ASL_LEVEL_ERR
, "kr: %d: %s", kr
, mach_error_string(kr
));
412 safe_vproc_transaction_begin();
414 kr
= mach_msg_server_once(helper_server
, MAX_MSG_SIZE
, gPort
,
415 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT
) | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0
));
416 if (KERN_SUCCESS
!= kr
)
417 { helplog(ASL_LEVEL_ERR
, "mach_msg_server: %s\n", mach_error_string(kr
)); exit(EXIT_FAILURE
); }
419 safe_vproc_transaction_end();
424 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
425 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
426 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
427 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s
428 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
430 // For convenience when using the "strings" command, this is the last thing in the file
431 // The "@(#) " pattern is a special prefix the "what" command looks for
432 const char VersionString_SCCS
[] = "@(#) mDNSResponderHelper " STRINGIFY(mDNSResponderVersion
) " (" __DATE__
" " __TIME__
")";
434 #if _BUILDING_XCODE_PROJECT_
435 // If the process crashes, then this string will be magically included in the automatically-generated crash log
436 const char *__crashreporter_info__
= VersionString_SCCS
+ 5;
437 asm(".desc ___crashreporter_info__, 0x10");