2 * Simple LD_PRELOAD wrapper to "convert" network sockets to UNIX sockets;
3 * works for clients and servers. See README for details.
5 * Copyright (C) 2013 Simon Ruderich
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 3 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, see <http://www.gnu.org/licenses/>.
22 /* Necessary for RTLD_NEXT. */
30 #include <netinet/in.h>
31 #include <netinet/ip.h>
36 #include <sys/socket.h>
38 #include <sys/types.h>
45 #define LOG_LEVEL_ERROR 1
46 #define LOG_LEVEL_WARNING 2
47 #define LOG_LEVEL_DEBUG 3
48 #define LOG_LEVEL_MASK LOG_LEVEL_DEBUG
50 #define LOG_LEVEL_PERROR 42
53 /* GLOBAL VARIABLES */
64 static struct list socket_list = {
65 .unix_sockfd = -1, /* must not match a valid sockfd */
69 /* LOG FUNCTIONS/MACROS */
71 static int get_log_level(void);
73 static void log_helper(int action, const char *file, int line, const char *format, va_list ap) {
74 int saved_errno = errno;
78 log_level = get_log_level();
81 int level = action & LOG_LEVEL_MASK;
82 if (level > log_level) {
87 if (level == LOG_LEVEL_DEBUG) {
89 } else if (level == LOG_LEVEL_WARNING) {
91 } else if (level == LOG_LEVEL_ERROR) {
97 /* Prevent other threads from interrupting the printf()s. */
100 fprintf(stderr, "socket2unix [%s] ", prefix);
101 fprintf(stderr, "[%s:%3d] ", file, line);
102 vfprintf(stderr, format, ap);
104 if ((action & ~LOG_LEVEL_MASK) == LOG_LEVEL_PERROR) {
105 fprintf(stderr, ": ");
113 if (level == LOG_LEVEL_ERROR) {
114 fprintf(stderr, "Aborting.\n");
119 static void log_(int level, const char *file, int line, const char *format, ...)
120 __attribute__((format(printf, 4, 5)));
121 static void log_(int level, const char *file, int line, const char *format, ...) {
124 va_start(ap, format);
125 log_helper(level, file, line, format, ap);
130 log_(LOG_LEVEL_ERROR, __FILE__, __LINE__, __VA_ARGS__)
132 log_(LOG_LEVEL_WARNING, __FILE__, __LINE__, __VA_ARGS__)
134 log_(LOG_LEVEL_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
137 log_(LOG_LEVEL_ERROR | LOG_LEVEL_PERROR, __FILE__, __LINE__, __VA_ARGS__)
142 /* Load the function name using dlsym() if necessary and store it in pointer.
143 * Terminate program on failure. */
144 #define LOAD_FUNCTION(pointer, name) \
145 if ((pointer) == NULL) { \
147 dlerror(); /* Clear possibly existing error. */ \
149 *(void **) (&(pointer)) = dlsym(RTLD_NEXT, (name)); \
151 if ((error = dlerror()) != NULL) { \
152 ERROR("%s\n", error); \
157 /* OTHER FUNCTIONS */
159 static struct list *find_sockfd(int sockfd) {
162 if (sockfd == socket_list.unix_sockfd) {
166 for (e = &socket_list; e != NULL; e = e->next) {
167 if (e->unix_sockfd == sockfd) {
173 static struct list *remove_sockfd(int sockfd) {
176 if (sockfd == socket_list.unix_sockfd) {
180 for (e = &socket_list, p = NULL; e != NULL; p = e, e = e->next) {
181 if (e->unix_sockfd == sockfd) {
189 static const char *get_socket_path(void) {
190 const char *path = getenv("SOCKET2UNIX_PATH");
192 ERROR("SOCKET2UNIX_PATH environment variable not defined\n");
194 if (path[0] != '/') {
195 ERROR("SOCKET2UNIX_PATH '%s' must be an absolute path\n", path);
199 static int get_log_level(void) {
200 const char *level = getenv("SOCKET2UNIX_DEBUG");
203 return LOG_LEVEL_DEBUG;
205 return LOG_LEVEL_WARNING;
208 int number = atoi(level);
209 if (number <= 0 || number > LOG_LEVEL_DEBUG) {
210 number = LOG_LEVEL_DEBUG;
215 static const char *af_to_name(int af) {
218 } else if (af == AF_LOCAL) {
220 } else if (af == AF_INET) {
222 } else if (af == AF_INET6) {
224 } else if (af == AF_IPX) {
226 } else if (af == AF_NETLINK) {
228 } else if (af == AF_X25) {
230 } else if (af == AF_AX25) {
232 } else if (af == AF_ATMPVC) {
234 } else if (af == AF_APPLETALK) {
235 return "AF_APPLETALK";
236 } else if (af == AF_PACKET) {
242 static const char *sock_to_name(int sock) {
243 if (sock & SOCK_STREAM) {
244 return "SOCK_STREAM";
245 } else if (sock & SOCK_DGRAM) {
247 } else if (sock & SOCK_SEQPACKET) {
248 return "SOCK_SEQPACKET";
249 } else if (sock & SOCK_RAW) {
251 } else if (sock & SOCK_RDM) {
253 } else if (sock & SOCK_PACKET) {
254 return "SOCK_PACKET";
256 return "SOCK_UNKNOWN";
259 /* for getsockopt()/setsockopt(). */
260 static const char *level_to_name(int level) {
261 if (level == SOL_SOCKET) {
263 } else if (level == SOL_IP) {
265 } else if (level == SOL_IPV6) {
267 } else if (level == IPPROTO_TCP) {
268 return "IPPROTO_TCP";
269 } else if (level == IPPROTO_UDP) {
270 return "IPPROTO_UDP";
272 return "SOL_UNKNOWN";
277 static int set_sockaddr_un(struct sockaddr_un *sockaddr,
278 const struct sockaddr *addr, socklen_t addrlen) {
279 /* Just in case ... */
280 if ((addr->sa_family == AF_INET
281 && addrlen < sizeof(struct sockaddr_in))
282 || (addr->sa_family == AF_INET6
283 && addrlen < sizeof(struct sockaddr_in6))) {
284 WARN("invalid addrlen from program\n");
288 const char *socket_path = get_socket_path();
290 /* The program may open multiple sockets, e.g. IPv4 and IPv6 and on
291 * multiple ports. Create unique paths. */
294 if (addr->sa_family == AF_INET) {
296 port = ntohs(((struct sockaddr_in *)addr)->sin_port);
297 } else if (addr->sa_family == AF_INET6) {
299 port = ntohs(((struct sockaddr_in6 *)addr)->sin6_port);
303 WARN("unknown sa_family '%s' (%d)\n",
304 af_to_name(addr->sa_family), addr->sa_family);
307 /* Initialize sockaddr_un. */
308 sockaddr->sun_family = AF_UNIX;
309 int written = snprintf(sockaddr->sun_path, sizeof(sockaddr->sun_path),
310 "%s-%s-%d", socket_path, af, port);
311 /* The maximum length is quite short, check it. */
312 if (written >= (int)sizeof(sockaddr->sun_path)) {
313 ERROR("path '%s-%s-%d' too long for UNIX socket",
314 socket_path, af, port);
321 /* FUNCTIONS OVERWRITTEN BY LD_PRELOAD */
323 int socket(int domain, int type, int protocol) {
324 static int (*real_socket)(int, int, int);
325 LOAD_FUNCTION(real_socket, "socket");
327 if (domain == AF_UNIX || domain == AF_LOCAL) {
328 return real_socket(domain, type, protocol);
331 DBG("socket(%s, %s, %d)\n",
332 af_to_name(domain), sock_to_name(type), protocol);
334 /* We must return the replacement socket in case the program uses select()
335 * or similar on it. */
337 int unix_sockfd = real_socket(AF_UNIX, type, 0);
338 if (unix_sockfd < 0) {
339 DIE("bind(): failed to create UNIX socket");
342 struct list *entry = malloc(sizeof(*entry));
344 DIE("socket(): malloc");
346 memset(entry, 0, sizeof(*entry));
348 entry->unix_sockfd = unix_sockfd;
349 entry->orig_domain = domain;
350 entry->orig_type = type;
352 entry->next = socket_list.next;
353 socket_list.next = entry;
359 static int (*real_close)(int);
360 LOAD_FUNCTION(real_close, "close");
362 DBG("close(%d)\n", fd);
364 struct list *entry = remove_sockfd(fd);
366 DBG("close(%d): sockfd not found\n", fd);
367 return real_close(fd);
369 assert(fd == entry->unix_sockfd);
372 return real_close(fd);
375 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
376 static int (*real_bind)(int, const struct sockaddr *, socklen_t);
377 LOAD_FUNCTION(real_bind, "bind");
379 DBG("bind(%d, ..)\n", sockfd);
381 if (addr == NULL || addrlen < sizeof(addr->sa_family)
382 || addr->sa_family == AF_UNIX
383 || addr->sa_family == AF_LOCAL) {
384 return real_bind(sockfd, addr, addrlen);
387 struct list *entry = find_sockfd(sockfd);
389 DBG("bind(%d, ..): sockfd not found\n", sockfd);
390 return real_bind(sockfd, addr, addrlen);
392 assert(sockfd == entry->unix_sockfd);
393 DBG("bind(%d, ..): %s %s\n",
395 af_to_name(entry->orig_domain), sock_to_name(entry->orig_type));
397 struct sockaddr_un sockaddr;
398 if (set_sockaddr_un(&sockaddr, addr, addrlen) != 0) {
399 ERROR("connect(%d, ..) failed\n", sockfd);
402 DBG("bind(%d, ..): using path '%s'\n", sockfd, sockaddr.sun_path);
405 while (attempts < 10) {
406 if (real_bind(entry->unix_sockfd, (struct sockaddr *)&sockaddr,
407 sizeof(sockaddr)) == 0) {
411 if (errno != EADDRINUSE) {
412 DIE("bind(%d, ..): failed to bind to '%s'",
413 sockfd, sockaddr.sun_path);
416 /* File already exists, unlink it if it's a socket. This has a race
417 * condition, but the worst case is that we delete a file created by
418 * the user at the path he told us to use. Tough luck .. */
421 if (lstat(sockaddr.sun_path, &buf) != 0) {
422 /* Looks like a race, better abort. */
423 DIE("bind(%d, ..): lstat on UNIX socket '%s' failed",
424 sockfd, sockaddr.sun_path);
427 if (!S_ISSOCK(buf.st_mode)) {
428 ERROR("bind(%d, ..): path '%s' exits and is no socket\n",
429 sockfd, sockaddr.sun_path);
432 WARN("bind(%d, ..): unlinking '%s'\n", sockfd, sockaddr.sun_path);
433 if (unlink(sockaddr.sun_path) != 0) {
434 DIE("bind(%d, ..): unlink '%s' failed",
435 sockfd, sockaddr.sun_path);
441 ERROR("bind(%d, ..): failed to create UNIX socket file\n", sockfd);
442 return -1; /* never reached */
445 int listen(int sockfd, int backlog) {
446 static int (*real_listen)(int, int);
447 LOAD_FUNCTION(real_listen, "listen");
449 DBG("listen(%d, %d)\n", sockfd, backlog);
451 struct list *entry = find_sockfd(sockfd);
453 DBG("listen(%d, %d): sockfd not found\n", sockfd, backlog);
454 return real_listen(sockfd, backlog);
456 assert(sockfd == entry->unix_sockfd);
457 DBG("listen(%d, %d): %s %s\n",
459 af_to_name(entry->orig_domain), sock_to_name(entry->orig_type));
461 if (real_listen(entry->unix_sockfd, backlog) != 0) {
462 DIE("listen(): failed to listen");
468 int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
469 static int (*real_accept)(int, struct sockaddr *, socklen_t *);
470 LOAD_FUNCTION(real_accept, "accept");
472 DBG("accept(%d, ..)\n", sockfd);
474 struct list *entry = find_sockfd(sockfd);
476 DBG("accept(%d, ..): sockfd not found\n", sockfd);
477 return real_accept(sockfd, addr, addrlen);
479 assert(sockfd == entry->unix_sockfd);
480 DBG("accept(%d, ..): %s %s\n",
482 af_to_name(entry->orig_domain), sock_to_name(entry->orig_type));
484 struct sockaddr_un sockaddr;
485 socklen_t size = sizeof(sockaddr);
486 int sock = real_accept(entry->unix_sockfd, (struct sockaddr *)&sockaddr,
489 DIE("accept(%d, ..): failed to accept", sockfd);
492 if (addr == NULL || addrlen == NULL) {
495 DBG("accept(%d, ..): caller requested sockaddr\n", sockfd);
497 if (*addrlen < size) {
498 WARN("accept(%d, ..): invalid addrlen from program", sockfd);
503 /* This is not the protocol the program asked for (AF_* vs. AF_UNIX), but
504 * it should work most of the time. */
505 memcpy(addr, &sockaddr, size);
508 /* TODO: is this enough? */
513 int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
514 static int (*real_connect)(int, const struct sockaddr *, socklen_t);
515 LOAD_FUNCTION(real_connect, "connect");
517 DBG("connect(%d, ..)\n", sockfd);
519 if (addr == NULL || addrlen < sizeof(addr->sa_family)
520 || addr->sa_family == AF_UNIX
521 || addr->sa_family == AF_LOCAL) {
522 return real_connect(sockfd, addr, addrlen);
525 struct list *entry = find_sockfd(sockfd);
527 DBG("connect(%d, ..): sockfd not found\n", sockfd);
528 return real_connect(sockfd, addr, addrlen);
530 assert(sockfd == entry->unix_sockfd);
531 DBG("connect(%d, ..): %s %s\n",
533 af_to_name(entry->orig_domain), sock_to_name(entry->orig_type));
535 struct sockaddr_un sockaddr;
536 if (set_sockaddr_un(&sockaddr, addr, addrlen) != 0) {
537 ERROR("connect(%d, ..) failed\n", sockfd);
540 DBG("connect(%d, ..): using path '%s'\n", sockfd, sockaddr.sun_path);
542 if (real_connect(entry->unix_sockfd, (struct sockaddr *)&sockaddr,
543 sizeof(sockaddr)) != 0) {
544 DIE("connect(%d, ..): failed to connect", sockfd);
551 int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
552 static int (*real_getsockname)(int, struct sockaddr *, socklen_t *);
553 LOAD_FUNCTION(real_getsockname, "getsockname");
555 DBG("getsockname(%d, ..)\n", sockfd);
557 return real_getsockname(sockfd, addr, addrlen);
560 int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
561 static int (*real_getpeername)(int, struct sockaddr *, socklen_t *);
562 LOAD_FUNCTION(real_getpeername, "getpeername");
564 DBG("getpeername(%d, ..)\n", sockfd);
566 return real_getpeername(sockfd, addr, addrlen);
569 int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen) {
570 static int (*real_getsockopt)(int, int, int, void *, socklen_t *);
571 LOAD_FUNCTION(real_getsockopt, "getsockopt");
573 DBG("getsockopt(%d, %d %s, %d, ..)\n",
574 sockfd, level, level_to_name(level), optname);
576 return real_getsockopt(sockfd, level, optname, optval, optlen);
578 int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) {
579 static int (*real_setsockopt)(int, int, int, const void *, socklen_t);
580 LOAD_FUNCTION(real_setsockopt, "setsockopt");
582 DBG("setsockopt(%d, %d %s, %d, ..)\n",
583 sockfd, level, level_to_name(level), optname);
585 return real_setsockopt(sockfd, level, optname, optval, optlen);