#include #include #include #include #include #include #include #include #include "proxy.h" #define BUFFER_SIZE (1 << 10) struct list { struct list *next, *prev; }; struct packet { struct list list; unsigned long long recvtime; size_t length; char data[BUFFER_SIZE]; }; static unsigned long long gettime() { struct timespec time; clock_gettime(CLOCK_MONOTONIC, &time); return time.tv_sec * 1000000000ULL + time.tv_nsec; } static int read_packet(int fd, struct list *tail, unsigned long long now) { struct packet *p = malloc(sizeof *p); if (!p) return -1; ssize_t len; while ((len = read(fd, p->data, BUFFER_SIZE)) == -1 && errno == EINTR) ; if (len <= 0) { free(p); return -1; } p->list.next = tail; p->list.prev = tail->prev; p->recvtime = now; p->length = len; p->list.next->prev = p->list.prev->next = &p->list; return 0; } static int write_packet(int fd, struct packet *p) { char *data = p->data; size_t len = p->length; ssize_t r; while (len && (r = write(fd, data, len)) != -1 && errno != EINTR) { data += r; len -= r; } if (r == -1) return -1; struct list *l = &p->list; l->prev->next = l->next; l->next->prev = l->prev; free(p); return 0; } static void proxy_one(int readfd, int writefd, unsigned long long delayns) { struct list head = {0}, tail = {0}; head.next = &tail; tail.prev = &head; struct pollfd fds = { .fd = readfd, .events = POLLIN }; int timeout = -1; for (;;) { while (poll(&fds, 1, timeout) == -1 && errno == EINTR) ; if (fds.revents & (POLLERR | POLLHUP)) return; unsigned long long now = gettime(); unsigned long long sendtime = now - delayns; if (fds.revents & POLLIN && read_packet(readfd, &tail, now)) return; while (head.next != &tail) { struct packet *p = (struct packet *)head.next; timeout = (p->recvtime - sendtime) / 1000000; if (timeout <= 0) { if (write_packet(writefd, p)) return; } else break; } if (head.next == &tail) timeout = -1; } } static pid_t make_child(int fd0, int fd1, unsigned long long delayns) { pid_t child = fork(); if (!child) { proxy_one(fd0, fd1, delayns); exit(0); } return child; } static pid_t wait_one(void) { pid_t p; while ((p = wait(NULL)) == -1 && errno == EINTR) ; return p; } static void kill_wait(pid_t p) { kill(p, SIGINT); wait_one(); } void proxy(int fd0, int fd1, unsigned delayms) { unsigned long long delayns = delayms * 1000000ULL; pid_t pid0, pid1; if ((pid0 = make_child(fd0, fd1, delayns)) < 0) return; if ((pid1 = make_child(fd1, fd0, delayns)) < 0) { kill_wait(pid0); return; } pid_t dead = wait_one(); kill_wait(dead == pid0 ? pid1 : pid0); }