ESP8266-based WiFi serial modem emulator ROM
at main 480 lines 11 kB view raw
1/* 2 * WiFiPPP 3 * Copyright (c) 2021 joshua stein <jcs@jcs.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18#include "SocksClient.h" 19#include "wifippp.h" 20 21enum { 22 STATE_DEAD = 0, 23 STATE_INIT, 24 STATE_METHOD, 25 STATE_REQUEST, 26 STATE_CONNECT, 27 STATE_PROXY, 28}; 29 30unsigned int tls_ports[] = { 31 443, /* https */ 32 993, /* imaps */ 33 995, /* pop3s */ 34}; 35 36/* https://datatracker.ietf.org/doc/html/rfc1928 */ 37 38#define VERSION_SOCKS5 0x05 39 40#define METHOD_MIN_LENGTH 3 41#define METHOD_AUTH_NONE 0x0 42#define METHOD_AUTH_BAD 0xFF 43 44#define REQUEST_MIN_LENGTH 9 45#define REQUEST_COMMAND_CONNECT 0x1 46#define REQUEST_ATYP_IP 0x1 47#define REQUEST_ATYP_HOSTNAME 0x3 48#define REQUEST_ATYP_IP6 0x4 49 50#define REPLY_SUCCESS 0x0 51#define REPLY_FAIL 0x1 52#define REPLY_EPERM 0x02 53#define REPLY_NET_UNREACHABLE 0x03 54#define REPLY_HOST_UNREACHABLE 0x04 55#define REPLY_CONN_REFUSED 0x05 56#define REPLY_TTL_EXPIRED 0x06 57#define REPLY_BAD_COMMAND 0x07 58#define REPLY_BAD_ADDRESS 0x08 59 60#define REMOTE_CLIENT (tls() ? remote_client_tls : remote_client) 61 62static unsigned long last_buffer_check = 0; 63 64SocksClient::~SocksClient() 65{ 66 local_client.stop(); 67 REMOTE_CLIENT.stop(); 68} 69 70SocksClient::SocksClient(int _slot, WiFiClient _client) 71 : slot(_slot), local_client(_client) 72{ 73 state = STATE_INIT; 74 75 memset(local_buf, 0, sizeof(local_buf)); 76 memset(remote_buf, 0, sizeof(remote_buf)); 77 local_buf_len = 0; 78 remote_buf_len = 0; 79 remote_port = 0; 80 ip4_addr_set_zero(&remote_ip); 81 _tls = false; 82 83#ifdef SOCKS_TRACE 84 syslog.logf(LOG_DEBUG, "[%d] in socks client init with ip %s", slot, 85 local_client.remoteIP().toString().c_str()); 86#endif 87} 88 89bool 90SocksClient::done() 91{ 92 return (state == STATE_DEAD); 93} 94 95void 96SocksClient::finish() 97{ 98 state = STATE_DEAD; 99} 100 101void 102SocksClient::process() 103{ 104 switch (state) { 105 case STATE_DEAD: 106 return; 107 case STATE_INIT: 108 case STATE_METHOD: 109 case STATE_REQUEST: 110 if (local_client.available() && 111 sizeof(local_buf) - local_buf_len > 0) 112 local_buf_len += local_client.read(local_buf + 113 local_buf_len, sizeof(local_buf) - local_buf_len); 114 break; 115 default: 116 /* proxy() will do its own buffering */ 117 break; 118 } 119 120 switch (state) { 121 case STATE_INIT: 122 if (local_buf_len >= METHOD_MIN_LENGTH) { 123 state = STATE_METHOD; 124 break; 125 } 126 break; 127 case STATE_METHOD: 128 verify_method(); 129 break; 130 case STATE_REQUEST: 131 handle_request(); 132 break; 133 case STATE_CONNECT: 134 connect(); 135 break; 136 case STATE_PROXY: 137 proxy(); 138 break; 139 } 140} 141 142void 143SocksClient::fail_close(char code) 144{ 145 unsigned char msg[] = { 146 VERSION_SOCKS5, 147 code, 148 0, 149 REQUEST_ATYP_IP, 150 0, 0, 0, 0, 151 0, 0, 152 }; 153 154 local_client.write(msg, sizeof(msg)); 155 156 finish(); 157} 158 159bool 160SocksClient::verify_version() 161{ 162 if (local_buf[0] != VERSION_SOCKS5) { 163 syslog.logf(LOG_ERR, "[%d] unsupported version 0x%x", slot, 164 local_buf[0]); 165 fail_close(REPLY_FAIL); 166 return false; 167 } 168 169 return true; 170} 171 172bool 173SocksClient::verify_state(int _state) 174{ 175 if (state != _state) { 176 syslog.logf(LOG_ERR, "[%d] in state %d but expected %d", slot, 177 state, _state); 178 state = STATE_DEAD; 179 return false; 180 } 181 182 return true; 183} 184 185void 186SocksClient::verify_method() 187{ 188 int i; 189 190 if (!verify_state(STATE_METHOD)) 191 return; 192 193 if (local_buf_len < METHOD_MIN_LENGTH) 194 return; 195 196 if (!verify_version()) 197 return; 198 199 /* buf[1] is NMETHODS, find one we like */ 200 for (i = 0; i < (unsigned char)local_buf[1]; i++) { 201 if (local_buf[2 + i] == METHOD_AUTH_NONE) { 202 /* send back method selection */ 203 unsigned char msg[] = { 204 VERSION_SOCKS5, 205 METHOD_AUTH_NONE, 206 }; 207 208 local_client.write(msg, sizeof(msg)); 209 state = STATE_REQUEST; 210 local_buf_len = 0; 211 return; 212 } 213 } 214 215 syslog.logf(LOG_ERR, "[%d] no supported auth methods", slot); 216 217 unsigned char msg[] = { 218 VERSION_SOCKS5, 219 METHOD_AUTH_BAD, 220 }; 221 local_client.write(msg, sizeof(msg)); 222 fail_close(REPLY_FAIL); 223} 224 225void 226SocksClient::handle_request() 227{ 228 if (!verify_state(STATE_REQUEST)) 229 return; 230 231 if (local_buf_len < METHOD_MIN_LENGTH) 232 return; 233 234 if (!verify_version()) 235 return; 236 237 if (local_buf[1] != REQUEST_COMMAND_CONNECT) { 238 syslog.logf(LOG_ERR, "[%d] unsupported request command 0x%x", 239 slot, local_buf[1]); 240 fail_close(REPLY_BAD_COMMAND); 241 return; 242 } 243 244 /* local_buf[2] is reserved */ 245 246 switch (local_buf[3]) { 247 case REQUEST_ATYP_IP: 248 if (local_buf_len < 4 + 4 + 2) 249 return; 250 251 IP4_ADDR(&remote_ip, local_buf[4], local_buf[5], local_buf[6], 252 local_buf[7]); 253 remote_port = (uint16_t)((local_buf[8] & 0xff) << 8) | 254 (local_buf[9] & 0xff); 255 256#ifdef SOCKS_TRACE 257 syslog.logf(LOG_DEBUG, "[%d] CONNECT request to IP %s:%d", 258 slot, ipaddr_ntoa(&remote_ip), remote_port); 259#endif 260 261 break; 262 case REQUEST_ATYP_HOSTNAME: { 263 IPAddress resip; 264 unsigned char hostlen; 265 266 if (local_buf_len < 4 + 2) 267 return; 268 269 hostlen = local_buf[4]; 270 if (local_buf_len < (unsigned char)(4 + hostlen + 2)) 271 return; 272 273 remote_hostname = (unsigned char *)malloc(hostlen + 1); 274 if (!remote_hostname) { 275 syslog.logf(LOG_ERR, "[%d] malloc(%d) failure", 276 slot, hostlen + 1); 277 fail_close(REPLY_FAIL); 278 return; 279 } 280 281 memcpy(remote_hostname, local_buf + 5, hostlen); 282 remote_hostname[hostlen] = '\0'; 283 284 /* network order */ 285 remote_port = (uint16_t)((local_buf[5 + hostlen] & 0xff) << 8) | 286 (local_buf[5 + hostlen + 1] & 0xff); 287 288 if (WiFi.hostByName((const char *)remote_hostname, 289 resip) != 1) { 290 syslog.logf(LOG_ERR, "[%d] CONNECT request to " 291 "hostname %s:%d, couldn't resolve name", 292 slot, remote_hostname, remote_port); 293 fail_close(REPLY_BAD_ADDRESS); 294 return; 295 } 296 297 ip4_addr_set_u32(&remote_ip, resip.v4()); 298 299#ifdef SOCKS_TRACE 300 syslog.logf(LOG_DEBUG, "[%d] CONNECT request to hostname " 301 "%s:%d, resolved to IP %s", slot, remote_hostname, 302 remote_port, ipaddr_ntoa(&remote_ip)); 303#endif 304 break; 305 } 306 case REQUEST_ATYP_IP6: 307 syslog.logf(LOG_ERR, "[%d] ipv6 not supported", slot); 308 fail_close(REPLY_BAD_ADDRESS); 309 return; 310 default: 311 syslog.logf(LOG_ERR, "[%d] request ATYP 0x%x not supported", 312 slot, local_buf[3]); 313 fail_close(REPLY_BAD_ADDRESS); 314 return; 315 } 316 317 switch (local_buf[1]) { 318 case REQUEST_COMMAND_CONNECT: 319 state = STATE_CONNECT; 320 return; 321 default: 322 syslog.logf(LOG_ERR, "[%d] unsupported command 0x%x", 323 slot, local_buf[1]); 324 fail_close(REPLY_BAD_COMMAND); 325 return; 326 } 327} 328 329void 330SocksClient::connect() 331{ 332 bool ret; 333 334 _tls = false; 335 336 if (!verify_state(STATE_CONNECT)) 337 return; 338 339 if (remote_port == 0 || ip4_addr_isany_val(remote_ip)) { 340 syslog.logf(LOG_ERR, "[%d] bogus ip/port %s:%d", slot, 341 ipaddr_ntoa(&remote_ip), remote_port); 342 fail_close(REPLY_BAD_ADDRESS); 343 return; 344 } 345 346 for (size_t i = 0; i < sizeof(tls_ports) / sizeof(tls_ports[0]); i++) { 347 if (remote_port == tls_ports[i]) { 348 _tls = true; 349 break; 350 } 351 } 352 353 if (tls()) { 354 remote_client_tls.setInsecure(); 355 remote_client_tls.setBufferSizes(1024, 1024); 356#ifdef SOCKS_TRACE 357 syslog.logf(LOG_DEBUG, "[%d] making TLS connection to %s:%d " 358 "with %d free mem", slot, ipaddr_ntoa(&remote_ip), 359 remote_port, ESP.getFreeHeap()); 360#endif 361 } 362 363 ret = REMOTE_CLIENT.connect(remote_ip, remote_port); 364 if (!ret) { 365 syslog.logf(LOG_WARNING, "[%d] connection to %s:%d%s failed", 366 slot, ipaddr_ntoa(&remote_ip), remote_port, 367 (tls() ? " (TLS decrypt)" : "")); 368 fail_close(REPLY_CONN_REFUSED); 369 return; 370 } 371 372 unsigned char msg[] = { 373 VERSION_SOCKS5, REPLY_SUCCESS, 0, REQUEST_ATYP_IP, 374 ip4_addr1(&remote_ip), ip4_addr2(&remote_ip), 375 ip4_addr3(&remote_ip), ip4_addr4(&remote_ip), 376 (unsigned char)((remote_port >> 8) & 0xff), 377 (unsigned char)(remote_port & 0xff) 378 }; 379 380 local_buf_len = 0; 381 local_client.write(msg, sizeof(msg)); 382 state = STATE_PROXY; 383} 384 385void 386SocksClient::proxy() 387{ 388 size_t len, wrote; 389 390 if (!verify_state(STATE_PROXY)) 391 return; 392 393 /* 394 * Process buffers before checking connection, we may have read some 395 * before the client closed. 396 */ 397 398 /* push out buffered data from remote to local client */ 399 if (remote_buf_len) { 400 len = remote_buf_len; 401 if (len > 64) 402 len = 64; 403 wrote = local_client.write(remote_buf, len); 404 if (wrote) { 405 memmove(remote_buf, remote_buf + wrote, 406 remote_buf_len - wrote); 407 remote_buf_len -= wrote; 408#ifdef SOCKS_TRACE 409 syslog.logf(LOG_DEBUG, "[%d] wrote %d to local " 410 "(%d left)", slot, wrote, remote_buf_len); 411#endif 412 } 413 } 414 415 /* push out buffered data from local to remote client */ 416 if (local_buf_len) { 417 wrote = REMOTE_CLIENT.write(local_buf, local_buf_len); 418 if (wrote) { 419 memmove(local_buf, local_buf + wrote, 420 local_buf_len - wrote); 421 local_buf_len -= wrote; 422#ifdef SOCKS_TRACE 423 syslog.logf(LOG_DEBUG, "[%d] wrote %d to remote " 424 "(%d left)", slot, wrote, local_buf_len); 425#endif 426 } 427 } 428 429 /* buffer new data from local client */ 430 if (local_client.available() && local_buf_len < sizeof(local_buf)) { 431 len = local_client.read(local_buf + local_buf_len, 432 sizeof(local_buf) - local_buf_len); 433#ifdef SOCKS_TRACE 434 syslog.logf(LOG_DEBUG, "[%d] read %d from local (now %d):", 435 slot, len, local_buf_len + len); 436 syslog_buf((const char *)local_buf + local_buf_len, len); 437#endif 438 local_buf_len += len; 439 } 440 441 /* and then read in new data from remote if we have room */ 442 if (REMOTE_CLIENT.available() && 443 (remote_buf_len < sizeof(remote_buf))) { 444 len = REMOTE_CLIENT.read(remote_buf + remote_buf_len, 445 sizeof(remote_buf) - remote_buf_len); 446#ifdef SOCKS_TRACE 447 syslog.logf(LOG_DEBUG, "[%d] read %d from remote (now %d):", 448 slot, len, remote_buf_len + len); 449 syslog_buf((const char *)remote_buf + remote_buf_len, len); 450#endif 451 remote_buf_len += len; 452 } 453 454#ifdef SOCKS_TRACE 455 if (millis() - last_buffer_check > (3 * 1000)) { 456 syslog.logf(LOG_DEBUG, "[%d] local:%d remote:%d free:%d", slot, 457 local_buf_len, remote_buf_len, ESP.getFreeHeap()); 458 last_buffer_check = millis(); 459 } 460#endif 461 462 if (!local_client.connected()) { 463#ifdef SOCKS_TRACE 464 syslog.logf(LOG_DEBUG, "[%d] local client closed", slot); 465#endif 466 REMOTE_CLIENT.stop(); 467 finish(); 468 return; 469 } 470 471 if (!REMOTE_CLIENT.connected()) { 472#ifdef SOCKS_TRACE 473 syslog.logf(LOG_DEBUG, "[%d] remote client closed", slot); 474#endif 475 local_client.stop(); 476 REMOTE_CLIENT.stop(); 477 finish(); 478 return; 479 } 480}