diff options
Diffstat (limited to 'vio/vio.c')
-rw-r--r-- | vio/vio.c | 365 |
1 files changed, 365 insertions, 0 deletions
diff --git a/vio/vio.c b/vio/vio.c new file mode 100644 index 00000000..7a98eb2a --- /dev/null +++ b/vio/vio.c @@ -0,0 +1,365 @@ +/* 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 + 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-1335 USA */ + +/* + Note that we can't have assertion on file descriptors; The reason for + this is that during mysql shutdown, another thread can close a file + we are working on. In this case we should just return read errors from + the file descriptior. +*/ + +#include "vio_priv.h" +#include "ssl_compat.h" + +PSI_memory_key key_memory_vio_ssl_fd; +PSI_memory_key key_memory_vio; +PSI_memory_key key_memory_vio_read_buffer; + +#ifdef HAVE_PSI_INTERFACE +static PSI_memory_info all_vio_memory[]= +{ + {&key_memory_vio_ssl_fd, "ssl_fd", 0}, + {&key_memory_vio, "vio", 0}, + {&key_memory_vio_read_buffer, "read_buffer", 0}, +}; + +void init_vio_psi_keys() +{ + const char* category= "vio"; + int count; + + count= array_elements(all_vio_memory); + mysql_memory_register(category, all_vio_memory, count); +} +#endif + +#ifdef _WIN32 + +/** + 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 1 The requested I/O event has occurred. +*/ + +static int no_io_wait(Vio *vio __attribute__((unused)), + enum enum_vio_io_event event __attribute__((unused)), + int timeout __attribute__((unused))) +{ + return 1; +} + +#endif + +static my_bool has_no_data(Vio *vio __attribute__((unused))) +{ + return FALSE; +} + +#ifdef _WIN32 +int vio_pipe_shutdown(Vio *vio, int how) +{ + vio->shutdown_flag= how; + return CancelIoEx(vio->hPipe, NULL); +} +#endif + +/* + * Helper to fill most of the Vio* with defaults. + */ + +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, (int)sd, flags)); + +#ifndef HAVE_VIO_READ_BUFF + flags&= ~VIO_BUFFERED_READ; +#endif + 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(key_memory_vio_read_buffer, + VIO_READ_BUFFER_SIZE, MYF(MY_WME)))) + flags&= ~VIO_BUFFERED_READ; +#ifdef _WIN32 + if (type == VIO_TYPE_NAMEDPIPE) + { + vio->viodelete =vio_delete; + vio->vioerrno =vio_errno; + vio->read =vio_read_pipe; + vio->write =vio_write_pipe; + vio->fastsend =vio_fastsend; + vio->viokeepalive =vio_keepalive; + vio->should_retry =vio_should_retry; + 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->io_wait =no_io_wait; + vio->is_connected =vio_is_connected_pipe; + vio->has_data =has_no_data; + vio->shutdown =vio_pipe_shutdown; + DBUG_VOID_RETURN; + } +#endif + +#ifdef HAVE_OPENSSL + if (type == VIO_TYPE_SSL) + { + vio->viodelete =vio_ssl_delete; + vio->vioerrno =vio_errno; + vio->read =vio_ssl_read; + vio->write =vio_ssl_write; + vio->fastsend =vio_fastsend; + vio->viokeepalive =vio_keepalive; + vio->should_retry =vio_should_retry; + 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->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 */ + vio->viodelete =vio_delete; + vio->vioerrno =vio_errno; + vio->read= (flags & VIO_BUFFERED_READ) ? vio_read_buff : vio_read; + vio->write =vio_write; + vio->fastsend =vio_fastsend; + vio->viokeepalive =vio_keepalive; + vio->should_retry =vio_should_retry; + 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->io_wait =vio_io_wait; + vio->is_connected =vio_is_connected; + vio->shutdown =vio_socket_shutdown; + vio->timeout =vio_socket_timeout; + vio->has_data = ((flags & VIO_BUFFERED_READ) ? + vio_buff_has_data : has_no_data); + DBUG_VOID_RETURN; +} + + +/** + 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. + + @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, 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. + + note: old_vio.read_timeout/old_vio.write_timeout is stored in ms + but vio_timeout() takes seconds as argument, hence the / 1000 + */ + if (old_vio.read_timeout >= 0) + ret|= vio_timeout(vio, 0, old_vio.read_timeout / 1000); + + if (old_vio.write_timeout >= 0) + ret|= vio_timeout(vio, 1, old_vio.write_timeout / 1000); + + DBUG_RETURN(MY_TEST(ret)); +} + + +/* Create a new VIO for socket or TCP/IP connection. */ + +Vio *mysql_socket_vio_new(MYSQL_SOCKET mysql_socket, enum enum_vio_type type, uint flags) +{ + Vio *vio; + my_socket sd= mysql_socket_getfd(mysql_socket); + DBUG_ENTER("mysql_socket_vio_new"); + DBUG_PRINT("enter", ("sd: %d", (int)sd)); + if ((vio = (Vio*) my_malloc(key_memory_vio, sizeof(*vio), MYF(MY_WME)))) + { + 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", (int)sd)); + + mysql_socket_setfd(&mysql_socket, sd); + vio = mysql_socket_vio_new(mysql_socket, type, flags); + + DBUG_RETURN(vio); +} + +#ifdef _WIN32 + +Vio *vio_new_win32pipe(HANDLE hPipe) +{ + Vio *vio; + DBUG_ENTER("vio_new_handle"); + if ((vio = (Vio*) my_malloc(PSI_INSTRUMENT_ME, sizeof(Vio),MYF(MY_WME)))) + { + 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); +} + + +#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) + return; /* It must be safe to delete null pointers. */ + + if (vio->type != VIO_CLOSED) + vio->vioclose(vio); + my_free(vio->read_buffer); + my_free(vio); +} + + +/* + Cleanup memory allocated by vio or the + components below it when application finish + +*/ +void vio_end(void) +{ +#ifdef HAVE_WOLFSSL + wolfSSL_Cleanup(); +#elif defined(HAVE_OPENSSL) + // This one is needed on the client side + ERR_remove_state(0); + ERR_free_strings(); + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); +#endif +} |