/*
     This file is part of GNUnet.
     Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 GNUnet e.V.

     GNUnet is free software: you can redistribute it and/or modify it
     under the terms of the GNU Affero General Public License as published
     by the Free Software Foundation, either version 3 of the License,
     or (at your option) any later version.

     GNUnet is distributed in the hope that it will be useful, but
     WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     Affero General Public License for more details.
    
     You should have received a copy of the GNU Affero General Public License
     along with this program.  If not, see <http://www.gnu.org/licenses/>.

     SPDX-License-Identifier: AGPL3.0-or-later
*/

/**
 * @file util/win.c
 * @brief Helper functions for MS Windows in C++
 * @author Nils Durner
 */

#ifndef _WIN_C
#define _WIN_C

#include "winproc.h"
#include "platform.h"
#include "gnunet_crypto_lib.h"
#include "gnunet_common.h"
#include "gnunet_connection_lib.h"

#include <ntdef.h>
#ifndef INHERITED_ACE
#define INHERITED_ACE 0x10
#endif

int plibc_conv_to_win_path(const char *pszUnix, char *pszWindows);

#define _IP_ADAPTER_UNICAST_ADDRESS_HEAD \
  union { \
    struct { \
      ULONG Length; \
      DWORD Flags; \
    }; \
  };

#define _IP_ADAPTER_UNICAST_ADDRESS_BASE \
  SOCKET_ADDRESS                     Address; \
  IP_PREFIX_ORIGIN                   PrefixOrigin; \
  IP_SUFFIX_ORIGIN                   SuffixOrigin; \
  IP_DAD_STATE                       DadState; \
  ULONG                              ValidLifetime; \
  ULONG                              PreferredLifetime; \
  ULONG                              LeaseLifetime;

#define _IP_ADAPTER_UNICAST_ADDRESS_ADD_VISTA \
  UINT8                              OnLinkPrefixLength;


#define _IP_ADAPTER_UNICAST_ADDRESS_DEFINE(suffix,addition) \
typedef struct _IP_ADAPTER_UNICAST_ADDRESS##suffix { \
  _IP_ADAPTER_UNICAST_ADDRESS_HEAD \
  struct _IP_ADAPTER_UNICAST_ADDRESS##suffix *Next; \
  _IP_ADAPTER_UNICAST_ADDRESS_BASE \
  addition \
} IP_ADAPTER_UNICAST_ADDRESS##suffix, *PIP_ADAPTER_UNICAST_ADDRESS##suffix;

/* _IP_ADAPTER_UNICAST_ADDRESS_DEFINE(,) defined in w32api headers */
_IP_ADAPTER_UNICAST_ADDRESS_DEFINE(_VISTA,_IP_ADAPTER_UNICAST_ADDRESS_ADD_VISTA)


#ifndef __MINGW64_VERSION_MAJOR
typedef struct _IP_ADAPTER_WINS_SERVER_ADDRESS {
  union {
    ULONGLONG Alignment;
    struct {
      ULONG Length;
      DWORD Reserved;
    };
  };
  struct _IP_ADAPTER_WINS_SERVER_ADDRESS  *Next;
  SOCKET_ADDRESS                         Address;
} IP_ADAPTER_WINS_SERVER_ADDRESS, *PIP_ADAPTER_WINS_SERVER_ADDRESS, *PIP_ADAPTER_WINS_SERVER_ADDRESS_LH;

typedef struct _IP_ADAPTER_GATEWAY_ADDRESS {
  union {
    ULONGLONG Alignment;
    struct {
      ULONG Length;
      DWORD Reserved;
    };
  };
  struct _IP_ADAPTER_GATEWAY_ADDRESS  *Next;
  SOCKET_ADDRESS                     Address;
} IP_ADAPTER_GATEWAY_ADDRESS, *PIP_ADAPTER_GATEWAY_ADDRESS, *PIP_ADAPTER_GATEWAY_ADDRESS_LH;
#endif

typedef UINT32 NET_IF_COMPARTMENT_ID;
typedef GUID NET_IF_NETWORK_GUID;

#ifndef __MINGW64_VERSION_MAJOR
typedef enum _NET_IF_CONNECTION_TYPE {
  NET_IF_CONNECTION_DEDICATED   = 1,
  NET_IF_CONNECTION_PASSIVE,
  NET_IF_CONNECTION_DEMAND,
  NET_IF_CONNECTION_MAXIMUM
} NET_IF_CONNECTION_TYPE, *PNET_IF_CONNECTION_TYPE;

typedef enum  {
  TUNNEL_TYPE_NONE      = 0,
  TUNNEL_TYPE_OTHER,
  TUNNEL_TYPE_DIRECT,
  TUNNEL_TYPE_6TO4,
  TUNNEL_TYPE_ISATAP,
  TUNNEL_TYPE_TEREDO,
  TUNNEL_TYPE_IPHTTPS
} TUNNEL_TYPE, *PTUNNEL_TYPE;
#endif

/*
A DUID consists of a two-octet type code represented in network byte
   order, followed by a variable number of octets that make up the
   actual identifier.  A DUID can be no more than 128 octets long (not
   including the type code).
*/
#define MAX_DHCPV6_DUID_LENGTH 130

#ifndef __MINGW64_VERSION_MAJOR
typedef union _NET_LUID {
  ULONG64 Value;
  struct {
    ULONG64 Reserved  :24;
    ULONG64 NetLuidIndex  :24;
    ULONG64 IfType  :16;
  } Info;
} NET_LUID, *PNET_LUID, IF_LUID;

#define MAX_DNS_SUFFIX_STRING_LENGTH 246

typedef struct _IP_ADAPTER_DNS_SUFFIX {
  struct _IP_ADAPTER_DNS_SUFFIX  *Next;
  WCHAR                         String[MAX_DNS_SUFFIX_STRING_LENGTH];
} IP_ADAPTER_DNS_SUFFIX, *PIP_ADAPTER_DNS_SUFFIX;
#endif



#define _IP_ADAPTER_ADDRESSES_HEAD \
  union { \
    ULONGLONG Alignment; \
    struct { \
      ULONG Length; \
      DWORD IfIndex; \
    }; \
  };

