]> ruderich.org/simon Gitweb - socket2unix/socket2unix.git/blob - src/socket2unix.c
c90c864ebecf79b805e96a29add3412efcb7596e
[socket2unix/socket2unix.git] / src / socket2unix.c
1 /*
2  * Simple LD_PRELOAD wrapper to "convert" network sockets to UNIX sockets;
3  * works for clients and servers. See README for details.
4  *
5  * Copyright (C) 2013  Simon Ruderich
6  *
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.
11  *
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.
16  *
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/>.
19  */
20
21
22 /* Necessary for RTLD_NEXT. */
23 #define _GNU_SOURCE
24
25 #include <config.h>
26
27 #include <assert.h>
28 #include <dlfcn.h>
29 #include <errno.h>
30 #include <netinet/in.h>
31 #include <stdarg.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/socket.h>
36 #include <sys/stat.h>
37 #include <sys/types.h>
38 #include <sys/un.h>
39 #include <unistd.h>
40
41
42 /* CONSTANTS */
43
44 #define LOG_LEVEL_ERROR   1
45 #define LOG_LEVEL_WARNING 2
46 #define LOG_LEVEL_DEBUG   3
47 #define LOG_LEVEL_MASK    LOG_LEVEL_DEBUG
48
49 #define LOG_LEVEL_PERROR  42
50
51
52 /* GLOBAL VARIABLES */
53
54 struct list {
55     int unix_sockfd;
56
57     int orig_domain;
58     int orig_type;
59
60     struct list *next;
61 };
62
63 static struct list socket_list = {
64     .unix_sockfd = -1, /* must not match a valid sockfd */
65 };
66
67
68 /* LOG FUNCTIONS/MACROS */
69
70 static int get_log_level(void);
71
72 static void log_helper(int action, const char *file, int line, const char *format, va_list ap) {
73     int saved_errno = errno;
74
75     static int log_level;
76     if (!log_level) {
77         log_level = get_log_level();
78     }
79
80     int level = action & LOG_LEVEL_MASK;
81     if (level > log_level) {
82         return;
83     }
84
85     const char *prefix;
86     if (level == LOG_LEVEL_DEBUG) {
87         prefix = "DEBUG";
88     } else if (level == LOG_LEVEL_WARNING) {
89         prefix = "WARN ";
90     } else if (level == LOG_LEVEL_ERROR) {
91         prefix = "ERROR";
92     } else {
93         prefix = "UNKNOWN";
94     }
95
96     /* Prevent other threads from interrupting the printf()s. */
97     flockfile(stderr);
98
99     fprintf(stderr, "socket2unix [%s] ", prefix);
100     fprintf(stderr, "[%s:%3d] ", file, line);
101     vfprintf(stderr, format, ap);
102
103     if ((action & ~LOG_LEVEL_MASK) == LOG_LEVEL_PERROR) {
104         fprintf(stderr, ": ");
105
106         errno = saved_errno;
107         perror("");
108     }
109
110     funlockfile(stderr);
111
112     if (level == LOG_LEVEL_ERROR) {
113         fprintf(stderr, "Aborting.\n");
114         exit(EXIT_FAILURE);
115     }
116 }
117
118 static void log_(int level, const char *file, int line, const char *format, ...)
119     __attribute__((format(printf, 4, 5)));
120 static void log_(int level, const char *file, int line, const char *format, ...) {
121     va_list ap;
122
123     va_start(ap, format);
124     log_helper(level, file, line, format, ap);
125     va_end(ap);
126 }
127
128 #define ERROR(...) \
129     log_(LOG_LEVEL_ERROR,   __FILE__, __LINE__, __VA_ARGS__)
130 #define WARN(...) \
131     log_(LOG_LEVEL_WARNING, __FILE__, __LINE__, __VA_ARGS__)
132 #define DBG(...) \
133     log_(LOG_LEVEL_DEBUG,   __FILE__, __LINE__, __VA_ARGS__)
134
135 #define DIE(...) \
136     log_(LOG_LEVEL_ERROR | LOG_LEVEL_PERROR, __FILE__, __LINE__, __VA_ARGS__)
137
138
139 /* LD_PRELOAD */
140
141 /* Load the function name using dlsym() if necessary and store it in pointer.
142  * Terminate program on failure. */
143 #define LOAD_FUNCTION(pointer, name) \
144     if ((pointer) == NULL) { \
145         char *error; \
146         dlerror(); /* Clear possibly existing error. */ \
147         \
148         *(void **) (&(pointer)) = dlsym(RTLD_NEXT, (name)); \
149         \
150         if ((error = dlerror()) != NULL) { \
151             ERROR("%s\n", error); \
152         } \
153     }
154
155
156 /* OTHER FUNCTIONS */
157
158 static struct list *find_sockfd(int sockfd) {
159     struct list *e;
160
161     if (sockfd == socket_list.unix_sockfd) {
162         return NULL;
163     }
164
165     for (e = &socket_list; e != NULL; e = e->next) {
166         if (e->unix_sockfd == sockfd) {
167             return e;
168         }
169     }
170     return NULL;
171 }
172 static struct list *remove_sockfd(int sockfd) {
173     struct list *e, *p;
174
175     if (sockfd == socket_list.unix_sockfd) {
176         return NULL;
177     }
178
179     for (e = &socket_list, p = NULL; e != NULL; p = e, e = e->next) {
180         if (e->unix_sockfd == sockfd) {
181             p->next = e->next;
182             return e;
183         }
184     }
185     return NULL;
186 }
187
188 static const char *get_socket_path(void) {
189     const char *path = getenv("SOCKET2UNIX_PATH");
190     if (!path) {
191         ERROR("SOCKET2UNIX_PATH environment variable not defined\n");
192     }
193     if (path[0] != '/') {
194         ERROR("SOCKET2UNIX_PATH '%s' must be an absolute path\n", path);
195     }
196     return path;
197 }
198 static int get_log_level(void) {
199     const char *level = getenv("SOCKET2UNIX_DEBUG");
200     if (!level) {
201 #ifdef DEBUG
202         return LOG_LEVEL_DEBUG;
203 #else
204         return LOG_LEVEL_WARNING;
205 #endif
206     }
207     int number = atoi(level);
208     if (number <= 0 || number > LOG_LEVEL_DEBUG) {
209         number = LOG_LEVEL_DEBUG;
210     }
211     return number;
212 }
213
214 static const char *af_to_name(int af) {
215     if (af == AF_UNIX) {
216         return "AF_UNIX";
217     } else if (af == AF_LOCAL) {
218         return "AF_LOCAL";
219     } else if (af == AF_INET) {
220         return "AF_INET";
221     } else if (af == AF_INET6) {
222         return "AF_INET6";
223     } else if (af == AF_IPX) {
224         return "AF_IPX";
225 #ifdef AF_NETLINK
226     } else if (af == AF_NETLINK) {
227         return "AF_NETLINK";
228 #endif
229 #ifdef AF_X25
230     } else if (af == AF_X25) {
231         return "AF_X25";
232 #endif
233 #ifdef AF_AX25
234     } else if (af == AF_AX25) {
235         return "AF_AX25";
236 #endif
237 #ifdef AF_ATMPVC
238     } else if (af == AF_ATMPVC) {
239         return "AF_ATMPVC";
240 #endif
241     } else if (af == AF_APPLETALK) {
242         return "AF_APPLETALK";
243 #ifdef AF_PACKET
244     } else if (af == AF_PACKET) {
245         return "AF_PACKET";
246 #endif
247     } else {
248         return "AF_UNKNOWN";
249     }
250 }
251 static const char *sock_to_name(int sock) {
252     if (sock & SOCK_STREAM) {
253         return "SOCK_STREAM";
254     } else if (sock & SOCK_DGRAM) {
255         return "SOCK_DGRAM";
256     } else if (sock & SOCK_SEQPACKET) {
257         return "SOCK_SEQPACKET";
258     } else if (sock & SOCK_RAW) {
259         return "SOCK_RAW";
260     } else if (sock & SOCK_RDM) {
261         return "SOCK_RDM";
262 #ifdef SOCK_PACKET
263     } else if (sock & SOCK_PACKET) {
264         return "SOCK_PACKET";
265 #endif
266     } else {
267         return "SOCK_UNKNOWN";
268     }
269 }
270 /* for getsockopt()/setsockopt(). */
271 static const char *level_to_name(int level) {
272     if (level == SOL_SOCKET) {
273         return "SOL_SOCKET";
274 #ifdef SOL_IP
275     } else if (level == SOL_IP) {
276         return "SOL_IP";
277 #endif
278 #ifdef SOL_IPV6
279     } else if (level == SOL_IPV6) {
280         return "SOL_IPV6";
281 #endif
282     } else if (level == IPPROTO_TCP) {
283         return "IPPROTO_TCP";
284     } else if (level == IPPROTO_UDP) {
285         return "IPPROTO_UDP";
286     } else {
287         return "SOL_UNKNOWN";
288     }
289 }
290
291
292 static int set_sockaddr_un(struct sockaddr_un *sockaddr,
293                            const struct sockaddr *addr, socklen_t addrlen) {
294     /* Just in case ... */
295     if ((addr->sa_family == AF_INET
296                 && addrlen < sizeof(struct sockaddr_in))
297             || (addr->sa_family == AF_INET6
298                 && addrlen < sizeof(struct sockaddr_in6))) {
299         WARN("invalid addrlen from program\n");
300         return -1;
301     }
302
303     const char *socket_path = get_socket_path();
304
305     /* The program may open multiple sockets, e.g. IPv4 and IPv6 and on
306      * multiple ports. Create unique paths. */
307     const char *af;
308     int port;
309     if (addr->sa_family == AF_INET) {
310         af   = "v4";
311         port = ntohs(((struct sockaddr_in *)addr)->sin_port);
312     } else if (addr->sa_family == AF_INET6) {
313         af   = "v6";
314         port = ntohs(((struct sockaddr_in6 *)addr)->sin6_port);
315     } else {
316         af   = "unknown";
317         port = 0;
318         WARN("unknown sa_family '%s' (%d)\n",
319              af_to_name(addr->sa_family), addr->sa_family);
320     }
321
322     /* Initialize sockaddr_un. */
323     sockaddr->sun_family = AF_UNIX;
324     int written = snprintf(sockaddr->sun_path, sizeof(sockaddr->sun_path),
325                            "%s-%s-%d", socket_path, af, port);
326     /* The maximum length is quite short, check it. */
327     if (written >= (int)sizeof(sockaddr->sun_path)) {
328         ERROR("path '%s-%s-%d' too long for UNIX socket",
329               socket_path, af, port);
330     }
331
332     return 0;
333 }
334
335
336 /* FUNCTIONS OVERWRITTEN BY LD_PRELOAD */
337
338 int socket(int domain, int type, int protocol) {
339     static int (*real_socket)(int, int, int);
340     LOAD_FUNCTION(real_socket, "socket");
341
342     if (domain == AF_UNIX || domain == AF_LOCAL) {
343         return real_socket(domain, type, protocol);
344     }
345
346     DBG("socket(%s, %s, %d)\n",
347         af_to_name(domain), sock_to_name(type), protocol);
348
349     /* We must return the replacement socket in case the program uses select()
350      * or similar on it. */
351
352     int unix_sockfd = real_socket(AF_UNIX, type, 0);
353     if (unix_sockfd < 0) {
354         DIE("bind(): failed to create UNIX socket");
355     }
356
357     struct list *entry = malloc(sizeof(*entry));
358     if (!entry) {
359         DIE("socket(): malloc");
360     }
361     memset(entry, 0, sizeof(*entry));
362
363     entry->unix_sockfd = unix_sockfd;
364     entry->orig_domain = domain;
365     entry->orig_type   = type;
366
367     entry->next = socket_list.next;
368     socket_list.next = entry;
369
370     return unix_sockfd;
371 }
372
373 int close(int fd) {
374     static int (*real_close)(int);
375     LOAD_FUNCTION(real_close, "close");
376
377     DBG("close(%d)\n", fd);
378
379     struct list *entry = remove_sockfd(fd);
380     if (entry == NULL) {
381         DBG("close(%d): sockfd not found\n", fd);
382         return real_close(fd);
383     }
384     assert(fd == entry->unix_sockfd);
385     free(entry);
386
387     return real_close(fd);
388 }
389
390 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
391     static int (*real_bind)(int, const struct sockaddr *, socklen_t);
392     LOAD_FUNCTION(real_bind, "bind");
393
394     DBG("bind(%d, ..)\n", sockfd);
395
396     if (addr == NULL || addrlen < sizeof(addr->sa_family)
397             || addr->sa_family == AF_UNIX
398             || addr->sa_family == AF_LOCAL) {
399         return real_bind(sockfd, addr, addrlen);
400     }
401
402     struct list *entry = find_sockfd(sockfd);
403     if (!entry) {
404         DBG("bind(%d, ..): sockfd not found\n", sockfd);
405         return real_bind(sockfd, addr, addrlen);
406     }
407     assert(sockfd == entry->unix_sockfd);
408     DBG("bind(%d, ..): %s %s\n",
409         sockfd,
410         af_to_name(entry->orig_domain), sock_to_name(entry->orig_type));
411
412     struct sockaddr_un sockaddr;
413     if (set_sockaddr_un(&sockaddr, addr, addrlen) != 0) {
414         ERROR("connect(%d, ..) failed\n", sockfd);
415     }
416
417     DBG("bind(%d, ..): using path '%s'\n", sockfd, sockaddr.sun_path);
418
419     int attempts = 0;
420     while (attempts < 10) {
421         if (real_bind(entry->unix_sockfd, (struct sockaddr *)&sockaddr,
422                                           sizeof(sockaddr)) == 0) {
423             /* Success. */
424             return 0;
425         }
426         if (errno != EADDRINUSE) {
427             DIE("bind(%d, ..): failed to bind to '%s'",
428                 sockfd, sockaddr.sun_path);
429         }
430
431         /* File already exists, unlink it if it's a socket. This has a race
432          * condition, but the worst case is that we delete a file created by
433          * the user at the path he told us to use. Tough luck .. */
434
435         struct stat buf;
436         if (lstat(sockaddr.sun_path, &buf) != 0) {
437             /* Looks like a race, better abort. */
438             DIE("bind(%d, ..): lstat on UNIX socket '%s' failed",
439                 sockfd, sockaddr.sun_path);
440         }
441
442         if (!S_ISSOCK(buf.st_mode)) {
443             ERROR("bind(%d, ..): path '%s' exits and is no socket\n",
444                   sockfd, sockaddr.sun_path);
445         }
446
447         WARN("bind(%d, ..): unlinking '%s'\n", sockfd, sockaddr.sun_path);
448         if (unlink(sockaddr.sun_path) != 0) {
449             DIE("bind(%d, ..): unlink '%s' failed",
450                 sockfd, sockaddr.sun_path);
451         }
452
453         attempts++;
454     }
455
456     ERROR("bind(%d, ..): failed to create UNIX socket file\n", sockfd);
457     return -1; /* never reached */
458 }
459
460 int listen(int sockfd, int backlog) {
461     static int (*real_listen)(int, int);
462     LOAD_FUNCTION(real_listen, "listen");
463
464     DBG("listen(%d, %d)\n", sockfd, backlog);
465
466     struct list *entry = find_sockfd(sockfd);
467     if (!entry) {
468         DBG("listen(%d, %d): sockfd not found\n", sockfd, backlog);
469         return real_listen(sockfd, backlog);
470     }
471     assert(sockfd == entry->unix_sockfd);
472     DBG("listen(%d, %d): %s %s\n",
473         sockfd, backlog,
474         af_to_name(entry->orig_domain), sock_to_name(entry->orig_type));
475
476     if (real_listen(entry->unix_sockfd, backlog) != 0) {
477         DIE("listen(): failed to listen");
478     }
479
480     return 0;
481 }
482
483 int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
484     static int (*real_accept)(int, struct sockaddr *, socklen_t *);
485     LOAD_FUNCTION(real_accept, "accept");
486
487     DBG("accept(%d, ..)\n", sockfd);
488
489     struct list *entry = find_sockfd(sockfd);
490     if (!entry) {
491         DBG("accept(%d, ..): sockfd not found\n", sockfd);
492         return real_accept(sockfd, addr, addrlen);
493     }
494     assert(sockfd == entry->unix_sockfd);
495     DBG("accept(%d, ..): %s %s\n",
496         sockfd,
497         af_to_name(entry->orig_domain), sock_to_name(entry->orig_type));
498
499     struct sockaddr_un sockaddr;
500     socklen_t size = sizeof(sockaddr);
501     int sock = real_accept(entry->unix_sockfd, (struct sockaddr *)&sockaddr,
502                                                &size);
503     if (sock < 0) {
504         DIE("accept(%d, ..): failed to accept", sockfd);
505     }
506
507     if (addr == NULL || addrlen == NULL) {
508         return sock;
509     }
510     DBG("accept(%d, ..): caller requested sockaddr\n", sockfd);
511
512     if (*addrlen < size) {
513         WARN("accept(%d, ..): invalid addrlen from program", sockfd);
514         errno = EINVAL;
515         return -1;
516     }
517
518     /* This is not the protocol the program asked for (AF_* vs. AF_UNIX), but
519      * it should work most of the time. */
520     memcpy(addr, &sockaddr, size);
521     *addrlen = size;
522
523     /* TODO: is this enough? */
524
525     return sock;
526 }
527
528 int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
529     static int (*real_connect)(int, const struct sockaddr *, socklen_t);
530     LOAD_FUNCTION(real_connect, "connect");
531
532     DBG("connect(%d, ..)\n", sockfd);
533
534     if (addr == NULL || addrlen < sizeof(addr->sa_family)
535             || addr->sa_family == AF_UNIX
536             || addr->sa_family == AF_LOCAL) {
537         return real_connect(sockfd, addr, addrlen);
538     }
539
540     struct list *entry = find_sockfd(sockfd);
541     if (!entry) {
542         DBG("connect(%d, ..): sockfd not found\n", sockfd);
543         return real_connect(sockfd, addr, addrlen);
544     }
545     assert(sockfd == entry->unix_sockfd);
546     DBG("connect(%d, ..): %s %s\n",
547         sockfd,
548         af_to_name(entry->orig_domain), sock_to_name(entry->orig_type));
549
550     struct sockaddr_un sockaddr;
551     if (set_sockaddr_un(&sockaddr, addr, addrlen) != 0) {
552         ERROR("connect(%d, ..) failed\n", sockfd);
553     }
554
555     DBG("connect(%d, ..): using path '%s'\n", sockfd, sockaddr.sun_path);
556
557     if (real_connect(entry->unix_sockfd, (struct sockaddr *)&sockaddr,
558                                          sizeof(sockaddr)) != 0) {
559         DIE("connect(%d, ..): failed to connect", sockfd);
560     }
561
562     return 0;
563 }
564
565
566 int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
567     static int (*real_getsockname)(int, struct sockaddr *, socklen_t *);
568     LOAD_FUNCTION(real_getsockname, "getsockname");
569
570     DBG("getsockname(%d, ..)\n", sockfd);
571
572     return real_getsockname(sockfd, addr, addrlen);
573 }
574
575 int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
576     static int (*real_getpeername)(int, struct sockaddr *, socklen_t *);
577     LOAD_FUNCTION(real_getpeername, "getpeername");
578
579     DBG("getpeername(%d, ..)\n", sockfd);
580
581     return real_getpeername(sockfd, addr, addrlen);
582 }
583
584 int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen) {
585     static int (*real_getsockopt)(int, int, int, void *, socklen_t *);
586     LOAD_FUNCTION(real_getsockopt, "getsockopt");
587
588     DBG("getsockopt(%d, %d %s, %d, ..)\n",
589         sockfd, level, level_to_name(level), optname);
590
591     return real_getsockopt(sockfd, level, optname, optval, optlen);
592 }
593 int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) {
594     static int (*real_setsockopt)(int, int, int, const void *, socklen_t);
595     LOAD_FUNCTION(real_setsockopt, "setsockopt");
596
597     DBG("setsockopt(%d, %d %s, %d, ..)\n",
598         sockfd, level, level_to_name(level), optname);
599
600     return real_setsockopt(sockfd, level, optname, optval, optlen);
601 }