]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/helper-main.c
mDNSResponder-176.2.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 Change History (most recent first):
18
19 $Log: helper-main.c,v $
20 Revision 1.15 2008/03/13 20:55:16 mcguire
21 <rdar://problem/5769316> fix deprecated warnings/errors
22 Additional cleanup: use a conditional macro instead of lots of #if
23
24 Revision 1.14 2008/03/12 23:02:59 mcguire
25 <rdar://problem/5769316> fix deprecated warnings/errors
26
27 Revision 1.13 2007/09/21 16:13:14 cheshire
28 Additional Tiger compatibility fix: After bootstrap_check_in, we need to give
29 ourselves a Mach "send" right to the port, otherwise our ten-second idle timeout
30 mechanism is not able to send the "mDNSIdleExit" message to itself
31
32 Revision 1.12 2007/09/20 22:26:20 cheshire
33 Add necessary bootstrap_check_in() in Tiger compatibility code (not used on Leopard)
34
35 Revision 1.11 2007/09/18 19:09:02 cheshire
36 <rdar://problem/5489549> mDNSResponderHelper (and other binaries) missing SCCS version strings
37
38 Revision 1.10 2007/09/09 02:21:17 mcguire
39 <rdar://problem/5469345> Leopard Server9A547(Insatll):mDNSResponderHelper crashing
40
41 Revision 1.9 2007/09/07 22:44:03 mcguire
42 <rdar://problem/5448420> Move CFUserNotification code to mDNSResponderHelper
43
44 Revision 1.8 2007/09/07 22:24:36 vazquez
45 <rdar://problem/5466301> Need to stop spewing mDNSResponderHelper logs
46
47 Revision 1.7 2007/08/31 18:09:32 cheshire
48 <rdar://problem/5434050> Restore ability to run mDNSResponder on Tiger
49
50 Revision 1.6 2007/08/31 17:45:13 cheshire
51 Allow maxidle time of zero, meaning "run indefinitely"
52
53 Revision 1.5 2007/08/31 00:09:54 cheshire
54 Deleted extraneous whitespace (shortened code from 260 lines to 160)
55
56 Revision 1.4 2007/08/28 00:33:04 jgraessley
57 <rdar://problem/5423932> Selective compilation options
58
59 Revision 1.3 2007/08/23 23:21:24 cheshire
60 Tiger compatibility: Use old bootstrap_register() instead of Leopard-only bootstrap_register2()
61
62 Revision 1.2 2007/08/23 21:36:17 cheshire
63 Made code layout style consistent with existing project style; added $Log header
64
65 Revision 1.1 2007/08/08 22:34:58 mcguire
66 <rdar://problem/5197869> Security: Run mDNSResponder as user id mdnsresponder instead of root
67 */
68
69 #define _FORTIFY_SOURCE 2
70 #include <CoreFoundation/CoreFoundation.h>
71 #include <sys/cdefs.h>
72 #include <sys/time.h>
73 #include <sys/types.h>
74 #include <mach/mach.h>
75 #include <mach/mach_error.h>
76 #include <servers/bootstrap.h>
77 #include <asl.h>
78 #include <launch.h>
79 #include <pwd.h>
80 #include <pthread.h>
81 #include <stdarg.h>
82 #include <stdbool.h>
83 #include <stdio.h>
84 #include <stdlib.h>
85 #include <time.h>
86 #include <unistd.h>
87 #include <Security/Security.h>
88 #include "helper.h"
89 #include "helper-server.h"
90 #include "helpermsg.h"
91 #include "helpermsgServer.h"
92
93 #if TARGET_OS_EMBEDDED
94 #include <bootstrap_priv.h>
95 #define NO_SECURITYFRAMEWORK 1
96
97 #define bootstrap_register(A,B,C) bootstrap_register2((A),(B),(C),0)
98 #endif
99
100 #ifndef LAUNCH_JOBKEY_MACHSERVICES
101 #define LAUNCH_JOBKEY_MACHSERVICES "MachServices"
102 #define LAUNCH_DATA_MACHPORT 10
103 #define launch_data_get_machport launch_data_get_fd
104 #endif
105
106 union max_msg_size
107 {
108 union __RequestUnion__proxy_helper_subsystem req;
109 union __ReplyUnion__proxy_helper_subsystem rep;
110 };
111
112 static const mach_msg_size_t MAX_MSG_SIZE = sizeof(union max_msg_size) + MAX_TRAILER_SIZE;
113 static aslclient logclient = NULL;
114 static int opt_debug;
115 static pthread_t idletimer_thread;
116
117 unsigned long maxidle = 10;
118 unsigned long actualidle = 3600;
119
120 CFRunLoopRef gRunLoop = NULL;
121 CFRunLoopTimerRef gTimer = NULL;
122
123 static void helplogv(int level, const char *fmt, va_list ap)
124 {
125 if (NULL == logclient) { vfprintf(stderr, fmt, ap); fflush(stderr); }
126 else asl_vlog(logclient, NULL, level, fmt, ap);
127 }
128
129 void helplog(int level, const char *fmt, ...)
130 {
131 va_list ap;
132 va_start(ap, fmt);
133 helplogv(level, fmt, ap);
134 va_end(ap);
135 }
136
137 static void initialize_logging(void)
138 {
139 logclient = asl_open(NULL, kmDNSHelperServiceName, (opt_debug ? ASL_OPT_STDERR : 0));
140 if (NULL == logclient) { fprintf(stderr, "Could not initialize ASL logging.\n"); fflush(stderr); return; }
141 if (opt_debug) asl_set_filter(logclient, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
142 }
143
144 static void initialize_id(void)
145 {
146 static char login[] = "_mdnsresponder";
147 struct passwd *pwd = getpwnam(login);
148
149 if (!pwd) { helplog(ASL_LEVEL_ERR, "Could not find account name `%s'. I will only help root.", login); return; }
150 mDNSResponderUID = pwd->pw_uid;
151 mDNSResponderGID = pwd->pw_gid;
152 }
153
154 static void diediedie(CFRunLoopTimerRef timer, void *context)
155 {
156 debug("entry");
157 assert(gTimer == timer);
158 if (maxidle)
159 (void)proxy_mDNSIdleExit((mach_port_t)context);
160 }
161
162 void pause_idle_timer(void)
163 {
164 debug("entry");
165 assert(gTimer);
166 assert(gRunLoop);
167 CFRunLoopRemoveTimer(gRunLoop, gTimer, kCFRunLoopDefaultMode);
168 }
169
170 void unpause_idle_timer(void)
171 {
172 debug("entry");
173 assert(gRunLoop);
174 assert(gTimer);
175 CFRunLoopAddTimer(gRunLoop, gTimer, kCFRunLoopDefaultMode);
176 }
177
178 void update_idle_timer(void)
179 {
180 debug("entry");
181 assert(gTimer);
182 CFRunLoopTimerSetNextFireDate(gTimer, CFAbsoluteTimeGetCurrent() + actualidle);
183 }
184
185 static void *idletimer(void *context)
186 {
187 debug("entry context=%p", context);
188 gRunLoop = CFRunLoopGetCurrent();
189
190 unpause_idle_timer();
191
192 for (;;)
193 {
194 debug("Running CFRunLoop");
195 CFRunLoopRun();
196 sleep(1);
197 }
198
199 return NULL;
200 }
201
202 static void initialize_timer(mach_port_t port)
203 {
204 CFRunLoopTimerContext cxt = {0, (void *)port, NULL, NULL, NULL};
205 gTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + actualidle, actualidle, 0, 0, diediedie, &cxt);
206 int err = 0;
207
208 debug("entry port=%p", port);
209 if (0 != (err = pthread_create(&idletimer_thread, NULL, idletimer, (void *)port)))
210 helplog(ASL_LEVEL_ERR, "Could not start idletimer thread: %s", strerror(err));
211 }
212
213 static mach_port_t checkin(char *service_name)
214 {
215 kern_return_t kr = KERN_SUCCESS;
216 mach_port_t port = MACH_PORT_NULL;
217 launch_data_t msg = NULL, reply = NULL, datum = NULL;
218
219 if (NULL == (msg = launch_data_new_string(LAUNCH_KEY_CHECKIN)))
220 { helplog(ASL_LEVEL_ERR, "Could not create checkin message for launchd."); goto fin; }
221 if (NULL == (reply = launch_msg(msg)))
222 { helplog(ASL_LEVEL_ERR, "Could not message launchd."); goto fin; }
223 if (LAUNCH_DATA_ERRNO == launch_data_get_type(reply))
224 {
225 if (launch_data_get_errno(reply) == EACCES) { launch_data_free(msg); launch_data_free(reply); return(MACH_PORT_NULL); }
226 helplog(ASL_LEVEL_ERR, "Launchd checkin failed: %s.", strerror(launch_data_get_errno(reply))); goto fin;
227 }
228 if (NULL == (datum = launch_data_dict_lookup(reply, LAUNCH_JOBKEY_MACHSERVICES)) || LAUNCH_DATA_DICTIONARY != launch_data_get_type(datum))
229 { helplog(ASL_LEVEL_ERR, "Launchd reply does not contain %s dictionary.", LAUNCH_JOBKEY_MACHSERVICES); goto fin; }
230 if (NULL == (datum = launch_data_dict_lookup(datum, service_name)) || LAUNCH_DATA_MACHPORT != launch_data_get_type(datum))
231 { helplog(ASL_LEVEL_ERR, "Launchd reply does not contain %s Mach port.", service_name); goto fin; }
232 if (MACH_PORT_NULL == (port = launch_data_get_machport(datum)))
233 { helplog(ASL_LEVEL_ERR, "Launchd gave me a null Mach port."); goto fin; }
234 if (KERN_SUCCESS != (kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND)))
235 { helplog(ASL_LEVEL_ERR, "mach_port_insert_right: %s", mach_error_string(kr)); goto fin; }
236
237 fin:
238 if (NULL != msg) launch_data_free(msg);
239 if (NULL != reply) launch_data_free(reply);
240 if (MACH_PORT_NULL == port) exit(EXIT_FAILURE);
241 return port;
242 }
243
244 static mach_port_t register_service(const char *service_name)
245 {
246 mach_port_t port = MACH_PORT_NULL;
247 kern_return_t kr;
248
249 if (KERN_SUCCESS == (kr = bootstrap_check_in(bootstrap_port, (char *)service_name, &port)))
250 {
251 if (KERN_SUCCESS != (kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND)))
252 helplog(ASL_LEVEL_ERR, "mach_port_insert_right: %s", mach_error_string(kr));
253 else
254 return port;
255 }
256 if (KERN_SUCCESS != (kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port)))
257 { helplog(ASL_LEVEL_ERR, "mach_port_allocate: %s", mach_error_string(kr)); goto error; }
258 if (KERN_SUCCESS != (kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND)))
259 { helplog(ASL_LEVEL_ERR, "mach_port_insert_right: %s", mach_error_string(kr)); goto error; }
260
261 // XXX bootstrap_register does not modify its second argument, but the prototype does not include const.
262 if (KERN_SUCCESS != (kr = bootstrap_register(bootstrap_port, (char *)service_name, port)))
263 { helplog(ASL_LEVEL_ERR, "bootstrap_register failed: %s", mach_error_string(kr)); goto error; }
264
265 return port;
266 error:
267 if (MACH_PORT_NULL != port) mach_port_deallocate(mach_task_self(), port);
268 return MACH_PORT_NULL;
269 }
270
271 int main(int ac, char *av[])
272 {
273 char *p = NULL;
274 kern_return_t kr = KERN_FAILURE;
275 mach_port_t port = MACH_PORT_NULL;
276 long n;
277 int ch;
278
279 while ((ch = getopt(ac, av, "dt:")) != -1)
280 switch (ch)
281 {
282 case 'd': opt_debug = 1; break;
283 case 't':
284 n = strtol(optarg, &p, 0);
285 if ('\0' == optarg[0] || '\0' != *p || n > LONG_MAX || n < 0)
286 { fprintf(stderr, "Invalid idle timeout: %s\n", optarg); exit(EXIT_FAILURE); }
287 maxidle = n;
288 break;
289 case '?':
290 default:
291 fprintf(stderr, "Usage: mDNSResponderHelper [-d] [-t maxidle]\n");
292 exit(EXIT_FAILURE);
293 }
294 ac -= optind;
295 av += optind;
296
297 initialize_logging();
298 helplog(ASL_LEVEL_INFO, "Starting");
299 initialize_id();
300
301 #ifndef NO_SECURITYFRAMEWORK
302 // We should normally be running as a system daemon. However, that might not be the case in some scenarios (e.g. debugging).
303 // Explicitly ensure that our Keychain operations utilize the system domain.
304 SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
305 #endif
306 port = checkin(kmDNSHelperServiceName);
307 if (!port)
308 {
309 helplog(ASL_LEVEL_ERR, "Launchd provided no launchdata; will open Mach port explicitly");
310 port = register_service(kmDNSHelperServiceName);
311 }
312
313 if (maxidle) actualidle = maxidle;
314 initialize_timer(port);
315
316 kr = mach_msg_server(helper_server, MAX_MSG_SIZE, port,
317 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0));
318 if (KERN_SUCCESS != kr)
319 { helplog(ASL_LEVEL_ERR, "mach_msg_server: %s\n", mach_error_string(kr)); exit(EXIT_FAILURE); }
320 exit(EXIT_SUCCESS);
321 }
322
323 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
324 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
325 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
326 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s
327 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
328
329 // For convenience when using the "strings" command, this is the last thing in the file
330 // The "@(#) " pattern is a special prefix the "what" command looks for
331 const char VersionString_SCCS[] = "@(#) mDNSResponderHelper " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
332
333 // If the process crashes, then this string will be magically included in the automatically-generated crash log
334 const char *__crashreporter_info__ = VersionString_SCCS + 5;
335 asm(".desc ___crashreporter_info__, 0x10");