Imported Upstream version 3.3.3
[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  * Copyright (c) 2007-2012 Zmanda, Inc.  All Rights Reserved.
5  * All Rights Reserved.
6  *
7  * Permission to use, copy, modify, distribute, and sell this software and its
8  * documentation for any purpose is hereby granted without fee, provided that
9  * the above copyright notice appear in all copies and that both that
10  * copyright notice and this permission notice appear in supporting
11  * documentation, and that the name of U.M. not be used in advertising or
12  * publicity pertaining to distribution of the software without specific,
13  * written prior permission.  U.M. makes no representations about the
14  * suitability of this software for any purpose.  It is provided "as is"
15  * without express or implied warranty.
16  *
17  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
19  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  *
24  * Author: James da Silva, Systems Design and Analysis Group
25  *                         Computer Science Department
26  *                         University of Maryland at College Park
27  */
28 /* 
29  * $Id: dgram.c,v 1.32 2006/07/05 19:54:20 martinea Exp $
30  *
31  * library routines to marshall/send, recv/unmarshall UDP packets
32  */
33 #include "amanda.h"
34 #include "arglist.h"
35 #include "dgram.h"
36 #include "util.h"
37 #include "conffile.h"
38 #include "sockaddr-util.h"
39
40 void
41 dgram_socket(
42     dgram_t *   dgram,
43     int         socket)
44 {
45     if(socket < 0 || socket >= (int)FD_SETSIZE) {
46         error(_("dgram_socket: socket %d out of range (0 .. %d)\n"),
47               socket, (int)FD_SETSIZE-1);
48         /*NOTREACHED*/
49     }
50     dgram->socket = socket;
51 }
52
53
54 int
55 dgram_bind(
56     dgram_t *   dgram,
57     sa_family_t family,
58     in_port_t * portp)
59 {
60     int s, retries;
61     socklen_t_equiv len;
62     sockaddr_union name;
63     int save_errno;
64     int *portrange;
65     int sndbufsize = MAX_DGRAM;
66
67     portrange = getconf_intrange(CNF_RESERVED_UDP_PORT);
68     *portp = (in_port_t)0;
69     g_debug("dgram_bind: setting up a socket with family %d", family);
70     if((s = socket(family, SOCK_DGRAM, 0)) == -1) {
71         save_errno = errno;
72         dbprintf(_("dgram_bind: socket() failed: %s\n"),
73                   strerror(save_errno));
74         errno = save_errno;
75         return -1;
76     }
77     if(s < 0 || s >= (int)FD_SETSIZE) {
78         dbprintf(_("dgram_bind: socket out of range: %d\n"),
79                   s);
80         aclose(s);
81         errno = EMFILE;                         /* out of range */
82         return -1;
83     }
84
85     /* try setting the buffer size (= maximum allowable UDP packet size) */
86     if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
87                    (void *) &sndbufsize, sizeof(sndbufsize)) < 0) {
88        dbprintf("dgram_bind: could not set udp send buffer to %d: %s (ignored)\n",
89                  sndbufsize, strerror(errno));
90     }
91
92     SU_INIT(&name, family);
93     SU_SET_INADDR_ANY(&name);
94
95     /*
96      * If a port range was specified, we try to get a port in that
97      * range first.  Next, we try to get a reserved port.  If that
98      * fails, we just go for any port.
99      * 
100      * In all cases, not to use port that's assigned to other services. 
101      *
102      * It is up to the caller to make sure we have the proper permissions
103      * to get the desired port, and to make sure we return a port that
104      * is within the range it requires.
105      */
106     for (retries = 0; ; retries++) {
107         if (bind_portrange(s, &name, portrange[0], portrange[1], "udp") == 0)
108             goto out;
109         dbprintf(_("dgram_bind: Could not bind to port in range: %d - %d.\n"),
110                   portrange[0], portrange[1]);
111         if (retries >= BIND_CYCLE_RETRIES) {
112             dbprintf(_("dgram_bind: Giving up...\n"));
113             break;
114         }
115
116         dbprintf(_("dgram_bind: Retrying entire range after 10 second delay.\n"));
117         sleep(15);
118     }
119
120     save_errno = errno;
121     dbprintf(_("dgram_bind: bind(in6addr_any) failed: %s\n"),
122                   strerror(save_errno));
123     aclose(s);
124     errno = save_errno;
125     return -1;
126
127 out:
128     /* find out what name was actually used */
129
130     len = (socklen_t_equiv)sizeof(name);
131     if(getsockname(s, (struct sockaddr *)&name, &len) == -1) {
132         save_errno = errno;
133         dbprintf(_("dgram_bind: getsockname() failed: %s\n"), strerror(save_errno));
134         errno = save_errno;
135         aclose(s);
136         return -1;
137     }
138     *portp = SU_GET_PORT(&name);
139     dgram->socket = s;
140
141     dbprintf(_("dgram_bind: socket %d bound to %s\n"),
142               dgram->socket, str_sockaddr(&name));
143     return 0;
144 }
145
146
147 int
148 dgram_send_addr(
149     sockaddr_union      *addr,
150     dgram_t *           dgram)
151 {
152     int s, rc;
153     int socket_opened;
154     int save_errno;
155     int max_wait;
156     int wait_count;
157 #if defined(USE_REUSEADDR)
158     const int on = 1;
159     int r;
160 #endif
161
162     dbprintf(_("dgram_send_addr(addr=%p, dgram=%p)\n"),
163               addr, dgram);
164     dump_sockaddr(addr);
165     dbprintf(_("dgram_send_addr: %p->socket = %d\n"),
166               dgram, dgram->socket);
167     if(dgram->socket != -1) {
168         s = dgram->socket;
169         socket_opened = 0;
170     } else {
171         int sndbufsize = MAX_DGRAM;
172
173         g_debug("dgram_send_addr: setting up a socket with family %d", SU_GET_FAMILY(addr));
174         if((s = socket(SU_GET_FAMILY(addr), SOCK_DGRAM, 0)) == -1) {
175             save_errno = errno;
176             dbprintf(_("dgram_send_addr: socket() failed: %s\n"),
177                       strerror(save_errno));
178             errno = save_errno;
179             return -1;
180         }
181         socket_opened = 1;
182 #ifdef USE_REUSEADDR
183         r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
184                 (void *)&on, (socklen_t_equiv)sizeof(on));
185         if (r < 0) {
186             dbprintf(_("dgram_send_addr: setsockopt(SO_REUSEADDR) failed: %s\n"),
187                       strerror(errno));
188         }
189 #endif
190
191         /* try setting the buffer size (= maximum allowable UDP packet size) */
192         if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
193                        (void *) &sndbufsize, sizeof(sndbufsize)) < 0) {
194            dbprintf("dgram_send_addr: could not set udp send buffer to %d: %s (ignored)\n",
195                      sndbufsize, strerror(errno));
196         }
197     }
198
199     if(s < 0 || s >= (int)FD_SETSIZE) {
200         dbprintf(_("dgram_send_addr: socket out of range: %d\n"), s);
201         errno = EMFILE;                         /* out of range */
202         rc = -1;
203     } else {
204         max_wait = 300 / 5;                             /* five minutes */
205         wait_count = 0;
206         rc = 0;
207         while(sendto(s,
208                  dgram->data,
209                  dgram->len,
210                  0, 
211                  (struct sockaddr *)addr,
212                  SS_LEN(addr)) == -1) {
213 #ifdef ECONNREFUSED
214             if(errno == ECONNREFUSED && wait_count++ < max_wait) {
215                 sleep(5);
216                 dbprintf(_("dgram_send_addr: sendto(%s): retry %d after ECONNREFUSED\n"),
217                       str_sockaddr(addr),
218                       wait_count);
219                 continue;
220             }
221 #endif
222 #ifdef EAGAIN
223             if(errno == EAGAIN && wait_count++ < max_wait) {
224                 sleep(5);
225                 dbprintf(_("dgram_send_addr: sendto(%s): retry %d after EAGAIN\n"),
226                       str_sockaddr(addr),
227                       wait_count);
228                 continue;
229             }
230 #endif
231             save_errno = errno;
232             dbprintf(_("dgram_send_addr: sendto(%s) failed: %s \n"),
233                   str_sockaddr(addr),
234                   strerror(save_errno));
235             errno = save_errno;
236             rc = -1;
237             break;
238         }
239     }
240
241     if(socket_opened) {
242         save_errno = errno;
243         if(close(s) == -1) {
244             dbprintf(_("dgram_send_addr: close(%s): failed: %s\n"),
245                       str_sockaddr(addr),
246                       strerror(errno));
247             /*
248              * Calling function should not care that the close failed.
249              * It does care about the send results though.
250              */
251         }
252         errno = save_errno;
253     }
254
255     return rc;
256 }
257
258
259
260 ssize_t
261 dgram_recv(
262     dgram_t *           dgram,
263     int                 timeout,
264     sockaddr_union *fromaddr)
265 {
266     SELECT_ARG_TYPE ready;
267     struct timeval to;
268     ssize_t size;
269     int sock;
270     socklen_t_equiv addrlen;
271     ssize_t nfound;
272     int save_errno;
273
274     sock = dgram->socket;
275
276     FD_ZERO(&ready);
277     FD_SET(sock, &ready);
278     to.tv_sec = timeout;
279     to.tv_usec = 0;
280
281     dbprintf(_("dgram_recv(dgram=%p, timeout=%u, fromaddr=%p)\n"),
282                 dgram, timeout, fromaddr);
283     
284     nfound = (ssize_t)select(sock+1, &ready, NULL, NULL, &to);
285     if(nfound <= 0 || !FD_ISSET(sock, &ready)) {
286         save_errno = errno;
287         if(nfound < 0) {
288             dbprintf(_("dgram_recv: select() failed: %s\n"), strerror(save_errno));
289         } else if(nfound == 0) {
290             dbprintf(plural(_("dgram_recv: timeout after %d second\n"),
291                             _("dgram_recv: timeout after %d seconds\n"),
292                             timeout),
293                      timeout);
294             nfound = 0;
295         } else if (!FD_ISSET(sock, &ready)) {
296             int i;
297
298             for(i = 0; i < sock + 1; i++) {
299                 if(FD_ISSET(i, &ready)) {
300                     dbprintf(_("dgram_recv: got fd %d instead of %d\n"), i, sock);
301                 }
302             }
303             save_errno = EBADF;
304             nfound = -1;
305         }
306         errno = save_errno;
307         return nfound;
308     }
309
310     addrlen = (socklen_t_equiv)sizeof(sockaddr_union);
311     size = recvfrom(sock, dgram->data, (size_t)MAX_DGRAM, 0,
312                     (struct sockaddr *)fromaddr, &addrlen);
313     if(size == -1) {
314         save_errno = errno;
315         dbprintf(_("dgram_recv: recvfrom() failed: %s\n"), 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 = g_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 }