/* * ===================================================================================== * PROXY VPN v6 TLS - EDICION PERSONALIZADA (NATIVA EN C) * Compilación en Ubuntu 24.04: gcc -o vpn_proxy vpn_proxy.c -lssl -lcrypto -lpthread * ===================================================================================== */ #include #include #include #include #include #include #include #include #include #include #include #include #include // --- CONFIGURACIÓN BASE --- #define DEFAULT_PORT 443 #define SSH_HOST "127.0.0.1" #define SSH_PORT 22 #define CERT_FILE "/root/cert.pem" #define KEY_FILE "/root/key.pem" #define LOG_FILE "/root/proxy-ssl-c.log" // --- SEGURIDAD Y LIMITES --- #define MAX_CONNECTIONS 200 #define BUFLEN 16384 #define BAN_TIME 3600 #define AUTO_BAN_STRIKES 3 #define COOLDOWN_SEC 1 // --- FAKE WEB RESPONSE (400 OK) --- const char *FAKE_WEB_RESPONSE = "HTTP/1.1 400 OK\r\n" "Server: nginx/1.21.0\r\n" "Content-Type: text/html; charset=UTF-8\r\n" "Connection: close\r\n\r\n" "\n\nError\n" "\n" "

Hola

\n

400 Bad Request

\n" "\n\n"; // --- MENSAJES ROTATIVOS --- const char *MENSAJES[] = { "🚀 CONEXION TLS ESTABLECIDA", "🛡️ CIFRADO MILITAR ACTIVO", "Pfsense", "OPNsense", "VyOS", "Claro" }; #define NUM_MENSAJES (sizeof(MENSAJES) / sizeof(MENSAJES[0])) int mensaje_idx = 0; pthread_mutex_t msg_mutex = PTHREAD_MUTEX_INITIALIZER; // --- ESTADO GLOBAL --- int active_connections = 0; pthread_mutex_t conn_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; // Estructura para pasar datos al hilo typedef struct { int client_fd; struct sockaddr_in addr; SSL_CTX *ssl_ctx; } client_data_t; // --- FUNCIONES DE SOPORTE --- void write_log(const char *ip, const char *msg) { pthread_mutex_lock(&log_mutex); FILE *f = fopen(LOG_FILE, "a"); if (f) { time_t now = time(NULL); struct tm *t = localtime(&now); char time_str[64]; strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", t); fprintf(f, "[%s] [%s] %s\n", time_str, ip ? ip : "SISTEMA", msg); printf("[%s] [%s] %s\n", time_str, ip ? ip : "SISTEMA", msg); fclose(f); } pthread_mutex_unlock(&log_mutex); } SSL_CTX *create_context() { const SSL_METHOD *method = TLS_server_method(); SSL_CTX *ctx = SSL_CTX_new(method); if (!ctx) { perror("No se pudo crear el contexto SSL"); ERR_print_errors_fp(stderr); exit(EXIT_FAILURE); } return ctx; } void configure_context(SSL_CTX *ctx) { if (SSL_CTX_use_certificate_file(ctx, CERT_FILE, SSL_FILETYPE_PEM) <= 0) { ERR_print_errors_fp(stderr); exit(EXIT_FAILURE); } if (SSL_CTX_use_PrivateKey_file(ctx, KEY_FILE, SSL_FILETYPE_PEM) <= 0 ) { ERR_print_errors_fp(stderr); exit(EXIT_FAILURE); } } // --- HILO DE CONEXIÓN (HANDLER) --- void *connection_handler(void *arg) { client_data_t *data = (client_data_t *)arg; int client_sock = data->client_fd; SSL_CTX *ctx = data->ssl_ctx; char client_ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(data->addr.sin_addr), client_ip, INET_ADDRSTRLEN); free(data); // Liberar la memoria de la estructura SSL *ssl = SSL_new(ctx); SSL_set_fd(ssl, client_sock); if (SSL_accept(ssl) <= 0) { // Fallo en el Handshake SSL (Probablemente un escáner no-TLS) SSL_free(ssl); close(client_sock); pthread_mutex_lock(&conn_mutex); active_connections--; pthread_mutex_unlock(&conn_mutex); pthread_exit(NULL); } // Configurar timeout de lectura inicial (3 segundos) 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 = SSL_read(ssl, buffer, sizeof(buffer) - 1); 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) { write_log(client_ip, "❌ Error conectando a SSH local"); goto cleanup; } if (strncmp(buffer, "SSH-", 4) == 0) { write_log(client_ip, "✅ Túnel (Modo SSH Directo)"); send(target_sock, buffer, bytes_read, 0); } else if (strstr(buffer, "HTTP/") != NULL && strstr(buffer, "Upgrade: websocket") == NULL) { write_log(client_ip, "🕵️ Escáner detectado. Respondiendo 400 OK Fake Web."); SSL_write(ssl, FAKE_WEB_RESPONSE, strlen(FAKE_WEB_RESPONSE)); goto cleanup; } else { // Modo Websocket / Inyector HTTP pthread_mutex_lock(&msg_mutex); const char *status_msg = MENSAJES[mensaje_idx]; mensaje_idx = (mensaje_idx + 1) % NUM_MENSAJES; pthread_mutex_unlock(&msg_mutex); char response[1024]; snprintf(response, sizeof(response), "HTTP/1.1 101 %s\r\n" "Server: nginx/1.21.0\r\n" "X-Proxy-Agent: Gemini-Ultra-Robust-C-Native\r\n" "Connection: Upgrade\r\n" "Upgrade: websocket\r\n\r\n", status_msg); SSL_write(ssl, response, strlen(response)); char log_msg[256]; snprintf(log_msg, sizeof(log_msg), "✅ Túnel (Modo WebSocket HTTP): %s", status_msg); write_log(client_ip, log_msg); // Si el payload tiene más datos después de las cabeceras HTTP, enviarlos // (En este proxy simplificado, asumimos que el handshake viene limpio) } } else { // Conexión vacía (Modo Stunnel Silencioso) 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) { write_log(client_ip, "✅ Túnel (Modo Stunnel Silencioso)"); } else { goto cleanup; } } // --- BUCLE DEL TÚNEL (I/O MULTIPLEXING) --- tv.tv_sec = 0; // Quitar timeout para el túnel setsockopt(client_sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv); int max_fd = (client_sock > target_sock) ? client_sock : target_sock; long tx_bytes = 0, rx_bytes = 0; while (1) { fd_set readfds; FD_ZERO(&readfds); FD_SET(client_sock, &readfds); FD_SET(target_sock, &readfds); struct timeval select_tv; select_tv.tv_sec = 300; // 5 minutos de inactividad máxima select_tv.tv_usec = 0; // Validar si OpenSSL tiene datos pendientes en memoria desencriptada int pending = SSL_pending(ssl); if (pending == 0) { int activity = select(max_fd + 1, &readfds, NULL, NULL, &select_tv); if (activity <= 0) break; // Error o Timeout } // Datos del Cliente (SSL) -> SSH if (pending > 0 || FD_ISSET(client_sock, &readfds)) { int bytes = SSL_read(ssl, buffer, sizeof(buffer)); if (bytes <= 0) break; send(target_sock, buffer, bytes, 0); rx_bytes += bytes; } // Datos del SSH -> Cliente (SSL) if (FD_ISSET(target_sock, &readfds)) { int bytes = recv(target_sock, buffer, sizeof(buffer), 0); if (bytes <= 0) break; SSL_write(ssl, buffer, bytes); tx_bytes += bytes; } } double total_mb = (double)(tx_bytes + rx_bytes) / (1024.0 * 1024.0); if (total_mb > 0.05) { char log_close[256]; snprintf(log_close, sizeof(log_close), "[*] Cierre de sesión. Tráfico: %.2f MB", total_mb); write_log(client_ip, log_close); } cleanup: if (target_sock != -1) close(target_sock); SSL_shutdown(ssl); SSL_free(ssl); close(client_sock); pthread_mutex_lock(&conn_mutex); active_connections--; pthread_mutex_unlock(&conn_mutex); pthread_exit(NULL); } // --- BUCLE PRINCIPAL --- int main(int argc, char **argv) { int port = DEFAULT_PORT; if (argc > 1) port = atoi(argv[1]); // Inicializar OpenSSL SSL_load_error_strings(); OpenSSL_add_ssl_algorithms(); SSL_CTX *ctx = create_context(); configure_context(ctx); 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 el Bind"); exit(EXIT_FAILURE); } if (listen(server_sock, 600) < 0) { perror("Fallo el Listen"); exit(EXIT_FAILURE); } write_log(NULL, "====================================================="); char init_msg[256]; snprintf(init_msg, sizeof(init_msg), "🚀 Servidor v6 TLS en NATIVO (C) - Puerto %d", port); write_log(NULL, init_msg); write_log(NULL, "🛡️ Máximo Rendimiento | Anti-Probing: 400 OK"); write_log(NULL, "====================================================="); 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); // Crear datos para el hilo client_data_t *data = malloc(sizeof(client_data_t)); data->client_fd = client_sock; data->addr = client_addr; data->ssl_ctx = ctx; // Desplegar Hilo pthread_t thread_id; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // Auto-limpieza del hilo al terminar if (pthread_create(&thread_id, &attr, connection_handler, (void *)data) != 0) { write_log(NULL, "Error creando hilo. Cerrando socket."); close(client_sock); free(data); pthread_mutex_lock(&conn_mutex); active_connections--; pthread_mutex_unlock(&conn_mutex); } pthread_attr_destroy(&attr); } close(server_sock); SSL_CTX_free(ctx); EVP_cleanup(); return 0; }