Imported Upstream version 3.3.3
[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  * Copyright (c) 2007-2012 Zmanda, Inc.  All Rights Reserved.
5  * All Rights Reserved.
6  *
7  * Permission to use, copy, modify, distribute, and sell this software and its
8  * documentation for any purpose is hereby granted without fee, provided that
9  * the above copyright notice appear in all copies and that both that
10  * copyright notice and this permission notice appear in supporting
11  * documentation, and that the name of U.M. not be used in advertising or
12  * publicity pertaining to distribution of the software without specific,
13  * written prior permission.  U.M. makes no representations about the
14  * suitability of this software for any purpose.  It is provided "as is"
15  * without express or implied warranty.
16  *
17  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
19  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  *
24  * Author: James da Silva, Systems Design and Analysis Group
25  *                         Computer Science Department
26  *                         University of Maryland at College Park
27  */
28 /*
29  * $Id: stream.c,v 1.39 2006/08/24 01:57:15 paddy_s Exp $
30  *
31  * functions for managing stream sockets
32  */
33 #include "amanda.h"
34 #include "dgram.h"
35 #include "stream.h"
36 #include "util.h"
37 #include "conffile.h"
38 #include "security-util.h"
39 #include "sockaddr-util.h"
40
41 /* local functions */
42 static void try_socksize(int sock, int which, size_t size);
43 static int stream_client_internal(const char *hostname, in_port_t port,
44                 size_t sendsize, size_t recvsize, in_port_t *localport,
45                 int nonblock, int priv);
46
47 int
48 stream_server(
49     int family,
50     in_port_t *portp,
51     size_t sendsize,
52     size_t recvsize,
53     int    priv)
54 {
55     int server_socket, retries;
56     socklen_t_equiv len;
57 #if defined(SO_KEEPALIVE) || defined(USE_REUSEADDR)
58     const int on = 1;
59     int r;
60 #endif
61     sockaddr_union server;
62     int save_errno;
63     int *portrange;
64     socklen_t_equiv socklen;
65     int socket_family;
66
67     *portp = USHRT_MAX;                         /* in case we error exit */
68     if (family == -1) {
69         socket_family = AF_NATIVE;
70     } else {
71         socket_family = family;
72     }
73     g_debug("stream_server opening socket with family %d (requested family was %d)", socket_family, family);
74     server_socket = socket(socket_family, SOCK_STREAM, 0);
75
76 #ifdef WORKING_IPV6
77     /* if that address family actually isn't supported, just try AF_INET */
78     if (server_socket == -1 && errno == EAFNOSUPPORT) {
79         g_debug("stream_server retrying socket with AF_INET");
80         socket_family = AF_INET;
81         server_socket = socket(AF_INET, SOCK_STREAM, 0);
82     }
83 #endif
84     if (server_socket == -1) {
85         save_errno = errno;
86         g_debug(_("stream_server: socket() failed: %s"),
87                   strerror(save_errno));
88         errno = save_errno;
89         return -1;
90     }
91     if(server_socket < 0 || server_socket >= (int)FD_SETSIZE) {
92         aclose(server_socket);
93         errno = EMFILE;                         /* out of range */
94         save_errno = errno;
95         g_debug(_("stream_server: socket out of range: %d"),
96                   server_socket);
97         errno = save_errno;
98         return -1;
99     }
100
101     SU_INIT(&server, socket_family);
102     SU_SET_INADDR_ANY(&server);
103
104 #ifdef USE_REUSEADDR
105     r = setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR,
106         (void *)&on, (socklen_t_equiv)sizeof(on));
107     if (r < 0) {
108         g_debug(_("stream_server: setsockopt(SO_REUSEADDR) failed: %s"),
109                   strerror(errno));
110     }
111 #endif
112
113     try_socksize(server_socket, SO_SNDBUF, sendsize);
114     try_socksize(server_socket, SO_RCVBUF, recvsize);
115
116     /*
117      * If a port range was specified, we try to get a port in that
118      * range first.  Next, we try to get a reserved port.  If that
119      * fails, we just go for any port.
120      * 
121      * In all cases, not to use port that's assigned to other services. 
122      *
123      * It is up to the caller to make sure we have the proper permissions
124      * to get the desired port, and to make sure we return a port that
125      * is within the range it requires.
126      */
127     for (retries = 0; ; retries++) {
128         if (priv) {
129             portrange = getconf_intrange(CNF_RESERVED_TCP_PORT);
130         } else {
131             portrange = getconf_intrange(CNF_UNRESERVED_TCP_PORT);
132         }
133
134         if (portrange[0] != 0 && portrange[1] != 0) {
135             if (bind_portrange(server_socket, &server, (in_port_t)portrange[0],
136                                (in_port_t)portrange[1], "tcp") == 0)
137                 goto out;
138             g_debug(_("stream_server: Could not bind to port in range: %d - %d."),
139                       portrange[0], portrange[1]);
140         } else {
141             socklen = SS_LEN(&server);
142             if (bind(server_socket, (struct sockaddr *)&server, socklen) == 0)
143                 goto out;
144             g_debug(_("stream_server: Could not bind to any port: %s"),
145                       strerror(errno));
146         }
147
148         if (retries >= BIND_CYCLE_RETRIES)
149             break;
150
151         g_debug(_("stream_server: Retrying entire range after 10 second delay."));
152
153         sleep(15);
154     }
155
156     save_errno = errno;
157     g_debug(_("stream_server: bind(in6addr_any) failed: %s"),
158                   strerror(save_errno));
159     aclose(server_socket);
160     errno = save_errno;
161     return -1;
162
163 out:
164     listen(server_socket, 1);
165
166     /* find out what port was actually used */
167
168     len = SIZEOF(server);
169     if(getsockname(server_socket, (struct sockaddr *)&server, &len) == -1) {
170         save_errno = errno;
171         g_debug(_("stream_server: getsockname() failed: %s"),
172                   strerror(save_errno));
173         aclose(server_socket);
174         errno = save_errno;
175         return -1;
176     }
177
178 #ifdef SO_KEEPALIVE
179     r = setsockopt(server_socket, SOL_SOCKET, SO_KEEPALIVE,
180         (void *)&on, (socklen_t_equiv)sizeof(on));
181     if(r == -1) {
182         save_errno = errno;
183         g_debug(_("stream_server: setsockopt(SO_KEEPALIVE) failed: %s"),
184                   strerror(save_errno));
185         aclose(server_socket);
186         errno = save_errno;
187         return -1;
188     }
189 #endif
190
191     *portp = SU_GET_PORT(&server);
192     g_debug(_("stream_server: waiting for connection: %s"),
193               str_sockaddr(&server));
194     return server_socket;
195 }
196
197 static int
198 stream_client_internal(
199     const char *hostname,
200     in_port_t port,
201     size_t sendsize,
202     size_t recvsize,
203     in_port_t *localport,
204     int nonblock,
205     int priv)
206 {
207     sockaddr_union svaddr, claddr;
208     int save_errno = 0;
209     int client_socket = 0;
210     int *portrange = NULL;
211     int result;
212     struct addrinfo *res, *res_addr;
213
214     result = resolve_hostname(hostname, SOCK_STREAM, &res, NULL);
215     if(result != 0) {
216         g_debug(_("resolve_hostname(%s): %s"), hostname, gai_strerror(result));
217         errno = EHOSTUNREACH;
218         return -1;
219     }
220     if(!res) {
221         g_debug(_("resolve_hostname(%s): no results"), hostname);
222         errno = EHOSTUNREACH;
223         return -1;
224     }
225
226     for (res_addr = res; res_addr != NULL; res_addr = res_addr->ai_next) {
227         /* copy the first (preferred) address we found */
228         copy_sockaddr(&svaddr, (sockaddr_union *)res_addr->ai_addr);
229         SU_SET_PORT(&svaddr, port);
230
231         SU_INIT(&claddr, SU_GET_FAMILY(&svaddr));
232         SU_SET_INADDR_ANY(&claddr);
233
234         /*
235          * If a privileged port range was requested, we try to get a port in
236          * that range first and fail if it is not available.  Next, we try
237          * to get a port in the range built in when Amanda was configured.
238          * If that fails, we just go for any port.
239          *
240          * It is up to the caller to make sure we have the proper permissions
241          * to get the desired port, and to make sure we return a port that
242          * is within the range it requires.
243          */
244         if (priv) {
245             portrange = getconf_intrange(CNF_RESERVED_TCP_PORT);
246         } else {
247             portrange = getconf_intrange(CNF_UNRESERVED_TCP_PORT);
248         }
249         client_socket = connect_portrange(&claddr, (in_port_t)portrange[0],
250                                           (in_port_t)portrange[1],
251                                           "tcp", &svaddr, nonblock);
252         save_errno = errno;
253         if (client_socket > 0)
254             break;
255     }
256
257     freeaddrinfo(res);
258                                           
259     if (client_socket > 0)
260         goto out;
261
262     g_debug(_("stream_client: Could not bind to port in range %d-%d."),
263               portrange[0], portrange[1]);
264
265     errno = save_errno;
266     return -1;
267
268 out:
269     try_socksize(client_socket, SO_SNDBUF, sendsize);
270     try_socksize(client_socket, SO_RCVBUF, recvsize);
271     if (localport != NULL)
272         *localport = SU_GET_PORT(&claddr);
273     return client_socket;
274 }
275
276 int
277 stream_client_privileged(
278     const char *hostname,
279     in_port_t port,
280     size_t sendsize,
281     size_t recvsize,
282     in_port_t *localport,
283     int nonblock)
284 {
285     return stream_client_internal(hostname,
286                                   port,
287                                   sendsize,
288                                   recvsize,
289                                   localport,
290                                   nonblock,
291                                   1);
292 }
293
294 int
295 stream_client(
296     const char *hostname,
297     in_port_t port,
298     size_t sendsize,
299     size_t recvsize,
300     in_port_t *localport,
301     int nonblock)
302 {
303     return stream_client_internal(hostname,
304                                   port,
305                                   sendsize,
306                                   recvsize,
307                                   localport,
308                                   nonblock,
309                                   0);
310 }
311
312 /* don't care about these values */
313 static sockaddr_union addr;
314 static socklen_t_equiv addrlen;
315
316 static gboolean
317 stream_accept_prolong(
318     gpointer data)
319 {
320     time_t *tp = data;
321     return time(NULL) <= *tp;
322 }
323
324 int
325 stream_accept(
326     int server_socket,
327     int timeout,
328     size_t sendsize,
329     size_t recvsize)
330 {
331     time_t timeout_time;
332     int connected_socket;
333     int save_errno;
334     in_port_t port;
335
336     assert(server_socket >= 0);
337
338     /* set the time we want to stop accepting */
339     timeout_time = time(NULL) + timeout;
340
341     while(1) {
342         addrlen = (socklen_t_equiv)sizeof(sockaddr_union);
343         connected_socket = interruptible_accept(server_socket,
344                                   (struct sockaddr *)&addr,
345                                   &addrlen, stream_accept_prolong,
346                                   &timeout_time);
347         if(connected_socket < 0) {
348             if (errno == 0) {
349                 g_debug(plural(_("stream_accept: timeout after %d second"),
350                                _("stream_accept: timeout after %d seconds"),
351                               timeout),
352                         timeout);
353                 errno = ETIMEDOUT;
354                 return -1;
355             }
356             break;
357         }
358         g_debug(_("stream_accept: connection from %s"),
359                   str_sockaddr(&addr));
360         /*
361          * Make certain we got an inet connection and that it is not
362          * from port 20 (a favorite unauthorized entry tool).
363          */
364         if (SU_GET_FAMILY(&addr) == AF_INET
365 #ifdef WORKING_IPV6
366             || SU_GET_FAMILY(&addr) == AF_INET6
367 #endif
368             ){
369             port = SU_GET_PORT(&addr);
370             if (port != (in_port_t)20) {
371                 try_socksize(connected_socket, SO_SNDBUF, sendsize);
372                 try_socksize(connected_socket, SO_RCVBUF, recvsize);
373                 return connected_socket;
374             } else {
375                 g_debug(_("remote port is %u: ignored"),
376                           (unsigned int)port);
377             }
378         } else {
379 #ifdef WORKING_IPV6
380             g_debug(_("family is %d instead of %d(AF_INET)"
381                       " or %d(AF_INET6): ignored"),
382                       SU_GET_FAMILY(&addr),
383                       AF_INET, AF_INET6);
384 #else
385             g_debug(_("family is %d instead of %d(AF_INET)"
386                       ": ignored"),
387                       SU_GET_FAMILY(&addr),
388                       AF_INET);
389 #endif
390         }
391         aclose(connected_socket);
392     }
393
394     save_errno = errno;
395     g_debug(_("stream_accept: accept() failed: %s"),
396               strerror(save_errno));
397     errno = save_errno;
398     return -1;
399 }
400
401 static void
402 try_socksize(
403     int sock,
404     int which,
405     size_t size)
406 {
407     size_t origsize;
408     int    isize;
409
410     if (size == 0)
411         return;
412
413     origsize = size;
414     isize = size;
415     /* keep trying, get as big a buffer as possible */
416     while((isize > 1024) &&
417           (setsockopt(sock, SOL_SOCKET,
418                       which, (void *) &isize, (socklen_t_equiv)sizeof(isize)) < 0)) {
419         isize -= 1024;
420     }
421     if(isize > 1024) {
422         g_debug(_("try_socksize: %s buffer size is %d"),
423                   (which == SO_SNDBUF) ? _("send") : _("receive"),
424                   isize);
425     } else {
426         g_debug(_("try_socksize: could not allocate %s buffer of %zu"),
427                   (which == SO_SNDBUF) ? _("send") : _("receive"),
428                   origsize);
429     }
430 }