#define _IP_ADAPTER_ADDRESSES_BASE \
  PCHAR                              AdapterName; \
  PIP_ADAPTER_UNICAST_ADDRESS        FirstUnicastAddress; \
  PIP_ADAPTER_ANYCAST_ADDRESS        FirstAnycastAddress; \
  PIP_ADAPTER_MULTICAST_ADDRESS      FirstMulticastAddress; \
  PIP_ADAPTER_DNS_SERVER_ADDRESS     FirstDnsServerAddress; \
  PWCHAR                             DnsSuffix; \
  PWCHAR                             Description; \
  PWCHAR                             FriendlyName; \
  BYTE                               PhysicalAddress[MAX_ADAPTER_ADDRESS_LENGTH]; \
  DWORD                              PhysicalAddressLength; \
  DWORD                              Flags; \
  DWORD                              Mtu; \
  DWORD                              IfType; \
  IF_OPER_STATUS                     OperStatus;

#define _IP_ADAPTER_ADDRESSES_ADD_XPSP1 \
  DWORD                              Ipv6IfIndex; \
  DWORD                              ZoneIndices[16]; \
  PIP_ADAPTER_PREFIX                 FirstPrefix; \


#define _IP_ADAPTER_ADDRESSES_ADD_VISTA \
  _IP_ADAPTER_ADDRESSES_ADD_XPSP1 \
  ULONG64                            TransmitLinkSpeed; \
  ULONG64                            ReceiveLinkSpeed; \
  PIP_ADAPTER_WINS_SERVER_ADDRESS_LH FirstWinsServerAddress; \
  PIP_ADAPTER_GATEWAY_ADDRESS_LH     FirstGatewayAddress; \
  ULONG                              Ipv4Metric; \
  ULONG                              Ipv6Metric; \
  IF_LUID                            Luid; \
  SOCKET_ADDRESS                     Dhcpv4Server; \
  NET_IF_COMPARTMENT_ID              CompartmentId; \
  NET_IF_NETWORK_GUID                NetworkGuid; \
  NET_IF_CONNECTION_TYPE             ConnectionType; \
  TUNNEL_TYPE                        TunnelType; \
  SOCKET_ADDRESS                     Dhcpv6Server; \
  BYTE                               Dhcpv6ClientDuid[MAX_DHCPV6_DUID_LENGTH]; \
  ULONG                              Dhcpv6ClientDuidLength; \
  ULONG                              Dhcpv6Iaid;

#define _IP_ADAPTER_ADDRESSES_ADD_2008_OR_VISTASP1 \
  _IP_ADAPTER_ADDRESSES_ADD_VISTA \
  PIP_ADAPTER_DNS_SUFFIX             FirstDnsSuffix;

#define _IP_ADAPTER_ADDRESSES_DEFINE(suffix,addition) \
typedef struct _IP_ADAPTER_ADDRESSES##suffix { \
  _IP_ADAPTER_ADDRESSES_HEAD \
  struct _IP_ADAPTER_ADDRESSES##suffix *Next; \
  _IP_ADAPTER_ADDRESSES_BASE \
  addition \
} IP_ADAPTER_ADDRESSES##suffix, *PIP_ADAPTER_ADDRESSES##suffix;


/* _IP_ADAPTER_ADDRESSES_DEFINE(,) defined in w32api headers */
_IP_ADAPTER_ADDRESSES_DEFINE(_XPSP1,_IP_ADAPTER_ADDRESSES_ADD_XPSP1)
_IP_ADAPTER_ADDRESSES_DEFINE(_VISTA,_IP_ADAPTER_ADDRESSES_ADD_VISTA)
_IP_ADAPTER_ADDRESSES_DEFINE(_2008_OR_VISTASP1,_IP_ADAPTER_ADDRESSES_ADD_2008_OR_VISTASP1)

static int
EnumNICs_IPv6_get_ifs_count (SOCKET s)
{
  DWORD dwret = 0, err;
  int iret;
  iret = WSAIoctl (s, SIO_ADDRESS_LIST_QUERY, NULL, 0, NULL, 0,
      &dwret, NULL, NULL);
  err = GetLastError ();
  if (iret == SOCKET_ERROR && err == WSAEFAULT)
    return dwret;
  else if (iret == 0)
    return 0;
  return GNUNET_SYSERR;
}

static int
EnumNICs_IPv6_get_ifs (SOCKET s, SOCKET_ADDRESS_LIST *inf, int size)
{
  int iret;
  DWORD dwret = 0;
  iret = WSAIoctl (s, SIO_ADDRESS_LIST_QUERY, NULL, 0, inf, size,
      &dwret, NULL, NULL);

  if (iret != 0 || dwret != size)
  {
    /* It's supposed to succeed! And size should be the same */
    return GNUNET_SYSERR;
  }
  return GNUNET_OK;
}

#undef GNUNET_malloc
#define GNUNET_malloc(a) HeapAlloc(GetProcessHeap (), HEAP_ZERO_MEMORY | \
    HEAP_GENERATE_EXCEPTIONS, a)

#undef GNUNET_free
#define GNUNET_free(a) HeapFree(GetProcessHeap (), 0, a)

#undef GNUNET_free_non_null
#define GNUNET_free_non_null(a) do { if ((a) != NULL) GNUNET_free(a); } while (0)

static int
EnumNICs_IPv4_get_ifs (SOCKET s, INTERFACE_INFO **inf, int *size)
{
  int iret;
  DWORD dwret = 0;
  DWORD error;
  INTERFACE_INFO *ii = NULL;
  DWORD ii_size = sizeof (INTERFACE_INFO) * 15;
  while (TRUE)
  {
    if (ii_size >= sizeof (INTERFACE_INFO) * 1000)
      return GNUNET_SYSERR;
    ii = (INTERFACE_INFO *) GNUNET_malloc (ii_size);
    dwret = 0;
    iret = WSAIoctl (s, SIO_GET_INTERFACE_LIST, NULL, 0, ii, ii_size,
        &dwret, NULL, NULL);
    error = GetLastError ();
    if (iret == SOCKET_ERROR)
    {
      if (error == WSAEFAULT)
      {
        GNUNET_free (ii);
        ii_size *= 2;
        continue;
      }
      GNUNET_free (ii);
      return GNUNET_SYSERR;
    }
    else
    {
      *inf = ii;
      *size = dwret;
      return GNUNET_OK;
    }
  }
  return GNUNET_SYSERR;
}

