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