Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 106 additions & 29 deletions Windows/src/ProxyBridge.c
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,18 @@
return sent;
}

static int recv_all(SOCKET sock, char *buf, int len)
{
int received = 0;
while (received < len) {
int n = recv(sock, buf + received, len - received, 0);
if (n <= 0) return n == 0 ? 0 : SOCKET_ERROR;
received += n;
}
return received;
}

static UINT32 parse_ipv4(const char *ip);
static UINT32 resolve_hostname(const char *hostname);
static int socks5_connect(SOCKET s, UINT32 dest_ip, UINT16 dest_port);
static int socks5_udp_associate(SOCKET s, struct sockaddr_in *relay_addr);
static DWORD WINAPI udp_relay_server(LPVOID arg);
Expand Down Expand Up @@ -560,6 +570,15 @@
}
else if (action == RULE_ACTION_PROXY)
{
// Only redirect new connections (SYN packets). Non-SYN packets for
// untracked connections belong to direct flows established before a
// rule matched; redirecting them would corrupt the TCP state machine.
if (!tcp_header->Syn)
{
WinDivertSend(windivert_handle, packet, packet_len, NULL, &addr);
continue;
}

add_connection(src_port, src_ip, orig_dest_ip, orig_dest_port);

tcp_header->DstPort = htons(g_local_relay_port);
Expand Down Expand Up @@ -678,17 +697,23 @@
return 0;
}

tcp_table = (MIB_TCPTABLE_OWNER_PID *)malloc(size);
if (tcp_table == NULL)
// The table can grow between the size query and the data query; retry on
// ERROR_INSUFFICIENT_BUFFER so we always get a complete snapshot.
while (1)
{
return 0;
}
tcp_table = (MIB_TCPTABLE_OWNER_PID *)malloc(size);
if (tcp_table == NULL)
return 0;

if (GetExtendedTcpTable(tcp_table, &size, FALSE, AF_INET,
TCP_TABLE_OWNER_PID_ALL, 0) != NO_ERROR)
{
DWORD ret = GetExtendedTcpTable(tcp_table, &size, FALSE, AF_INET,
TCP_TABLE_OWNER_PID_ALL, 0);
if (ret == NO_ERROR)
break;
free(tcp_table);
return 0;
tcp_table = NULL;
if (ret != ERROR_INSUFFICIENT_BUFFER)
return 0;
// size was updated by GetExtendedTcpTable; retry with the new size
}

for (DWORD i = 0; i < tcp_table->dwNumEntries; i++)
Expand Down Expand Up @@ -729,17 +754,21 @@
return 0;
}

udp_table = (MIB_UDPTABLE_OWNER_PID *)malloc(size);
if (udp_table == NULL)
// Retry on ERROR_INSUFFICIENT_BUFFER in case the table grows between calls.
while (1)
{
return 0;
}
udp_table = (MIB_UDPTABLE_OWNER_PID *)malloc(size);
if (udp_table == NULL)
return 0;

if (GetExtendedUdpTable(udp_table, &size, FALSE, AF_INET,
UDP_TABLE_OWNER_PID, 0) != NO_ERROR)
{
DWORD ret = GetExtendedUdpTable(udp_table, &size, FALSE, AF_INET,
UDP_TABLE_OWNER_PID, 0);
if (ret == NO_ERROR)
break;
free(udp_table);
return 0;
udp_table = NULL;
if (ret != ERROR_INSUFFICIENT_BUFFER)
return 0;
}

// First pass: Try exact match (IP + port)
Expand Down Expand Up @@ -1223,7 +1252,7 @@
}


static int socks5_connect(SOCKET s, UINT32 dest_ip, UINT16 dest_port)

Check failure on line 1255 in Windows/src/ProxyBridge.c

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 36 to the 25 allowed.

See more on https://sonarcloud.io/project/issues?id=InterceptSuite_ProxyBridge&issues=AZzS8nxwHR1OXVxFWpcP&open=AZzS8nxwHR1OXVxFWpcP&pullRequest=87
{
unsigned char buf[SOCKS5_BUFFER_SIZE];
int len;
Expand Down Expand Up @@ -1252,7 +1281,7 @@
}
}

len = recv(s, (char*)buf, 2, 0);
len = recv_all(s, (char*)buf, 2);
if (len != 2 || buf[0] != SOCKS5_VERSION)
{
log_message("SOCKS5: Invalid auth response");
Expand Down Expand Up @@ -1289,7 +1318,7 @@
return -1;
}

len = recv(s, (char*)buf, 2, 0);
len = recv_all(s, (char*)buf, 2);
if (len != 2 || buf[0] != 0x01 || buf[1] != 0x00)
{
log_message("SOCKS5: Authentication failed");
Expand Down Expand Up @@ -1320,13 +1349,36 @@
return -1;
}

