|
|
@@ -0,0 +1,232 @@
|
|
|
+/*
|
|
|
+ * =====================================================================================
|
|
|
+ * PROXY MULTIFILAMENTADO PROFESIONAL - VERSIÓN 3 (NATIVO EN C)
|
|
|
+ * Compilación en Ubuntu: gcc -O3 -o proxy_v3 proxy_v3_nativo.c -lpthread
|
|
|
+ * =====================================================================================
|
|
|
+ */
|
|
|
+
|
|
|
+#include <stdio.h>
|
|
|
+#include <stdlib.h>
|
|
|
+#include <string.h>
|
|
|
+#include <unistd.h>
|
|
|
+#include <arpa/inet.h>
|
|
|
+#include <sys/socket.h>
|
|
|
+#include <sys/select.h>
|
|
|
+#include <pthread.h>
|
|
|
+#include <signal.h>
|
|
|
+#include <time.h>
|
|
|
+
|
|
|
+// --- CONFIGURACIÓN BASE ---
|
|
|
+#define DEFAULT_PORT 8080 // Puedes cambiarlo al ejecutar
|
|
|
+#define SSH_HOST "127.0.0.1"
|
|
|
+#define SSH_PORT 22
|
|
|
+#define BUFLEN 16384
|
|
|
+#define MAX_CONNECTIONS 300
|
|
|
+
|
|
|
+// --- FAKE WEB ROBUSTA (Réplica exacta de Nginx 1.24) ---
|
|
|
+const char *FAKE_WEB_RESPONSE =
|
|
|
+ "HTTP/1.1 400 Bad Request\r\n"
|
|
|
+ "Server: nginx/1.24.0\r\n"
|
|
|
+ "Content-Type: text/html\r\n"
|
|
|
+ "Content-Length: 157\r\n"
|
|
|
+ "Connection: close\r\n\r\n"
|
|
|
+ "<html>\r\n<head><title>400 Bad Request</title></head>\r\n"
|
|
|
+ "<body>\r\n<center><h1>400 Bad Request</h1></center>\r\n"
|
|
|
+ "<hr><center>nginx/1.24.0</center>\r\n</body>\r\n</html>\r\n";
|
|
|
+
|
|
|
+// --- RESPUESTA DE CONEXIÓN ACEPTADA (Con tu Agent) ---
|
|
|
+const char *HTTP_101_RESPONSE =
|
|
|
+ "HTTP/1.1 101 Switching Protocols\r\n"
|
|
|
+ "Server: nginx/1.24.0\r\n"
|
|
|
+ "Upgrade: websocket\r\n"
|
|
|
+ "Connection: Upgrade\r\n"
|
|
|
+ "X-Proxy-Agent: Gemini-Ultra-Robust-v3\r\n\r\n";
|
|
|
+
|
|
|
+// Variables globales de estado
|
|
|
+int active_connections = 0;
|
|
|
+pthread_mutex_t conn_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
+
|
|
|
+// Estructura para pasar datos al hilo
|
|
|
+typedef struct {
|
|
|
+ int client_fd;
|
|
|
+ struct sockaddr_in addr;
|
|
|
+} client_data_t;
|
|
|
+
|
|
|
+// Imprimir logs en pantalla
|
|
|
+void log_msg(const char *ip, const char *msg) {
|
|
|
+ time_t now = time(NULL);
|
|
|
+ struct tm *t = localtime(&now);
|
|
|
+ char time_str[64];
|
|
|
+ strftime(time_str, sizeof(time_str), "%H:%M:%S", t);
|
|
|
+ printf("[%s] [%s] %s\n", time_str, ip ? ip : "SISTEMA", msg);
|
|
|
+}
|
|
|
+
|
|
|
+// Hilo de manejo de conexión
|
|
|
+void *connection_handler(void *arg) {
|
|
|
+ client_data_t *data = (client_data_t *)arg;
|
|
|
+ int client_sock = data->client_fd;
|
|
|
+
|
|
|
+ char client_ip[INET_ADDRSTRLEN];
|
|
|
+ inet_ntop(AF_INET, &(data->addr.sin_addr), client_ip, INET_ADDRSTRLEN);
|
|
|
+ free(data); // Liberar memoria del argumento
|
|
|
+
|
|
|
+ // Timeout inicial para leer la petición
|
|
|
+ struct timeval tv;
|
|
|
+ tv.tv_sec = 3; tv.tv_usec = 0;
|
|
|
+ setsockopt(client_sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
|
|
|
+
|
|
|
+ char buffer[BUFLEN];
|
|
|
+ int bytes_read = recv(client_sock, buffer, sizeof(buffer) - 1, 0);
|
|
|
+ int target_sock = -1;
|
|
|
+
|
|
|
+ if (bytes_read > 0) {
|
|
|
+ buffer[bytes_read] = '\0';
|
|
|
+
|
|
|
+ // Conectar al destino (SSH Local)
|
|
|
+ struct sockaddr_in target_addr;
|
|
|
+ target_sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
+ target_addr.sin_family = AF_INET;
|
|
|
+ target_addr.sin_port = htons(SSH_PORT);
|
|
|
+ inet_pton(AF_INET, SSH_HOST, &target_addr.sin_addr);
|
|
|
+
|
|
|
+ if (connect(target_sock, (struct sockaddr *)&target_addr, sizeof(target_addr)) < 0) {
|
|
|
+ log_msg(client_ip, "❌ Error destino SSH");
|
|
|
+ goto cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Lógica de ruteo y Fake Web
|
|
|
+ if (strncmp(buffer, "SSH-", 4) == 0) {
|
|
|
+ log_msg(client_ip, "✅ Túnel Directo SSH");
|
|
|
+ send(target_sock, buffer, bytes_read, 0);
|
|
|
+ } else if (strstr(buffer, "HTTP/") != NULL && strstr(buffer, "Upgrade: websocket") == NULL) {
|
|
|
+ log_msg(client_ip, "🕵️ Escáner detectado. Enviando Nginx Fake Web (400).");
|
|
|
+ send(client_sock, FAKE_WEB_RESPONSE, strlen(FAKE_WEB_RESPONSE), 0);
|
|
|
+ goto cleanup;
|
|
|
+ } else {
|
|
|
+ // Es NetMod / Inyector HTTP
|
|
|
+ log_msg(client_ip, "✅ Túnel WebSocket Inyectado (V3)");
|
|
|
+ send(client_sock, HTTP_101_RESPONSE, strlen(HTTP_101_RESPONSE), 0);
|
|
|
+ // Si hay datos extra (payload SSH adelantado), reenviarlo
|
|
|
+ char *headers_end = strstr(buffer, "\r\n\r\n");
|
|
|
+ if (headers_end) {
|
|
|
+ int header_len = (headers_end - buffer) + 4;
|
|
|
+ if (bytes_read > header_len) {
|
|
|
+ send(target_sock, buffer + header_len, bytes_read - header_len, 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ goto cleanup; // Conexión vacía o timeout
|
|
|
+ }
|
|
|
+
|
|
|
+ // --- BUCLE SELECT (EL PUENTE DE DATOS) ---
|
|
|
+ tv.tv_sec = 0; // Quitar timeout
|
|
|
+ setsockopt(client_sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
|
|
|
+
|
|
|
+ int max_fd = (client_sock > target_sock) ? client_sock : target_sock;
|
|
|
+
|
|
|
+ while (1) {
|
|
|
+ fd_set readfds;
|
|
|
+ FD_ZERO(&readfds);
|
|
|
+ FD_SET(client_sock, &readfds);
|
|
|
+ FD_SET(target_sock, &readfds);
|
|
|
+
|
|
|
+ // Timeout de inactividad de 5 minutos
|
|
|
+ struct timeval select_tv = {300, 0};
|
|
|
+
|
|
|
+ int activity = select(max_fd + 1, &readfds, NULL, NULL, &select_tv);
|
|
|
+ if (activity <= 0) break; // Cierre por timeout o error
|
|
|
+
|
|
|
+ // Del Cliente al SSH
|
|
|
+ if (FD_ISSET(client_sock, &readfds)) {
|
|
|
+ int bytes = recv(client_sock, buffer, sizeof(buffer), 0);
|
|
|
+ if (bytes <= 0) break;
|
|
|
+ if (send(target_sock, buffer, bytes, 0) <= 0) break;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Del SSH al Cliente
|
|
|
+ if (FD_ISSET(target_sock, &readfds)) {
|
|
|
+ int bytes = recv(target_sock, buffer, sizeof(buffer), 0);
|
|
|
+ if (bytes <= 0) break;
|
|
|
+ if (send(client_sock, buffer, bytes, 0) <= 0) break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+cleanup:
|
|
|
+ if (target_sock != -1) close(target_sock);
|
|
|
+ close(client_sock);
|
|
|
+
|
|
|
+ pthread_mutex_lock(&conn_mutex);
|
|
|
+ active_connections--;
|
|
|
+ pthread_mutex_unlock(&conn_mutex);
|
|
|
+
|
|
|
+ pthread_exit(NULL);
|
|
|
+}
|
|
|
+
|
|
|
+int main(int argc, char **argv) {
|
|
|
+ int port = DEFAULT_PORT;
|
|
|
+ if (argc > 1) port = atoi(argv[1]);
|
|
|
+
|
|
|
+ // Ignorar SIGPIPE (Vital en C: evita que el servidor crashee si un cliente corta de golpe)
|
|
|
+ signal(SIGPIPE, SIG_IGN);
|
|
|
+
|
|
|
+ int server_sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
+ int opt = 1;
|
|
|
+ setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
|
|
+
|
|
|
+ struct sockaddr_in server_addr;
|
|
|
+ server_addr.sin_family = AF_INET;
|
|
|
+ server_addr.sin_addr.s_addr = INADDR_ANY;
|
|
|
+ server_addr.sin_port = htons(port);
|
|
|
+
|
|
|
+ if (bind(server_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
|
|
|
+ perror("Fallo en bind");
|
|
|
+ exit(EXIT_FAILURE);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (listen(server_sock, 500) < 0) {
|
|
|
+ perror("Fallo en listen");
|
|
|
+ exit(EXIT_FAILURE);
|
|
|
+ }
|
|
|
+
|
|
|
+ printf("=====================================================\n");
|
|
|
+ printf("🚀 PROXY NATIVO V3 (TCP) INICIADO - PUERTO %d\n", port);
|
|
|
+ printf("🛡️ Fake Web: Nginx 1.24 | Agent: Gemini-Ultra-Robust-v3\n");
|
|
|
+ printf("=====================================================\n");
|
|
|
+
|
|
|
+ while (1) {
|
|
|
+ struct sockaddr_in client_addr;
|
|
|
+ socklen_t addr_len = sizeof(client_addr);
|
|
|
+ int client_sock = accept(server_sock, (struct sockaddr *)&client_addr, &addr_len);
|
|
|
+
|
|
|
+ if (client_sock < 0) continue;
|
|
|
+
|
|
|
+ pthread_mutex_lock(&conn_mutex);
|
|
|
+ if (active_connections >= MAX_CONNECTIONS) {
|
|
|
+ pthread_mutex_unlock(&conn_mutex);
|
|
|
+ close(client_sock);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ active_connections++;
|
|
|
+ pthread_mutex_unlock(&conn_mutex);
|
|
|
+
|
|
|
+ client_data_t *data = malloc(sizeof(client_data_t));
|
|
|
+ data->client_fd = client_sock;
|
|
|
+ data->addr = client_addr;
|
|
|
+
|
|
|
+ pthread_t thread_id;
|
|
|
+ pthread_attr_t attr;
|
|
|
+ pthread_attr_init(&attr);
|
|
|
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
|
|
+
|
|
|
+ if (pthread_create(&thread_id, &attr, connection_handler, (void *)data) != 0) {
|
|
|
+ close(client_sock);
|
|
|
+ free(data);
|
|
|
+ pthread_mutex_lock(&conn_mutex); active_connections--; pthread_mutex_unlock(&conn_mutex);
|
|
|
+ }
|
|
|
+ pthread_attr_destroy(&attr);
|
|
|
+ }
|
|
|
+
|
|
|
+ close(server_sock);
|
|
|
+ return 0;
|
|
|
+}
|