2 * Copyright (c) 2007-2019 Apple Inc. All rights reserved.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #define _FORTIFY_SOURCE 2
19 #include <CoreFoundation/CoreFoundation.h>
20 #include <sys/cdefs.h>
21 #include <sys/socket.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>
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
:
268 pfArray_t pfprotocols
;
271 xpc_object_t xpc_obj_port_array
;
272 size_t port_array_count
= 0;
273 xpc_object_t xpc_obj_protocol_array
;
274 size_t protocol_array_count
= 0;
276 cmd
= xpc_dictionary_get_uint64(req
, "pf_opcode");
277 if_name
= xpc_dictionary_get_string(req
, "pf_ifname");
278 xpc_obj_port_array
= xpc_dictionary_get_value(req
, "xpc_obj_array_port");
279 if ((void *)xpc_obj_port_array
!= NULL
)
280 port_array_count
= xpc_array_get_count(xpc_obj_port_array
);
281 xpc_obj_protocol_array
= xpc_dictionary_get_value(req
, "xpc_obj_array_protocol");
282 if ((void *)xpc_obj_protocol_array
!= NULL
)
283 protocol_array_count
= xpc_array_get_count(xpc_obj_protocol_array
);
284 if (port_array_count
!= protocol_array_count
)
286 if (port_array_count
> PFPortArraySize
)
288 count
= port_array_count
;
290 for (size_t i
= 0; i
< count
; i
++) {
291 pfports
[i
] = (uint16_t)xpc_array_get_uint64(xpc_obj_port_array
, i
);
292 pfprotocols
[i
] = (uint16_t)xpc_array_get_uint64(xpc_obj_protocol_array
, i
);
295 os_log_info(log_handle
,"Calling new PacketFilterControl()");
296 PacketFilterControl(cmd
, if_name
, count
, pfports
, pfprotocols
);
304 title
= xpc_dictionary_get_string(req
, "notify_title");
305 msg
= xpc_dictionary_get_string(req
, "notify_msg");
307 os_log_info(log_handle
,"Calling new UserNotify() title:%s msg:%s", title
, msg
);
308 UserNotify(title
, msg
);
315 key
= xpc_dictionary_get_uint64(req
, "powerreq_key");
316 interval
= xpc_dictionary_get_uint64(req
, "powerreq_interval");
318 os_log_info(log_handle
,"Calling new PowerRequest() key[%d] interval[%d]", key
, interval
);
319 PowerRequest(key
, interval
, &error_code
);
325 const char *ether_addr
;
330 if_id
= (unsigned int)xpc_dictionary_get_uint64(req
, "interface_index");
331 ether_addr
= xpc_dictionary_get_string(req
, "ethernet_address");
332 ip_addr
= xpc_dictionary_get_string(req
, "ip_address");
333 iteration
= (int)xpc_dictionary_get_uint64(req
, "swp_iteration");
335 os_log_info(log_handle
, "Calling new SendWakeupPacket() ether_addr[%s] ip_addr[%s] if_id[%d] iteration[%d]",
336 ether_addr
, ip_addr
, if_id
, iteration
);
337 SendWakeupPacket(if_id
, ether_addr
, ip_addr
, iteration
);
341 case set_localaddr_cacheentry
:
343 int if_index
, family
;
344 size_t ip_len
, eth_len
;
346 if_index
= xpc_dictionary_get_uint64(req
, "slace_ifindex");
347 family
= xpc_dictionary_get_uint64(req
, "slace_family");
349 const uint8_t * const ip
= (const uint8_t *)xpc_dictionary_get_data(req
, "slace_ip", &ip_len
);
350 if (ip_len
!= sizeof(v6addr_t
))
352 error_code
= kHelperErr_ParamErr
;
356 const uint8_t * const eth
= (const uint8_t *)xpc_dictionary_get_data(req
, "slace_eth", ð_len
);
357 if (eth_len
!= sizeof(ethaddr_t
))
359 error_code
= kHelperErr_ParamErr
;
363 os_log_info(log_handle
, "Calling new SetLocalAddressCacheEntry() if_index[%d] family[%d] ", if_index
, family
);
365 SetLocalAddressCacheEntry(if_index
, family
, ip
, eth
, &error_code
);
371 uint16_t lport
, rport
, win
;
373 size_t sadd6_len
, dadd6_len
;
375 lport
= xpc_dictionary_get_uint64(req
, "send_keepalive_lport");
376 rport
= xpc_dictionary_get_uint64(req
, "send_keepalive_rport");
377 seq
= xpc_dictionary_get_uint64(req
, "send_keepalive_seq");
378 ack
= xpc_dictionary_get_uint64(req
, "send_keepalive_ack");
379 win
= xpc_dictionary_get_uint64(req
, "send_keepalive_win");
381 const uint8_t * const sadd6
= (const uint8_t *)xpc_dictionary_get_data(req
, "send_keepalive_sadd", &sadd6_len
);
382 const uint8_t * const dadd6
= (const uint8_t *)xpc_dictionary_get_data(req
, "send_keepalive_dadd", &dadd6_len
);
383 if ((sadd6_len
!= sizeof(v6addr_t
)) || (dadd6_len
!= sizeof(v6addr_t
)))
385 error_code
= kHelperErr_ParamErr
;
389 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]",
390 lport
, rport
, seq
, ack
, win
);
392 SendKeepalive(sadd6
, dadd6
, lport
, rport
, seq
, ack
, win
);
396 case retreive_tcpinfo
:
398 uint16_t lport
, rport
;
403 size_t laddr_len
, raddr_len
;
405 lport
= xpc_dictionary_get_uint64(req
, "retreive_tcpinfo_lport");
406 rport
= xpc_dictionary_get_uint64(req
, "retreive_tcpinfo_rport");
407 family
= xpc_dictionary_get_uint64(req
, "retreive_tcpinfo_family");
409 const uint8_t * const laddr
= (const uint8_t *)xpc_dictionary_get_data(req
, "retreive_tcpinfo_laddr", &laddr_len
);
410 const uint8_t * const raddr
= (const uint8_t *)xpc_dictionary_get_data(req
, "retreive_tcpinfo_raddr", &raddr_len
);
411 if ((laddr_len
!= sizeof(v6addr_t
)) || (raddr_len
!= sizeof(v6addr_t
)))
413 error_code
= kHelperErr_ParamErr
;
417 os_log_info(log_handle
, "helper-main: handle_request: retreive_tcpinfo: lport is[%d] rport is[%d] family is [%d]",
418 lport
, rport
, family
);
420 RetrieveTCPInfo(family
, laddr
, lport
, raddr
, rport
, &seq
, &ack
, &win
, &intfid
, &error_code
);
424 xpc_dictionary_set_uint64(response
, "retreive_tcpinfo_seq", seq
);
425 xpc_dictionary_set_uint64(response
, "retreive_tcpinfo_ack", ack
);
426 xpc_dictionary_set_uint64(response
, "retreive_tcpinfo_win", win
);
427 xpc_dictionary_set_uint64(response
, "retreive_tcpinfo_ifid", intfid
);
430 os_log_info(log_handle
, "helper-main: handle_request: retreive_tcpinfo: seq is[%d] ack is[%d] win is [%d] intfid is [%d]",
431 seq
, ack
, win
, intfid
);
436 case keychain_getsecrets
:
438 unsigned int num_sec
= 0;
439 unsigned long secrets
= 0;
440 unsigned int sec_cnt
= 0;
442 os_log_info(log_handle
,"Calling new KeyChainGetSecrets()");
444 KeychainGetSecrets(&num_sec
, &secrets
, &sec_cnt
, &error_code
);
448 xpc_dictionary_set_uint64(response
, "keychain_num_secrets", num_sec
);
449 xpc_dictionary_set_data(response
, "keychain_secrets", (void *)secrets
, sec_cnt
);
452 os_log_info(log_handle
,"helper-main: handle_request: keychain_getsecrets: num_secrets is %u, secrets is %lu, secrets_Cnt is %u",
453 num_sec
, secrets
, sec_cnt
);
456 vm_deallocate(mach_task_self(), secrets
, sec_cnt
);
463 os_log(log_handle
, "handle_request: Unrecognized mode!");
464 error_code
= kHelperErr_UndefinedMode
;
469 // Return Response Status back to the client (essentially ACKing the request)
472 xpc_dictionary_set_uint64(response
, kHelperReplyStatus
, kHelperReply_ACK
);
473 xpc_dictionary_set_int64(response
, kHelperErrCode
, error_code
);
474 xpc_connection_send_message(remote_conn
, response
);
475 xpc_release(response
);
479 os_log(log_handle
, "handle_requests: Response Dictionary could not be created!");
485 static void accept_client(xpc_connection_t conn
)
487 int c_pid
= xpc_connection_get_pid(conn
);
489 if (!(check_entitlement(conn
, kHelperService
)))
491 os_log(log_handle
, "accept_client: Helper Client PID[%d] is missing Entitlement. Cancelling connection", c_pid
);
492 xpc_connection_cancel(conn
);
497 xpc_connection_set_target_queue(conn
, xpc_queue
);
498 xpc_connection_set_event_handler(conn
, ^(xpc_object_t req_msg
)
500 xpc_type_t type
= xpc_get_type(req_msg
);
502 if (type
== XPC_TYPE_DICTIONARY
)
504 os_log_info(log_handle
,"accept_client:conn:[%p] client[%d](mDNSResponder) requesting service", (void *) conn
, c_pid
);
505 handle_request(req_msg
);
507 else // We hit this case ONLY if Client Terminated Connection OR Crashed
509 os_log(log_handle
, "accept_client:conn:[%p] client[%d](mDNSResponder) teared down the connection (OR Crashed)", (void *) conn
, c_pid
);
510 // handle_termination();
515 xpc_connection_resume(conn
);
519 static void init_helper_service(const char *service_name
)
522 xpc_connection_t xpc_listener
= xpc_connection_create_mach_service(service_name
, NULL
, XPC_CONNECTION_MACH_SERVICE_LISTENER
);
523 if (!xpc_listener
|| xpc_get_type(xpc_listener
) != XPC_TYPE_CONNECTION
)
525 os_log(log_handle
, "init_helper_service: Error Creating XPC Listener for mDNSResponderHelperService !!");
529 os_log_info(log_handle
,"init_helper_service: XPC Listener for mDNSResponderHelperService Listening");
531 xpc_queue
= dispatch_queue_create("com.apple.mDNSHelper.service_queue", NULL
);
533 xpc_connection_set_event_handler(xpc_listener
, ^(xpc_object_t eventmsg
)
535 xpc_type_t type
= xpc_get_type(eventmsg
);
537 if (type
== XPC_TYPE_CONNECTION
)
539 os_log_info(log_handle
,"init_helper_service: new mDNSResponderHelper Client %p", eventmsg
);
540 accept_client(eventmsg
);
542 else if (type
== XPC_TYPE_ERROR
) // Ideally, we would never hit these cases below
544 os_log(log_handle
, "init_helper_service: XPCError: %s", xpc_dictionary_get_string(eventmsg
, XPC_ERROR_KEY_DESCRIPTION
));
549 os_log(log_handle
, "init_helper_service: Unknown EventMsg type");
554 xpc_connection_resume(xpc_listener
);
558 int main(int ac
, char *av
[])
564 while ((ch
= getopt(ac
, av
, "dt:")) != -1)
572 n
= strtol(optarg
, &p
, 0);
573 if ('\0' == optarg
[0] || '\0' != *p
|| n
> LONG_MAX
|| n
< 0)
575 fprintf(stderr
, "Invalid idle timeout: %s\n", optarg
);
582 fprintf(stderr
, "Usage: mDNSResponderHelper [-d] [-t maxidle]\n");
591 initialize_logging();
594 mDNSHelperLogEnabled
= HelperPrefsGetValueBool(kPreferencesKey_mDNSHelperLog
, mDNSHelperLogEnabled
);
596 // Currently on Fuji/Whitetail releases we are keeping the logging always enabled.
597 // Hence mDNSHelperLogEnabled is set to true below by default.
598 mDNSHelperLogEnabled
= 1;
600 os_log_info(log_handle
,"mDNSResponderHelper Starting to run");
602 #ifndef NO_SECURITYFRAMEWORK
603 // We should normally be running as a system daemon. However, that might not be the case in some scenarios (e.g. debugging).
604 // Explicitly ensure that our Keychain operations utilize the system domain.
606 SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem
);
610 actualidle
= maxidle
;
612 signal(SIGTERM
, handle_sigterm
);
614 if (initialize_timer())
616 for (n
=0; n
<100000; n
++)
622 os_log(log_handle
, "gRunLoop not set after waiting");
626 init_helper_service(kHelperService
);
627 os_log_info(log_handle
,"mDNSResponderHelper is now running");
632 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
633 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
634 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
635 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) # s
636 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
638 // For convenience when using the "strings" command, this is the last thing in the file
639 // The "@(#) " pattern is a special prefix the "what" command looks for
640 const char VersionString_SCCS
[] = "@(#) mDNSResponderHelper " STRINGIFY(mDNSResponderVersion
) " (" __DATE__
" " __TIME__
")";
642 #if _BUILDING_XCODE_PROJECT_
643 // If the process crashes, then this string will be magically included in the automatically-generated crash log
644 const char *__crashreporter_info__
= VersionString_SCCS
+ 5;
645 asm (".desc ___crashreporter_info__, 0x10");