int
EnumNICs2 (INTERFACE_INFO **ifs4, int *ifs4_len, SOCKET_ADDRESS_LIST **ifs6)
{
  int result = 0;
  SOCKET s4;
  SOCKET s6;
  int ifs4len = 0;
  int ifs6len = 0;
  INTERFACE_INFO *interfaces4 = NULL;
  SOCKET_ADDRESS_LIST *interfaces6 = NULL;

  SetLastError (0);
  s4 = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
  (void) GetLastError ();
  SetLastError (0);
  s6 = socket (AF_INET6, SOCK_STREAM, IPPROTO_TCP);
  (void) GetLastError ();
  if (s6 != INVALID_SOCKET)
  {
    ifs6len = EnumNICs_IPv6_get_ifs_count (s6);
    if (ifs6len > 0)
    {
      interfaces6 = (SOCKET_ADDRESS_LIST *) GNUNET_malloc (ifs6len);
      result = EnumNICs_IPv6_get_ifs (s6, interfaces6, ifs6len) || result;
    }
    closesocket (s6);
    s6 = INVALID_SOCKET;
  }

  if (s4 != INVALID_SOCKET)
  {
    result = EnumNICs_IPv4_get_ifs (s4, &interfaces4, &ifs4len) || result;
    closesocket (s4);
    s4 = INVALID_SOCKET;
  }
  if (ifs6len + ifs4len == 0)
    goto error;

  if (!result)
  {
    *ifs4 = interfaces4;
    *ifs4_len = ifs4len;
    *ifs6 = interfaces6;
    return GNUNET_OK;
  }
error:
  if (interfaces4 != NULL)
    GNUNET_free (interfaces4);
  if (interfaces6 != NULL)
    GNUNET_free (interfaces6);
  if (s4 != INVALID_SOCKET)
    closesocket (s4);
  if (s6 != INVALID_SOCKET)
    closesocket (s6);
  return GNUNET_SYSERR;
}


/**
 * @returns #GNUNET_OK on success, #GNUNET_SYSERR on error
 */
