Imported Upstream version 2.5.2p1
[debian/amanda] / common-src / dgram.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1999 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: dgram.c,v 1.32 2006/07/05 19:54:20 martinea Exp $
29  *
30  * library routines to marshall/send, recv/unmarshall UDP packets
31  */
32 #include "amanda.h"
33 #include "arglist.h"
34 #include "dgram.h"
35 #include "util.h"
36 #include "conffile.h"
37
38 void
39 dgram_socket(
40     dgram_t *   dgram,
41     int         socket)
42 {
43     if(socket < 0 || socket >= FD_SETSIZE) {
44         error("dgram_socket: socket %d out of range (0 .. %d)\n",
45               socket, FD_SETSIZE-1);
46         /*NOTREACHED*/
47     }
48     dgram->socket = socket;
49 }
50
51
52 int
53 dgram_bind(
54     dgram_t *   dgram,
55     sa_family_t family,
56     in_port_t * portp)
57 {
58     int s, retries;
59     socklen_t len;
60     struct sockaddr_storage name;
61     int save_errno;
62     int *portrange;
63
64     portrange = getconf_intrange(CNF_RESERVED_UDP_PORT);
65     *portp = (in_port_t)0;
66     if((s = socket(family, SOCK_DGRAM, 0)) == -1) {
67         save_errno = errno;
68         dbprintf(("%s: dgram_bind: socket() failed: %s\n",
69                   debug_prefix_time(NULL),
70                   strerror(save_errno)));
71         errno = save_errno;
72         return -1;
73     }
74     if(s < 0 || s >= (int)FD_SETSIZE) {
75         dbprintf(("%s: dgram_bind: socket out of range: %d\n",
76                   debug_prefix_time(NULL),
77                   s));
78         aclose(s);
79         errno = EMFILE;                         /* out of range */
80         return -1;
81     }
82
83     SS_INIT(&name, family);
84     SS_SET_INADDR_ANY(&name);
85
86     /*
87      * If a port range was specified, we try to get a port in that
88      * range first.  Next, we try to get a reserved port.  If that
89      * fails, we just go for any port.
90      * 
91      * In all cases, not to use port that's assigned to other services. 
92      *
93      * It is up to the caller to make sure we have the proper permissions
94      * to get the desired port, and to make sure we return a port that
95      * is within the range it requires.
96      */
97     for (retries = 0; ; retries++) {
98         if (bind_portrange(s, &name, portrange[0], portrange[1], "udp") == 0)
99             goto out;
100         dbprintf(("%s: dgram_bind: Could not bind to port in range: %d - %d.\n",
101                   debug_prefix_time(NULL), portrange[0], portrange[1]));
102         if (retries >= BIND_CYCLE_RETRIES) {
103             dbprintf(("%s: dgram_bind: Giving up...\n",
104                       debug_prefix_time(NULL)));
105             break;
106         }
107
108         dbprintf(("%s: dgram_bind: Retrying entire range after 10 second delay.\n",
109                   debug_prefix_time(NULL)));
110         sleep(15);
111     }
112
113     save_errno = errno;
114     dbprintf(("%s: dgram_bind: bind(in6addr_any) failed: %s\n",
115                   debug_prefix_time(NULL),
116                   strerror(save_errno)));
117     aclose(s);
118     errno = save_errno;
119     return -1;
120
121 out:
122     /* find out what name was actually used */
123
124     len = (socklen_t)sizeof(name);
125     if(getsockname(s, (struct sockaddr *)&name, &len) == -1) {
126         save_errno = errno;
127         dbprintf(("%s: dgram_bind: getsockname() failed: %s\n",
128                   debug_prefix_time(NULL),
129                   strerror(save_errno)));
130         errno = save_errno;
131         aclose(s);
132         return -1;
133     }
134     *portp = SS_GET_PORT(&name);
135     dgram->socket = s;
136
137     dbprintf(("%s: dgram_bind: socket %d bound to %s\n",
138               debug_prefix_time(NULL), dgram->socket, str_sockaddr(&name)));
139     return 0;
140 }
141
142
143 int
144 dgram_send_addr(
145     struct sockaddr_storage     *addr,
146     dgram_t *           dgram)
147 {
148     int s, rc;
149     int socket_opened;
150     int save_errno;
151     int max_wait;
152     int wait_count;
153 #if defined(USE_REUSEADDR)
154     const int on = 1;
155     int r;
156 #endif
157
158     dbprintf(("%s: dgram_send_addr(addr=%p, dgram=%p)\n",
159               debug_prefix_time(NULL), addr, dgram));
160     dump_sockaddr(addr);
161     dbprintf(("%s: dgram_send_addr: %p->socket = %d\n",
162               debug_prefix_time(NULL), dgram, dgram->socket));
163     if(dgram->socket != -1) {
164         s = dgram->socket;
165         socket_opened = 0;
166     } else {
167         if((s = socket(addr->ss_family, SOCK_DGRAM, 0)) == -1) {
168             save_errno = errno;
169             dbprintf(("%s: dgram_send_addr: socket() failed: %s\n",
170                       debug_prefix_time(NULL),
171                       strerror(save_errno)));
172             errno = save_errno;
173             return -1;
174         }
175         socket_opened = 1;
176 #ifdef USE_REUSEADDR
177         r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
178                 (void *)&on, (socklen_t)sizeof(on));
179         if (r < 0) {
180             dbprintf(("%s: dgram_send_addr: setsockopt(SO_REUSEADDR) failed: %s\n",
181                       debug_prefix_time(NULL),
182                       strerror(errno)));
183         }
184 #endif
185     }
186
187     if(s < 0 || s >= FD_SETSIZE) {
188         dbprintf(("%s: dgram_send_addr: socket out of range: %d\n",
189                   debug_prefix_time(NULL),
190                   s));
191         errno = EMFILE;                         /* out of range */
192         rc = -1;
193     } else {
194         max_wait = 300 / 5;                             /* five minutes */
195         wait_count = 0;
196         rc = 0;
197         while(sendto(s,
198                  dgram->data,
199                  dgram->len,
200                  0, 
201                  (struct sockaddr *)addr,
202                  SS_LEN(addr)) == -1) {
203 #ifdef ECONNREFUSED
204             if(errno == ECONNREFUSED && wait_count++ < max_wait) {
205                 sleep(5);
206                 dbprintf(("%s: dgram_send_addr: sendto(%s): retry %d after ECONNREFUSED\n",
207                       debug_prefix_time(NULL),
208                       str_sockaddr(addr),
209                       wait_count));
210                 continue;
211             }
212 #endif
213 #ifdef EAGAIN
214             if(errno == EAGAIN && wait_count++ < max_wait) {
215                 sleep(5);
216                 dbprintf(("%s: dgram_send_addr: sendto(%s): retry %d after EAGAIN\n",
217                       debug_prefix_time(NULL),
218                       str_sockaddr(addr),
219                       wait_count));
220                 continue;
221             }
222 #endif
223             save_errno = errno;
224             dbprintf(("%s: dgram_send_addr: sendto(%s) failed: %s \n",
225                   debug_prefix_time(NULL),
226                   str_sockaddr(addr),
227                   strerror(save_errno)));
228             errno = save_errno;
229             rc = -1;
230             break;
231         }
232     }
233
234     if(socket_opened) {
235         save_errno = errno;
236         if(close(s) == -1) {
237             dbprintf(("%s: dgram_send_addr: close(%s): failed: %s\n",
238                       debug_prefix_time(NULL),
239                       str_sockaddr(addr),
240                       strerror(errno)));
241             /*
242              * Calling function should not care that the close failed.
243              * It does care about the send results though.
244              */
245         }
246         errno = save_errno;
247     }
248
249     return rc;
250 }
251
252
253 ssize_t
254 dgram_recv(
255     dgram_t *           dgram,
256     int                 timeout,
257     struct sockaddr_storage *fromaddr)
258 {
259     SELECT_ARG_TYPE ready;
260     struct timeval to;
261     ssize_t size;
262     int sock;
263     socklen_t addrlen;
264     ssize_t nfound;
265     int save_errno;
266
267     sock = dgram->socket;
268
269     FD_ZERO(&ready);
270     FD_SET(sock, &ready);
271     to.tv_sec = timeout;
272     to.tv_usec = 0;
273
274     dbprintf(("%s: dgram_recv(dgram=%p, timeout=%u, fromaddr=%p)\n",
275                 debug_prefix_time(NULL), dgram, timeout, fromaddr));
276     
277     nfound = (ssize_t)select(sock+1, &ready, NULL, NULL, &to);
278     if(nfound <= 0 || !FD_ISSET(sock, &ready)) {
279         save_errno = errno;
280         if(nfound < 0) {
281             dbprintf(("%s: dgram_recv: select() failed: %s\n",
282                       debug_prefix_time(NULL),
283                       strerror(save_errno)));
284         } else if(nfound == 0) {
285             dbprintf(("%s: dgram_recv: timeout after %d second%s\n",
286                       debug_prefix_time(NULL),
287                       timeout,
288                       (timeout == 1) ? "" : "s"));
289             nfound = 0;
290         } else if (!FD_ISSET(sock, &ready)) {
291             int i;
292
293             for(i = 0; i < sock + 1; i++) {
294                 if(FD_ISSET(i, &ready)) {
295                     dbprintf(("%s: dgram_recv: got fd %d instead of %d\n",
296                               debug_prefix_time(NULL),
297                               i,
298                               sock));
299                 }
300             }
301             save_errno = EBADF;
302             nfound = -1;
303         }
304         errno = save_errno;
305         return nfound;
306     }
307
308     addrlen = (socklen_t)sizeof(struct sockaddr_storage);
309     size = recvfrom(sock, dgram->data, (size_t)MAX_DGRAM, 0,
310                     (struct sockaddr *)fromaddr, &addrlen);
311     if(size == -1) {
312         save_errno = errno;
313         dbprintf(("%s: dgram_recv: recvfrom() failed: %s\n",
314                   debug_prefix_time(NULL),
315                   strerror(save_errno)));
316         errno = save_errno;
317         return -1;
318     }
319     dump_sockaddr(fromaddr);
320     dgram->len = (size_t)size;
321     dgram->data[size] = '\0';
322     dgram->cur = dgram->data;
323     return size;
324 }
325
326
327 void
328 dgram_zero(
329     dgram_t *   dgram)
330 {
331     dgram->cur = dgram->data;
332     dgram->len = 0;
333     *(dgram->cur) = '\0';
334 }
335
336 printf_arglist_function1(int dgram_cat, dgram_t *, dgram, const char *, fmt)
337 {
338     ssize_t bufsize;
339     va_list argp;
340     int len;
341
342     assert(dgram != NULL);
343     assert(fmt != NULL);
344
345     assert(dgram->len == (size_t)(dgram->cur - dgram->data));
346     assert(dgram->len < SIZEOF(dgram->data));
347
348     bufsize = (ssize_t)(sizeof(dgram->data) - dgram->len);
349     if (bufsize <= 0)
350         return -1;
351
352     arglist_start(argp, fmt);
353     len = vsnprintf(dgram->cur, (size_t)bufsize, fmt, argp);
354     arglist_end(argp);
355     if(len < 0) {
356         return -1;
357     } else if((ssize_t)len > bufsize) {
358         dgram->len = sizeof(dgram->data);
359         dgram->cur = dgram->data + dgram->len;
360         return -1;
361     }
362     else {
363         dgram->len += len;
364         dgram->cur = dgram->data + dgram->len;
365     }
366     return 0;
367 }
368
369 void
370 dgram_eatline(
371     dgram_t *   dgram)
372 {
373     char *p = dgram->cur;
374     char *end = dgram->data + dgram->len;
375
376     while(p < end && *p && *p != '\n')
377         p++;
378     if(*p == '\n')
379         p++;
380     dgram->cur = p;
381 }