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"
40 static void try_socksize(int sock, int which, size_t size);
41 static int stream_client_internal(const char *hostname, in_port_t port,
42 size_t sendsize, size_t recvsize, in_port_t *localport,
43 int nonblock, int priv);
52 int server_socket, retries;
54 #if defined(SO_KEEPALIVE) || defined(USE_REUSEADDR)
58 struct sockaddr_storage server;
64 *portp = USHRT_MAX; /* in case we error exit */
65 socket_family = AF_NATIVE;
66 server_socket = socket(AF_NATIVE, SOCK_STREAM, 0);
68 /* if that address family actually isn't supported, just try AF_INET */
69 if (server_socket == -1 && errno == EAFNOSUPPORT) {
70 socket_family = AF_INET;
71 server_socket = socket(AF_INET, SOCK_STREAM, 0);
74 if (server_socket == -1) {
76 dbprintf(("%s: stream_server: socket() failed: %s\n",
77 debug_prefix_time(NULL),
78 strerror(save_errno)));
82 if(server_socket < 0 || server_socket >= (int)FD_SETSIZE) {
83 aclose(server_socket);
84 errno = EMFILE; /* out of range */
86 dbprintf(("%s: stream_server: socket out of range: %d\n",
87 debug_prefix_time(NULL),
93 SS_INIT(&server, socket_family);
94 SS_SET_INADDR_ANY(&server);
97 r = setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR,
98 (void *)&on, (socklen_t)sizeof(on));
100 dbprintf(("%s: stream_server: setsockopt(SO_REUSEADDR) failed: %s\n",
101 debug_prefix_time(NULL),
106 try_socksize(server_socket, SO_SNDBUF, sendsize);
107 try_socksize(server_socket, SO_RCVBUF, recvsize);
110 * If a port range was specified, we try to get a port in that
111 * range first. Next, we try to get a reserved port. If that
112 * fails, we just go for any port.
114 * In all cases, not to use port that's assigned to other services.
116 * It is up to the caller to make sure we have the proper permissions
117 * to get the desired port, and to make sure we return a port that
118 * is within the range it requires.
120 for (retries = 0; ; retries++) {
122 portrange = getconf_intrange(CNF_RESERVED_TCP_PORT);
124 portrange = getconf_intrange(CNF_UNRESERVED_TCP_PORT);
127 if (portrange[0] != 0 && portrange[1] != 0) {
128 if (bind_portrange(server_socket, &server, (in_port_t)portrange[0],
129 (in_port_t)portrange[1], "tcp") == 0)
131 dbprintf(("%s: stream_server: Could not bind to port in range: %d - %d.\n",
132 debug_prefix_time(NULL), portrange[0], portrange[1]));
134 socklen = SS_LEN(&server);
135 if (bind(server_socket, (struct sockaddr *)&server, socklen) == 0)
137 dbprintf(("%s: stream_server: Could not bind to any port: %s\n",
138 debug_prefix_time(NULL), strerror(errno)));
141 if (retries >= BIND_CYCLE_RETRIES)
144 dbprintf(("%s: stream_server: Retrying entire range after 10 second delay.\n",
145 debug_prefix_time(NULL)));
151 dbprintf(("%s: stream_server: bind(in6addr_any) failed: %s\n",
152 debug_prefix_time(NULL),
153 strerror(save_errno)));
154 aclose(server_socket);
159 listen(server_socket, 1);
161 /* find out what port was actually used */
163 len = SIZEOF(server);
164 if(getsockname(server_socket, (struct sockaddr *)&server, &len) == -1) {
166 dbprintf(("%s: stream_server: getsockname() failed: %s\n",
167 debug_prefix_time(NULL),
168 strerror(save_errno)));
169 aclose(server_socket);
175 r = setsockopt(server_socket, SOL_SOCKET, SO_KEEPALIVE,
176 (void *)&on, (socklen_t)sizeof(on));
179 dbprintf(("%s: stream_server: setsockopt(SO_KEEPALIVE) failed: %s\n",
180 debug_prefix_time(NULL),
181 strerror(save_errno)));
182 aclose(server_socket);
188 *portp = SS_GET_PORT(&server);
189 dbprintf(("%s: stream_server: waiting for connection: %s\n",
190 debug_prefix_time(NULL),
191 str_sockaddr(&server)));
192 return server_socket;
196 stream_client_internal(
197 const char *hostname,
201 in_port_t *localport,
205 struct sockaddr_storage svaddr, claddr;
211 struct addrinfo hints;
212 struct addrinfo *res = NULL;
214 f = priv ? "stream_client_privileged" : "stream_client";
217 hints.ai_flags = AI_CANONNAME | AI_V4MAPPED | AI_ALL;
218 hints.ai_family = (sa_family_t)AF_UNSPEC;
220 hints.ai_flags = AI_CANONNAME;
221 hints.ai_family = (sa_family_t)AF_INET;
223 hints.ai_socktype = SOCK_DGRAM;
224 hints.ai_protocol = IPPROTO_UDP;
225 hints.ai_addrlen = 0;
226 hints.ai_addr = NULL;
227 hints.ai_canonname = NULL;
228 hints.ai_next = NULL;
229 result = getaddrinfo(hostname, NULL, &hints, &res);
232 hints.ai_flags = AI_CANONNAME;
233 hints.ai_family = AF_UNSPEC;
234 result = getaddrinfo(hostname, NULL, &hints, &res);
238 dbprintf(("getaddrinfo: %s\n", gai_strerror(result)));
242 memcpy(&svaddr, res->ai_addr, (size_t)res->ai_addrlen);
244 SS_SET_PORT(&svaddr, port);
246 SS_INIT(&claddr, svaddr.ss_family);
247 SS_SET_INADDR_ANY(&claddr);
250 * If a privileged port range was requested, we try to get a port in
251 * that range first and fail if it is not available. Next, we try
252 * to get a port in the range built in when Amanda was configured.
253 * If that fails, we just go for any port.
255 * It is up to the caller to make sure we have the proper permissions
256 * to get the desired port, and to make sure we return a port that
257 * is within the range it requires.
260 portrange = getconf_intrange(CNF_RESERVED_TCP_PORT);
262 portrange = getconf_intrange(CNF_UNRESERVED_TCP_PORT);
264 client_socket = connect_portrange(&claddr, (in_port_t)portrange[0],
265 (in_port_t)portrange[1],
266 "tcp", &svaddr, nonblock);
269 if (client_socket > 0)
272 dbprintf(("%s: stream_client: Could not bind to port in range %d-%d.\n",
273 debug_prefix_time(NULL), portrange[0], portrange[1]));
279 try_socksize(client_socket, SO_SNDBUF, sendsize);
280 try_socksize(client_socket, SO_RCVBUF, recvsize);
281 if (localport != NULL)
282 *localport = SS_GET_PORT(&claddr);
283 return client_socket;
287 stream_client_privileged(
288 const char *hostname,
292 in_port_t *localport,
295 return stream_client_internal(hostname,
306 const char *hostname,
310 in_port_t *localport,
313 return stream_client_internal(hostname,
322 /* don't care about these values */
323 static struct sockaddr_storage addr;
324 static socklen_t addrlen;
333 SELECT_ARG_TYPE readset;
335 int nfound, connected_socket;
340 assert(server_socket >= 0);
344 memset(&tv, 0, SIZEOF(tv));
346 memset(&readset, 0, SIZEOF(readset));
348 FD_SET(server_socket, &readset);
349 nfound = select(server_socket+1, &readset, NULL, NULL, &tv);
350 if(nfound <= 0 || !FD_ISSET(server_socket, &readset)) {
353 dbprintf(("%s: stream_accept: select() failed: %s\n",
354 debug_prefix_time(NULL),
355 strerror(save_errno)));
356 } else if(nfound == 0) {
357 dbprintf(("%s: stream_accept: timeout after %d second%s\n",
358 debug_prefix_time(NULL),
360 (timeout == 1) ? "" : "s"));
361 errno = ENOENT; /* ??? */
363 } else if (!FD_ISSET(server_socket, &readset)) {
366 for(i = 0; i < server_socket + 1; i++) {
367 if(FD_ISSET(i, &readset)) {
368 dbprintf(("%s: stream_accept: got fd %d instead of %d\n",
369 debug_prefix_time(NULL),
381 } while (nfound <= 0);
384 addrlen = (socklen_t)sizeof(struct sockaddr_storage);
385 connected_socket = accept(server_socket,
386 (struct sockaddr *)&addr,
388 if(connected_socket < 0) {
391 dbprintf(("%s: stream_accept: connection from %s\n",
392 debug_prefix_time(NULL),
393 str_sockaddr(&addr)));
395 * Make certain we got an inet connection and that it is not
396 * from port 20 (a favorite unauthorized entry tool).
398 if (addr.ss_family == (sa_family_t)AF_INET
400 || addr.ss_family == (sa_family_t)AF_INET6
403 port = SS_GET_PORT(&addr);
404 if (port != (in_port_t)20) {
405 try_socksize(connected_socket, SO_SNDBUF, sendsize);
406 try_socksize(connected_socket, SO_RCVBUF, recvsize);
407 return connected_socket;
409 dbprintf(("%s: remote port is %u: ignored\n",
410 debug_prefix_time(NULL), (unsigned int)port));
414 dbprintf(("%s: family is %d instead of %d(AF_INET)"
415 " or %d(AF_INET6): ignored\n",
416 debug_prefix_time(NULL),
420 dbprintf(("%s: family is %d instead of %d(AF_INET)"
422 debug_prefix_time(NULL),
427 aclose(connected_socket);
431 dbprintf(("%s: stream_accept: accept() failed: %s\n",
432 debug_prefix_time(NULL),
433 strerror(save_errno)));
452 /* keep trying, get as big a buffer as possible */
453 while((isize > 1024) &&
454 (setsockopt(sock, SOL_SOCKET,
455 which, (void *) &isize, (socklen_t)sizeof(isize)) < 0)) {
459 dbprintf(("%s: try_socksize: %s buffer size is %d\n",
460 debug_prefix_time(NULL),
461 (which == SO_SNDBUF) ? "send" : "receive",
464 dbprintf(("%s: try_socksize: could not allocate %s buffer of " SIZE_T_FMT "\n",
465 debug_prefix_time(NULL),
466 (which == SO_SNDBUF) ? "send" : "receive",