diff --git a/Makefile b/Makefile index ecb3265..55e623a 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ prefix = /usr/local bindir = $(prefix)/bin PROG = microsocks -SRCS = sockssrv.c server.c sblist.c sblist_delete.c +SRCS = sockssrv.c server.c sblist.c sblist_delete.c bind2device.c OBJS = $(SRCS:.c=.o) LIBS = -lpthread diff --git a/README.md b/README.md index 2b7a04a..cc800a9 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ libc is not even 50 KB. that's easily usable even on the cheapest routers. command line options -------------------- - microsocks -1 -q -i listenip -p port -u user -P passw -b bindaddr -w wl + microsocks -1 -q -i listenip -p port -u user -P passw -b bindaddr -B bind2device -w wl all arguments are optional. by default listenip is 0.0.0.0 and port 1080. diff --git a/bind2device.c b/bind2device.c new file mode 100644 index 0000000..ca0a91f --- /dev/null +++ b/bind2device.c @@ -0,0 +1,55 @@ +#undef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200809L + +#define _GNU_SOURCE +#define _DARWIN_C_SOURCE + +#include +#include +#include +#include +#include +#include + +#include "bind2device.h" + +#if (defined(IP_BOUND_IF) || defined(IPV6_BOUND_IF)) + +int bind2device(int sockfd, int socket_family, const char *device) +{ + int ifindex = if_nametoindex(device); + if (ifindex == 0) + return -1; + switch (socket_family) + { +#if defined(IPV6_BOUND_IF) + case AF_INET6: + return setsockopt(sockfd, IPPROTO_IPV6, IPV6_BOUND_IF, &ifindex, sizeof(ifindex)); +#endif +#if defined(IP_BOUND_IF) + case AF_INET: + return setsockopt(sockfd, IPPROTO_IP, IP_BOUND_IF, &ifindex, sizeof(ifindex)); +#endif + default: // can't bind to interface for selected socket_family: operation not supported on socket + errno = EOPNOTSUPP; + return -1; + } +} + +#elif defined(SO_BINDTODEVICE) + +int bind2device(int sockfd, int socket_family, const char *device) +{ + return setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device) + 1); +} + +#else +#pragma message "Platform does not support bind2device, generating stub." + +int bind2device(int sockfd, int socket_family, const char *device) +{ + errno = ENOSYS; // unsupported platform: not implemented + return -1; +} + +#endif diff --git a/bind2device.h b/bind2device.h new file mode 100644 index 0000000..87a1bb8 --- /dev/null +++ b/bind2device.h @@ -0,0 +1,6 @@ +#ifndef BIND2DEVICE_H +#define BIND2DEVICE_H + +int bind2device(int sockfd, int socket_family, const char* device); + +#endif diff --git a/sockssrv.c b/sockssrv.c index dbad9c2..d03d374 100644 --- a/sockssrv.c +++ b/sockssrv.c @@ -35,6 +35,7 @@ #include #include "server.h" #include "sblist.h" +#include "bind2device.h" /* timeout in microseconds on resource exhaustion to prevent excessive cpu usage. */ @@ -68,6 +69,7 @@ static sblist* auth_ips; static pthread_rwlock_t auth_ips_lock = PTHREAD_RWLOCK_INITIALIZER; static const struct server* server; static union sockaddr_union bind_addr = {.v4.sin_family = AF_UNSPEC}; +static const char* bind_device; enum socksstate { SS_1_CONNECTED, @@ -159,6 +161,8 @@ static int connect_socks_target(unsigned char *buf, size_t n, struct client *cli if(resolve(namebuf, port, &remote)) return -EC_GENERAL_FAILURE; struct addrinfo* raddr = addr_choose(remote, &bind_addr); int fd = socket(raddr->ai_family, SOCK_STREAM, 0); + if(bind_device && bind2device(fd, raddr->ai_family, bind_device) == -1) + goto eval_errno; if(fd == -1) { eval_errno: if(fd != -1) close(fd); @@ -383,7 +387,7 @@ static int usage(void) { dprintf(2, "MicroSocks SOCKS5 Server\n" "------------------------\n" - "usage: microsocks -1 -q -i listenip -p port -u user -P pass -b bindaddr -w ips\n" + "usage: microsocks -1 -q -i listenip -p port -u user -P pass -b bindaddr -B bind2device -w ips\n" "all arguments are optional.\n" "by default listenip is 0.0.0.0 and port 1080.\n\n" "option -q disables logging.\n" @@ -413,7 +417,7 @@ int main(int argc, char** argv) { const char *listenip = "0.0.0.0"; char *p, *q; unsigned port = 1080; - while((ch = getopt(argc, argv, ":1qb:i:p:u:P:w:")) != -1) { + while((ch = getopt(argc, argv, ":1qb:B:i:p:u:P:w:")) != -1) { switch(ch) { case 'w': /* fall-through */ case '1': @@ -439,6 +443,10 @@ int main(int argc, char** argv) { case 'b': resolve_sa(optarg, 0, &bind_addr); break; + case 'B': + bind_device = strdup(optarg); + zero_arg(optarg); + break; case 'u': auth_user = strdup(optarg); zero_arg(optarg);