Home | History | Annotate | Line # | Download | only in librumpuser
sp_common.c revision 1.1
      1 /*      $NetBSD: sp_common.c,v 1.1 2010/11/04 20:54:07 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/socket.h>
     37 
     38 #include <arpa/inet.h>
     39 #include <netinet/in.h>
     40 #include <netinet/tcp.h>
     41 
     42 #include <assert.h>
     43 #include <errno.h>
     44 #include <fcntl.h>
     45 #include <poll.h>
     46 #include <stdarg.h>
     47 #include <stdio.h>
     48 #include <stdlib.h>
     49 #include <string.h>
     50 #include <unistd.h>
     51 
     52 //#define DEBUG
     53 #ifdef DEBUG
     54 #define DPRINTF(x) mydprintf x
     55 static void
     56 mydprintf(const char *fmt, ...)
     57 {
     58 	va_list ap;
     59 
     60 	va_start(ap, fmt);
     61 	vfprintf(stderr, fmt, ap);
     62 	va_end(ap);
     63 }
     64 #else
     65 #define DPRINTF(x)
     66 #endif
     67 
     68 /*
     69  * Bah, I hate writing on-off-wire conversions in C
     70  */
     71 
     72 enum {
     73 	RUMPSP_SYSCALL_REQ,	RUMPSP_SYSCALL_RESP,
     74 	RUMPSP_COPYIN_REQ,	RUMPSP_COPYIN_RESP,
     75 	RUMPSP_COPYOUT_REQ,	/* no copyout resp */
     76 	RUMPSP_ANONMMAP_REQ,	RUMPSP_ANONMMAP_RESP
     77 };
     78 
     79 struct rsp_hdr {
     80 	uint64_t rsp_len;
     81 	uint64_t rsp_reqno;
     82 	uint32_t rsp_type;
     83 	/*
     84 	 * We want this structure 64bit-aligned for typecast fun,
     85 	 * so might as well use the following for something.
     86 	 */
     87 	uint32_t rsp_sysnum;
     88 };
     89 #define HDRSZ sizeof(struct rsp_hdr)
     90 
     91 /*
     92  * Data follows the header.  We have two types of structured data.
     93  */
     94 
     95 /* copyin/copyout */
     96 struct rsp_copydata {
     97 	size_t rcp_len;
     98 	void *rcp_addr;
     99 	uint8_t rcp_data[0];
    100 };
    101 
    102 /* syscall response */
    103 struct rsp_sysresp {
    104 	int rsys_error;
    105 	register_t rsys_retval[2];
    106 };
    107 
    108 
    109 struct spclient {
    110 	int spc_fd;
    111 	struct lwp *spc_lwp;
    112 
    113 	/* incoming */
    114 	struct rsp_hdr spc_hdr;
    115 	uint8_t *spc_buf;
    116 	size_t spc_off;
    117 
    118 #if 0
    119 	/* outgoing */
    120 	int spc_obusy;
    121 	pthread_mutex_t spc_omtx;
    122 	pthread_cond_t spc_cv;
    123 #endif
    124 };
    125 
    126 typedef int (*addrparse_fn)(const char *, struct sockaddr **, int);
    127 typedef int (*connecthook_fn)(int);
    128 
    129 static uint64_t nextreq;
    130 
    131 static int
    132 dosend(struct spclient *spc, const void *data, size_t dlen)
    133 {
    134 	struct pollfd pfd;
    135 	const uint8_t *sdata = data;
    136 	ssize_t n;
    137 	size_t sent;
    138 	int fd = spc->spc_fd;
    139 
    140 	pfd.fd = fd;
    141 	pfd.events = POLLOUT;
    142 
    143 	for (sent = 0, n = 0; sent < dlen; ) {
    144 		if (n) {
    145 			if (poll(&pfd, 1, INFTIM) == -1) {
    146 				if (errno == EINTR)
    147 					continue;
    148 				return errno;
    149 			}
    150 		}
    151 
    152 		n = write(fd, sdata + sent, dlen - sent);
    153 		if (n == 0) {
    154 			return EFAULT;
    155 		}
    156 		if (n == -1 && errno != EAGAIN) {
    157 			return EFAULT;
    158 		}
    159 		sent += n;
    160 	}
    161 
    162 	return 0;
    163 }
    164 
    165 static int
    166 readframe(struct spclient *spc)
    167 {
    168 	int fd = spc->spc_fd;
    169 	size_t left;
    170 	size_t framelen;
    171 	ssize_t n;
    172 
    173 	/* still reading header? */
    174 	if (spc->spc_off < HDRSZ) {
    175 		DPRINTF(("rump_sp: readframe getting header at offset %zu\n",
    176 		    spc->spc_off));
    177 
    178 		left = HDRSZ - spc->spc_off;
    179 		/*LINTED: cast ok */
    180 		n = read(fd, (uint8_t *)&spc->spc_hdr + spc->spc_off, left);
    181 		if (n == 0) {
    182 			return -1;
    183 		}
    184 		if (n == -1) {
    185 			if (errno == EAGAIN)
    186 				return 0;
    187 			return -1;
    188 		}
    189 
    190 		spc->spc_off += n;
    191 		if (spc->spc_off < HDRSZ)
    192 			return -1;
    193 
    194 		/*LINTED*/
    195 		framelen = spc->spc_hdr.rsp_len;
    196 
    197 		if (framelen < HDRSZ) {
    198 			return -1;
    199 		} else if (framelen == HDRSZ) {
    200 			return 1;
    201 		}
    202 
    203 		spc->spc_buf = malloc(framelen - HDRSZ);
    204 		if (spc->spc_buf == NULL) {
    205 			return -1;
    206 		}
    207 		memset(spc->spc_buf, 0, framelen - HDRSZ);
    208 
    209 		/* "fallthrough" */
    210 	} else {
    211 		/*LINTED*/
    212 		framelen = spc->spc_hdr.rsp_len;
    213 	}
    214 
    215 	left = framelen - spc->spc_off;
    216 
    217 	DPRINTF(("rump_sp: readframe getting body at offset %zu, left %zu\n",
    218 	    spc->spc_off, left));
    219 
    220 	if (left == 0)
    221 		return 1;
    222 	n = read(fd, spc->spc_buf + (spc->spc_off - HDRSZ), left);
    223 	if (n == 0) {
    224 		return -1;
    225 	}
    226 	if (n == -1) {
    227 		if (errno == EAGAIN)
    228 			return 0;
    229 		return -1;
    230 	}
    231 	spc->spc_off += n;
    232 	left -= n;
    233 
    234 	/* got everything? */
    235 	if (left == 0)
    236 		return 1;
    237 	else
    238 		return 0;
    239 }
    240 
    241 static int
    242 tcp_parse(const char *addr, struct sockaddr **sa, int allow_wildcard)
    243 {
    244 	struct sockaddr_in sin;
    245 	char buf[64];
    246 	const char *p;
    247 	size_t l;
    248 	int port;
    249 
    250 	memset(&sin, 0, sizeof(sin));
    251 	sin.sin_len = sizeof(sin);
    252 	sin.sin_family = AF_INET;
    253 
    254 	p = strchr(addr, ':');
    255 	if (!p) {
    256 		fprintf(stderr, "rump_sp_tcp: missing port specifier\n");
    257 		return EINVAL;
    258 	}
    259 
    260 	l = p - addr;
    261 	if (l > sizeof(buf)-1) {
    262 		fprintf(stderr, "rump_sp_tcp: address too long\n");
    263 		return EINVAL;
    264 	}
    265 	strncpy(buf, addr, l);
    266 	buf[l] = '\0';
    267 
    268 	/* special INADDR_ANY treatment */
    269 	if (strcmp(buf, "*") == 0 || strcmp(buf, "0") == 0) {
    270 		sin.sin_addr.s_addr = INADDR_ANY;
    271 	} else {
    272 		switch (inet_pton(AF_INET, buf, &sin.sin_addr)) {
    273 		case 1:
    274 			break;
    275 		case 0:
    276 			fprintf(stderr, "rump_sp_tcp: cannot parse %s\n", buf);
    277 			return EINVAL;
    278 		case -1:
    279 			fprintf(stderr, "rump_sp_tcp: inet_pton failed\n");
    280 			return errno;
    281 		default:
    282 			assert(/*CONSTCOND*/0);
    283 			return EINVAL;
    284 		}
    285 	}
    286 
    287 	if (!allow_wildcard && sin.sin_addr.s_addr == INADDR_ANY) {
    288 		fprintf(stderr, "rump_sp_tcp: client needs !INADDR_ANY\n");
    289 		return EINVAL;
    290 	}
    291 
    292 	/* advance to port number & parse */
    293 	p++;
    294 	l = strspn(p, "0123456789");
    295 	if (l == 0) {
    296 		fprintf(stderr, "rump_sp_tcp: port now found: %s\n", p);
    297 		return EINVAL;
    298 	}
    299 	strncpy(buf, p, l);
    300 	buf[l] = '\0';
    301 
    302 	if (*(p+l) != '/' && *(p+l) != '\0') {
    303 		fprintf(stderr, "rump_sp_tcp: junk at end of port: %s\n", addr);
    304 		return EINVAL;
    305 	}
    306 
    307 	port = atoi(buf);
    308 	if (port < 0 || port >= (1<<(8*sizeof(in_port_t)))) {
    309 		fprintf(stderr, "rump_sp_tcp: port %d out of range\n", port);
    310 		return ERANGE;
    311 	}
    312 	sin.sin_port = htons(port);
    313 
    314 	*sa = malloc(sizeof(sin));
    315 	if (*sa == NULL)
    316 		return errno;
    317 	memcpy(*sa, &sin, sizeof(sin));
    318 	return 0;
    319 }
    320 
    321 static int
    322 tcp_connecthook(int s)
    323 {
    324 	int x;
    325 
    326 	x = 1;
    327 	setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &x, sizeof(x));
    328 
    329 	return 0;
    330 }
    331 
    332 /*ARGSUSED*/
    333 static int
    334 notsupp(void)
    335 {
    336 
    337 	fprintf(stderr, "rump_sp: support not yet implemented\n");
    338 	return EOPNOTSUPP;
    339 }
    340 
    341 static int
    342 success(void)
    343 {
    344 
    345 	return 0;
    346 }
    347 
    348 struct {
    349 	const char *id;
    350 	int domain;
    351 	addrparse_fn ap;
    352 	connecthook_fn connhook;
    353 } parsetab[] = {
    354 	{ "tcp", PF_INET, tcp_parse, tcp_connecthook },
    355 	{ "unix", PF_LOCAL, (addrparse_fn)notsupp, (connecthook_fn)success },
    356 	{ "tcp6", PF_INET6, (addrparse_fn)notsupp, (connecthook_fn)success },
    357 };
    358 #define NPARSE (sizeof(parsetab)/sizeof(parsetab[0]))
    359 
    360 static int
    361 parseurl(const char *url, struct sockaddr **sap, unsigned *idxp,
    362 	int allow_wildcard)
    363 {
    364 	char id[16];
    365 	const char *p, *p2;
    366 	size_t l;
    367 	unsigned i;
    368 	int error;
    369 
    370 	/*
    371 	 * Parse the url
    372 	 */
    373 
    374 	p = url;
    375 	p2 = strstr(p, "://");
    376 	if (!p2) {
    377 		fprintf(stderr, "rump_sp: invalid locator ``%s''\n", p);
    378 		return EINVAL;
    379 	}
    380 	l = p2-p;
    381 	if (l > sizeof(id)-1) {
    382 		fprintf(stderr, "rump_sp: identifier too long in ``%s''\n", p);
    383 		return EINVAL;
    384 	}
    385 
    386 	strncpy(id, p, l);
    387 	id[l] = '\0';
    388 	p2 += 3; /* beginning of address */
    389 
    390 	for (i = 0; i < NPARSE; i++) {
    391 		if (strcmp(id, parsetab[i].id) == 0) {
    392 			error = parsetab[i].ap(p2, sap, allow_wildcard);
    393 			if (error)
    394 				return error;
    395 			break;
    396 		}
    397 	}
    398 	if (i == NPARSE) {
    399 		fprintf(stderr, "rump_sp: invalid identifier ``%s''\n", p);
    400 		return EINVAL;
    401 	}
    402 
    403 	*idxp = i;
    404 	return 0;
    405 }
    406