1226031Sstas/*
2226031Sstas * Copyright (c) 2009 Kungliga Tekniska H�gskolan
3226031Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4226031Sstas * All rights reserved.
5226031Sstas *
6226031Sstas * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
7226031Sstas *
8226031Sstas * Redistribution and use in source and binary forms, with or without
9226031Sstas * modification, are permitted provided that the following conditions
10226031Sstas * are met:
11226031Sstas *
12226031Sstas * 1. Redistributions of source code must retain the above copyright
13226031Sstas *    notice, this list of conditions and the following disclaimer.
14226031Sstas *
15226031Sstas * 2. Redistributions in binary form must reproduce the above copyright
16226031Sstas *    notice, this list of conditions and the following disclaimer in the
17226031Sstas *    documentation and/or other materials provided with the distribution.
18226031Sstas *
19226031Sstas * 3. Neither the name of the Institute nor the names of its contributors
20226031Sstas *    may be used to endorse or promote products derived from this software
21226031Sstas *    without specific prior written permission.
22226031Sstas *
23226031Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24226031Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25226031Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26226031Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27226031Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28226031Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29226031Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30226031Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31226031Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32226031Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33226031Sstas * SUCH DAMAGE.
34226031Sstas */
35226031Sstas
36226031Sstas#include "hi_locl.h"
37226031Sstas#include <assert.h>
38226031Sstas
39226031Sstas#define MAX_PACKET_SIZE (128 * 1024)
40226031Sstas
41226031Sstasstruct heim_sipc {
42226031Sstas    int (*release)(heim_sipc ctx);
43226031Sstas    heim_ipc_callback callback;
44226031Sstas    void *userctx;
45226031Sstas    void *mech;
46226031Sstas};
47226031Sstas
48226031Sstas#if defined(__APPLE__) && defined(HAVE_GCD)
49226031Sstas
50226031Sstas#include "heim_ipcServer.h"
51226031Sstas#include "heim_ipc_reply.h"
52226031Sstas#include "heim_ipc_async.h"
53226031Sstas
54226031Sstasstatic dispatch_source_t timer;
55226031Sstasstatic dispatch_queue_t timerq;
56226031Sstasstatic uint64_t timeoutvalue;
57226031Sstas
58226031Sstasstatic dispatch_queue_t eventq;
59226031Sstas
60226031Sstasstatic dispatch_queue_t workq;
61226031Sstas
62226031Sstasstatic void
63226031Sstasdefault_timer_ev(void)
64226031Sstas{
65226031Sstas    exit(0);
66226031Sstas}
67226031Sstas
68226031Sstasstatic void (*timer_ev)(void) = default_timer_ev;
69226031Sstas
70226031Sstasstatic void
71226031Sstasset_timer(void)
72226031Sstas{
73226031Sstas    dispatch_source_set_timer(timer,
74226031Sstas			      dispatch_time(DISPATCH_TIME_NOW,
75226031Sstas					    timeoutvalue * NSEC_PER_SEC),
76226031Sstas			      timeoutvalue * NSEC_PER_SEC, 1000000);
77226031Sstas}
78226031Sstas
79226031Sstasstatic void
80226031Sstasinit_globals(void)
81226031Sstas{
82226031Sstas    static dispatch_once_t once;
83226031Sstas    dispatch_once(&once, ^{
84226031Sstas	timerq = dispatch_queue_create("hiem-sipc-timer-q", NULL);
85226031Sstas        timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, timerq);
86226031Sstas	dispatch_source_set_event_handler(timer, ^{ timer_ev(); } );
87226031Sstas
88226031Sstas	workq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
89226031Sstas	eventq = dispatch_queue_create("heim-ipc.event-queue", NULL);
90226031Sstas    });
91226031Sstas}
92226031Sstas
93226031Sstasstatic void
94226031Sstassuspend_timer(void)
95226031Sstas{
96226031Sstas    dispatch_suspend(timer);
97226031Sstas}
98226031Sstas
99226031Sstasstatic void
100226031Sstasrestart_timer(void)
101226031Sstas{
102226031Sstas    dispatch_sync(timerq, ^{ set_timer(); });
103226031Sstas    dispatch_resume(timer);
104226031Sstas}
105226031Sstas
106226031Sstasstruct mach_service {
107226031Sstas    mach_port_t sport;
108226031Sstas    dispatch_source_t source;
109226031Sstas    dispatch_queue_t queue;
110226031Sstas};
111226031Sstas
112226031Sstasstruct mach_call_ctx {
113226031Sstas    mach_port_t reply_port;
114226031Sstas    heim_icred cred;
115226031Sstas    heim_idata req;
116226031Sstas};
117226031Sstas
118226031Sstas
119226031Sstasstatic void
120226031Sstasmach_complete_sync(heim_sipc_call ctx, int returnvalue, heim_idata *reply)
121226031Sstas{
122226031Sstas    struct mach_call_ctx *s = (struct mach_call_ctx *)ctx;
123226031Sstas    heim_ipc_message_inband_t replyin;
124226031Sstas    mach_msg_type_number_t replyinCnt;
125226031Sstas    heim_ipc_message_outband_t replyout;
126226031Sstas    mach_msg_type_number_t replyoutCnt;
127226031Sstas    kern_return_t kr;
128226031Sstas
129226031Sstas    if (returnvalue) {
130226031Sstas	/* on error, no reply */
131226031Sstas	replyinCnt = 0;
132226031Sstas	replyout = 0; replyoutCnt = 0;
133226031Sstas	kr = KERN_SUCCESS;
134226031Sstas    } else if (reply->length < 2048) {
135226031Sstas	replyinCnt = reply->length;
136226031Sstas	memcpy(replyin, reply->data, replyinCnt);
137226031Sstas	replyout = 0; replyoutCnt = 0;
138226031Sstas	kr = KERN_SUCCESS;
139226031Sstas    } else {
140226031Sstas	replyinCnt = 0;
141226031Sstas	kr = vm_read(mach_task_self(),
142226031Sstas		     (vm_address_t)reply->data, reply->length,
143226031Sstas		     (vm_address_t *)&replyout, &replyoutCnt);
144226031Sstas    }
145226031Sstas
146226031Sstas    mheim_ripc_call_reply(s->reply_port, returnvalue,
147226031Sstas			  replyin, replyinCnt,
148226031Sstas			  replyout, replyoutCnt);
149226031Sstas
150226031Sstas    heim_ipc_free_cred(s->cred);
151226031Sstas    free(s->req.data);
152226031Sstas    free(s);
153226031Sstas    restart_timer();
154226031Sstas}
155226031Sstas
156226031Sstasstatic void
157226031Sstasmach_complete_async(heim_sipc_call ctx, int returnvalue, heim_idata *reply)
158226031Sstas{
159226031Sstas    struct mach_call_ctx *s = (struct mach_call_ctx *)ctx;
160226031Sstas    heim_ipc_message_inband_t replyin;
161226031Sstas    mach_msg_type_number_t replyinCnt;
162226031Sstas    heim_ipc_message_outband_t replyout;
163226031Sstas    mach_msg_type_number_t replyoutCnt;
164226031Sstas    kern_return_t kr;
165226031Sstas
166226031Sstas    if (returnvalue) {
167226031Sstas	/* on error, no reply */
168226031Sstas	replyinCnt = 0;
169226031Sstas	replyout = 0; replyoutCnt = 0;
170226031Sstas	kr = KERN_SUCCESS;
171226031Sstas    } else if (reply->length < 2048) {
172226031Sstas	replyinCnt = reply->length;
173226031Sstas	memcpy(replyin, reply->data, replyinCnt);
174226031Sstas	replyout = 0; replyoutCnt = 0;
175226031Sstas	kr = KERN_SUCCESS;
176226031Sstas    } else {
177226031Sstas	replyinCnt = 0;
178226031Sstas	kr = vm_read(mach_task_self(),
179226031Sstas		     (vm_address_t)reply->data, reply->length,
180226031Sstas		     (vm_address_t *)&replyout, &replyoutCnt);
181226031Sstas    }
182226031Sstas
183226031Sstas    kr = mheim_aipc_acall_reply(s->reply_port, returnvalue,
184226031Sstas				replyin, replyinCnt,
185226031Sstas				replyout, replyoutCnt);
186226031Sstas    heim_ipc_free_cred(s->cred);
187226031Sstas    free(s->req.data);
188226031Sstas    free(s);
189226031Sstas    restart_timer();
190226031Sstas}
191226031Sstas
192226031Sstas
193226031Sstaskern_return_t
194226031Sstasmheim_do_call(mach_port_t server_port,
195226031Sstas	      audit_token_t client_creds,
196226031Sstas	      mach_port_t reply_port,
197226031Sstas	      heim_ipc_message_inband_t requestin,
198226031Sstas	      mach_msg_type_number_t requestinCnt,
199226031Sstas	      heim_ipc_message_outband_t requestout,
200226031Sstas	      mach_msg_type_number_t requestoutCnt,
201226031Sstas	      int *returnvalue,
202226031Sstas	      heim_ipc_message_inband_t replyin,
203226031Sstas	      mach_msg_type_number_t *replyinCnt,
204226031Sstas	      heim_ipc_message_outband_t *replyout,
205226031Sstas	      mach_msg_type_number_t *replyoutCnt)
206226031Sstas{
207226031Sstas    heim_sipc ctx = dispatch_get_context(dispatch_get_current_queue());
208226031Sstas    struct mach_call_ctx *s;
209226031Sstas    kern_return_t kr;
210226031Sstas    uid_t uid;
211226031Sstas    gid_t gid;
212226031Sstas    pid_t pid;
213226031Sstas    au_asid_t session;
214226031Sstas
215226031Sstas    *replyout = NULL;
216226031Sstas    *replyoutCnt = 0;
217226031Sstas    *replyinCnt = 0;
218226031Sstas
219226031Sstas    s = malloc(sizeof(*s));
220226031Sstas    if (s == NULL)
221226031Sstas	return KERN_MEMORY_FAILURE; /* XXX */
222226031Sstas
223226031Sstas    s->reply_port = reply_port;
224226031Sstas
225226031Sstas    audit_token_to_au32(client_creds, NULL, &uid, &gid, NULL, NULL, &pid, &session, NULL);
226226031Sstas
227226031Sstas    kr = _heim_ipc_create_cred(uid, gid, pid, session, &s->cred);
228226031Sstas    if (kr) {
229226031Sstas	free(s);
230226031Sstas	return kr;
231226031Sstas    }
232226031Sstas
233226031Sstas    suspend_timer();
234226031Sstas
235226031Sstas    if (requestinCnt) {
236226031Sstas	s->req.data = malloc(requestinCnt);
237226031Sstas	memcpy(s->req.data, requestin, requestinCnt);
238226031Sstas	s->req.length = requestinCnt;
239226031Sstas    } else {
240226031Sstas	s->req.data = malloc(requestoutCnt);
241226031Sstas	memcpy(s->req.data, requestout, requestoutCnt);
242226031Sstas	s->req.length = requestoutCnt;
243226031Sstas    }
244226031Sstas
245226031Sstas    dispatch_async(workq, ^{
246226031Sstas	(ctx->callback)(ctx->userctx, &s->req, s->cred,
247226031Sstas			mach_complete_sync, (heim_sipc_call)s);
248226031Sstas    });
249226031Sstas
250226031Sstas    return MIG_NO_REPLY;
251226031Sstas}
252226031Sstas
253226031Sstaskern_return_t
254226031Sstasmheim_do_call_request(mach_port_t server_port,
255226031Sstas		      audit_token_t client_creds,
256226031Sstas		      mach_port_t reply_port,
257226031Sstas		      heim_ipc_message_inband_t requestin,
258226031Sstas		      mach_msg_type_number_t requestinCnt,
259226031Sstas		      heim_ipc_message_outband_t requestout,
260226031Sstas		      mach_msg_type_number_t requestoutCnt)
261226031Sstas{
262226031Sstas    heim_sipc ctx = dispatch_get_context(dispatch_get_current_queue());
263226031Sstas    struct mach_call_ctx *s;
264226031Sstas    kern_return_t kr;
265226031Sstas    uid_t uid;
266226031Sstas    gid_t gid;
267226031Sstas    pid_t pid;
268226031Sstas    au_asid_t session;
269226031Sstas
270226031Sstas    s = malloc(sizeof(*s));
271226031Sstas    if (s == NULL)
272226031Sstas	return KERN_MEMORY_FAILURE; /* XXX */
273226031Sstas
274226031Sstas    s->reply_port = reply_port;
275226031Sstas
276226031Sstas    audit_token_to_au32(client_creds, NULL, &uid, &gid, NULL, NULL, &pid, &session, NULL);
277226031Sstas
278226031Sstas    kr = _heim_ipc_create_cred(uid, gid, pid, session, &s->cred);
279226031Sstas    if (kr) {
280226031Sstas	free(s);
281226031Sstas	return kr;
282226031Sstas    }
283226031Sstas
284226031Sstas    suspend_timer();
285226031Sstas
286226031Sstas    if (requestinCnt) {
287226031Sstas	s->req.data = malloc(requestinCnt);
288226031Sstas	memcpy(s->req.data, requestin, requestinCnt);
289226031Sstas	s->req.length = requestinCnt;
290226031Sstas    } else {
291226031Sstas	s->req.data = malloc(requestoutCnt);
292226031Sstas	memcpy(s->req.data, requestout, requestoutCnt);
293226031Sstas	s->req.length = requestoutCnt;
294226031Sstas    }
295226031Sstas
296226031Sstas    dispatch_async(workq, ^{
297226031Sstas	(ctx->callback)(ctx->userctx, &s->req, s->cred,
298226031Sstas			mach_complete_async, (heim_sipc_call)s);
299226031Sstas    });
300226031Sstas
301226031Sstas    return KERN_SUCCESS;
302226031Sstas}
303226031Sstas
304226031Sstasstatic int
305226031Sstasmach_init(const char *service, mach_port_t sport, heim_sipc ctx)
306226031Sstas{
307226031Sstas    struct mach_service *s;
308226031Sstas    char *name;
309226031Sstas
310226031Sstas    init_globals();
311226031Sstas
312226031Sstas    s = calloc(1, sizeof(*s));
313226031Sstas    if (s == NULL)
314226031Sstas	return ENOMEM;
315226031Sstas
316226031Sstas    asprintf(&name, "heim-ipc-mach-%s", service);
317226031Sstas
318226031Sstas    s->queue = dispatch_queue_create(name, NULL);
319226031Sstas    free(name);
320226031Sstas    s->sport = sport;
321226031Sstas
322226031Sstas    s->source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV,
323226031Sstas				       s->sport, 0, s->queue);
324226031Sstas    if (s->source == NULL) {
325226031Sstas	dispatch_release(s->queue);
326226031Sstas	free(s);
327226031Sstas	return ENOMEM;
328226031Sstas    }
329226031Sstas    ctx->mech = s;
330226031Sstas
331226031Sstas    dispatch_set_context(s->queue, ctx);
332226031Sstas    dispatch_set_context(s->source, s);
333226031Sstas
334226031Sstas    dispatch_source_set_event_handler(s->source, ^{
335226031Sstas	    dispatch_mig_server(s->source, sizeof(union __RequestUnion__mheim_do_mheim_ipc_subsystem), mheim_ipc_server);
336226031Sstas	});
337226031Sstas
338226031Sstas    dispatch_source_set_cancel_handler(s->source, ^{
339226031Sstas	    heim_sipc ctx = dispatch_get_context(dispatch_get_current_queue());
340226031Sstas	    struct mach_service *st = ctx->mech;
341226031Sstas	    mach_port_mod_refs(mach_task_self(), st->sport,
342226031Sstas			       MACH_PORT_RIGHT_RECEIVE, -1);
343226031Sstas	    dispatch_release(st->queue);
344226031Sstas	    dispatch_release(st->source);
345226031Sstas	    free(st);
346226031Sstas	    free(ctx);
347226031Sstas	});
348226031Sstas
349226031Sstas    dispatch_resume(s->source);
350226031Sstas
351226031Sstas    return 0;
352226031Sstas}
353226031Sstas
354226031Sstasstatic int
355226031Sstasmach_release(heim_sipc ctx)
356226031Sstas{
357226031Sstas    struct mach_service *s = ctx->mech;
358226031Sstas    dispatch_source_cancel(s->source);
359226031Sstas    dispatch_release(s->source);
360226031Sstas    return 0;
361226031Sstas}
362226031Sstas
363226031Sstasstatic mach_port_t
364226031Sstasmach_checkin_or_register(const char *service)
365226031Sstas{
366226031Sstas    mach_port_t mp;
367226031Sstas    kern_return_t kr;
368226031Sstas
369226031Sstas    kr = bootstrap_check_in(bootstrap_port, service, &mp);
370226031Sstas    if (kr == KERN_SUCCESS)
371226031Sstas	return mp;
372226031Sstas
373226031Sstas#if __MAC_OS_X_VERSION_MIN_REQUIRED <= 1050
374226031Sstas    /* Pre SnowLeopard version */
375226031Sstas    kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp);
376226031Sstas    if (kr != KERN_SUCCESS)
377226031Sstas	return MACH_PORT_NULL;
378226031Sstas
379226031Sstas    kr = mach_port_insert_right(mach_task_self(), mp, mp,
380226031Sstas				MACH_MSG_TYPE_MAKE_SEND);
381226031Sstas    if (kr != KERN_SUCCESS) {
382226031Sstas	mach_port_destroy(mach_task_self(), mp);
383226031Sstas	return MACH_PORT_NULL;
384226031Sstas    }
385226031Sstas
386226031Sstas    kr = bootstrap_register(bootstrap_port, rk_UNCONST(service), mp);
387226031Sstas    if (kr != KERN_SUCCESS) {
388226031Sstas	mach_port_destroy(mach_task_self(), mp);
389226031Sstas	return MACH_PORT_NULL;
390226031Sstas    }
391226031Sstas
392226031Sstas    return mp;
393226031Sstas#else
394226031Sstas    return MACH_PORT_NULL;
395226031Sstas#endif
396226031Sstas}
397226031Sstas
398226031Sstas
399226031Sstas#endif /* __APPLE__ && HAVE_GCD */
400226031Sstas
401226031Sstas
402226031Sstasint
403226031Sstasheim_sipc_launchd_mach_init(const char *service,
404226031Sstas			    heim_ipc_callback callback,
405226031Sstas			    void *user, heim_sipc *ctx)
406226031Sstas{
407226031Sstas#if defined(__APPLE__) && defined(HAVE_GCD)
408226031Sstas    mach_port_t sport = MACH_PORT_NULL;
409226031Sstas    heim_sipc c = NULL;
410226031Sstas    int ret;
411226031Sstas
412226031Sstas    *ctx = NULL;
413226031Sstas
414226031Sstas    sport = mach_checkin_or_register(service);
415226031Sstas    if (sport == MACH_PORT_NULL) {
416226031Sstas	ret = ENOENT;
417226031Sstas	goto error;
418226031Sstas    }
419226031Sstas
420226031Sstas    c = calloc(1, sizeof(*c));
421226031Sstas    if (c == NULL) {
422226031Sstas	ret = ENOMEM;
423226031Sstas	goto error;
424226031Sstas    }
425226031Sstas    c->release = mach_release;
426226031Sstas    c->userctx = user;
427226031Sstas    c->callback = callback;
428226031Sstas
429226031Sstas    ret = mach_init(service, sport, c);
430226031Sstas    if (ret)
431226031Sstas	goto error;
432226031Sstas
433226031Sstas    *ctx = c;
434226031Sstas    return 0;
435226031Sstas error:
436226031Sstas    if (c)
437226031Sstas	free(c);
438226031Sstas    if (sport != MACH_PORT_NULL)
439226031Sstas	mach_port_mod_refs(mach_task_self(), sport,
440226031Sstas			   MACH_PORT_RIGHT_RECEIVE, -1);
441226031Sstas    return ret;
442226031Sstas#else /* !(__APPLE__ && HAVE_GCD) */
443226031Sstas    *ctx = NULL;
444226031Sstas    return EINVAL;
445226031Sstas#endif /* __APPLE__ && HAVE_GCD */
446226031Sstas}
447226031Sstas
448226031Sstasstruct client {
449226031Sstas    int fd;
450226031Sstas    heim_ipc_callback callback;
451226031Sstas    void *userctx;
452226031Sstas    int flags;
453226031Sstas#define LISTEN_SOCKET	1
454226031Sstas#define WAITING_READ	2
455226031Sstas#define WAITING_WRITE	4
456226031Sstas#define WAITING_CLOSE	8
457226031Sstas
458226031Sstas#define HTTP_REPLY	16
459226031Sstas
460226031Sstas#define INHERIT_MASK	0xffff0000
461226031Sstas#define INCLUDE_ERROR_CODE (1 << 16)
462226031Sstas#define ALLOW_HTTP	(1<<17)
463226031Sstas#define UNIX_SOCKET	(1<<18)
464226031Sstas    unsigned calls;
465226031Sstas    size_t ptr, len;
466226031Sstas    uint8_t *inmsg;
467226031Sstas    size_t olen;
468226031Sstas    uint8_t *outmsg;
469226031Sstas#ifdef HAVE_GCD
470226031Sstas    dispatch_source_t in;
471226031Sstas    dispatch_source_t out;
472226031Sstas#endif
473226031Sstas    struct {
474226031Sstas	uid_t uid;
475226031Sstas	gid_t gid;
476226031Sstas	pid_t pid;
477226031Sstas    } unixrights;
478226031Sstas};
479226031Sstas
480226031Sstas#ifndef HAVE_GCD
481226031Sstasstatic unsigned num_clients = 0;
482226031Sstasstatic struct client **clients = NULL;
483226031Sstas#endif
484226031Sstas
485226031Sstasstatic void handle_read(struct client *);
486226031Sstasstatic void handle_write(struct client *);
487226031Sstasstatic int maybe_close(struct client *);
488226031Sstas
489226031Sstas/*
490226031Sstas * Update peer credentials from socket.
491226031Sstas *
492226031Sstas * SCM_CREDS can only be updated the first time there is read data to
493226031Sstas * read from the filedescriptor, so if we read do it before this
494226031Sstas * point, the cred data might not be is not there yet.
495226031Sstas */
496226031Sstas
497226031Sstasstatic int
498226031Sstasupdate_client_creds(struct client *c)
499226031Sstas{
500226031Sstas#ifdef HAVE_GETPEERUCRED
501226031Sstas    /* Solaris 10 */
502226031Sstas    {
503226031Sstas	ucred_t *peercred;
504226031Sstas
505226031Sstas	if (getpeerucred(c->fd, &peercred) != 0) {
506226031Sstas	    c->unixrights.uid = ucred_geteuid(peercred);
507226031Sstas	    c->unixrights.gid = ucred_getegid(peercred);
508226031Sstas	    c->unixrights.pid = 0;
509226031Sstas	    ucred_free(peercred);
510226031Sstas	    return 1;
511226031Sstas	}
512226031Sstas    }
513226031Sstas#endif
514226031Sstas#ifdef HAVE_GETPEEREID
515226031Sstas    /* FreeBSD, OpenBSD */
516226031Sstas    {
517226031Sstas	uid_t uid;
518226031Sstas	gid_t gid;
519226031Sstas
520226031Sstas	if (getpeereid(c->fd, &uid, &gid) == 0) {
521226031Sstas	    c->unixrights.uid = uid;
522226031Sstas	    c->unixrights.gid = gid;
523226031Sstas	    c->unixrights.pid = 0;
524226031Sstas	    return 1;
525226031Sstas	}
526226031Sstas    }
527226031Sstas#endif
528226031Sstas#ifdef SO_PEERCRED
529226031Sstas    /* Linux */
530226031Sstas    {
531226031Sstas	struct ucred pc;
532226031Sstas	socklen_t pclen = sizeof(pc);
533226031Sstas
534226031Sstas	if (getsockopt(c->fd, SOL_SOCKET, SO_PEERCRED, (void *)&pc, &pclen) == 0) {
535226031Sstas	    c->unixrights.uid = pc.uid;
536226031Sstas	    c->unixrights.gid = pc.gid;
537226031Sstas	    c->unixrights.pid = pc.pid;
538226031Sstas	    return 1;
539226031Sstas	}
540226031Sstas    }
541226031Sstas#endif
542226031Sstas#if defined(LOCAL_PEERCRED) && defined(XUCRED_VERSION)
543226031Sstas    {
544226031Sstas	struct xucred peercred;
545226031Sstas	socklen_t peercredlen = sizeof(peercred);
546226031Sstas
547226031Sstas	if (getsockopt(c->fd, LOCAL_PEERCRED, 1,
548226031Sstas		       (void *)&peercred, &peercredlen) == 0
549226031Sstas	    && peercred.cr_version == XUCRED_VERSION)
550226031Sstas	{
551226031Sstas	    c->unixrights.uid = peercred.cr_uid;
552226031Sstas	    c->unixrights.gid = peercred.cr_gid;
553226031Sstas	    c->unixrights.pid = 0;
554226031Sstas	    return 1;
555226031Sstas	}
556226031Sstas    }
557226031Sstas#endif
558226031Sstas#if defined(SOCKCREDSIZE) && defined(SCM_CREDS)
559226031Sstas    /* NetBSD */
560226031Sstas    if (c->unixrights.uid == (uid_t)-1) {
561226031Sstas	struct msghdr msg;
562226031Sstas	socklen_t crmsgsize;
563226031Sstas	void *crmsg;
564226031Sstas	struct cmsghdr *cmp;
565226031Sstas	struct sockcred *sc;
566226031Sstas
567226031Sstas	memset(&msg, 0, sizeof(msg));
568233294Sstas	crmsgsize = CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX));
569226031Sstas	if (crmsgsize == 0)
570226031Sstas	    return 1 ;
571226031Sstas
572226031Sstas	crmsg = malloc(crmsgsize);
573226031Sstas	if (crmsg == NULL)
574226031Sstas	    goto failed_scm_creds;
575226031Sstas
576226031Sstas	memset(crmsg, 0, crmsgsize);
577226031Sstas
578226031Sstas	msg.msg_control = crmsg;
579226031Sstas	msg.msg_controllen = crmsgsize;
580226031Sstas
581226031Sstas	if (recvmsg(c->fd, &msg, 0) < 0) {
582226031Sstas	    free(crmsg);
583226031Sstas	    goto failed_scm_creds;
584226031Sstas	}
585226031Sstas
586226031Sstas	if (msg.msg_controllen == 0 || (msg.msg_flags & MSG_CTRUNC) != 0) {
587226031Sstas	    free(crmsg);
588226031Sstas	    goto failed_scm_creds;
589226031Sstas	}
590226031Sstas
591226031Sstas	cmp = CMSG_FIRSTHDR(&msg);
592226031Sstas	if (cmp->cmsg_level != SOL_SOCKET || cmp->cmsg_type != SCM_CREDS) {
593226031Sstas	    free(crmsg);
594226031Sstas	    goto failed_scm_creds;
595226031Sstas	}
596226031Sstas
597226031Sstas	sc = (struct sockcred *)(void *)CMSG_DATA(cmp);
598226031Sstas
599226031Sstas	c->unixrights.uid = sc->sc_euid;
600226031Sstas	c->unixrights.gid = sc->sc_egid;
601226031Sstas	c->unixrights.pid = 0;
602226031Sstas
603226031Sstas	free(crmsg);
604226031Sstas	return 1;
605226031Sstas    } else {
606226031Sstas	/* we already got the cred, just return it */
607226031Sstas	return 1;
608226031Sstas    }
609226031Sstas failed_scm_creds:
610226031Sstas#endif
611226031Sstas    return 0;
612226031Sstas}
613226031Sstas
614226031Sstas
615226031Sstasstatic struct client *
616226031Sstasadd_new_socket(int fd,
617226031Sstas	       int flags,
618226031Sstas	       heim_ipc_callback callback,
619226031Sstas	       void *userctx)
620226031Sstas{
621226031Sstas    struct client *c;
622226031Sstas    int fileflags;
623226031Sstas
624226031Sstas    c = calloc(1, sizeof(*c));
625226031Sstas    if (c == NULL)
626226031Sstas	return NULL;
627226031Sstas
628226031Sstas    if (flags & LISTEN_SOCKET) {
629226031Sstas	c->fd = fd;
630226031Sstas    } else {
631226031Sstas	c->fd = accept(fd, NULL, NULL);
632226031Sstas	if(c->fd < 0) {
633226031Sstas	    free(c);
634226031Sstas	    return NULL;
635226031Sstas	}
636226031Sstas    }
637226031Sstas
638226031Sstas    c->flags = flags;
639226031Sstas    c->callback = callback;
640226031Sstas    c->userctx = userctx;
641226031Sstas
642226031Sstas    fileflags = fcntl(c->fd, F_GETFL, 0);
643226031Sstas    fcntl(c->fd, F_SETFL, fileflags | O_NONBLOCK);
644226031Sstas
645226031Sstas#ifdef HAVE_GCD
646226031Sstas    init_globals();
647226031Sstas
648226031Sstas    c->in = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
649226031Sstas				   c->fd, 0, eventq);
650226031Sstas    c->out = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE,
651226031Sstas				    c->fd, 0, eventq);
652226031Sstas
653226031Sstas    dispatch_source_set_event_handler(c->in, ^{
654226031Sstas	    int rw = (c->flags & WAITING_WRITE);
655226031Sstas	    handle_read(c);
656226031Sstas	    if (rw == 0 && (c->flags & WAITING_WRITE))
657226031Sstas		dispatch_resume(c->out);
658226031Sstas	    if ((c->flags & WAITING_READ) == 0)
659226031Sstas		dispatch_suspend(c->in);
660226031Sstas	    maybe_close(c);
661226031Sstas	});
662226031Sstas    dispatch_source_set_event_handler(c->out, ^{
663226031Sstas	    handle_write(c);
664226031Sstas	    if ((c->flags & WAITING_WRITE) == 0) {
665226031Sstas		dispatch_suspend(c->out);
666226031Sstas	    }
667226031Sstas	    maybe_close(c);
668226031Sstas	});
669226031Sstas
670226031Sstas    dispatch_resume(c->in);
671226031Sstas#else
672226031Sstas    clients = erealloc(clients, sizeof(clients[0]) * (num_clients + 1));
673226031Sstas    clients[num_clients] = c;
674226031Sstas    num_clients++;
675226031Sstas#endif
676226031Sstas
677226031Sstas    return c;
678226031Sstas}
679226031Sstas
680226031Sstasstatic int
681226031Sstasmaybe_close(struct client *c)
682226031Sstas{
683226031Sstas    if (c->calls != 0)
684226031Sstas	return 0;
685226031Sstas    if (c->flags & (WAITING_READ|WAITING_WRITE))
686226031Sstas	return 0;
687226031Sstas
688226031Sstas#ifdef HAVE_GCD
689226031Sstas    dispatch_source_cancel(c->in);
690226031Sstas    if ((c->flags & WAITING_READ) == 0)
691226031Sstas	dispatch_resume(c->in);
692226031Sstas    dispatch_release(c->in);
693226031Sstas
694226031Sstas    dispatch_source_cancel(c->out);
695226031Sstas    if ((c->flags & WAITING_WRITE) == 0)
696226031Sstas	dispatch_resume(c->out);
697226031Sstas    dispatch_release(c->out);
698226031Sstas#endif
699226031Sstas    close(c->fd); /* ref count fd close */
700226031Sstas    free(c);
701226031Sstas    return 1;
702226031Sstas}
703226031Sstas
704226031Sstas
705226031Sstasstruct socket_call {
706226031Sstas    heim_idata in;
707226031Sstas    struct client *c;
708226031Sstas    heim_icred cred;
709226031Sstas};
710226031Sstas
711226031Sstasstatic void
712226031Sstasoutput_data(struct client *c, const void *data, size_t len)
713226031Sstas{
714226031Sstas    if (c->olen + len < c->olen)
715226031Sstas	abort();
716226031Sstas    c->outmsg = erealloc(c->outmsg, c->olen + len);
717226031Sstas    memcpy(&c->outmsg[c->olen], data, len);
718226031Sstas    c->olen += len;
719226031Sstas    c->flags |= WAITING_WRITE;
720226031Sstas}
721226031Sstas
722226031Sstasstatic void
723226031Sstassocket_complete(heim_sipc_call ctx, int returnvalue, heim_idata *reply)
724226031Sstas{
725226031Sstas    struct socket_call *sc = (struct socket_call *)ctx;
726226031Sstas    struct client *c = sc->c;
727226031Sstas
728226031Sstas    /* double complete ? */
729226031Sstas    if (c == NULL)
730226031Sstas	abort();
731226031Sstas
732226031Sstas    if ((c->flags & WAITING_CLOSE) == 0) {
733226031Sstas	uint32_t u32;
734226031Sstas
735226031Sstas	/* length */
736226031Sstas	u32 = htonl(reply->length);
737226031Sstas	output_data(c, &u32, sizeof(u32));
738226031Sstas
739226031Sstas	/* return value */
740226031Sstas	if (c->flags & INCLUDE_ERROR_CODE) {
741226031Sstas	    u32 = htonl(returnvalue);
742226031Sstas	    output_data(c, &u32, sizeof(u32));
743226031Sstas	}
744226031Sstas
745226031Sstas	/* data */
746226031Sstas	output_data(c, reply->data, reply->length);
747226031Sstas
748226031Sstas	/* if HTTP, close connection */
749226031Sstas	if (c->flags & HTTP_REPLY) {
750226031Sstas	    c->flags |= WAITING_CLOSE;
751226031Sstas	    c->flags &= ~WAITING_READ;
752226031Sstas	}
753226031Sstas    }
754226031Sstas
755226031Sstas    c->calls--;
756226031Sstas    if (sc->cred)
757226031Sstas	heim_ipc_free_cred(sc->cred);
758226031Sstas    free(sc->in.data);
759226031Sstas    sc->c = NULL; /* so we can catch double complete */
760226031Sstas    free(sc);
761226031Sstas
762226031Sstas    maybe_close(c);
763226031Sstas}
764226031Sstas
765226031Sstas/* remove HTTP %-quoting from buf */
766226031Sstasstatic int
767226031Sstasde_http(char *buf)
768226031Sstas{
769226031Sstas    unsigned char *p, *q;
770226031Sstas    for(p = q = (unsigned char *)buf; *p; p++, q++) {
771226031Sstas	if(*p == '%' && isxdigit(p[1]) && isxdigit(p[2])) {
772226031Sstas	    unsigned int x;
773226031Sstas	    if(sscanf((char *)p + 1, "%2x", &x) != 1)
774226031Sstas		return -1;
775226031Sstas	    *q = x;
776226031Sstas	    p += 2;
777226031Sstas	} else
778226031Sstas	    *q = *p;
779226031Sstas    }
780226031Sstas    *q = '\0';
781226031Sstas    return 0;
782226031Sstas}
783226031Sstas
784226031Sstasstatic struct socket_call *
785226031Sstashandle_http_tcp(struct client *c)
786226031Sstas{
787226031Sstas    struct socket_call *cs;
788226031Sstas    char *s, *p, *t;
789226031Sstas    void *data;
790226031Sstas    char *proto;
791226031Sstas    int len;
792226031Sstas
793226031Sstas    s = (char *)c->inmsg;
794226031Sstas
795226031Sstas    p = strstr(s, "\r\n");
796226031Sstas    if (p == NULL)
797226031Sstas	return NULL;
798226031Sstas
799226031Sstas    *p = 0;
800226031Sstas
801226031Sstas    p = NULL;
802226031Sstas    t = strtok_r(s, " \t", &p);
803226031Sstas    if (t == NULL)
804226031Sstas	return NULL;
805226031Sstas
806226031Sstas    t = strtok_r(NULL, " \t", &p);
807226031Sstas    if (t == NULL)
808226031Sstas	return NULL;
809226031Sstas
810226031Sstas    data = malloc(strlen(t));
811226031Sstas    if (data == NULL)
812226031Sstas	return NULL;
813226031Sstas
814226031Sstas    if(*t == '/')
815226031Sstas	t++;
816226031Sstas    if(de_http(t) != 0) {
817226031Sstas	free(data);
818226031Sstas	return NULL;
819226031Sstas    }
820226031Sstas    proto = strtok_r(NULL, " \t", &p);
821226031Sstas    if (proto == NULL) {
822226031Sstas	free(data);
823226031Sstas	return NULL;
824226031Sstas    }
825226031Sstas    len = base64_decode(t, data);
826226031Sstas    if(len <= 0){
827226031Sstas	const char *msg =
828226031Sstas	    " 404 Not found\r\n"
829226031Sstas	    "Server: Heimdal/" VERSION "\r\n"
830226031Sstas	    "Cache-Control: no-cache\r\n"
831226031Sstas	    "Pragma: no-cache\r\n"
832226031Sstas	    "Content-type: text/html\r\n"
833226031Sstas	    "Content-transfer-encoding: 8bit\r\n\r\n"
834226031Sstas	    "<TITLE>404 Not found</TITLE>\r\n"
835226031Sstas	    "<H1>404 Not found</H1>\r\n"
836226031Sstas	    "That page doesn't exist, maybe you are looking for "
837226031Sstas	    "<A HREF=\"http://www.h5l.org/\">Heimdal</A>?\r\n";
838226031Sstas	free(data);
839226031Sstas	output_data(c, proto, strlen(proto));
840226031Sstas	output_data(c, msg, strlen(msg));
841226031Sstas	return NULL;
842226031Sstas    }
843226031Sstas
844226031Sstas    cs = emalloc(sizeof(*cs));
845226031Sstas    cs->c = c;
846226031Sstas    cs->in.data = data;
847226031Sstas    cs->in.length = len;
848226031Sstas    c->ptr = 0;
849226031Sstas
850226031Sstas    {
851226031Sstas	const char *msg =
852226031Sstas	    " 200 OK\r\n"
853226031Sstas	    "Server: Heimdal/" VERSION "\r\n"
854226031Sstas	    "Cache-Control: no-cache\r\n"
855226031Sstas	    "Pragma: no-cache\r\n"
856226031Sstas	    "Content-type: application/octet-stream\r\n"
857226031Sstas	    "Content-transfer-encoding: binary\r\n\r\n";
858226031Sstas	output_data(c, proto, strlen(proto));
859226031Sstas	output_data(c, msg, strlen(msg));
860226031Sstas    }
861226031Sstas
862226031Sstas    return cs;
863226031Sstas}
864226031Sstas
865226031Sstas
866226031Sstasstatic void
867226031Sstashandle_read(struct client *c)
868226031Sstas{
869226031Sstas    ssize_t len;
870226031Sstas    uint32_t dlen;
871226031Sstas
872226031Sstas    if (c->flags & LISTEN_SOCKET) {
873226031Sstas	add_new_socket(c->fd,
874226031Sstas		       WAITING_READ | (c->flags & INHERIT_MASK),
875226031Sstas		       c->callback,
876226031Sstas		       c->userctx);
877226031Sstas	return;
878226031Sstas    }
879226031Sstas
880226031Sstas    if (c->ptr - c->len < 1024) {
881226031Sstas	c->inmsg = erealloc(c->inmsg,
882226031Sstas			    c->len + 1024);
883226031Sstas	c->len += 1024;
884226031Sstas    }
885226031Sstas
886226031Sstas    len = read(c->fd, c->inmsg + c->ptr, c->len - c->ptr);
887226031Sstas    if (len <= 0) {
888226031Sstas	c->flags |= WAITING_CLOSE;
889226031Sstas	c->flags &= ~WAITING_READ;
890226031Sstas	return;
891226031Sstas    }
892226031Sstas    c->ptr += len;
893226031Sstas    if (c->ptr > c->len)
894226031Sstas	abort();
895226031Sstas
896226031Sstas    while (c->ptr >= sizeof(dlen)) {
897226031Sstas	struct socket_call *cs;
898226031Sstas
899226031Sstas	if((c->flags & ALLOW_HTTP) && c->ptr >= 4 &&
900226031Sstas	   strncmp((char *)c->inmsg, "GET ", 4) == 0 &&
901226031Sstas	   strncmp((char *)c->inmsg + c->ptr - 4, "\r\n\r\n", 4) == 0) {
902226031Sstas
903226031Sstas	    /* remove the trailing \r\n\r\n so the string is NUL terminated */
904226031Sstas	    c->inmsg[c->ptr - 4] = '\0';
905226031Sstas
906226031Sstas	    c->flags |= HTTP_REPLY;
907226031Sstas
908226031Sstas	    cs = handle_http_tcp(c);
909226031Sstas	    if (cs == NULL) {
910226031Sstas		c->flags |= WAITING_CLOSE;
911226031Sstas		c->flags &= ~WAITING_READ;
912226031Sstas		break;
913226031Sstas	    }
914226031Sstas	} else {
915226031Sstas	    memcpy(&dlen, c->inmsg, sizeof(dlen));
916226031Sstas	    dlen = ntohl(dlen);
917226031Sstas
918226031Sstas	    if (dlen > MAX_PACKET_SIZE) {
919226031Sstas		c->flags |= WAITING_CLOSE;
920226031Sstas		c->flags &= ~WAITING_READ;
921226031Sstas		return;
922226031Sstas	    }
923226031Sstas	    if (dlen > c->ptr - sizeof(dlen)) {
924226031Sstas		break;
925226031Sstas	    }
926226031Sstas
927226031Sstas	    cs = emalloc(sizeof(*cs));
928226031Sstas	    cs->c = c;
929226031Sstas	    cs->in.data = emalloc(dlen);
930226031Sstas	    memcpy(cs->in.data, c->inmsg + sizeof(dlen), dlen);
931226031Sstas	    cs->in.length = dlen;
932226031Sstas
933226031Sstas	    c->ptr -= sizeof(dlen) + dlen;
934226031Sstas	    memmove(c->inmsg,
935226031Sstas		    c->inmsg + sizeof(dlen) + dlen,
936226031Sstas		    c->ptr);
937226031Sstas	}
938226031Sstas
939226031Sstas	c->calls++;
940226031Sstas
941226031Sstas	if ((c->flags & UNIX_SOCKET) != 0) {
942226031Sstas	    if (update_client_creds(c))
943226031Sstas		_heim_ipc_create_cred(c->unixrights.uid, c->unixrights.gid,
944226031Sstas				      c->unixrights.pid, -1, &cs->cred);
945226031Sstas	}
946226031Sstas
947226031Sstas	c->callback(c->userctx, &cs->in,
948226031Sstas		    cs->cred, socket_complete,
949226031Sstas		    (heim_sipc_call)cs);
950226031Sstas    }
951226031Sstas}
952226031Sstas
953226031Sstasstatic void
954226031Sstashandle_write(struct client *c)
955226031Sstas{
956226031Sstas    ssize_t len;
957226031Sstas
958226031Sstas    len = write(c->fd, c->outmsg, c->olen);
959226031Sstas    if (len <= 0) {
960226031Sstas	c->flags |= WAITING_CLOSE;
961226031Sstas	c->flags &= ~(WAITING_WRITE);
962226031Sstas    } else if (c->olen != (size_t)len) {
963226031Sstas	memmove(&c->outmsg[0], &c->outmsg[len], c->olen - len);
964226031Sstas	c->olen -= len;
965226031Sstas    } else {
966226031Sstas	c->olen = 0;
967226031Sstas	free(c->outmsg);
968226031Sstas	c->outmsg = NULL;
969226031Sstas	c->flags &= ~(WAITING_WRITE);
970226031Sstas    }
971226031Sstas}
972226031Sstas
973226031Sstas
974226031Sstas#ifndef HAVE_GCD
975226031Sstas
976226031Sstasstatic void
977226031Sstasprocess_loop(void)
978226031Sstas{
979226031Sstas    struct pollfd *fds;
980226031Sstas    unsigned n;
981226031Sstas    unsigned num_fds;
982226031Sstas
983226031Sstas    while(num_clients > 0) {
984226031Sstas
985226031Sstas	fds = malloc(num_clients * sizeof(fds[0]));
986226031Sstas	if(fds == NULL)
987226031Sstas	    abort();
988226031Sstas
989226031Sstas	num_fds = num_clients;
990226031Sstas
991226031Sstas	for (n = 0 ; n < num_fds; n++) {
992226031Sstas	    fds[n].fd = clients[n]->fd;
993226031Sstas	    fds[n].events = 0;
994226031Sstas	    if (clients[n]->flags & WAITING_READ)
995226031Sstas		fds[n].events |= POLLIN;
996226031Sstas	    if (clients[n]->flags & WAITING_WRITE)
997226031Sstas		fds[n].events |= POLLOUT;
998226031Sstas
999226031Sstas	    fds[n].revents = 0;
1000226031Sstas	}
1001226031Sstas
1002226031Sstas	poll(fds, num_fds, -1);
1003226031Sstas
1004226031Sstas	for (n = 0 ; n < num_fds; n++) {
1005226031Sstas	    if (clients[n] == NULL)
1006226031Sstas		continue;
1007226031Sstas	    if (fds[n].revents & POLLERR) {
1008226031Sstas		clients[n]->flags |= WAITING_CLOSE;
1009226031Sstas		continue;
1010226031Sstas	    }
1011226031Sstas
1012226031Sstas	    if (fds[n].revents & POLLIN)
1013226031Sstas		handle_read(clients[n]);
1014226031Sstas	    if (fds[n].revents & POLLOUT)
1015226031Sstas		handle_write(clients[n]);
1016226031Sstas	}
1017226031Sstas
1018226031Sstas	n = 0;
1019226031Sstas	while (n < num_clients) {
1020226031Sstas	    struct client *c = clients[n];
1021226031Sstas	    if (maybe_close(c)) {
1022226031Sstas		if (n < num_clients - 1)
1023226031Sstas		    clients[n] = clients[num_clients - 1];
1024226031Sstas		num_clients--;
1025226031Sstas	    } else
1026226031Sstas		n++;
1027226031Sstas	}
1028226031Sstas
1029226031Sstas	free(fds);
1030226031Sstas    }
1031226031Sstas}
1032226031Sstas
1033226031Sstas#endif
1034226031Sstas
1035226031Sstasstatic int
1036226031Sstassocket_release(heim_sipc ctx)
1037226031Sstas{
1038226031Sstas    struct client *c = ctx->mech;
1039226031Sstas    c->flags |= WAITING_CLOSE;
1040226031Sstas    return 0;
1041226031Sstas}
1042226031Sstas
1043226031Sstasint
1044226031Sstasheim_sipc_stream_listener(int fd, int type,
1045226031Sstas			  heim_ipc_callback callback,
1046226031Sstas			  void *user, heim_sipc *ctx)
1047226031Sstas{
1048226031Sstas    heim_sipc ct = calloc(1, sizeof(*ct));
1049226031Sstas    struct client *c;
1050226031Sstas
1051226031Sstas    if ((type & HEIM_SIPC_TYPE_IPC) && (type & (HEIM_SIPC_TYPE_UINT32|HEIM_SIPC_TYPE_HTTP)))
1052226031Sstas	return EINVAL;
1053226031Sstas
1054226031Sstas    switch (type) {
1055226031Sstas    case HEIM_SIPC_TYPE_IPC:
1056226031Sstas	c = add_new_socket(fd, LISTEN_SOCKET|WAITING_READ|INCLUDE_ERROR_CODE, callback, user);
1057226031Sstas	break;
1058226031Sstas    case HEIM_SIPC_TYPE_UINT32:
1059226031Sstas	c = add_new_socket(fd, LISTEN_SOCKET|WAITING_READ, callback, user);
1060226031Sstas	break;
1061226031Sstas    case HEIM_SIPC_TYPE_HTTP:
1062226031Sstas    case HEIM_SIPC_TYPE_UINT32|HEIM_SIPC_TYPE_HTTP:
1063226031Sstas	c = add_new_socket(fd, LISTEN_SOCKET|WAITING_READ|ALLOW_HTTP, callback, user);
1064226031Sstas	break;
1065226031Sstas    default:
1066226031Sstas	free(ct);
1067226031Sstas	return EINVAL;
1068226031Sstas    }
1069226031Sstas
1070226031Sstas    ct->mech = c;
1071226031Sstas    ct->release = socket_release;
1072226031Sstas
1073226031Sstas    c->unixrights.uid = (uid_t) -1;
1074226031Sstas    c->unixrights.gid = (gid_t) -1;
1075226031Sstas    c->unixrights.pid = (pid_t) 0;
1076226031Sstas
1077226031Sstas    *ctx = ct;
1078226031Sstas    return 0;
1079226031Sstas}
1080226031Sstas
1081226031Sstasint
1082226031Sstasheim_sipc_service_unix(const char *service,
1083226031Sstas		       heim_ipc_callback callback,
1084226031Sstas		       void *user, heim_sipc *ctx)
1085226031Sstas{
1086226031Sstas    struct sockaddr_un un;
1087226031Sstas    int fd, ret;
1088226031Sstas
1089226031Sstas    un.sun_family = AF_UNIX;
1090226031Sstas
1091226031Sstas    snprintf(un.sun_path, sizeof(un.sun_path),
1092226031Sstas	     "/var/run/.heim_%s-socket", service);
1093226031Sstas    fd = socket(AF_UNIX, SOCK_STREAM, 0);
1094226031Sstas    if (fd < 0)
1095226031Sstas	return errno;
1096226031Sstas
1097226031Sstas    socket_set_reuseaddr(fd, 1);
1098226031Sstas#ifdef LOCAL_CREDS
1099226031Sstas    {
1100226031Sstas	int one = 1;
1101226031Sstas	setsockopt(fd, 0, LOCAL_CREDS, (void *)&one, sizeof(one));
1102226031Sstas    }
1103226031Sstas#endif
1104226031Sstas
1105226031Sstas    unlink(un.sun_path);
1106226031Sstas
1107226031Sstas    if (bind(fd, (struct sockaddr *)&un, sizeof(un)) < 0) {
1108226031Sstas	close(fd);
1109226031Sstas	return errno;
1110226031Sstas    }
1111226031Sstas
1112226031Sstas    if (listen(fd, SOMAXCONN) < 0) {
1113226031Sstas	close(fd);
1114226031Sstas	return errno;
1115226031Sstas    }
1116226031Sstas
1117226031Sstas    chmod(un.sun_path, 0666);
1118226031Sstas
1119226031Sstas    ret = heim_sipc_stream_listener(fd, HEIM_SIPC_TYPE_IPC,
1120226031Sstas				    callback, user, ctx);
1121226031Sstas    if (ret == 0) {
1122226031Sstas	struct client *c = (*ctx)->mech;
1123226031Sstas	c->flags |= UNIX_SOCKET;
1124226031Sstas    }
1125226031Sstas
1126226031Sstas    return ret;
1127226031Sstas}
1128226031Sstas
1129226031Sstas/**
1130226031Sstas * Set the idle timeout value
1131226031Sstas
1132226031Sstas * The timeout event handler is triggered recurrently every idle
1133226031Sstas * period `t'. The default action is rather draconian and just calls
1134226031Sstas * exit(0), so you might want to change this to something more
1135226031Sstas * graceful using heim_sipc_set_timeout_handler().
1136226031Sstas */
1137226031Sstas
1138226031Sstasvoid
1139226031Sstasheim_sipc_timeout(time_t t)
1140226031Sstas{
1141226031Sstas#ifdef HAVE_GCD
1142226031Sstas    static dispatch_once_t timeoutonce;
1143226031Sstas    init_globals();
1144226031Sstas    dispatch_sync(timerq, ^{
1145226031Sstas	    timeoutvalue = t;
1146226031Sstas	    set_timer();
1147226031Sstas	});
1148226031Sstas    dispatch_once(&timeoutonce, ^{  dispatch_resume(timer); });
1149226031Sstas#else
1150226031Sstas    abort();
1151226031Sstas#endif
1152226031Sstas}
1153226031Sstas
1154226031Sstas/**
1155226031Sstas * Set the timeout event handler
1156226031Sstas *
1157226031Sstas * Replaces the default idle timeout action.
1158226031Sstas */
1159226031Sstas
1160226031Sstasvoid
1161226031Sstasheim_sipc_set_timeout_handler(void (*func)(void))
1162226031Sstas{
1163226031Sstas#ifdef HAVE_GCD
1164226031Sstas    init_globals();
1165226031Sstas    dispatch_sync(timerq, ^{ timer_ev = func; });
1166226031Sstas#else
1167226031Sstas    abort();
1168226031Sstas#endif
1169226031Sstas}
1170226031Sstas
1171226031Sstas
1172226031Sstasvoid
1173226031Sstasheim_sipc_free_context(heim_sipc ctx)
1174226031Sstas{
1175226031Sstas    (ctx->release)(ctx);
1176226031Sstas}
1177226031Sstas
1178226031Sstasvoid
1179226031Sstasheim_ipc_main(void)
1180226031Sstas{
1181226031Sstas#ifdef HAVE_GCD
1182226031Sstas    dispatch_main();
1183226031Sstas#else
1184226031Sstas    process_loop();
1185226031Sstas#endif
1186226031Sstas}
1187226031Sstas
1188