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.
24 #ifdef SOCKET_WRAPPER_REPLACE
38 /* LD_PRELOAD doesn't work yet, so REWRITE_CALLS is all we support
43 #define real_accept accept
44 #define real_connect connect
45 #define real_bind bind
46 #define real_getpeername getpeername
47 #define real_getsockname getsockname
48 #define real_getsockopt getsockopt
49 #define real_setsockopt setsockopt
50 #define real_recvfrom recvfrom
51 #define real_sendto sendto
52 #define real_socket socket
53 #define real_close close
59 /* we need to use a very terse format here as IRIX 6.4 silently
60 truncates names to 16 chars, so if we use a longer name then we
61 can't tell which port a packet came from with recvfrom()
63 with this format we have 8 chars left for the directory name
65 #define SOCKET_FORMAT "%u_%u"
67 static struct sockaddr
*sockaddr_dup(const void *data
, socklen_t len
)
69 struct sockaddr
*ret
= (struct sockaddr
*)malloc(len
);
70 memcpy(ret
, data
, len
);
86 struct sockaddr
*myname
;
89 struct sockaddr
*peername
;
90 socklen_t peername_len
;
92 struct socket_info
*prev
, *next
;
95 static struct socket_info
*sockets
= NULL
;
98 static const char *socket_wrapper_dir(void)
100 const char *s
= getenv("SOCKET_WRAPPER_DIR");
104 if (strncmp(s
, "./", 2) == 0) {
110 static int convert_un_in(const struct sockaddr_un
*un
, struct sockaddr_in
*in
, socklen_t
*len
)
116 if ((*len
) < sizeof(struct sockaddr_in
)) {
120 in
->sin_family
= AF_INET
;
121 in
->sin_port
= htons(1025); /* Default to 1025 */
122 p
= strrchr(un
->sun_path
, '/');
123 if (p
) p
++; else p
= un
->sun_path
;
125 if (sscanf(p
, SOCKET_FORMAT
, &type
, &prt
) == 2) {
126 in
->sin_port
= htons(prt
);
128 in
->sin_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
129 *len
= sizeof(struct sockaddr_in
);
133 static int convert_in_un(struct socket_info
*si
, const struct sockaddr_in
*in
, struct sockaddr_un
*un
)
136 uint16_t prt
= ntohs(in
->sin_port
);
139 /* handle auto-allocation of ephemeral ports */
142 snprintf(un
->sun_path
, sizeof(un
->sun_path
), "%s/"SOCKET_FORMAT
,
143 socket_wrapper_dir(), type
, ++prt
);
144 } while (stat(un
->sun_path
, &st
) == 0 && prt
< 10000);
145 ((struct sockaddr_in
*)si
->myname
)->sin_port
= htons(prt
);
147 snprintf(un
->sun_path
, sizeof(un
->sun_path
), "%s/"SOCKET_FORMAT
,
148 socket_wrapper_dir(), type
, prt
);
152 static struct socket_info
*find_socket_info(int fd
)
154 struct socket_info
*i
;
155 for (i
= sockets
; i
; i
= i
->next
) {
163 static int sockaddr_convert_to_un(struct socket_info
*si
, const struct sockaddr
*in_addr
, socklen_t in_len
,
164 struct sockaddr_un
*out_addr
)
169 out_addr
->sun_family
= AF_UNIX
;
171 switch (in_addr
->sa_family
) {
173 return convert_in_un(si
, (const struct sockaddr_in
*)in_addr
, out_addr
);
175 memcpy(out_addr
, in_addr
, sizeof(*out_addr
));
181 errno
= EAFNOSUPPORT
;
185 static int sockaddr_convert_from_un(const struct socket_info
*si
,
186 const struct sockaddr_un
*in_addr
,
187 socklen_t un_addrlen
,
189 struct sockaddr
*out_addr
,
192 if (out_addr
== NULL
|| out_len
== NULL
)
195 if (un_addrlen
== 0) {
202 return convert_un_in(in_addr
, (struct sockaddr_in
*)out_addr
, out_len
);
204 memcpy(out_addr
, in_addr
, sizeof(*in_addr
));
205 *out_len
= sizeof(*in_addr
);
211 errno
= EAFNOSUPPORT
;
215 int swrap_socket(int domain
, int type
, int protocol
)
217 struct socket_info
*si
;
220 if (!socket_wrapper_dir()) {
221 return real_socket(domain
, type
, protocol
);
224 fd
= real_socket(AF_UNIX
, type
, 0);
226 if (fd
== -1) return -1;
228 si
= calloc(1, sizeof(struct socket_info
));
232 si
->protocol
= protocol
;
235 DLIST_ADD(sockets
, si
);
240 int swrap_accept(int s
, struct sockaddr
*addr
, socklen_t
*addrlen
)
242 struct socket_info
*parent_si
, *child_si
;
244 socklen_t un_addrlen
= sizeof(struct sockaddr_un
);
245 struct sockaddr_un un_addr
;
248 parent_si
= find_socket_info(s
);
250 return real_accept(s
, addr
, addrlen
);
253 memset(&un_addr
, 0, sizeof(un_addr
));
255 ret
= real_accept(s
, (struct sockaddr
*)&un_addr
, &un_addrlen
);
256 if (ret
== -1) return ret
;
260 ret
= sockaddr_convert_from_un(parent_si
, &un_addr
, un_addrlen
,
261 parent_si
->domain
, addr
, addrlen
);
262 if (ret
== -1) return ret
;
264 child_si
= malloc(sizeof(struct socket_info
));
265 memset(child_si
, 0, sizeof(*child_si
));
270 child_si
->myname_len
= parent_si
->myname_len
;
271 child_si
->myname
= sockaddr_dup(parent_si
->myname
, parent_si
->myname_len
);
273 child_si
->peername_len
= *addrlen
;
274 child_si
->peername
= sockaddr_dup(addr
, *addrlen
);
276 DLIST_ADD(sockets
, child_si
);
281 /* using sendto() or connect() on an unbound socket would give the
282 recipient no way to reply, as unlike UDP and TCP, a unix domain
283 socket can't auto-assign emphemeral port numbers, so we need to
285 static int swrap_auto_bind(struct socket_info
*si
)
287 struct sockaddr_un un_addr
;
288 struct sockaddr_in in
;
291 un_addr
.sun_family
= AF_UNIX
;
293 for (i
=0;i
<1000;i
++) {
294 snprintf(un_addr
.sun_path
, sizeof(un_addr
.sun_path
),
295 "%s/"SOCKET_FORMAT
, socket_wrapper_dir(),
296 SOCK_DGRAM
, i
+ 10000);
297 if (bind(si
->fd
, (struct sockaddr
*)&un_addr
,
298 sizeof(un_addr
)) == 0) {
299 si
->tmp_path
= strdup(un_addr
.sun_path
);
308 memset(&in
, 0, sizeof(in
));
309 in
.sin_family
= AF_INET
;
310 in
.sin_port
= htons(i
);
311 in
.sin_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
313 si
->myname_len
= sizeof(in
);
314 si
->myname
= sockaddr_dup(&in
, si
->myname_len
);
320 int swrap_connect(int s
, const struct sockaddr
*serv_addr
, socklen_t addrlen
)
323 struct sockaddr_un un_addr
;
324 struct socket_info
*si
= find_socket_info(s
);
327 return real_connect(s
, serv_addr
, addrlen
);
330 /* only allow pseudo loopback connections */
331 if (serv_addr
->sa_family
== AF_INET
&&
332 ((const struct sockaddr_in
*)serv_addr
)->sin_addr
.s_addr
!=
333 htonl(INADDR_LOOPBACK
)) {
338 if (si
->bound
== 0 && si
->domain
!= AF_UNIX
) {
339 ret
= swrap_auto_bind(si
);
340 if (ret
== -1) return -1;
343 ret
= sockaddr_convert_to_un(si
, (const struct sockaddr
*)serv_addr
, addrlen
, &un_addr
);
344 if (ret
== -1) return -1;
346 ret
= real_connect(s
, (struct sockaddr
*)&un_addr
,
347 sizeof(struct sockaddr_un
));
350 si
->peername_len
= addrlen
;
351 si
->peername
= sockaddr_dup(serv_addr
, addrlen
);
357 int swrap_bind(int s
, const struct sockaddr
*myaddr
, socklen_t addrlen
)
360 struct sockaddr_un un_addr
;
361 struct socket_info
*si
= find_socket_info(s
);
364 return real_bind(s
, myaddr
, addrlen
);
367 si
->myname_len
= addrlen
;
368 si
->myname
= sockaddr_dup(myaddr
, addrlen
);
370 if (myaddr
->sa_family
== AF_INET
&&
371 ((const struct sockaddr_in
*)myaddr
)->sin_addr
.s_addr
== 0) {
372 ((struct sockaddr_in
*)si
->myname
)->sin_addr
.s_addr
=
373 htonl(INADDR_LOOPBACK
);
375 ret
= sockaddr_convert_to_un(si
, (const struct sockaddr
*)myaddr
, addrlen
, &un_addr
);
376 if (ret
== -1) return -1;
378 unlink(un_addr
.sun_path
);
380 ret
= real_bind(s
, (struct sockaddr
*)&un_addr
,
381 sizeof(struct sockaddr_un
));
390 int swrap_getpeername(int s
, struct sockaddr
*name
, socklen_t
*addrlen
)
392 struct socket_info
*si
= find_socket_info(s
);
395 return real_getpeername(s
, name
, addrlen
);
404 memcpy(name
, si
->peername
, si
->peername_len
);
405 *addrlen
= si
->peername_len
;
410 int swrap_getsockname(int s
, struct sockaddr
*name
, socklen_t
*addrlen
)
412 struct socket_info
*si
= find_socket_info(s
);
415 return real_getsockname(s
, name
, addrlen
);
418 memcpy(name
, si
->myname
, si
->myname_len
);
419 *addrlen
= si
->myname_len
;
424 int swrap_getsockopt(int s
, int level
, int optname
, void *optval
, socklen_t
*optlen
)
426 struct socket_info
*si
= find_socket_info(s
);
429 return real_getsockopt(s
, level
, optname
, optval
, optlen
);
432 if (level
== SOL_SOCKET
) {
433 return real_getsockopt(s
, level
, optname
, optval
, optlen
);
436 switch (si
->domain
) {
438 return real_getsockopt(s
, level
, optname
, optval
, optlen
);
445 int swrap_setsockopt(int s
, int level
, int optname
, const void *optval
, socklen_t optlen
)
447 struct socket_info
*si
= find_socket_info(s
);
450 return real_setsockopt(s
, level
, optname
, optval
, optlen
);
453 if (level
== SOL_SOCKET
) {
454 return real_setsockopt(s
, level
, optname
, optval
, optlen
);
457 switch (si
->domain
) {
459 return real_setsockopt(s
, level
, optname
, optval
, optlen
);
461 /* Silence some warnings */
463 if (optname
== TCP_NODELAY
)
472 ssize_t
swrap_recvfrom(int s
, void *buf
, size_t len
, int flags
, struct sockaddr
*from
, socklen_t
*fromlen
)
474 struct sockaddr_un un_addr
;
475 socklen_t un_addrlen
= sizeof(un_addr
);
477 struct socket_info
*si
= find_socket_info(s
);
480 return real_recvfrom(s
, buf
, len
, flags
, from
, fromlen
);
483 /* irix 6.4 forgets to null terminate the sun_path string :-( */
484 memset(&un_addr
, 0, sizeof(un_addr
));
485 ret
= real_recvfrom(s
, buf
, len
, flags
, (struct sockaddr
*)&un_addr
, &un_addrlen
);
489 if (sockaddr_convert_from_un(si
, &un_addr
, un_addrlen
,
490 si
->domain
, from
, fromlen
) == -1) {
498 ssize_t
swrap_sendto(int s
, const void *buf
, size_t len
, int flags
, const struct sockaddr
*to
, socklen_t tolen
)
500 struct sockaddr_un un_addr
;
502 struct socket_info
*si
= find_socket_info(s
);
505 return real_sendto(s
, buf
, len
, flags
, to
, tolen
);
508 if (si
->bound
== 0 && si
->domain
!= AF_UNIX
) {
509 ret
= swrap_auto_bind(si
);
510 if (ret
== -1) return -1;
513 ret
= sockaddr_convert_to_un(si
, to
, tolen
, &un_addr
);
514 if (ret
== -1) return -1;
516 ret
= real_sendto(s
, buf
, len
, flags
, (struct sockaddr
*)&un_addr
, sizeof(un_addr
));
521 int swrap_close(int fd
)
523 struct socket_info
*si
= find_socket_info(fd
);
526 DLIST_REMOVE(sockets
, si
);
532 unlink(si
->tmp_path
);
538 return real_close(fd
);