How to specify the source address for all outbound connections

If you have multiple IPs assigned on your Linux pc then there is a chance that you want to use different IPs for some applications than default one. Updating IP routes every time isn’t a good idea and you may mess up.

get bindhack.c

“`

wget ‘https://gist.githubusercontent.com/akhilin/f6660a2f93f64545ff8fcc0d6b23e42a/raw/7bf3f066b74a4b9e3d3768a8affee26da6a3ada6/bindhack.c’ -P /tmp/

“`

compile it

“`

gcc -fPIC -static -shared -o /tmp/bindhack.so /tmp/bindhack.c -lc -ldl

“`

Copy it to library folder

“`

cp /tmp/bindhack.so /usr/lib/ && chmod +x /usr/lib/bindhack.so

“`

Optional (ignore if you have it already )

“`

echo ‘nameserver 8.8.8.8’ >> /etc/resolv.conf

“`

using bindhack

“`

BIND_ADDR=<source ip> LD_PRELOAD=/usr/lib/bindhack.so <command here>

“`

Example

 

you can add below function in your .bashrc to spin it at any time

 

bindhack() {
# Author: Akhil Jalagam
[ $# -lt 2 ] && echo "missing arguments: $0 [bind ip] [command with quotes]"
wget 'https://gist.githubusercontent.com/akhilin/f6660a2f93f64545ff8fcc0d6b23e42a/raw/7bf3f066b74a4b9e3d3768a8affee26da6a3ada6/bindhack.c' -P /tmp/
gcc -fPIC -static -shared -o /tmp/bindhack.so /tmp/bindhack.c -lc -ldl
[ -f /usr/lib/bindhack.so ] || `type -P cp` /tmp/bindhack.so /usr/lib/ && chmod +x /usr/lib/bindhack.so
[ -f /etc/resolv.conf ] && `type -P cp` /etc/resolv.conf /etc/resolv.conf.bak && echo 'nameserver 8.8.8.8' >> /etc/resolv.conf
[ $# -eq 2 ] && BIND_ADDR=$1 LD_PRELOAD=/usr/lib/bindhack.so $2
echo ''
`type -P cp` /etc/resolv.conf.bak /etc/resolv.conf
}

 

take a look at bindhack.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <dlfcn.h>

#include <arpa/inet.h>

/* 
   This is the address you want to force everything to use. It can be
   overriden at runtime by specifying the BIND_SRC environment 
   variable.
*/
#define SRC_ADDR	"192.168.0.1"

/* 
   LIBC_NAME should be the name of the library that contains the real
   bind() and connect() library calls. On Linux this is libc, but on
   other OS's such as Solaris this would be the socket library
*/
#define LIBC_NAME	"libc.so.6" 

#define YES	1
#define NO	0

int
bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen)
{

	struct sockaddr_in src_addr;
	void	*libc;
	int	(*bind_ptr)(int, void *, int);
	int	ret;
	int	passthru;
	char 	*bind_src;

#ifdef DEBUG
	fprintf(stderr, "bind() override called for addr: %s\n", SRC_ADDR);
#endif

	libc = dlopen(LIBC_NAME, RTLD_LAZY);

	if (!libc)
	{
		fprintf(stderr, "Unable to open libc!\n");
		exit(-1);
	}

	*(void **) (&bind_ptr) = dlsym(libc, "bind");

	if (!bind_ptr)
	{
		fprintf(stderr, "Unable to locate bind function in lib\n");
		exit(-1);
	}
	
	passthru = YES;	/* By default, we just call regular bind() */

	if (my_addr==NULL)
	{
		/* If we get a NULL it's because we're being called
		   from the connect() hack */

		passthru = NO;

#ifdef DEBUG
		fprintf(stderr, "bind() Received NULL address.\n");
#endif

	}
	else
	{

		if (my_addr->sa_family == AF_INET)
		{
			struct sockaddr_in	myaddr_in;

			/* If this is an INET socket, then we spring to
			   action! */
			passthru = NO;

			memcpy(&myaddr_in, my_addr, addrlen);

			src_addr.sin_port = myaddr_in.sin_port;


		}
		else
		{
			passthru = YES;
		}

	}

	if (!passthru)
	{

#ifdef DEBUG
		fprintf(stderr, "Proceeding with bind hack\n");
#endif

		src_addr.sin_family = AF_INET;

		bind_src=getenv("BIND_SRC");

		/* If the environment variable BIND_SRC is set, then use
		   that as the source IP to bind instead of the hard-coded
		   SRC_ADDR one.
		*/
		if (bind_src)
		{
			ret = inet_pton(AF_INET, bind_src, &src_addr.sin_addr);
			if (ret<=0)
			{
				/* If the above failed, then try the
				   built in address. */

				inet_pton(AF_INET, SRC_ADDR, 
						&src_addr.sin_addr);
			}
		}
		else
		{
			inet_pton(AF_INET, SRC_ADDR, &src_addr.sin_addr);
		}


	/* Call real bind function */
		ret = (int)(*bind_ptr)(sockfd, 
					(void *)&src_addr, 
					sizeof(src_addr));
	}
	else
	{

#ifdef DEBUG
		fprintf(stderr, "Calling real bind unmolested\n");
#endif

	/* Call real bind function */
		ret = (int)(*bind_ptr)(sockfd, 
					(void *)my_addr, 
					addrlen);

	}
#ifdef DEBUG
	fprintf(stderr, "The real bind function returned: %d\n", ret);
#endif

	/* Clean up */
	dlclose(libc);

	return ret;

}

/* 
	Sometimes (alot of times) programs don't bother to call bind() 
	if they're just making an outgoing connection. To take care of
	these cases, we need to call bind when they call connect 
	instead. And of course, then call connect as well...
*/

int
connect(int  sockfd, const struct sockaddr *serv_addr, socklen_t addrlen)
{
	int	(*connect_ptr)(int, void *, int);
	void	*libc;
	int	ret;

#ifdef DEBUG
	fprintf(stderr, "connect() override called for addr: %s\n", SRC_ADDR);
#endif

	/* Before we call connect, let's call bind() and make sure we're
	   using our preferred source address.
	*/

	ret = bind(sockfd, NULL, 0); /* Our fake bind doesn't really need
					those params */

	libc = dlopen(LIBC_NAME, RTLD_LAZY);

	if (!libc)
	{
		fprintf(stderr, "Unable to open libc!\n");
		exit(-1);
	}

	*(void **) (&connect_ptr) = dlsym(libc, "connect");

	if (!connect_ptr)
	{
		fprintf(stderr, "Unable to locate connect function in lib\n");
		exit(-1);
	}


	/* Call real connect function */
	ret = (int)(*connect_ptr)(sockfd, (void *)serv_addr, addrlen);

	/* Clean up */
	dlclose(libc);

	return ret;	

}

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *