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