1 /* $NetBSD: client.c,v 1.1.1.1 2011/04/13 18:15:28 elric Exp $ */ 2 3 /* 4 * Copyright (c) 2009 Kungliga Tekniska H�gskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Portions Copyright (c) 2009 Apple Inc. All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * 3. Neither the name of the Institute nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 38 #include "hi_locl.h" 39 40 #if defined(__APPLE__) && defined(HAVE_GCD) 41 42 #include "heim_ipc.h" 43 #include "heim_ipc_asyncServer.h" 44 45 #include <dispatch/dispatch.h> 46 #include <mach/mach.h> 47 48 static dispatch_once_t jobqinited = 0; 49 static dispatch_queue_t jobq = NULL; 50 static dispatch_queue_t syncq; 51 52 struct mach_ctx { 53 mach_port_t server; 54 char *name; 55 }; 56 57 static int 58 mach_release(void *ctx); 59 60 static int 61 mach_init(const char *service, void **ctx) 62 { 63 struct mach_ctx *ipc; 64 mach_port_t sport; 65 int ret; 66 67 dispatch_once(&jobqinited, ^{ 68 jobq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 69 syncq = dispatch_queue_create("heim-ipc-syncq", NULL); 70 }); 71 72 ret = bootstrap_look_up(bootstrap_port, service, &sport); 73 if (ret) 74 return ret; 75 76 ipc = malloc(sizeof(*ipc)); 77 if (ipc == NULL) { 78 mach_port_destroy(mach_task_self(), sport); 79 return ENOMEM; 80 } 81 82 ipc->server = sport; 83 ipc->name = strdup(service); 84 if (ipc->name == NULL) { 85 mach_release(ipc); 86 return ENOMEM; 87 } 88 89 *ctx = ipc; 90 91 return 0; 92 } 93 94 static int 95 mach_ipc(void *ctx, 96 const heim_idata *request, heim_idata *response, 97 heim_icred *cred) 98 { 99 struct mach_ctx *ipc = ctx; 100 heim_ipc_message_inband_t requestin; 101 mach_msg_type_number_t requestin_length = 0; 102 heim_ipc_message_outband_t requestout = NULL; 103 mach_msg_type_number_t requestout_length = 0; 104 heim_ipc_message_inband_t replyin; 105 mach_msg_type_number_t replyin_length; 106 heim_ipc_message_outband_t replyout; 107 mach_msg_type_number_t replyout_length; 108 int ret, errorcode, retries = 0; 109 110 memcpy(requestin, request->data, request->length); 111 requestin_length = request->length; 112 113 while (retries < 2) { 114 __block mach_port_t sport; 115 116 dispatch_sync(syncq, ^{ sport = ipc->server; }); 117 118 ret = mheim_ipc_call(sport, 119 requestin, requestin_length, 120 requestout, requestout_length, 121 &errorcode, 122 replyin, &replyin_length, 123 &replyout, &replyout_length); 124 if (ret == MACH_SEND_INVALID_DEST) { 125 mach_port_t nport; 126 /* race other threads to get a new port */ 127 ret = bootstrap_look_up(bootstrap_port, ipc->name, &nport); 128 if (ret) 129 return ret; 130 dispatch_sync(syncq, ^{ 131 /* check if we lost the race to lookup the port */ 132 if (sport != ipc->server) { 133 mach_port_deallocate(mach_task_self(), nport); 134 } else { 135 mach_port_deallocate(mach_task_self(), ipc->server); 136 ipc->server = nport; 137 } 138 }); 139 retries++; 140 } else if (ret) { 141 return ret; 142 } else 143 break; 144 } 145 if (retries >= 2) 146 return EINVAL; 147 148 if (errorcode) { 149 if (replyout_length) 150 vm_deallocate (mach_task_self (), (vm_address_t) replyout, 151 replyout_length); 152 return errorcode; 153 } 154 155 if (replyout_length) { 156 response->data = malloc(replyout_length); 157 if (response->data == NULL) { 158 vm_deallocate (mach_task_self (), (vm_address_t) replyout, 159 replyout_length); 160 return ENOMEM; 161 } 162 memcpy(response->data, replyout, replyout_length); 163 response->length = replyout_length; 164 vm_deallocate (mach_task_self (), (vm_address_t) replyout, 165 replyout_length); 166 } else { 167 response->data = malloc(replyin_length); 168 if (response->data == NULL) 169 return ENOMEM; 170 memcpy(response->data, replyin, replyin_length); 171 response->length = replyin_length; 172 } 173 174 return 0; 175 } 176 177 struct async_client { 178 mach_port_t mp; 179 dispatch_source_t source; 180 dispatch_queue_t queue; 181 void (*func)(void *, int, heim_idata *, heim_icred); 182 void *userctx; 183 }; 184 185 kern_return_t 186 mheim_ado_acall_reply(mach_port_t server_port, 187 audit_token_t client_creds, 188 int returnvalue, 189 heim_ipc_message_inband_t replyin, 190 mach_msg_type_number_t replyinCnt, 191 heim_ipc_message_outband_t replyout, 192 mach_msg_type_number_t replyoutCnt) 193 { 194 struct async_client *c = dispatch_get_context(dispatch_get_current_queue()); 195 heim_idata response; 196 197 if (returnvalue) { 198 response.data = NULL; 199 response.length = 0; 200 } else if (replyoutCnt) { 201 response.data = replyout; 202 response.length = replyoutCnt; 203 } else { 204 response.data = replyin; 205 response.length = replyinCnt; 206 } 207 208 (*c->func)(c->userctx, returnvalue, &response, NULL); 209 210 if (replyoutCnt) 211 vm_deallocate (mach_task_self (), (vm_address_t) replyout, replyoutCnt); 212 213 dispatch_source_cancel(c->source); 214 215 return 0; 216 217 218 } 219 220 221 static int 222 mach_async(void *ctx, const heim_idata *request, void *userctx, 223 void (*func)(void *, int, heim_idata *, heim_icred)) 224 { 225 struct mach_ctx *ipc = ctx; 226 heim_ipc_message_inband_t requestin; 227 mach_msg_type_number_t requestin_length = 0; 228 heim_ipc_message_outband_t requestout = NULL; 229 mach_msg_type_number_t requestout_length = 0; 230 int ret, retries = 0; 231 kern_return_t kr; 232 struct async_client *c; 233 234 /* first create the service that will catch the reply from the server */ 235 /* XXX these object should be cached and reused */ 236 237 c = malloc(sizeof(*c)); 238 if (c == NULL) 239 return ENOMEM; 240 241 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &c->mp); 242 if (kr != KERN_SUCCESS) 243 return EINVAL; 244 245 c->queue = dispatch_queue_create("heim-ipc-async-client", NULL); 246 c->source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, c->mp, 0, c->queue); 247 dispatch_set_context(c->queue, c); 248 249 dispatch_source_set_event_handler(c->source, ^{ 250 dispatch_mig_server(c->source, 251 sizeof(union __RequestUnion__mheim_ado_mheim_aipc_subsystem), 252 mheim_aipc_server); 253 }); 254 255 dispatch_source_set_cancel_handler(c->source, ^{ 256 mach_port_mod_refs(mach_task_self(), c->mp, 257 MACH_PORT_RIGHT_RECEIVE, -1); 258 dispatch_release(c->queue); 259 dispatch_release(c->source); 260 free(c); 261 }); 262 263 c->func = func; 264 c->userctx = userctx; 265 266 dispatch_resume(c->source); 267 268 /* ok, send the message */ 269 270 memcpy(requestin, request->data, request->length); 271 requestin_length = request->length; 272 273 while (retries < 2) { 274 __block mach_port_t sport; 275 276 dispatch_sync(syncq, ^{ sport = ipc->server; }); 277 278 ret = mheim_ipc_call_request(sport, c->mp, 279 requestin, requestin_length, 280 requestout, requestout_length); 281 if (ret == MACH_SEND_INVALID_DEST) { 282 ret = bootstrap_look_up(bootstrap_port, ipc->name, &sport); 283 if (ret) { 284 dispatch_source_cancel(c->source); 285 return ret; 286 } 287 mach_port_deallocate(mach_task_self(), ipc->server); 288 ipc->server = sport; 289 retries++; 290 } else if (ret) { 291 dispatch_source_cancel(c->source); 292 return ret; 293 } else 294 break; 295 } 296 if (retries >= 2) { 297 dispatch_source_cancel(c->source); 298 return EINVAL; 299 } 300 301 return 0; 302 } 303 304 static int 305 mach_release(void *ctx) 306 { 307 struct mach_ctx *ipc = ctx; 308 if (ipc->server != MACH_PORT_NULL) 309 mach_port_deallocate(mach_task_self(), ipc->server); 310 free(ipc->name); 311 free(ipc); 312 return 0; 313 } 314 315 #endif 316 317 struct path_ctx { 318 char *path; 319 int fd; 320 }; 321 322 static int common_release(void *); 323 324 static int 325 connect_unix(struct path_ctx *s) 326 { 327 struct sockaddr_un addr; 328 329 addr.sun_family = AF_UNIX; 330 strlcpy(addr.sun_path, s->path, sizeof(addr.sun_path)); 331 332 s->fd = socket(AF_UNIX, SOCK_STREAM, 0); 333 if (s->fd < 0) 334 return errno; 335 rk_cloexec(s->fd); 336 337 if (connect(s->fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) { 338 close(s->fd); 339 return errno; 340 } 341 342 return 0; 343 } 344 345 static int 346 common_path_init(const char *service, 347 const char *file, 348 void **ctx) 349 { 350 struct path_ctx *s; 351 352 s = malloc(sizeof(*s)); 353 if (s == NULL) 354 return ENOMEM; 355 s->fd = -1; 356 357 asprintf(&s->path, "/var/run/.heim_%s-%s", service, file); 358 359 *ctx = s; 360 361 return 0; 362 } 363 364 static int 365 unix_socket_init(const char *service, 366 void **ctx) 367 { 368 int ret; 369 370 ret = common_path_init(service, "socket", ctx); 371 if (ret) 372 return ret; 373 ret = connect_unix(*ctx); 374 if (ret) 375 common_release(*ctx); 376 377 return ret; 378 } 379 380 static int 381 unix_socket_ipc(void *ctx, 382 const heim_idata *req, heim_idata *rep, 383 heim_icred *cred) 384 { 385 struct path_ctx *s = ctx; 386 uint32_t len = htonl(req->length); 387 uint32_t rv; 388 int retval; 389 390 if (cred) 391 *cred = NULL; 392 393 rep->data = NULL; 394 rep->length = 0; 395 396 if (net_write(s->fd, &len, sizeof(len)) != sizeof(len)) 397 return -1; 398 if (net_write(s->fd, req->data, req->length) != req->length) 399 return -1; 400 401 if (net_read(s->fd, &len, sizeof(len)) != sizeof(len)) 402 return -1; 403 if (net_read(s->fd, &rv, sizeof(rv)) != sizeof(rv)) 404 return -1; 405 retval = ntohl(rv); 406 407 rep->length = ntohl(len); 408 if (rep->length > 0) { 409 rep->data = malloc(rep->length); 410 if (rep->data == NULL) 411 return -1; 412 if (net_read(s->fd, rep->data, rep->length) != rep->length) 413 return -1; 414 } else 415 rep->data = NULL; 416 417 return retval; 418 } 419 420 int 421 common_release(void *ctx) 422 { 423 struct path_ctx *s = ctx; 424 if (s->fd >= 0) 425 close(s->fd); 426 free(s->path); 427 free(s); 428 return 0; 429 } 430 431 #ifdef HAVE_DOOR 432 433 static int 434 door_init(const char *service, 435 void **ctx) 436 { 437 ret = common_path_init(context, service, "door", ctx); 438 if (ret) 439 return ret; 440 ret = connect_door(*ctx); 441 if (ret) 442 common_release(*ctx); 443 return ret; 444 } 445 446 static int 447 door_ipc(void *ctx, 448 const heim_idata *request, heim_idata *response, 449 heim_icred *cred) 450 { 451 door_arg_t arg; 452 int ret; 453 454 arg.data_ptr = request->data; 455 arg.data_size = request->length; 456 arg.desc_ptr = NULL; 457 arg.desc_num = 0; 458 arg.rbuf = NULL; 459 arg.rsize = 0; 460 461 ret = door_call(fd, &arg); 462 close(fd); 463 if (ret != 0) 464 return errno; 465 466 response->data = malloc(arg.rsize); 467 if (response->data == NULL) { 468 munmap(arg.rbuf, arg.rsize); 469 return ENOMEM; 470 } 471 memcpy(response->data, arg.rbuf, arg.rsize); 472 response->length = arg.rsize; 473 munmap(arg.rbuf, arg.rsize); 474 475 return ret; 476 } 477 478 #endif 479 480 struct hipc_ops { 481 const char *prefix; 482 int (*init)(const char *, void **); 483 int (*release)(void *); 484 int (*ipc)(void *,const heim_idata *, heim_idata *, heim_icred *); 485 int (*async)(void *, const heim_idata *, void *, 486 void (*)(void *, int, heim_idata *, heim_icred)); 487 }; 488 489 struct hipc_ops ipcs[] = { 490 #if defined(__APPLE__) && defined(HAVE_GCD) 491 { "MACH", mach_init, mach_release, mach_ipc, mach_async }, 492 #endif 493 #ifdef HAVE_DOOR 494 { "DOOR", door_init, common_release, door_ipc } 495 #endif 496 { "UNIX", unix_socket_init, common_release, unix_socket_ipc } 497 }; 498 499 struct heim_ipc { 500 struct hipc_ops *ops; 501 void *ctx; 502 }; 503 504 505 int 506 heim_ipc_init_context(const char *name, heim_ipc *ctx) 507 { 508 unsigned int i; 509 int ret, any = 0; 510 511 for(i = 0; i < sizeof(ipcs)/sizeof(ipcs[0]); i++) { 512 size_t prefix_len = strlen(ipcs[i].prefix); 513 heim_ipc c; 514 if(strncmp(ipcs[i].prefix, name, prefix_len) == 0 515 && name[prefix_len] == ':') { 516 } else if (strncmp("ANY:", name, 4) == 0) { 517 prefix_len = 3; 518 any = 1; 519 } else 520 continue; 521 522 c = calloc(1, sizeof(*c)); 523 if (c == NULL) 524 return ENOMEM; 525 526 c->ops = &ipcs[i]; 527 528 ret = (c->ops->init)(name + prefix_len + 1, &c->ctx); 529 if (ret) { 530 free(c); 531 if (any) 532 continue; 533 return ret; 534 } 535 536 *ctx = c; 537 return 0; 538 } 539 540 return ENOENT; 541 } 542 543 void 544 heim_ipc_free_context(heim_ipc ctx) 545 { 546 (ctx->ops->release)(ctx->ctx); 547 free(ctx); 548 } 549 550 int 551 heim_ipc_call(heim_ipc ctx, const heim_idata *send, heim_idata *recv, 552 heim_icred *cred) 553 { 554 if (cred) 555 *cred = NULL; 556 return (ctx->ops->ipc)(ctx->ctx, send, recv, cred); 557 } 558 559 int 560 heim_ipc_async(heim_ipc ctx, const heim_idata *send, void *userctx, 561 void (*func)(void *, int, heim_idata *, heim_icred)) 562 { 563 if (ctx->ops->async == NULL) { 564 heim_idata recv; 565 heim_icred cred = NULL; 566 int ret; 567 568 ret = (ctx->ops->ipc)(ctx->ctx, send, &recv, &cred); 569 (*func)(userctx, ret, &recv, cred); 570 heim_ipc_free_cred(cred); 571 free(recv.data); 572 return ret; 573 } else { 574 return (ctx->ops->async)(ctx->ctx, send, userctx, func); 575 } 576 } 577