Imported Upstream version 2.5.2p1
[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 #include "conffile.h"
37 #include "security-util.h"
38
39 /* local functions */
40 static void try_socksize(int sock, int which, size_t size);
41 static int stream_client_internal(const char *hostname, in_port_t port,
42                 size_t sendsize, size_t recvsize, in_port_t *localport,
43                 int nonblock, int priv);
44
45 int
46 stream_server(
47     in_port_t *portp,
48     size_t sendsize,
49     size_t recvsize,
50     int    priv)
51 {
52     int server_socket, retries;
53     socklen_t len;
54 #if defined(SO_KEEPALIVE) || defined(USE_REUSEADDR)
55     const int on = 1;
56     int r;
57 #endif
58     struct sockaddr_storage server;
59     int save_errno;
60     int *portrange;
61     socklen_t socklen;
62     int socket_family;
63
64     *portp = USHRT_MAX;                         /* in case we error exit */
65     socket_family = AF_NATIVE;
66     server_socket = socket(AF_NATIVE, SOCK_STREAM, 0);
67 #ifdef WORKING_IPV6
68     /* if that address family actually isn't supported, just try AF_INET */
69     if (server_socket == -1 && errno == EAFNOSUPPORT) {
70         socket_family = AF_INET;
71         server_socket = socket(AF_INET, SOCK_STREAM, 0);
72     }
73 #endif
74     if (server_socket == -1) {
75         save_errno = errno;
76         dbprintf(("%s: stream_server: socket() failed: %s\n",
77                   debug_prefix_time(NULL),
78                   strerror(save_errno)));
79         errno = save_errno;
80         return -1;
81     }
82     if(server_socket < 0 || server_socket >= (int)FD_SETSIZE) {
83         aclose(server_socket);
84         errno = EMFILE;                         /* out of range */
85         save_errno = errno;
86         dbprintf(("%s: stream_server: socket out of range: %d\n",
87                   debug_prefix_time(NULL),
88                   server_socket));
89         errno = save_errno;
90         return -1;
91     }
92
93     SS_INIT(&server, socket_family);
94     SS_SET_INADDR_ANY(&server);
95
96 #ifdef USE_REUSEADDR
97     r = setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR,
98         (void *)&on, (socklen_t)sizeof(on));
99     if (r < 0) {
100         dbprintf(("%s: stream_server: setsockopt(SO_REUSEADDR) failed: %s\n",
101                   debug_prefix_time(NULL),
102                   strerror(errno)));
103     }
104 #endif
105
106     try_socksize(server_socket, SO_SNDBUF, sendsize);
107     try_socksize(server_socket, SO_RCVBUF, recvsize);
108
109     /*
110      * If a port range was specified, we try to get a port in that
111      * range first.  Next, we try to get a reserved port.  If that
112      * fails, we just go for any port.
113      * 
114      * In all cases, not to use port that's assigned to other services. 
115      *
116      * It is up to the caller to make sure we have the proper permissions
117      * to get the desired port, and to make sure we return a port that
118      * is within the range it requires.
119      */
120     for (retries = 0; ; retries++) {
121         if (priv) {
122             portrange = getconf_intrange(CNF_RESERVED_TCP_PORT);
123         } else {
124             portrange = getconf_intrange(CNF_UNRESERVED_TCP_PORT);
125         }
126
127         if (portrange[0] != 0 && portrange[1] != 0) {
128             if (bind_portrange(server_socket, &server, (in_port_t)portrange[0],
129                                (in_port_t)portrange[1], "tcp") == 0)
130                 goto out;
131             dbprintf(("%s: stream_server: Could not bind to port in range: %d - %d.\n",
132                       debug_prefix_time(NULL), portrange[0], portrange[1]));
133         } else {
134             socklen = SS_LEN(&server);
135             if (bind(server_socket, (struct sockaddr *)&server, socklen) == 0)
136                 goto out;
137             dbprintf(("%s: stream_server: Could not bind to any port: %s\n",
138                       debug_prefix_time(NULL), strerror(errno)));
139         }
140
141         if (retries >= BIND_CYCLE_RETRIES)
142             break;
143
144         dbprintf(("%s: stream_server: Retrying entire range after 10 second delay.\n",
145                   debug_prefix_time(NULL)));
146
147         sleep(15);
148     }
149
150     save_errno = errno;
151     dbprintf(("%s: stream_server: bind(in6addr_any) failed: %s\n",
152                   debug_prefix_time(NULL),
153                   strerror(save_errno)));
154     aclose(server_socket);
155     errno = save_errno;
156     return -1;
157
158 out:
159     listen(server_socket, 1);
160
161     /* find out what port was actually used */
162
163     len = SIZEOF(server);
164     if(getsockname(server_socket, (struct sockaddr *)&server, &len) == -1) {
165         save_errno = errno;
166         dbprintf(("%s: stream_server: getsockname() failed: %s\n",
167                   debug_prefix_time(NULL),
168                   strerror(save_errno)));
169         aclose(server_socket);
170         errno = save_errno;
171         return -1;
172     }
173
174 #ifdef SO_KEEPALIVE
175     r = setsockopt(server_socket, SOL_SOCKET, SO_KEEPALIVE,
176         (void *)&on, (socklen_t)sizeof(on));
177     if(r == -1) {
178         save_errno = errno;
179         dbprintf(("%s: stream_server: setsockopt(SO_KEEPALIVE) failed: %s\n",
180                   debug_prefix_time(NULL),
181                   strerror(save_errno)));
182         aclose(server_socket);
183         errno = save_errno;
184         return -1;
185     }
186 #endif
187
188     *portp = SS_GET_PORT(&server);
189     dbprintf(("%s: stream_server: waiting for connection: %s\n",
190               debug_prefix_time(NULL),
191               str_sockaddr(&server)));
192     return server_socket;
193 }
194
195 static int
196 stream_client_internal(
197     const char *hostname,
198     in_port_t port,
199     size_t sendsize,
200     size_t recvsize,
201     in_port_t *localport,
202     int nonblock,
203     int priv)
204 {
205     struct sockaddr_storage svaddr, claddr;
206     int save_errno;
207     char *f;
208     int client_socket;
209     int *portrange;
210     int result;
211     struct addrinfo hints;
212     struct addrinfo *res = NULL;
213
214     f = priv ? "stream_client_privileged" : "stream_client";
215
216 #ifdef WORKING_IPV6
217     hints.ai_flags = AI_CANONNAME | AI_V4MAPPED | AI_ALL;
218     hints.ai_family = (sa_family_t)AF_UNSPEC;
219 #else
220     hints.ai_flags = AI_CANONNAME;
221     hints.ai_family = (sa_family_t)AF_INET;
222 #endif
223     hints.ai_socktype = SOCK_DGRAM;
224     hints.ai_protocol = IPPROTO_UDP;
225     hints.ai_addrlen = 0;
226     hints.ai_addr = NULL;
227     hints.ai_canonname = NULL;
228     hints.ai_next = NULL;
229     result = getaddrinfo(hostname, NULL, &hints, &res);
230 #ifdef WORKING_IPV6
231     if(result != 0) {
232         hints.ai_flags = AI_CANONNAME;
233         hints.ai_family = AF_UNSPEC;
234         result = getaddrinfo(hostname, NULL, &hints, &res);
235     }
236 #endif
237     if(result != 0) {
238         dbprintf(("getaddrinfo: %s\n", gai_strerror(result)));
239         return -1;
240     }
241
242     memcpy(&svaddr, res->ai_addr, (size_t)res->ai_addrlen);
243     freeaddrinfo(res);
244     SS_SET_PORT(&svaddr, port);
245
246     SS_INIT(&claddr, svaddr.ss_family);
247     SS_SET_INADDR_ANY(&claddr);
248
249     /*
250      * If a privileged port range was requested, we try to get a port in
251      * that range first and fail if it is not available.  Next, we try
252      * to get a port in the range built in when Amanda was configured.
253      * If that fails, we just go for any port.
254      *
255      * It is up to the caller to make sure we have the proper permissions
256      * to get the desired port, and to make sure we return a port that
257      * is within the range it requires.
258      */
259     if (priv) {
260         portrange = getconf_intrange(CNF_RESERVED_TCP_PORT);
261     } else {
262         portrange = getconf_intrange(CNF_UNRESERVED_TCP_PORT);
263     }
264     client_socket = connect_portrange(&claddr, (in_port_t)portrange[0],
265                                       (in_port_t)portrange[1],
266                                       "tcp", &svaddr, nonblock);
267     save_errno = errno;
268                                           
269     if (client_socket > 0)
270         goto out;
271
272     dbprintf(("%s: stream_client: Could not bind to port in range %d-%d.\n",
273               debug_prefix_time(NULL), portrange[0], portrange[1]));
274
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 = SS_GET_PORT(&claddr);
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_storage 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     in_port_t port;
339
340     assert(server_socket >= 0);
341
342     do {
343         ntries++;
344         memset(&tv, 0, SIZEOF(tv));
345         tv.tv_sec = timeout;
346         memset(&readset, 0, SIZEOF(readset));
347         FD_ZERO(&readset);
348         FD_SET(server_socket, &readset);
349         nfound = select(server_socket+1, &readset, NULL, NULL, &tv);
350         if(nfound <= 0 || !FD_ISSET(server_socket, &readset)) {
351             save_errno = errno;
352             if(nfound < 0) {
353                 dbprintf(("%s: stream_accept: select() failed: %s\n",
354                       debug_prefix_time(NULL),
355                       strerror(save_errno)));
356             } else if(nfound == 0) {
357                 dbprintf(("%s: stream_accept: timeout after %d second%s\n",
358                       debug_prefix_time(NULL),
359                       timeout,
360                       (timeout == 1) ? "" : "s"));
361                 errno = ENOENT;                 /* ??? */
362                 return -1;
363             } else if (!FD_ISSET(server_socket, &readset)) {
364                 int i;
365
366                 for(i = 0; i < server_socket + 1; i++) {
367                     if(FD_ISSET(i, &readset)) {
368                         dbprintf(("%s: stream_accept: got fd %d instead of %d\n",
369                               debug_prefix_time(NULL),
370                               i,
371                               server_socket));
372                     }
373                 }
374                 save_errno = EBADF;
375             }
376             if (ntries > 5) {
377                 errno = save_errno;
378                 return -1;
379             }
380         }
381     } while (nfound <= 0);
382
383     while(1) {
384         addrlen = (socklen_t)sizeof(struct sockaddr_storage);
385         connected_socket = accept(server_socket,
386                                   (struct sockaddr *)&addr,
387                                   &addrlen);
388         if(connected_socket < 0) {
389             break;
390         }
391         dbprintf(("%s: stream_accept: connection from %s\n",
392                   debug_prefix_time(NULL),
393                   str_sockaddr(&addr)));
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.ss_family == (sa_family_t)AF_INET
399 #ifdef WORKING_IPV6
400             || addr.ss_family == (sa_family_t)AF_INET6
401 #endif
402             ){
403             port = SS_GET_PORT(&addr);
404             if (port != (in_port_t)20) {
405                 try_socksize(connected_socket, SO_SNDBUF, sendsize);
406                 try_socksize(connected_socket, SO_RCVBUF, recvsize);
407                 return connected_socket;
408             } else {
409                 dbprintf(("%s: remote port is %u: ignored\n",
410                           debug_prefix_time(NULL), (unsigned int)port));
411             }
412         } else {
413 #ifdef WORKING_IPV6
414             dbprintf(("%s: family is %d instead of %d(AF_INET)"
415                       " or %d(AF_INET6): ignored\n",
416                       debug_prefix_time(NULL),
417                       addr.ss_family,
418                       AF_INET, AF_INET6));
419 #else
420             dbprintf(("%s: family is %d instead of %d(AF_INET)"
421                       ": ignored\n",
422                       debug_prefix_time(NULL),
423                       addr.ss_family,
424                       AF_INET));
425 #endif
426         }
427         aclose(connected_socket);
428     }
429
430     save_errno = errno;
431     dbprintf(("%s: stream_accept: accept() failed: %s\n",
432               debug_prefix_time(NULL),
433               strerror(save_errno)));
434     errno = save_errno;
435     return -1;
436 }
437
438 static void
439 try_socksize(
440     int sock,
441     int which,
442     size_t size)
443 {
444     size_t origsize;
445     int    isize;
446
447     if (size == 0)
448         return;
449
450     origsize = size;
451     isize = size;
452     /* keep trying, get as big a buffer as possible */
453     while((isize > 1024) &&
454           (setsockopt(sock, SOL_SOCKET,
455                       which, (void *) &isize, (socklen_t)sizeof(isize)) < 0)) {
456         isize -= 1024;
457     }
458     if(isize > 1024) {
459         dbprintf(("%s: try_socksize: %s buffer size is %d\n",
460                   debug_prefix_time(NULL),
461                   (which == SO_SNDBUF) ? "send" : "receive",
462                   isize));
463     } else {
464         dbprintf(("%s: try_socksize: could not allocate %s buffer of " SIZE_T_FMT "\n",
465                   debug_prefix_time(NULL),
466                   (which == SO_SNDBUF) ? "send" : "receive",
467                   origsize));
468     }
469 }