--- /dev/null
+// 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 <stdio.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <map>
+#include <chrono>
+#include <vector>
+#include <mutex>
+#include <thread>
+#include <algorithm>
+
+using namespace std;
+using namespace std::chrono;
+
+struct Client {
+ sockaddr_in6 addr;
+ steady_clock::time_point last_seen;
+};
+vector<Client> 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<mutex> 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<mutex> 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();
+}