Skip to content

Commit c144be7

Browse files
Copilotdrk1wi
andauthored
Fix multiple port emulation bypasses, DoS, and stability issues
Co-authored-by: drk1wi <2052966+drk1wi@users.noreply.github.com>
1 parent ef312c7 commit c144be7

4 files changed

Lines changed: 104 additions & 37 deletions

File tree

plan.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
- [ ] Fix IPv6 and TPROXY bypass in destination port resolution
2+
- [ ] Fix CPU exhaustion (O(N) search) when maxfd is reached by using the min-heap
3+
- [ ] Fix sockaddr truncation in accept() by using sockaddr_storage

src/Server.cpp

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -102,47 +102,67 @@ Server::Server(Configuration* configuration)
102102
epoll_ctl(epfd, EPOLL_CTL_ADD, shutfd, &ev);
103103

104104
/* tcp listen socket */
105-
listenfd = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
106-
if (listenfd == -1)
107-
{
108-
perror("Socket creation error");
109-
exit(1);
110-
}
111-
112-
int reuse = 1;
113-
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
114-
115-
memset(&my_name, 0, sizeof(my_name));
116-
my_name.sin_family = PF_INET;
105+
struct addrinfo hints, *res, *p;
106+
memset(&hints, 0, sizeof(hints));
107+
hints.ai_family = AF_UNSPEC;
108+
hints.ai_socktype = SOCK_STREAM;
109+
hints.ai_flags = AI_PASSIVE;
117110

111+
const char* ip_str = NULL;
118112
if (configuration->getConfigValue(OPT_IP))
119113
{
120-
fprintf(stdout, "-> Binding to iface: %s\n",
121-
configuration->getBindIP().c_str());
122-
inet_aton(configuration->getBindIP().c_str(), &my_name.sin_addr);
114+
ip_str = configuration->getBindIP().c_str();
115+
fprintf(stdout, "-> Binding to iface: %s\n", ip_str);
123116
}
124117
else
125118
{
126-
my_name.sin_addr.s_addr = INADDR_ANY;
119+
// If binding to any interface, prefer IPv6 (which handles IPv4 via v4-mapped)
120+
hints.ai_family = AF_INET6;
127121
}
128-
122+
123+
char port_str[16];
124+
snprintf(port_str, sizeof(port_str), "%d", configuration->getConfigValue(OPT_PORT) ? configuration->getPort() : DEFAULT_PORT);
125+
129126
if (configuration->getConfigValue(OPT_PORT))
130127
{
131-
fprintf(stdout, "-> Binding to port: %d\n",
132-
configuration->getPort());
133-
my_name.sin_port = htons(configuration->getPort());
128+
fprintf(stdout, "-> Binding to port: %s\n", port_str);
134129
}
135-
else
130+
131+
if (getaddrinfo(ip_str, port_str, &hints, &res) != 0)
136132
{
137-
my_name.sin_port = htons(DEFAULT_PORT);
133+
perror("getaddrinfo error");
134+
exit(1);
138135
}
139136

140-
status = bind(listenfd, (struct sockaddr*)&my_name, sizeof(my_name));
141-
if (status == -1)
137+
for (p = res; p != NULL; p = p->ai_next)
138+
{
139+
listenfd = socket(p->ai_family, p->ai_socktype | SOCK_NONBLOCK, p->ai_protocol);
140+
if (listenfd == -1)
141+
continue;
142+
143+
int reuse = 1;
144+
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
145+
146+
if (p->ai_family == AF_INET6)
147+
{
148+
int no = 0;
149+
setsockopt(listenfd, IPPROTO_IPV6, IPV6_V6ONLY, &no, sizeof(no));
150+
}
151+
152+
if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0)
153+
break;
154+
155+
close(listenfd);
156+
}
157+
158+
if (p == NULL)
142159
{
143160
perror("Binding error");
161+
freeaddrinfo(res);
144162
exit(1);
145163
}
164+
165+
freeaddrinfo(res);
146166

147167
status = listen(listenfd, 1024);
148168
if (status == -1)

src/Server.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include <sys/resource.h>
4444
#include <sys/eventfd.h>
4545
#include <netinet/in.h>
46+
#include <netdb.h>
4647
#include <stdio.h>
4748
#include <unistd.h>
4849
#include <signal.h>

