Mercurial > notdcc
comparison dcclib/get_port.c @ 0:c7f6b056b673
First import of vendor version
author | Peter Gervai <grin@grin.hu> |
---|---|
date | Tue, 10 Mar 2009 13:49:58 +0100 |
parents | |
children | f6716cb00029 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:c7f6b056b673 |
---|---|
1 /* Distributed Checksum Clearinghouse | |
2 * | |
3 * convert a service name to a port number | |
4 * | |
5 * Copyright (c) 2008 by Rhyolite Software, LLC | |
6 * | |
7 * This agreement is not applicable to any entity which sells anti-spam | |
8 * solutions to others or provides an anti-spam solution as part of a | |
9 * security solution sold to other entities, or to a private network | |
10 * which employs the DCC or uses data provided by operation of the DCC | |
11 * but does not provide corresponding data to other users. | |
12 * | |
13 * Permission to use, copy, modify, and distribute this software without | |
14 * changes for any purpose with or without fee is hereby granted, provided | |
15 * that the above copyright notice and this permission notice appear in all | |
16 * copies and any distributed versions or copies are either unchanged | |
17 * or not called anything similar to "DCC" or "Distributed Checksum | |
18 * Clearinghouse". | |
19 * | |
20 * Parties not eligible to receive a license under this agreement can | |
21 * obtain a commercial license to use DCC by contacting Rhyolite Software | |
22 * at sales@rhyolite.com. | |
23 * | |
24 * A commercial license would be for Distributed Checksum and Reputation | |
25 * Clearinghouse software. That software includes additional features. This | |
26 * free license for Distributed ChecksumClearinghouse Software does not in any | |
27 * way grant permision to use Distributed Checksum and Reputation Clearinghouse | |
28 * software | |
29 * | |
30 * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL | |
31 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES | |
32 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC | |
33 * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES | |
34 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, | |
35 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, | |
36 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS | |
37 * SOFTWARE. | |
38 * | |
39 * Rhyolite Software DCC 1.3.103-1.79 $Revision$ | |
40 */ | |
41 | |
42 #include "dcc_clnt.h" | |
43 #ifndef DCC_WIN32 | |
44 #include <arpa/inet.h> /* for AIX */ | |
45 #endif | |
46 | |
47 | |
48 DCC_SOCKU dcc_hostaddrs[MAX_DCC_HOSTADDRS]; | |
49 char dcc_host_canonname[DCC_MAXDOMAINLEN]; | |
50 DCC_SOCKU *dcc_hostaddrs_end; | |
51 | |
52 | |
53 /* get port number | |
54 * Note that this function uses dcc_host_lock() and dcc_host_unlock() */ | |
55 u_int /* DCC_GET_PORT_INVALID or port # */ | |
56 dcc_get_port(DCC_EMSG emsg, | |
57 const char *portname, | |
58 u_int def_port, /* DCC_GET_PORT_INVALID or default */ | |
59 const char *fnm, int lno) | |
60 { | |
61 DCC_FNM_LNO_BUF fnm_buf; | |
62 char *p; | |
63 unsigned long l; | |
64 struct servent *sp; | |
65 u_int16_t port; | |
66 | |
67 | |
68 if (portname[0] == '\0' | |
69 || !strcmp(portname, "-")) { | |
70 if (def_port != DCC_GET_PORT_INVALID) | |
71 return def_port; | |
72 dcc_pemsg(EX_USAGE, emsg, "missing port%s", | |
73 fnm_lno(&fnm_buf, fnm, lno)); | |
74 return DCC_GET_PORT_INVALID; | |
75 } | |
76 | |
77 /* first try a numeric port number, since that is common and | |
78 * the getservby* functions are so slow. */ | |
79 l = strtoul(portname, &p,0); | |
80 if (*p == '\0' && l > 0 && l <= 65535) | |
81 return htons((u_int16_t)l); | |
82 | |
83 dcc_host_lock(); | |
84 sp = getservbyname(portname, 0); | |
85 if (sp) { | |
86 port = sp->s_port; | |
87 dcc_host_unlock(); | |
88 return port; | |
89 } | |
90 dcc_host_unlock(); | |
91 | |
92 dcc_pemsg(EX_USAGE, emsg, "invalid port \"%s\"%s", | |
93 portname, fnm_lno(&fnm_buf, fnm, lno)); | |
94 return DCC_GET_PORT_INVALID; | |
95 } | |
96 | |
97 | |
98 | |
99 static void | |
100 copy_hp_to_hostaddrs(const struct hostent *hp, | |
101 u_char family) | |
102 { | |
103 DCC_SOCKU *sup, *sup1; | |
104 int i; | |
105 const void *v; | |
106 | |
107 if (hp->h_name && dcc_host_canonname[0] == '\0') | |
108 BUFCPY(dcc_host_canonname, hp->h_name); | |
109 | |
110 sup = dcc_hostaddrs_end; | |
111 for (i = 0; | |
112 (v = hp->h_addr_list[i]) != 0 && sup <= LAST(dcc_hostaddrs); | |
113 ++i) { | |
114 dcc_mk_su(sup, family, v, 0); | |
115 /* deal with stuttering */ | |
116 sup1 = dcc_hostaddrs; | |
117 for (;;) { | |
118 if (sup1 >= sup) { | |
119 ++sup; | |
120 break; | |
121 } | |
122 if (DCC_SU_EQ(sup1, sup)) | |
123 break; | |
124 ++sup1; | |
125 } | |
126 } | |
127 dcc_hostaddrs_end = sup; | |
128 } | |
129 | |
130 | |
131 | |
132 #ifdef USE_GETADDRINFO | |
133 static void | |
134 copy_ai_to_hostaddrs(const struct addrinfo *ai, u_char use_ipv6) | |
135 { | |
136 DCC_SOCKU *sup, *sup1; | |
137 | |
138 if (ai->ai_canonname && dcc_host_canonname[0] == '\0') | |
139 BUFCPY(dcc_host_canonname, ai->ai_canonname); | |
140 | |
141 for (sup = dcc_hostaddrs_end; | |
142 ai && sup <= LAST(dcc_hostaddrs); | |
143 ai = ai->ai_next) { | |
144 if (ai->ai_family == AF_INET) { | |
145 if (use_ipv6 == 1) | |
146 continue; | |
147 dcc_mk_su(sup, AF_INET, | |
148 &((struct sockaddr_in * | |
149 )ai->ai_addr)->sin_addr, 0); | |
150 } else if (ai->ai_family == AF_INET6) { | |
151 if (use_ipv6 == 0) | |
152 continue; | |
153 dcc_mk_su(sup, AF_INET6, | |
154 &((struct sockaddr_in6 * | |
155 )ai->ai_addr)->sin6_addr, 0); | |
156 } else { | |
157 continue; | |
158 } | |
159 | |
160 /* deal with stuttering */ | |
161 sup1 = dcc_hostaddrs; | |
162 for (;;) { | |
163 if (sup1 >= sup) { | |
164 ++sup; | |
165 break; | |
166 } | |
167 if (DCC_SU_EQ(sup1, sup)) | |
168 break; | |
169 ++sup1; | |
170 } | |
171 } | |
172 dcc_hostaddrs_end = sup; | |
173 } | |
174 #endif /* USE_GETADDRINFO */ | |
175 | |
176 | |
177 | |
178 /* Convert a host name to an IPv4 address by calling | |
179 * Rgethostbyname() or gethostbyname() | |
180 * This must be protected with dcc_host_lock() and dcc_host_unlock(). */ | |
181 static u_char | |
182 dcc_get_host_ipv4(const char *nm, /* look for this name */ | |
183 int *ep, /* put errno or herrno here */ | |
184 struct hostent *(WSAAPI fnc)(const char *)) | |
185 { | |
186 const struct hostent *hp; | |
187 struct in_addr ipv4; | |
188 | |
189 if (!dcc_host_locked) | |
190 dcc_logbad(EX_SOFTWARE, "dcc_get_host() not locked"); | |
191 | |
192 dcc_host_canonname[0] = '\0'; | |
193 dcc_hostaddrs_end = &dcc_hostaddrs[0]; | |
194 | |
195 /* First see if it is a number to avoid the MicroStupid stall | |
196 * when doing a gethostbyname() on a number */ | |
197 ipv4.s_addr = inet_addr(nm); | |
198 if (ipv4.s_addr != INADDR_NONE) { | |
199 dcc_mk_su(&dcc_hostaddrs[0], AF_INET, &ipv4, 0); | |
200 dcc_hostaddrs_end = &dcc_hostaddrs[1]; | |
201 return 1; | |
202 } | |
203 | |
204 hp = fnc(nm); | |
205 if (!hp) { | |
206 *ep = h_errno; | |
207 return 0; | |
208 } | |
209 copy_hp_to_hostaddrs(hp, AF_INET); | |
210 return 1; | |
211 } | |
212 | |
213 | |
214 | |
215 /* This must be protected with dcc_host_lock()and dcc_host_unlock(). */ | |
216 u_char /* 0=failed */ | |
217 dcc_get_host_SOCKS(const char *nm, /* look for this name */ | |
218 u_char use_ipv6, /* 0=v4 1=v6 2=prefer v6 3=prefer v4 */ | |
219 int *ep) /* put errno or herrno here */ | |
220 { | |
221 int error1, error2; | |
222 | |
223 /* since there is no Rgetaddrinfo() or equivalent, | |
224 * use the normal resolver if we need an IPv6 address */ | |
225 switch (use_ipv6) { | |
226 case 0: | |
227 return dcc_get_host_ipv4(nm, ep, Rgethostbyname); | |
228 case 1: | |
229 return dcc_get_host(nm, 1, ep); | |
230 case 2: | |
231 if (dcc_get_host(nm, 1, &error1)) | |
232 return 1; | |
233 return dcc_get_host_ipv4(nm, ep, Rgethostbyname); | |
234 case 3: | |
235 default: | |
236 if (dcc_get_host_ipv4(nm, &error1, Rgethostbyname)) | |
237 return 1; | |
238 if (dcc_get_host(nm, 1, &error2)) | |
239 return 1; | |
240 *ep = error1; | |
241 return 0; | |
242 } | |
243 } | |
244 | |
245 | |
246 | |
247 /* This must be protected with dcc_host_lock()and dcc_host_unlock(). | |
248 * It does not assme that gethostbyname() or whatever is thread safe. | |
249 * This function is mentioned in dccifd/dccif-test/dccif-test.c | |
250 * and so cannot change lightly. */ | |
251 u_char /* 0=failed */ | |
252 dcc_get_host(const char *nm, /* look for this name */ | |
253 u_char use_ipv6, /* 0=v4 1=v6 2=prefer v6 3=prefer v4 */ | |
254 int *ep) /* put errno or herrno here */ | |
255 { | |
256 #undef EXPANDED | |
257 #if defined(USE_GETIPNODEBYNAME) && !defined(EXPANDED) && !defined(NO_IPV6) | |
258 #define EXPANDED | |
259 static struct hostent *hp; | |
260 | |
261 if (!dcc_host_locked) | |
262 dcc_logbad(EX_SOFTWARE, "dcc_get_host() not locked"); | |
263 | |
264 dcc_host_canonname[0] = '\0'; | |
265 dcc_hostaddrs_end = &dcc_hostaddrs[0]; | |
266 | |
267 /* given a choice, return both IPv4 and IPv6 addresses */ | |
268 if (use_ipv6 == 0 || use_ipv6 == 3) { | |
269 hp = getipnodebyname(nm, AF_INET, 0, ep); | |
270 if (hp) { | |
271 copy_hp_to_hostaddrs(hp, AF_INET); | |
272 freehostent(hp); | |
273 } | |
274 } | |
275 if (use_ipv6 != 0) { | |
276 hp = getipnodebyname(nm, AF_INET6,0, ep); | |
277 if (hp) { | |
278 copy_hp_to_hostaddrs(hp, AF_INET6); | |
279 freehostent(hp); | |
280 } | |
281 } | |
282 if (use_ipv6 == 2) { | |
283 hp = getipnodebyname(nm, AF_INET, 0, ep); | |
284 if (hp) { | |
285 copy_hp_to_hostaddrs(hp, AF_INET); | |
286 freehostent(hp); | |
287 } | |
288 } | |
289 if (dcc_hostaddrs_end != &dcc_hostaddrs[0]) | |
290 return 1; | |
291 *ep = h_errno; | |
292 return 0; | |
293 #endif | |
294 #if defined(USE_GETADDRINFO) && !defined(EXPANDED) && !defined(NO_IPV6) | |
295 #define EXPANDED | |
296 static struct addrinfo hints; | |
297 struct addrinfo *ai; | |
298 int error = 0; | |
299 | |
300 if (!dcc_host_locked) | |
301 dcc_logbad(EX_SOFTWARE, "dcc_get_host() not locked"); | |
302 | |
303 dcc_host_canonname[0] = '\0'; | |
304 dcc_hostaddrs_end = &dcc_hostaddrs[0]; | |
305 | |
306 hints.ai_flags = AI_CANONNAME; | |
307 | |
308 /* the FreeBSD version stutters trying to provide both UDP and | |
309 * TCP if you do not choose */ | |
310 hints.ai_protocol = IPPROTO_TCP; | |
311 | |
312 /* if you think AF_UNSPEC should provide IPv4 and IPv6, you evidently | |
313 * think wrong for at least the FreeBSD version, unless both flavors | |
314 * are in /etc/hosts */ | |
315 if (use_ipv6 == 0 || use_ipv6 == 3) { | |
316 hints.ai_family = AF_INET; | |
317 error = getaddrinfo(nm, 0, &hints, &ai); | |
318 if (!error) { | |
319 copy_ai_to_hostaddrs(ai, 0); | |
320 freeaddrinfo(ai); | |
321 } | |
322 } | |
323 if (use_ipv6 != 0) { | |
324 hints.ai_family = AF_INET6; | |
325 error = getaddrinfo(nm, 0, &hints, &ai); | |
326 if (!error) { | |
327 copy_ai_to_hostaddrs(ai, use_ipv6); | |
328 freeaddrinfo(ai); | |
329 } | |
330 } | |
331 if (use_ipv6 == 2) { | |
332 hints.ai_family = AF_INET; | |
333 error = getaddrinfo(nm, 0, &hints, &ai); | |
334 if (!error) { | |
335 copy_ai_to_hostaddrs(ai, 0); | |
336 freeaddrinfo(ai); | |
337 } | |
338 } | |
339 if (dcc_hostaddrs_end != &dcc_hostaddrs[0]) | |
340 return 1; | |
341 *ep = error; | |
342 return 0; | |
343 #endif | |
344 #ifndef EXPANDED | |
345 /* this platform can only handle IPv4 */ | |
346 if (use_ipv6 == 1) { | |
347 *ep = HOST_NOT_FOUND; | |
348 return 0; | |
349 } | |
350 return dcc_get_host_ipv4(nm, ep, gethostbyname); | |
351 #endif | |
352 #undef EXPANDED | |
353 } | |
354 | |
355 | |
356 | |
357 /* make socket address from an IP address, a family, and a port number */ | |
358 DCC_SOCKU * | |
359 dcc_mk_su(DCC_SOCKU *su, /* put it here */ | |
360 int family, /* AF_INET or AF_INET6 */ | |
361 const void *addrp, /* this IP address; 0=INADDR_ANY */ | |
362 u_short port) | |
363 { | |
364 memset(su, 0, sizeof(*su)); /* assume INADDR_ANY=0 */ | |
365 su->sa.sa_family = family; | |
366 if (family == AF_INET) { | |
367 #ifdef HAVE_SA_LEN | |
368 su->sa.sa_len = sizeof(struct sockaddr_in); | |
369 #endif | |
370 su->ipv4.sin_port = port; | |
371 if (addrp) | |
372 memcpy(&su->ipv4.sin_addr, addrp, | |
373 sizeof(su->ipv4.sin_addr)); | |
374 } else { | |
375 #ifdef HAVE_SA_LEN | |
376 su->sa.sa_len = sizeof(struct sockaddr_in6); | |
377 #endif | |
378 su->ipv6.sin6_port = port; | |
379 if (addrp) | |
380 memcpy(&su->ipv6.sin6_addr, addrp, | |
381 sizeof(su->ipv6.sin6_addr)); | |
382 } | |
383 | |
384 return su; | |
385 } | |
386 | |
387 | |
388 | |
389 /* strip leading and trailing white space */ | |
390 static const char * | |
391 dcc_strip_white(const char *str, u_int *lenp) | |
392 { | |
393 const char *end; | |
394 char c; | |
395 | |
396 str += strspn(str, DCC_WHITESPACE); | |
397 end = str+strlen(str); | |
398 while (end > str) { | |
399 c = *(end-1); | |
400 if (c != ' ' && c != '\t' && c != '\r' && c != '\n') | |
401 break; | |
402 --end; | |
403 } | |
404 *lenp = end-str; | |
405 return str; | |
406 } | |
407 | |
408 | |
409 | |
410 /* get a socket address from a dotted quad or IPv6 string */ | |
411 u_char | |
412 dcc_str2ip(DCC_SOCKU *su, const char *str) | |
413 { | |
414 #ifndef NO_IPV6 | |
415 u_int len; | |
416 char buf[INET6_ADDRSTRLEN]; | |
417 #endif | |
418 | |
419 #ifdef HAVE_INET_ATON | |
420 if (0 < inet_aton(str, &su->ipv4.sin_addr)) { | |
421 su->sa.sa_family = AF_INET; | |
422 return 1; | |
423 } | |
424 #else | |
425 u_int addr = inet_addr(str); | |
426 if (su->ipv4.sin_addr.s_addr != INADDR_NONE) { | |
427 su->ipv4.sin_addr.s_addr = addr; | |
428 su->sa.sa_family = AF_INET; | |
429 return 1; | |
430 } | |
431 #endif | |
432 | |
433 #ifndef NO_IPV6 | |
434 /* Try IPv6 only after failing to understand the address as IPv4. | |
435 * | |
436 * inet_pton() does not like blanks or terminal '\n' | |
437 * It is also too smart by half and assumes that it is | |
438 * given a pointer to struct sockaddr. When it decodes | |
439 * an IPv4 address, it sticks it 4 bytes before the | |
440 * start of the IPv6 buffer it is given. */ | |
441 str = dcc_strip_white(str, &len); | |
442 if (len == 0 || len >= sizeof(buf)) | |
443 return 0; | |
444 memcpy(buf, str, len); | |
445 buf[len] = '\0'; | |
446 if (0 < inet_pton(AF_INET6, buf, &su->ipv6.sin6_addr)) { | |
447 su->sa.sa_family = AF_INET6; | |
448 return 1; | |
449 } | |
450 #endif | |
451 return 0; | |
452 } | |
453 | |
454 | |
455 | |
456 void | |
457 dcc_bits2mask(struct in6_addr *mask, int bits) | |
458 { | |
459 int wordno, i; | |
460 | |
461 for (wordno = 0; wordno < 4; ++wordno) { | |
462 i = bits - wordno*32; | |
463 if (i >= 32) { | |
464 mask->s6_addr32[wordno] = 0xffffffff; | |
465 continue; | |
466 } | |
467 if (i <= 0) { | |
468 mask->s6_addr32[wordno] = 0; | |
469 } else { | |
470 mask->s6_addr32[wordno] = 0xffffffff << (32-i); | |
471 } | |
472 mask->s6_addr32[wordno] = htonl(mask->s6_addr32[wordno]); | |
473 } | |
474 } | |
475 | |
476 | |
477 | |
478 /* get an IPv6 address and netmask */ | |
479 int /* # of bits, 0=not address, -1 error */ | |
480 dcc_str2cidr(DCC_EMSG emsg, | |
481 struct in6_addr *addr6, | |
482 struct in6_addr *mask6, | |
483 u_char *is_ipv6, | |
484 const char *str, | |
485 const char *fnm, int lno) | |
486 { | |
487 DCC_FNM_LNO_BUF fnm_buf; | |
488 char addrstr[INET6_ADDRSTRLEN]; | |
489 DCC_SOCKU su; | |
490 struct in6_addr mask6_loc; | |
491 const char *bitsp; | |
492 char *p; | |
493 u_int str_len, addr_len, bits_len; | |
494 int n, bits, wordno; | |
495 | |
496 str = dcc_strip_white(str, &str_len); | |
497 bitsp = strchr(str, '/'); | |
498 | |
499 if (!bitsp) { | |
500 addr_len = str_len; | |
501 } else { | |
502 addr_len = bitsp - str; | |
503 } | |
504 if (addr_len == 0 || addr_len >= ISZ(addrstr)) | |
505 return 0; /* not an IP address */ | |
506 memcpy(addrstr, str, addr_len); | |
507 addrstr[addr_len] = '\0'; | |
508 if (!dcc_str2ip(&su, addrstr)) | |
509 return 0; /* not an IP address */ | |
510 | |
511 if (!bitsp) { | |
512 if (su.sa.sa_family == AF_INET6) { | |
513 bitsp = "128"; | |
514 bits_len = 3; | |
515 } else { | |
516 bitsp = "32"; | |
517 bits_len = 2; | |
518 } | |
519 bits = 128; | |
520 } else { | |
521 bits_len = str_len - addr_len - 1; | |
522 n = strtoul(++bitsp, &p, 10); | |
523 bits = n; | |
524 if (su.sa.sa_family == AF_INET) | |
525 bits += 128-32; | |
526 if (p < bitsp+bits_len || bits > 128 || n == 0) { | |
527 dcc_pemsg(EX_NOHOST, emsg, | |
528 "invalid CIDR block length \"%.*s\"%s", | |
529 str_len, str, fnm_lno(&fnm_buf, fnm, lno)); | |
530 return -1; | |
531 } | |
532 } | |
533 | |
534 if (su.sa.sa_family == AF_INET6) { | |
535 *addr6 = su.ipv6.sin6_addr; | |
536 if (is_ipv6) | |
537 *is_ipv6 = 1; | |
538 } else { | |
539 dcc_ipv4toipv6(addr6, su.ipv4.sin_addr); | |
540 if (is_ipv6) | |
541 *is_ipv6 = 0; | |
542 } | |
543 | |
544 if (!mask6) | |
545 mask6 = &mask6_loc; | |
546 dcc_bits2mask(mask6, bits); | |
547 for (wordno = 0; wordno < 4; ++wordno) { | |
548 if ((addr6->s6_addr32[wordno] | |
549 & ~mask6->s6_addr32[wordno]) != 0) { | |
550 dcc_pemsg(EX_NOHOST, emsg, | |
551 "%s does not start on a" | |
552 " %.*s-bit CIDR boundary%s", | |
553 addrstr, bits_len, bitsp, | |
554 fnm_lno(&fnm_buf, fnm, lno)); | |
555 return -1; | |
556 } | |
557 } | |
558 | |
559 return bits; | |
560 } | |
561 | |
562 | |
563 | |
564 /* Create and bind a UDP socket */ | |
565 int /* -1=possible IPv6 problem, 0=failed */ | |
566 dcc_udp_bind(DCC_EMSG emsg, | |
567 SOCKET *socp, /* INVALID_SOCKET or existing socket */ | |
568 const DCC_SOCKU *sup, | |
569 int *retry_secsp) /* -1=1 retry for anonymous port, | |
570 * 0=no retry, >0=seconds trying */ | |
571 { | |
572 DCC_SOCKU su; | |
573 #ifdef DCC_WIN32 | |
574 u_long on; | |
575 #endif | |
576 int tenths, result; | |
577 | |
578 if (*socp == INVALID_SOCKET) { | |
579 #ifdef NO_IPV6 | |
580 if (sup->sa.sa_family == AF_INET6) { | |
581 dcc_pemsg(EX_OSERR, emsg, | |
582 "attempt to create IPv6 UDP socket"); | |
583 return -1; | |
584 } | |
585 #endif | |
586 *socp = socket(sup->sa.sa_family, SOCK_DGRAM, 0); | |
587 if (*socp == INVALID_SOCKET) { | |
588 /* Let the caller try again if this system does not do | |
589 * IPv6 but IPv6 support has not been compiled out. | |
590 * It would be reasonable to use | |
591 * (errno == EPFNOSUPPORT || errno == EPROTONOSUPPORT) | |
592 * but some systems including Linux are unreasonable. */ | |
593 if (sup->sa.sa_family == AF_INET6) { | |
594 dcc_pemsg(EX_OSERR, emsg, | |
595 "socket(UDP IPv6): %s", ERROR_STR()); | |
596 return -1; | |
597 } else { | |
598 dcc_pemsg(EX_OSERR, emsg, | |
599 "socket(UDP IPv4): %s", ERROR_STR()); | |
600 return 0; | |
601 } | |
602 } | |
603 } | |
604 | |
605 #ifdef DCC_WIN32 | |
606 on = 1; | |
607 if (SOCKET_ERROR == ioctlsocket(*socp, FIONBIO, &on)) { | |
608 dcc_pemsg(EX_OSERR, emsg, "ioctlsocket(UDP, FIONBIO): %s", | |
609 ERROR_STR()); | |
610 closesocket(*socp); | |
611 *socp = INVALID_SOCKET; | |
612 return 0; | |
613 } | |
614 #else | |
615 if (0 > fcntl(*socp, F_SETFD, FD_CLOEXEC)) { | |
616 dcc_pemsg(EX_OSERR, emsg, "fcntl(UDP, FD_CLOEXEC): %s", | |
617 ERROR_STR()); | |
618 closesocket(*socp); | |
619 *socp = INVALID_SOCKET; | |
620 return 0; | |
621 } | |
622 if (-1 == fcntl(*socp, F_SETFL, | |
623 fcntl(*socp, F_GETFL, 0) | O_NONBLOCK)) { | |
624 dcc_pemsg(EX_OSERR, emsg, "fcntl(UDP O_NONBLOCK): %s", | |
625 ERROR_STR()); | |
626 closesocket(*socp); | |
627 *socp = INVALID_SOCKET; | |
628 return 0; | |
629 } | |
630 #endif | |
631 | |
632 tenths = 10; | |
633 for (;;) { | |
634 if (SOCKET_ERROR != bind(*socp, &sup->sa, DCC_SU_LEN(sup))) | |
635 return 1; | |
636 | |
637 if (errno == EADDRINUSE | |
638 && retry_secsp && *retry_secsp < 0 | |
639 && sup != &su | |
640 && *DCC_SU_PORTP(sup) != 0) { | |
641 /* If the initial number of seconds to retry was <0, | |
642 * then make one last try for a new ephemeral port */ | |
643 su = *sup; | |
644 *DCC_SU_PORTP(&su) = 0; | |
645 sup = &su; | |
646 continue; | |
647 } | |
648 | |
649 if (errno != EADDRINUSE | |
650 || !retry_secsp || *retry_secsp <= 0) { | |
651 /* no retries were allowed or we have exhasuted them */ | |
652 #ifndef NO_IPV6 | |
653 if (sup->sa.sa_family == AF_INET6 | |
654 && (errno == EPFNOSUPPORT | |
655 || errno == EPROTONOSUPPORT)) | |
656 result = -1; | |
657 else | |
658 #endif | |
659 result = 0; | |
660 dcc_pemsg(EX_OSERR, emsg, "bind(UDP %s): %s", | |
661 dcc_su2str_err(sup), ERROR_STR()); | |
662 closesocket(*socp); | |
663 *socp = INVALID_SOCKET; | |
664 return result; | |
665 } | |
666 | |
667 usleep(100*1000); | |
668 if (!--tenths) { | |
669 --*retry_secsp; | |
670 tenths = 10; | |
671 } | |
672 } | |
673 } |