Home | History | Annotate | Line # | Download | only in ipc
server.c revision 1.1.1.1.10.1
      1 /*	$NetBSD: server.c,v 1.1.1.1.10.1 2014/08/19 23:45:19 tls Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2009 Kungliga Tekniska Hgskolan
      5  * (Royal Institute of Technology, Stockholm, Sweden).
      6  * All rights reserved.
      7  *
      8  * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  *
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  *
     17  * 2. Redistributions in binary form must reproduce the above copyright
     18  *    notice, this list of conditions and the following disclaimer in the
     19  *    documentation and/or other materials provided with the distribution.
     20  *
     21  * 3. Neither the name of the Institute nor the names of its contributors
     22  *    may be used to endorse or promote products derived from this software
     23  *    without specific prior written permission.
     24  *
     25  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
     26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
     29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     35  * SUCH DAMAGE.
     36  */
     37 
     38 #include "hi_locl.h"
     39 #include <assert.h>
     40 
     41 #define MAX_PACKET_SIZE (128 * 1024)
     42 
     43 struct heim_sipc {
     44     int (*release)(heim_sipc ctx);
     45     heim_ipc_callback callback;
     46     void *userctx;
     47     void *mech;
     48 };
     49 
     50 #if defined(__APPLE__) && defined(HAVE_GCD)
     51 
     52 #include "heim_ipcServer.h"
     53 #include "heim_ipc_reply.h"
     54 #include "heim_ipc_async.h"
     55 
     56 static dispatch_source_t timer;
     57 static dispatch_queue_t timerq;
     58 static uint64_t timeoutvalue;
     59 
     60 static dispatch_queue_t eventq;
     61 
     62 static dispatch_queue_t workq;
     63 
     64 static void
     65 default_timer_ev(void)
     66 {
     67     exit(0);
     68 }
     69 
     70 static void (*timer_ev)(void) = default_timer_ev;
     71 
     72 static void
     73 set_timer(void)
     74 {
     75     dispatch_source_set_timer(timer,
     76 			      dispatch_time(DISPATCH_TIME_NOW,
     77 					    timeoutvalue * NSEC_PER_SEC),
     78 			      timeoutvalue * NSEC_PER_SEC, 1000000);
     79 }
     80 
     81 static void
     82 init_globals(void)
     83 {
     84     static dispatch_once_t once;
     85     dispatch_once(&once, ^{
     86 	timerq = dispatch_queue_create("hiem-sipc-timer-q", NULL);
     87         timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, timerq);
     88 	dispatch_source_set_event_handler(timer, ^{ timer_ev(); } );
     89 
     90 	workq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
     91 	eventq = dispatch_queue_create("heim-ipc.event-queue", NULL);
     92     });
     93 }
     94 
     95 static void
     96 suspend_timer(void)
     97 {
     98     dispatch_suspend(timer);
     99 }
    100 
    101 static void
    102 restart_timer(void)
    103 {
    104     dispatch_sync(timerq, ^{ set_timer(); });
    105     dispatch_resume(timer);
    106 }
    107 
    108 struct mach_service {
    109     mach_port_t sport;
    110     dispatch_source_t source;
    111     dispatch_queue_t queue;
    112 };
    113 
    114 struct mach_call_ctx {
    115     mach_port_t reply_port;
    116     heim_icred cred;
    117     heim_idata req;
    118 };
    119 
    120 
    121 static void
    122 mach_complete_sync(heim_sipc_call ctx, int returnvalue, heim_idata *reply)
    123 {
    124     struct mach_call_ctx *s = (struct mach_call_ctx *)ctx;
    125     heim_ipc_message_inband_t replyin;
    126     mach_msg_type_number_t replyinCnt;
    127     heim_ipc_message_outband_t replyout;
    128     mach_msg_type_number_t replyoutCnt;
    129     kern_return_t kr;
    130 
    131     if (returnvalue) {
    132 	/* on error, no reply */
    133 	replyinCnt = 0;
    134 	replyout = 0; replyoutCnt = 0;
    135 	kr = KERN_SUCCESS;
    136     } else if (reply->length < 2048) {
    137 	replyinCnt = reply->length;
    138 	memcpy(replyin, reply->data, replyinCnt);
    139 	replyout = 0; replyoutCnt = 0;
    140 	kr = KERN_SUCCESS;
    141     } else {
    142 	replyinCnt = 0;
    143 	kr = vm_read(mach_task_self(),
    144 		     (vm_address_t)reply->data, reply->length,
    145 		     (vm_address_t *)&replyout, &replyoutCnt);
    146     }
    147 
    148     mheim_ripc_call_reply(s->reply_port, returnvalue,
    149 			  replyin, replyinCnt,
    150 			  replyout, replyoutCnt);
    151 
    152     heim_ipc_free_cred(s->cred);
    153     free(s->req.data);
    154     free(s);
    155     restart_timer();
    156 }
    157 
    158 static void
    159 mach_complete_async(heim_sipc_call ctx, int returnvalue, heim_idata *reply)
    160 {
    161     struct mach_call_ctx *s = (struct mach_call_ctx *)ctx;
    162     heim_ipc_message_inband_t replyin;
    163     mach_msg_type_number_t replyinCnt;
    164     heim_ipc_message_outband_t replyout;
    165     mach_msg_type_number_t replyoutCnt;
    166     kern_return_t kr;
    167 
    168     if (returnvalue) {
    169 	/* on error, no reply */
    170 	replyinCnt = 0;
    171 	replyout = 0; replyoutCnt = 0;
    172 	kr = KERN_SUCCESS;
    173     } else if (reply->length < 2048) {
    174 	replyinCnt = reply->length;
    175 	memcpy(replyin, reply->data, replyinCnt);
    176 	replyout = 0; replyoutCnt = 0;
    177 	kr = KERN_SUCCESS;
    178     } else {
    179 	replyinCnt = 0;
    180 	kr = vm_read(mach_task_self(),
    181 		     (vm_address_t)reply->data, reply->length,
    182 		     (vm_address_t *)&replyout, &replyoutCnt);
    183     }
    184 
    185     kr = mheim_aipc_acall_reply(s->reply_port, returnvalue,
    186 				replyin, replyinCnt,
    187 				replyout, replyoutCnt);
    188     heim_ipc_free_cred(s->cred);
    189     free(s->req.data);
    190     free(s);
    191     restart_timer();
    192 }
    193 
    194 
    195 kern_return_t
    196 mheim_do_call(mach_port_t server_port,
    197 	      audit_token_t client_creds,
    198 	      mach_port_t reply_port,
    199 	      heim_ipc_message_inband_t requestin,
    200 	      mach_msg_type_number_t requestinCnt,
    201 	      heim_ipc_message_outband_t requestout,
    202 	      mach_msg_type_number_t requestoutCnt,
    203 	      int *returnvalue,
    204 	      heim_ipc_message_inband_t replyin,
    205 	      mach_msg_type_number_t *replyinCnt,
    206 	      heim_ipc_message_outband_t *replyout,
    207 	      mach_msg_type_number_t *replyoutCnt)
    208 {
    209     heim_sipc ctx = dispatch_get_context(dispatch_get_current_queue());
    210     struct mach_call_ctx *s;
    211     kern_return_t kr;
    212     uid_t uid;
    213     gid_t gid;
    214     pid_t pid;
    215     au_asid_t session;
    216 
    217     *replyout = NULL;
    218     *replyoutCnt = 0;
    219     *replyinCnt = 0;
    220 
    221     s = malloc(sizeof(*s));
    222     if (s == NULL)
    223 	return KERN_MEMORY_FAILURE; /* XXX */
    224 
    225     s->reply_port = reply_port;
    226 
    227     audit_token_to_au32(client_creds, NULL, &uid, &gid, NULL, NULL, &pid, &session, NULL);
    228 
    229     kr = _heim_ipc_create_cred(uid, gid, pid, session, &s->cred);
    230     if (kr) {
    231 	free(s);
    232 	return kr;
    233     }
    234 
    235     suspend_timer();
    236 
    237     if (requestinCnt) {
    238 	s->req.data = malloc(requestinCnt);
    239 	memcpy(s->req.data, requestin, requestinCnt);
    240 	s->req.length = requestinCnt;
    241     } else {
    242 	s->req.data = malloc(requestoutCnt);
    243 	memcpy(s->req.data, requestout, requestoutCnt);
    244 	s->req.length = requestoutCnt;
    245     }
    246 
    247     dispatch_async(workq, ^{
    248 	(ctx->callback)(ctx->userctx, &s->req, s->cred,
    249 			mach_complete_sync, (heim_sipc_call)s);
    250     });
    251 
    252     return MIG_NO_REPLY;
    253 }
    254 
    255 kern_return_t
    256 mheim_do_call_request(mach_port_t server_port,
    257 		      audit_token_t client_creds,
    258 		      mach_port_t reply_port,
    259 		      heim_ipc_message_inband_t requestin,
    260 		      mach_msg_type_number_t requestinCnt,
    261 		      heim_ipc_message_outband_t requestout,
    262 		      mach_msg_type_number_t requestoutCnt)
    263 {
    264     heim_sipc ctx = dispatch_get_context(dispatch_get_current_queue());
    265     struct mach_call_ctx *s;
    266     kern_return_t kr;
    267     uid_t uid;
    268     gid_t gid;
    269     pid_t pid;
    270     au_asid_t session;
    271 
    272     s = malloc(sizeof(*s));
    273     if (s == NULL)
    274 	return KERN_MEMORY_FAILURE; /* XXX */
    275 
    276     s->reply_port = reply_port;
    277 
    278     audit_token_to_au32(client_creds, NULL, &uid, &gid, NULL, NULL, &pid, &session, NULL);
    279 
    280     kr = _heim_ipc_create_cred(uid, gid, pid, session, &s->cred);
    281     if (kr) {
    282 	free(s);
    283 	return kr;
    284     }
    285 
    286     suspend_timer();
    287 
    288     if (requestinCnt) {
    289 	s->req.data = malloc(requestinCnt);
    290 	memcpy(s->req.data, requestin, requestinCnt);
    291 	s->req.length = requestinCnt;
    292     } else {
    293 	s->req.data = malloc(requestoutCnt);
    294 	memcpy(s->req.data, requestout, requestoutCnt);
    295 	s->req.length = requestoutCnt;
    296     }
    297 
    298     dispatch_async(workq, ^{
    299 	(ctx->callback)(ctx->userctx, &s->req, s->cred,
    300 			mach_complete_async, (heim_sipc_call)s);
    301     });
    302 
    303     return KERN_SUCCESS;
    304 }
    305 
    306 static int
    307 mach_init(const char *service, mach_port_t sport, heim_sipc ctx)
    308 {
    309     struct mach_service *s;
    310     char *name;
    311 
    312     init_globals();
    313 
    314     s = calloc(1, sizeof(*s));
    315     if (s == NULL)
    316 	return ENOMEM;
    317 
    318     asprintf(&name, "heim-ipc-mach-%s", service);
    319 
    320     s->queue = dispatch_queue_create(name, NULL);
    321     free(name);
    322     s->sport = sport;
    323 
    324     s->source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV,
    325 				       s->sport, 0, s->queue);
    326     if (s->source == NULL) {
    327 	dispatch_release(s->queue);
    328 	free(s);
    329 	return ENOMEM;
    330     }
    331     ctx->mech = s;
    332 
    333     dispatch_set_context(s->queue, ctx);
    334     dispatch_set_context(s->source, s);
    335 
    336     dispatch_source_set_event_handler(s->source, ^{
    337 	    dispatch_mig_server(s->source, sizeof(union __RequestUnion__mheim_do_mheim_ipc_subsystem), mheim_ipc_server);
    338 	});
    339 
    340     dispatch_source_set_cancel_handler(s->source, ^{
    341 	    heim_sipc ctx = dispatch_get_context(dispatch_get_current_queue());
    342 	    struct mach_service *st = ctx->mech;
    343 	    mach_port_mod_refs(mach_task_self(), st->sport,
    344 			       MACH_PORT_RIGHT_RECEIVE, -1);
    345 	    dispatch_release(st->queue);
    346 	    dispatch_release(st->source);
    347 	    free(st);
    348 	    free(ctx);
    349 	});
    350 
    351     dispatch_resume(s->source);
    352 
    353     return 0;
    354 }
    355 
    356 static int
    357 mach_release(heim_sipc ctx)
    358 {
    359     struct mach_service *s = ctx->mech;
    360     dispatch_source_cancel(s->source);
    361     dispatch_release(s->source);
    362     return 0;
    363 }
    364 
    365 static mach_port_t
    366 mach_checkin_or_register(const char *service)
    367 {
    368     mach_port_t mp;
    369     kern_return_t kr;
    370 
    371     kr = bootstrap_check_in(bootstrap_port, service, &mp);
    372     if (kr == KERN_SUCCESS)
    373 	return mp;
    374 
    375 #if __MAC_OS_X_VERSION_MIN_REQUIRED <= 1050
    376     /* Pre SnowLeopard version */
    377     kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp);
    378     if (kr != KERN_SUCCESS)
    379 	return MACH_PORT_NULL;
    380 
    381     kr = mach_port_insert_right(mach_task_self(), mp, mp,
    382 				MACH_MSG_TYPE_MAKE_SEND);
    383     if (kr != KERN_SUCCESS) {
    384 	mach_port_destroy(mach_task_self(), mp);
    385 	return MACH_PORT_NULL;
    386     }
    387 
    388     kr = bootstrap_register(bootstrap_port, rk_UNCONST(service), mp);
    389     if (kr != KERN_SUCCESS) {
    390 	mach_port_destroy(mach_task_self(), mp);
    391 	return MACH_PORT_NULL;
    392     }
    393 
    394     return mp;
    395 #else
    396     return MACH_PORT_NULL;
    397 #endif
    398 }
    399 
    400 
    401 #endif /* __APPLE__ && HAVE_GCD */
    402 
    403 
    404 int
    405 heim_sipc_launchd_mach_init(const char *service,
    406 			    heim_ipc_callback callback,
    407 			    void *user, heim_sipc *ctx)
    408 {
    409 #if defined(__APPLE__) && defined(HAVE_GCD)
    410     mach_port_t sport = MACH_PORT_NULL;
    411     heim_sipc c = NULL;
    412     int ret;
    413 
    414     *ctx = NULL;
    415 
    416     sport = mach_checkin_or_register(service);
    417     if (sport == MACH_PORT_NULL) {
    418 	ret = ENOENT;
    419 	goto error;
    420     }
    421 
    422     c = calloc(1, sizeof(*c));
    423     if (c == NULL) {
    424 	ret = ENOMEM;
    425 	goto error;
    426     }
    427     c->release = mach_release;
    428     c->userctx = user;
    429     c->callback = callback;
    430 
    431     ret = mach_init(service, sport, c);
    432     if (ret)
    433 	goto error;
    434 
    435     *ctx = c;
    436     return 0;
    437  error:
    438     if (c)
    439 	free(c);
    440     if (sport != MACH_PORT_NULL)
    441 	mach_port_mod_refs(mach_task_self(), sport,
    442 			   MACH_PORT_RIGHT_RECEIVE, -1);
    443     return ret;
    444 #else /* !(__APPLE__ && HAVE_GCD) */
    445     *ctx = NULL;
    446     return EINVAL;
    447 #endif /* __APPLE__ && HAVE_GCD */
    448 }
    449 
    450 struct client {
    451     int fd;
    452     heim_ipc_callback callback;
    453     void *userctx;
    454     int flags;
    455 #define LISTEN_SOCKET	1
    456 #define WAITING_READ	2
    457 #define WAITING_WRITE	4
    458 #define WAITING_CLOSE	8
    459 
    460 #define HTTP_REPLY	16
    461 
    462 #define INHERIT_MASK	0xffff0000
    463 #define INCLUDE_ERROR_CODE (1 << 16)
    464 #define ALLOW_HTTP	(1<<17)
    465 #define UNIX_SOCKET	(1<<18)
    466     unsigned calls;
    467     size_t ptr, len;
    468     uint8_t *inmsg;
    469     size_t olen;
    470     uint8_t *outmsg;
    471 #ifdef HAVE_GCD
    472     dispatch_source_t in;
    473     dispatch_source_t out;
    474 #endif
    475     struct {
    476 	uid_t uid;
    477 	gid_t gid;
    478 	pid_t pid;
    479     } unixrights;
    480 };
    481 
    482 #ifndef HAVE_GCD
    483 static unsigned num_clients = 0;
    484 static struct client **clients = NULL;
    485 #endif
    486 
    487 static void handle_read(struct client *);
    488 static void handle_write(struct client *);
    489 static int maybe_close(struct client *);
    490 
    491 /*
    492  * Update peer credentials from socket.
    493  *
    494  * SCM_CREDS can only be updated the first time there is read data to
    495  * read from the filedescriptor, so if we read do it before this
    496  * point, the cred data might not be is not there yet.
    497  */
    498 
    499 static int
    500 update_client_creds(struct client *c)
    501 {
    502 #ifdef HAVE_GETPEERUCRED
    503     /* Solaris 10 */
    504     {
    505 	ucred_t *peercred;
    506 
    507 	if (getpeerucred(c->fd, &peercred) != 0) {
    508 	    c->unixrights.uid = ucred_geteuid(peercred);
    509 	    c->unixrights.gid = ucred_getegid(peercred);
    510 	    c->unixrights.pid = 0;
    511 	    ucred_free(peercred);
    512 	    return 1;
    513 	}
    514     }
    515 #endif
    516 #ifdef HAVE_GETPEEREID
    517     /* FreeBSD, OpenBSD */
    518     {
    519 	uid_t uid;
    520 	gid_t gid;
    521 
    522 	if (getpeereid(c->fd, &uid, &gid) == 0) {
    523 	    c->unixrights.uid = uid;
    524 	    c->unixrights.gid = gid;
    525 	    c->unixrights.pid = 0;
    526 	    return 1;
    527 	}
    528     }
    529 #endif
    530 #if defined(SO_PEERCRED) && defined(__linux__)
    531     /* Linux */
    532     {
    533 	struct ucred pc;
    534 	socklen_t pclen = sizeof(pc);
    535 
    536 	if (getsockopt(c->fd, SOL_SOCKET, SO_PEERCRED, (void *)&pc, &pclen) == 0) {
    537 	    c->unixrights.uid = pc.uid;
    538 	    c->unixrights.gid = pc.gid;
    539 	    c->unixrights.pid = pc.pid;
    540 	    return 1;
    541 	}
    542     }
    543 #endif
    544 #if defined(LOCAL_PEERCRED) && defined(XUCRED_VERSION)
    545     {
    546 	struct xucred peercred;
    547 	socklen_t peercredlen = sizeof(peercred);
    548 
    549 	if (getsockopt(c->fd, LOCAL_PEERCRED, 1,
    550 		       (void *)&peercred, &peercredlen) == 0
    551 	    && peercred.cr_version == XUCRED_VERSION)
    552 	{
    553 	    c->unixrights.uid = peercred.cr_uid;
    554 	    c->unixrights.gid = peercred.cr_gid;
    555 	    c->unixrights.pid = 0;
    556 	    return 1;
    557 	}
    558     }
    559 #endif
    560 #if defined(SOCKCREDSIZE) && defined(SCM_CREDS)
    561     /* NetBSD */
    562     if (c->unixrights.uid == (uid_t)-1) {
    563 	struct msghdr msg;
    564 	socklen_t crmsgsize;
    565 	void *crmsg;
    566 	struct cmsghdr *cmp;
    567 	struct sockcred *sc;
    568 
    569 	memset(&msg, 0, sizeof(msg));
    570 	crmsgsize = CMSG_SPACE(SOCKCREDSIZE(NGROUPS));
    571 	if (crmsgsize == 0)
    572 	    return 1 ;
    573 
    574 	crmsg = malloc(crmsgsize);
    575 	if (crmsg == NULL)
    576 	    goto failed_scm_creds;
    577 
    578 	memset(crmsg, 0, crmsgsize);
    579 
    580 	msg.msg_control = crmsg;
    581 	msg.msg_controllen = crmsgsize;
    582 
    583 	if (recvmsg(c->fd, &msg, 0) < 0) {
    584 	    free(crmsg);
    585 	    goto failed_scm_creds;
    586 	}
    587 
    588 	if (msg.msg_controllen == 0 || (msg.msg_flags & MSG_CTRUNC) != 0) {
    589 	    free(crmsg);
    590 	    goto failed_scm_creds;
    591 	}
    592 
    593 	cmp = CMSG_FIRSTHDR(&msg);
    594 	if (cmp->cmsg_level != SOL_SOCKET || cmp->cmsg_type != SCM_CREDS) {
    595 	    free(crmsg);
    596 	    goto failed_scm_creds;
    597 	}
    598 
    599 	sc = (struct sockcred *)(void *)CMSG_DATA(cmp);
    600 
    601 	c->unixrights.uid = sc->sc_euid;
    602 	c->unixrights.gid = sc->sc_egid;
    603 	c->unixrights.pid = 0;
    604 
    605 	free(crmsg);
    606 	return 1;
    607     } else {
    608 	/* we already got the cred, just return it */
    609 	return 1;
    610     }
    611  failed_scm_creds:
    612 #endif
    613     return 0;
    614 }
    615 
    616 
    617 static struct client *
    618 add_new_socket(int fd,
    619 	       int flags,
    620 	       heim_ipc_callback callback,
    621 	       void *userctx)
    622 {
    623     struct client *c;
    624     int fileflags;
    625 
    626     c = calloc(1, sizeof(*c));
    627     if (c == NULL)
    628 	return NULL;
    629 
    630     if (flags & LISTEN_SOCKET) {
    631 	c->fd = fd;
    632     } else {
    633 	c->fd = accept(fd, NULL, NULL);
    634 	if(c->fd < 0) {
    635 	    free(c);
    636 	    return NULL;
    637 	}
    638     }
    639 
    640     c->flags = flags;
    641     c->callback = callback;
    642     c->userctx = userctx;
    643 
    644     fileflags = fcntl(c->fd, F_GETFL, 0);
    645     fcntl(c->fd, F_SETFL, fileflags | O_NONBLOCK);
    646 
    647 #ifdef HAVE_GCD
    648     init_globals();
    649 
    650     c->in = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
    651 				   c->fd, 0, eventq);
    652     c->out = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE,
    653 				    c->fd, 0, eventq);
    654 
    655     dispatch_source_set_event_handler(c->in, ^{
    656 	    int rw = (c->flags & WAITING_WRITE);
    657 	    handle_read(c);
    658 	    if (rw == 0 && (c->flags & WAITING_WRITE))
    659 		dispatch_resume(c->out);
    660 	    if ((c->flags & WAITING_READ) == 0)
    661 		dispatch_suspend(c->in);
    662 	    maybe_close(c);
    663 	});
    664     dispatch_source_set_event_handler(c->out, ^{
    665 	    handle_write(c);
    666 	    if ((c->flags & WAITING_WRITE) == 0) {
    667 		dispatch_suspend(c->out);
    668 	    }
    669 	    maybe_close(c);
    670 	});
    671 
    672     dispatch_resume(c->in);
    673 #else
    674     clients = erealloc(clients, sizeof(clients[0]) * (num_clients + 1));
    675     clients[num_clients] = c;
    676     num_clients++;
    677 #endif
    678 
    679     return c;
    680 }
    681 
    682 static int
    683 maybe_close(struct client *c)
    684 {
    685     if (c->calls != 0)
    686 	return 0;
    687     if (c->flags & (WAITING_READ|WAITING_WRITE))
    688 	return 0;
    689 
    690 #ifdef HAVE_GCD
    691     dispatch_source_cancel(c->in);
    692     if ((c->flags & WAITING_READ) == 0)
    693 	dispatch_resume(c->in);
    694     dispatch_release(c->in);
    695 
    696     dispatch_source_cancel(c->out);
    697     if ((c->flags & WAITING_WRITE) == 0)
    698 	dispatch_resume(c->out);
    699     dispatch_release(c->out);
    700 #endif
    701     close(c->fd); /* ref count fd close */
    702     free(c);
    703     return 1;
    704 }
    705 
    706 
    707 struct socket_call {
    708     heim_idata in;
    709     struct client *c;
    710     heim_icred cred;
    711 };
    712 
    713 static void
    714 output_data(struct client *c, const void *data, size_t len)
    715 {
    716     if (c->olen + len < c->olen)
    717 	abort();
    718     c->outmsg = erealloc(c->outmsg, c->olen + len);
    719     memcpy(&c->outmsg[c->olen], data, len);
    720     c->olen += len;
    721     c->flags |= WAITING_WRITE;
    722 }
    723 
    724 static void
    725 socket_complete(heim_sipc_call ctx, int returnvalue, heim_idata *reply)
    726 {
    727     struct socket_call *sc = (struct socket_call *)ctx;
    728     struct client *c = sc->c;
    729 
    730     /* double complete ? */
    731     if (c == NULL)
    732 	abort();
    733 
    734     if ((c->flags & WAITING_CLOSE) == 0) {
    735 	uint32_t u32;
    736 
    737 	/* length */
    738 	u32 = htonl(reply->length);
    739 	output_data(c, &u32, sizeof(u32));
    740 
    741 	/* return value */
    742 	if (c->flags & INCLUDE_ERROR_CODE) {
    743 	    u32 = htonl(returnvalue);
    744 	    output_data(c, &u32, sizeof(u32));
    745 	}
    746 
    747 	/* data */
    748 	output_data(c, reply->data, reply->length);
    749 
    750 	/* if HTTP, close connection */
    751 	if (c->flags & HTTP_REPLY) {
    752 	    c->flags |= WAITING_CLOSE;
    753 	    c->flags &= ~WAITING_READ;
    754 	}
    755     }
    756 
    757     c->calls--;
    758     if (sc->cred)
    759 	heim_ipc_free_cred(sc->cred);
    760     free(sc->in.data);
    761     sc->c = NULL; /* so we can catch double complete */
    762     free(sc);
    763 
    764     maybe_close(c);
    765 }
    766 
    767 /* remove HTTP %-quoting from buf */
    768 static int
    769 de_http(char *buf)
    770 {
    771     unsigned char *p, *q;
    772     for(p = q = (unsigned char *)buf; *p; p++, q++) {
    773 	if(*p == '%' && isxdigit(p[1]) && isxdigit(p[2])) {
    774 	    unsigned int x;
    775 	    if(sscanf((char *)p + 1, "%2x", &x) != 1)
    776 		return -1;
    777 	    *q = x;
    778 	    p += 2;
    779 	} else
    780 	    *q = *p;
    781     }
    782     *q = '\0';
    783     return 0;
    784 }
    785 
    786 static struct socket_call *
    787 handle_http_tcp(struct client *c)
    788 {
    789     struct socket_call *cs;
    790     char *s, *p, *t;
    791     void *data;
    792     char *proto;
    793     int len;
    794 
    795     s = (char *)c->inmsg;
    796 
    797     p = strstr(s, "\r\n");
    798     if (p == NULL)
    799 	return NULL;
    800 
    801     *p = 0;
    802 
    803     p = NULL;
    804     t = strtok_r(s, " \t", &p);
    805     if (t == NULL)
    806 	return NULL;
    807 
    808     t = strtok_r(NULL, " \t", &p);
    809     if (t == NULL)
    810 	return NULL;
    811 
    812     data = malloc(strlen(t));
    813     if (data == NULL)
    814 	return NULL;
    815 
    816     if(*t == '/')
    817 	t++;
    818     if(de_http(t) != 0) {
    819 	free(data);
    820 	return NULL;
    821     }
    822     proto = strtok_r(NULL, " \t", &p);
    823     if (proto == NULL) {
    824 	free(data);
    825 	return NULL;
    826     }
    827     len = base64_decode(t, data);
    828     if(len <= 0){
    829 	const char *msg =
    830 	    " 404 Not found\r\n"
    831 	    "Server: Heimdal/" VERSION "\r\n"
    832 	    "Cache-Control: no-cache\r\n"
    833 	    "Pragma: no-cache\r\n"
    834 	    "Content-type: text/html\r\n"
    835 	    "Content-transfer-encoding: 8bit\r\n\r\n"
    836 	    "<TITLE>404 Not found</TITLE>\r\n"
    837 	    "<H1>404 Not found</H1>\r\n"
    838 	    "That page doesn't exist, maybe you are looking for "
    839 	    "<A HREF=\"http://www.h5l.org/\">Heimdal</A>?\r\n";
    840 	free(data);
    841 	output_data(c, proto, strlen(proto));
    842 	output_data(c, msg, strlen(msg));
    843 	return NULL;
    844     }
    845 
    846     cs = emalloc(sizeof(*cs));
    847     cs->c = c;
    848     cs->in.data = data;
    849     cs->in.length = len;
    850     c->ptr = 0;
    851 
    852     {
    853 	const char *msg =
    854 	    " 200 OK\r\n"
    855 	    "Server: Heimdal/" VERSION "\r\n"
    856 	    "Cache-Control: no-cache\r\n"
    857 	    "Pragma: no-cache\r\n"
    858 	    "Content-type: application/octet-stream\r\n"
    859 	    "Content-transfer-encoding: binary\r\n\r\n";
    860 	output_data(c, proto, strlen(proto));
    861 	output_data(c, msg, strlen(msg));
    862     }
    863 
    864     return cs;
    865 }
    866 
    867 
    868 static void
    869 handle_read(struct client *c)
    870 {
    871     ssize_t len;
    872     uint32_t dlen;
    873 
    874     if (c->flags & LISTEN_SOCKET) {
    875 	add_new_socket(c->fd,
    876 		       WAITING_READ | (c->flags & INHERIT_MASK),
    877 		       c->callback,
    878 		       c->userctx);
    879 	return;
    880     }
    881 
    882     if (c->ptr - c->len < 1024) {
    883 	c->inmsg = erealloc(c->inmsg,
    884 			    c->len + 1024);
    885 	c->len += 1024;
    886     }
    887 
    888     len = read(c->fd, c->inmsg + c->ptr, c->len - c->ptr);
    889     if (len <= 0) {
    890 	c->flags |= WAITING_CLOSE;
    891 	c->flags &= ~WAITING_READ;
    892 	return;
    893     }
    894     c->ptr += len;
    895     if (c->ptr > c->len)
    896 	abort();
    897 
    898     while (c->ptr >= sizeof(dlen)) {
    899 	struct socket_call *cs;
    900 
    901 	if((c->flags & ALLOW_HTTP) && c->ptr >= 4 &&
    902 	   strncmp((char *)c->inmsg, "GET ", 4) == 0 &&
    903 	   strncmp((char *)c->inmsg + c->ptr - 4, "\r\n\r\n", 4) == 0) {
    904 
    905 	    /* remove the trailing \r\n\r\n so the string is NUL terminated */
    906 	    c->inmsg[c->ptr - 4] = '\0';
    907 
    908 	    c->flags |= HTTP_REPLY;
    909 
    910 	    cs = handle_http_tcp(c);
    911 	    if (cs == NULL) {
    912 		c->flags |= WAITING_CLOSE;
    913 		c->flags &= ~WAITING_READ;
    914 		break;
    915 	    }
    916 	} else {
    917 	    memcpy(&dlen, c->inmsg, sizeof(dlen));
    918 	    dlen = ntohl(dlen);
    919 
    920 	    if (dlen > MAX_PACKET_SIZE) {
    921 		c->flags |= WAITING_CLOSE;
    922 		c->flags &= ~WAITING_READ;
    923 		return;
    924 	    }
    925 	    if (dlen > c->ptr - sizeof(dlen)) {
    926 		break;
    927 	    }
    928 
    929 	    cs = emalloc(sizeof(*cs));
    930 	    cs->c = c;
    931 	    cs->in.data = emalloc(dlen);
    932 	    memcpy(cs->in.data, c->inmsg + sizeof(dlen), dlen);
    933 	    cs->in.length = dlen;
    934 
    935 	    c->ptr -= sizeof(dlen) + dlen;
    936 	    memmove(c->inmsg,
    937 		    c->inmsg + sizeof(dlen) + dlen,
    938 		    c->ptr);
    939 	}
    940 
    941 	c->calls++;
    942 
    943 	if ((c->flags & UNIX_SOCKET) != 0) {
    944 	    if (update_client_creds(c))
    945 		_heim_ipc_create_cred(c->unixrights.uid, c->unixrights.gid,
    946 				      c->unixrights.pid, -1, &cs->cred);
    947 	}
    948 
    949 	c->callback(c->userctx, &cs->in,
    950 		    cs->cred, socket_complete,
    951 		    (heim_sipc_call)cs);
    952     }
    953 }
    954 
    955 static void
    956 handle_write(struct client *c)
    957 {
    958     ssize_t len;
    959 
    960     len = write(c->fd, c->outmsg, c->olen);
    961     if (len <= 0) {
    962 	c->flags |= WAITING_CLOSE;
    963 	c->flags &= ~(WAITING_WRITE);
    964     } else if (c->olen != (size_t)len) {
    965 	memmove(&c->outmsg[0], &c->outmsg[len], c->olen - len);
    966 	c->olen -= len;
    967     } else {
    968 	c->olen = 0;
    969 	free(c->outmsg);
    970 	c->outmsg = NULL;
    971 	c->flags &= ~(WAITING_WRITE);
    972     }
    973 }
    974 
    975 
    976 #ifndef HAVE_GCD
    977 
    978 static void
    979 process_loop(void)
    980 {
    981     struct pollfd *fds;
    982     unsigned n;
    983     unsigned num_fds;
    984 
    985     while(num_clients > 0) {
    986 
    987 	fds = malloc(num_clients * sizeof(fds[0]));
    988 	if(fds == NULL)
    989 	    abort();
    990 
    991 	num_fds = num_clients;
    992 
    993 	for (n = 0 ; n < num_fds; n++) {
    994 	    fds[n].fd = clients[n]->fd;
    995 	    fds[n].events = 0;
    996 	    if (clients[n]->flags & WAITING_READ)
    997 		fds[n].events |= POLLIN;
    998 	    if (clients[n]->flags & WAITING_WRITE)
    999 		fds[n].events |= POLLOUT;
   1000 
   1001 	    fds[n].revents = 0;
   1002 	}
   1003 
   1004 	poll(fds, num_fds, -1);
   1005 
   1006 	for (n = 0 ; n < num_fds; n++) {
   1007 	    if (clients[n] == NULL)
   1008 		continue;
   1009 	    if (fds[n].revents & POLLERR) {
   1010 		clients[n]->flags |= WAITING_CLOSE;
   1011 		continue;
   1012 	    }
   1013 
   1014 	    if (fds[n].revents & POLLIN)
   1015 		handle_read(clients[n]);
   1016 	    if (fds[n].revents & POLLOUT)
   1017 		handle_write(clients[n]);
   1018 	}
   1019 
   1020 	n = 0;
   1021 	while (n < num_clients) {
   1022 	    struct client *c = clients[n];
   1023 	    if (maybe_close(c)) {
   1024 		if (n < num_clients - 1)
   1025 		    clients[n] = clients[num_clients - 1];
   1026 		num_clients--;
   1027 	    } else
   1028 		n++;
   1029 	}
   1030 
   1031 	free(fds);
   1032     }
   1033 }
   1034 
   1035 #endif
   1036 
   1037 static int
   1038 socket_release(heim_sipc ctx)
   1039 {
   1040     struct client *c = ctx->mech;
   1041     c->flags |= WAITING_CLOSE;
   1042     return 0;
   1043 }
   1044 
   1045 int
   1046 heim_sipc_stream_listener(int fd, int type,
   1047 			  heim_ipc_callback callback,
   1048 			  void *user, heim_sipc *ctx)
   1049 {
   1050     heim_sipc ct = calloc(1, sizeof(*ct));
   1051     struct client *c;
   1052 
   1053     if ((type & HEIM_SIPC_TYPE_IPC) && (type & (HEIM_SIPC_TYPE_UINT32|HEIM_SIPC_TYPE_HTTP)))
   1054 	return EINVAL;
   1055 
   1056     switch (type) {
   1057     case HEIM_SIPC_TYPE_IPC:
   1058 	c = add_new_socket(fd, LISTEN_SOCKET|WAITING_READ|INCLUDE_ERROR_CODE, callback, user);
   1059 	break;
   1060     case HEIM_SIPC_TYPE_UINT32:
   1061 	c = add_new_socket(fd, LISTEN_SOCKET|WAITING_READ, callback, user);
   1062 	break;
   1063     case HEIM_SIPC_TYPE_HTTP:
   1064     case HEIM_SIPC_TYPE_UINT32|HEIM_SIPC_TYPE_HTTP:
   1065 	c = add_new_socket(fd, LISTEN_SOCKET|WAITING_READ|ALLOW_HTTP, callback, user);
   1066 	break;
   1067     default:
   1068 	free(ct);
   1069 	return EINVAL;
   1070     }
   1071 
   1072     ct->mech = c;
   1073     ct->release = socket_release;
   1074 
   1075     c->unixrights.uid = (uid_t) -1;
   1076     c->unixrights.gid = (gid_t) -1;
   1077     c->unixrights.pid = (pid_t) 0;
   1078 
   1079     *ctx = ct;
   1080     return 0;
   1081 }
   1082 
   1083 int
   1084 heim_sipc_service_unix(const char *service,
   1085 		       heim_ipc_callback callback,
   1086 		       void *user, heim_sipc *ctx)
   1087 {
   1088     struct sockaddr_un un;
   1089     int fd, ret;
   1090 
   1091     un.sun_family = AF_UNIX;
   1092 
   1093     snprintf(un.sun_path, sizeof(un.sun_path),
   1094 	     "/var/run/.heim_%s-socket", service);
   1095     fd = socket(AF_UNIX, SOCK_STREAM, 0);
   1096     if (fd < 0)
   1097 	return errno;
   1098 
   1099     socket_set_reuseaddr(fd, 1);
   1100 #ifdef LOCAL_CREDS
   1101     {
   1102 	int one = 1;
   1103 	setsockopt(fd, 0, LOCAL_CREDS, (void *)&one, sizeof(one));
   1104     }
   1105 #endif
   1106 
   1107     unlink(un.sun_path);
   1108 
   1109     if (bind(fd, (struct sockaddr *)&un, sizeof(un)) < 0) {
   1110 	close(fd);
   1111 	return errno;
   1112     }
   1113 
   1114     if (listen(fd, SOMAXCONN) < 0) {
   1115 	close(fd);
   1116 	return errno;
   1117     }
   1118 
   1119     chmod(un.sun_path, 0666);
   1120 
   1121     ret = heim_sipc_stream_listener(fd, HEIM_SIPC_TYPE_IPC,
   1122 				    callback, user, ctx);
   1123     if (ret == 0) {
   1124 	struct client *c = (*ctx)->mech;
   1125 	c->flags |= UNIX_SOCKET;
   1126     }
   1127 
   1128     return ret;
   1129 }
   1130 
   1131 /**
   1132  * Set the idle timeout value
   1133 
   1134  * The timeout event handler is triggered recurrently every idle
   1135  * period `t'. The default action is rather draconian and just calls
   1136  * exit(0), so you might want to change this to something more
   1137  * graceful using heim_sipc_set_timeout_handler().
   1138  */
   1139 
   1140 void
   1141 heim_sipc_timeout(time_t t)
   1142 {
   1143 #ifdef HAVE_GCD
   1144     static dispatch_once_t timeoutonce;
   1145     init_globals();
   1146     dispatch_sync(timerq, ^{
   1147 	    timeoutvalue = t;
   1148 	    set_timer();
   1149 	});
   1150     dispatch_once(&timeoutonce, ^{  dispatch_resume(timer); });
   1151 #else
   1152     abort();
   1153 #endif
   1154 }
   1155 
   1156 /**
   1157  * Set the timeout event handler
   1158  *
   1159  * Replaces the default idle timeout action.
   1160  */
   1161 
   1162 void
   1163 heim_sipc_set_timeout_handler(void (*func)(void))
   1164 {
   1165 #ifdef HAVE_GCD
   1166     init_globals();
   1167     dispatch_sync(timerq, ^{ timer_ev = func; });
   1168 #else
   1169     abort();
   1170 #endif
   1171 }
   1172 
   1173 
   1174 void
   1175 heim_sipc_free_context(heim_sipc ctx)
   1176 {
   1177     (ctx->release)(ctx);
   1178 }
   1179 
   1180 void
   1181 heim_ipc_main(void)
   1182 {
   1183 #ifdef HAVE_GCD
   1184     dispatch_main();
   1185 #else
   1186     process_loop();
   1187 #endif
   1188 }
   1189 
   1190