lib/ipc: heim_sipc_stream_listener 'ct' lifecycle
[heimdal.git] / lib / ipc / server.c
blobae6c2f73f047c89feb4cac7eb88728230508ec99
1 /*
2 * Copyright (c) 2009 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
36 #include "hi_locl.h"
37 #include <assert.h>
38 #include <err.h>
40 #define MAX_PACKET_SIZE (128 * 1024)
42 struct heim_sipc {
43 int (*release)(heim_sipc ctx);
44 heim_ipc_callback callback;
45 void *userctx;
46 void *mech;
49 #if defined(__APPLE__) && defined(HAVE_GCD)
51 #include "heim_ipcServer.h"
52 #include "heim_ipc_reply.h"
53 #include "heim_ipc_async.h"
55 static dispatch_source_t timer;
56 static dispatch_queue_t timerq;
57 static uint64_t timeoutvalue;
59 static dispatch_queue_t eventq;
61 static dispatch_queue_t workq;
63 static void
64 default_timer_ev(void)
66 exit(0);
69 static void (*timer_ev)(void) = default_timer_ev;
71 static void
72 set_timer(void)
74 dispatch_source_set_timer(timer,
75 dispatch_time(DISPATCH_TIME_NOW,
76 timeoutvalue * NSEC_PER_SEC),
77 timeoutvalue * NSEC_PER_SEC, 1000000);
80 static void
81 init_globals(void)
83 static dispatch_once_t once;
84 dispatch_once(&once, ^{
85 timerq = dispatch_queue_create("hiem-sipc-timer-q", NULL);
86 timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, timerq);
87 dispatch_source_set_event_handler(timer, ^{ timer_ev(); } );
89 workq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
90 eventq = dispatch_queue_create("heim-ipc.event-queue", NULL);
91 });
94 static void
95 suspend_timer(void)
97 dispatch_suspend(timer);
100 static void
101 restart_timer(void)
103 dispatch_sync(timerq, ^{ set_timer(); });
104 dispatch_resume(timer);
107 struct mach_service {
108 mach_port_t sport;
109 dispatch_source_t source;
110 dispatch_queue_t queue;
113 struct mach_call_ctx {
114 mach_port_t reply_port;
115 heim_icred cred;
116 heim_idata req;
120 static void
121 mach_complete_sync(heim_sipc_call ctx, int returnvalue, heim_idata *reply)
123 struct mach_call_ctx *s = (struct mach_call_ctx *)ctx;
124 heim_ipc_message_inband_t replyin;
125 mach_msg_type_number_t replyinCnt;
126 heim_ipc_message_outband_t replyout;
127 mach_msg_type_number_t replyoutCnt;
128 kern_return_t kr;
130 if (returnvalue) {
131 /* on error, no reply */
132 replyinCnt = 0;
133 replyout = 0; replyoutCnt = 0;
134 kr = KERN_SUCCESS;
135 } else if (reply->length < 2048) {
136 replyinCnt = reply->length;
137 memcpy(replyin, reply->data, replyinCnt);
138 replyout = 0; replyoutCnt = 0;
139 kr = KERN_SUCCESS;
140 } else {
141 replyinCnt = 0;
142 kr = vm_read(mach_task_self(),
143 (vm_address_t)reply->data, reply->length,
144 (vm_address_t *)&replyout, &replyoutCnt);
147 mheim_ripc_call_reply(s->reply_port, returnvalue,
148 replyin, replyinCnt,
149 replyout, replyoutCnt);
151 heim_ipc_free_cred(s->cred);
152 free(s->req.data);
153 free(s);
154 restart_timer();
157 static void
158 mach_complete_async(heim_sipc_call ctx, int returnvalue, heim_idata *reply)
160 struct mach_call_ctx *s = (struct mach_call_ctx *)ctx;
161 heim_ipc_message_inband_t replyin;
162 mach_msg_type_number_t replyinCnt;
163 heim_ipc_message_outband_t replyout;
164 mach_msg_type_number_t replyoutCnt;
165 kern_return_t kr;
167 if (returnvalue) {
168 /* on error, no reply */
169 replyinCnt = 0;
170 replyout = 0; replyoutCnt = 0;
171 kr = KERN_SUCCESS;
172 } else if (reply->length < 2048) {
173 replyinCnt = reply->length;
174 memcpy(replyin, reply->data, replyinCnt);
175 replyout = 0; replyoutCnt = 0;
176 kr = KERN_SUCCESS;
177 } else {
178 replyinCnt = 0;
179 kr = vm_read(mach_task_self(),
180 (vm_address_t)reply->data, reply->length,
181 (vm_address_t *)&replyout, &replyoutCnt);
184 kr = mheim_aipc_acall_reply(s->reply_port, returnvalue,
185 replyin, replyinCnt,
186 replyout, replyoutCnt);
187 heim_ipc_free_cred(s->cred);
188 free(s->req.data);
189 free(s);
190 restart_timer();
194 kern_return_t
195 mheim_do_call(mach_port_t server_port,
196 audit_token_t client_creds,
197 mach_port_t reply_port,
198 heim_ipc_message_inband_t requestin,
199 mach_msg_type_number_t requestinCnt,
200 heim_ipc_message_outband_t requestout,
201 mach_msg_type_number_t requestoutCnt,
202 int *returnvalue,
203 heim_ipc_message_inband_t replyin,
204 mach_msg_type_number_t *replyinCnt,
205 heim_ipc_message_outband_t *replyout,
206 mach_msg_type_number_t *replyoutCnt)
208 heim_sipc ctx = dispatch_get_context(dispatch_get_current_queue());
209 struct mach_call_ctx *s;
210 kern_return_t kr;
211 uid_t uid;
212 gid_t gid;
213 pid_t pid;
214 au_asid_t session;
216 *replyout = NULL;
217 *replyoutCnt = 0;
218 *replyinCnt = 0;
220 s = malloc(sizeof(*s));
221 if (s == NULL)
222 return KERN_MEMORY_FAILURE; /* XXX */
224 s->reply_port = reply_port;
226 audit_token_to_au32(client_creds, NULL, &uid, &gid, NULL, NULL, &pid, &session, NULL);
228 kr = _heim_ipc_create_cred(uid, gid, pid, session, &s->cred);
229 if (kr) {
230 free(s);
231 return kr;
234 suspend_timer();
236 if (requestinCnt) {
237 s->req.data = malloc(requestinCnt);
238 memcpy(s->req.data, requestin, requestinCnt);
239 s->req.length = requestinCnt;
240 } else {
241 s->req.data = malloc(requestoutCnt);
242 memcpy(s->req.data, requestout, requestoutCnt);
243 s->req.length = requestoutCnt;
246 dispatch_async(workq, ^{
247 (ctx->callback)(ctx->userctx, &s->req, s->cred,
248 mach_complete_sync, (heim_sipc_call)s);
251 return MIG_NO_REPLY;
254 kern_return_t
255 mheim_do_call_request(mach_port_t server_port,
256 audit_token_t client_creds,
257 mach_port_t reply_port,
258 heim_ipc_message_inband_t requestin,
259 mach_msg_type_number_t requestinCnt,
260 heim_ipc_message_outband_t requestout,
261 mach_msg_type_number_t requestoutCnt)
263 heim_sipc ctx = dispatch_get_context(dispatch_get_current_queue());
264 struct mach_call_ctx *s;
265 kern_return_t kr;
266 uid_t uid;
267 gid_t gid;
268 pid_t pid;
269 au_asid_t session;
271 s = malloc(sizeof(*s));
272 if (s == NULL)
273 return KERN_MEMORY_FAILURE; /* XXX */
275 s->reply_port = reply_port;
277 audit_token_to_au32(client_creds, NULL, &uid, &gid, NULL, NULL, &pid, &session, NULL);
279 kr = _heim_ipc_create_cred(uid, gid, pid, session, &s->cred);
280 if (kr) {
281 free(s);
282 return kr;
285 suspend_timer();
287 if (requestinCnt) {
288 s->req.data = malloc(requestinCnt);
289 memcpy(s->req.data, requestin, requestinCnt);
290 s->req.length = requestinCnt;
291 } else {
292 s->req.data = malloc(requestoutCnt);
293 memcpy(s->req.data, requestout, requestoutCnt);
294 s->req.length = requestoutCnt;
297 dispatch_async(workq, ^{
298 (ctx->callback)(ctx->userctx, &s->req, s->cred,
299 mach_complete_async, (heim_sipc_call)s);
302 return KERN_SUCCESS;
305 static int
306 mach_init(const char *service, mach_port_t sport, heim_sipc ctx)
308 struct mach_service *s;
309 char *name;
311 init_globals();
313 s = calloc(1, sizeof(*s));
314 if (s == NULL)
315 return ENOMEM;
317 asprintf(&name, "heim-ipc-mach-%s", service);
319 s->queue = dispatch_queue_create(name, NULL);
320 free(name);
321 s->sport = sport;
323 s->source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV,
324 s->sport, 0, s->queue);
325 if (s->source == NULL) {
326 dispatch_release(s->queue);
327 free(s);
328 return ENOMEM;
330 ctx->mech = s;
332 dispatch_set_context(s->queue, ctx);
333 dispatch_set_context(s->source, s);
335 dispatch_source_set_event_handler(s->source, ^{
336 dispatch_mig_server(s->source, sizeof(union __RequestUnion__mheim_do_mheim_ipc_subsystem), mheim_ipc_server);
339 dispatch_source_set_cancel_handler(s->source, ^{
340 heim_sipc sctx = dispatch_get_context(dispatch_get_current_queue());
341 struct mach_service *st = sctx->mech;
342 mach_port_mod_refs(mach_task_self(), st->sport,
343 MACH_PORT_RIGHT_RECEIVE, -1);
344 dispatch_release(st->queue);
345 dispatch_release(st->source);
346 free(st);
347 free(sctx);
350 dispatch_resume(s->source);
352 return 0;
355 static int
356 mach_release(heim_sipc ctx)
358 struct mach_service *s = ctx->mech;
359 dispatch_source_cancel(s->source);
360 dispatch_release(s->source);
361 return 0;
364 static mach_port_t
365 mach_checkin_or_register(const char *service)
367 mach_port_t mp;
368 kern_return_t kr;
370 kr = bootstrap_check_in(bootstrap_port, service, &mp);
371 if (kr == KERN_SUCCESS)
372 return mp;
374 #if __MAC_OS_X_VERSION_MIN_REQUIRED <= 1050
375 /* Pre SnowLeopard version */
376 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp);
377 if (kr != KERN_SUCCESS)
378 return MACH_PORT_NULL;
380 kr = mach_port_insert_right(mach_task_self(), mp, mp,
381 MACH_MSG_TYPE_MAKE_SEND);
382 if (kr != KERN_SUCCESS) {
383 mach_port_destroy(mach_task_self(), mp);
384 return MACH_PORT_NULL;
387 kr = bootstrap_register(bootstrap_port, rk_UNCONST(service), mp);
388 if (kr != KERN_SUCCESS) {
389 mach_port_destroy(mach_task_self(), mp);
390 return MACH_PORT_NULL;
393 return mp;
394 #else
395 return MACH_PORT_NULL;
396 #endif
400 #endif /* __APPLE__ && HAVE_GCD */
404 heim_sipc_launchd_mach_init(const char *service,
405 heim_ipc_callback callback,
406 void *user, heim_sipc *ctx)
408 #if defined(__APPLE__) && defined(HAVE_GCD)
409 mach_port_t sport = MACH_PORT_NULL;
410 heim_sipc c = NULL;
411 int ret;
413 *ctx = NULL;
415 sport = mach_checkin_or_register(service);
416 if (sport == MACH_PORT_NULL) {
417 ret = ENOENT;
418 goto error;
421 c = calloc(1, sizeof(*c));
422 if (c == NULL) {
423 ret = ENOMEM;
424 goto error;
426 c->release = mach_release;
427 c->userctx = user;
428 c->callback = callback;
430 ret = mach_init(service, sport, c);
431 if (ret)
432 goto error;
434 *ctx = c;
435 return 0;
436 error:
437 if (c)
438 free(c);
439 if (sport != MACH_PORT_NULL)
440 mach_port_mod_refs(mach_task_self(), sport,
441 MACH_PORT_RIGHT_RECEIVE, -1);
442 return ret;
443 #else /* !(__APPLE__ && HAVE_GCD) */
444 *ctx = NULL;
445 return EINVAL;
446 #endif /* __APPLE__ && HAVE_GCD */
449 struct client {
450 int fd;
451 heim_ipc_callback callback;
452 void *userctx;
453 int flags;
454 #define LISTEN_SOCKET 1
455 #define WAITING_READ 2
456 #define WAITING_WRITE 4
457 #define WAITING_CLOSE 8
459 #define HTTP_REPLY 16
460 #define DOOR_FD 32
462 #define INHERIT_MASK 0xffff0000
463 #define INCLUDE_ERROR_CODE (1 << 16)
464 #define ALLOW_HTTP (1<<17)
465 #define UNIX_SOCKET (1<<18)
466 unsigned calls;
467 size_t ptr, len;
468 uint8_t *inmsg;
469 size_t olen;
470 uint8_t *outmsg;
471 #ifdef HAVE_GCD
472 dispatch_source_t in;
473 dispatch_source_t out;
474 #endif
475 struct {
476 uid_t uid;
477 gid_t gid;
478 pid_t pid;
479 } unixrights;
482 #ifndef HAVE_GCD
483 static unsigned num_clients = 0;
484 static struct client **clients = NULL;
485 #endif
487 static void handle_read(struct client *);
488 static void handle_write(struct client *);
489 static int maybe_close(struct client *);
492 * Update peer credentials from socket.
494 * SCM_CREDS can only be updated the first time there is read data to
495 * read from the filedescriptor, so if we read do it before this
496 * point, the cred data might not be is not there yet.
499 static int
500 update_client_creds(struct client *c)
502 #ifdef HAVE_GETPEERUCRED
503 /* Solaris 10 */
505 ucred_t *peercred = NULL;
507 if (getpeerucred(c->fd, &peercred) == 0) {
508 c->unixrights.uid = ucred_geteuid(peercred);
509 c->unixrights.gid = ucred_getegid(peercred);
510 c->unixrights.pid = 0;
511 ucred_free(peercred);
512 return 1;
515 #endif
516 #ifdef HAVE_GETPEEREID
517 /* FreeBSD, OpenBSD */
519 uid_t uid;
520 gid_t gid;
522 if (getpeereid(c->fd, &uid, &gid) == 0) {
523 c->unixrights.uid = uid;
524 c->unixrights.gid = gid;
525 c->unixrights.pid = 0;
526 return 1;
529 #endif
530 #if defined(SO_PEERCRED) && defined(__linux__)
531 /* Linux */
533 struct ucred pc;
534 socklen_t pclen = sizeof(pc);
536 if (getsockopt(c->fd, SOL_SOCKET, SO_PEERCRED, (void *)&pc, &pclen) == 0) {
537 c->unixrights.uid = pc.uid;
538 c->unixrights.gid = pc.gid;
539 c->unixrights.pid = pc.pid;
540 return 1;
543 #endif
544 #if defined(LOCAL_PEERCRED) && defined(XUCRED_VERSION)
546 struct xucred peercred;
547 socklen_t peercredlen = sizeof(peercred);
549 if (getsockopt(c->fd, LOCAL_PEERCRED, 1,
550 (void *)&peercred, &peercredlen) == 0
551 && peercred.cr_version == XUCRED_VERSION)
553 c->unixrights.uid = peercred.cr_uid;
554 c->unixrights.gid = peercred.cr_gid;
555 c->unixrights.pid = 0;
556 return 1;
559 #endif
560 #if defined(SOCKCREDSIZE) && defined(SCM_CREDS)
561 /* NetBSD */
562 if (c->unixrights.uid == (uid_t)-1) {
563 struct msghdr msg;
564 socklen_t crmsgsize;
565 void *crmsg;
566 struct cmsghdr *cmp;
567 struct sockcred *sc;
569 memset(&msg, 0, sizeof(msg));
570 crmsgsize = CMSG_SPACE(SOCKCREDSIZE(NGROUPS));
571 if (crmsgsize == 0)
572 return 1 ;
574 crmsg = malloc(crmsgsize);
575 if (crmsg == NULL)
576 goto failed_scm_creds;
578 memset(crmsg, 0, crmsgsize);
580 msg.msg_control = crmsg;
581 msg.msg_controllen = crmsgsize;
583 if (recvmsg(c->fd, &msg, 0) < 0) {
584 free(crmsg);
585 goto failed_scm_creds;
588 if (msg.msg_controllen == 0 || (msg.msg_flags & MSG_CTRUNC) != 0) {
589 free(crmsg);
590 goto failed_scm_creds;
593 cmp = CMSG_FIRSTHDR(&msg);
594 if (cmp->cmsg_level != SOL_SOCKET || cmp->cmsg_type != SCM_CREDS) {
595 free(crmsg);
596 goto failed_scm_creds;
599 sc = (struct sockcred *)(void *)CMSG_DATA(cmp);
601 c->unixrights.uid = sc->sc_euid;
602 c->unixrights.gid = sc->sc_egid;
603 c->unixrights.pid = 0;
605 free(crmsg);
606 return 1;
607 } else {
608 /* we already got the cred, just return it */
609 return 1;
611 failed_scm_creds:
612 #endif
613 return 0;
616 static struct client *
617 add_new_socket(int fd,
618 int flags,
619 heim_ipc_callback callback,
620 void *userctx)
622 struct client *c;
623 int fileflags;
625 c = calloc(1, sizeof(*c));
626 if (c == NULL)
627 return NULL;
629 if (flags & LISTEN_SOCKET) {
630 c->fd = fd;
631 } else if (flags & DOOR_FD) {
632 c->fd = -1; /* cannot poll a door descriptor */
633 } else {
634 c->fd = accept(fd, NULL, NULL);
635 if(c->fd < 0) {
636 free(c);
637 return NULL;
641 c->flags = flags;
642 c->callback = callback;
643 c->userctx = userctx;
645 fileflags = fcntl(c->fd, F_GETFL, 0);
646 fcntl(c->fd, F_SETFL, fileflags | O_NONBLOCK);
648 #ifdef HAVE_GCD
649 init_globals();
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);
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);
665 dispatch_source_set_event_handler(c->out, ^{
666 handle_write(c);
667 if ((c->flags & WAITING_WRITE) == 0) {
668 dispatch_suspend(c->out);
670 maybe_close(c);
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
680 return c;
683 static int
684 maybe_close(struct client *c)
686 if (c->calls != 0)
687 return 0;
688 if (c->flags & (WAITING_READ|WAITING_WRITE))
689 return 0;
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);
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;
708 struct socket_call {
709 heim_idata in;
710 struct client *c;
711 heim_icred cred;
714 static void
715 output_data(struct client *c, const void *data, size_t len)
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;
725 static void
726 socket_complete(heim_sipc_call ctx, int returnvalue, heim_idata *reply)
728 struct socket_call *sc = (struct socket_call *)ctx;
729 struct client *c = sc->c;
731 /* double complete ? */
732 if (c == NULL)
733 abort();
735 if ((c->flags & WAITING_CLOSE) == 0) {
736 uint32_t u32;
738 /* length */
739 u32 = htonl(reply->length);
740 output_data(c, &u32, sizeof(u32));
742 /* return value */
743 if (c->flags & INCLUDE_ERROR_CODE) {
744 u32 = htonl(returnvalue);
745 output_data(c, &u32, sizeof(u32));
748 /* data */
749 output_data(c, reply->data, reply->length);
751 /* if HTTP, close connection */
752 if (c->flags & HTTP_REPLY) {
753 c->flags |= WAITING_CLOSE;
754 c->flags &= ~WAITING_READ;
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);
765 maybe_close(c);
768 /* remove HTTP %-quoting from buf */
769 static int
770 de_http(char *buf)
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;
783 *q = '\0';
784 return 0;
787 static struct socket_call *
788 handle_http_tcp(struct client *c)
790 struct socket_call *cs;
791 char *s, *p, *t;
792 void *data;
793 char *proto;
794 int len;
796 s = (char *)c->inmsg;
798 p = strstr(s, "\r\n");
799 if (p == NULL)
800 return NULL;
802 *p = 0;
804 p = NULL;
805 t = strtok_r(s, " \t", &p);
806 if (t == NULL)
807 return NULL;
809 t = strtok_r(NULL, " \t", &p);
810 if (t == NULL)
811 return NULL;
813 data = malloc(strlen(t));
814 if (data == NULL)
815 return NULL;
817 if(*t == '/')
818 t++;
819 if(de_http(t) != 0) {
820 free(data);
821 return NULL;
823 proto = strtok_r(NULL, " \t", &p);
824 if (proto == NULL) {
825 free(data);
826 return NULL;
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;
847 cs = emalloc(sizeof(*cs));
848 cs->c = c;
849 cs->in.data = data;
850 cs->in.length = len;
851 c->ptr = 0;
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));
865 return cs;
869 static void
870 handle_read(struct client *c)
872 ssize_t len;
873 uint32_t dlen;
875 assert((c->flags & DOOR_FD) == 0);
877 if (c->flags & LISTEN_SOCKET) {
878 add_new_socket(c->fd,
879 WAITING_READ | (c->flags & INHERIT_MASK),
880 c->callback,
881 c->userctx);
882 return;
885 if (c->ptr - c->len < 1024) {
886 c->inmsg = erealloc(c->inmsg,
887 c->len + 1024);
888 c->len += 1024;
891 len = read(c->fd, c->inmsg + c->ptr, c->len - c->ptr);
892 if (len <= 0) {
893 c->flags |= WAITING_CLOSE;
894 c->flags &= ~WAITING_READ;
895 return;
897 c->ptr += len;
898 if (c->ptr > c->len)
899 abort();
901 while (c->ptr >= sizeof(dlen)) {
902 struct socket_call *cs;
904 if((c->flags & ALLOW_HTTP) && c->ptr >= 4 &&
905 strncmp((char *)c->inmsg, "GET ", 4) == 0 &&
906 strncmp((char *)c->inmsg + c->ptr - 4, "\r\n\r\n", 4) == 0) {
908 /* remove the trailing \r\n\r\n so the string is NUL terminated */
909 c->inmsg[c->ptr - 4] = '\0';
911 c->flags |= HTTP_REPLY;
913 cs = handle_http_tcp(c);
914 if (cs == NULL) {
915 c->flags |= WAITING_CLOSE;
916 c->flags &= ~WAITING_READ;
917 break;
919 } else {
920 memcpy(&dlen, c->inmsg, sizeof(dlen));
921 dlen = ntohl(dlen);
923 if (dlen > MAX_PACKET_SIZE) {
924 c->flags |= WAITING_CLOSE;
925 c->flags &= ~WAITING_READ;
926 return;
928 if (dlen > c->ptr - sizeof(dlen)) {
929 break;
932 cs = emalloc(sizeof(*cs));
933 cs->c = c;
934 cs->in.data = emalloc(dlen);
935 memcpy(cs->in.data, c->inmsg + sizeof(dlen), dlen);
936 cs->in.length = dlen;
937 cs->cred = NULL;
939 c->ptr -= sizeof(dlen) + dlen;
940 memmove(c->inmsg,
941 c->inmsg + sizeof(dlen) + dlen,
942 c->ptr);
945 c->calls++;
947 if ((c->flags & UNIX_SOCKET) != 0) {
948 if (update_client_creds(c))
949 _heim_ipc_create_cred(c->unixrights.uid, c->unixrights.gid,
950 c->unixrights.pid, -1, &cs->cred);
953 c->callback(c->userctx, &cs->in,
954 cs->cred, socket_complete,
955 (heim_sipc_call)cs);
959 static void
960 handle_write(struct client *c)
962 ssize_t len;
964 len = write(c->fd, c->outmsg, c->olen);
965 if (len <= 0) {
966 c->flags |= WAITING_CLOSE;
967 c->flags &= ~(WAITING_WRITE);
968 } else if (c->olen != (size_t)len) {
969 memmove(&c->outmsg[0], &c->outmsg[len], c->olen - len);
970 c->olen -= len;
971 } else {
972 c->olen = 0;
973 free(c->outmsg);
974 c->outmsg = NULL;
975 c->flags &= ~(WAITING_WRITE);
980 #ifndef HAVE_GCD
982 static void
983 process_loop(void)
985 struct pollfd *fds;
986 unsigned n;
987 unsigned num_fds;
989 while (num_clients > 0) {
991 fds = malloc(num_clients * sizeof(fds[0]));
992 if(fds == NULL)
993 abort();
995 num_fds = num_clients;
997 for (n = 0 ; n < num_fds; n++) {
998 fds[n].fd = clients[n]->fd;
999 fds[n].events = 0;
1000 if (clients[n]->flags & WAITING_READ)
1001 fds[n].events |= POLLIN;
1002 if (clients[n]->flags & WAITING_WRITE)
1003 fds[n].events |= POLLOUT;
1005 fds[n].revents = 0;
1008 while (poll(fds, num_fds, -1) == -1) {
1009 if (errno == EINTR || errno == EAGAIN)
1010 continue;
1011 err(1, "poll(2) failed");
1014 for (n = 0 ; n < num_fds; n++) {
1015 if (clients[n] == NULL)
1016 continue;
1017 if (fds[n].revents & POLLERR) {
1018 clients[n]->flags |= WAITING_CLOSE;
1019 continue;
1022 if (fds[n].revents & POLLIN)
1023 handle_read(clients[n]);
1024 if (fds[n].revents & POLLOUT)
1025 handle_write(clients[n]);
1028 n = 0;
1029 while (n < num_clients) {
1030 struct client *c = clients[n];
1031 if (maybe_close(c)) {
1032 if (n < num_clients - 1)
1033 clients[n] = clients[num_clients - 1];
1034 num_clients--;
1035 } else
1036 n++;
1039 free(fds);
1043 #endif
1045 static int
1046 socket_release(heim_sipc ctx)
1048 struct client *c = ctx->mech;
1049 c->flags |= WAITING_CLOSE;
1050 return 0;
1054 heim_sipc_stream_listener(int fd, int type,
1055 heim_ipc_callback callback,
1056 void *user, heim_sipc *ctx)
1058 heim_sipc ct;
1059 struct client *c;
1061 if ((type & HEIM_SIPC_TYPE_IPC) && (type & (HEIM_SIPC_TYPE_UINT32|HEIM_SIPC_TYPE_HTTP)))
1062 return EINVAL;
1064 ct = calloc(1, sizeof(*ct));
1065 if (ct == NULL)
1066 return ENOMEM;
1068 switch (type) {
1069 case HEIM_SIPC_TYPE_IPC:
1070 c = add_new_socket(fd, LISTEN_SOCKET|WAITING_READ|INCLUDE_ERROR_CODE, callback, user);
1071 break;
1072 case HEIM_SIPC_TYPE_UINT32:
1073 c = add_new_socket(fd, LISTEN_SOCKET|WAITING_READ, callback, user);
1074 break;
1075 case HEIM_SIPC_TYPE_HTTP:
1076 case HEIM_SIPC_TYPE_UINT32|HEIM_SIPC_TYPE_HTTP:
1077 c = add_new_socket(fd, LISTEN_SOCKET|WAITING_READ|ALLOW_HTTP, callback, user);
1078 break;
1079 default:
1080 free(ct);
1081 return EINVAL;
1084 ct->mech = c;
1085 ct->release = socket_release;
1087 c->unixrights.uid = (uid_t) -1;
1088 c->unixrights.gid = (gid_t) -1;
1089 c->unixrights.pid = (pid_t) 0;
1091 *ctx = ct;
1092 return 0;
1096 heim_sipc_service_unix(const char *service,
1097 heim_ipc_callback callback,
1098 void *user, heim_sipc *ctx)
1100 struct sockaddr_un un;
1101 const char *d = secure_getenv("HEIM_IPC_DIR");
1102 int fd, ret;
1104 un.sun_family = AF_UNIX;
1106 if (snprintf(un.sun_path, sizeof(un.sun_path),
1107 "%s/.heim_%s-socket", d ? d : _PATH_VARRUN,
1108 service) > sizeof(un.sun_path) + sizeof("-s") - 1)
1109 return ENAMETOOLONG;
1110 fd = socket(AF_UNIX, SOCK_STREAM, 0);
1111 if (fd < 0)
1112 return errno;
1114 socket_set_reuseaddr(fd, 1);
1115 #ifdef LOCAL_CREDS
1117 int one = 1;
1118 setsockopt(fd, 0, LOCAL_CREDS, (void *)&one, sizeof(one));
1120 #endif
1122 unlink(un.sun_path);
1124 if (bind(fd, (struct sockaddr *)&un, sizeof(un)) < 0) {
1125 close(fd);
1126 return errno;
1129 if (listen(fd, SOMAXCONN) < 0) {
1130 close(fd);
1131 return errno;
1134 chmod(un.sun_path, 0666);
1136 ret = heim_sipc_stream_listener(fd, HEIM_SIPC_TYPE_IPC,
1137 callback, user, ctx);
1138 if (ret == 0) {
1139 struct client *c = (*ctx)->mech;
1140 c->flags |= UNIX_SOCKET;
1143 return ret;
1146 #ifdef HAVE_DOOR_CREATE
1147 #include <door.h>
1149 #ifdef HAVE_SYS_MMAN_H
1150 #include <sys/mman.h>
1151 #endif
1153 #include "heim_threads.h"
1155 static HEIMDAL_thread_key door_key;
1157 struct door_call {
1158 heim_idata in;
1159 door_desc_t *dp;
1160 heim_icred cred;
1163 struct door_reply {
1164 int returnvalue;
1165 size_t length;
1166 unsigned char data[1];
1169 static int
1170 door_release(heim_sipc ctx)
1172 struct client *c = ctx->mech;
1173 return 0;
1176 static void
1177 door_reply_destroy(void *data)
1179 free(data);
1182 static void
1183 door_key_create(void *key)
1185 int ret;
1187 HEIMDAL_key_create((HEIMDAL_thread_key *)key, door_reply_destroy, ret);
1190 static void
1191 door_complete(heim_sipc_call ctx, int returnvalue, heim_idata *reply)
1193 static heim_base_once_t once = HEIM_BASE_ONCE_INIT;
1194 struct door_call *cs = (struct door_call *)ctx;
1195 size_t rlen;
1196 struct door_reply *r = NULL;
1197 union {
1198 struct door_reply reply;
1199 char buffer[offsetof(struct door_reply, data) + BUFSIZ];
1200 } replyBuf;
1202 heim_base_once_f(&once, &door_key, door_key_create);
1204 /* door_return() doesn't return; don't leak cred */
1205 heim_ipc_free_cred(cs->cred);
1207 error_reply:
1208 rlen = offsetof(struct door_reply, data);
1209 if (returnvalue == 0)
1210 rlen += reply->length;
1212 /* long replies (> BUFSIZ) are allocated from the heap */
1213 if (rlen > BUFSIZ) {
1214 int ret;
1216 /* door_return() doesn't return, so stash reply buffer in TLS */
1217 r = realloc(HEIMDAL_getspecific(door_key), rlen);
1218 if (r == NULL) {
1219 returnvalue = EAGAIN; /* don't leak ENOMEM to caller */
1220 goto error_reply;
1223 HEIMDAL_setspecific(door_key, r, ret);
1224 } else {
1225 r = &replyBuf.reply;
1228 r->returnvalue = returnvalue;
1229 if (r->returnvalue == 0) {
1230 r->length = reply->length;
1231 memcpy(r->data, reply->data, reply->length);
1232 } else {
1233 r->length = 0;
1236 door_return((char *)r, rlen, NULL, 0);
1239 static void
1240 door_callback(void *cookie,
1241 char *argp,
1242 size_t arg_size,
1243 door_desc_t *dp,
1244 uint_t n_desc)
1246 heim_sipc c = (heim_sipc)cookie;
1247 struct door_call cs = { 0 };
1248 ucred_t *peercred = NULL;
1250 if (door_ucred(&peercred) < 0)
1251 return;
1253 _heim_ipc_create_cred(ucred_geteuid(peercred),
1254 ucred_getegid(peercred),
1255 ucred_getpid(peercred),
1257 &cs.cred);
1258 ucred_free(peercred);
1260 cs.dp = dp;
1261 cs.in.data = argp;
1262 cs.in.length = arg_size;
1264 c->callback(c->userctx, &cs.in, cs.cred, door_complete, (heim_sipc_call)&cs);
1268 heim_sipc_service_door(const char *service,
1269 heim_ipc_callback callback,
1270 void *user, heim_sipc *ctx)
1272 char path[PATH_MAX];
1273 int fd = -1, dfd = -1, ret;
1274 heim_sipc ct = NULL;
1275 struct client *c = NULL;
1277 ct = calloc(1, sizeof(*ct));
1278 if (ct == NULL) {
1279 ret = ENOMEM;
1280 goto cleanup;
1282 ct->release = door_release;
1283 ct->userctx = user;
1284 ct->callback = callback;
1286 if (snprintf(path, sizeof(path), "/var/run/.heim_%s-door",
1287 service) >= sizeof(path) + sizeof("-d") - 1) {
1288 ret = ENAMETOOLONG;
1289 goto cleanup;
1291 fd = door_create(door_callback, ct, DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
1292 if (fd < 0) {
1293 ret = errno;
1294 goto cleanup;
1297 fdetach(path);
1298 dfd = open(path, O_RDWR | O_CREAT, 0666);
1299 if (dfd < 0) {
1300 ret = errno;
1301 goto cleanup;
1303 fchmod(dfd, 0666); /* XXX */
1305 if (fattach(fd, path) < 0) {
1306 ret = errno;
1307 goto cleanup;
1310 c = add_new_socket(fd, DOOR_FD, callback, user);
1311 ct->mech = c;
1313 *ctx = ct;
1314 ret = 0;
1316 cleanup:
1317 if (ret != 0) {
1318 free(ct);
1319 free(c);
1320 if (fd != -1)
1321 close(fd);
1323 if (dfd != -1)
1324 close(dfd);
1326 return ret;
1328 #endif /* HAVE_DOOR_CREATE */
1331 * Set the idle timeout value
1333 * The timeout event handler is triggered recurrently every idle
1334 * period `t'. The default action is rather draconian and just calls
1335 * exit(0), so you might want to change this to something more
1336 * graceful using heim_sipc_set_timeout_handler().
1339 void
1340 heim_sipc_timeout(time_t t)
1342 #ifdef HAVE_GCD
1343 static dispatch_once_t timeoutonce;
1344 init_globals();
1345 dispatch_sync(timerq, ^{
1346 timeoutvalue = t;
1347 set_timer();
1349 dispatch_once(&timeoutonce, ^{ dispatch_resume(timer); });
1350 #else
1351 abort();
1352 #endif
1356 * Set the timeout event handler
1358 * Replaces the default idle timeout action.
1361 void
1362 heim_sipc_set_timeout_handler(void (*func)(void))
1364 #ifdef HAVE_GCD
1365 init_globals();
1366 dispatch_sync(timerq, ^{ timer_ev = func; });
1367 #else
1368 abort();
1369 #endif
1373 void
1374 heim_sipc_free_context(heim_sipc ctx)
1376 (ctx->release)(ctx);
1379 void
1380 heim_ipc_main(void)
1382 #ifdef HAVE_GCD
1383 dispatch_main();
1384 #else
1385 process_loop();
1386 #endif