packages feed

Scurry-0.0.1: src/C/help-win.c

/* 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;
}