int
EnumNICs3 (struct EnumNICs3_results **results, int *results_count)
{
  DWORD dwRetVal = 0;
  int count = 0;
  ULONG flags = /*GAA_FLAG_INCLUDE_PREFIX |*/ GAA_FLAG_SKIP_ANYCAST |
      GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER;
  struct sockaddr_in6 examplecom6;
  IPAddr examplecom;
  DWORD best_interface = 0;
  DWORD best_interface6 = 0;

  int use_enum2 = 0;
  INTERFACE_INFO *interfaces4 = NULL;
  int interfaces4_len = 0;
  SOCKET_ADDRESS_LIST *interfaces6 = NULL;

  unsigned long outBufLen = sizeof (IP_ADAPTER_ADDRESSES);
  IP_ADAPTER_ADDRESSES *pCurrentAddress = NULL;
  IP_ADAPTER_ADDRESSES *pAddresses = (IP_ADAPTER_ADDRESSES *) GNUNET_malloc (outBufLen);

  if (GetAdaptersAddresses (AF_UNSPEC, flags, NULL, pAddresses, &outBufLen)
      == ERROR_BUFFER_OVERFLOW)
  {
    GNUNET_free (pAddresses);
    pAddresses = (IP_ADAPTER_ADDRESSES *) GNUNET_malloc (outBufLen);
  }

  dwRetVal = GetAdaptersAddresses (AF_UNSPEC, flags, NULL, pAddresses, &outBufLen);

  if (dwRetVal != NO_ERROR)
  {
    GNUNET_free (pAddresses);
    return GNUNET_SYSERR;
  }

  if (pAddresses->Length < sizeof (IP_ADAPTER_ADDRESSES_VISTA))
  {
    use_enum2 = 1;

    /* Enumerate NICs using WSAIoctl() */
    if (GNUNET_OK != EnumNICs2 (&interfaces4, &interfaces4_len, &interfaces6))
    {
      GNUNET_free (pAddresses);
      return GNUNET_SYSERR;
    }
  }

  examplecom = inet_addr("192.0.34.166"); /* www.example.com */
  if (GetBestInterface (examplecom, &best_interface) != NO_ERROR)
    best_interface = 0;

  if (GNGetBestInterfaceEx != NULL)
  {
    examplecom6.sin6_family = AF_INET6;
    examplecom6.sin6_port = 0;
    examplecom6.sin6_flowinfo = 0;
    examplecom6.sin6_scope_id = 0;
    inet_pton (AF_INET6, "2001:500:88:200:0:0:0:10",
        (struct sockaddr *) &examplecom6.sin6_addr);
    dwRetVal = GNGetBestInterfaceEx ((struct sockaddr *) &examplecom6,
        &best_interface6);
    if (dwRetVal != NO_ERROR)
      best_interface6 = 0;
  }

  /* Give IPv6 a priority */
  if (best_interface6 != 0)
    best_interface = best_interface6;

  count = 0;
  for (pCurrentAddress = pAddresses;
      pCurrentAddress != NULL; pCurrentAddress = pCurrentAddress->Next)
  {
    if (pCurrentAddress->OperStatus == IfOperStatusUp)
    {
      IP_ADAPTER_UNICAST_ADDRESS *unicast = NULL;
      for (unicast = pCurrentAddress->FirstUnicastAddress; unicast != NULL;
          unicast = unicast->Next)
      {
        if ((unicast->Address.lpSockaddr->sa_family == AF_INET ||
            unicast->Address.lpSockaddr->sa_family == AF_INET6) &&
            (unicast->DadState == IpDadStateDeprecated ||
            unicast->DadState == IpDadStatePreferred))
          count += 1;
      }
    }
  }

  if (count == 0)
  {
    *results = NULL;
    *results_count = 0;
    GNUNET_free (pAddresses);
    GNUNET_free_non_null (interfaces4);
    GNUNET_free_non_null (interfaces6);
    return GNUNET_OK;
  }

  *results = (struct EnumNICs3_results *) GNUNET_malloc (
      sizeof (struct EnumNICs3_results) * count);
  *results_count = count;

  count = 0;
  for (pCurrentAddress = pAddresses;
      pCurrentAddress != NULL; pCurrentAddress = pCurrentAddress->Next)
  {
    struct EnumNICs3_results *r;
    IP_ADAPTER_UNICAST_ADDRESS *unicast = NULL;
    if (pCurrentAddress->OperStatus != IfOperStatusUp)
      continue;
    for (unicast = pCurrentAddress->FirstUnicastAddress; unicast != NULL;
        unicast = unicast->Next)
    {
      int i, j;
      int mask_length = -1;
      char dst[INET6_ADDRSTRLEN + 1];

      if ((unicast->Address.lpSockaddr->sa_family != AF_INET &&
          unicast->Address.lpSockaddr->sa_family != AF_INET6) ||
          (unicast->DadState != IpDadStateDeprecated &&
          unicast->DadState != IpDadStatePreferred))
        continue;

      r = &(*results)[count];
      r->flags = 0;
      if (pCurrentAddress->IfIndex > 0 &&
          pCurrentAddress->IfIndex == best_interface &&
          unicast->Address.lpSockaddr->sa_family == AF_INET)
        r->is_default = 1;
      else if (pCurrentAddress->Ipv6IfIndex > 0 &&
          pCurrentAddress->Ipv6IfIndex == best_interface6 &&
          unicast->Address.lpSockaddr->sa_family == AF_INET6)
        r->is_default = 1;
      else
        r->is_default = 0;

      /* Don't choose default interface twice */
      if (r->is_default)
        best_interface = best_interface6 = 0;

      if (!use_enum2)
      {
        GNUNET_memcpy (&r->address, unicast->Address.lpSockaddr,
            unicast->Address.iSockaddrLength);
        memset (&r->mask, 0, sizeof (struct sockaddr));
        mask_length = ((IP_ADAPTER_UNICAST_ADDRESS_VISTA *) unicast)->
              OnLinkPrefixLength;
        /* OnLinkPrefixLength is the number of leading 1s in the mask.
         * OnLinkPrefixLength is available on Vista and later (hence use_enum2).
         */
        if (unicast->Address.lpSockaddr->sa_family == AF_INET)
        {
          struct sockaddr_in *m = (struct sockaddr_in *) &r->mask;
          for (i = 0; i < mask_length; i++)
              ((unsigned char *) &m->sin_addr)[i / 8] |= 0x80 >> (i % 8);
        }
        else if (unicast->Address.lpSockaddr->sa_family == AF_INET6)
        {
          struct sockaddr_in6 *m = (struct sockaddr_in6 *) &r->mask;
          struct sockaddr_in6 *b = (struct sockaddr_in6 *) &r->broadcast;
          for (i = 0; i < mask_length; i++)
            ((unsigned char *) &m->sin6_addr)[i / 8] |= 0x80 >> (i % 8);
          GNUNET_memcpy (&r->broadcast, &r->address, unicast->Address.iSockaddrLength);
          for (i = mask_length; i < 128; i++)
            ((unsigned char *) &b->sin6_addr)[i / 8] |= 0x80 >> (i % 8);
        }
        r->flags |= ENUMNICS3_MASK_OK;
      }
      else
      {
        int found = 0;
        if (unicast->Address.lpSockaddr->sa_family == AF_INET)
        {
          for (i = 0; !found && i < interfaces4_len / sizeof (INTERFACE_INFO); i++)
          {
            struct sockaddr_in *m = (struct sockaddr_in *) &r->mask;
            GNUNET_memcpy (&interfaces4[i].iiAddress.Address,
                unicast->Address.lpSockaddr,
                unicast->Address.iSockaddrLength);
            found = 1;
            GNUNET_memcpy (&r->address, &interfaces4[i].iiAddress.Address,
                sizeof (struct sockaddr_in));
            GNUNET_memcpy (&r->mask, &interfaces4[i].iiNetmask.Address,
                sizeof (struct sockaddr_in));
            for (mask_length = 0;
                ((unsigned char *) &m->sin_addr)[mask_length / 8] &
                0x80 >> (mask_length % 8); mask_length++)
            {
            }
            r->flags |= ENUMNICS3_MASK_OK;
          }
        }
        else if (unicast->Address.lpSockaddr->sa_family == AF_INET6)
        {
          for (i = 0;
              interfaces6 != NULL && !found && i < interfaces6->iAddressCount;
              i++)
          {
            GNUNET_memcpy (interfaces6->Address[i].lpSockaddr,
                unicast->Address.lpSockaddr,
                unicast->Address.iSockaddrLength);
            found = 1;
            GNUNET_memcpy (&r->address, interfaces6->Address[i].lpSockaddr,
                sizeof (struct sockaddr_in6));
            /* TODO: Find a way to reliably get network mask for IPv6 on XP */
            memset (&r->mask, 0, sizeof (struct sockaddr));
            r->flags &= ~ENUMNICS3_MASK_OK;
          }
        }
        if (!found)
        {
          DebugBreak ();
        }
      }
      if (unicast->Address.lpSockaddr->sa_family == AF_INET)
      {
        struct sockaddr_in *m = (struct sockaddr_in *) &r->mask;
        struct sockaddr_in *a = (struct sockaddr_in *) &r->address;
        /* copy address to broadcast, then flip all the trailing bits not
         * falling under netmask to 1,
         * so we get, 192.168.0.255 from, say, 192.168.0.43 with mask == 24.
         */
        GNUNET_memcpy (&r->broadcast, &r->address, unicast->Address.iSockaddrLength);
        for (i = mask_length; i < 32; i++)
          ((unsigned char *) &m->sin_addr)[i / 8] |= 0x80 >> (i % 8);
        r->flags |= ENUMNICS3_BCAST_OK;
        r->addr_size = sizeof (struct sockaddr_in);
        inet_ntop (AF_INET, &a->sin_addr, dst, INET_ADDRSTRLEN);
      }
      else if (unicast->Address.lpSockaddr->sa_family == AF_INET6)
      {
        struct sockaddr_in6 *a = (struct sockaddr_in6 *) &r->address;
        /* for IPv6 broadcast is not defined, zero it down */
        memset (&r->broadcast, 0, sizeof (struct sockaddr));
        r->flags &= ~ENUMNICS3_BCAST_OK;
        r->addr_size = sizeof (struct sockaddr_in6);
        inet_ntop (AF_INET6, &a->sin6_addr, dst, INET6_ADDRSTRLEN);
      }

      i = 0;
      i += snprintf (&r->pretty_name[i], 1000 - i > 0 ? 1000 - i : 0,
          "%S (%s", pCurrentAddress->FriendlyName, dst);
      for (j = 0; j < pCurrentAddress->PhysicalAddressLength; j++)
        i += snprintf (&r->pretty_name[i], 1000 - i > 0 ? 1000 - i : 0,
            "%s%02X",j > 0 ? ":" : " - ", pCurrentAddress->PhysicalAddress[j]);
      i += snprintf (&r->pretty_name[i], 1000 - i > 0 ? 1000 - i : 0, ")");
      r->pretty_name[1000] = '\0';
      count += 1;
    }
  }

  if (use_enum2)
  {
    GNUNET_free_non_null (interfaces4);
    GNUNET_free_non_null (interfaces6);
  }

  GNUNET_free (pAddresses);
  return GNUNET_OK;
}

