mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 04:46:15 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			365 lines
		
	
	
	
		
			9.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			365 lines
		
	
	
	
		
			9.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* 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
 | 
						|
}
 |