diff options
Diffstat (limited to 'src/util/win.c')
-rw-r--r-- | src/util/win.c | 1329 |
1 files changed, 1329 insertions, 0 deletions
diff --git a/src/util/win.c b/src/util/win.c new file mode 100644 index 0000000..99b8b69 --- /dev/null +++ b/src/util/win.c @@ -0,0 +1,1329 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @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_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) + + +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; + +typedef UINT32 NET_IF_COMPARTMENT_ID; +typedef GUID NET_IF_NETWORK_GUID; + +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; + +/* +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 + +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; + + + +#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 = INVALID_SOCKET, s6 = INVALID_SOCKET; + DWORD dwret1 = 0, dwret2; + DWORD err1, err2; + int ifs4len = 0, ifs6len = 0; + INTERFACE_INFO *interfaces4 = NULL; + SOCKET_ADDRESS_LIST *interfaces6 = NULL; + SetLastError (0); + s4 = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + err1 = GetLastError (); + SetLastError (0); + s6 = socket (AF_INET6, SOCK_STREAM, IPPROTO_TCP); + err2 = 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 OK, 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) + { + 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); + 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; + if (memcpy (&interfaces4[i].iiAddress.Address, + unicast->Address.lpSockaddr, + unicast->Address.iSockaddrLength) != 0) + continue; + found = 1; + memcpy (&r->address, &interfaces4[i].iiAddress.Address, + sizeof (struct sockaddr_in)); + 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++) + { + if (memcpy (interfaces6->Address[i].lpSockaddr, + unicast->Address.lpSockaddr, + unicast->Address.iSockaddrLength) != 0) + continue; + found = 1; + 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. + */ + 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 */ + ); + } + 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]; + DWORD dwErr; + 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 (_OpenPolicy(NULL, POLICY_ALL_ACCESS, &hPolicy) != + STATUS_SUCCESS) + return 3; + + _GetAccountSid(NULL, (LPCTSTR) pszName, &pSID); + + if (_SetPrivilegeOnAccount(hPolicy, pSID, (LPWSTR) L"SeServiceLogonRight", TRUE) != STATUS_SUCCESS) + 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 |