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