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