X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/5c6d46329b2c83ec0bbb8e6344d63517c8d8eec6..9541770faf6cdb2851a39fa5feaa067a6e0d751a:/socket.c diff --git a/socket.c b/socket.c index 9079093c..fcb2a3be 100644 --- a/socket.c +++ b/socket.c @@ -1,51 +1,49 @@ -/* -*- c-file-style: "linux" -*- - - rsync -- fast file replication program - - Copyright (C) 1992-2001 by Andrew Tridgell - Copyright (C) 2001, 2002 by Martin Pool - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -/** - * @file socket.c - * +/* * Socket functions used in rsync. * - * This file is now converted to use the new-style getaddrinfo() + * Copyright (C) 1992-2001 Andrew Tridgell + * Copyright (C) 2001, 2002 Martin Pool + * Copyright (C) 2003-2009 Wayne Davison + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, visit the http://fsf.org website. + */ + +/* This file is now converted to use the new-style getaddrinfo() * interface, which supports IPv6 but is also supported on recent * IPv4-only machines. On systems that don't have that interface, we - * emulate it using the KAME implementation. - **/ + * emulate it using the KAME implementation. */ #include "rsync.h" +#include "itypes.h" +#include +#include +#include extern char *bind_address; +extern char *sockopts; extern int default_af_hint; +extern int connect_timeout; #ifdef HAVE_SIGACTION static struct sigaction sigact; #endif -/** - * Establish a proxy connection on an open socket to a web proxy by - * using the CONNECT method. If proxy_user and proxy_pass are not NULL, - * they are used to authenticate to the proxy using the "Basic" - * proxy-authorization protocol - **/ +static int sock_exec(const char *prog); + +/* Establish a proxy connection on an open socket to a web proxy by using the + * CONNECT method. If proxy_user and proxy_pass are not NULL, they are used to + * authenticate to the proxy using the "Basic" proxy-authorization protocol. */ static int establish_proxy_connection(int fd, char *host, int port, char *proxy_user, char *proxy_pass) { @@ -98,7 +96,7 @@ static int establish_proxy_connection(int fd, char *host, int port, buffer); return -1; } - for (cp = &buffer[5]; isdigit(*(uchar*)cp) || *cp == '.'; cp++) {} + for (cp = &buffer[5]; isDigit(cp) || *cp == '.'; cp++) {} while (*cp == ' ') cp++; if (*cp != '2') { @@ -126,10 +124,8 @@ static int establish_proxy_connection(int fd, char *host, int port, } -/** - * Try to set the local address for a newly-created socket. Return -1 - * if this fails. - **/ +/* Try to set the local address for a newly-created socket. + * Return -1 if this fails. */ int try_bind_local(int s, int ai_family, int ai_socktype, const char *bind_addr) { @@ -160,27 +156,28 @@ int try_bind_local(int s, int ai_family, int ai_socktype, return -1; } +/* connect() timeout handler based on alarm() */ +static RETSIGTYPE contimeout_handler(UNUSED(int val)) +{ + connect_timeout = -1; +} -/** - * Open a socket to a tcp remote host with the specified port . +/* Open a socket to a tcp remote host with the specified port. * * Based on code from Warren. Proxy support by Stephen Rothwell. * getaddrinfo() rewrite contributed by KAME.net. * - * Now that we support IPv6 we need to look up the remote machine's - * address first, using @p af_hint to set a preference for the type - * of address. Then depending on whether it has v4 or v6 addresses we - * try to open a connection. + * Now that we support IPv6 we need to look up the remote machine's address + * first, using af_hint to set a preference for the type of address. Then + * depending on whether it has v4 or v6 addresses we try to open a connection. * - * The loop allows for machines with some addresses which may not be - * reachable, perhaps because we can't e.g. route ipv6 to that network - * but we can get ip4 packets through. + * The loop allows for machines with some addresses which may not be reachable, + * perhaps because we can't e.g. route ipv6 to that network but we can get ip4 + * packets through. * - * @param bind_addr Local address to use. Normally NULL to bind - * the wildcard address. + * bind_addr: local address to use. Normally NULL to bind the wildcard address. * - * @param af_hint Address family, e.g. AF_INET or AF_INET6. - **/ + * af_hint: address family, e.g. AF_INET or AF_INET6. */ int open_socket_out(char *host, int port, const char *bind_addr, int af_hint) { @@ -228,7 +225,7 @@ int open_socket_out(char *host, int port, const char *bind_addr, } *cp++ = '\0'; strlcpy(portbuf, cp, sizeof portbuf); - if (verbose >= 2) { + if (DEBUG_GTE(CONNECT, 1)) { rprintf(FINFO, "connection via http proxy %s port %s\n", h, portbuf); } @@ -264,11 +261,28 @@ int open_socket_out(char *host, int port, const char *bind_addr, s = -1; continue; } - if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { + if (connect_timeout > 0) { + SIGACTION(SIGALRM, contimeout_handler); + alarm(connect_timeout); + } + + set_socket_options(s, sockopts); + while (connect(s, res->ai_addr, res->ai_addrlen) < 0) { + if (connect_timeout < 0) + exit_cleanup(RERR_CONTIMEOUT); + if (errno == EINTR) + continue; close(s); s = -1; - continue; + break; } + + if (connect_timeout > 0) + alarm(0); + + if (s < 0) + continue; + if (proxied && establish_proxy_connection(s, host, port, proxy_user, proxy_pass) != 0) { @@ -287,8 +301,7 @@ int open_socket_out(char *host, int port, const char *bind_addr, } -/** - * Open an outgoing socket, but allow for it to be intercepted by +/* Open an outgoing socket, but allow for it to be intercepted by * $RSYNC_CONNECT_PROG, which will execute a program across a TCP * socketpair rather than really opening a socket. * @@ -297,14 +310,49 @@ int open_socket_out(char *host, int port, const char *bind_addr, * * This is based on the Samba LIBSMB_PROG feature. * - * @param bind_addr Local address to use. Normally NULL to get the stack default. - **/ + * bind_addr: local address to use. Normally NULL to get the stack default. */ int open_socket_out_wrapped(char *host, int port, const char *bind_addr, int af_hint) { char *prog = getenv("RSYNC_CONNECT_PROG"); - if (verbose >= 2) { + if (prog && strchr(prog, '%')) { + int hlen = strlen(host); + int len = strlen(prog) + 1; + char *f, *t; + for (f = prog; *f; f++) { + if (*f != '%') + continue; + /* Compute more than enough room. */ + if (f[1] == '%') + f++; + else + len += hlen; + } + f = prog; + if (!(prog = new_array(char, len))) + out_of_memory("open_socket_out_wrapped"); + for (t = prog; *f; f++) { + if (*f == '%') { + switch (*++f) { + case '%': + /* Just skips the extra '%'. */ + break; + case 'H': + memcpy(t, host, hlen); + t += hlen; + continue; + default: + f--; /* pass % through */ + break; + } + } + *t++ = *f; + } + *t = '\0'; + } + + if (DEBUG_GTE(CONNECT, 1)) { rprintf(FINFO, "%sopening tcp connection to %s port %d\n", prog ? "Using RSYNC_CONNECT_PROG instead of " : "", host, port); @@ -315,9 +363,7 @@ int open_socket_out_wrapped(char *host, int port, const char *bind_addr, } - -/** - * Open one or more sockets for incoming data using the specified type, +/* Open one or more sockets for incoming data using the specified type, * port, and address. * * The getaddrinfo() call may return several address results, e.g. for @@ -326,9 +372,7 @@ int open_socket_out_wrapped(char *host, int port, const char *bind_addr, * We return an array of file-descriptors to the sockets, with a trailing * -1 value to indicate the end of the list. * - * @param bind_addr Local address to bind, or NULL to allow it to - * default. - **/ + * bind_addr: local address to bind, or NULL to allow it to default. */ static int *open_socket_in(int type, int port, const char *bind_addr, int af_hint) { @@ -378,6 +422,10 @@ static int *open_socket_in(int type, int port, const char *bind_addr, setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof one); + if (sockopts) + set_socket_options(s, sockopts); + else + set_socket_options(s, lp_socket_options()); #ifdef IPV6_V6ONLY if (resp->ai_family == AF_INET6) { @@ -394,7 +442,8 @@ static int *open_socket_in(int type, int port, const char *bind_addr, if (bind(s, resp->ai_addr, resp->ai_addrlen) < 0) { /* Nope, try another */ int r = asprintf(&errmsgs[ecnt++], - "bind() failed: %s\n", strerror(errno)); + "bind() failed: %s (address-family %d)\n", + strerror(errno), (int)resp->ai_family); if (r < 0) out_of_memory("open_socket_in"); close(s); @@ -411,8 +460,8 @@ static int *open_socket_in(int type, int port, const char *bind_addr, /* Only output the socket()/bind() messages if we were totally * unsuccessful, or if the daemon is being run with -vv. */ for (s = 0; s < ecnt; s++) { - if (!i || verbose > 1) - rwrite(FLOG, errmsgs[s], strlen(errmsgs[s])); + if (!i || DEBUG_GTE(BIND, 1)) + rwrite(FLOG, errmsgs[s], strlen(errmsgs[s]), 0); free(errmsgs[s]); } free(errmsgs); @@ -428,9 +477,7 @@ static int *open_socket_in(int type, int port, const char *bind_addr, } -/* - * Determine if a file descriptor is in fact a socket - */ +/* Determine if a file descriptor is in fact a socket. */ int is_a_socket(int fd) { int v; @@ -516,7 +563,7 @@ void start_accept_loop(int port, int (*fn)(int, int)) fds = deffds; #endif - if (select(maxfd + 1, &fds, NULL, NULL, NULL) != 1) + if (select(maxfd + 1, &fds, NULL, NULL, NULL) < 1) continue; for (i = 0, fd = -1; sp[i] >= 0; i++) { @@ -598,13 +645,11 @@ struct #ifdef SO_RCVTIMEO {"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, 0, OPT_INT}, #endif - {NULL,0,0,0,0}}; - + {NULL,0,0,0,0} +}; -/** - * Set user socket options - **/ +/* Set user socket options. */ void set_socket_options(int fd, char *options) { char *tok; @@ -669,45 +714,11 @@ void set_socket_options(int fd, char *options) free(options); } -/** - * Become a daemon, discarding the controlling terminal - **/ -void become_daemon(void) -{ - int i; - - if (fork()) { - _exit(0); - } - - /* detach from the terminal */ -#ifdef HAVE_SETSID - setsid(); -#elif defined TIOCNOTTY - i = open("/dev/tty", O_RDWR); - if (i >= 0) { - ioctl(i, (int)TIOCNOTTY, (char *)0); - close(i); - } -#endif - /* make sure that stdin, stdout an stderr don't stuff things - * up (library functions, for example) */ - for (i = 0; i < 3; i++) { - close(i); - open("/dev/null", O_RDWR); - } -} - -/** - * This is like socketpair but uses tcp. It is used by the Samba - * regression test code. - * - * The function guarantees that nobody else can attach to the socket, - * or if they do that this function fails and the socket gets closed - * returns 0 on success, -1 on failure the resulting file descriptors - * are symmetrical. - **/ +/* This is like socketpair but uses tcp. The function guarantees that nobody + * else can attach to the socket, or if they do that this function fails and + * the socket gets closed. Returns 0 on success, -1 on failure. The resulting + * file descriptors are symmetrical. Currently only for RSYNC_CONNECT_PROG. */ static int socketpair_tcp(int fd[2]) { int listener; @@ -728,16 +739,12 @@ static int socketpair_tcp(int fd[2]) sock2.sin_len = sizeof sock2; #endif sock2.sin_family = PF_INET; + sock2.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - bind(listener, (struct sockaddr *)&sock2, sizeof sock2); - - if (listen(listener, 1) != 0) - goto failed; - - if (getsockname(listener, (struct sockaddr *)&sock, &socklen) != 0) - goto failed; - - if ((fd[1] = socket(PF_INET, SOCK_STREAM, 0)) == -1) + if (bind(listener, (struct sockaddr *)&sock2, sizeof sock2) != 0 + || listen(listener, 1) != 0 + || getsockname(listener, (struct sockaddr *)&sock, &socklen) != 0 + || (fd[1] = socket(PF_INET, SOCK_STREAM, 0)) == -1) goto failed; set_nonblocking(fd[1]); @@ -750,7 +757,7 @@ static int socketpair_tcp(int fd[2]) } else connect_done = 1; - if ((fd[0] = accept(listener, (struct sockaddr *)&sock, &socklen)) == -1) + if ((fd[0] = accept(listener, (struct sockaddr *)&sock2, &socklen)) == -1) goto failed; close(listener); @@ -778,34 +785,40 @@ static int socketpair_tcp(int fd[2]) } - -/** - * Run a program on a local tcp socket, so that we can talk to it's - * stdin and stdout. This is used to fake a connection to a daemon - * for testing -- not for the normal case of running SSH. +/* Run a program on a local tcp socket, so that we can talk to it's stdin and + * stdout. This is used to fake a connection to a daemon for testing -- not + * for the normal case of running SSH. * - * @return a socket which is attached to a subprocess running - * "prog". stdin and stdout are attached. stderr is left attached to - * the original stderr - **/ -int sock_exec(const char *prog) + * Retruns a socket which is attached to a subprocess running "prog". stdin and + * stdout are attached. stderr is left attached to the original stderr. */ +static int sock_exec(const char *prog) { + pid_t pid; int fd[2]; if (socketpair_tcp(fd) != 0) { rsyserr(FERROR, errno, "socketpair_tcp failed"); return -1; } - if (verbose >= 2) + if (DEBUG_GTE(CMD, 1)) rprintf(FINFO, "Running socket program: \"%s\"\n", prog); - if (fork() == 0) { + + pid = fork(); + if (pid < 0) { + rsyserr(FERROR, errno, "fork"); + exit_cleanup(RERR_IPC); + } + + if (pid == 0) { close(fd[0]); - close(0); - close(1); - dup(fd[1]); - dup(fd[1]); + if (dup2(fd[1], STDIN_FILENO) < 0 + || dup2(fd[1], STDOUT_FILENO) < 0) { + fprintf(stderr, "Failed to run \"%s\"\n", prog); + exit(1); + } exit(system(prog)); } + close(fd[1]); return fd[0]; }