Skip to content

Instantly share code, notes, and snippets.

@nolanlum
Created September 22, 2014 08:37
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save nolanlum/ad2f2f01d0b2a2258895 to your computer and use it in GitHub Desktop.
Save nolanlum/ad2f2f01d0b2a2258895 to your computer and use it in GitHub Desktop.
LD_PRELOAD library to make bind and connect use a specific IP address. Now works with IPv6.
/*
* Copyright (c) 2014 Nolan Lum <nolan@nolm.name>
*
* Updated to support IPv6. Use the environment variable BIND_ADDR6.
*/
/*
Copyright (C) 2000 Daniel Ryde
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
*/
/*
LD_PRELOAD library to make bind and connect to use a virtual
IP address as localaddress. Specified via the enviroment
variable BIND_ADDR.
Compile on Linux with:
gcc -nostartfiles -fpic -shared bind.c -o bind.so -ldl -D_GNU_SOURCE
Example in bash to make inetd only listen to the localhost
lo interface, thus disabling remote connections and only
enable to/from localhost:
BIND_ADDR="127.0.0.1" LD_PRELOAD=./bind.so /sbin/inetd
Example in bash to use your virtual IP as your outgoing
sourceaddress for ircII:
BIND_ADDR="your-virt-ip" LD_PRELOAD=./bind.so ircII
Note that you have to set up your servers virtual IP first.
This program was made by Daniel Ryde
email: daniel@ryde.net
web: http://www.ryde.net/
TODO: I would like to extend it to the accept calls too, like a
general tcp-wrapper. Also like an junkbuster for web-banners.
For libc5 you need to replace socklen_t with int.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <dlfcn.h>
#include <errno.h>
int (*real_bind)(int, const struct sockaddr *, socklen_t);
int (*real_connect)(int, const struct sockaddr *, socklen_t);
char *bind_addr_env;
char *bind_addr6_env;
unsigned long int inaddr_any_saddr;
unsigned long int bind_addr_saddr;
struct sockaddr_in local_sockaddr_in[] = { 0 };
struct in6_addr bind_addr6;
struct sockaddr_in6 local_sockaddr_in6[] = { 0 };
void _init (void)
{
const char *err;
real_bind = dlsym (RTLD_NEXT, "bind");
if ((err = dlerror ()) != NULL) {
fprintf (stderr, "dlsym (bind): %s\n", err);
}
real_connect = dlsym (RTLD_NEXT, "connect");
if ((err = dlerror ()) != NULL) {
fprintf (stderr, "dlsym (connect): %s\n", err);
}
inaddr_any_saddr = htonl (INADDR_ANY);
if (bind_addr_env = getenv ("BIND_ADDR")) {
bind_addr_saddr = inet_addr (bind_addr_env);
local_sockaddr_in->sin_family = AF_INET;
local_sockaddr_in->sin_addr.s_addr = bind_addr_saddr;
local_sockaddr_in->sin_port = htons(0);
}
if (bind_addr6_env = getenv("BIND_ADDR6")) {
inet_pton(AF_INET6, bind_addr6_env, &bind_addr6);
memset(local_sockaddr_in6, 0, sizeof(struct sockaddr_in6));
local_sockaddr_in6->sin6_family = AF_INET6;
memcpy(&local_sockaddr_in6->sin6_addr, &bind_addr6, sizeof(struct in6_addr));
local_sockaddr_in6->sin6_port = htons(0);
}
}
int bind (int fd, const struct sockaddr *sk, socklen_t sl)
{
static struct sockaddr_in *lsk_in;
static struct sockaddr_in6 *lsk_in6;
static char addr_str[INET6_ADDRSTRLEN];
lsk_in = (struct sockaddr_in *) sk;
lsk_in6 = (struct sockaddr_in6 *) sk;
if (lsk_in->sin_family == AF_INET) {
#ifdef DEBUG
inet_ntop(AF_INET, &lsk_in->sin_addr, addr_str, INET6_ADDRSTRLEN);
printf("-bind: %d %s:%d\n", fd, addr_str, ntohs(lsk_in->sin_port));
#endif
if ((lsk_in->sin_addr.s_addr == inaddr_any_saddr) && bind_addr_env) {
lsk_in->sin_addr.s_addr = bind_addr_saddr;
}
#ifdef DEBUG
inet_ntop(AF_INET, &lsk_in->sin_addr, addr_str, INET6_ADDRSTRLEN);
printf("+bind: %d %s:%d\n", fd, addr_str, ntohs(lsk_in->sin_port));
#endif
} else if (lsk_in->sin_family == AF_INET6) {
#ifdef DEBUG
inet_ntop(AF_INET6, &lsk_in6->sin6_addr, addr_str, INET6_ADDRSTRLEN);
printf("-bind6: %d [%s]:%d\n", fd, addr_str, ntohs(lsk_in->sin_port));
#endif
if (!memcmp(&lsk_in6->sin6_addr, &in6addr_any, sizeof(struct in6_addr)) && bind_addr6_env) {
memcpy(&lsk_in6->sin6_addr, &bind_addr6, sizeof(struct in6_addr));
}
#ifdef DEBUG
inet_ntop(AF_INET6, &lsk_in6->sin6_addr, addr_str, INET6_ADDRSTRLEN);
printf("+bind6: %d [%s]:%d\n", fd, addr_str, ntohs(lsk_in->sin_port));
#endif
}
return real_bind (fd, sk, sl);
}
int connect (int fd, const struct sockaddr *sk, socklen_t sl)
{
static struct sockaddr_in *rsk_in;
static struct sockaddr_in6 *rsk_in6;
static char addr_str[INET6_ADDRSTRLEN];
rsk_in = (struct sockaddr_in *) sk;
rsk_in6 = (struct sockaddr_in6 *) sk;
if (rsk_in->sin_family == AF_INET) {
#ifdef DEBUG
inet_ntop(AF_INET, &rsk_in->sin_addr, addr_str, INET6_ADDRSTRLEN);
printf("connect: %d %s:%d\n", fd, addr_str, ntohs(rsk_in->sin_port));
#endif
if (bind_addr_env) {
#ifdef DEBUG
inet_ntop(AF_INET, &local_sockaddr_in->sin_addr, addr_str, INET6_ADDRSTRLEN);
printf("++ bind: %d %s\n", fd, addr_str);
#endif
real_bind(fd, (struct sockaddr *) local_sockaddr_in, sizeof(struct sockaddr_in));
}
} else if (rsk_in->sin_family == AF_INET6) {
#ifdef DEBUG
inet_ntop(AF_INET6, &rsk_in6->sin6_addr, addr_str, INET6_ADDRSTRLEN);
printf("connect6: %d [%s]:%d\n", fd, addr_str, ntohs(rsk_in->sin_port));
#endif
if (bind_addr6_env) {
#ifdef DEBUG
inet_ntop(AF_INET6, &local_sockaddr_in6->sin6_addr, addr_str, INET6_ADDRSTRLEN);
printf("++ bind6: %d [%s]\n", fd, addr_str);
#endif
real_bind(fd, (struct sockaddr *) local_sockaddr_in6, sizeof(struct sockaddr_in6));
}
}
return real_connect (fd, sk, sl);
}
@mjt0k
Copy link

mjt0k commented Oct 22, 2023

Please use static keyword for all local variables

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment