Add files for swift over UDP.
[swifty.git] / src / libswift_udp / nat_test.cpp
diff --git a/src/libswift_udp/nat_test.cpp b/src/libswift_udp/nat_test.cpp
new file mode 100644 (file)
index 0000000..11d9fef
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ *  nat_test.cpp
+ *  NAT type testing.
+ *
+ *  Created by Gertjan Halkes.
+ *  Copyright 2010 Delft University of Technology. All rights reserved.
+ *
+ */
+
+#include "swift.h"
+#ifdef _WIN32
+#include <iphlpapi.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <ifaddrs.h>
+#include <errno.h>
+#include <netinet/in.h>
+#endif
+
+#define REQUEST_MAGIC 0x5a9e5fa1
+#define REPLY_MAGIC 0xa655c5d5
+#define REPLY_SEC_MAGIC 0x85e4a5ca
+#define MAX_TRIES 3
+namespace swift {
+
+static void on_may_receive(SOCKET sock);
+static void on_may_send(SOCKET sock);
+static tint test_start;
+static int tries;
+static int packets_since_last_try;
+
+static sckrwecb_t callbacks(0, on_may_receive, on_may_send, NULL);
+/* Note that we lookup the addresses when we actually send, because Windows requires that
+   the winsock library is first intialized. If we use Address type variables here, the
+   lookup would be tried before that initialization, which fails... */
+//FIXME: Change addresses to actual addresses used in test (at least 2 should be provided!)
+static const char *servers[] = { "dutigp.st.ewi.tudelft.nl:18375" ,
+    "127.0.0.3:18375" };
+
+static void on_may_receive(SOCKET sock) {
+    Datagram data(sock);
+
+    data.Recv();
+
+    uint32_t magic = data.Pull32();
+    if ((magic != REPLY_MAGIC && magic != REPLY_SEC_MAGIC) ||
+            (magic == REPLY_MAGIC && data.size() != 6) || (magic == REPLY_SEC_MAGIC && data.size() != 0))
+    {
+        dprintf("%s #0 NATTEST weird packet %s \n", tintstr(), data.address().str());
+        return;
+    }
+
+    if (magic == REPLY_MAGIC) {
+        uint32_t ip = data.Pull32();
+        uint16_t port = data.Pull16();
+        Address reported(ip, port);
+        dprintf("%s #0 NATTEST incoming %s %s\n", tintstr(), data.address().str(), reported.str());
+    } else {
+        dprintf("%s #0 NATTEST incoming secondary %s\n", tintstr(), data.address().str());
+    }
+    packets_since_last_try++;
+}
+
+static void on_may_send(SOCKET sock) {
+    callbacks.may_write = NULL;
+    Datagram::Listen3rdPartySocket(callbacks);
+
+    for (size_t i = 0; i < (sizeof(servers)/sizeof(servers[0])); i++) {
+        Datagram request(sock, Address(servers[i]));
+
+        request.Push32(REQUEST_MAGIC);
+        request.Send();
+    }
+    test_start = NOW;
+
+    struct sockaddr_in name;
+    socklen_t namelen = sizeof(name);
+    if (getsockname(sock, (struct sockaddr *) &name, &namelen) < 0) {
+        dprintf("%s #0 NATTEST could not get local address\n", tintstr());
+    } else {
+        Address local(ntohl(name.sin_addr.s_addr), ntohs(name.sin_port));
+        dprintf("%s #0 NATTEST local %s\n", tintstr(), local.str());
+    }
+}
+
+static void printAddresses(void) {
+#ifdef _WIN32
+    IP_ADAPTER_INFO *adapterInfo = NULL;
+    IP_ADAPTER_INFO *adapter = NULL;
+    DWORD retval = 0;
+    UINT i;
+    ULONG size = 0;
+
+    if ((retval = GetAdaptersInfo(adapterInfo, &size)) != ERROR_BUFFER_OVERFLOW) {
+        dprintf("ERROR: %d\n", (int) retval);
+        return;
+    }
+
+    adapterInfo = (IP_ADAPTER_INFO *) malloc(size);
+    if (adapterInfo == NULL) {
+        dprintf("ERROR: out of memory\n");
+        return;
+    }
+
+    if ((retval = GetAdaptersInfo(adapterInfo, &size)) == NO_ERROR) {
+        adapter = adapterInfo;
+        while (adapter) {
+            IP_ADDR_STRING *address;
+            for (address = &adapter->IpAddressList; address != NULL; address = address->Next) {
+                if (address->IpAddress.String[0] != 0)
+                    dprintf("ADDRESS: %s\n", address->IpAddress.String);
+            }
+            adapter = adapter->Next;
+        }
+    } else {
+        dprintf("ERROR: %d\n", (int) retval);
+    }
+    free(adapterInfo);
+#else
+    struct ifaddrs *addrs, *ptr;
+    if (getifaddrs(&addrs) < 0) {
+        dprintf("ERROR: %s\n", strerror(errno));
+        return;
+    }
+
+    for (ptr = addrs; ptr != NULL; ptr = ptr->ifa_next) {
+        if (ptr->ifa_addr->sa_family == AF_INET) {
+            dprintf("ADDRESS: %s\n", inet_ntoa(((struct sockaddr_in *) ptr->ifa_addr)->sin_addr));
+        }
+    }
+    freeifaddrs(addrs);
+#endif
+}
+
+
+void nat_test_update(void) {
+    static bool initialized;
+    if (!initialized) {
+        initialized = true;
+        printAddresses();
+    }
+
+    if (tries < MAX_TRIES && NOW - test_start > 30 * TINT_SEC) {
+        if (tries == 0) {
+            Address any;
+            SOCKET sock = Datagram::Bind(any, callbacks);
+            callbacks.sock = sock;
+        } else if (packets_since_last_try == 0) {
+            // Keep on trying if we didn't receive _any_ packet in response to our last request
+            tries--;
+        }
+        tries++;
+        callbacks.may_write = on_may_send;
+        Datagram::Listen3rdPartySocket(callbacks);
+    }
+}
+
+}