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.
25 #include "system/network.h"
26 #include "system/filesys.h"
28 #include <sys/types.h>
30 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <netinet/tcp.h>
40 #include "lib/util/dlinklist.h"
42 /* LD_PRELOAD doesn't work yet, so REWRITE_CALLS is all we support
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_recv recv
57 #define real_send send
58 #define real_socket socket
59 #define real_close close
62 /* we need to use a very terse format here as IRIX 6.4 silently
63 truncates names to 16 chars, so if we use a longer name then we
64 can't tell which port a packet came from with recvfrom()
66 with this format we have 8 chars left for the directory name
68 #define SOCKET_FORMAT "%c%02X%04X"
69 #define SOCKET_TYPE_CHAR_TCP 'T'
70 #define SOCKET_TYPE_CHAR_UDP 'U'
72 static struct sockaddr *sockaddr_dup(const void *data, socklen_t len)
74 struct sockaddr *ret = (struct sockaddr *)malloc(len);
75 memcpy(ret, data, len);
92 struct sockaddr *myname;
95 struct sockaddr *peername;
96 socklen_t peername_len;
98 struct socket_info *prev, *next;
101 static struct socket_info *sockets = NULL;
104 static const char *socket_wrapper_dir(void)
106 const char *s = getenv("SOCKET_WRAPPER_DIR");
110 if (strncmp(s, "./", 2) == 0) {
116 static const char *socket_wrapper_dump_dir(void)
118 const char *s = getenv("SOCKET_WRAPPER_DUMP_DIR");
120 if (!socket_wrapper_dir()) {
127 if (strncmp(s, "./", 2) == 0) {
133 static unsigned int socket_wrapper_default_iface(void)
135 const char *s = getenv("SOCKET_WRAPPER_DEFAULT_IFACE");
138 if (sscanf(s, "%u", &iface) == 1) {
139 if (iface >= 1 && iface <= 0xFF) {
145 return 1;/* 127.0.0.1 */
148 static int convert_un_in(const struct sockaddr_un *un, struct sockaddr_in *in, socklen_t *len)
155 if ((*len) < sizeof(struct sockaddr_in)) {
159 p = strrchr(un->sun_path, '/');
160 if (p) p++; else p = un->sun_path;
162 if (sscanf(p, SOCKET_FORMAT, &type, &iface, &prt) != 3) {
167 if (type != SOCKET_TYPE_CHAR_TCP && type != SOCKET_TYPE_CHAR_UDP) {
172 if (iface == 0 || iface > 0xFF) {
182 in->sin_family = AF_INET;
183 in->sin_addr.s_addr = htonl((127<<24) | iface);
184 in->sin_port = htons(prt);
186 *len = sizeof(struct sockaddr_in);
190 static int convert_in_un_remote(struct socket_info *si, const struct sockaddr_in *in, struct sockaddr_un *un,
197 unsigned int addr= ntohl(in->sin_addr.s_addr);
198 unsigned int prt = ntohs(in->sin_port);
202 if (bcast) *bcast = 0;
211 u_type = SOCKET_TYPE_CHAR_TCP;
214 u_type = SOCKET_TYPE_CHAR_UDP;
215 a_type = SOCKET_TYPE_CHAR_UDP;
216 b_type = SOCKET_TYPE_CHAR_UDP;
220 if (a_type && addr == 0xFFFFFFFF) {
221 /* 255.255.255.255 only udp */
224 iface = socket_wrapper_default_iface();
225 } else if (b_type && addr == 0x7FFFFFFF) {
226 /* 127.255.255.255 only udp */
229 iface = socket_wrapper_default_iface();
230 } else if ((addr & 0xFFFFFF00) == 0x7F000000) {
234 iface = (addr & 0x000000FF);
240 if (bcast) *bcast = is_bcast;
243 snprintf(un->sun_path, sizeof(un->sun_path), "%s/EINVAL",
244 socket_wrapper_dir());
245 /* the caller need to do more processing */
249 snprintf(un->sun_path, sizeof(un->sun_path), "%s/"SOCKET_FORMAT,
250 socket_wrapper_dir(), type, iface, prt);
255 static int convert_in_un_alloc(struct socket_info *si, const struct sockaddr_in *in, struct sockaddr_un *un,
263 unsigned int addr= ntohl(in->sin_addr.s_addr);
264 unsigned int prt = ntohs(in->sin_port);
269 if (bcast) *bcast = 0;
273 u_type = SOCKET_TYPE_CHAR_TCP;
274 d_type = SOCKET_TYPE_CHAR_TCP;
277 u_type = SOCKET_TYPE_CHAR_UDP;
278 d_type = SOCKET_TYPE_CHAR_UDP;
279 a_type = SOCKET_TYPE_CHAR_UDP;
280 b_type = SOCKET_TYPE_CHAR_UDP;
288 iface = socket_wrapper_default_iface();
289 } else if (a_type && addr == 0xFFFFFFFF) {
290 /* 255.255.255.255 only udp */
293 iface = socket_wrapper_default_iface();
294 } else if (b_type && addr == 0x7FFFFFFF) {
295 /* 127.255.255.255 only udp */
298 iface = socket_wrapper_default_iface();
299 } else if ((addr & 0xFFFFFF00) == 0x7F000000) {
303 iface = (addr & 0x000000FF);
305 errno = EADDRNOTAVAIL;
309 if (bcast) *bcast = is_bcast;
312 /* handle auto-allocation of ephemeral ports */
313 for (prt = 5001; prt < 10000; prt++) {
314 snprintf(un->sun_path, sizeof(un->sun_path), "%s/"SOCKET_FORMAT,
315 socket_wrapper_dir(), type, iface, prt);
316 if (stat(un->sun_path, &st) == 0) continue;
318 ((struct sockaddr_in *)si->myname)->sin_port = htons(prt);
325 snprintf(un->sun_path, sizeof(un->sun_path), "%s/"SOCKET_FORMAT,
326 socket_wrapper_dir(), type, iface, prt);
330 static struct socket_info *find_socket_info(int fd)
332 struct socket_info *i;
333 for (i = sockets; i; i = i->next) {
341 static int sockaddr_convert_to_un(struct socket_info *si, const struct sockaddr *in_addr, socklen_t in_len,
342 struct sockaddr_un *out_addr, int alloc_sock, int *bcast)
347 out_addr->sun_family = AF_UNIX;
349 switch (in_addr->sa_family) {
356 errno = ESOCKTNOSUPPORT;
360 return convert_in_un_alloc(si, (const struct sockaddr_in *)in_addr, out_addr, bcast);
362 return convert_in_un_remote(si, (const struct sockaddr_in *)in_addr, out_addr, bcast);
368 errno = EAFNOSUPPORT;
372 static int sockaddr_convert_from_un(const struct socket_info *si,
373 const struct sockaddr_un *in_addr,
374 socklen_t un_addrlen,
376 struct sockaddr *out_addr,
377 socklen_t *_out_addrlen)
379 socklen_t out_addrlen;
381 if (out_addr == NULL || _out_addrlen == NULL)
384 if (un_addrlen == 0) {
389 out_addrlen = *_out_addrlen;
390 if (out_addrlen > un_addrlen) {
391 out_addrlen = un_addrlen;
401 errno = ESOCKTNOSUPPORT;
404 return convert_un_in(in_addr, (struct sockaddr_in *)out_addr, _out_addrlen);
409 errno = EAFNOSUPPORT;
413 enum swrap_packet_type {
423 static void swrap_dump_packet(struct socket_info *si, const struct sockaddr *addr,
424 enum swrap_packet_type type,
425 const void *buf, size_t len, ssize_t ret)
427 if (!socket_wrapper_dump_dir()) {
433 _PUBLIC_ int swrap_socket(int family, int type, int protocol)
435 struct socket_info *si;
438 if (!socket_wrapper_dir()) {
439 return real_socket(family, type, protocol);
446 return real_socket(family, type, protocol);
448 errno = EAFNOSUPPORT;
452 fd = real_socket(AF_UNIX, type, 0);
454 if (fd == -1) return -1;
456 si = calloc(1, sizeof(struct socket_info));
460 si->protocol = protocol;
463 DLIST_ADD(sockets, si);
468 _PUBLIC_ int swrap_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
470 struct socket_info *parent_si, *child_si;
472 struct sockaddr_un un_addr;
473 socklen_t un_addrlen = sizeof(un_addr);
474 struct sockaddr_un un_my_addr;
475 socklen_t un_my_addrlen = sizeof(un_my_addr);
476 struct sockaddr my_addr;
477 socklen_t my_addrlen = sizeof(my_addr);
480 parent_si = find_socket_info(s);
482 return real_accept(s, addr, addrlen);
485 memset(&un_addr, 0, sizeof(un_addr));
486 memset(&un_my_addr, 0, sizeof(un_my_addr));
487 memset(&my_addr, 0, sizeof(my_addr));
489 ret = real_accept(s, (struct sockaddr *)&un_addr, &un_addrlen);
490 if (ret == -1) return ret;
494 ret = sockaddr_convert_from_un(parent_si, &un_addr, un_addrlen,
495 parent_si->family, addr, addrlen);
496 if (ret == -1) return ret;
498 child_si = malloc(sizeof(struct socket_info));
499 memset(child_si, 0, sizeof(*child_si));
502 child_si->family = parent_si->family;
503 child_si->type = parent_si->type;
504 child_si->protocol = parent_si->protocol;
507 ret = real_getsockname(fd, (struct sockaddr *)&un_my_addr, &un_my_addrlen);
508 if (ret == -1) return ret;
510 ret = sockaddr_convert_from_un(child_si, &un_my_addr, un_my_addrlen,
511 child_si->family, &my_addr, &my_addrlen);
512 if (ret == -1) return ret;
514 child_si->myname_len = my_addrlen;
515 child_si->myname = sockaddr_dup(&my_addr, my_addrlen);
517 child_si->peername_len = *addrlen;
518 child_si->peername = sockaddr_dup(addr, *addrlen);
520 DLIST_ADD(sockets, child_si);
522 swrap_dump_packet(child_si, addr, SWRAP_ACCEPT, NULL, 0, 0);
527 /* using sendto() or connect() on an unbound socket would give the
528 recipient no way to reply, as unlike UDP and TCP, a unix domain
529 socket can't auto-assign emphemeral port numbers, so we need to
531 static int swrap_auto_bind(struct socket_info *si)
533 struct sockaddr_un un_addr;
534 struct sockaddr_in in;
540 un_addr.sun_family = AF_UNIX;
544 type = SOCKET_TYPE_CHAR_TCP;
547 type = SOCKET_TYPE_CHAR_UDP;
550 errno = ESOCKTNOSUPPORT;
554 for (i=0;i<1000;i++) {
555 snprintf(un_addr.sun_path, sizeof(un_addr.sun_path),
556 "%s/"SOCKET_FORMAT, socket_wrapper_dir(),
557 type, socket_wrapper_default_iface(), i + 10000);
558 if (stat(un_addr.sun_path, &st) == 0) continue;
560 ret = real_bind(si->fd, (struct sockaddr *)&un_addr, sizeof(un_addr));
561 if (ret == -1) return ret;
563 si->tmp_path = strdup(un_addr.sun_path);
572 memset(&in, 0, sizeof(in));
573 in.sin_family = AF_INET;
574 in.sin_port = htons(i);
575 in.sin_addr.s_addr = htonl(127<<24 | socket_wrapper_default_iface());
577 si->myname_len = sizeof(in);
578 si->myname = sockaddr_dup(&in, si->myname_len);
584 _PUBLIC_ int swrap_connect(int s, const struct sockaddr *serv_addr, socklen_t addrlen)
587 struct sockaddr_un un_addr;
588 struct socket_info *si = find_socket_info(s);
591 return real_connect(s, serv_addr, addrlen);
594 if (si->bound == 0) {
595 ret = swrap_auto_bind(si);
596 if (ret == -1) return -1;
599 ret = sockaddr_convert_to_un(si, (const struct sockaddr *)serv_addr, addrlen, &un_addr, 0, NULL);
600 if (ret == -1) return -1;
602 ret = real_connect(s, (struct sockaddr *)&un_addr,
603 sizeof(struct sockaddr_un));
605 /* to give better errors */
606 if (ret == -1 && errno == ENOENT) {
607 errno = EHOSTUNREACH;
611 si->peername_len = addrlen;
612 si->peername = sockaddr_dup(serv_addr, addrlen);
615 swrap_dump_packet(si, serv_addr, SWRAP_CONNECT, NULL, 0, ret);
620 _PUBLIC_ int swrap_bind(int s, const struct sockaddr *myaddr, socklen_t addrlen)
623 struct sockaddr_un un_addr;
624 struct socket_info *si = find_socket_info(s);
627 return real_bind(s, myaddr, addrlen);
630 si->myname_len = addrlen;
631 si->myname = sockaddr_dup(myaddr, addrlen);
633 ret = sockaddr_convert_to_un(si, (const struct sockaddr *)myaddr, addrlen, &un_addr, 1, &si->bcast);
634 if (ret == -1) return -1;
636 unlink(un_addr.sun_path);
638 ret = real_bind(s, (struct sockaddr *)&un_addr,
639 sizeof(struct sockaddr_un));
648 _PUBLIC_ int swrap_getpeername(int s, struct sockaddr *name, socklen_t *addrlen)
650 struct socket_info *si = find_socket_info(s);
653 return real_getpeername(s, name, addrlen);
662 memcpy(name, si->peername, si->peername_len);
663 *addrlen = si->peername_len;
668 _PUBLIC_ int swrap_getsockname(int s, struct sockaddr *name, socklen_t *addrlen)
670 struct socket_info *si = find_socket_info(s);
673 return real_getsockname(s, name, addrlen);
676 memcpy(name, si->myname, si->myname_len);
677 *addrlen = si->myname_len;
682 _PUBLIC_ int swrap_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
684 struct socket_info *si = find_socket_info(s);
687 return real_getsockopt(s, level, optname, optval, optlen);
690 if (level == SOL_SOCKET) {
691 return real_getsockopt(s, level, optname, optval, optlen);
698 _PUBLIC_ int swrap_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen)
700 struct socket_info *si = find_socket_info(s);
703 return real_setsockopt(s, level, optname, optval, optlen);
706 if (level == SOL_SOCKET) {
707 return real_setsockopt(s, level, optname, optval, optlen);
710 switch (si->family) {
719 _PUBLIC_ ssize_t swrap_recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen)
721 struct sockaddr_un un_addr;
722 socklen_t un_addrlen = sizeof(un_addr);
724 struct socket_info *si = find_socket_info(s);
727 return real_recvfrom(s, buf, len, flags, from, fromlen);
730 /* irix 6.4 forgets to null terminate the sun_path string :-( */
731 memset(&un_addr, 0, sizeof(un_addr));
732 ret = real_recvfrom(s, buf, len, flags, (struct sockaddr *)&un_addr, &un_addrlen);
736 if (sockaddr_convert_from_un(si, &un_addr, un_addrlen,
737 si->family, from, fromlen) == -1) {
741 swrap_dump_packet(si, from, SWRAP_RECVFROM, buf, len, ret);
747 _PUBLIC_ ssize_t swrap_sendto(int s, const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen)
749 struct sockaddr_un un_addr;
751 struct socket_info *si = find_socket_info(s);
755 return real_sendto(s, buf, len, flags, to, tolen);
758 if (si->bound == 0) {
759 ret = swrap_auto_bind(si);
760 if (ret == -1) return -1;
763 ret = sockaddr_convert_to_un(si, to, tolen, &un_addr, 0, &bcast);
764 if (ret == -1) return -1;
769 unsigned int prt = ntohs(((const struct sockaddr_in *)to)->sin_port);
772 type = SOCKET_TYPE_CHAR_UDP;
774 for(iface=0; iface <= 0xFF; iface++) {
775 snprintf(un_addr.sun_path, sizeof(un_addr.sun_path), "%s/"SOCKET_FORMAT,
776 socket_wrapper_dir(), type, iface, prt);
777 if (stat(un_addr.sun_path, &st) != 0) continue;
779 /* ignore the any errors in broadcast sends */
780 real_sendto(s, buf, len, flags, (struct sockaddr *)&un_addr, sizeof(un_addr));
783 swrap_dump_packet(si, to, SWRAP_SENDTO, buf, len, len);
788 ret = real_sendto(s, buf, len, flags, (struct sockaddr *)&un_addr, sizeof(un_addr));
790 /* to give better errors */
791 if (ret == -1 && errno == ENOENT) {
792 errno = EHOSTUNREACH;
795 swrap_dump_packet(si, to, SWRAP_SENDTO, buf, len, ret);
800 _PUBLIC_ ssize_t swrap_recv(int s, void *buf, size_t len, int flags)
803 struct socket_info *si = find_socket_info(s);
806 return real_recv(s, buf, len, flags);
809 ret = real_recv(s, buf, len, flags);
813 swrap_dump_packet(si, NULL, SWRAP_RECV, buf, len, ret);
819 _PUBLIC_ ssize_t swrap_send(int s, const void *buf, size_t len, int flags)
822 struct socket_info *si = find_socket_info(s);
825 return real_send(s, buf, len, flags);
828 ret = real_send(s, buf, len, flags);
832 swrap_dump_packet(si, NULL, SWRAP_SEND, buf, len, ret);
837 _PUBLIC_ int swrap_close(int fd)
839 struct socket_info *si = find_socket_info(fd);
842 DLIST_REMOVE(sockets, si);
844 swrap_dump_packet(si, NULL, SWRAP_CLOSE, NULL, 0, 0);
850 unlink(si->tmp_path);
856 return real_close(fd);