5fdc52adbf1e801c895a537d33ab4ac773275a40
[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 2002/11/12 19:19:03 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, len;
154 #ifdef SO_KEEPALIVE
155     int on = 1;
156     int r;
157 #endif
158     struct sockaddr_in svaddr, claddr;
159     struct hostent *hostp;
160     int save_errno;
161     char *f;
162
163     f = priv ? "stream_client_privileged" : "stream_client";
164
165     if((hostp = gethostbyname(hostname)) == NULL) {
166         save_errno = errno;
167         dbprintf(("%s: %s: gethostbyname(%s) failed\n",
168                   debug_prefix(NULL),
169                   f,
170                   hostname));
171         errno = save_errno;
172         return -1;
173     }
174
175     memset(&svaddr, 0, sizeof(svaddr));
176     svaddr.sin_family = AF_INET;
177     svaddr.sin_port = htons(port);
178     memcpy(&svaddr.sin_addr, hostp->h_addr, hostp->h_length);
179
180     if((client_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
181         save_errno = errno;
182         dbprintf(("%s: %s: socket() failed: %s\n",
183                   debug_prefix(NULL),
184                   f,
185                   strerror(save_errno)));
186         errno = save_errno;
187         return -1;
188     }
189     if(client_socket < 0 || client_socket >= FD_SETSIZE) {
190         aclose(client_socket);
191         errno = EMFILE;                         /* out of range */
192         return -1;
193     }
194
195 #ifdef SO_KEEPALIVE
196     r = setsockopt(client_socket, SOL_SOCKET, SO_KEEPALIVE,
197                    (void *)&on, sizeof(on));
198     if(r == -1) {
199         save_errno = errno;
200         dbprintf(("%s: %s: setsockopt() failed: %s\n",
201                   debug_prefix(NULL),
202                   f,
203                   strerror(save_errno)));
204         aclose(client_socket);
205         errno = save_errno;
206         return -1;
207     }
208 #endif
209
210     memset(&claddr, 0, sizeof(claddr));
211     claddr.sin_family = AF_INET;
212     claddr.sin_addr.s_addr = INADDR_ANY;
213
214     /*
215      * If a privileged port range was requested, we try to get a port in
216      * that range first and fail if it is not available.  Next, we try
217      * to get a port in the range built in when Amanda was configured.
218      * If that fails, we just go for any port.
219      *
220      * It is up to the caller to make sure we have the proper permissions
221      * to get the desired port, and to make sure we return a port that
222      * is within the range it requires.
223      */
224     if (priv) {
225         int b;
226
227         b = bind_portrange(client_socket, &claddr, 512, IPPORT_RESERVED - 1);
228         if (b == 0) {
229             goto out;                           /* got what we wanted */
230         }
231         save_errno = errno;
232         dbprintf(("%s: %s: bind(IPPORT_RESERVED) failed: %s\n",
233                   debug_prefix(NULL),
234                   f,
235                   strerror(save_errno)));
236         aclose(client_socket);
237         errno = save_errno;
238         return -1;
239     }
240
241 #ifdef TCPPORTRANGE
242     if (bind_portrange(client_socket, &claddr, TCPPORTRANGE) == 0)
243         goto out;
244 #endif
245
246     claddr.sin_port = INADDR_ANY;
247     if (bind(client_socket, (struct sockaddr *)&claddr, sizeof(claddr)) == -1) {
248         save_errno = errno;
249         dbprintf(("%s: %s: bind(INADDR_ANY) failed: %s\n",
250                   debug_prefix(NULL),
251                   f,
252                   strerror(save_errno)));
253         aclose(client_socket);
254         errno = save_errno;
255         return -1;
256     }
257
258 out:
259
260     /* find out what port was actually used */
261
262     len = sizeof(claddr);
263     if(getsockname(client_socket, (struct sockaddr *)&claddr, &len) == -1) {
264         save_errno = errno;
265         dbprintf(("%s: %s: getsockname() failed: %s\n",
266                   debug_prefix(NULL),
267                   f,
268                   strerror(save_errno)));
269         aclose(client_socket);
270         errno = save_errno;
271         return -1;
272     }
273
274     if(connect(client_socket, (struct sockaddr *)&svaddr, sizeof(svaddr))
275        == -1) {
276         save_errno = errno;
277         dbprintf(("%s: %s: connect to %s.%d failed: %s\n",
278                   debug_prefix_time(NULL),
279                   f,
280                   inet_ntoa(svaddr.sin_addr),
281                   ntohs(svaddr.sin_port),
282                   strerror(save_errno)));
283         aclose(client_socket);
284         errno = save_errno;
285         return -1;
286     }
287
288     dbprintf(("%s: %s: connected to %s.%d\n",
289               debug_prefix_time(NULL),
290               f,
291               inet_ntoa(svaddr.sin_addr),
292               ntohs(svaddr.sin_port)));
293     dbprintf(("%s: %s: our side is %s.%d\n",
294               debug_prefix(NULL),
295               f,
296               inet_ntoa(claddr.sin_addr),
297               ntohs(claddr.sin_port)));
298
299     if(sendsize >= 0) 
300         try_socksize(client_socket, SO_SNDBUF, sendsize);
301     if(recvsize >= 0) 
302         try_socksize(client_socket, SO_RCVBUF, recvsize);
303
304     if (localport != NULL)
305         *localport = ntohs(claddr.sin_port);
306
307     return client_socket;
308 }
309
310 int
311 stream_client_privileged(hostname, port, sendsize, recvsize, localport)
312     char *hostname;
313     int port, sendsize, recvsize, *localport;
314 {
315     return stream_client_internal(hostname,
316                                   port,
317                                   sendsize,
318                                   recvsize,
319                                   localport,
320                                   1);
321 }
322
323 int
324 stream_client(hostname, port, sendsize, recvsize, localport)
325     char *hostname;
326     int port, sendsize, recvsize, *localport;
327 {
328     return stream_client_internal(hostname,
329                                   port,
330                                   sendsize,
331                                   recvsize,
332                                   localport,
333                                   0);
334 }
335
336 /* don't care about these values */
337 static struct sockaddr_in addr;
338 static int addrlen;
339
340 int stream_accept(server_socket, timeout, sendsize, recvsize)
341 int server_socket, timeout, sendsize, recvsize;
342 {
343     fd_set readset;
344     struct timeval tv;
345     int nfound, connected_socket;
346     int save_errno;
347
348     assert(server_socket >= 0);
349
350     tv.tv_sec = timeout;
351     tv.tv_usec = 0;
352     FD_ZERO(&readset);
353     FD_SET(server_socket, &readset);
354     nfound = select(server_socket+1,
355                     (SELECT_ARG_TYPE *)&readset,
356                     NULL,
357                     NULL,
358                     &tv);
359     if(nfound <= 0 || !FD_ISSET(server_socket, &readset)) {
360         save_errno = errno;
361         if(nfound < 0) {
362             dbprintf(("%s: stream_accept: select() failed: %s\n",
363                       debug_prefix_time(NULL),
364                       strerror(save_errno)));
365         } else if(nfound == 0) {
366             dbprintf(("%s: stream_accept: timeout after %d second%s\n",
367                       debug_prefix_time(NULL),
368                       timeout,
369                       (timeout == 1) ? "" : "s"));
370             save_errno = ENOENT;                        /* ??? */
371         } else if (!FD_ISSET(server_socket, &readset)) {
372             int i;
373
374             for(i = 0; i < server_socket + 1; i++) {
375                 if(FD_ISSET(i, &readset)) {
376                     dbprintf(("%s: stream_accept: got fd %d instead of %d\n",
377                               debug_prefix_time(NULL),
378                               i,
379                               server_socket));
380                 }
381             }
382             save_errno = EBADF;
383         }
384         errno = save_errno;
385         return -1;
386     }
387
388     while(1) {
389         addrlen = sizeof(struct sockaddr);
390         connected_socket = accept(server_socket,
391                                   (struct sockaddr *)&addr,
392                                   &addrlen);
393         if(connected_socket < 0) {
394             save_errno = errno;
395             dbprintf(("%s: stream_accept: accept() failed: %s\n",
396                       debug_prefix_time(NULL),
397                       strerror(save_errno)));
398             errno = save_errno;
399             return -1;
400         }
401         dbprintf(("%s: stream_accept: connection from %s.%d\n",
402                   debug_prefix_time(NULL),
403                   inet_ntoa(addr.sin_addr),
404                   ntohs(addr.sin_port)));
405         /*
406          * Make certain we got an inet connection and that it is not
407          * from port 20 (a favorite unauthorized entry tool).
408          */
409         if(addr.sin_family == AF_INET && ntohs(addr.sin_port) != 20) {
410             break;
411         }
412         if(addr.sin_family != AF_INET) {
413             dbprintf(("%s: family is %d instead of %d(AF_INET): ignored\n",
414                       debug_prefix_time(NULL),
415                       addr.sin_family,
416                       AF_INET));
417         }
418         if(ntohs(addr.sin_port) == 20) {
419             dbprintf(("%s: remote port is %d: ignored\n",
420                       debug_prefix_time(NULL),
421                       ntohs(addr.sin_port)));
422         }
423         aclose(connected_socket);
424     }
425
426     if(sendsize >= 0) 
427         try_socksize(connected_socket, SO_SNDBUF, sendsize);
428     if(recvsize >= 0) 
429         try_socksize(connected_socket, SO_RCVBUF, recvsize);
430
431     return connected_socket;
432 }
433
434 static void try_socksize(sock, which, size)
435 int sock, which, size;
436 {
437     int origsize;
438
439     origsize = size;
440     /* keep trying, get as big a buffer as possible */
441     while(size > 1024 &&
442           setsockopt(sock, SOL_SOCKET, which, (void *) &size, sizeof(int)) < 0)
443         size -= 1024;
444     if(size > 1024) {
445         dbprintf(("%s: try_socksize: %s buffer size is %d\n",
446                   debug_prefix(NULL),
447                   (which == SO_SNDBUF) ? "send" : "receive",
448                   size));
449     } else {
450         dbprintf(("%s: try_socksize: could not allocate %s buffer of %d\n",
451                   debug_prefix(NULL),
452                   (which == SO_SNDBUF) ? "send" : "receive",
453                   origsize));
454     }
455 }