From 5e71f7daffad301f89571d87ee42dd937b7683f0 Mon Sep 17 00:00:00 2001 From: Vladimir Dubrovin <3proxy@3proxy.ru> Date: Sat, 10 Oct 2026 17:38:44 +0300 Subject: [PATCH] Fix missing device binding for few cases --- src/common.c | 63 +++++++++++++++++++++++++++++++++--------------- src/ftppr.c | 1 + src/proxy.h | 3 +++ src/proxymain.c | 31 +++++++++--------------- src/socks.c | 2 ++ src/udpsockmap.c | 5 ++++ 6 files changed, 66 insertions(+), 39 deletions(-) diff --git a/src/common.c b/src/common.c index 5c6e695..92386b8 100644 --- a/src/common.c +++ b/src/common.c @@ -683,6 +683,49 @@ int connectwithpoll(struct clientparam *param, SOCKET sock, struct sockaddr *sa, static int bindrange(struct clientparam *param, SOCKET sock, PROXYSOCKADDRTYPE *sa, uint32_t range, int *tries); +/* -Di / -De: ties a socket to the interface asked for, for TCP and UDP alike. + Where the option is set per protocol, family is the one of the address the + socket will be bound to. */ +int bindtodevice(struct sockfuncs *sf, void *state, SOCKET sock, const char *device, int family){ + if(!device) return 0; +#if defined SO_BINDTODEVICE + if(sf->_setsockopt(state, sock, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device) + 1)) + return 1; +#elif defined IP_BOUND_IF + { + int idx; + idx = if_nametoindex(device); + if(!idx || (family == AF_INET && sf->_setsockopt(state, sock, IPPROTO_IP, IP_BOUND_IF, &idx, sizeof(idx)))) + return 1; +#ifndef NOIPV6 +#ifndef IPV6_BOUND_IF +#define IPV6_BOUND_IF 125 +#endif + if(family == AF_INET6 && sf->_setsockopt(state, sock, IPPROTO_IPV6, IPV6_BOUND_IF, &idx, sizeof(idx))) return 1; +#endif + } +#endif + return 0; +} + +/* the external side, a socket to be bound to sinsl */ +int extbindtodevice(struct clientparam *param, SOCKET sock){ +#if defined SO_BINDTODEVICE || defined IP_BOUND_IF + return bindtodevice(¶m->srv->so, param->sostate, sock, param->srv->obindtodevice, *SAFAMILY(¶m->sinsl)); +#else + return 0; +#endif +} + +/* the internal side, a socket to be bound to sincl */ +int intbindtodevice(struct clientparam *param, SOCKET sock){ +#if defined SO_BINDTODEVICE || defined IP_BOUND_IF + return bindtodevice(¶m->srv->so, param->sostate, sock, param->srv->ibindtodevice, *SAFAMILY(¶m->sincl)); +#else + return 0; +#endif +} + int doconnect(struct clientparam * param){ SASIZETYPE size; @@ -732,25 +775,7 @@ int doconnect(struct clientparam * param){ setopts(param->remsock, param->srv->srvsockopts); param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg)); -#if defined SO_BINDTODEVICE - if(param->srv->obindtodevice) { - if(param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_BINDTODEVICE, param->srv->obindtodevice, strlen(param->srv->obindtodevice) + 1)) - return 12; - } -#elif defined IP_BOUND_IF - if(param->srv->obindtodevice) { - int idx; - idx = if_nametoindex(param->srv->obindtodevice); - if(!idx || (*SAFAMILY(¶m->sinsl) == AF_INET && param->srv->so._setsockopt(param->sostate, param->remsock, IPPROTO_IP, IP_BOUND_IF, &idx, sizeof(idx)))) - return 12; -#ifndef NOIPV6 -#ifndef IPV6_BOUND_IF -#define IPV6_BOUND_IF 125 -#endif - if(*SAFAMILY(¶m->sinsl) == AF_INET6 && param->srv->so._setsockopt(param->sostate, param->remsock, IPPROTO_IPV6, IPV6_BOUND_IF, &idx, sizeof(idx))) return 12; -#endif - } -#endif + if(extbindtodevice(param, param->remsock)) return 12; #ifdef WITH_UN if(*SAFAMILY(¶m->sinsl) != AF_UNIX) #endif diff --git a/src/ftppr.c b/src/ftppr.c index abe1006..f7d4ce6 100644 --- a/src/ftppr.c +++ b/src/ftppr.c @@ -129,6 +129,7 @@ void * ftpprchild(struct clientparam* param) { clidatasock = INVALID_SOCKET; } if ((clidatasock=socket(SASOCK(¶m->sincl), SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {RETURN(821);} + if(intbindtodevice(param, clidatasock)){RETURN(822);} *SAPORT(¶m->sincl) = 0; if(bindwithrange(param, clidatasock, ¶m->sincl, param->intport)){RETURN(822);} if (pasv) { diff --git a/src/proxy.h b/src/proxy.h index 4473e3b..c347ebe 100644 --- a/src/proxy.h +++ b/src/proxy.h @@ -391,6 +391,9 @@ int readconfig(FILE * fp); void initcommands(void); int connectwithpoll(struct clientparam *param, SOCKET sock, struct sockaddr *sa, SASIZETYPE size, int to); int bindwithrange(struct clientparam *param, SOCKET sock, PROXYSOCKADDRTYPE *sa, uint32_t range); +int bindtodevice(struct sockfuncs *sf, void *state, SOCKET sock, const char *device, int family); +int extbindtodevice(struct clientparam *param, SOCKET sock); +int intbindtodevice(struct clientparam *param, SOCKET sock); #ifdef WITH_PCRE /* One regular expression implementation for the whole program: the pcre commands and every pattern that carries a pcre: prefix. */ diff --git a/src/proxymain.c b/src/proxymain.c index 89c14c2..1d74167 100644 --- a/src/proxymain.c +++ b/src/proxymain.c @@ -856,29 +856,11 @@ int MODULEMAINFUNC (int argc, char** argv){ srv.so._setsockopt(srv.so.state, sock, SOL_SOCKET, SO_REUSEPORT, (char *)&opt, sizeof(int)); #endif } -#if defined SO_BINDTODEVICE - if(srv.ibindtodevice && srv.so._setsockopt(srv.so.state, sock, SOL_SOCKET, SO_BINDTODEVICE, srv.ibindtodevice, strlen(srv.ibindtodevice) + 1)) { +#if defined SO_BINDTODEVICE || defined IP_BOUND_IF + if(bindtodevice(&srv.so, srv.so.state, sock, srv.ibindtodevice, *SAFAMILY(&srv.intsa))) { dolog(&defparam, (unsigned char *)"failed to bind device"); return -12; } -#elif defined IP_BOUND_IF - if(srv.ibindtodevice){ - int idx; - idx = if_nametoindex(srv.ibindtodevice); - if(!idx || (*SAFAMILY(&srv.intsa) == AF_INET && setsockopt(sock, IPPROTO_IP, IP_BOUND_IF, &idx, sizeof(idx)))) { - dolog(&defparam, (unsigned char *)"failed to bind device"); - return -12; - } -#ifndef NOIPV6 -#ifndef IPV6_BOUND_IF -#define IPV6_BOUND_IF 125 -#endif - if((*SAFAMILY(&srv.intsa) == AF_INET6 && srv.so._setsockopt(srv.so.state, sock, IPPROTO_IPV6, IPV6_BOUND_IF, &idx, sizeof(idx)))) { - dolog(&defparam, (unsigned char *)"failed to bind device"); - return -12; - } -#endif - } #endif } #ifdef WITH_UN @@ -1032,6 +1014,15 @@ int MODULEMAINFUNC (int argc, char** argv){ setopts(new_sock, srv.cbcsockopts); parsehost(srv.family, cbc_string, (struct sockaddr *)&defparam.sincr); +#if defined SO_BINDTODEVICE || defined IP_BOUND_IF + /* as for a listening socket, an interface that cannot be + bound to is not worth retrying */ + if(bindtodevice(&so, so.state, new_sock, srv.ibindtodevice, *SAFAMILY(&defparam.sincr))) { + dolog(&defparam, (unsigned char *)"failed to bind device"); + so._closesocket(so.state, new_sock); + break; + } +#endif if(connectwithpoll(NULL, new_sock,(struct sockaddr *)&defparam.sincr,SASIZE(&defparam.sincr),CONNBACK_TO)) { so._closesocket(so.state, new_sock); new_sock = INVALID_SOCKET; diff --git a/src/socks.c b/src/socks.c index a3576a0..5cdc1d3 100644 --- a/src/socks.c +++ b/src/socks.c @@ -185,6 +185,7 @@ void * sockschild(struct clientparam* param) { if(command == 2){ if ((param->remsock=param->srv->so._socket(param->sostate, SASOCK(¶m->req), SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {RETURN (11);} setopts(param->remsock, param->srv->lissockopts); + if(extbindtodevice(param, param->remsock)) {RETURN (12);} } break; @@ -244,6 +245,7 @@ fflush(stderr); #else fcntl(param->clisock, F_SETFL, O_NONBLOCK | fcntl(param->clisock, F_GETFL)); #endif + if(intbindtodevice(param, param->clisock)) {RETURN (12);} sin = param->sincl; *SAPORT(&sin) = 0; /* the port the client is told to send its datagrams to */ diff --git a/src/udpsockmap.c b/src/udpsockmap.c index 96df681..ff84a7d 100644 --- a/src/udpsockmap.c +++ b/src/udpsockmap.c @@ -121,6 +121,11 @@ int udpbind(struct clientparam *param) fcntl(s, F_SETFL, O_NONBLOCK | fcntl(s, F_GETFL)); #endif param->remsock = s; + if (extbindtodevice(param, param->remsock)) { + param->srv->so._closesocket(param->sostate, param->remsock); + param->remsock = INVALID_SOCKET; + return 12; + } if (bindwithrange(param, param->remsock, ¶m->sinsl, param->extport)) { if (param->extport) { param->srv->so._closesocket(param->sostate, param->remsock);