]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/helper-main.c
mDNSResponder-379.32.1.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / helper-main.c
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2007 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 // We set VERSION_MIN_REQUIRED to 10.4 to avoid "bootstrap_register is deprecated" warnings from bootstrap.h
21 #define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_4
22
23 #include <CoreFoundation/CoreFoundation.h>
24 #include <sys/cdefs.h>
25 #include <sys/time.h>
26 #include <sys/types.h>
27 #include <mach/mach.h>
28 #include <mach/mach_error.h>
29 #include <servers/bootstrap.h>
30 #include <asl.h>
31 #include <launch.h>
32 #include <pwd.h>
33 #include <pthread.h>
34 #include <stdarg.h>
35 #include <stdbool.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <time.h>
39 #include <unistd.h>
40 #include <Security/Security.h>
41 #include "helper.h"
42 #include "helper-server.h"
43 #include "helpermsg.h"
44 #include "helpermsgServer.h"
45 #include <vproc.h>
46
47 #if TARGET_OS_EMBEDDED
48 #include <bootstrap_priv.h>
49 #define NO_SECURITYFRAMEWORK 1
50
51 #define bootstrap_register(A,B,C) bootstrap_register2((A),(B),(C),0)
52 #endif
53
54 #ifndef LAUNCH_JOBKEY_MACHSERVICES
55 #define LAUNCH_JOBKEY_MACHSERVICES "MachServices"
56 #define LAUNCH_DATA_MACHPORT 10
57 #define launch_data_get_machport launch_data_get_fd
58 #endif
59
60 union max_msg_size
61 {
62 union __RequestUnion__proxy_helper_subsystem req;
63 union __ReplyUnion__proxy_helper_subsystem rep;
64 };
65
66 static const mach_msg_size_t MAX_MSG_SIZE = sizeof(union max_msg_size) + MAX_TRAILER_SIZE;
67 static aslclient logclient = NULL;
68 static int opt_debug;
69 static pthread_t idletimer_thread;
70
71 unsigned long maxidle = 15;
72 unsigned long actualidle = 3600;
73
74 CFRunLoopRef gRunLoop = NULL;
75 CFRunLoopTimerRef gTimer = NULL;
76
77 mach_port_t gPort = MACH_PORT_NULL;
78
79 static void helplogv(int level, const char *fmt, va_list ap)
80 {
81 if (NULL == logclient) { vfprintf(stderr, fmt, ap); fflush(stderr); }
82 else asl_vlog(logclient, NULL, level, fmt, ap);
83 }
84
85 void helplog(int level, const char *fmt, ...)
86 {
87 va_list ap;
88 va_start(ap, fmt);
89 helplogv(level, fmt, ap);
90 va_end(ap);
91 }
92
93 // for safe_vproc
94 void LogMsgWithLevel(mDNSLogLevel_t logLevel, const char *fmt, ...)
95 {
96 (void)logLevel;
97 va_list ap;
98 va_start(ap, fmt);
99 // safe_vproc only calls LogMsg, so assume logLevel maps to ASL_LEVEL_ERR
100 helplog(ASL_LEVEL_ERR, fmt, ap);
101 va_end(ap);
102 }
103
104 static void handle_sigterm(int sig)
105 {
106 // debug("entry sig=%d", sig); Can't use syslog from within a signal handler
107 assert(sig == SIGTERM);
108 (void)proxy_mDNSExit(gPort);
109 }
110
111 static void initialize_logging(void)
112 {
113 logclient = asl_open(NULL, kmDNSHelperServiceName, (opt_debug ? ASL_OPT_STDERR : 0));
114 if (NULL == logclient) { fprintf(stderr, "Could not initialize ASL logging.\n"); fflush(stderr); return; }
115 if (opt_debug) asl_set_filter(logclient, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
116 }
117
118 static void initialize_id(void)
119 {
120 static char login[] = "_mdnsresponder";
121 struct passwd hardcode;
122 struct passwd *pwd = &hardcode; // getpwnam(login);
123 hardcode.pw_uid = 65;
124 hardcode.pw_gid = 65;
125
126 if (!pwd) { helplog(ASL_LEVEL_ERR, "Could not find account name `%s'. I will only help root.", login); return; }
127 mDNSResponderUID = pwd->pw_uid;
128 mDNSResponderGID = pwd->pw_gid;
129 }
130
131 static void diediedie(CFRunLoopTimerRef timer, void *context)
132 {
133 debug("entry %p %p %d", timer, context, maxidle);
134 assert(gTimer == timer);
135 if (maxidle)
136 (void)proxy_mDNSExit(gPort);
137 }
138
139 void pause_idle_timer(void)
140 {
141 debug("entry");
142 assert(gTimer);
143 assert(gRunLoop);
144 CFRunLoopRemoveTimer(gRunLoop, gTimer, kCFRunLoopDefaultMode);
145 }
146
147 void unpause_idle_timer(void)
148 {
149 debug("entry");
150 assert(gRunLoop);
151 assert(gTimer);
152 CFRunLoopAddTimer(gRunLoop, gTimer, kCFRunLoopDefaultMode);
153 }
154
155 void update_idle_timer(void)
156 {
157 debug("entry");
158 assert(gTimer);
159 CFRunLoopTimerSetNextFireDate(gTimer, CFAbsoluteTimeGetCurrent() + actualidle);
160 }
161
162 static void *idletimer(void *context)
163 {
164 debug("entry context=%p", context);
165 gRunLoop = CFRunLoopGetCurrent();
166
167 unpause_idle_timer();
168
169 for (;;)
170 {
171 debug("Running CFRunLoop");
172 CFRunLoopRun();
173 sleep(1);
174 }
175
176 return NULL;
177 }
178
179 static int initialize_timer()
180 {
181 gTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + actualidle, actualidle, 0, 0, diediedie, NULL);
182 int err = 0;
183
184 debug("entry");
185 if (0 != (err = pthread_create(&idletimer_thread, NULL, idletimer, NULL)))
186 helplog(ASL_LEVEL_ERR, "Could not start idletimer thread: %s", strerror(err));
187
188 return err;
189 }
190
191 static mach_port_t checkin(char *service_name)
192 {
193 kern_return_t kr = KERN_SUCCESS;
194 mach_port_t port = MACH_PORT_NULL;
195 launch_data_t msg = NULL, reply = NULL, datum = NULL;
196
197 if (NULL == (msg = launch_data_new_string(LAUNCH_KEY_CHECKIN)))
198 { helplog(ASL_LEVEL_ERR, "Could not create checkin message for launchd."); goto fin; }
199 if (NULL == (reply = launch_msg(msg)))
200 { helplog(ASL_LEVEL_ERR, "Could not message launchd."); goto fin; }
201 if (LAUNCH_DATA_ERRNO == launch_data_get_type(reply))
202 {
203 if (launch_data_get_errno(reply) == EACCES) { launch_data_free(msg); launch_data_free(reply); return(MACH_PORT_NULL); }
204 helplog(ASL_LEVEL_ERR, "Launchd checkin failed: %s.", strerror(launch_data_get_errno(reply))); goto fin;
205 }
206 if (NULL == (datum = launch_data_dict_lookup(reply, LAUNCH_JOBKEY_MACHSERVICES)) || LAUNCH_DATA_DICTIONARY != launch_data_get_type(datum))
207 { helplog(ASL_LEVEL_ERR, "Launchd reply does not contain %s dictionary.", LAUNCH_JOBKEY_MACHSERVICES); goto fin; }
208 if (NULL == (datum = launch_data_dict_lookup(datum, service_name)) || LAUNCH_DATA_MACHPORT != launch_data_get_type(datum))
209 { helplog(ASL_LEVEL_ERR, "Launchd reply does not contain %s Mach port.", service_name); goto fin; }
210 if (MACH_PORT_NULL == (port = launch_data_get_machport(datum)))
211 { helplog(ASL_LEVEL_ERR, "Launchd gave me a null Mach port."); goto fin; }
212 if (KERN_SUCCESS != (kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND)))
213 { helplog(ASL_LEVEL_ERR, "mach_port_insert_right: %d %X %s", kr, kr, mach_error_string(kr)); goto fin; }
214
215 fin:
216 if (NULL != msg) launch_data_free(msg);
217 if (NULL != reply) launch_data_free(reply);
218 if (MACH_PORT_NULL == port) exit(EXIT_FAILURE);
219 return port;
220 }
221
222 static mach_port_t register_service(const char *service_name)
223 {
224 mach_port_t port = MACH_PORT_NULL;
225 kern_return_t kr;
226
227 if (KERN_SUCCESS == (kr = bootstrap_check_in(bootstrap_port, (char *)service_name, &port)))
228 {
229 if (KERN_SUCCESS != (kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND)))
230 helplog(ASL_LEVEL_ERR, "mach_port_insert_right: %d %X %s", kr, kr, mach_error_string(kr));
231 else
232 return port;
233 }
234 if (KERN_SUCCESS != (kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port)))
235 { helplog(ASL_LEVEL_ERR, "mach_port_allocate: %d %X %s", kr, kr, mach_error_string(kr)); goto error; }
236 if (KERN_SUCCESS != (kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND)))
237 { helplog(ASL_LEVEL_ERR, "mach_port_insert_right: %d %X %s", kr, kr, mach_error_string(kr)); goto error; }
238
239 // XXX bootstrap_register does not modify its second argument, but the prototype does not include const.
240 if (KERN_SUCCESS != (kr = bootstrap_register(bootstrap_port, (char *)service_name, port)))
241 { helplog(ASL_LEVEL_ERR, "bootstrap_register failed: %s %d %X %s", service_name, kr, kr, mach_error_string(kr)); goto error; }
242
243 return port;
244 error:
245 if (MACH_PORT_NULL != port) mach_port_deallocate(mach_task_self(), port);
246 return MACH_PORT_NULL;
247 }
248
249 int main(int ac, char *av[])
250 {
251 char *p = NULL;
252 kern_return_t kr = KERN_FAILURE;
253 long n;
254 int ch;
255 mach_msg_header_t hdr;
256
257 while ((ch = getopt(ac, av, "dt:")) != -1)
258 switch (ch)
259 {
260 case 'd': opt_debug = 1; break;
261 case 't':
262 n = strtol(optarg, &p, 0);
263 if ('\0' == optarg[0] || '\0' != *p || n > LONG_MAX || n < 0)
264 { fprintf(stderr, "Invalid idle timeout: %s\n", optarg); exit(EXIT_FAILURE); }
265 maxidle = n;
266 break;
267 case '?':
268 default:
269 fprintf(stderr, "Usage: mDNSResponderHelper [-d] [-t maxidle]\n");
270 exit(EXIT_FAILURE);
271 }
272 ac -= optind;
273 av += optind;
274
275 initialize_logging();
276 helplog(ASL_LEVEL_INFO, "Starting");
277 initialize_id();
278
279 #ifndef NO_SECURITYFRAMEWORK
280 // We should normally be running as a system daemon. However, that might not be the case in some scenarios (e.g. debugging).
281 // Explicitly ensure that our Keychain operations utilize the system domain.
282 if (opt_debug) SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
283 #endif
284 gPort = checkin(kmDNSHelperServiceName);
285 if (!gPort)
286 {
287 helplog(ASL_LEVEL_ERR, "Launchd provided no launchdata; will open Mach port explicitly");
288 gPort = register_service(kmDNSHelperServiceName);
289 }
290
291 if (maxidle) actualidle = maxidle;
292
293 signal(SIGTERM, handle_sigterm);
294
295 // We use BeginTransactionAtShutdown in the plist that ensures that we will
296 // receive a SIGTERM during shutdown rather than a SIGKILL. But launchd (due to some
297 // limitation) currently requires us to still start and end the transaction for
298 // its proper initialization.
299 vproc_transaction_t vt = vproc_transaction_begin(NULL);
300 if (vt) vproc_transaction_end(NULL, vt);
301
302 if (initialize_timer()) exit(EXIT_FAILURE);
303 for (n=0; n<100000; n++) if (!gRunLoop) usleep(100);
304 if (!gRunLoop)
305 {
306 helplog(ASL_LEVEL_ERR, "gRunLoop not set after waiting");
307 exit(EXIT_FAILURE);
308 }
309
310 for(;;)
311 {
312 hdr.msgh_bits = 0;
313 hdr.msgh_local_port = gPort;
314 hdr.msgh_remote_port = MACH_PORT_NULL;
315 hdr.msgh_size = sizeof(hdr);
316 hdr.msgh_id = 0;
317 kr = mach_msg(&hdr, MACH_RCV_LARGE | MACH_RCV_MSG, 0, hdr.msgh_size, gPort, 0, 0);
318 if (MACH_RCV_TOO_LARGE != kr)
319 helplog(ASL_LEVEL_ERR, "main MACH_RCV_MSG error: %d %X %s", kr, kr, mach_error_string(kr));
320
321 kr = mach_msg_server_once(helper_server, MAX_MSG_SIZE, gPort,
322 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0));
323 if (KERN_SUCCESS != kr)
324 { helplog(ASL_LEVEL_ERR, "mach_msg_server: %d %X %s", kr, kr, mach_error_string(kr)); exit(EXIT_FAILURE); }
325
326 }
327 exit(EXIT_SUCCESS);
328 }
329
330 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
331 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
332 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
333 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) # s
334 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
335
336 // For convenience when using the "strings" command, this is the last thing in the file
337 // The "@(#) " pattern is a special prefix the "what" command looks for
338 const char VersionString_SCCS[] = "@(#) mDNSResponderHelper " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
339
340 #if _BUILDING_XCODE_PROJECT_
341 // If the process crashes, then this string will be magically included in the automatically-generated crash log
342 const char *__crashreporter_info__ = VersionString_SCCS + 5;
343 asm (".desc ___crashreporter_info__, 0x10");
344 #endif