]> sjero.net Git - wget/blob - src/connect.c
[svn] Replace conaddr with socket_ip_address.
[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 (const char *host, struct sockaddr *sa, int flags)
170 {
171   struct address_list *al;
172
173   /* #### Shouldn't we do this only once?  opt.bind_address won't
174      change during a Wget run!  */
175
176   al = lookup_host (host, flags | LH_SILENT | LH_PASSIVE);
177   if (al == NULL)
178     {
179       /* #### We should print the error message here. */
180       logprintf (LOG_NOTQUIET,
181                  _("%s: unable to resolve bind address `%s'; disabling bind.\n"),
182                  exec_name, opt.bind_address);
183       return 0;
184     }
185
186   /* Pick the first address in the list and use it as bind address.
187      Perhaps we should try multiple addresses, but I don't think
188      that's necessary in practice.  */
189   sockaddr_set_data (sa, address_list_address_at (al, 0), 0);
190   address_list_release (al);
191   return 1;
192 }
193 \f
194 struct cwt_context {
195   int fd;
196   const struct sockaddr *addr;
197   socklen_t addrlen;
198   int result;
199 };
200
201 static void
202 connect_with_timeout_callback (void *arg)
203 {
204   struct cwt_context *ctx = (struct cwt_context *)arg;
205   ctx->result = connect (ctx->fd, ctx->addr, ctx->addrlen);
206 }
207
208 /* Like connect, but specifies a timeout.  If connecting takes longer
209    than TIMEOUT seconds, -1 is returned and errno is set to
210    ETIMEDOUT.  */
211
212 static int
213 connect_with_timeout (int fd, const struct sockaddr *addr, socklen_t addrlen,
214                       double timeout)
215 {
216   struct cwt_context ctx;
217   ctx.fd = fd;
218   ctx.addr = addr;
219   ctx.addrlen = addrlen;
220
221   if (run_with_timeout (timeout, connect_with_timeout_callback, &ctx))
222     {
223       errno = ETIMEDOUT;
224       return -1;
225     }
226   if (ctx.result == -1 && errno == EINTR)
227     errno = ETIMEDOUT;
228   return ctx.result;
229 }
230 \f
231 /* Connect to a remote endpoint whose IP address is known.  */
232
233 int
234 connect_to_ip (const ip_address *ip, int port, const char *print)
235 {
236   struct sockaddr_storage ss;
237   struct sockaddr *sa = (struct sockaddr *)&ss;
238   int sock = -1;
239
240   /* If PRINT is non-NULL, print the "Connecting to..." line, with
241      PRINT being the host name we're connecting to.  */
242   if (print)
243     {
244       const char *txt_addr = pretty_print_address (ip);
245       if (print && 0 != strcmp (print, txt_addr))
246         logprintf (LOG_VERBOSE,
247                    _("Connecting to %s|%s|:%d... "), print, txt_addr, port);
248       else
249         logprintf (LOG_VERBOSE, _("Connecting to %s:%d... "), txt_addr, port);
250     }
251
252   /* Store the sockaddr info to SA.  */
253   sockaddr_set_data (sa, ip, port);
254
255   /* Create the socket of the family appropriate for the address.  */
256   sock = socket (sa->sa_family, SOCK_STREAM, 0);
257   if (sock < 0)
258     goto err;
259
260   /* For very small rate limits, set the buffer size (and hence,
261      hopefully, the kernel's TCP window size) to the per-second limit.
262      That way we should never have to sleep for more than 1s between
263      network reads.  */
264   if (opt.limit_rate && opt.limit_rate < 8192)
265     {
266       int bufsize = opt.limit_rate;
267       if (bufsize < 512)
268         bufsize = 512;          /* avoid pathologically small values */
269 #ifdef SO_RCVBUF
270       setsockopt (sock, SOL_SOCKET, SO_RCVBUF,
271                   (void *)&bufsize, (socklen_t)sizeof (bufsize));
272 #endif
273       /* When we add limit_rate support for writing, which is useful
274          for POST, we should also set SO_SNDBUF here.  */
275     }
276
277   if (opt.bind_address)
278     {
279       /* Bind the client side of the socket to the requested
280          address.  */
281       struct sockaddr_storage bind_ss;
282       struct sockaddr *bind_sa = (struct sockaddr *)&bind_ss;
283       if (resolve_bind_address (opt.bind_address, bind_sa, 0))
284         {
285           if (bind (sock, bind_sa, sockaddr_size (bind_sa)) < 0)
286             goto err;
287         }
288     }
289
290   /* Connect the socket to the remote endpoint.  */
291   if (connect_with_timeout (sock, sa, sockaddr_size (sa),
292                             opt.connect_timeout) < 0)
293     goto err;
294
295   /* Success. */
296   assert (sock >= 0);
297   if (print)
298     logprintf (LOG_VERBOSE, _("connected.\n"));
299   DEBUGP (("Created socket %d.\n", sock));
300   return sock;
301
302  err:
303   {
304     /* Protect errno from possible modifications by close and
305        logprintf.  */
306     int save_errno = errno;
307     if (sock >= 0)
308       xclose (sock);
309     if (print)
310       logprintf (LOG_VERBOSE, "failed: %s.\n", strerror (errno));
311     errno = save_errno;
312     return -1;
313   }
314 }
315
316 /* Connect to a remote endpoint specified by host name.  */
317
318 int
319 connect_to_host (const char *host, int port)
320 {
321   int i, start, end;
322   struct address_list *al;
323   int sock = -1;
324
325  again:
326   al = lookup_host (host, 0);
327   if (!al)
328     return E_HOST;
329
330   address_list_get_bounds (al, &start, &end);
331   for (i = start; i < end; i++)
332     {
333       const ip_address *ip = address_list_address_at (al, i);
334       sock = connect_to_ip (ip, port, host);
335       if (sock >= 0)
336         /* Success. */
337         break;
338
339       address_list_set_faulty (al, i);
340
341       /* The attempt to connect has failed.  Continue with the loop
342          and try next address. */
343     }
344   address_list_release (al);
345
346   if (sock < 0 && address_list_cached_p (al))
347     {
348       /* We were unable to connect to any address in a list we've
349          obtained from cache.  There is a possibility that the host is
350          under dynamic DNS and has changed its address.  Resolve it
351          again.  */
352       forget_host_lookup (host);
353       goto again;
354     }
355
356   return sock;
357 }
358
359 int
360 test_socket_open (int sock)
361 {
362 #ifdef HAVE_SELECT
363   fd_set check_set;
364   struct timeval to;
365
366   /* Check if we still have a valid (non-EOF) connection.  From Andrew
367    * Maholski's code in the Unix Socket FAQ.  */
368
369   FD_ZERO (&check_set);
370   FD_SET (sock, &check_set);
371
372   /* Wait one microsecond */
373   to.tv_sec = 0;
374   to.tv_usec = 1;
375
376   /* If we get a timeout, then that means still connected */
377   if (select (sock + 1, &check_set, NULL, NULL, &to) == 0)
378     {
379       /* Connection is valid (not EOF), so continue */
380       return 1;
381     }
382   else
383     return 0;
384 #else
385   /* Without select, it's hard to know for sure. */
386   return 1;
387 #endif
388 }
389
390 /* Create a socket and bind it to PORT locally.  Calling accept() on
391    such a socket waits for and accepts incoming TCP connections.  The
392    resulting socket is stored to LOCAL_SOCK.  */
393
394 uerr_t
395 bindport (const ip_address *bind_address, int *port, int *local_sock)
396 {
397   int sock;
398   int family = AF_INET;
399   struct sockaddr_storage ss;
400   struct sockaddr *sa = (struct sockaddr *)&ss;
401
402   /* For setting options with setsockopt. */
403   int setopt_val = 1;
404   void *setopt_ptr = (void *)&setopt_val;
405   socklen_t setopt_size = sizeof (setopt_val);
406
407 #ifdef ENABLE_IPV6
408   if (bind_address->type == IPV6_ADDRESS) 
409     family = AF_INET6;
410 #endif
411
412   if ((sock = socket (family, SOCK_STREAM, 0)) < 0)
413     return CONSOCKERR;
414
415 #ifdef SO_REUSEADDR
416   setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, setopt_ptr, setopt_size);
417 #endif
418
419 #ifdef ENABLE_IPV6
420 # ifdef HAVE_IPV6_V6ONLY
421   if (family == AF_INET6)
422     setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, setopt_ptr, setopt_size);
423 # endif
424 #endif
425
426   xzero (ss);
427   sockaddr_set_data (sa, bind_address, *port);
428   if (bind (sock, sa, sockaddr_size (sa)) < 0)
429     {
430       xclose (sock);
431       return BINDERR;
432     }
433   DEBUGP (("Local socket fd %d bound.\n", sock));
434   if (!*port)
435     {
436       socklen_t sa_len = sockaddr_size (sa);
437       if (getsockname (sock, sa, &sa_len) < 0)
438         {
439           xclose (sock);
440           return CONPORTERR;
441         }
442       sockaddr_get_data (sa, NULL, port);
443       DEBUGP (("binding to address %s using port %i.\n", 
444                pretty_print_address (bind_address), *port));
445     }
446   if (listen (sock, 1) < 0)
447     {
448       xclose (sock);
449       return LISTENERR;
450     }
451   *local_sock = sock;
452   return BINDOK;
453 }
454
455 #ifdef HAVE_SELECT
456 /* Wait for file descriptor FD to be readable or writable or both,
457    timing out after MAXTIME seconds.  Returns 1 if FD is available, 0
458    for timeout and -1 for error.  The argument WAIT_FOR can be a
459    combination of WAIT_READ and WAIT_WRITE.
460
461    This is a mere convenience wrapper around the select call, and
462    should be taken as such.  */
463
464 int
465 select_fd (int fd, double maxtime, int wait_for)
466 {
467   fd_set fdset;
468   fd_set *rd = NULL, *wr = NULL;
469   struct timeval tmout;
470   int result;
471
472   FD_ZERO (&fdset);
473   FD_SET (fd, &fdset);
474   if (wait_for & WAIT_FOR_READ)
475     rd = &fdset;
476   if (wait_for & WAIT_FOR_WRITE)
477     wr = &fdset;
478
479   tmout.tv_sec = (long) maxtime;
480   tmout.tv_usec = 1000000L * (maxtime - (long) maxtime);
481
482   do
483     result = select (fd + 1, rd, wr, NULL, &tmout);
484   while (result < 0 && errno == EINTR);
485
486   return result;
487 }
488 #endif /* HAVE_SELECT */
489
490 /* Accept a connection on LOCAL_SOCK, and store the new socket to
491    *SOCK.  It blocks the caller until a connection is established.  If
492    no connection is established for opt.connect_timeout seconds, the
493    function exits with an error status.  */
494
495 uerr_t
496 acceptport (int local_sock, int *sock)
497 {
498   struct sockaddr_storage ss;
499   struct sockaddr *sa = (struct sockaddr *)&ss;
500   socklen_t addrlen = sizeof (ss);
501
502 #ifdef HAVE_SELECT
503   if (opt.connect_timeout)
504     if (select_fd (local_sock, opt.connect_timeout, WAIT_FOR_READ) <= 0)
505       return ACCEPTERR;
506 #endif
507   if ((*sock = accept (local_sock, sa, &addrlen)) < 0)
508     return ACCEPTERR;
509   DEBUGP (("Created socket fd %d.\n", *sock));
510   return ACCEPTOK;
511 }
512
513 /* Get the IP address associated with the connection on FD and store
514    it to IP.  Return 1 on success, 0 otherwise.
515
516    If ENDPOINT is ENDPOINT_LOCAL, it returns the address of the local
517    (client) side of the socket.  Else if ENDPOINT is ENDPOINT_PEER, it
518    returns the address of the remote (peer's) side of the socket.  */
519
520 int
521 socket_ip_address (int sock, ip_address *ip, int endpoint)
522 {
523   struct sockaddr_storage storage;
524   struct sockaddr *sockaddr = (struct sockaddr *)&storage;
525   socklen_t addrlen = sizeof (storage);
526   int ret;
527
528   if (endpoint == ENDPOINT_LOCAL)
529     ret = getsockname (sock, sockaddr, &addrlen);
530   else if (endpoint == ENDPOINT_PEER)
531     ret = getpeername (sock, sockaddr, &addrlen);
532   else
533     abort ();
534   if (ret < 0)
535     return 0;
536
537   switch (sockaddr->sa_family)
538     {
539 #ifdef ENABLE_IPV6
540     case AF_INET6:
541       {
542         struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&storage;
543         ip->type = IPV6_ADDRESS;
544         ADDRESS_IPV6_IN6_ADDR (ip) = sa6->sin6_addr;
545 #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
546         ADDRESS_IPV6_SCOPE (ip) = sa6->sin6_scope_id;
547 #endif
548         DEBUGP (("conaddr is: %s\n", pretty_print_address (ip)));
549         return 1;
550       }
551 #endif
552     case AF_INET:
553       {
554         struct sockaddr_in *sa = (struct sockaddr_in *)&storage;
555         ip->type = IPV4_ADDRESS;
556         ADDRESS_IPV4_IN_ADDR (ip) = sa->sin_addr;
557         DEBUGP (("conaddr is: %s\n", pretty_print_address (ip)));
558         return 1;
559       }
560     default:
561       abort ();
562     }
563
564   return 0;
565 }
566 \f
567 /* Basic socket operations, mostly EINTR wrappers.  */
568
569 #ifdef WINDOWS
570 # define read(fd, buf, cnt) recv (fd, buf, cnt, 0)
571 # define write(fd, buf, cnt) send (fd, buf, cnt, 0)
572 # define close(fd) closesocket (fd)
573 #endif
574
575 #ifdef __BEOS__
576 # define read(fd, buf, cnt) recv (fd, buf, cnt, 0)
577 # define write(fd, buf, cnt) send (fd, buf, cnt, 0)
578 #endif
579
580 static int
581 sock_read (int fd, char *buf, int bufsize)
582 {
583   int res;
584   do
585     res = read (fd, buf, bufsize);
586   while (res == -1 && errno == EINTR);
587   return res;
588 }
589
590 static int
591 sock_write (int fd, char *buf, int bufsize)
592 {
593   int res = 0;
594   do
595     res = write (fd, buf, bufsize);
596   while (res == -1 && errno == EINTR);
597   return res;
598 }
599
600 static int
601 sock_poll (int fd, double timeout, int wait_for)
602 {
603 #ifdef HAVE_SELECT
604   return select_fd (fd, timeout, wait_for);
605 #else
606   return 1;
607 #endif
608 }
609
610 static void
611 sock_close (int fd)
612 {
613   close (fd);
614   DEBUGP (("Closed fd %d\n", fd));
615 }
616 #undef read
617 #undef write
618 #undef close
619 \f
620 /* Reading and writing from the network.  We build around the socket
621    (file descriptor) API, but support "extended" operations for things
622    that are not mere file descriptors under the hood, such as SSL
623    sockets.
624
625    That way the user code can call xread(fd, ...) and we'll run read
626    or SSL_read or whatever is necessary.  */
627
628 static struct hash_table *extended_map;
629 static int extended_map_modified_tick;
630
631 struct extended_info {
632   xreader_t reader;
633   xwriter_t writer;
634   xpoller_t poller;
635   xcloser_t closer;
636   void *ctx;
637 };
638
639 void
640 register_extended (int fd, xreader_t reader, xwriter_t writer,
641                    xpoller_t poller, xcloser_t closer, void *ctx)
642 {
643   struct extended_info *info;
644
645   /* The file descriptor must be non-negative to be registered.
646      Negative values are ignored by xclose(), and -1 cannot be used as
647      hash key.  */
648   assert (fd >= 0);
649
650   info = xnew (struct extended_info);
651   info->reader = reader;
652   info->writer = writer;
653   info->poller = poller;
654   info->closer = closer;
655   info->ctx = ctx;
656   if (!extended_map)
657     extended_map = hash_table_new (0, NULL, NULL);
658   hash_table_put (extended_map, (void *) fd, info);
659   ++extended_map_modified_tick;
660 }
661
662 /* When xread/xwrite are called multiple times in a loop, they should
663    remember the INFO pointer instead of fetching it every time.  It is
664    not enough to compare FD to LAST_FD because FD might have been
665    closed and reopened.  modified_tick ensures that changes to
666    extended_map will not be unnoticed.
667
668    This is a macro because we want the static storage variables to be
669    per-function.  */
670
671 #define LAZY_RETRIEVE_INFO(info) do {                                   \
672   static struct extended_info *last_info;                               \
673   static int last_fd = -1, last_tick;                                   \
674   if (!extended_map)                                                    \
675     info = NULL;                                                        \
676   else if (last_fd == fd && last_tick == extended_map_modified_tick)    \
677     info = last_info;                                                   \
678   else                                                                  \
679     {                                                                   \
680       info = hash_table_get (extended_map, (void *) fd);                \
681       last_fd = fd;                                                     \
682       last_info = info;                                                 \
683       last_tick = extended_map_modified_tick;                           \
684     }                                                                   \
685 } while (0)
686
687 /* Read no more than BUFSIZE bytes of data from FD, storing them to
688    BUF.  If TIMEOUT is non-zero, the operation aborts if no data is
689    received after that many seconds.  If TIMEOUT is -1, the value of
690    opt.timeout is used for TIMEOUT.  */
691
692 int
693 xread (int fd, char *buf, int bufsize, double timeout)
694 {
695   struct extended_info *info;
696   LAZY_RETRIEVE_INFO (info);
697   if (timeout == -1)
698     timeout = opt.read_timeout;
699   if (timeout)
700     {
701       int test;
702       if (info && info->poller)
703         test = info->poller (fd, timeout, WAIT_FOR_READ, info->ctx);
704       else
705         test = sock_poll (fd, timeout, WAIT_FOR_READ);
706       if (test == 0)
707         errno = ETIMEDOUT;
708       if (test <= 0)
709         return -1;
710     }
711   if (info && info->reader)
712     return info->reader (fd, buf, bufsize, info->ctx);
713   else
714     return sock_read (fd, buf, bufsize);
715 }
716
717 /* Write the entire contents of BUF to FD.  If TIMEOUT is non-zero,
718    the operation aborts if no data is received after that many
719    seconds.  If TIMEOUT is -1, the value of opt.timeout is used for
720    TIMEOUT.  */
721
722 int
723 xwrite (int fd, char *buf, int bufsize, double timeout)
724 {
725   int res;
726   struct extended_info *info;
727   LAZY_RETRIEVE_INFO (info);
728   if (timeout == -1)
729     timeout = opt.read_timeout;
730
731   /* `write' may write less than LEN bytes, thus the loop keeps trying
732      it until all was written, or an error occurred.  */
733   res = 0;
734   while (bufsize > 0)
735     {
736       if (timeout)
737         {
738           int test;
739           if (info && info->poller)
740             test = info->poller (fd, timeout, WAIT_FOR_WRITE, info->ctx);
741           else
742             test = sock_poll (fd, timeout, WAIT_FOR_WRITE);
743           if (test == 0)
744             errno = ETIMEDOUT;
745           if (test <= 0)
746             return -1;
747         }
748       if (info && info->writer)
749         res = info->writer (fd, buf, bufsize, info->ctx);
750       else
751         res = sock_write (fd, buf, bufsize);
752       if (res <= 0)
753         break;
754       buf += res;
755       bufsize -= res;
756     }
757   return res;
758 }
759
760 /* Close the file descriptor FD.  */
761
762 void
763 xclose (int fd)
764 {
765   struct extended_info *info;
766   if (fd < 0)
767     return;
768
769   /* We don't need to be extra-fast here, so save some code by
770      avoiding LAZY_RETRIEVE_INFO. */
771   info = NULL;
772   if (extended_map)
773     info = hash_table_get (extended_map, (void *) fd);
774
775   if (info && info->closer)
776     info->closer (fd, info->ctx);
777   else
778     sock_close (fd);
779
780   if (info)
781     {
782       hash_table_remove (extended_map, (void *) fd);
783       xfree (info);
784       ++extended_map_modified_tick;
785     }
786 }