// Un intermediario con una falla: copia los datos de un cliente hacia un
// servidor e intercambia un par de bytes cada cierta cantidad de bytes. Imita a
// un gateway con la memoria dañada, como el del artículo de Saltzer, Reed y Clark.
// Cada lado usa su propia conexión TCP, con su checksum y sus retransmisiones.
//   flaky_relay <puerto> <ip_destino> <puerto_destino> <cada_n_bytes>
//   Con cada_n_bytes = 0, no daña nada.
#define _POSIX_C_SOURCE 200809L

#include "io_util.h"

#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
    if (argc != 5) {
        fprintf(stderr, "uso: %s <puerto> <ip_destino> <puerto_destino> <cada_n_bytes>\n",
                argv[0]);
        return EXIT_FAILURE;
    }
    long every = strtol(argv[4], NULL, 10);
    if (every < 0) {
        fprintf(stderr, "cada_n_bytes no puede ser negativo\n");
        return EXIT_FAILURE;
    }
    int listener = tcp_listen(parse_port(argv[1]));
    if (listener == -1 || print_bound_port(listener) == -1) {
        return EXIT_FAILURE;
    }
    int in = accept(listener, NULL, NULL);
    if (in == -1) {
        perror("accept");
        return EXIT_FAILURE;
    }

    struct sockaddr_in dst;
    memset(&dst, 0, sizeof dst);
    dst.sin_family = AF_INET;
    dst.sin_port = htons(parse_port(argv[3]));
    if (inet_pton(AF_INET, argv[2], &dst.sin_addr) != 1) {
        fprintf(stderr, "dirección no válida: %s\n", argv[2]);
        return EXIT_FAILURE;
    }
    int out = socket(AF_INET, SOCK_STREAM, 0);
    if (out == -1) {
        perror("socket");
        return EXIT_FAILURE;
    }
    if (connect(out, (struct sockaddr *)&dst, sizeof dst) == -1) {
        perror("connect");
        return EXIT_FAILURE;
    }

    char buf[8192];
    unsigned long long total = 0, damaged = 0;
    long countdown = every;
    for (;;) {
        ssize_t n = recv(in, buf, sizeof buf, 0);
        if (n == -1) {
            if (errno == EINTR) {
                continue;
            }
            perror("recv");
            return EXIT_FAILURE;
        }
        if (n == 0) {
            break;
        }
        // Aquí los datos no están protegidos por ningún checksum de TCP.
        for (ssize_t i = 0; every > 0 && i + 1 < n; i++) {
            if (--countdown == 0) {
                char tmp = buf[i];
                buf[i] = buf[i + 1];
                buf[i + 1] = tmp;
                countdown = every;
                damaged++;
            }
        }
        if (send_all(out, buf, (size_t)n) == -1) {
            return EXIT_FAILURE;
        }
        total += (unsigned long long)n;
    }
    printf("copiados %llu bytes, %llu pares intercambiados\n", total, damaged);
    close_checked(out);
    close_checked(in);
    close_checked(listener);
    return EXIT_SUCCESS;
}