void
EnumNICs3_free (struct EnumNICs3_results *r)
{
  GNUNET_free_non_null (r);
}


/**
 * Lists all network interfaces in a combo box
 * Used by the basic GTK configurator
 *
 * @param callback function to call for each NIC
 * @param callback_cls closure for callback
 */
int
ListNICs (void (*callback) (void *, const char *, int), void * callback_cls)
{
  int r;
  int i;
  struct EnumNICs3_results *results = NULL;
  int results_count;

  r = EnumNICs3 (&results, &results_count);
  if (r != GNUNET_OK)
    return GNUNET_NO;

  for (i = 0; i < results_count; i++)
    callback (callback_cls, results[i].pretty_name, results[i].is_default);
  GNUNET_free_non_null (results);
  return GNUNET_YES;
}

/**
 * @brief Installs the Windows service
 * @param servicename name of the service as diplayed by the SCM
 * @param application path to the application binary
 * @param username the name of the service's user account
 * @returns 0 on success
 *          1 if the Windows version doesn't support services
 *          2 if the SCM could not be opened
 *          3 if the service could not be created
 */
int InstallAsService(char *servicename, char *application, char *username)
{
  SC_HANDLE hManager, hService;
  char szEXE[_MAX_PATH + 17] = "\"";
  char *user = NULL;

  if (! GNOpenSCManager)
    return 1;

  plibc_conv_to_win_path(application, szEXE + 1);
  strcat(szEXE, "\" --win-service");
  hManager = GNOpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
  if (! hManager)
    return 2;

  if (username)
  {
  	user = (char *) malloc(strlen(username) + 3);
  	sprintf(user, ".\\%s", username);
  }

  hService = GNCreateService(hManager, (LPCTSTR) servicename, (LPCTSTR) servicename, 0,
    SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, (LPCTSTR) szEXE,
    NULL, NULL, NULL, (LPCTSTR) user, (LPCTSTR) username);

  if (user)
    free(user);

  if (! hService)
    return 3;

  GNCloseServiceHandle(hService);

  return 0;
}


/**
 * @brief Uninstall Windows service
 * @param servicename name of the service to delete
 * @returns 0 on success
 *          1 if the Windows version doesn't support services
 *          2 if the SCM could not be openend
 *          3 if the service cannot be accessed
 *          4 if the service cannot be deleted
 */
int
UninstallService(char *servicename)
{
  SC_HANDLE hManager, hService;

  if (! GNOpenSCManager)
    return 1;

  hManager = GNOpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
  if (! hManager)
    return 2;

  if (! (hService = GNOpenService(hManager, (LPCTSTR) servicename, DELETE)))
  {
    if (GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST)
      return 3;
    else
      goto closeSCM;
  }

  if (! GNDeleteService(hService))
    if (GetLastError() != ERROR_SERVICE_MARKED_FOR_DELETE)
      return 4;

closeSCM:
  GNCloseServiceHandle(hService);

  return 0;
}

/**
 * @author Scott Field, Microsoft
 * @see http://support.microsoft.com/?scid=kb;en-us;132958
 * @date 12-Jul-95
 */
void
_InitLsaString(PLSA_UNICODE_STRING LsaString, LPWSTR String)
{
  DWORD StringLength;

  if(String == NULL)
  {
    LsaString->Buffer = NULL;
    LsaString->Length = 0;
    LsaString->MaximumLength = 0;
    return;
  }

  StringLength = wcslen(String);
  LsaString->Buffer = String;
  LsaString->Length = (USHORT) StringLength *sizeof(WCHAR);
  LsaString->MaximumLength = (USHORT) (StringLength + 1) * sizeof(WCHAR);
}


/**
 * @author Scott Field, Microsoft
 * @see http://support.microsoft.com/?scid=kb;en-us;132958
 * @date 12-Jul-95
 */
NTSTATUS
_OpenPolicy(LPWSTR ServerName, DWORD DesiredAccess, PLSA_HANDLE PolicyHandle)
{
  LSA_OBJECT_ATTRIBUTES ObjectAttributes;
  LSA_UNICODE_STRING ServerString;
  PLSA_UNICODE_STRING Server = NULL;

  /* Always initialize the object attributes to all zeroes. */
  ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));

  if(ServerName != NULL)
  {
    /* Make a LSA_UNICODE_STRING out of the LPWSTR passed in */
    _InitLsaString(&ServerString, ServerName);
    Server = &ServerString;
  }

  /* Attempt to open the policy. */
  return GNLsaOpenPolicy(Server,
                       &ObjectAttributes, DesiredAccess, PolicyHandle);
}

