1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2007-2012 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 #include <CoreFoundation/CoreFoundation.h>
21 #include <sys/cdefs.h>
23 #include <sys/types.h>
24 #include <mach/mach.h>
25 #include <mach/mach_error.h>
26 #include <servers/bootstrap.h>
36 #include <Security/Security.h>
38 #include "helper-server.h"
39 #include <xpc/private.h>
41 #if TARGET_OS_EMBEDDED
42 #define NO_SECURITYFRAMEWORK 1
45 #ifndef LAUNCH_JOBKEY_MACHSERVICES
46 #define LAUNCH_JOBKEY_MACHSERVICES "MachServices"
47 #define LAUNCH_DATA_MACHPORT 10
48 #define launch_data_get_machport launch_data_get_fd
52 int mDNSHelperLogEnabled
= 0;
53 os_log_t log_handle
= NULL
;
55 static dispatch_queue_t xpc_queue
= NULL
;
57 static pthread_t idletimer_thread
;
59 unsigned long maxidle
= 15;
60 unsigned long actualidle
= 3600;
62 CFRunLoopRef gRunLoop
= NULL
;
63 CFRunLoopTimerRef gTimer
= NULL
;
66 static void handle_sigterm(int sig
)
68 // os_log_debug(log_handle,"entry sig=%d", sig); Can't use syslog from within a signal handler
69 assert(sig
== SIGTERM
);
73 static void initialize_logging(void)
75 log_handle
= os_log_create("com.apple.mDNSResponderHelper", "INFO");
79 // OS_LOG_DEFAULT is the default logging object, if you are not creating a custom subsystem/category
80 os_log_error(OS_LOG_DEFAULT
, "Could NOT create log handle in mDNSResponderHelper");
85 static void initialize_id(void)
87 static char login
[] = "_mdnsresponder";
88 struct passwd hardcode
;
89 struct passwd
*pwd
= &hardcode
; // getpwnam(login);
95 os_log(log_handle
, "Could not find account name `%s'. I will only help root.", login
);
98 mDNSResponderUID
= pwd
->pw_uid
;
99 mDNSResponderGID
= pwd
->pw_gid
;
102 static void diediedie(CFRunLoopTimerRef timer
, void *context
)
104 os_log_info(log_handle
, "entry %p %p %lu", timer
, context
, actualidle
);
106 assert(gTimer
== timer
);
107 os_log_info(log_handle
, "mDNSResponderHelper exiting after [%lu] seconds", actualidle
);
113 void pause_idle_timer(void)
115 os_log_debug(log_handle
,"entry");
118 CFRunLoopRemoveTimer(gRunLoop
, gTimer
, kCFRunLoopDefaultMode
);
121 void unpause_idle_timer(void)
123 os_log_debug(log_handle
,"entry");
126 CFRunLoopAddTimer(gRunLoop
, gTimer
, kCFRunLoopDefaultMode
);
129 void update_idle_timer(void)
131 os_log_debug(log_handle
,"entry");
133 CFRunLoopTimerSetNextFireDate(gTimer
, CFAbsoluteTimeGetCurrent() + actualidle
);
136 static void *idletimer(void *context
)
138 os_log_debug(log_handle
,"entry context=%p", context
);
139 gRunLoop
= CFRunLoopGetMain();
141 unpause_idle_timer();
145 // os_log_debug(log_handle,"Running CFRunLoop");
153 static int initialize_timer()
155 gTimer
= CFRunLoopTimerCreate(kCFAllocatorDefault
, CFAbsoluteTimeGetCurrent() + actualidle
, actualidle
, 0, 0, diediedie
, NULL
);
157 os_log_info(log_handle
, "mDNSResponderHelper initialize_timer() started");
159 if (0 != (err
= pthread_create(&idletimer_thread
, NULL
, idletimer
, NULL
)))
160 os_log(log_handle
, "Could not start idletimer thread: %s", strerror(err
));
166 Reads the user's program arguments for mDNSResponderHelper
167 For now we have only one option: mDNSHelperDebugLogging which is used to turn on mDNSResponderHelperLogging
169 To turn ON mDNSResponderHelper Verbose Logging,
170 1] sudo defaults write /Library/Preferences/com.apple.mDNSResponderHelper.plist mDNSHelperDebugLogging -bool YES
173 To turn OFF mDNSResponderHelper Logging,
174 1] sudo defaults delete /Library/Preferences/com.apple.mDNSResponderHelper.plist
176 To view the current options set,
177 1] plutil -p /Library/Preferences/com.apple.mDNSResponderHelper.plist
179 1] sudo defaults read /Library/Preferences/com.apple.mDNSResponderHelper.plist
182 static mDNSBool
HelperPrefsGetValueBool(CFStringRef key
, mDNSBool defaultVal
)
184 CFBooleanRef boolean
;
185 mDNSBool result
= defaultVal
;
187 boolean
= CFPreferencesCopyAppValue(key
, kmDNSHelperProgramArgs
);
190 if (CFGetTypeID(boolean
) == CFBooleanGetTypeID())
191 result
= CFBooleanGetValue(boolean
) ? mDNStrue
: mDNSfalse
;
199 // Verify Client's Entitlement
200 static mDNSBool
check_entitlement(xpc_connection_t conn
, const char *password
)
202 mDNSBool entitled
= mDNSfalse
;
203 xpc_object_t ent
= xpc_connection_copy_entitlement_value(conn
, password
);
207 if (xpc_get_type(ent
) == XPC_TYPE_BOOL
&& xpc_bool_get_value(ent
))
215 os_log(log_handle
, "client entitlement is NULL");
219 os_log(log_handle
, "entitlement check failed -> client is missing entitlement!");
225 static void handle_request(xpc_object_t req
)
227 mDNSu32 helper_mode
= 0;
230 xpc_connection_t remote_conn
= xpc_dictionary_get_remote_connection(req
);
231 xpc_object_t response
= xpc_dictionary_create_reply(req
);
233 // switch here based on dictionary to handle different requests from mDNSResponder
234 if ((xpc_dictionary_get_uint64(req
, kHelperMode
)))
236 os_log_info(log_handle
, "Getting mDNSResponder request mode");
237 helper_mode
= (mDNSu32
)(xpc_dictionary_get_uint64(req
, kHelperMode
));
244 os_log_info(log_handle
, "Calling new RequestBPF()");
251 const char *old_name
;
252 const char *new_name
;
255 pref_key
= (int)(xpc_dictionary_get_uint64(req
, kPrefsNameKey
));
256 old_name
= xpc_dictionary_get_string(req
, kPrefsOldName
);
257 new_name
= xpc_dictionary_get_string(req
, kPrefsNewName
);
259 os_log_info(log_handle
, "Calling new SetName() oldname: %s newname: %s key:%d", old_name
, new_name
, pref_key
);
260 PreferencesSetName(pref_key
, old_name
, new_name
);
264 case p2p_packetfilter
:
267 pfArray_t pfprotocols
;
272 cmd
= xpc_dictionary_get_uint64(req
, "pf_opcode");
273 if_name
= xpc_dictionary_get_string(req
, "pf_ifname");
274 count
= xpc_dictionary_get_uint64(req
, "pf_count");
276 pfports
[0] = (uint16_t)xpc_dictionary_get_uint64(req
, "pf_port0");
277 pfports
[1] = (uint16_t)xpc_dictionary_get_uint64(req
, "pf_port1");
278 pfports
[2] = (uint16_t)xpc_dictionary_get_uint64(req
, "pf_port2");
279 pfports
[3] = (uint16_t)xpc_dictionary_get_uint64(req
, "pf_port3");
281 pfprotocols
[0] = (uint16_t)xpc_dictionary_get_uint64(req
, "pf_protocol0");
282 pfprotocols
[1] = (uint16_t)xpc_dictionary_get_uint64(req
, "pf_protocol1");
283 pfprotocols
[2] = (uint16_t)xpc_dictionary_get_uint64(req
, "pf_protocol2");
284 pfprotocols
[3] = (uint16_t)xpc_dictionary_get_uint64(req
, "pf_protocol3");
286 os_log_info(log_handle
,"Calling new PacketFilterControl()");
287 PacketFilterControl(cmd
, if_name
, count
, pfports
, pfprotocols
);
295 title
= xpc_dictionary_get_string(req
, "notify_title");
296 msg
= xpc_dictionary_get_string(req
, "notify_msg");
298 os_log_info(log_handle
,"Calling new UserNotify() title:%s msg:%s", title
, msg
);
299 UserNotify(title
, msg
);
306 key
= xpc_dictionary_get_uint64(req
, "powerreq_key");
307 interval
= xpc_dictionary_get_uint64(req
, "powerreq_interval");
309 os_log_info(log_handle
,"Calling new PowerRequest() key[%d] interval[%d]", key
, interval
);
310 PowerRequest(key
, interval
, &error_code
);
316 const char *ether_addr
;
321 if_id
= (unsigned int)xpc_dictionary_get_uint64(req
, "interface_index");
322 ether_addr
= xpc_dictionary_get_string(req
, "ethernet_address");
323 ip_addr
= xpc_dictionary_get_string(req
, "ip_address");
324 iteration
= (int)xpc_dictionary_get_uint64(req
, "swp_iteration");
326 os_log_info(log_handle
, "Calling new SendWakeupPacket() ether_addr[%s] ip_addr[%s] if_id[%d] iteration[%d]",
327 ether_addr
, ip_addr
, if_id
, iteration
);
328 SendWakeupPacket(if_id
, ether_addr
, ip_addr
, iteration
);
332 case set_localaddr_cacheentry
:
334 int if_index
, family
;
336 if_index
= xpc_dictionary_get_uint64(req
, "slace_ifindex");
337 family
= xpc_dictionary_get_uint64(req
, "slace_family");
339 const uint8_t* ip
= xpc_dictionary_get_data(req
, "slace_ip", NULL
);
340 const uint8_t* eth
= xpc_dictionary_get_data(req
, "slace_eth", NULL
);
342 os_log_info(log_handle
, "Calling new SetLocalAddressCacheEntry() if_index[%d] family[%d] ", if_index
, family
);
344 SetLocalAddressCacheEntry(if_index
, family
, ip
, eth
, &error_code
);
347 static int v6addr_to_string(const v6addr_t addr, char *buf, size_t buflen)
349 if (NULL == inet_ntop(AF_INET6, addr, buf, buflen))
351 os_log(log_handle, "inet_ntop failed: %s", strerror(errno));
360 ethaddr_t eth = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 } ;
361 const uint8_t* slace_ip = NULL;
364 slace_ip = xpc_dictionary_get_data(req, "slace_ip", &ip_len);
365 if (slace_ip && (ip_len == sizeof(v6addr_t)))
367 os_log(log_handle, "mDNSResponderHelper: doing memcpy()");
368 memcpy(&addr_ipv6, slace_ip, ip_len);
370 char test_ipv6_str[46];
371 v6addr_to_string(addr_ipv6, test_ipv6_str, sizeof(test_ipv6_str));
372 os_log(log_handle, "mDNSResponderHelper: handle_request: set_localaddr_cacheentry: test_ipv6_str is %s", test_ipv6_str);
380 uint16_t lport
, rport
, win
;
383 lport
= xpc_dictionary_get_uint64(req
, "send_keepalive_lport");
384 rport
= xpc_dictionary_get_uint64(req
, "send_keepalive_rport");
385 seq
= xpc_dictionary_get_uint64(req
, "send_keepalive_seq");
386 ack
= xpc_dictionary_get_uint64(req
, "send_keepalive_ack");
387 win
= xpc_dictionary_get_uint64(req
, "send_keepalive_win");
389 const uint8_t* sadd6
= xpc_dictionary_get_data(req
, "send_keepalive_sadd", NULL
);
390 const uint8_t* dadd6
= xpc_dictionary_get_data(req
, "send_keepalive_dadd", NULL
);
392 os_log_info(log_handle
, "helper-main: handle_request: send_keepalive: lport is[%d] rport is[%d] seq is[%d] ack is[%d] win is[%d]",
393 lport
, rport
, seq
, ack
, win
);
395 SendKeepalive(sadd6
, dadd6
, lport
, rport
, seq
, ack
, win
);
399 case retreive_tcpinfo
:
401 uint16_t lport
, rport
;
407 lport
= xpc_dictionary_get_uint64(req
, "retreive_tcpinfo_lport");
408 rport
= xpc_dictionary_get_uint64(req
, "retreive_tcpinfo_rport");
409 family
= xpc_dictionary_get_uint64(req
, "retreive_tcpinfo_family");
411 const uint8_t* laddr
= xpc_dictionary_get_data(req
, "retreive_tcpinfo_laddr", NULL
);
412 const uint8_t* raddr
= xpc_dictionary_get_data(req
, "retreive_tcpinfo_raddr", NULL
);
414 os_log_info(log_handle
, "helper-main: handle_request: retreive_tcpinfo: lport is[%d] rport is[%d] family is [%d]",
415 lport
, rport
, family
);
417 RetrieveTCPInfo(family
, laddr
, lport
, raddr
, rport
, &seq
, &ack
, &win
, &intfid
, &error_code
);
421 xpc_dictionary_set_uint64(response
, "retreive_tcpinfo_seq", seq
);
422 xpc_dictionary_set_uint64(response
, "retreive_tcpinfo_ack", ack
);
423 xpc_dictionary_set_uint64(response
, "retreive_tcpinfo_win", win
);
424 xpc_dictionary_set_uint64(response
, "retreive_tcpinfo_ifid", intfid
);
427 os_log_info(log_handle
, "helper-main: handle_request: retreive_tcpinfo: seq is[%d] ack is[%d] win is [%d] intfid is [%d]",
428 seq
, ack
, win
, intfid
);
433 case autotunnel_setkeys
:
435 uint16_t lport
, rport
;
439 lport
= xpc_dictionary_get_uint64(req
, "autotunnelsetkeys_lport");
440 rport
= xpc_dictionary_get_uint64(req
, "autotunnelsetkeys_rport");
441 replace_del
= xpc_dictionary_get_uint64(req
, "autotunnelsetkeys_repdel");
443 const uint8_t* local_inner
= xpc_dictionary_get_data(req
, "autotunnelsetkeys_localinner", NULL
);
444 const uint8_t* local_outer
= xpc_dictionary_get_data(req
, "autotunnelsetkeys_localouter", NULL
);
445 const uint8_t* remote_inner
= xpc_dictionary_get_data(req
, "autotunnelsetkeys_remoteinner", NULL
);
446 const uint8_t* remote_outer
= xpc_dictionary_get_data(req
, "autotunnelsetkeys_remoteouter", NULL
);
448 fqdnstr
= xpc_dictionary_get_string(req
, "autotunnelsetkeys_fqdnStr");
450 os_log_info(log_handle
, "helper-main: handle_request: autotunnel_setkeys: lport is[%d] rport is[%d] replace_del is [%d]",
451 lport
, rport
, replace_del
);
454 HelperAutoTunnelSetKeys(replace_del
, local_inner
, local_outer
, lport
, remote_inner
, remote_outer
, rport
, fqdnstr
, &error_code
);
460 case keychain_getsecrets
:
462 unsigned int num_sec
= 0;
463 unsigned long secrets
= 0;
464 unsigned int sec_cnt
= 0;
466 os_log_info(log_handle
,"Calling new KeyChainGetSecrets()");
468 KeychainGetSecrets(&num_sec
, &secrets
, &sec_cnt
, &error_code
);
472 xpc_dictionary_set_uint64(response
, "keychain_num_secrets", num_sec
);
473 xpc_dictionary_set_data(response
, "keychain_secrets", (void *)secrets
, sec_cnt
);
474 xpc_dictionary_set_uint64(response
, "keychain_secrets_count", sec_cnt
);
477 os_log_info(log_handle
,"helper-main: handle_request: keychain_getsecrets: num_secrets is %d, secrets is %lu, secrets_Cnt is %d",
478 num_sec
, secrets
, sec_cnt
);
481 vm_deallocate(mach_task_self(), secrets
, sec_cnt
);
488 os_log(log_handle
, "handle_request: Unrecognized mode!");
489 error_code
= kHelperErr_UndefinedMode
;
494 // Return Response Status back to the client (essentially ACKing the request)
497 xpc_dictionary_set_uint64(response
, kHelperReplyStatus
, kHelperReply_ACK
);
498 xpc_dictionary_set_int64(response
, kHelperErrCode
, error_code
);
499 xpc_connection_send_message(remote_conn
, response
);
500 xpc_release(response
);
504 os_log(log_handle
, "handle_requests: Response Dictionary could not be created!");
510 static void accept_client(xpc_connection_t conn
)
512 int c_pid
= xpc_connection_get_pid(conn
);
514 if (!(check_entitlement(conn
, kHelperService
)))
516 os_log(log_handle
, "accept_client: Helper Client PID[%d] is missing Entitlement. Cancelling connection", c_pid
);
517 xpc_connection_cancel(conn
);
522 xpc_connection_set_target_queue(conn
, xpc_queue
);
523 xpc_connection_set_event_handler(conn
, ^(xpc_object_t req_msg
)
525 xpc_type_t type
= xpc_get_type(req_msg
);
527 if (type
== XPC_TYPE_DICTIONARY
)
529 os_log_info(log_handle
,"accept_client:conn:[%p] client[%d](mDNSResponder) requesting service", (void *) conn
, c_pid
);
530 handle_request(req_msg
);
532 else // We hit this case ONLY if Client Terminated Connection OR Crashed
534 os_log(log_handle
, "accept_client:conn:[%p] client[%d](mDNSResponder) teared down the connection (OR Crashed)", (void *) conn
, c_pid
);
535 // handle_termination();
540 xpc_connection_resume(conn
);
544 static void init_helper_service(const char *service_name
)
547 xpc_connection_t xpc_listener
= xpc_connection_create_mach_service(service_name
, NULL
, XPC_CONNECTION_MACH_SERVICE_LISTENER
);
548 if (!xpc_listener
|| xpc_get_type(xpc_listener
) != XPC_TYPE_CONNECTION
)
550 os_log(log_handle
, "init_helper_service: Error Creating XPC Listener for mDNSResponderHelperService !!");
554 os_log_info(log_handle
,"init_helper_service: XPC Listener for mDNSResponderHelperService Listening");
556 xpc_queue
= dispatch_queue_create("com.apple.mDNSHelper.service_queue", NULL
);
558 xpc_connection_set_event_handler(xpc_listener
, ^(xpc_object_t eventmsg
)
560 xpc_type_t type
= xpc_get_type(eventmsg
);
562 if (type
== XPC_TYPE_CONNECTION
)
564 os_log_info(log_handle
,"init_helper_service: new mDNSResponderHelper Client %p", eventmsg
);
565 accept_client(eventmsg
);
567 else if (type
== XPC_TYPE_ERROR
) // Ideally, we would never hit these cases below
569 os_log(log_handle
, "init_helper_service: XPCError: %s", xpc_dictionary_get_string(eventmsg
, XPC_ERROR_KEY_DESCRIPTION
));
574 os_log(log_handle
, "init_helper_service: Unknown EventMsg type");
579 xpc_connection_resume(xpc_listener
);
583 int main(int ac
, char *av
[])
589 while ((ch
= getopt(ac
, av
, "dt:")) != -1)
597 n
= strtol(optarg
, &p
, 0);
598 if ('\0' == optarg
[0] || '\0' != *p
|| n
> LONG_MAX
|| n
< 0)
600 fprintf(stderr
, "Invalid idle timeout: %s\n", optarg
);
607 fprintf(stderr
, "Usage: mDNSResponderHelper [-d] [-t maxidle]\n");
616 initialize_logging();
619 mDNSHelperLogEnabled
= HelperPrefsGetValueBool(kPreferencesKey_mDNSHelperLog
, mDNSHelperLogEnabled
);
621 // Currently on Fuji/Whitetail releases we are keeping the logging always enabled.
622 // Hence mDNSHelperLogEnabled is set to true below by default.
623 mDNSHelperLogEnabled
= 1;
625 os_log_info(log_handle
,"mDNSResponderHelper Starting to run");
627 #ifndef NO_SECURITYFRAMEWORK
628 // We should normally be running as a system daemon. However, that might not be the case in some scenarios (e.g. debugging).
629 // Explicitly ensure that our Keychain operations utilize the system domain.
631 SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem
);
635 actualidle
= maxidle
;
637 signal(SIGTERM
, handle_sigterm
);
639 if (initialize_timer())
641 for (n
=0; n
<100000; n
++)
647 os_log(log_handle
, "gRunLoop not set after waiting");
651 init_helper_service(kHelperService
);
652 os_log_info(log_handle
,"mDNSResponderHelper is now running");
657 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
658 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
659 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
660 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) # s
661 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
663 // For convenience when using the "strings" command, this is the last thing in the file
664 // The "@(#) " pattern is a special prefix the "what" command looks for
665 const char VersionString_SCCS
[] = "@(#) mDNSResponderHelper " STRINGIFY(mDNSResponderVersion
) " (" __DATE__
" " __TIME__
")";
667 #if _BUILDING_XCODE_PROJECT_
668 // If the process crashes, then this string will be magically included in the automatically-generated crash log
669 const char *__crashreporter_info__
= VersionString_SCCS
+ 5;
670 asm (".desc ___crashreporter_info__, 0x10");