1 /*
2 * Copyright 2014-2015 iXsystems, Inc.
3 * All rights reserved
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted providing that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
23 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 *
26 */
27
28 #include <errno.h>
29 #include <mach/mach.h>
30 #include <servers/bootstrap.h>
31 #include <xpc/xpc.h>
32 #include <machine/atomic.h>
33 #include <Block.h>
34 #include "xpc_internal.h"
35
36 #define XPC_CONNECTION_NEXT_ID(conn) (atomic_fetchadd_int(&conn->xc_last_id, 1))
37
38 static void xpc_connection_recv_message();
39 static void xpc_send(xpc_connection_t xconn, xpc_object_t message, uint64_t id);
40
41 xpc_connection_t
xpc_connection_create(const char * name,dispatch_queue_t targetq)42 xpc_connection_create(const char *name, dispatch_queue_t targetq)
43 {
44 kern_return_t kr;
45 char *qname;
46 struct xpc_connection *conn;
47
48 if ((conn = malloc(sizeof(struct xpc_connection))) == NULL) {
49 errno = ENOMEM;
50 return (NULL);
51 }
52
53 memset(conn, 0, sizeof(struct xpc_connection));
54 conn->xc_last_id = 1;
55 TAILQ_INIT(&conn->xc_peers);
56 TAILQ_INIT(&conn->xc_pending);
57
58 /* Create send queue */
59 asprintf(&qname, "com.ixsystems.xpc.connection.sendq.%p", conn);
60 conn->xc_send_queue = dispatch_queue_create(qname, NULL);
61
62 /* Create recv queue */
63 asprintf(&qname, "com.ixsystems.xpc.connection.recvq.%p", conn);
64 conn->xc_recv_queue = dispatch_queue_create(qname, NULL);
65
66 /* Create target queue */
67 conn->xc_target_queue = targetq ? targetq : dispatch_get_main_queue();
68
69 /* Receive queue is initially suspended */
70 dispatch_suspend(conn->xc_recv_queue);
71
72 /* Create local port */
73 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
74 &conn->xc_local_port);
75 if (kr != KERN_SUCCESS) {
76 errno = EPERM;
77 return (NULL);
78 }
79
80 kr = mach_port_insert_right(mach_task_self(), conn->xc_local_port,
81 conn->xc_local_port, MACH_MSG_TYPE_MAKE_SEND);
82 if (kr != KERN_SUCCESS) {
83 errno = EPERM;
84 return (NULL);
85 }
86
87 return (conn);
88 }
89
90 xpc_connection_t
xpc_connection_create_mach_service(const char * name,dispatch_queue_t targetq,uint64_t flags)91 xpc_connection_create_mach_service(const char *name, dispatch_queue_t targetq,
92 uint64_t flags)
93 {
94 kern_return_t kr;
95 struct xpc_connection *conn;
96
97 conn = xpc_connection_create(name, targetq);
98 if (conn == NULL)
99 return (NULL);
100
101 conn->xc_flags = flags;
102
103 if (flags & XPC_CONNECTION_MACH_SERVICE_LISTENER) {
104 kr = bootstrap_check_in(bootstrap_port, name,
105 &conn->xc_local_port);
106 if (kr != KERN_SUCCESS) {
107 errno = EBUSY;
108 free(conn);
109 return (NULL);
110 }
111
112 return (conn);
113 }
114
115 if (!strcmp(name, "bootstrap")) {
116 conn->xc_remote_port = bootstrap_port;
117 return (conn);
118 }
119
120 /* Look up named mach service */
121 kr = bootstrap_look_up(bootstrap_port, name, &conn->xc_remote_port);
122 if (kr != KERN_SUCCESS) {
123 errno = ENOENT;
124 free(conn);
125 return (NULL);
126 }
127
128 return (conn);
129 }
130
131 xpc_connection_t
xpc_connection_create_from_endpoint(xpc_endpoint_t endpoint)132 xpc_connection_create_from_endpoint(xpc_endpoint_t endpoint)
133 {
134 kern_return_t kr;
135 struct xpc_connection *conn;
136
137 conn = xpc_connection_create("anonymous", NULL);
138 if (conn == NULL)
139 return (NULL);
140
141 conn->xc_remote_port = (mach_port_t)endpoint;
142 return (conn);
143 }
144
145 void
xpc_connection_set_target_queue(xpc_connection_t xconn,dispatch_queue_t targetq)146 xpc_connection_set_target_queue(xpc_connection_t xconn,
147 dispatch_queue_t targetq)
148 {
149 struct xpc_connection *conn;
150
151 debugf("connection=%p", xconn);
152 conn = xconn;
153 conn->xc_target_queue = targetq;
154 }
155
156 void
xpc_connection_set_event_handler(xpc_connection_t xconn,xpc_handler_t handler)157 xpc_connection_set_event_handler(xpc_connection_t xconn,
158 xpc_handler_t handler)
159 {
160 struct xpc_connection *conn;
161
162 debugf("connection=%p", xconn);
163 conn = xconn;
164 conn->xc_handler = (xpc_handler_t)Block_copy(handler);
165 }
166
167 void
xpc_connection_suspend(xpc_connection_t xconn)168 xpc_connection_suspend(xpc_connection_t xconn)
169 {
170 struct xpc_connection *conn;
171
172 conn = xconn;
173 dispatch_suspend(conn->xc_recv_source);
174 }
175
176 void
xpc_connection_resume(xpc_connection_t xconn)177 xpc_connection_resume(xpc_connection_t xconn)
178 {
179 struct xpc_connection *conn;
180
181 debugf("connection=%p", xconn);
182 conn = xconn;
183
184 /* Create dispatch source for top-level connection */
185 if (conn->xc_parent == NULL) {
186 conn->xc_recv_source = dispatch_source_create(
187 DISPATCH_SOURCE_TYPE_MACH_RECV, conn->xc_local_port, 0,
188 conn->xc_recv_queue);
189 dispatch_set_context(conn->xc_recv_source, conn);
190 dispatch_source_set_event_handler_f(conn->xc_recv_source,
191 xpc_connection_recv_message);
192 dispatch_resume(conn->xc_recv_source);
193 }
194
195 dispatch_resume(conn->xc_recv_queue);
196 }
197
198 void
xpc_connection_send_message(xpc_connection_t xconn,xpc_object_t message)199 xpc_connection_send_message(xpc_connection_t xconn,
200 xpc_object_t message)
201 {
202 struct xpc_connection *conn;
203 uint64_t id;
204
205 conn = xconn;
206 id = xpc_dictionary_get_uint64(message, XPC_SEQID);
207
208 if (id == 0)
209 id = XPC_CONNECTION_NEXT_ID(conn);
210
211 dispatch_async(conn->xc_send_queue, ^{
212 xpc_send(conn, message, id);
213 });
214 }
215
216 void
xpc_connection_send_message_with_reply(xpc_connection_t xconn,xpc_object_t message,dispatch_queue_t targetq,xpc_handler_t handler)217 xpc_connection_send_message_with_reply(xpc_connection_t xconn,
218 xpc_object_t message, dispatch_queue_t targetq, xpc_handler_t handler)
219 {
220 struct xpc_connection *conn;
221 struct xpc_pending_call *call;
222
223 conn = xconn;
224 call = malloc(sizeof(struct xpc_pending_call));
225 call->xp_id = XPC_CONNECTION_NEXT_ID(conn);
226 call->xp_handler = handler;
227 call->xp_queue = targetq;
228 TAILQ_INSERT_TAIL(&conn->xc_pending, call, xp_link);
229
230 dispatch_async(conn->xc_send_queue, ^{
231 xpc_send(conn, message, call->xp_id);
232 });
233
234 }
235
236 xpc_object_t
xpc_connection_send_message_with_reply_sync(xpc_connection_t conn,xpc_object_t message)237 xpc_connection_send_message_with_reply_sync(xpc_connection_t conn,
238 xpc_object_t message)
239 {
240 __block xpc_object_t result;
241 dispatch_semaphore_t sem = dispatch_semaphore_create(0);
242
243 xpc_connection_send_message_with_reply(conn, message, NULL,
244 ^(xpc_object_t o) {
245 result = o;
246 dispatch_semaphore_signal(sem);
247 });
248
249 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
250 return (result);
251 }
252
253 void
xpc_connection_send_barrier(xpc_connection_t xconn,dispatch_block_t barrier)254 xpc_connection_send_barrier(xpc_connection_t xconn, dispatch_block_t barrier)
255 {
256 struct xpc_connection *conn;
257
258 conn = xconn;
259 dispatch_sync(conn->xc_send_queue, barrier);
260 }
261
262 void
xpc_connection_cancel(xpc_connection_t connection)263 xpc_connection_cancel(xpc_connection_t connection)
264 {
265
266 }
267
268 const char *
xpc_connection_get_name(xpc_connection_t connection)269 xpc_connection_get_name(xpc_connection_t connection)
270 {
271
272 return ("unknown"); /* ??? */
273 }
274
275 uid_t
xpc_connection_get_euid(xpc_connection_t xconn)276 xpc_connection_get_euid(xpc_connection_t xconn)
277 {
278 struct xpc_connection *conn;
279
280 conn = xconn;
281 return (conn->xc_remote_euid);
282 }
283
284 gid_t
xpc_connection_get_guid(xpc_connection_t xconn)285 xpc_connection_get_guid(xpc_connection_t xconn)
286 {
287 struct xpc_connection *conn;
288
289 conn = xconn;
290 return (conn->xc_remote_guid);
291 }
292
293 pid_t
xpc_connection_get_pid(xpc_connection_t xconn)294 xpc_connection_get_pid(xpc_connection_t xconn)
295 {
296 struct xpc_connection *conn;
297
298 conn = xconn;
299 return (conn->xc_remote_pid);
300 }
301
302 au_asid_t
xpc_connection_get_asid(xpc_connection_t xconn)303 xpc_connection_get_asid(xpc_connection_t xconn)
304 {
305 struct xpc_connection *conn;
306
307 conn = xconn;
308 return (conn->xc_remote_asid);
309 }
310
311 void
xpc_connection_set_context(xpc_connection_t xconn,void * ctx)312 xpc_connection_set_context(xpc_connection_t xconn, void *ctx)
313 {
314 struct xpc_connection *conn;
315
316 conn = xconn;
317 conn->xc_context = ctx;
318 }
319
320 void *
xpc_connection_get_context(xpc_connection_t xconn)321 xpc_connection_get_context(xpc_connection_t xconn)
322 {
323 struct xpc_connection *conn;
324
325 conn = xconn;
326 return (conn->xc_context);
327 }
328
329 void
xpc_connection_set_finalizer_f(xpc_connection_t connection,xpc_finalizer_t finalizer)330 xpc_connection_set_finalizer_f(xpc_connection_t connection,
331 xpc_finalizer_t finalizer)
332 {
333
334 }
335
336 xpc_endpoint_t
xpc_endpoint_create(xpc_connection_t connection)337 xpc_endpoint_create(xpc_connection_t connection)
338 {
339
340 }
341
342 void
xpc_main(xpc_connection_handler_t handler)343 xpc_main(xpc_connection_handler_t handler)
344 {
345
346 dispatch_main();
347 }
348
349 void
xpc_transaction_begin(void)350 xpc_transaction_begin(void)
351 {
352
353 }
354
355 void
xpc_transaction_end(void)356 xpc_transaction_end(void)
357 {
358
359 }
360
361 static void
xpc_send(xpc_connection_t xconn,xpc_object_t message,uint64_t id)362 xpc_send(xpc_connection_t xconn, xpc_object_t message, uint64_t id)
363 {
364 struct xpc_connection *conn;
365 kern_return_t kr;
366
367 debugf("connection=%p, message=%p, id=%d", xconn, message, id);
368
369 conn = xconn;
370 kr = xpc_pipe_send(message, conn->xc_remote_port,
371 conn->xc_local_port, id);
372
373 if (kr != KERN_SUCCESS)
374 debugf("send failed, kr=%d", kr);
375 }
376
377 static void
xpc_connection_set_credentials(struct xpc_connection * conn,audit_token_t * tok)378 xpc_connection_set_credentials(struct xpc_connection *conn, audit_token_t *tok)
379 {
380 uid_t uid;
381 gid_t gid;
382 pid_t pid;
383 au_asid_t asid;
384
385 if (tok == NULL)
386 return;
387
388 audit_token_to_au32(*tok, NULL, &uid, &gid, NULL, NULL, &pid, &asid,
389 NULL);
390
391 conn->xc_remote_euid = uid;
392 conn->xc_remote_guid = gid;
393 conn->xc_remote_pid = pid;
394 conn->xc_remote_asid = asid;
395 }
396
397 static void
xpc_connection_recv_message(void * context)398 xpc_connection_recv_message(void *context)
399 {
400 struct xpc_pending_call *call;
401 struct xpc_connection *conn, *peer;
402 xpc_object_t result;
403 mach_port_t remote;
404 kern_return_t kr;
405 uint64_t id;
406
407 debugf("connection=%p", context);
408
409 conn = context;
410 kr = xpc_pipe_receive(conn->xc_local_port, &remote, &result, &id);
411 if (kr != KERN_SUCCESS)
412 return;
413
414 debugf("message=%p, id=%d, remote=<%d>", result, id, remote);
415
416 if (conn->xc_flags & XPC_CONNECTION_MACH_SERVICE_LISTENER) {
417 TAILQ_FOREACH(peer, &conn->xc_peers, xc_link) {
418 if (remote == peer->xc_remote_port) {
419 dispatch_async(peer->xc_target_queue, ^{
420 peer->xc_handler(result);
421 });
422 return;
423 }
424 }
425
426 debugf("new peer on port <%u>", remote);
427
428 /* New peer */
429 peer = xpc_connection_create(NULL, NULL);
430 peer->xc_parent = conn;
431 peer->xc_remote_port = remote;
432 xpc_connection_set_credentials(peer,
433 ((struct xpc_object *)result)->xo_audit_token);
434
435 TAILQ_INSERT_TAIL(&conn->xc_peers, peer, xc_link);
436
437 dispatch_async(conn->xc_target_queue, ^{
438 conn->xc_handler(peer);
439 });
440
441 dispatch_async(peer->xc_target_queue, ^{
442 peer->xc_handler(result);
443 });
444
445 } else {
446 xpc_connection_set_credentials(conn,
447 ((struct xpc_object *)result)->xo_audit_token);
448
449 TAILQ_FOREACH(call, &conn->xc_pending, xp_link) {
450 if (call->xp_id == id) {
451 dispatch_async(conn->xc_target_queue, ^{
452 call->xp_handler(result);
453 TAILQ_REMOVE(&conn->xc_pending, call,
454 xp_link);
455 free(call);
456 });
457 return;
458 }
459 }
460
461 if (conn->xc_handler) {
462 dispatch_async(conn->xc_target_queue, ^{
463 conn->xc_handler(result);
464 });
465 }
466 }
467 }
468