/**
 * @brief Obtain a SID representing the supplied account on the supplied system
 * @return TRUE on success, FALSE on failure
 * @author Scott Field, Microsoft
 * @date 12-Jul-95
 * @remarks A buffer is allocated which contains the SID representing the
 *          supplied account. This buffer should be freed when it is no longer
 *          needed by calling\n
 *            HeapFree(GetProcessHeap(), 0, buffer)
 * @remarks Call GetLastError() to obtain extended error information.
 * @see http://support.microsoft.com/?scid=kb;en-us;132958
 */
BOOL
_GetAccountSid(LPCTSTR SystemName, LPCTSTR AccountName, PSID * Sid)
{
  LPTSTR ReferencedDomain = NULL;
  DWORD cbSid = 128;  							/* initial allocation attempt */
  DWORD cchReferencedDomain = 16;  	/* initial allocation size */
  SID_NAME_USE peUse;
  BOOL bSuccess = FALSE;  					/* assume this function will fail */

  /* initial memory allocations */
  if ((*Sid = HeapAlloc (GetProcessHeap (), 0, cbSid)) == NULL)
  	return FALSE;

  if ((ReferencedDomain = (LPTSTR) HeapAlloc (GetProcessHeap (),
  				    0,
  				    cchReferencedDomain *
  				    sizeof (TCHAR))) == NULL)
  	return FALSE;

    /* Obtain the SID of the specified account on the specified system. */
  	while (!GNLookupAccountName(SystemName,	/* machine to lookup account on */
  		   AccountName,												/* account to lookup */
  		   *Sid,															/* SID of interest */
  		   &cbSid,														/* size of SID */
  		   ReferencedDomain,									/* domain account was found on */
  		   &cchReferencedDomain, &peUse))
  	{
  		if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  		{
  			/* reallocate memory */
  			if ((*Sid = HeapReAlloc (GetProcessHeap (), 0, *Sid, cbSid)) == NULL)
  				return FALSE;

  			if ((ReferencedDomain = (LPTSTR) HeapReAlloc (GetProcessHeap (),
  					      0,
  					      ReferencedDomain,
  					      cchReferencedDomain
  					      * sizeof (TCHAR))) == NULL)
  				return FALSE;
      }
      else
        goto end;
  }

  /* Indicate success. */
  bSuccess = TRUE;

end:
  /* Cleanup and indicate failure, if appropriate. */
  HeapFree (GetProcessHeap (), 0, ReferencedDomain);

  if (!bSuccess)
  {
    if (*Sid != NULL)
    {
  		HeapFree (GetProcessHeap (), 0, *Sid);
  		*Sid = NULL;
    }
  }

  return bSuccess;
}

/**
 * @author Scott Field, Microsoft
 * @see http://support.microsoft.com/?scid=kb;en-us;132958
 * @date 12-Jul-95
 */
NTSTATUS
_SetPrivilegeOnAccount(LSA_HANDLE PolicyHandle,/* open policy handle */
                       PSID AccountSid,   			/* SID to grant privilege to */
                       LPWSTR PrivilegeName,  	/* privilege to grant (Unicode) */
                       BOOL bEnable  						/* enable or disable */
  )
{
  LSA_UNICODE_STRING PrivilegeString;

  /* Create a LSA_UNICODE_STRING for the privilege name. */
  _InitLsaString(&PrivilegeString, PrivilegeName);

  /* grant or revoke the privilege, accordingly */
  if(bEnable)
  {
    NTSTATUS i;

    i = GNLsaAddAccountRights(PolicyHandle,  				/* open policy handle */
                               AccountSid,        			/* target SID */
                               &PrivilegeString,        /* privileges */
                               1  											/* privilege count */
      );
    return i;
  }
  else
  {
    return GNLsaRemoveAccountRights(PolicyHandle,  			/* open policy handle */
                                  AccountSid,     			/* target SID */
                                  FALSE,  							/* do not disable all rights */
                                  &PrivilegeString,  		/* privileges */
                                  1  										/* privilege count */
      );
  }
}

/**
 * @brief Create a Windows service account
 * @return 0 on success, > 0 otherwise
 * @param pszName the name of the account
 * @param pszDesc description of the account
 */
int
CreateServiceAccount(const char *pszName,
                     const char *pszDesc)
{
  USER_INFO_1 ui;
  USER_INFO_1008 ui2;
  NET_API_STATUS nStatus;
  wchar_t wszName[MAX_NAME_LENGTH], wszDesc[MAX_NAME_LENGTH];
  LSA_HANDLE hPolicy;
  PSID pSID;

  if (! GNNetUserAdd)
  	return 1;
  mbstowcs(wszName, pszName, strlen(pszName) + 1);
  mbstowcs(wszDesc, pszDesc, strlen(pszDesc) + 1);

  memset(&ui, 0, sizeof(ui));
  ui.usri1_name = wszName;
  ui.usri1_password = wszName; /* account is locked anyway */
  ui.usri1_priv = USER_PRIV_USER;
  ui.usri1_comment = wszDesc;
  ui.usri1_flags = UF_SCRIPT;

  nStatus = GNNetUserAdd(NULL, 1, (LPBYTE)&ui, NULL);

  if (nStatus != NERR_Success && nStatus != NERR_UserExists)
  	return 2;

  ui2.usri1008_flags = UF_PASSWD_CANT_CHANGE | UF_DONT_EXPIRE_PASSWD;
  GNNetUserSetInfo(NULL, wszName, 1008, (LPBYTE)&ui2, NULL);

  if (!NT_SUCCESS(_OpenPolicy(NULL, POLICY_ALL_ACCESS, &hPolicy)))
  	return 3;

  _GetAccountSid(NULL, (LPCTSTR) pszName, &pSID);

  if (!NT_SUCCESS(_SetPrivilegeOnAccount(hPolicy, pSID, (LPWSTR) L"SeServiceLogonRight", TRUE)))
  	return 4;

  _SetPrivilegeOnAccount(hPolicy, pSID, (LPWSTR) L"SeDenyInteractiveLogonRight", TRUE);
  _SetPrivilegeOnAccount(hPolicy, pSID, (LPWSTR) L"SeDenyBatchLogonRight", TRUE);
  _SetPrivilegeOnAccount(hPolicy, pSID, (LPWSTR) L"SeDenyNetworkLogonRight", TRUE);

  GNLsaClose(hPolicy);

  return 0;
}

