]> sjero.net Git - wget/blob - src/connect.c
[svn] Set last_info.
[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 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 /* Return the local IP address associated with the connection on FD.  */
514
515 int
516 conaddr (int fd, ip_address *ip)
517 {
518   struct sockaddr_storage storage;
519   struct sockaddr *sockaddr = (struct sockaddr *)&storage;
520   socklen_t addrlen = sizeof (storage);
521
522   if (getsockname (fd, sockaddr, &addrlen) < 0)
523     return 0;
524
525   switch (sockaddr->sa_family)
526     {
527 #ifdef ENABLE_IPV6
528     case AF_INET6:
529       {
530         struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&storage;
531         ip->type = IPV6_ADDRESS;
532         ADDRESS_IPV6_IN6_ADDR (ip) = sa6->sin6_addr;
533 #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
534         ADDRESS_IPV6_SCOPE (ip) = sa6->sin6_scope_id;
535 #endif
536         DEBUGP (("conaddr is: %s\n", pretty_print_address (ip)));
537         return 1;
538       }
539 #endif
540     case AF_INET:
541       {
542         struct sockaddr_in *sa = (struct sockaddr_in *)&storage;
543         ip->type = IPV4_ADDRESS;
544         ADDRESS_IPV4_IN_ADDR (ip) = sa->sin_addr;
545         DEBUGP (("conaddr is: %s\n", pretty_print_address (ip)));
546         return 1;
547       }
548     default:
549       abort ();
550     }
551
552   return 0;
553 }
554 \f
555 /* Basic socket operations, mostly EINTR wrappers.  */
556
557 #ifdef WINDOWS
558 # define read(fd, buf, cnt) recv (fd, buf, cnt, 0)
559 # define write(fd, buf, cnt) send (fd, buf, cnt, 0)
560 # define close(fd) closesocket (fd)
561 #endif
562
563 #ifdef __BEOS__
564 # define read(fd, buf, cnt) recv (fd, buf, cnt, 0)
565 # define write(fd, buf, cnt) send (fd, buf, cnt, 0)
566 #endif
567
568 static int
569 sock_read (int fd, char *buf, int bufsize)
570 {
571   int res;
572   do
573     res = read (fd, buf, bufsize);
574   while (res == -1 && errno == EINTR);
575   return res;
576 }
577
578 static int
579 sock_write (int fd, char *buf, int bufsize)
580 {
581   int res = 0;
582   do
583     res = write (fd, buf, bufsize);
584   while (res == -1 && errno == EINTR);
585   return res;
586 }
587
588 static int
589 sock_poll (int fd, double timeout, int wait_for)
590 {
591 #ifdef HAVE_SELECT
592   return select_fd (fd, timeout, wait_for);
593 #else
594   return 1;
595 #endif
596 }
597
598 static void
599 sock_close (int fd)
600 {
601   close (fd);
602   DEBUGP (("Closed fd %d\n", fd));
603 }
604 #undef read
605 #undef write
606 #undef close
607 \f
608 /* Reading and writing from the network.  We build around the socket
609    (file descriptor) API, but support "extended" operations for things
610    that are not mere file descriptors under the hood, such as SSL
611    sockets.
612
613    That way the user code can call xread(fd, ...) and we'll run read
614    or SSL_read or whatever is necessary.  */
615
616 static struct hash_table *extended_map;
617 static int extended_map_modified_tick;
618
619 struct extended_info {
620   xreader_t reader;
621   xwriter_t writer;
622   xpoller_t poller;
623   xcloser_t closer;
624   void *ctx;
625 };
626
627 void
628 register_extended (int fd, xreader_t reader, xwriter_t writer,
629                    xpoller_t poller, xcloser_t closer, void *ctx)
630 {
631   struct extended_info *info = xnew (struct extended_info);
632   info->reader = reader;
633   info->writer = writer;
634   info->poller = poller;
635   info->closer = closer;
636   info->ctx = ctx;
637   if (!extended_map)
638     extended_map = hash_table_new (0, NULL, NULL);
639   hash_table_put (extended_map, (void *) fd, info);
640   ++extended_map_modified_tick;
641 }
642
643 /* When xread/xwrite are called multiple times in a loop, they should
644    remember the INFO pointer instead of fetching it every time.  It is
645    not enough to compare FD to LAST_FD because FD might have been
646    closed and reopened.  modified_tick ensures that changes to
647    extended_map will not be unnoticed.
648
649    This is a macro because we want the static storage variables to be
650    per-function.  */
651
652 #define LAZY_RETRIEVE_INFO(info) do {                                   \
653   static struct extended_info *last_info;                               \
654   static int last_fd = -1, last_tick;                                   \
655   if (!extended_map)                                                    \
656     info = NULL;                                                        \
657   else if (last_fd == fd && last_tick == extended_map_modified_tick)    \
658     info = last_info;                                                   \
659   else                                                                  \
660     {                                                                   \
661       info = hash_table_get (extended_map, (void *) fd);                \
662       last_fd = fd;                                                     \
663       last_info = info;                                                 \
664       last_tick = extended_map_modified_tick;                           \
665     }                                                                   \
666 } while (0)
667
668 /* Read no more than BUFSIZE bytes of data from FD, storing them to
669    BUF.  If TIMEOUT is non-zero, the operation aborts if no data is
670    received after that many seconds.  If TIMEOUT is -1, the value of
671    opt.timeout is used for TIMEOUT.  */
672
673 int
674 xread (int fd, char *buf, int bufsize, double timeout)
675 {
676   struct extended_info *info;
677   LAZY_RETRIEVE_INFO (info);
678   if (timeout == -1)
679     timeout = opt.read_timeout;
680   if (timeout)
681     {
682       int test;
683       if (info && info->poller)
684         test = info->poller (fd, timeout, WAIT_FOR_READ, info->ctx);
685       else
686         test = sock_poll (fd, timeout, WAIT_FOR_READ);
687       if (test == 0)
688         errno = ETIMEDOUT;
689       if (test <= 0)
690         return -1;
691     }
692   if (info && info->reader)
693     return info->reader (fd, buf, bufsize, info->ctx);
694   else
695     return sock_read (fd, buf, bufsize);
696 }
697
698 /* Write the entire contents of BUF to FD.  If TIMEOUT is non-zero,
699    the operation aborts if no data is received after that many
700    seconds.  If TIMEOUT is -1, the value of opt.timeout is used for
701    TIMEOUT.  */
702
703 int
704 xwrite (int fd, char *buf, int bufsize, double timeout)
705 {
706   int res;
707   struct extended_info *info;
708   LAZY_RETRIEVE_INFO (info);
709   if (timeout == -1)
710     timeout = opt.read_timeout;
711
712   /* `write' may write less than LEN bytes, thus the loop keeps trying
713      it until all was written, or an error occurred.  */
714   res = 0;
715   while (bufsize > 0)
716     {
717       if (timeout)
718         {
719           int test;
720           if (info && info->poller)
721             test = info->poller (fd, timeout, WAIT_FOR_WRITE, info->ctx);
722           else
723             test = sock_poll (fd, timeout, WAIT_FOR_WRITE);
724           if (test == 0)
725             errno = ETIMEDOUT;
726           if (test <= 0)
727             return -1;
728         }
729       if (info && info->writer)
730         res = info->writer (fd, buf, bufsize, info->ctx);
731       else
732         res = sock_write (fd, buf, bufsize);
733       if (res <= 0)
734         break;
735       buf += res;
736       bufsize -= res;
737     }
738   return res;
739 }
740
741 /* Close the file descriptor FD.  */
742
743 void
744 xclose (int fd)
745 {
746   struct extended_info *info;
747   if (fd < 0)
748     return;
749
750   /* We don't need to be extra-fast here, so save some code by
751      avoiding LAZY_RETRIEVE_INFO. */
752   info = NULL;
753   if (extended_map)
754     info = hash_table_get (extended_map, (void *) fd);
755
756   if (info && info->closer)
757     info->closer (fd, info->ctx);
758   else
759     sock_close (fd);
760
761   if (info)
762     {
763       hash_table_remove (extended_map, (void *) fd);
764       xfree (info);
765       ++extended_map_modified_tick;
766     }
767 }