diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+The name of John Van Enk; nor the names of his contributors
+may be used to endorse or promote products derived from this software
+without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/Scurry.cabal b/Scurry.cabal
new file mode 100644
--- /dev/null
+++ b/Scurry.cabal
@@ -0,0 +1,72 @@
+Name:          Scurry
+Version:       0.0.1
+Category:      Network
+Description:   A distributed VPN system written in Haskell.
+License:       BSD3
+License-File:  LICENSE
+Author:        John Van Enk
+Maintainer:    vanenkj@gmail.com
+Copyright:     John Van Enk, 2009
+
+Synopsis:      A cross platform P2P VPN application built using Haskell.
+Stability:     Experimental
+
+Description:   P2P VPN implementation currently without any encryption.
+               Supports Windows and Unix variants. Features include:
+               .
+               * Simple NAT Traversal using UDP (similar to STUN, not the same).
+               .
+               * Automatic address assignment to new peers
+               .
+               * Emulates a LAN between machines
+               .
+               Note: Scurry currently has no encryption layer or authentication
+               mechanism at all. Also, the packet switching is currently
+               implemented using Data.List.lookup as opposed to something
+               more appropriate. This really isn't much of a problem until
+               you hit hundreds or thousands of peers.
+               .
+               Requires a TAP-Win32 driver to run in Windows. Requires the tun
+               module to be loaded to run in Linux (expects \/dev\/net\/tun).
+
+Homepage:      http://code.google.com/p/scurry/
+
+Cabal-Version: >= 1.2.0
+Build-Type:    Simple
+
+extra-source-files: src/C/help.h
+
+executable scurry
+    build-depends: base >= 3.0.0.0
+    build-depends: binary >= 0.4.3.1
+    build-depends: network >= 2.1.0.0
+    build-depends: network-bytestring >= 0.1.1.2
+    build-depends: bytestring >= 0.9.1.3
+    build-depends: stm >= 2.1.1.0
+    build-depends: parsec >= 3.0.0
+    build-depends: containers
+    build-depends: time >= 1.1.2.2
+    build-depends: random >= 1.0.0.0
+
+    if os(mingw32)
+        c-sources:          src/C/help-win.c
+        extra-libraries:    Iphlpapi, ws2_32
+        cc-options:         -D MINGW32
+        cpp-options:        -DCALLCONV=stdcall
+
+    if os(linux)
+        build-depends: unix >= 2.3.0.0
+        c-sources:          src/C/help-linux.c
+        cc-options:         -D LINUX
+        cpp-options:        -DCALLCONV=ccall
+
+    if os(darwin) || os(freebsd)
+        build-depends: unix >= 2.3.0.0
+        c-sources:          src/C/help-bsd.c
+        cc-options:         -D BSD
+        cpp-options:        -DCALLCONV=ccall
+ 
+    hs-source-dirs:     src
+    ghc-options:        -Wall -threaded
+    main-is:            scurry.hs
+    
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,4 @@
+module Main where
+
+import Distribution.Simple
+main = defaultMain
diff --git a/src/C/help-bsd.c b/src/C/help-bsd.c
new file mode 100644
--- /dev/null
+++ b/src/C/help-bsd.c
@@ -0,0 +1,153 @@
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ifaddrs.h>
+
+#include "help.h"
+#include "tun_ioctls.h"
+
+int get_mac(struct ifreq * ifr, int sock, struct tap_info * ti);
+
+static int set_ip(struct ifreq * ifr, int sock, ip4_addr_t ip, ip4_addr_t mask);
+static int set_mtu(struct ifreq * ifr, int sock, unsigned int mtu);
+
+int open_tap(ip4_addr_t local_ip, ip4_addr_t local_mask, struct tap_info * ti)
+{
+    struct ifreq ifr_tap;
+    int r = 0;
+
+    int fd = -1;
+    int sock = -1;
+
+    if ((fd = open("/dev/tap0", O_RDWR)) < 0)
+        return -1;
+
+    memset(&ifr_tap, 0, sizeof(ifr_tap));
+
+    /* setup tap */
+    strncpy(ifr_tap.ifr_name, "tap0", IFNAMSIZ);
+    
+    /*
+    if ((ioctl(fd, TUNSIFHEAD, (void *)&ifr_tap)) < 0)
+        return -2;
+    */
+    
+    /* setup ip */
+    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+        return -3;
+
+    if (set_ip(&ifr_tap, sock, local_ip, local_mask) < 0)
+        return -4;
+
+    if ( ioctl(sock, SIOCGIFFLAGS, &ifr_tap) < 0)
+        return -6;
+
+    if ( get_mac(&ifr_tap, sock, ti) < 0)
+        return -7;
+
+    ifr_tap.ifr_flags |= IFF_UP;
+    ifr_tap.ifr_flags |= IFF_RUNNING;
+
+    if (ioctl(sock, SIOCSIFFLAGS, &ifr_tap) < 0)
+        return -8;
+
+    if (set_mtu(&ifr_tap, sock, 1200) < 0)
+        return -9;
+
+    ti->desc->desc = fd;
+
+    return fd;
+}
+
+static int set_ip(struct ifreq * ifr, int sock, ip4_addr_t ip, ip4_addr_t mask)
+{
+    /* Setting a single address of an interface is depreciated. Now uses ifaliasreq and it is done in one call */
+    struct ifaliasreq ifa;
+    struct sockaddr_in *in;
+    
+    memset(&ifa, 0, sizeof(ifa));
+    strcpy(ifa.ifra_name, ifr->ifr_name);
+    
+    in = (struct sockaddr_in *) &ifa.ifra_addr;
+	in->sin_family = AF_INET;
+	in->sin_len = sizeof(ifa.ifra_addr);
+	in->sin_addr.s_addr = ip;
+	
+	in = (struct sockaddr_in *) &ifa.ifra_mask;
+	in->sin_family = AF_INET;
+	in->sin_len = sizeof(ifa.ifra_mask);
+	in->sin_addr.s_addr = mask;
+    
+    if ( ioctl(sock, SIOCSIFADDR, &ifa) < 0) {
+        printf("SIOCAIFADDR: %s\n", strerror(errno));
+        return -1;
+    }
+
+    return 0; 
+}
+
+static int set_mtu(struct ifreq * ifr, int sock, unsigned int mtu)
+{
+    /* Set the MTU of the tap interface */
+    ifr->ifr_mtu = mtu; 
+    if (ioctl(sock, SIOCSIFMTU, ifr) < 0)  {
+        printf("SIOCSIFMTU: %s\n", strerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+void close_tap(union tap_desc * td)
+{
+    if (0 <= td->desc) 
+    {
+        close(td->desc);
+    }
+}
+
+int get_mac(struct ifreq * ifr, int sock, struct tap_info * ti)
+{
+    struct ifaddrs *ifap;
+        
+    if (getifaddrs(&ifap) == 0) {
+        struct ifaddrs *p;
+        for (p = ifap; p; p = p->ifa_next) {
+            if (p->ifa_addr->sa_family == AF_LINK && strncmp(((struct sockaddr_dl *) p->ifa_addr)->sdl_data, "tap", 3) == 0) {
+            	struct sockaddr_dl *sdp = (struct sockaddr_dl *) p->ifa_addr;
+            	memcpy(&(ti->mac), LLADDR(sdp), 6);
+            	freeifaddrs(ifap);
+                return 0;
+            }
+        }
+        freeifaddrs(ifap);
+    }
+
+    printf("Unable to get MAC address.");
+    return -1;    
+}
+
+int read_tap(union tap_desc * td, char * buf, int len)
+{   
+    int ret = read(td->desc,buf,len);
+    return ret;
+}
+
+int write_tap(union tap_desc * td, const char * buf, int len)
+{
+    int ret = write(td->desc,buf,len);
+    return ret;
+}
diff --git a/src/C/help-linux.c b/src/C/help-linux.c
new file mode 100644
--- /dev/null
+++ b/src/C/help-linux.c
@@ -0,0 +1,161 @@
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <net/route.h>
+#include <netinet/if_ether.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "help.h"
+
+int open_tap(ip4_addr_t local_ip, ip4_addr_t local_mask, struct tap_info * ti);
+int get_mac(struct ifreq * ifr, int sock, struct tap_info * ti);
+void close_tap(union tap_desc * td);
+
+static int set_ip(struct ifreq * ifr, int sock, ip4_addr_t ip4);
+static int set_mask(struct ifreq * ifr, int sock, ip4_addr_t ip4);
+static int set_mtu(struct ifreq * ifr, int sock, unsigned int mtu);
+
+
+int open_tap(ip4_addr_t local_ip, ip4_addr_t local_mask, struct tap_info * ti)
+{
+    struct ifreq ifr_tap;
+    int r = 0;
+
+    int fd = -1;
+    int sock = -1;
+
+    if ((fd = open("/dev/net/tun", O_RDWR)) < 0)
+        return -1;
+
+    memset(&ifr_tap, 0, sizeof(ifr_tap));
+
+    /* setup tap */
+    ifr_tap.ifr_flags = IFF_TAP | IFF_NO_PI;
+
+    if ((ioctl(fd, TUNSETIFF, (void *)&ifr_tap)) < 0)
+        return -2;
+    
+    /* setup ip */
+    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+        return -3;
+
+    if (set_ip(&ifr_tap, sock, local_ip) < 0)
+        return -4;
+
+    if (set_mask(&ifr_tap, sock, local_mask) < 0)
+        return -5;
+
+    if ( ioctl(sock, SIOCGIFFLAGS, &ifr_tap) < 0)
+        return -6;
+
+    if ( get_mac(&ifr_tap,sock,ti) < 0)
+        return -7;
+
+    ifr_tap.ifr_flags |= IFF_UP;
+    ifr_tap.ifr_flags |= IFF_RUNNING;
+
+    if (ioctl(sock, SIOCSIFFLAGS, &ifr_tap) < 0)
+        return -8;
+
+    if (set_mtu(&ifr_tap, sock, 1200) < 0)
+        return -9;
+
+    ti->desc->desc = fd;
+
+    return fd;
+}
+
+static int set_ip(struct ifreq * ifr, int sock, ip4_addr_t ip4)
+{
+    struct sockaddr_in addr;
+
+    /* set the IP of this end point of tunnel */
+    memset( &addr, 0, sizeof(addr) );
+    addr.sin_addr.s_addr = ip4; /*network byte order*/
+    addr.sin_family = AF_INET;
+    memcpy( &ifr->ifr_addr, &addr, sizeof(struct sockaddr) );
+
+    if ( ioctl(sock, SIOCSIFADDR, ifr) < 0) {
+        printf("SIOCSIFADDR: %s\n", strerror(errno));
+        return -1;
+    }
+
+    return 0; 
+}
+
+static int set_mask(struct ifreq * ifr, int sock, ip4_addr_t ip4)
+{
+    struct sockaddr_in addr;
+
+    memset( &addr, 0, sizeof(addr) );
+    addr.sin_addr.s_addr = ip4; /*network byte order*/
+    addr.sin_family = AF_INET;
+    memcpy( &ifr->ifr_addr, &addr, sizeof(struct sockaddr) );
+    
+    if ( ioctl(sock, SIOCSIFNETMASK, ifr) < 0) {
+        printf("SIOCSIFNETMASK: %s\n", strerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+static int set_mtu(struct ifreq * ifr, int sock, unsigned int mtu)
+{
+    /* Set the MTU of the tap interface */
+    ifr->ifr_mtu = mtu; 
+    if (ioctl(sock, SIOCSIFMTU, ifr) < 0)  {
+        printf("SIOCSIFMTU: %s\n", strerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+void close_tap(union tap_desc * td)
+{
+    if (0 <= td->desc) 
+    {
+        close(td->desc);
+    }
+}
+
+int get_mac(struct ifreq * ifr, int sock, struct tap_info * ti)
+{
+    
+    if ( ioctl(sock, SIOCGIFHWADDR, ifr) < 0) {
+        printf("SIOCGIFHWADDR: %s\n", strerror(errno));
+        return -1;
+    }
+    else
+    {
+        memcpy(&(ti->mac),&(ifr->ifr_hwaddr.sa_data),6);
+    }
+
+
+    return 0;
+}
+
+/* I HATE WINDOWS IT SUCKS SO HARD AHHH!! */
+int read_tap(union tap_desc * td, char * buf, int len)
+{
+    int ret = read(td->desc,buf,len);
+    return ret;
+}
+
+int write_tap(union tap_desc * td, const char * buf, int len)
+{
+    int ret = write(td->desc,buf,len);
+    return ret;
+}
diff --git a/src/C/help-win.c b/src/C/help-win.c
new file mode 100644
--- /dev/null
+++ b/src/C/help-win.c
@@ -0,0 +1,529 @@
+/* NOTE: most of this code is a direct copy of the qemu tap code
+*/
+
+#include <stdio.h>
+#include <wchar.h>
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winioctl.h>
+#include <ws2tcpip.h>
+#include <iphlpapi.h>
+#include <io.h>
+#include <Fcntl.h>
+#include <malloc.h>
+
+//=============
+// TAP IOCTLs
+//=============
+
+#define TAP_CONTROL_CODE(request,method) \
+  CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
+
+#define TAP_IOCTL_GET_MAC               TAP_CONTROL_CODE (1, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_VERSION           TAP_CONTROL_CODE (2, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_MTU               TAP_CONTROL_CODE (3, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_INFO              TAP_CONTROL_CODE (4, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE (5, METHOD_BUFFERED)
+#define TAP_IOCTL_SET_MEDIA_STATUS      TAP_CONTROL_CODE (6, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_DHCP_MASQ      TAP_CONTROL_CODE (7, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_LOG_LINE          TAP_CONTROL_CODE (8, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_DHCP_SET_OPT   TAP_CONTROL_CODE (9, METHOD_BUFFERED)
+
+//=================
+// Registry keys
+//=================
+
+#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+
+#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+
+//======================
+// Filesystem prefixes
+//======================
+
+#define USERMODEDEVICEDIR "\\\\.\\Global\\"
+#define TAPSUFFIX         ".tap"
+
+#define TAP_COMPONENT_ID "tap0801"
+
+//======================
+// Compile time configuration
+//======================
+
+//#define DEBUG_TAP_WIN32 1
+
+#define TUN_ASYNCHRONOUS_WRITES 1
+
+#define TUN_BUFFER_SIZE 1560
+#define TUN_MAX_BUFFER_COUNT 32
+ 
+
+#include "help.h"
+
+static OVERLAPPED overlap_read, overlap_write;
+
+
+int open_tap(ip4_addr_t local_ip, ip4_addr_t local_mask, struct tap_info * ti);
+
+static const IP_ADAPTER_INFO *
+get_adapter (const IP_ADAPTER_INFO *ai, DWORD index)
+{
+  if (ai && index != (DWORD)~0)
+    {
+      const IP_ADAPTER_INFO *a;
+
+      /* find index in the linked list */
+      for (a = ai; a != NULL; a = a->Next)
+	{
+	  if (a->Index == index)
+	    return a;
+	}
+    }
+  return NULL;
+}
+
+
+static void
+delete_temp_addresses (DWORD index)
+{
+  ULONG size = 4096;
+  DWORD status;
+  IP_ADAPTER_INFO *adapters = (IP_ADAPTER_INFO *) malloc(size);
+  if ((status = GetAdaptersInfo (adapters, &size)) != NO_ERROR)
+	 return;
+   
+  const IP_ADAPTER_INFO *a = get_adapter(adapters, index);
+
+  if (a)
+  {
+    const IP_ADDR_STRING *ip = &a->IpAddressList;
+    while (ip)
+    {
+      
+      const DWORD context = ip->Context;
+
+      if ((status = DeleteIPAddress ((ULONG) context)) == NO_ERROR)
+      {
+        //msg (M_INFO, "Successfully deleted previously set dynamic IP/netmask: %s/%s",  ip->IpAddress.String,    ip->IpMask.String);
+      }
+      else
+      {
+        const char *empty = "0.0.0.0";
+        if (strcmp (ip->IpAddress.String, empty)
+         || strcmp (ip->IpMask.String, empty)) {}
+          //msg (M_INFO, "NOTE: could not delete previously set dynamic IP/netmask: %s/%s (status=%u)", ip->IpAddress.String, ip->IpMask.String,         (unsigned int)status);
+      }
+      ip = ip->Next;
+    }
+  }
+  free(adapters);
+}
+
+static int is_tap_win32_dev(const char *guid)
+{
+    HKEY netcard_key;
+    LONG status;
+    DWORD len;
+    int i = 0;
+
+    status = RegOpenKeyEx(
+        HKEY_LOCAL_MACHINE,
+        (LPCTSTR) ADAPTER_KEY,
+        0,
+        KEY_READ,
+        (PHKEY)&netcard_key);
+
+    if (status != ERROR_SUCCESS) {
+        return FALSE;
+    }
+
+    for (;;) {
+        char enum_name[256];
+        char unit_string[256];
+        HKEY unit_key;
+        char component_id_string[] = "ComponentId";
+        char component_id[256];
+        char net_cfg_instance_id_string[] = "NetCfgInstanceId";
+        char net_cfg_instance_id[256];
+        DWORD data_type;
+
+        len = sizeof (enum_name);
+        status = RegEnumKeyEx(
+            netcard_key,
+            i,
+            (LPTSTR) enum_name,
+            &len,
+            NULL,
+            NULL,
+            NULL,
+            NULL);
+
+        if (status == ERROR_NO_MORE_ITEMS)
+            break;
+        else if (status != ERROR_SUCCESS) {
+            return FALSE;
+        }
+
+        snprintf (unit_string, sizeof(unit_string), "%s\\%s",
+                  ADAPTER_KEY, enum_name);
+
+        status = RegOpenKeyEx(
+            HKEY_LOCAL_MACHINE,
+            (LPCTSTR) unit_string,
+            0,
+            KEY_READ,
+            &unit_key);
+
+        if (status != ERROR_SUCCESS) {
+            return FALSE;
+        } else {
+            len = sizeof (component_id);
+            status = RegQueryValueEx(
+                unit_key,
+                (LPCTSTR) component_id_string,
+                NULL,
+                &data_type,
+                component_id,
+                &len);
+
+            if (!(status != ERROR_SUCCESS || data_type != REG_SZ)) {
+                len = sizeof (net_cfg_instance_id);
+                status = RegQueryValueEx(
+                    unit_key,
+                    (LPCTSTR) net_cfg_instance_id_string,
+                    NULL,
+                    &data_type,
+                    net_cfg_instance_id,
+                    &len);
+
+                if (status == ERROR_SUCCESS && data_type == REG_SZ) {
+                    if (!strcmp (component_id, TAP_COMPONENT_ID) &&
+                        !strcmp (net_cfg_instance_id, guid)) {
+                        RegCloseKey (unit_key);
+                        RegCloseKey (netcard_key);
+                        return TRUE;
+                    }
+                }
+            }
+            RegCloseKey (unit_key);
+        }
+        ++i;
+    }
+
+    RegCloseKey (netcard_key);
+    return FALSE;
+}
+
+static int get_device_guid(
+    char *name,
+    int name_size,
+    char *actual_name,
+    int actual_name_size)
+{
+    LONG status;
+    HKEY control_net_key;
+    DWORD len;
+    int i = 0;
+    int stop = 0;
+
+    status = RegOpenKeyEx(
+        HKEY_LOCAL_MACHINE,
+        (LPCTSTR) NETWORK_CONNECTIONS_KEY,
+        0,
+        KEY_READ,
+        &control_net_key);
+
+    if (status != ERROR_SUCCESS) {
+        return -1;
+    }
+
+    while (!stop)
+    {
+        char enum_name[256];
+        char connection_string[256];
+        HKEY connection_key;
+        char name_data[256];
+        DWORD name_type;
+        const char name_string[] = "Name";
+
+        len = sizeof (enum_name);
+        status = RegEnumKeyEx(
+            control_net_key,
+            i,
+            (LPTSTR) enum_name,
+            &len,
+            NULL,
+            NULL,
+            NULL,
+            NULL);
+
+        if (status == ERROR_NO_MORE_ITEMS)
+            break;
+        else if (status != ERROR_SUCCESS) {
+            return -1;
+        }
+
+        snprintf(connection_string,
+             sizeof(connection_string),
+             "%s\\%s\\Connection",
+             NETWORK_CONNECTIONS_KEY, enum_name);
+
+        status = RegOpenKeyEx(
+            HKEY_LOCAL_MACHINE,
+            (LPCTSTR) connection_string,
+            0,
+            KEY_READ,
+            &connection_key);
+
+        if (status == ERROR_SUCCESS) {
+            len = sizeof (name_data);
+            status = RegQueryValueEx(
+                connection_key,
+                (LPTSTR) name_string,
+                NULL,
+                &name_type,
+                name_data,
+                &len);
+
+            if (status != ERROR_SUCCESS || name_type != REG_SZ) {
+                    return -1;
+            }
+            else {
+                if (is_tap_win32_dev(enum_name)) {
+                    snprintf(name, name_size, "%s", enum_name);
+                    if (actual_name) {
+                        if (strcmp(actual_name, "") != 0) {
+                            if (strcmp(name_data, actual_name) != 0) {
+                                RegCloseKey (connection_key);
+                                ++i;
+                                continue;
+                            }
+                        }
+                        else {
+                            snprintf(actual_name, actual_name_size, "%s", name_data);
+                        }
+                    }
+                    stop = 1;
+                }
+            }
+
+            RegCloseKey (connection_key);
+        }
+        ++i;
+    }
+
+    RegCloseKey (control_net_key);
+
+    if (stop == 0)
+        return -1;
+
+    return 0;
+}
+
+static DWORD get_interface_index (const char *guid)
+{
+  ULONG index;
+  DWORD status;
+  wchar_t wbuf[256];
+  snwprintf (wbuf, sizeof (wbuf), L"\\DEVICE\\TCPIP_%S", guid);
+  wbuf [sizeof(wbuf) - 1] = 0;
+  if ((status = GetAdapterIndex (wbuf, &index)) != NO_ERROR)
+    return (DWORD)~0;
+  else
+    return index;
+}
+
+static int tap_win32_set_status(HANDLE handle, int status)
+{
+    unsigned long len = 0;
+
+    return DeviceIoControl(handle, TAP_IOCTL_SET_MEDIA_STATUS,
+                &status, sizeof (status),
+                &status, sizeof (status), &len, NULL);
+}
+
+int open_tap(ip4_addr_t local_ip, ip4_addr_t local_mask, struct tap_info * ti)
+{
+    // #TODO this function needs some pretty heavy cleanup
+    const char *prefered_name = NULL;
+      
+    DWORD err;
+    char device_path[256];
+    char device_guid[0x100];
+    int rc;
+    HANDLE handle;
+    DWORD index;
+    ULONG ipapi_context;
+    ULONG ipapi_instance;
+    BOOL bret;
+    char name_buffer[0x100] = {0, };
+    struct {
+        unsigned long major;
+        unsigned long minor;
+        unsigned long debug;
+    } version;
+    DWORD len;
+
+    if (prefered_name != NULL)
+        snprintf(name_buffer, sizeof(name_buffer), "%s", prefered_name);
+
+    rc = get_device_guid(device_guid, sizeof(device_guid), name_buffer, sizeof(name_buffer));
+    if (rc)
+        return -1;
+    index = get_interface_index(device_guid);
+
+    snprintf (device_path, sizeof(device_path), "%s%s%s",
+              USERMODEDEVICEDIR,
+              device_guid,
+              TAPSUFFIX);
+
+    handle = CreateFile (
+        (LPCTSTR) device_path,
+        GENERIC_READ | GENERIC_WRITE,
+        0,
+        0,
+        OPEN_EXISTING,
+        FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
+        0 );
+
+    if (handle == INVALID_HANDLE_VALUE) {
+        return -2;
+    }
+
+    bret = DeviceIoControl(handle, TAP_IOCTL_GET_VERSION,
+                           &version, sizeof (version),
+                           &version, sizeof (version), &len, NULL);
+
+    if (bret == FALSE) {
+        CloseHandle(handle);
+        return -3;
+    }
+    
+    if(!DeviceIoControl(handle, TAP_IOCTL_GET_MAC, ti->mac, 6, ti->mac, 6, &len, 0)) {
+      return -4;
+    }
+    
+    uint32_t ep[4];
+    ep[0] = local_ip;
+    ep[1] = local_mask;
+    ep[2] = 0xFE00000A;
+    ep[3] = 0x00FFFFFF;
+    if (!DeviceIoControl (handle, TAP_IOCTL_CONFIG_DHCP_MASQ,
+			    ep, sizeof (ep),
+			    ep, sizeof (ep), &len, NULL))
+      return -5;
+    
+    if (!tap_win32_set_status(handle, TRUE)) {
+      return -6;
+    }
+    
+    FlushIpNetTable(index);
+    
+    overlap_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+	  overlap_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+	  if (!overlap_read.hEvent || !overlap_write.hEvent) {
+  		return -7;
+  	}
+    
+    // delete_temp_addresses (index);
+    
+    // printf("%x\n", local_ip);
+    // if ((err = AddIPAddress (local_ip,
+                             // local_mask,
+                             // index,
+                             // &ipapi_context,
+                             // &ipapi_instance)) != NO_ERROR)
+    // {
+      // printf("AddIPAddress failed! (%d)\n", err);
+
+      // LPVOID lpMsgBuf;
+
+      // if (5010 != err) {
+        // if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                          // FORMAT_MESSAGE_FROM_SYSTEM |
+                          // FORMAT_MESSAGE_IGNORE_INSERTS,
+                          // NULL, err,
+                          // MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                          // (LPTSTR) & lpMsgBuf, 0, NULL)) {
+              // printf("\tError: %s", lpMsgBuf);
+              // LocalFree(lpMsgBuf);
+        // }
+        // return -5;
+      // }
+    // }
+    // if (AddIPAddress (local_ip,
+                      // local_mask,
+                      // index,
+                      // &ipapi_context,
+                      // &ipapi_instance) != NO_ERROR)
+      // return -7;
+    
+
+
+    
+    ti->desc->desc = handle;
+    ti->desc->context = ipapi_context;
+    
+    return 0;
+}
+
+
+void close_tap(union tap_desc * td)
+{
+  //DeleteIPAddress(td->context);
+  CloseHandle(td->desc);
+}
+
+
+/* Read a frame from a tap device. The buffer needs
+ * to be at least as large as the MTU of the device. */
+int read_tap(union tap_desc * td, char * buf, int len)
+{
+	DWORD read_size;
+	
+	ResetEvent(overlap_read.hEvent);
+	if (ReadFile(td->desc, buf, len, &read_size, &overlap_read)) {
+		return read_size;
+	}
+	switch (GetLastError()) {
+	case ERROR_IO_PENDING:
+		WaitForSingleObject(overlap_read.hEvent, INFINITE);
+		GetOverlappedResult(td->desc, &overlap_read, &read_size, FALSE);
+		return read_size;
+		break;
+	default:
+		break;
+	}
+	
+	return -1;
+}
+
+/* Write a frame to a tap device. The frame length
+ * must be less than the MTU of the device. */
+int write_tap(union tap_desc * td, const char * buf, int len)
+{  
+  DWORD write_size;
+	
+	ResetEvent(overlap_write.hEvent);
+	if (WriteFile(td->desc,
+		buf,
+		len,
+		&write_size,
+		&overlap_write)) {
+		return write_size;
+	}
+	switch (GetLastError()) {
+	case ERROR_IO_PENDING:
+		WaitForSingleObject(overlap_write.hEvent, INFINITE);
+		GetOverlappedResult(td->desc, &overlap_write,
+			&write_size, FALSE);
+		return write_size;
+		break;
+	default:
+		break;
+	}
+	
+	return -1;
+}
+
+
diff --git a/src/C/help.h b/src/C/help.h
new file mode 100644
--- /dev/null
+++ b/src/C/help.h
@@ -0,0 +1,57 @@
+#ifndef __HELP_HASKELL_VPN__
+#define __HELP_HASKELL_VPN__ __HELP_HASKELL_VPN__
+
+#include <assert.h>
+
+typedef uint32_t ip4_addr_t;
+
+#define TAP_DESC_SIZE 32
+
+/* A union that serves to be a common storage medium for
+ * whatever the OS wants to use as a device descriptor
+ * for the TAP device. In Linux, this is just a file 
+ * descriptor. */
+union tap_desc {
+    char pad[TAP_DESC_SIZE];
+#if defined(MINGW32)
+    struct
+    {
+      HANDLE desc;
+      unsigned long context;
+    };
+#elif defined(LINUX)
+    int desc;
+#elif defined(BSD)
+    int desc;
+#else
+    #error "ERROR: This platform not supported."
+#endif
+};
+
+/* A struct used to pass information about
+ * a tap device around. */
+struct tap_info {
+    union tap_desc * desc;
+    char mac[8]; /* Use 8, instead of 6, to force alignment */
+};
+
+/* Open a tap device. The file descriptor and the
+ * MAC address are written to the tap_info struct.
+ * On failure, a negative value is returned. */
+int open_tap(ip4_addr_t local_ip,
+             ip4_addr_t local_mask,
+             struct tap_info * ti);
+
+/* Close a tap device. */
+void close_tap(union tap_desc * td);
+
+/* Read a frame from a tap device. The buffer needs
+ * to be at least as large as the MTU of the device. */
+int read_tap(union tap_desc * td, char * buf, int len);
+
+/* Write a frame to a tap device. The frame length
+ * must be less than the MTU of the device. */
+int write_tap(union tap_desc * td, const char * buf, int len);
+
+#endif /* __HELP_HASKELL_VPN__ */
+
diff --git a/src/scurry.hs b/src/scurry.hs
new file mode 100644
--- /dev/null
+++ b/src/scurry.hs
@@ -0,0 +1,137 @@
+module Main where
+
+import System.Console.GetOpt
+import System.Environment
+import System.IO
+import System.Exit
+
+import Network.Socket (withSocketsDo)
+import Network.BSD
+
+import Scurry.Peer
+import Scurry.Comm
+-- import Scurry.Management.Config
+-- import Scurry.Management.Tracker
+import Scurry.State
+import Scurry.Util(inet_addr)
+import Scurry.Network
+import Scurry.Types.Network
+import Data.Word
+import Data.Maybe
+
+data ScurryOpts = ScurryOpts {
+    vpnAddr  :: Maybe ScurryAddress,
+    vpnMask  :: Maybe ScurryMask,
+    bindAddr :: ScurryAddress,
+    bindPort :: ScurryPort,
+    peers    :: [EndPoint]
+} deriving (Show)
+
+data ScurryOpt = SOVAddr ScurryAddress
+               | SOVMask ScurryMask
+               | SOBAddr ScurryAddress
+               | SOBPort ScurryPort
+    deriving (Show)
+
+defaultPort :: ScurryPort
+defaultPort = ScurryPort 24999
+
+readPort :: String -> ScurryPort
+readPort [] = error "I need some numbers!"
+readPort s = let w = read s :: Word16
+             in (ScurryPort . fromIntegral) w
+
+lookupName :: String -> IO EndPoint
+lookupName ep = do
+    let name = takeWhile (/=':') ep
+        port = case drop 1 $ dropWhile (/=':') ep of
+                    [] -> defaultPort
+                    x  -> readPort x
+
+    HostEntry { hostAddresses = as } <- getHostByName name
+
+    return $ EndPoint (ScurryAddress (as !! 0)) port
+          
+parseScurryOpts :: [String] -> IO (Either String ScurryOpts)
+parseScurryOpts args = let def_opt = ScurryOpts { vpnAddr = Nothing
+                                                , vpnMask = Nothing
+                                                , bindAddr = ScurryAddress 0
+                                                , bindPort = ScurryPort 24999
+                                                , peers = [] }
+                           va_t = SOVAddr . fromJust . inet_addr
+                           vm_t = SOVMask . fromJust . inet_addr
+                           ba_t = SOBAddr . fromJust . inet_addr
+                           bp_t = SOBPort . readPort
+                           options = [ Option "a" ["vpn-addr"]  (ReqArg va_t "VPN_ADDRESS")  "The IP address to use for the VPN."
+                                     , Option "m" ["vpn-mask"]  (ReqArg vm_t "VPN_NETMASK")  "The network mask the for the VPN."
+                                     , Option "b" ["bind-addr"] (ReqArg ba_t "BIND_ADDRESS") "The network interface on which listen for connections."
+                                     , Option "p" ["bind-port"] (ReqArg bp_t "BIND_PORT")    "The port on which to listen for connections." ]
+                           (m,o,_) = getOpt RequireOrder options args -- ignore errors for now
+                           u = Left (usageInfo "How to use scurry." options)
+                           opt' = foldr rplcOpts def_opt m
+                       in case args of
+                               ["-h"]     -> return u
+                               ["--help"] -> return u
+                               _          -> do ps <- mapM lookupName o
+                                                return $ Right (opt' { peers = ps })
+
+    where rplcOpts (SOVAddr a) sos = sos { vpnAddr  = Just a }
+          rplcOpts (SOVMask m) sos = sos { vpnMask  = Just m }
+          rplcOpts (SOBAddr a) sos = sos { bindAddr = a }
+          rplcOpts (SOBPort p) sos = sos { bindPort = p }
+
+main :: IO ()
+main = withSocketsDo $ do 
+    args <- getArgs
+
+    opts_ <- parseScurryOpts args
+    -- let (configPath:trackerPath:_) = args
+
+    opts <- case opts_ of
+                 (Left msg) -> putStrLn msg >> exitFailure
+                 (Right p)  -> return p
+
+    -- (Just config)  <- load_scurry_config_file configPath
+    -- (Just tracker) <- load_tracker_file trackerPath
+
+    -- let (Scurry (VpnConfig tapIp tapMask) (NetworkConfig mySockAddr)) = config
+    --     trackerEndPoints = filter (/= mySockAddr) $ map tToS tracker
+
+    let initState = ScurryState {
+            scurryPeers = [],
+            scurryEndPoint = EndPoint (bindAddr opts) (bindPort opts),
+            scurryNetwork = ScurryNetwork {
+                scurryMask = vpnMask opts
+            },
+            scurryMyRecord = PeerRecord {
+                peerMAC = Nothing,
+                peerEndPoint = EndPoint (ScurryAddress 0) (ScurryPort 0),
+                peerVPNAddr = vpnAddr opts,
+                peerLocalPort = bindPort opts -- (\(EndPoint _ p) -> p) mySockAddr
+            }
+        }
+        tapNet = (vpnAddr opts, vpnMask opts)
+
+    local <- prepEndPoint (scurryEndPoint initState) -- mySockAddr
+
+    startCom tapNet local initState (peers opts)
+
+    {-
+    where
+        tToS (ScurryPeer ip port) = EndPoint ip port 
+    -}
+
+
+{-
+getAddress :: Socket -> [EndPoint] -> IO (ScurryAddress, ScurryMask)
+getAddress sock (t:trackers) = do
+    let msg = SAddrRequest
+        cmd = sendToAddr sock msg
+    
+    putStrLn "Requesting address..."
+    cmd t
+
+    where
+        delayRead = do
+            threadDelay (sToMs 1.5)
+-}            
