rebind.c 2.81 KB
Newer Older
1 2 3
/*
 * rebind: Intercept bind calls and bind to a different port
 * Copyright 2010 Joel Martin
4
 * Licensed under MPL-2.0 (see docs/LICENSE.MPL-2.0)
5 6 7 8 9
 *
 * Overload (LD_PRELOAD) bind system call. If REBIND_PORT_OLD and
 * REBIND_PORT_NEW environment variables are set then bind on the new
 * port (of localhost) instead of the old port. 
 *
10 11
 * This allows a bridge/proxy (such as websockify) to run on the old port and
 * translate traffic to/from the new port.
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
 *
 * Usage:
 *     LD_PRELOAD=./rebind.so \
 *     REBIND_PORT_OLD=23 \
 *     REBIND_PORT_NEW=2023 \
 *     program
 */

//#define DO_DEBUG 1

#include <stdio.h>
#include <stdlib.h>

#define __USE_GNU 1  // Pull in RTLD_NEXT
#include <dlfcn.h>

#include <string.h>
#include <netinet/in.h>


#if defined(DO_DEBUG)
#define DEBUG(...) \
    fprintf(stderr, "wswrapper: "); \
    fprintf(stderr, __VA_ARGS__);
#else
#define DEBUG(...)
#endif


int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
    static void * (*func)();
    int do_move = 0;
    struct sockaddr_in * addr_in = (struct sockaddr_in *)addr;
    struct sockaddr_in addr_tmp;
    socklen_t addrlen_tmp;
    char * PORT_OLD, * PORT_NEW, * end1, * end2;
    int ret, oldport, newport, askport = htons(addr_in->sin_port);
    uint32_t askaddr = htons(addr_in->sin_addr.s_addr);
    if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "bind");

    DEBUG(">> bind(%d, _, %d), askaddr %d, askport %d\n",
          sockfd, addrlen, askaddr, askport);

    /* Determine if we should move this socket */
    if (addr_in->sin_family == AF_INET) {
        // TODO: support IPv6
        PORT_OLD = getenv("REBIND_OLD_PORT");
        PORT_NEW = getenv("REBIND_NEW_PORT");
        if (PORT_OLD && (*PORT_OLD != '\0') &&
            PORT_NEW && (*PORT_NEW != '\0')) {
            oldport = strtol(PORT_OLD, &end1, 10);
            newport = strtol(PORT_NEW, &end2, 10);
            if (oldport && (*end1 == '\0') &&
                newport && (*end2 == '\0') &&
                (oldport == askport)) {
                do_move = 1;
            }
        }
    }

    if (! do_move) {
        /* Just pass everything right through to the real bind */
        ret = (int) func(sockfd, addr, addrlen);
        DEBUG("<< bind(%d, _, %d) ret %d\n", sockfd, addrlen, ret);
        return ret;
    }

    DEBUG("binding fd %d on localhost:%d instead of 0x%x:%d\n",
        sockfd, newport, ntohl(addr_in->sin_addr.s_addr), oldport);

    /* Use a temporary location for the new address information */
    addrlen_tmp = sizeof(addr_tmp);
    memcpy(&addr_tmp, addr, addrlen_tmp);

    /* Bind to other port on the loopback instead */
    addr_tmp.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    addr_tmp.sin_port = htons(newport);
    ret = (int) func(sockfd, &addr_tmp, addrlen_tmp);

    DEBUG("<< bind(%d, _, %d) ret %d\n", sockfd, addrlen, ret);
    return ret;
}