sp_common.c revision 1.10 1 /* $NetBSD: sp_common.c,v 1.10 2010/11/25 17:59:02 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 <poll.h>
48 #include <pthread.h>
49 #include <stdarg.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
54
55 //#define DEBUG
56 #ifdef DEBUG
57 #define DPRINTF(x) mydprintf x
58 static void
59 mydprintf(const char *fmt, ...)
60 {
61 va_list ap;
62
63 va_start(ap, fmt);
64 vfprintf(stderr, fmt, ap);
65 va_end(ap);
66 }
67 #else
68 #define DPRINTF(x)
69 #endif
70
71 /*
72 * Bah, I hate writing on-off-wire conversions in C
73 */
74
75 enum { RUMPSP_REQ, RUMPSP_RESP };
76 enum { RUMPSP_SYSCALL,
77 RUMPSP_COPYIN, RUMPSP_COPYINSTR,
78 RUMPSP_COPYOUT, RUMPSP_COPYOUTSTR,
79 RUMPSP_ANONMMAP };
80
81 struct rsp_hdr {
82 uint64_t rsp_len;
83 uint64_t rsp_reqno;
84 uint16_t rsp_class;
85 uint16_t rsp_type;
86 /*
87 * We want this structure 64bit-aligned for typecast fun,
88 * so might as well use the following for something.
89 */
90 uint32_t rsp_sysnum;
91 };
92 #define HDRSZ sizeof(struct rsp_hdr)
93
94 /*
95 * Data follows the header. We have two types of structured data.
96 */
97
98 /* copyin/copyout */
99 struct rsp_copydata {
100 size_t rcp_len;
101 void *rcp_addr;
102 uint8_t rcp_data[0];
103 };
104
105 /* syscall response */
106 struct rsp_sysresp {
107 int rsys_error;
108 register_t rsys_retval[2];
109 };
110
111 struct respwait {
112 uint64_t rw_reqno;
113 void *rw_data;
114 size_t rw_dlen;
115
116 pthread_cond_t rw_cv;
117
118 TAILQ_ENTRY(respwait) rw_entries;
119 };
120
121 struct spclient {
122 int spc_fd;
123 int spc_refcnt;
124 int spc_dying;
125
126 struct lwp *spc_mainlwp;
127 pid_t spc_pid;
128
129 struct pollfd *spc_pfd;
130
131 struct rsp_hdr spc_hdr;
132 uint8_t *spc_buf;
133 size_t spc_off;
134
135 pthread_mutex_t spc_mtx;
136 pthread_cond_t spc_cv;
137
138 uint64_t spc_nextreq;
139 int spc_ostatus, spc_istatus;
140
141 TAILQ_HEAD(, respwait) spc_respwait;
142 };
143 #define SPCSTATUS_FREE 0
144 #define SPCSTATUS_BUSY 1
145 #define SPCSTATUS_WANTED 2
146
147 typedef int (*addrparse_fn)(const char *, struct sockaddr **, int);
148 typedef int (*connecthook_fn)(int);
149
150 static int readframe(struct spclient *);
151 static void handlereq(struct spclient *);
152
153 static void
154 sendlock(struct spclient *spc)
155 {
156
157 pthread_mutex_lock(&spc->spc_mtx);
158 while (spc->spc_ostatus != SPCSTATUS_FREE) {
159 spc->spc_ostatus = SPCSTATUS_WANTED;
160 pthread_cond_wait(&spc->spc_cv, &spc->spc_mtx);
161 }
162 spc->spc_ostatus = SPCSTATUS_BUSY;
163 pthread_mutex_unlock(&spc->spc_mtx);
164 }
165
166 static void
167 sendunlock(struct spclient *spc)
168 {
169
170 pthread_mutex_lock(&spc->spc_mtx);
171 if (spc->spc_ostatus == SPCSTATUS_WANTED)
172 pthread_cond_broadcast(&spc->spc_cv);
173 spc->spc_ostatus = SPCSTATUS_FREE;
174 pthread_mutex_unlock(&spc->spc_mtx);
175 }
176
177 static int
178 dosend(struct spclient *spc, const void *data, size_t dlen)
179 {
180 struct pollfd pfd;
181 const uint8_t *sdata = data;
182 ssize_t n;
183 size_t sent;
184 int fd = spc->spc_fd;
185
186 pfd.fd = fd;
187 pfd.events = POLLOUT;
188
189 for (sent = 0, n = 0; sent < dlen; ) {
190 if (n) {
191 if (poll(&pfd, 1, INFTIM) == -1) {
192 if (errno == EINTR)
193 continue;
194 return errno;
195 }
196 }
197
198 n = send(fd, sdata + sent, dlen - sent, MSG_NOSIGNAL);
199 if (n == 0) {
200 return EFAULT;
201 }
202 if (n == -1) {
203 if (errno != EAGAIN)
204 return EFAULT;
205 continue;
206 }
207 sent += n;
208 }
209
210 return 0;
211 }
212
213 static void
214 putwait(struct spclient *spc, struct respwait *rw, struct rsp_hdr *rhdr)
215 {
216
217 rw->rw_data = NULL;
218 rw->rw_dlen = 0;
219 pthread_cond_init(&rw->rw_cv, NULL);
220
221 pthread_mutex_lock(&spc->spc_mtx);
222 rw->rw_reqno = rhdr->rsp_reqno = spc->spc_nextreq++;
223 TAILQ_INSERT_TAIL(&spc->spc_respwait, rw, rw_entries);
224 }
225
226 static void
227 unputwait(struct spclient *spc, struct respwait *rw)
228 {
229
230 TAILQ_REMOVE(&spc->spc_respwait, rw, rw_entries);
231 pthread_mutex_unlock(&spc->spc_mtx);
232 pthread_cond_destroy(&rw->rw_cv);
233 }
234
235 static void
236 kickwaiter(struct spclient *spc)
237 {
238 struct respwait *rw;
239
240 pthread_mutex_lock(&spc->spc_mtx);
241 TAILQ_FOREACH(rw, &spc->spc_respwait, rw_entries) {
242 if (rw->rw_reqno == spc->spc_hdr.rsp_reqno)
243 break;
244 }
245 if (rw == NULL) {
246 printf("PANIC: no waiter\n");
247 abort();
248 return;
249 }
250 DPRINTF(("rump_sp: client %p woke up waiter at %p\n", spc, rw));
251 rw->rw_data = spc->spc_buf;
252 rw->rw_dlen = (size_t)spc->spc_off;
253 pthread_cond_signal(&rw->rw_cv);
254 pthread_mutex_unlock(&spc->spc_mtx);
255
256 spc->spc_buf = NULL;
257 spc->spc_off = 0;
258 }
259
260 static void
261 kickall(struct spclient *spc)
262 {
263 struct respwait *rw;
264
265 /* DIAGASSERT(mutex_owned(spc_lock)) */
266 TAILQ_FOREACH(rw, &spc->spc_respwait, rw_entries)
267 pthread_cond_signal(&rw->rw_cv);
268 }
269
270 static int
271 waitresp(struct spclient *spc, struct respwait *rw)
272 {
273 struct pollfd pfd;
274 int rv = 0;
275
276 while (rw->rw_data == NULL && spc->spc_dying == 0) {
277 /* are we free to receive? */
278 if (spc->spc_istatus == SPCSTATUS_FREE) {
279 int gotresp;
280
281 spc->spc_istatus = SPCSTATUS_BUSY;
282 pthread_mutex_unlock(&spc->spc_mtx);
283
284 pfd.fd = spc->spc_fd;
285 pfd.events = POLLIN;
286
287 for (gotresp = 0; !gotresp; ) {
288 switch (readframe(spc)) {
289 case 0:
290 poll(&pfd, 1, INFTIM);
291 continue;
292 case -1:
293 rv = errno;
294 spc->spc_dying = 1;
295 break;
296 default:
297 break;
298 }
299
300 switch (spc->spc_hdr.rsp_class) {
301 case RUMPSP_RESP:
302 kickwaiter(spc);
303 gotresp = spc->spc_hdr.rsp_reqno ==
304 rw->rw_reqno;
305 break;
306 case RUMPSP_REQ:
307 handlereq(spc);
308 break;
309 default:
310 /* panic */
311 break;
312 }
313 }
314 pthread_mutex_lock(&spc->spc_mtx);
315 if (spc->spc_istatus == SPCSTATUS_WANTED)
316 kickall(spc);
317 spc->spc_istatus = SPCSTATUS_FREE;
318 } else {
319 spc->spc_istatus = SPCSTATUS_WANTED;
320 pthread_cond_wait(&rw->rw_cv, &spc->spc_mtx);
321 }
322 }
323
324 TAILQ_REMOVE(&spc->spc_respwait, rw, rw_entries);
325 pthread_mutex_unlock(&spc->spc_mtx);
326
327 pthread_cond_destroy(&rw->rw_cv);
328
329 return rv;
330 }
331
332 static int
333 readframe(struct spclient *spc)
334 {
335 int fd = spc->spc_fd;
336 size_t left;
337 size_t framelen;
338 ssize_t n;
339
340 /* still reading header? */
341 if (spc->spc_off < HDRSZ) {
342 DPRINTF(("rump_sp: readframe getting header at offset %zu\n",
343 spc->spc_off));
344
345 left = HDRSZ - spc->spc_off;
346 /*LINTED: cast ok */
347 n = read(fd, (uint8_t *)&spc->spc_hdr + spc->spc_off, left);
348 if (n == 0) {
349 return -1;
350 }
351 if (n == -1) {
352 if (errno == EAGAIN)
353 return 0;
354 return -1;
355 }
356
357 spc->spc_off += n;
358 if (spc->spc_off < HDRSZ)
359 return -1;
360
361 /*LINTED*/
362 framelen = spc->spc_hdr.rsp_len;
363
364 if (framelen < HDRSZ) {
365 return -1;
366 } else if (framelen == HDRSZ) {
367 return 1;
368 }
369
370 spc->spc_buf = malloc(framelen - HDRSZ);
371 if (spc->spc_buf == NULL) {
372 return -1;
373 }
374 memset(spc->spc_buf, 0, framelen - HDRSZ);
375
376 /* "fallthrough" */
377 } else {
378 /*LINTED*/
379 framelen = spc->spc_hdr.rsp_len;
380 }
381
382 left = framelen - spc->spc_off;
383
384 DPRINTF(("rump_sp: readframe getting body at offset %zu, left %zu\n",
385 spc->spc_off, left));
386
387 if (left == 0)
388 return 1;
389 n = read(fd, spc->spc_buf + (spc->spc_off - HDRSZ), left);
390 if (n == 0) {
391 return -1;
392 }
393 if (n == -1) {
394 if (errno == EAGAIN)
395 return 0;
396 return -1;
397 }
398 spc->spc_off += n;
399 left -= n;
400
401 /* got everything? */
402 if (left == 0)
403 return 1;
404 else
405 return 0;
406 }
407
408 static int
409 tcp_parse(const char *addr, struct sockaddr **sa, int allow_wildcard)
410 {
411 struct sockaddr_in sin;
412 char buf[64];
413 const char *p;
414 size_t l;
415 int port;
416
417 memset(&sin, 0, sizeof(sin));
418 sin.sin_len = sizeof(sin);
419 sin.sin_family = AF_INET;
420
421 p = strchr(addr, ':');
422 if (!p) {
423 fprintf(stderr, "rump_sp_tcp: missing port specifier\n");
424 return EINVAL;
425 }
426
427 l = p - addr;
428 if (l > sizeof(buf)-1) {
429 fprintf(stderr, "rump_sp_tcp: address too long\n");
430 return EINVAL;
431 }
432 strncpy(buf, addr, l);
433 buf[l] = '\0';
434
435 /* special INADDR_ANY treatment */
436 if (strcmp(buf, "*") == 0 || strcmp(buf, "0") == 0) {
437 sin.sin_addr.s_addr = INADDR_ANY;
438 } else {
439 switch (inet_pton(AF_INET, buf, &sin.sin_addr)) {
440 case 1:
441 break;
442 case 0:
443 fprintf(stderr, "rump_sp_tcp: cannot parse %s\n", buf);
444 return EINVAL;
445 case -1:
446 fprintf(stderr, "rump_sp_tcp: inet_pton failed\n");
447 return errno;
448 default:
449 assert(/*CONSTCOND*/0);
450 return EINVAL;
451 }
452 }
453
454 if (!allow_wildcard && sin.sin_addr.s_addr == INADDR_ANY) {
455 fprintf(stderr, "rump_sp_tcp: client needs !INADDR_ANY\n");
456 return EINVAL;
457 }
458
459 /* advance to port number & parse */
460 p++;
461 l = strspn(p, "0123456789");
462 if (l == 0) {
463 fprintf(stderr, "rump_sp_tcp: port now found: %s\n", p);
464 return EINVAL;
465 }
466 strncpy(buf, p, l);
467 buf[l] = '\0';
468
469 if (*(p+l) != '/' && *(p+l) != '\0') {
470 fprintf(stderr, "rump_sp_tcp: junk at end of port: %s\n", addr);
471 return EINVAL;
472 }
473
474 port = atoi(buf);
475 if (port < 0 || port >= (1<<(8*sizeof(in_port_t)))) {
476 fprintf(stderr, "rump_sp_tcp: port %d out of range\n", port);
477 return ERANGE;
478 }
479 sin.sin_port = htons(port);
480
481 *sa = malloc(sizeof(sin));
482 if (*sa == NULL)
483 return errno;
484 memcpy(*sa, &sin, sizeof(sin));
485 return 0;
486 }
487
488 static int
489 tcp_connecthook(int s)
490 {
491 int x;
492
493 x = 1;
494 setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &x, sizeof(x));
495
496 return 0;
497 }
498
499 /*ARGSUSED*/
500 static int
501 unix_parse(const char *addr, struct sockaddr **sa, int allow_wildcard)
502 {
503 struct sockaddr_un sun;
504 size_t slen;
505
506 if (strlen(addr) > sizeof(sun.sun_path))
507 return ENAMETOOLONG;
508
509 /*
510 * The pathname can be all kinds of spaghetti elementals,
511 * so meek and obidient we accept everything.
512 */
513 memset(&sun, 0, sizeof(sun));
514 sun.sun_family = AF_LOCAL;
515 strlcpy(sun.sun_path, addr, sizeof(sun.sun_path));
516 sun.sun_len = slen = SUN_LEN(&sun);
517
518 *sa = malloc(slen);
519 if (*sa == NULL)
520 return errno;
521 memcpy(*sa, &sun, slen);
522
523 return 0;
524 }
525
526 /*ARGSUSED*/
527 static int
528 notsupp(void)
529 {
530
531 fprintf(stderr, "rump_sp: support not yet implemented\n");
532 return EOPNOTSUPP;
533 }
534
535 static int
536 success(void)
537 {
538
539 return 0;
540 }
541
542 struct {
543 const char *id;
544 int domain;
545 addrparse_fn ap;
546 connecthook_fn connhook;
547 } parsetab[] = {
548 { "tcp", PF_INET, tcp_parse, tcp_connecthook },
549 { "unix", PF_LOCAL, unix_parse, (connecthook_fn)success },
550 { "tcp6", PF_INET6, (addrparse_fn)notsupp, (connecthook_fn)success },
551 };
552 #define NPARSE (sizeof(parsetab)/sizeof(parsetab[0]))
553
554 static int
555 parseurl(const char *url, struct sockaddr **sap, unsigned *idxp,
556 int allow_wildcard)
557 {
558 char id[16];
559 const char *p, *p2;
560 size_t l;
561 unsigned i;
562 int error;
563
564 /*
565 * Parse the url
566 */
567
568 p = url;
569 p2 = strstr(p, "://");
570 if (!p2) {
571 fprintf(stderr, "rump_sp: invalid locator ``%s''\n", p);
572 return EINVAL;
573 }
574 l = p2-p;
575 if (l > sizeof(id)-1) {
576 fprintf(stderr, "rump_sp: identifier too long in ``%s''\n", p);
577 return EINVAL;
578 }
579
580 strncpy(id, p, l);
581 id[l] = '\0';
582 p2 += 3; /* beginning of address */
583
584 for (i = 0; i < NPARSE; i++) {
585 if (strcmp(id, parsetab[i].id) == 0) {
586 error = parsetab[i].ap(p2, sap, allow_wildcard);
587 if (error)
588 return error;
589 break;
590 }
591 }
592 if (i == NPARSE) {
593 fprintf(stderr, "rump_sp: invalid identifier ``%s''\n", p);
594 return EINVAL;
595 }
596
597 *idxp = i;
598 return 0;
599 }
600