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.10.2.6.4.4.2.4.2.1 2005/10/02 13:48:42 martinea Exp $
30 * functions for managing stream sockets
39 static void try_socksize P((int sock, int which, int size));
41 int stream_server(portp, sendsize, recvsize)
43 int sendsize, recvsize;
51 struct sockaddr_in server;
54 *portp = -1; /* in case we error exit */
55 if((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
57 dbprintf(("%s: stream_server: socket() failed: %s\n",
59 strerror(save_errno)));
63 if(server_socket < 0 || server_socket >= FD_SETSIZE) {
64 aclose(server_socket);
65 errno = EMFILE; /* out of range */
67 dbprintf(("%s: stream_server: socket out of range: %d\n",
73 memset(&server, 0, sizeof(server));
74 server.sin_family = AF_INET;
75 server.sin_addr.s_addr = INADDR_ANY;
78 try_socksize(server_socket, SO_SNDBUF, sendsize);
80 try_socksize(server_socket, SO_RCVBUF, recvsize);
83 * If a port range was specified, we try to get a port in that
84 * range first. Next, we try to get a reserved port. If that
85 * fails, we just go for any port.
87 * It is up to the caller to make sure we have the proper permissions
88 * to get the desired port, and to make sure we return a port that
89 * is within the range it requires.
92 if (bind_portrange(server_socket, &server, TCPPORTRANGE) == 0)
96 if (bind_portrange(server_socket, &server, 512, IPPORT_RESERVED - 1) == 0)
99 server.sin_port = INADDR_ANY;
100 if (bind(server_socket, (struct sockaddr *)&server, (socklen_t) sizeof(server)) == -1) {
102 dbprintf(("%s: stream_server: bind(INADDR_ANY) failed: %s\n",
104 strerror(save_errno)));
105 aclose(server_socket);
111 listen(server_socket, 1);
113 /* find out what port was actually used */
115 len = sizeof(server);
116 if(getsockname(server_socket, (struct sockaddr *)&server, &len) == -1) {
118 dbprintf(("%s: stream_server: getsockname() failed: %s\n",
120 strerror(save_errno)));
121 aclose(server_socket);
127 r = setsockopt(server_socket, SOL_SOCKET, SO_KEEPALIVE,
128 (void *)&on, (socklen_t)sizeof(on));
131 dbprintf(("%s: stream_server: setsockopt(SO_KEEPALIVE) failed: %s\n",
133 strerror(save_errno)));
134 aclose(server_socket);
140 *portp = (int) ntohs(server.sin_port);
141 dbprintf(("%s: stream_server: waiting for connection: %s.%d\n",
142 debug_prefix_time(NULL),
143 inet_ntoa(server.sin_addr),
145 return server_socket;
149 stream_client_internal(hostname, port, sendsize, recvsize, localport, priv)
151 int port, sendsize, recvsize, *localport, priv;
159 struct sockaddr_in svaddr, claddr;
160 struct hostent *hostp;
164 f = priv ? "stream_client_privileged" : "stream_client";
166 if((hostp = gethostbyname(hostname)) == NULL) {
168 dbprintf(("%s: %s: gethostbyname(%s) failed\n",
176 memset(&svaddr, 0, sizeof(svaddr));
177 svaddr.sin_family = AF_INET;
178 svaddr.sin_port = htons(port);
179 memcpy(&svaddr.sin_addr, hostp->h_addr, hostp->h_length);
181 if((client_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
183 dbprintf(("%s: %s: socket() failed: %s\n",
186 strerror(save_errno)));
190 if(client_socket < 0 || client_socket >= FD_SETSIZE) {
191 aclose(client_socket);
192 errno = EMFILE; /* out of range */
197 r = setsockopt(client_socket, SOL_SOCKET, SO_KEEPALIVE,
198 (void *)&on, sizeof(on));
201 dbprintf(("%s: %s: setsockopt() failed: %s\n",
204 strerror(save_errno)));
205 aclose(client_socket);
211 memset(&claddr, 0, sizeof(claddr));
212 claddr.sin_family = AF_INET;
213 claddr.sin_addr.s_addr = INADDR_ANY;
216 * If a privileged port range was requested, we try to get a port in
217 * that range first and fail if it is not available. Next, we try
218 * to get a port in the range built in when Amanda was configured.
219 * If that fails, we just go for any port.
221 * It is up to the caller to make sure we have the proper permissions
222 * to get the desired port, and to make sure we return a port that
223 * is within the range it requires.
228 b = bind_portrange(client_socket, &claddr, 512, IPPORT_RESERVED - 1);
230 goto out; /* got what we wanted */
233 dbprintf(("%s: %s: bind(IPPORT_RESERVED) failed: %s\n",
236 strerror(save_errno)));
237 aclose(client_socket);
243 if (bind_portrange(client_socket, &claddr, TCPPORTRANGE) == 0)
247 claddr.sin_port = INADDR_ANY;
248 if (bind(client_socket, (struct sockaddr *)&claddr, sizeof(claddr)) == -1) {
250 dbprintf(("%s: %s: bind(INADDR_ANY) failed: %s\n",
253 strerror(save_errno)));
254 aclose(client_socket);
261 /* find out what port was actually used */
263 len = sizeof(claddr);
264 if(getsockname(client_socket, (struct sockaddr *)&claddr, &len) == -1) {
266 dbprintf(("%s: %s: getsockname() failed: %s\n",
269 strerror(save_errno)));
270 aclose(client_socket);
275 if(connect(client_socket, (struct sockaddr *)&svaddr, sizeof(svaddr))
278 dbprintf(("%s: %s: connect to %s.%d failed: %s\n",
279 debug_prefix_time(NULL),
281 inet_ntoa(svaddr.sin_addr),
282 ntohs(svaddr.sin_port),
283 strerror(save_errno)));
284 aclose(client_socket);
289 dbprintf(("%s: %s: connected to %s.%d\n",
290 debug_prefix_time(NULL),
292 inet_ntoa(svaddr.sin_addr),
293 ntohs(svaddr.sin_port)));
294 dbprintf(("%s: %s: our side is %s.%d\n",
297 inet_ntoa(claddr.sin_addr),
298 ntohs(claddr.sin_port)));
301 try_socksize(client_socket, SO_SNDBUF, sendsize);
303 try_socksize(client_socket, SO_RCVBUF, recvsize);
305 if (localport != NULL)
306 *localport = ntohs(claddr.sin_port);
308 return client_socket;
312 stream_client_privileged(hostname, port, sendsize, recvsize, localport)
314 int port, sendsize, recvsize, *localport;
316 return stream_client_internal(hostname,
325 stream_client(hostname, port, sendsize, recvsize, localport)
327 int port, sendsize, recvsize, *localport;
329 return stream_client_internal(hostname,
337 /* don't care about these values */
338 static struct sockaddr_in addr;
339 static socklen_t addrlen;
341 int stream_accept(server_socket, timeout, sendsize, recvsize)
342 int server_socket, timeout, sendsize, recvsize;
346 int nfound, connected_socket;
349 assert(server_socket >= 0);
354 FD_SET(server_socket, &readset);
355 nfound = select(server_socket+1,
356 (SELECT_ARG_TYPE *)&readset,
360 if(nfound <= 0 || !FD_ISSET(server_socket, &readset)) {
363 dbprintf(("%s: stream_accept: select() failed: %s\n",
364 debug_prefix_time(NULL),
365 strerror(save_errno)));
366 } else if(nfound == 0) {
367 dbprintf(("%s: stream_accept: timeout after %d second%s\n",
368 debug_prefix_time(NULL),
370 (timeout == 1) ? "" : "s"));
371 save_errno = ENOENT; /* ??? */
372 } else if (!FD_ISSET(server_socket, &readset)) {
375 for(i = 0; i < server_socket + 1; i++) {
376 if(FD_ISSET(i, &readset)) {
377 dbprintf(("%s: stream_accept: got fd %d instead of %d\n",
378 debug_prefix_time(NULL),
390 addrlen = sizeof(struct sockaddr);
391 connected_socket = accept(server_socket,
392 (struct sockaddr *)&addr,
394 if(connected_socket < 0) {
396 dbprintf(("%s: stream_accept: accept() failed: %s\n",
397 debug_prefix_time(NULL),
398 strerror(save_errno)));
402 dbprintf(("%s: stream_accept: connection from %s.%d\n",
403 debug_prefix_time(NULL),
404 inet_ntoa(addr.sin_addr),
405 ntohs(addr.sin_port)));
407 * Make certain we got an inet connection and that it is not
408 * from port 20 (a favorite unauthorized entry tool).
410 if(addr.sin_family == AF_INET && ntohs(addr.sin_port) != 20) {
413 if(addr.sin_family != AF_INET) {
414 dbprintf(("%s: family is %d instead of %d(AF_INET): ignored\n",
415 debug_prefix_time(NULL),
419 if(ntohs(addr.sin_port) == 20) {
420 dbprintf(("%s: remote port is %d: ignored\n",
421 debug_prefix_time(NULL),
422 ntohs(addr.sin_port)));
424 aclose(connected_socket);
428 try_socksize(connected_socket, SO_SNDBUF, sendsize);
430 try_socksize(connected_socket, SO_RCVBUF, recvsize);
432 return connected_socket;
435 static void try_socksize(sock, which, size)
436 int sock, which, size;
441 /* keep trying, get as big a buffer as possible */
443 setsockopt(sock, SOL_SOCKET, which, (void *) &size, sizeof(int)) < 0)
446 dbprintf(("%s: try_socksize: %s buffer size is %d\n",
448 (which == SO_SNDBUF) ? "send" : "receive",
451 dbprintf(("%s: try_socksize: could not allocate %s buffer of %d\n",
453 (which == SO_SNDBUF) ? "send" : "receive",