/* version 2026-02-12 */ /* compilation: gcc -Wall -o client client.c */ #include #include #include #include #include #include #include #include #include #include #include int fullread(int fd, void *_buf, int count) { char *buf = _buf; int ret = 0; int l; while (count) { l = read(fd, buf, count); if (l <= 0) return -1; count -= l; buf += l; ret += l; } return ret; } int fullwrite(int fd, void *_buf, int count) { char *buf = _buf; int ret = 0; int l; while (count) { l = write(fd, buf, count); if (l <= 0) return -1; count -= l; buf += l; ret += l; } return ret; } typedef struct { int socket; int port; int dead; } link_t; #define MAX 256 link_t links[MAX]; int links_count; char tohex(int v) { if (v >= 0 && v <= 9) return '0' +v; return 'a' + v - 10; } void get_rand(char *b) { int f = open("/dev/urandom", O_RDONLY); if (f == -1) { perror("/dev/urandom"); exit(1); } if (read(f, b+256, 256) != 256) exit(1); close(f); for (int i = 0; i < 256; i++) { b[2*i] = tohex(b[i+256] >> 4); b[2*i+1] = tohex(b[i+256] & 0x0f); } memcpy(b, "tunnek", 6); } int main(int n, char **v) { int central_port = 8001; char *central_ip = "127.0.0.1"; int client_port = -1; char *client_ip = "0.0.0.0"; if (n != 2) { printf("gimme port to listen to\n"); exit(1); } client_port = atoi(v[1]); printf("listen on port %d\n", client_port); char rand[512 + 1]; rand[512] = 0; get_rand(rand); printf("random is %s\n", rand); /* s is socket to connect to central */ int s = socket(AF_INET, SOCK_STREAM, 0); if (s == -1) abort(); int vv = 1; if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &vv, sizeof(vv)) == -1) { perror("setsockopt(NODELAY)"); exit(1); } struct sockaddr_in a; a.sin_family = AF_INET; a.sin_port = htons(central_port); a.sin_addr.s_addr = inet_addr(central_ip); if (connect(s, (struct sockaddr *)&a, sizeof(a)) == -1) { perror("connect"); exit(1); } if (fullwrite(s, rand, 512) != 512) abort(); /* t is socket taking client connections */ int t = socket(AF_INET, SOCK_STREAM, 0); if (t == -1) abort(); vv = 1; if (setsockopt(t, IPPROTO_TCP, TCP_NODELAY, &vv, sizeof(vv)) == -1) { perror("setsockopt(NODELAY)"); exit(1); } vv = 1; if (setsockopt(t, SOL_SOCKET, SO_REUSEADDR, &vv, sizeof(vv)) == -1) { perror("setsockopt(NODELAY)"); exit(1); } a.sin_family = AF_INET; a.sin_port = htons(client_port); a.sin_addr.s_addr = inet_addr(client_ip); if (bind(t, (struct sockaddr *)&a, sizeof(a)) == -1) { perror("bind"); exit(1); } if (listen(t, 5)) abort(); while (1) { struct pollfd fds[2 + MAX]; fds[0].fd = s; fds[0].events = POLLIN; fds[0].revents = 0; fds[1].fd = t; fds[1].events = POLLIN; fds[1].revents = 0; for (int i = 0; i < links_count; i++) { fds[2 + i].fd = links[i].socket; fds[2 + i].events = POLLIN; fds[2 + i].revents = 0; } int fds_size = 2 + links_count; int r = poll(fds, fds_size, -1); if (r == -1) abort(); if (r == 0) continue; /* new client connection? */ if (fds[1].revents & POLLIN) { struct sockaddr_in a; socklen_t alen = sizeof(a); int t2 = accept(t, (struct sockaddr *)&a, &alen); if (t2 == -1) continue; printf("client connect from %s port %d\n", inet_ntoa(a.sin_addr), ntohs(a.sin_port)); if (links_count == MAX) { printf("too many connections, dropping\n"); close(t2); continue; } vv = 1; if (setsockopt(t2, IPPROTO_TCP, TCP_NODELAY, &vv, sizeof(vv)) == -1) { perror("setsockopt(NODELAY)"); exit(1); } int new_link = links_count; links[new_link].socket = t2; links[new_link].port = ntohs(a.sin_port); links[new_link].dead = 0; links_count++; /* inform remote of a new connection */ if (fullwrite(s, &links[new_link].port, sizeof(int)) != sizeof(int)) exit(1); int x = 0; if (fullwrite(s, &x, sizeof(int)) != sizeof(int)) exit(1); } /* remote says something? */ if (fds[0].revents & POLLIN) { char b[512]; int port, length; if (fullread(s, &port, sizeof(int)) != sizeof(int)) exit(1); if (fullread(s, &length, sizeof(int)) != sizeof(int)) exit(1); if (length > 512) { printf("too big length %d\n", length); exit(1); } /* forward to client - ignore if none found */ if (length > 0) { if (fullread(s, b, length) != length) exit(1); } for (int i = 0; i < links_count; i++) { if (links[i].port != port) continue; if (length == -1) { /* length -1 means to remove the client */ links[i].dead = 1; continue; } if (length > 0) { if (fullwrite(links[i].socket, b, length) != length) { /* problem with client, kill it */ links[i].dead = 1; } } } } /* a client says something? */ for (int i = 2; i < fds_size; i++) { if (!(fds[i].revents & POLLIN)) continue; int link = i - 2; /* ignore dead clients */ if (links[link].dead) continue; char b[512]; int r = read(links[link].socket, b, 512); if (r <= 0) { /* client dead */ links[link].dead = 1; } else { /* send to remote */ if (fullwrite(s, &links[link].port, sizeof(int)) != sizeof(int)) exit(1); if (fullwrite(s, &r, sizeof(int)) != sizeof(int)) exit(1); if (fullwrite(s, b, r) != r) exit(1); } } /* remove dead clients */ for (int i = 0; i < links_count; i++) { if (!links[i].dead) continue; /* inform remote of a dead connection */ if (fullwrite(s, &links[i].port, sizeof(int)) != sizeof(int)) exit(1); int x = -1; if (fullwrite(s, &x, sizeof(int)) != sizeof(int)) exit(1); close(links[i].socket); memmove(&links[i], &links[i+1], sizeof(link_t) * (links_count - 1 - i)); i--; links_count--; } } return 0; }