]> sjero.net Git - wget/blob - src/connect.c
[svn] Split passive host lookups to a separate function.
[wget] / src / connect.c
1 /* Establishing and handling network connections.
2    Copyright (C) 1995, 1996, 1997, 2001, 2002 Free Software Foundation, Inc.
3
4 This file is part of GNU Wget.
5
6 GNU Wget is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10
11 GNU Wget is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Wget; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20 In addition, as a special exception, the Free Software Foundation
21 gives permission to link the code of its release of Wget with the
22 OpenSSL project's "OpenSSL" library (or with modified versions of it
23 that use the same license as the "OpenSSL" library), and distribute
24 the linked executables.  You must obey the GNU General Public License
25 in all respects for all of the code used other than "OpenSSL".  If you
26 modify this file, you may extend this exception to your version of the
27 file, but you are not obligated to do so.  If you do not wish to do
28 so, delete this exception statement from your version.  */
29
30 #include <config.h>
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <sys/types.h>
35 #ifdef HAVE_UNISTD_H
36 # include <unistd.h>
37 #endif
38 #include <assert.h>
39
40 #ifndef WINDOWS
41 # include <sys/socket.h>
42 # include <netdb.h>
43 # include <netinet/in.h>
44 # ifndef __BEOS__
45 #  include <arpa/inet.h>
46 # endif
47 #endif /* not WINDOWS */
48
49 #include <errno.h>
50 #ifdef HAVE_STRING_H
51 # include <string.h>
52 #else
53 # include <strings.h>
54 #endif /* HAVE_STRING_H */
55 #ifdef HAVE_SYS_SELECT_H
56 # include <sys/select.h>
57 #endif /* HAVE_SYS_SELECT_H */
58
59 #include "wget.h"
60 #include "utils.h"
61 #include "host.h"
62 #include "connect.h"
63 #include "hash.h"
64
65 #ifndef errno
66 extern int errno;
67 #endif
68
69 \f
70 /* Fill SA as per the data in IP and PORT.  SA shoult point to struct
71    sockaddr_storage if ENABLE_IPV6 is defined, to struct sockaddr_in
72    otherwise.  */
73
74 static void
75 sockaddr_set_data (struct sockaddr *sa, const ip_address *ip, int port)
76 {
77   switch (ip->type)
78     {
79     case IPV4_ADDRESS:
80       {
81         struct sockaddr_in *sin = (struct sockaddr_in *)sa;
82         sin->sin_family = AF_INET;
83         sin->sin_port = htons (port);
84         sin->sin_addr = ADDRESS_IPV4_IN_ADDR (ip);
85         break;
86       }
87 #ifdef ENABLE_IPV6
88     case IPV6_ADDRESS:
89       {
90         struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
91         sin6->sin6_family = AF_INET6;
92         sin6->sin6_port = htons (port);
93         sin6->sin6_addr = ADDRESS_IPV6_IN6_ADDR (ip);
94 #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
95         sin6->sin6_scope_id = ADDRESS_IPV6_SCOPE (ip);
96 #endif
97         break;
98       }
99 #endif /* ENABLE_IPV6 */
100     default:
101       abort ();
102     }
103 }
104
105 /* Get the data of SA, specifically the IP address and the port.  If
106    you're not interested in one or the other information, pass NULL as
107    the pointer.  */
108
109 static void
110 sockaddr_get_data (const struct sockaddr *sa, ip_address *ip, int *port)
111 {
112   switch (sa->sa_family)
113     {
114     case AF_INET:
115       {
116         struct sockaddr_in *sin = (struct sockaddr_in *)sa;
117         if (ip)
118           {
119             ip->type = IPV4_ADDRESS;
120             ADDRESS_IPV4_IN_ADDR (ip) = sin->sin_addr;
121           }
122         if (port)
123           *port = ntohs (sin->sin_port);
124         break;
125       }
126 #ifdef ENABLE_IPV6
127     case AF_INET6:
128       {
129         struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
130         if (ip)
131           {
132             ip->type = IPV6_ADDRESS;
133             ADDRESS_IPV6_IN6_ADDR (ip) = sin6->sin6_addr;
134 #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
135             ADDRESS_IPV6_SCOPE (ip) = sin6->sin6_scope_id;
136 #endif
137           }
138         if (port)
139           *port = ntohs (sin6->sin6_port);
140         break;
141       }
142 #endif
143     default:
144       abort ();
145     }
146 }
147
148 /* Return the size of the sockaddr structure depending on its
149    family.  */
150
151 static socklen_t
152 sockaddr_size (const struct sockaddr *sa)
153 {
154   switch (sa->sa_family)
155     {
156     case AF_INET:
157       return sizeof (struct sockaddr_in);
158 #ifdef ENABLE_IPV6
159     case AF_INET6:
160       return sizeof (struct sockaddr_in6);
161 #endif
162     default:
163       abort ();
164       return 0;                 /* so the compiler shuts up. */
165     }
166 }
167 \f
168 static int
169 resolve_bind_address (struct sockaddr *sa)
170 {
171   struct address_list *al;
172
173   /* Make sure this is called only once.  opt.bind_address doesn't
174      change during a Wget run.  */
175   static int called, should_bind;
176   static ip_address ip;
177   if (called)
178     {
179       if (should_bind)
180         sockaddr_set_data (sa, &ip, 0);
181       return should_bind;
182     }
183   called = 1;
184
185   al = lookup_host_passive (opt.bind_address);
186   if (!al)
187     {
188       /* #### We should be able to print the error message here. */
189       logprintf (LOG_NOTQUIET,
190                  _("%s: unable to resolve bind address `%s'; disabling bind.\n"),
191                  exec_name, opt.bind_address);
192       should_bind = 0;
193       return 0;
194     }
195
196   /* Pick the first address in the list and use it as bind address.
197      Perhaps we should try multiple addresses in succession, but I
198      don't think that's necessary in practice.  */
199   ip = *address_list_address_at (al, 0);
200   address_list_release (al);
201
202   sockaddr_set_data (sa, &ip, 0);
203   should_bind = 1;
204   return 1;
205 }
206 \f
207 struct cwt_context {
208   int fd;
209   const struct sockaddr *addr;
210   socklen_t addrlen;
211   int result;
212 };
213
214 static void
215 connect_with_timeout_callback (void *arg)
216 {
217   struct cwt_context *ctx = (struct cwt_context *)arg;
218   ctx->result = connect (ctx->fd, ctx->addr, ctx->addrlen);
219 }
220
221 /* Like connect, but specifies a timeout.  If connecting takes longer
222    than TIMEOUT seconds, -1 is returned and errno is set to
223    ETIMEDOUT.  */
224
225 static int
226 connect_with_timeout (int fd, const struct sockaddr *addr, socklen_t addrlen,
227                       double timeout)
228 {
229   struct cwt_context ctx;
230   ctx.fd = fd;
231   ctx.addr = addr;
232   ctx.addrlen = addrlen;
233
234   if (run_with_timeout (timeout, connect_with_timeout_callback, &ctx))
235     {
236       errno = ETIMEDOUT;
237       return -1;
238     }
239   if (ctx.result == -1 && errno == EINTR)
240     errno = ETIMEDOUT;
241   return ctx.result;
242 }
243 \f
244 /* Connect to a remote endpoint whose IP address is known.  */
245
246 int
247 connect_to_ip (const ip_address *ip, int port, const char *print)
248 {
249   struct sockaddr_storage ss;
250   struct sockaddr *sa = (struct sockaddr *)&ss;
251   int sock = -1;
252
253   /* If PRINT is non-NULL, print the "Connecting to..." line, with
254      PRINT being the host name we're connecting to.  */
255   if (print)
256     {
257       const char *txt_addr = pretty_print_address (ip);
258       if (print && 0 != strcmp (print, txt_addr))
259         logprintf (LOG_VERBOSE,
260                    _("Connecting to %s|%s|:%d... "), print, txt_addr, port);
261       else
262         logprintf (LOG_VERBOSE, _("Connecting to %s:%d... "), txt_addr, port);
263     }
264
265   /* Store the sockaddr info to SA.  */
266   sockaddr_set_data (sa, ip, port);
267
268   /* Create the socket of the family appropriate for the address.  */
269   sock = socket (sa->sa_family, SOCK_STREAM, 0);
270   if (sock < 0)
271     goto err;
272
273   /* For very small rate limits, set the buffer size (and hence,
274      hopefully, the kernel's TCP window size) to the per-second limit.
275      That way we should never have to sleep for more than 1s between
276      network reads.  */
277   if (opt.limit_rate && opt.limit_rate < 8192)
278     {
279       int bufsize = opt.limit_rate;
280       if (bufsize < 512)
281         bufsize = 512;          /* avoid pathologically small values */
282 #ifdef SO_RCVBUF
283       setsockopt (sock, SOL_SOCKET, SO_RCVBUF,
284                   (void *)&bufsize, (socklen_t)sizeof (bufsize));
285 #endif
286       /* When we add limit_rate support for writing, which is useful
287          for POST, we should also set SO_SNDBUF here.  */
288     }
289
290   if (opt.bind_address)
291     {
292       /* Bind the client side of the socket to the requested
293          address.  */
294       struct sockaddr_storage bind_ss;
295       struct sockaddr *bind_sa = (struct sockaddr *)&bind_ss;
296       if (resolve_bind_address (bind_sa))
297         {
298           if (bind (sock, bind_sa, sockaddr_size (bind_sa)) < 0)
299             goto err;
300         }
301     }
302
303   /* Connect the socket to the remote endpoint.  */
304   if (connect_with_timeout (sock, sa, sockaddr_size (sa),
305                             opt.connect_timeout) < 0)
306     goto err;
307
308   /* Success. */
309   assert (sock >= 0);
310   if (print)
311     logprintf (LOG_VERBOSE, _("connected.\n"));
312   DEBUGP (("Created socket %d.\n", sock));
313   return sock;
314
315  err:
316   {
317     /* Protect errno from possible modifications by close and
318        logprintf.  */
319     int save_errno = errno;
320     if (sock >= 0)
321       xclose (sock);
322     if (print)
323       logprintf (LOG_VERBOSE, "failed: %s.\n", strerror (errno));
324     errno = save_errno;
325     return -1;
326   }
327 }
328
329 /* Connect to a remote endpoint specified by host name.  */
330
331 int
332 connect_to_host (const char *host, int port)
333 {
334   int i, start, end;
335   struct address_list *al;
336   int sock = -1;
337
338  again:
339   al = lookup_host (host, 0);
340   if (!al)
341     return E_HOST;
342
343   address_list_get_bounds (al, &start, &end);
344   for (i = start; i < end; i++)
345     {
346       const ip_address *ip = address_list_address_at (al, i);
347       sock = connect_to_ip (ip, port, host);
348       if (sock >= 0)
349         /* Success. */
350         break;
351
352       address_list_set_faulty (al, i);
353
354       /* The attempt to connect has failed.  Continue with the loop
355          and try next address. */
356     }
357   address_list_release (al);
358
359   if (sock < 0 && address_list_cached_p (al))
360     {
361       /* We were unable to connect to any address in a list we've
362          obtained from cache.  There is a possibility that the host is
363          under dynamic DNS and has changed its address.  Resolve it
364          again.  */
365       forget_host_lookup (host);
366       goto again;
367     }
368
369   return sock;
370 }
371
372 int
373 test_socket_open (int sock)
374 {
375 #ifdef HAVE_SELECT
376   fd_set check_set;
377   struct timeval to;
378
379   /* Check if we still have a valid (non-EOF) connection.  From Andrew
380    * Maholski's code in the Unix Socket FAQ.  */
381
382   FD_ZERO (&check_set);
383   FD_SET (sock, &check_set);
384
385   /* Wait one microsecond */
386   to.tv_sec = 0;
387   to.tv_usec = 1;
388
389   /* If we get a timeout, then that means still connected */
390   if (select (sock + 1, &check_set, NULL, NULL, &to) == 0)
391     {
392       /* Connection is valid (not EOF), so continue */
393       return 1;
394     }
395   else
396     return 0;
397 #else
398   /* Without select, it's hard to know for sure. */
399   return 1;
400 #endif
401 }
402
403 /* Create a socket and bind it to PORT locally.  Calling accept() on
404    such a socket waits for and accepts incoming TCP connections.  The
405    resulting socket is stored to LOCAL_SOCK.  */
406
407 uerr_t
408 bindport (const ip_address *bind_address, int *port, int *local_sock)
409 {
410   int sock;
411   int family = AF_INET;
412   struct sockaddr_storage ss;
413   struct sockaddr *sa = (struct sockaddr *)&ss;
414
415   /* For setting options with setsockopt. */
416   int setopt_val = 1;
417   void *setopt_ptr = (void *)&setopt_val;
418   socklen_t setopt_size = sizeof (setopt_val);
419
420 #ifdef ENABLE_IPV6
421   if (bind_address->type == IPV6_ADDRESS) 
422     family = AF_INET6;
423 #endif
424
425   if ((sock = socket (family, SOCK_STREAM, 0)) < 0)
426     return CONSOCKERR;
427
428 #ifdef SO_REUSEADDR
429   setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, setopt_ptr, setopt_size);
430 #endif
431
432 #ifdef ENABLE_IPV6
433 # ifdef HAVE_IPV6_V6ONLY
434   if (family == AF_INET6)
435     setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, setopt_ptr, setopt_size);
436 # endif
437 #endif
438
439   xzero (ss);
440   sockaddr_set_data (sa, bind_address, *port);
441   if (bind (sock, sa, sockaddr_size (sa)) < 0)
442     {
443       xclose (sock);
444       return BINDERR;
445     }
446   DEBUGP (("Local socket fd %d bound.\n", sock));
447   if (!*port)
448     {
449       socklen_t sa_len = sockaddr_size (sa);
450       if (getsockname (sock, sa, &sa_len) < 0)
451         {
452           xclose (sock);
453           return CONPORTERR;
454         }
455       sockaddr_get_data (sa, NULL, port);
456       DEBUGP (("binding to address %s using port %i.\n", 
457                pretty_print_address (bind_address), *port));
458     }
459   if (listen (sock, 1) < 0)
460     {
461       xclose (sock);
462       return LISTENERR;
463     }
464   *local_sock = sock;
465   return BINDOK;
466 }
467
468 #ifdef HAVE_SELECT
469 /* Wait for file descriptor FD to be readable or writable or both,
470    timing out after MAXTIME seconds.  Returns 1 if FD is available, 0
471    for timeout and -1 for error.  The argument WAIT_FOR can be a
472    combination of WAIT_READ and WAIT_WRITE.
473
474    This is a mere convenience wrapper around the select call, and
475    should be taken as such.  */
476
477 int
478 select_fd (int fd, double maxtime, int wait_for)
479 {
480   fd_set fdset;
481   fd_set *rd = NULL, *wr = NULL;
482   struct timeval tmout;
483   int result;
484
485   FD_ZERO (&fdset);
486   FD_SET (fd, &fdset);
487   if (wait_for & WAIT_FOR_READ)
488     rd = &fdset;
489   if (wait_for & WAIT_FOR_WRITE)
490     wr = &fdset;
491
492   tmout.tv_sec = (long) maxtime;
493   tmout.tv_usec = 1000000L * (maxtime - (long) maxtime);
494
495   do
496     result = select (fd + 1, rd, wr, NULL, &tmout);
497   while (result < 0 && errno == EINTR);
498
499   return result;
500 }
501 #endif /* HAVE_SELECT */
502
503 /* Accept a connection on LOCAL_SOCK, and store the new socket to
504    *SOCK.  It blocks the caller until a connection is established.  If
505    no connection is established for opt.connect_timeout seconds, the
506    function exits with an error status.  */
507
508 uerr_t
509 acceptport (int local_sock, int *sock)
510 {
511   struct sockaddr_storage ss;
512   struct sockaddr *sa = (struct sockaddr *)&ss;
513   socklen_t addrlen = sizeof (ss);
514
515 #ifdef HAVE_SELECT
516   if (opt.connect_timeout)
517     if (select_fd (local_sock, opt.connect_timeout, WAIT_FOR_READ) <= 0)
518       return ACCEPTERR;
519 #endif
520   if ((*sock = accept (local_sock, sa, &addrlen)) < 0)
521     return ACCEPTERR;
522   DEBUGP (("Created socket fd %d.\n", *sock));
523   return ACCEPTOK;
524 }
525
526 /* Get the IP address associated with the connection on FD and store
527    it to IP.  Return 1 on success, 0 otherwise.
528
529    If ENDPOINT is ENDPOINT_LOCAL, it returns the address of the local
530    (client) side of the socket.  Else if ENDPOINT is ENDPOINT_PEER, it
531    returns the address of the remote (peer's) side of the socket.  */
532
533 int
534 socket_ip_address (int sock, ip_address *ip, int endpoint)
535 {
536   struct sockaddr_storage storage;
537   struct sockaddr *sockaddr = (struct sockaddr *)&storage;
538   socklen_t addrlen = sizeof (storage);
539   int ret;
540
541   if (endpoint == ENDPOINT_LOCAL)
542     ret = getsockname (sock, sockaddr, &addrlen);
543   else if (endpoint == ENDPOINT_PEER)
544     ret = getpeername (sock, sockaddr, &addrlen);
545   else
546     abort ();
547   if (ret < 0)
548     return 0;
549
550   switch (sockaddr->sa_family)
551     {
552 #ifdef ENABLE_IPV6
553     case AF_INET6:
554       {
555         struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&storage;
556         ip->type = IPV6_ADDRESS;
557         ADDRESS_IPV6_IN6_ADDR (ip) = sa6->sin6_addr;
558 #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
559         ADDRESS_IPV6_SCOPE (ip) = sa6->sin6_scope_id;
560 #endif
561         DEBUGP (("conaddr is: %s\n", pretty_print_address (ip)));
562         return 1;
563       }
564 #endif
565     case AF_INET:
566       {
567         struct sockaddr_in *sa = (struct sockaddr_in *)&storage;
568         ip->type = IPV4_ADDRESS;
569         ADDRESS_IPV4_IN_ADDR (ip) = sa->sin_addr;
570         DEBUGP (("conaddr is: %s\n", pretty_print_address (ip)));
571         return 1;
572       }
573     default:
574       abort ();
575     }
576
577   return 0;
578 }
579 \f
580 /* Basic socket operations, mostly EINTR wrappers.  */
581
582 #ifdef WINDOWS
583 # define read(fd, buf, cnt) recv (fd, buf, cnt, 0)
584 # define write(fd, buf, cnt) send (fd, buf, cnt, 0)
585 # define close(fd) closesocket (fd)
586 #endif
587
588 #ifdef __BEOS__
589 # define read(fd, buf, cnt) recv (fd, buf, cnt, 0)
590 # define write(fd, buf, cnt) send (fd, buf, cnt, 0)
591 #endif
592
593 static int
594 sock_read (int fd, char *buf, int bufsize)
595 {
596   int res;
597   do
598     res = read (fd, buf, bufsize);
599   while (res == -1 && errno == EINTR);
600   return res;
601 }
602
603 static int
604 sock_write (int fd, char *buf, int bufsize)
605 {
606   int res = 0;
607   do
608     res = write (fd, buf, bufsize);
609   while (res == -1 && errno == EINTR);
610   return res;
611 }
612
613 static int
614 sock_poll (int fd, double timeout, int wait_for)
615 {
616 #ifdef HAVE_SELECT
617   return select_fd (fd, timeout, wait_for);
618 #else
619   return 1;
620 #endif
621 }
622
623 static void
624 sock_close (int fd)
625 {
626   close (fd);
627   DEBUGP (("Closed fd %d\n", fd));
628 }
629 #undef read
630 #undef write
631 #undef close
632 \f
633 /* Reading and writing from the network.  We build around the socket
634    (file descriptor) API, but support "extended" operations for things
635    that are not mere file descriptors under the hood, such as SSL
636    sockets.
637
638    That way the user code can call xread(fd, ...) and we'll run read
639    or SSL_read or whatever is necessary.  */
640
641 static struct hash_table *extended_map;
642 static int extended_map_modified_tick;
643
644 struct extended_info {
645   xreader_t reader;
646   xwriter_t writer;
647   xpoller_t poller;
648   xcloser_t closer;
649   void *ctx;
650 };
651
652 void
653 register_extended (int fd, xreader_t reader, xwriter_t writer,
654                    xpoller_t poller, xcloser_t closer, void *ctx)
655 {
656   struct extended_info *info;
657
658   /* The file descriptor must be non-negative to be registered.
659      Negative values are ignored by xclose(), and -1 cannot be used as
660      hash key.  */
661   assert (fd >= 0);
662
663   info = xnew (struct extended_info);
664   info->reader = reader;
665   info->writer = writer;
666   info->poller = poller;
667   info->closer = closer;
668   info->ctx = ctx;
669   if (!extended_map)
670     extended_map = hash_table_new (0, NULL, NULL);
671   hash_table_put (extended_map, (void *) fd, info);
672   ++extended_map_modified_tick;
673 }
674
675 /* When xread/xwrite are called multiple times in a loop, they should
676    remember the INFO pointer instead of fetching it every time.  It is
677    not enough to compare FD to LAST_FD because FD might have been
678    closed and reopened.  modified_tick ensures that changes to
679    extended_map will not be unnoticed.
680
681    This is a macro because we want the static storage variables to be
682    per-function.  */
683
684 #define LAZY_RETRIEVE_INFO(info) do {                                   \
685   static struct extended_info *last_info;                               \
686   static int last_fd = -1, last_tick;                                   \
687   if (!extended_map)                                                    \
688     info = NULL;                                                        \
689   else if (last_fd == fd && last_tick == extended_map_modified_tick)    \
690     info = last_info;                                                   \
691   else                                                                  \
692     {                                                                   \
693       info = hash_table_get (extended_map, (void *) fd);                \
694       last_fd = fd;                                                     \
695       last_info = info;                                                 \
696       last_tick = extended_map_modified_tick;                           \
697     }                                                                   \
698 } while (0)
699
700 /* Read no more than BUFSIZE bytes of data from FD, storing them to
701    BUF.  If TIMEOUT is non-zero, the operation aborts if no data is
702    received after that many seconds.  If TIMEOUT is -1, the value of
703    opt.timeout is used for TIMEOUT.  */
704
705 int
706 xread (int fd, char *buf, int bufsize, double timeout)
707 {
708   struct extended_info *info;
709   LAZY_RETRIEVE_INFO (info);
710   if (timeout == -1)
711     timeout = opt.read_timeout;
712   if (timeout)
713     {
714       int test;
715       if (info && info->poller)
716         test = info->poller (fd, timeout, WAIT_FOR_READ, info->ctx);
717       else
718         test = sock_poll (fd, timeout, WAIT_FOR_READ);
719       if (test == 0)
720         errno = ETIMEDOUT;
721       if (test <= 0)
722         return -1;
723     }
724   if (info && info->reader)
725     return info->reader (fd, buf, bufsize, info->ctx);
726   else
727     return sock_read (fd, buf, bufsize);
728 }
729
730 /* Write the entire contents of BUF to FD.  If TIMEOUT is non-zero,
731    the operation aborts if no data is received after that many
732    seconds.  If TIMEOUT is -1, the value of opt.timeout is used for
733    TIMEOUT.  */
734
735 int
736 xwrite (int fd, char *buf, int bufsize, double timeout)
737 {
738   int res;
739   struct extended_info *info;
740   LAZY_RETRIEVE_INFO (info);
741   if (timeout == -1)
742     timeout = opt.read_timeout;
743
744   /* `write' may write less than LEN bytes, thus the loop keeps trying
745      it until all was written, or an error occurred.  */
746   res = 0;
747   while (bufsize > 0)
748     {
749       if (timeout)
750         {
751           int test;
752           if (info && info->poller)
753             test = info->poller (fd, timeout, WAIT_FOR_WRITE, info->ctx);
754           else
755             test = sock_poll (fd, timeout, WAIT_FOR_WRITE);
756           if (test == 0)
757             errno = ETIMEDOUT;
758           if (test <= 0)
759             return -1;
760         }
761       if (info && info->writer)
762         res = info->writer (fd, buf, bufsize, info->ctx);
763       else
764         res = sock_write (fd, buf, bufsize);
765       if (res <= 0)
766         break;
767       buf += res;
768       bufsize -= res;
769     }
770   return res;
771 }
772
773 /* Close the file descriptor FD.  */
774
775 void
776 xclose (int fd)
777 {
778   struct extended_info *info;
779   if (fd < 0)
780     return;
781
782   /* We don't need to be extra-fast here, so save some code by
783      avoiding LAZY_RETRIEVE_INFO. */
784   info = NULL;
785   if (extended_map)
786     info = hash_table_get (extended_map, (void *) fd);
787
788   if (info && info->closer)
789     info->closer (fd, info->ctx);
790   else
791     sock_close (fd);
792
793   if (info)
794     {
795       hash_table_remove (extended_map, (void *) fd);
796       xfree (info);
797       ++extended_map_modified_tick;
798     }
799 }