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 fd
= real_socket(AF_UNIX
, type
, 0);
416 if (fd
== -1) return -1;
418 si
= calloc(1, sizeof(struct socket_info
));
422 si
->protocol
= protocol
;
425 DLIST_ADD(sockets
, si
);
430 _PUBLIC_
int swrap_accept(int s
, struct sockaddr
*addr
, socklen_t
*addrlen
)
432 struct socket_info
*parent_si
, *child_si
;
434 struct sockaddr_un un_addr
;
435 socklen_t un_addrlen
= sizeof(un_addr
);
436 struct sockaddr_un un_my_addr
;
437 socklen_t un_my_addrlen
= sizeof(un_my_addr
);
438 struct sockaddr my_addr
;
439 socklen_t my_addrlen
= sizeof(my_addr
);
442 parent_si
= find_socket_info(s
);
444 return real_accept(s
, addr
, addrlen
);
447 memset(&un_addr
, 0, sizeof(un_addr
));
448 memset(&un_my_addr
, 0, sizeof(un_my_addr
));
449 memset(&my_addr
, 0, sizeof(my_addr
));
451 ret
= real_accept(s
, (struct sockaddr
*)&un_addr
, &un_addrlen
);
452 if (ret
== -1) return ret
;
456 ret
= sockaddr_convert_from_un(parent_si
, &un_addr
, un_addrlen
,
457 parent_si
->domain
, addr
, addrlen
);
458 if (ret
== -1) return ret
;
460 child_si
= malloc(sizeof(struct socket_info
));
461 memset(child_si
, 0, sizeof(*child_si
));
464 child_si
->domain
= parent_si
->domain
;
465 child_si
->type
= parent_si
->type
;
466 child_si
->protocol
= parent_si
->protocol
;
469 ret
= real_getsockname(fd
, &un_my_addr
, &un_my_addrlen
);
470 if (ret
== -1) return ret
;
472 ret
= sockaddr_convert_from_un(child_si
, &un_my_addr
, un_my_addrlen
,
473 child_si
->domain
, &my_addr
, &my_addrlen
);
474 if (ret
== -1) return ret
;
476 child_si
->myname_len
= my_addrlen
;
477 child_si
->myname
= sockaddr_dup(&my_addr
, my_addrlen
);
479 child_si
->peername_len
= *addrlen
;
480 child_si
->peername
= sockaddr_dup(addr
, *addrlen
);
482 DLIST_ADD(sockets
, child_si
);
487 /* using sendto() or connect() on an unbound socket would give the
488 recipient no way to reply, as unlike UDP and TCP, a unix domain
489 socket can't auto-assign emphemeral port numbers, so we need to
491 static int swrap_auto_bind(struct socket_info
*si
)
493 struct sockaddr_un un_addr
;
494 struct sockaddr_in in
;
500 un_addr
.sun_family
= AF_UNIX
;
504 type
= SOCKET_TYPE_CHAR_TCP
;
507 type
= SOCKET_TYPE_CHAR_UDP
;
510 errno
= ESOCKTNOSUPPORT
;
514 for (i
=0;i
<1000;i
++) {
515 snprintf(un_addr
.sun_path
, sizeof(un_addr
.sun_path
),
516 "%s/"SOCKET_FORMAT
, socket_wrapper_dir(),
517 type
, socket_wrapper_default_iface(), i
+ 10000);
518 if (stat(un_addr
.sun_path
, &st
) == 0) continue;
520 ret
= real_bind(si
->fd
, (struct sockaddr
*)&un_addr
, sizeof(un_addr
));
521 if (ret
== -1) return ret
;
523 si
->tmp_path
= strdup(un_addr
.sun_path
);
532 memset(&in
, 0, sizeof(in
));
533 in
.sin_family
= AF_INET
;
534 in
.sin_port
= htons(i
);
535 in
.sin_addr
.s_addr
= htonl(127<<24 | socket_wrapper_default_iface());
537 si
->myname_len
= sizeof(in
);
538 si
->myname
= sockaddr_dup(&in
, si
->myname_len
);
544 _PUBLIC_
int swrap_connect(int s
, const struct sockaddr
*serv_addr
, socklen_t addrlen
)
547 struct sockaddr_un un_addr
;
548 struct socket_info
*si
= find_socket_info(s
);
551 return real_connect(s
, serv_addr
, addrlen
);
554 if (si
->bound
== 0 && si
->domain
!= AF_UNIX
) {
555 ret
= swrap_auto_bind(si
);
556 if (ret
== -1) return -1;
559 ret
= sockaddr_convert_to_un(si
, (const struct sockaddr
*)serv_addr
, addrlen
, &un_addr
, 0, NULL
);
560 if (ret
== -1) return -1;
562 ret
= real_connect(s
, (struct sockaddr
*)&un_addr
,
563 sizeof(struct sockaddr_un
));
565 /* to give better errors */
566 if (serv_addr
->sa_family
== AF_INET
) {
567 if (ret
== -1 && errno
== ENOENT
) {
568 errno
= EHOSTUNREACH
;
573 si
->peername_len
= addrlen
;
574 si
->peername
= sockaddr_dup(serv_addr
, addrlen
);
580 _PUBLIC_
int swrap_bind(int s
, const struct sockaddr
*myaddr
, socklen_t addrlen
)
583 struct sockaddr_un un_addr
;
584 struct socket_info
*si
= find_socket_info(s
);
587 return real_bind(s
, myaddr
, addrlen
);
590 si
->myname_len
= addrlen
;
591 si
->myname
= sockaddr_dup(myaddr
, addrlen
);
593 ret
= sockaddr_convert_to_un(si
, (const struct sockaddr
*)myaddr
, addrlen
, &un_addr
, 1, &si
->bcast
);
594 if (ret
== -1) return -1;
596 unlink(un_addr
.sun_path
);
598 ret
= real_bind(s
, (struct sockaddr
*)&un_addr
,
599 sizeof(struct sockaddr_un
));
608 _PUBLIC_
int swrap_getpeername(int s
, struct sockaddr
*name
, socklen_t
*addrlen
)
610 struct socket_info
*si
= find_socket_info(s
);
613 return real_getpeername(s
, name
, addrlen
);
622 memcpy(name
, si
->peername
, si
->peername_len
);
623 *addrlen
= si
->peername_len
;
628 _PUBLIC_
int swrap_getsockname(int s
, struct sockaddr
*name
, socklen_t
*addrlen
)
630 struct socket_info
*si
= find_socket_info(s
);
633 return real_getsockname(s
, name
, addrlen
);
636 memcpy(name
, si
->myname
, si
->myname_len
);
637 *addrlen
= si
->myname_len
;
642 _PUBLIC_
int swrap_getsockopt(int s
, int level
, int optname
, void *optval
, socklen_t
*optlen
)
644 struct socket_info
*si
= find_socket_info(s
);
647 return real_getsockopt(s
, level
, optname
, optval
, optlen
);
650 if (level
== SOL_SOCKET
) {
651 return real_getsockopt(s
, level
, optname
, optval
, optlen
);
654 switch (si
->domain
) {
656 return real_getsockopt(s
, level
, optname
, optval
, optlen
);
663 _PUBLIC_
int swrap_setsockopt(int s
, int level
, int optname
, const void *optval
, socklen_t optlen
)
665 struct socket_info
*si
= find_socket_info(s
);
668 return real_setsockopt(s
, level
, optname
, optval
, optlen
);
671 if (level
== SOL_SOCKET
) {
672 return real_setsockopt(s
, level
, optname
, optval
, optlen
);
675 switch (si
->domain
) {
677 return real_setsockopt(s
, level
, optname
, optval
, optlen
);
679 /* Silence some warnings */
681 if (optname
== TCP_NODELAY
)
690 _PUBLIC_ ssize_t
swrap_recvfrom(int s
, void *buf
, size_t len
, int flags
, struct sockaddr
*from
, socklen_t
*fromlen
)
692 struct sockaddr_un un_addr
;
693 socklen_t un_addrlen
= sizeof(un_addr
);
695 struct socket_info
*si
= find_socket_info(s
);
698 return real_recvfrom(s
, buf
, len
, flags
, from
, fromlen
);
701 /* irix 6.4 forgets to null terminate the sun_path string :-( */
702 memset(&un_addr
, 0, sizeof(un_addr
));
703 ret
= real_recvfrom(s
, buf
, len
, flags
, (struct sockaddr
*)&un_addr
, &un_addrlen
);
707 if (sockaddr_convert_from_un(si
, &un_addr
, un_addrlen
,
708 si
->domain
, from
, fromlen
) == -1) {
716 _PUBLIC_ ssize_t
swrap_sendto(int s
, const void *buf
, size_t len
, int flags
, const struct sockaddr
*to
, socklen_t tolen
)
718 struct sockaddr_un un_addr
;
720 struct socket_info
*si
= find_socket_info(s
);
724 return real_sendto(s
, buf
, len
, flags
, to
, tolen
);
727 if (si
->bound
== 0 && si
->domain
!= AF_UNIX
) {
728 ret
= swrap_auto_bind(si
);
729 if (ret
== -1) return -1;
732 ret
= sockaddr_convert_to_un(si
, to
, tolen
, &un_addr
, 0, &bcast
);
733 if (ret
== -1) return -1;
738 unsigned int prt
= ntohs(((const struct sockaddr_in
*)to
)->sin_port
);
741 type
= SOCKET_TYPE_CHAR_UDP
;
743 for(iface
=0; iface
<= 0xFF; iface
++) {
744 snprintf(un_addr
.sun_path
, sizeof(un_addr
.sun_path
), "%s/"SOCKET_FORMAT
,
745 socket_wrapper_dir(), type
, iface
, prt
);
746 if (stat(un_addr
.sun_path
, &st
) != 0) continue;
748 /* ignore the any errors in broadcast sends */
749 real_sendto(s
, buf
, len
, flags
, (struct sockaddr
*)&un_addr
, sizeof(un_addr
));
754 ret
= real_sendto(s
, buf
, len
, flags
, (struct sockaddr
*)&un_addr
, sizeof(un_addr
));
756 /* to give better errors */
757 if (to
->sa_family
== AF_INET
) {
758 if (ret
== -1 && errno
== ENOENT
) {
759 errno
= EHOSTUNREACH
;
766 _PUBLIC_
int swrap_close(int fd
)
768 struct socket_info
*si
= find_socket_info(fd
);
771 DLIST_REMOVE(sockets
, si
);
777 unlink(si
->tmp_path
);
783 return real_close(fd
);