2 Socket wrapper library. Passes all socket communication over
3 unix domain sockets if the environment variable SOCKET_WRAPPER_DIR
5 Copyright (C) Jelmer Vernooij 2005
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.
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.
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.
29 #ifdef SOCKET_WRAPPER_REPLACE
43 /* LD_PRELOAD doesn't work yet, so REWRITE_CALLS is all we support
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
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()
68 with this format we have 8 chars left for the directory name
70 #define SOCKET_FORMAT "%c%02X%04X"
71 #define SOCKET_TYPE_CHAR_TCP 'T'
72 #define SOCKET_TYPE_CHAR_UDP 'U'
74 static struct sockaddr
*sockaddr_dup(const void *data
, socklen_t len
)
76 struct sockaddr
*ret
= (struct sockaddr
*)malloc(len
);
77 memcpy(ret
, data
, len
);
94 struct sockaddr
*myname
;
97 struct sockaddr
*peername
;
98 socklen_t peername_len
;
100 struct socket_info
*prev
, *next
;
103 static struct socket_info
*sockets
= NULL
;
106 static const char *socket_wrapper_dir(void)
108 const char *s
= getenv("SOCKET_WRAPPER_DIR");
112 if (strncmp(s
, "./", 2) == 0) {
118 static unsigned int socket_wrapper_default_iface(void)
120 const char *s
= getenv("SOCKET_WRAPPER_DEFAULT_IFACE");
123 if (sscanf(s
, "%u", &iface
) == 1) {
124 if (iface
>= 1 && iface
<= 0xFF) {
130 return 1;/* 127.0.0.1 */
133 static int convert_un_in(const struct sockaddr_un
*un
, struct sockaddr_in
*in
, socklen_t
*len
)
140 if ((*len
) < sizeof(struct sockaddr_in
)) {
144 p
= strrchr(un
->sun_path
, '/');
145 if (p
) p
++; else p
= un
->sun_path
;
147 if (sscanf(p
, SOCKET_FORMAT
, &type
, &iface
, &prt
) != 3) {
152 if (type
!= SOCKET_TYPE_CHAR_TCP
&& type
!= SOCKET_TYPE_CHAR_UDP
) {
157 if (iface
== 0 || iface
> 0xFF) {
167 in
->sin_family
= AF_INET
;
168 in
->sin_addr
.s_addr
= htonl((127<<24) | iface
);
169 in
->sin_port
= htons(prt
);
171 *len
= sizeof(struct sockaddr_in
);
175 static int convert_in_un_remote(struct socket_info
*si
, const struct sockaddr_in
*in
, struct sockaddr_un
*un
,
182 unsigned int addr
= ntohl(in
->sin_addr
.s_addr
);
183 unsigned int prt
= ntohs(in
->sin_port
);
187 if (bcast
) *bcast
= 0;
196 u_type
= SOCKET_TYPE_CHAR_TCP
;
199 u_type
= SOCKET_TYPE_CHAR_UDP
;
200 a_type
= SOCKET_TYPE_CHAR_UDP
;
201 b_type
= SOCKET_TYPE_CHAR_UDP
;
205 if (a_type
&& addr
== 0xFFFFFFFF) {
206 /* 255.255.255.255 only udp */
209 iface
= socket_wrapper_default_iface();
210 } else if (b_type
&& addr
== 0x7FFFFFFF) {
211 /* 127.255.255.255 only udp */
214 iface
= socket_wrapper_default_iface();
215 } else if ((addr
& 0xFFFFFF00) == 0x7F000000) {
219 iface
= (addr
& 0x000000FF);
225 if (bcast
) *bcast
= 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 */
234 snprintf(un
->sun_path
, sizeof(un
->sun_path
), "%s/"SOCKET_FORMAT
,
235 socket_wrapper_dir(), type
, iface
, prt
);
240 static int convert_in_un_alloc(struct socket_info
*si
, const struct sockaddr_in
*in
, struct sockaddr_un
*un
,
248 unsigned int addr
= ntohl(in
->sin_addr
.s_addr
);
249 unsigned int prt
= ntohs(in
->sin_port
);
254 if (bcast
) *bcast
= 0;
258 u_type
= SOCKET_TYPE_CHAR_TCP
;
259 d_type
= SOCKET_TYPE_CHAR_TCP
;
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
;
273 iface
= socket_wrapper_default_iface();
274 } else if (a_type
&& addr
== 0xFFFFFFFF) {
275 /* 255.255.255.255 only udp */
278 iface
= socket_wrapper_default_iface();
279 } else if (b_type
&& addr
== 0x7FFFFFFF) {
280 /* 127.255.255.255 only udp */
283 iface
= socket_wrapper_default_iface();
284 } else if ((addr
& 0xFFFFFF00) == 0x7F000000) {
288 iface
= (addr
& 0x000000FF);
290 errno
= EADDRNOTAVAIL
;
294 if (bcast
) *bcast
= is_bcast
;
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;
303 ((struct sockaddr_in
*)si
->myname
)->sin_port
= htons(prt
);
310 snprintf(un
->sun_path
, sizeof(un
->sun_path
), "%s/"SOCKET_FORMAT
,
311 socket_wrapper_dir(), type
, iface
, prt
);
315 static struct socket_info
*find_socket_info(int fd
)
317 struct socket_info
*i
;
318 for (i
= sockets
; i
; i
= i
->next
) {
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
)
332 out_addr
->sun_family
= AF_UNIX
;
334 switch (in_addr
->sa_family
) {
341 errno
= ESOCKTNOSUPPORT
;
345 return convert_in_un_alloc(si
, (const struct sockaddr_in
*)in_addr
, out_addr
, bcast
);
347 return convert_in_un_remote(si
, (const struct sockaddr_in
*)in_addr
, out_addr
, bcast
);
350 memcpy(out_addr
, in_addr
, sizeof(*out_addr
));
356 errno
= EAFNOSUPPORT
;
360 static int sockaddr_convert_from_un(const struct socket_info
*si
,
361 const struct sockaddr_un
*in_addr
,
362 socklen_t un_addrlen
,
364 struct sockaddr
*out_addr
,
365 socklen_t
*_out_addrlen
)
367 socklen_t out_addrlen
;
369 if (out_addr
== NULL
|| _out_addrlen
== NULL
)
372 if (un_addrlen
== 0) {
377 out_addrlen
= *_out_addrlen
;
378 if (out_addrlen
> un_addrlen
) {
379 out_addrlen
= un_addrlen
;
389 errno
= ESOCKTNOSUPPORT
;
392 return convert_un_in(in_addr
, (struct sockaddr_in
*)out_addr
, _out_addrlen
);
394 memcpy(out_addr
, in_addr
, out_addrlen
);
395 *_out_addrlen
= out_addrlen
;
401 errno
= EAFNOSUPPORT
;
405 _PUBLIC_
int swrap_socket(int domain
, int type
, int protocol
)
407 struct socket_info
*si
;
410 if (!socket_wrapper_dir()) {
411 return real_socket(domain
, type
, protocol
);
414 si
= (struct socket_info
*)calloc(1, sizeof(struct socket_info
));
420 fd
= real_socket(AF_UNIX
, type
, 0);
422 if (fd
== -1) return -1;
426 si
->protocol
= protocol
;
429 DLIST_ADD(sockets
, si
);
434 _PUBLIC_
int swrap_accept(int s
, struct sockaddr
*addr
, socklen_t
*addrlen
)
436 struct socket_info
*parent_si
, *child_si
;
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
);
446 parent_si
= find_socket_info(s
);
448 return real_accept(s
, addr
, addrlen
);
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
));
455 ret
= real_accept(s
, (struct sockaddr
*)&un_addr
, &un_addrlen
);
456 if (ret
== -1) return ret
;
460 ret
= sockaddr_convert_from_un(parent_si
, &un_addr
, un_addrlen
,
461 parent_si
->domain
, addr
, addrlen
);
462 if (ret
== -1) return ret
;
464 child_si
= (struct socket_info
*)malloc(sizeof(struct socket_info
));
465 if (child_si
== NULL
) {
470 memset(child_si
, 0, sizeof(*child_si
));
473 child_si
->domain
= parent_si
->domain
;
474 child_si
->type
= parent_si
->type
;
475 child_si
->protocol
= parent_si
->protocol
;
478 ret
= real_getsockname(fd
, (struct sockaddr
*)&un_my_addr
, &un_my_addrlen
);
479 if (ret
== -1) return ret
;
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
;
485 child_si
->myname_len
= my_addrlen
;
486 child_si
->myname
= sockaddr_dup(&my_addr
, my_addrlen
);
488 child_si
->peername_len
= *addrlen
;
489 child_si
->peername
= sockaddr_dup(addr
, *addrlen
);
491 DLIST_ADD(sockets
, child_si
);
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
500 static int swrap_auto_bind(struct socket_info
*si
)
502 struct sockaddr_un un_addr
;
503 struct sockaddr_in in
;
509 un_addr
.sun_family
= AF_UNIX
;
513 type
= SOCKET_TYPE_CHAR_TCP
;
516 type
= SOCKET_TYPE_CHAR_UDP
;
519 errno
= ESOCKTNOSUPPORT
;
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;
529 ret
= real_bind(si
->fd
, (struct sockaddr
*)&un_addr
, sizeof(un_addr
));
530 if (ret
== -1) return ret
;
532 si
->tmp_path
= strdup(un_addr
.sun_path
);
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());
546 si
->myname_len
= sizeof(in
);
547 si
->myname
= sockaddr_dup(&in
, si
->myname_len
);
553 _PUBLIC_
int swrap_connect(int s
, const struct sockaddr
*serv_addr
, socklen_t addrlen
)
556 struct sockaddr_un un_addr
;
557 struct socket_info
*si
= find_socket_info(s
);
560 return real_connect(s
, serv_addr
, addrlen
);
563 if (si
->bound
== 0 && si
->domain
!= AF_UNIX
) {
564 ret
= swrap_auto_bind(si
);
565 if (ret
== -1) return -1;
568 ret
= sockaddr_convert_to_un(si
, (const struct sockaddr
*)serv_addr
, addrlen
, &un_addr
, 0, NULL
);
569 if (ret
== -1) return -1;
571 ret
= real_connect(s
, (struct sockaddr
*)&un_addr
,
572 sizeof(struct sockaddr_un
));
574 /* to give better errors */
575 if (serv_addr
->sa_family
== AF_INET
) {
576 if (ret
== -1 && errno
== ENOENT
) {
577 errno
= EHOSTUNREACH
;
582 si
->peername_len
= addrlen
;
583 si
->peername
= sockaddr_dup(serv_addr
, addrlen
);
589 _PUBLIC_
int swrap_bind(int s
, const struct sockaddr
*myaddr
, socklen_t addrlen
)
592 struct sockaddr_un un_addr
;
593 struct socket_info
*si
= find_socket_info(s
);
596 return real_bind(s
, myaddr
, addrlen
);
599 si
->myname_len
= addrlen
;
600 si
->myname
= sockaddr_dup(myaddr
, addrlen
);
602 ret
= sockaddr_convert_to_un(si
, (const struct sockaddr
*)myaddr
, addrlen
, &un_addr
, 1, &si
->bcast
);
603 if (ret
== -1) return -1;
605 unlink(un_addr
.sun_path
);
607 ret
= real_bind(s
, (struct sockaddr
*)&un_addr
,
608 sizeof(struct sockaddr_un
));
617 _PUBLIC_
int swrap_getpeername(int s
, struct sockaddr
*name
, socklen_t
*addrlen
)
619 struct socket_info
*si
= find_socket_info(s
);
622 return real_getpeername(s
, name
, addrlen
);
631 memcpy(name
, si
->peername
, si
->peername_len
);
632 *addrlen
= si
->peername_len
;
637 _PUBLIC_
int swrap_getsockname(int s
, struct sockaddr
*name
, socklen_t
*addrlen
)
639 struct socket_info
*si
= find_socket_info(s
);
642 return real_getsockname(s
, name
, addrlen
);
645 memcpy(name
, si
->myname
, si
->myname_len
);
646 *addrlen
= si
->myname_len
;
651 _PUBLIC_
int swrap_getsockopt(int s
, int level
, int optname
, void *optval
, socklen_t
*optlen
)
653 struct socket_info
*si
= find_socket_info(s
);
656 return real_getsockopt(s
, level
, optname
, optval
, optlen
);
659 if (level
== SOL_SOCKET
) {
660 return real_getsockopt(s
, level
, optname
, optval
, optlen
);
663 switch (si
->domain
) {
665 return real_getsockopt(s
, level
, optname
, optval
, optlen
);
672 _PUBLIC_
int swrap_setsockopt(int s
, int level
, int optname
, const void *optval
, socklen_t optlen
)
674 struct socket_info
*si
= find_socket_info(s
);
677 return real_setsockopt(s
, level
, optname
, optval
, optlen
);
680 if (level
== SOL_SOCKET
) {
681 return real_setsockopt(s
, level
, optname
, optval
, optlen
);
684 switch (si
->domain
) {
686 return real_setsockopt(s
, level
, optname
, optval
, optlen
);
688 /* Silence some warnings */
690 if (optname
== TCP_NODELAY
)
699 _PUBLIC_ ssize_t
swrap_recvfrom(int s
, void *buf
, size_t len
, int flags
, struct sockaddr
*from
, socklen_t
*fromlen
)
701 struct sockaddr_un un_addr
;
702 socklen_t un_addrlen
= sizeof(un_addr
);
704 struct socket_info
*si
= find_socket_info(s
);
707 return real_recvfrom(s
, buf
, len
, flags
, from
, fromlen
);
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
);
716 if (sockaddr_convert_from_un(si
, &un_addr
, un_addrlen
,
717 si
->domain
, from
, fromlen
) == -1) {
725 _PUBLIC_ ssize_t
swrap_sendto(int s
, const void *buf
, size_t len
, int flags
, const struct sockaddr
*to
, socklen_t tolen
)
727 struct sockaddr_un un_addr
;
729 struct socket_info
*si
= find_socket_info(s
);
733 return real_sendto(s
, buf
, len
, flags
, to
, tolen
);
736 if (si
->bound
== 0 && si
->domain
!= AF_UNIX
) {
737 ret
= swrap_auto_bind(si
);
738 if (ret
== -1) return -1;
741 ret
= sockaddr_convert_to_un(si
, to
, tolen
, &un_addr
, 0, &bcast
);
742 if (ret
== -1) return -1;
747 unsigned int prt
= ntohs(((const struct sockaddr_in
*)to
)->sin_port
);
750 type
= SOCKET_TYPE_CHAR_UDP
;
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;
757 /* ignore the any errors in broadcast sends */
758 real_sendto(s
, buf
, len
, flags
, (struct sockaddr
*)&un_addr
, sizeof(un_addr
));
763 ret
= real_sendto(s
, buf
, len
, flags
, (struct sockaddr
*)&un_addr
, sizeof(un_addr
));
765 /* to give better errors */
766 if (to
->sa_family
== AF_INET
) {
767 if (ret
== -1 && errno
== ENOENT
) {
768 errno
= EHOSTUNREACH
;
775 _PUBLIC_
int swrap_close(int fd
)
777 struct socket_info
*si
= find_socket_info(fd
);
780 DLIST_REMOVE(sockets
, si
);
786 unlink(si
->tmp_path
);
792 return real_close(fd
);