]> git.sesse.net Git - ultimatescore/commitdiff
Add a little UDP proxy.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Sun, 13 Oct 2019 12:32:29 +0000 (14:32 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Sun, 13 Oct 2019 12:32:29 +0000 (14:32 +0200)
micropubsub/proxy.cpp [new file with mode: 0644]

diff --git a/micropubsub/proxy.cpp b/micropubsub/proxy.cpp
new file mode 100644 (file)
index 0000000..0b5c705
--- /dev/null
@@ -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 <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();
+}