X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=common-src%2Fstream.c;h=22b9b0c33fe7fcdebc26afb2d89acc926061e1de;hb=2627875b7d18858bc1f9f7652811e4d8c15a23eb;hp=b5e24456e7cc64355b77367d064f87dcf198de6b;hpb=1194fb66aa28d9929c3f2bef3cc6c1c3f40a60a4;p=debian%2Famanda diff --git a/common-src/stream.c b/common-src/stream.c index b5e2445..22b9b0c 100644 --- a/common-src/stream.c +++ b/common-src/stream.c @@ -25,7 +25,7 @@ * University of Maryland at College Park */ /* - * $Id: stream.c,v 1.31 2006/01/14 04:37:19 paddy_s Exp $ + * $Id: stream.c,v 1.39 2006/08/24 01:57:15 paddy_s Exp $ * * functions for managing stream sockets */ @@ -33,50 +33,84 @@ #include "dgram.h" #include "stream.h" #include "util.h" +#include "conffile.h" +#include "security-util.h" +#include "sockaddr-util.h" /* local functions */ -static void try_socksize P((int sock, int which, int size)); +static void try_socksize(int sock, int which, size_t size); +static int stream_client_internal(const char *hostname, in_port_t port, + size_t sendsize, size_t recvsize, in_port_t *localport, + int nonblock, int priv); -int stream_server(portp, sendsize, recvsize) -int *portp; -int sendsize, recvsize; +int +stream_server( + int family, + in_port_t *portp, + size_t sendsize, + size_t recvsize, + int priv) { - int server_socket; - socklen_t len; -#ifdef SO_KEEPALIVE - int on = 1; + int server_socket, retries; + socklen_t_equiv len; +#if defined(SO_KEEPALIVE) || defined(USE_REUSEADDR) + const int on = 1; int r; #endif - struct sockaddr_in server; + sockaddr_union server; int save_errno; + int *portrange; + socklen_t_equiv socklen; + int socket_family; - *portp = -1; /* in case we error exit */ - if((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + *portp = USHRT_MAX; /* in case we error exit */ + if (family == -1) { + socket_family = AF_NATIVE; + } else { + socket_family = family; + } + g_debug("stream_server opening socket with family %d (requested family was %d)", socket_family, family); + server_socket = socket(socket_family, SOCK_STREAM, 0); + +#ifdef WORKING_IPV6 + /* if that address family actually isn't supported, just try AF_INET */ + if (server_socket == -1 && errno == EAFNOSUPPORT) { + g_debug("stream_server retrying socket with AF_INET"); + socket_family = AF_INET; + server_socket = socket(AF_INET, SOCK_STREAM, 0); + } +#endif + if (server_socket == -1) { save_errno = errno; - dbprintf(("%s: stream_server: socket() failed: %s\n", - debug_prefix(NULL), - strerror(save_errno))); + dbprintf(_("stream_server: socket() failed: %s\n"), + strerror(save_errno)); errno = save_errno; return -1; } - if(server_socket < 0 || server_socket >= FD_SETSIZE) { + if(server_socket < 0 || server_socket >= (int)FD_SETSIZE) { aclose(server_socket); errno = EMFILE; /* out of range */ save_errno = errno; - dbprintf(("%s: stream_server: socket out of range: %d\n", - debug_prefix(NULL), - server_socket)); + dbprintf(_("stream_server: socket out of range: %d\n"), + server_socket); errno = save_errno; return -1; } - memset(&server, 0, sizeof(server)); - server.sin_family = AF_INET; - server.sin_addr.s_addr = INADDR_ANY; - if(sendsize >= 0) - try_socksize(server_socket, SO_SNDBUF, sendsize); - if(recvsize >= 0) - try_socksize(server_socket, SO_RCVBUF, recvsize); + SU_INIT(&server, socket_family); + SU_SET_INADDR_ANY(&server); + +#ifdef USE_REUSEADDR + r = setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, + (void *)&on, (socklen_t_equiv)sizeof(on)); + if (r < 0) { + dbprintf(_("stream_server: setsockopt(SO_REUSEADDR) failed: %s\n"), + strerror(errno)); + } +#endif + + try_socksize(server_socket, SO_SNDBUF, sendsize); + try_socksize(server_socket, SO_RCVBUF, recvsize); /* * If a port range was specified, we try to get a port in that @@ -89,36 +123,52 @@ int sendsize, recvsize; * to get the desired port, and to make sure we return a port that * is within the range it requires. */ -#ifdef TCPPORTRANGE - if (bind_portrange(server_socket, &server, TCPPORTRANGE, "tcp") == 0) - goto out; -#endif + for (retries = 0; ; retries++) { + if (priv) { + portrange = getconf_intrange(CNF_RESERVED_TCP_PORT); + } else { + portrange = getconf_intrange(CNF_UNRESERVED_TCP_PORT); + } - if (bind_portrange(server_socket, &server, 512, IPPORT_RESERVED - 1, "tcp") == 0) - goto out; + if (portrange[0] != 0 && portrange[1] != 0) { + if (bind_portrange(server_socket, &server, (in_port_t)portrange[0], + (in_port_t)portrange[1], "tcp") == 0) + goto out; + dbprintf(_("stream_server: Could not bind to port in range: %d - %d.\n"), + portrange[0], portrange[1]); + } else { + socklen = SS_LEN(&server); + if (bind(server_socket, (struct sockaddr *)&server, socklen) == 0) + goto out; + dbprintf(_("stream_server: Could not bind to any port: %s\n"), + strerror(errno)); + } - server.sin_port = INADDR_ANY; - if (bind(server_socket, (struct sockaddr *)&server, (socklen_t) sizeof(server)) == -1) { - save_errno = errno; - dbprintf(("%s: stream_server: bind(INADDR_ANY) failed: %s\n", - debug_prefix(NULL), - strerror(save_errno))); - aclose(server_socket); - errno = save_errno; - return -1; + if (retries >= BIND_CYCLE_RETRIES) + break; + + dbprintf(_("stream_server: Retrying entire range after 10 second delay.\n")); + + sleep(15); } + save_errno = errno; + dbprintf(_("stream_server: bind(in6addr_any) failed: %s\n"), + strerror(save_errno)); + aclose(server_socket); + errno = save_errno; + return -1; + out: listen(server_socket, 1); /* find out what port was actually used */ - len = sizeof(server); + len = SIZEOF(server); if(getsockname(server_socket, (struct sockaddr *)&server, &len) == -1) { save_errno = errno; - dbprintf(("%s: stream_server: getsockname() failed: %s\n", - debug_prefix(NULL), - strerror(save_errno))); + dbprintf(_("stream_server: getsockname() failed: %s\n"), + strerror(save_errno)); aclose(server_socket); errno = save_errno; return -1; @@ -126,208 +176,113 @@ out: #ifdef SO_KEEPALIVE r = setsockopt(server_socket, SOL_SOCKET, SO_KEEPALIVE, - (void *)&on, (socklen_t)sizeof(on)); + (void *)&on, (socklen_t_equiv)sizeof(on)); if(r == -1) { save_errno = errno; - dbprintf(("%s: stream_server: setsockopt(SO_KEEPALIVE) failed: %s\n", - debug_prefix(NULL), - strerror(save_errno))); + dbprintf(_("stream_server: setsockopt(SO_KEEPALIVE) failed: %s\n"), + strerror(save_errno)); aclose(server_socket); errno = save_errno; return -1; } #endif - *portp = (int) ntohs(server.sin_port); - dbprintf(("%s: stream_server: waiting for connection: %s.%d\n", - debug_prefix_time(NULL), - inet_ntoa(server.sin_addr), - *portp)); + *portp = SU_GET_PORT(&server); + dbprintf(_("stream_server: waiting for connection: %s\n"), + str_sockaddr(&server)); return server_socket; } static int -stream_client_internal(hostname, - port, - sendsize, - recvsize, - localport, - nonblock, - priv) - const char *hostname; - int port, sendsize, recvsize, *localport, nonblock, priv; +stream_client_internal( + const char *hostname, + in_port_t port, + size_t sendsize, + size_t recvsize, + in_port_t *localport, + int nonblock, + int priv) { - int client_socket; - socklen_t len; -#ifdef SO_KEEPALIVE - int on = 1; - int r; -#endif - struct sockaddr_in svaddr, claddr; - struct hostent *hostp; - int save_errno; + sockaddr_union svaddr, claddr; + int save_errno = 0; char *f; + int client_socket = 0; + int *portrange = NULL; + int result; + struct addrinfo *res, *res_addr; f = priv ? "stream_client_privileged" : "stream_client"; - if((hostp = gethostbyname(hostname)) == NULL) { - save_errno = errno; - dbprintf(("%s: %s: gethostbyname(%s) failed\n", - debug_prefix(NULL), - f, - hostname)); - errno = save_errno; - return -1; - } - - memset(&svaddr, 0, sizeof(svaddr)); - svaddr.sin_family = AF_INET; - svaddr.sin_port = htons(port); - memcpy(&svaddr.sin_addr, hostp->h_addr, hostp->h_length); - - if((client_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { - save_errno = errno; - dbprintf(("%s: %s: socket() failed: %s\n", - debug_prefix(NULL), - f, - strerror(save_errno))); - errno = save_errno; + result = resolve_hostname(hostname, SOCK_STREAM, &res, NULL); + if(result != 0) { + dbprintf(_("resolve_hostname(%s): %s\n"), hostname, gai_strerror(result)); + errno = EHOSTUNREACH; return -1; } - if(client_socket < 0 || client_socket >= FD_SETSIZE) { - aclose(client_socket); - errno = EMFILE; /* out of range */ + if(!res) { + dbprintf(_("resolve_hostname(%s): no results\n"), hostname); + errno = EHOSTUNREACH; return -1; } -#ifdef SO_KEEPALIVE - r = setsockopt(client_socket, SOL_SOCKET, SO_KEEPALIVE, - (void *)&on, sizeof(on)); - if(r == -1) { - save_errno = errno; - dbprintf(("%s: %s: setsockopt() failed: %s\n", - debug_prefix(NULL), - f, - strerror(save_errno))); - aclose(client_socket); - errno = save_errno; - return -1; - } -#endif - - memset(&claddr, 0, sizeof(claddr)); - claddr.sin_family = AF_INET; - claddr.sin_addr.s_addr = INADDR_ANY; + for (res_addr = res; res_addr != NULL; res_addr = res_addr->ai_next) { + /* copy the first (preferred) address we found */ + copy_sockaddr(&svaddr, (sockaddr_union *)res_addr->ai_addr); + SU_SET_PORT(&svaddr, port); - /* - * If a privileged port range was requested, we try to get a port in - * that range first and fail if it is not available. Next, we try - * to get a port in the range built in when Amanda was configured. - * If that fails, we just go for any port. - * - * It is up to the caller to make sure we have the proper permissions - * to get the desired port, and to make sure we return a port that - * is within the range it requires. - */ - if (priv) { - int b; + SU_INIT(&claddr, SU_GET_FAMILY(&svaddr)); + SU_SET_INADDR_ANY(&claddr); - b = bind_portrange(client_socket, &claddr, 512, IPPORT_RESERVED - 1, "tcp"); - if (b == 0) { - goto out; /* got what we wanted */ + /* + * If a privileged port range was requested, we try to get a port in + * that range first and fail if it is not available. Next, we try + * to get a port in the range built in when Amanda was configured. + * If that fails, we just go for any port. + * + * It is up to the caller to make sure we have the proper permissions + * to get the desired port, and to make sure we return a port that + * is within the range it requires. + */ + if (priv) { + portrange = getconf_intrange(CNF_RESERVED_TCP_PORT); + } else { + portrange = getconf_intrange(CNF_UNRESERVED_TCP_PORT); } + client_socket = connect_portrange(&claddr, (in_port_t)portrange[0], + (in_port_t)portrange[1], + "tcp", &svaddr, nonblock); save_errno = errno; - dbprintf(("%s: %s: bind(IPPORT_RESERVED) failed: %s\n", - debug_prefix(NULL), - f, - strerror(save_errno))); - aclose(client_socket); - errno = save_errno; - return -1; + if (client_socket > 0) + break; } -#ifdef TCPPORTRANGE - if (bind_portrange(client_socket, &claddr, TCPPORTRANGE, "tcp") == 0) + freeaddrinfo(res); + + if (client_socket > 0) goto out; -#endif - - claddr.sin_port = INADDR_ANY; - if (bind(client_socket, (struct sockaddr *)&claddr, sizeof(claddr)) == -1) { - save_errno = errno; - dbprintf(("%s: %s: bind(INADDR_ANY) failed: %s\n", - debug_prefix(NULL), - f, - strerror(save_errno))); - aclose(client_socket); - errno = save_errno; - return -1; - } - -out: - - /* find out what port was actually used */ - - len = sizeof(claddr); - if(getsockname(client_socket, (struct sockaddr *)&claddr, &len) == -1) { - save_errno = errno; - dbprintf(("%s: %s: getsockname() failed: %s\n", - debug_prefix(NULL), - f, - strerror(save_errno))); - aclose(client_socket); - errno = save_errno; - return -1; - } - - if (nonblock) - fcntl(client_socket, F_SETFL, - fcntl(client_socket, F_GETFL, 0)|O_NONBLOCK); - if(connect(client_socket, (struct sockaddr *)&svaddr, sizeof(svaddr)) - == -1 && !nonblock) { - save_errno = errno; - dbprintf(("%s: %s: connect to %s.%d failed: %s\n", - debug_prefix_time(NULL), - f, - inet_ntoa(svaddr.sin_addr), - ntohs(svaddr.sin_port), - strerror(save_errno))); - aclose(client_socket); - errno = save_errno; - return -1; - } + dbprintf(_("stream_client: Could not bind to port in range %d-%d.\n"), + portrange[0], portrange[1]); - dbprintf(("%s: %s: connected to %s.%d\n", - debug_prefix_time(NULL), - f, - inet_ntoa(svaddr.sin_addr), - ntohs(svaddr.sin_port))); - dbprintf(("%s: %s: our side is %s.%d\n", - debug_prefix(NULL), - f, - inet_ntoa(claddr.sin_addr), - ntohs(claddr.sin_port))); - - if(sendsize >= 0) - try_socksize(client_socket, SO_SNDBUF, sendsize); - if(recvsize >= 0) - try_socksize(client_socket, SO_RCVBUF, recvsize); + errno = save_errno; + return -1; +out: + try_socksize(client_socket, SO_SNDBUF, sendsize); + try_socksize(client_socket, SO_RCVBUF, recvsize); if (localport != NULL) - *localport = ntohs(claddr.sin_port); - + *localport = SU_GET_PORT(&claddr); return client_socket; } int -stream_client_privileged(hostname, - port, - sendsize, - recvsize, - localport, - nonblock) - const char *hostname; - int port, sendsize, recvsize, *localport, nonblock; +stream_client_privileged( + const char *hostname, + in_port_t port, + size_t sendsize, + size_t recvsize, + in_port_t *localport, + int nonblock) { return stream_client_internal(hostname, port, @@ -339,9 +294,13 @@ stream_client_privileged(hostname, } int -stream_client(hostname, port, sendsize, recvsize, localport, nonblock) - const char *hostname; - int port, sendsize, recvsize, *localport, nonblock; +stream_client( + const char *hostname, + in_port_t port, + size_t sendsize, + size_t recvsize, + in_port_t *localport, + int nonblock) { return stream_client_internal(hostname, port, @@ -353,118 +312,142 @@ stream_client(hostname, port, sendsize, recvsize, localport, nonblock) } /* don't care about these values */ -static struct sockaddr_in addr; -static socklen_t addrlen; +static sockaddr_union addr; +static socklen_t_equiv addrlen; -int stream_accept(server_socket, timeout, sendsize, recvsize) -int server_socket, timeout, sendsize, recvsize; +int +stream_accept( + int server_socket, + int timeout, + size_t sendsize, + size_t recvsize) { - fd_set readset; + SELECT_ARG_TYPE readset; struct timeval tv; int nfound, connected_socket; int save_errno; + int ntries = 0; + in_port_t port; assert(server_socket >= 0); - tv.tv_sec = timeout; - tv.tv_usec = 0; - FD_ZERO(&readset); - FD_SET(server_socket, &readset); - nfound = select(server_socket+1, (SELECT_ARG_TYPE *)&readset, NULL, NULL, &tv); - if(nfound <= 0 || !FD_ISSET(server_socket, &readset)) { - save_errno = errno; - if(nfound < 0) { - dbprintf(("%s: stream_accept: select() failed: %s\n", - debug_prefix_time(NULL), - strerror(save_errno))); - } else if(nfound == 0) { - dbprintf(("%s: stream_accept: timeout after %d second%s\n", - debug_prefix_time(NULL), - timeout, - (timeout == 1) ? "" : "s")); - save_errno = ENOENT; /* ??? */ - } else if (!FD_ISSET(server_socket, &readset)) { - int i; - - for(i = 0; i < server_socket + 1; i++) { - if(FD_ISSET(i, &readset)) { - dbprintf(("%s: stream_accept: got fd %d instead of %d\n", - debug_prefix_time(NULL), + do { + ntries++; + memset(&tv, 0, SIZEOF(tv)); + tv.tv_sec = timeout; + memset(&readset, 0, SIZEOF(readset)); + FD_ZERO(&readset); + FD_SET(server_socket, &readset); + nfound = select(server_socket+1, &readset, NULL, NULL, &tv); + if(nfound <= 0 || !FD_ISSET(server_socket, &readset)) { + save_errno = errno; + if(nfound < 0) { + dbprintf(_("stream_accept: select() failed: %s\n"), + strerror(save_errno)); + } else if(nfound == 0) { + dbprintf(plural(_("stream_accept: timeout after %d second\n"), + _("stream_accept: timeout after %d seconds\n"), + timeout), + timeout); + errno = ETIMEDOUT; + return -1; + } else if (!FD_ISSET(server_socket, &readset)) { + int i; + + for(i = 0; i < server_socket + 1; i++) { + if(FD_ISSET(i, &readset)) { + dbprintf(_("stream_accept: got fd %d instead of %d\n"), i, - server_socket)); + server_socket); + } } + save_errno = EBADF; } - save_errno = EBADF; - } - errno = save_errno; - return -1; - } + if (ntries > 5) { + errno = save_errno; + return -1; + } + } + } while (nfound <= 0); while(1) { - addrlen = sizeof(struct sockaddr); + addrlen = (socklen_t_equiv)sizeof(sockaddr_union); connected_socket = accept(server_socket, (struct sockaddr *)&addr, &addrlen); if(connected_socket < 0) { - save_errno = errno; - dbprintf(("%s: stream_accept: accept() failed: %s\n", - debug_prefix_time(NULL), - strerror(save_errno))); - errno = save_errno; - return -1; + break; } - dbprintf(("%s: stream_accept: connection from %s.%d\n", - debug_prefix_time(NULL), - inet_ntoa(addr.sin_addr), - ntohs(addr.sin_port))); + dbprintf(_("stream_accept: connection from %s\n"), + str_sockaddr(&addr)); /* * Make certain we got an inet connection and that it is not * from port 20 (a favorite unauthorized entry tool). */ - if(addr.sin_family == AF_INET && ntohs(addr.sin_port) != 20) { - break; - } - if(addr.sin_family != AF_INET) { - dbprintf(("%s: family is %d instead of %d(AF_INET): ignored\n", - debug_prefix_time(NULL), - addr.sin_family, - AF_INET)); - } - if(ntohs(addr.sin_port) == 20) { - dbprintf(("%s: remote port is %d: ignored\n", - debug_prefix_time(NULL), - ntohs(addr.sin_port))); + if (SU_GET_FAMILY(&addr) == AF_INET +#ifdef WORKING_IPV6 + || SU_GET_FAMILY(&addr) == AF_INET6 +#endif + ){ + port = SU_GET_PORT(&addr); + if (port != (in_port_t)20) { + try_socksize(connected_socket, SO_SNDBUF, sendsize); + try_socksize(connected_socket, SO_RCVBUF, recvsize); + return connected_socket; + } else { + dbprintf(_("remote port is %u: ignored\n"), + (unsigned int)port); + } + } else { +#ifdef WORKING_IPV6 + dbprintf(_("family is %d instead of %d(AF_INET)" + " or %d(AF_INET6): ignored\n"), + SU_GET_FAMILY(&addr), + AF_INET, AF_INET6); +#else + dbprintf(_("family is %d instead of %d(AF_INET)" + ": ignored\n"), + SU_GET_FAMILY(&addr), + AF_INET); +#endif } aclose(connected_socket); } - if(sendsize >= 0) - try_socksize(connected_socket, SO_SNDBUF, sendsize); - if(recvsize >= 0) - try_socksize(connected_socket, SO_RCVBUF, recvsize); - - return connected_socket; + save_errno = errno; + dbprintf(_("stream_accept: accept() failed: %s\n"), + strerror(save_errno)); + errno = save_errno; + return -1; } -static void try_socksize(sock, which, size) -int sock, which, size; +static void +try_socksize( + int sock, + int which, + size_t size) { - int origsize; + size_t origsize; + int isize; + + if (size == 0) + return; origsize = size; + isize = size; /* keep trying, get as big a buffer as possible */ - while(size > 1024 && - setsockopt(sock, SOL_SOCKET, which, (void *) &size, sizeof(int)) < 0) - size -= 1024; - if(size > 1024) { - dbprintf(("%s: try_socksize: %s buffer size is %d\n", - debug_prefix(NULL), - (which == SO_SNDBUF) ? "send" : "receive", - size)); + while((isize > 1024) && + (setsockopt(sock, SOL_SOCKET, + which, (void *) &isize, (socklen_t_equiv)sizeof(isize)) < 0)) { + isize -= 1024; + } + if(isize > 1024) { + dbprintf(_("try_socksize: %s buffer size is %d\n"), + (which == SO_SNDBUF) ? _("send") : _("receive"), + isize); } else { - dbprintf(("%s: try_socksize: could not allocate %s buffer of %d\n", - debug_prefix(NULL), - (which == SO_SNDBUF) ? "send" : "receive", - origsize)); + dbprintf(_("try_socksize: could not allocate %s buffer of %zu\n"), + (which == SO_SNDBUF) ? _("send") : _("receive"), + origsize); } }