/* This file is part of libmicrohttpd Copyright (C) 2014-2016 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * @file microhttpd/mhd_sockets.c * @brief Implementation for sockets functions * @author Karlson2k (Evgeny Grin) */ #include "mhd_sockets.h" #ifdef HAVE_UNISTD_H #include #endif /* HAVE_UNISTD_H */ #include #ifdef MHD_WINSOCK_SOCKETS /** * Return pointer to string description of specified WinSock error * @param err the WinSock error code. * @return pointer to string description of specified WinSock error. */ const char* MHD_W32_strerror_winsock_(int err) { switch (err) { 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"; } /** * Create pair of mutually connected TCP/IP sockets on loopback address * @param sockets_pair array to receive resulted sockets * @param non_blk if set to non-zero value, sockets created in non-blocking mode * otherwise sockets will be in blocking mode * @return non-zero if succeeded, zero otherwise */ int MHD_W32_socket_pair_(SOCKET sockets_pair[2], int non_blk) { int i; if (! sockets_pair) { WSASetLastError (WSAEFAULT); return 0; } #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; unsigned long on_val = 1; unsigned long off_val = 0; 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 = 0; /* same as 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); struct sockaddr_in accepted_from_addr; struct sockaddr_in client_addr; SOCKET server_s; if (INVALID_SOCKET == client_s) { /* try again */ closesocket (listen_s); continue; } if ( (0 != ioctlsocket (client_s, FIONBIO, &on_val)) || ( (0 != connect (client_s, (struct sockaddr*) &listen_addr, c_addinlen)) && (WSAGetLastError() != WSAEWOULDBLOCK)) ) { /* try again */ closesocket (listen_s); closesocket (client_s); continue; } addr_len = c_addinlen; server_s = accept (listen_s, (struct sockaddr*) &accepted_from_addr, &addr_len); if (INVALID_SOCKET == server_s) { /* try again */ closesocket (listen_s); closesocket (client_s); continue; } addr_len = c_addinlen; 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 != non_blk) ? (0 == ioctlsocket(server_s, FIONBIO, &on_val)) : (0 == ioctlsocket(client_s, FIONBIO, &off_val)) ) ) { closesocket (listen_s); sockets_pair[0] = server_s; sockets_pair[1] = client_s; return !0; } closesocket (server_s); closesocket (client_s); } closesocket(listen_s); } sockets_pair[0] = INVALID_SOCKET; sockets_pair[1] = INVALID_SOCKET; WSASetLastError(WSAECONNREFUSED); return 0; } #endif /* MHD_WINSOCK_SOCKETS */ /** * Add @a fd to the @a set. If @a fd is * greater than @a max_fd, set @a max_fd to @a fd. * * @param fd file descriptor to add to the @a set * @param set set to modify * @param max_fd maximum value to potentially update * @param fd_setsize value of FD_SETSIZE * @return non-zero if succeeded, zero otherwise */ int MHD_add_to_fd_set_ (MHD_socket fd, fd_set *set, MHD_socket *max_fd, unsigned int fd_setsize) { if ( (NULL == set) || (MHD_INVALID_SOCKET == fd) ) return 0; if (! MHD_SCKT_FD_FITS_FDSET_SETSIZE_ (fd, set, fd_setsize)) return 0; MHD_SCKT_ADD_FD_TO_FDSET_SETSIZE_(fd, set, fd_setsize); if ( (NULL != max_fd) && ( (fd > *max_fd) || (MHD_INVALID_SOCKET == *max_fd) ) ) *max_fd = fd; return ! 0; } /** * Change socket options to be non-blocking. * * @param sock socket to manipulate * @return non-zero if succeeded, zero otherwise */ int MHD_socket_nonblocking_ (MHD_socket sock) { #if defined(MHD_POSIX_SOCKETS) int flags; flags = fcntl (sock, F_GETFL); if (-1 == flags) return 0; if ( ((flags | O_NONBLOCK) != flags) && (0 != fcntl (sock, F_SETFL, flags | O_NONBLOCK)) ) return 0; #elif defined(MHD_WINSOCK_SOCKETS) unsigned long flags = 1; if (0 != ioctlsocket (sock, FIONBIO, &flags)) return 0; #endif /* MHD_WINSOCK_SOCKETS */ return !0; } /** * Change socket options to be non-inheritable. * * @param sock socket to manipulate * @return non-zero if succeeded, zero otherwise * @warning Does not set socket error on W32. */ int MHD_socket_noninheritable_ (MHD_socket sock) { #if defined(MHD_POSIX_SOCKETS) int flags; flags = fcntl (sock, F_GETFD); if (-1 == flags) return 0; if ( ((flags | FD_CLOEXEC) != flags) && (0 != fcntl (sock, F_SETFD, flags | FD_CLOEXEC)) ) return 0; #elif defined(MHD_WINSOCK_SOCKETS) if (! SetHandleInformation ((HANDLE)sock, HANDLE_FLAG_INHERIT, 0)) return 0; #endif /* MHD_WINSOCK_SOCKETS */ return !0; } /** * Change socket buffering mode to default. * * @param sock socket to manipulate * @return non-zero if succeeded, zero otherwise */ int MHD_socket_buffering_reset_ (MHD_socket sock) { int res = !0; #if defined(TCP_NODELAY) || defined(MHD_TCP_CORK_NOPUSH) const MHD_SCKT_OPT_BOOL_ off_val = 0; #if defined(MHD_TCP_CORK_NOPUSH) //#if OLD_SOCKOPT /* Disable extra buffering */ res = (0 == setsockopt (sock, IPPROTO_TCP, MHD_TCP_CORK_NOPUSH, (const void *) &off_val, sizeof (off_val))) && res; //#endif #endif /* MHD_TCP_CORK_NOPUSH */ #if defined(TCP_NODELAY) //#if OLD_SOCKOPT /* Enable Nagle's algorithm for normal buffering */ res = (0 == setsockopt (sock, IPPROTO_TCP, TCP_NODELAY, (const void *) &off_val, sizeof (off_val))) && res; //#endif #endif /* TCP_NODELAY */ #else /* !TCP_NODELAY && !MHD_TCP_CORK_NOPUSH */ (void) sock; #endif /* !TCP_NODELAY && !MHD_TCP_CORK_NOPUSH */ return res; } /** * Create a listen socket, with noninheritable flag if possible. * * @param pf protocol family to use * @return created socket or MHD_INVALID_SOCKET in case of errors */ MHD_socket MHD_socket_create_listen_ (int pf) { MHD_socket fd; int cloexec_set; #if defined(SOCK_NOSIGPIPE) || defined(MHD_socket_nosignal_) int nosigpipe_set; #endif /* SOCK_NOSIGPIPE || MHD_socket_nosignal_ */ #if defined(MHD_POSIX_SOCKETS) && ( defined(SOCK_CLOEXEC) || defined(SOCK_NOSIGPIPE) ) fd = socket (pf, SOCK_STREAM | SOCK_CLOEXEC | MAYBE_SOCK_NOSIGPIPE, 0); cloexec_set = (MAYBE_SOCK_CLOEXEC != 0); #if defined(SOCK_NOSIGPIPE) || defined(MHD_socket_nosignal_) nosigpipe_set = (MAYBE_SOCK_NOSIGPIPE != 0); #endif /* SOCK_NOSIGPIPE || MHD_socket_nosignal_ */ #elif defined(MHD_WINSOCK_SOCKETS) && defined (WSA_FLAG_NO_HANDLE_INHERIT) fd = WSASocketW (pf, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT); cloexec_set = !0; #else /* !SOCK_CLOEXEC */ fd = MHD_INVALID_SOCKET; #endif /* !SOCK_CLOEXEC */ if (MHD_INVALID_SOCKET == fd) { fd = socket (pf, SOCK_STREAM, 0); cloexec_set = 0; } if (MHD_INVALID_SOCKET == fd) return MHD_INVALID_SOCKET; #if defined(SOCK_NOSIGPIPE) || defined(MHD_socket_nosignal_) if ( ( (! nosigpipe_set) #ifdef MHD_socket_nosignal_ || (! MHD_socket_nosignal_(fd)) #endif /* MHD_socket_nosignal_ */ ) && (0 == MAYBE_MSG_NOSIGNAL) ) { /* SIGPIPE disable is possible on this platform * (so application expect that it will be disabled), * but failed to be disabled here and it is not * possible to disable SIGPIPE by MSG_NOSIGNAL. */ const int err = MHD_socket_get_error_ (); (void) MHD_socket_close_ (fd); MHD_socket_fset_error_ (err); return MHD_INVALID_SOCKET; } #endif /* SOCK_NOSIGPIPE || MHD_socket_nosignal_ */ if (! cloexec_set) (void) MHD_socket_noninheritable_ (fd); return fd; }