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