|
|
@@ -0,0 +1,337 @@
|
|
|
+/*
|
|
|
+ * =====================================================================================
|
|
|
+ * 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 <stdio.h>
|
|
|
+#include <stdlib.h>
|
|
|
+#include <string.h>
|
|
|
+#include <unistd.h>
|
|
|
+#include <arpa/inet.h>
|
|
|
+#include <sys/socket.h>
|
|
|
+#include <sys/select.h>
|
|
|
+#include <sys/time.h>
|
|
|
+#include <pthread.h>
|
|
|
+#include <openssl/ssl.h>
|
|
|
+#include <openssl/err.h>
|
|
|
+#include <time.h>
|
|
|
+#include <fcntl.h>
|
|
|
+
|
|
|
+// --- 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"
|
|
|
+ "<!DOCTYPE html>\n<html>\n<head><title>Error</title></head>\n"
|
|
|
+ "<body style='text-align:center; padding:50px; font-family:sans-serif;'>\n"
|
|
|
+ "<h1>Hola</h1>\n<p>400 Bad Request</p>\n"
|
|
|
+ "</body>\n</html>\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;
|
|
|
+}
|
|
|
+
|