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