diff options
author | Bobby Bingham <koorogi@koorogi.info> | 2016-01-31 16:29:03 -0600 |
---|---|---|
committer | Bobby Bingham <koorogi@koorogi.info> | 2016-01-31 16:29:03 -0600 |
commit | 24da062c6f75a3fa229d74359150f653b63d3c04 (patch) | |
tree | a92a3e6cea31c18f004ffc028aa689151f5d93a3 |
initial commit
-rw-r--r-- | Makefile | 18 | ||||
-rw-r--r-- | latency.c | 97 | ||||
-rw-r--r-- | proxy.c | 135 | ||||
-rw-r--r-- | proxy.h | 2 |
4 files changed, 252 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d091e68 --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +CFLAGS = -D_POSIX_C_SOURCE=200809L -O2 -Wall + +SRCS = $(sort $(wildcard *.c)) +OBJS = $(SRCS:.c=.o) + +all: latency + +clean: + rm -f latency + rm -f $(OBJS) + +latency: $(OBJS) + $(CC) $(CFLAGS) $^ -o $@ + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +.PHONY: all clean diff --git a/latency.c b/latency.c new file mode 100644 index 0000000..6181e84 --- /dev/null +++ b/latency.c @@ -0,0 +1,97 @@ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <errno.h> +#include <netdb.h> +#include <stdio.h> +#include <unistd.h> + +#include "proxy.h" + +static int parse_host(const char *host, const char *port, struct addrinfo **addr) +{ + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM, + .ai_flags = AI_PASSIVE, + }; + + int err; + if ((err = getaddrinfo(host, port, &hints, addr))) { + fprintf(stderr, "getaddrinfo failed: %s\n", gai_strerror(err)); + return -1; + } + + return 0; +} + +static int listen_source(struct addrinfo *source) +{ + int fd = -1; + for (struct addrinfo *a = source; a; a = a->ai_next) { + if ((fd = socket(a->ai_family, a->ai_socktype, a->ai_protocol)) == -1) + continue; + if (!bind(fd, a->ai_addr, a->ai_addrlen) && !listen(fd, 1)) + break; + close(fd); + fd = -1; + } + return fd; +} + +static int connect_target(struct addrinfo *target) +{ + int fd = -1; + for (struct addrinfo *a = target; a; a = a->ai_next) { + if ((fd = socket(a->ai_family, a->ai_socktype, a->ai_protocol)) == -1) + continue; + if (connect(fd, a->ai_addr, a->ai_addrlen) != -1) + break; + close(fd); + fd = -1; + } + return fd; +} + +int main(int argc, char **argv) +{ + struct addrinfo *source; + struct addrinfo *target; + + if (argc != 3) { + fprintf(stderr, "usage: %s targethost targetport\n", argv[0]); + return -1; + } + + if (parse_host(NULL, "1234", &source)) + return -1; + if (parse_host(argv[1], argv[2], &target)) + return -1; + + int listenfd = listen_source(source); + if (listenfd < 0) { + perror("listen_source"); + return -1; + } + + for (;;) { + int sourcefd = accept(listenfd, NULL, NULL); + if (sourcefd == -1) break; + + int targetfd = connect_target(target); + if (targetfd == -1) { + perror("connect_target"); + close(sourcefd); + break; + } + + proxy(sourcefd, targetfd, 500); + + close(targetfd); + close(sourcefd); + } + + close(listenfd); + return 0; +} + @@ -0,0 +1,135 @@ +#include <errno.h> +#include <poll.h> +#include <signal.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> + +#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); +} + @@ -0,0 +1,2 @@ +void proxy(int, int, unsigned); + |