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.31 2006/01/14 04:37:19 paddy_s Exp $
30 * functions for managing stream sockets
38 static void try_socksize P((int sock, int which, int size));
40 int stream_server(portp, sendsize, recvsize)
42 int sendsize, recvsize;
50 struct sockaddr_in server;
53 *portp = -1; /* in case we error exit */
54 if((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
56 dbprintf(("%s: stream_server: socket() failed: %s\n",
58 strerror(save_errno)));
62 if(server_socket < 0 || server_socket >= FD_SETSIZE) {
63 aclose(server_socket);
64 errno = EMFILE; /* out of range */
66 dbprintf(("%s: stream_server: socket out of range: %d\n",
72 memset(&server, 0, sizeof(server));
73 server.sin_family = AF_INET;
74 server.sin_addr.s_addr = INADDR_ANY;
77 try_socksize(server_socket, SO_SNDBUF, sendsize);
79 try_socksize(server_socket, SO_RCVBUF, recvsize);
82 * If a port range was specified, we try to get a port in that
83 * range first. Next, we try to get a reserved port. If that
84 * fails, we just go for any port.
86 * In all cases, not to use port that's assigned to other services.
88 * It is up to the caller to make sure we have the proper permissions
89 * to get the desired port, and to make sure we return a port that
90 * is within the range it requires.
93 if (bind_portrange(server_socket, &server, TCPPORTRANGE, "tcp") == 0)
97 if (bind_portrange(server_socket, &server, 512, IPPORT_RESERVED - 1, "tcp") == 0)
100 server.sin_port = INADDR_ANY;
101 if (bind(server_socket, (struct sockaddr *)&server, (socklen_t) sizeof(server)) == -1) {
103 dbprintf(("%s: stream_server: bind(INADDR_ANY) failed: %s\n",
105 strerror(save_errno)));
106 aclose(server_socket);
112 listen(server_socket, 1);
114 /* find out what port was actually used */
116 len = sizeof(server);
117 if(getsockname(server_socket, (struct sockaddr *)&server, &len) == -1) {
119 dbprintf(("%s: stream_server: getsockname() failed: %s\n",
121 strerror(save_errno)));
122 aclose(server_socket);
128 r = setsockopt(server_socket, SOL_SOCKET, SO_KEEPALIVE,
129 (void *)&on, (socklen_t)sizeof(on));
132 dbprintf(("%s: stream_server: setsockopt(SO_KEEPALIVE) failed: %s\n",
134 strerror(save_errno)));
135 aclose(server_socket);
141 *portp = (int) ntohs(server.sin_port);
142 dbprintf(("%s: stream_server: waiting for connection: %s.%d\n",
143 debug_prefix_time(NULL),
144 inet_ntoa(server.sin_addr),
146 return server_socket;
150 stream_client_internal(hostname,
157 const char *hostname;
158 int port, sendsize, recvsize, *localport, nonblock, priv;
166 struct sockaddr_in svaddr, claddr;
167 struct hostent *hostp;
171 f = priv ? "stream_client_privileged" : "stream_client";
173 if((hostp = gethostbyname(hostname)) == NULL) {
175 dbprintf(("%s: %s: gethostbyname(%s) failed\n",
183 memset(&svaddr, 0, sizeof(svaddr));
184 svaddr.sin_family = AF_INET;
185 svaddr.sin_port = htons(port);
186 memcpy(&svaddr.sin_addr, hostp->h_addr, hostp->h_length);
188 if((client_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
190 dbprintf(("%s: %s: socket() failed: %s\n",
193 strerror(save_errno)));
197 if(client_socket < 0 || client_socket >= FD_SETSIZE) {
198 aclose(client_socket);
199 errno = EMFILE; /* out of range */
204 r = setsockopt(client_socket, SOL_SOCKET, SO_KEEPALIVE,
205 (void *)&on, sizeof(on));
208 dbprintf(("%s: %s: setsockopt() failed: %s\n",
211 strerror(save_errno)));
212 aclose(client_socket);
218 memset(&claddr, 0, sizeof(claddr));
219 claddr.sin_family = AF_INET;
220 claddr.sin_addr.s_addr = INADDR_ANY;
223 * If a privileged port range was requested, we try to get a port in
224 * that range first and fail if it is not available. Next, we try
225 * to get a port in the range built in when Amanda was configured.
226 * If that fails, we just go for any port.
228 * It is up to the caller to make sure we have the proper permissions
229 * to get the desired port, and to make sure we return a port that
230 * is within the range it requires.
235 b = bind_portrange(client_socket, &claddr, 512, IPPORT_RESERVED - 1, "tcp");
237 goto out; /* got what we wanted */
240 dbprintf(("%s: %s: bind(IPPORT_RESERVED) failed: %s\n",
243 strerror(save_errno)));
244 aclose(client_socket);
250 if (bind_portrange(client_socket, &claddr, TCPPORTRANGE, "tcp") == 0)
254 claddr.sin_port = INADDR_ANY;
255 if (bind(client_socket, (struct sockaddr *)&claddr, sizeof(claddr)) == -1) {
257 dbprintf(("%s: %s: bind(INADDR_ANY) failed: %s\n",
260 strerror(save_errno)));
261 aclose(client_socket);
268 /* find out what port was actually used */
270 len = sizeof(claddr);
271 if(getsockname(client_socket, (struct sockaddr *)&claddr, &len) == -1) {
273 dbprintf(("%s: %s: getsockname() failed: %s\n",
276 strerror(save_errno)));
277 aclose(client_socket);
283 fcntl(client_socket, F_SETFL,
284 fcntl(client_socket, F_GETFL, 0)|O_NONBLOCK);
286 if(connect(client_socket, (struct sockaddr *)&svaddr, sizeof(svaddr))
287 == -1 && !nonblock) {
289 dbprintf(("%s: %s: connect to %s.%d failed: %s\n",
290 debug_prefix_time(NULL),
292 inet_ntoa(svaddr.sin_addr),
293 ntohs(svaddr.sin_port),
294 strerror(save_errno)));
295 aclose(client_socket);
300 dbprintf(("%s: %s: connected to %s.%d\n",
301 debug_prefix_time(NULL),
303 inet_ntoa(svaddr.sin_addr),
304 ntohs(svaddr.sin_port)));
305 dbprintf(("%s: %s: our side is %s.%d\n",
308 inet_ntoa(claddr.sin_addr),
309 ntohs(claddr.sin_port)));
312 try_socksize(client_socket, SO_SNDBUF, sendsize);
314 try_socksize(client_socket, SO_RCVBUF, recvsize);
316 if (localport != NULL)
317 *localport = ntohs(claddr.sin_port);
319 return client_socket;
323 stream_client_privileged(hostname,
329 const char *hostname;
330 int port, sendsize, recvsize, *localport, nonblock;
332 return stream_client_internal(hostname,
342 stream_client(hostname, port, sendsize, recvsize, localport, nonblock)
343 const char *hostname;
344 int port, sendsize, recvsize, *localport, nonblock;
346 return stream_client_internal(hostname,
355 /* don't care about these values */
356 static struct sockaddr_in addr;
357 static socklen_t addrlen;
359 int stream_accept(server_socket, timeout, sendsize, recvsize)
360 int server_socket, timeout, sendsize, recvsize;
364 int nfound, connected_socket;
367 assert(server_socket >= 0);
372 FD_SET(server_socket, &readset);
373 nfound = select(server_socket+1, (SELECT_ARG_TYPE *)&readset, NULL, NULL, &tv);
374 if(nfound <= 0 || !FD_ISSET(server_socket, &readset)) {
377 dbprintf(("%s: stream_accept: select() failed: %s\n",
378 debug_prefix_time(NULL),
379 strerror(save_errno)));
380 } else if(nfound == 0) {
381 dbprintf(("%s: stream_accept: timeout after %d second%s\n",
382 debug_prefix_time(NULL),
384 (timeout == 1) ? "" : "s"));
385 save_errno = ENOENT; /* ??? */
386 } else if (!FD_ISSET(server_socket, &readset)) {
389 for(i = 0; i < server_socket + 1; i++) {
390 if(FD_ISSET(i, &readset)) {
391 dbprintf(("%s: stream_accept: got fd %d instead of %d\n",
392 debug_prefix_time(NULL),
404 addrlen = sizeof(struct sockaddr);
405 connected_socket = accept(server_socket,
406 (struct sockaddr *)&addr,
408 if(connected_socket < 0) {
410 dbprintf(("%s: stream_accept: accept() failed: %s\n",
411 debug_prefix_time(NULL),
412 strerror(save_errno)));
416 dbprintf(("%s: stream_accept: connection from %s.%d\n",
417 debug_prefix_time(NULL),
418 inet_ntoa(addr.sin_addr),
419 ntohs(addr.sin_port)));
421 * Make certain we got an inet connection and that it is not
422 * from port 20 (a favorite unauthorized entry tool).
424 if(addr.sin_family == AF_INET && ntohs(addr.sin_port) != 20) {
427 if(addr.sin_family != AF_INET) {
428 dbprintf(("%s: family is %d instead of %d(AF_INET): ignored\n",
429 debug_prefix_time(NULL),
433 if(ntohs(addr.sin_port) == 20) {
434 dbprintf(("%s: remote port is %d: ignored\n",
435 debug_prefix_time(NULL),
436 ntohs(addr.sin_port)));
438 aclose(connected_socket);
442 try_socksize(connected_socket, SO_SNDBUF, sendsize);
444 try_socksize(connected_socket, SO_RCVBUF, recvsize);
446 return connected_socket;
449 static void try_socksize(sock, which, size)
450 int sock, which, size;
455 /* keep trying, get as big a buffer as possible */
457 setsockopt(sock, SOL_SOCKET, which, (void *) &size, sizeof(int)) < 0)
460 dbprintf(("%s: try_socksize: %s buffer size is %d\n",
462 (which == SO_SNDBUF) ? "send" : "receive",
465 dbprintf(("%s: try_socksize: could not allocate %s buffer of %d\n",
467 (which == SO_SNDBUF) ? "send" : "receive",