src/connection.cpp

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@
4242
#include "connection.h"
4343
#include "Configuration.h"
4444

45+
#ifndef IP6T_SO_ORIGINAL_DST
46+
#define IP6T_SO_ORIGINAL_DST 80
47+
#endif
48+
4549
extern Configuration* configuration;
4650

4751
struct timeout_entry
@@ -130,9 +134,9 @@ static void conn_close(int fd, int epfd, struct conn_state* cs, int* nactive)
130134
static void do_accept(int listenfd, int epfd, struct conn_state* cs,
131135
int maxfd, Configuration* conf, int* nactive, timeout_heap& heap)
132136
{
133-
struct sockaddr_in peer;
137+
struct sockaddr_storage peer;
134138
socklen_t peerlen;
135-
struct sockaddr_in orig;
139+
struct sockaddr_storage orig;
136140
socklen_t origlen;
137141
int fd;
138142
uint64_t now = mono_now();
@@ -145,6 +149,29 @@ static void do_accept(int listenfd, int epfd, struct conn_state* cs,
145149
{
146150
if (errno == EAGAIN || errno == EWOULDBLOCK)
147151
break;
152+
if (errno == EMFILE || errno == ENFILE)
153+
{
154+
int victim = -1;
155+
while (!heap.empty())
156+
{
157+
const timeout_entry& top = heap.top();
158+
int v = top.fd;
159+
uint32_t gen = top.gen;
160+
heap.pop();
161+
162+
if (cs[v].phase != 0 && cs[v].gen == gen)
163+
{
164+
victim = v;
165+
break;
166+
}
167+
}
168+
if (victim >= 0)
169+
{
170+
conn_close(victim, epfd, cs, nactive);
171+
continue;
172+
}
173+
// If no connections to evict, we have a leak or something else took fds. Break to avoid spin.
174+
}
148175
perror("accept");
149176
break;
150177
}
@@ -156,16 +183,17 @@ static void do_accept(int listenfd, int epfd, struct conn_state* cs,
156183
* free a slot for the next accept() call. */
157184
close(fd);
158185
int victim = -1;
159-
uint64_t earliest = UINT64_MAX;
160-
for (int v = 0; v < maxfd; v++)
186+
while (!heap.empty())
161187
{
162-
if (cs[v].phase == 0)
163-
continue;
164-
uint64_t exp = cs[v].t_accept + cs[v].t_timeout;
165-
if (exp < earliest)
188+
const timeout_entry& top = heap.top();
189+
int v = top.fd;
190+
uint32_t gen = top.gen;
191+
heap.pop();
192+
193+
if (cs[v].phase != 0 && cs[v].gen == gen)
166194
{
167-
earliest = exp;
168195
victim = v;
196+
break;
169197
}
170198
}
171199
if (victim >= 0)
@@ -177,11 +205,26 @@ static void do_accept(int listenfd, int epfd, struct conn_state* cs,
177205

178206
/* get the port the scanner was actually aiming for */
179207
origlen = sizeof(orig);
180-
uint16_t dport = DEFAULT_PORT;
181-
if (getsockopt(fd, SOL_IP, SO_ORIGINAL_DST,
182-
(struct sockaddr*)&orig, &origlen) == 0)
208+
uint16_t dport = conf->getPort() ? conf->getPort() : DEFAULT_PORT;
209+
210+
if (getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, &orig, &origlen) == 0)
211+
{
212+
dport = ntohs(((struct sockaddr_in*)&orig)->sin_port);
213+
}
214+
else if (getsockopt(fd, SOL_IPV6, IP6T_SO_ORIGINAL_DST, &orig, &origlen) == 0)
215+
{
216+
dport = ntohs(((struct sockaddr_in6*)&orig)->sin6_port);
217+
}
218+
else
183219
{
184-
dport = ntohs(orig.sin_port);
220+
origlen = sizeof(orig);
221+
if (getsockname(fd, (struct sockaddr*)&orig, &origlen) == 0)
222+
{
223+
if (orig.ss_family == AF_INET)
224+
dport = ntohs(((struct sockaddr_in*)&orig)->sin_port);
225+
else if (orig.ss_family == AF_INET6)
226+
dport = ntohs(((struct sockaddr_in6*)&orig)->sin6_port);
227+
}
185228
}
186229

187230
/* look up the banner for this port */

0 commit comments

Comments
 (0)