/**
 * @brief Grant permission to a file
 * @param lpszFileName the name of the file or directory
 * @param lpszAccountName the user account
 * @param dwAccessMask the desired access (e.g. GENERIC_ALL)
 * @return TRUE on success
 * @remark based on http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q102102&
 */
BOOL AddPathAccessRights(char *lpszFileName, char *lpszAccountName,
      DWORD dwAccessMask)
{
  /* SID variables. */
  SID_NAME_USE   snuType;
  TCHAR *        szDomain       = NULL;
  DWORD          cbDomain       = 0;
  LPVOID         pUserSID       = NULL;
  DWORD          cbUserSID      = 0;

  /* File SD variables. */
  PSECURITY_DESCRIPTOR pFileSD  = NULL;
  DWORD          cbFileSD       = 0;

  /* New SD variables. */
  SECURITY_DESCRIPTOR  newSD;

  /* ACL variables. */
  PACL           pACL           = NULL;
  BOOL           fDaclPresent;
  BOOL           fDaclDefaulted;
  ACL_SIZE_INFORMATION AclInfo;

  /* New ACL variables. */
  PACL           pNewACL        = NULL;
  DWORD          cbNewACL       = 0;

  /* Temporary ACE. */
  LPVOID         pTempAce       = NULL;
  UINT           CurrentAceIndex = 0;

  UINT           newAceIndex = 0;

  /* Assume function will fail. */
  BOOL           fResult        = FALSE;
  BOOL           fAPISuccess;

  SECURITY_INFORMATION secInfo = DACL_SECURITY_INFORMATION;

  /**
   * STEP 1: Get SID of the account name specified.
   */
  fAPISuccess = GNLookupAccountName(NULL, (LPCTSTR) lpszAccountName,
        pUserSID, &cbUserSID, (LPTSTR) szDomain, &cbDomain, &snuType);

  /* API should have failed with insufficient buffer. */
  if (fAPISuccess)
     goto end;
  else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
     goto end;
  }

  pUserSID = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbUserSID);
  if (!pUserSID) {
     goto end;
  }

  szDomain = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbDomain * sizeof(TCHAR));
  if (!szDomain) {
     goto end;
  }

  fAPISuccess = GNLookupAccountName(NULL, (LPCTSTR) lpszAccountName,
        pUserSID, &cbUserSID, (LPTSTR) szDomain, &cbDomain, &snuType);
  if (!fAPISuccess) {
     goto end;
  }

  /**
   *  STEP 2: Get security descriptor (SD) of the file specified.
   */
  fAPISuccess = GNGetFileSecurity((LPCTSTR) lpszFileName,
        secInfo, pFileSD, 0, &cbFileSD);

  /* API should have failed with insufficient buffer. */
  if (fAPISuccess)
     goto end;
  else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
     goto end;
  }

  pFileSD = (PSECURITY_DESCRIPTOR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
  	cbFileSD);
  if (!pFileSD) {
     goto end;
  }

  fAPISuccess = GNGetFileSecurity((LPCTSTR) lpszFileName,
        secInfo, pFileSD, cbFileSD, &cbFileSD);
  if (!fAPISuccess) {
     goto end;
  }

  /**
   * STEP 3: Initialize new SD.
   */
  if (!GNInitializeSecurityDescriptor(&newSD,
        SECURITY_DESCRIPTOR_REVISION)) {
     goto end;
  }

  /**
   * STEP 4: Get DACL from the old SD.
   */
  if (!GNGetSecurityDescriptorDacl(pFileSD, &fDaclPresent, &pACL,
        &fDaclDefaulted)) {
     goto end;
  }

  /**
   * STEP 5: Get size information for DACL.
   */
  AclInfo.AceCount = 0; // Assume NULL DACL.
  AclInfo.AclBytesFree = 0;
  AclInfo.AclBytesInUse = sizeof(ACL);

  if (pACL == NULL)
     fDaclPresent = FALSE;

  /* If not NULL DACL, gather size information from DACL. */
  if (fDaclPresent) {

     if (!GNGetAclInformation(pACL, &AclInfo,
           sizeof(ACL_SIZE_INFORMATION), AclSizeInformation)) {
        goto end;
     }
  }

  /**
   * STEP 6: Compute size needed for the new ACL.
   */
  cbNewACL = AclInfo.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE)
        + GetLengthSid(pUserSID) - sizeof(DWORD);

  /**
   * STEP 7: Allocate memory for new ACL.
   */
  pNewACL = (PACL) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbNewACL);
  if (!pNewACL) {
     goto end;
  }

  /**
   * STEP 8: Initialize the new ACL.
   */
  if (!GNInitializeAcl(pNewACL, cbNewACL, ACL_REVISION2)) {
     goto end;
  }

  /**
   * STEP 9 If DACL is present, copy all the ACEs from the old DACL
   * to the new DACL.
   *
   * The following code assumes that the old DACL is
   * already in Windows 2000 preferred order.  To conform
   * to the new Windows 2000 preferred order, first we will
   * copy all non-inherited ACEs from the old DACL to the
   * new DACL, irrespective of the ACE type.
   */

  newAceIndex = 0;

  if (fDaclPresent && AclInfo.AceCount) {

     for (CurrentAceIndex = 0;
           CurrentAceIndex < AclInfo.AceCount;
           CurrentAceIndex++) {

        /**
         * TEP 10: Get an ACE.
         */
        if (!GNGetAce(pACL, CurrentAceIndex, &pTempAce)) {
           goto end;
        }

        /**
         * STEP 11: Check if it is a non-inherited ACE.
         * If it is an inherited ACE, break from the loop so
         * that the new access allowed non-inherited ACE can
         * be added in the correct position, immediately after
         * all non-inherited ACEs.
         */
        if (((ACCESS_ALLOWED_ACE *)pTempAce)->Header.AceFlags
           & INHERITED_ACE)
           break;

        /**
         * STEP 12: Skip adding the ACE, if the SID matches
         * with the account specified, as we are going to
         * add an access allowed ACE with a different access
         * mask.
         */
        if (GNEqualSid(pUserSID,
           &(((ACCESS_ALLOWED_ACE *)pTempAce)->SidStart)))
           continue;

        /**
         * STEP 13: Add the ACE to the new ACL.
         */
        if (!GNAddAce(pNewACL, ACL_REVISION, MAXDWORD, pTempAce,
              ((PACE_HEADER) pTempAce)->AceSize)) {
           goto end;
        }

        newAceIndex++;
     }
  }

  /**
   * STEP 14: Add the access-allowed ACE to the new DACL.
   * The new ACE added here will be in the correct position,
   * immediately after all existing non-inherited ACEs.
   */
  if (!GNAddAccessAllowedAce(pNewACL, ACL_REVISION2, dwAccessMask,
        pUserSID)) {
     goto end;
  }

  /**
   * STEP 14.5: Make new ACE inheritable
   */
  if (!GetAce(pNewACL, newAceIndex, &pTempAce))
    goto end;
  ((ACCESS_ALLOWED_ACE *)pTempAce)->Header.AceFlags |=
    (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE);

  /**
   * STEP 15: To conform to the new Windows 2000 preferred order,
   * we will now copy the rest of inherited ACEs from the
   * old DACL to the new DACL.
   */
  if (fDaclPresent && AclInfo.AceCount) {

     for (;
          CurrentAceIndex < AclInfo.AceCount;
          CurrentAceIndex++) {

        /**
         * STEP 16: Get an ACE.
         */
        if (!GNGetAce(pACL, CurrentAceIndex, &pTempAce)) {
           goto end;
        }

        /**
         * STEP 17: Add the ACE to the new ACL.
         */
        if (!GNAddAce(pNewACL, ACL_REVISION, MAXDWORD, pTempAce,
              ((PACE_HEADER) pTempAce)->AceSize)) {
           goto end;
        }
     }
  }

  /**
   * STEP 18: Set permissions
   */
  if (GNSetNamedSecurityInfo((LPTSTR) lpszFileName, SE_FILE_OBJECT,
    DACL_SECURITY_INFORMATION, NULL, NULL, pNewACL, NULL) != ERROR_SUCCESS) {
    	goto end;
  }

  fResult = TRUE;

