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