sp_common.c revision 1.13 1 /* $NetBSD: sp_common.c,v 1.13 2010/11/29 16:08:03 pooka Exp $ */
2
3 /*
4 * Copyright (c) 2010 Antti Kantee. All Rights Reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 /*
29 * Common client/server sysproxy routines. #included.
30 */
31
32 #include <sys/cdefs.h>
33
34 #include <sys/types.h>
35 #include <sys/mman.h>
36 #include <sys/queue.h>
37 #include <sys/socket.h>
38 #include <sys/un.h>
39
40 #include <arpa/inet.h>
41 #include <netinet/in.h>
42 #include <netinet/tcp.h>
43
44 #include <assert.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <inttypes.h>
48 #include <poll.h>
49 #include <pthread.h>
50 #include <stdarg.h>
51 #include <stddef.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <unistd.h>
56
57 //#define DEBUG
58 #ifdef DEBUG
59 #define DPRINTF(x) mydprintf x
60 static void
61 mydprintf(const char *fmt, ...)
62 {
63 va_list ap;
64
65 va_start(ap, fmt);
66 vfprintf(stderr, fmt, ap);
67 va_end(ap);
68 }
69 #else
70 #define DPRINTF(x)
71 #endif
72
73 /*
74 * Bah, I hate writing on-off-wire conversions in C
75 */
76
77 enum { RUMPSP_REQ, RUMPSP_RESP, RUMPSP_ERROR };
78 enum { RUMPSP_SYSCALL,
79 RUMPSP_COPYIN, RUMPSP_COPYINSTR,
80 RUMPSP_COPYOUT, RUMPSP_COPYOUTSTR,
81 RUMPSP_ANONMMAP };
82
83 struct rsp_hdr {
84 uint64_t rsp_len;
85 uint64_t rsp_reqno;
86 uint16_t rsp_class;
87 uint16_t rsp_type;
88 /*
89 * We want this structure 64bit-aligned for typecast fun,
90 * so might as well use the following for something.
91 */
92 union {
93 uint32_t sysnum;
94 uint32_t error;
95 } u;
96 };
97 #define HDRSZ sizeof(struct rsp_hdr)
98 #define rsp_sysnum u.sysnum
99 #define rsp_error u.error
100
101 /*
102 * Data follows the header. We have two types of structured data.
103 */
104
105 /* copyin/copyout */
106 struct rsp_copydata {
107 size_t rcp_len;
108 void *rcp_addr;
109 uint8_t rcp_data[0];
110 };
111
112 /* syscall response */
113 struct rsp_sysresp {
114 int rsys_error;
115 register_t rsys_retval[2];
116 };
117
118 struct respwait {
119 uint64_t rw_reqno;
120 void *rw_data;
121 size_t rw_dlen;
122 int rw_error;
123
124 pthread_cond_t rw_cv;
125
126 TAILQ_ENTRY(respwait) rw_entries;
127 };
128
129 struct spclient {
130 int spc_fd;
131 int spc_refcnt;
132 int spc_dying;
133
134 pthread_mutex_t spc_mtx;
135 pthread_cond_t spc_cv;
136
137 struct lwp *spc_mainlwp;
138 pid_t spc_pid;
139
140 TAILQ_HEAD(, respwait) spc_respwait;
141
142 /* rest of the fields are zeroed upon disconnect */
143 #define SPC_ZEROFF offsetof(struct spclient, spc_pfd)
144 struct pollfd *spc_pfd;
145
146 struct rsp_hdr spc_hdr;
147 uint8_t *spc_buf;
148 size_t spc_off;
149
150 uint64_t spc_nextreq;
151 int spc_ostatus, spc_istatus;
152 };
153 #define SPCSTATUS_FREE 0
154 #define SPCSTATUS_BUSY 1
155 #define SPCSTATUS_WANTED 2
156
157 typedef int (*addrparse_fn)(const char *, struct sockaddr **, int);
158 typedef int (*connecthook_fn)(int);
159
160 static int readframe(struct spclient *);
161 static void handlereq(struct spclient *);
162
163 static __inline void
164 spcresetbuf(struct spclient *spc)
165 {
166
167 spc->spc_buf = NULL;
168 spc->spc_off = 0;
169 }
170
171 static __inline void
172 spcfreebuf(struct spclient *spc)
173 {
174
175 free(spc->spc_buf);
176 spcresetbuf(spc);
177 }
178
179 static void
180 sendlockl(struct spclient *spc)
181 {
182
183 /* assert(pthread_mutex_owned) */
184 while (spc->spc_ostatus != SPCSTATUS_FREE) {
185 spc->spc_ostatus = SPCSTATUS_WANTED;
186 pthread_cond_wait(&spc->spc_cv, &spc->spc_mtx);
187 }
188 spc->spc_ostatus = SPCSTATUS_BUSY;
189 }
190
191 static void
192 sendlock(struct spclient *spc)
193 {
194
195 pthread_mutex_lock(&spc->spc_mtx);
196 sendlockl(spc);
197 pthread_mutex_unlock(&spc->spc_mtx);
198 }
199
200 static void
201 sendunlockl(struct spclient *spc)
202 {
203
204 /* assert(pthread_mutex_owned) */
205 if (spc->spc_ostatus == SPCSTATUS_WANTED)
206 pthread_cond_broadcast(&spc->spc_cv);
207 spc->spc_ostatus = SPCSTATUS_FREE;
208 }
209
210 static void
211 sendunlock(struct spclient *spc)
212 {
213
214 pthread_mutex_lock(&spc->spc_mtx);
215 sendunlockl(spc);
216 pthread_mutex_unlock(&spc->spc_mtx);
217 }
218
219 static int
220 dosend(struct spclient *spc, const void *data, size_t dlen)
221 {
222 struct pollfd pfd;
223 const uint8_t *sdata = data;
224 ssize_t n;
225 size_t sent;
226 int fd = spc->spc_fd;
227
228 pfd.fd = fd;
229 pfd.events = POLLOUT;
230
231 for (sent = 0, n = 0; sent < dlen; ) {
232 if (n) {
233 if (poll(&pfd, 1, INFTIM) == -1) {
234 if (errno == EINTR)
235 continue;
236 return errno;
237 }
238 }
239
240 n = send(fd, sdata + sent, dlen - sent, MSG_NOSIGNAL);
241 if (n == 0) {
242 return EFAULT;
243 }
244 if (n == -1) {
245 if (errno != EAGAIN)
246 return EFAULT;
247 continue;
248 }
249 sent += n;
250 }
251
252 return 0;
253 }
254
255 static void
256 putwait(struct spclient *spc, struct respwait *rw, struct rsp_hdr *rhdr)
257 {
258
259 rw->rw_data = NULL;
260 rw->rw_dlen = 0;
261 pthread_cond_init(&rw->rw_cv, NULL);
262
263 pthread_mutex_lock(&spc->spc_mtx);
264 rw->rw_reqno = rhdr->rsp_reqno = spc->spc_nextreq++;
265 TAILQ_INSERT_TAIL(&spc->spc_respwait, rw, rw_entries);
266
267 sendlockl(spc);
268 }
269
270 static void
271 unputwait(struct spclient *spc, struct respwait *rw)
272 {
273
274 sendunlockl(spc);
275
276 TAILQ_REMOVE(&spc->spc_respwait, rw, rw_entries);
277 pthread_mutex_unlock(&spc->spc_mtx);
278 pthread_cond_destroy(&rw->rw_cv);
279 }
280
281 static void
282 kickwaiter(struct spclient *spc)
283 {
284 struct respwait *rw;
285
286 pthread_mutex_lock(&spc->spc_mtx);
287 TAILQ_FOREACH(rw, &spc->spc_respwait, rw_entries) {
288 if (rw->rw_reqno == spc->spc_hdr.rsp_reqno)
289 break;
290 }
291 if (rw == NULL) {
292 DPRINTF(("no waiter found, invalid reqno %" PRIu64 "?\n",
293 spc->spc_hdr.rsp_reqno));
294 return;
295 }
296 DPRINTF(("rump_sp: client %p woke up waiter at %p\n", spc, rw));
297 rw->rw_data = spc->spc_buf;
298 rw->rw_dlen = (size_t)(spc->spc_off - HDRSZ);
299 if (spc->spc_hdr.rsp_class == RUMPSP_ERROR) {
300 rw->rw_error = spc->spc_hdr.rsp_error;
301 } else {
302 rw->rw_error = 0;
303 }
304 pthread_cond_signal(&rw->rw_cv);
305 pthread_mutex_unlock(&spc->spc_mtx);
306
307 if (rw->rw_error)
308 spcfreebuf(spc);
309 else
310 spcresetbuf(spc);
311 }
312
313 static void
314 kickall(struct spclient *spc)
315 {
316 struct respwait *rw;
317
318 /* DIAGASSERT(mutex_owned(spc_lock)) */
319 TAILQ_FOREACH(rw, &spc->spc_respwait, rw_entries)
320 pthread_cond_broadcast(&rw->rw_cv);
321 }
322
323 static int
324 waitresp(struct spclient *spc, struct respwait *rw)
325 {
326 struct pollfd pfd;
327 int rv = 0;
328
329 sendunlockl(spc);
330
331 rw->rw_error = 0;
332 while (rw->rw_data == NULL && rw->rw_error == 0 && spc->spc_dying == 0){
333 /* are we free to receive? */
334 if (spc->spc_istatus == SPCSTATUS_FREE) {
335 int gotresp;
336
337 spc->spc_istatus = SPCSTATUS_BUSY;
338 pthread_mutex_unlock(&spc->spc_mtx);
339
340 pfd.fd = spc->spc_fd;
341 pfd.events = POLLIN;
342
343 for (gotresp = 0; !gotresp; ) {
344 switch (readframe(spc)) {
345 case 0:
346 poll(&pfd, 1, INFTIM);
347 continue;
348 case -1:
349 rv = errno;
350 spc->spc_dying = 1;
351 goto cleanup;
352 default:
353 break;
354 }
355
356 switch (spc->spc_hdr.rsp_class) {
357 case RUMPSP_RESP:
358 case RUMPSP_ERROR:
359 kickwaiter(spc);
360 gotresp = spc->spc_hdr.rsp_reqno ==
361 rw->rw_reqno;
362 break;
363 case RUMPSP_REQ:
364 handlereq(spc);
365 break;
366 default:
367 /* panic */
368 break;
369 }
370 }
371 cleanup:
372 pthread_mutex_lock(&spc->spc_mtx);
373 if (spc->spc_istatus == SPCSTATUS_WANTED)
374 kickall(spc);
375 spc->spc_istatus = SPCSTATUS_FREE;
376 } else {
377 spc->spc_istatus = SPCSTATUS_WANTED;
378 pthread_cond_wait(&rw->rw_cv, &spc->spc_mtx);
379 }
380 }
381
382 TAILQ_REMOVE(&spc->spc_respwait, rw, rw_entries);
383 pthread_mutex_unlock(&spc->spc_mtx);
384
385 pthread_cond_destroy(&rw->rw_cv);
386
387 if (rv)
388 return rv;
389 if (spc->spc_dying)
390 return ENOTCONN;
391 return rw->rw_error;
392 }
393
394 static int
395 readframe(struct spclient *spc)
396 {
397 int fd = spc->spc_fd;
398 size_t left;
399 size_t framelen;
400 ssize_t n;
401
402 /* still reading header? */
403 if (spc->spc_off < HDRSZ) {
404 DPRINTF(("rump_sp: readframe getting header at offset %zu\n",
405 spc->spc_off));
406
407 left = HDRSZ - spc->spc_off;
408 /*LINTED: cast ok */
409 n = read(fd, (uint8_t *)&spc->spc_hdr + spc->spc_off, left);
410 if (n == 0) {
411 return -1;
412 }
413 if (n == -1) {
414 if (errno == EAGAIN)
415 return 0;
416 return -1;
417 }
418
419 spc->spc_off += n;
420 if (spc->spc_off < HDRSZ)
421 return -1;
422
423 /*LINTED*/
424 framelen = spc->spc_hdr.rsp_len;
425
426 if (framelen < HDRSZ) {
427 return -1;
428 } else if (framelen == HDRSZ) {
429 return 1;
430 }
431
432 spc->spc_buf = malloc(framelen - HDRSZ);
433 if (spc->spc_buf == NULL) {
434 return -1;
435 }
436 memset(spc->spc_buf, 0, framelen - HDRSZ);
437
438 /* "fallthrough" */
439 } else {
440 /*LINTED*/
441 framelen = spc->spc_hdr.rsp_len;
442 }
443
444 left = framelen - spc->spc_off;
445
446 DPRINTF(("rump_sp: readframe getting body at offset %zu, left %zu\n",
447 spc->spc_off, left));
448
449 if (left == 0)
450 return 1;
451 n = read(fd, spc->spc_buf + (spc->spc_off - HDRSZ), left);
452 if (n == 0) {
453 return -1;
454 }
455 if (n == -1) {
456 if (errno == EAGAIN)
457 return 0;
458 return -1;
459 }
460 spc->spc_off += n;
461 left -= n;
462
463 /* got everything? */
464 if (left == 0)
465 return 1;
466 else
467 return 0;
468 }
469
470 static int
471 tcp_parse(const char *addr, struct sockaddr **sa, int allow_wildcard)
472 {
473 struct sockaddr_in sin;
474 char buf[64];
475 const char *p;
476 size_t l;
477 int port;
478
479 memset(&sin, 0, sizeof(sin));
480 sin.sin_len = sizeof(sin);
481 sin.sin_family = AF_INET;
482
483 p = strchr(addr, ':');
484 if (!p) {
485 fprintf(stderr, "rump_sp_tcp: missing port specifier\n");
486 return EINVAL;
487 }
488
489 l = p - addr;
490 if (l > sizeof(buf)-1) {
491 fprintf(stderr, "rump_sp_tcp: address too long\n");
492 return EINVAL;
493 }
494 strncpy(buf, addr, l);
495 buf[l] = '\0';
496
497 /* special INADDR_ANY treatment */
498 if (strcmp(buf, "*") == 0 || strcmp(buf, "0") == 0) {
499 sin.sin_addr.s_addr = INADDR_ANY;
500 } else {
501 switch (inet_pton(AF_INET, buf, &sin.sin_addr)) {
502 case 1:
503 break;
504 case 0:
505 fprintf(stderr, "rump_sp_tcp: cannot parse %s\n", buf);
506 return EINVAL;
507 case -1:
508 fprintf(stderr, "rump_sp_tcp: inet_pton failed\n");
509 return errno;
510 default:
511 assert(/*CONSTCOND*/0);
512 return EINVAL;
513 }
514 }
515
516 if (!allow_wildcard && sin.sin_addr.s_addr == INADDR_ANY) {
517 fprintf(stderr, "rump_sp_tcp: client needs !INADDR_ANY\n");
518 return EINVAL;
519 }
520
521 /* advance to port number & parse */
522 p++;
523 l = strspn(p, "0123456789");
524 if (l == 0) {
525 fprintf(stderr, "rump_sp_tcp: port now found: %s\n", p);
526 return EINVAL;
527 }
528 strncpy(buf, p, l);
529 buf[l] = '\0';
530
531 if (*(p+l) != '/' && *(p+l) != '\0') {
532 fprintf(stderr, "rump_sp_tcp: junk at end of port: %s\n", addr);
533 return EINVAL;
534 }
535
536 port = atoi(buf);
537 if (port < 0 || port >= (1<<(8*sizeof(in_port_t)))) {
538 fprintf(stderr, "rump_sp_tcp: port %d out of range\n", port);
539 return ERANGE;
540 }
541 sin.sin_port = htons(port);
542
543 *sa = malloc(sizeof(sin));
544 if (*sa == NULL)
545 return errno;
546 memcpy(*sa, &sin, sizeof(sin));
547 return 0;
548 }
549
550 static int
551 tcp_connecthook(int s)
552 {
553 int x;
554
555 x = 1;
556 setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &x, sizeof(x));
557
558 return 0;
559 }
560
561 /*ARGSUSED*/
562 static int
563 unix_parse(const char *addr, struct sockaddr **sa, int allow_wildcard)
564 {
565 struct sockaddr_un sun;
566 size_t slen;
567
568 if (strlen(addr) > sizeof(sun.sun_path))
569 return ENAMETOOLONG;
570
571 /*
572 * The pathname can be all kinds of spaghetti elementals,
573 * so meek and obidient we accept everything.
574 */
575 memset(&sun, 0, sizeof(sun));
576 sun.sun_family = AF_LOCAL;
577 strlcpy(sun.sun_path, addr, sizeof(sun.sun_path));
578 sun.sun_len = slen = SUN_LEN(&sun);
579
580 *sa = malloc(slen);
581 if (*sa == NULL)
582 return errno;
583 memcpy(*sa, &sun, slen);
584
585 return 0;
586 }
587
588 /*ARGSUSED*/
589 static int
590 notsupp(void)
591 {
592
593 fprintf(stderr, "rump_sp: support not yet implemented\n");
594 return EOPNOTSUPP;
595 }
596
597 static int
598 success(void)
599 {
600
601 return 0;
602 }
603
604 struct {
605 const char *id;
606 int domain;
607 addrparse_fn ap;
608 connecthook_fn connhook;
609 } parsetab[] = {
610 { "tcp", PF_INET, tcp_parse, tcp_connecthook },
611 { "unix", PF_LOCAL, unix_parse, (connecthook_fn)success },
612 { "tcp6", PF_INET6, (addrparse_fn)notsupp, (connecthook_fn)success },
613 };
614 #define NPARSE (sizeof(parsetab)/sizeof(parsetab[0]))
615
616 static int
617 parseurl(const char *url, struct sockaddr **sap, unsigned *idxp,
618 int allow_wildcard)
619 {
620 char id[16];
621 const char *p, *p2;
622 size_t l;
623 unsigned i;
624 int error;
625
626 /*
627 * Parse the url
628 */
629
630 p = url;
631 p2 = strstr(p, "://");
632 if (!p2) {
633 fprintf(stderr, "rump_sp: invalid locator ``%s''\n", p);
634 return EINVAL;
635 }
636 l = p2-p;
637 if (l > sizeof(id)-1) {
638 fprintf(stderr, "rump_sp: identifier too long in ``%s''\n", p);
639 return EINVAL;
640 }
641
642 strncpy(id, p, l);
643 id[l] = '\0';
644 p2 += 3; /* beginning of address */
645
646 for (i = 0; i < NPARSE; i++) {
647 if (strcmp(id, parsetab[i].id) == 0) {
648 error = parsetab[i].ap(p2, sap, allow_wildcard);
649 if (error)
650 return error;
651 break;
652 }
653 }
654 if (i == NPARSE) {
655 fprintf(stderr, "rump_sp: invalid identifier ``%s''\n", p);
656 return EINVAL;
657 }
658
659 *idxp = i;
660 return 0;
661 }
662