socks_proxy: поддержка HTTP-прокси (GET/POST/HEAD/OPTIONS/...) — не только CONNECT

process_http_request() теперь обрабатывает любые HTTP-методы:
- CONNECT — туннель как раньше, без изменений
- GET/POST/PUT/HEAD/OPTIONS/... — парсинг абсолютного URL, DNS,
  send_connect + пересборка строки с относительным путём + тело DATA

test_socks_http_proxy: +3-й worker (http_proxy через -x без --proxytunnel),
+проверки POST (echo), HEAD (200 OK), OPTIONS (Allow). Все 78 проверок — PASS.
This commit is contained in:
Evgeny committed 2026-07-02 22:04:37 +03:00
1 parent 754c08e9c2
commit 2cb85cbc3d
2 files changed
+180 -28

No files matched your search

+112 -23
View File
@@ -186,48 +186,137 @@ error: {
}
// ====================================================================
// HTTP CONNECT handshake
// HTTP CONNECT / HTTP proxy handshake
// ====================================================================
static void process_http_request(struct socks_proxy_conn* c) {
// Ищем "\r\n\r\n" или первую "\r\n" для строки CONNECT
char* end = memmem(c->buf, c->buf_len, "\r\n\r\n", 4);
if (!end) { end = memmem(c->buf, c->buf_len, "\r\n", 2); if (!end) return; }
char* line_end = memmem(c->buf, c->buf_len, "\r\n", 2);
if (!line_end) return;
uint16_t line_len = (uint16_t)((uint8_t*)end - c->buf);
uint16_t line_len = (uint16_t)((uint8_t*)line_end - c->buf);
if (line_len < 8) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: http request too short"); goto error; }
// Парсим "CONNECT host:port HTTP/1.1"
char line[512]; if (line_len > sizeof(line) - 1) line_len = sizeof(line) - 1;
memcpy(line, c->buf, line_len); line[line_len] = '\0';
char host_port[256];
if (sscanf(line, "CONNECT %255s HTTP/", host_port) != 1) {
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: bad http connect: %s", line);
// ===== CONNECT =====
if (strncmp(line, "CONNECT ", 8) == 0) {
char host_port[256];
if (sscanf(line, "CONNECT %255s HTTP/", host_port) != 1) {
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: bad http connect: %s", line);
goto error;
}
char* colon = strrchr(host_port, ':');
if (!colon) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: no port in %s", host_port); goto error; }
*colon = '\0'; int port = atoi(colon + 1);
if (port <= 0 || port > 65535) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: bad port %d", port); goto error; }
struct hostent* he = gethostbyname(host_port);
if (!he || he->h_addrtype != AF_INET) {
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: DNS failed for %s", host_port);
uint8_t resp[] = "HTTP/1.1 502 Bad Gateway\r\n\r\n";
write_to_client(c, resp, (uint16_t)strlen((char*)resp)); tcp_conn_push_close(c->tc); return;
}
memcpy(c->dest_ip, he->h_addr, 4); c->dest_port = htons((uint16_t)port);
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socks_proxy: HTTP CONNECT %s → %d.%d.%d.%d:%d",
host_port, c->dest_ip[0], c->dest_ip[1], c->dest_ip[2], c->dest_ip[3], port);
c->buf_len = 0;
c->state = HTTP_STATE_RELAY;
uint8_t resp[] = "HTTP/1.1 200 Connection Established\r\n\r\n";
write_to_client(c, resp, (uint16_t)strlen((char*)resp));
if (send_connect(c) < 0) { tcp_conn_push_close(c->tc); }
return;
}
// ===== Не-CONNECT: HTTP-прокси (GET, POST, PUT, HEAD, OPTIONS, ...) =====
char* hdr_end = memmem(c->buf, c->buf_len, "\r\n\r\n", 4);
if (!hdr_end) return;
uint16_t headers_len = (uint16_t)((uint8_t*)hdr_end - c->buf) + 4;
char method[16] = {0}, url[512] = {0};
if (sscanf(line, "%15s %511s", method, url) < 2) {
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: bad http request line: %s", line);
goto error;
}
char* colon = strrchr(host_port, ':');
if (!colon) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: no port in %s", host_port); goto error; }
*colon = '\0'; int port = atoi(colon + 1);
if (port <= 0 || port > 65535) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: bad port %d", port); goto error; }
if (strncmp(url, "http://", 7) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: not an absolute http URL: %s", url);
goto error;
}
struct hostent* he = gethostbyname(host_port);
char host[256];
int port = 80;
const char* host_start = url + 7;
const char* path_start = strchr(host_start, '/');
const char* col = NULL;
for (const char* p = host_start; (path_start ? p < path_start : *p); p++) {
if (*p == ':') { col = p; break; }
}
if (col && (!path_start || col < path_start)) {
size_t hlen = (size_t)(col - host_start);
if (hlen >= sizeof(host)) goto error;
memcpy(host, host_start, hlen); host[hlen] = '\0';
port = atoi(col + 1);
if (port <= 0 || port > 65535) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: bad port %d in %s", port, url); goto error; }
} else if (path_start) {
size_t hlen = (size_t)(path_start - host_start);
if (hlen >= sizeof(host)) goto error;
memcpy(host, host_start, hlen); host[hlen] = '\0';
} else {
size_t hlen = strlen(host_start);
if (hlen >= sizeof(host)) goto error;
memcpy(host, host_start, hlen); host[hlen] = '\0';
}
if (host[0] == '\0') { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: empty host in %s", url); goto error; }
if (!path_start || *path_start == '\0') path_start = "/";
struct hostent* he = gethostbyname(host);
if (!he || he->h_addrtype != AF_INET) {
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: DNS failed for %s", host_port);
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: DNS failed for %s", host);
uint8_t resp[] = "HTTP/1.1 502 Bad Gateway\r\n\r\n";
write_to_client(c, resp, (uint16_t)strlen((char*)resp)); tcp_conn_push_close(c->tc); return;
}
memcpy(c->dest_ip, he->h_addr, 4); c->dest_port = htons((uint16_t)port);
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socks_proxy: HTTP CONNECT %s → %d.%d.%d.%d:%d",
host_port, c->dest_ip[0], c->dest_ip[1], c->dest_ip[2], c->dest_ip[3], port);
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socks_proxy: HTTP %s %s:%d%s → %d.%d.%d.%d:%d",
method, host, port, path_start,
c->dest_ip[0], c->dest_ip[1], c->dest_ip[2], c->dest_ip[3], port);
if (send_connect(c) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: send_connect failed for %s", host);
uint8_t resp[] = "HTTP/1.1 502 Bad Gateway\r\n\r\n";
write_to_client(c, resp, (uint16_t)strlen((char*)resp)); tcp_conn_push_close(c->tc); return;
}
const char* version = strstr(line, " HTTP/");
char version_str[16] = " HTTP/1.1";
if (version) {
size_t vlen = strlen(version);
if (vlen >= sizeof(version_str)) vlen = sizeof(version_str) - 1;
memcpy(version_str, version, vlen + 1);
}
uint16_t rest_off = line_len + 2;
uint16_t rest_len = headers_len - rest_off;
uint8_t hdr_buf[2048];
int hdr_n = snprintf((char*)hdr_buf, sizeof(hdr_buf), "%s %s%s\r\n", method, path_start, version_str);
if (hdr_n < 0 || (size_t)hdr_n + rest_len > sizeof(hdr_buf)) {
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: header reconstruction overflow hdr_n=%d rest=%u", hdr_n, rest_len);
goto error;
}
memcpy(hdr_buf + hdr_n, c->buf + rest_off, rest_len);
send_data(c, hdr_buf, (uint16_t)(hdr_n + rest_len));
if (headers_len < c->buf_len) {
send_data(c, c->buf + headers_len, c->buf_len - headers_len);
}
c->buf_len = 0;
c->state = HTTP_STATE_RELAY; // reply immediately, no waiting
uint8_t resp[] = "HTTP/1.1 200 Connection Established\r\n\r\n";
write_to_client(c, resp, (uint16_t)strlen((char*)resp));
if (send_connect(c) < 0) {
tcp_conn_push_close(c->tc);
}
c->state = HTTP_STATE_RELAY;
return;
error: {