diff options
Diffstat (limited to '')
-rw-r--r-- | network_io/win32/sendrecv.c | 442 | ||||
-rw-r--r-- | network_io/win32/sockets.c | 559 | ||||
-rw-r--r-- | network_io/win32/sockopt.c | 302 |
3 files changed, 1303 insertions, 0 deletions
diff --git a/network_io/win32/sendrecv.c b/network_io/win32/sendrecv.c new file mode 100644 index 0000000..6620e13 --- /dev/null +++ b/network_io/win32/sendrecv.c @@ -0,0 +1,442 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "apr_arch_networkio.h" +#include "apr_errno.h" +#include "apr_general.h" +#include "apr_network_io.h" +#include "apr_lib.h" +#include "apr_arch_file_io.h" +#if APR_HAVE_TIME_H +#include <time.h> +#endif + +/* MAX_SEGMENT_SIZE is the maximum amount of data that will be sent to a client + * in one call of TransmitFile. This number must be small enough to give the + * slowest client time to receive the data before the socket timeout triggers. + * The same problem can exist with apr_socket_send(). In that case, we rely on + * the application to adjust socket timeouts and max send segment + * sizes appropriately. + * For example, Apache will in most cases call apr_socket_send() with less + * than 8193 bytes. + */ +#define MAX_SEGMENT_SIZE 65536 +#define WSABUF_ON_STACK 50 + +APR_DECLARE(apr_status_t) apr_socket_send(apr_socket_t *sock, const char *buf, + apr_size_t *len) +{ + apr_ssize_t rv; + WSABUF wsaData; + int lasterror; + DWORD dwBytes = 0; + + wsaData.len = (u_long)*len; + wsaData.buf = (char*) buf; + +#ifndef _WIN32_WCE + rv = WSASend(sock->socketdes, &wsaData, 1, &dwBytes, 0, NULL, NULL); +#else + rv = send(sock->socketdes, wsaData.buf, wsaData.len, 0); + dwBytes = rv; +#endif + if (rv == SOCKET_ERROR) { + lasterror = apr_get_netos_error(); + *len = 0; + return lasterror; + } + + *len = dwBytes; + + return APR_SUCCESS; +} + + +APR_DECLARE(apr_status_t) apr_socket_recv(apr_socket_t *sock, char *buf, + apr_size_t *len) +{ + apr_ssize_t rv; + WSABUF wsaData; + int lasterror; + DWORD dwBytes = 0; + DWORD flags = 0; + + wsaData.len = (u_long)*len; + wsaData.buf = (char*) buf; + +#ifndef _WIN32_WCE + rv = WSARecv(sock->socketdes, &wsaData, 1, &dwBytes, &flags, NULL, NULL); +#else + rv = recv(sock->socketdes, wsaData.buf, wsaData.len, 0); + dwBytes = rv; +#endif + if (rv == SOCKET_ERROR) { + lasterror = apr_get_netos_error(); + *len = 0; + return lasterror; + } + + *len = dwBytes; + return dwBytes == 0 ? APR_EOF : APR_SUCCESS; +} + + +APR_DECLARE(apr_status_t) apr_socket_sendv(apr_socket_t *sock, + const struct iovec *vec, + apr_int32_t in_vec, apr_size_t *nbytes) +{ + apr_status_t rc = APR_SUCCESS; + apr_ssize_t rv; + apr_size_t total_len; + apr_int32_t i; + DWORD dwBytes = 0; + WSABUF *pWsaBuf; + + total_len = 0; + for (i = 0; i < in_vec; i++) { + apr_size_t iov_len = vec[i].iov_len; + if (iov_len > (apr_size_t)MAXDWORD - total_len) { + /* WSASend() returns NumberOfBytesSent as DWORD, so the total size + should be less than that. */ + return APR_EINVAL; + } + total_len += iov_len; + } + + pWsaBuf = (in_vec <= WSABUF_ON_STACK) ? _alloca(sizeof(WSABUF) * (in_vec)) + : malloc(sizeof(WSABUF) * (in_vec)); + if (!pWsaBuf) + return APR_ENOMEM; + + for (i = 0; i < in_vec; i++) { + pWsaBuf[i].buf = vec[i].iov_base; + pWsaBuf[i].len = (ULONG) vec[i].iov_len; + } +#ifndef _WIN32_WCE + rv = WSASend(sock->socketdes, pWsaBuf, in_vec, &dwBytes, 0, NULL, NULL); + if (rv == SOCKET_ERROR) { + rc = apr_get_netos_error(); + } +#else + for (i = 0; i < in_vec; i++) { + rv = send(sock->socketdes, pWsaBuf[i].buf, pWsaBuf[i].len, 0); + if (rv == SOCKET_ERROR) { + rc = apr_get_netos_error(); + break; + } + dwBytes += rv; + } +#endif + if (in_vec > WSABUF_ON_STACK) + free(pWsaBuf); + + *nbytes = dwBytes; + return rc; +} + + +APR_DECLARE(apr_status_t) apr_socket_sendto(apr_socket_t *sock, + apr_sockaddr_t *where, + apr_int32_t flags, const char *buf, + apr_size_t *len) +{ + apr_ssize_t rv; + + rv = sendto(sock->socketdes, buf, (int)*len, flags, + (const struct sockaddr*)&where->sa, + where->salen); + if (rv == SOCKET_ERROR) { + *len = 0; + return apr_get_netos_error(); + } + + *len = rv; + return APR_SUCCESS; +} + + +APR_DECLARE(apr_status_t) apr_socket_recvfrom(apr_sockaddr_t *from, + apr_socket_t *sock, + apr_int32_t flags, + char *buf, apr_size_t *len) +{ + apr_ssize_t rv; + + from->salen = sizeof(from->sa); + + rv = recvfrom(sock->socketdes, buf, (int)*len, flags, + (struct sockaddr*)&from->sa, &from->salen); + if (rv == SOCKET_ERROR) { + (*len) = 0; + return apr_get_netos_error(); + } + + apr_sockaddr_vars_set(from, from->sa.sin.sin_family, + ntohs(from->sa.sin.sin_port)); + + (*len) = rv; + if (rv == 0 && sock->type == SOCK_STREAM) + return APR_EOF; + + return APR_SUCCESS; +} + + +#if APR_HAS_SENDFILE +static apr_status_t collapse_iovec(char **off, apr_size_t *len, + struct iovec *iovec, int numvec, + char *buf, apr_size_t buflen) +{ + if (numvec == 1) { + *off = iovec[0].iov_base; + *len = iovec[0].iov_len; + } + else { + int i; + for (i = 0; i < numvec; i++) { + *len += iovec[i].iov_len; + } + + if (*len > buflen) { + *len = 0; + return APR_INCOMPLETE; + } + + *off = buf; + + for (i = 0; i < numvec; i++) { + memcpy(buf, iovec[i].iov_base, iovec[i].iov_len); + buf += iovec[i].iov_len; + } + } + return APR_SUCCESS; +} + + +/* + * apr_status_t apr_socket_sendfile(apr_socket_t *, apr_file_t *, apr_hdtr_t *, + * apr_off_t *, apr_size_t *, apr_int32_t flags) + * Send a file from an open file descriptor to a socket, along with + * optional headers and trailers + * arg 1) The socket to which we're writing + * arg 2) The open file from which to read + * arg 3) A structure containing the headers and trailers to send + * arg 4) Offset into the file where we should begin writing + * arg 5) Number of bytes to send out of the file + * arg 6) APR flags that are mapped to OS specific flags + */ +APR_DECLARE(apr_status_t) apr_socket_sendfile(apr_socket_t *sock, + apr_file_t *file, + apr_hdtr_t *hdtr, + apr_off_t *offset, + apr_size_t *len, + apr_int32_t flags) +{ + apr_status_t status = APR_SUCCESS; + apr_status_t rv; + apr_off_t curoff = *offset; + DWORD dwFlags = 0; + apr_size_t nbytes; + TRANSMIT_FILE_BUFFERS tfb, *ptfb = NULL; + apr_size_t bytes_to_send; /* Bytes to send out of the file (not including headers) */ + int disconnected = 0; + int sendv_trailers = 0; + char hdtrbuf[4096]; + + if (apr_os_level < APR_WIN_NT) { + return APR_ENOTIMPL; + } + + /* Use len to keep track of number of total bytes sent (including headers) */ + bytes_to_send = *len; + *len = 0; + + /* Handle the goofy case of sending headers/trailers and a zero byte file */ + if (!bytes_to_send && hdtr) { + if (hdtr->numheaders) { + rv = apr_socket_sendv(sock, hdtr->headers, hdtr->numheaders, + &nbytes); + if (rv != APR_SUCCESS) + return rv; + *len += nbytes; + } + if (hdtr->numtrailers) { + rv = apr_socket_sendv(sock, hdtr->trailers, hdtr->numtrailers, + &nbytes); + if (rv != APR_SUCCESS) + return rv; + *len += nbytes; + } + return APR_SUCCESS; + } + + memset(&tfb, '\0', sizeof (tfb)); + + /* Collapse the headers into a single buffer */ + if (hdtr && hdtr->numheaders) { + apr_size_t head_length = tfb.HeadLength; + ptfb = &tfb; + nbytes = 0; + rv = collapse_iovec((char **)&ptfb->Head, &head_length, + hdtr->headers, hdtr->numheaders, + hdtrbuf, sizeof(hdtrbuf)); + + tfb.HeadLength = (DWORD)head_length; + + /* If not enough buffer, punt to sendv */ + if (rv == APR_INCOMPLETE) { + rv = apr_socket_sendv(sock, hdtr->headers, hdtr->numheaders, &nbytes); + if (rv != APR_SUCCESS) + return rv; + *len += nbytes; + ptfb = NULL; + } + } + + /* Initialize the overlapped structure used on TransmitFile + */ + if (!sock->overlapped) { + sock->overlapped = apr_pcalloc(sock->pool, sizeof(OVERLAPPED)); + sock->overlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + } + while (bytes_to_send) { + DWORD xmitbytes; + + if (bytes_to_send > MAX_SEGMENT_SIZE) { + xmitbytes = MAX_SEGMENT_SIZE; + } + else { + /* Last call to TransmitFile() */ + xmitbytes = (DWORD)bytes_to_send; + /* Collapse the trailers into a single buffer */ + if (hdtr && hdtr->numtrailers) { + apr_size_t tail_length = tfb.TailLength; + ptfb = &tfb; + rv = collapse_iovec((char**) &ptfb->Tail, &tail_length, + hdtr->trailers, hdtr->numtrailers, + hdtrbuf + ptfb->HeadLength, + sizeof(hdtrbuf) - ptfb->HeadLength); + + tfb.TailLength = (DWORD)tail_length; + + if (rv == APR_INCOMPLETE) { + /* If not enough buffer, punt to sendv, later */ + sendv_trailers = 1; + } + } + /* Disconnect the socket after last send */ + if ((flags & APR_SENDFILE_DISCONNECT_SOCKET) + && !sendv_trailers) { + dwFlags |= TF_REUSE_SOCKET; + dwFlags |= TF_DISCONNECT; + disconnected = 1; + } + } + + sock->overlapped->Offset = (DWORD)(curoff); +#if APR_HAS_LARGE_FILES + sock->overlapped->OffsetHigh = (DWORD)(curoff >> 32); +#endif + /* XXX BoundsChecker claims dwFlags must not be zero. */ + rv = TransmitFile(sock->socketdes, /* socket */ + file->filehand, /* open file descriptor of the file to be sent */ + xmitbytes, /* number of bytes to send. 0=send all */ + 0, /* Number of bytes per send. 0=use default */ + sock->overlapped, /* OVERLAPPED structure */ + ptfb, /* header and trailer buffers */ + dwFlags); /* flags to control various aspects of TransmitFile */ + if (!rv) { + status = apr_get_netos_error(); + if ((status == APR_FROM_OS_ERROR(ERROR_IO_PENDING)) || + (status == APR_FROM_OS_ERROR(WSA_IO_PENDING))) + { + rv = WaitForSingleObject(sock->overlapped->hEvent, + (DWORD)(sock->timeout >= 0 + ? sock->timeout_ms : INFINITE)); + if (rv == WAIT_OBJECT_0) { + status = APR_SUCCESS; + if (!disconnected) { + if (!WSAGetOverlappedResult(sock->socketdes, + sock->overlapped, + &xmitbytes, + FALSE, + &dwFlags)) { + status = apr_get_netos_error(); + } + /* Ugly code alert: WSAGetOverlappedResult returns + * a count of all bytes sent. This loop only + * tracks bytes sent out of the file. + */ + else if (ptfb) { + xmitbytes -= (ptfb->HeadLength + ptfb->TailLength); + } + } + } + else if (rv == WAIT_TIMEOUT) { + status = APR_FROM_OS_ERROR(WAIT_TIMEOUT); + } + else if (rv == WAIT_ABANDONED) { + /* Hummm... WAIT_ABANDONDED is not an error code. It is + * a return specific to the Win32 WAIT functions that + * indicates that a thread exited while holding a + * mutex. Should consider triggering an assert + * to detect the condition... + */ + status = APR_FROM_OS_ERROR(WAIT_TIMEOUT); + } + else + status = apr_get_os_error(); + } + } + if (status != APR_SUCCESS) + break; + + bytes_to_send -= xmitbytes; + curoff += xmitbytes; + *len += xmitbytes; + /* Adjust len for any headers/trailers sent */ + if (ptfb) { + *len += (ptfb->HeadLength + ptfb->TailLength); + memset(&tfb, '\0', sizeof (tfb)); + ptfb = NULL; + } + } + + if (status == APR_SUCCESS) { + if (sendv_trailers) { + rv = apr_socket_sendv(sock, hdtr->trailers, hdtr->numtrailers, &nbytes); + if (rv != APR_SUCCESS) + return rv; + *len += nbytes; + } + + + /* Mark the socket as disconnected, but do not close it. + * Note: The application must have stored the socket prior to making + * the call to apr_socket_sendfile in order to either reuse it + * or close it. + */ + if (disconnected) { + sock->disconnected = 1; + sock->socketdes = INVALID_SOCKET; + } + } + + return status; +} + +#endif + diff --git a/network_io/win32/sockets.c b/network_io/win32/sockets.c new file mode 100644 index 0000000..5013bba --- /dev/null +++ b/network_io/win32/sockets.c @@ -0,0 +1,559 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "apr_arch_networkio.h" +#include "apr_network_io.h" +#include "apr_general.h" +#include "apr_lib.h" +#include "apr_portable.h" +#include "apr_strings.h" +#include <string.h> +#include "apr_arch_inherit.h" +#include "apr_arch_misc.h" + +/* Borrow the definition of SOMAXCONN_HINT() from Windows SDK 8, + * in case the SDK we are building against doesn't have it. + */ +#ifndef SOMAXCONN_HINT +#define SOMAXCONN_HINT(b) (-(b)) +#endif + +static char generic_inaddr_any[16] = {0}; /* big enough for IPv4 or IPv6 */ + +static apr_status_t socket_cleanup(void *sock) +{ + apr_socket_t *thesocket = sock; + + if (thesocket->socketdes != INVALID_SOCKET) { + if (closesocket(thesocket->socketdes) == SOCKET_ERROR) { + return apr_get_netos_error(); + } + thesocket->socketdes = INVALID_SOCKET; + } +#if APR_HAS_SENDFILE + if (thesocket->overlapped) { + CloseHandle(thesocket->overlapped->hEvent); + thesocket->overlapped = NULL; + } +#endif + return APR_SUCCESS; +} + +static void set_socket_vars(apr_socket_t *sock, int family, int type, int protocol) +{ + sock->type = type; + sock->protocol = protocol; + apr_sockaddr_vars_set(sock->local_addr, family, 0); + apr_sockaddr_vars_set(sock->remote_addr, family, 0); +#if APR_HAVE_IPV6 + /* hard-coded behavior for older Windows IPv6 */ + if (apr_os_level < APR_WIN_VISTA && family == AF_INET6) { + apr_set_option(sock, APR_IPV6_V6ONLY, 1); + } +#endif +} +static void alloc_socket(apr_socket_t **new, apr_pool_t *p) +{ + *new = (apr_socket_t *)apr_pcalloc(p, sizeof(apr_socket_t)); + (*new)->pool = p; + (*new)->local_addr = (apr_sockaddr_t *)apr_pcalloc((*new)->pool, + sizeof(apr_sockaddr_t)); + (*new)->local_addr->pool = p; + + (*new)->remote_addr = (apr_sockaddr_t *)apr_pcalloc((*new)->pool, + sizeof(apr_sockaddr_t)); + (*new)->remote_addr->pool = p; + (*new)->remote_addr_unknown = 1; + + /* Create a pollset with room for one descriptor. */ + /* ### check return codes */ + (void) apr_pollset_create(&(*new)->pollset, 1, p, 0); +} + +APR_DECLARE(apr_status_t) apr_socket_protocol_get(apr_socket_t *sock, + int *protocol) +{ + *protocol = sock->protocol; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_socket_create(apr_socket_t **new, int family, + int type, int protocol, + apr_pool_t *cont) +{ +#if APR_HAVE_IPV6 + int downgrade = (family == AF_UNSPEC); +#endif + + if (family == AF_UNSPEC) { +#if APR_HAVE_IPV6 + family = AF_INET6; +#else + family = AF_INET; +#endif + } + + alloc_socket(new, cont); + + /* For right now, we are not using socket groups. We may later. + * No flags to use when creating a socket, so use 0 for that parameter as well. + */ + (*new)->socketdes = socket(family, type, protocol); +#if APR_HAVE_IPV6 + if ((*new)->socketdes == INVALID_SOCKET && downgrade) { + family = AF_INET; + (*new)->socketdes = socket(family, type, protocol); + } +#endif + + if ((*new)->socketdes == INVALID_SOCKET) { + return apr_get_netos_error(); + } + +#ifdef WIN32 + /* Socket handles are never truly inheritable, there are too many + * bugs associated. WSADuplicateSocket will copy them, but for our + * purposes, always transform the socket() created as a non-inherited + * handle + */ +#if APR_HAS_UNICODE_FS && !defined(_WIN32_WCE) + IF_WIN_OS_IS_UNICODE { + /* A different approach. Many users report errors such as + * (32538)An operation was attempted on something that is not + * a socket. : Parent: WSADuplicateSocket failed... + * + * This appears that the duplicated handle is no longer recognized + * as a socket handle. SetHandleInformation should overcome that + * problem by not altering the handle identifier. But this won't + * work on 9x - it's unsupported. + */ + SetHandleInformation((HANDLE) (*new)->socketdes, + HANDLE_FLAG_INHERIT, 0); + } +#if APR_HAS_ANSI_FS + /* only if APR_HAS_ANSI_FS && APR_HAS_UNICODE_FS */ + ELSE_WIN_OS_IS_ANSI +#endif +#endif +#if APR_HAS_ANSI_FS || defined(_WIN32_WCE) + { + HANDLE hProcess = GetCurrentProcess(); + HANDLE dup; + if (DuplicateHandle(hProcess, (HANDLE) (*new)->socketdes, hProcess, + &dup, 0, FALSE, DUPLICATE_SAME_ACCESS)) { + closesocket((*new)->socketdes); + (*new)->socketdes = (SOCKET) dup; + } + } +#endif + +#endif /* def WIN32 */ + + set_socket_vars(*new, family, type, protocol); + + (*new)->timeout = -1; + (*new)->disconnected = 0; + + apr_pool_cleanup_register((*new)->pool, (void *)(*new), + socket_cleanup, apr_pool_cleanup_null); + + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_socket_shutdown(apr_socket_t *thesocket, + apr_shutdown_how_e how) +{ + int winhow = 0; + +#ifdef SD_RECEIVE + switch (how) { + case APR_SHUTDOWN_READ: { + winhow = SD_RECEIVE; + break; + } + case APR_SHUTDOWN_WRITE: { + winhow = SD_SEND; + break; + } + case APR_SHUTDOWN_READWRITE: { + winhow = SD_BOTH; + break; + } + default: + return APR_BADARG; + } +#endif + if (shutdown(thesocket->socketdes, winhow) == 0) { + return APR_SUCCESS; + } + else { + return apr_get_netos_error(); + } +} + +APR_DECLARE(apr_status_t) apr_socket_close(apr_socket_t *thesocket) +{ + apr_pool_cleanup_kill(thesocket->pool, thesocket, socket_cleanup); + return socket_cleanup(thesocket); +} + +APR_DECLARE(apr_status_t) apr_socket_bind(apr_socket_t *sock, + apr_sockaddr_t *sa) +{ + if (bind(sock->socketdes, + (struct sockaddr *)&sa->sa, + sa->salen) == -1) { + return apr_get_netos_error(); + } + else { + sock->local_addr = sa; + if (sock->local_addr->sa.sin.sin_port == 0) { + sock->local_port_unknown = 1; /* ephemeral port */ + } + return APR_SUCCESS; + } +} + +APR_DECLARE(apr_status_t) apr_socket_listen(apr_socket_t *sock, + apr_int32_t backlog) +{ + int backlog_val; + + if (apr_os_level >= APR_WIN_8) { + /* Starting from Windows 8, listen() accepts a special SOMAXCONN_HINT() + * arg that allows setting the listen backlog value to a larger + * value than the predefined Winsock 2 limit (several hundred). + * https://blogs.msdn.microsoft.com/winsdk/2015/06/01/winsocks-listen-backlog-offers-more-flexibility-in-windows-8/ + */ + backlog_val = SOMAXCONN_HINT(backlog); + } + else { + backlog_val = backlog; + } + + if (listen(sock->socketdes, backlog_val) == SOCKET_ERROR) + return apr_get_netos_error(); + else + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_socket_accept(apr_socket_t **new, + apr_socket_t *sock, apr_pool_t *p) +{ + SOCKET s; +#if APR_HAVE_IPV6 + struct sockaddr_storage sa; +#else + struct sockaddr sa; +#endif + int salen = sizeof(sock->remote_addr->sa); + + /* Don't allocate the memory until after we call accept. This allows + us to work with nonblocking sockets. */ + s = accept(sock->socketdes, (struct sockaddr *)&sa, &salen); + if (s == INVALID_SOCKET) { + return apr_get_netos_error(); + } + + alloc_socket(new, p); + set_socket_vars(*new, sock->local_addr->sa.sin.sin_family, SOCK_STREAM, + sock->protocol); + + (*new)->timeout = -1; + (*new)->disconnected = 0; + + (*new)->socketdes = s; + /* XXX next line looks bogus w.r.t. AF_INET6 support */ + (*new)->remote_addr->salen = sizeof((*new)->remote_addr->sa); + memcpy (&(*new)->remote_addr->sa, &sa, salen); + *(*new)->local_addr = *sock->local_addr; + (*new)->remote_addr_unknown = 0; + + /* The above assignment just overwrote the pool entry. Setting the local_addr + pool for the accepted socket back to what it should be. Otherwise all + allocations for this socket will come from a server pool that is not + freed until the process goes down.*/ + (*new)->local_addr->pool = p; + + /* fix up any pointers which are no longer valid */ + if (sock->local_addr->sa.sin.sin_family == AF_INET) { + (*new)->local_addr->ipaddr_ptr = &(*new)->local_addr->sa.sin.sin_addr; + } +#if APR_HAVE_IPV6 + else if (sock->local_addr->sa.sin.sin_family == AF_INET6) { + (*new)->local_addr->ipaddr_ptr = &(*new)->local_addr->sa.sin6.sin6_addr; + } +#endif + (*new)->remote_addr->port = ntohs((*new)->remote_addr->sa.sin.sin_port); + if (sock->local_port_unknown) { + /* not likely for a listening socket, but theoretically possible :) */ + (*new)->local_port_unknown = 1; + } + +#if APR_TCP_NODELAY_INHERITED + if (apr_is_option_set(sock, APR_TCP_NODELAY) == 1) { + apr_set_option(*new, APR_TCP_NODELAY, 1); + } +#endif /* TCP_NODELAY_INHERITED */ +#if APR_O_NONBLOCK_INHERITED + if (apr_is_option_set(sock, APR_SO_NONBLOCK) == 1) { + apr_set_option(*new, APR_SO_NONBLOCK, 1); + } +#endif /* APR_O_NONBLOCK_INHERITED */ + + if (sock->local_interface_unknown || + !memcmp(sock->local_addr->ipaddr_ptr, + generic_inaddr_any, + sock->local_addr->ipaddr_len)) { + /* If the interface address inside the listening socket's local_addr wasn't + * up-to-date, we don't know local interface of the connected socket either. + * + * If the listening socket was not bound to a specific interface, we + * don't know the local_addr of the connected socket. + */ + (*new)->local_interface_unknown = 1; + } + + apr_pool_cleanup_register((*new)->pool, (void *)(*new), + socket_cleanup, apr_pool_cleanup_null); + return APR_SUCCESS; +} + +static apr_status_t wait_for_connect(apr_socket_t *sock) +{ + int rc; + struct timeval tv, *tvptr; + fd_set wfdset, efdset; + + /* wait for the connect to complete or timeout */ + FD_ZERO(&wfdset); + FD_SET(sock->socketdes, &wfdset); + FD_ZERO(&efdset); + FD_SET(sock->socketdes, &efdset); + + if (sock->timeout < 0) { + tvptr = NULL; + } + else { + /* casts for winsock/timeval definition */ + tv.tv_sec = (long)apr_time_sec(sock->timeout); + tv.tv_usec = (int)apr_time_usec(sock->timeout); + tvptr = &tv; + } + rc = select(FD_SETSIZE+1, NULL, &wfdset, &efdset, tvptr); + if (rc == SOCKET_ERROR) { + return apr_get_netos_error(); + } + else if (!rc) { + return APR_FROM_OS_ERROR(WSAETIMEDOUT); + } + /* Evaluate the efdset */ + if (FD_ISSET(sock->socketdes, &efdset)) { + /* The connect failed. */ + int rclen = sizeof(rc); + if (getsockopt(sock->socketdes, SOL_SOCKET, SO_ERROR, (char*) &rc, &rclen)) { + return apr_get_netos_error(); + } + return APR_FROM_OS_ERROR(rc); + } + + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_socket_connect(apr_socket_t *sock, + apr_sockaddr_t *sa) +{ + apr_status_t rv; + + if ((sock->socketdes == INVALID_SOCKET) || (!sock->local_addr)) { + return APR_ENOTSOCK; + } + + if (connect(sock->socketdes, (const struct sockaddr *)&sa->sa.sin, + sa->salen) == SOCKET_ERROR) { + rv = apr_get_netos_error(); + } + else { + rv = APR_SUCCESS; + } + + if (rv == APR_FROM_OS_ERROR(WSAEWOULDBLOCK)) { + if (sock->timeout == 0) { + /* Tell the app that the connect is in progress... + * Gotta play some games here. connect on Unix will return + * EINPROGRESS under the same circumstances that Windows + * returns WSAEWOULDBLOCK. Do some adhoc canonicalization... + */ + rv = APR_FROM_OS_ERROR(WSAEINPROGRESS); + } + else { + rv = wait_for_connect(sock); + if (rv != APR_SUCCESS) { + return rv; + } + } + } + + if (memcmp(sa->ipaddr_ptr, generic_inaddr_any, sa->ipaddr_len)) { + /* A real remote address was passed in. If the unspecified + * address was used, the actual remote addr will have to be + * determined using getpeername() if required. */ + sock->remote_addr_unknown = 0; + + /* Copy the address structure details in. */ + sock->remote_addr = sa; + } + + if (sock->local_addr->sa.sin.sin_port == 0) { + /* connect() got us an ephemeral port */ + sock->local_port_unknown = 1; + } + if (!memcmp(sock->local_addr->ipaddr_ptr, + generic_inaddr_any, + sock->local_addr->ipaddr_len)) { + /* not bound to specific local interface; connect() had to assign + * one for the socket + */ + sock->local_interface_unknown = 1; + } + + if (rv != APR_SUCCESS && rv != APR_FROM_OS_ERROR(WSAEISCONN)) { + return rv; + } + + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_socket_type_get(apr_socket_t *sock, int *type) +{ + *type = sock->type; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_socket_data_get(void **data, const char *key, + apr_socket_t *sock) +{ + sock_userdata_t *cur = sock->userdata; + + *data = NULL; + + while (cur) { + if (!strcmp(cur->key, key)) { + *data = cur->data; + break; + } + cur = cur->next; + } + + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_socket_data_set(apr_socket_t *sock, void *data, + const char *key, + apr_status_t (*cleanup)(void *)) +{ + sock_userdata_t *new = apr_palloc(sock->pool, sizeof(sock_userdata_t)); + + new->key = apr_pstrdup(sock->pool, key); + new->data = data; + new->next = sock->userdata; + sock->userdata = new; + + if (cleanup) { + apr_pool_cleanup_register(sock->pool, data, cleanup, cleanup); + } + + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_os_sock_get(apr_os_sock_t *thesock, + apr_socket_t *sock) +{ + *thesock = sock->socketdes; + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_os_sock_make(apr_socket_t **apr_sock, + apr_os_sock_info_t *os_sock_info, + apr_pool_t *cont) +{ + alloc_socket(apr_sock, cont); + set_socket_vars(*apr_sock, os_sock_info->family, os_sock_info->type, os_sock_info->protocol); + (*apr_sock)->timeout = -1; + (*apr_sock)->disconnected = 0; + (*apr_sock)->socketdes = *os_sock_info->os_sock; + if (os_sock_info->local) { + memcpy(&(*apr_sock)->local_addr->sa.sin, + os_sock_info->local, + (*apr_sock)->local_addr->salen); + (*apr_sock)->local_addr->pool = cont; + /* XXX IPv6 - this assumes sin_port and sin6_port at same offset */ + (*apr_sock)->local_addr->port = ntohs((*apr_sock)->local_addr->sa.sin.sin_port); + } + else { + (*apr_sock)->local_port_unknown = (*apr_sock)->local_interface_unknown = 1; + } + if (os_sock_info->remote) { + memcpy(&(*apr_sock)->remote_addr->sa.sin, + os_sock_info->remote, + (*apr_sock)->remote_addr->salen); + (*apr_sock)->remote_addr->pool = cont; + /* XXX IPv6 - this assumes sin_port and sin6_port at same offset */ + (*apr_sock)->remote_addr->port = ntohs((*apr_sock)->remote_addr->sa.sin.sin_port); + (*apr_sock)->remote_addr_unknown = 0; + } + + apr_pool_cleanup_register((*apr_sock)->pool, (void *)(*apr_sock), + socket_cleanup, apr_pool_cleanup_null); + + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_os_sock_put(apr_socket_t **sock, + apr_os_sock_t *thesock, + apr_pool_t *cont) +{ + if ((*sock) == NULL) { + alloc_socket(sock, cont); + /* XXX figure out the actual socket type here */ + /* *or* just decide that apr_os_sock_put() has to be told the family and type */ + set_socket_vars(*sock, AF_INET, SOCK_STREAM, 0); + (*sock)->timeout = -1; + (*sock)->disconnected = 0; + } + (*sock)->local_port_unknown = (*sock)->local_interface_unknown = 1; + (*sock)->remote_addr_unknown = 1; + (*sock)->socketdes = *thesock; + return APR_SUCCESS; +} + + +/* Sockets cannot be inherited through the standard sockets + * inheritence. WSADuplicateSocket must be used. + * This is not trivial to implement. + */ + +APR_DECLARE(apr_status_t) apr_socket_inherit_set(apr_socket_t *socket) +{ + return APR_ENOTIMPL; +} + +APR_DECLARE(apr_status_t) apr_socket_inherit_unset(apr_socket_t *socket) +{ + return APR_ENOTIMPL; +} + +APR_POOL_IMPLEMENT_ACCESSOR(socket); diff --git a/network_io/win32/sockopt.c b/network_io/win32/sockopt.c new file mode 100644 index 0000000..463eeeb --- /dev/null +++ b/network_io/win32/sockopt.c @@ -0,0 +1,302 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "apr_arch_networkio.h" +#include "apr_arch_misc.h" /* apr_os_level */ +#include "apr_network_io.h" +#include "apr_general.h" +#include "apr_strings.h" +#include <string.h> + +/* IPV6_V6ONLY is missing from pre-Windows 2008 SDK as well as MinGW + * (at least up through 1.0.16). + * Runtime support is a separate issue. + */ +#ifndef IPV6_V6ONLY +#define IPV6_V6ONLY 27 +#endif + +static apr_status_t soblock(SOCKET sd) +{ + u_long zero = 0; + + if (ioctlsocket(sd, FIONBIO, &zero) == SOCKET_ERROR) { + return apr_get_netos_error(); + } + return APR_SUCCESS; +} + +static apr_status_t sononblock(SOCKET sd) +{ + u_long one = 1; + + if (ioctlsocket(sd, FIONBIO, &one) == SOCKET_ERROR) { + return apr_get_netos_error(); + } + return APR_SUCCESS; +} + + +APR_DECLARE(apr_status_t) apr_socket_timeout_set(apr_socket_t *sock, apr_interval_time_t t) +{ + apr_status_t stat; + + if (t == 0) { + /* Set the socket non-blocking if it was previously blocking */ + if (sock->timeout != 0) { + if ((stat = sononblock(sock->socketdes)) != APR_SUCCESS) + return stat; + } + } + else if (t > 0) { + /* Set the socket to blocking if it was previously non-blocking */ + if (sock->timeout == 0 || apr_is_option_set(sock, APR_SO_NONBLOCK)) { + if ((stat = soblock(sock->socketdes)) != APR_SUCCESS) + return stat; + apr_set_option(sock, APR_SO_NONBLOCK, 0); + } + /* Reset socket timeouts if the new timeout differs from the old timeout */ + if (sock->timeout != t) + { + /* Win32 timeouts are in msec, represented as int */ + sock->timeout_ms = (int)apr_time_as_msec(t); + setsockopt(sock->socketdes, SOL_SOCKET, SO_RCVTIMEO, + (char *) &sock->timeout_ms, + sizeof(sock->timeout_ms)); + setsockopt(sock->socketdes, SOL_SOCKET, SO_SNDTIMEO, + (char *) &sock->timeout_ms, + sizeof(sock->timeout_ms)); + } + } + else if (t < 0) { + int zero = 0; + /* Set the socket to blocking with infinite timeouts */ + if ((stat = soblock(sock->socketdes)) != APR_SUCCESS) + return stat; + setsockopt(sock->socketdes, SOL_SOCKET, SO_RCVTIMEO, + (char *) &zero, sizeof(zero)); + setsockopt(sock->socketdes, SOL_SOCKET, SO_SNDTIMEO, + (char *) &zero, sizeof(zero)); + } + sock->timeout = t; + return APR_SUCCESS; +} + + +APR_DECLARE(apr_status_t) apr_socket_opt_set(apr_socket_t *sock, + apr_int32_t opt, apr_int32_t on) +{ + int one; + apr_status_t stat; + + one = on ? 1 : 0; + + switch (opt) { + case APR_SO_KEEPALIVE: + if (on != apr_is_option_set(sock, APR_SO_KEEPALIVE)) { + if (setsockopt(sock->socketdes, SOL_SOCKET, SO_KEEPALIVE, + (void *)&one, sizeof(int)) == -1) { + return apr_get_netos_error(); + } + apr_set_option(sock, APR_SO_KEEPALIVE, on); + } + break; + case APR_SO_DEBUG: + if (on != apr_is_option_set(sock, APR_SO_DEBUG)) { + if (setsockopt(sock->socketdes, SOL_SOCKET, SO_DEBUG, + (void *)&one, sizeof(int)) == -1) { + return apr_get_netos_error(); + } + apr_set_option(sock, APR_SO_DEBUG, on); + } + break; + case APR_SO_SNDBUF: + if (setsockopt(sock->socketdes, SOL_SOCKET, SO_SNDBUF, + (void *)&on, sizeof(int)) == -1) { + return apr_get_netos_error(); + } + break; + case APR_SO_RCVBUF: + if (setsockopt(sock->socketdes, SOL_SOCKET, SO_RCVBUF, + (void *)&on, sizeof(int)) == -1) { + return apr_get_netos_error(); + } + break; + case APR_SO_BROADCAST: + if (on != apr_is_option_set(sock, APR_SO_BROADCAST)) { + if (setsockopt(sock->socketdes, SOL_SOCKET, SO_BROADCAST, + (void *)&one, sizeof(int)) == -1) { + return apr_get_netos_error(); + } + apr_set_option(sock, APR_SO_BROADCAST, on); + } + break; + case APR_SO_REUSEADDR: + if (on != apr_is_option_set(sock, APR_SO_REUSEADDR)) { + if (setsockopt(sock->socketdes, SOL_SOCKET, SO_REUSEADDR, + (void *)&one, sizeof(int)) == -1) { + return apr_get_netos_error(); + } + apr_set_option(sock, APR_SO_REUSEADDR, on); + } + break; + case APR_SO_NONBLOCK: + if (apr_is_option_set(sock, APR_SO_NONBLOCK) != on) { + if (on) { + if ((stat = sononblock(sock->socketdes)) != APR_SUCCESS) + return stat; + } + else { + if ((stat = soblock(sock->socketdes)) != APR_SUCCESS) + return stat; + } + apr_set_option(sock, APR_SO_NONBLOCK, on); + } + break; + case APR_SO_LINGER: + { + if (apr_is_option_set(sock, APR_SO_LINGER) != on) { + struct linger li; + li.l_onoff = on; + li.l_linger = APR_MAX_SECS_TO_LINGER; + if (setsockopt(sock->socketdes, SOL_SOCKET, SO_LINGER, + (char *) &li, sizeof(struct linger)) == -1) { + return apr_get_netos_error(); + } + apr_set_option(sock, APR_SO_LINGER, on); + } + break; + } + case APR_TCP_DEFER_ACCEPT: +#if defined(TCP_DEFER_ACCEPT) + if (apr_is_option_set(sock, APR_TCP_DEFER_ACCEPT) != on) { + int optlevel = IPPROTO_TCP; + int optname = TCP_DEFER_ACCEPT; + + if (setsockopt(sock->socketdes, optlevel, optname, + (void *)&on, sizeof(int)) == -1) { + return apr_get_netos_error(); + } + apr_set_option(sock, APR_TCP_DEFER_ACCEPT, on); + } +#else + return APR_ENOTIMPL; +#endif + case APR_TCP_NODELAY: + if (apr_is_option_set(sock, APR_TCP_NODELAY) != on) { + int optlevel = IPPROTO_TCP; + int optname = TCP_NODELAY; + +#if APR_HAVE_SCTP + if (sock->protocol == IPPROTO_SCTP) { + optlevel = IPPROTO_SCTP; + optname = SCTP_NODELAY; + } +#endif + if (setsockopt(sock->socketdes, optlevel, optname, + (void *)&on, sizeof(int)) == -1) { + return apr_get_netos_error(); + } + apr_set_option(sock, APR_TCP_NODELAY, on); + } + break; + case APR_IPV6_V6ONLY: +#if APR_HAVE_IPV6 + if (apr_os_level < APR_WIN_VISTA && + sock->local_addr->family == AF_INET6) { + /* apr_set_option() called at socket creation */ + if (on) { + return APR_SUCCESS; + } + else { + return APR_ENOTIMPL; + } + } + /* we don't know the initial setting of this option, + * so don't check sock->options since that optimization + * won't work + */ + if (setsockopt(sock->socketdes, IPPROTO_IPV6, IPV6_V6ONLY, + (void *)&on, sizeof(int)) == -1) { + return apr_get_netos_error(); + } + apr_set_option(sock, APR_IPV6_V6ONLY, on); +#else + return APR_ENOTIMPL; +#endif + break; + default: + return APR_EINVAL; + break; + } + return APR_SUCCESS; +} + + +APR_DECLARE(apr_status_t) apr_socket_timeout_get(apr_socket_t *sock, apr_interval_time_t *t) +{ + *t = sock->timeout; + return APR_SUCCESS; +} + + +APR_DECLARE(apr_status_t) apr_socket_opt_get(apr_socket_t *sock, + apr_int32_t opt, apr_int32_t *on) +{ + switch (opt) { + case APR_SO_DISCONNECTED: + *on = sock->disconnected; + break; + case APR_SO_KEEPALIVE: + case APR_SO_DEBUG: + case APR_SO_REUSEADDR: + case APR_SO_NONBLOCK: + case APR_SO_LINGER: + default: + *on = apr_is_option_set(sock, opt); + break; + } + return APR_SUCCESS; +} + + +APR_DECLARE(apr_status_t) apr_socket_atmark(apr_socket_t *sock, int *atmark) +{ + u_long oobmark; + + if (ioctlsocket(sock->socketdes, SIOCATMARK, (void*) &oobmark) < 0) + return apr_get_netos_error(); + + *atmark = (oobmark != 0); + + return APR_SUCCESS; +} + + +APR_DECLARE(apr_status_t) apr_gethostname(char *buf, int len, + apr_pool_t *cont) +{ + if (gethostname(buf, len) == -1) { + buf[0] = '\0'; + return apr_get_netos_error(); + } + else if (!memchr(buf, '\0', len)) { /* buffer too small */ + buf[0] = '\0'; + return APR_ENAMETOOLONG; + } + return APR_SUCCESS; +} + |