96d3b6fdab00a0b24a1419c4f502a6e1d21906aa
[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 #include "sockaddr-util.h"
39
40 /* local functions */
41 static void try_socksize(int sock, int which, size_t size);
42 static int stream_client_internal(const char *hostname, in_port_t port,
43                 size_t sendsize, size_t recvsize, in_port_t *localport,
44                 int nonblock, int priv);
45
46 int
47 stream_server(
48     int family,
49     in_port_t *portp,
50     size_t sendsize,
51     size_t recvsize,
52     int    priv)
53 {
54     int server_socket, retries;
55     socklen_t_equiv len;
56 #if defined(SO_KEEPALIVE) || defined(USE_REUSEADDR)
57     const int on = 1;
58     int r;
59 #endif
60     sockaddr_union server;
61     int save_errno;
62     int *portrange;
63     socklen_t_equiv socklen;
64     int socket_family;
65
66     *portp = USHRT_MAX;                         /* in case we error exit */
67     if (family == -1) {
68         socket_family = AF_NATIVE;
69     } else {
70         socket_family = family;
71     }
72     g_debug("stream_server opening socket with family %d (requested family was %d)", socket_family, family);
73     server_socket = socket(socket_family, SOCK_STREAM, 0);
74
75 #ifdef WORKING_IPV6
76     /* if that address family actually isn't supported, just try AF_INET */
77     if (server_socket == -1 && errno == EAFNOSUPPORT) {
78         g_debug("stream_server retrying socket with AF_INET");
79         socket_family = AF_INET;
80         server_socket = socket(AF_INET, SOCK_STREAM, 0);
81     }
82 #endif
83     if (server_socket == -1) {
84         save_errno = errno;
85         g_debug(_("stream_server: socket() failed: %s"),
86                   strerror(save_errno));
87         errno = save_errno;
88         return -1;
89     }
90     if(server_socket < 0 || server_socket >= (int)FD_SETSIZE) {
91         aclose(server_socket);
92         errno = EMFILE;                         /* out of range */
93         save_errno = errno;
94         g_debug(_("stream_server: socket out of range: %d"),
95                   server_socket);
96         errno = save_errno;
97         return -1;
98     }
99
100     SU_INIT(&server, socket_family);
101     SU_SET_INADDR_ANY(&server);
102
103 #ifdef USE_REUSEADDR
104     r = setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR,
105         (void *)&on, (socklen_t_equiv)sizeof(on));
106     if (r < 0) {
107         g_debug(_("stream_server: setsockopt(SO_REUSEADDR) failed: %s"),
108                   strerror(errno));
109     }
110 #endif
111
112     try_socksize(server_socket, SO_SNDBUF, sendsize);
113     try_socksize(server_socket, SO_RCVBUF, recvsize);
114
115     /*
116      * If a port range was specified, we try to get a port in that
117      * range first.  Next, we try to get a reserved port.  If that
118      * fails, we just go for any port.
119      * 
120      * In all cases, not to use port that's assigned to other services. 
121      *
122      * It is up to the caller to make sure we have the proper permissions
123      * to get the desired port, and to make sure we return a port that
124      * is within the range it requires.
125      */
126     for (retries = 0; ; retries++) {
127         if (priv) {
128             portrange = getconf_intrange(CNF_RESERVED_TCP_PORT);
129         } else {
130             portrange = getconf_intrange(CNF_UNRESERVED_TCP_PORT);
131         }
132
133         if (portrange[0] != 0 && portrange[1] != 0) {
134             if (bind_portrange(server_socket, &server, (in_port_t)portrange[0],
135                                (in_port_t)portrange[1], "tcp") == 0)
136                 goto out;
137             g_debug(_("stream_server: Could not bind to port in range: %d - %d."),
138                       portrange[0], portrange[1]);
139         } else {
140             socklen = SS_LEN(&server);
141             if (bind(server_socket, (struct sockaddr *)&server, socklen) == 0)
142                 goto out;
143             g_debug(_("stream_server: Could not bind to any port: %s"),
144                       strerror(errno));
145         }
146
147         if (retries >= BIND_CYCLE_RETRIES)
148             break;
149
150         g_debug(_("stream_server: Retrying entire range after 10 second delay."));
151
152         sleep(15);
153     }
154
155     save_errno = errno;
156     g_debug(_("stream_server: bind(in6addr_any) failed: %s"),
157                   strerror(save_errno));
158     aclose(server_socket);
159     errno = save_errno;
160     return -1;
161
162 out:
163     listen(server_socket, 1);
164
165     /* find out what port was actually used */
166
167     len = SIZEOF(server);
168     if(getsockname(server_socket, (struct sockaddr *)&server, &len) == -1) {
169         save_errno = errno;
170         g_debug(_("stream_server: getsockname() failed: %s"),
171                   strerror(save_errno));
172         aclose(server_socket);
173         errno = save_errno;
174         return -1;
175     }
176
177 #ifdef SO_KEEPALIVE
178     r = setsockopt(server_socket, SOL_SOCKET, SO_KEEPALIVE,
179         (void *)&on, (socklen_t_equiv)sizeof(on));
180     if(r == -1) {
181         save_errno = errno;
182         g_debug(_("stream_server: setsockopt(SO_KEEPALIVE) failed: %s"),
183                   strerror(save_errno));
184         aclose(server_socket);
185         errno = save_errno;
186         return -1;
187     }
188 #endif
189
190     *portp = SU_GET_PORT(&server);
191     g_debug(_("stream_server: waiting for connection: %s"),
192               str_sockaddr(&server));
193     return server_socket;
194 }
195
196 static int
197 stream_client_internal(
198     const char *hostname,
199     in_port_t port,
200     size_t sendsize,
201     size_t recvsize,
202     in_port_t *localport,
203     int nonblock,
204     int priv)
205 {
206     sockaddr_union svaddr, claddr;
207     int save_errno = 0;
208     char *f;
209     int client_socket = 0;
210     int *portrange = NULL;
211     int result;
212     struct addrinfo *res, *res_addr;
213
214     f = priv ? "stream_client_privileged" : "stream_client";
215
216     result = resolve_hostname(hostname, SOCK_STREAM, &res, NULL);
217     if(result != 0) {
218         g_debug(_("resolve_hostname(%s): %s"), hostname, gai_strerror(result));
219         errno = EHOSTUNREACH;
220         return -1;
221     }
222     if(!res) {
223         g_debug(_("resolve_hostname(%s): no results"), hostname);
224         errno = EHOSTUNREACH;
225         return -1;
226     }
227
228     for (res_addr = res; res_addr != NULL; res_addr = res_addr->ai_next) {
229         /* copy the first (preferred) address we found */
230         copy_sockaddr(&svaddr, (sockaddr_union *)res_addr->ai_addr);
231         SU_SET_PORT(&svaddr, port);
232
233         SU_INIT(&claddr, SU_GET_FAMILY(&svaddr));
234         SU_SET_INADDR_ANY(&claddr);
235
236         /*
237          * If a privileged port range was requested, we try to get a port in
238          * that range first and fail if it is not available.  Next, we try
239          * to get a port in the range built in when Amanda was configured.
240          * If that fails, we just go for any port.
241          *
242          * It is up to the caller to make sure we have the proper permissions
243          * to get the desired port, and to make sure we return a port that
244          * is within the range it requires.
245          */
246         if (priv) {
247             portrange = getconf_intrange(CNF_RESERVED_TCP_PORT);
248         } else {
249             portrange = getconf_intrange(CNF_UNRESERVED_TCP_PORT);
250         }
251         client_socket = connect_portrange(&claddr, (in_port_t)portrange[0],
252                                           (in_port_t)portrange[1],
253                                           "tcp", &svaddr, nonblock);
254         save_errno = errno;
255         if (client_socket > 0)
256             break;
257     }
258
259     freeaddrinfo(res);
260                                           
261     if (client_socket > 0)
262         goto out;
263
264     g_debug(_("stream_client: Could not bind to port in range %d-%d."),
265               portrange[0], portrange[1]);
266
267     errno = save_errno;
268     return -1;
269
270 out:
271     try_socksize(client_socket, SO_SNDBUF, sendsize);
272     try_socksize(client_socket, SO_RCVBUF, recvsize);
273     if (localport != NULL)
274         *localport = SU_GET_PORT(&claddr);
275     return client_socket;
276 }
277
278 int
279 stream_client_privileged(
280     const char *hostname,
281     in_port_t port,
282     size_t sendsize,
283     size_t recvsize,
284     in_port_t *localport,
285     int nonblock)
286 {
287     return stream_client_internal(hostname,
288                                   port,
289                                   sendsize,
290                                   recvsize,
291                                   localport,
292                                   nonblock,
293                                   1);
294 }
295
296 int
297 stream_client(
298     const char *hostname,
299     in_port_t port,
300     size_t sendsize,
301     size_t recvsize,
302     in_port_t *localport,
303     int nonblock)
304 {
305     return stream_client_internal(hostname,
306                                   port,
307                                   sendsize,
308                                   recvsize,
309                                   localport,
310                                   nonblock,
311                                   0);
312 }
313
314 /* don't care about these values */
315 static sockaddr_union addr;
316 static socklen_t_equiv addrlen;
317
318 int
319 stream_accept(
320     int server_socket,
321     int timeout,
322     size_t sendsize,
323     size_t recvsize)
324 {
325     SELECT_ARG_TYPE readset;
326     struct timeval tv;
327     int nfound, connected_socket;
328     int save_errno;
329     int ntries = 0;
330     in_port_t port;
331
332     assert(server_socket >= 0);
333
334     do {
335         ntries++;
336         memset(&tv, 0, SIZEOF(tv));
337         tv.tv_sec = timeout;
338         memset(&readset, 0, SIZEOF(readset));
339         FD_ZERO(&readset);
340         FD_SET(server_socket, &readset);
341         nfound = select(server_socket+1, &readset, NULL, NULL, &tv);
342         if(nfound <= 0 || !FD_ISSET(server_socket, &readset)) {
343             save_errno = errno;
344             if(nfound < 0) {
345                 g_debug(_("stream_accept: select() failed: %s"),
346                       strerror(save_errno));
347             } else if(nfound == 0) {
348                 g_debug(plural(_("stream_accept: timeout after %d second"),
349                                 _("stream_accept: timeout after %d seconds"),
350                                timeout),
351                          timeout);
352                 errno = ETIMEDOUT;
353                 return -1;
354             } else if (!FD_ISSET(server_socket, &readset)) {
355                 int i;
356
357                 for(i = 0; i < server_socket + 1; i++) {
358                     if(FD_ISSET(i, &readset)) {
359                         g_debug(_("stream_accept: got fd %d instead of %d"),
360                               i,
361                               server_socket);
362                     }
363                 }
364                 save_errno = EBADF;
365             }
366             if (ntries > 5) {
367                 errno = save_errno;
368                 return -1;
369             }
370         }
371     } while (nfound <= 0);
372
373     while(1) {
374         addrlen = (socklen_t_equiv)sizeof(sockaddr_union);
375         connected_socket = accept(server_socket,
376                                   (struct sockaddr *)&addr,
377                                   &addrlen);
378         if(connected_socket < 0) {
379             break;
380         }
381         g_debug(_("stream_accept: connection from %s"),
382                   str_sockaddr(&addr));
383         /*
384          * Make certain we got an inet connection and that it is not
385          * from port 20 (a favorite unauthorized entry tool).
386          */
387         if (SU_GET_FAMILY(&addr) == AF_INET
388 #ifdef WORKING_IPV6
389             || SU_GET_FAMILY(&addr) == AF_INET6
390 #endif
391             ){
392             port = SU_GET_PORT(&addr);
393             if (port != (in_port_t)20) {
394                 try_socksize(connected_socket, SO_SNDBUF, sendsize);
395                 try_socksize(connected_socket, SO_RCVBUF, recvsize);
396                 return connected_socket;
397             } else {
398                 g_debug(_("remote port is %u: ignored"),
399                           (unsigned int)port);
400             }
401         } else {
402 #ifdef WORKING_IPV6
403             g_debug(_("family is %d instead of %d(AF_INET)"
404                       " or %d(AF_INET6): ignored"),
405                       SU_GET_FAMILY(&addr),
406                       AF_INET, AF_INET6);
407 #else
408             g_debug(_("family is %d instead of %d(AF_INET)"
409                       ": ignored"),
410                       SU_GET_FAMILY(&addr),
411                       AF_INET);
412 #endif
413         }
414         aclose(connected_socket);
415     }
416
417     save_errno = errno;
418     g_debug(_("stream_accept: accept() failed: %s"),
419               strerror(save_errno));
420     errno = save_errno;
421     return -1;
422 }
423
424 static void
425 try_socksize(
426     int sock,
427     int which,
428     size_t size)
429 {
430     size_t origsize;
431     int    isize;
432
433     if (size == 0)
434         return;
435
436     origsize = size;
437     isize = size;
438     /* keep trying, get as big a buffer as possible */
439     while((isize > 1024) &&
440           (setsockopt(sock, SOL_SOCKET,
441                       which, (void *) &isize, (socklen_t_equiv)sizeof(isize)) < 0)) {
442         isize -= 1024;
443     }
444     if(isize > 1024) {
445         g_debug(_("try_socksize: %s buffer size is %d"),
446                   (which == SO_SNDBUF) ? _("send") : _("receive"),
447                   isize);
448     } else {
449         g_debug(_("try_socksize: could not allocate %s buffer of %zu"),
450                   (which == SO_SNDBUF) ? _("send") : _("receive"),
451                   origsize);
452     }
453 }