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 #include <CoreFoundation/CoreFoundation.h>
18 #include <sys/cdefs.h>
19 #include <sys/socket.h>
21 #include <sys/types.h>
22 #include <mach/mach.h>
23 #include <mach/mach_error.h>
24 #include <servers/bootstrap.h>
34 #include <Security/Security.h>
36 #include "helper-server.h"
37 #include <xpc/private.h>
40 #define NO_SECURITYFRAMEWORK 1
43 #ifndef LAUNCH_JOBKEY_MACHSERVICES
44 #define LAUNCH_JOBKEY_MACHSERVICES "MachServices"
45 #define LAUNCH_DATA_MACHPORT 10
46 #define launch_data_get_machport launch_data_get_fd
50 int mDNSHelperLogEnabled
= 0;
51 os_log_t log_handle
= NULL
;
53 static dispatch_queue_t xpc_queue
= NULL
;
55 static pthread_t idletimer_thread
;
57 unsigned long maxidle
= 15;
58 unsigned long actualidle
= 3600;
60 CFRunLoopRef gRunLoop
= NULL
;
61 CFRunLoopTimerRef gTimer
= NULL
;
64 static void handle_sigterm(int sig
)
66 // os_log_debug(log_handle,"entry sig=%d", sig); Can't use syslog from within a signal handler
67 assert(sig
== SIGTERM
);
71 static void initialize_logging(void)
73 log_handle
= os_log_create("com.apple.mDNSResponderHelper", "INFO");
77 // OS_LOG_DEFAULT is the default logging object, if you are not creating a custom subsystem/category
78 os_log_error(OS_LOG_DEFAULT
, "Could NOT create log handle in mDNSResponderHelper");
83 static void initialize_id(void)
85 static char login
[] = "_mdnsresponder";
86 struct passwd hardcode
;
87 struct passwd
*pwd
= &hardcode
; // getpwnam(login);
93 os_log(log_handle
, "Could not find account name `%s'. I will only help root.", login
);
96 mDNSResponderUID
= pwd
->pw_uid
;
97 mDNSResponderGID
= pwd
->pw_gid
;
100 static void diediedie(CFRunLoopTimerRef timer
, void *context
)
102 os_log_info(log_handle
, "entry %p %p %lu", timer
, context
, actualidle
);
104 assert(gTimer
== timer
);
105 os_log_info(log_handle
, "mDNSResponderHelper exiting after [%lu] seconds", actualidle
);
111 void pause_idle_timer(void)
113 os_log_debug(log_handle
,"entry");
116 CFRunLoopRemoveTimer(gRunLoop
, gTimer
, kCFRunLoopDefaultMode
);
119 void unpause_idle_timer(void)
121 os_log_debug(log_handle
,"entry");
124 CFRunLoopAddTimer(gRunLoop
, gTimer
, kCFRunLoopDefaultMode
);
127 void update_idle_timer(void)
129 os_log_debug(log_handle
,"entry");
131 CFRunLoopTimerSetNextFireDate(gTimer
, CFAbsoluteTimeGetCurrent() + actualidle
);
134 static void *idletimer(void *context
)
136 os_log_debug(log_handle
,"entry context=%p", context
);
137 gRunLoop
= CFRunLoopGetMain();
139 unpause_idle_timer();
143 // os_log_debug(log_handle,"Running CFRunLoop");
151 static int initialize_timer()
153 gTimer
= CFRunLoopTimerCreate(kCFAllocatorDefault
, CFAbsoluteTimeGetCurrent() + actualidle
, actualidle
, 0, 0, diediedie
, NULL
);
155 os_log_info(log_handle
, "mDNSResponderHelper initialize_timer() started");
157 if (0 != (err
= pthread_create(&idletimer_thread
, NULL
, idletimer
, NULL
)))
158 os_log(log_handle
, "Could not start idletimer thread: %s", strerror(err
));
164 Reads the user's program arguments for mDNSResponderHelper
165 For now we have only one option: mDNSHelperDebugLogging which is used to turn on mDNSResponderHelperLogging
167 To turn ON mDNSResponderHelper Verbose Logging,
168 1] sudo defaults write /Library/Preferences/com.apple.mDNSResponderHelper.plist mDNSHelperDebugLogging -bool YES
171 To turn OFF mDNSResponderHelper Logging,
172 1] sudo defaults delete /Library/Preferences/com.apple.mDNSResponderHelper.plist
174 To view the current options set,
175 1] plutil -p /Library/Preferences/com.apple.mDNSResponderHelper.plist
177 1] sudo defaults read /Library/Preferences/com.apple.mDNSResponderHelper.plist
180 static mDNSBool
HelperPrefsGetValueBool(CFStringRef key
, mDNSBool defaultVal
)
182 CFBooleanRef boolean
;
183 mDNSBool result
= defaultVal
;
185 boolean
= CFPreferencesCopyAppValue(key
, kmDNSHelperProgramArgs
);
188 if (CFGetTypeID(boolean
) == CFBooleanGetTypeID())
189 result
= CFBooleanGetValue(boolean
) ? mDNStrue
: mDNSfalse
;
197 // Verify Client's Entitlement
198 static mDNSBool
check_entitlement(xpc_connection_t conn
, const char *password
)
200 mDNSBool entitled
= mDNSfalse
;
201 xpc_object_t ent
= xpc_connection_copy_entitlement_value(conn
, password
);
205 if (xpc_get_type(ent
) == XPC_TYPE_BOOL
&& xpc_bool_get_value(ent
))
213 os_log(log_handle
, "client entitlement is NULL");
217 os_log(log_handle
, "entitlement check failed -> client is missing entitlement!");
223 static void handle_request(xpc_object_t req
)
225 mDNSu32 helper_mode
= 0;
228 xpc_connection_t remote_conn
= xpc_dictionary_get_remote_connection(req
);
229 xpc_object_t response
= xpc_dictionary_create_reply(req
);
231 // switch here based on dictionary to handle different requests from mDNSResponder
232 if ((xpc_dictionary_get_uint64(req
, kHelperMode
)))
234 os_log_info(log_handle
, "Getting mDNSResponder request mode");
235 helper_mode
= (mDNSu32
)(xpc_dictionary_get_uint64(req
, kHelperMode
));
242 os_log_info(log_handle
, "Calling new RequestBPF()");
249 const char *old_name
;
250 const char *new_name
;
253 pref_key
= (int)(xpc_dictionary_get_uint64(req
, kPrefsNameKey
));
254 old_name
= xpc_dictionary_get_string(req
, kPrefsOldName
);
255 new_name
= xpc_dictionary_get_string(req
, kPrefsNewName
);
257 os_log_info(log_handle
, "Calling new SetName() oldname: %s newname: %s key:%d", old_name
, new_name
, pref_key
);
258 PreferencesSetName(pref_key
, old_name
, new_name
);
262 case p2p_packetfilter
:
266 pfArray_t pfprotocols
;
269 xpc_object_t xpc_obj_port_array
;
270 size_t port_array_count
= 0;
271 xpc_object_t xpc_obj_protocol_array
;
272 size_t protocol_array_count
= 0;
274 cmd
= xpc_dictionary_get_uint64(req
, "pf_opcode");
275 if_name
= xpc_dictionary_get_string(req
, "pf_ifname");
276 xpc_obj_port_array
= xpc_dictionary_get_value(req
, "xpc_obj_array_port");
277 if ((void *)xpc_obj_port_array
!= NULL
)
278 port_array_count
= xpc_array_get_count(xpc_obj_port_array
);
279 xpc_obj_protocol_array
= xpc_dictionary_get_value(req
, "xpc_obj_array_protocol");
280 if ((void *)xpc_obj_protocol_array
!= NULL
)
281 protocol_array_count
= xpc_array_get_count(xpc_obj_protocol_array
);
282 if (port_array_count
!= protocol_array_count
)
284 if (port_array_count
> PFPortArraySize
)
286 count
= port_array_count
;
288 for (size_t i
= 0; i
< count
; i
++) {
289 pfports
[i
] = (uint16_t)xpc_array_get_uint64(xpc_obj_port_array
, i
);
290 pfprotocols
[i
] = (uint16_t)xpc_array_get_uint64(xpc_obj_protocol_array
, i
);
293 os_log_info(log_handle
,"Calling new PacketFilterControl()");
294 PacketFilterControl(cmd
, if_name
, count
, pfports
, pfprotocols
);
302 title
= xpc_dictionary_get_string(req
, "notify_title");
303 msg
= xpc_dictionary_get_string(req
, "notify_msg");
305 os_log_info(log_handle
,"Calling new UserNotify() title:%s msg:%s", title
, msg
);
306 UserNotify(title
, msg
);
313 key
= xpc_dictionary_get_uint64(req
, "powerreq_key");
314 interval
= xpc_dictionary_get_uint64(req
, "powerreq_interval");
316 os_log_info(log_handle
,"Calling new PowerRequest() key[%d] interval[%d]", key
, interval
);
317 PowerRequest(key
, interval
, &error_code
);
323 const char *ether_addr
;
328 if_id
= (unsigned int)xpc_dictionary_get_uint64(req
, "interface_index");
329 ether_addr
= xpc_dictionary_get_string(req
, "ethernet_address");
330 ip_addr
= xpc_dictionary_get_string(req
, "ip_address");
331 iteration
= (int)xpc_dictionary_get_uint64(req
, "swp_iteration");
333 os_log_info(log_handle
, "Calling new SendWakeupPacket() ether_addr[%s] ip_addr[%s] if_id[%d] iteration[%d]",
334 ether_addr
, ip_addr
, if_id
, iteration
);
335 SendWakeupPacket(if_id
, ether_addr
, ip_addr
, iteration
);
339 case set_localaddr_cacheentry
:
341 int if_index
, family
;
342 size_t ip_len
, eth_len
;
344 if_index
= xpc_dictionary_get_uint64(req
, "slace_ifindex");
345 family
= xpc_dictionary_get_uint64(req
, "slace_family");
347 const uint8_t * const ip
= (const uint8_t *)xpc_dictionary_get_data(req
, "slace_ip", &ip_len
);
348 if (ip_len
!= sizeof(v6addr_t
))
350 error_code
= kHelperErr_ParamErr
;
354 const uint8_t * const eth
= (const uint8_t *)xpc_dictionary_get_data(req
, "slace_eth", ð_len
);
355 if (eth_len
!= sizeof(ethaddr_t
))
357 error_code
= kHelperErr_ParamErr
;
361 os_log_info(log_handle
, "Calling new SetLocalAddressCacheEntry() if_index[%d] family[%d] ", if_index
, family
);
363 SetLocalAddressCacheEntry(if_index
, family
, ip
, eth
, &error_code
);
369 uint16_t lport
, rport
, win
;
371 size_t sadd6_len
, dadd6_len
;
373 lport
= xpc_dictionary_get_uint64(req
, "send_keepalive_lport");
374 rport
= xpc_dictionary_get_uint64(req
, "send_keepalive_rport");
375 seq
= xpc_dictionary_get_uint64(req
, "send_keepalive_seq");
376 ack
= xpc_dictionary_get_uint64(req
, "send_keepalive_ack");
377 win
= xpc_dictionary_get_uint64(req
, "send_keepalive_win");
379 const uint8_t * const sadd6
= (const uint8_t *)xpc_dictionary_get_data(req
, "send_keepalive_sadd", &sadd6_len
);
380 const uint8_t * const dadd6
= (const uint8_t *)xpc_dictionary_get_data(req
, "send_keepalive_dadd", &dadd6_len
);
381 if ((sadd6_len
!= sizeof(v6addr_t
)) || (dadd6_len
!= sizeof(v6addr_t
)))
383 error_code
= kHelperErr_ParamErr
;
387 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]",
388 lport
, rport
, seq
, ack
, win
);
390 SendKeepalive(sadd6
, dadd6
, lport
, rport
, seq
, ack
, win
);
394 case retreive_tcpinfo
:
396 uint16_t lport
, rport
;
401 size_t laddr_len
, raddr_len
;
403 lport
= xpc_dictionary_get_uint64(req
, "retreive_tcpinfo_lport");
404 rport
= xpc_dictionary_get_uint64(req
, "retreive_tcpinfo_rport");
405 family
= xpc_dictionary_get_uint64(req
, "retreive_tcpinfo_family");
407 const uint8_t * const laddr
= (const uint8_t *)xpc_dictionary_get_data(req
, "retreive_tcpinfo_laddr", &laddr_len
);
408 const uint8_t * const raddr
= (const uint8_t *)xpc_dictionary_get_data(req
, "retreive_tcpinfo_raddr", &raddr_len
);
409 if ((laddr_len
!= sizeof(v6addr_t
)) || (raddr_len
!= sizeof(v6addr_t
)))
411 error_code
= kHelperErr_ParamErr
;
415 os_log_info(log_handle
, "helper-main: handle_request: retreive_tcpinfo: lport is[%d] rport is[%d] family is [%d]",
416 lport
, rport
, family
);
418 RetrieveTCPInfo(family
, laddr
, lport
, raddr
, rport
, &seq
, &ack
, &win
, &intfid
, &error_code
);
422 xpc_dictionary_set_uint64(response
, "retreive_tcpinfo_seq", seq
);
423 xpc_dictionary_set_uint64(response
, "retreive_tcpinfo_ack", ack
);
424 xpc_dictionary_set_uint64(response
, "retreive_tcpinfo_win", win
);
425 xpc_dictionary_set_uint64(response
, "retreive_tcpinfo_ifid", intfid
);
428 os_log_info(log_handle
, "helper-main: handle_request: retreive_tcpinfo: seq is[%d] ack is[%d] win is [%d] intfid is [%d]",
429 seq
, ack
, win
, intfid
);
434 case keychain_getsecrets
:
436 unsigned int num_sec
= 0;
437 unsigned long secrets
= 0;
438 unsigned int sec_cnt
= 0;
440 os_log_info(log_handle
,"Calling new KeyChainGetSecrets()");
442 KeychainGetSecrets(&num_sec
, &secrets
, &sec_cnt
, &error_code
);
446 xpc_dictionary_set_uint64(response
, "keychain_num_secrets", num_sec
);
447 xpc_dictionary_set_data(response
, "keychain_secrets", (void *)secrets
, sec_cnt
);
450 os_log_info(log_handle
,"helper-main: handle_request: keychain_getsecrets: num_secrets is %u, secrets is %lu, secrets_Cnt is %u",
451 num_sec
, secrets
, sec_cnt
);
454 vm_deallocate(mach_task_self(), secrets
, sec_cnt
);
461 os_log(log_handle
, "handle_request: Unrecognized mode!");
462 error_code
= kHelperErr_UndefinedMode
;
467 // Return Response Status back to the client (essentially ACKing the request)
470 xpc_dictionary_set_uint64(response
, kHelperReplyStatus
, kHelperReply_ACK
);
471 xpc_dictionary_set_int64(response
, kHelperErrCode
, error_code
);
472 xpc_connection_send_message(remote_conn
, response
);
473 xpc_release(response
);
477 os_log(log_handle
, "handle_requests: Response Dictionary could not be created!");
483 static void accept_client(xpc_connection_t conn
)
485 int c_pid
= xpc_connection_get_pid(conn
);
487 if (!(check_entitlement(conn
, kHelperService
)))
489 os_log(log_handle
, "accept_client: Helper Client PID[%d] is missing Entitlement. Cancelling connection", c_pid
);
490 xpc_connection_cancel(conn
);
495 xpc_connection_set_target_queue(conn
, xpc_queue
);
496 xpc_connection_set_event_handler(conn
, ^(xpc_object_t req_msg
)
498 xpc_type_t type
= xpc_get_type(req_msg
);
500 if (type
== XPC_TYPE_DICTIONARY
)
502 os_log_info(log_handle
,"accept_client:conn:[%p] client[%d](mDNSResponder) requesting service", (void *) conn
, c_pid
);
503 handle_request(req_msg
);
505 else // We hit this case ONLY if Client Terminated Connection OR Crashed
507 os_log(log_handle
, "accept_client:conn:[%p] client[%d](mDNSResponder) teared down the connection (OR Crashed)", (void *) conn
, c_pid
);
508 // handle_termination();
513 xpc_connection_resume(conn
);
517 static void init_helper_service(const char *service_name
)
520 xpc_connection_t xpc_listener
= xpc_connection_create_mach_service(service_name
, NULL
, XPC_CONNECTION_MACH_SERVICE_LISTENER
);
521 if (!xpc_listener
|| xpc_get_type(xpc_listener
) != XPC_TYPE_CONNECTION
)
523 os_log(log_handle
, "init_helper_service: Error Creating XPC Listener for mDNSResponderHelperService !!");
527 os_log_info(log_handle
,"init_helper_service: XPC Listener for mDNSResponderHelperService Listening");
529 xpc_queue
= dispatch_queue_create("com.apple.mDNSHelper.service_queue", NULL
);
531 xpc_connection_set_event_handler(xpc_listener
, ^(xpc_object_t eventmsg
)
533 xpc_type_t type
= xpc_get_type(eventmsg
);
535 if (type
== XPC_TYPE_CONNECTION
)
537 os_log_info(log_handle
,"init_helper_service: new mDNSResponderHelper Client %p", eventmsg
);
538 accept_client(eventmsg
);
540 else if (type
== XPC_TYPE_ERROR
) // Ideally, we would never hit these cases below
542 os_log(log_handle
, "init_helper_service: XPCError: %s", xpc_dictionary_get_string(eventmsg
, XPC_ERROR_KEY_DESCRIPTION
));
547 os_log(log_handle
, "init_helper_service: Unknown EventMsg type");
552 xpc_connection_resume(xpc_listener
);
556 int main(int ac
, char *av
[])
562 while ((ch
= getopt(ac
, av
, "dt:")) != -1)
570 n
= strtol(optarg
, &p
, 0);
571 if ('\0' == optarg
[0] || '\0' != *p
|| n
> LONG_MAX
|| n
< 0)
573 fprintf(stderr
, "Invalid idle timeout: %s\n", optarg
);
580 fprintf(stderr
, "Usage: mDNSResponderHelper [-d] [-t maxidle]\n");
589 initialize_logging();
592 mDNSHelperLogEnabled
= HelperPrefsGetValueBool(kPreferencesKey_mDNSHelperLog
, mDNSHelperLogEnabled
);
594 // Currently on Fuji/Whitetail releases we are keeping the logging always enabled.
595 // Hence mDNSHelperLogEnabled is set to true below by default.
596 mDNSHelperLogEnabled
= 1;
598 os_log_info(log_handle
,"mDNSResponderHelper Starting to run");
600 #ifndef NO_SECURITYFRAMEWORK
601 // We should normally be running as a system daemon. However, that might not be the case in some scenarios (e.g. debugging).
602 // Explicitly ensure that our Keychain operations utilize the system domain.
604 SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem
);
608 actualidle
= maxidle
;
610 signal(SIGTERM
, handle_sigterm
);
612 if (initialize_timer())
614 for (n
=0; n
<100000; n
++)
620 os_log(log_handle
, "gRunLoop not set after waiting");
624 init_helper_service(kHelperService
);
625 os_log_info(log_handle
,"mDNSResponderHelper is now running");
630 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
631 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
632 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
633 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) # s
634 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
636 // For convenience when using the "strings" command, this is the last thing in the file
637 // The "@(#) " pattern is a special prefix the "what" command looks for
638 const char VersionString_SCCS
[] = "@(#) mDNSResponderHelper " STRINGIFY(mDNSResponderVersion
) " (" __DATE__
" " __TIME__
")";
640 #if _BUILDING_XCODE_PROJECT_
641 // If the process crashes, then this string will be magically included in the automatically-generated crash log
642 const char *__crashreporter_info__
= VersionString_SCCS
+ 5;
643 asm (".desc ___crashreporter_info__, 0x10");