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