diff options
Diffstat (limited to 'src/libstrongswan/utils/windows.c')
-rw-r--r-- | src/libstrongswan/utils/windows.c | 641 |
1 files changed, 641 insertions, 0 deletions
diff --git a/src/libstrongswan/utils/windows.c b/src/libstrongswan/utils/windows.c new file mode 100644 index 000000000..8820287b1 --- /dev/null +++ b/src/libstrongswan/utils/windows.c @@ -0,0 +1,641 @@ +/* + * Copyright (C) 2013 Martin Willi + * Copyright (C) 2013 revosec AG + * + * This program 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 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program 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. + */ + +#include "utils.h" + +#include <errno.h> + +/** + * See header + */ +void windows_init() +{ + WSADATA wsad; + + /* initialize winsock2 */ + WSAStartup(MAKEWORD(2, 2), &wsad); +} + +/** + * See header + */ +void windows_deinit() +{ + WSACleanup(); +} + +/** + * See header + */ +int usleep(useconds_t usec) +{ + if (usec > 0 && usec < 1000) + { /* do not Sleep(0) for small values */ + usec = 1000; + } + SleepEx(usec / 1000, TRUE); + return 0; +} + +/** + * See header. + */ +char* strndup(const char *s, size_t n) +{ + char *dst; + + n = min(strnlen(s, n), n); + dst = malloc(n + 1); + memcpy(dst, s, n); + dst[n] = '\0'; + + return dst; +} + +/* + * See header. + */ +void *dlopen(const char *filename, int flag) +{ + return LoadLibrary(filename); +} + +/** + * Load a symbol from known default libs (monolithic build) + */ +static void* dlsym_default(const char *name) +{ + const char *dlls[] = { + "libstrongswan-0.dll", + "libhydra-0.dll", + "libcharon-0.dll", + "libtnccs-0.dll", + NULL /* .exe */ + }; + HANDLE handle; + void *sym = NULL; + int i; + + for (i = 0; i < countof(dlls); i++) + { + handle = GetModuleHandle(dlls[i]); + if (handle) + { + sym = GetProcAddress(handle, name); + if (sym) + { + break; + } + } + } + return sym; +} + +/** + * Emulate RTLD_NEXT for some known symbols + */ +static void* dlsym_next(const char *name) +{ + struct { + const char *dll; + const char *syms[4]; + } dlls[] = { + /* for leak detective */ + { "msvcrt", + { "malloc", "calloc", "realloc", "free" } + }, + }; + HANDLE handle = NULL; + int i, j; + + for (i = 0; i < countof(dlls); i++) + { + for (j = 0; j < countof(dlls[0].syms); j++) + { + if (dlls[i].syms[j] && streq(dlls[i].syms[j], name)) + { + handle = GetModuleHandle(dlls[i].dll); + break; + } + } + } + if (handle) + { + return GetProcAddress(handle, name); + } + return handle; +} + +/** + * See header. + */ +void* dlsym(void *handle, const char *symbol) +{ + if (handle == RTLD_DEFAULT) + { + return dlsym_default(symbol); + } + if (handle == RTLD_NEXT) + { + return dlsym_next(symbol); + } + return GetProcAddress((HMODULE)handle, symbol); +} + +/** + * See header. + */ +char* dlerror(void) +{ + static char buf[128]; + char *pos; + DWORD err; + + err = GetLastError(); + if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err, 0, buf, sizeof(buf), NULL) > 0) + { + pos = strchr(buf, '\n'); + if (pos) + { + *pos = '\0'; + } + } + else + { + snprintf(buf, sizeof(buf), "(%u)", err); + } + return buf; +} + +/** + * See header. + */ +int dlclose(void *handle) +{ + return FreeLibrary((HMODULE)handle); +} + +/** + * See header + */ +int socketpair(int domain, int type, int protocol, int sv[2]) +{ + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(INADDR_LOOPBACK), + }; + socklen_t len = sizeof(addr); + int s, c, sc; + BOOL on; + + /* We don't check domain for AF_INET, as we use it as replacement for + * AF_UNIX. */ + if (type != SOCK_STREAM) + { + errno = EINVAL; + return -1; + } + if (protocol != 0 && protocol != IPPROTO_TCP) + { + errno = EINVAL; + return -1; + } + s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (s == -1) + { + return -1; + } + c = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (c == -1) + { + closesocket(s); + return -1; + } + if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) == 0 && + getsockname(s,(struct sockaddr*)&addr, &len) == 0 && + listen(s, 0) == 0 && + connect(c, (struct sockaddr*)&addr, sizeof(addr)) == 0) + { + sc = accept(s, NULL, NULL); + if (sc >= 0) + { + closesocket(s); + s = sc; + if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, + (void*)&on, sizeof(on)) == 0 && + setsockopt(c, IPPROTO_TCP, TCP_NODELAY, + (void*)&on, sizeof(on)) == 0) + { + sv[0] = s; + sv[1] = c; + return 0; + } + } + } + closesocket(s); + closesocket(c); + return -1; +} + +/** + * See header + */ +char* getpass(const char *prompt) +{ + static char buf[64] = ""; + char *pos; + HANDLE in, out; + DWORD mode, written = 0, total, done; + + out = GetStdHandle(STD_OUTPUT_HANDLE); + in = GetStdHandle(STD_INPUT_HANDLE); + + if (out == INVALID_HANDLE_VALUE || in == INVALID_HANDLE_VALUE || + !GetConsoleMode(out, &mode) || !GetConsoleMode(in, &mode)) + { + return NULL; + } + + total = strlen(prompt); + while (written < total) + { + if (!WriteConsole(out, prompt + written, total - written, &done, NULL)) + { + return NULL; + } + written += done; + } + + if (!SetConsoleMode(in, mode & ~ENABLE_ECHO_INPUT)) + { + return NULL; + } + + while (TRUE) + { + if (!ReadConsole(in, buf, sizeof(buf), &done, NULL)) + { + SetConsoleMode(in, mode); + return NULL; + } + buf[sizeof(buf)-1] = '\0'; + + if (done) + { + pos = strchr(buf, '\r'); + if (pos) + { + *pos = '\0'; + } + break; + } + } + SetConsoleMode(in, mode); + + /* append a newline, as we have no echo during input */ + WriteConsole(out, "\r\n", 2, &done, NULL); + + return buf; +} + +/** + * See header. + */ +#undef strerror_s +int strerror_s_extended(char *buf, size_t buflen, int errnum) +{ + const char *errstr [] = { + /* EADDRINUSE */ "Address in use", + /* EADDRNOTAVAIL */ "Address not available", + /* EAFNOSUPPORT */ "Address family not supported", + /* EALREADY */ "Connection already in progress", + /* EBADMSG */ "Bad message", + /* ECANCELED */ "Operation canceled", + /* ECONNABORTED */ "Connection aborted", + /* ECONNREFUSED */ "Connection refused", + /* ECONNRESET */ "Connection reset", + /* EDESTADDRREQ */ "Destination address required", + /* EHOSTUNREACH */ "Host is unreachable", + /* EIDRM */ "Identifier removed", + /* EINPROGRESS */ "Operation in progress", + /* EISCONN */ "Socket is connected", + /* ELOOP */ "Too many levels of symbolic links", + /* EMSGSIZE */ "Message too large", + /* ENETDOWN */ "Network is down", + /* ENETRESET */ "Connection aborted by network", + /* ENETUNREACH */ "Network unreachable", + /* ENOBUFS */ "No buffer space available", + /* ENODATA */ "No message is available", + /* ENOLINK */ "No link", + /* ENOMSG */ "No message of the desired type", + /* ENOPROTOOPT */ "Protocol not available", + /* ENOSR */ "No stream resources", + /* ENOSTR */ "Not a stream", + /* ENOTCONN */ "The socket is not connected", + /* ENOTRECOVERABLE */ "State not recoverable", + /* ENOTSOCK */ "Not a socket", + /* ENOTSUP */ "Not supported", + /* EOPNOTSUPP */ "Operation not supported on socket", + /* EOTHER */ "Other error", + /* EOVERFLOW */ "Value too large to be stored in data type", + /* EOWNERDEAD */ "Previous owner died", + /* EPROTO */ "Protocol error", + /* EPROTONOSUPPORT */ "Protocol not supported", + /* EPROTOTYPE */ "Protocol wrong type for socket", + /* ETIME */ "Timeout", + /* ETIMEDOUT */ "Connection timed out", + /* ETXTBSY */ "Text file busy", + /* EWOULDBLOCK */ "Operation would block", + }; + int offset = EADDRINUSE; + + if (errnum < offset || errnum >= offset + countof(errstr)) + { + return strerror_s(buf, buflen, errnum); + } + strncpy(buf, errstr[errnum - offset], buflen); + buf[buflen - 1] = '\0'; + return 0; +} + +/** + * Set errno for a function setting WSA error on failure + */ +static int wserr(int retval) +{ + if (retval < 0) + { + static const struct { + DWORD wsa; + int err; + } map[] = { + { WSANOTINITIALISED, EBADF }, + { WSAENETDOWN, ENETDOWN }, + { WSAENETRESET, ENETRESET }, + { WSAECONNABORTED, ECONNABORTED }, + { WSAESHUTDOWN, ECONNABORTED }, + { WSAEACCES, EACCES }, + { WSAEINTR, EINTR }, + { WSAEINPROGRESS, EINPROGRESS }, + { WSAEFAULT, EFAULT }, + { WSAENOBUFS, ENOBUFS }, + { WSAENOTSOCK, ENOTSOCK }, + { WSAEOPNOTSUPP, EOPNOTSUPP }, + { WSAEWOULDBLOCK, EWOULDBLOCK }, + { WSAEMSGSIZE, EMSGSIZE }, + { WSAEINVAL, EINVAL }, + { WSAENOTCONN, ENOTCONN }, + { WSAEHOSTUNREACH, EHOSTUNREACH }, + { WSAENETUNREACH, ENETUNREACH }, + { WSAECONNABORTED, ECONNABORTED }, + { WSAECONNRESET, ECONNRESET }, + { WSAETIMEDOUT, ETIMEDOUT }, + { WSAEMFILE, EMFILE }, + { WSAEALREADY, EALREADY }, + { WSAEDESTADDRREQ, EDESTADDRREQ }, + { WSAEISCONN, EISCONN }, + { WSAEOPNOTSUPP, EOPNOTSUPP }, + { WSAEPROTOTYPE, EPROTOTYPE }, + { WSAENOPROTOOPT, ENOPROTOOPT }, + { WSAEPROTONOSUPPORT, EPROTONOSUPPORT }, + { WSAEPFNOSUPPORT, EPROTONOSUPPORT }, + { WSAEAFNOSUPPORT, EAFNOSUPPORT }, + { WSAEADDRNOTAVAIL, EADDRNOTAVAIL }, + { WSAEADDRINUSE, EADDRINUSE }, + { WSAETIMEDOUT, ETIMEDOUT }, + { WSAECONNREFUSED, ECONNREFUSED }, + { WSAELOOP, ELOOP }, + { WSAENAMETOOLONG, ENAMETOOLONG }, + { WSAENOTEMPTY, ENOTEMPTY }, + { WSAEPROTOTYPE, EPROTOTYPE }, + { WSAVERNOTSUPPORTED, ENOTSUP }, + }; + DWORD wsa, i; + + wsa = WSAGetLastError(); + for (i = 0; i < countof(map); i++) + { + if (map[i].wsa == wsa) + { + errno = map[i].err; + return retval; + } + } + errno = ENOENT; + return retval; + } + errno = 0; + return retval; +} + +/** + * Check and clear the dontwait flag + */ +static bool check_dontwait(int *flags) +{ + if (*flags & MSG_DONTWAIT) + { + *flags &= ~MSG_DONTWAIT; + return TRUE; + } + return FALSE; +} + +/** + * See header + */ +#undef shutdown +int windows_shutdown(int sockfd, int how) +{ + return wserr(shutdown(sockfd, how)); +} + +/** + * See header + */ +#undef accept +int windows_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) +{ + return wserr(accept(sockfd, addr, addrlen)); +} + +/** + * See header + */ +#undef bind +int windows_bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) +{ + return wserr(bind(sockfd, addr, addrlen)); +} + +/** + * See header + */ +#undef connect +int windows_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) +{ + return wserr(connect(sockfd, addr, addrlen)); +} + +/** + * See header + */ +#undef getsockname +int windows_getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen) +{ + return wserr(getsockname(sockfd, addr, addrlen)); +} + +/** + * See header + */ +#undef getsockopt +int windows_getsockopt(int sockfd, int level, int optname, + void *optval, socklen_t *optlen) +{ + return wserr(getsockopt(sockfd, level, optname, optval, optlen)); +} + +/** + * See header + */ +#undef setsockopt +int windows_setsockopt(int sockfd, int level, int optname, + const void *optval, socklen_t optlen) +{ + return wserr(setsockopt(sockfd, level, optname, optval, optlen)); +} + +/** + * See header + */ +#undef socket +int windows_socket(int domain, int type, int protocol) +{ + return wserr(socket(domain, type, protocol)); +} + +/** + * See header + */ +#undef select +int windows_select(int nfds, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, struct timeval *timeout) +{ + return wserr(select(nfds, readfds, writefds, exceptfds, timeout)); +} + +/** + * See header + */ +#undef close +int windows_close(int fd) +{ + int ret; + + ret = close(fd); + if (ret == -1 && errno == EBADF) + { /* Winsock socket? */ + ret = wserr(closesocket(fd)); + } + return ret; +} + +/** + * See header + */ +#undef recv +ssize_t windows_recv(int sockfd, void *buf, size_t len, int flags) +{ + u_long on = 1, off = 0; + ssize_t outlen = -1; + + if (!check_dontwait(&flags)) + { + return wserr(recv(sockfd, buf, len, flags)); + } + if (wserr(ioctlsocket(sockfd, FIONBIO, &on) == 0)) + { + outlen = wserr(recv(sockfd, buf, len, flags)); + ioctlsocket(sockfd, FIONBIO, &off); + } + return outlen; +} + +/** + * See header + */ +#undef recvfrom +ssize_t windows_recvfrom(int sockfd, void *buf, size_t len, int flags, + struct sockaddr *src_addr, socklen_t *addrlen) +{ + u_long on = 1, off = 0; + ssize_t outlen = -1; + + if (!check_dontwait(&flags)) + { + return wserr(recvfrom(sockfd, buf, len, flags, src_addr, addrlen)); + } + if (wserr(ioctlsocket(sockfd, FIONBIO, &on)) == 0) + { + outlen = wserr(recvfrom(sockfd, buf, len, flags, src_addr, addrlen)); + ioctlsocket(sockfd, FIONBIO, &off); + } + return outlen; +} + +/** + * See header + */ +#undef send +ssize_t windows_send(int sockfd, const void *buf, size_t len, int flags) +{ + u_long on = 1, off = 0; + ssize_t outlen = -1; + + if (!check_dontwait(&flags)) + { + return wserr(send(sockfd, buf, len, flags)); + } + if (wserr(ioctlsocket(sockfd, FIONBIO, &on)) == 0) + { + outlen = wserr(send(sockfd, buf, len, flags)); + ioctlsocket(sockfd, FIONBIO, &off); + } + return outlen; +} + +/** + * See header + */ +#undef sendto +ssize_t windows_sendto(int sockfd, const void *buf, size_t len, int flags, + const struct sockaddr *dest_addr, socklen_t addrlen) +{ + u_long on = 1, off = 0; + ssize_t outlen = -1; + + if (!check_dontwait(&flags)) + { + return wserr(sendto(sockfd, buf, len, flags, dest_addr, addrlen)); + } + if (wserr(ioctlsocket(sockfd, FIONBIO, &on)) == 0) + { + outlen = wserr(sendto(sockfd, buf, len, flags, dest_addr, addrlen)); + ioctlsocket(sockfd, FIONBIO, &off); + } + return outlen; +} |