Remove building with NOCRYPTO option
[minix.git] / crypto / external / bsd / heimdal / dist / lib / ipc / server.c
blob9c0445ff2788a5c4ead68b7db18fa1b1cf3cc1bb
1 /* $NetBSD: server.c,v 1.1.1.2 2014/04/24 12:45:48 pettai Exp $ */
3 /*
4 * Copyright (c) 2009 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
8 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the Institute nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
38 #include "hi_locl.h"
39 #include <assert.h>
41 #define MAX_PACKET_SIZE (128 * 1024)
43 struct heim_sipc {
44 int (*release)(heim_sipc ctx);
45 heim_ipc_callback callback;
46 void *userctx;
47 void *mech;
50 #if defined(__APPLE__) && defined(HAVE_GCD)
52 #include "heim_ipcServer.h"
53 #include "heim_ipc_reply.h"
54 #include "heim_ipc_async.h"
56 static dispatch_source_t timer;
57 static dispatch_queue_t timerq;
58 static uint64_t timeoutvalue;
60 static dispatch_queue_t eventq;
62 static dispatch_queue_t workq;
64 static void
65 default_timer_ev(void)
67 exit(0);
70 static void (*timer_ev)(void) = default_timer_ev;
72 static void
73 set_timer(void)
75 dispatch_source_set_timer(timer,
76 dispatch_time(DISPATCH_TIME_NOW,
77 timeoutvalue * NSEC_PER_SEC),
78 timeoutvalue * NSEC_PER_SEC, 1000000);
81 static void
82 init_globals(void)
84 static dispatch_once_t once;
85 dispatch_once(&once, ^{
86 timerq = dispatch_queue_create("hiem-sipc-timer-q", NULL);
87 timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, timerq);
88 dispatch_source_set_event_handler(timer, ^{ timer_ev(); } );
90 workq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
91 eventq = dispatch_queue_create("heim-ipc.event-queue", NULL);
92 });
95 static void
96 suspend_timer(void)
98 dispatch_suspend(timer);
101 static void
102 restart_timer(void)
104 dispatch_sync(timerq, ^{ set_timer(); });
105 dispatch_resume(timer);
108 struct mach_service {
109 mach_port_t sport;
110 dispatch_source_t source;
111 dispatch_queue_t queue;
114 struct mach_call_ctx {
115 mach_port_t reply_port;
116 heim_icred cred;
117 heim_idata req;
121 static void
122 mach_complete_sync(heim_sipc_call ctx, int returnvalue, heim_idata *reply)
124 struct mach_call_ctx *s = (struct mach_call_ctx *)ctx;
125 heim_ipc_message_inband_t replyin;
126 mach_msg_type_number_t replyinCnt;
127 heim_ipc_message_outband_t replyout;
128 mach_msg_type_number_t replyoutCnt;
129 kern_return_t kr;
131 if (returnvalue) {
132 /* on error, no reply */
133 replyinCnt = 0;
134 replyout = 0; replyoutCnt = 0;
135 kr = KERN_SUCCESS;
136 } else if (reply->length < 2048) {
137 replyinCnt = reply->length;
138 memcpy(replyin, reply->data, replyinCnt);
139 replyout = 0; replyoutCnt = 0;
140 kr = KERN_SUCCESS;
141 } else {
142 replyinCnt = 0;
143 kr = vm_read(mach_task_self(),
144 (vm_address_t)reply->data, reply->length,
145 (vm_address_t *)&replyout, &replyoutCnt);
148 mheim_ripc_call_reply(s->reply_port, returnvalue,
149 replyin, replyinCnt,
150 replyout, replyoutCnt);
152 heim_ipc_free_cred(s->cred);
153 free(s->req.data);
154 free(s);
155 restart_timer();
158 static void
159 mach_complete_async(heim_sipc_call ctx, int returnvalue, heim_idata *reply)
161 struct mach_call_ctx *s = (struct mach_call_ctx *)ctx;
162 heim_ipc_message_inband_t replyin;
163 mach_msg_type_number_t replyinCnt;
164 heim_ipc_message_outband_t replyout;
165 mach_msg_type_number_t replyoutCnt;
166 kern_return_t kr;
168 if (returnvalue) {
169 /* on error, no reply */
170 replyinCnt = 0;
171 replyout = 0; replyoutCnt = 0;
172 kr = KERN_SUCCESS;
173 } else if (reply->length < 2048) {
174 replyinCnt = reply->length;
175 memcpy(replyin, reply->data, replyinCnt);
176 replyout = 0; replyoutCnt = 0;
177 kr = KERN_SUCCESS;
178 } else {
179 replyinCnt = 0;
180 kr = vm_read(mach_task_self(),
181 (vm_address_t)reply->data, reply->length,
182 (vm_address_t *)&replyout, &replyoutCnt);
185 kr = mheim_aipc_acall_reply(s->reply_port, returnvalue,
186 replyin, replyinCnt,
187 replyout, replyoutCnt);
188 heim_ipc_free_cred(s->cred);
189 free(s->req.data);
190 free(s);
191 restart_timer();
195 kern_return_t
196 mheim_do_call(mach_port_t server_port,
197 audit_token_t client_creds,
198 mach_port_t reply_port,
199 heim_ipc_message_inband_t requestin,
200 mach_msg_type_number_t requestinCnt,
201 heim_ipc_message_outband_t requestout,
202 mach_msg_type_number_t requestoutCnt,
203 int *returnvalue,
204 heim_ipc_message_inband_t replyin,
205 mach_msg_type_number_t *replyinCnt,
206 heim_ipc_message_outband_t *replyout,
207 mach_msg_type_number_t *replyoutCnt)
209 heim_sipc ctx = dispatch_get_context(dispatch_get_current_queue());
210 struct mach_call_ctx *s;
211 kern_return_t kr;
212 uid_t uid;
213 gid_t gid;
214 pid_t pid;
215 au_asid_t session;
217 *replyout = NULL;
218 *replyoutCnt = 0;
219 *replyinCnt = 0;
221 s = malloc(sizeof(*s));
222 if (s == NULL)
223 return KERN_MEMORY_FAILURE; /* XXX */
225 s->reply_port = reply_port;
227 audit_token_to_au32(client_creds, NULL, &uid, &gid, NULL, NULL, &pid, &session, NULL);
229 kr = _heim_ipc_create_cred(uid, gid, pid, session, &s->cred);
230 if (kr) {
231 free(s);
232 return kr;
235 suspend_timer();
237 if (requestinCnt) {
238 s->req.data = malloc(requestinCnt);
239 memcpy(s->req.data, requestin, requestinCnt);
240 s->req.length = requestinCnt;
241 } else {
242 s->req.data = malloc(requestoutCnt);
243 memcpy(s->req.data, requestout, requestoutCnt);
244 s->req.length = requestoutCnt;
247 dispatch_async(workq, ^{
248 (ctx->callback)(ctx->userctx, &s->req, s->cred,
249 mach_complete_sync, (heim_sipc_call)s);
252 return MIG_NO_REPLY;
255 kern_return_t
256 mheim_do_call_request(mach_port_t server_port,
257 audit_token_t client_creds,
258 mach_port_t reply_port,
259 heim_ipc_message_inband_t requestin,
260 mach_msg_type_number_t requestinCnt,
261 heim_ipc_message_outband_t requestout,
262 mach_msg_type_number_t requestoutCnt)
264 heim_sipc ctx = dispatch_get_context(dispatch_get_current_queue());
265 struct mach_call_ctx *s;
266 kern_return_t kr;
267 uid_t uid;
268 gid_t gid;
269 pid_t pid;
270 au_asid_t session;
272 s = malloc(sizeof(*s));
273 if (s == NULL)
274 return KERN_MEMORY_FAILURE; /* XXX */
276 s->reply_port = reply_port;
278 audit_token_to_au32(client_creds, NULL, &uid, &gid, NULL, NULL, &pid, &session, NULL);
280 kr = _heim_ipc_create_cred(uid, gid, pid, session, &s->cred);
281 if (kr) {
282 free(s);
283 return kr;
286 suspend_timer();
288 if (requestinCnt) {
289 s->req.data = malloc(requestinCnt);
290 memcpy(s->req.data, requestin, requestinCnt);
291 s->req.length = requestinCnt;
292 } else {
293 s->req.data = malloc(requestoutCnt);
294 memcpy(s->req.data, requestout, requestoutCnt);
295 s->req.length = requestoutCnt;
298 dispatch_async(workq, ^{
299 (ctx->callback)(ctx->userctx, &s->req, s->cred,
300 mach_complete_async, (heim_sipc_call)s);
303 return KERN_SUCCESS;
306 static int
307 mach_init(const char *service, mach_port_t sport, heim_sipc ctx)
309 struct mach_service *s;
310 char *name;
312 init_globals();
314 s = calloc(1, sizeof(*s));
315 if (s == NULL)
316 return ENOMEM;
318 asprintf(&name, "heim-ipc-mach-%s", service);
320 s->queue = dispatch_queue_create(name, NULL);
321 free(name);
322 s->sport = sport;
324 s->source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV,
325 s->sport, 0, s->queue);
326 if (s->source == NULL) {
327 dispatch_release(s->queue);
328 free(s);
329 return ENOMEM;
331 ctx->mech = s;
333 dispatch_set_context(s->queue, ctx);
334 dispatch_set_context(s->source, s);
336 dispatch_source_set_event_handler(s->source, ^{
337 dispatch_mig_server(s->source, sizeof(union __RequestUnion__mheim_do_mheim_ipc_subsystem), mheim_ipc_server);
340 dispatch_source_set_cancel_handler(s->source, ^{
341 heim_sipc ctx = dispatch_get_context(dispatch_get_current_queue());
342 struct mach_service *st = ctx->mech;
343 mach_port_mod_refs(mach_task_self(), st->sport,
344 MACH_PORT_RIGHT_RECEIVE, -1);
345 dispatch_release(st->queue);
346 dispatch_release(st->source);
347 free(st);
348 free(ctx);
351 dispatch_resume(s->source);
353 return 0;
356 static int
357 mach_release(heim_sipc ctx)
359 struct mach_service *s = ctx->mech;
360 dispatch_source_cancel(s->source);
361 dispatch_release(s->source);
362 return 0;
365 static mach_port_t
366 mach_checkin_or_register(const char *service)
368 mach_port_t mp;
369 kern_return_t kr;
371 kr = bootstrap_check_in(bootstrap_port, service, &mp);
372 if (kr == KERN_SUCCESS)
373 return mp;
375 #if __MAC_OS_X_VERSION_MIN_REQUIRED <= 1050
376 /* Pre SnowLeopard version */
377 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp);
378 if (kr != KERN_SUCCESS)
379 return MACH_PORT_NULL;
381 kr = mach_port_insert_right(mach_task_self(), mp, mp,
382 MACH_MSG_TYPE_MAKE_SEND);
383 if (kr != KERN_SUCCESS) {
384 mach_port_destroy(mach_task_self(), mp);
385 return MACH_PORT_NULL;
388 kr = bootstrap_register(bootstrap_port, rk_UNCONST(service), mp);
389 if (kr != KERN_SUCCESS) {
390 mach_port_destroy(mach_task_self(), mp);
391 return MACH_PORT_NULL;
394 return mp;
395 #else
396 return MACH_PORT_NULL;
397 #endif
401 #endif /* __APPLE__ && HAVE_GCD */
405 heim_sipc_launchd_mach_init(const char *service,
406 heim_ipc_callback callback,
407 void *user, heim_sipc *ctx)
409 #if defined(__APPLE__) && defined(HAVE_GCD)
410 mach_port_t sport = MACH_PORT_NULL;
411 heim_sipc c = NULL;
412 int ret;
414 *ctx = NULL;
416 sport = mach_checkin_or_register(service);
417 if (sport == MACH_PORT_NULL) {
418 ret = ENOENT;
419 goto error;
422 c = calloc(1, sizeof(*c));
423 if (c == NULL) {
424 ret = ENOMEM;
425 goto error;
427 c->release = mach_release;
428 c->userctx = user;
429 c->callback = callback;
431 ret = mach_init(service, sport, c);
432 if (ret)
433 goto error;
435 *ctx = c;
436 return 0;
437 error:
438 if (c)
439 free(c);
440 if (sport != MACH_PORT_NULL)
441 mach_port_mod_refs(mach_task_self(), sport,
442 MACH_PORT_RIGHT_RECEIVE, -1);
443 return ret;
444 #else /* !(__APPLE__ && HAVE_GCD) */
445 *ctx = NULL;
446 return EINVAL;
447 #endif /* __APPLE__ && HAVE_GCD */
450 struct client {
451 int fd;
452 heim_ipc_callback callback;
453 void *userctx;
454 int flags;
455 #define LISTEN_SOCKET 1
456 #define WAITING_READ 2
457 #define WAITING_WRITE 4
458 #define WAITING_CLOSE 8
460 #define HTTP_REPLY 16
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;
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;
617 static struct client *
618 add_new_socket(int fd,
619 int flags,
620 heim_ipc_callback callback,
621 void *userctx)
623 struct client *c;
624 int fileflags;
626 c = calloc(1, sizeof(*c));
627 if (c == NULL)
628 return NULL;
630 if (flags & LISTEN_SOCKET) {
631 c->fd = fd;
632 } else {
633 c->fd = accept(fd, NULL, NULL);
634 if(c->fd < 0) {
635 free(c);
636 return NULL;
640 c->flags = flags;
641 c->callback = callback;
642 c->userctx = userctx;
644 fileflags = fcntl(c->fd, F_GETFL, 0);
645 fcntl(c->fd, F_SETFL, fileflags | O_NONBLOCK);
647 #ifdef HAVE_GCD
648 init_globals();
650 c->in = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
651 c->fd, 0, eventq);
652 c->out = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE,
653 c->fd, 0, eventq);
655 dispatch_source_set_event_handler(c->in, ^{
656 int rw = (c->flags & WAITING_WRITE);
657 handle_read(c);
658 if (rw == 0 && (c->flags & WAITING_WRITE))
659 dispatch_resume(c->out);
660 if ((c->flags & WAITING_READ) == 0)
661 dispatch_suspend(c->in);
662 maybe_close(c);
664 dispatch_source_set_event_handler(c->out, ^{
665 handle_write(c);
666 if ((c->flags & WAITING_WRITE) == 0) {
667 dispatch_suspend(c->out);
669 maybe_close(c);
672 dispatch_resume(c->in);
673 #else
674 clients = erealloc(clients, sizeof(clients[0]) * (num_clients + 1));
675 clients[num_clients] = c;
676 num_clients++;
677 #endif
679 return c;
682 static int
683 maybe_close(struct client *c)
685 if (c->calls != 0)
686 return 0;
687 if (c->flags & (WAITING_READ|WAITING_WRITE))
688 return 0;
690 #ifdef HAVE_GCD
691 dispatch_source_cancel(c->in);
692 if ((c->flags & WAITING_READ) == 0)
693 dispatch_resume(c->in);
694 dispatch_release(c->in);
696 dispatch_source_cancel(c->out);
697 if ((c->flags & WAITING_WRITE) == 0)
698 dispatch_resume(c->out);
699 dispatch_release(c->out);
700 #endif
701 close(c->fd); /* ref count fd close */
702 free(c);
703 return 1;
707 struct socket_call {
708 heim_idata in;
709 struct client *c;
710 heim_icred cred;
713 static void
714 output_data(struct client *c, const void *data, size_t len)
716 if (c->olen + len < c->olen)
717 abort();
718 c->outmsg = erealloc(c->outmsg, c->olen + len);
719 memcpy(&c->outmsg[c->olen], data, len);
720 c->olen += len;
721 c->flags |= WAITING_WRITE;
724 static void
725 socket_complete(heim_sipc_call ctx, int returnvalue, heim_idata *reply)
727 struct socket_call *sc = (struct socket_call *)ctx;
728 struct client *c = sc->c;
730 /* double complete ? */
731 if (c == NULL)
732 abort();
734 if ((c->flags & WAITING_CLOSE) == 0) {
735 uint32_t u32;
737 /* length */
738 u32 = htonl(reply->length);
739 output_data(c, &u32, sizeof(u32));
741 /* return value */
742 if (c->flags & INCLUDE_ERROR_CODE) {
743 u32 = htonl(returnvalue);
744 output_data(c, &u32, sizeof(u32));
747 /* data */
748 output_data(c, reply->data, reply->length);
750 /* if HTTP, close connection */
751 if (c->flags & HTTP_REPLY) {
752 c->flags |= WAITING_CLOSE;
753 c->flags &= ~WAITING_READ;
757 c->calls--;
758 if (sc->cred)
759 heim_ipc_free_cred(sc->cred);
760 free(sc->in.data);
761 sc->c = NULL; /* so we can catch double complete */
762 free(sc);
764 maybe_close(c);
767 /* remove HTTP %-quoting from buf */
768 static int
769 de_http(char *buf)
771 unsigned char *p, *q;
772 for(p = q = (unsigned char *)buf; *p; p++, q++) {
773 if(*p == '%' && isxdigit(p[1]) && isxdigit(p[2])) {
774 unsigned int x;
775 if(sscanf((char *)p + 1, "%2x", &x) != 1)
776 return -1;
777 *q = x;
778 p += 2;
779 } else
780 *q = *p;
782 *q = '\0';
783 return 0;
786 static struct socket_call *
787 handle_http_tcp(struct client *c)
789 struct socket_call *cs;
790 char *s, *p, *t;
791 void *data;
792 char *proto;
793 int len;
795 s = (char *)c->inmsg;
797 p = strstr(s, "\r\n");
798 if (p == NULL)
799 return NULL;
801 *p = 0;
803 p = NULL;
804 t = strtok_r(s, " \t", &p);
805 if (t == NULL)
806 return NULL;
808 t = strtok_r(NULL, " \t", &p);
809 if (t == NULL)
810 return NULL;
812 data = malloc(strlen(t));
813 if (data == NULL)
814 return NULL;
816 if(*t == '/')
817 t++;
818 if(de_http(t) != 0) {
819 free(data);
820 return NULL;
822 proto = strtok_r(NULL, " \t", &p);
823 if (proto == NULL) {
824 free(data);
825 return NULL;
827 len = base64_decode(t, data);
828 if(len <= 0){
829 const char *msg =
830 " 404 Not found\r\n"
831 "Server: Heimdal/" VERSION "\r\n"
832 "Cache-Control: no-cache\r\n"
833 "Pragma: no-cache\r\n"
834 "Content-type: text/html\r\n"
835 "Content-transfer-encoding: 8bit\r\n\r\n"
836 "<TITLE>404 Not found</TITLE>\r\n"
837 "<H1>404 Not found</H1>\r\n"
838 "That page doesn't exist, maybe you are looking for "
839 "<A HREF=\"http://www.h5l.org/\">Heimdal</A>?\r\n";
840 free(data);
841 output_data(c, proto, strlen(proto));
842 output_data(c, msg, strlen(msg));
843 return NULL;
846 cs = emalloc(sizeof(*cs));
847 cs->c = c;
848 cs->in.data = data;
849 cs->in.length = len;
850 c->ptr = 0;
853 const char *msg =
854 " 200 OK\r\n"
855 "Server: Heimdal/" VERSION "\r\n"
856 "Cache-Control: no-cache\r\n"
857 "Pragma: no-cache\r\n"
858 "Content-type: application/octet-stream\r\n"
859 "Content-transfer-encoding: binary\r\n\r\n";
860 output_data(c, proto, strlen(proto));
861 output_data(c, msg, strlen(msg));
864 return cs;
868 static void
869 handle_read(struct client *c)
871 ssize_t len;
872 uint32_t dlen;
874 if (c->flags & LISTEN_SOCKET) {
875 add_new_socket(c->fd,
876 WAITING_READ | (c->flags & INHERIT_MASK),
877 c->callback,
878 c->userctx);
879 return;
882 if (c->ptr - c->len < 1024) {
883 c->inmsg = erealloc(c->inmsg,
884 c->len + 1024);
885 c->len += 1024;
888 len = read(c->fd, c->inmsg + c->ptr, c->len - c->ptr);
889 if (len <= 0) {
890 c->flags |= WAITING_CLOSE;
891 c->flags &= ~WAITING_READ;
892 return;
894 c->ptr += len;
895 if (c->ptr > c->len)
896 abort();
898 while (c->ptr >= sizeof(dlen)) {
899 struct socket_call *cs;
901 if((c->flags & ALLOW_HTTP) && c->ptr >= 4 &&
902 strncmp((char *)c->inmsg, "GET ", 4) == 0 &&
903 strncmp((char *)c->inmsg + c->ptr - 4, "\r\n\r\n", 4) == 0) {
905 /* remove the trailing \r\n\r\n so the string is NUL terminated */
906 c->inmsg[c->ptr - 4] = '\0';
908 c->flags |= HTTP_REPLY;
910 cs = handle_http_tcp(c);
911 if (cs == NULL) {
912 c->flags |= WAITING_CLOSE;
913 c->flags &= ~WAITING_READ;
914 break;
916 } else {
917 memcpy(&dlen, c->inmsg, sizeof(dlen));
918 dlen = ntohl(dlen);
920 if (dlen > MAX_PACKET_SIZE) {
921 c->flags |= WAITING_CLOSE;
922 c->flags &= ~WAITING_READ;
923 return;
925 if (dlen > c->ptr - sizeof(dlen)) {
926 break;
929 cs = emalloc(sizeof(*cs));
930 cs->c = c;
931 cs->in.data = emalloc(dlen);
932 memcpy(cs->in.data, c->inmsg + sizeof(dlen), dlen);
933 cs->in.length = dlen;
935 c->ptr -= sizeof(dlen) + dlen;
936 memmove(c->inmsg,
937 c->inmsg + sizeof(dlen) + dlen,
938 c->ptr);
941 c->calls++;
943 if ((c->flags & UNIX_SOCKET) != 0) {
944 if (update_client_creds(c))
945 _heim_ipc_create_cred(c->unixrights.uid, c->unixrights.gid,
946 c->unixrights.pid, -1, &cs->cred);
949 c->callback(c->userctx, &cs->in,
950 cs->cred, socket_complete,
951 (heim_sipc_call)cs);
955 static void
956 handle_write(struct client *c)
958 ssize_t len;
960 len = write(c->fd, c->outmsg, c->olen);
961 if (len <= 0) {
962 c->flags |= WAITING_CLOSE;
963 c->flags &= ~(WAITING_WRITE);
964 } else if (c->olen != (size_t)len) {
965 memmove(&c->outmsg[0], &c->outmsg[len], c->olen - len);
966 c->olen -= len;
967 } else {
968 c->olen = 0;
969 free(c->outmsg);
970 c->outmsg = NULL;
971 c->flags &= ~(WAITING_WRITE);
976 #ifndef HAVE_GCD
978 static void
979 process_loop(void)
981 struct pollfd *fds;
982 unsigned n;
983 unsigned num_fds;
985 while(num_clients > 0) {
987 fds = malloc(num_clients * sizeof(fds[0]));
988 if(fds == NULL)
989 abort();
991 num_fds = num_clients;
993 for (n = 0 ; n < num_fds; n++) {
994 fds[n].fd = clients[n]->fd;
995 fds[n].events = 0;
996 if (clients[n]->flags & WAITING_READ)
997 fds[n].events |= POLLIN;
998 if (clients[n]->flags & WAITING_WRITE)
999 fds[n].events |= POLLOUT;
1001 fds[n].revents = 0;
1004 poll(fds, num_fds, -1);
1006 for (n = 0 ; n < num_fds; n++) {
1007 if (clients[n] == NULL)
1008 continue;
1009 if (fds[n].revents & POLLERR) {
1010 clients[n]->flags |= WAITING_CLOSE;
1011 continue;
1014 if (fds[n].revents & POLLIN)
1015 handle_read(clients[n]);
1016 if (fds[n].revents & POLLOUT)
1017 handle_write(clients[n]);
1020 n = 0;
1021 while (n < num_clients) {
1022 struct client *c = clients[n];
1023 if (maybe_close(c)) {
1024 if (n < num_clients - 1)
1025 clients[n] = clients[num_clients - 1];
1026 num_clients--;
1027 } else
1028 n++;
1031 free(fds);
1035 #endif
1037 static int
1038 socket_release(heim_sipc ctx)
1040 struct client *c = ctx->mech;
1041 c->flags |= WAITING_CLOSE;
1042 return 0;
1046 heim_sipc_stream_listener(int fd, int type,
1047 heim_ipc_callback callback,
1048 void *user, heim_sipc *ctx)
1050 heim_sipc ct = calloc(1, sizeof(*ct));
1051 struct client *c;
1053 if ((type & HEIM_SIPC_TYPE_IPC) && (type & (HEIM_SIPC_TYPE_UINT32|HEIM_SIPC_TYPE_HTTP)))
1054 return EINVAL;
1056 switch (type) {
1057 case HEIM_SIPC_TYPE_IPC:
1058 c = add_new_socket(fd, LISTEN_SOCKET|WAITING_READ|INCLUDE_ERROR_CODE, callback, user);
1059 break;
1060 case HEIM_SIPC_TYPE_UINT32:
1061 c = add_new_socket(fd, LISTEN_SOCKET|WAITING_READ, callback, user);
1062 break;
1063 case HEIM_SIPC_TYPE_HTTP:
1064 case HEIM_SIPC_TYPE_UINT32|HEIM_SIPC_TYPE_HTTP:
1065 c = add_new_socket(fd, LISTEN_SOCKET|WAITING_READ|ALLOW_HTTP, callback, user);
1066 break;
1067 default:
1068 free(ct);
1069 return EINVAL;
1072 ct->mech = c;
1073 ct->release = socket_release;
1075 c->unixrights.uid = (uid_t) -1;
1076 c->unixrights.gid = (gid_t) -1;
1077 c->unixrights.pid = (pid_t) 0;
1079 *ctx = ct;
1080 return 0;
1084 heim_sipc_service_unix(const char *service,
1085 heim_ipc_callback callback,
1086 void *user, heim_sipc *ctx)
1088 struct sockaddr_un un;
1089 int fd, ret;
1091 un.sun_family = AF_UNIX;
1093 snprintf(un.sun_path, sizeof(un.sun_path),
1094 "/var/run/.heim_%s-socket", service);
1095 fd = socket(AF_UNIX, SOCK_STREAM, 0);
1096 if (fd < 0)
1097 return errno;
1099 socket_set_reuseaddr(fd, 1);
1100 #ifdef LOCAL_CREDS
1102 int one = 1;
1103 setsockopt(fd, 0, LOCAL_CREDS, (void *)&one, sizeof(one));
1105 #endif
1107 unlink(un.sun_path);
1109 if (bind(fd, (struct sockaddr *)&un, sizeof(un)) < 0) {
1110 close(fd);
1111 return errno;
1114 if (listen(fd, SOMAXCONN) < 0) {
1115 close(fd);
1116 return errno;
1119 chmod(un.sun_path, 0666);
1121 ret = heim_sipc_stream_listener(fd, HEIM_SIPC_TYPE_IPC,
1122 callback, user, ctx);
1123 if (ret == 0) {
1124 struct client *c = (*ctx)->mech;
1125 c->flags |= UNIX_SOCKET;
1128 return ret;
1132 * Set the idle timeout value
1134 * The timeout event handler is triggered recurrently every idle
1135 * period `t'. The default action is rather draconian and just calls
1136 * exit(0), so you might want to change this to something more
1137 * graceful using heim_sipc_set_timeout_handler().
1140 void
1141 heim_sipc_timeout(time_t t)
1143 #ifdef HAVE_GCD
1144 static dispatch_once_t timeoutonce;
1145 init_globals();
1146 dispatch_sync(timerq, ^{
1147 timeoutvalue = t;
1148 set_timer();
1150 dispatch_once(&timeoutonce, ^{ dispatch_resume(timer); });
1151 #else
1152 abort();
1153 #endif
1157 * Set the timeout event handler
1159 * Replaces the default idle timeout action.
1162 void
1163 heim_sipc_set_timeout_handler(void (*func)(void))
1165 #ifdef HAVE_GCD
1166 init_globals();
1167 dispatch_sync(timerq, ^{ timer_ev = func; });
1168 #else
1169 abort();
1170 #endif
1174 void
1175 heim_sipc_free_context(heim_sipc ctx)
1177 (ctx->release)(ctx);
1180 void
1181 heim_ipc_main(void)
1183 #ifdef HAVE_GCD
1184 dispatch_main();
1185 #else
1186 process_loop();
1187 #endif