len = recv(s, (char*)buf, 10, 0);
if (len < 10 || buf[0] != SOCKS5_VERSION || buf[1] != 0x00)
// Read the fixed-length header: VER, REP, RSV, ATYP (4 bytes)
len = recv_all(s, (char*)buf, 4);
if (len != 4 || buf[0] != SOCKS5_VERSION || buf[1] != 0x00)
{
log_message("SOCKS5: CONNECT failed (reply=%d)", len > 1 ? buf[1] : -1);
return -1;
}

// Drain the BND.ADDR and BND.PORT based on ATYP so the socket is clean
// for the bidirectional relay that follows.
unsigned char atyp = buf[3];
if (atyp == 0x01) // IPv4: 4 bytes address + 2 bytes port
{
len = recv_all(s, (char*)buf, 6);
if (len != 6) return -1;
}
else if (atyp == 0x04) // IPv6: 16 bytes address + 2 bytes port
{
len = recv_all(s, (char*)buf, 18);
if (len != 18) return -1;
}
else if (atyp == 0x03) // Domain: 1 byte length + N bytes domain + 2 bytes port
{
len = recv_all(s, (char*)buf, 1);
if (len != 1) return -1;
unsigned char domain_len = buf[0];
len = recv_all(s, (char*)buf, (int)domain_len + 2);
if (len != (int)domain_len + 2) return -1;
}

return 0;
}

Expand Down Expand Up @@ -1426,7 +1478,7 @@
return 0;
}

static int socks5_udp_associate(SOCKET s, struct sockaddr_in *relay_addr)

Check failure on line 1481 in Windows/src/ProxyBridge.c

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 31 to the 25 allowed.

See more on https://sonarcloud.io/project/issues?id=InterceptSuite_ProxyBridge&issues=AZzS8nxwHR1OXVxFWpcQ&open=AZzS8nxwHR1OXVxFWpcQ&pullRequest=87
{
unsigned char buf[SOCKS5_BUFFER_SIZE];
int len;
Expand All @@ -1449,7 +1501,7 @@
return -1;
}

len = recv(s, (char*)buf, 2, 0);
len = recv_all(s, (char*)buf, 2);
if (len != 2 || buf[0] != SOCKS5_VERSION)
return -1;

Expand All @@ -1472,7 +1524,7 @@
if (send(s, (char*)buf, 3 + user_len + pass_len, 0) != (int)(3 + user_len + pass_len))
return -1;

len = recv(s, (char*)buf, 2, 0);
len = recv_all(s, (char*)buf, 2);
if (len != 2 || buf[0] != 0x01 || buf[1] != 0x00)
return -1;
}
Expand All @@ -1495,13 +1547,38 @@
if (send(s, (char*)buf, 10, 0) != 10)
return -1;

len = recv(s, (char*)buf, 10, 0);
if (len < 10 || buf[0] != SOCKS5_VERSION || buf[1] != 0x00)
// Read VER, REP, RSV, ATYP first
len = recv_all(s, (char*)buf, 4);
if (len != 4 || buf[0] != SOCKS5_VERSION || buf[1] != 0x00)
return -1;

relay_addr->sin_family = AF_INET;
relay_addr->sin_addr.s_addr = *(UINT32*)&buf[4];
relay_addr->sin_port = *(UINT16*)&buf[8];
// For UDP_ASSOCIATE we need the relay address; only handle IPv4 BND.ADDR here.
// Read the remaining 6 bytes (4-byte IPv4 address + 2-byte port).
unsigned char atyp = buf[3];
if (atyp == 0x01)
{
len = recv_all(s, (char*)buf, 6);
if (len != 6) return -1;
relay_addr->sin_family = AF_INET;
relay_addr->sin_addr.s_addr = *(UINT32*)&buf[0];
relay_addr->sin_port = *(UINT16*)&buf[4];
}
else if (atyp == 0x04) // IPv6: drain 16-byte address + 2-byte port
{
recv_all(s, (char*)buf, 18);
return -1;
}
else if (atyp == 0x03) // Domain: drain 1-byte length + domain + 2-byte port
{
len = recv_all(s, (char*)buf, 1);
if (len == 1)
recv_all(s, (char*)buf, (int)buf[0] + 2);
return -1;
}
else
{
return -1;
}

return 0;
}
Expand Down Expand Up @@ -2926,7 +3003,7 @@
}

WinDivertSetParam(windivert_handle, WINDIVERT_PARAM_QUEUE_LENGTH, 8192);
WinDivertSetParam(windivert_handle, WINDIVERT_PARAM_QUEUE_TIME, 8); // 8ms for low latency
WinDivertSetParam(windivert_handle, WINDIVERT_PARAM_QUEUE_TIME, 2000); // 2s to avoid drops under load

for (int i = 0; i < NUM_PACKET_THREADS; i++)
{
Expand Down