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.
18 #define _FORTIFY_SOURCE 2
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
23 #include <CoreFoundation/CoreFoundation.h>
24 #include <sys/cdefs.h>
26 #include <sys/types.h>
27 #include <mach/mach.h>
28 #include <mach/mach_error.h>
29 #include <servers/bootstrap.h>
40 #include <Security/Security.h>
42 #include "helper-server.h"
43 #include "helpermsg.h"
44 #include "helpermsgServer.h"
47 #if TARGET_OS_EMBEDDED
48 #include <bootstrap_priv.h>
49 #define NO_SECURITYFRAMEWORK 1
51 #define bootstrap_register(A,B,C) bootstrap_register2((A),(B),(C),0)
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
62 union __RequestUnion__proxy_helper_subsystem req
;
63 union __ReplyUnion__proxy_helper_subsystem rep
;
66 static const mach_msg_size_t MAX_MSG_SIZE
= sizeof(union max_msg_size
) + MAX_TRAILER_SIZE
;
67 static aslclient logclient
= NULL
;
69 static pthread_t idletimer_thread
;
71 unsigned long maxidle
= 15;
72 unsigned long actualidle
= 3600;
74 CFRunLoopRef gRunLoop
= NULL
;
75 CFRunLoopTimerRef gTimer
= NULL
;
77 mach_port_t gPort
= MACH_PORT_NULL
;
79 static void helplogv(int level
, const char *fmt
, va_list ap
)
81 if (NULL
== logclient
) { vfprintf(stderr
, fmt
, ap
); fflush(stderr
); }
82 else asl_vlog(logclient
, NULL
, level
, fmt
, ap
);
85 void helplog(int level
, const char *fmt
, ...)
89 helplogv(level
, fmt
, ap
);
94 void LogMsgWithLevel(mDNSLogLevel_t logLevel
, const char *fmt
, ...)
99 // safe_vproc only calls LogMsg, so assume logLevel maps to ASL_LEVEL_ERR
100 helplog(ASL_LEVEL_ERR
, fmt
, ap
);
104 static void handle_sigterm(int sig
)
106 // debug("entry sig=%d", sig); Can't use syslog from within a signal handler
107 assert(sig
== SIGTERM
);
108 (void)proxy_mDNSExit(gPort
);
111 static void initialize_logging(void)
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
));
118 static void initialize_id(void)
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;
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
;
131 static void diediedie(CFRunLoopTimerRef timer
, void *context
)
133 debug("entry %p %p %d", timer
, context
, maxidle
);
134 assert(gTimer
== timer
);
136 (void)proxy_mDNSExit(gPort
);
139 void pause_idle_timer(void)
144 CFRunLoopRemoveTimer(gRunLoop
, gTimer
, kCFRunLoopDefaultMode
);
147 void unpause_idle_timer(void)
152 CFRunLoopAddTimer(gRunLoop
, gTimer
, kCFRunLoopDefaultMode
);
155 void update_idle_timer(void)
159 CFRunLoopTimerSetNextFireDate(gTimer
, CFAbsoluteTimeGetCurrent() + actualidle
);
162 static void *idletimer(void *context
)
164 debug("entry context=%p", context
);
165 gRunLoop
= CFRunLoopGetCurrent();
167 unpause_idle_timer();
171 debug("Running CFRunLoop");
179 static int initialize_timer()
181 gTimer
= CFRunLoopTimerCreate(kCFAllocatorDefault
, CFAbsoluteTimeGetCurrent() + actualidle
, actualidle
, 0, 0, diediedie
, NULL
);
185 if (0 != (err
= pthread_create(&idletimer_thread
, NULL
, idletimer
, NULL
)))
186 helplog(ASL_LEVEL_ERR
, "Could not start idletimer thread: %s", strerror(err
));
191 static mach_port_t
checkin(char *service_name
)
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
;
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
))
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
;
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
; }
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
);
222 static mach_port_t
register_service(const char *service_name
)
224 mach_port_t port
= MACH_PORT_NULL
;
227 if (KERN_SUCCESS
== (kr
= bootstrap_check_in(bootstrap_port
, (char *)service_name
, &port
)))
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
));
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
; }
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
; }
245 if (MACH_PORT_NULL
!= port
) mach_port_deallocate(mach_task_self(), port
);
246 return MACH_PORT_NULL
;
249 int main(int ac
, char *av
[])
252 kern_return_t kr
= KERN_FAILURE
;
255 mach_msg_header_t hdr
;
257 while ((ch
= getopt(ac
, av
, "dt:")) != -1)
260 case 'd': opt_debug
= 1; break;
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
); }
269 fprintf(stderr
, "Usage: mDNSResponderHelper [-d] [-t maxidle]\n");
275 initialize_logging();
276 helplog(ASL_LEVEL_INFO
, "Starting");
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
);
284 gPort
= checkin(kmDNSHelperServiceName
);
287 helplog(ASL_LEVEL_ERR
, "Launchd provided no launchdata; will open Mach port explicitly");
288 gPort
= register_service(kmDNSHelperServiceName
);
291 if (maxidle
) actualidle
= maxidle
;
293 signal(SIGTERM
, handle_sigterm
);
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
);
302 if (initialize_timer()) exit(EXIT_FAILURE
);
303 for (n
=0; n
<100000; n
++) if (!gRunLoop
) usleep(100);
306 helplog(ASL_LEVEL_ERR
, "gRunLoop not set after waiting");
313 hdr
.msgh_local_port
= gPort
;
314 hdr
.msgh_remote_port
= MACH_PORT_NULL
;
315 hdr
.msgh_size
= sizeof(hdr
);
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
));
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
); }
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)
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__
")";
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");