2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of U.M. not be used in advertising or
11 * publicity pertaining to distribution of the software without specific,
12 * written prior permission. U.M. makes no representations about the
13 * suitability of this software for any purpose. It is provided "as is"
14 * without express or implied warranty.
16 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * Author: James da Silva, Systems Design and Analysis Group
24 * Computer Science Department
25 * University of Maryland at College Park
28 * $Id: stream.c,v 1.39 2006/08/24 01:57:15 paddy_s Exp $
30 * functions for managing stream sockets
37 #include "security-util.h"
38 #include "sockaddr-util.h"
41 static void try_socksize(int sock, int which, size_t size);
42 static int stream_client_internal(const char *hostname, in_port_t port,
43 size_t sendsize, size_t recvsize, in_port_t *localport,
44 int nonblock, int priv);
54 int server_socket, retries;
56 #if defined(SO_KEEPALIVE) || defined(USE_REUSEADDR)
60 sockaddr_union server;
63 socklen_t_equiv socklen;
66 *portp = USHRT_MAX; /* in case we error exit */
68 socket_family = AF_NATIVE;
70 socket_family = family;
72 g_debug("stream_server opening socket with family %d (requested family was %d)", socket_family, family);
73 server_socket = socket(socket_family, SOCK_STREAM, 0);
76 /* if that address family actually isn't supported, just try AF_INET */
77 if (server_socket == -1 && errno == EAFNOSUPPORT) {
78 g_debug("stream_server retrying socket with AF_INET");
79 socket_family = AF_INET;
80 server_socket = socket(AF_INET, SOCK_STREAM, 0);
83 if (server_socket == -1) {
85 g_debug(_("stream_server: socket() failed: %s"),
86 strerror(save_errno));
90 if(server_socket < 0 || server_socket >= (int)FD_SETSIZE) {
91 aclose(server_socket);
92 errno = EMFILE; /* out of range */
94 g_debug(_("stream_server: socket out of range: %d"),
100 SU_INIT(&server, socket_family);
101 SU_SET_INADDR_ANY(&server);
104 r = setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR,
105 (void *)&on, (socklen_t_equiv)sizeof(on));
107 g_debug(_("stream_server: setsockopt(SO_REUSEADDR) failed: %s"),
112 try_socksize(server_socket, SO_SNDBUF, sendsize);
113 try_socksize(server_socket, SO_RCVBUF, recvsize);
116 * If a port range was specified, we try to get a port in that
117 * range first. Next, we try to get a reserved port. If that
118 * fails, we just go for any port.
120 * In all cases, not to use port that's assigned to other services.
122 * It is up to the caller to make sure we have the proper permissions
123 * to get the desired port, and to make sure we return a port that
124 * is within the range it requires.
126 for (retries = 0; ; retries++) {
128 portrange = getconf_intrange(CNF_RESERVED_TCP_PORT);
130 portrange = getconf_intrange(CNF_UNRESERVED_TCP_PORT);
133 if (portrange[0] != 0 && portrange[1] != 0) {
134 if (bind_portrange(server_socket, &server, (in_port_t)portrange[0],
135 (in_port_t)portrange[1], "tcp") == 0)
137 g_debug(_("stream_server: Could not bind to port in range: %d - %d."),
138 portrange[0], portrange[1]);
140 socklen = SS_LEN(&server);
141 if (bind(server_socket, (struct sockaddr *)&server, socklen) == 0)
143 g_debug(_("stream_server: Could not bind to any port: %s"),
147 if (retries >= BIND_CYCLE_RETRIES)
150 g_debug(_("stream_server: Retrying entire range after 10 second delay."));
156 g_debug(_("stream_server: bind(in6addr_any) failed: %s"),
157 strerror(save_errno));
158 aclose(server_socket);
163 listen(server_socket, 1);
165 /* find out what port was actually used */
167 len = SIZEOF(server);
168 if(getsockname(server_socket, (struct sockaddr *)&server, &len) == -1) {
170 g_debug(_("stream_server: getsockname() failed: %s"),
171 strerror(save_errno));
172 aclose(server_socket);
178 r = setsockopt(server_socket, SOL_SOCKET, SO_KEEPALIVE,
179 (void *)&on, (socklen_t_equiv)sizeof(on));
182 g_debug(_("stream_server: setsockopt(SO_KEEPALIVE) failed: %s"),
183 strerror(save_errno));
184 aclose(server_socket);
190 *portp = SU_GET_PORT(&server);
191 g_debug(_("stream_server: waiting for connection: %s"),
192 str_sockaddr(&server));
193 return server_socket;
197 stream_client_internal(
198 const char *hostname,
202 in_port_t *localport,
206 sockaddr_union svaddr, claddr;
209 int client_socket = 0;
210 int *portrange = NULL;
212 struct addrinfo *res, *res_addr;
214 f = priv ? "stream_client_privileged" : "stream_client";
216 result = resolve_hostname(hostname, SOCK_STREAM, &res, NULL);
218 g_debug(_("resolve_hostname(%s): %s"), hostname, gai_strerror(result));
219 errno = EHOSTUNREACH;
223 g_debug(_("resolve_hostname(%s): no results"), hostname);
224 errno = EHOSTUNREACH;
228 for (res_addr = res; res_addr != NULL; res_addr = res_addr->ai_next) {
229 /* copy the first (preferred) address we found */
230 copy_sockaddr(&svaddr, (sockaddr_union *)res_addr->ai_addr);
231 SU_SET_PORT(&svaddr, port);
233 SU_INIT(&claddr, SU_GET_FAMILY(&svaddr));
234 SU_SET_INADDR_ANY(&claddr);
237 * If a privileged port range was requested, we try to get a port in
238 * that range first and fail if it is not available. Next, we try
239 * to get a port in the range built in when Amanda was configured.
240 * If that fails, we just go for any port.
242 * It is up to the caller to make sure we have the proper permissions
243 * to get the desired port, and to make sure we return a port that
244 * is within the range it requires.
247 portrange = getconf_intrange(CNF_RESERVED_TCP_PORT);
249 portrange = getconf_intrange(CNF_UNRESERVED_TCP_PORT);
251 client_socket = connect_portrange(&claddr, (in_port_t)portrange[0],
252 (in_port_t)portrange[1],
253 "tcp", &svaddr, nonblock);
255 if (client_socket > 0)
261 if (client_socket > 0)
264 g_debug(_("stream_client: Could not bind to port in range %d-%d."),
265 portrange[0], portrange[1]);
271 try_socksize(client_socket, SO_SNDBUF, sendsize);
272 try_socksize(client_socket, SO_RCVBUF, recvsize);
273 if (localport != NULL)
274 *localport = SU_GET_PORT(&claddr);
275 return client_socket;
279 stream_client_privileged(
280 const char *hostname,
284 in_port_t *localport,
287 return stream_client_internal(hostname,
298 const char *hostname,
302 in_port_t *localport,
305 return stream_client_internal(hostname,
314 /* don't care about these values */
315 static sockaddr_union addr;
316 static socklen_t_equiv addrlen;
325 SELECT_ARG_TYPE readset;
327 int nfound, connected_socket;
332 assert(server_socket >= 0);
336 memset(&tv, 0, SIZEOF(tv));
338 memset(&readset, 0, SIZEOF(readset));
340 FD_SET(server_socket, &readset);
341 nfound = select(server_socket+1, &readset, NULL, NULL, &tv);
342 if(nfound <= 0 || !FD_ISSET(server_socket, &readset)) {
345 g_debug(_("stream_accept: select() failed: %s"),
346 strerror(save_errno));
347 } else if(nfound == 0) {
348 g_debug(plural(_("stream_accept: timeout after %d second"),
349 _("stream_accept: timeout after %d seconds"),
354 } else if (!FD_ISSET(server_socket, &readset)) {
357 for(i = 0; i < server_socket + 1; i++) {
358 if(FD_ISSET(i, &readset)) {
359 g_debug(_("stream_accept: got fd %d instead of %d"),
371 } while (nfound <= 0);
374 addrlen = (socklen_t_equiv)sizeof(sockaddr_union);
375 connected_socket = accept(server_socket,
376 (struct sockaddr *)&addr,
378 if(connected_socket < 0) {
381 g_debug(_("stream_accept: connection from %s"),
382 str_sockaddr(&addr));
384 * Make certain we got an inet connection and that it is not
385 * from port 20 (a favorite unauthorized entry tool).
387 if (SU_GET_FAMILY(&addr) == AF_INET
389 || SU_GET_FAMILY(&addr) == AF_INET6
392 port = SU_GET_PORT(&addr);
393 if (port != (in_port_t)20) {
394 try_socksize(connected_socket, SO_SNDBUF, sendsize);
395 try_socksize(connected_socket, SO_RCVBUF, recvsize);
396 return connected_socket;
398 g_debug(_("remote port is %u: ignored"),
403 g_debug(_("family is %d instead of %d(AF_INET)"
404 " or %d(AF_INET6): ignored"),
405 SU_GET_FAMILY(&addr),
408 g_debug(_("family is %d instead of %d(AF_INET)"
410 SU_GET_FAMILY(&addr),
414 aclose(connected_socket);
418 g_debug(_("stream_accept: accept() failed: %s"),
419 strerror(save_errno));
438 /* keep trying, get as big a buffer as possible */
439 while((isize > 1024) &&
440 (setsockopt(sock, SOL_SOCKET,
441 which, (void *) &isize, (socklen_t_equiv)sizeof(isize)) < 0)) {
445 g_debug(_("try_socksize: %s buffer size is %d"),
446 (which == SO_SNDBUF) ? _("send") : _("receive"),
449 g_debug(_("try_socksize: could not allocate %s buffer of %zu"),
450 (which == SO_SNDBUF) ? _("send") : _("receive"),