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