upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/proxy.cpp
diff options
context:
space:
mode:
authorNsable-sh <nsable-sh@noreply.git.bitflash.network>2026-08-11 15:51:52 -0300
committerNsable-sh <nsable-sh@noreply.git.bitflash.network>2026-08-11 15:51:52 -0300
commit066379ebcb1dac720221fe1a1841fa6f59df21d9 (patch)
treec679ea133384fb33f2f77602e99430896ef9b50c /src/proxy.cpp
parentff3829b28fd2ddc85a9a3bb4424cd5b4719854ab (diff)
net: harden SOCKS5 proxy parsing and tests
Diffstat (limited to 'src/proxy.cpp')
-rw-r--r--src/proxy.cpp115
1 files changed, 95 insertions, 20 deletions
diff --git a/src/proxy.cpp b/src/proxy.cpp
index 1e62ff9..d7dc324 100644
--- a/src/proxy.cpp
+++ b/src/proxy.cpp
@@ -5,6 +5,8 @@
5#include "proxy.h" 5#include "proxy.h"
6#include "sockcount.h" 6#include "sockcount.h"
7 7
8#include <errno.h>
9
8static CCriticalSection cs_socks5; 10static CCriticalSection cs_socks5;
9static bool g_fSocks5Proxy = false; 11static bool g_fSocks5Proxy = false;
10static std::string g_socks5Host; 12static std::string g_socks5Host;
@@ -60,6 +62,40 @@ static bool ValidProxyHostChar(char c)
60 return isalnum((unsigned char)c) || c == '.' || c == '-' || c == '_'; 62 return isalnum((unsigned char)c) || c == '.' || c == '-' || c == '_';
61} 63}
62 64
65static bool ValidProxyIpv6HostChar(char c)
66{
67 return isxdigit((unsigned char)c) || c == ':' || c == '.';
68}
69
70static bool ParsePort(const std::string& port, unsigned short& portOut,
71 std::string& errOut)
72{
73 if (port.empty())
74 {
75 errOut = "port is empty";
76 return false;
77 }
78 for (size_t i = 0; i < port.size(); i++)
79 {
80 if (!isdigit((unsigned char)port[i]))
81 {
82 errOut = "port is not numeric";
83 return false;
84 }
85 }
86
87 errno = 0;
88 char* end = NULL;
89 long nPort = strtol(port.c_str(), &end, 10);
90 if (errno == ERANGE || end == NULL || *end != '\0' || nPort <= 0 || nPort > 65535)
91 {
92 errOut = "port is out of range";
93 return false;
94 }
95 portOut = (unsigned short)nPort;
96 return true;
97}
98
63bool BtfParseSocks5Proxy(const std::string& spec, std::string& hostOut, 99bool BtfParseSocks5Proxy(const std::string& spec, std::string& hostOut,
64 unsigned short& portOut, std::string& errOut) 100 unsigned short& portOut, std::string& errOut)
65{ 101{
@@ -67,45 +103,76 @@ bool BtfParseSocks5Proxy(const std::string& spec, std::string& hostOut,
67 portOut = 0; 103 portOut = 0;
68 errOut.clear(); 104 errOut.clear();
69 105
70 size_t colon = spec.rfind(':'); 106 std::string host;
71 if (colon == std::string::npos || colon == 0 || colon + 1 >= spec.size()) 107 std::string port;
108 bool fBracketedIpv6 = false;
109 if (!spec.empty() && spec[0] == '[')
72 { 110 {
73 errOut = "expected HOST:PORT"; 111 size_t close = spec.find(']');
74 return false; 112 if (close == std::string::npos || close == 1 || close + 1 >= spec.size() || spec[close + 1] != ':')
113 {
114 errOut = "expected [IPv6]:PORT";
115 return false;
116 }
117 host = spec.substr(1, close - 1);
118 port = spec.substr(close + 2);
119 fBracketedIpv6 = true;
120 }
121 else
122 {
123 size_t colon = spec.rfind(':');
124 if (colon == std::string::npos || colon == 0 || colon + 1 >= spec.size())
125 {
126 errOut = "expected HOST:PORT";
127 return false;
128 }
129 host = spec.substr(0, colon);
130 port = spec.substr(colon + 1);
131 if (host.find(':') != std::string::npos)
132 {
133 errOut = "IPv6 proxy hosts must use [ADDR]:PORT";
134 return false;
135 }
75 } 136 }
76 137
77 std::string host = spec.substr(0, colon);
78 std::string port = spec.substr(colon + 1);
79 if (host.size() > 255) 138 if (host.size() > 255)
80 { 139 {
81 errOut = "host is too long"; 140 errOut = "host is too long";
82 return false; 141 return false;
83 } 142 }
84 for (size_t i = 0; i < host.size(); i++) 143 if (fBracketedIpv6)
85 { 144 {
86 if (!ValidProxyHostChar(host[i])) 145 bool fHasColon = false;
146 for (size_t i = 0; i < host.size(); i++)
147 {
148 fHasColon = fHasColon || host[i] == ':';
149 if (!ValidProxyIpv6HostChar(host[i]))
150 {
151 errOut = "IPv6 proxy host contains unsupported characters";
152 return false;
153 }
154 }
155 if (!fHasColon)
87 { 156 {
88 errOut = "host contains unsupported characters"; 157 errOut = "bracketed proxy host is not IPv6";
89 return false; 158 return false;
90 } 159 }
91 } 160 }
92 for (size_t i = 0; i < port.size(); i++) 161 else
93 { 162 {
94 if (!isdigit((unsigned char)port[i])) 163 for (size_t i = 0; i < host.size(); i++)
95 { 164 {
96 errOut = "port is not numeric"; 165 if (!ValidProxyHostChar(host[i]))
97 return false; 166 {
167 errOut = "host contains unsupported characters";
168 return false;
169 }
98 } 170 }
99 } 171 }
100 int nPort = atoi(port.c_str()); 172 if (!ParsePort(port, portOut, errOut))
101 if (nPort <= 0 || nPort > 65535)
102 {
103 errOut = "port is out of range";
104 return false; 173 return false;
105 }
106 174
107 hostOut = host; 175 hostOut = host;
108 portOut = (unsigned short)nPort;
109 return true; 176 return true;
110} 177}
111 178
@@ -147,6 +214,8 @@ std::string BtfSocks5ProxyName()
147 { 214 {
148 if (!g_fSocks5Proxy) 215 if (!g_fSocks5Proxy)
149 return ""; 216 return "";
217 if (g_socks5Host.find(':') != std::string::npos)
218 return strprintf("[%s]:%u", g_socks5Host.c_str(), (unsigned)g_socks5Port);
150 return strprintf("%s:%u", g_socks5Host.c_str(), (unsigned)g_socks5Port); 219 return strprintf("%s:%u", g_socks5Host.c_str(), (unsigned)g_socks5Port);
151 } 220 }
152 return ""; 221 return "";
@@ -207,8 +276,14 @@ static bool Socks5Connect(SOCKET s, const std::string& destHost,
207 return false; 276 return false;
208 277
209 unsigned char rep[4] = {0,0,0,0}; 278 unsigned char rep[4] = {0,0,0,0};
210 if (!ReadN(s, rep, sizeof(rep)) || rep[0] != 0x05 || rep[1] != 0x00) 279 if (!ReadN(s, rep, sizeof(rep)) || rep[0] != 0x05)
211 return false; 280 return false;
281 if (rep[1] != 0x00)
282 {
283 error("SOCKS5 CONNECT to %s:%u failed with reply 0x%02x\n",
284 destHost.c_str(), (unsigned)destPort, (unsigned)rep[1]);
285 return false;
286 }
212 int nAddr = 0; 287 int nAddr = 0;
213 if (rep[3] == 0x01) nAddr = 4; 288 if (rep[3] == 0x01) nAddr = 4;
214 else if (rep[3] == 0x04) nAddr = 16; 289 else if (rep[3] == 0x04) nAddr = 16;