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.2.1 2006/12/12 14:56:38 martinea Exp $
30 * functions for managing stream sockets
38 static void try_socksize(int sock, int which, size_t size);
39 static int stream_client_internal(const char *hostname, in_port_t port,
40 size_t sendsize, size_t recvsize, in_port_t *localport,
41 int nonblock, int priv);
50 int server_socket, retries;
52 #if defined(SO_KEEPALIVE) || defined(USE_REUSEADDR)
56 struct sockaddr_in server;
59 *portp = USHRT_MAX; /* in case we error exit */
60 if((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
62 dbprintf(("%s: stream_server: socket() failed: %s\n",
64 strerror(save_errno)));
68 if(server_socket < 0 || server_socket >= (int)FD_SETSIZE) {
69 aclose(server_socket);
70 errno = EMFILE; /* out of range */
72 dbprintf(("%s: stream_server: socket out of range: %d\n",
78 memset(&server, 0, SIZEOF(server));
79 server.sin_family = (sa_family_t)AF_INET;
80 server.sin_addr.s_addr = INADDR_ANY;
83 r = setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR,
84 (void *)&on, (socklen_t)sizeof(on));
86 dbprintf(("%s: stream_server: setsockopt(SO_REUSEADDR) failed: %s\n",
92 try_socksize(server_socket, SO_SNDBUF, sendsize);
93 try_socksize(server_socket, SO_RCVBUF, recvsize);
96 * If a port range was specified, we try to get a port in that
97 * range first. Next, we try to get a reserved port. If that
98 * fails, we just go for any port.
100 * In all cases, not to use port that's assigned to other services.
102 * It is up to the caller to make sure we have the proper permissions
103 * to get the desired port, and to make sure we return a port that
104 * is within the range it requires.
106 for (retries = 0; ; retries++) {
108 if (bind_portrange(server_socket, &server, TCPPORTRANGE, "tcp") == 0)
110 dbprintf(("%s: stream_server: Could not bind to port in range: %d - %d.\n",
111 debug_prefix(NULL), TCPPORTRANGE));
115 if (bind_portrange(server_socket, &server,
116 (in_port_t)512, (in_port_t)(IPPORT_RESERVED - 1), "tcp") == 0)
118 dbprintf(("%s: stream_server: Could not bind to port in range 512 - %d.\n",
119 debug_prefix(NULL), IPPORT_RESERVED - 1));
122 server.sin_port = INADDR_ANY;
123 if (bind(server_socket, (struct sockaddr *)&server, (socklen_t)sizeof(server)) == 0)
125 dbprintf(("%s: stream_server: Could not bind to any port: %s\n",
126 debug_prefix(NULL), strerror(errno)));
128 if (retries >= BIND_CYCLE_RETRIES)
131 dbprintf(("%s: stream_server: Retrying entire range after 10 second delay.\n",
132 debug_prefix(NULL)));
138 dbprintf(("%s: stream_server: bind(INADDR_ANY) failed: %s\n",
140 strerror(save_errno)));
141 aclose(server_socket);
146 listen(server_socket, 1);
148 /* find out what port was actually used */
150 len = SIZEOF(server);
151 if(getsockname(server_socket, (struct sockaddr *)&server, &len) == -1) {
153 dbprintf(("%s: stream_server: getsockname() failed: %s\n",
155 strerror(save_errno)));
156 aclose(server_socket);
162 r = setsockopt(server_socket, SOL_SOCKET, SO_KEEPALIVE,
163 (void *)&on, (socklen_t)sizeof(on));
166 dbprintf(("%s: stream_server: setsockopt(SO_KEEPALIVE) failed: %s\n",
168 strerror(save_errno)));
169 aclose(server_socket);
175 *portp = (in_port_t)ntohs(server.sin_port);
176 dbprintf(("%s: stream_server: waiting for connection: %s.%d\n",
177 debug_prefix_time(NULL),
178 inet_ntoa(server.sin_addr),
180 return server_socket;
184 stream_client_internal(
185 const char *hostname,
189 in_port_t *localport,
193 struct sockaddr_in svaddr, claddr;
194 struct hostent *hostp;
199 f = priv ? "stream_client_privileged" : "stream_client";
201 if((hostp = gethostbyname(hostname)) == NULL) {
202 save_errno = EHOSTUNREACH;
203 dbprintf(("%s: %s: gethostbyname(%s) failed\n",
211 memset(&svaddr, 0, SIZEOF(svaddr));
212 svaddr.sin_family = (sa_family_t)AF_INET;
213 svaddr.sin_port = (in_port_t)htons(port);
214 memcpy(&svaddr.sin_addr, hostp->h_addr, (size_t)hostp->h_length);
217 memset(&claddr, 0, SIZEOF(claddr));
218 claddr.sin_family = (sa_family_t)AF_INET;
219 claddr.sin_addr.s_addr = INADDR_ANY;
222 * If a privileged port range was requested, we try to get a port in
223 * that range first and fail if it is not available. Next, we try
224 * to get a port in the range built in when Amanda was configured.
225 * If that fails, we just go for any port.
227 * It is up to the caller to make sure we have the proper permissions
228 * to get the desired port, and to make sure we return a port that
229 * is within the range it requires.
232 #ifdef LOW_TCPPORTRANGE
233 client_socket = connect_portrange(&claddr, LOW_TCPPORTRANGE,
234 "tcp", &svaddr, nonblock);
236 client_socket = connect_portrange(&claddr, (socklen_t)512,
237 (socklen_t)(IPPORT_RESERVED - 1),
238 "tcp", &svaddr, nonblock);
241 if (client_socket > 0)
244 #ifdef LOW_TCPPORTRANGE
246 "%s: stream_client: Could not bind to port in range %d-%d.\n",
247 debug_prefix(NULL), LOW_TCPPORTRANGE));
250 "%s: stream_client: Could not bind to port in range 512-%d.\n",
251 debug_prefix(NULL), IPPORT_RESERVED - 1));
256 client_socket = connect_portrange(&claddr, TCPPORTRANGE,
257 "tcp", &svaddr, nonblock);
258 if(client_socket > 0)
261 dbprintf(("%s: stream_client: Could not bind to port in range %d - %d.\n",
262 debug_prefix(NULL), TCPPORTRANGE));
265 client_socket = connect_portrange(&claddr, (socklen_t)(IPPORT_RESERVED+1),
267 "tcp", &svaddr, nonblock);
269 if(client_socket > 0)
273 dbprintf(("%s: stream_client: Could not bind to any port: %s\n",
274 debug_prefix(NULL), strerror(save_errno)));
279 try_socksize(client_socket, SO_SNDBUF, sendsize);
280 try_socksize(client_socket, SO_RCVBUF, recvsize);
281 if (localport != NULL)
282 *localport = (in_port_t)ntohs(claddr.sin_port);
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_in addr;
324 static socklen_t addrlen;
333 SELECT_ARG_TYPE readset;
335 int nfound, connected_socket;
339 assert(server_socket >= 0);
343 memset(&tv, 0, SIZEOF(tv));
345 memset(&readset, 0, SIZEOF(readset));
347 FD_SET(server_socket, &readset);
348 nfound = select(server_socket+1, &readset, NULL, NULL, &tv);
349 if(nfound <= 0 || !FD_ISSET(server_socket, &readset)) {
352 dbprintf(("%s: stream_accept: select() failed: %s\n",
353 debug_prefix_time(NULL),
354 strerror(save_errno)));
355 } else if(nfound == 0) {
356 dbprintf(("%s: stream_accept: timeout after %d second%s\n",
357 debug_prefix_time(NULL),
359 (timeout == 1) ? "" : "s"));
360 errno = ENOENT; /* ??? */
362 } else if (!FD_ISSET(server_socket, &readset)) {
365 for(i = 0; i < server_socket + 1; i++) {
366 if(FD_ISSET(i, &readset)) {
367 dbprintf(("%s: stream_accept: got fd %d instead of %d\n",
368 debug_prefix_time(NULL),
380 } while (nfound <= 0);
383 addrlen = (socklen_t)sizeof(struct sockaddr);
384 connected_socket = accept(server_socket,
385 (struct sockaddr *)&addr,
387 if(connected_socket < 0) {
390 dbprintf(("%s: stream_accept: connection from %s.%d\n",
391 debug_prefix_time(NULL),
392 inet_ntoa(addr.sin_addr),
393 ntohs(addr.sin_port)));
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.sin_family == (sa_family_t)AF_INET)
399 && (ntohs(addr.sin_port) != (in_port_t)20)) {
400 try_socksize(connected_socket, SO_SNDBUF, sendsize);
401 try_socksize(connected_socket, SO_RCVBUF, recvsize);
402 return connected_socket;
404 if(addr.sin_family != (sa_family_t)AF_INET) {
405 dbprintf(("%s: family is %d instead of %d(AF_INET): ignored\n",
406 debug_prefix_time(NULL),
410 if(ntohs(addr.sin_port) == 20) {
411 dbprintf(("%s: remote port is %d: ignored\n",
412 debug_prefix_time(NULL),
413 ntohs(addr.sin_port)));
415 aclose(connected_socket);
419 dbprintf(("%s: stream_accept: accept() failed: %s\n",
420 debug_prefix_time(NULL),
421 strerror(save_errno)));
438 /* keep trying, get as big a buffer as possible */
439 while((size > 1024) &&
440 (setsockopt(sock, SOL_SOCKET,
441 which, (void *) &size, (socklen_t)sizeof(int)) < 0)) {
445 dbprintf(("%s: try_socksize: %s buffer size is %zu\n",
447 (which == SO_SNDBUF) ? "send" : "receive",
450 dbprintf(("%s: try_socksize: could not allocate %s buffer of %zu\n",
452 (which == SO_SNDBUF) ? "send" : "receive",