Imported Upstream version 3.3.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 #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     int client_socket = 0;
209     int *portrange = NULL;
210     int result;
211     struct addrinfo *res, *res_addr;
212
213     result = resolve_hostname(hostname, SOCK_STREAM, &res, NULL);
214     if(result != 0) {
215         g_debug(_("resolve_hostname(%s): %s"), hostname, gai_strerror(result));
216         errno = EHOSTUNREACH;
217         return -1;
218     }
219     if(!res) {
220         g_debug(_("resolve_hostname(%s): no results"), hostname);
221         errno = EHOSTUNREACH;
222         return -1;
223     }
224
225     for (res_addr = res; res_addr != NULL; res_addr = res_addr->ai_next) {
226         /* copy the first (preferred) address we found */
227         copy_sockaddr(&svaddr, (sockaddr_union *)res_addr->ai_addr);
228         SU_SET_PORT(&svaddr, port);
229
230         SU_INIT(&claddr, SU_GET_FAMILY(&svaddr));
231         SU_SET_INADDR_ANY(&claddr);
232
233         /*
234          * If a privileged port range was requested, we try to get a port in
235          * that range first and fail if it is not available.  Next, we try
236          * to get a port in the range built in when Amanda was configured.
237          * If that fails, we just go for any port.
238          *
239          * It is up to the caller to make sure we have the proper permissions
240          * to get the desired port, and to make sure we return a port that
241          * is within the range it requires.
242          */
243         if (priv) {
244             portrange = getconf_intrange(CNF_RESERVED_TCP_PORT);
245         } else {
246             portrange = getconf_intrange(CNF_UNRESERVED_TCP_PORT);
247         }
248         client_socket = connect_portrange(&claddr, (in_port_t)portrange[0],
249                                           (in_port_t)portrange[1],
250                                           "tcp", &svaddr, nonblock);
251         save_errno = errno;
252         if (client_socket > 0)
253             break;
254     }
255
256     freeaddrinfo(res);
257                                           
258     if (client_socket > 0)
259         goto out;
260
261     g_debug(_("stream_client: Could not bind to port in range %d-%d."),
262               portrange[0], portrange[1]);
263
264     errno = save_errno;
265     return -1;
266
267 out:
268     try_socksize(client_socket, SO_SNDBUF, sendsize);
269     try_socksize(client_socket, SO_RCVBUF, recvsize);
270     if (localport != NULL)
271         *localport = SU_GET_PORT(&claddr);
272     return client_socket;
273 }
274
275 int
276 stream_client_privileged(
277     const char *hostname,
278     in_port_t port,
279     size_t sendsize,
280     size_t recvsize,
281     in_port_t *localport,
282     int nonblock)
283 {
284     return stream_client_internal(hostname,
285                                   port,
286                                   sendsize,
287                                   recvsize,
288                                   localport,
289                                   nonblock,
290                                   1);
291 }
292
293 int
294 stream_client(
295     const char *hostname,
296     in_port_t port,
297     size_t sendsize,
298     size_t recvsize,
299     in_port_t *localport,
300     int nonblock)
301 {
302     return stream_client_internal(hostname,
303                                   port,
304                                   sendsize,
305                                   recvsize,
306                                   localport,
307                                   nonblock,
308                                   0);
309 }
310
311 /* don't care about these values */
312 static sockaddr_union addr;
313 static socklen_t_equiv addrlen;
314
315 static gboolean
316 stream_accept_prolong(
317     gpointer data)
318 {
319     time_t *tp = data;
320     return time(NULL) <= *tp;
321 }
322
323 int
324 stream_accept(
325     int server_socket,
326     int timeout,
327     size_t sendsize,
328     size_t recvsize)
329 {
330     time_t timeout_time;
331     int connected_socket;
332     int save_errno;
333     in_port_t port;
334
335     assert(server_socket >= 0);
336
337     /* set the time we want to stop accepting */
338     timeout_time = time(NULL) + timeout;
339
340     while(1) {
341         addrlen = (socklen_t_equiv)sizeof(sockaddr_union);
342         connected_socket = interruptible_accept(server_socket,
343                                   (struct sockaddr *)&addr,
344                                   &addrlen, stream_accept_prolong,
345                                   &timeout_time);
346         if(connected_socket < 0) {
347             if (errno == 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             }
355             break;
356         }
357         g_debug(_("stream_accept: connection from %s"),
358                   str_sockaddr(&addr));
359         /*
360          * Make certain we got an inet connection and that it is not
361          * from port 20 (a favorite unauthorized entry tool).
362          */
363         if (SU_GET_FAMILY(&addr) == AF_INET
364 #ifdef WORKING_IPV6
365             || SU_GET_FAMILY(&addr) == AF_INET6
366 #endif
367             ){
368             port = SU_GET_PORT(&addr);
369             if (port != (in_port_t)20) {
370                 try_socksize(connected_socket, SO_SNDBUF, sendsize);
371                 try_socksize(connected_socket, SO_RCVBUF, recvsize);
372                 return connected_socket;
373             } else {
374                 g_debug(_("remote port is %u: ignored"),
375                           (unsigned int)port);
376             }
377         } else {
378 #ifdef WORKING_IPV6
379             g_debug(_("family is %d instead of %d(AF_INET)"
380                       " or %d(AF_INET6): ignored"),
381                       SU_GET_FAMILY(&addr),
382                       AF_INET, AF_INET6);
383 #else
384             g_debug(_("family is %d instead of %d(AF_INET)"
385                       ": ignored"),
386                       SU_GET_FAMILY(&addr),
387                       AF_INET);
388 #endif
389         }
390         aclose(connected_socket);
391     }
392
393     save_errno = errno;
394     g_debug(_("stream_accept: accept() failed: %s"),
395               strerror(save_errno));
396     errno = save_errno;
397     return -1;
398 }
399
400 static void
401 try_socksize(
402     int sock,
403     int which,
404     size_t size)
405 {
406     size_t origsize;
407     int    isize;
408
409     if (size == 0)
410         return;
411
412     origsize = size;
413     isize = size;
414     /* keep trying, get as big a buffer as possible */
415     while((isize > 1024) &&
416           (setsockopt(sock, SOL_SOCKET,
417                       which, (void *) &isize, (socklen_t_equiv)sizeof(isize)) < 0)) {
418         isize -= 1024;
419     }
420     if(isize > 1024) {
421         g_debug(_("try_socksize: %s buffer size is %d"),
422                   (which == SO_SNDBUF) ? _("send") : _("receive"),
423                   isize);
424     } else {
425         g_debug(_("try_socksize: could not allocate %s buffer of %zu"),
426                   (which == SO_SNDBUF) ? _("send") : _("receive"),
427                   origsize);
428     }
429 }