diff options
Diffstat (limited to 'src/platform/w32functions.c')
-rw-r--r-- | src/platform/w32functions.c | 704 |
1 files changed, 704 insertions, 0 deletions
diff --git a/src/platform/w32functions.c b/src/platform/w32functions.c new file mode 100644 index 00000000..bc42fef4 --- /dev/null +++ b/src/platform/w32functions.c @@ -0,0 +1,704 @@ +/* + 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 */ |