cdf8a09912110f1f414e625c193afe2242b89959
[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.29 2006/01/12 01:57:05 paddy_s 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
37 void dgram_socket(dgram, socket)
38 dgram_t *dgram;
39 int socket;
40 {
41     if(socket < 0 || socket >= FD_SETSIZE) {
42         error("dgram_socket: socket %d out of range (0 .. %d)\n",
43               socket, FD_SETSIZE-1);
44     }
45     dgram->socket = socket;
46 }
47
48
49 int dgram_bind(dgram, portp)
50 dgram_t *dgram;
51 int *portp;
52 {
53     int s;
54     socklen_t len;
55     struct sockaddr_in name;
56     int save_errno;
57
58     if((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
59         save_errno = errno;
60         dbprintf(("%s: dgram_bind: socket() failed: %s\n",
61                   debug_prefix(NULL),
62                   strerror(save_errno)));
63         errno = save_errno;
64         return -1;
65     }
66     if(s < 0 || s >= FD_SETSIZE) {
67         dbprintf(("%s: dgram_bind: socket out of range: %d\n",
68                   debug_prefix(NULL),
69                   s));
70         aclose(s);
71         errno = EMFILE;                         /* out of range */
72         return -1;
73     }
74
75     memset(&name, 0, sizeof(name));
76     name.sin_family = AF_INET;
77     name.sin_addr.s_addr = INADDR_ANY;
78
79     /*
80      * If a port range was specified, we try to get a port in that
81      * range first.  Next, we try to get a reserved port.  If that
82      * fails, we just go for any port.
83      * 
84      * In all cases, not to use port that's assigned to other services. 
85      *
86      * It is up to the caller to make sure we have the proper permissions
87      * to get the desired port, and to make sure we return a port that
88      * is within the range it requires.
89      */
90 #ifdef UDPPORTRANGE
91     if (bind_portrange(s, &name, UDPPORTRANGE, "udp") == 0)
92         goto out;
93 #endif
94
95     if (bind_portrange(s, &name, 512, IPPORT_RESERVED - 1, "udp") == 0)
96         goto out;
97
98     name.sin_port = INADDR_ANY;
99     if (bind(s, (struct sockaddr *)&name, (socklen_t)sizeof name) == -1) {
100         save_errno = errno;
101         dbprintf(("%s: dgram_bind: bind(INADDR_ANY) failed: %s\n",
102                   debug_prefix(NULL),
103                   strerror(save_errno)));
104         errno = save_errno;
105         aclose(s);
106         return -1;
107     }
108
109 out:
110     /* find out what name was actually used */
111
112     len = (socklen_t) sizeof name;
113     if(getsockname(s, (struct sockaddr *)&name, &len) == -1) {
114         save_errno = errno;
115         dbprintf(("%s: dgram_bind: getsockname() failed: %s\n",
116                   debug_prefix(NULL),
117                   strerror(save_errno)));
118         errno = save_errno;
119         aclose(s);
120         return -1;
121     }
122     *portp = ntohs(name.sin_port);
123     dgram->socket = s;
124
125     dbprintf(("%s: dgram_bind: socket bound to %s.%d\n",
126               debug_prefix_time(NULL),
127               inet_ntoa(name.sin_addr),
128               *portp));
129     return 0;
130 }
131
132
133 int dgram_send_addr(addr, dgram)
134 struct sockaddr_in addr;
135 dgram_t *dgram;
136 {
137     int s;
138     int socket_opened;
139     struct sockaddr_in addr_save;
140     int save_errno;
141     int max_wait;
142     int wait_count;
143
144     if(dgram->socket != -1) {
145         s = dgram->socket;
146         socket_opened = 0;
147     } else {
148         if((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
149             save_errno = errno;
150             dbprintf(("%s: dgram_send_addr: socket() failed: %s\n",
151                       debug_prefix(NULL),
152                       strerror(save_errno)));
153             errno = save_errno;
154             return -1;
155         }
156         socket_opened = 1;
157     }
158
159     if(s < 0 || s >= FD_SETSIZE) {
160         dbprintf(("%s: dgram_send_addr: socket out of range: %d\n",
161                   debug_prefix(NULL),
162                   s));
163         if(socket_opened) {
164             aclose(s);
165         }
166         errno = EMFILE;                         /* out of range */
167         return -1;
168     }
169
170     memcpy(&addr_save, &addr, sizeof(addr));
171     max_wait = 300 / 5;                         /* five minutes */
172     wait_count = 0;
173     while(sendto(s,
174                  dgram->data,
175                  dgram->len,
176                  0, 
177                  (struct sockaddr *)&addr,
178                  (socklen_t) sizeof(struct sockaddr_in)) == -1) {
179 #ifdef ECONNREFUSED
180         if(errno == ECONNREFUSED && wait_count++ < max_wait) {
181             sleep(5);
182             dbprintf(("%s: dgram_send_addr: sendto(%s.%d): retry %d after ECONNREFUSED\n",
183                       debug_prefix_time(NULL),
184                       inet_ntoa(addr_save.sin_addr),
185                       (int) ntohs(addr.sin_port),
186                       wait_count));
187             continue;
188         }
189 #endif
190 #ifdef EAGAIN
191         if(errno == EAGAIN && wait_count++ < max_wait) {
192             sleep(5);
193             dbprintf(("%s: dgram_send_addr: sendto(%s.%d): retry %d after EAGAIN\n",
194                       debug_prefix_time(NULL),
195                       inet_ntoa(addr_save.sin_addr),
196                       (int) ntohs(addr.sin_port),
197                       wait_count));
198             continue;
199         }
200 #endif
201         save_errno = errno;
202         dbprintf(("%s: dgram_send_addr: sendto(%s.%d) failed: %s \n",
203                   debug_prefix_time(NULL),
204                   inet_ntoa(addr_save.sin_addr),
205                   (int) ntohs(addr.sin_port),
206                   strerror(save_errno)));
207         errno = save_errno;
208         return -1;
209     }
210
211     if(socket_opened) {
212         if(close(s) == -1) {
213             save_errno = errno;
214             dbprintf(("%s: dgram_send_addr: close(%s.%d): failed: %s\n",
215                       debug_prefix(NULL),
216                       inet_ntoa(addr_save.sin_addr),
217                       (int) ntohs(addr.sin_port),
218                       strerror(save_errno)));
219             errno = save_errno;
220             return -1;
221         }
222         s = -1;
223     }
224
225     return 0;
226 }
227
228
229 int dgram_send(hostname, port, dgram)
230 char *hostname;
231 int port;
232 dgram_t *dgram;
233 {
234     struct sockaddr_in name;
235     struct hostent *hp;
236     int save_errno;
237
238     if((hp = gethostbyname(hostname)) == 0) {
239         save_errno = errno;
240         dbprintf(("%s: dgram_send: gethostbyname(%s) failed\n",
241                   debug_prefix_time(NULL),
242                   hostname));
243         errno = save_errno;
244         return -1;
245     }
246     memcpy(&name.sin_addr, hp->h_addr, hp->h_length);
247     name.sin_family = AF_INET;
248     name.sin_port = htons(port);
249
250     return dgram_send_addr(name, dgram);
251 }
252
253
254 int dgram_recv(dgram, timeout, fromaddr)
255 dgram_t *dgram;
256 int timeout;
257 struct sockaddr_in *fromaddr;
258 {
259     fd_set ready;
260     struct timeval to;
261     ssize_t size;
262     int sock;
263     socklen_t addrlen;
264     int 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     nfound = select(sock+1, (SELECT_ARG_TYPE *)&ready, NULL, NULL, &to);
275     if(nfound <= 0 || !FD_ISSET(sock, &ready)) {
276         save_errno = errno;
277         if(nfound < 0) {
278             dbprintf(("%s: dgram_recv: select() failed: %s\n",
279                       debug_prefix_time(NULL),
280                       strerror(save_errno)));
281         } else if(nfound == 0) {
282             dbprintf(("%s: dgram_recv: timeout after %d second%s\n",
283                       debug_prefix_time(NULL),
284                       timeout,
285                       (timeout == 1) ? "" : "s"));
286             nfound = 0;
287         } else if (!FD_ISSET(sock, &ready)) {
288             int i;
289
290             for(i = 0; i < sock + 1; i++) {
291                 if(FD_ISSET(i, &ready)) {
292                     dbprintf(("%s: dgram_recv: got fd %d instead of %d\n",
293                               debug_prefix_time(NULL),
294                               i,
295                               sock));
296                 }
297             }
298             save_errno = EBADF;
299             nfound = -1;
300         }
301         errno = save_errno;
302         return nfound;
303     }
304
305     addrlen = (socklen_t) sizeof(struct sockaddr_in);
306     size = recvfrom(sock, dgram->data, MAX_DGRAM, 0,
307                     (struct sockaddr *)fromaddr, &addrlen);
308     if(size == -1) {
309         save_errno = errno;
310         dbprintf(("%s: dgram_recv: recvfrom() failed: %s\n",
311                   debug_prefix(NULL),
312                   strerror(save_errno)));
313         errno = save_errno;
314         return -1;
315     }
316     dgram->len = size;
317     dgram->data[size] = '\0';
318     dgram->cur = dgram->data;
319     return size;
320 }
321
322
323 void dgram_zero(dgram)
324 dgram_t *dgram;
325 {
326     dgram->cur = dgram->data;
327     dgram->len = 0;
328     *(dgram->cur) = '\0';
329 }
330
331 printf_arglist_function1(void dgram_cat, dgram_t *, dgram, const char *, fmt)
332 {
333     size_t bufsize;
334     va_list argp;
335
336     assert(dgram != NULL);
337     assert(fmt != NULL);
338
339     assert(dgram->len == dgram->cur - dgram->data);
340     assert(dgram->len >= 0 && dgram->len < sizeof(dgram->data));
341
342     bufsize = sizeof(dgram->data) - dgram->len;
343     if (bufsize <= 0)
344         return;
345
346     arglist_start(argp, fmt);
347     dgram->len += vsnprintf(dgram->cur, bufsize, fmt, argp);
348     dgram->cur = dgram->data + dgram->len;
349     arglist_end(argp);
350 }
351
352 void dgram_eatline(dgram)
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') p++;
359     if(*p == '\n') p++;
360     dgram->cur = p;
361 }