ESP8266-based WiFi serial modem emulator ROM

SocksClient: Use buffers on both sides of the proxy

Read into the buffer, shift it out only when write() says it's
written those bytes, and then don't read new data if the buffer is
full.

+113 -99
+102 -92
SocksClient.cpp
··· 57 57 #define REPLY_BAD_COMMAND 0x07 58 58 #define REPLY_BAD_ADDRESS 0x08 59 59 60 + #define REMOTE_CLIENT (tls() ? remote_client_tls : remote_client) 61 + 60 62 SocksClient::~SocksClient() 61 63 { 62 - client_in.stop(); 63 - if (tls()) 64 - client_out_tls.stop(); 65 - else 66 - client_out.stop(); 64 + local_client.stop(); 65 + REMOTE_CLIENT.stop(); 67 66 } 68 67 69 68 SocksClient::SocksClient(int _slot, WiFiClient _client) 70 - : slot(_slot), client_in(_client) 69 + : slot(_slot), local_client(_client) 71 70 { 72 71 state = STATE_INIT; 73 72 74 - memset(buf, 0, sizeof(buf)); 75 - buflen = 0; 73 + memset(local_buf, 0, sizeof(local_buf)); 74 + memset(remote_buf, 0, sizeof(remote_buf)); 75 + local_buf_len = 0; 76 + remote_buf_len = 0; 76 77 remote_port = 0; 77 78 ip4_addr_set_zero(&remote_ip); 78 79 _tls = false; 79 80 80 81 #ifdef SOCKS_TRACE 81 82 syslog.logf(LOG_DEBUG, "[%d] in socks client init with ip %s", slot, 82 - client_in.remoteIP().toString().c_str()); 83 + local_client.remoteIP().toString().c_str()); 83 84 #endif 84 85 } 85 86 ··· 104 105 case STATE_INIT: 105 106 case STATE_METHOD: 106 107 case STATE_REQUEST: 107 - if (client_in.available() && sizeof(buf) - buflen > 0) 108 - buflen += client_in.read(buf + buflen, 109 - sizeof(buf) - buflen); 108 + if (local_client.available() && 109 + sizeof(local_buf) - local_buf_len > 0) 110 + local_buf_len += local_client.read(local_buf + 111 + local_buf_len, sizeof(local_buf) - local_buf_len); 110 112 break; 111 113 default: 112 114 /* proxy() will do its own buffering */ ··· 115 117 116 118 switch (state) { 117 119 case STATE_INIT: 118 - if (buflen >= METHOD_MIN_LENGTH) { 120 + if (local_buf_len >= METHOD_MIN_LENGTH) { 119 121 state = STATE_METHOD; 120 122 break; 121 123 } ··· 147 149 0, 0, 148 150 }; 149 151 150 - client_in.write(msg, sizeof(msg)); 152 + local_client.write(msg, sizeof(msg)); 151 153 152 154 finish(); 153 155 } ··· 155 157 bool 156 158 SocksClient::verify_version() 157 159 { 158 - if (buf[0] != VERSION_SOCKS5) { 160 + if (local_buf[0] != VERSION_SOCKS5) { 159 161 syslog.logf(LOG_ERR, "[%d] unsupported version 0x%x", slot, 160 - buf[0]); 162 + local_buf[0]); 161 163 fail_close(REPLY_FAIL); 162 164 return false; 163 165 } ··· 169 171 SocksClient::verify_state(int _state) 170 172 { 171 173 if (state != _state) { 172 - syslog.logf(LOG_ERR, "[%d] in state %d but expected %d", 173 - slot, state, _state); 174 + syslog.logf(LOG_ERR, "[%d] in state %d but expected %d", slot, 175 + state, _state); 174 176 state = STATE_DEAD; 175 177 return false; 176 178 } ··· 186 188 if (!verify_state(STATE_METHOD)) 187 189 return; 188 190 189 - if (buflen < METHOD_MIN_LENGTH) 191 + if (local_buf_len < METHOD_MIN_LENGTH) 190 192 return; 191 193 192 194 if (!verify_version()) 193 195 return; 194 196 195 197 /* buf[1] is NMETHODS, find one we like */ 196 - for (i = 0; i < (unsigned char)buf[1]; i++) { 197 - if (buf[2 + i] == METHOD_AUTH_NONE) { 198 + for (i = 0; i < (unsigned char)local_buf[1]; i++) { 199 + if (local_buf[2 + i] == METHOD_AUTH_NONE) { 198 200 /* send back method selection */ 199 201 unsigned char msg[] = { 200 202 VERSION_SOCKS5, 201 203 METHOD_AUTH_NONE, 202 204 }; 203 205 204 - client_in.write(msg, sizeof(msg)); 206 + local_client.write(msg, sizeof(msg)); 205 207 state = STATE_REQUEST; 206 - buflen = 0; 208 + local_buf_len = 0; 207 209 return; 208 210 } 209 211 } ··· 214 216 VERSION_SOCKS5, 215 217 METHOD_AUTH_BAD, 216 218 }; 217 - client_in.write(msg, sizeof(msg)); 219 + local_client.write(msg, sizeof(msg)); 218 220 fail_close(REPLY_FAIL); 219 221 } 220 222 ··· 224 226 if (!verify_state(STATE_REQUEST)) 225 227 return; 226 228 227 - if (buflen < METHOD_MIN_LENGTH) 229 + if (local_buf_len < METHOD_MIN_LENGTH) 228 230 return; 229 231 230 232 if (!verify_version()) 231 233 return; 232 234 233 - if (buf[1] != REQUEST_COMMAND_CONNECT) { 235 + if (local_buf[1] != REQUEST_COMMAND_CONNECT) { 234 236 syslog.logf(LOG_ERR, "[%d] unsupported request command 0x%x", 235 - slot, buf[1]); 237 + slot, local_buf[1]); 236 238 fail_close(REPLY_BAD_COMMAND); 237 239 return; 238 240 } 239 241 240 - /* buf[2] is reserved */ 242 + /* local_buf[2] is reserved */ 241 243 242 - switch (buf[3]) { 244 + switch (local_buf[3]) { 243 245 case REQUEST_ATYP_IP: 244 - if (buflen < 4 + 4 + 2) 246 + if (local_buf_len < 4 + 4 + 2) 245 247 return; 246 248 247 - IP4_ADDR(&remote_ip, buf[4], buf[5], buf[6], buf[7]); 248 - remote_port = (uint16_t)((buf[8] & 0xff) << 8) | 249 - (buf[9] & 0xff); 249 + IP4_ADDR(&remote_ip, local_buf[4], local_buf[5], local_buf[6], 250 + local_buf[7]); 251 + remote_port = (uint16_t)((local_buf[8] & 0xff) << 8) | 252 + (local_buf[9] & 0xff); 250 253 251 254 #ifdef SOCKS_TRACE 252 255 syslog.logf(LOG_DEBUG, "[%d] CONNECT request to IP %s:%d", ··· 258 261 IPAddress resip; 259 262 unsigned char hostlen; 260 263 261 - if (buflen < 4 + 2) 264 + if (local_buf_len < 4 + 2) 262 265 return; 263 266 264 - hostlen = buf[4]; 265 - if (buflen < (unsigned char)(4 + hostlen + 2)) 267 + hostlen = local_buf[4]; 268 + if (local_buf_len < (unsigned char)(4 + hostlen + 2)) 266 269 return; 267 270 268 271 remote_hostname = (unsigned char *)malloc(hostlen + 1); ··· 273 276 return; 274 277 } 275 278 276 - memcpy(remote_hostname, buf + 5, hostlen); 279 + memcpy(remote_hostname, local_buf + 5, hostlen); 277 280 remote_hostname[hostlen] = '\0'; 278 281 279 282 /* network order */ 280 - remote_port = (uint16_t)((buf[5 + hostlen] & 0xff) << 8) | 281 - (buf[5 + hostlen + 1] & 0xff); 283 + remote_port = (uint16_t)((local_buf[5 + hostlen] & 0xff) << 8) | 284 + (local_buf[5 + hostlen + 1] & 0xff); 282 285 283 286 if (WiFi.hostByName((const char *)remote_hostname, 284 287 resip) != 1) { ··· 304 307 return; 305 308 default: 306 309 syslog.logf(LOG_ERR, "[%d] request ATYP 0x%x not supported", 307 - slot, buf[3]); 310 + slot, local_buf[3]); 308 311 fail_close(REPLY_BAD_ADDRESS); 309 312 return; 310 313 } 311 314 312 - switch (buf[1]) { 315 + switch (local_buf[1]) { 313 316 case REQUEST_COMMAND_CONNECT: 314 317 state = STATE_CONNECT; 315 318 return; 316 319 default: 317 320 syslog.logf(LOG_ERR, "[%d] unsupported command 0x%x", 318 - slot, buf[1]); 321 + slot, local_buf[1]); 319 322 fail_close(REPLY_BAD_COMMAND); 320 323 return; 321 324 } ··· 332 335 return; 333 336 334 337 if (remote_port == 0 || ip4_addr_isany_val(remote_ip)) { 335 - syslog.logf(LOG_ERR, "[%d] bogus ip/port %s:%d", 336 - slot, ipaddr_ntoa(&remote_ip), remote_port); 338 + syslog.logf(LOG_ERR, "[%d] bogus ip/port %s:%d", slot, 339 + ipaddr_ntoa(&remote_ip), remote_port); 337 340 fail_close(REPLY_BAD_ADDRESS); 338 341 return; 339 342 } ··· 346 349 } 347 350 348 351 if (tls()) { 349 - client_out_tls.setInsecure(); 350 - client_out_tls.setBufferSizes(1024, 1024); 352 + remote_client_tls.setInsecure(); 353 + remote_client_tls.setBufferSizes(1024, 1024); 351 354 #ifdef SOCKS_TRACE 352 355 syslog.logf(LOG_DEBUG, "[%d] making TLS connection to %s:%d " 353 356 "with %d free mem", slot, ipaddr_ntoa(&remote_ip), 354 357 remote_port, ESP.getFreeHeap()); 355 358 #endif 356 - ret = client_out_tls.connect(remote_ip, remote_port); 357 - } else 358 - ret = client_out.connect(remote_ip, remote_port); 359 + } 359 360 361 + ret = REMOTE_CLIENT.connect(remote_ip, remote_port); 360 362 if (!ret) { 361 363 syslog.logf(LOG_WARNING, "[%d] connection to %s:%d%s failed", 362 364 slot, ipaddr_ntoa(&remote_ip), remote_port, ··· 373 375 (unsigned char)(remote_port & 0xff) 374 376 }; 375 377 376 - buflen = 0; 377 - client_in.write(msg, sizeof(msg)); 378 + local_buf_len = 0; 379 + local_client.write(msg, sizeof(msg)); 378 380 state = STATE_PROXY; 379 381 } 380 382 381 383 void 382 384 SocksClient::proxy() 383 385 { 384 - size_t len; 386 + size_t len, wrote; 385 387 386 388 if (!verify_state(STATE_PROXY)) 387 389 return; ··· 390 392 * Process buffers before checking connection, we may have read some 391 393 * before the client closed. 392 394 */ 393 - while (client_in.available()) { 394 - len = client_in.read(buf, sizeof(buf)); 395 + 396 + /* push out buffered data from remote to local client */ 397 + if (remote_buf_len) { 398 + wrote = local_client.write(remote_buf, remote_buf_len); 399 + if (wrote) { 400 + memmove(remote_buf, remote_buf + wrote, 401 + remote_buf_len - wrote); 402 + remote_buf_len -= wrote; 395 403 #ifdef SOCKS_TRACE 396 - syslog.logf(LOG_DEBUG, "[%d] read %d bytes from client in, " 397 - "sending to client out %s", slot, len, 398 - (tls() ? "tls" : "")); 399 - syslog_buf(buf, len); 404 + syslog.logf(LOG_DEBUG, "[%d] wrote %d to local " 405 + "(%d left)", slot, wrote, remote_buf_len); 400 406 #endif 401 - if (tls()) 402 - client_out_tls.write(buf, len); 403 - else 404 - client_out.write(buf, len); 407 + } 405 408 } 406 409 407 - if (tls()) { 408 - while (client_out_tls.available()) { 409 - len = client_out_tls.read(buf, sizeof(buf)); 410 + /* push out buffered data from local to remote client */ 411 + if (local_buf_len) { 412 + wrote = REMOTE_CLIENT.write(local_buf, local_buf_len); 413 + if (wrote) { 414 + memmove(local_buf, local_buf + wrote, 415 + local_buf_len - wrote); 416 + local_buf_len -= wrote; 410 417 #ifdef SOCKS_TRACE 411 - syslog.logf(LOG_DEBUG, "[%d] read %d bytes from " 412 - "client out tls, sending to client in", 413 - slot, len); 414 - syslog_buf(buf, len); 418 + syslog.logf(LOG_DEBUG, "[%d] wrote %d to remote " 419 + "(%d left)", slot, wrote, local_buf_len); 415 420 #endif 416 - client_in.write(buf, len); 417 421 } 418 - } else { 419 - while (client_out.available()) { 420 - len = client_out.read(buf, sizeof(buf)); 422 + } 423 + 424 + /* buffer new data from local client */ 425 + if (local_client.available() && local_buf_len < sizeof(local_buf)) { 426 + len = local_client.read(local_buf + local_buf_len, 427 + sizeof(local_buf) - local_buf_len); 421 428 #ifdef SOCKS_TRACE 422 - syslog.logf(LOG_DEBUG, "[%d] read %d bytes from " 423 - "client out, sending to client in", slot, len); 424 - syslog_buf(buf, len); 429 + syslog.logf(LOG_DEBUG, "[%d] read %d from local (now %d):", 430 + slot, len, local_buf_len + len); 431 + syslog_buf((const char *)local_buf + local_buf_len, len); 425 432 #endif 426 - client_in.write(buf, len); 427 - } 433 + local_buf_len += len; 428 434 } 429 435 430 - if (!client_in.connected()) { 436 + /* and then read in new data from remote if we have room */ 437 + if (REMOTE_CLIENT.available() && 438 + (remote_buf_len < sizeof(remote_buf))) { 439 + len = REMOTE_CLIENT.read(remote_buf + remote_buf_len, 440 + sizeof(remote_buf) - remote_buf_len); 431 441 #ifdef SOCKS_TRACE 432 - syslog.logf(LOG_DEBUG, "[%d] client in closed", slot); 442 + syslog.logf(LOG_DEBUG, "[%d] read %d from remote (now %d):", 443 + slot, len, remote_buf_len + len); 444 + syslog_buf((const char *)remote_buf + remote_buf_len, len); 433 445 #endif 434 - if (tls()) 435 - client_out_tls.stop(); 436 - else 437 - client_out.stop(); 438 - finish(); 439 - return; 446 + remote_buf_len += len; 440 447 } 441 448 442 - if (tls() && !client_out_tls.connected()) { 449 + if (!local_client.connected()) { 443 450 #ifdef SOCKS_TRACE 444 - syslog.logf(LOG_DEBUG, "[%d] client out tls closed", slot); 451 + syslog.logf(LOG_DEBUG, "[%d] local client closed", slot); 445 452 #endif 446 - client_in.stop(); 453 + REMOTE_CLIENT.stop(); 447 454 finish(); 448 455 return; 449 - } else if (!tls() && !client_out.connected()) { 456 + } 457 + 458 + if (!REMOTE_CLIENT.connected()) { 450 459 #ifdef SOCKS_TRACE 451 - syslog.logf(LOG_DEBUG, "[%d] client out closed", slot); 460 + syslog.logf(LOG_DEBUG, "[%d] remote client closed", slot); 452 461 #endif 453 - client_in.stop(); 462 + local_client.stop(); 463 + REMOTE_CLIENT.stop(); 454 464 finish(); 455 465 return; 456 466 }
+11 -7
SocksClient.h
··· 31 31 bool tls() { return _tls; }; 32 32 int state; 33 33 int slot; 34 - WiFiClient client_in; 35 - WiFiClient client_out; 36 - WiFiClientSecure client_out_tls; 34 + WiFiClient local_client; 35 + WiFiClient remote_client; 36 + WiFiClientSecure remote_client_tls; 37 37 38 38 private: 39 39 void verify_method(); ··· 45 45 void proxy(); 46 46 void finish(); 47 47 48 - void dump_buf(size_t len); 48 + void dump_buf(char *buf, size_t len); 49 49 50 - unsigned char buf[64]; 50 + /* data from local client */ 51 + unsigned char local_buf[64]; 52 + size_t local_buf_len; 53 + 54 + /* data from remote client */ 55 + unsigned char remote_buf[64]; 56 + size_t remote_buf_len; 51 57 unsigned char *remote_hostname; 52 58 ip4_addr_t remote_ip; 53 59 uint16_t remote_port; 54 60 bool _tls; 55 - 56 - size_t buflen; 57 61 };