Imported Upstream version 3.2.0
[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 static gboolean
319 stream_accept_prolong(
320     gpointer data)
321 {
322     time_t *tp = data;
323     return time(NULL) <= *tp;
324 }
325
326 int
327 stream_accept(
328     int server_socket,
329     int timeout,
330     size_t sendsize,
331     size_t recvsize)
332 {
333     time_t timeout_time;
334     int connected_socket;
335     int save_errno;
336     in_port_t port;
337
338     assert(server_socket >= 0);
339
340     /* set the time we want to stop accepting */
341     timeout_time = time(NULL) + timeout;
342
343     while(1) {
344         addrlen = (socklen_t_equiv)sizeof(sockaddr_union);
345         connected_socket = interruptible_accept(server_socket,
346                                   (struct sockaddr *)&addr,
347                                   &addrlen, stream_accept_prolong,
348                                   &timeout_time);
349         if(connected_socket < 0) {
350             if (errno == 0) {
351                 g_debug(plural(_("stream_accept: timeout after %d second"),
352                                _("stream_accept: timeout after %d seconds"),
353                               timeout),
354                         timeout);
355                 errno = ETIMEDOUT;
356                 return -1;
357             }
358             break;
359         }
360         g_debug(_("stream_accept: connection from %s"),
361                   str_sockaddr(&addr));
362         /*
363          * Make certain we got an inet connection and that it is not
364          * from port 20 (a favorite unauthorized entry tool).
365          */
366         if (SU_GET_FAMILY(&addr) == AF_INET
367 #ifdef WORKING_IPV6
368             || SU_GET_FAMILY(&addr) == AF_INET6
369 #endif
370             ){
371             port = SU_GET_PORT(&addr);
372             if (port != (in_port_t)20) {
373                 try_socksize(connected_socket, SO_SNDBUF, sendsize);
374                 try_socksize(connected_socket, SO_RCVBUF, recvsize);
375                 return connected_socket;
376             } else {
377                 g_debug(_("remote port is %u: ignored"),
378                           (unsigned int)port);
379             }
380         } else {
381 #ifdef WORKING_IPV6
382             g_debug(_("family is %d instead of %d(AF_INET)"
383                       " or %d(AF_INET6): ignored"),
384                       SU_GET_FAMILY(&addr),
385                       AF_INET, AF_INET6);
386 #else
387             g_debug(_("family is %d instead of %d(AF_INET)"
388                       ": ignored"),
389                       SU_GET_FAMILY(&addr),
390                       AF_INET);
391 #endif
392         }
393         aclose(connected_socket);
394     }
395
396     save_errno = errno;
397     g_debug(_("stream_accept: accept() failed: %s"),
398               strerror(save_errno));
399     errno = save_errno;
400     return -1;
401 }
402
403 static void
404 try_socksize(
405     int sock,
406     int which,
407     size_t size)
408 {
409     size_t origsize;
410     int    isize;
411
412     if (size == 0)
413         return;
414
415     origsize = size;
416     isize = size;
417     /* keep trying, get as big a buffer as possible */
418     while((isize > 1024) &&
419           (setsockopt(sock, SOL_SOCKET,
420                       which, (void *) &isize, (socklen_t_equiv)sizeof(isize)) < 0)) {
421         isize -= 1024;
422     }
423     if(isize > 1024) {
424         g_debug(_("try_socksize: %s buffer size is %d"),
425                   (which == SO_SNDBUF) ? _("send") : _("receive"),
426                   isize);
427     } else {
428         g_debug(_("try_socksize: could not allocate %s buffer of %zu"),
429                   (which == SO_SNDBUF) ? _("send") : _("receive"),
430                   origsize);
431     }
432 }