/ lib / ipc / server.c
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  }