6bb7f6464f567c77486a5f8c3344fcffa9860c9a
[gd/samba-autobuild/.git] / source4 / lib / socket_wrapper / socket_wrapper.c
1 /* 
2    Socket wrapper library. Passes all socket communication over 
3    unix domain sockets if the environment variable SOCKET_WRAPPER_DIR 
4    is set.
5    Copyright (C) Jelmer Vernooij 2005
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #ifdef _SAMBA_BUILD_
23 #include "includes.h"
24 #undef SOCKET_WRAPPER
25 #include "system/network.h"
26 #include "system/filesys.h"
27 #else
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/socket.h>
31 #include <errno.h>
32 #include <sys/un.h>
33 #include <netinet/in.h>
34 #include <netinet/tcp.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <string.h>
38 #include <stdio.h>
39 #endif
40 #include "dlinklist.h"
41
42 /* LD_PRELOAD doesn't work yet, so REWRITE_CALLS is all we support
43  * for now */
44 #define REWRITE_CALLS 
45
46 #ifdef REWRITE_CALLS
47 #define real_accept accept
48 #define real_connect connect
49 #define real_bind bind
50 #define real_getpeername getpeername
51 #define real_getsockname getsockname
52 #define real_getsockopt getsockopt
53 #define real_setsockopt setsockopt
54 #define real_recvfrom recvfrom
55 #define real_sendto sendto
56 #define real_socket socket
57 #define real_close close
58 #endif
59
60 /* we need to use a very terse format here as IRIX 6.4 silently
61    truncates names to 16 chars, so if we use a longer name then we
62    can't tell which port a packet came from with recvfrom() 
63    
64    with this format we have 8 chars left for the directory name
65 */
66 #define SOCKET_FORMAT "%c%02X%04X"
67 #define SOCKET_TYPE_CHAR_TCP            'T'
68 #define SOCKET_TYPE_CHAR_UDP            'U'
69
70 static struct sockaddr *sockaddr_dup(const void *data, socklen_t len)
71 {
72         struct sockaddr *ret = (struct sockaddr *)malloc(len);
73         memcpy(ret, data, len);
74         return ret;
75 }
76
77 struct socket_info
78 {
79         int fd;
80
81         int domain;
82         int type;
83         int protocol;
84         int bound;
85         int bcast;
86
87         char *path;
88         char *tmp_path;
89
90         struct sockaddr *myname;
91         socklen_t myname_len;
92
93         struct sockaddr *peername;
94         socklen_t peername_len;
95
96         struct socket_info *prev, *next;
97 };
98
99 static struct socket_info *sockets = NULL;
100
101
102 static const char *socket_wrapper_dir(void)
103 {
104         const char *s = getenv("SOCKET_WRAPPER_DIR");
105         if (s == NULL) {
106                 return NULL;
107         }
108         if (strncmp(s, "./", 2) == 0) {
109                 s += 2;
110         }
111         return s;
112 }
113
114 static unsigned int socket_wrapper_default_iface(void)
115 {
116         const char *s = getenv("SOCKET_WRAPPER_DEFAULT_IFACE");
117         if (s) {
118                 unsigned int iface;
119                 if (sscanf(s, "%u", &iface) == 1) {
120                         if (iface >= 1 && iface <= 0xFF) {
121                                 return iface;
122                         }
123                 }
124         }
125
126         return 1;/* 127.0.0.1 */
127 }
128
129 static int convert_un_in(const struct sockaddr_un *un, struct sockaddr_in *in, socklen_t *len)
130 {
131         unsigned int iface;
132         unsigned int prt;
133         const char *p;
134         char type;
135
136         if ((*len) < sizeof(struct sockaddr_in)) {
137                 return 0;
138         }
139
140         p = strrchr(un->sun_path, '/');
141         if (p) p++; else p = un->sun_path;
142
143         if (sscanf(p, SOCKET_FORMAT, &type, &iface, &prt) != 3) {
144                 errno = EINVAL;
145                 return -1;
146         }
147
148         if (type != SOCKET_TYPE_CHAR_TCP && type != SOCKET_TYPE_CHAR_UDP) {
149                 errno = EINVAL;
150                 return -1;
151         }
152
153         if (iface == 0 || iface > 0xFF) {
154                 errno = EINVAL;
155                 return -1;
156         }
157
158         if (prt > 0xFFFF) {
159                 errno = EINVAL;
160                 return -1;
161         }
162
163         in->sin_family = AF_INET;
164         in->sin_addr.s_addr = htonl((127<<24) | iface);
165         in->sin_port = htons(prt);
166
167         *len = sizeof(struct sockaddr_in);
168         return 0;
169 }
170
171 static int convert_in_un_remote(struct socket_info *si, const struct sockaddr_in *in, struct sockaddr_un *un,
172                                 int *bcast)
173 {
174         char u_type = '\0';
175         char b_type = '\0';
176         char a_type = '\0';
177         char type = '\0';
178         unsigned int addr= ntohl(in->sin_addr.s_addr);
179         unsigned int prt = ntohs(in->sin_port);
180         unsigned int iface;
181         int is_bcast = 0;
182
183         if (bcast) *bcast = 0;
184
185         if (prt == 0) {
186                 errno = EINVAL;
187                 return -1;
188         }
189
190         switch (si->type) {
191         case SOCK_STREAM:
192                 u_type = SOCKET_TYPE_CHAR_TCP;
193                 break;
194         case SOCK_DGRAM:
195                 u_type = SOCKET_TYPE_CHAR_UDP;
196                 a_type = SOCKET_TYPE_CHAR_UDP;
197                 b_type = SOCKET_TYPE_CHAR_UDP;
198                 break;
199         }
200
201         if (a_type && addr == 0xFFFFFFFF) {
202                 /* 255.255.255.255 only udp */
203                 is_bcast = 2;
204                 type = a_type;
205                 iface = socket_wrapper_default_iface();
206         } else if (b_type && addr == 0x7FFFFFFF) {
207                 /* 127.255.255.255 only udp */
208                 is_bcast = 1;
209                 type = b_type;
210                 iface = socket_wrapper_default_iface();
211         } else if ((addr & 0xFFFFFF00) == 0x7F000000) {
212                 /* 127.0.0.X */
213                 is_bcast = 0;
214                 type = u_type;
215                 iface = (addr & 0x000000FF);
216         } else {
217                 errno = ENETUNREACH;
218                 return -1;
219         }
220
221         if (bcast) *bcast = is_bcast;
222
223         if (is_bcast) {
224                 snprintf(un->sun_path, sizeof(un->sun_path), "%s/EINVAL", 
225                          socket_wrapper_dir());
226                 /* the caller need to do more processing */
227                 return 0;
228         }
229
230         snprintf(un->sun_path, sizeof(un->sun_path), "%s/"SOCKET_FORMAT, 
231                  socket_wrapper_dir(), type, iface, prt);
232
233         return 0;
234 }
235
236 static int convert_in_un_alloc(struct socket_info *si, const struct sockaddr_in *in, struct sockaddr_un *un,
237                                int *bcast)
238 {
239         char u_type = '\0';
240         char d_type = '\0';
241         char b_type = '\0';
242         char a_type = '\0';
243         char type = '\0';
244         unsigned int addr= ntohl(in->sin_addr.s_addr);
245         unsigned int prt = ntohs(in->sin_port);
246         unsigned int iface;
247         struct stat st;
248         int is_bcast = 0;
249
250         if (bcast) *bcast = 0;
251
252         switch (si->type) {
253         case SOCK_STREAM:
254                 u_type = SOCKET_TYPE_CHAR_TCP;
255                 d_type = SOCKET_TYPE_CHAR_TCP;
256                 break;
257         case SOCK_DGRAM:
258                 u_type = SOCKET_TYPE_CHAR_UDP;
259                 d_type = SOCKET_TYPE_CHAR_UDP;
260                 a_type = SOCKET_TYPE_CHAR_UDP;
261                 b_type = SOCKET_TYPE_CHAR_UDP;
262                 break;
263         }
264
265         if (addr == 0) {
266                 /* 0.0.0.0 */
267                 is_bcast = 0;
268                 type = d_type;
269                 iface = socket_wrapper_default_iface();
270         } else if (a_type && addr == 0xFFFFFFFF) {
271                 /* 255.255.255.255 only udp */
272                 is_bcast = 2;
273                 type = a_type;
274                 iface = socket_wrapper_default_iface();
275         } else if (b_type && addr == 0x7FFFFFFF) {
276                 /* 127.255.255.255 only udp */
277                 is_bcast = 1;
278                 type = b_type;
279                 iface = socket_wrapper_default_iface();
280         } else if ((addr & 0xFFFFFF00) == 0x7F000000) {
281                 /* 127.0.0.X */
282                 is_bcast = 0;
283                 type = u_type;
284                 iface = (addr & 0x000000FF);
285         } else {
286                 errno = EADDRNOTAVAIL;
287                 return -1;
288         }
289
290         if (bcast) *bcast = is_bcast;
291
292         if (prt == 0) {
293                 /* handle auto-allocation of ephemeral ports */
294                 for (prt = 5001; prt < 10000; prt++) {
295                         snprintf(un->sun_path, sizeof(un->sun_path), "%s/"SOCKET_FORMAT, 
296                                  socket_wrapper_dir(), type, iface, prt);
297                         if (stat(un->sun_path, &st) == 0) continue;
298
299                         ((struct sockaddr_in *)si->myname)->sin_port = htons(prt);
300                         return 0;
301                 }
302                 errno = ENFILE;
303                 return -1;
304         }
305
306         snprintf(un->sun_path, sizeof(un->sun_path), "%s/"SOCKET_FORMAT, 
307                  socket_wrapper_dir(), type, iface, prt);
308         return 0;
309 }
310
311 static struct socket_info *find_socket_info(int fd)
312 {
313         struct socket_info *i;
314         for (i = sockets; i; i = i->next) {
315                 if (i->fd == fd) 
316                         return i;
317         }
318
319         return NULL;
320 }
321
322 static int sockaddr_convert_to_un(struct socket_info *si, const struct sockaddr *in_addr, socklen_t in_len, 
323                                   struct sockaddr_un *out_addr, int alloc_sock, int *bcast)
324 {
325         if (!out_addr)
326                 return 0;
327
328         out_addr->sun_family = AF_UNIX;
329
330         switch (in_addr->sa_family) {
331         case AF_INET:
332                 switch (si->type) {
333                 case SOCK_STREAM:
334                 case SOCK_DGRAM:
335                         break;
336                 default:
337                         errno = ESOCKTNOSUPPORT;
338                         return -1;
339                 }
340                 if (alloc_sock) {
341                         return convert_in_un_alloc(si, (const struct sockaddr_in *)in_addr, out_addr, bcast);
342                 } else {
343                         return convert_in_un_remote(si, (const struct sockaddr_in *)in_addr, out_addr, bcast);
344                 }
345         case AF_UNIX:
346                 memcpy(out_addr, in_addr, sizeof(*out_addr));
347                 return 0;
348         default:
349                 break;
350         }
351         
352         errno = EAFNOSUPPORT;
353         return -1;
354 }
355
356 static int sockaddr_convert_from_un(const struct socket_info *si, 
357                                     const struct sockaddr_un *in_addr, 
358                                     socklen_t un_addrlen,
359                                     int family,
360                                     struct sockaddr *out_addr,
361                                     socklen_t *_out_addrlen)
362 {
363         socklen_t out_addrlen;
364
365         if (out_addr == NULL || _out_addrlen == NULL) 
366                 return 0;
367
368         if (un_addrlen == 0) {
369                 *_out_addrlen = 0;
370                 return 0;
371         }
372
373         out_addrlen = *_out_addrlen;
374         if (out_addrlen > un_addrlen) {
375                 out_addrlen = un_addrlen;
376         }
377
378         switch (family) {
379         case AF_INET:
380                 switch (si->type) {
381                 case SOCK_STREAM:
382                 case SOCK_DGRAM:
383                         break;
384                 default:
385                         errno = ESOCKTNOSUPPORT;
386                         return -1;
387                 }
388                 return convert_un_in(in_addr, (struct sockaddr_in *)out_addr, _out_addrlen);
389         case AF_UNIX:
390                 memcpy(out_addr, in_addr, out_addrlen);
391                 *_out_addrlen = out_addrlen;
392                 return 0;
393         default:
394                 break;
395         }
396
397         errno = EAFNOSUPPORT;
398         return -1;
399 }
400
401 _PUBLIC_ int swrap_socket(int domain, int type, int protocol)
402 {
403         struct socket_info *si;
404         int fd;
405
406         if (!socket_wrapper_dir()) {
407                 return real_socket(domain, type, protocol);
408         }
409         
410         fd = real_socket(AF_UNIX, type, 0);
411
412         if (fd == -1) return -1;
413
414         si = calloc(1, sizeof(struct socket_info));
415
416         si->domain = domain;
417         si->type = type;
418         si->protocol = protocol;
419         si->fd = fd;
420
421         DLIST_ADD(sockets, si);
422
423         return si->fd;
424 }
425
426 _PUBLIC_ int swrap_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
427 {
428         struct socket_info *parent_si, *child_si;
429         int fd;
430         struct sockaddr_un un_addr;
431         socklen_t un_addrlen = sizeof(un_addr);
432         struct sockaddr_un un_my_addr;
433         socklen_t un_my_addrlen = sizeof(un_my_addr);
434         struct sockaddr my_addr;
435         socklen_t my_addrlen = sizeof(my_addr);
436         int ret;
437
438         parent_si = find_socket_info(s);
439         if (!parent_si) {
440                 return real_accept(s, addr, addrlen);
441         }
442
443         memset(&un_addr, 0, sizeof(un_addr));
444         memset(&un_my_addr, 0, sizeof(un_my_addr));
445         memset(&my_addr, 0, sizeof(my_addr));
446
447         ret = real_accept(s, (struct sockaddr *)&un_addr, &un_addrlen);
448         if (ret == -1) return ret;
449
450         fd = ret;
451
452         ret = sockaddr_convert_from_un(parent_si, &un_addr, un_addrlen,
453                                        parent_si->domain, addr, addrlen);
454         if (ret == -1) return ret;
455
456         child_si = malloc(sizeof(struct socket_info));
457         memset(child_si, 0, sizeof(*child_si));
458
459         child_si->fd = fd;
460         child_si->domain = parent_si->domain;
461         child_si->type = parent_si->type;
462         child_si->protocol = parent_si->protocol;
463         child_si->bound = 1;
464
465         ret = real_getsockname(fd, (struct sockaddr *)&un_my_addr, &un_my_addrlen);
466         if (ret == -1) return ret;
467
468         ret = sockaddr_convert_from_un(child_si, &un_my_addr, un_my_addrlen,
469                                        child_si->domain, &my_addr, &my_addrlen);
470         if (ret == -1) return ret;
471
472         child_si->myname_len = my_addrlen;
473         child_si->myname = sockaddr_dup(&my_addr, my_addrlen);
474
475         child_si->peername_len = *addrlen;
476         child_si->peername = sockaddr_dup(addr, *addrlen);
477
478         DLIST_ADD(sockets, child_si);
479
480         return fd;
481 }
482
483 /* using sendto() or connect() on an unbound socket would give the
484    recipient no way to reply, as unlike UDP and TCP, a unix domain
485    socket can't auto-assign emphemeral port numbers, so we need to
486    assign it here */
487 static int swrap_auto_bind(struct socket_info *si)
488 {
489         struct sockaddr_un un_addr;
490         struct sockaddr_in in;
491         int i;
492         char type;
493         int ret;
494         struct stat st;
495         
496         un_addr.sun_family = AF_UNIX;
497
498         switch (si->type) {
499         case SOCK_STREAM:
500                 type = SOCKET_TYPE_CHAR_TCP;
501                 break;
502         case SOCK_DGRAM:
503                 type = SOCKET_TYPE_CHAR_UDP;
504                 break;
505         default:
506                 errno = ESOCKTNOSUPPORT;
507                 return -1;
508         }
509         
510         for (i=0;i<1000;i++) {
511                 snprintf(un_addr.sun_path, sizeof(un_addr.sun_path), 
512                          "%s/"SOCKET_FORMAT, socket_wrapper_dir(),
513                          type, socket_wrapper_default_iface(), i + 10000);
514                 if (stat(un_addr.sun_path, &st) == 0) continue;
515                 
516                 ret = real_bind(si->fd, (struct sockaddr *)&un_addr, sizeof(un_addr));
517                 if (ret == -1) return ret;
518
519                 si->tmp_path = strdup(un_addr.sun_path);
520                 si->bound = 1;
521                 break;
522         }
523         if (i == 1000) {
524                 errno = ENFILE;
525                 return -1;
526         }
527         
528         memset(&in, 0, sizeof(in));
529         in.sin_family = AF_INET;
530         in.sin_port   = htons(i);
531         in.sin_addr.s_addr = htonl(127<<24 | socket_wrapper_default_iface());
532         
533         si->myname_len = sizeof(in);
534         si->myname = sockaddr_dup(&in, si->myname_len);
535         si->bound = 1;
536         return 0;
537 }
538
539
540 _PUBLIC_ int swrap_connect(int s, const struct sockaddr *serv_addr, socklen_t addrlen)
541 {
542         int ret;
543         struct sockaddr_un un_addr;
544         struct socket_info *si = find_socket_info(s);
545
546         if (!si) {
547                 return real_connect(s, serv_addr, addrlen);
548         }
549
550         if (si->bound == 0 && si->domain != AF_UNIX) {
551                 ret = swrap_auto_bind(si);
552                 if (ret == -1) return -1;
553         }
554
555         ret = sockaddr_convert_to_un(si, (const struct sockaddr *)serv_addr, addrlen, &un_addr, 0, NULL);
556         if (ret == -1) return -1;
557
558         ret = real_connect(s, (struct sockaddr *)&un_addr, 
559                            sizeof(struct sockaddr_un));
560
561         /* to give better errors */
562         if (serv_addr->sa_family == AF_INET) {
563                 if (ret == -1 && errno == ENOENT) {
564                         errno = EHOSTUNREACH;
565                 }
566         }
567
568         if (ret == 0) {
569                 si->peername_len = addrlen;
570                 si->peername = sockaddr_dup(serv_addr, addrlen);
571         }
572
573         return ret;
574 }
575
576 _PUBLIC_ int swrap_bind(int s, const struct sockaddr *myaddr, socklen_t addrlen)
577 {
578         int ret;
579         struct sockaddr_un un_addr;
580         struct socket_info *si = find_socket_info(s);
581
582         if (!si) {
583                 return real_bind(s, myaddr, addrlen);
584         }
585
586         si->myname_len = addrlen;
587         si->myname = sockaddr_dup(myaddr, addrlen);
588
589         ret = sockaddr_convert_to_un(si, (const struct sockaddr *)myaddr, addrlen, &un_addr, 1, &si->bcast);
590         if (ret == -1) return -1;
591
592         unlink(un_addr.sun_path);
593
594         ret = real_bind(s, (struct sockaddr *)&un_addr,
595                         sizeof(struct sockaddr_un));
596
597         if (ret == 0) {
598                 si->bound = 1;
599         }
600
601         return ret;
602 }
603
604 _PUBLIC_ int swrap_getpeername(int s, struct sockaddr *name, socklen_t *addrlen)
605 {
606         struct socket_info *si = find_socket_info(s);
607
608         if (!si) {
609                 return real_getpeername(s, name, addrlen);
610         }
611
612         if (!si->peername) 
613         {
614                 errno = ENOTCONN;
615                 return -1;
616         }
617
618         memcpy(name, si->peername, si->peername_len);
619         *addrlen = si->peername_len;
620
621         return 0;
622 }
623
624 _PUBLIC_ int swrap_getsockname(int s, struct sockaddr *name, socklen_t *addrlen)
625 {
626         struct socket_info *si = find_socket_info(s);
627
628         if (!si) {
629                 return real_getsockname(s, name, addrlen);
630         }
631
632         memcpy(name, si->myname, si->myname_len);
633         *addrlen = si->myname_len;
634
635         return 0;
636 }
637
638 _PUBLIC_ int swrap_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
639 {
640         struct socket_info *si = find_socket_info(s);
641
642         if (!si) {
643                 return real_getsockopt(s, level, optname, optval, optlen);
644         }
645
646         if (level == SOL_SOCKET) {
647                 return real_getsockopt(s, level, optname, optval, optlen);
648         } 
649
650         switch (si->domain) {
651         case AF_UNIX:
652                 return real_getsockopt(s, level, optname, optval, optlen);
653         default:
654                 errno = ENOPROTOOPT;
655                 return -1;
656         }
657 }
658
659 _PUBLIC_ int swrap_setsockopt(int s, int  level,  int  optname,  const  void  *optval, socklen_t optlen)
660 {
661         struct socket_info *si = find_socket_info(s);
662
663         if (!si) {
664                 return real_setsockopt(s, level, optname, optval, optlen);
665         }
666
667         if (level == SOL_SOCKET) {
668                 return real_setsockopt(s, level, optname, optval, optlen);
669         }
670
671         switch (si->domain) {
672         case AF_UNIX:
673                 return real_setsockopt(s, level, optname, optval, optlen);
674         case AF_INET:
675                 return 0;
676         default:
677                 errno = ENOPROTOOPT;
678                 return -1;
679         }
680 }
681
682 _PUBLIC_ ssize_t swrap_recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen)
683 {
684         struct sockaddr_un un_addr;
685         socklen_t un_addrlen = sizeof(un_addr);
686         int ret;
687         struct socket_info *si = find_socket_info(s);
688
689         if (!si) {
690                 return real_recvfrom(s, buf, len, flags, from, fromlen);
691         }
692
693         /* irix 6.4 forgets to null terminate the sun_path string :-( */
694         memset(&un_addr, 0, sizeof(un_addr));
695         ret = real_recvfrom(s, buf, len, flags, (struct sockaddr *)&un_addr, &un_addrlen);
696         if (ret == -1) 
697                 return ret;
698
699         if (sockaddr_convert_from_un(si, &un_addr, un_addrlen,
700                                      si->domain, from, fromlen) == -1) {
701                 return -1;
702         }
703         
704         return ret;
705 }
706
707
708 _PUBLIC_ ssize_t swrap_sendto(int  s,  const  void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen)
709 {
710         struct sockaddr_un un_addr;
711         int ret;
712         struct socket_info *si = find_socket_info(s);
713         int bcast = 0;
714
715         if (!si) {
716                 return real_sendto(s, buf, len, flags, to, tolen);
717         }
718
719         if (si->bound == 0 && si->domain != AF_UNIX) {
720                 ret = swrap_auto_bind(si);
721                 if (ret == -1) return -1;
722         }
723
724         ret = sockaddr_convert_to_un(si, to, tolen, &un_addr, 0, &bcast);
725         if (ret == -1) return -1;
726
727         if (bcast) {
728                 struct stat st;
729                 unsigned int iface;
730                 unsigned int prt = ntohs(((const struct sockaddr_in *)to)->sin_port);
731                 char type;
732
733                 type = SOCKET_TYPE_CHAR_UDP;
734
735                 for(iface=0; iface <= 0xFF; iface++) {
736                         snprintf(un_addr.sun_path, sizeof(un_addr.sun_path), "%s/"SOCKET_FORMAT, 
737                                  socket_wrapper_dir(), type, iface, prt);
738                         if (stat(un_addr.sun_path, &st) != 0) continue;
739
740                         /* ignore the any errors in broadcast sends */
741                         real_sendto(s, buf, len, flags, (struct sockaddr *)&un_addr, sizeof(un_addr));
742                 }
743                 return len;
744         }
745
746         ret = real_sendto(s, buf, len, flags, (struct sockaddr *)&un_addr, sizeof(un_addr));
747
748         /* to give better errors */
749         if (to->sa_family == AF_INET) {
750                 if (ret == -1 && errno == ENOENT) {
751                         errno = EHOSTUNREACH;
752                 }
753         }
754
755         return ret;
756 }
757
758 _PUBLIC_ int swrap_close(int fd)
759 {
760         struct socket_info *si = find_socket_info(fd);
761
762         if (si) {
763                 DLIST_REMOVE(sockets, si);
764
765                 free(si->path);
766                 free(si->myname);
767                 free(si->peername);
768                 if (si->tmp_path) {
769                         unlink(si->tmp_path);
770                         free(si->tmp_path);
771                 }
772                 free(si);
773         }
774
775         return real_close(fd);
776 }