summaryrefslogtreecommitdiff
path: root/vio
diff options
context:
space:
mode:
Diffstat (limited to 'vio')
-rw-r--r--vio/CMakeLists.txt2
-rw-r--r--vio/vio.c249
-rw-r--r--vio/vio_priv.h26
-rw-r--r--vio/viopipe.c145
-rw-r--r--vio/vioshm.c217
-rw-r--r--vio/viosocket.c1125
-rw-r--r--vio/viossl.c275
-rw-r--r--vio/viosslfactories.c70
8 files changed, 1366 insertions, 743 deletions
diff --git a/vio/CMakeLists.txt b/vio/CMakeLists.txt
index b83518cd749..2fb82ef9dd2 100644
--- a/vio/CMakeLists.txt
+++ b/vio/CMakeLists.txt
@@ -17,7 +17,7 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include
${SSL_INCLUDE_DIRS})
ADD_DEFINITIONS(${SSL_DEFINES})
-SET(VIO_SOURCES vio.c viosocket.c viossl.c viosslfactories.c)
+SET(VIO_SOURCES vio.c viosocket.c viossl.c viopipe.c vioshm.c viosslfactories.c)
ADD_CONVENIENCE_LIBRARY(vio ${VIO_SOURCES})
TARGET_LINK_LIBRARIES(vio ${LIBSOCKET})
diff --git a/vio/vio.c b/vio/vio.c
index bdb21077f44..97e3d49be21 100644
--- a/vio/vio.c
+++ b/vio/vio.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
Copyright (c) 2012, Monty Program Ab
This program is free software; you can redistribute it and/or modify
@@ -23,24 +23,26 @@
#include "vio_priv.h"
-#if defined(__WIN__) || defined(HAVE_SMEM)
+#ifdef _WIN32
/**
- Stub poll_read method that defaults to indicate that there
- is data to read.
+ Stub io_wait method that defaults to indicate that
+ requested I/O event is ready.
Used for named pipe and shared memory VIO types.
@param vio Unused.
+ @param event Unused.
@param timeout Unused.
- @retval FALSE There is data to read.
+ @retval 1 The requested I/O event has occurred.
*/
-static my_bool no_poll_read(Vio *vio __attribute__((unused)),
- uint timeout __attribute__((unused)))
+static int no_io_wait(Vio *vio __attribute__((unused)),
+ enum enum_vio_io_event event __attribute__((unused)),
+ int timeout __attribute__((unused)))
{
- return FALSE;
+ return 1;
}
#endif
@@ -73,8 +75,8 @@ int vio_pipe_shutdown(Vio *vio, int how)
* Helper to fill most of the Vio* with defaults.
*/
-static void vio_init(Vio* vio, enum enum_vio_type type,
- my_socket sd, HANDLE hPipe, uint flags)
+static void vio_init(Vio *vio, enum enum_vio_type type,
+ my_socket sd, uint flags)
{
DBUG_ENTER("vio_init");
DBUG_PRINT("enter", ("type: %d sd: %d flags: %d", type, sd, flags));
@@ -82,11 +84,12 @@ static void vio_init(Vio* vio, enum enum_vio_type type,
#ifndef HAVE_VIO_READ_BUFF
flags&= ~VIO_BUFFERED_READ;
#endif
- bzero((char*) vio, sizeof(*vio));
- vio->type = type;
- vio->sd = sd;
- vio->hPipe = hPipe;
+ memset(vio, 0, sizeof(*vio));
+ vio->type= type;
+ vio->mysql_socket= MYSQL_INVALID_SOCKET;
+ mysql_socket_setfd(&vio->mysql_socket, sd);
vio->localhost= flags & VIO_LOCALHOST;
+ vio->read_timeout= vio->write_timeout= -1;
if ((flags & VIO_BUFFERED_READ) &&
!(vio->read_buffer= (char*)my_malloc(VIO_READ_BUFFER_SIZE, MYF(MY_WME))))
flags&= ~VIO_BUFFERED_READ;
@@ -100,26 +103,19 @@ static void vio_init(Vio* vio, enum enum_vio_type type,
vio->fastsend =vio_fastsend;
vio->viokeepalive =vio_keepalive;
vio->should_retry =vio_should_retry;
- vio->was_interrupted=vio_was_interrupted;
+ vio->was_timeout =vio_was_timeout;
vio->vioclose =vio_close_pipe;
vio->peer_addr =vio_peer_addr;
vio->vioblocking =vio_blocking;
vio->is_blocking =vio_is_blocking;
-
- vio->poll_read =no_poll_read;
+ vio->io_wait =no_io_wait;
vio->is_connected =vio_is_connected_pipe;
vio->has_data =has_no_data;
vio->shutdown =vio_pipe_shutdown;
-
- vio->timeout=vio_win32_timeout;
- /* Set default timeout */
- vio->read_timeout_ms= INFINITE;
- vio->write_timeout_ms= INFINITE;
- vio->pipe_overlapped.hEvent= CreateEvent(NULL, TRUE, FALSE, NULL);
DBUG_VOID_RETURN;
}
#endif
-#ifdef HAVE_SMEM
+#ifdef HAVE_SMEM
if (type == VIO_TYPE_SHARED_MEMORY)
{
vio->viodelete =vio_delete;
@@ -129,26 +125,19 @@ static void vio_init(Vio* vio, enum enum_vio_type type,
vio->fastsend =vio_fastsend;
vio->viokeepalive =vio_keepalive;
vio->should_retry =vio_should_retry;
- vio->was_interrupted=vio_was_interrupted;
+ vio->was_timeout =vio_was_timeout;
vio->vioclose =vio_close_shared_memory;
vio->peer_addr =vio_peer_addr;
vio->vioblocking =vio_blocking;
vio->is_blocking =vio_is_blocking;
-
- vio->poll_read =no_poll_read;
+ vio->io_wait =no_io_wait;
vio->is_connected =vio_is_connected_shared_memory;
vio->has_data =vio_shared_memory_has_data;
vio->shutdown =vio_shared_memory_shutdown;
-
- /* Currently, shared memory is on Windows only, hence the below is ok*/
- vio->timeout= vio_win32_timeout;
- /* Set default timeout */
- vio->read_timeout_ms= INFINITE;
- vio->write_timeout_ms= INFINITE;
DBUG_VOID_RETURN;
}
-#endif
-#ifdef HAVE_OPENSSL
+#endif
+#ifdef HAVE_OPENSSL
if (type == VIO_TYPE_SSL)
{
vio->viodelete =vio_ssl_delete;
@@ -158,16 +147,16 @@ static void vio_init(Vio* vio, enum enum_vio_type type,
vio->fastsend =vio_fastsend;
vio->viokeepalive =vio_keepalive;
vio->should_retry =vio_should_retry;
- vio->was_interrupted=vio_was_interrupted;
+ vio->was_timeout =vio_was_timeout;
vio->vioclose =vio_ssl_close;
vio->peer_addr =vio_peer_addr;
vio->vioblocking =vio_ssl_blocking;
vio->is_blocking =vio_is_blocking;
- vio->timeout =vio_timeout;
- vio->poll_read =vio_poll_read;
+ vio->io_wait =vio_io_wait;
vio->is_connected =vio_is_connected;
vio->has_data =vio_ssl_has_data;
vio->shutdown =vio_socket_shutdown;
+ vio->timeout =vio_socket_timeout;
DBUG_VOID_RETURN;
}
#endif /* HAVE_OPENSSL */
@@ -178,77 +167,110 @@ static void vio_init(Vio* vio, enum enum_vio_type type,
vio->fastsend =vio_fastsend;
vio->viokeepalive =vio_keepalive;
vio->should_retry =vio_should_retry;
- vio->was_interrupted =vio_was_interrupted;
+ vio->was_timeout =vio_was_timeout;
vio->vioclose =vio_close;
vio->peer_addr =vio_peer_addr;
- vio->vioblocking =vio_blocking;
- vio->is_blocking =vio_is_blocking;
- vio->timeout =vio_timeout;
- vio->poll_read =vio_poll_read;
+ vio->vioblocking =vio_blocking;
+ vio->is_blocking =vio_is_blocking;
+ vio->io_wait =vio_io_wait;
vio->is_connected =vio_is_connected;
vio->shutdown =vio_socket_shutdown;
- vio->has_data= (flags & VIO_BUFFERED_READ) ?
- vio_buff_has_data : has_no_data;
+ vio->timeout =vio_socket_timeout;
+ vio->has_data = ((flags & VIO_BUFFERED_READ) ?
+ vio_buff_has_data : has_no_data);
DBUG_VOID_RETURN;
}
-/* Reset initialized VIO to use with another transport type */
+/**
+ Reinitialize an existing Vio object.
+
+ @remark Used to rebind an initialized socket-based Vio object
+ to another socket-based transport type. For example,
+ rebind a TCP/IP transport to SSL.
+
+ @param vio A VIO object.
+ @param type A socket-based transport type.
+ @param sd The socket.
+ @param ssl An optional SSL structure.
+ @param flags Flags passed to vio_init.
-void vio_reset(Vio* vio, enum enum_vio_type type,
- my_socket sd, HANDLE hPipe, uint flags)
+ @return Return value is zero on success.
+*/
+
+my_bool vio_reset(Vio* vio, enum enum_vio_type type,
+ my_socket sd, void *ssl __attribute__((unused)), uint flags)
{
+ int ret= FALSE;
+ Vio old_vio= *vio;
+ DBUG_ENTER("vio_reset");
+
+ /* The only supported rebind is from a socket-based transport type. */
+ DBUG_ASSERT(vio->type == VIO_TYPE_TCPIP || vio->type == VIO_TYPE_SOCKET);
+
+ /*
+ Will be reinitialized depending on the flags.
+ Nonetheless, already buffered inside the SSL layer.
+ */
my_free(vio->read_buffer);
- vio_init(vio, type, sd, hPipe, flags);
+
+ vio_init(vio, type, sd, flags);
+
+ /* Preserve perfschema info for this connection */
+ vio->mysql_socket.m_psi= old_vio.mysql_socket.m_psi;
+
+#ifdef HAVE_OPENSSL
+ vio->ssl_arg= ssl;
+#endif
+
+ /*
+ Propagate the timeout values. Necessary to also propagate
+ the underlying proprieties associated with the timeout,
+ such as the socket blocking mode.
+ */
+ if (old_vio.read_timeout >= 0)
+ ret|= vio_timeout(vio, 0, old_vio.read_timeout);
+
+ if (old_vio.write_timeout >= 0)
+ ret|= vio_timeout(vio, 1, old_vio.write_timeout);
+
+ DBUG_RETURN(MY_TEST(ret));
}
-/* Open the socket or TCP/IP connection and read the fnctl() status */
+/* Create a new VIO for socket or TCP/IP connection. */
-Vio *vio_new(my_socket sd, enum enum_vio_type type, uint flags)
+Vio *mysql_socket_vio_new(MYSQL_SOCKET mysql_socket, enum enum_vio_type type, uint flags)
{
Vio *vio;
- DBUG_ENTER("vio_new");
+ my_socket sd= mysql_socket_getfd(mysql_socket);
+ DBUG_ENTER("mysql_socket_vio_new");
DBUG_PRINT("enter", ("sd: %d", sd));
if ((vio = (Vio*) my_malloc(sizeof(*vio),MYF(MY_WME))))
{
- vio_init(vio, type, sd, 0, flags);
- sprintf(vio->desc,
- (vio->type == VIO_TYPE_SOCKET ? "socket (%d)" : "TCP/IP (%d)"),
- vio->sd);
-#if !defined(__WIN__)
-#if !defined(NO_FCNTL_NONBLOCK)
- /*
- We call fcntl() to set the flags and then immediately read them back
- to make sure that we and the system are in agreement on the state of
- things.
-
- An example of why we need to do this is FreeBSD (and apparently some
- other BSD-derived systems, like Mac OS X), where the system sometimes
- reports that the socket is set for non-blocking when it really will
- block.
- */
- fcntl(sd, F_SETFL, 0);
- vio->fcntl_mode= fcntl(sd, F_GETFL);
-#elif defined(HAVE_SYS_IOCTL_H) /* hpux */
- /* Non blocking sockets doesn't work good on HPUX 11.0 */
- (void) ioctl(sd,FIOSNBIO,0);
- vio->fcntl_mode &= ~O_NONBLOCK;
-#endif
-#else /* !defined(__WIN__) */
- {
- /* set to blocking mode by default */
- ulong arg=0, r;
- r = ioctlsocket(sd,FIONBIO,(void*) &arg);
- vio->fcntl_mode &= ~O_NONBLOCK;
- }
-#endif
+ vio_init(vio, type, sd, flags);
+ vio->desc= (vio->type == VIO_TYPE_SOCKET ? "socket" : "TCP/IP");
+ vio->mysql_socket= mysql_socket;
}
DBUG_RETURN(vio);
}
+/* Open the socket or TCP/IP connection and read the fnctl() status */
+
+Vio *vio_new(my_socket sd, enum enum_vio_type type, uint flags)
+{
+ Vio *vio;
+ MYSQL_SOCKET mysql_socket= MYSQL_INVALID_SOCKET;
+ DBUG_ENTER("vio_new");
+ DBUG_PRINT("enter", ("sd: %d", sd));
+
+ mysql_socket_setfd(&mysql_socket, sd);
+ vio = mysql_socket_vio_new(mysql_socket, type, flags);
-#ifdef __WIN__
+ DBUG_RETURN(vio);
+}
+
+#ifdef _WIN32
Vio *vio_new_win32pipe(HANDLE hPipe)
{
@@ -256,8 +278,16 @@ Vio *vio_new_win32pipe(HANDLE hPipe)
DBUG_ENTER("vio_new_handle");
if ((vio = (Vio*) my_malloc(sizeof(Vio),MYF(MY_WME))))
{
- vio_init(vio, VIO_TYPE_NAMEDPIPE, 0, hPipe, VIO_LOCALHOST);
- strmov(vio->desc, "named pipe");
+ vio_init(vio, VIO_TYPE_NAMEDPIPE, 0, VIO_LOCALHOST);
+ vio->desc= "named pipe";
+ /* Create an object for event notification. */
+ vio->overlapped.hEvent= CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (vio->overlapped.hEvent == NULL)
+ {
+ my_free(vio);
+ DBUG_RETURN(NULL);
+ }
+ vio->hPipe= hPipe;
}
DBUG_RETURN(vio);
}
@@ -272,7 +302,8 @@ Vio *vio_new_win32shared_memory(HANDLE handle_file_map, HANDLE handle_map,
DBUG_ENTER("vio_new_win32shared_memory");
if ((vio = (Vio*) my_malloc(sizeof(Vio),MYF(MY_WME))))
{
- vio_init(vio, VIO_TYPE_SHARED_MEMORY, 0, 0, VIO_LOCALHOST);
+ vio_init(vio, VIO_TYPE_SHARED_MEMORY, 0, VIO_LOCALHOST);
+ vio->desc= "shared memory";
vio->handle_file_map= handle_file_map;
vio->handle_map= handle_map;
vio->event_server_wrote= event_server_wrote;
@@ -282,7 +313,6 @@ Vio *vio_new_win32shared_memory(HANDLE handle_file_map, HANDLE handle_map,
vio->event_conn_closed= event_conn_closed;
vio->shared_memory_remain= 0;
vio->shared_memory_pos= handle_map;
- strmov(vio->desc, "shared memory");
}
DBUG_RETURN(vio);
}
@@ -290,6 +320,49 @@ Vio *vio_new_win32shared_memory(HANDLE handle_file_map, HANDLE handle_map,
#endif
+/**
+ Set timeout for a network send or receive operation.
+
+ @remark A non-infinite timeout causes the socket to be
+ set to non-blocking mode. On infinite timeouts,
+ the socket is set to blocking mode.
+
+ @remark A negative timeout means an infinite timeout.
+
+ @param vio A VIO object.
+ @param which Whether timeout is for send (1) or receive (0).
+ @param timeout Timeout interval in seconds.
+
+ @return FALSE on success, TRUE otherwise.
+*/
+
+int vio_timeout(Vio *vio, uint which, int timeout_sec)
+{
+ int timeout_ms;
+ my_bool old_mode;
+
+ /*
+ Vio timeouts are measured in milliseconds. Check for a possible
+ overflow. In case of overflow, set to infinite.
+ */
+ if (timeout_sec > INT_MAX/1000)
+ timeout_ms= -1;
+ else
+ timeout_ms= (int) (timeout_sec * 1000);
+
+ /* Deduce the current timeout status mode. */
+ old_mode= vio->write_timeout < 0 && vio->read_timeout < 0;
+
+ if (which)
+ vio->write_timeout= timeout_ms;
+ else
+ vio->read_timeout= timeout_ms;
+
+ /* VIO-specific timeout handling. Might change the blocking mode. */
+ return vio->timeout ? vio->timeout(vio, which, old_mode) : 0;
+}
+
+
void vio_delete(Vio* vio)
{
if (!vio)
diff --git a/vio/vio_priv.h b/vio/vio_priv.h
index 61a8ab150a9..248e1a59b23 100644
--- a/vio/vio_priv.h
+++ b/vio/vio_priv.h
@@ -1,7 +1,4 @@
-#ifndef VIO_PRIV_INCLUDED
-#define VIO_PRIV_INCLUDED
-
-/* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2003, 2011, Oracle and/or its affiliates.
Copyright (c) 2012, Monty Program Ab
This program is free software; you can redistribute it and/or modify
@@ -17,6 +14,9 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#ifndef VIO_PRIV_INCLUDED
+#define VIO_PRIV_INCLUDED
+
/* Structures and functions private to the vio package */
#define DONT_MAP_VIO
@@ -26,16 +26,7 @@
#include <m_string.h>
#include <violite.h>
-#ifndef __WIN__
-#include <sys/socket.h>
-#include <netdb.h>
-#endif
-
#ifdef _WIN32
-void vio_win32_timeout(Vio *vio, uint which, uint timeout);
-#endif
-
-#ifdef __WIN__
size_t vio_read_pipe(Vio *vio, uchar * buf, size_t size);
size_t vio_write_pipe(Vio *vio, const uchar * buf, size_t size);
my_bool vio_is_connected_pipe(Vio *vio);
@@ -53,9 +44,10 @@ my_bool vio_shared_memory_has_data(Vio *vio);
int vio_shutdown_shared_memory(Vio *vio, int how);
#endif
-int vio_socket_shutdown(Vio *vio, int how);
-void vio_timeout(Vio *vio,uint which, uint timeout);
-my_bool vio_buff_has_data(Vio *vio);
+int vio_socket_shutdown(Vio *vio, int how);
+my_bool vio_buff_has_data(Vio *vio);
+int vio_socket_io_wait(Vio *vio, enum enum_vio_io_event event);
+int vio_socket_timeout(Vio *vio, uint which, my_bool old_mode);
#ifdef HAVE_OPENSSL
#include "my_net.h" /* needed because of struct in_addr */
@@ -66,9 +58,7 @@ size_t vio_ssl_write(Vio *vio,const uchar* buf, size_t size);
/* When the workday is over... */
int vio_ssl_close(Vio *vio);
void vio_ssl_delete(Vio *vio);
-
int vio_ssl_blocking(Vio *vio, my_bool set_blocking_mode, my_bool *old_mode);
-
my_bool vio_ssl_has_data(Vio *vio);
#endif /* HAVE_OPENSSL */
diff --git a/vio/viopipe.c b/vio/viopipe.c
new file mode 100644
index 00000000000..f9af50bc3c9
--- /dev/null
+++ b/vio/viopipe.c
@@ -0,0 +1,145 @@
+/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "vio_priv.h"
+
+#ifdef _WIN32
+
+/*
+ Disable posting IO completion event to the port.
+ In some cases (synchronous timed IO) we want to skip IOCP notifications.
+*/
+static void disable_iocp_notification(OVERLAPPED *overlapped)
+{
+ HANDLE *handle = &(overlapped->hEvent);
+ *handle = ((HANDLE)((ULONG_PTR) *handle|1));
+}
+
+/* Enable posting IO completion event to the port */
+static void enable_iocp_notification(OVERLAPPED *overlapped)
+{
+ HANDLE *handle = &(overlapped->hEvent);
+ *handle = (HANDLE)((ULONG_PTR) *handle & ~1);
+}
+
+static size_t wait_overlapped_result(Vio *vio, int timeout)
+{
+ size_t ret= (size_t) -1;
+ DWORD transferred, wait_status, timeout_ms;
+
+ timeout_ms= timeout >= 0 ? timeout : INFINITE;
+
+ /* Wait for the overlapped operation to be completed. */
+ wait_status= WaitForSingleObject(vio->overlapped.hEvent, timeout_ms);
+
+ /* The operation might have completed, attempt to retrieve the result. */
+ if (wait_status == WAIT_OBJECT_0)
+ {
+ /* If retrieval fails, a error code will have been set. */
+ if (GetOverlappedResult(vio->hPipe, &vio->overlapped, &transferred, FALSE))
+ ret= transferred;
+ }
+ else
+ {
+ /* Error or timeout, cancel the pending I/O operation. */
+ CancelIo(vio->hPipe);
+
+ /*
+ If the wait timed out, set error code to indicate a
+ timeout error. Otherwise, wait_status is WAIT_FAILED
+ and extended error information was already set.
+ */
+ if (wait_status == WAIT_TIMEOUT)
+ SetLastError(SOCKET_ETIMEDOUT);
+ }
+
+ return ret;
+}
+
+
+size_t vio_read_pipe(Vio *vio, uchar *buf, size_t count)
+{
+ DWORD transferred;
+ size_t ret= (size_t) -1;
+ DBUG_ENTER("vio_read_pipe");
+
+ disable_iocp_notification(&vio->overlapped);
+
+ /* Attempt to read from the pipe (overlapped I/O). */
+ if (ReadFile(vio->hPipe, buf, count, &transferred, &vio->overlapped))
+ {
+ /* The operation completed immediately. */
+ ret= transferred;
+ }
+ /* Read operation is pending completion asynchronously? */
+ else if (GetLastError() == ERROR_IO_PENDING)
+ ret= wait_overlapped_result(vio, vio->read_timeout);
+
+ enable_iocp_notification(&vio->overlapped);
+
+ DBUG_RETURN(ret);
+}
+
+
+size_t vio_write_pipe(Vio *vio, const uchar *buf, size_t count)
+{
+ DWORD transferred;
+ size_t ret= (size_t) -1;
+ DBUG_ENTER("vio_write_pipe");
+
+ disable_iocp_notification(&vio->overlapped);
+ /* Attempt to write to the pipe (overlapped I/O). */
+ if (WriteFile(vio->hPipe, buf, count, &transferred, &vio->overlapped))
+ {
+ /* The operation completed immediately. */
+ ret= transferred;
+ }
+ /* Write operation is pending completion asynchronously? */
+ else if (GetLastError() == ERROR_IO_PENDING)
+ ret= wait_overlapped_result(vio, vio->write_timeout);
+
+ enable_iocp_notification(&vio->overlapped);
+ DBUG_RETURN(ret);
+}
+
+
+my_bool vio_is_connected_pipe(Vio *vio)
+{
+ if (PeekNamedPipe(vio->hPipe, NULL, 0, NULL, NULL, NULL))
+ return TRUE;
+ else
+ return (GetLastError() != ERROR_BROKEN_PIPE);
+}
+
+
+int vio_close_pipe(Vio *vio)
+{
+ BOOL ret;
+ DBUG_ENTER("vio_close_pipe");
+
+ CancelIo(vio->hPipe);
+ CloseHandle(vio->overlapped.hEvent);
+ DisconnectNamedPipe(vio->hPipe);
+ ret= CloseHandle(vio->hPipe);
+
+ vio->type= VIO_CLOSED;
+ vio->hPipe= NULL;
+ vio->mysql_socket= MYSQL_INVALID_SOCKET;
+
+ DBUG_RETURN(ret);
+}
+
+#endif
+
diff --git a/vio/vioshm.c b/vio/vioshm.c
new file mode 100644
index 00000000000..acc7d2402c5
--- /dev/null
+++ b/vio/vioshm.c
@@ -0,0 +1,217 @@
+/* Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "vio_priv.h"
+
+#if defined(_WIN32) && defined(HAVE_SMEM)
+
+size_t vio_read_shared_memory(Vio *vio, uchar *buf, size_t size)
+{
+ size_t length;
+ size_t remain_local;
+ char *current_position;
+ HANDLE events[2];
+ DWORD timeout;
+ DBUG_ENTER("vio_read_shared_memory");
+
+ remain_local= size;
+ current_position= buf;
+ timeout= vio->read_timeout >= 0 ? vio->read_timeout : INFINITE;
+
+ events[0]= vio->event_server_wrote;
+ events[1]= vio->event_conn_closed;
+
+ do
+ {
+ if (vio->shared_memory_remain == 0)
+ {
+ DWORD wait_status;
+
+ wait_status= WaitForMultipleObjects(array_elements(events), events,
+ FALSE, timeout);
+
+ /*
+ WaitForMultipleObjects can return next values:
+ WAIT_OBJECT_0+0 - event from vio->event_server_wrote
+ WAIT_OBJECT_0+1 - event from vio->event_conn_closed.
+ We can't read anything
+ WAIT_ABANDONED_0 and WAIT_TIMEOUT - fail. We can't read anything
+ */
+ if (wait_status != WAIT_OBJECT_0)
+ {
+ /*
+ If wait_status is WAIT_TIMEOUT, set error code to indicate a
+ timeout error. If vio->event_conn_closed was set, use an EOF
+ condition (return value of zero) to indicate that the operation
+ has been aborted.
+ */
+ if (wait_status == WAIT_TIMEOUT)
+ SetLastError(SOCKET_ETIMEDOUT);
+ else if (wait_status == (WAIT_OBJECT_0 + 1))
+ DBUG_RETURN(0);
+
+ DBUG_RETURN(-1);
+ }
+
+ vio->shared_memory_pos= vio->handle_map;
+ vio->shared_memory_remain= uint4korr((ulong*)vio->shared_memory_pos);
+ vio->shared_memory_pos+= 4;
+ }
+
+ length= size;
+
+ if (vio->shared_memory_remain < length)
+ length= vio->shared_memory_remain;
+ if (length > remain_local)
+ length= remain_local;
+
+ memcpy(current_position, vio->shared_memory_pos, length);
+
+ vio->shared_memory_remain-= length;
+ vio->shared_memory_pos+= length;
+ current_position+= length;
+ remain_local-= length;
+
+ if (!vio->shared_memory_remain)
+ {
+ if (!SetEvent(vio->event_client_read))
+ DBUG_RETURN(-1);
+ }
+ } while (remain_local);
+ length= size;
+
+ DBUG_RETURN(length);
+}
+
+
+size_t vio_write_shared_memory(Vio *vio, const uchar *buf, size_t size)
+{
+ size_t length, remain, sz;
+ HANDLE pos;
+ const uchar *current_position;
+ HANDLE events[2];
+ DWORD timeout;
+ DBUG_ENTER("vio_write_shared_memory");
+
+ remain= size;
+ current_position= buf;
+ timeout= vio->write_timeout >= 0 ? vio->write_timeout : INFINITE;
+
+ events[0]= vio->event_server_read;
+ events[1]= vio->event_conn_closed;
+
+ while (remain != 0)
+ {
+ DWORD wait_status;
+
+ wait_status= WaitForMultipleObjects(array_elements(events), events,
+ FALSE, timeout);
+
+ if (wait_status != WAIT_OBJECT_0)
+ {
+ /* Set error code to indicate a timeout error or disconnect. */
+ if (wait_status == WAIT_TIMEOUT)
+ SetLastError(SOCKET_ETIMEDOUT);
+ else
+ SetLastError(ERROR_GRACEFUL_DISCONNECT);
+
+ DBUG_RETURN((size_t) -1);
+ }
+
+ sz= (remain > shared_memory_buffer_length ? shared_memory_buffer_length :
+ remain);
+
+ int4store(vio->handle_map, sz);
+ pos= vio->handle_map + 4;
+ memcpy(pos, current_position, sz);
+ remain-= sz;
+ current_position+= sz;
+ if (!SetEvent(vio->event_client_wrote))
+ DBUG_RETURN((size_t) -1);
+ }
+ length= size;
+
+ DBUG_RETURN(length);
+}
+
+
+my_bool vio_is_connected_shared_memory(Vio *vio)
+{
+ return (WaitForSingleObject(vio->event_conn_closed, 0) != WAIT_OBJECT_0);
+}
+
+
+/**
+ Close shared memory and DBUG_PRINT any errors that happen on closing.
+ @return Zero if all closing functions succeed, and nonzero otherwise.
+*/
+int vio_close_shared_memory(Vio * vio)
+{
+ int error_count= 0;
+ DBUG_ENTER("vio_close_shared_memory");
+ if (vio->type != VIO_CLOSED)
+ {
+ /*
+ Set event_conn_closed for notification of both client and server that
+ connection is closed
+ */
+ SetEvent(vio->event_conn_closed);
+ /*
+ Close all handlers. UnmapViewOfFile and CloseHandle return non-zero
+ result if they are success.
+ */
+ if (UnmapViewOfFile(vio->handle_map) == 0)
+ {
+ error_count++;
+ DBUG_PRINT("vio_error", ("UnmapViewOfFile() failed"));
+ }
+ if (CloseHandle(vio->event_server_wrote) == 0)
+ {
+ error_count++;
+ DBUG_PRINT("vio_error", ("CloseHandle(vio->esw) failed"));
+ }
+ if (CloseHandle(vio->event_server_read) == 0)
+ {
+ error_count++;
+ DBUG_PRINT("vio_error", ("CloseHandle(vio->esr) failed"));
+ }
+ if (CloseHandle(vio->event_client_wrote) == 0)
+ {
+ error_count++;
+ DBUG_PRINT("vio_error", ("CloseHandle(vio->ecw) failed"));
+ }
+ if (CloseHandle(vio->event_client_read) == 0)
+ {
+ error_count++;
+ DBUG_PRINT("vio_error", ("CloseHandle(vio->ecr) failed"));
+ }
+ if (CloseHandle(vio->handle_file_map) == 0)
+ {
+ error_count++;
+ DBUG_PRINT("vio_error", ("CloseHandle(vio->hfm) failed"));
+ }
+ if (CloseHandle(vio->event_conn_closed) == 0)
+ {
+ error_count++;
+ DBUG_PRINT("vio_error", ("CloseHandle(vio->ecc) failed"));
+ }
+ }
+ vio->type= VIO_CLOSED;
+ vio->mysql_socket= MYSQL_INVALID_SOCKET;
+ DBUG_RETURN(error_count);
+}
+
+#endif /* #if defined(_WIN32) && defined(HAVE_SMEM) */
+
diff --git a/vio/viosocket.c b/vio/viosocket.c
index baefa1c6d06..5576dfc48d8 100644
--- a/vio/viosocket.c
+++ b/vio/viosocket.c
@@ -24,12 +24,12 @@
the file descriptior.
*/
+#include "vio_priv.h"
#ifdef __WIN__
#include <winsock2.h>
#include <MSWSock.h>
#pragma comment(lib, "ws2_32.lib")
#endif
-#include "vio_priv.h"
#include "my_context.h"
#include <mysql_async.h>
@@ -37,23 +37,126 @@
# include <sys/filio.h>
#endif
+/* Network io wait callbacks for threadpool */
+static void (*before_io_wait)(void)= 0;
+static void (*after_io_wait)(void)= 0;
+
+/* Wait callback macros (both performance schema and threadpool */
+#define START_SOCKET_WAIT(locker, state_ptr, sock, which, timeout) \
+do \
+{ \
+ MYSQL_START_SOCKET_WAIT(locker, state_ptr, sock, \
+ which, 0); \
+ if (timeout && before_io_wait) \
+ before_io_wait(); \
+} while(0)
+
+
+#define END_SOCKET_WAIT(locker,timeout) \
+do \
+{ \
+ MYSQL_END_SOCKET_WAIT(locker, 0); \
+ if (timeout && after_io_wait) \
+ after_io_wait(); \
+} while(0)
+
+
+
+void vio_set_wait_callback(void (*before_wait)(void),
+ void (*after_wait)(void))
+{
+ before_io_wait= before_wait;
+ after_io_wait= after_wait;
+}
+
int vio_errno(Vio *vio __attribute__((unused)))
{
- return socket_errno; /* On Win32 this mapped to WSAGetLastError() */
+ /* These transport types are not Winsock based. */
+#ifdef _WIN32
+ if (vio->type == VIO_TYPE_NAMEDPIPE ||
+ vio->type == VIO_TYPE_SHARED_MEMORY)
+ return GetLastError();
+#endif
+
+ /* Mapped to WSAGetLastError() on Win32. */
+ return socket_errno;
+}
+
+
+/**
+ Attempt to wait for an I/O event on a socket.
+
+ @param vio VIO object representing a connected socket.
+ @param event The type of I/O event (read or write) to wait for.
+
+ @return Return value is -1 on failure, 0 on success.
+*/
+
+int vio_socket_io_wait(Vio *vio, enum enum_vio_io_event event)
+{
+ int timeout, ret;
+
+ DBUG_ASSERT(event == VIO_IO_EVENT_READ || event == VIO_IO_EVENT_WRITE);
+
+ /* Choose an appropriate timeout. */
+ if (event == VIO_IO_EVENT_READ)
+ timeout= vio->read_timeout;
+ else
+ timeout= vio->write_timeout;
+
+ /* Wait for input data to become available. */
+ switch (vio_io_wait(vio, event, timeout))
+ {
+ case -1:
+ /* Upon failure, vio_read/write() shall return -1. */
+ ret= -1;
+ break;
+ case 0:
+ /* The wait timed out. */
+ ret= -1;
+ break;
+ default:
+ /* A positive value indicates an I/O event. */
+ ret= 0;
+ break;
+ }
+
+ return ret;
}
-size_t vio_read(Vio * vio, uchar* buf, size_t size)
+/*
+ Define a stub MSG_DONTWAIT if unavailable. In this case, fcntl
+ (or a equivalent) is used to enable non-blocking operations.
+ The flag must be supported in both send and recv operations.
+*/
+#if defined(__linux__)
+#define VIO_USE_DONTWAIT 1
+#define VIO_DONTWAIT MSG_DONTWAIT
+#else
+#define VIO_DONTWAIT 0
+#endif
+
+size_t vio_read(Vio *vio, uchar *buf, size_t size)
{
- size_t r;
+ ssize_t ret;
+ int flags= 0;
DBUG_ENTER("vio_read");
- DBUG_PRINT("enter", ("sd: %d buf: 0x%lx size: %u", vio->sd, (long) buf,
- (uint) size));
+ DBUG_PRINT("enter", ("sd: %d buf: %p size: %d",
+ mysql_socket_getfd(vio->mysql_socket), buf,
+ (int) size));
- /* Ensure nobody uses vio_read_buff and vio_read simultaneously */
+ /* Ensure nobody uses vio_read_buff and vio_read simultaneously. */
DBUG_ASSERT(vio->read_end == vio->read_pos);
+
+ /* If timeout is enabled, do not block if data is unavailable. */
+ if (vio->read_timeout >= 0)
+ flags= VIO_DONTWAIT;
+
if (vio->async_context && vio->async_context->active)
- r= my_recv_async(vio->async_context, vio->sd, buf, size, vio->read_timeout);
+ ret= my_recv_async(vio->async_context,
+ mysql_socket_getfd(vio->mysql_socket),
+ buf, size, vio->read_timeout);
else
{
if (vio->async_context)
@@ -65,21 +168,28 @@ size_t vio_read(Vio * vio, uchar* buf, size_t size)
my_bool old_mode;
vio_blocking(vio, TRUE, &old_mode);
}
-#ifdef __WIN__
- r = recv(vio->sd, buf, size,0);
-#else
- errno=0; /* For linux */
- r = read(vio->sd, buf, size);
-#endif /* __WIN__ */
+ while ((ret= mysql_socket_recv(vio->mysql_socket, (SOCKBUF_T *)buf, size,
+ flags)) == -1)
+ {
+ int error= socket_errno;
+
+ /* The operation would block? */
+ if (error != SOCKET_EAGAIN && error != SOCKET_EWOULDBLOCK)
+ break;
+
+ /* Wait for input data to become available. */
+ if ((ret= vio_socket_io_wait(vio, VIO_IO_EVENT_READ)))
+ break;
+ }
}
#ifndef DBUG_OFF
- if (r == (size_t) -1)
+ if (ret == -1)
{
- DBUG_PRINT("vio_error", ("Got error %d during read",errno));
+ DBUG_PRINT("vio_error", ("Got error %d during read", errno));
}
#endif /* DBUG_OFF */
- DBUG_PRINT("exit", ("%ld", (long) r));
- DBUG_RETURN(r);
+ DBUG_PRINT("exit", ("%d", (int) ret));
+ DBUG_RETURN(ret);
}
@@ -93,12 +203,13 @@ size_t vio_read_buff(Vio *vio, uchar* buf, size_t size)
size_t rc;
#define VIO_UNBUFFERED_READ_MIN_SIZE 2048
DBUG_ENTER("vio_read_buff");
- DBUG_PRINT("enter", ("sd: %d buf: 0x%lx size: %u", vio->sd, (long) buf,
- (uint) size));
+ DBUG_PRINT("enter", ("sd: %d buf: %p size: %d",
+ mysql_socket_getfd(vio->mysql_socket),
+ buf, (int) size));
if (vio->read_pos < vio->read_end)
{
- rc= min((size_t) (vio->read_end - vio->read_pos), size);
+ rc= MY_MIN((size_t) (vio->read_end - vio->read_pos), size);
memcpy(buf, vio->read_pos, rc);
vio->read_pos+= rc;
/*
@@ -127,19 +238,29 @@ size_t vio_read_buff(Vio *vio, uchar* buf, size_t size)
#undef VIO_UNBUFFERED_READ_MIN_SIZE
}
+
my_bool vio_buff_has_data(Vio *vio)
{
return (vio->read_pos != vio->read_end);
}
-size_t vio_write(Vio * vio, const uchar* buf, size_t size)
+
+size_t vio_write(Vio *vio, const uchar* buf, size_t size)
{
- size_t r;
+ ssize_t ret;
+ int flags= 0;
DBUG_ENTER("vio_write");
- DBUG_PRINT("enter", ("sd: %d buf: 0x%lx size: %u", vio->sd, (long) buf,
- (uint) size));
+ DBUG_PRINT("enter", ("sd: %d buf: %p size: %d",
+ mysql_socket_getfd(vio->mysql_socket), buf,
+ (int) size));
+
+ /* If timeout is enabled, do not block. */
+ if (vio->write_timeout >= 0)
+ flags= VIO_DONTWAIT;
+
if (vio->async_context && vio->async_context->active)
- r= my_send_async(vio->async_context, vio->sd, buf, size,
+ ret= my_send_async(vio->async_context,
+ mysql_socket_getfd(vio->mysql_socket), buf, size,
vio->write_timeout);
else
{
@@ -152,20 +273,27 @@ size_t vio_write(Vio * vio, const uchar* buf, size_t size)
my_bool old_mode;
vio_blocking(vio, TRUE, &old_mode);
}
-#ifdef __WIN__
- r = send(vio->sd, buf, size,0);
-#else
- r = write(vio->sd, buf, size);
-#endif /* __WIN__ */
+ while ((ret= mysql_socket_send(vio->mysql_socket, (SOCKBUF_T *)buf, size,
+ flags)) == -1)
+ {
+ int error= socket_errno;
+ /* The operation would block? */
+ if (error != SOCKET_EAGAIN && error != SOCKET_EWOULDBLOCK)
+ break;
+
+ /* Wait for the output buffer to become writable.*/
+ if ((ret= vio_socket_io_wait(vio, VIO_IO_EVENT_WRITE)))
+ break;
+ }
}
#ifndef DBUG_OFF
- if (r == (size_t) -1)
+ if (ret == -1)
{
DBUG_PRINT("vio_error", ("Got error on write: %d",socket_errno));
}
#endif /* DBUG_OFF */
- DBUG_PRINT("exit", ("%u", (uint) r));
- DBUG_RETURN(r);
+ DBUG_PRINT("exit", ("%d", (int) ret));
+ DBUG_RETURN(ret);
}
#ifdef _WIN32
@@ -177,8 +305,9 @@ static void CALLBACK cancel_io_apc(ULONG_PTR data)
/*
Cancel IO on Windows.
- On XP, issue CancelIo as asynchronous procedure call to the thread that started
- IO. On Vista+, simpler cancelation is done with CancelIoEx.
+ On XP, issue CancelIo as asynchronous procedure call to the thread
+ that started IO. On Vista+, simpler cancelation is done with
+ CancelIoEx.
*/
int cancel_io(HANDLE handle, DWORD thread_id)
@@ -212,39 +341,43 @@ int cancel_io(HANDLE handle, DWORD thread_id)
}
#endif
+
int vio_socket_shutdown(Vio *vio, int how)
{
- int ret= shutdown(vio->sd, how);
+ int ret= shutdown(mysql_socket_getfd(vio->mysql_socket), how);
#ifdef _WIN32
/* Cancel possible IO in progress (shutdown does not do that on Windows). */
- (void) cancel_io((HANDLE)vio->sd, vio->thread_id);
+ (void) cancel_io((HANDLE) mysql_socket_getfd(vio->mysql_socket),
+ vio->thread_id);
#endif
return ret;
}
-int vio_blocking(Vio * vio __attribute__((unused)), my_bool set_blocking_mode,
- my_bool *old_mode)
+int vio_blocking(Vio *vio, my_bool set_blocking_mode, my_bool *old_mode)
{
- int r=0;
+ int r= 0;
+#if defined(__WIN__) || !defined(NO_FCNTL_NONBLOCK)
+ my_socket sd= mysql_socket_getfd(vio->mysql_socket);
+#endif
DBUG_ENTER("vio_blocking");
- *old_mode= test(!(vio->fcntl_mode & O_NONBLOCK));
+ *old_mode= MY_TEST(!(vio->fcntl_mode & O_NONBLOCK));
DBUG_PRINT("enter", ("set_blocking_mode: %d old_mode: %d",
(int) set_blocking_mode, (int) *old_mode));
#if !defined(__WIN__)
#if !defined(NO_FCNTL_NONBLOCK)
- if (vio->sd >= 0)
+ if (sd >= 0)
{
- int old_fcntl=vio->fcntl_mode;
+ int old_fcntl= vio->fcntl_mode;
if (set_blocking_mode)
vio->fcntl_mode &= ~O_NONBLOCK; /* clear bit */
else
vio->fcntl_mode |= O_NONBLOCK; /* set bit */
if (old_fcntl != vio->fcntl_mode)
{
- r= fcntl(vio->sd, F_SETFL, vio->fcntl_mode);
+ r= fcntl(sd, F_SETFL, vio->fcntl_mode);
if (r == -1)
{
DBUG_PRINT("info", ("fcntl failed, errno %d", errno));
@@ -271,10 +404,10 @@ int vio_blocking(Vio * vio __attribute__((unused)), my_bool set_blocking_mode,
vio->fcntl_mode |= O_NONBLOCK; /* set bit */
}
if (old_fcntl != vio->fcntl_mode)
- r = ioctlsocket(vio->sd,FIONBIO,(void*) &arg);
+ r = ioctlsocket(sd,FIONBIO,(void*) &arg);
}
else
- r= test(!(vio->fcntl_mode & O_NONBLOCK)) != set_blocking_mode;
+ r= MY_TEST(!(vio->fcntl_mode & O_NONBLOCK)) != set_blocking_mode;
#endif /* !defined(__WIN__) */
DBUG_PRINT("exit", ("%d", r));
DBUG_RETURN(r);
@@ -291,6 +424,75 @@ vio_is_blocking(Vio * vio)
}
+int vio_socket_timeout(Vio *vio,
+ uint which __attribute__((unused)),
+ my_bool old_mode __attribute__((unused)))
+{
+ int ret= 0;
+ DBUG_ENTER("vio_socket_timeout");
+
+#if defined(_WIN32)
+ {
+ int optname;
+ DWORD timeout= 0;
+ const char *optval= (const char *) &timeout;
+
+ /*
+ The default socket timeout value is zero, which means an infinite
+ timeout. Values less than 500 milliseconds are interpreted to be of
+ 500 milliseconds. Hence, the VIO behavior for zero timeout, which is
+ intended to cause the send or receive operation to fail immediately
+ if no data is available, is not supported on WIN32 and neither is
+ necessary as it's not possible to set the VIO timeout value to zero.
+
+ Assert that the VIO timeout is either positive or set to infinite.
+ */
+ DBUG_ASSERT(which || vio->read_timeout);
+ DBUG_ASSERT(!which || vio->write_timeout);
+
+ if (which)
+ {
+ optname= SO_SNDTIMEO;
+ if (vio->write_timeout > 0)
+ timeout= vio->write_timeout;
+ }
+ else
+ {
+ optname= SO_RCVTIMEO;
+ if (vio->read_timeout > 0)
+ timeout= vio->read_timeout;
+ }
+
+ ret= mysql_socket_setsockopt(vio->mysql_socket, SOL_SOCKET, optname,
+ optval, sizeof(timeout));
+ }
+#else
+ /*
+ The MSG_DONTWAIT trick is not used with SSL sockets as the send and
+ receive I/O operations are wrapped through SSL-specific functions
+ (SSL_read and SSL_write) which are not equivalent to the standard
+ recv(2) and send(2) used in vio_read() and vio_write(). Hence, the
+ socket blocking mode is changed and vio_io_wait() is used to wait
+ for I/O or timeout.
+ */
+#ifdef VIO_USE_DONTWAIT
+ if (vio->type == VIO_TYPE_SSL)
+#endif
+ {
+ /* Deduce what should be the new blocking mode of the socket. */
+ my_bool new_mode= vio->write_timeout < 0 && vio->read_timeout < 0;
+ my_bool not_used;
+
+ /* If necessary, update the blocking mode. */
+ if (new_mode != old_mode)
+ ret= vio_blocking(vio, new_mode, &not_used);
+ }
+#endif
+
+ DBUG_RETURN(ret);
+}
+
+
int vio_fastsend(Vio * vio __attribute__((unused)))
{
int r=0;
@@ -304,7 +506,8 @@ int vio_fastsend(Vio * vio __attribute__((unused)))
#if defined(IPTOS_THROUGHPUT)
{
int tos = IPTOS_THROUGHPUT;
- r= setsockopt(vio->sd, IPPROTO_IP, IP_TOS, (void *) &tos, sizeof(tos));
+ r= mysql_socket_setsockopt(vio->mysql_socket, IPPROTO_IP, IP_TOS,
+ (void *)&tos, sizeof(tos));
}
#endif /* IPTOS_THROUGHPUT */
if (!r)
@@ -315,14 +518,16 @@ int vio_fastsend(Vio * vio __attribute__((unused)))
int nodelay = 1;
#endif
- r= setsockopt(vio->sd, IPPROTO_TCP, TCP_NODELAY,
+ r= mysql_socket_setsockopt(vio->mysql_socket, IPPROTO_TCP, TCP_NODELAY,
IF_WIN((const char*), (void*)) &nodelay,
sizeof(nodelay));
}
if (r)
{
- DBUG_PRINT("warning", ("Couldn't set socket option for fast send"));
+ DBUG_PRINT("warning",
+ ("Couldn't set socket option for fast send, error %d",
+ socket_errno));
r= -1;
}
DBUG_PRINT("exit", ("%d", r));
@@ -334,78 +539,56 @@ int vio_keepalive(Vio* vio, my_bool set_keep_alive)
int r=0;
uint opt = 0;
DBUG_ENTER("vio_keepalive");
- DBUG_PRINT("enter", ("sd: %d set_keep_alive: %d", vio->sd, (int)
- set_keep_alive));
+ DBUG_PRINT("enter", ("sd: %d set_keep_alive: %d",
+ mysql_socket_getfd(vio->mysql_socket),
+ (int)set_keep_alive));
+
if (vio->type != VIO_TYPE_NAMEDPIPE && vio->type != VIO_TYPE_SHARED_MEMORY)
{
if (set_keep_alive)
opt = 1;
- r = setsockopt(vio->sd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt,
- sizeof(opt));
+ r = mysql_socket_setsockopt(vio->mysql_socket, SOL_SOCKET, SO_KEEPALIVE,
+ (char *)&opt, sizeof(opt));
}
DBUG_RETURN(r);
}
-my_bool
-vio_should_retry(Vio * vio)
-{
- int en = socket_errno;
- /*
- man 2 read write
- EAGAIN or EWOULDBLOCK when a socket is a non-blocking mode means
- that the read/write would block.
- man 7 socket
- EAGAIN or EWOULDBLOCK when a socket is in a blocking mode means
- that the corresponding receiving or sending timeout was reached.
- */
- return en == SOCKET_EINTR ||
- (!vio_is_blocking(vio) &&
- (en == SOCKET_EAGAIN || en == SOCKET_EWOULDBLOCK));
-}
+/**
+ Indicate whether a I/O operation must be retried later.
+ @param vio A VIO object
+
+ @return Whether a I/O operation should be deferred.
+ @retval TRUE Temporary failure, retry operation.
+ @retval FALSE Indeterminate failure.
+*/
my_bool
-vio_was_interrupted(Vio *vio __attribute__((unused)))
+vio_should_retry(Vio *vio)
{
- int en= socket_errno;
- return (en == SOCKET_EAGAIN || en == SOCKET_EINTR ||
- en == SOCKET_EWOULDBLOCK || en == SOCKET_ETIMEDOUT);
+ return (vio_errno(vio) == SOCKET_EINTR);
}
-int
-mysql_socket_shutdown(my_socket mysql_socket, int how)
-{
- int result;
+/**
+ Indicate whether a I/O operation timed out.
-#ifdef __WIN__
- static LPFN_DISCONNECTEX DisconnectEx = NULL;
- if (DisconnectEx == NULL)
- {
- DWORD dwBytesReturned;
- GUID guidDisconnectEx = WSAID_DISCONNECTEX;
- WSAIoctl(mysql_socket, SIO_GET_EXTENSION_FUNCTION_POINTER,
- &guidDisconnectEx, sizeof(GUID),
- &DisconnectEx, sizeof(DisconnectEx),
- &dwBytesReturned, NULL, NULL);
- }
-#endif
+ @param vio A VIO object
- /* Non instrumented code */
-#ifdef __WIN__
- if (DisconnectEx)
- result= (DisconnectEx(mysql_socket, (LPOVERLAPPED) NULL,
- (DWORD) 0, (DWORD) 0) == TRUE) ? 0 : -1;
- else
-#endif
- result= shutdown(mysql_socket, how);
+ @return Whether a I/O operation timed out.
+ @retval TRUE Operation timed out.
+ @retval FALSE Not a timeout failure.
+*/
- return result;
+my_bool
+vio_was_timeout(Vio *vio)
+{
+ return (vio_errno(vio) == SOCKET_ETIMEDOUT);
}
-int vio_close(Vio * vio)
+int vio_close(Vio *vio)
{
int r=0;
DBUG_ENTER("vio_close");
@@ -416,10 +599,10 @@ int vio_close(Vio * vio)
vio->type == VIO_TYPE_SOCKET ||
vio->type == VIO_TYPE_SSL);
- DBUG_ASSERT(vio->sd >= 0);
- if (mysql_socket_shutdown(vio->sd, SHUT_RDWR))
+ DBUG_ASSERT(mysql_socket_getfd(vio->mysql_socket) >= 0);
+ if (mysql_socket_shutdown(vio->mysql_socket, SHUT_RDWR))
r= -1;
- if (closesocket(vio->sd))
+ if (mysql_socket_close(vio->mysql_socket))
r= -1;
}
if (r)
@@ -428,7 +611,7 @@ int vio_close(Vio * vio)
/* FIXME: error handling (not critical for MySQL) */
}
vio->type= VIO_CLOSED;
- vio->sd= -1;
+ vio->mysql_socket= MYSQL_INVALID_SOCKET;
DBUG_RETURN(r);
}
@@ -445,7 +628,7 @@ enum enum_vio_type vio_type(Vio* vio)
my_socket vio_fd(Vio* vio)
{
- return vio->sd;
+ return mysql_socket_getfd(vio->mysql_socket);
}
/**
@@ -471,6 +654,7 @@ my_socket vio_fd(Vio* vio)
(sockaddr_storage).
@param dst_length [out] actual length of the normalized IP address.
*/
+
static void vio_get_normalized_ip(const struct sockaddr *src,
int src_length,
struct sockaddr *dst,
@@ -586,7 +770,8 @@ my_bool vio_peer_addr(Vio *vio, char *ip_buffer, uint16 *port,
size_t ip_buffer_size)
{
DBUG_ENTER("vio_peer_addr");
- DBUG_PRINT("enter", ("Client socked fd: %d", (int) vio->sd));
+ DBUG_PRINT("enter", ("Client socked fd: %d",
+ (int)mysql_socket_getfd(vio->mysql_socket)));
if (vio->localhost)
{
@@ -617,7 +802,7 @@ my_bool vio_peer_addr(Vio *vio, char *ip_buffer, uint16 *port,
/* Get sockaddr by socked fd. */
- err_code= getpeername(vio->sd, addr, &addr_length);
+ err_code= mysql_socket_getpeername(vio->mysql_socket, addr, &addr_length);
if (err_code)
{
@@ -655,58 +840,6 @@ my_bool vio_peer_addr(Vio *vio, char *ip_buffer, uint16 *port,
/**
- Indicate whether there is data to read on a given socket.
-
- @note An exceptional condition event and/or errors are
- interpreted as if there is data to read.
-
- @param sd A connected socket.
- @param timeout Maximum time in seconds to poll.
-
- @retval FALSE There is data to read.
- @retval TRUE There is no data to read.
-*/
-
-static my_bool socket_poll_read(my_socket sd, uint timeout)
-{
-#ifdef __WIN__
- int res;
- my_socket fd= sd;
- fd_set readfds, errorfds;
- struct timeval tm;
- DBUG_ENTER("socket_poll_read");
- tm.tv_sec= timeout;
- tm.tv_usec= 0;
- FD_ZERO(&readfds);
- FD_ZERO(&errorfds);
- FD_SET(fd, &readfds);
- FD_SET(fd, &errorfds);
- /* The first argument is ignored on Windows, so a conversion to int is OK */
- if ((res= select((int) fd, &readfds, NULL, &errorfds, &tm) <= 0))
- {
- DBUG_RETURN(res < 0 ? 0 : 1);
- }
- res= FD_ISSET(fd, &readfds) || FD_ISSET(fd, &errorfds);
- DBUG_RETURN(!res);
-#elif defined(HAVE_POLL)
- struct pollfd fds;
- int res;
- DBUG_ENTER("socket_poll_read");
- fds.fd=sd;
- fds.events=POLLIN;
- fds.revents=0;
- if ((res=poll(&fds,1,(int) timeout*1000)) <= 0)
- {
- DBUG_RETURN(res < 0 ? 0 : 1); /* Don't return 1 on errors */
- }
- DBUG_RETURN(fds.revents & (POLLIN | POLLERR | POLLHUP) ? 0 : 1);
-#else
- return 0;
-#endif
-}
-
-
-/**
Retrieve the amount of data that can be read from a socket.
@param vio A VIO object.
@@ -715,510 +848,412 @@ static my_bool socket_poll_read(my_socket sd, uint timeout)
@retval FALSE Success.
@retval TRUE Failure.
*/
+// WL#4896: Not covered
static my_bool socket_peek_read(Vio *vio, uint *bytes)
{
+ my_socket sd= mysql_socket_getfd(vio->mysql_socket);
#if defined(_WIN32)
int len;
- if (ioctlsocket(vio->sd, FIONREAD, &len))
+ if (ioctlsocket(sd, FIONREAD, &len))
return TRUE;
*bytes= len;
return FALSE;
#elif defined(FIONREAD_IN_SYS_IOCTL) || defined(FIONREAD_IN_SYS_FILIO)
int len;
- if (ioctl(vio->sd, FIONREAD, &len) < 0)
+ if (ioctl(sd, FIONREAD, &len) < 0)
return TRUE;
*bytes= len;
return FALSE;
#else
char buf[1024];
- ssize_t res= recv(vio->sd, &buf, sizeof(buf), MSG_PEEK);
+ ssize_t res= recv(sd, &buf, sizeof(buf), MSG_PEEK);
if (res < 0)
return TRUE;
*bytes= res;
return FALSE;
-#endif
+#endif /*_WIN32*/
}
+#ifndef _WIN32
/**
- Indicate whether there is data to read on a given socket.
-
- @remark Errors are interpreted as if there is data to read.
-
- @param sd A connected socket.
- @param timeout Maximum time in seconds to wait.
-
- @retval FALSE There is data (or EOF) to read. Also FALSE if error.
- @retval TRUE There is _NO_ data to read or timed out.
+ Set of event flags grouped by operations.
*/
-my_bool vio_poll_read(Vio *vio, uint timeout)
-{
- my_socket sd= vio->sd;
- DBUG_ENTER("vio_poll_read");
- if (vio->async_context && vio->async_context->active)
- DBUG_RETURN(my_poll_read_async(vio->async_context, timeout));
-#ifdef HAVE_OPENSSL
- if (vio->type == VIO_TYPE_SSL)
- sd= SSL_get_fd((SSL*) vio->ssl_arg);
+/*
+ Linux specific flag used to detect connection shutdown. The flag is
+ also used for half-closed notification, which here is interpreted as
+ if there is data available to be read from the socket.
+*/
+#ifndef POLLRDHUP
+#define POLLRDHUP 0
#endif
- DBUG_RETURN(socket_poll_read(sd, timeout));
-}
+/* Data may be read. */
+#define MY_POLL_SET_IN (POLLIN | POLLPRI)
+/* Data may be written. */
+#define MY_POLL_SET_OUT (POLLOUT)
+/* An error or hangup. */
+#define MY_POLL_SET_ERR (POLLERR | POLLHUP | POLLNVAL)
+
+#endif /* _WIN32 */
/**
- Determine if the endpoint of a connection is still available.
+ Wait for an I/O event on a VIO socket.
- @remark The socket is assumed to be disconnected if an EOF
- condition is encountered.
+ @param vio VIO object representing a connected socket.
+ @param event The type of I/O event to wait for.
+ @param timeout Interval (in milliseconds) to wait for an I/O event.
+ A negative timeout value means an infinite timeout.
- @param vio The VIO object.
+ @remark sock_errno is set to SOCKET_ETIMEDOUT on timeout.
- @retval TRUE EOF condition not found.
- @retval FALSE EOF condition is signaled.
+ @return A three-state value which indicates the operation status.
+ @retval -1 Failure, socket_errno indicates the error.
+ @retval 0 The wait has timed out.
+ @retval 1 The requested I/O event has occurred.
*/
-my_bool vio_is_connected(Vio *vio)
+#ifndef _WIN32
+int vio_io_wait(Vio *vio, enum enum_vio_io_event event, int timeout)
{
- uint bytes= 0;
- DBUG_ENTER("vio_is_connected");
-
- /* In the presence of errors the socket is assumed to be connected. */
+ int ret;
+ short revents __attribute__((unused)) = 0;
+ struct pollfd pfd;
+ my_socket sd= mysql_socket_getfd(vio->mysql_socket);
+ MYSQL_SOCKET_WAIT_VARIABLES(locker, state) /* no ';' */
+ DBUG_ENTER("vio_io_wait");
/*
- The first step of detecting a EOF condition is veryfing
- whether there is data to read. Data in this case would
- be the EOF.
+ Note that if zero timeout, then we will not block, so we do not need to
+ yield to calling application in the async case.
*/
- if (vio_poll_read(vio, 0))
- DBUG_RETURN(TRUE);
-
- /*
- The second step is read() or recv() from the socket returning
- 0 (EOF). Unfortunelly, it's not possible to call read directly
- as we could inadvertently read meaningful connection data.
- Simulate a read by retrieving the number of bytes available to
- read -- 0 meaning EOF.
- */
- if (socket_peek_read(vio, &bytes))
- DBUG_RETURN(TRUE);
-
-#ifdef HAVE_OPENSSL
- /* There might be buffered data at the SSL layer. */
- if (!bytes && vio->type == VIO_TYPE_SSL)
- bytes= SSL_pending((SSL*) vio->ssl_arg);
-#endif
-
- DBUG_RETURN(bytes ? TRUE : FALSE);
-}
-
-
-void vio_timeout(Vio *vio, uint which, uint timeout)
-{
-#if defined(SO_SNDTIMEO) && defined(SO_RCVTIMEO)
- int r;
- DBUG_ENTER("vio_timeout");
-
+ if (timeout != 0 && vio->async_context && vio->async_context->active)
{
-#ifdef __WIN__
- /* Windows expects time in milliseconds as int */
- int wait_timeout= (int) timeout * 1000;
-#else
- /* POSIX specifies time as struct timeval. */
- struct timeval wait_timeout;
- wait_timeout.tv_sec= timeout;
- wait_timeout.tv_usec= 0;
-#endif
-
- r= setsockopt(vio->sd, SOL_SOCKET, which ? SO_SNDTIMEO : SO_RCVTIMEO,
- IF_WIN((const char*), (const void*))&wait_timeout,
- sizeof(wait_timeout));
-
+ START_SOCKET_WAIT(locker, &state, vio->mysql_socket,
+ PSI_SOCKET_SELECT, timeout);
+ ret= my_io_wait_async(vio->async_context, event, timeout);
+ if (ret == 0)
+ errno= SOCKET_ETIMEDOUT;
+ END_SOCKET_WAIT(locker,timeout);
+ DBUG_RETURN(ret);
}
- if (r != 0)
- DBUG_PRINT("error", ("setsockopt failed: %d, errno: %d", r, socket_errno));
-
- DBUG_VOID_RETURN;
-#else
-/*
- Platforms not suporting setting of socket timeout should either use
- thr_alarm or just run without read/write timeout(s)
-*/
-#endif
- /* Make timeout values available for async operations. */
- if (which)
- vio->write_timeout= timeout;
- else
- vio->read_timeout= timeout;
-}
-
-
-#ifdef __WIN__
-/*
- Disable posting IO completion event to the port.
- In some cases (synchronous timed IO) we want to skip IOCP notifications.
-*/
-static void disable_iocp_notification(OVERLAPPED *overlapped)
-{
- HANDLE *handle = &(overlapped->hEvent);
- *handle = ((HANDLE)((ULONG_PTR) *handle|1));
-}
-
-/* Enable posting IO completion event to the port */
-static void enable_iocp_notification(OVERLAPPED *overlapped)
-{
- HANDLE *handle = &(overlapped->hEvent);
- *handle = (HANDLE)((ULONG_PTR) *handle & ~1);
-}
-
-/*
- Finish pending IO on pipe. Honor wait timeout
-*/
-static size_t pipe_complete_io(Vio* vio, char* buf, size_t size, DWORD timeout_ms)
-{
- DWORD length;
- DWORD ret;
+ memset(&pfd, 0, sizeof(pfd));
- DBUG_ENTER("pipe_complete_io");
+ pfd.fd= sd;
- ret= WaitForSingleObjectEx(vio->pipe_overlapped.hEvent, timeout_ms, TRUE);
/*
- WaitForSingleObjects will normally return WAIT_OBJECT_O (success, IO completed)
- or WAIT_TIMEOUT.
+ Set the poll bitmask describing the type of events.
+ The error flags are only valid in the revents bitmask.
*/
- if(ret != WAIT_OBJECT_0)
+ switch (event)
{
- CancelIo(vio->hPipe);
- DBUG_PRINT("error",("WaitForSingleObject() returned %d", ret));
- DBUG_RETURN((size_t)-1);
+ case VIO_IO_EVENT_READ:
+ pfd.events= MY_POLL_SET_IN;
+ revents= MY_POLL_SET_IN | MY_POLL_SET_ERR | POLLRDHUP;
+ break;
+ case VIO_IO_EVENT_WRITE:
+ case VIO_IO_EVENT_CONNECT:
+ pfd.events= MY_POLL_SET_OUT;
+ revents= MY_POLL_SET_OUT | MY_POLL_SET_ERR;
+ break;
}
- if (!GetOverlappedResult(vio->hPipe,&(vio->pipe_overlapped),&length, FALSE))
+ START_SOCKET_WAIT(locker, &state, vio->mysql_socket, PSI_SOCKET_SELECT, timeout);
+ /*
+ Wait for the I/O event and return early in case of
+ error or timeout.
+ */
+ switch ((ret= poll(&pfd, 1, timeout)))
{
- DBUG_PRINT("error",("GetOverlappedResult() returned last error %d",
- GetLastError()));
- DBUG_RETURN((size_t)-1);
+ case -1:
+ /* On error, -1 is returned. */
+ break;
+ case 0:
+ /*
+ Set errno to indicate a timeout error.
+ (This is not compiled in on WIN32.)
+ */
+ errno= SOCKET_ETIMEDOUT;
+ break;
+ default:
+ /* Ensure that the requested I/O event has completed. */
+ DBUG_ASSERT(pfd.revents & revents);
+ break;
}
- DBUG_RETURN(length);
+ END_SOCKET_WAIT(locker, timeout);
+ DBUG_RETURN(ret);
}
+#else
-size_t vio_read_pipe(Vio * vio, uchar *buf, size_t size)
+int vio_io_wait(Vio *vio, enum enum_vio_io_event event, int timeout)
{
- DWORD bytes_read;
- size_t retval;
- DBUG_ENTER("vio_read_pipe");
- DBUG_PRINT("enter", ("sd: %d buf: 0x%lx size: %u", vio->sd, (long) buf,
- (uint) size));
-
- disable_iocp_notification(&vio->pipe_overlapped);
- if (ReadFile(vio->hPipe, buf, (DWORD)size, &bytes_read,
- &(vio->pipe_overlapped)))
+ int ret;
+ struct timeval tm;
+ my_socket fd= mysql_socket_getfd(vio->mysql_socket);
+ fd_set readfds, writefds, exceptfds;
+ MYSQL_SOCKET_WAIT_VARIABLES(locker, state) /* no ';' */
+ DBUG_ENTER("vio_io_wait");
+
+ /*
+ Note that if zero timeout, then we will not block, so we do not need to
+ yield to calling application in the async case.
+ */
+ if (timeout != 0 && vio->async_context && vio->async_context->active)
{
- retval= bytes_read;
+ START_SOCKET_WAIT(locker, &state, vio->mysql_socket,
+ PSI_SOCKET_SELECT, timeout);
+ ret= my_io_wait_async(vio->async_context, event, timeout);
+ if (ret == 0)
+ WSASetLastError(SOCKET_ETIMEDOUT);
+ END_SOCKET_WAIT(locker, timeout);
+ DBUG_RETURN(ret);
}
- else
+
+ /* Convert the timeout, in milliseconds, to seconds and microseconds. */
+ if (timeout >= 0)
{
- if (GetLastError() != ERROR_IO_PENDING)
- {
- enable_iocp_notification(&vio->pipe_overlapped);
- DBUG_PRINT("error",("ReadFile() returned last error %d",
- GetLastError()));
- DBUG_RETURN((size_t)-1);
- }
- retval= pipe_complete_io(vio, buf, size,vio->read_timeout_ms);
+ tm.tv_sec= timeout / 1000;
+ tm.tv_usec= (timeout % 1000) * 1000;
}
- enable_iocp_notification(&vio->pipe_overlapped);
- DBUG_PRINT("exit", ("%lld", (longlong)retval));
- DBUG_RETURN(retval);
-}
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ FD_ZERO(&exceptfds);
-size_t vio_write_pipe(Vio * vio, const uchar* buf, size_t size)
-{
- DWORD bytes_written;
- size_t retval;
- DBUG_ENTER("vio_write_pipe");
- DBUG_PRINT("enter", ("sd: %d buf: 0x%lx size: %u", vio->sd, (long) buf,
- (uint) size));
- disable_iocp_notification(&vio->pipe_overlapped);
- if (WriteFile(vio->hPipe, buf, (DWORD)size, &bytes_written,
- &(vio->pipe_overlapped)))
- {
- retval= bytes_written;
- }
- else
+ /* Always receive notification of exceptions. */
+ FD_SET(fd, &exceptfds);
+
+ switch (event)
{
- enable_iocp_notification(&vio->pipe_overlapped);
- if (GetLastError() != ERROR_IO_PENDING)
- {
- DBUG_PRINT("vio_error",("WriteFile() returned last error %d",
- GetLastError()));
- DBUG_RETURN((size_t)-1);
- }
- retval= pipe_complete_io(vio, (char *)buf, size, vio->write_timeout_ms);
+ case VIO_IO_EVENT_READ:
+ /* Readiness for reading. */
+ FD_SET(fd, &readfds);
+ break;
+ case VIO_IO_EVENT_WRITE:
+ case VIO_IO_EVENT_CONNECT:
+ /* Readiness for writing. */
+ FD_SET(fd, &writefds);
+ break;
}
- enable_iocp_notification(&vio->pipe_overlapped);
- DBUG_PRINT("exit", ("%lld", (longlong)retval));
- DBUG_RETURN(retval);
-}
+ START_SOCKET_WAIT(locker, &state, vio->mysql_socket, PSI_SOCKET_SELECT, timeout);
-my_bool vio_is_connected_pipe(Vio *vio)
-{
- if (PeekNamedPipe(vio->hPipe, NULL, 0, NULL, NULL, NULL))
- return TRUE;
- else
- return (GetLastError() != ERROR_BROKEN_PIPE);
-}
+ /* The first argument is ignored on Windows. */
+ ret= select(0, &readfds, &writefds, &exceptfds, (timeout >= 0) ? &tm : NULL);
+ END_SOCKET_WAIT(locker, timeout);
-int vio_close_pipe(Vio * vio)
-{
- int r;
- DBUG_ENTER("vio_close_pipe");
+ /* Set error code to indicate a timeout error. */
+ if (ret == 0)
+ WSASetLastError(SOCKET_ETIMEDOUT);
- CancelIo(vio->hPipe);
- CloseHandle(vio->pipe_overlapped.hEvent);
- DisconnectNamedPipe(vio->hPipe);
- r= CloseHandle(vio->hPipe);
- if (r)
+ /* Error or timeout? */
+ if (ret <= 0)
+ DBUG_RETURN(ret);
+
+ /* The requested I/O event is ready? */
+ switch (event)
{
- DBUG_PRINT("vio_error", ("close() failed, error: %d",GetLastError()));
- /* FIXME: error handling (not critical for MySQL) */
+ case VIO_IO_EVENT_READ:
+ ret= MY_TEST(FD_ISSET(fd, &readfds));
+ break;
+ case VIO_IO_EVENT_WRITE:
+ case VIO_IO_EVENT_CONNECT:
+ ret= MY_TEST(FD_ISSET(fd, &writefds));
+ break;
}
- vio->type= VIO_CLOSED;
- vio->sd= -1;
- DBUG_RETURN(r);
-}
+ /* Error conditions pending? */
+ ret|= MY_TEST(FD_ISSET(fd, &exceptfds));
-void vio_win32_timeout(Vio *vio, uint which , uint timeout_sec)
-{
- DWORD timeout_ms;
- /*
- Windows is measuring timeouts in milliseconds. Check for possible int
- overflow.
- */
- if (timeout_sec > UINT_MAX/1000)
- timeout_ms= INFINITE;
- else
- timeout_ms= timeout_sec * 1000;
+ /* Not a timeout, ensure that a condition was met. */
+ DBUG_ASSERT(ret);
- /* which == 1 means "write", which == 0 means "read".*/
- if(which)
- vio->write_timeout_ms= timeout_ms;
- else
- vio->read_timeout_ms= timeout_ms;
+ DBUG_RETURN(ret);
}
+#endif /* _WIN32 */
-#ifdef HAVE_SMEM
-
-size_t vio_read_shared_memory(Vio * vio, uchar* buf, size_t size)
-{
- size_t length;
- size_t remain_local;
- char *current_position;
- HANDLE events[2];
- DBUG_ENTER("vio_read_shared_memory");
- DBUG_PRINT("enter", ("sd: %d buf: 0x%lx size: %d", vio->sd, (long) buf,
- size));
+/**
+ Connect to a peer address.
- remain_local = size;
- current_position=buf;
+ @param vio A VIO object.
+ @param addr Socket address containing the peer address.
+ @param len Length of socket address.
+ @param timeout Interval (in milliseconds) to wait until a
+ connection is established.
- events[0]= vio->event_server_wrote;
- events[1]= vio->event_conn_closed;
+ @retval FALSE A connection was successfully established.
+ @retval TRUE A fatal error. See socket_errno.
+*/
- do
- {
- if (vio->shared_memory_remain == 0)
- {
- /*
- WaitForMultipleObjects can return next values:
- WAIT_OBJECT_0+0 - event from vio->event_server_wrote
- WAIT_OBJECT_0+1 - event from vio->event_conn_closed. We can't read
- anything
- WAIT_ABANDONED_0 and WAIT_TIMEOUT - fail. We can't read anything
- */
- if (WaitForMultipleObjects(array_elements(events), events, FALSE,
- vio->read_timeout_ms) != WAIT_OBJECT_0)
- {
- DBUG_RETURN(-1);
- };
+my_bool
+vio_socket_connect(Vio *vio, struct sockaddr *addr, socklen_t len, int timeout)
+{
+ int ret, wait;
+ my_bool not_used;
+ DBUG_ENTER("vio_socket_connect");
- vio->shared_memory_pos = vio->handle_map;
- vio->shared_memory_remain = uint4korr((ulong*)vio->shared_memory_pos);
- vio->shared_memory_pos+=4;
- }
+ /* Only for socket-based transport types. */
+ DBUG_ASSERT(vio->type == VIO_TYPE_SOCKET || vio->type == VIO_TYPE_TCPIP);
- length = size;
+ /* If timeout is not infinite, set socket to non-blocking mode. */
+ if ((timeout > -1) && vio_blocking(vio, FALSE, &not_used))
+ DBUG_RETURN(TRUE);
- if (vio->shared_memory_remain < length)
- length = vio->shared_memory_remain;
- if (length > remain_local)
- length = remain_local;
+ /* Initiate the connection. */
+ ret= mysql_socket_connect(vio->mysql_socket, addr, len);
- memcpy(current_position,vio->shared_memory_pos,length);
+#ifdef _WIN32
+ wait= (ret == SOCKET_ERROR) &&
+ (WSAGetLastError() == WSAEINPROGRESS ||
+ WSAGetLastError() == WSAEWOULDBLOCK);
+#else
+ wait= (ret == -1) && (errno == EINPROGRESS || errno == EALREADY);
+#endif
- vio->shared_memory_remain-=length;
- vio->shared_memory_pos+=length;
- current_position+=length;
- remain_local-=length;
+ /*
+ The connection is in progress. The vio_io_wait() call can be used
+ to wait up to a specified period of time for the connection to
+ succeed.
+
+ If vio_io_wait() returns 0 (after waiting however many seconds),
+ the socket never became writable (host is probably unreachable.)
+ Otherwise, if vio_io_wait() returns 1, then one of two conditions
+ exist:
+
+ 1. An error occurred. Use getsockopt() to check for this.
+ 2. The connection was set up successfully: getsockopt() will
+ return 0 as an error.
+ */
+ if (wait && (vio_io_wait(vio, VIO_IO_EVENT_CONNECT, timeout) == 1))
+ {
+ int error;
+ IF_WIN(int, socklen_t) optlen= sizeof(error);
+ IF_WIN(char, void) *optval= (IF_WIN(char, void) *) &error;
- if (!vio->shared_memory_remain)
+ /*
+ At this point, we know that something happened on the socket.
+ But this does not means that everything is alright. The connect
+ might have failed. We need to retrieve the error code from the
+ socket layer. We must return success only if we are sure that
+ it was really a success. Otherwise we might prevent the caller
+ from trying another address to connect to.
+ */
+ if (!(ret= mysql_socket_getsockopt(vio->mysql_socket, SOL_SOCKET,
+ SO_ERROR, optval, &optlen)))
{
- if (!SetEvent(vio->event_client_read))
- DBUG_RETURN(-1);
+#ifdef _WIN32
+ WSASetLastError(error);
+#else
+ errno= error;
+#endif
+ ret= MY_TEST(error);
}
- } while (remain_local);
- length = size;
+ }
- DBUG_PRINT("exit", ("%lu", (ulong) length));
- DBUG_RETURN(length);
+ /* If necessary, restore the blocking mode, but only if connect succeeded. */
+ if ((timeout > -1) && (ret == 0))
+ {
+ my_bool not_used;
+ if (vio_blocking(vio, TRUE, &not_used))
+ DBUG_RETURN(TRUE);
+ }
+
+ DBUG_RETURN(MY_TEST(ret));
}
-size_t vio_write_shared_memory(Vio * vio, const uchar* buf, size_t size)
-{
- size_t length, remain, sz;
- HANDLE pos;
- const uchar *current_position;
- HANDLE events[2];
+/**
+ Determine if the endpoint of a connection is still available.
- DBUG_ENTER("vio_write_shared_memory");
- DBUG_PRINT("enter", ("sd: %d buf: 0x%lx size: %d", vio->sd, (long) buf,
- size));
+ @remark The socket is assumed to be disconnected if an EOF
+ condition is encountered.
- remain = size;
- current_position = buf;
+ @param vio The VIO object.
- events[0]= vio->event_server_read;
- events[1]= vio->event_conn_closed;
+ @retval TRUE EOF condition not found.
+ @retval FALSE EOF condition is signaled.
+*/
- while (remain != 0)
- {
- if (WaitForMultipleObjects(array_elements(events), events, FALSE,
- vio->write_timeout_ms) != WAIT_OBJECT_0)
- {
- DBUG_RETURN((size_t) -1);
- }
+my_bool vio_is_connected(Vio *vio)
+{
+ uint bytes= 0;
+ DBUG_ENTER("vio_is_connected");
- sz= (remain > shared_memory_buffer_length ? shared_memory_buffer_length :
- remain);
+ /*
+ The first step of detecting an EOF condition is verifying
+ whether there is data to read. Data in this case would be
+ the EOF. An exceptional condition event and/or errors are
+ interpreted as if there is data to read.
+ */
+ if (!vio_io_wait(vio, VIO_IO_EVENT_READ, 0))
+ DBUG_RETURN(TRUE);
- int4store(vio->handle_map,sz);
- pos = vio->handle_map + 4;
- memcpy(pos,current_position,sz);
- remain-=sz;
- current_position+=sz;
- if (!SetEvent(vio->event_client_wrote))
- DBUG_RETURN((size_t) -1);
+ /*
+ The second step is read() or recv() from the socket returning
+ 0 (EOF). Unfortunately, it's not possible to call read directly
+ as we could inadvertently read meaningful connection data.
+ Simulate a read by retrieving the number of bytes available to
+ read -- 0 meaning EOF. In the presence of unrecoverable errors,
+ the socket is assumed to be disconnected.
+ */
+ while (socket_peek_read(vio, &bytes))
+ {
+ if (socket_errno != SOCKET_EINTR)
+ DBUG_RETURN(FALSE);
}
- length = size;
-
- DBUG_PRINT("exit", ("%lu", (ulong) length));
- DBUG_RETURN(length);
-}
+#ifdef HAVE_OPENSSL
+ /* There might be buffered data at the SSL layer. */
+ if (!bytes && vio->type == VIO_TYPE_SSL)
+ bytes= SSL_pending((SSL*) vio->ssl_arg);
+#endif
-my_bool vio_is_connected_shared_memory(Vio *vio)
-{
- return (WaitForSingleObject(vio->event_conn_closed, 0) != WAIT_OBJECT_0);
+ DBUG_RETURN(bytes ? TRUE : FALSE);
}
+#ifndef DBUG_OFF
/**
- Close shared memory and DBUG_PRINT any errors that happen on closing.
- @return Zero if all closing functions succeed, and nonzero otherwise.
-*/
-int vio_close_shared_memory(Vio * vio)
-{
- int error_count= 0;
- DBUG_ENTER("vio_close_shared_memory");
- if (vio->type != VIO_CLOSED)
- {
- /*
- Set event_conn_closed for notification of both client and server that
- connection is closed
- */
- SetEvent(vio->event_conn_closed);
- /*
- Close all handlers. UnmapViewOfFile and CloseHandle return non-zero
- result if they are success.
- */
- if (UnmapViewOfFile(vio->handle_map) == 0)
- {
- error_count++;
- DBUG_PRINT("vio_error", ("UnmapViewOfFile() failed"));
- }
- if (CloseHandle(vio->event_server_wrote) == 0)
- {
- error_count++;
- DBUG_PRINT("vio_error", ("CloseHandle(vio->esw) failed"));
- }
- if (CloseHandle(vio->event_server_read) == 0)
- {
- error_count++;
- DBUG_PRINT("vio_error", ("CloseHandle(vio->esr) failed"));
- }
- if (CloseHandle(vio->event_client_wrote) == 0)
- {
- error_count++;
- DBUG_PRINT("vio_error", ("CloseHandle(vio->ecw) failed"));
- }
- if (CloseHandle(vio->event_client_read) == 0)
- {
- error_count++;
- DBUG_PRINT("vio_error", ("CloseHandle(vio->ecr) failed"));
- }
- if (CloseHandle(vio->handle_file_map) == 0)
- {
- error_count++;
- DBUG_PRINT("vio_error", ("CloseHandle(vio->hfm) failed"));
- }
- if (CloseHandle(vio->event_conn_closed) == 0)
- {
- error_count++;
- DBUG_PRINT("vio_error", ("CloseHandle(vio->ecc) failed"));
- }
- }
- vio->type= VIO_CLOSED;
- vio->sd= -1;
- DBUG_RETURN(error_count);
-}
-#endif /* HAVE_SMEM */
-#endif /* __WIN__ */
+ Number of bytes in the read or socket buffer
+ @remark An EOF condition might count as one readable byte.
-/**
- Number of bytes in the read buffer.
-
- @return number of bytes in the read buffer or < 0 if error.
+ @return number of bytes in one of the buffers or < 0 if error.
*/
ssize_t vio_pending(Vio *vio)
{
-#ifdef HAVE_OPENSSL
- SSL *ssl= (SSL*) vio->ssl_arg;
-#endif
+ uint bytes= 0;
+ /* Data pending on the read buffer. */
if (vio->read_pos < vio->read_end)
return vio->read_end - vio->read_pos;
-#ifdef HAVE_OPENSSL
- if (ssl)
- return SSL_pending(ssl);
-#endif
+ /* Skip non-socket based transport types. */
+ if (vio->type == VIO_TYPE_TCPIP || vio->type == VIO_TYPE_SOCKET)
+ {
+ /* Obtain number of readable bytes in the socket buffer. */
+ if (socket_peek_read(vio, &bytes))
+ return -1;
+ }
- return 0;
+ /*
+ SSL not checked due to a yaSSL bug in SSL_pending that
+ causes it to attempt to read from the socket.
+ */
+
+ return (ssize_t) bytes;
}
+#endif /* DBUG_OFF */
/**
Checks if the error code, returned by vio_getnameinfo(), means it was the
diff --git a/vio/viossl.c b/vio/viossl.c
index 5181f496768..0bc2c263336 100644
--- a/vio/viossl.c
+++ b/vio/viossl.c
@@ -26,7 +26,7 @@
#ifdef HAVE_OPENSSL
-#ifndef HAVE_YASSL
+#ifdef HAVE_YASSL
/*
yassl seem to be different here, SSL_get_error() value can be
directly passed to ERR_error_string(), and these errors don't go
@@ -35,84 +35,184 @@
namespace, one needs to use ERR_get_error() as an argument
for ERR_error_string().
*/
-#define SSL_get_error(X,Y) ERR_get_error()
+#define SSL_errno(X,Y) SSL_get_error(X,Y)
+#else
+#define SSL_errno(X,Y) ERR_get_error()
#endif
-#ifndef DBUG_OFF
+/**
+ Obtain the equivalent system error status for the last SSL I/O operation.
-static void
-report_errors(SSL* ssl)
-{
- unsigned long l;
- const char *file;
- const char *data;
- int line, flags;
- char buf[512];
+ @param ssl_error The result code of the failed TLS/SSL I/O operation.
+*/
- DBUG_ENTER("report_errors");
+static void ssl_set_sys_error(int ssl_error)
+{
+ int error= 0;
- while ((l= ERR_get_error_line_data(&file,&line,&data,&flags)))
+ switch (ssl_error)
+ {
+ case SSL_ERROR_ZERO_RETURN:
+ error= SOCKET_ECONNRESET;
+ break;
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+#ifdef SSL_ERROR_WANT_CONNECT
+ case SSL_ERROR_WANT_CONNECT:
+#endif
+#ifdef SSL_ERROR_WANT_ACCEPT
+ case SSL_ERROR_WANT_ACCEPT:
+#endif
+ error= SOCKET_EWOULDBLOCK;
+ break;
+ case SSL_ERROR_SSL:
+ /* Protocol error. */
+#ifdef EPROTO
+ error= EPROTO;
+#else
+ error= SOCKET_ECONNRESET;
+#endif
+ break;
+ case SSL_ERROR_SYSCALL:
+ case SSL_ERROR_NONE:
+ default:
+ break;
+ };
+
+ /* Set error status to a equivalent of the SSL error. */
+ if (error)
{
- DBUG_PRINT("error", ("OpenSSL: %s:%s:%d:%s\n", ERR_error_string(l,buf),
- file,line,(flags&ERR_TXT_STRING)?data:"")) ;
+#ifdef _WIN32
+ WSASetLastError(error);
+#else
+ errno= error;
+#endif
}
+}
- if (ssl)
+
+/**
+ Indicate whether a SSL I/O operation must be retried later.
+
+ @param vio VIO object representing a SSL connection.
+ @param ret Value returned by a SSL I/O function.
+ @param event[out] The type of I/O event to wait/retry.
+
+ @return Whether a SSL I/O operation should be deferred.
+ @retval TRUE Temporary failure, retry operation.
+ @retval FALSE Indeterminate failure.
+*/
+
+static my_bool ssl_should_retry(Vio *vio, int ret, enum enum_vio_io_event *event)
+{
+ int ssl_error;
+ SSL *ssl= vio->ssl_arg;
+ my_bool should_retry= TRUE;
+
+ /* Retrieve the result for the SSL I/O operation. */
+ ssl_error= SSL_get_error(ssl, ret);
+
+ /* Retrieve the result for the SSL I/O operation. */
+ switch (ssl_error)
{
-#ifndef DBUG_OFF
- int error= SSL_get_error(ssl, l);
- DBUG_PRINT("error", ("error: %s (%d)",
- ERR_error_string(error, buf), error));
-#endif
+ case SSL_ERROR_WANT_READ:
+ *event= VIO_IO_EVENT_READ;
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ *event= VIO_IO_EVENT_WRITE;
+ break;
+ default:
+ should_retry= FALSE;
+ ssl_set_sys_error(ssl_error);
+ break;
}
- DBUG_PRINT("info", ("socket_errno: %d", socket_errno));
- DBUG_VOID_RETURN;
+ return should_retry;
}
-#endif
-
-size_t vio_ssl_read(Vio *vio, uchar* buf, size_t size)
+size_t vio_ssl_read(Vio *vio, uchar *buf, size_t size)
{
- size_t r;
+ int ret;
+ SSL *ssl= vio->ssl_arg;
DBUG_ENTER("vio_ssl_read");
- DBUG_PRINT("enter", ("sd: %d buf: 0x%lx size: %u ssl: 0x%lx",
- vio->sd, (long) buf, (uint) size, (long) vio->ssl_arg));
+ DBUG_PRINT("enter", ("sd: %d buf: %p size: %d ssl: %p",
+ mysql_socket_getfd(vio->mysql_socket), buf, (int) size,
+ vio->ssl_arg));
if (vio->async_context && vio->async_context->active)
- r= my_ssl_read_async(vio->async_context, (SSL *)vio->ssl_arg, buf, size);
+ ret= my_ssl_read_async(vio->async_context, (SSL *)vio->ssl_arg, buf, size);
else
- r= SSL_read((SSL*) vio->ssl_arg, buf, size);
-#ifndef DBUG_OFF
- if (r == (size_t) -1)
- report_errors((SSL*) vio->ssl_arg);
-#endif
- DBUG_PRINT("exit", ("%u", (uint) r));
- DBUG_RETURN(r);
+ {
+ while ((ret= SSL_read(ssl, buf, size)) < 0)
+ {
+ enum enum_vio_io_event event;
+
+ /* Process the SSL I/O error. */
+ if (!ssl_should_retry(vio, ret, &event))
+ break;
+ /* Attempt to wait for an I/O event. */
+ if (vio_socket_io_wait(vio, event))
+ break;
+ }
+ }
+
+ DBUG_PRINT("exit", ("%d", (int) ret));
+ DBUG_RETURN(ret < 0 ? -1 : ret);
+
}
-size_t vio_ssl_write(Vio *vio, const uchar* buf, size_t size)
+size_t vio_ssl_write(Vio *vio, const uchar *buf, size_t size)
{
- size_t r;
+ int ret;
+ SSL *ssl= vio->ssl_arg;
DBUG_ENTER("vio_ssl_write");
- DBUG_PRINT("enter", ("sd: %d buf: 0x%lx size: %u", vio->sd,
- (long) buf, (uint) size));
+ DBUG_PRINT("enter", ("sd: %d buf: %p size: %d",
+ mysql_socket_getfd(vio->mysql_socket),
+ buf, (int) size));
if (vio->async_context && vio->async_context->active)
- r= my_ssl_write_async(vio->async_context, (SSL *)vio->ssl_arg, buf, size);
+ ret= my_ssl_write_async(vio->async_context, (SSL *)vio->ssl_arg, buf,
+ size);
else
- r= SSL_write((SSL*) vio->ssl_arg, buf, size);
-#ifndef DBUG_OFF
- if (r == (size_t) -1)
- report_errors((SSL*) vio->ssl_arg);
-#endif
- DBUG_PRINT("exit", ("%u", (uint) r));
- DBUG_RETURN(r);
+ {
+ while ((ret= SSL_write(ssl, buf, size)) < 0)
+ {
+ enum enum_vio_io_event event;
+
+ /* Process the SSL I/O error. */
+ if (!ssl_should_retry(vio, ret, &event))
+ break;
+
+ /* Attempt to wait for an I/O event. */
+ if (vio_socket_io_wait(vio, event))
+ break;
+ }
+ }
+
+ DBUG_RETURN(ret < 0 ? -1 : ret);
+}
+
+#ifdef HAVE_YASSL
+
+/* Emulate a blocking recv() call with vio_read(). */
+static long yassl_recv(void *ptr, void *buf, size_t len,
+ int flag __attribute__((unused)))
+{
+ return vio_read(ptr, buf, len);
}
+/* Emulate a blocking send() call with vio_write(). */
+static long yassl_send(void *ptr, const void *buf, size_t len,
+ int flag __attribute__((unused)))
+{
+ return vio_write(ptr, buf, len);
+}
+
+#endif
+
int vio_ssl_close(Vio *vio)
{
int r= 0;
@@ -144,7 +244,7 @@ int vio_ssl_close(Vio *vio)
break;
default: /* Shutdown failed */
DBUG_PRINT("vio_error", ("SSL_shutdown() failed, error: %d",
- (int)SSL_get_error(ssl, r)));
+ SSL_get_error(ssl, r)));
break;
}
}
@@ -170,16 +270,57 @@ void vio_ssl_delete(Vio *vio)
}
+/** SSL handshake handler. */
+typedef int (*ssl_handshake_func_t)(SSL*);
+
+
+/**
+ Loop and wait until a SSL handshake is completed.
+
+ @param vio VIO object representing a SSL connection.
+ @param ssl SSL structure for the connection.
+ @param func SSL handshake handler.
+
+ @return Return value is 1 on success.
+*/
+
+static int ssl_handshake_loop(Vio *vio, SSL *ssl, ssl_handshake_func_t func)
+{
+ int ret;
+
+ vio->ssl_arg= ssl;
+
+ /* Initiate the SSL handshake. */
+ while ((ret= func(ssl)) < 1)
+ {
+ enum enum_vio_io_event event;
+
+ /* Process the SSL I/O error. */
+ if (!ssl_should_retry(vio, ret, &event))
+ break;
+
+ /* Wait for I/O so that the handshake can proceed. */
+ if (vio_socket_io_wait(vio, event))
+ break;
+ }
+
+ vio->ssl_arg= NULL;
+
+ return ret;
+}
+
+
static int ssl_do(struct st_VioSSLFd *ptr, Vio *vio, long timeout,
- int (*connect_accept_func)(SSL*), unsigned long *errptr)
+ ssl_handshake_func_t func, unsigned long *errptr)
{
int r;
SSL *ssl;
my_bool unused;
my_bool was_blocking;
+ my_socket sd= mysql_socket_getfd(vio->mysql_socket);
DBUG_ENTER("ssl_do");
DBUG_PRINT("enter", ("ptr: 0x%lx, sd: %d ctx: 0x%lx",
- (long) ptr, vio->sd, (long) ptr->ssl_context));
+ (long) ptr, sd, (long) ptr->ssl_context));
/* Set socket to blocking if not already set */
vio_blocking(vio, 1, &was_blocking);
@@ -194,15 +335,30 @@ static int ssl_do(struct st_VioSSLFd *ptr, Vio *vio, long timeout,
DBUG_PRINT("info", ("ssl: 0x%lx timeout: %ld", (long) ssl, timeout));
SSL_clear(ssl);
SSL_SESSION_set_timeout(SSL_get_session(ssl), timeout);
- SSL_set_fd(ssl, vio->sd);
-#if !defined(HAVE_YASSL) && defined(SSL_OP_NO_COMPRESSION)
+ SSL_set_fd(ssl, sd);
+
+ /*
+ Since yaSSL does not support non-blocking send operations, use
+ special transport functions that properly handles non-blocking
+ sockets. These functions emulate the behavior of blocking I/O
+ operations by waiting for I/O to become available.
+ */
+#ifdef HAVE_YASSL
+ /* Set first argument of the transport functions. */
+ yaSSL_transport_set_ptr(ssl, vio);
+ /* Set functions to use in order to send and receive data. */
+ yaSSL_transport_set_recv_function(ssl, yassl_recv);
+ yaSSL_transport_set_send_function(ssl, yassl_send);
+#endif
+
+#if !defined(HAVE_YASSL) && defined(SSL_OP_NO_COMPRESSION)
SSL_set_options(ssl, SSL_OP_NO_COMPRESSION);
#endif
- if ((r= connect_accept_func(ssl)) < 1)
+ if ((r= ssl_handshake_loop(vio, ssl, func)) < 1)
{
DBUG_PRINT("error", ("SSL_connect/accept failure"));
- *errptr= SSL_get_error(ssl, r);
+ *errptr= SSL_errno(ssl, r);
SSL_free(ssl);
vio_blocking(vio, was_blocking, &unused);
DBUG_RETURN(1);
@@ -213,8 +369,11 @@ static int ssl_do(struct st_VioSSLFd *ptr, Vio *vio, long timeout,
change type, set sd to the fd used when connecting
and set pointer to the SSL structure
*/
- vio_reset(vio, VIO_TYPE_SSL, SSL_get_fd(ssl), 0, 0);
- vio->ssl_arg= (void*)ssl;
+ if (vio_reset(vio, VIO_TYPE_SSL, SSL_get_fd(ssl), ssl, 0))
+ {
+ vio_blocking(vio, was_blocking, &unused);
+ DBUG_RETURN(1);
+ }
#ifndef DBUG_OFF
{
diff --git a/vio/viosslfactories.c b/vio/viosslfactories.c
index 41c1dbbd0e4..0f685593f18 100644
--- a/vio/viosslfactories.c
+++ b/vio/viosslfactories.c
@@ -52,27 +52,6 @@ static DH *get_dh512(void)
}
-static void
-report_errors()
-{
- unsigned long l;
- const char* file;
- const char* data;
- int line,flags;
-
- DBUG_ENTER("report_errors");
-
- while ((l=ERR_get_error_line_data(&file,&line,&data,&flags)) != 0)
- {
-#ifndef DBUG_OFF /* Avoid warning */
- char buf[200];
- DBUG_PRINT("error", ("OpenSSL: %s:%s:%d:%s\n", ERR_error_string(l,buf),
- file,line,(flags & ERR_TXT_STRING) ? data : "")) ;
-#endif
- }
- DBUG_VOID_RETURN;
-}
-
static const char*
ssl_error_string[] =
{
@@ -170,7 +149,8 @@ static struct st_VioSSLFd *
new_VioSSLFd(const char *key_file, const char *cert_file,
const char *ca_file, const char *ca_path,
const char *cipher, my_bool is_client_method,
- enum enum_ssl_init_error* error)
+ enum enum_ssl_init_error *error,
+ const char *crl_file, const char *crl_path)
{
DH *dh;
struct st_VioSSLFd *ssl_fd;
@@ -178,12 +158,14 @@ new_VioSSLFd(const char *key_file, const char *cert_file,
DBUG_ENTER("new_VioSSLFd");
DBUG_PRINT("enter",
("key_file: '%s' cert_file: '%s' ca_file: '%s' ca_path: '%s' "
- "cipher: '%s'",
+ "cipher: '%s' crl_file: '%s' crl_path: '%s' ",
key_file ? key_file : "NULL",
cert_file ? cert_file : "NULL",
ca_file ? ca_file : "NULL",
ca_path ? ca_path : "NULL",
- cipher ? cipher : "NULL"));
+ cipher ? cipher : "NULL",
+ crl_file ? crl_file : "NULL",
+ crl_path ? crl_path : "NULL"));
check_ssl_init();
@@ -197,7 +179,6 @@ new_VioSSLFd(const char *key_file, const char *cert_file,
{
*error= SSL_INITERR_MEMFAIL;
DBUG_PRINT("error", ("%s", sslGetErrString(*error)));
- report_errors();
my_free(ssl_fd);
DBUG_RETURN(0);
}
@@ -214,7 +195,6 @@ new_VioSSLFd(const char *key_file, const char *cert_file,
{
*error= SSL_INITERR_CIPHERS;
DBUG_PRINT("error", ("%s", sslGetErrString(*error)));
- report_errors();
SSL_CTX_free(ssl_fd->ssl_context);
my_free(ssl_fd);
DBUG_RETURN(0);
@@ -231,7 +211,6 @@ new_VioSSLFd(const char *key_file, const char *cert_file,
*error= SSL_INITERR_BAD_PATHS;
DBUG_PRINT("error", ("SSL_CTX_load_verify_locations failed : %s",
sslGetErrString(*error)));
- report_errors();
SSL_CTX_free(ssl_fd->ssl_context);
my_free(ssl_fd);
DBUG_RETURN(0);
@@ -242,17 +221,38 @@ new_VioSSLFd(const char *key_file, const char *cert_file,
{
*error= SSL_INITERR_BAD_PATHS;
DBUG_PRINT("error", ("%s", sslGetErrString(*error)));
- report_errors();
SSL_CTX_free(ssl_fd->ssl_context);
my_free(ssl_fd);
DBUG_RETURN(0);
}
}
+ if (crl_file || crl_path)
+ {
+#ifdef HAVE_YASSL
+ DBUG_PRINT("warning", ("yaSSL doesn't support CRL"));
+ DBUG_ASSERT(0);
+#else
+ X509_STORE *store= SSL_CTX_get_cert_store(ssl_fd->ssl_context);
+ /* Load crls from the trusted ca */
+ if (X509_STORE_load_locations(store, crl_file, crl_path) == 0 ||
+ X509_STORE_set_flags(store,
+ X509_V_FLAG_CRL_CHECK |
+ X509_V_FLAG_CRL_CHECK_ALL) == 0)
+ {
+ DBUG_PRINT("warning", ("X509_STORE_load_locations for CRL failed"));
+ *error= SSL_INITERR_BAD_PATHS;
+ DBUG_PRINT("error", ("%s", sslGetErrString(*error)));
+ SSL_CTX_free(ssl_fd->ssl_context);
+ my_free(ssl_fd);
+ DBUG_RETURN(0);
+ }
+#endif
+ }
+
if (vio_set_cert_stuff(ssl_fd->ssl_context, cert_file, key_file, error))
{
DBUG_PRINT("error", ("vio_set_cert_stuff failed"));
- report_errors();
SSL_CTX_free(ssl_fd->ssl_context);
my_free(ssl_fd);
DBUG_RETURN(0);
@@ -273,7 +273,8 @@ new_VioSSLFd(const char *key_file, const char *cert_file,
struct st_VioSSLFd *
new_VioSSLConnectorFd(const char *key_file, const char *cert_file,
const char *ca_file, const char *ca_path,
- const char *cipher, enum enum_ssl_init_error* error)
+ const char *cipher, enum enum_ssl_init_error* error,
+ const char *crl_file, const char *crl_path)
{
struct st_VioSSLFd *ssl_fd;
int verify= SSL_VERIFY_PEER;
@@ -286,7 +287,8 @@ new_VioSSLConnectorFd(const char *key_file, const char *cert_file,
verify= SSL_VERIFY_NONE;
if (!(ssl_fd= new_VioSSLFd(key_file, cert_file, ca_file,
- ca_path, cipher, TRUE, error)))
+ ca_path, cipher, TRUE, error,
+ crl_file, crl_path)))
{
return 0;
}
@@ -303,12 +305,14 @@ new_VioSSLConnectorFd(const char *key_file, const char *cert_file,
struct st_VioSSLFd *
new_VioSSLAcceptorFd(const char *key_file, const char *cert_file,
const char *ca_file, const char *ca_path,
- const char *cipher, enum enum_ssl_init_error* error)
+ const char *cipher, enum enum_ssl_init_error* error,
+ const char *crl_file, const char *crl_path)
{
struct st_VioSSLFd *ssl_fd;
int verify= SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
if (!(ssl_fd= new_VioSSLFd(key_file, cert_file, ca_file,
- ca_path, cipher, FALSE, error)))
+ ca_path, cipher, FALSE, error,
+ crl_file, crl_path)))
{
return 0;
}