Imported Upstream version 2.6.1
[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 #include "sockaddr-util.h"
38
39 void
40 dgram_socket(
41     dgram_t *   dgram,
42     int         socket)
43 {
44     if(socket < 0 || socket >= (int)FD_SETSIZE) {
45         error(_("dgram_socket: socket %d out of range (0 .. %d)\n"),
46               socket, (int)FD_SETSIZE-1);
47         /*NOTREACHED*/
48     }
49     dgram->socket = socket;
50 }
51
52
53 int
54 dgram_bind(
55     dgram_t *   dgram,
56     sa_family_t family,
57     in_port_t * portp)
58 {
59     int s, retries;
60     socklen_t_equiv len;
61     sockaddr_union name;
62     int save_errno;
63     int *portrange;
64
65     portrange = getconf_intrange(CNF_RESERVED_UDP_PORT);
66     *portp = (in_port_t)0;
67     g_debug("dgram_bind: setting up a socket with family %d", family);
68     if((s = socket(family, SOCK_DGRAM, 0)) == -1) {
69         save_errno = errno;
70         dbprintf(_("dgram_bind: socket() failed: %s\n"),
71                   strerror(save_errno));
72         errno = save_errno;
73         return -1;
74     }
75     if(s < 0 || s >= (int)FD_SETSIZE) {
76         dbprintf(_("dgram_bind: socket out of range: %d\n"),
77                   s);
78         aclose(s);
79         errno = EMFILE;                         /* out of range */
80         return -1;
81     }
82
83     SU_INIT(&name, family);
84     SU_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(_("dgram_bind: Could not bind to port in range: %d - %d.\n"),
101                   portrange[0], portrange[1]);
102         if (retries >= BIND_CYCLE_RETRIES) {
103             dbprintf(_("dgram_bind: Giving up...\n"));
104             break;
105         }
106
107         dbprintf(_("dgram_bind: Retrying entire range after 10 second delay.\n"));
108         sleep(15);
109     }
110
111     save_errno = errno;
112     dbprintf(_("dgram_bind: bind(in6addr_any) failed: %s\n"),
113                   strerror(save_errno));
114     aclose(s);
115     errno = save_errno;
116     return -1;
117
118 out:
119     /* find out what name was actually used */
120
121     len = (socklen_t_equiv)sizeof(name);
122     if(getsockname(s, (struct sockaddr *)&name, &len) == -1) {
123         save_errno = errno;
124         dbprintf(_("dgram_bind: getsockname() failed: %s\n"), strerror(save_errno));
125         errno = save_errno;
126         aclose(s);
127         return -1;
128     }
129     *portp = SU_GET_PORT(&name);
130     dgram->socket = s;
131
132     dbprintf(_("dgram_bind: socket %d bound to %s\n"),
133               dgram->socket, str_sockaddr(&name));
134     return 0;
135 }
136
137
138 int
139 dgram_send_addr(
140     sockaddr_union      *addr,
141     dgram_t *           dgram)
142 {
143     int s, rc;
144     int socket_opened;
145     int save_errno;
146     int max_wait;
147     int wait_count;
148 #if defined(USE_REUSEADDR)
149     const int on = 1;
150     int r;
151 #endif
152
153     dbprintf(_("dgram_send_addr(addr=%p, dgram=%p)\n"),
154               addr, dgram);
155     dump_sockaddr(addr);
156     dbprintf(_("dgram_send_addr: %p->socket = %d\n"),
157               dgram, dgram->socket);
158     if(dgram->socket != -1) {
159         s = dgram->socket;
160         socket_opened = 0;
161     } else {
162         g_debug("dgram_send_addr: setting up a socket with family %d", SU_GET_FAMILY(addr));
163         if((s = socket(SU_GET_FAMILY(addr), SOCK_DGRAM, 0)) == -1) {
164             save_errno = errno;
165             dbprintf(_("dgram_send_addr: socket() failed: %s\n"),
166                       strerror(save_errno));
167             errno = save_errno;
168             return -1;
169         }
170         socket_opened = 1;
171 #ifdef USE_REUSEADDR
172         r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
173                 (void *)&on, (socklen_t_equiv)sizeof(on));
174         if (r < 0) {
175             dbprintf(_("dgram_send_addr: setsockopt(SO_REUSEADDR) failed: %s\n"),
176                       strerror(errno));
177         }
178 #endif
179     }
180
181     if(s < 0 || s >= (int)FD_SETSIZE) {
182         dbprintf(_("dgram_send_addr: socket out of range: %d\n"), s);
183         errno = EMFILE;                         /* out of range */
184         rc = -1;
185     } else {
186         max_wait = 300 / 5;                             /* five minutes */
187         wait_count = 0;
188         rc = 0;
189         while(sendto(s,
190                  dgram->data,
191                  dgram->len,
192                  0, 
193                  (struct sockaddr *)addr,
194                  SS_LEN(addr)) == -1) {
195 #ifdef ECONNREFUSED
196             if(errno == ECONNREFUSED && wait_count++ < max_wait) {
197                 sleep(5);
198                 dbprintf(_("dgram_send_addr: sendto(%s): retry %d after ECONNREFUSED\n"),
199                       str_sockaddr(addr),
200                       wait_count);
201                 continue;
202             }
203 #endif
204 #ifdef EAGAIN
205             if(errno == EAGAIN && wait_count++ < max_wait) {
206                 sleep(5);
207                 dbprintf(_("dgram_send_addr: sendto(%s): retry %d after EAGAIN\n"),
208                       str_sockaddr(addr),
209                       wait_count);
210                 continue;
211             }
212 #endif
213             save_errno = errno;
214             dbprintf(_("dgram_send_addr: sendto(%s) failed: %s \n"),
215                   str_sockaddr(addr),
216                   strerror(save_errno));
217             errno = save_errno;
218             rc = -1;
219             break;
220         }
221     }
222
223     if(socket_opened) {
224         save_errno = errno;
225         if(close(s) == -1) {
226             dbprintf(_("dgram_send_addr: close(%s): failed: %s\n"),
227                       str_sockaddr(addr),
228                       strerror(errno));
229             /*
230              * Calling function should not care that the close failed.
231              * It does care about the send results though.
232              */
233         }
234         errno = save_errno;
235     }
236
237     return rc;
238 }
239
240
241
242 ssize_t
243 dgram_recv(
244     dgram_t *           dgram,
245     int                 timeout,
246     sockaddr_union *fromaddr)
247 {
248     SELECT_ARG_TYPE ready;
249     struct timeval to;
250     ssize_t size;
251     int sock;
252     socklen_t_equiv addrlen;
253     ssize_t nfound;
254     int save_errno;
255
256     sock = dgram->socket;
257
258     FD_ZERO(&ready);
259     FD_SET(sock, &ready);
260     to.tv_sec = timeout;
261     to.tv_usec = 0;
262
263     dbprintf(_("dgram_recv(dgram=%p, timeout=%u, fromaddr=%p)\n"),
264                 dgram, timeout, fromaddr);
265     
266     nfound = (ssize_t)select(sock+1, &ready, NULL, NULL, &to);
267     if(nfound <= 0 || !FD_ISSET(sock, &ready)) {
268         save_errno = errno;
269         if(nfound < 0) {
270             dbprintf(_("dgram_recv: select() failed: %s\n"), strerror(save_errno));
271         } else if(nfound == 0) {
272             dbprintf(plural(_("dgram_recv: timeout after %d second\n"),
273                             _("dgram_recv: timeout after %d seconds\n"),
274                             timeout),
275                      timeout);
276             nfound = 0;
277         } else if (!FD_ISSET(sock, &ready)) {
278             int i;
279
280             for(i = 0; i < sock + 1; i++) {
281                 if(FD_ISSET(i, &ready)) {
282                     dbprintf(_("dgram_recv: got fd %d instead of %d\n"), i, sock);
283                 }
284             }
285             save_errno = EBADF;
286             nfound = -1;
287         }
288         errno = save_errno;
289         return nfound;
290     }
291
292     addrlen = (socklen_t_equiv)sizeof(sockaddr_union);
293     size = recvfrom(sock, dgram->data, (size_t)MAX_DGRAM, 0,
294                     (struct sockaddr *)fromaddr, &addrlen);
295     if(size == -1) {
296         save_errno = errno;
297         dbprintf(_("dgram_recv: recvfrom() failed: %s\n"), strerror(save_errno));
298         errno = save_errno;
299         return -1;
300     }
301     dump_sockaddr(fromaddr);
302     dgram->len = (size_t)size;
303     dgram->data[size] = '\0';
304     dgram->cur = dgram->data;
305     return size;
306 }
307
308
309 void
310 dgram_zero(
311     dgram_t *   dgram)
312 {
313     dgram->cur = dgram->data;
314     dgram->len = 0;
315     *(dgram->cur) = '\0';
316 }
317
318 printf_arglist_function1(int dgram_cat, dgram_t *, dgram, const char *, fmt)
319 {
320     ssize_t bufsize;
321     va_list argp;
322     int len;
323
324     assert(dgram != NULL);
325     assert(fmt != NULL);
326
327     assert(dgram->len == (size_t)(dgram->cur - dgram->data));
328     assert(dgram->len < SIZEOF(dgram->data));
329
330     bufsize = (ssize_t)(sizeof(dgram->data) - dgram->len);
331     if (bufsize <= 0)
332         return -1;
333
334     arglist_start(argp, fmt);
335     len = g_vsnprintf(dgram->cur, (size_t)bufsize, fmt, argp);
336     arglist_end(argp);
337     if(len < 0) {
338         return -1;
339     } else if((ssize_t)len > bufsize) {
340         dgram->len = sizeof(dgram->data);
341         dgram->cur = dgram->data + dgram->len;
342         return -1;
343     }
344     else {
345         dgram->len += len;
346         dgram->cur = dgram->data + dgram->len;
347     }
348     return 0;
349 }
350
351 void
352 dgram_eatline(
353     dgram_t *   dgram)
354 {
355     char *p = dgram->cur;
356     char *end = dgram->data + dgram->len;
357
358     while(p < end && *p && *p != '\n')
359         p++;
360     if(*p == '\n')
361         p++;
362     dgram->cur = p;
363 }