Annotation of embedaddon/rsync/socket.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Socket functions used in rsync.
! 3: *
! 4: * Copyright (C) 1992-2001 Andrew Tridgell <tridge@samba.org>
! 5: * Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
! 6: * Copyright (C) 2003-2009 Wayne Davison
! 7: *
! 8: * This program is free software; you can redistribute it and/or modify
! 9: * it under the terms of the GNU General Public License as published by
! 10: * the Free Software Foundation; either version 3 of the License, or
! 11: * (at your option) any later version.
! 12: *
! 13: * This program is distributed in the hope that it will be useful,
! 14: * but WITHOUT ANY WARRANTY; without even the implied warranty of
! 15: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
! 16: * GNU General Public License for more details.
! 17: *
! 18: * You should have received a copy of the GNU General Public License along
! 19: * with this program; if not, visit the http://fsf.org website.
! 20: */
! 21:
! 22: /* This file is now converted to use the new-style getaddrinfo()
! 23: * interface, which supports IPv6 but is also supported on recent
! 24: * IPv4-only machines. On systems that don't have that interface, we
! 25: * emulate it using the KAME implementation. */
! 26:
! 27: #include "rsync.h"
! 28: #include "ifuncs.h"
! 29: #ifdef HAVE_NETINET_IN_SYSTM_H
! 30: #include <netinet/in_systm.h>
! 31: #endif
! 32: #ifdef HAVE_NETINET_IP_H
! 33: #include <netinet/ip.h>
! 34: #endif
! 35: #include <netinet/tcp.h>
! 36:
! 37: extern char *bind_address;
! 38: extern char *sockopts;
! 39: extern int default_af_hint;
! 40: extern int connect_timeout;
! 41:
! 42: #ifdef HAVE_SIGACTION
! 43: static struct sigaction sigact;
! 44: #endif
! 45:
! 46: static int sock_exec(const char *prog);
! 47:
! 48: /* Establish a proxy connection on an open socket to a web proxy by using the
! 49: * CONNECT method. If proxy_user and proxy_pass are not NULL, they are used to
! 50: * authenticate to the proxy using the "Basic" proxy-authorization protocol. */
! 51: static int establish_proxy_connection(int fd, char *host, int port,
! 52: char *proxy_user, char *proxy_pass)
! 53: {
! 54: char *cp, buffer[1024];
! 55: char *authhdr, authbuf[1024];
! 56: int len;
! 57:
! 58: if (proxy_user && proxy_pass) {
! 59: stringjoin(buffer, sizeof buffer,
! 60: proxy_user, ":", proxy_pass, NULL);
! 61: len = strlen(buffer);
! 62:
! 63: if ((len*8 + 5) / 6 >= (int)sizeof authbuf - 3) {
! 64: rprintf(FERROR,
! 65: "authentication information is too long\n");
! 66: return -1;
! 67: }
! 68:
! 69: base64_encode(buffer, len, authbuf, 1);
! 70: authhdr = "\r\nProxy-Authorization: Basic ";
! 71: } else {
! 72: *authbuf = '\0';
! 73: authhdr = "";
! 74: }
! 75:
! 76: snprintf(buffer, sizeof buffer, "CONNECT %s:%d HTTP/1.0%s%s\r\n\r\n",
! 77: host, port, authhdr, authbuf);
! 78: len = strlen(buffer);
! 79: if (write(fd, buffer, len) != len) {
! 80: rsyserr(FERROR, errno, "failed to write to proxy");
! 81: return -1;
! 82: }
! 83:
! 84: for (cp = buffer; cp < &buffer[sizeof buffer - 1]; cp++) {
! 85: if (read(fd, cp, 1) != 1) {
! 86: rsyserr(FERROR, errno, "failed to read from proxy");
! 87: return -1;
! 88: }
! 89: if (*cp == '\n')
! 90: break;
! 91: }
! 92:
! 93: if (*cp != '\n')
! 94: cp++;
! 95: *cp-- = '\0';
! 96: if (*cp == '\r')
! 97: *cp = '\0';
! 98: if (strncmp(buffer, "HTTP/", 5) != 0) {
! 99: rprintf(FERROR, "bad response from proxy -- %s\n",
! 100: buffer);
! 101: return -1;
! 102: }
! 103: for (cp = &buffer[5]; isDigit(cp) || *cp == '.'; cp++) {}
! 104: while (*cp == ' ')
! 105: cp++;
! 106: if (*cp != '2') {
! 107: rprintf(FERROR, "bad response from proxy -- %s\n",
! 108: buffer);
! 109: return -1;
! 110: }
! 111: /* throw away the rest of the HTTP header */
! 112: while (1) {
! 113: for (cp = buffer; cp < &buffer[sizeof buffer - 1]; cp++) {
! 114: if (read(fd, cp, 1) != 1) {
! 115: rsyserr(FERROR, errno,
! 116: "failed to read from proxy");
! 117: return -1;
! 118: }
! 119: if (*cp == '\n')
! 120: break;
! 121: }
! 122: if (cp > buffer && *cp == '\n')
! 123: cp--;
! 124: if (cp == buffer && (*cp == '\n' || *cp == '\r'))
! 125: break;
! 126: }
! 127: return 0;
! 128: }
! 129:
! 130:
! 131: /* Try to set the local address for a newly-created socket.
! 132: * Return -1 if this fails. */
! 133: int try_bind_local(int s, int ai_family, int ai_socktype,
! 134: const char *bind_addr)
! 135: {
! 136: int error;
! 137: struct addrinfo bhints, *bres_all, *r;
! 138:
! 139: memset(&bhints, 0, sizeof bhints);
! 140: bhints.ai_family = ai_family;
! 141: bhints.ai_socktype = ai_socktype;
! 142: bhints.ai_flags = AI_PASSIVE;
! 143: if ((error = getaddrinfo(bind_addr, NULL, &bhints, &bres_all))) {
! 144: rprintf(FERROR, RSYNC_NAME ": getaddrinfo %s: %s\n",
! 145: bind_addr, gai_strerror(error));
! 146: return -1;
! 147: }
! 148:
! 149: for (r = bres_all; r; r = r->ai_next) {
! 150: if (bind(s, r->ai_addr, r->ai_addrlen) == -1)
! 151: continue;
! 152: freeaddrinfo(bres_all);
! 153: return s;
! 154: }
! 155:
! 156: /* no error message; there might be some problem that allows
! 157: * creation of the socket but not binding, perhaps if the
! 158: * machine has no ipv6 address of this name. */
! 159: freeaddrinfo(bres_all);
! 160: return -1;
! 161: }
! 162:
! 163: /* connect() timeout handler based on alarm() */
! 164: static RETSIGTYPE contimeout_handler(UNUSED(int val))
! 165: {
! 166: connect_timeout = -1;
! 167: }
! 168:
! 169: /* Open a socket to a tcp remote host with the specified port.
! 170: *
! 171: * Based on code from Warren. Proxy support by Stephen Rothwell.
! 172: * getaddrinfo() rewrite contributed by KAME.net.
! 173: *
! 174: * Now that we support IPv6 we need to look up the remote machine's address
! 175: * first, using af_hint to set a preference for the type of address. Then
! 176: * depending on whether it has v4 or v6 addresses we try to open a connection.
! 177: *
! 178: * The loop allows for machines with some addresses which may not be reachable,
! 179: * perhaps because we can't e.g. route ipv6 to that network but we can get ip4
! 180: * packets through.
! 181: *
! 182: * bind_addr: local address to use. Normally NULL to bind the wildcard address.
! 183: *
! 184: * af_hint: address family, e.g. AF_INET or AF_INET6. */
! 185: int open_socket_out(char *host, int port, const char *bind_addr,
! 186: int af_hint)
! 187: {
! 188: int type = SOCK_STREAM;
! 189: int error, s, j, addr_cnt, *errnos;
! 190: struct addrinfo hints, *res0, *res;
! 191: char portbuf[10];
! 192: char *h, *cp;
! 193: int proxied = 0;
! 194: char buffer[1024];
! 195: char *proxy_user = NULL, *proxy_pass = NULL;
! 196:
! 197: /* if we have a RSYNC_PROXY env variable then redirect our
! 198: * connetcion via a web proxy at the given address. */
! 199: h = getenv("RSYNC_PROXY");
! 200: proxied = h != NULL && *h != '\0';
! 201:
! 202: if (proxied) {
! 203: strlcpy(buffer, h, sizeof buffer);
! 204:
! 205: /* Is the USER:PASS@ prefix present? */
! 206: if ((cp = strrchr(buffer, '@')) != NULL) {
! 207: *cp++ = '\0';
! 208: /* The remainder is the HOST:PORT part. */
! 209: h = cp;
! 210:
! 211: if ((cp = strchr(buffer, ':')) == NULL) {
! 212: rprintf(FERROR,
! 213: "invalid proxy specification: should be USER:PASS@HOST:PORT\n");
! 214: return -1;
! 215: }
! 216: *cp++ = '\0';
! 217:
! 218: proxy_user = buffer;
! 219: proxy_pass = cp;
! 220: } else {
! 221: /* The whole buffer is the HOST:PORT part. */
! 222: h = buffer;
! 223: }
! 224:
! 225: if ((cp = strchr(h, ':')) == NULL) {
! 226: rprintf(FERROR,
! 227: "invalid proxy specification: should be HOST:PORT\n");
! 228: return -1;
! 229: }
! 230: *cp++ = '\0';
! 231: strlcpy(portbuf, cp, sizeof portbuf);
! 232: if (verbose >= 2) {
! 233: rprintf(FINFO, "connection via http proxy %s port %s\n",
! 234: h, portbuf);
! 235: }
! 236: } else {
! 237: snprintf(portbuf, sizeof portbuf, "%d", port);
! 238: h = host;
! 239: }
! 240:
! 241: memset(&hints, 0, sizeof hints);
! 242: hints.ai_family = af_hint;
! 243: hints.ai_socktype = type;
! 244: error = getaddrinfo(h, portbuf, &hints, &res0);
! 245: if (error) {
! 246: rprintf(FERROR, RSYNC_NAME ": getaddrinfo: %s %s: %s\n",
! 247: h, portbuf, gai_strerror(error));
! 248: return -1;
! 249: }
! 250:
! 251: for (res = res0, addr_cnt = 0; res; res = res->ai_next, addr_cnt++) {}
! 252: errnos = new_array0(int, addr_cnt);
! 253: if (!errnos)
! 254: out_of_memory("open_socket_out");
! 255:
! 256: s = -1;
! 257: /* Try to connect to all addresses for this machine until we get
! 258: * through. It might e.g. be multi-homed, or have both IPv4 and IPv6
! 259: * addresses. We need to create a socket for each record, since the
! 260: * address record tells us what protocol to use to try to connect. */
! 261: for (res = res0, j = 0; res; res = res->ai_next, j++) {
! 262: s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
! 263: if (s < 0)
! 264: continue;
! 265:
! 266: if (bind_addr
! 267: && try_bind_local(s, res->ai_family, type,
! 268: bind_addr) == -1) {
! 269: close(s);
! 270: s = -1;
! 271: continue;
! 272: }
! 273: if (connect_timeout > 0) {
! 274: SIGACTION(SIGALRM, contimeout_handler);
! 275: alarm(connect_timeout);
! 276: }
! 277:
! 278: set_socket_options(s, sockopts);
! 279: while (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
! 280: if (connect_timeout < 0)
! 281: exit_cleanup(RERR_CONTIMEOUT);
! 282: if (errno == EINTR)
! 283: continue;
! 284: close(s);
! 285: s = -1;
! 286: break;
! 287: }
! 288:
! 289: if (connect_timeout > 0)
! 290: alarm(0);
! 291:
! 292: if (s < 0) {
! 293: errnos[j] = errno;
! 294: continue;
! 295: }
! 296:
! 297: if (proxied
! 298: && establish_proxy_connection(s, host, port,
! 299: proxy_user, proxy_pass) != 0) {
! 300: close(s);
! 301: s = -1;
! 302: continue;
! 303: }
! 304: if (verbose >= 3) {
! 305: char buf[2048];
! 306: if ((error = getnameinfo(res->ai_addr, res->ai_addrlen, buf, sizeof buf, NULL, 0, NI_NUMERICHOST)) != 0)
! 307: snprintf(buf, sizeof buf, "*getnameinfo failure: %s*", gai_strerror(error));
! 308: rprintf(FINFO, "Connected to %s (%s)\n", h, buf);
! 309: }
! 310: break;
! 311: }
! 312:
! 313: if (s < 0 || verbose >= 3) {
! 314: char buf[2048];
! 315: for (res = res0, j = 0; res; res = res->ai_next, j++) {
! 316: if (errnos[j] == 0)
! 317: continue;
! 318: if ((error = getnameinfo(res->ai_addr, res->ai_addrlen, buf, sizeof buf, NULL, 0, NI_NUMERICHOST)) != 0)
! 319: snprintf(buf, sizeof buf, "*getnameinfo failure: %s*", gai_strerror(error));
! 320: rsyserr(FERROR, errnos[j], "failed to connect to %s (%s)", h, buf);
! 321: }
! 322: if (s < 0)
! 323: s = -1;
! 324: }
! 325:
! 326: freeaddrinfo(res0);
! 327: free(errnos);
! 328:
! 329: return s;
! 330: }
! 331:
! 332:
! 333: /* Open an outgoing socket, but allow for it to be intercepted by
! 334: * $RSYNC_CONNECT_PROG, which will execute a program across a TCP
! 335: * socketpair rather than really opening a socket.
! 336: *
! 337: * We use this primarily in testing to detect TCP flow bugs, but not
! 338: * cause security problems by really opening remote connections.
! 339: *
! 340: * This is based on the Samba LIBSMB_PROG feature.
! 341: *
! 342: * bind_addr: local address to use. Normally NULL to get the stack default. */
! 343: int open_socket_out_wrapped(char *host, int port, const char *bind_addr,
! 344: int af_hint)
! 345: {
! 346: char *prog = getenv("RSYNC_CONNECT_PROG");
! 347:
! 348: if (prog && strchr(prog, '%')) {
! 349: int hlen = strlen(host);
! 350: int len = strlen(prog) + 1;
! 351: char *f, *t;
! 352: for (f = prog; *f; f++) {
! 353: if (*f != '%')
! 354: continue;
! 355: /* Compute more than enough room. */
! 356: if (f[1] == '%')
! 357: f++;
! 358: else
! 359: len += hlen;
! 360: }
! 361: f = prog;
! 362: if (!(prog = new_array(char, len)))
! 363: out_of_memory("open_socket_out_wrapped");
! 364: for (t = prog; *f; f++) {
! 365: if (*f == '%') {
! 366: switch (*++f) {
! 367: case '%':
! 368: /* Just skips the extra '%'. */
! 369: break;
! 370: case 'H':
! 371: memcpy(t, host, hlen);
! 372: t += hlen;
! 373: continue;
! 374: default:
! 375: f--; /* pass % through */
! 376: break;
! 377: }
! 378: }
! 379: *t++ = *f;
! 380: }
! 381: *t = '\0';
! 382: }
! 383:
! 384: if (verbose >= 2) {
! 385: rprintf(FINFO, "%sopening tcp connection to %s port %d\n",
! 386: prog ? "Using RSYNC_CONNECT_PROG instead of " : "",
! 387: host, port);
! 388: }
! 389: if (prog)
! 390: return sock_exec(prog);
! 391: return open_socket_out(host, port, bind_addr, af_hint);
! 392: }
! 393:
! 394:
! 395: /* Open one or more sockets for incoming data using the specified type,
! 396: * port, and address.
! 397: *
! 398: * The getaddrinfo() call may return several address results, e.g. for
! 399: * the machine's IPv4 and IPv6 name.
! 400: *
! 401: * We return an array of file-descriptors to the sockets, with a trailing
! 402: * -1 value to indicate the end of the list.
! 403: *
! 404: * bind_addr: local address to bind, or NULL to allow it to default. */
! 405: static int *open_socket_in(int type, int port, const char *bind_addr,
! 406: int af_hint)
! 407: {
! 408: int one = 1;
! 409: int s, *socks, maxs, i, ecnt;
! 410: struct addrinfo hints, *all_ai, *resp;
! 411: char portbuf[10], **errmsgs;
! 412: int error;
! 413:
! 414: memset(&hints, 0, sizeof hints);
! 415: hints.ai_family = af_hint;
! 416: hints.ai_socktype = type;
! 417: hints.ai_flags = AI_PASSIVE;
! 418: snprintf(portbuf, sizeof portbuf, "%d", port);
! 419: error = getaddrinfo(bind_addr, portbuf, &hints, &all_ai);
! 420: if (error) {
! 421: rprintf(FERROR, RSYNC_NAME ": getaddrinfo: bind address %s: %s\n",
! 422: bind_addr, gai_strerror(error));
! 423: return NULL;
! 424: }
! 425:
! 426: /* Count max number of sockets we might open. */
! 427: for (maxs = 0, resp = all_ai; resp; resp = resp->ai_next, maxs++) {}
! 428:
! 429: socks = new_array(int, maxs + 1);
! 430: errmsgs = new_array(char *, maxs);
! 431: if (!socks || !errmsgs)
! 432: out_of_memory("open_socket_in");
! 433:
! 434: /* We may not be able to create the socket, if for example the
! 435: * machine knows about IPv6 in the C library, but not in the
! 436: * kernel. */
! 437: for (resp = all_ai, i = ecnt = 0; resp; resp = resp->ai_next) {
! 438: s = socket(resp->ai_family, resp->ai_socktype,
! 439: resp->ai_protocol);
! 440:
! 441: if (s == -1) {
! 442: int r = asprintf(&errmsgs[ecnt++],
! 443: "socket(%d,%d,%d) failed: %s\n",
! 444: (int)resp->ai_family, (int)resp->ai_socktype,
! 445: (int)resp->ai_protocol, strerror(errno));
! 446: if (r < 0)
! 447: out_of_memory("open_socket_in");
! 448: /* See if there's another address that will work... */
! 449: continue;
! 450: }
! 451:
! 452: setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
! 453: (char *)&one, sizeof one);
! 454: if (sockopts)
! 455: set_socket_options(s, sockopts);
! 456: else
! 457: set_socket_options(s, lp_socket_options());
! 458:
! 459: #ifdef IPV6_V6ONLY
! 460: if (resp->ai_family == AF_INET6) {
! 461: if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
! 462: (char *)&one, sizeof one) < 0
! 463: && default_af_hint != AF_INET6) {
! 464: close(s);
! 465: continue;
! 466: }
! 467: }
! 468: #endif
! 469:
! 470: /* Now we've got a socket - we need to bind it. */
! 471: if (bind(s, resp->ai_addr, resp->ai_addrlen) < 0) {
! 472: /* Nope, try another */
! 473: int r = asprintf(&errmsgs[ecnt++],
! 474: "bind() failed: %s (address-family %d)\n",
! 475: strerror(errno), (int)resp->ai_family);
! 476: if (r < 0)
! 477: out_of_memory("open_socket_in");
! 478: close(s);
! 479: continue;
! 480: }
! 481:
! 482: socks[i++] = s;
! 483: }
! 484: socks[i] = -1;
! 485:
! 486: if (all_ai)
! 487: freeaddrinfo(all_ai);
! 488:
! 489: /* Only output the socket()/bind() messages if we were totally
! 490: * unsuccessful, or if the daemon is being run with -vv. */
! 491: for (s = 0; s < ecnt; s++) {
! 492: if (!i || verbose > 1)
! 493: rwrite(FLOG, errmsgs[s], strlen(errmsgs[s]), 0);
! 494: free(errmsgs[s]);
! 495: }
! 496: free(errmsgs);
! 497:
! 498: if (!i) {
! 499: rprintf(FERROR,
! 500: "unable to bind any inbound sockets on port %d\n",
! 501: port);
! 502: free(socks);
! 503: return NULL;
! 504: }
! 505: return socks;
! 506: }
! 507:
! 508:
! 509: /* Determine if a file descriptor is in fact a socket. */
! 510: int is_a_socket(int fd)
! 511: {
! 512: int v;
! 513: socklen_t l = sizeof (int);
! 514:
! 515: /* Parameters to getsockopt, setsockopt etc are very
! 516: * unstandardized across platforms, so don't be surprised if
! 517: * there are compiler warnings on e.g. SCO OpenSwerver or AIX.
! 518: * It seems they all eventually get the right idea.
! 519: *
! 520: * Debian says: ``The fifth argument of getsockopt and
! 521: * setsockopt is in reality an int [*] (and this is what BSD
! 522: * 4.* and libc4 and libc5 have). Some POSIX confusion
! 523: * resulted in the present socklen_t. The draft standard has
! 524: * not been adopted yet, but glibc2 already follows it and
! 525: * also has socklen_t [*]. See also accept(2).''
! 526: *
! 527: * We now return to your regularly scheduled programming. */
! 528: return getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&v, &l) == 0;
! 529: }
! 530:
! 531:
! 532: static RETSIGTYPE sigchld_handler(UNUSED(int val))
! 533: {
! 534: #ifdef WNOHANG
! 535: while (waitpid(-1, NULL, WNOHANG) > 0) {}
! 536: #endif
! 537: #ifndef HAVE_SIGACTION
! 538: signal(SIGCHLD, sigchld_handler);
! 539: #endif
! 540: }
! 541:
! 542:
! 543: void start_accept_loop(int port, int (*fn)(int, int))
! 544: {
! 545: fd_set deffds;
! 546: int *sp, maxfd, i;
! 547:
! 548: #ifdef HAVE_SIGACTION
! 549: sigact.sa_flags = SA_NOCLDSTOP;
! 550: #endif
! 551:
! 552: /* open an incoming socket */
! 553: sp = open_socket_in(SOCK_STREAM, port, bind_address, default_af_hint);
! 554: if (sp == NULL)
! 555: exit_cleanup(RERR_SOCKETIO);
! 556:
! 557: /* ready to listen */
! 558: FD_ZERO(&deffds);
! 559: for (i = 0, maxfd = -1; sp[i] >= 0; i++) {
! 560: if (listen(sp[i], 5) < 0) {
! 561: rsyserr(FERROR, errno, "listen() on socket failed");
! 562: #ifdef INET6
! 563: if (errno == EADDRINUSE && i > 0) {
! 564: rprintf(FINFO,
! 565: "Try using --ipv4 or --ipv6 to avoid this listen() error.\n");
! 566: }
! 567: #endif
! 568: exit_cleanup(RERR_SOCKETIO);
! 569: }
! 570: FD_SET(sp[i], &deffds);
! 571: if (maxfd < sp[i])
! 572: maxfd = sp[i];
! 573: }
! 574:
! 575: /* now accept incoming connections - forking a new process
! 576: * for each incoming connection */
! 577: while (1) {
! 578: fd_set fds;
! 579: pid_t pid;
! 580: int fd;
! 581: struct sockaddr_storage addr;
! 582: socklen_t addrlen = sizeof addr;
! 583:
! 584: /* close log file before the potentially very long select so
! 585: * file can be trimmed by another process instead of growing
! 586: * forever */
! 587: logfile_close();
! 588:
! 589: #ifdef FD_COPY
! 590: FD_COPY(&deffds, &fds);
! 591: #else
! 592: fds = deffds;
! 593: #endif
! 594:
! 595: if (select(maxfd + 1, &fds, NULL, NULL, NULL) < 1)
! 596: continue;
! 597:
! 598: for (i = 0, fd = -1; sp[i] >= 0; i++) {
! 599: if (FD_ISSET(sp[i], &fds)) {
! 600: fd = accept(sp[i], (struct sockaddr *)&addr,
! 601: &addrlen);
! 602: break;
! 603: }
! 604: }
! 605:
! 606: if (fd < 0)
! 607: continue;
! 608:
! 609: SIGACTION(SIGCHLD, sigchld_handler);
! 610:
! 611: if ((pid = fork()) == 0) {
! 612: int ret;
! 613: for (i = 0; sp[i] >= 0; i++)
! 614: close(sp[i]);
! 615: /* Re-open log file in child before possibly giving
! 616: * up privileges (see logfile_close() above). */
! 617: logfile_reopen();
! 618: ret = fn(fd, fd);
! 619: close_all();
! 620: _exit(ret);
! 621: } else if (pid < 0) {
! 622: rsyserr(FERROR, errno,
! 623: "could not create child server process");
! 624: close(fd);
! 625: /* This might have happened because we're
! 626: * overloaded. Sleep briefly before trying to
! 627: * accept again. */
! 628: sleep(2);
! 629: } else {
! 630: /* Parent doesn't need this fd anymore. */
! 631: close(fd);
! 632: }
! 633: }
! 634: }
! 635:
! 636:
! 637: enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON};
! 638:
! 639: struct
! 640: {
! 641: char *name;
! 642: int level;
! 643: int option;
! 644: int value;
! 645: int opttype;
! 646: } socket_options[] = {
! 647: {"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, 0, OPT_BOOL},
! 648: {"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, 0, OPT_BOOL},
! 649: #ifdef SO_BROADCAST
! 650: {"SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, 0, OPT_BOOL},
! 651: #endif
! 652: #ifdef TCP_NODELAY
! 653: {"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, 0, OPT_BOOL},
! 654: #endif
! 655: #ifdef IPTOS_LOWDELAY
! 656: {"IPTOS_LOWDELAY", IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY, OPT_ON},
! 657: #endif
! 658: #ifdef IPTOS_THROUGHPUT
! 659: {"IPTOS_THROUGHPUT", IPPROTO_IP, IP_TOS, IPTOS_THROUGHPUT, OPT_ON},
! 660: #endif
! 661: #ifdef SO_SNDBUF
! 662: {"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, 0, OPT_INT},
! 663: #endif
! 664: #ifdef SO_RCVBUF
! 665: {"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, 0, OPT_INT},
! 666: #endif
! 667: #ifdef SO_SNDLOWAT
! 668: {"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, 0, OPT_INT},
! 669: #endif
! 670: #ifdef SO_RCVLOWAT
! 671: {"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, 0, OPT_INT},
! 672: #endif
! 673: #ifdef SO_SNDTIMEO
! 674: {"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, 0, OPT_INT},
! 675: #endif
! 676: #ifdef SO_RCVTIMEO
! 677: {"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, 0, OPT_INT},
! 678: #endif
! 679: {NULL,0,0,0,0}
! 680: };
! 681:
! 682:
! 683: /* Set user socket options. */
! 684: void set_socket_options(int fd, char *options)
! 685: {
! 686: char *tok;
! 687:
! 688: if (!options || !*options)
! 689: return;
! 690:
! 691: options = strdup(options);
! 692:
! 693: if (!options)
! 694: out_of_memory("set_socket_options");
! 695:
! 696: for (tok = strtok(options, " \t,"); tok; tok = strtok(NULL," \t,")) {
! 697: int ret=0,i;
! 698: int value = 1;
! 699: char *p;
! 700: int got_value = 0;
! 701:
! 702: if ((p = strchr(tok,'='))) {
! 703: *p = 0;
! 704: value = atoi(p+1);
! 705: got_value = 1;
! 706: }
! 707:
! 708: for (i = 0; socket_options[i].name; i++) {
! 709: if (strcmp(socket_options[i].name,tok)==0)
! 710: break;
! 711: }
! 712:
! 713: if (!socket_options[i].name) {
! 714: rprintf(FERROR,"Unknown socket option %s\n",tok);
! 715: continue;
! 716: }
! 717:
! 718: switch (socket_options[i].opttype) {
! 719: case OPT_BOOL:
! 720: case OPT_INT:
! 721: ret = setsockopt(fd,socket_options[i].level,
! 722: socket_options[i].option,
! 723: (char *)&value, sizeof (int));
! 724: break;
! 725:
! 726: case OPT_ON:
! 727: if (got_value)
! 728: rprintf(FERROR,"syntax error -- %s does not take a value\n",tok);
! 729:
! 730: {
! 731: int on = socket_options[i].value;
! 732: ret = setsockopt(fd,socket_options[i].level,
! 733: socket_options[i].option,
! 734: (char *)&on, sizeof (int));
! 735: }
! 736: break;
! 737: }
! 738:
! 739: if (ret != 0) {
! 740: rsyserr(FERROR, errno,
! 741: "failed to set socket option %s", tok);
! 742: }
! 743: }
! 744:
! 745: free(options);
! 746: }
! 747:
! 748:
! 749: /* This is like socketpair but uses tcp. The function guarantees that nobody
! 750: * else can attach to the socket, or if they do that this function fails and
! 751: * the socket gets closed. Returns 0 on success, -1 on failure. The resulting
! 752: * file descriptors are symmetrical. Currently only for RSYNC_CONNECT_PROG. */
! 753: static int socketpair_tcp(int fd[2])
! 754: {
! 755: int listener;
! 756: struct sockaddr_in sock;
! 757: struct sockaddr_in sock2;
! 758: socklen_t socklen = sizeof sock;
! 759: int connect_done = 0;
! 760:
! 761: fd[0] = fd[1] = listener = -1;
! 762:
! 763: memset(&sock, 0, sizeof sock);
! 764:
! 765: if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1)
! 766: goto failed;
! 767:
! 768: memset(&sock2, 0, sizeof sock2);
! 769: #ifdef HAVE_SOCKADDR_IN_LEN
! 770: sock2.sin_len = sizeof sock2;
! 771: #endif
! 772: sock2.sin_family = PF_INET;
! 773: sock2.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
! 774:
! 775: if (bind(listener, (struct sockaddr *)&sock2, sizeof sock2) != 0
! 776: || listen(listener, 1) != 0
! 777: || getsockname(listener, (struct sockaddr *)&sock, &socklen) != 0
! 778: || (fd[1] = socket(PF_INET, SOCK_STREAM, 0)) == -1)
! 779: goto failed;
! 780:
! 781: set_nonblocking(fd[1]);
! 782:
! 783: sock.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
! 784:
! 785: if (connect(fd[1], (struct sockaddr *)&sock, sizeof sock) == -1) {
! 786: if (errno != EINPROGRESS)
! 787: goto failed;
! 788: } else
! 789: connect_done = 1;
! 790:
! 791: if ((fd[0] = accept(listener, (struct sockaddr *)&sock2, &socklen)) == -1)
! 792: goto failed;
! 793:
! 794: close(listener);
! 795: listener = -1;
! 796:
! 797: set_blocking(fd[1]);
! 798:
! 799: if (connect_done == 0) {
! 800: if (connect(fd[1], (struct sockaddr *)&sock, sizeof sock) != 0
! 801: && errno != EISCONN)
! 802: goto failed;
! 803: }
! 804:
! 805: /* all OK! */
! 806: return 0;
! 807:
! 808: failed:
! 809: if (fd[0] != -1)
! 810: close(fd[0]);
! 811: if (fd[1] != -1)
! 812: close(fd[1]);
! 813: if (listener != -1)
! 814: close(listener);
! 815: return -1;
! 816: }
! 817:
! 818:
! 819: /* Run a program on a local tcp socket, so that we can talk to it's stdin and
! 820: * stdout. This is used to fake a connection to a daemon for testing -- not
! 821: * for the normal case of running SSH.
! 822: *
! 823: * Retruns a socket which is attached to a subprocess running "prog". stdin and
! 824: * stdout are attached. stderr is left attached to the original stderr. */
! 825: static int sock_exec(const char *prog)
! 826: {
! 827: pid_t pid;
! 828: int fd[2];
! 829:
! 830: if (socketpair_tcp(fd) != 0) {
! 831: rsyserr(FERROR, errno, "socketpair_tcp failed");
! 832: return -1;
! 833: }
! 834: if (verbose >= 2)
! 835: rprintf(FINFO, "Running socket program: \"%s\"\n", prog);
! 836:
! 837: pid = fork();
! 838: if (pid < 0) {
! 839: rsyserr(FERROR, errno, "fork");
! 840: exit_cleanup(RERR_IPC);
! 841: }
! 842:
! 843: if (pid == 0) {
! 844: close(fd[0]);
! 845: if (dup2(fd[1], STDIN_FILENO) < 0
! 846: || dup2(fd[1], STDOUT_FILENO) < 0) {
! 847: fprintf(stderr, "Failed to run \"%s\"\n", prog);
! 848: exit(1);
! 849: }
! 850: exit(system(prog));
! 851: }
! 852:
! 853: close(fd[1]);
! 854: return fd[0];
! 855: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>