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