server.c
1 /* 2 * Copyright (c) 2009 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include "hi_locl.h" 37 #include "heimbase.h" 38 #include <assert.h> 39 #include <syslog.h> 40 41 struct heim_sipc { 42 int (*release)(heim_sipc ctx); 43 heim_ipc_callback callback; 44 void *userctx; 45 void *mech; 46 }; 47 48 49 #if defined(__APPLE__) && defined(HAVE_GCD) 50 51 #include "heim_ipcServer.h" 52 #include "heim_ipc_reply.h" 53 #include "heim_ipc_async.h" 54 55 static dispatch_source_t timer; 56 static dispatch_queue_t timerq; 57 static uint64_t timeoutvalue; 58 59 static dispatch_queue_t eventq; 60 61 static dispatch_queue_t workq; 62 63 struct dispatch_signal { 64 dispatch_source_t s; 65 struct dispatch_signal *next; 66 }; 67 68 static struct dispatch_signal *dispatch_signals; 69 70 static void default_timer_ev(void) __attribute__((__noreturn__)); 71 72 static void 73 default_timer_ev(void) 74 { 75 exit(0); 76 } 77 78 static void (*timer_ev)(void) = default_timer_ev; 79 80 static void 81 set_timer(void) 82 { 83 dispatch_source_set_timer(timer, 84 dispatch_time(DISPATCH_TIME_NOW, 85 timeoutvalue * NSEC_PER_SEC), 86 DISPATCH_TIME_FOREVER, 1000000); 87 } 88 89 static void 90 init_globals(void) 91 { 92 static dispatch_once_t once; 93 dispatch_once(&once, ^{ 94 timerq = dispatch_queue_create("hiem-sipc-timer-q", NULL); 95 timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, timerq); 96 dispatch_source_set_event_handler(timer, ^{ timer_ev(); } ); 97 98 workq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 99 eventq = dispatch_queue_create("heim-ipc.event-queue", NULL); 100 dispatch_suspend(eventq); 101 }); 102 } 103 104 void 105 heim_ipc_init_globals(void) 106 { 107 init_globals(); 108 } 109 110 void heim_ipc_resume_events(void) 111 { 112 dispatch_resume(eventq); 113 } 114 115 void heim_ipc_suspend_events(void) 116 { 117 dispatch_suspend(eventq); 118 } 119 120 void 121 _heim_ipc_suspend_timer(void) 122 { 123 dispatch_suspend(timer); 124 } 125 126 void 127 _heim_ipc_restart_timer(void) 128 { 129 dispatch_async(timerq, ^{ 130 set_timer(); 131 dispatch_resume(timer); 132 }); 133 } 134 135 struct mach_service { 136 mach_port_t sport; 137 dispatch_source_t source; 138 dispatch_queue_t queue; 139 }; 140 141 struct mach_call_ctx { 142 mach_port_t reply_port; 143 heim_icred cred; 144 heim_idata req; 145 }; 146 147 148 static void 149 mach_complete_sync(heim_sipc_call ctx, int returnvalue, heim_idata *reply) 150 { 151 struct mach_call_ctx *s = (struct mach_call_ctx *)ctx; 152 heim_ipc_message_inband_t replyin; 153 mach_msg_type_number_t replyinCnt; 154 heim_ipc_message_outband_t replyout; 155 mach_msg_type_number_t replyoutCnt; 156 157 if (returnvalue) { 158 /* on error, no reply */ 159 replyinCnt = 0; 160 replyout = 0; replyoutCnt = 0; 161 } else if (reply->length < 2048) { 162 replyinCnt = (mach_msg_type_number_t)reply->length; 163 memcpy(replyin, reply->data, replyinCnt); 164 replyout = 0; replyoutCnt = 0; 165 } else { 166 replyinCnt = 0; 167 vm_read(mach_task_self(), 168 (vm_address_t)reply->data, reply->length, 169 (vm_address_t *)&replyout, &replyoutCnt); 170 } 171 172 mheim_ripc_call_reply(s->reply_port, returnvalue, 173 replyin, replyinCnt, 174 replyout, replyoutCnt); 175 176 heim_ipc_free_cred(s->cred); 177 free(s->req.data); 178 free(s); 179 _heim_ipc_restart_timer(); 180 } 181 182 static void 183 mach_complete_async(heim_sipc_call ctx, int returnvalue, heim_idata *reply) 184 { 185 struct mach_call_ctx *s = (struct mach_call_ctx *)ctx; 186 heim_ipc_message_inband_t replyin; 187 mach_msg_type_number_t replyinCnt; 188 heim_ipc_message_outband_t replyout; 189 mach_msg_type_number_t replyoutCnt; 190 kern_return_t kr; 191 192 if (returnvalue) { 193 /* on error, no reply */ 194 replyinCnt = 0; 195 replyout = 0; replyoutCnt = 0; 196 kr = KERN_SUCCESS; 197 } else if (reply->length < 2048) { 198 replyinCnt = (mach_msg_type_number_t)reply->length; 199 memcpy(replyin, reply->data, replyinCnt); 200 replyout = 0; replyoutCnt = 0; 201 kr = KERN_SUCCESS; 202 } else { 203 replyinCnt = 0; 204 kr = vm_read(mach_task_self(), 205 (vm_address_t)reply->data, reply->length, 206 (vm_address_t *)&replyout, &replyoutCnt); 207 } 208 209 kr = mheim_aipc_acall_reply(s->reply_port, returnvalue, 210 replyin, replyinCnt, 211 replyout, replyoutCnt); 212 heim_ipc_free_cred(s->cred); 213 free(s->req.data); 214 memset(s, 0, sizeof(*s)); 215 free(s); 216 _heim_ipc_restart_timer(); 217 } 218 219 220 kern_return_t 221 mheim_do_call(mach_port_t server_port, 222 audit_token_t client_creds, 223 mach_port_t reply_port, 224 heim_ipc_message_inband_t requestin, 225 mach_msg_type_number_t requestinCnt, 226 heim_ipc_message_outband_t requestout, 227 mach_msg_type_number_t requestoutCnt, 228 int *returnvalue, 229 heim_ipc_message_inband_t replyin, 230 mach_msg_type_number_t *replyinCnt, 231 heim_ipc_message_outband_t *replyout, 232 mach_msg_type_number_t *replyoutCnt) 233 { 234 heim_sipc ctx = dispatch_get_specific(mheim_ipc_server); 235 struct mach_call_ctx *s; 236 kern_return_t kr; 237 uid_t uid; 238 gid_t gid; 239 pid_t pid; 240 au_asid_t session; 241 242 *replyout = NULL; 243 *replyoutCnt = 0; 244 *replyinCnt = 0; 245 246 s = malloc(sizeof(*s)); 247 if (s == NULL) 248 return KERN_MEMORY_FAILURE; /* XXX */ 249 250 s->reply_port = reply_port; 251 252 audit_token_to_au32(client_creds, NULL, &uid, &gid, NULL, NULL, &pid, &session, NULL); 253 254 kr = _heim_ipc_create_cred_with_audit_token(uid, gid, pid, session, client_creds, &s->cred); 255 if (kr) { 256 free(s); 257 return kr; 258 } 259 260 _heim_ipc_suspend_timer(); 261 262 if (requestinCnt) { 263 s->req.data = malloc(requestinCnt); 264 memcpy(s->req.data, requestin, requestinCnt); 265 s->req.length = requestinCnt; 266 } else { 267 s->req.data = malloc(requestoutCnt); 268 memcpy(s->req.data, requestout, requestoutCnt); 269 s->req.length = requestoutCnt; 270 } 271 272 dispatch_async(workq, ^{ 273 (ctx->callback)(ctx->userctx, &s->req, s->cred, 274 mach_complete_sync, (heim_sipc_call)s); 275 }); 276 277 return MIG_NO_REPLY; 278 } 279 280 kern_return_t 281 mheim_do_call_request(mach_port_t server_port, 282 audit_token_t client_creds, 283 mach_port_t reply_port, 284 heim_ipc_message_inband_t requestin, 285 mach_msg_type_number_t requestinCnt, 286 heim_ipc_message_outband_t requestout, 287 mach_msg_type_number_t requestoutCnt) 288 { 289 heim_sipc ctx = dispatch_get_specific(mheim_ipc_server); 290 struct mach_call_ctx *s; 291 kern_return_t kr; 292 uid_t uid; 293 gid_t gid; 294 pid_t pid; 295 au_asid_t session; 296 297 s = malloc(sizeof(*s)); 298 if (s == NULL) 299 return KERN_MEMORY_FAILURE; /* XXX */ 300 301 s->reply_port = reply_port; 302 303 audit_token_to_au32(client_creds, NULL, &uid, &gid, NULL, NULL, &pid, &session, NULL); 304 305 kr = _heim_ipc_create_cred_with_audit_token(uid, gid, pid, session, client_creds, &s->cred); 306 if (kr) { 307 free(s); 308 return kr; 309 } 310 311 _heim_ipc_suspend_timer(); 312 313 if (requestinCnt) { 314 s->req.data = malloc(requestinCnt); 315 memcpy(s->req.data, requestin, requestinCnt); 316 s->req.length = requestinCnt; 317 } else { 318 s->req.data = malloc(requestoutCnt); 319 memcpy(s->req.data, requestout, requestoutCnt); 320 s->req.length = requestoutCnt; 321 } 322 323 dispatch_async(workq, ^{ 324 (ctx->callback)(ctx->userctx, &s->req, s->cred, 325 mach_complete_async, (heim_sipc_call)s); 326 }); 327 328 return KERN_SUCCESS; 329 } 330 331 static int 332 mach_init(const char *service, mach_port_t sport, heim_sipc ctx) 333 { 334 struct mach_service *s; 335 char *name; 336 337 init_globals(); 338 339 s = calloc(1, sizeof(*s)); 340 if (s == NULL) 341 return ENOMEM; 342 343 asprintf(&name, "heim-ipc-mach-%s", service); 344 345 s->queue = dispatch_queue_create(name, NULL); 346 free(name); 347 s->sport = sport; 348 349 s->source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, 350 s->sport, 0, s->queue); 351 if (s->source == NULL) { 352 dispatch_release(s->queue); 353 free(s); 354 return ENOMEM; 355 } 356 ctx->mech = s; 357 358 dispatch_queue_set_specific(s->queue, mheim_ipc_server, ctx, NULL); 359 360 dispatch_source_set_event_handler(s->source, ^{ 361 heim_sipc cctx = dispatch_get_specific(mheim_ipc_server); 362 struct mach_service *st = cctx->mech; 363 dispatch_mig_server(st->source, sizeof(union __RequestUnion__mheim_do_mheim_ipc_subsystem), mheim_ipc_server); 364 }); 365 366 dispatch_source_set_cancel_handler(s->source, ^{ 367 heim_sipc cctx = dispatch_get_specific(mheim_ipc_server); 368 struct mach_service *st = cctx->mech; 369 mach_port_mod_refs(mach_task_self(), st->sport, 370 MACH_PORT_RIGHT_RECEIVE, -1); 371 dispatch_release(st->queue); 372 dispatch_release(st->source); 373 free(st); 374 free(ctx); 375 }); 376 377 dispatch_resume(s->source); 378 379 return 0; 380 } 381 382 static int 383 mach_release(heim_sipc ctx) 384 { 385 struct mach_service *s = ctx->mech; 386 dispatch_source_cancel(s->source); 387 dispatch_release(s->source); 388 return 0; 389 } 390 391 static mach_port_t 392 mach_checkin_or_register(const char *service) 393 { 394 mach_port_t mp; 395 kern_return_t kr; 396 397 kr = bootstrap_check_in(bootstrap_port, service, &mp); 398 if (kr == KERN_SUCCESS) 399 return mp; 400 401 return MACH_PORT_NULL; 402 } 403 404 405 #endif /* __APPLE__ && HAVE_GCD */ 406 407 408 #ifndef HAVE_GCD 409 410 /* 411 * Signal handler logic for the non GCD case 412 */ 413 414 struct ipc_signal { 415 int signo; 416 sig_atomic_t signal_set; 417 void (*handler)(void *); 418 void *ctx; 419 }; 420 421 static struct ipc_signal *ipc_signals = NULL; 422 static size_t num_signals = 0; 423 424 static RETSIGTYPE 425 signal_handler(int signo) 426 { 427 size_t n; 428 for (n = 0; n < num_signals; n++) 429 if (ipc_signals[n].signo == signo) 430 ipc_signals[n].signal_set = 1; 431 432 SIGRETURN(0); 433 } 434 435 #endif 436 437 int 438 heim_sipc_launchd_mach_init(const char *service, 439 heim_ipc_callback callback, 440 void *user, heim_sipc *ctx) 441 { 442 #if defined(__APPLE__) && defined(HAVE_GCD) 443 mach_port_t sport = MACH_PORT_NULL; 444 heim_sipc c = NULL; 445 int ret; 446 447 *ctx = NULL; 448 449 sport = mach_checkin_or_register(service); 450 if (sport == MACH_PORT_NULL) { 451 ret = ENOENT; 452 goto error; 453 } 454 455 c = calloc(1, sizeof(*c)); 456 if (c == NULL) { 457 ret = ENOMEM; 458 goto error; 459 } 460 c->release = mach_release; 461 c->userctx = user; 462 c->callback = callback; 463 464 ret = mach_init(service, sport, c); 465 if (ret) 466 goto error; 467 468 *ctx = c; 469 return 0; 470 error: 471 if (c) 472 free(c); 473 if (sport != MACH_PORT_NULL) 474 mach_port_mod_refs(mach_task_self(), sport, 475 MACH_PORT_RIGHT_RECEIVE, -1); 476 return ret; 477 #else /* !(__APPLE__ && HAVE_GCD) */ 478 *ctx = NULL; 479 return EINVAL; 480 #endif /* __APPLE__ && HAVE_GCD */ 481 } 482 483 struct client { 484 int fd; 485 void (*handle_read)(struct client *); 486 heim_ipc_callback callback; 487 void *userctx; 488 int flags; 489 #define LISTEN_SOCKET 1 490 #define WAITING_READ 2 491 #define WAITING_WRITE 4 492 #define WRITE_RUN 8 493 494 #define WAITING_CLOSE 16 495 496 #define CLOSE_ON_REPLY 32 497 498 #define DGRAM_SOCKET 64 499 500 #define INHERIT_MASK 0xffff0000 501 #define INCLUDE_ERROR_CODE (1 << 16) 502 #define ALLOW_HTTP (1<<17) 503 #define UNIX_SOCKET (1<<18) 504 unsigned calls; 505 size_t ptr, len; 506 uint8_t *inmsg; 507 size_t olen; 508 uint8_t *outmsg; 509 #ifdef HAVE_GCD 510 dispatch_source_t in; 511 dispatch_source_t out; 512 #endif 513 514 heim_icred streamcred; 515 uint16_t dgramport; 516 }; 517 518 #ifndef HAVE_GCD 519 static unsigned num_clients = 0; 520 static struct client **clients = NULL; 521 #endif 522 523 static void handle_listen(struct client *); 524 static void handle_stream(struct client *); 525 static void handle_dgram(struct client *); 526 static void handle_write(struct client *); 527 static int maybe_close(struct client *); 528 529 struct socket_call { 530 heim_idata in; 531 struct client *c; 532 heim_icred cred; 533 }; 534 535 /* 536 * Update peer credentials from socket. 537 * 538 * SCM_CREDS can only be updated the first time there is read data to 539 * read from the filedescriptor, so if we read do it before this 540 * point, the cred data might not be is not there yet. 541 */ 542 543 static int 544 update_client_creds(struct client *c, struct socket_call *cs) 545 { 546 if (cs->cred != NULL) 547 return 1; 548 549 #ifdef HAVE_GETPEERUCRED 550 /* Solaris 10 */ 551 { 552 ucred_t *peercred; 553 554 if (getpeerucred(c->fd, &peercred) != 0) { 555 _heim_ipc_create_cred(ucred_geteuid(peercred), ucred_getegid(peercred), 0, 0, &cs->cred); 556 ucred_free(peercred); 557 return 1; 558 } 559 } 560 #endif 561 #ifdef HAVE_GETPEEREID 562 /* FreeBSD, OpenBSD */ 563 { 564 uid_t uid; 565 gid_t gid; 566 567 if (getpeereid(c->fd, &uid, &gid) == 0) { 568 _heim_ipc_create_cred(uid, gid, 0, 0, &cs->cred); 569 return 1; 570 } 571 } 572 #endif 573 #ifdef SO_PEERCRED 574 /* Linux */ 575 { 576 struct ucred pc; 577 socklen_t pclen = sizeof(pc); 578 579 if (getsockopt(c->fd, SOL_SOCKET, SO_PEERCRED, (void *)&pc, &pclen) == 0) { 580 _heim_ipc_create_cred(pc.uid, pc.gid, pc.pid, 0, &cs->cred); 581 return 1; 582 } 583 } 584 #endif 585 #if defined(LOCAL_PEERCRED) && defined(XUCRED_VERSION) 586 { 587 struct xucred peercred; 588 socklen_t peercredlen = sizeof(peercred); 589 590 if (getsockopt(c->fd, LOCAL_PEERCRED, 1, 591 (void *)&peercred, &peercredlen) == 0 592 && peercred.cr_version == XUCRED_VERSION) 593 { 594 _heim_ipc_create_cred(peercred.cr_uid, peercred.cr_gid, 0, 0, &cs->cred); 595 return 1; 596 } 597 } 598 #endif 599 #if defined(SOCKCREDSIZE) && defined(SCM_CREDS) 600 /* NetBSD */ 601 { 602 struct msghdr msg; 603 socklen_t crmsgsize; 604 void *crmsg; 605 struct cmsghdr *cmp; 606 struct sockcred *sc; 607 608 memset(&msg, 0, sizeof(msg)); 609 crmsgsize = CMSG_SPACE(SOCKCREDSIZE(NGROUPS)); 610 if (crmsgsize == 0) 611 return 1 ; 612 613 crmsg = malloc(crmsgsize); 614 if (crmsg == NULL) 615 goto failed_scm_creds; 616 617 memset(crmsg, 0, crmsgsize); 618 619 msg.msg_control = crmsg; 620 msg.msg_controllen = crmsgsize; 621 622 if (recvmsg(c->fd, &msg, 0) < 0) { 623 free(crmsg); 624 goto failed_scm_creds; 625 } 626 627 if (msg.msg_controllen == 0 || (msg.msg_flags & MSG_CTRUNC) != 0) { 628 free(crmsg); 629 goto failed_scm_creds; 630 } 631 632 cmp = CMSG_FIRSTHDR(&msg); 633 if (cmp->cmsg_level != SOL_SOCKET || cmp->cmsg_type != SCM_CREDS) { 634 free(crmsg); 635 goto failed_scm_creds; 636 } 637 638 sc = (struct sockcred *)(void *)CMSG_DATA(cmp); 639 640 _heim_ipc_create_cred(sc->sc_euid, sc->sc_egid, 0, 0, &cs->cred); 641 642 free(crmsg); 643 return 1; 644 } 645 failed_scm_creds: 646 #endif 647 return 0; 648 } 649 650 651 static struct client * 652 add_new_socket(int fd, 653 int flags, 654 heim_ipc_callback callback, 655 void *userctx) 656 { 657 struct client *c; 658 int fileflags; 659 int ret; 660 661 c = calloc(1, sizeof(*c)); 662 if (c == NULL) 663 return NULL; 664 665 c->flags = flags; 666 c->callback = callback; 667 c->userctx = userctx; 668 669 if (flags & LISTEN_SOCKET) { 670 c->handle_read = handle_listen; 671 c->fd = fd; 672 } else if (flags & DGRAM_SOCKET) { 673 c->handle_read = handle_dgram; 674 c->fd = fd; 675 } else { 676 c->handle_read = handle_stream; 677 c->fd = accept(fd, NULL, NULL); 678 if (c->fd < 0) { 679 free(c); 680 return NULL; 681 } 682 } 683 684 fileflags = fcntl(c->fd, F_GETFL, 0); 685 fcntl(c->fd, F_SETFL, fileflags | O_NONBLOCK); 686 687 if (c->flags & DGRAM_SOCKET) { 688 struct sockaddr_storage ss; 689 krb5_socklen_t sssize; 690 int on = 1; 691 692 /* if its a dgram socket, don't allocate anything */ 693 c->len = 0; 694 c->inmsg = NULL; 695 c->ptr = 0; 696 697 sssize = sizeof(ss); 698 ret = getsockname(c->fd, (struct sockaddr *)&ss, &sssize); 699 if (ret == 0) 700 c->dgramport = socket_get_port((struct sockaddr *)&ss); 701 702 703 #ifdef IPV6_RECVPKTINFO 704 setsockopt(c->fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); 705 #endif 706 #ifdef IP_RECVPKTINFO 707 setsockopt(c->fd, IPPROTO_IP, IP_RECVPKTINFO, &on, sizeof(on)); 708 #endif 709 } else if ((flags & LISTEN_SOCKET) == 0) { 710 struct sockaddr_storage server, client; 711 krb5_socklen_t server_size = sizeof(server), 712 client_size = sizeof(client); 713 714 ret = getpeername(c->fd, (struct sockaddr *)&client, &client_size); 715 if (ret == 0) 716 ret = getsockname(c->fd, (struct sockaddr *)&server, &server_size); 717 718 if (ret) { 719 free(c); 720 close(fd); 721 return NULL; 722 } 723 724 ret = _heim_ipc_create_network_cred((struct sockaddr *)&client, client_size, 725 (struct sockaddr *)&server, server_size, 726 &c->streamcred); 727 if (ret) 728 abort(); 729 } 730 731 #ifdef HAVE_GCD 732 init_globals(); 733 734 c->in = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, 735 c->fd, 0, eventq); 736 c->out = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, 737 c->fd, 0, eventq); 738 739 dispatch_source_set_event_handler(c->in, ^{ 740 c->handle_read(c); 741 if ((c->flags & (WAITING_WRITE|WRITE_RUN)) == WAITING_WRITE) { 742 c->flags |= WRITE_RUN; 743 dispatch_resume(c->out); 744 } 745 if ((c->flags & WAITING_READ) == 0) 746 dispatch_suspend(c->in); 747 maybe_close(c); 748 }); 749 dispatch_source_set_event_handler(c->out, ^{ 750 assert((c->flags & WRITE_RUN) != 0); 751 handle_write(c); 752 if ((c->flags & WAITING_WRITE) == 0) { 753 c->flags &= ~WRITE_RUN; 754 dispatch_suspend(c->out); 755 } 756 maybe_close(c); 757 }); 758 759 dispatch_resume(c->in); 760 #else 761 clients = erealloc(clients, sizeof(clients[0]) * (num_clients + 1)); 762 clients[num_clients] = c; 763 num_clients++; 764 #endif 765 766 return c; 767 } 768 769 static int 770 maybe_close(struct client *c) 771 { 772 if (c->calls != 0) 773 return 0; 774 if (c->flags & (WAITING_READ|WAITING_WRITE)) 775 return 0; 776 777 #ifdef HAVE_GCD 778 dispatch_source_cancel(c->in); 779 if ((c->flags & WAITING_READ) == 0) 780 dispatch_resume(c->in); 781 dispatch_release(c->in); 782 783 dispatch_source_cancel(c->out); 784 785 if ((c->flags & WRITE_RUN) == 0) { 786 c->flags |= WRITE_RUN; 787 dispatch_resume(c->out); 788 } 789 dispatch_release(c->out); 790 #endif 791 close(c->fd); /* ref count fd close */ 792 free(c->inmsg); 793 794 if (c->streamcred) 795 heim_ipc_free_cred(c->streamcred); 796 797 free(c); 798 799 return 1; 800 } 801 802 static void 803 output_data(struct client *c, const void *data, size_t len) 804 { 805 if (c->olen + len < c->olen) 806 abort(); 807 if (len) { 808 c->outmsg = erealloc(c->outmsg, c->olen + len); 809 memcpy(&c->outmsg[c->olen], data, len); 810 c->olen += len; 811 c->flags |= WAITING_WRITE; 812 } 813 } 814 815 static void 816 stream_complete(heim_sipc_call ctx, int returnvalue, heim_idata *reply) 817 { 818 struct socket_call *sc = (struct socket_call *)ctx; 819 struct client *c = sc->c; 820 821 /* double complete ? */ 822 if (c == NULL) 823 abort(); 824 825 if ((c->flags & WAITING_CLOSE) == 0) { 826 uint32_t u32; 827 828 /* length */ 829 if ((c->flags & DGRAM_SOCKET) == 0) { 830 u32 = htonl((uint32_t)reply->length); 831 output_data(c, &u32, sizeof(u32)); 832 } 833 834 /* return value */ 835 if (c->flags & INCLUDE_ERROR_CODE) { 836 u32 = htonl(returnvalue); 837 output_data(c, &u32, sizeof(u32)); 838 } 839 840 /* data */ 841 output_data(c, reply->data, reply->length); 842 843 if ((c->flags & (WRITE_RUN|WAITING_WRITE)) == WAITING_WRITE) { 844 c->flags |= WRITE_RUN; 845 dispatch_resume(c->out); 846 } 847 848 /* if HTTP, close connection */ 849 if (c->flags & CLOSE_ON_REPLY) { 850 c->flags |= WAITING_CLOSE; 851 c->flags &= ~WAITING_READ; 852 } 853 } 854 855 c->calls--; 856 if (sc->cred) 857 heim_ipc_free_cred(sc->cred); 858 free(sc->in.data); 859 sc->c = NULL; /* so we can catch double complete */ 860 861 heim_assert(sc->cred == NULL, "cred on handle for stream ?"); 862 free(sc); 863 864 maybe_close(c); 865 } 866 867 static void 868 dgram_complete(heim_sipc_call ctx, int returnvalue, heim_idata *reply) 869 { 870 struct socket_call *sc = (struct socket_call *)ctx; 871 struct client *c = sc->c; 872 struct msghdr hdr; 873 struct iovec iov[2], *iovp; 874 uint32_t u32rv; 875 struct cmsghdr *cm; 876 void *cmsg; 877 size_t cmsglen = 0; 878 krb5_socklen_t namelen; 879 struct sockaddr *client, *server; 880 881 sc->c = NULL; /* so we can catch double complete */ 882 883 /* double complete ? */ 884 heim_assert(c != NULL, "dgram request packet already completed"); 885 heim_assert(sc->cred != NULL, "no cred on dgram transaction"); 886 887 888 #ifdef IPV6_RECVPKTINFO 889 cmsglen += CMSG_SPACE(sizeof(struct in6_pktinfo)); 890 #endif 891 #ifdef IP_RECVPKTINFO 892 cmsglen += CMSG_SPACE(sizeof(struct in_pktinfo)); 893 #endif 894 cmsg = calloc(1, cmsglen); 895 heim_assert(cmsg != NULL, "out of memory"); 896 897 iovp = &iov[0]; 898 899 if (c->flags & INCLUDE_ERROR_CODE) { 900 u32rv = htonl(returnvalue); 901 iovp->iov_base = &u32rv; 902 iovp->iov_len = sizeof(u32rv); 903 iovp++; 904 } 905 906 iovp->iov_base = reply->data; 907 iovp->iov_len = reply->length; 908 iovp++; 909 910 client = heim_ipc_cred_get_client_address(sc->cred, &namelen); 911 912 hdr.msg_name = (void *)client; 913 hdr.msg_namelen = namelen; 914 915 hdr.msg_iov = iov; 916 hdr.msg_iovlen = (int)(iovp - iov); 917 hdr.msg_control = cmsg; 918 hdr.msg_controllen = (socklen_t)cmsglen; 919 920 server = heim_ipc_cred_get_server_address(sc->cred, &namelen); 921 922 switch (server->sa_family) { 923 case AF_INET6: { 924 #ifdef IPV6_RECVPKTINFO 925 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)server; 926 struct in6_pktinfo *pi6; 927 928 cm = CMSG_FIRSTHDR(&hdr); 929 cm->cmsg_level = IPPROTO_IPV6; 930 cm->cmsg_type = IPV6_PKTINFO; 931 cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); 932 pi6 = (struct in6_pktinfo *)CMSG_DATA(cm); 933 934 memcpy(&pi6->ipi6_addr, &sin6->sin6_addr, sizeof(pi6->ipi6_addr)); 935 pi6->ipi6_ifindex = 0; 936 937 hdr.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); 938 #endif 939 break; 940 } 941 case AF_INET: { 942 #ifdef IP_RECVPKTINFO 943 struct sockaddr_in *sin = (struct sockaddr_in *)server; 944 struct in_pktinfo *pi4; 945 946 cm = CMSG_FIRSTHDR(&hdr); 947 cm->cmsg_level = IPPROTO_IP; 948 cm->cmsg_type = IP_PKTINFO; 949 cm->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); 950 pi4 = (struct in_pktinfo *)CMSG_DATA(cm); 951 952 pi4->ipi_ifindex = 0; 953 pi4->ipi_spec_dst = sin->sin_addr; 954 955 hdr.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo)); 956 #endif 957 break; 958 } 959 default: 960 hdr.msg_control = NULL; 961 hdr.msg_controllen = 0; 962 break; 963 } 964 965 (void)sendmsg(c->fd, &hdr, 0); 966 free(cmsg); 967 968 c->calls--; 969 970 free(sc->in.data); 971 heim_ipc_free_cred(sc->cred); 972 sc->cred = NULL; 973 free(sc); 974 975 maybe_close(c); 976 } 977 978 979 /* remove HTTP %-quoting from buf */ 980 static int 981 de_http(char *buf) 982 { 983 unsigned char *p, *q; 984 for(p = q = (unsigned char *)buf; *p; p++, q++) { 985 if (*p == '%' && isxdigit(p[1]) && isxdigit(p[2])) { 986 unsigned int x; 987 if (sscanf((char *)p + 1, "%2x", &x) != 1) 988 return -1; 989 *q = x; 990 p += 2; 991 } else 992 *q = *p; 993 } 994 *q = '\0'; 995 return 0; 996 } 997 998 static struct socket_call * 999 handle_http_tcp(struct client *c, heim_icred cred) 1000 { 1001 struct socket_call *cs; 1002 char *s, *p, *t; 1003 void *data; 1004 char *proto; 1005 int len; 1006 1007 s = (char *)c->inmsg; 1008 1009 /* If its a multi line query, truncate off the first line */ 1010 p = strstr(s, "\r\n"); 1011 if (p) 1012 *p = 0; 1013 1014 p = NULL; 1015 t = strtok_r(s, " \t", &p); 1016 if (t == NULL) 1017 return NULL; 1018 1019 t = strtok_r(NULL, " \t", &p); 1020 if (t == NULL) 1021 return NULL; 1022 1023 data = malloc(strlen(t)); 1024 if (data == NULL) 1025 return NULL; 1026 1027 if (*t == '/') 1028 t++; 1029 if (de_http(t) != 0) { 1030 free(data); 1031 return NULL; 1032 } 1033 proto = strtok_r(NULL, " \t", &p); 1034 if (proto == NULL) { 1035 free(data); 1036 return NULL; 1037 } 1038 len = base64_decode(t, data); 1039 1040 if (len <= 0) { 1041 const char *msg = 1042 "HTTP/1.0 404 Not found\r\n" 1043 "Server: Heimdal/" VERSION "\r\n" 1044 "Cache-Control: no-cache\r\n" 1045 "Pragma: no-cache\r\n" 1046 "Content-type: text/html\r\n" 1047 "Content-transfer-encoding: 8bit\r\n\r\n" 1048 "<TITLE>404 Not found</TITLE>\r\n" 1049 "<H1>404 Not found</H1>\r\n" 1050 "That page doesn't exist, maybe you are looking for " 1051 "<A HREF=\"http://www.h5l.org/\">Heimdal</A>?\r\n"; 1052 free(data); 1053 output_data(c, msg, strlen(msg)); 1054 return NULL; 1055 } 1056 1057 cs = emalloc(sizeof(*cs)); 1058 cs->c = c; 1059 cs->in.data = data; 1060 cs->in.length = len; 1061 cs->cred = NULL; 1062 c->ptr = 0; 1063 1064 { 1065 const char *msg = 1066 " 200 OK\r\n" 1067 "Server: Heimdal/" VERSION "\r\n" 1068 "Cache-Control: no-cache\r\n" 1069 "Pragma: no-cache\r\n" 1070 "Content-type: application/octet-stream\r\n" 1071 "Content-transfer-encoding: binary\r\n\r\n"; 1072 output_data(c, proto, strlen(proto)); 1073 output_data(c, msg, strlen(msg)); 1074 } 1075 1076 return cs; 1077 } 1078 1079 static struct sockaddr * 1080 capture_localaddr(struct msghdr *hdr, uint16_t port, struct sockaddr *sa, krb5_socklen_t *sslen) 1081 { 1082 struct cmsghdr *cm; 1083 1084 for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(hdr); cm; 1085 cm = (struct cmsghdr *)CMSG_NXTHDR(hdr, cm)) { 1086 #ifdef IPV6_RECVPKTINFO 1087 if (cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_PKTINFO && 1088 cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { 1089 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; 1090 struct in6_pktinfo *pi6; 1091 1092 pi6 = (struct in6_pktinfo *)(CMSG_DATA(cm)); 1093 1094 sin6->sin6_family = AF_INET6; 1095 memcpy(&sin6->sin6_addr, &pi6->ipi6_addr, sizeof(pi6->ipi6_addr)); 1096 sin6->sin6_port = port; 1097 1098 *sslen = sizeof(struct sockaddr_in6); 1099 1100 return sa; 1101 } 1102 #endif 1103 #ifdef IP_RECVPKTINFO 1104 if (cm->cmsg_level == IPPROTO_IP && cm->cmsg_type == IP_PKTINFO && 1105 cm->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) { 1106 struct sockaddr_in *sin = (struct sockaddr_in *)sa; 1107 struct in_pktinfo *pi4; 1108 1109 pi4 = (struct in_pktinfo *)(CMSG_DATA(cm)); 1110 1111 sin->sin_family = AF_INET; 1112 sin->sin_addr = pi4->ipi_addr; 1113 sin->sin_port = port; 1114 1115 *sslen = sizeof(struct sockaddr_in); 1116 1117 return sa; 1118 } 1119 #endif 1120 } 1121 return NULL; 1122 } 1123 1124 static void 1125 handle_dgram(struct client *c) 1126 { 1127 krb5_socklen_t server_size = 0; 1128 struct sockaddr_storage clientss, serverss; 1129 struct sockaddr *server = NULL; 1130 struct socket_call *cs; 1131 struct msghdr hdr; 1132 size_t cmsglen = 0; 1133 void *cmsg; 1134 heim_idata data; 1135 struct iovec iov[1]; 1136 int ret; 1137 ssize_t len; 1138 heim_icred cred; 1139 1140 heim_assert(c->inmsg == NULL, "dgram have data buffer in struct client"); 1141 1142 memset(&clientss, 0, sizeof(clientss)); 1143 memset(&serverss, 0, sizeof(serverss)); 1144 1145 #ifdef IPV6_RECVPKTINFO 1146 cmsglen += CMSG_SPACE(sizeof(struct in6_pktinfo)); 1147 #endif 1148 #ifdef IP_RECVPKTINFO 1149 cmsglen += CMSG_SPACE(sizeof(struct in_pktinfo)); 1150 #endif 1151 cmsg = malloc(cmsglen); 1152 1153 data.data = emalloc(MAX_PACKET_SIZE); 1154 data.length = MAX_PACKET_SIZE; 1155 1156 iov[0].iov_base = data.data; 1157 iov[0].iov_len = data.length; 1158 1159 hdr.msg_name = (void *)&clientss; 1160 hdr.msg_namelen = sizeof(clientss); 1161 hdr.msg_iov = iov; 1162 hdr.msg_iovlen = 1; 1163 hdr.msg_control = cmsg; 1164 hdr.msg_controllen = (socklen_t)cmsglen; 1165 1166 len = recvmsg(c->fd, &hdr, 0); 1167 if (len <= 0) { 1168 free(data.data); 1169 free(cmsg); 1170 return; 1171 } 1172 1173 heim_assert(len <= MAX_PACKET_SIZE, "packet too large!"); 1174 1175 data.length = len; 1176 1177 server = capture_localaddr(&hdr, c->dgramport, (struct sockaddr *)&serverss, &server_size); 1178 1179 free(cmsg); 1180 1181 ret = _heim_ipc_create_network_cred(hdr.msg_name, hdr.msg_namelen, 1182 server, server_size, &cred); 1183 if (ret) 1184 abort(); 1185 1186 cs = emalloc(sizeof(*cs)); 1187 cs->c = c; 1188 cs->in = data; 1189 cs->cred = cred; 1190 1191 c->calls++; 1192 c->callback(c->userctx, &cs->in, 1193 cs->cred, dgram_complete, 1194 (heim_sipc_call)cs); 1195 return; 1196 } 1197 1198 static void 1199 handle_listen(struct client *c) 1200 { 1201 (void)add_new_socket(c->fd, 1202 WAITING_READ | (c->flags & INHERIT_MASK), 1203 c->callback, 1204 c->userctx); 1205 } 1206 1207 static void 1208 handle_stream(struct client *c) 1209 { 1210 uint32_t dlen; 1211 ssize_t len; 1212 1213 heim_assert(c->len >= c->ptr, "ptr past end of buffer"); 1214 1215 if (c->len - c->ptr < 1024) { 1216 c->inmsg = erealloc(c->inmsg, 1217 c->len + 1024); 1218 c->len += 1024; 1219 } 1220 1221 len = read(c->fd, c->inmsg + c->ptr, c->len - c->ptr); 1222 if (len <= 0) { 1223 c->flags |= WAITING_CLOSE; 1224 c->flags &= ~WAITING_READ; 1225 return; 1226 } 1227 c->ptr += len; 1228 if (c->ptr > c->len) 1229 abort(); 1230 1231 while (c->ptr >= sizeof(dlen)) { 1232 struct socket_call *cs; 1233 1234 if ((c->flags & ALLOW_HTTP) && strncmp((char *)c->inmsg, "GET ", 4) == 0) { 1235 1236 if (strncmp((char *)c->inmsg + c->ptr - 4, "\r\n\r\n", 4) != 0) 1237 break; 1238 1239 /* remove the trailing \r\n\r\n so the string is NUL terminated */ 1240 c->inmsg[c->ptr - 4] = '\0'; 1241 1242 c->flags |= CLOSE_ON_REPLY; 1243 1244 cs = handle_http_tcp(c, c->streamcred); 1245 if (cs == NULL) { 1246 c->flags |= WAITING_CLOSE; 1247 c->flags &= ~WAITING_READ; 1248 break; 1249 } 1250 } else { 1251 memcpy(&dlen, c->inmsg, sizeof(dlen)); 1252 dlen = ntohl(dlen); 1253 1254 if (dlen > MAX_PACKET_SIZE) { 1255 c->flags |= WAITING_CLOSE; 1256 c->flags &= ~WAITING_READ; 1257 return; 1258 } 1259 if (dlen > c->ptr - sizeof(dlen)) { 1260 break; 1261 } 1262 1263 cs = emalloc(sizeof(*cs)); 1264 cs->c = c; 1265 cs->cred = NULL; 1266 cs->in.data = emalloc(dlen); 1267 memcpy(cs->in.data, c->inmsg + sizeof(dlen), dlen); 1268 cs->in.length = dlen; 1269 1270 c->ptr -= sizeof(dlen) + dlen; 1271 memmove(c->inmsg, 1272 c->inmsg + sizeof(dlen) + dlen, 1273 c->ptr); 1274 } 1275 1276 c->calls++; 1277 1278 if (c->flags & UNIX_SOCKET) { 1279 if (update_client_creds(c, cs) == 0) { 1280 c->flags |= WAITING_CLOSE; 1281 c->flags &= ~WAITING_READ; 1282 return; 1283 } 1284 } 1285 1286 c->callback(c->userctx, &cs->in, 1287 c->streamcred, stream_complete, 1288 (heim_sipc_call)cs); 1289 } 1290 } 1291 1292 static void 1293 handle_write(struct client *c) 1294 { 1295 ssize_t len; 1296 1297 heim_assert(c->olen != 0, "output buffer is zero on write"); 1298 1299 len = sendto(c->fd, c->outmsg, c->olen, 0, NULL, 0); 1300 if (len < 0) { 1301 c->flags |= WAITING_CLOSE; 1302 c->flags &= ~(WAITING_WRITE); 1303 } else if (c->olen != (size_t)len) { 1304 memmove(&c->outmsg[0], &c->outmsg[len], c->olen - len); 1305 c->olen -= len; 1306 } else { 1307 c->olen = 0; 1308 free(c->outmsg); 1309 c->outmsg = NULL; 1310 c->flags &= ~(WAITING_WRITE); 1311 } 1312 } 1313 1314 1315 #ifndef HAVE_GCD 1316 1317 static void 1318 process_loop(void) 1319 { 1320 struct pollfd *fds; 1321 unsigned n; 1322 unsigned num_fds; 1323 int ret; 1324 1325 while (num_clients > 0) { 1326 1327 fds = malloc(num_clients * sizeof(fds[0])); 1328 if (fds == NULL) 1329 abort(); 1330 1331 num_fds = num_clients; 1332 1333 for (n = 0 ; n < num_fds; n++) { 1334 fds[n].fd = clients[n]->fd; 1335 fds[n].events = 0; 1336 if (clients[n]->flags & WAITING_READ) 1337 fds[n].events |= POLLIN; 1338 if (clients[n]->flags & WAITING_WRITE) 1339 fds[n].events |= POLLOUT; 1340 1341 fds[n].revents = 0; 1342 } 1343 1344 poll(fds, num_fds, -1); 1345 1346 for (n = 0 ; n < num_fds; n++) { 1347 if (clients[n] == NULL) 1348 continue; 1349 if (fds[n].revents & POLLERR) { 1350 clients[n]->flags |= WAITING_CLOSE; 1351 continue; 1352 } 1353 1354 if (fds[n].revents & POLLIN) 1355 c->handle_read(clients[n]); 1356 if (fds[n].revents & POLLOUT) 1357 handle_write(clients[n]); 1358 } 1359 1360 n = 0; 1361 while (n < num_clients) { 1362 struct client *c = clients[n]; 1363 if (maybe_close(c)) { 1364 if (n < num_clients - 1) 1365 clients[n] = clients[num_clients - 1]; 1366 num_clients--; 1367 } else 1368 n++; 1369 } 1370 1371 free(fds); 1372 1373 for (n = 0; n < num_signals; n++) { 1374 if (ipc_signals[n].signal_set) { 1375 ipc_signals[n].signal_set = 0; 1376 ipc_signals[n].handler(ipc_signals[n].ctx); 1377 } 1378 } 1379 } 1380 } 1381 1382 #endif 1383 1384 static int 1385 socket_release(heim_sipc ctx) 1386 { 1387 struct client *c = ctx->mech; 1388 c->flags |= WAITING_CLOSE; 1389 return 0; 1390 } 1391 1392 int 1393 heim_sipc_stream_listener(int fd, int type, 1394 heim_ipc_callback callback, 1395 void *user, heim_sipc *ctx) 1396 { 1397 heim_sipc ct = calloc(1, sizeof(*ct)); 1398 struct client *c; 1399 int flags = LISTEN_SOCKET|WAITING_READ; 1400 1401 if ((type & HEIM_SIPC_TYPE_IPC) && (type & (HEIM_SIPC_TYPE_UINT32|HEIM_SIPC_TYPE_HTTP))) { 1402 free(ct); 1403 return EINVAL; 1404 } 1405 1406 if (type & HEIM_SIPC_TYPE_ONE_REQUEST) { 1407 flags |= CLOSE_ON_REPLY; 1408 type &= ~HEIM_SIPC_TYPE_ONE_REQUEST; 1409 } 1410 1411 switch (type) { 1412 case HEIM_SIPC_TYPE_IPC: 1413 c = add_new_socket(fd, flags|INCLUDE_ERROR_CODE, callback, user); 1414 break; 1415 case HEIM_SIPC_TYPE_UINT32: 1416 c = add_new_socket(fd, flags, callback, user); 1417 break; 1418 case HEIM_SIPC_TYPE_HTTP: 1419 case HEIM_SIPC_TYPE_UINT32|HEIM_SIPC_TYPE_HTTP: 1420 c = add_new_socket(fd, flags|ALLOW_HTTP, callback, user); 1421 break; 1422 default: 1423 free(ct); 1424 return EINVAL; 1425 } 1426 1427 ct->mech = c; 1428 ct->release = socket_release; 1429 1430 *ctx = ct; 1431 return 0; 1432 } 1433 1434 int 1435 heim_sipc_service_dgram(int fd, int type, 1436 heim_ipc_callback callback, 1437 void *user, heim_sipc *ctx) 1438 { 1439 heim_sipc ct = calloc(1, sizeof(*ct)); 1440 1441 if (type != 0) { 1442 free(ct); 1443 return EINVAL; 1444 } 1445 1446 ct->mech = add_new_socket(fd, WAITING_READ|DGRAM_SOCKET, callback, user); 1447 if (ct->mech == NULL) { 1448 free(ct); 1449 return ENOMEM; 1450 } 1451 1452 ct->release = socket_release; 1453 *ctx = ct; 1454 return 0; 1455 } 1456 1457 int 1458 heim_sipc_service_unix(const char *service, 1459 heim_ipc_callback callback, 1460 void *user, heim_sipc *ctx) 1461 { 1462 struct sockaddr_un un; 1463 int fd, ret; 1464 1465 un.sun_family = AF_UNIX; 1466 1467 snprintf(un.sun_path, sizeof(un.sun_path), 1468 "/var/run/.heim_%s-socket", service); 1469 fd = socket(AF_UNIX, SOCK_STREAM, 0); 1470 if (fd < 0) 1471 return errno; 1472 1473 socket_set_nopipe(fd, 1); 1474 socket_set_reuseaddr(fd, 1); 1475 #ifdef LOCAL_CREDS 1476 { 1477 int one = 1; 1478 setsockopt(fd, 0, LOCAL_CREDS, (void *)&one, sizeof(one)); 1479 } 1480 #endif 1481 1482 unlink(un.sun_path); 1483 1484 if (bind(fd, (struct sockaddr *)&un, sizeof(un)) < 0) { 1485 close(fd); 1486 return errno; 1487 } 1488 1489 if (listen(fd, SOMAXCONN) < 0) { 1490 close(fd); 1491 return errno; 1492 } 1493 1494 chmod(un.sun_path, 0666); 1495 1496 ret = heim_sipc_stream_listener(fd, HEIM_SIPC_TYPE_IPC, 1497 callback, user, ctx); 1498 if (ret) { 1499 close(fd); 1500 } else { 1501 struct client *c = (*ctx)->mech; 1502 c->flags |= UNIX_SOCKET; 1503 } 1504 1505 return ret; 1506 } 1507 1508 /** 1509 * Set the idle timeout value 1510 1511 * The timeout event handler is triggered recurrently every idle 1512 * period `t'. The default action is rather draconian and just calls 1513 * exit(0), so you might want to change this to something more 1514 * graceful using heim_sipc_set_timeout_handler(). 1515 */ 1516 1517 void 1518 heim_sipc_timeout(time_t t) 1519 { 1520 #ifdef HAVE_GCD 1521 static dispatch_once_t timeoutonce; 1522 init_globals(); 1523 dispatch_sync(timerq, ^{ 1524 timeoutvalue = t; 1525 set_timer(); 1526 }); 1527 dispatch_once(&timeoutonce, ^{ dispatch_resume(timer); }); 1528 #else 1529 abort(); 1530 #endif 1531 } 1532 1533 /** 1534 * Set the timeout event handler 1535 * 1536 * Replaces the default idle timeout action. 1537 */ 1538 1539 void 1540 heim_sipc_set_timeout_handler(void (*func)(void)) 1541 { 1542 #ifdef HAVE_GCD 1543 init_globals(); 1544 dispatch_sync(timerq, ^{ timer_ev = func; }); 1545 #else 1546 abort(); 1547 #endif 1548 } 1549 1550 void 1551 heim_sipc_free_context(heim_sipc ctx) 1552 { 1553 (ctx->release)(ctx); 1554 } 1555 1556 /** 1557 * Start processing events for the heimdal event loop 1558 */ 1559 1560 void 1561 heim_ipc_main(void) 1562 { 1563 #ifdef HAVE_GCD 1564 init_globals(); 1565 1566 /** 1567 * We only start to process events after we run heim_ipc_main() to 1568 * make sure we are propperly chrooted()/sandboxed. 1569 */ 1570 dispatch_resume(eventq); 1571 dispatch_main(); 1572 #else 1573 process_loop(); 1574 #endif 1575 } 1576 1577 void 1578 heim_sipc_signal_handler(int signo, void (*handler)(void *), void *ctx) 1579 { 1580 #ifdef HAVE_GCD 1581 init_globals(); 1582 1583 dispatch_sync(timerq, ^{ 1584 struct dispatch_signal *signal; 1585 1586 signal = calloc(1, sizeof(*signal)); 1587 1588 signal->s = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, signo, 0, workq); 1589 if (signal->s == NULL) 1590 abort(); 1591 1592 dispatch_source_set_event_handler(signal->s, ^{ 1593 handler(ctx); 1594 }); 1595 dispatch_resume(signal->s); 1596 1597 /* avoid leaks(1) finding leaked memory */ 1598 signal->next = dispatch_signals; 1599 dispatch_signals = signal; 1600 }); 1601 #else /* !HAVE_GCD */ 1602 1603 ipc_signals = realloc(ipc_signals, sizeof(ipc_signals[0]) * (num_signals + 1)); 1604 if (ipc_signals == NULL) 1605 abort(); 1606 1607 ipc_signals[num_signals].signo = signo; 1608 ipc_signals[num_signals].handler = handler; 1609 ipc_signals[num_signals].ctx = ctx; 1610 num_signals++; 1611 1612 #ifdef HAVE_SIGACTION 1613 { 1614 struct sigaction sa; 1615 1616 sa.sa_flags = 0; 1617 #ifdef SA_RESTART 1618 sa.sa_flags |= SA_RESTART; 1619 #endif 1620 sa.sa_handler = signal_handler; 1621 sigemptyset(&sa.sa_mask); 1622 1623 sigaction(signo, &sa, NULL); 1624 } 1625 #else /* !HAVE_SIGACTION */ 1626 signal(SIGINT, signal_hander); 1627 #endif /* !HAVE_SIGACTION */ 1628 1629 #endif /* !HAVE_GCD */ 1630 }