1 /* $NetBSD: sp_common.c,v 1.45 2025/04/02 07:25:42 martin Exp $ */ 2 3 /* 4 * Copyright (c) 2010, 2011 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 "rumpuser_port.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 #include <sys/uio.h> 40 41 #include <arpa/inet.h> 42 #include <netinet/in.h> 43 #include <netinet/tcp.h> 44 45 #include <assert.h> 46 #include <errno.h> 47 #include <fcntl.h> 48 #include <inttypes.h> 49 #include <limits.h> 50 #include <poll.h> 51 #include <pthread.h> 52 #include <stdarg.h> 53 #include <stddef.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <unistd.h> 58 59 /* 60 * XXX: NetBSD's __unused collides with Linux headers, so we cannot 61 * define it before we've included everything. 62 */ 63 #if !defined(__unused) && (defined(__clang__) || defined(__GNUC__)) 64 #define __unused __attribute__((__unused__)) 65 #endif 66 #if !defined(__printflike) && (defined(__clang__) || defined(__GNUC__)) 67 #define __printflike(a,b) __attribute__((__format__(__printf__, a, b)))) 68 #endif 69 70 //#define DEBUG 71 #ifdef DEBUG 72 #define DPRINTF(x) mydprintf x 73 static __printflike(1, 2) void 74 mydprintf(const char *fmt, ...) 75 { 76 va_list ap; 77 78 if (getenv("RUMPUSER_DEBUG") == NULL) 79 return; 80 va_start(ap, fmt); 81 vfprintf(stderr, fmt, ap); 82 va_end(ap); 83 } 84 #else 85 #define DPRINTF(x) 86 #endif 87 88 #ifndef HOSTOPS 89 #define host_poll poll 90 #define host_read read 91 #define host_sendmsg sendmsg 92 #define host_setsockopt setsockopt 93 #endif 94 95 #define IOVPUT(_io_, _b_) _io_.iov_base = \ 96 (void *)&_b_; _io_.iov_len = sizeof(_b_); 97 #define IOVPUT_WITHSIZE(_io_, _b_, _l_) _io_.iov_base = \ 98 (void *)(_b_); _io_.iov_len = _l_; 99 #define SENDIOV(_spc_, _iov_) dosend(_spc_, _iov_, __arraycount(_iov_)) 100 101 /* 102 * Bah, I hate writing on-off-wire conversions in C 103 */ 104 105 enum { RUMPSP_REQ, RUMPSP_RESP, RUMPSP_ERROR }; 106 enum { RUMPSP_HANDSHAKE, 107 RUMPSP_SYSCALL, 108 RUMPSP_COPYIN, RUMPSP_COPYINSTR, 109 RUMPSP_COPYOUT, RUMPSP_COPYOUTSTR, 110 RUMPSP_ANONMMAP, 111 RUMPSP_PREFORK, 112 RUMPSP_RAISE }; 113 114 enum { HANDSHAKE_GUEST, HANDSHAKE_AUTH, HANDSHAKE_FORK, HANDSHAKE_EXEC }; 115 116 /* 117 * error types used for RUMPSP_ERROR 118 */ 119 enum rumpsp_err { RUMPSP_ERR_NONE = 0, RUMPSP_ERR_TRYAGAIN, RUMPSP_ERR_AUTH, 120 RUMPSP_ERR_INVALID_PREFORK, RUMPSP_ERR_RFORK_FAILED, 121 RUMPSP_ERR_INEXEC, RUMPSP_ERR_NOMEM, RUMPSP_ERR_MALFORMED_REQUEST }; 122 123 /* 124 * The mapping of the above types to errno. They are almost never exposed 125 * to the client after handshake (except for a server resource shortage 126 * and the client trying to be funny). This is a function instead of 127 * an array to catch missing values. Theoretically, the compiled code 128 * should be the same. 129 */ 130 static int 131 errmap(enum rumpsp_err error) 132 { 133 134 switch (error) { 135 /* XXX: no EAUTH on Linux */ 136 case RUMPSP_ERR_NONE: return 0; 137 case RUMPSP_ERR_AUTH: return EPERM; 138 case RUMPSP_ERR_TRYAGAIN: return EAGAIN; 139 case RUMPSP_ERR_INVALID_PREFORK: return ESRCH; 140 case RUMPSP_ERR_RFORK_FAILED: return EIO; /* got a light? */ 141 case RUMPSP_ERR_INEXEC: return EBUSY; 142 case RUMPSP_ERR_NOMEM: return ENOMEM; 143 case RUMPSP_ERR_MALFORMED_REQUEST: return EINVAL; 144 } 145 146 return -1; 147 } 148 149 #define AUTHLEN 4 /* 128bit fork auth */ 150 151 struct rsp_hdr { 152 uint64_t rsp_len; 153 uint64_t rsp_reqno; 154 uint16_t rsp_class; 155 uint16_t rsp_type; 156 /* 157 * We want this structure 64bit-aligned for typecast fun, 158 * so might as well use the following for something. 159 */ 160 union { 161 uint32_t sysnum; 162 uint32_t error; 163 uint32_t handshake; 164 uint32_t signo; 165 } u; 166 }; 167 #define HDRSZ sizeof(struct rsp_hdr) 168 #define rsp_sysnum u.sysnum 169 #define rsp_error u.error 170 #define rsp_handshake u.handshake 171 #define rsp_signo u.signo 172 173 #define MAXBANNER 96 174 175 /* 176 * Data follows the header. We have two types of structured data. 177 */ 178 179 /* copyin/copyout */ 180 struct rsp_copydata { 181 size_t rcp_len; 182 void *rcp_addr; 183 uint8_t rcp_data[0]; 184 }; 185 186 /* syscall response */ 187 struct rsp_sysresp { 188 int rsys_error; 189 register_t rsys_retval[2]; 190 }; 191 192 struct handshake_fork { 193 uint32_t rf_auth[4]; 194 int rf_cancel; 195 }; 196 197 struct respwait { 198 uint64_t rw_reqno; 199 void *rw_data; 200 size_t rw_dlen; 201 int rw_done; 202 int rw_error; 203 204 pthread_cond_t rw_cv; 205 206 TAILQ_ENTRY(respwait) rw_entries; 207 }; 208 209 struct prefork; 210 struct spclient { 211 int spc_fd; 212 int spc_refcnt; 213 int spc_state; 214 215 pthread_mutex_t spc_mtx; 216 pthread_cond_t spc_cv; 217 218 struct lwp *spc_mainlwp; 219 pid_t spc_pid; 220 221 TAILQ_HEAD(, respwait) spc_respwait; 222 223 /* rest of the fields are zeroed upon disconnect */ 224 #define SPC_ZEROFF offsetof(struct spclient, spc_pfd) 225 struct pollfd *spc_pfd; 226 227 struct rsp_hdr spc_hdr; 228 uint8_t *spc_buf; 229 size_t spc_off; 230 231 uint64_t spc_nextreq; 232 uint64_t spc_syscallreq; 233 uint64_t spc_generation; 234 int spc_ostatus, spc_istatus; 235 int spc_reconnecting; 236 int spc_inexec; 237 238 LIST_HEAD(, prefork) spc_pflist; 239 }; 240 #define SPCSTATUS_FREE 0 241 #define SPCSTATUS_BUSY 1 242 #define SPCSTATUS_WANTED 2 243 244 #define SPCSTATE_NEW 0 245 #define SPCSTATE_RUNNING 1 246 #define SPCSTATE_DYING 2 247 248 typedef int (*addrparse_fn)(const char *, struct sockaddr **, int); 249 typedef int (*connecthook_fn)(int); 250 typedef void (*cleanup_fn)(struct sockaddr *); 251 252 static int readframe(struct spclient *); 253 static void handlereq(struct spclient *); 254 255 static __inline void 256 spcresetbuf(struct spclient *spc) 257 { 258 259 spc->spc_buf = NULL; 260 spc->spc_off = 0; 261 } 262 263 static __inline void 264 spcfreebuf(struct spclient *spc) 265 { 266 267 free(spc->spc_buf); 268 spcresetbuf(spc); 269 } 270 271 static void 272 sendlockl(struct spclient *spc) 273 { 274 275 while (spc->spc_ostatus != SPCSTATUS_FREE) { 276 spc->spc_ostatus = SPCSTATUS_WANTED; 277 pthread_cond_wait(&spc->spc_cv, &spc->spc_mtx); 278 } 279 spc->spc_ostatus = SPCSTATUS_BUSY; 280 } 281 282 static void __unused 283 sendlock(struct spclient *spc) 284 { 285 286 pthread_mutex_lock(&spc->spc_mtx); 287 sendlockl(spc); 288 pthread_mutex_unlock(&spc->spc_mtx); 289 } 290 291 static void 292 sendunlockl(struct spclient *spc) 293 { 294 295 if (spc->spc_ostatus == SPCSTATUS_WANTED) 296 pthread_cond_broadcast(&spc->spc_cv); 297 spc->spc_ostatus = SPCSTATUS_FREE; 298 } 299 300 static void 301 sendunlock(struct spclient *spc) 302 { 303 304 pthread_mutex_lock(&spc->spc_mtx); 305 sendunlockl(spc); 306 pthread_mutex_unlock(&spc->spc_mtx); 307 } 308 309 static int 310 dosend(struct spclient *spc, struct iovec *iov, size_t iovlen) 311 { 312 struct msghdr msg; 313 struct pollfd pfd; 314 ssize_t n = 0; 315 int fd = spc->spc_fd; 316 317 pfd.fd = fd; 318 pfd.events = POLLOUT; 319 320 memset(&msg, 0, sizeof(msg)); 321 322 for (;;) { 323 /* not first round? poll */ 324 if (n) { 325 if (host_poll(&pfd, 1, INFTIM) == -1) { 326 if (errno == EINTR) 327 continue; 328 return errno; 329 } 330 } 331 332 msg.msg_iov = iov; 333 msg.msg_iovlen = iovlen; 334 n = host_sendmsg(fd, &msg, MSG_NOSIGNAL); 335 if (n == -1) { 336 if (errno == EPIPE) 337 return ENOTCONN; 338 if (errno != EAGAIN) 339 return errno; 340 continue; 341 } 342 if (n == 0) { 343 return ENOTCONN; 344 } 345 346 /* ok, need to adjust iovec for potential next round */ 347 while (iovlen && n >= (ssize_t)iov[0].iov_len) { 348 n -= iov[0].iov_len; 349 iov++; 350 iovlen--; 351 } 352 353 if (iovlen == 0) { 354 _DIAGASSERT(n == 0); 355 break; 356 } else { 357 iov[0].iov_base = 358 (void *)((uint8_t *)iov[0].iov_base + n); 359 iov[0].iov_len -= n; 360 } 361 } 362 363 return 0; 364 } 365 366 static void 367 doputwait(struct spclient *spc, struct respwait *rw, struct rsp_hdr *rhdr) 368 { 369 370 rw->rw_data = NULL; 371 rw->rw_dlen = rw->rw_done = rw->rw_error = 0; 372 pthread_cond_init(&rw->rw_cv, NULL); 373 374 pthread_mutex_lock(&spc->spc_mtx); 375 rw->rw_reqno = rhdr->rsp_reqno = spc->spc_nextreq++; 376 TAILQ_INSERT_TAIL(&spc->spc_respwait, rw, rw_entries); 377 } 378 379 static void __unused 380 putwait_locked(struct spclient *spc, struct respwait *rw, struct rsp_hdr *rhdr) 381 { 382 383 doputwait(spc, rw, rhdr); 384 pthread_mutex_unlock(&spc->spc_mtx); 385 } 386 387 static void 388 putwait(struct spclient *spc, struct respwait *rw, struct rsp_hdr *rhdr) 389 { 390 391 doputwait(spc, rw, rhdr); 392 sendlockl(spc); 393 pthread_mutex_unlock(&spc->spc_mtx); 394 } 395 396 static void 397 dounputwait(struct spclient *spc, struct respwait *rw) 398 { 399 400 TAILQ_REMOVE(&spc->spc_respwait, rw, rw_entries); 401 pthread_mutex_unlock(&spc->spc_mtx); 402 pthread_cond_destroy(&rw->rw_cv); 403 404 } 405 406 static void __unused 407 unputwait_locked(struct spclient *spc, struct respwait *rw) 408 { 409 410 pthread_mutex_lock(&spc->spc_mtx); 411 dounputwait(spc, rw); 412 } 413 414 static void 415 unputwait(struct spclient *spc, struct respwait *rw) 416 { 417 418 pthread_mutex_lock(&spc->spc_mtx); 419 sendunlockl(spc); 420 421 dounputwait(spc, rw); 422 } 423 424 static void 425 kickwaiter(struct spclient *spc) 426 { 427 struct respwait *rw; 428 int error = 0; 429 430 pthread_mutex_lock(&spc->spc_mtx); 431 TAILQ_FOREACH(rw, &spc->spc_respwait, rw_entries) { 432 if (rw->rw_reqno == spc->spc_hdr.rsp_reqno) 433 break; 434 } 435 if (rw == NULL) { 436 DPRINTF(("no waiter found, invalid reqno %" PRIu64 "?\n", 437 spc->spc_hdr.rsp_reqno)); 438 pthread_mutex_unlock(&spc->spc_mtx); 439 spcfreebuf(spc); 440 return; 441 } 442 DPRINTF(("rump_sp: client %p woke up waiter at %p\n", spc, rw)); 443 rw->rw_data = spc->spc_buf; 444 rw->rw_done = 1; 445 rw->rw_dlen = (size_t)(spc->spc_off - HDRSZ); 446 if (spc->spc_hdr.rsp_class == RUMPSP_ERROR) { 447 error = rw->rw_error = errmap(spc->spc_hdr.rsp_error); 448 } 449 pthread_cond_signal(&rw->rw_cv); 450 pthread_mutex_unlock(&spc->spc_mtx); 451 452 if (error) 453 spcfreebuf(spc); 454 else 455 spcresetbuf(spc); 456 } 457 458 static void 459 kickall(struct spclient *spc) 460 { 461 struct respwait *rw; 462 463 /* DIAGASSERT(mutex_owned(spc_lock)) */ 464 TAILQ_FOREACH(rw, &spc->spc_respwait, rw_entries) 465 pthread_cond_broadcast(&rw->rw_cv); 466 } 467 468 static int 469 readframe(struct spclient *spc) 470 { 471 int fd = spc->spc_fd; 472 size_t left; 473 size_t framelen; 474 ssize_t n; 475 476 /* still reading header? */ 477 if (spc->spc_off < HDRSZ) { 478 DPRINTF(("rump_sp: readframe getting header at offset %zu\n", 479 spc->spc_off)); 480 481 left = HDRSZ - spc->spc_off; 482 /*LINTED: cast ok */ 483 n = host_read(fd, (uint8_t*)&spc->spc_hdr + spc->spc_off, left); 484 if (n == 0) { 485 return -1; 486 } 487 if (n == -1) { 488 if (errno == EAGAIN) 489 return 0; 490 return -1; 491 } 492 493 spc->spc_off += n; 494 if (spc->spc_off < HDRSZ) { 495 return 0; 496 } 497 498 /*LINTED*/ 499 framelen = spc->spc_hdr.rsp_len; 500 501 if (framelen < HDRSZ) { 502 return -1; 503 } else if (framelen == HDRSZ) { 504 return 1; 505 } 506 507 /* Add an extra byte so that we are always NUL-terminated */ 508 spc->spc_buf = malloc(framelen - HDRSZ + 1); 509 if (spc->spc_buf == NULL) { 510 return -1; 511 } 512 memset(spc->spc_buf, 0, framelen - HDRSZ + 1); 513 514 /* "fallthrough" */ 515 } else { 516 /*LINTED*/ 517 framelen = spc->spc_hdr.rsp_len; 518 } 519 520 left = framelen - spc->spc_off; 521 522 DPRINTF(("rump_sp: readframe getting body at offset %zu, left %zu\n", 523 spc->spc_off, left)); 524 525 if (left == 0) 526 return 1; 527 n = host_read(fd, spc->spc_buf + (spc->spc_off - HDRSZ), left); 528 if (n == 0) { 529 return -1; 530 } 531 if (n == -1) { 532 if (errno == EAGAIN) 533 return 0; 534 return -1; 535 } 536 spc->spc_off += n; 537 left -= n; 538 539 /* got everything? */ 540 if (left == 0) 541 return 1; 542 else 543 return 0; 544 } 545 546 static int 547 tcp_parse(const char *addr, struct sockaddr **sa, int allow_wildcard) 548 { 549 struct sockaddr_in sin; 550 char buf[64]; 551 const char *p; 552 size_t l; 553 int port; 554 555 memset(&sin, 0, sizeof(sin)); 556 SIN_SETLEN(sin, sizeof(sin)); 557 sin.sin_family = AF_INET; 558 559 p = strchr(addr, ':'); 560 if (!p) { 561 fprintf(stderr, "rump_sp_tcp: missing port specifier\n"); 562 return EINVAL; 563 } 564 565 l = p - addr; 566 if (l > sizeof(buf)-1) { 567 fprintf(stderr, "rump_sp_tcp: address too long\n"); 568 return EINVAL; 569 } 570 strncpy(buf, addr, l); 571 buf[l] = '\0'; 572 573 /* special INADDR_ANY treatment */ 574 if (strcmp(buf, "*") == 0 || strcmp(buf, "0") == 0) { 575 sin.sin_addr.s_addr = INADDR_ANY; 576 } else { 577 switch (inet_pton(AF_INET, buf, &sin.sin_addr)) { 578 case 1: 579 break; 580 case 0: 581 fprintf(stderr, "rump_sp_tcp: cannot parse %s\n", buf); 582 return EINVAL; 583 case -1: 584 fprintf(stderr, "rump_sp_tcp: inet_pton failed\n"); 585 return errno; 586 default: 587 assert(/*CONSTCOND*/0); 588 return EINVAL; 589 } 590 } 591 592 if (!allow_wildcard && sin.sin_addr.s_addr == INADDR_ANY) { 593 fprintf(stderr, "rump_sp_tcp: client needs !INADDR_ANY\n"); 594 return EINVAL; 595 } 596 597 /* advance to port number & parse */ 598 p++; 599 l = strspn(p, "0123456789"); 600 if (l == 0) { 601 fprintf(stderr, "rump_sp_tcp: port now found: %s\n", p); 602 return EINVAL; 603 } 604 strncpy(buf, p, l); 605 buf[l] = '\0'; 606 607 if (*(p+l) != '/' && *(p+l) != '\0') { 608 fprintf(stderr, "rump_sp_tcp: junk at end of port: %s\n", addr); 609 return EINVAL; 610 } 611 612 port = atoi(buf); 613 if (port < 0 || port >= (1<<(8*sizeof(in_port_t)))) { 614 fprintf(stderr, "rump_sp_tcp: port %d out of range\n", port); 615 return ERANGE; 616 } 617 sin.sin_port = htons(port); 618 619 *sa = malloc(sizeof(sin)); 620 if (*sa == NULL) 621 return errno; 622 memcpy(*sa, &sin, sizeof(sin)); 623 return 0; 624 } 625 626 static int 627 tcp_connecthook(int s) 628 { 629 int x; 630 631 x = 1; 632 host_setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &x, sizeof(x)); 633 634 return 0; 635 } 636 637 static char parsedurl[256]; 638 639 /*ARGSUSED*/ 640 static int 641 unix_parse(const char *addr, struct sockaddr **sa, int allow_wildcard) 642 { 643 struct sockaddr_un s_un; 644 size_t slen; 645 int savepath = 0; 646 647 if (strlen(addr) >= sizeof(s_un.sun_path)) 648 return ENAMETOOLONG; 649 650 /* 651 * The pathname can be all kinds of spaghetti elementals, 652 * so meek and obidient we accept everything. However, use 653 * full path for easy cleanup in case someone gives a relative 654 * one and the server does a chdir() between now than the 655 * cleanup. 656 */ 657 memset(&s_un, 0, sizeof(s_un)); 658 s_un.sun_family = AF_LOCAL; 659 if (*addr != '/') { 660 char mywd[PATH_MAX]; 661 662 if (getcwd(mywd, sizeof(mywd)) == NULL) { 663 fprintf(stderr, "warning: cannot determine cwd, " 664 "omitting socket cleanup\n"); 665 } else { 666 if (strlen(addr)+strlen(mywd)+1 667 >= sizeof(s_un.sun_path)) 668 return ENAMETOOLONG; 669 strcpy(s_un.sun_path, mywd); 670 strcat(s_un.sun_path, "/"); 671 savepath = 1; 672 } 673 } 674 strcat(s_un.sun_path, addr); 675 #if !(defined(__linux__) || defined(__sun__) || defined(__CYGWIN__)) 676 s_un.sun_len = SUN_LEN(&s_un); 677 #endif 678 slen = sizeof(s_un); 679 680 if (savepath && *parsedurl == '\0') { 681 snprintf(parsedurl, sizeof(parsedurl), 682 "unix://%s", s_un.sun_path); 683 } 684 685 *sa = malloc(slen); 686 if (*sa == NULL) 687 return errno; 688 memcpy(*sa, &s_un, slen); 689 690 return 0; 691 } 692 693 static void 694 unix_cleanup(struct sockaddr *sa) 695 { 696 struct sockaddr_un *s_sun = (void *)sa; 697 698 /* 699 * cleanup only absolute paths. see unix_parse() above 700 */ 701 if (*s_sun->sun_path == '/') { 702 unlink(s_sun->sun_path); 703 } 704 } 705 706 /*ARGSUSED*/ 707 static int 708 addrparse_notsupp(const char *addr __unused, struct sockaddr **sa __unused, 709 int allow_wildcard __unused) 710 { 711 712 fprintf(stderr, "rump_sp: support not yet implemented\n"); 713 return EOPNOTSUPP; 714 } 715 716 static void 717 cleanup_success(struct sockaddr *sa __unused) 718 { 719 } 720 721 static int 722 connecthook_success(int s __unused) 723 { 724 725 return 0; 726 } 727 728 static struct { 729 const char *id; 730 int domain; 731 socklen_t slen; 732 addrparse_fn ap; 733 connecthook_fn connhook; 734 cleanup_fn cleanup; 735 } parsetab[] = { 736 { "tcp", PF_INET, sizeof(struct sockaddr_in), 737 tcp_parse, tcp_connecthook, cleanup_success }, 738 { "unix", PF_LOCAL, sizeof(struct sockaddr_un), 739 unix_parse, connecthook_success, unix_cleanup }, 740 { "tcp6", PF_INET6, sizeof(struct sockaddr_in6), 741 addrparse_notsupp, connecthook_success, 742 cleanup_success }, 743 }; 744 #define NPARSE (sizeof(parsetab)/sizeof(parsetab[0])) 745 746 static int 747 parseurl(const char *url, struct sockaddr **sap, unsigned *idxp, 748 int allow_wildcard) 749 { 750 char id[16]; 751 const char *p, *p2; 752 size_t l; 753 unsigned i; 754 int error; 755 756 /* 757 * Parse the url 758 */ 759 760 p = url; 761 p2 = strstr(p, "://"); 762 if (!p2) { 763 fprintf(stderr, "rump_sp: invalid locator ``%s''\n", p); 764 return EINVAL; 765 } 766 l = p2-p; 767 if (l > sizeof(id)-1) { 768 fprintf(stderr, "rump_sp: identifier too long in ``%s''\n", p); 769 return EINVAL; 770 } 771 772 strncpy(id, p, l); 773 id[l] = '\0'; 774 p2 += 3; /* beginning of address */ 775 776 for (i = 0; i < NPARSE; i++) { 777 if (strcmp(id, parsetab[i].id) == 0) { 778 error = parsetab[i].ap(p2, sap, allow_wildcard); 779 if (error) 780 return error; 781 break; 782 } 783 } 784 if (i == NPARSE) { 785 fprintf(stderr, "rump_sp: invalid identifier ``%s''\n", p); 786 return EINVAL; 787 } 788 789 *idxp = i; 790 return 0; 791 } 792