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