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