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