blob: bc42fef433223e4a849428df00adc414ecbf80de [file] [log] [blame]
/*
This file is part of libmicrohttpd
Copyright (C) 2014 Karlson2k (Evgeny Grin)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library.
If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file platform/w32functions.h
* @brief internal functions for W32 systems
* @author Karlson2k (Evgeny Grin)
*/
#include "w32functions.h"
#include <errno.h>
#include <winsock2.h>
#include <string.h>
#include <stdint.h>
#include <time.h>
#include <stdio.h>
#include <stdarg.h>
/**
* Return errno equivalent of last winsock error
* @return errno equivalent of last winsock error
*/
int MHD_W32_errno_from_winsock_(void)
{
switch(WSAGetLastError())
{
case 0: return 0;
case WSA_INVALID_HANDLE: return EBADF;
case WSA_NOT_ENOUGH_MEMORY: return ENOMEM;
case WSA_INVALID_PARAMETER: return EINVAL;
case WSAEINTR: return EINTR;
case WSAEWOULDBLOCK: return EWOULDBLOCK;
case WSAEINPROGRESS: return EINPROGRESS;
case WSAEALREADY: return EALREADY;
case WSAENOTSOCK: return ENOTSOCK;
case WSAEDESTADDRREQ: return EDESTADDRREQ;
case WSAEMSGSIZE: return EMSGSIZE;
case WSAEPROTOTYPE: return EPROTOTYPE;
case WSAENOPROTOOPT: return ENOPROTOOPT;
case WSAEPROTONOSUPPORT: return EPROTONOSUPPORT;
case WSAESOCKTNOSUPPORT: return ESOCKTNOSUPPORT;
case WSAEOPNOTSUPP: return EOPNOTSUPP;
case WSAEPFNOSUPPORT: return EPFNOSUPPORT;
case WSAEAFNOSUPPORT: return EAFNOSUPPORT;
case WSAEADDRINUSE: return EADDRINUSE;
case WSAEADDRNOTAVAIL: return EADDRNOTAVAIL;
case WSAENETDOWN: return ENETDOWN;
case WSAENETUNREACH: return ENETUNREACH;
case WSAENETRESET: return ENETRESET;
case WSAECONNABORTED: return ECONNABORTED;
case WSAECONNRESET: return ECONNRESET;
case WSAENOBUFS: return ENOBUFS;
case WSAEISCONN: return EISCONN;
case WSAENOTCONN: return ENOTCONN;
case WSAESHUTDOWN: return ESHUTDOWN;
case WSAETOOMANYREFS: return ETOOMANYREFS;
case WSAETIMEDOUT: return ETIMEDOUT;
case WSAECONNREFUSED: return ECONNREFUSED;
case WSAELOOP: return ELOOP;
case WSAENAMETOOLONG: return ENAMETOOLONG;
case WSAEHOSTDOWN: return EHOSTDOWN;
case WSAEHOSTUNREACH: return EHOSTUNREACH;
case WSAENOTEMPTY: return ENOTEMPTY;
case WSAEPROCLIM: return EPROCLIM;
case WSAEUSERS: return EUSERS;
case WSAEDQUOT: return EDQUOT;
case WSAESTALE: return ESTALE;
case WSAEREMOTE: return EREMOTE;
case WSAEINVAL: return EINVAL;
case WSAEFAULT: return EFAULT;
case WSANO_DATA: return ENODATA;
/* Rough equivalents */
case WSAEDISCON: return ECONNRESET;
case WSAEINVALIDPROCTABLE: return EFAULT;
case WSASYSNOTREADY:
case WSANOTINITIALISED:
case WSASYSCALLFAILURE: return ENOBUFS;
case WSAVERNOTSUPPORTED: return EOPNOTSUPP;
case WSAEREFUSED: return EIO;
}
return EINVAL;
}
/**
* Return pointer to string description of errnum error
* Works fine with both standard errno errnums
* and errnums from MHD_W32_errno_from_winsock_
* @param errnum the errno or value from MHD_W32_errno_from_winsock_()
* @return pointer to string description of error
*/
const char* MHD_W32_strerror_(int errnum)
{
switch(errnum)
{
case 0:
return "No error";
case EWOULDBLOCK:
return "Operation would block";
case EINPROGRESS:
return "Connection already in progress";
case EALREADY:
return "Socket already connected";
case ENOTSOCK:
return "Socket operation on non-socket";
case EDESTADDRREQ:
return "Destination address required";
case EMSGSIZE:
return "Message too long";
case EPROTOTYPE:
return "Protocol wrong type for socket";
case ENOPROTOOPT:
return "Protocol not available";
case EPROTONOSUPPORT:
return "Unknown protocol";
case ESOCKTNOSUPPORT:
return "Socket type not supported";
case EOPNOTSUPP:
return "Operation not supported on socket";
case EPFNOSUPPORT:
return "Protocol family not supported";
case EAFNOSUPPORT:
return "Address family not supported by protocol family";
case EADDRINUSE:
return "Address already in use";
case EADDRNOTAVAIL:
return "Cannot assign requested address";
case ENETDOWN:
return "Network is down";
case ENETUNREACH:
return "Network is unreachable";
case ENETRESET:
return "Network dropped connection on reset";
case ECONNABORTED:
return "Software caused connection abort";
case ECONNRESET:
return "Connection reset by peer";
case ENOBUFS:
return "No system resources available";
case EISCONN:
return "Socket is already connected";
case ENOTCONN:
return "Socket is not connected";
case ESHUTDOWN:
return "Can't send after socket shutdown";
case ETOOMANYREFS:
return "Too many references: cannot splice";
case ETIMEDOUT:
return "Connection timed out";
case ECONNREFUSED:
return "Connection refused";
case ELOOP:
return "Cannot translate name";
case EHOSTDOWN:
return "Host is down";
case EHOSTUNREACH:
return "Host is unreachable";
case EPROCLIM:
return "Too many processes";
case EUSERS:
return "Too many users";
case EDQUOT:
return "Disk quota exceeded";
case ESTALE:
return "Stale file handle reference";
case EREMOTE:
return "Resource is remote";
case ENODATA:
return "No data available";
}
return strerror(errnum);
}
/**
* Return pointer to string description of last winsock error
* @return pointer to string description of last winsock error
*/
const char* MHD_W32_strerror_last_winsock_(void)
{
switch (WSAGetLastError())
{
case 0:
return "No error";
case WSA_INVALID_HANDLE:
return "Specified event object handle is invalid";
case WSA_NOT_ENOUGH_MEMORY:
return "Insufficient memory available";
case WSA_INVALID_PARAMETER:
return "One or more parameters are invalid";
case WSA_OPERATION_ABORTED:
return "Overlapped operation aborted";
case WSA_IO_INCOMPLETE:
return "Overlapped I/O event object not in signaled state";
case WSA_IO_PENDING:
return "Overlapped operations will complete later";
case WSAEINTR:
return "Interrupted function call";
case WSAEBADF:
return "File handle is not valid";
case WSAEACCES:
return "Permission denied";
case WSAEFAULT:
return "Bad address";
case WSAEINVAL:
return "Invalid argument";
case WSAEMFILE:
return "Too many open files";
case WSAEWOULDBLOCK:
return "Resource temporarily unavailable";
case WSAEINPROGRESS:
return "Operation now in progress";
case WSAEALREADY:
return "Operation already in progress";
case WSAENOTSOCK:
return "Socket operation on nonsocket";
case WSAEDESTADDRREQ:
return "Destination address required";
case WSAEMSGSIZE:
return "Message too long";
case WSAEPROTOTYPE:
return "Protocol wrong type for socket";
case WSAENOPROTOOPT:
return "Bad protocol option";
case WSAEPROTONOSUPPORT:
return "Protocol not supported";
case WSAESOCKTNOSUPPORT:
return "Socket type not supported";
case WSAEOPNOTSUPP:
return "Operation not supported";
case WSAEPFNOSUPPORT:
return "Protocol family not supported";
case WSAEAFNOSUPPORT:
return "Address family not supported by protocol family";
case WSAEADDRINUSE:
return "Address already in use";
case WSAEADDRNOTAVAIL:
return "Cannot assign requested address";
case WSAENETDOWN:
return "Network is down";
case WSAENETUNREACH:
return "Network is unreachable";
case WSAENETRESET:
return "Network dropped connection on reset";
case WSAECONNABORTED:
return "Software caused connection abort";
case WSAECONNRESET:
return "Connection reset by peer";
case WSAENOBUFS:
return "No buffer space available";
case WSAEISCONN:
return "Socket is already connected";
case WSAENOTCONN:
return "Socket is not connected";
case WSAESHUTDOWN:
return "Cannot send after socket shutdown";
case WSAETOOMANYREFS:
return "Too many references";
case WSAETIMEDOUT:
return "Connection timed out";
case WSAECONNREFUSED:
return "Connection refused";
case WSAELOOP:
return "Cannot translate name";
case WSAENAMETOOLONG:
return "Name too long";
case WSAEHOSTDOWN:
return "Host is down";
case WSAEHOSTUNREACH:
return "No route to host";
case WSAENOTEMPTY:
return "Directory not empty";
case WSAEPROCLIM:
return "Too many processes";
case WSAEUSERS:
return "User quota exceeded";
case WSAEDQUOT:
return "Disk quota exceeded";
case WSAESTALE:
return "Stale file handle reference";
case WSAEREMOTE:
return "Item is remote";
case WSASYSNOTREADY:
return "Network subsystem is unavailable";
case WSAVERNOTSUPPORTED:
return "Winsock.dll version out of range";
case WSANOTINITIALISED:
return "Successful WSAStartup not yet performed";
case WSAEDISCON:
return "Graceful shutdown in progress";
case WSAENOMORE:
return "No more results";
case WSAECANCELLED:
return "Call has been canceled";
case WSAEINVALIDPROCTABLE:
return "Procedure call table is invalid";
case WSAEINVALIDPROVIDER:
return "Service provider is invalid";
case WSAEPROVIDERFAILEDINIT:
return "Service provider failed to initialize";
case WSASYSCALLFAILURE:
return "System call failure";
case WSASERVICE_NOT_FOUND:
return "Service not found";
case WSATYPE_NOT_FOUND:
return "Class type not found";
case WSA_E_NO_MORE:
return "No more results";
case WSA_E_CANCELLED:
return "Call was canceled";
case WSAEREFUSED:
return "Database query was refused";
case WSAHOST_NOT_FOUND:
return "Host not found";
case WSATRY_AGAIN:
return "Nonauthoritative host not found";
case WSANO_RECOVERY:
return "This is a nonrecoverable error";
case WSANO_DATA:
return "Valid name, no data record of requested type";
case WSA_QOS_RECEIVERS:
return "QoS receivers";
case WSA_QOS_SENDERS:
return "QoS senders";
case WSA_QOS_NO_SENDERS:
return "No QoS senders";
case WSA_QOS_NO_RECEIVERS:
return "QoS no receivers";
case WSA_QOS_REQUEST_CONFIRMED:
return "QoS request confirmed";
case WSA_QOS_ADMISSION_FAILURE:
return "QoS admission error";
case WSA_QOS_POLICY_FAILURE:
return "QoS policy failure";
case WSA_QOS_BAD_STYLE:
return "QoS bad style";
case WSA_QOS_BAD_OBJECT:
return "QoS bad object";
case WSA_QOS_TRAFFIC_CTRL_ERROR:
return "QoS traffic control error";
case WSA_QOS_GENERIC_ERROR:
return "QoS generic error";
case WSA_QOS_ESERVICETYPE:
return "QoS service type error";
case WSA_QOS_EFLOWSPEC:
return "QoS flowspec error";
case WSA_QOS_EPROVSPECBUF:
return "Invalid QoS provider buffer";
case WSA_QOS_EFILTERSTYLE:
return "Invalid QoS filter style";
case WSA_QOS_EFILTERTYPE:
return "Invalid QoS filter type";
case WSA_QOS_EFILTERCOUNT:
return "Incorrect QoS filter count";
case WSA_QOS_EOBJLENGTH:
return "Invalid QoS object length";
case WSA_QOS_EFLOWCOUNT:
return "Incorrect QoS flow count";
case WSA_QOS_EUNKOWNPSOBJ:
return "Unrecognized QoS object";
case WSA_QOS_EPOLICYOBJ:
return "Invalid QoS policy object";
case WSA_QOS_EFLOWDESC:
return "Invalid QoS flow descriptor";
case WSA_QOS_EPSFLOWSPEC:
return "Invalid QoS provider-specific flowspec";
case WSA_QOS_EPSFILTERSPEC:
return "Invalid QoS provider-specific filterspec";
case WSA_QOS_ESDMODEOBJ:
return "Invalid QoS shape discard mode object";
case WSA_QOS_ESHAPERATEOBJ:
return "Invalid QoS shaping rate object";
case WSA_QOS_RESERVED_PETYPE:
return "Reserved policy QoS element type";
}
return "Unknown winsock error";
}
/**
* Set last winsock error to equivalent of given errno value
* @param errnum the errno value to set
*/
void MHD_W32_set_last_winsock_error_(int errnum)
{
switch (errnum)
{
case 0:
WSASetLastError(0);
break;
case EBADF:
WSASetLastError(WSA_INVALID_HANDLE);
break;
case ENOMEM:
WSASetLastError(WSA_NOT_ENOUGH_MEMORY);
break;
case EINVAL:
WSASetLastError(WSA_INVALID_PARAMETER);
break;
case EINTR:
WSASetLastError(WSAEINTR);
break;
case EWOULDBLOCK:
WSASetLastError(WSAEWOULDBLOCK);
break;
case EINPROGRESS:
WSASetLastError(WSAEINPROGRESS);
break;
case EALREADY:
WSASetLastError(WSAEALREADY);
break;
case ENOTSOCK:
WSASetLastError(WSAENOTSOCK);
break;
case EDESTADDRREQ:
WSASetLastError(WSAEDESTADDRREQ);
break;
case EMSGSIZE:
WSASetLastError(WSAEMSGSIZE);
break;
case EPROTOTYPE:
WSASetLastError(WSAEPROTOTYPE);
break;
case ENOPROTOOPT:
WSASetLastError(WSAENOPROTOOPT);
break;
case EPROTONOSUPPORT:
WSASetLastError(WSAEPROTONOSUPPORT);
break;
case ESOCKTNOSUPPORT:
WSASetLastError(WSAESOCKTNOSUPPORT);
break;
case EOPNOTSUPP:
WSASetLastError(WSAEOPNOTSUPP);
break;
case EPFNOSUPPORT:
WSASetLastError(WSAEPFNOSUPPORT);
break;
case EAFNOSUPPORT:
WSASetLastError(WSAEAFNOSUPPORT);
break;
case EADDRINUSE:
WSASetLastError(WSAEADDRINUSE);
break;
case EADDRNOTAVAIL:
WSASetLastError(WSAEADDRNOTAVAIL);
break;
case ENETDOWN:
WSASetLastError(WSAENETDOWN);
break;
case ENETUNREACH:
WSASetLastError(WSAENETUNREACH);
break;
case ENETRESET:
WSASetLastError(WSAENETRESET);
break;
case ECONNABORTED:
WSASetLastError(WSAECONNABORTED);
break;
case ECONNRESET:
WSASetLastError(WSAECONNRESET);
break;
case ENOBUFS:
WSASetLastError(WSAENOBUFS);
break;
case EISCONN:
WSASetLastError(WSAEISCONN);
break;
case ENOTCONN:
WSASetLastError(WSAENOTCONN);
break;
case ESHUTDOWN:
WSASetLastError(WSAESHUTDOWN);
break;
case ETOOMANYREFS:
WSASetLastError(WSAETOOMANYREFS);
break;
case ETIMEDOUT:
WSASetLastError(WSAETIMEDOUT);
break;
case ECONNREFUSED:
WSASetLastError(WSAECONNREFUSED);
break;
case ELOOP:
WSASetLastError(WSAELOOP);
break;
case ENAMETOOLONG:
WSASetLastError(WSAENAMETOOLONG);
break;
case EHOSTDOWN:
WSASetLastError(WSAEHOSTDOWN);
break;
case EHOSTUNREACH:
WSASetLastError(WSAEHOSTUNREACH);
break;
case ENOTEMPTY:
WSASetLastError(WSAENOTEMPTY);
break;
case EPROCLIM:
WSASetLastError(WSAEPROCLIM);
break;
case EUSERS:
WSASetLastError(WSAEUSERS);
break;
case EDQUOT:
WSASetLastError(WSAEDQUOT);
break;
case ESTALE:
WSASetLastError(WSAESTALE);
break;
case EREMOTE:
WSASetLastError(WSAEREMOTE);
break;
case EFAULT:
WSASetLastError(WSAEFAULT);
break;
case ENODATA:
WSASetLastError(WSANO_DATA);
break;
#if EAGAIN != EWOULDBLOCK
case EAGAIN:
WSASetLastError(WSAEWOULDBLOCK);
break;
#endif
/* Rough equivalent */
case EIO:
WSASetLastError(WSAEREFUSED);
break;
default: /* Unmapped errors */
WSASetLastError(WSAENOBUFS);
break;
}
}
/**
* Create pair of mutually connected TCP/IP sockets on loopback address
* @param sockets_pair array to receive resulted sockets
* @return zero on success, -1 otherwise
*/
int MHD_W32_pair_of_sockets_(SOCKET sockets_pair[2])
{
int i;
if (!sockets_pair)
{
errno = EINVAL;
return -1;
}
#define PAIRMAXTRYIES 800
for (i = 0; i < PAIRMAXTRYIES; i++)
{
struct sockaddr_in listen_addr;
SOCKET listen_s;
static const int c_addinlen = sizeof(struct sockaddr_in); /* help compiler to optimize */
int addr_len = c_addinlen;
int opt = 1;
listen_s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == listen_s)
break; /* can't create even single socket */
listen_addr.sin_family = AF_INET;
listen_addr.sin_port = htons(0);
listen_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (0 == bind(listen_s, (struct sockaddr*) &listen_addr, c_addinlen)
&& 0 == listen(listen_s, 1)
&& 0 == getsockname(listen_s, (struct sockaddr*) &listen_addr,
&addr_len))
{
SOCKET client_s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET != client_s)
{
if (0 == ioctlsocket(client_s, FIONBIO, (u_long*) &opt)
&& (0 == connect(client_s, (struct sockaddr*) &listen_addr, c_addinlen)
|| WSAGetLastError() == WSAEWOULDBLOCK))
{
struct sockaddr_in accepted_from_addr;
addr_len = c_addinlen;
SOCKET server_s = accept(listen_s,
(struct sockaddr*) &accepted_from_addr, &addr_len);
if (INVALID_SOCKET != server_s)
{
struct sockaddr_in client_addr;
addr_len = c_addinlen;
opt = 0;
if (0 == getsockname(client_s, (struct sockaddr*) &client_addr, &addr_len)
&& accepted_from_addr.sin_family == client_addr.sin_family
&& accepted_from_addr.sin_port == client_addr.sin_port
&& accepted_from_addr.sin_addr.s_addr == client_addr.sin_addr.s_addr
&& 0 == ioctlsocket(client_s, FIONBIO, (u_long*) &opt)
&& 0 == ioctlsocket(server_s, FIONBIO, (u_long*) &opt))
{
closesocket(listen_s);
sockets_pair[0] = client_s;
sockets_pair[1] = server_s;
return 0;
}
closesocket(server_s);
}
}
closesocket(client_s);
}
}
closesocket(listen_s);
}
sockets_pair[0] = INVALID_SOCKET;
sockets_pair[1] = INVALID_SOCKET;
return -1;
}
/**
* Static variable used by pseudo random number generator
*/
static int32_t rnd_val = 0;
/**
* Generate 31-bit pseudo random number.
* Function initialize itself at first call to current time.
* @return 31-bit pseudo random number.
*/
int MHD_W32_random_(void)
{
if (0 == rnd_val)
rnd_val = (int32_t)time(NULL);
/* stolen from winsup\cygwin\random.cc */
rnd_val = (16807 * (rnd_val % 127773) - 2836 * (rnd_val / 127773))
& 0x7fffffff;
return (int)rnd_val;
}
/* Emulate snprintf function on W32 */
int W32_snprintf(char *__restrict s, size_t n, const char *__restrict format, ...)
{
int ret;
va_list args;
if (0 != n && NULL != s )
{
va_start(args, format);
ret = _vsnprintf(s, n, format, args);
va_end(args);
if (n == ret)
s[n - 1] = 0;
if (ret >= 0)
return ret;
}
va_start(args, format);
ret = _vscprintf(format, args);
va_end(args);
if (0 <= ret && 0 != n && NULL == s)
return -1;
return ret;
}
#ifdef _MSC_FULL_VER
/**
* Set thread name
* @param thread_id ID of thread, -1 for current thread
* @param thread_name name to set
*/
void W32_SetThreadName(const DWORD thread_id, const char *thread_name)
{
static const DWORD VC_SETNAME_EXC = 0x406D1388;
#pragma pack(push,8)
struct thread_info_struct
{
DWORD type; // Must be 0x1000.
LPCSTR name; // Pointer to name (in user address space).
DWORD ID; // Thread ID (-1=caller thread).
DWORD flags; // Reserved for future use, must be zero.
} thread_info;
#pragma pack(pop)
if (NULL == thread_name)
return;
thread_info.type = 0x1000;
thread_info.name = thread_name;
thread_info.ID = thread_id;
thread_info.flags = 0;
__try
{ /* This exception is intercepted by debugger */
RaiseException(VC_SETNAME_EXC, 0, sizeof(thread_info) / sizeof(ULONG_PTR), (ULONG_PTR*)&thread_info);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{}
}
#endif /* _MSC_FULL_VER */