]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/http-addrlist.c
Migrate Windows conditional code to _WIN32 define.
[thirdparty/cups.git] / cups / http-addrlist.c
CommitLineData
ef416fc2 1/*
da003234 2 * HTTP address list routines for CUPS.
ef416fc2 3 *
3416fe95 4 * Copyright 2007-2018 by Apple Inc.
da003234 5 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
ef416fc2 6 *
3416fe95
MS
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
8 * information.
ef416fc2 9 */
10
11/*
12 * Include necessary headers...
13 */
14
71e16022 15#include "cups-private.h"
49d87452
MS
16#ifdef HAVE_RESOLV_H
17# include <resolv.h>
18#endif /* HAVE_RESOLV_H */
dcb445bc
MS
19#ifdef HAVE_POLL
20# include <poll.h>
21#endif /* HAVE_POLL */
19dc16f7 22#ifndef _WIN32
6961465f 23# include <fcntl.h>
19dc16f7 24#endif /* _WIN32 */
ef416fc2 25
26
27/*
28 * 'httpAddrConnect()' - Connect to any of the addresses in the list.
29 *
53af7f21 30 * @since CUPS 1.2/macOS 10.5@ @exclude all@
ef416fc2 31 */
32
33http_addrlist_t * /* O - Connected address or NULL on failure */
34httpAddrConnect(
35 http_addrlist_t *addrlist, /* I - List of potential addresses */
36 int *sock) /* O - Socket */
dcb445bc 37{
807315e6 38 DEBUG_printf(("httpAddrConnect(addrlist=%p, sock=%p)", (void *)addrlist, (void *)sock));
dcb445bc
MS
39
40 return (httpAddrConnect2(addrlist, sock, 30000, NULL));
41}
42
43
44/*
45 * 'httpAddrConnect2()' - Connect to any of the addresses in the list with a
46 * timeout and optional cancel.
47 *
8072030b 48 * @since CUPS 1.7/macOS 10.9@
dcb445bc
MS
49 */
50
51http_addrlist_t * /* O - Connected address or NULL on failure */
52httpAddrConnect2(
53 http_addrlist_t *addrlist, /* I - List of potential addresses */
54 int *sock, /* O - Socket */
55 int msec, /* I - Timeout in milliseconds */
56 int *cancel) /* I - Pointer to "cancel" variable */
ef416fc2 57{
10d09e33 58 int val; /* Socket option value */
19dc16f7 59#ifndef _WIN32
21aa3f37 60 int flags; /* Socket flags */
19dc16f7 61#endif /* !_WIN32 */
21aa3f37 62 int remaining; /* Remaining timeout */
7b0a28e3 63 int i, j, /* Looping vars */
d5cc05c9
MS
64 nfds, /* Number of file descriptors */
65 fds[100], /* Socket file descriptors */
66 result; /* Result from select() or poll() */
67 http_addrlist_t *addrs[100]; /* Addresses */
21aa3f37
MS
68#ifndef HAVE_POLL
69 int max_fd = -1; /* Highest file descriptor */
70#endif /* !HAVE_POLL */
71#ifdef O_NONBLOCK
12f89d24 72# ifdef HAVE_POLL
d5cc05c9 73 struct pollfd pfds[100]; /* Polled file descriptors */
12f89d24 74# else
dcb445bc 75 fd_set input_set, /* select() input set */
4c3f8a9b
MS
76 output_set, /* select() output set */
77 error_set; /* select() error set */
dcb445bc 78 struct timeval timeout; /* Timeout */
12f89d24 79# endif /* HAVE_POLL */
12f89d24 80#endif /* O_NONBLOCK */
1ff0402e 81#ifdef DEBUG
6fb588e0
MS
82 socklen_t len; /* Length of value */
83 http_addr_t peer; /* Peer address */
10d09e33 84 char temp[256]; /* Temporary address string */
1ff0402e 85#endif /* DEBUG */
ef416fc2 86
87
807315e6 88 DEBUG_printf(("httpAddrConnect2(addrlist=%p, sock=%p, msec=%d, cancel=%p)", (void *)addrlist, (void *)sock, msec, (void *)cancel));
1ff0402e
MS
89
90 if (!sock)
91 {
92 errno = EINVAL;
cb7f98ee 93 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
1ff0402e
MS
94 return (NULL);
95 }
96
dcb445bc
MS
97 if (cancel && *cancel)
98 return (NULL);
99
d5cc05c9 100 if (msec <= 0)
dcb445bc
MS
101 msec = INT_MAX;
102
ef416fc2 103 /*
104 * Loop through each address until we connect or run out of addresses...
105 */
106
6fb588e0
MS
107 nfds = 0;
108 remaining = msec;
109
110 while (remaining > 0)
ef416fc2 111 {
12f89d24 112 if (cancel && *cancel)
d5cc05c9
MS
113 {
114 while (nfds > 0)
115 {
116 nfds --;
117 httpAddrClose(NULL, fds[nfds]);
118 }
119
12f89d24 120 return (NULL);
d5cc05c9 121 }
12f89d24 122
6fb588e0 123 if (addrlist && nfds < (int)(sizeof(fds) / sizeof(fds[0])))
ef416fc2 124 {
125 /*
6fb588e0 126 * Create the socket...
ef416fc2 127 */
128
6fb588e0 129 DEBUG_printf(("2httpAddrConnect2: Trying %s:%d...", httpAddrString(&(addrlist->addr), temp, sizeof(temp)), httpAddrPort(&(addrlist->addr))));
ef416fc2 130
6fb588e0
MS
131 if ((fds[nfds] = (int)socket(httpAddrFamily(&(addrlist->addr)), SOCK_STREAM, 0)) < 0)
132 {
133 /*
134 * Don't abort yet, as this could just be an issue with the local
34016d2a
MS
135 * system not being configured with IPv4/IPv6/domain socket enabled.
136 *
137 * Just skip this address...
6fb588e0 138 */
ef416fc2 139
34016d2a 140 addrlist = addrlist->next;
6fb588e0
MS
141 continue;
142 }
143
144 /*
145 * Set options...
146 */
147
148 val = 1;
149 setsockopt(fds[nfds], SOL_SOCKET, SO_REUSEADDR, CUPS_SOCAST &val, sizeof(val));
ef416fc2 150
151#ifdef SO_REUSEPORT
6fb588e0
MS
152 val = 1;
153 setsockopt(fds[nfds], SOL_SOCKET, SO_REUSEPORT, CUPS_SOCAST &val, sizeof(val));
ef416fc2 154#endif /* SO_REUSEPORT */
155
fa73b229 156#ifdef SO_NOSIGPIPE
6fb588e0
MS
157 val = 1;
158 setsockopt(fds[nfds], SOL_SOCKET, SO_NOSIGPIPE, CUPS_SOCAST &val, sizeof(val));
fa73b229 159#endif /* SO_NOSIGPIPE */
160
6fb588e0
MS
161 /*
162 * Using TCP_NODELAY improves responsiveness, especially on systems
163 * with a slow loopback interface...
164 */
ef416fc2 165
6fb588e0
MS
166 val = 1;
167 setsockopt(fds[nfds], IPPROTO_TCP, TCP_NODELAY, CUPS_SOCAST &val, sizeof(val));
ef416fc2 168
169#ifdef FD_CLOEXEC
6fb588e0
MS
170 /*
171 * Close this socket when starting another process...
172 */
ef416fc2 173
6fb588e0 174 fcntl(fds[nfds], F_SETFD, FD_CLOEXEC);
ef416fc2 175#endif /* FD_CLOEXEC */
176
dcb445bc 177#ifdef O_NONBLOCK
6fb588e0
MS
178 /*
179 * Do an asynchronous connect by setting the socket non-blocking...
180 */
dcb445bc 181
6fb588e0 182 DEBUG_printf(("httpAddrConnect2: Setting non-blocking connect()"));
bb0d23b2 183
6fb588e0
MS
184 flags = fcntl(fds[nfds], F_GETFL, 0);
185 fcntl(fds[nfds], F_SETFL, flags | O_NONBLOCK);
dcb445bc
MS
186#endif /* O_NONBLOCK */
187
6fb588e0
MS
188 /*
189 * Then connect...
190 */
ef416fc2 191
6fb588e0
MS
192 if (!connect(fds[nfds], &(addrlist->addr.addr), (socklen_t)httpAddrLength(&(addrlist->addr))))
193 {
194 DEBUG_printf(("1httpAddrConnect2: Connected to %s:%d...", httpAddrString(&(addrlist->addr), temp, sizeof(temp)), httpAddrPort(&(addrlist->addr))));
dcb445bc
MS
195
196#ifdef O_NONBLOCK
6fb588e0 197 fcntl(fds[nfds], F_SETFL, flags);
dcb445bc
MS
198#endif /* O_NONBLOCK */
199
6fb588e0 200 *sock = fds[nfds];
d5cc05c9 201
6fb588e0
MS
202 while (nfds > 0)
203 {
204 nfds --;
205 httpAddrClose(NULL, fds[nfds]);
206 }
d5cc05c9 207
6fb588e0
MS
208 return (addrlist);
209 }
dcb445bc 210
19dc16f7 211#ifdef _WIN32
6fb588e0 212 if (WSAGetLastError() != WSAEINPROGRESS && WSAGetLastError() != WSAEWOULDBLOCK)
d5cc05c9 213#else
6fb588e0 214 if (errno != EINPROGRESS && errno != EWOULDBLOCK)
19dc16f7 215#endif /* _WIN32 */
6fb588e0
MS
216 {
217 DEBUG_printf(("1httpAddrConnect2: Unable to connect to %s:%d: %s", httpAddrString(&(addrlist->addr), temp, sizeof(temp)), httpAddrPort(&(addrlist->addr)), strerror(errno)));
218 httpAddrClose(NULL, fds[nfds]);
34016d2a 219 addrlist = addrlist->next;
6fb588e0
MS
220 continue;
221 }
dcb445bc 222
19dc16f7 223#ifndef _WIN32
6fb588e0 224 fcntl(fds[nfds], F_SETFL, flags);
19dc16f7 225#endif /* !_WIN32 */
dcb445bc 226
d5cc05c9 227#ifndef HAVE_POLL
6fb588e0
MS
228 if (fds[nfds] > max_fd)
229 max_fd = fds[nfds];
d5cc05c9
MS
230#endif /* !HAVE_POLL */
231
6fb588e0
MS
232 addrs[nfds] = addrlist;
233 nfds ++;
234 addrlist = addrlist->next;
235 }
236
e8916f41
MS
237 if (!addrlist && nfds == 0)
238 break;
239
6fb588e0
MS
240 /*
241 * See if we can connect to any of the addresses so far...
242 */
d5cc05c9
MS
243
244#ifdef O_NONBLOCK
6fb588e0 245 DEBUG_puts("1httpAddrConnect2: Finishing async connect()");
d5cc05c9 246
d5cc05c9
MS
247 do
248 {
249 if (cancel && *cancel)
12f89d24 250 {
d5cc05c9
MS
251 /*
252 * Close this socket and return...
253 */
dcb445bc 254
d5cc05c9 255 DEBUG_puts("1httpAddrConnect2: Canceled connect()");
12f89d24 256
d5cc05c9
MS
257 while (nfds > 0)
258 {
259 nfds --;
260 httpAddrClose(NULL, fds[nfds]);
261 }
dcb445bc 262
d5cc05c9 263 *sock = -1;
dcb445bc 264
d5cc05c9
MS
265 return (NULL);
266 }
12f89d24
MS
267
268# ifdef HAVE_POLL
d5cc05c9
MS
269 for (i = 0; i < nfds; i ++)
270 {
6fb588e0
MS
271 pfds[i].fd = fds[i];
272 pfds[i].events = POLLIN | POLLOUT;
d5cc05c9 273 }
12f89d24 274
6fb588e0 275 result = poll(pfds, (nfds_t)nfds, addrlist ? 100 : remaining > 250 ? 250 : remaining);
12f89d24 276
d5cc05c9 277 DEBUG_printf(("1httpAddrConnect2: poll() returned %d (%d)", result, errno));
dcb445bc 278
12f89d24 279# else
d5cc05c9
MS
280 FD_ZERO(&input_set);
281 for (i = 0; i < nfds; i ++)
6fb588e0 282 FD_SET(fds[i], &input_set);
d5cc05c9 283 output_set = input_set;
4c3f8a9b 284 error_set = input_set;
dcb445bc 285
d5cc05c9 286 timeout.tv_sec = 0;
6fb588e0 287 timeout.tv_usec = (addrlist ? 100 : remaining > 250 ? 250 : remaining) * 1000;
dcb445bc 288
4c3f8a9b 289 result = select(max_fd + 1, &input_set, &output_set, &error_set, &timeout);
12f89d24 290
d5cc05c9 291 DEBUG_printf(("1httpAddrConnect2: select() returned %d (%d)", result, errno));
12f89d24 292# endif /* HAVE_POLL */
d5cc05c9 293 }
19dc16f7 294# ifdef _WIN32
d5cc05c9 295 while (result < 0 && (WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEWOULDBLOCK));
12f89d24 296# else
d5cc05c9 297 while (result < 0 && (errno == EINTR || errno == EAGAIN));
19dc16f7 298# endif /* _WIN32 */
dcb445bc 299
d5cc05c9
MS
300 if (result > 0)
301 {
0ca77b3e
MS
302 http_addrlist_t *connaddr = NULL; /* Connected address, if any */
303
d5cc05c9
MS
304 for (i = 0; i < nfds; i ++)
305 {
306# ifdef HAVE_POLL
6fb588e0 307 DEBUG_printf(("pfds[%d].revents=%x\n", i, pfds[i].revents));
4c3f8a9b 308 if (pfds[i].revents && !(pfds[i].revents & (POLLERR | POLLHUP)))
d5cc05c9 309# else
c7505715 310 if (FD_ISSET(fds[i], &input_set) && !FD_ISSET(fds[i], &error_set))
d5cc05c9 311# endif /* HAVE_POLL */
6fb588e0
MS
312 {
313 *sock = fds[i];
0ca77b3e 314 connaddr = addrs[i];
6fb588e0
MS
315
316# ifdef DEBUG
d5cc05c9
MS
317 len = sizeof(peer);
318 if (!getpeername(fds[i], (struct sockaddr *)&peer, &len))
d5cc05c9 319 DEBUG_printf(("1httpAddrConnect2: Connected to %s:%d...", httpAddrString(&peer, temp, sizeof(temp)), httpAddrPort(&peer)));
6fb588e0 320# endif /* DEBUG */
7b0a28e3
MS
321
322 break;
d5cc05c9 323 }
0ca77b3e
MS
324# ifdef HAVE_POLL
325 else if (pfds[i].revents & (POLLERR | POLLHUP))
326# else
c7505715 327 else if (FD_ISSET(fds[i], &error_set))
0ca77b3e
MS
328# endif /* HAVE_POLL */
329 {
330 /*
331 * Error on socket, remove from the "pool"...
332 */
333
d5cc05c9 334 httpAddrClose(NULL, fds[i]);
0ca77b3e
MS
335 nfds --;
336 if (i < nfds)
337 {
338 memmove(fds + i, fds + i + 1, (size_t)(nfds - i) * (sizeof(fds[0])));
339 memmove(addrs + i, addrs + i + 1, (size_t)(nfds - i) * (sizeof(addrs[0])));
340 }
341 i --;
342 }
dcb445bc 343 }
d5cc05c9 344
0ca77b3e 345 if (connaddr)
7b0a28e3
MS
346 {
347 /*
348 * Connected on one address, close all of the other sockets we have so
349 * far and return...
350 */
351
352 for (j = 0; j < i; j ++)
353 httpAddrClose(NULL, fds[j]);
354
355 for (j ++; j < nfds; j ++)
356 httpAddrClose(NULL, fds[j]);
357
0ca77b3e 358 return (connaddr);
7b0a28e3 359 }
1ff0402e 360 }
dcb445bc 361#endif /* O_NONBLOCK */
1ff0402e 362
6fb588e0
MS
363 if (addrlist)
364 remaining -= 100;
365 else
366 remaining -= 250;
367 }
368
d5cc05c9
MS
369 while (nfds > 0)
370 {
371 nfds --;
372 httpAddrClose(NULL, fds[nfds]);
ef416fc2 373 }
374
19dc16f7 375#ifdef _WIN32
d5cc05c9 376 _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, "Connection failed", 0);
12f89d24 377#else
d5cc05c9 378 _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, strerror(errno), 0);
19dc16f7 379#endif /* _WIN32 */
7cf5915e 380
d5cc05c9 381 return (NULL);
ef416fc2 382}
383
384
a469f8a5
MS
385/*
386 * 'httpAddrCopyList()' - Copy an address list.
387 *
8072030b 388 * @since CUPS 1.7/macOS 10.9@
a469f8a5
MS
389 */
390
391http_addrlist_t * /* O - New address list or @code NULL@ on error */
392httpAddrCopyList(
393 http_addrlist_t *src) /* I - Source address list */
394{
395 http_addrlist_t *dst = NULL, /* First list entry */
396 *prev = NULL, /* Previous list entry */
397 *current = NULL;/* Current list entry */
398
399
400 while (src)
401 {
402 if ((current = malloc(sizeof(http_addrlist_t))) == NULL)
403 {
404 current = dst;
405
406 while (current)
407 {
408 prev = current;
409 current = current->next;
410
411 free(prev);
412 }
413
414 return (NULL);
415 }
416
417 memcpy(current, src, sizeof(http_addrlist_t));
418
419 current->next = NULL;
420
421 if (prev)
422 prev->next = current;
423 else
424 dst = current;
425
db8b865d
MS
426 prev = current;
427 src = src->next;
a469f8a5
MS
428 }
429
430 return (dst);
431}
432
433
ef416fc2 434/*
435 * 'httpAddrFreeList()' - Free an address list.
436 *
8072030b 437 * @since CUPS 1.2/macOS 10.5@
ef416fc2 438 */
439
440void
441httpAddrFreeList(
442 http_addrlist_t *addrlist) /* I - Address list to free */
443{
444 http_addrlist_t *next; /* Next address in list */
445
446
447 /*
448 * Free each address in the list...
449 */
450
451 while (addrlist)
452 {
453 next = addrlist->next;
454
455 free(addrlist);
456
457 addrlist = next;
458 }
459}
460
461
462/*
463 * 'httpAddrGetList()' - Get a list of addresses for a hostname.
464 *
8072030b 465 * @since CUPS 1.2/macOS 10.5@
ef416fc2 466 */
467
468http_addrlist_t * /* O - List of addresses or NULL */
469httpAddrGetList(const char *hostname, /* I - Hostname, IP address, or NULL for passive listen address */
470 int family, /* I - Address family or AF_UNSPEC */
471 const char *service) /* I - Service name or port number */
472{
473 http_addrlist_t *first, /* First address in list */
474 *addr, /* Current address in list */
475 *temp; /* New address */
49d87452
MS
476 _cups_globals_t *cg = _cupsGlobals();
477 /* Global data */
ef416fc2 478
479
480#ifdef DEBUG
ae71f5de
MS
481 _cups_debug_printf("httpAddrGetList(hostname=\"%s\", family=AF_%s, "
482 "service=\"%s\")\n",
483 hostname ? hostname : "(nil)",
484 family == AF_UNSPEC ? "UNSPEC" :
ef416fc2 485# ifdef AF_LOCAL
ae71f5de 486 family == AF_LOCAL ? "LOCAL" :
ef416fc2 487# endif /* AF_LOCAL */
488# ifdef AF_INET6
ae71f5de 489 family == AF_INET6 ? "INET6" :
ef416fc2 490# endif /* AF_INET6 */
ae71f5de 491 family == AF_INET ? "INET" : "???", service);
ef416fc2 492#endif /* DEBUG */
493
49d87452
MS
494#ifdef HAVE_RES_INIT
495 /*
496 * STR #2920: Initialize resolver after failure in cups-polld
497 *
498 * If the previous lookup failed, re-initialize the resolver to prevent
499 * temporary network errors from persisting. This *should* be handled by
500 * the resolver libraries, but apparently the glibc folks do not agree.
501 *
502 * We set a flag at the end of this function if we encounter an error that
503 * requires reinitialization of the resolver functions. We then call
504 * res_init() if the flag is set on the next call here or in httpAddrLookup().
505 */
506
507 if (cg->need_res_init)
508 {
509 res_init();
510
511 cg->need_res_init = 0;
512 }
513#endif /* HAVE_RES_INIT */
514
ef416fc2 515 /*
516 * Lookup the address the best way we can...
517 */
518
519 first = addr = NULL;
520
521#ifdef AF_LOCAL
522 if (hostname && hostname[0] == '/')
523 {
524 /*
525 * Domain socket address...
526 */
527
91c84a35
MS
528 if ((first = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t))) != NULL)
529 {
b1564bae 530 addr = first;
91c84a35
MS
531 first->addr.un.sun_family = AF_LOCAL;
532 strlcpy(first->addr.un.sun_path, hostname, sizeof(first->addr.un.sun_path));
533 }
ef416fc2 534 }
535 else
536#endif /* AF_LOCAL */
88f9aafc 537 if (!hostname || _cups_strcasecmp(hostname, "localhost"))
ef416fc2 538 {
539#ifdef HAVE_GETADDRINFO
540 struct addrinfo hints, /* Address lookup hints */
541 *results, /* Address lookup results */
542 *current; /* Current result */
82f97232 543 char ipv6[64], /* IPv6 address */
ef416fc2 544 *ipv6zone; /* Pointer to zone separator */
545 int ipv6len; /* Length of IPv6 address */
49d87452
MS
546 int error; /* getaddrinfo() error */
547
ef416fc2 548
549 /*
550 * Lookup the address as needed...
551 */
552
553 memset(&hints, 0, sizeof(hints));
554 hints.ai_family = family;
555 hints.ai_flags = hostname ? 0 : AI_PASSIVE;
556 hints.ai_socktype = SOCK_STREAM;
557
558 if (hostname && *hostname == '[')
559 {
560 /*
561 * Remove brackets from numeric IPv6 address...
562 */
563
564 if (!strncmp(hostname, "[v1.", 4))
565 {
566 /*
567 * Copy the newer address format which supports link-local addresses...
568 */
569
570 strlcpy(ipv6, hostname + 4, sizeof(ipv6));
b86bc4cf 571 if ((ipv6len = (int)strlen(ipv6) - 1) >= 0 && ipv6[ipv6len] == ']')
ef416fc2 572 {
573 ipv6[ipv6len] = '\0';
574 hostname = ipv6;
575
576 /*
577 * Convert "+zone" in address to "%zone"...
578 */
579
580 if ((ipv6zone = strrchr(ipv6, '+')) != NULL)
581 *ipv6zone = '%';
582 }
583 }
584 else
585 {
586 /*
587 * Copy the regular non-link-local IPv6 address...
588 */
589
590 strlcpy(ipv6, hostname + 1, sizeof(ipv6));
b86bc4cf 591 if ((ipv6len = (int)strlen(ipv6) - 1) >= 0 && ipv6[ipv6len] == ']')
ef416fc2 592 {
593 ipv6[ipv6len] = '\0';
594 hostname = ipv6;
595 }
596 }
597 }
598
49d87452 599 if ((error = getaddrinfo(hostname, service, &hints, &results)) == 0)
ef416fc2 600 {
601 /*
602 * Copy the results to our own address list structure...
603 */
604
605 for (current = results; current; current = current->ai_next)
606 if (current->ai_family == AF_INET || current->ai_family == AF_INET6)
607 {
608 /*
609 * Copy the address over...
610 */
611
612 temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
613 if (!temp)
614 {
615 httpAddrFreeList(first);
3416fe95 616 freeaddrinfo(results);
cb7f98ee 617 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
ef416fc2 618 return (NULL);
619 }
620
621 if (current->ai_family == AF_INET6)
622 memcpy(&(temp->addr.ipv6), current->ai_addr,
623 sizeof(temp->addr.ipv6));
624 else
625 memcpy(&(temp->addr.ipv4), current->ai_addr,
626 sizeof(temp->addr.ipv4));
627
628 /*
629 * Append the address to the list...
630 */
631
632 if (!first)
633 first = temp;
634
635 if (addr)
636 addr->next = temp;
637
638 addr = temp;
639 }
640
641 /*
642 * Free the results from getaddrinfo()...
643 */
644
645 freeaddrinfo(results);
646 }
dcb445bc
MS
647 else
648 {
649 if (error == EAI_FAIL)
650 cg->need_res_init = 1;
651
cb7f98ee 652 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, gai_strerror(error), 0);
dcb445bc 653 }
49d87452 654
ef416fc2 655#else
656 if (hostname)
657 {
658 int i; /* Looping vars */
659 unsigned ip[4]; /* IPv4 address components */
660 const char *ptr; /* Pointer into hostname */
661 struct hostent *host; /* Result of lookup */
662 struct servent *port; /* Port number for service */
663 int portnum; /* Port number */
664
665
666 /*
667 * Lookup the service...
668 */
669
670 if (!service)
671 portnum = 0;
672 else if (isdigit(*service & 255))
673 portnum = atoi(service);
674 else if ((port = getservbyname(service, NULL)) != NULL)
675 portnum = ntohs(port->s_port);
676 else if (!strcmp(service, "http"))
677 portnum = 80;
678 else if (!strcmp(service, "https"))
679 portnum = 443;
321d8d57 680 else if (!strcmp(service, "ipp") || !strcmp(service, "ipps"))
ef416fc2 681 portnum = 631;
682 else if (!strcmp(service, "lpd"))
683 portnum = 515;
684 else if (!strcmp(service, "socket"))
685 portnum = 9100;
686 else
687 return (NULL);
688
689 /*
690 * This code is needed because some operating systems have a
691 * buggy implementation of gethostbyname() that does not support
692 * IPv4 addresses. If the hostname string is an IPv4 address, then
693 * sscanf() is used to extract the IPv4 components. We then pack
694 * the components into an IPv4 address manually, since the
695 * inet_aton() function is deprecated. We use the htonl() macro
696 * to get the right byte order for the address.
697 */
698
699 for (ptr = hostname; isdigit(*ptr & 255) || *ptr == '.'; ptr ++);
700
701 if (!*ptr)
702 {
703 /*
704 * We have an IPv4 address; break it up and create an IPv4 address...
705 */
706
707 if (sscanf(hostname, "%u.%u.%u.%u", ip, ip + 1, ip + 2, ip + 3) == 4 &&
708 ip[0] <= 255 && ip[1] <= 255 && ip[2] <= 255 && ip[3] <= 255)
709 {
710 first = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
711 if (!first)
712 return (NULL);
713
714 first->addr.ipv4.sin_family = AF_INET;
da003234
MS
715 first->addr.ipv4.sin_addr.s_addr = htonl((((((((unsigned)ip[0] << 8) |
716 (unsigned)ip[1]) << 8) |
717 (unsigned)ip[2]) << 8) |
718 (unsigned)ip[3]));
ef416fc2 719 first->addr.ipv4.sin_port = htons(portnum);
720 }
721 }
722 else if ((host = gethostbyname(hostname)) != NULL &&
723# ifdef AF_INET6
724 (host->h_addrtype == AF_INET || host->h_addrtype == AF_INET6))
725# else
726 host->h_addrtype == AF_INET)
727# endif /* AF_INET6 */
728 {
729 for (i = 0; host->h_addr_list[i]; i ++)
730 {
731 /*
732 * Copy the address over...
733 */
734
735 temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
736 if (!temp)
737 {
738 httpAddrFreeList(first);
739 return (NULL);
740 }
741
742# ifdef AF_INET6
743 if (host->h_addrtype == AF_INET6)
744 {
d6ae789d 745 temp->addr.ipv6.sin6_family = AF_INET6;
ed486911 746 memcpy(&(temp->addr.ipv6.sin6_addr), host->h_addr_list[i],
ef416fc2 747 sizeof(temp->addr.ipv6));
748 temp->addr.ipv6.sin6_port = htons(portnum);
749 }
750 else
751# endif /* AF_INET6 */
752 {
d6ae789d 753 temp->addr.ipv4.sin_family = AF_INET;
ed486911 754 memcpy(&(temp->addr.ipv4.sin_addr), host->h_addr_list[i],
ef416fc2 755 sizeof(temp->addr.ipv4));
756 temp->addr.ipv4.sin_port = htons(portnum);
757 }
758
759 /*
760 * Append the address to the list...
761 */
762
763 if (!first)
764 first = temp;
765
766 if (addr)
767 addr->next = temp;
768
769 addr = temp;
770 }
771 }
dcb445bc
MS
772 else
773 {
774 if (h_errno == NO_RECOVERY)
775 cg->need_res_init = 1;
776
cb7f98ee 777 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, hstrerror(h_errno), 0);
dcb445bc 778 }
ef416fc2 779 }
780#endif /* HAVE_GETADDRINFO */
781 }
782
783 /*
784 * Detect some common errors and handle them sanely...
785 */
786
88f9aafc 787 if (!addr && (!hostname || !_cups_strcasecmp(hostname, "localhost")))
ef416fc2 788 {
789 struct servent *port; /* Port number for service */
790 int portnum; /* Port number */
791
792
793 /*
794 * Lookup the service...
795 */
796
797 if (!service)
798 portnum = 0;
799 else if (isdigit(*service & 255))
800 portnum = atoi(service);
801 else if ((port = getservbyname(service, NULL)) != NULL)
802 portnum = ntohs(port->s_port);
803 else if (!strcmp(service, "http"))
804 portnum = 80;
805 else if (!strcmp(service, "https"))
806 portnum = 443;
321d8d57 807 else if (!strcmp(service, "ipp") || !strcmp(service, "ipps"))
ef416fc2 808 portnum = 631;
809 else if (!strcmp(service, "lpd"))
810 portnum = 515;
811 else if (!strcmp(service, "socket"))
812 portnum = 9100;
813 else
321d8d57
MS
814 {
815 httpAddrFreeList(first);
dcb445bc 816
cb7f98ee 817 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unknown service name."), 1);
ef416fc2 818 return (NULL);
321d8d57 819 }
ef416fc2 820
88f9aafc 821 if (hostname && !_cups_strcasecmp(hostname, "localhost"))
ef416fc2 822 {
823 /*
824 * Unfortunately, some users ignore all of the warnings in the
825 * /etc/hosts file and delete "localhost" from it. If we get here
826 * then we were unable to resolve the name, so use the IPv6 and/or
827 * IPv4 loopback interface addresses...
828 */
829
830#ifdef AF_INET6
831 if (family != AF_INET)
832 {
833 /*
834 * Add [::1] to the address list...
835 */
836
837 temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
838 if (!temp)
839 {
cb7f98ee 840 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
ef416fc2 841 httpAddrFreeList(first);
842 return (NULL);
843 }
844
845 temp->addr.ipv6.sin6_family = AF_INET6;
846 temp->addr.ipv6.sin6_port = htons(portnum);
19dc16f7 847# ifdef _WIN32
ef416fc2 848 temp->addr.ipv6.sin6_addr.u.Byte[15] = 1;
849# else
850 temp->addr.ipv6.sin6_addr.s6_addr32[3] = htonl(1);
19dc16f7 851# endif /* _WIN32 */
ef416fc2 852
ed486911 853 if (!first)
854 first = temp;
855
ef416fc2 856 addr = temp;
857 }
858
859 if (family != AF_INET6)
860#endif /* AF_INET6 */
861 {
862 /*
863 * Add 127.0.0.1 to the address list...
864 */
865
866 temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
867 if (!temp)
868 {
cb7f98ee 869 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
ef416fc2 870 httpAddrFreeList(first);
871 return (NULL);
872 }
873
874 temp->addr.ipv4.sin_family = AF_INET;
875 temp->addr.ipv4.sin_port = htons(portnum);
876 temp->addr.ipv4.sin_addr.s_addr = htonl(0x7f000001);
877
ed486911 878 if (!first)
879 first = temp;
880
ef416fc2 881 if (addr)
882 addr->next = temp;
ef416fc2 883 }
884 }
885 else if (!hostname)
886 {
887 /*
888 * Provide one or more passive listening addresses...
889 */
890
891#ifdef AF_INET6
892 if (family != AF_INET)
893 {
894 /*
895 * Add [::] to the address list...
896 */
897
898 temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
899 if (!temp)
900 {
cb7f98ee 901 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
ef416fc2 902 httpAddrFreeList(first);
903 return (NULL);
904 }
905
906 temp->addr.ipv6.sin6_family = AF_INET6;
907 temp->addr.ipv6.sin6_port = htons(portnum);
908
ed486911 909 if (!first)
910 first = temp;
911
ef416fc2 912 addr = temp;
913 }
914
915 if (family != AF_INET6)
916#endif /* AF_INET6 */
917 {
918 /*
919 * Add 0.0.0.0 to the address list...
920 */
921
922 temp = (http_addrlist_t *)calloc(1, sizeof(http_addrlist_t));
923 if (!temp)
924 {
cb7f98ee 925 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
ef416fc2 926 httpAddrFreeList(first);
927 return (NULL);
928 }
929
930 temp->addr.ipv4.sin_family = AF_INET;
931 temp->addr.ipv4.sin_port = htons(portnum);
932
ed486911 933 if (!first)
934 first = temp;
935
ef416fc2 936 if (addr)
937 addr->next = temp;
ef416fc2 938 }
939 }
940 }
941
942 /*
943 * Return the address list...
944 */
945
946 return (first);
947}