From 04d37d73a81b67fb36a26793d4ab1ed5ce7178a8 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Sun, 13 Oct 2019 14:32:29 +0200 Subject: [PATCH] Add a little UDP proxy. --- micropubsub/proxy.cpp | 152 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 micropubsub/proxy.cpp diff --git a/micropubsub/proxy.cpp b/micropubsub/proxy.cpp new file mode 100644 index 0000000..0b5c705 --- /dev/null +++ b/micropubsub/proxy.cpp @@ -0,0 +1,152 @@ +// An UDP proxy to get around NAT issues. Use as +// +// g++ -Wall -O2 -o proxy proxy.cpp -pthread +// ./proxy 6001 7001 +// +// Send packets from the bodet client to port 6001. Anyone who contacts +// port 7001 often enough will get those packets sent back to them. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace std::chrono; + +struct Client { + sockaddr_in6 addr; + steady_clock::time_point last_seen; +}; +vector clients; +mutex clients_mu; + +bool sockaddr6_eq(const sockaddr_in6 &addr1, const sockaddr_in6 &addr2) +{ + return memcmp(addr1.sin6_addr.s6_addr, addr2.sin6_addr.s6_addr, 16) == 0 && + addr1.sin6_port == addr2.sin6_port; +} + +string sockaddr_to_string(const sockaddr_in6 &addr) +{ + char buf[256], buf2[256]; + snprintf(buf2, sizeof(buf2), "%s:%d", + inet_ntop(AF_INET6, &addr.sin6_addr, buf, sizeof(buf)), + ntohs(addr.sin6_port)); + return buf2; +} + +int create_sock(int port) +{ + int sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (sock == -1) { + perror("socket"); + exit(1); + } + + int one = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1) { + perror("setsockopt"); + exit(1); + } + + sockaddr_in6 saddr6; + memset(&saddr6, 0, sizeof(saddr6)); + saddr6.sin6_family = AF_INET6; + inet_pton(AF_INET6, "::", &saddr6.sin6_addr); + saddr6.sin6_port = htons(port); + if (bind(sock, (sockaddr *)&saddr6, sizeof(saddr6)) == -1) { + perror("bind"); + exit(1); + } + + return sock; +} + +void recv_thread_func(int bodet_sock, int client_sock) +{ + for ( ;; ) { + char buf[4096]; + int err = recv(bodet_sock, buf, sizeof(buf), 0); + if (err == -1) { + perror("recv"); + exit(1); + } + + printf("received packet '%.*s' from bodet\n", err, buf); + + steady_clock::time_point now = steady_clock::now(); + + lock_guard lock(clients_mu); + + // time out old clients + auto rm_it = remove_if(clients.begin(), clients.end(), [now](const Client &c) { + return now - c.last_seen > seconds(60); + }); + for (auto it = rm_it; it != clients.end(); ++it) { + printf(" - timing out %s\n", sockaddr_to_string(it->addr).c_str()); + } + clients.erase(rm_it, clients.end()); + + for (const Client &client : clients) { + printf(" - sending it to %s\n", sockaddr_to_string(client.addr).c_str()); + sendto(client_sock, buf, err, 0, (sockaddr *)&client.addr, sizeof(client.addr)); + } + } +} + +void attach_thread_func(int sock) +{ + for ( ;; ) { + sockaddr_in6 addr; + socklen_t addrlen = sizeof(addr); + + char buf[4096]; + int err = recvfrom(sock, buf, sizeof(buf), 0, (sockaddr *)&addr, &addrlen); + if (err == -1) { + perror("recv"); + exit(1); + } + + printf("received hello packet from %s\n", sockaddr_to_string(addr).c_str()); + + lock_guard lock(clients_mu); + bool found = false; + for (Client &client : clients) { + if (sockaddr6_eq(client.addr, addr)) { + found = true; + client.last_seen = steady_clock::now(); + break; + } + } + + if (!found) { + printf(" - new client added!\n"); + clients.push_back(Client{ addr, steady_clock::now() }); + } + } +} + +int main(int argc, char **argv) +{ + int bodet_port = atoi(argv[1]); + int client_port = atoi(argv[2]); + + int bodet_sock = create_sock(bodet_port); + int client_sock = create_sock(client_port); + + thread t1(recv_thread_func, bodet_sock, client_sock); + thread t2(attach_thread_func, client_sock); + t1.join(); + t2.join(); +} -- 2.39.2