// connect_probe: intenta una conexión y dice qué devolvió connect y cuánto demoró.
//
//   connect_probe <ip> <puerto> [reintentos_de_syn [espera_ms]]
//
// El tercer argumento fija TCP_SYNCNT, para no esperar los dos minutos que Linux tarda por
// defecto en rendirse cuando nadie contesta. El cuarto mantiene abierta la conexión un rato
// antes de cerrarla, para que el otro extremo pueda cerrar primero.
#define _POSIX_C_SOURCE 200809L
#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <time.h>
#include <unistd.h>

static double now_seconds(void) {
    struct timespec ts;
    if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) {
        perror("clock_gettime");
        exit(1);
    }
    return (double)ts.tv_sec + (double)ts.tv_nsec / 1e9;
}

static const char *errno_name(int number) {
    switch (number) {
    case ECONNREFUSED: return "ECONNREFUSED";
    case ETIMEDOUT: return "ETIMEDOUT";
    case EHOSTUNREACH: return "EHOSTUNREACH";
    case ENETUNREACH: return "ENETUNREACH";
    default: return "otro";
    }
}

int main(int argc, char **argv) {
    if (argc < 3) {
        fprintf(stderr, "uso: %s <ip> <puerto> [reintentos_de_syn [espera_ms]]\n", argv[0]);
        return 2;
    }
    struct sockaddr_in addr = {.sin_family = AF_INET, .sin_port = htons((uint16_t)atoi(argv[2]))};
    if (inet_pton(AF_INET, argv[1], &addr.sin_addr) != 1) {
        fprintf(stderr, "dirección inválida: %s\n", argv[1]);
        return 2;
    }
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0) {
        perror("socket");
        return 1;
    }
    if (argc > 3) {
        int retries = atoi(argv[3]);
        if (setsockopt(fd, IPPROTO_TCP, TCP_SYNCNT, &retries, sizeof retries) < 0) {
            perror("setsockopt TCP_SYNCNT");
            return 1;
        }
    }
    double start = now_seconds();
    int result = connect(fd, (struct sockaddr *)&addr, sizeof addr);
    int saved = errno;
    double seconds = now_seconds() - start;
    if (result == 0) {
        printf("connect devolvió 0 en %.3f s\n", seconds);
    } else {
        printf("connect devolvió -1 en %.3f s: %s (%s)\n", seconds, errno_name(saved),
               strerror(saved));
    }
    if (result == 0 && argc > 4) {
        long ms = atol(argv[4]);
        struct timespec ts = {ms / 1000, (ms % 1000) * 1000000L};
        if (nanosleep(&ts, NULL) < 0) {
            perror("nanosleep");
            return 1;
        }
    }
    if (close(fd) < 0) {
        perror("close");
        return 1;
    }
    return 0;
}
