| /* |
| 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 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, |
| (int) 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_port == client_addr.sin_port) && |
| (accepted_from_addr.sin_addr.s_addr == |
| client_addr.sin_addr.s_addr) && |
| (accepted_from_addr.sin_family == client_addr.sin_family) && |
| ( (0 != non_blk) ? |
| (0 == ioctlsocket (server_s, |
| (int) FIONBIO, |
| &on_val)) : |
| (0 == ioctlsocket (client_s, |
| (int) FIONBIO, |
| &off_val)) ) && |
| (0 == setsockopt (server_s, |
| IPPROTO_TCP, |
| TCP_NODELAY, |
| (const void *) (&on_val), |
| sizeof (on_val))) && |
| (0 == setsockopt (client_s, |
| IPPROTO_TCP, |
| TCP_NODELAY, |
| (const void *) (&on_val), |
| sizeof (on_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, |
| (int) 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; |
| } |
| |
| |
| /** |
| * Disable Nagle's algorithm on @a sock. This is what we do by default for |
| * all TCP sockets in MHD, unless the platform does not support the MSG_MORE |
| * or MSG_CORK or MSG_NOPUSH options. |
| * |
| * @param sock socket to manipulate |
| * @param on value to use |
| * @return 0 on success |
| */ |
| int |
| MHD_socket_set_nodelay_ (MHD_socket sock, |
| bool on) |
| { |
| #ifdef TCP_NODELAY |
| { |
| const MHD_SCKT_OPT_BOOL_ off_val = 0; |
| const MHD_SCKT_OPT_BOOL_ on_val = 1; |
| /* Disable Nagle's algorithm for normal buffering */ |
| return setsockopt (sock, |
| IPPROTO_TCP, |
| TCP_NODELAY, |
| (const void *) ((on) ? &on_val : &off_val), |
| sizeof (on_val)); |
| } |
| #else |
| (void) sock; |
| return 0; |
| #endif /* TCP_NODELAY */ |
| } |
| |
| |
| /** |
| * 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) ) |
| |
| cloexec_set = (SOCK_CLOEXEC_OR_ZERO != 0); |
| #if defined(SOCK_NOSIGPIPE) || defined(MHD_socket_nosignal_) |
| nosigpipe_set = (SOCK_NOSIGPIPE_OR_ZERO != 0); |
| #endif /* SOCK_NOSIGPIPE || MHD_socket_nosignal_ */ |
| fd = socket (pf, |
| SOCK_STREAM | SOCK_CLOEXEC | SOCK_NOSIGPIPE_OR_ZERO, |
| 0); |
| #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 defined(SOCK_NOSIGPIPE) || defined(MHD_socket_nosignal_) |
| nosigpipe_set = 0; |
| #endif /* SOCK_NOSIGPIPE || MHD_socket_nosignal_ */ |
| } |
| if (MHD_INVALID_SOCKET == fd) |
| return MHD_INVALID_SOCKET; |
| |
| #if defined(MHD_socket_nosignal_) |
| if ( (! nosigpipe_set) && |
| (0 == MHD_socket_nosignal_ (fd)) && |
| (0 == MSG_NOSIGNAL_OR_ZERO) ) |
| { |
| /* 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 /* defined(MHD_socket_nosignal_) */ |
| if (! cloexec_set) |
| (void) MHD_socket_noninheritable_ (fd); |
| |
| return fd; |
| } |