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 struct sockaddr_storage server;
66 *portp = USHRT_MAX; /* in case we error exit */
68 socket_family = AF_NATIVE;
70 socket_family = family;
72 server_socket = socket(socket_family, SOCK_STREAM, 0);
75 /* if that address family actually isn't supported, just try AF_INET */
76 if (server_socket == -1 && errno == EAFNOSUPPORT) {
77 socket_family = AF_INET;
78 server_socket = socket(AF_INET, SOCK_STREAM, 0);
81 if (server_socket == -1) {
83 dbprintf(_("stream_server: socket() failed: %s\n"),
84 strerror(save_errno));
88 if(server_socket < 0 || server_socket >= (int)FD_SETSIZE) {
89 aclose(server_socket);
90 errno = EMFILE; /* out of range */
92 dbprintf(_("stream_server: socket out of range: %d\n"),
98 SS_INIT(&server, socket_family);
99 SS_SET_INADDR_ANY(&server);
102 r = setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR,
103 (void *)&on, (socklen_t)sizeof(on));
105 dbprintf(_("stream_server: setsockopt(SO_REUSEADDR) failed: %s\n"),
110 try_socksize(server_socket, SO_SNDBUF, sendsize);
111 try_socksize(server_socket, SO_RCVBUF, recvsize);
114 * If a port range was specified, we try to get a port in that
115 * range first. Next, we try to get a reserved port. If that
116 * fails, we just go for any port.
118 * In all cases, not to use port that's assigned to other services.
120 * It is up to the caller to make sure we have the proper permissions
121 * to get the desired port, and to make sure we return a port that
122 * is within the range it requires.
124 for (retries = 0; ; retries++) {
126 portrange = getconf_intrange(CNF_RESERVED_TCP_PORT);
128 portrange = getconf_intrange(CNF_UNRESERVED_TCP_PORT);
131 if (portrange[0] != 0 && portrange[1] != 0) {
132 if (bind_portrange(server_socket, &server, (in_port_t)portrange[0],
133 (in_port_t)portrange[1], "tcp") == 0)
135 dbprintf(_("stream_server: Could not bind to port in range: %d - %d.\n"),
136 portrange[0], portrange[1]);
138 socklen = SS_LEN(&server);
139 if (bind(server_socket, (struct sockaddr *)&server, socklen) == 0)
141 dbprintf(_("stream_server: Could not bind to any port: %s\n"),
145 if (retries >= BIND_CYCLE_RETRIES)
148 dbprintf(_("stream_server: Retrying entire range after 10 second delay.\n"));
154 dbprintf(_("stream_server: bind(in6addr_any) failed: %s\n"),
155 strerror(save_errno));
156 aclose(server_socket);
161 listen(server_socket, 1);
163 /* find out what port was actually used */
165 len = SIZEOF(server);
166 if(getsockname(server_socket, (struct sockaddr *)&server, &len) == -1) {
168 dbprintf(_("stream_server: getsockname() failed: %s\n"),
169 strerror(save_errno));
170 aclose(server_socket);
176 r = setsockopt(server_socket, SOL_SOCKET, SO_KEEPALIVE,
177 (void *)&on, (socklen_t)sizeof(on));
180 dbprintf(_("stream_server: setsockopt(SO_KEEPALIVE) failed: %s\n"),
181 strerror(save_errno));
182 aclose(server_socket);
188 *portp = SS_GET_PORT(&server);
189 dbprintf(_("stream_server: waiting for connection: %s\n"),
190 str_sockaddr(&server));
191 return server_socket;
195 stream_client_internal(
196 const char *hostname,
200 in_port_t *localport,
204 struct sockaddr_storage svaddr, claddr;
210 struct addrinfo *res, *res_addr;
212 f = priv ? "stream_client_privileged" : "stream_client";
214 result = resolve_hostname(hostname, SOCK_STREAM, &res, NULL);
216 dbprintf(_("resolve_hostname(%s): %s\n"), hostname, gai_strerror(result));
217 errno = EHOSTUNREACH;
221 dbprintf(_("resolve_hostname(%s): no results\n"), hostname);
222 errno = EHOSTUNREACH;
226 for (res_addr = res; res_addr != NULL; res_addr = res_addr->ai_next) {
227 /* copy the first (preferred) address we found */
228 copy_sockaddr(&svaddr, res_addr->ai_addr);
229 SS_SET_PORT(&svaddr, port);
231 SS_INIT(&claddr, svaddr.ss_family);
232 SS_SET_INADDR_ANY(&claddr);
235 * If a privileged port range was requested, we try to get a port in
236 * that range first and fail if it is not available. Next, we try
237 * to get a port in the range built in when Amanda was configured.
238 * If that fails, we just go for any port.
240 * It is up to the caller to make sure we have the proper permissions
241 * to get the desired port, and to make sure we return a port that
242 * is within the range it requires.
245 portrange = getconf_intrange(CNF_RESERVED_TCP_PORT);
247 portrange = getconf_intrange(CNF_UNRESERVED_TCP_PORT);
249 client_socket = connect_portrange(&claddr, (in_port_t)portrange[0],
250 (in_port_t)portrange[1],
251 "tcp", &svaddr, nonblock);
253 if (client_socket > 0)
259 if (client_socket > 0)
262 dbprintf(_("stream_client: Could not bind to port in range %d-%d.\n"),
263 portrange[0], portrange[1]);
269 try_socksize(client_socket, SO_SNDBUF, sendsize);
270 try_socksize(client_socket, SO_RCVBUF, recvsize);
271 if (localport != NULL)
272 *localport = SS_GET_PORT(&claddr);
273 return client_socket;
277 stream_client_privileged(
278 const char *hostname,
282 in_port_t *localport,
285 return stream_client_internal(hostname,
296 const char *hostname,
300 in_port_t *localport,
303 return stream_client_internal(hostname,
312 /* don't care about these values */
313 static struct sockaddr_storage addr;
314 static socklen_t addrlen;
323 SELECT_ARG_TYPE readset;
325 int nfound, connected_socket;
330 assert(server_socket >= 0);
334 memset(&tv, 0, SIZEOF(tv));
336 memset(&readset, 0, SIZEOF(readset));
338 FD_SET(server_socket, &readset);
339 nfound = select(server_socket+1, &readset, NULL, NULL, &tv);
340 if(nfound <= 0 || !FD_ISSET(server_socket, &readset)) {
343 dbprintf(_("stream_accept: select() failed: %s\n"),
344 strerror(save_errno));
345 } else if(nfound == 0) {
346 dbprintf(plural(_("stream_accept: timeout after %d second\n"),
347 _("stream_accept: timeout after %d seconds\n"),
350 errno = ENOENT; /* ??? */
352 } else if (!FD_ISSET(server_socket, &readset)) {
355 for(i = 0; i < server_socket + 1; i++) {
356 if(FD_ISSET(i, &readset)) {
357 dbprintf(_("stream_accept: got fd %d instead of %d\n"),
369 } while (nfound <= 0);
372 addrlen = (socklen_t)sizeof(struct sockaddr_storage);
373 connected_socket = accept(server_socket,
374 (struct sockaddr *)&addr,
376 if(connected_socket < 0) {
379 dbprintf(_("stream_accept: connection from %s\n"),
380 str_sockaddr(&addr));
382 * Make certain we got an inet connection and that it is not
383 * from port 20 (a favorite unauthorized entry tool).
385 if (addr.ss_family == (sa_family_t)AF_INET
387 || addr.ss_family == (sa_family_t)AF_INET6
390 port = SS_GET_PORT(&addr);
391 if (port != (in_port_t)20) {
392 try_socksize(connected_socket, SO_SNDBUF, sendsize);
393 try_socksize(connected_socket, SO_RCVBUF, recvsize);
394 return connected_socket;
396 dbprintf(_("remote port is %u: ignored\n"),
401 dbprintf(_("family is %d instead of %d(AF_INET)"
402 " or %d(AF_INET6): ignored\n"),
406 dbprintf(_("family is %d instead of %d(AF_INET)"
412 aclose(connected_socket);
416 dbprintf(_("stream_accept: accept() failed: %s\n"),
417 strerror(save_errno));
436 /* keep trying, get as big a buffer as possible */
437 while((isize > 1024) &&
438 (setsockopt(sock, SOL_SOCKET,
439 which, (void *) &isize, (socklen_t)sizeof(isize)) < 0)) {
443 dbprintf(_("try_socksize: %s buffer size is %d\n"),
444 (which == SO_SNDBUF) ? _("send") : _("receive"),
447 dbprintf(_("try_socksize: could not allocate %s buffer of %zu\n"),
448 (which == SO_SNDBUF) ? _("send") : _("receive"),