summaryrefslogtreecommitdiff
path: root/proxy.c
blob: 04ac9f2ba2401155bd75443e789de789a221f754 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
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);
}