end:

  /**
   * STEP 19: Free allocated memory
   */
  if (pUserSID)
     HeapFree(GetProcessHeap(), 0, pUserSID);

  if (szDomain)
     HeapFree(GetProcessHeap(), 0, szDomain);

  if (pFileSD)
     HeapFree(GetProcessHeap(), 0, pFileSD);

  if (pNewACL)
     HeapFree(GetProcessHeap(), 0, pNewACL);

  return fResult;
}

char *winErrorStr(const char *prefix, int dwErr)
{
  char *err, *ret;
  int mem;

  if (! FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
    NULL, (DWORD) dwErr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &err,
    0, NULL ))
  {
    err = (char *) LocalAlloc (LMEM_FIXED | LMEM_ZEROINIT, 1);
  }

  mem = strlen(err) + strlen(prefix) + 20;
  ret = (char *) malloc(mem);

  snprintf(ret, mem, "%s: %s (#%u)", prefix, err, dwErr);

  LocalFree(err);

  return ret;
}

/**
 * Terminate a process by creating a remote thread within it,
 * which proceeds to call ExitProcess() inside that process.
 * Safer than TerminateProcess ().
 *
 * Code is from From http://private-storm.de/2009/08/11/case-terminateprocess/
 *
 * @param hProcess handle of a process to terminate
 * @param uExitCode exit code to use for ExitProcess()
 * @param dwTimeout number of ms to wait for the process to terminate
 * @return TRUE on success, FALSE on failure (check last error for the code)
 */
BOOL
SafeTerminateProcess (HANDLE hProcess, UINT uExitCode, DWORD dwTimeout)
{
  DWORD dwTID, dwCode, dwErr = 0;
  HANDLE hProcessDup = INVALID_HANDLE_VALUE;
  HANDLE hRT = NULL;
  HINSTANCE hKernel = GetModuleHandle ("Kernel32");
  BOOL bSuccess = FALSE;

  BOOL bDup = DuplicateHandle (GetCurrentProcess (), hProcess,
      GetCurrentProcess (), &hProcessDup, PROCESS_ALL_ACCESS,
      FALSE, 0);

  /* Detect the special case where the process is
   * already dead...
   */
  if (GetExitCodeProcess (bDup ? hProcessDup : hProcess, &dwCode) &&
      (STILL_ACTIVE ==  dwCode))
  {
    FARPROC pfnExitProc;

    pfnExitProc = GetProcAddress (hKernel, "ExitProcess");

    hRT = CreateRemoteThread ((bDup) ? hProcessDup : hProcess, NULL, 0,
        (LPTHREAD_START_ROUTINE) pfnExitProc, (PVOID) uExitCode, 0, &dwTID);

    dwErr = GetLastError ();
  }
  else
  {
    dwErr = ERROR_PROCESS_ABORTED;
  }

  if (hRT)
  {
    /* Must wait process to terminate to
     * guarantee that it has exited...
     */
    DWORD dwWaitResult = WaitForSingleObject ((bDup) ? hProcessDup : hProcess,
        dwTimeout);
    if (dwWaitResult == WAIT_TIMEOUT)
      dwErr = WAIT_TIMEOUT;
    else
      dwErr = GetLastError ();

    CloseHandle (hRT);
    bSuccess = dwErr == NO_ERROR;
  }

  if (bDup)
    CloseHandle (hProcessDup);

  SetLastError (dwErr);

  return bSuccess;
}

#endif
