Imported Upstream version 2.5.1
[debian/amanda] / common-src / stream.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * All Rights Reserved.
5  *
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.
15  *
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.
22  *
23  * Author: James da Silva, Systems Design and Analysis Group
24  *                         Computer Science Department
25  *                         University of Maryland at College Park
26  */
27 /*
28  * $Id: stream.c,v 1.39 2006/08/24 01:57:15 paddy_s Exp $
29  *
30  * functions for managing stream sockets
31  */
32 #include "amanda.h"
33 #include "dgram.h"
34 #include "stream.h"
35 #include "util.h"
36
37 /* local functions */
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);
42
43 int
44 stream_server(
45     in_port_t *portp,
46     size_t sendsize,
47     size_t recvsize,
48     int    priv)
49 {
50     int server_socket, retries;
51     socklen_t len;
52 #if defined(SO_KEEPALIVE) || defined(USE_REUSEADDR)
53     const int on = 1;
54     int r;
55 #endif
56     struct sockaddr_in server;
57     int save_errno;
58
59     *portp = USHRT_MAX;                         /* in case we error exit */
60     if((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
61         save_errno = errno;
62         dbprintf(("%s: stream_server: socket() failed: %s\n",
63                   debug_prefix(NULL),
64                   strerror(save_errno)));
65         errno = save_errno;
66         return -1;
67     }
68     if(server_socket < 0 || server_socket >= (int)FD_SETSIZE) {
69         aclose(server_socket);
70         errno = EMFILE;                         /* out of range */
71         save_errno = errno;
72         dbprintf(("%s: stream_server: socket out of range: %d\n",
73                   debug_prefix(NULL),
74                   server_socket));
75         errno = save_errno;
76         return -1;
77     }
78     memset(&server, 0, SIZEOF(server));
79     server.sin_family = (sa_family_t)AF_INET;
80     server.sin_addr.s_addr = INADDR_ANY;
81
82 #ifdef USE_REUSEADDR
83     r = setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR,
84         (void *)&on, (socklen_t)sizeof(on));
85     if (r < 0) {
86         dbprintf(("%s: stream_server: setsockopt(SO_REUSEADDR) failed: %s\n",
87                   debug_prefix(NULL),
88                   strerror(errno)));
89     }
90 #endif
91
92     try_socksize(server_socket, SO_SNDBUF, sendsize);
93     try_socksize(server_socket, SO_RCVBUF, recvsize);
94
95     /*
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.
99      * 
100      * In all cases, not to use port that's assigned to other services. 
101      *
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.
105      */
106     for (retries = 0; ; retries++) {
107 #ifdef TCPPORTRANGE
108         if (bind_portrange(server_socket, &server, TCPPORTRANGE, "tcp") == 0)
109             goto out;
110         dbprintf(("%s: stream_server: Could not bind to port in range: %d - %d.\n",
111                   debug_prefix(NULL), TCPPORTRANGE));
112 #endif
113
114         if(priv) {
115             if (bind_portrange(server_socket, &server,
116                            (in_port_t)512, (in_port_t)(IPPORT_RESERVED - 1), "tcp") == 0)
117                 goto out;
118             dbprintf(("%s: stream_server: Could not bind to port in range 512 - %d.\n",
119                       debug_prefix(NULL), IPPORT_RESERVED - 1));
120         }
121
122         server.sin_port = INADDR_ANY;
123         if (bind(server_socket, (struct sockaddr *)&server, (socklen_t)sizeof(server)) == 0)
124             goto out;
125         dbprintf(("%s: stream_server: Could not bind to any port: %s\n",
126                   debug_prefix(NULL), strerror(errno)));
127
128         if (retries >= BIND_CYCLE_RETRIES)
129             break;
130
131         dbprintf(("%s: stream_server: Retrying entire range after 10 second delay.\n",
132                   debug_prefix(NULL)));
133
134         sleep(15);
135     }
136
137     save_errno = errno;
138     dbprintf(("%s: stream_server: bind(INADDR_ANY) failed: %s\n",
139                   debug_prefix(NULL),
140                   strerror(save_errno)));
141     aclose(server_socket);
142     errno = save_errno;
143     return -1;
144
145 out:
146     listen(server_socket, 1);
147
148     /* find out what port was actually used */
149
150     len = SIZEOF(server);
151     if(getsockname(server_socket, (struct sockaddr *)&server, &len) == -1) {
152         save_errno = errno;
153         dbprintf(("%s: stream_server: getsockname() failed: %s\n",
154                   debug_prefix(NULL),
155                   strerror(save_errno)));
156         aclose(server_socket);
157         errno = save_errno;
158         return -1;
159     }
160
161 #ifdef SO_KEEPALIVE
162     r = setsockopt(server_socket, SOL_SOCKET, SO_KEEPALIVE,
163         (void *)&on, (socklen_t)sizeof(on));
164     if(r == -1) {
165         save_errno = errno;
166         dbprintf(("%s: stream_server: setsockopt(SO_KEEPALIVE) failed: %s\n",
167                   debug_prefix(NULL),
168                   strerror(save_errno)));
169         aclose(server_socket);
170         errno = save_errno;
171         return -1;
172     }
173 #endif
174
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),
179               *portp));
180     return server_socket;
181 }
182
183 static int
184 stream_client_internal(
185     const char *hostname,
186     in_port_t port,
187     size_t sendsize,
188     size_t recvsize,
189     in_port_t *localport,
190     int nonblock,
191     int priv)
192 {
193     struct sockaddr_in svaddr, claddr;
194     struct hostent *hostp;
195     int save_errno;
196     char *f;
197     int client_socket;
198
199     f = priv ? "stream_client_privileged" : "stream_client";
200
201     if((hostp = gethostbyname(hostname)) == NULL) {
202         save_errno = EHOSTUNREACH;
203         dbprintf(("%s: %s: gethostbyname(%s) failed\n",
204                   debug_prefix(NULL),
205                   f,
206                   hostname));
207         errno = save_errno;
208         return -1;
209     }
210
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);
215
216
217     memset(&claddr, 0, SIZEOF(claddr));
218     claddr.sin_family = (sa_family_t)AF_INET;
219     claddr.sin_addr.s_addr = INADDR_ANY;
220
221     /*
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.
226      *
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.
230      */
231     if (priv) {
232 #ifdef LOW_TCPPORTRANGE
233         client_socket = connect_portrange(&claddr, LOW_TCPPORTRANGE,
234                                           "tcp", &svaddr, nonblock);
235 #else
236         client_socket = connect_portrange(&claddr, (socklen_t)512,
237                                           (socklen_t)(IPPORT_RESERVED - 1),
238                                           "tcp", &svaddr, nonblock);
239 #endif
240                                           
241         if (client_socket > 0)
242             goto out;
243
244 #ifdef LOW_TCPPORTRANGE
245         dbprintf((
246                 "%s: stream_client: Could not bind to port in range %d-%d.\n",
247                 debug_prefix(NULL), LOW_TCPPORTRANGE));
248 #else
249         dbprintf((
250                 "%s: stream_client: Could not bind to port in range 512-%d.\n",
251                 debug_prefix(NULL), IPPORT_RESERVED - 1));
252 #endif
253     }
254
255 #ifdef TCPPORTRANGE
256     client_socket = connect_portrange(&claddr, TCPPORTRANGE,
257                                       "tcp", &svaddr, nonblock);
258     if(client_socket > 0)
259         goto out;
260
261     dbprintf(("%s: stream_client: Could not bind to port in range %d - %d.\n",
262               debug_prefix(NULL), TCPPORTRANGE));
263 #endif
264
265     client_socket = connect_portrange(&claddr, (socklen_t)(IPPORT_RESERVED+1),
266                                       (socklen_t)(65535),
267                                       "tcp", &svaddr, nonblock);
268
269     if(client_socket > 0)
270         goto out;
271
272     save_errno = errno;
273     dbprintf(("%s: stream_client: Could not bind to any port: %s\n",
274               debug_prefix(NULL), strerror(save_errno)));
275     errno = save_errno;
276     return -1;
277
278 out:
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;
284 }
285
286 int
287 stream_client_privileged(
288     const char *hostname,
289     in_port_t port,
290     size_t sendsize,
291     size_t recvsize,
292     in_port_t *localport,
293     int nonblock)
294 {
295     return stream_client_internal(hostname,
296                                   port,
297                                   sendsize,
298                                   recvsize,
299                                   localport,
300                                   nonblock,
301                                   1);
302 }
303
304 int
305 stream_client(
306     const char *hostname,
307     in_port_t port,
308     size_t sendsize,
309     size_t recvsize,
310     in_port_t *localport,
311     int nonblock)
312 {
313     return stream_client_internal(hostname,
314                                   port,
315                                   sendsize,
316                                   recvsize,
317                                   localport,
318                                   nonblock,
319                                   0);
320 }
321
322 /* don't care about these values */
323 static struct sockaddr_in addr;
324 static socklen_t addrlen;
325
326 int
327 stream_accept(
328     int server_socket,
329     int timeout,
330     size_t sendsize,
331     size_t recvsize)
332 {
333     SELECT_ARG_TYPE readset;
334     struct timeval tv;
335     int nfound, connected_socket;
336     int save_errno;
337     int ntries = 0;
338
339     assert(server_socket >= 0);
340
341     do {
342         ntries++;
343         memset(&tv, 0, SIZEOF(tv));
344         tv.tv_sec = timeout;
345         memset(&readset, 0, SIZEOF(readset));
346         FD_ZERO(&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)) {
350             save_errno = errno;
351             if(nfound < 0) {
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),
358                       timeout,
359                       (timeout == 1) ? "" : "s"));
360                 errno = ENOENT;                 /* ??? */
361                 return -1;
362             } else if (!FD_ISSET(server_socket, &readset)) {
363                 int i;
364
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),
369                               i,
370                               server_socket));
371                     }
372                 }
373                 save_errno = EBADF;
374             }
375             if (ntries > 5) {
376                 errno = save_errno;
377                 return -1;
378             }
379         }
380     } while (nfound <= 0);
381
382     while(1) {
383         addrlen = (socklen_t)sizeof(struct sockaddr);
384         connected_socket = accept(server_socket,
385                                   (struct sockaddr *)&addr,
386                                   &addrlen);
387         if(connected_socket < 0) {
388             break;
389         }
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)));
394         /*
395          * Make certain we got an inet connection and that it is not
396          * from port 20 (a favorite unauthorized entry tool).
397          */
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;
403         }
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),
407                       addr.sin_family,
408                       AF_INET));
409         }
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)));
414         }
415         aclose(connected_socket);
416     }
417
418     save_errno = errno;
419     dbprintf(("%s: stream_accept: accept() failed: %s\n",
420               debug_prefix_time(NULL),
421               strerror(save_errno)));
422     errno = save_errno;
423     return -1;
424 }
425
426 static void
427 try_socksize(
428     int sock,
429     int which,
430     size_t size)
431 {
432     size_t origsize;
433
434     if (size == 0)
435         return;
436
437     origsize = size;
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)) {
442         size -= 1024;
443     }
444     if(size > 1024) {
445         dbprintf(("%s: try_socksize: %s buffer size is %d\n",
446                   debug_prefix(NULL),
447                   (which == SO_SNDBUF) ? "send" : "receive",
448                   size));
449     } else {
450         dbprintf(("%s: try_socksize: could not allocate %s buffer of %d\n",
451                   debug_prefix(NULL),
452                   (which == SO_SNDBUF) ? "send" : "receive",
453                   origsize));
454     }
455 }