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