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