Imported Upstream version 2.6.0p2
[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     server_socket = socket(socket_family, SOCK_STREAM, 0);
73     
74 #ifdef WORKING_IPV6
75     /* if that address family actually isn't supported, just try AF_INET */
76     if (server_socket == -1 && errno == EAFNOSUPPORT) {
77         socket_family = AF_INET;
78         server_socket = socket(AF_INET, SOCK_STREAM, 0);
79     }
80 #endif
81     if (server_socket == -1) {
82         save_errno = errno;
83         dbprintf(_("stream_server: socket() failed: %s\n"),
84                   strerror(save_errno));
85         errno = save_errno;
86         return -1;
87     }
88     if(server_socket < 0 || server_socket >= (int)FD_SETSIZE) {
89         aclose(server_socket);
90         errno = EMFILE;                         /* out of range */
91         save_errno = errno;
92         dbprintf(_("stream_server: socket out of range: %d\n"),
93                   server_socket);
94         errno = save_errno;
95         return -1;
96     }
97
98     SU_INIT(&server, socket_family);
99     SU_SET_INADDR_ANY(&server);
100
101 #ifdef USE_REUSEADDR
102     r = setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR,
103         (void *)&on, (socklen_t_equiv)sizeof(on));
104     if (r < 0) {
105         dbprintf(_("stream_server: setsockopt(SO_REUSEADDR) failed: %s\n"),
106                   strerror(errno));
107     }
108 #endif
109
110     try_socksize(server_socket, SO_SNDBUF, sendsize);
111     try_socksize(server_socket, SO_RCVBUF, recvsize);
112
113     /*
114      * If a port range was specified, we try to get a port in that
115      * range first.  Next, we try to get a reserved port.  If that
116      * fails, we just go for any port.
117      * 
118      * In all cases, not to use port that's assigned to other services. 
119      *
120      * It is up to the caller to make sure we have the proper permissions
121      * to get the desired port, and to make sure we return a port that
122      * is within the range it requires.
123      */
124     for (retries = 0; ; retries++) {
125         if (priv) {
126             portrange = getconf_intrange(CNF_RESERVED_TCP_PORT);
127         } else {
128             portrange = getconf_intrange(CNF_UNRESERVED_TCP_PORT);
129         }
130
131         if (portrange[0] != 0 && portrange[1] != 0) {
132             if (bind_portrange(server_socket, &server, (in_port_t)portrange[0],
133                                (in_port_t)portrange[1], "tcp") == 0)
134                 goto out;
135             dbprintf(_("stream_server: Could not bind to port in range: %d - %d.\n"),
136                       portrange[0], portrange[1]);
137         } else {
138             socklen = SS_LEN(&server);
139             if (bind(server_socket, (struct sockaddr *)&server, socklen) == 0)
140                 goto out;
141             dbprintf(_("stream_server: Could not bind to any port: %s\n"),
142                       strerror(errno));
143         }
144
145         if (retries >= BIND_CYCLE_RETRIES)
146             break;
147
148         dbprintf(_("stream_server: Retrying entire range after 10 second delay.\n"));
149
150         sleep(15);
151     }
152
153     save_errno = errno;
154     dbprintf(_("stream_server: bind(in6addr_any) failed: %s\n"),
155                   strerror(save_errno));
156     aclose(server_socket);
157     errno = save_errno;
158     return -1;
159
160 out:
161     listen(server_socket, 1);
162
163     /* find out what port was actually used */
164
165     len = SIZEOF(server);
166     if(getsockname(server_socket, (struct sockaddr *)&server, &len) == -1) {
167         save_errno = errno;
168         dbprintf(_("stream_server: getsockname() failed: %s\n"),
169                   strerror(save_errno));
170         aclose(server_socket);
171         errno = save_errno;
172         return -1;
173     }
174
175 #ifdef SO_KEEPALIVE
176     r = setsockopt(server_socket, SOL_SOCKET, SO_KEEPALIVE,
177         (void *)&on, (socklen_t_equiv)sizeof(on));
178     if(r == -1) {
179         save_errno = errno;
180         dbprintf(_("stream_server: setsockopt(SO_KEEPALIVE) failed: %s\n"),
181                   strerror(save_errno));
182         aclose(server_socket);
183         errno = save_errno;
184         return -1;
185     }
186 #endif
187
188     *portp = SU_GET_PORT(&server);
189     dbprintf(_("stream_server: waiting for connection: %s\n"),
190               str_sockaddr(&server));
191     return server_socket;
192 }
193
194 static int
195 stream_client_internal(
196     const char *hostname,
197     in_port_t port,
198     size_t sendsize,
199     size_t recvsize,
200     in_port_t *localport,
201     int nonblock,
202     int priv)
203 {
204     sockaddr_union svaddr, claddr;
205     int save_errno;
206     char *f;
207     int client_socket;
208     int *portrange;
209     int result;
210     struct addrinfo *res, *res_addr;
211
212     f = priv ? "stream_client_privileged" : "stream_client";
213
214     result = resolve_hostname(hostname, SOCK_STREAM, &res, NULL);
215     if(result != 0) {
216         dbprintf(_("resolve_hostname(%s): %s\n"), hostname, gai_strerror(result));
217         errno = EHOSTUNREACH;
218         return -1;
219     }
220     if(!res) {
221         dbprintf(_("resolve_hostname(%s): no results\n"), 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     dbprintf(_("stream_client: Could not bind to port in range %d-%d.\n"),
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 int
317 stream_accept(
318     int server_socket,
319     int timeout,
320     size_t sendsize,
321     size_t recvsize)
322 {
323     SELECT_ARG_TYPE readset;
324     struct timeval tv;
325     int nfound, connected_socket;
326     int save_errno;
327     int ntries = 0;
328     in_port_t port;
329
330     assert(server_socket >= 0);
331
332     do {
333         ntries++;
334         memset(&tv, 0, SIZEOF(tv));
335         tv.tv_sec = timeout;
336         memset(&readset, 0, SIZEOF(readset));
337         FD_ZERO(&readset);
338         FD_SET(server_socket, &readset);
339         nfound = select(server_socket+1, &readset, NULL, NULL, &tv);
340         if(nfound <= 0 || !FD_ISSET(server_socket, &readset)) {
341             save_errno = errno;
342             if(nfound < 0) {
343                 dbprintf(_("stream_accept: select() failed: %s\n"),
344                       strerror(save_errno));
345             } else if(nfound == 0) {
346                 dbprintf(plural(_("stream_accept: timeout after %d second\n"),
347                                 _("stream_accept: timeout after %d seconds\n"),
348                                timeout),
349                          timeout);
350                 errno = ENOENT;                 /* ??? */
351                 return -1;
352             } else if (!FD_ISSET(server_socket, &readset)) {
353                 int i;
354
355                 for(i = 0; i < server_socket + 1; i++) {
356                     if(FD_ISSET(i, &readset)) {
357                         dbprintf(_("stream_accept: got fd %d instead of %d\n"),
358                               i,
359                               server_socket);
360                     }
361                 }
362                 save_errno = EBADF;
363             }
364             if (ntries > 5) {
365                 errno = save_errno;
366                 return -1;
367             }
368         }
369     } while (nfound <= 0);
370
371     while(1) {
372         addrlen = (socklen_t_equiv)sizeof(sockaddr_union);
373         connected_socket = accept(server_socket,
374                                   (struct sockaddr *)&addr,
375                                   &addrlen);
376         if(connected_socket < 0) {
377             break;
378         }
379         dbprintf(_("stream_accept: connection from %s\n"),
380                   str_sockaddr(&addr));
381         /*
382          * Make certain we got an inet connection and that it is not
383          * from port 20 (a favorite unauthorized entry tool).
384          */
385         if (SU_GET_FAMILY(&addr) == AF_INET
386 #ifdef WORKING_IPV6
387             || SU_GET_FAMILY(&addr) == AF_INET6
388 #endif
389             ){
390             port = SU_GET_PORT(&addr);
391             if (port != (in_port_t)20) {
392                 try_socksize(connected_socket, SO_SNDBUF, sendsize);
393                 try_socksize(connected_socket, SO_RCVBUF, recvsize);
394                 return connected_socket;
395             } else {
396                 dbprintf(_("remote port is %u: ignored\n"),
397                           (unsigned int)port);
398             }
399         } else {
400 #ifdef WORKING_IPV6
401             dbprintf(_("family is %d instead of %d(AF_INET)"
402                       " or %d(AF_INET6): ignored\n"),
403                       SU_GET_FAMILY(&addr),
404                       AF_INET, AF_INET6);
405 #else
406             dbprintf(_("family is %d instead of %d(AF_INET)"
407                       ": ignored\n"),
408                       SU_GET_FAMILY(&addr),
409                       AF_INET);
410 #endif
411         }
412         aclose(connected_socket);
413     }
414
415     save_errno = errno;
416     dbprintf(_("stream_accept: accept() failed: %s\n"),
417               strerror(save_errno));
418     errno = save_errno;
419     return -1;
420 }
421
422 static void
423 try_socksize(
424     int sock,
425     int which,
426     size_t size)
427 {
428     size_t origsize;
429     int    isize;
430
431     if (size == 0)
432         return;
433
434     origsize = size;
435     isize = size;
436     /* keep trying, get as big a buffer as possible */
437     while((isize > 1024) &&
438           (setsockopt(sock, SOL_SOCKET,
439                       which, (void *) &isize, (socklen_t_equiv)sizeof(isize)) < 0)) {
440         isize -= 1024;
441     }
442     if(isize > 1024) {
443         dbprintf(_("try_socksize: %s buffer size is %d\n"),
444                   (which == SO_SNDBUF) ? _("send") : _("receive"),
445                   isize);
446     } else {
447         dbprintf(_("try_socksize: could not allocate %s buffer of %zu\n"),
448                   (which == SO_SNDBUF) ? _("send") : _("receive"),
449                   origsize);
450     }
451 }