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