summaryrefslogtreecommitdiffstats
path: root/vio
diff options
context:
space:
mode:
Diffstat (limited to 'vio')
-rw-r--r--vio/CMakeLists.txt22
-rw-r--r--vio/docs/COPYING.openssl127
-rw-r--r--vio/docs/INSTALL3
-rw-r--r--vio/test-ssl.c152
-rw-r--r--vio/test-sslclient.c105
-rw-r--r--vio/test-sslserver.c156
-rw-r--r--vio/vio.c365
-rw-r--r--vio/vio_priv.h62
-rw-r--r--vio/viopipe.c160
-rw-r--r--vio/viosocket.c1301
-rw-r--r--vio/viossl.c409
-rw-r--r--vio/viosslfactories.c526
-rw-r--r--vio/viotest-ssl.c154
-rw-r--r--vio/viotest-sslconnect.cc95
-rw-r--r--vio/viotest.cc63
15 files changed, 3700 insertions, 0 deletions
diff --git a/vio/CMakeLists.txt b/vio/CMakeLists.txt
new file mode 100644
index 00000000..85810840
--- /dev/null
+++ b/vio/CMakeLists.txt
@@ -0,0 +1,22 @@
+# Copyright (c) 2006, 2014, 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-1335 USA
+
+INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include
+${SSL_INCLUDE_DIRS})
+ADD_DEFINITIONS(${SSL_DEFINES})
+
+SET(VIO_SOURCES vio.c viosocket.c viossl.c viopipe.c viosslfactories.c)
+ADD_CONVENIENCE_LIBRARY(vio ${VIO_SOURCES})
+TARGET_LINK_LIBRARIES(vio ${LIBSOCKET})
diff --git a/vio/docs/COPYING.openssl b/vio/docs/COPYING.openssl
new file mode 100644
index 00000000..b9e18d5e
--- /dev/null
+++ b/vio/docs/COPYING.openssl
@@ -0,0 +1,127 @@
+
+ LICENSE ISSUES
+ ==============
+
+ The OpenSSL toolkit stays under a dual license, i.e. both the conditions of
+ the OpenSSL License and the original SSLeay license apply to the toolkit.
+ See below for the actual license texts. Actually both licenses are BSD-style
+ Open Source licenses. In case of any license issues related to OpenSSL
+ please contact openssl-core@openssl.org.
+
+ OpenSSL License
+ ---------------
+
+/* ====================================================================
+ * Copyright (c) 1998-1999 The OpenSSL Project. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ * nor may "OpenSSL" appear in their names without prior written
+ * permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com). This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+ Original SSLeay License
+ -----------------------
+
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the rouines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
diff --git a/vio/docs/INSTALL b/vio/docs/INSTALL
new file mode 100644
index 00000000..bab073d6
--- /dev/null
+++ b/vio/docs/INSTALL
@@ -0,0 +1,3 @@
+As the Vio is currently used only as part of MySQL, separate
+installation isn't currently supported.
+
diff --git a/vio/test-ssl.c b/vio/test-ssl.c
new file mode 100644
index 00000000..dc116e7e
--- /dev/null
+++ b/vio/test-ssl.c
@@ -0,0 +1,152 @@
+/* Copyright (c) 2000, 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-1335 USA */
+
+#include <my_global.h>
+#if defined(HAVE_OPENSSL)
+#include <my_sys.h>
+#include <m_string.h>
+#include <m_ctype.h>
+#include "mysql.h"
+#include "errmsg.h"
+#include <my_dir.h>
+#include <my_getopt.h>
+#include <signal.h>
+#include <violite.h>
+
+const char *VER="0.2";
+
+
+#ifndef DBUG_OFF
+const char *default_dbug_option="d:t:O,-";
+#endif
+
+void
+fatal_error( const char* r)
+{
+ perror(r);
+ exit(0);
+}
+
+void
+print_usage()
+{
+ printf("viossl-test: testing SSL virtual IO. Usage:\n");
+ printf("viossl-test server-key server-cert client-key client-cert [CAfile] [CApath]\n");
+}
+
+
+int
+main(int argc, char** argv)
+{
+ char* server_key = 0, *server_cert = 0;
+ char* client_key = 0, *client_cert = 0;
+ char* ca_file = 0, *ca_path = 0;
+ char* cipher=0;
+ int child_pid,sv[2];
+ my_bool unused;
+ struct st_VioSSLFd* ssl_acceptor= 0;
+ struct st_VioSSLFd* ssl_connector= 0;
+ Vio* client_vio=0, *server_vio=0;
+ enum enum_ssl_init_error ssl_init_error;
+ unsigned long ssl_error;
+
+ MY_INIT(argv[0]);
+ DBUG_PROCESS(argv[0]);
+ DBUG_PUSH(default_dbug_option);
+
+ if (argc<5)
+ {
+ print_usage();
+ return 1;
+ }
+
+ server_key = argv[1];
+ server_cert = argv[2];
+ client_key = argv[3];
+ client_cert = argv[4];
+ if (argc>5)
+ ca_file = argv[5];
+ if (argc>6)
+ ca_path = argv[6];
+ printf("Server key/cert : %s/%s\n", server_key, server_cert);
+ printf("Client key/cert : %s/%s\n", client_key, client_cert);
+ if (ca_file!=0)
+ printf("CAfile : %s\n", ca_file);
+ if (ca_path!=0)
+ printf("CApath : %s\n", ca_path);
+
+
+ if (socketpair(PF_UNIX, SOCK_STREAM, IPPROTO_IP, sv)==-1)
+ fatal_error("socketpair");
+
+ ssl_acceptor = new_VioSSLAcceptorFd(server_key, server_cert, ca_file,
+ ca_path, cipher);
+ ssl_connector = new_VioSSLConnectorFd(client_key, client_cert, ca_file,
+ ca_path, cipher, &ssl_init_error);
+
+ client_vio = (struct st_vio*)my_malloc(sizeof(struct st_vio),MYF(0));
+ client_vio->sd = sv[0];
+ client_vio->vioblocking(client_vio, 0, &unused);
+ sslconnect(ssl_connector,client_vio,60L,&ssl_error);
+ server_vio = (struct st_vio*)my_malloc(sizeof(struct st_vio),MYF(0));
+ server_vio->sd = sv[1];
+ server_vio->vioblocking(client_vio, 0, &unused);
+ sslaccept(ssl_acceptor,server_vio,60L, &ssl_error);
+
+ printf("Socketpair: %d , %d\n", client_vio->sd, server_vio->sd);
+
+ child_pid = fork();
+ if (child_pid==-1) {
+ my_free(ssl_acceptor);
+ my_free(ssl_connector);
+ fatal_error("fork");
+ }
+ if (child_pid==0)
+ {
+ /* child, therefore, client */
+ char xbuf[100];
+ int r = vio_read(client_vio,xbuf, sizeof(xbuf));
+ if (r<=0) {
+ my_free(ssl_acceptor);
+ my_free(ssl_connector);
+ fatal_error("client:SSL_read");
+ }
+ xbuf[r] = 0;
+ printf("client:got %s\n", xbuf);
+ my_free(client_vio);
+ my_free(ssl_acceptor);
+ my_free(ssl_connector);
+ }
+ else
+ {
+ const char* s = "Huhuhuh";
+ int r = vio_write(server_vio,(uchar*)s, strlen(s));
+ if (r<=0) {
+ my_free(ssl_acceptor);
+ my_free(ssl_connector);
+ fatal_error("server:SSL_write");
+ }
+ my_free(server_vio);
+ my_free(ssl_acceptor);
+ my_free(ssl_connector);
+ }
+ return 0;
+}
+#else /* HAVE_OPENSSL */
+
+int main() {
+return 0;
+}
+#endif /* HAVE_OPENSSL */
diff --git a/vio/test-sslclient.c b/vio/test-sslclient.c
new file mode 100644
index 00000000..487c7b92
--- /dev/null
+++ b/vio/test-sslclient.c
@@ -0,0 +1,105 @@
+/* Copyright (c) 2000, 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-1335 USA */
+
+#include <my_global.h>
+#ifdef HAVE_OPENSSL
+#include <my_sys.h>
+#include <m_string.h>
+#include <m_ctype.h>
+#include "mysql.h"
+#include "errmsg.h"
+#include <my_dir.h>
+#include <my_getopt.h>
+#include <signal.h>
+#include <violite.h>
+
+const char *VER="0.2";
+
+
+#ifndef DBUG_OFF
+const char *default_dbug_option="d:t:O,-";
+#endif
+
+void
+fatal_error( const char* r)
+{
+ perror(r);
+ exit(0);
+}
+
+int
+main( int argc __attribute__((unused)),
+ char** argv)
+{
+ char client_key[] = "../SSL/client-key.pem", client_cert[] = "../SSL/client-cert.pem";
+ char ca_file[] = "../SSL/cacert.pem", *ca_path = 0, *cipher=0;
+ struct st_VioSSLFd* ssl_connector= 0;
+ struct sockaddr_in sa;
+ Vio* client_vio=0;
+ int err;
+ char xbuf[100]="Ohohhhhoh1234";
+ enum enum_ssl_init_error ssl_init_error;
+ unsigned long ssl_error;
+
+ MY_INIT(argv[0]);
+ DBUG_PROCESS(argv[0]);
+ DBUG_PUSH(default_dbug_option);
+
+ printf("Client key/cert : %s/%s\n", client_key, client_cert);
+ if (ca_file!=0)
+ printf("CAfile : %s\n", ca_file);
+ if (ca_path!=0)
+ printf("CApath : %s\n", ca_path);
+
+ ssl_connector = new_VioSSLConnectorFd(client_key, client_cert, ca_file, ca_path, cipher,
+ &ssl_init_error);
+ if(!ssl_connector) {
+ fatal_error("client:new_VioSSLConnectorFd failed");
+ }
+
+ /* ----------------------------------------------- */
+ /* Create a socket and connect to server using normal socket calls. */
+
+ client_vio = vio_new(socket (AF_INET, SOCK_STREAM, 0), VIO_TYPE_TCPIP, TRUE);
+
+ memset (&sa, '\0', sizeof(sa));
+ sa.sin_family = AF_INET;
+ sa.sin_addr.s_addr = inet_addr ("127.0.0.1"); /* Server IP */
+ sa.sin_port = htons (1111); /* Server Port number */
+
+ err = connect(client_vio->sd, (struct sockaddr*) &sa,
+ sizeof(sa));
+
+ /* ----------------------------------------------- */
+ /* Now we have TCP conncetion. Start SSL negotiation. */
+ read(client_vio->sd,xbuf, sizeof(xbuf));
+ sslconnect(ssl_connector,client_vio,60L,&ssl_error);
+ err = vio_read(client_vio,xbuf, sizeof(xbuf));
+ if (err<=0) {
+ my_free(ssl_connector);
+ fatal_error("client:SSL_read");
+ }
+ xbuf[err] = 0;
+ printf("client:got %s\n", xbuf);
+ my_free(client_vio);
+ my_free(ssl_connector);
+ return 0;
+}
+#else /* HAVE_OPENSSL */
+
+int main() {
+return 0;
+}
+#endif /* HAVE_OPENSSL */
diff --git a/vio/test-sslserver.c b/vio/test-sslserver.c
new file mode 100644
index 00000000..8d63fd16
--- /dev/null
+++ b/vio/test-sslserver.c
@@ -0,0 +1,156 @@
+/* Copyright (c) 2000, 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-1335 USA */
+
+#include <my_global.h>
+#ifdef HAVE_OPENSSL
+#include <my_sys.h>
+#include <m_string.h>
+#include <m_ctype.h>
+#include "mysql.h"
+#include "errmsg.h"
+#include <my_dir.h>
+#include <my_getopt.h>
+#include <signal.h>
+#include <violite.h>
+
+const char *VER="0.2";
+
+
+#ifndef DBUG_OFF
+const char *default_dbug_option="d:t:O,-";
+#endif
+
+#if 0
+static void
+fatal_error( const char* r)
+{
+ perror(r);
+ exit(0);
+}
+#endif
+
+typedef struct {
+ int sd;
+ struct st_VioSSLFd* ssl_acceptor;
+} TH_ARGS;
+
+static void
+do_ssl_stuff( TH_ARGS* args)
+{
+ const char* s = "Huhuhuhuuu";
+ Vio* server_vio;
+ int err;
+ unsigned long ssl_error;
+ DBUG_ENTER("do_ssl_stuff");
+
+ server_vio = vio_new(args->sd, VIO_TYPE_TCPIP, TRUE);
+
+ /* ----------------------------------------------- */
+ /* TCP connection is ready. Do server side SSL. */
+
+ err = write(server_vio->sd,(uchar*)s, strlen(s));
+ sslaccept(args->ssl_acceptor,server_vio,60L,&ssl_error);
+ err = server_vio->write(server_vio,(uchar*)s, strlen(s));
+ DBUG_VOID_RETURN;
+}
+
+static void*
+client_thread( void* arg)
+{
+ my_thread_init();
+ do_ssl_stuff((TH_ARGS*)arg);
+ return 0;
+}
+
+int
+main(int argc __attribute__((unused)), char** argv)
+{
+ char server_key[] = "../SSL/server-key.pem",
+ server_cert[] = "../SSL/server-cert.pem";
+ char ca_file[] = "../SSL/cacert.pem",
+ *ca_path = 0,
+ *cipher = 0;
+ struct st_VioSSLFd* ssl_acceptor;
+ pthread_t th;
+ TH_ARGS th_args;
+
+
+ struct sockaddr_in sa_serv;
+ struct sockaddr_in sa_cli;
+ int listen_sd;
+ int err;
+ size_socket client_len;
+ int reuseaddr = 1; /* better testing, uh? */
+
+ MY_INIT(argv[0]);
+ DBUG_PROCESS(argv[0]);
+ DBUG_PUSH(default_dbug_option);
+
+ printf("Server key/cert : %s/%s\n", server_key, server_cert);
+ if (ca_file!=0)
+
+ printf("CAfile : %s\n", ca_file);
+ if (ca_path!=0)
+ printf("CApath : %s\n", ca_path);
+
+ th_args.ssl_acceptor = ssl_acceptor = new_VioSSLAcceptorFd(server_key, server_cert, ca_file, ca_path,cipher);
+
+ /* ----------------------------------------------- */
+ /* Prepare TCP socket for receiving connections */
+
+ listen_sd = socket (AF_INET, SOCK_STREAM, 0);
+ setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(&reuseaddr));
+
+ memset (&sa_serv, '\0', sizeof(sa_serv));
+ sa_serv.sin_family = AF_INET;
+ sa_serv.sin_addr.s_addr = INADDR_ANY;
+ sa_serv.sin_port = htons (1111); /* Server Port number */
+
+ err = bind(listen_sd, (struct sockaddr*) &sa_serv,
+ sizeof (sa_serv));
+
+ /* Receive a TCP connection. */
+
+ err = listen (listen_sd, 5);
+ client_len = sizeof(sa_cli);
+ th_args.sd = accept (listen_sd, (struct sockaddr*) &sa_cli, &client_len);
+ close (listen_sd);
+
+ printf ("Connection from %lx, port %x\n",
+ (long)sa_cli.sin_addr.s_addr, sa_cli.sin_port);
+
+ /* ----------------------------------------------- */
+ /* TCP connection is ready. Do server side SSL. */
+
+ err = pthread_create(&th, NULL, client_thread, (void*)&th_args);
+ DBUG_PRINT("info", ("pthread_create: %d", err));
+ pthread_join(th, NULL);
+
+#if 0
+ if (err<=0) {
+ my_free(ssl_acceptor);
+ fatal_error("server:SSL_write");
+ }
+#endif /* 0 */
+
+ my_free(ssl_acceptor);
+ return 0;
+}
+#else /* HAVE_OPENSSL */
+
+int main() {
+return 0;
+}
+#endif /* HAVE_OPENSSL */
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
+}
diff --git a/vio/vio_priv.h b/vio/vio_priv.h
new file mode 100644
index 00000000..55c34be9
--- /dev/null
+++ b/vio/vio_priv.h
@@ -0,0 +1,62 @@
+/* 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
+ 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 */
+
+#ifndef VIO_PRIV_INCLUDED
+#define VIO_PRIV_INCLUDED
+
+/* Structures and functions private to the vio package */
+
+#define DONT_MAP_VIO
+#include <my_global.h>
+#include <mysql_com.h>
+#include <my_sys.h>
+#include <m_string.h>
+#include <violite.h>
+
+extern PSI_memory_key key_memory_vio;
+extern PSI_memory_key key_memory_vio_read_buffer;
+extern PSI_memory_key key_memory_vio_ssl_fd;
+
+#ifdef _WIN32
+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);
+int vio_close_pipe(Vio * vio);
+int cancel_io(HANDLE handle, DWORD thread_id);
+int vio_shutdown_pipe(Vio *vio,int how);
+uint vio_pending_pipe(Vio* vio);
+#endif
+
+
+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 */
+
+size_t vio_ssl_read(Vio *vio,uchar* buf, size_t size);
+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 */
+#endif /* VIO_PRIV_INCLUDED */
diff --git a/vio/viopipe.c b/vio/viopipe.c
new file mode 100644
index 00000000..aeaad311
--- /dev/null
+++ b/vio/viopipe.c
@@ -0,0 +1,160 @@
+/* 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-1335 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");
+
+ if (vio->shutdown_flag)
+ return ret;
+
+ disable_iocp_notification(&vio->overlapped);
+
+ /* Attempt to read from the pipe (overlapped I/O). */
+ if (ReadFile(vio->hPipe, buf, (DWORD)count, &transferred, &vio->overlapped))
+ {
+ /* The operation completed immediately. */
+ ret= transferred;
+ }
+ /* Read operation is pending completion asynchronously? */
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ if (vio->shutdown_flag)
+ CancelIo(vio->hPipe);
+ 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");
+
+ if (vio->shutdown_flag == SHUT_RDWR)
+ return ret;
+ disable_iocp_notification(&vio->overlapped);
+ /* Attempt to write to the pipe (overlapped I/O). */
+ if (WriteFile(vio->hPipe, buf, (DWORD)count, &transferred, &vio->overlapped))
+ {
+ /* The operation completed immediately. */
+ ret= transferred;
+ }
+ /* Write operation is pending completion asynchronously? */
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ if (vio->shutdown_flag == SHUT_RDWR)
+ CancelIo(vio->hPipe);
+ 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");
+
+ CloseHandle(vio->overlapped.hEvent);
+ ret= CloseHandle(vio->hPipe);
+
+ vio->type= VIO_CLOSED;
+ vio->hPipe= NULL;
+ vio->mysql_socket= MYSQL_INVALID_SOCKET;
+
+ DBUG_RETURN(ret);
+}
+
+/* return number of bytes readable from pipe.*/
+uint vio_pending_pipe(Vio *vio)
+{
+ DWORD bytes;
+ return PeekNamedPipe(vio->hPipe, NULL, 0, NULL, &bytes, NULL) ? bytes : 0;
+}
+#endif
+
diff --git a/vio/viosocket.c b/vio/viosocket.c
new file mode 100644
index 00000000..002ff274
--- /dev/null
+++ b/vio/viosocket.c
@@ -0,0 +1,1301 @@
+/*
+ Copyright (c) 2001, 2012, Oracle and/or its affiliates
+ 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"
+#ifdef _WIN32
+ #include <winsock2.h>
+ #include <MSWSock.h>
+ #include <mstcpip.h>
+ #pragma comment(lib, "ws2_32.lib")
+#endif
+
+#ifdef FIONREAD_IN_SYS_FILIO
+# 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)))
+{
+ /* These transport types are not Winsock based. */
+#ifdef _WIN32
+ if (vio->type == VIO_TYPE_NAMEDPIPE)
+ return GetLastError();
+#endif
+
+ /* Mapped to WSAGetLastError() on Win32. */
+ return socket_errno;
+}
+
+static int vio_set_linger(my_socket s, unsigned short timeout_sec)
+{
+ struct linger s_linger;
+ int ret;
+ s_linger.l_onoff = 1;
+ s_linger.l_linger = timeout_sec;
+ ret = setsockopt(s, SOL_SOCKET, SO_LINGER, (const char *)&s_linger, (int)sizeof(s_linger));
+ return ret;
+}
+
+
+/**
+ 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;
+ vio_set_linger(vio->mysql_socket.fd, 0);
+ break;
+ default:
+ /* A positive value indicates an I/O event. */
+ ret= 0;
+ break;
+ }
+
+ return ret;
+}
+
+
+/*
+ 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
+
+#ifndef SOCKET_EAGAIN
+#define SOCKET_EAGAIN SOCKET_EWOULDBLOCK
+#endif
+
+/*
+ returns number of bytes read or -1 in case of an error
+*/
+size_t vio_read(Vio *vio, uchar *buf, size_t size)
+{
+ ssize_t ret;
+ int flags= 0;
+ DBUG_ENTER("vio_read");
+ DBUG_PRINT("enter", ("sd: %d buf: %p size: %zu",
+ (int)mysql_socket_getfd(vio->mysql_socket), buf,
+ size));
+
+ /* 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;
+
+ 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 (ret == -1)
+ {
+ DBUG_PRINT("vio_error", ("Got error %d during read", errno));
+ }
+#ifndef DEBUG_DATA_PACKETS
+ else
+ {
+ DBUG_DUMP("read_data", buf, ret);
+ }
+#endif /* DEBUG_DATA_PACKETS */
+#endif /* DBUG_OFF */
+ DBUG_PRINT("exit", ("%d", (int) ret));
+ DBUG_RETURN(ret);
+}
+
+
+/*
+ Buffered read: if average read size is small it may
+ reduce number of syscalls.
+*/
+
+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: %p size:%zu",
+ (int)mysql_socket_getfd(vio->mysql_socket),
+ buf, size));
+
+ if (vio->read_pos < vio->read_end)
+ {
+ rc= MY_MIN((size_t) (vio->read_end - vio->read_pos), size);
+ memcpy(buf, vio->read_pos, rc);
+ vio->read_pos+= rc;
+ /*
+ Do not try to read from the socket now even if rc < size:
+ vio_read can return -1 due to an error or non-blocking mode, and
+ the safest way to handle it is to move to a separate branch.
+ */
+ }
+ else if (size < VIO_UNBUFFERED_READ_MIN_SIZE)
+ {
+ rc= vio_read(vio, (uchar*) vio->read_buffer, VIO_READ_BUFFER_SIZE);
+ if (rc != 0 && rc != (size_t) -1)
+ {
+ if (rc > size)
+ {
+ vio->read_pos= vio->read_buffer + size;
+ vio->read_end= vio->read_buffer + rc;
+ rc= size;
+ }
+ memcpy(buf, vio->read_buffer, rc);
+ }
+ }
+ else
+ rc= vio_read(vio, buf, size);
+ DBUG_RETURN(rc);
+#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)
+{
+ ssize_t ret;
+ int flags= 0;
+ DBUG_ENTER("vio_write");
+ DBUG_PRINT("enter", ("sd: %d buf: %p size: %zu",
+ (int)mysql_socket_getfd(vio->mysql_socket), buf,
+ size));
+
+ /* If timeout is enabled, do not block. */
+ if (vio->write_timeout >= 0)
+ flags= VIO_DONTWAIT;
+
+ 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 (ret == -1)
+ {
+ DBUG_PRINT("vio_error", ("Got error on write: %d",socket_errno));
+ }
+#endif /* DBUG_OFF */
+ DBUG_PRINT("exit", ("%d", (int) ret));
+ DBUG_RETURN(ret);
+}
+
+int vio_socket_shutdown(Vio *vio, int 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) CancelIoEx((HANDLE)mysql_socket_getfd(vio->mysql_socket), NULL);
+#endif
+ return ret;
+}
+
+
+int vio_blocking(Vio *vio, my_bool set_blocking_mode, my_bool *old_mode)
+{
+ int r= 0;
+#if defined(_WIN32) || !defined(NO_FCNTL_NONBLOCK)
+ my_socket sd= mysql_socket_getfd(vio->mysql_socket);
+#endif
+ DBUG_ENTER("vio_blocking");
+
+ *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(_WIN32)
+#if !defined(NO_FCNTL_NONBLOCK)
+ if (sd >= 0)
+ {
+ 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(sd, F_SETFL, vio->fcntl_mode);
+ if (r == -1)
+ {
+ DBUG_PRINT("info", ("fcntl failed, errno %d", errno));
+ vio->fcntl_mode= old_fcntl;
+ }
+ }
+ }
+#else
+ r= set_blocking_mode ? 0 : 1;
+#endif /* !defined(NO_FCNTL_NONBLOCK) */
+#else /* !defined(_WIN32) */
+ if (vio->type != VIO_TYPE_NAMEDPIPE)
+ {
+ ulong arg;
+ int old_fcntl=vio->fcntl_mode;
+ if (set_blocking_mode)
+ {
+ arg = 0;
+ vio->fcntl_mode &= ~O_NONBLOCK; /* clear bit */
+ }
+ else
+ {
+ arg = 1;
+ vio->fcntl_mode |= O_NONBLOCK; /* set bit */
+ }
+ if (old_fcntl != vio->fcntl_mode)
+ r = ioctlsocket(sd,FIONBIO,(void*) &arg);
+ }
+ else
+ r= MY_TEST(!(vio->fcntl_mode & O_NONBLOCK)) != set_blocking_mode;
+#endif /* !defined(_WIN32) */
+ DBUG_PRINT("exit", ("%d", r));
+ DBUG_RETURN(r);
+}
+
+/*
+ Check if vio is blocking
+
+ @retval 0 is not blocking
+ @retval 1 is blocking
+*/
+
+my_bool
+vio_is_blocking(Vio * vio)
+{
+ my_bool r;
+ DBUG_ENTER("vio_is_blocking");
+ r = !(vio->fcntl_mode & O_NONBLOCK);
+ DBUG_PRINT("exit", ("%d", (int) r));
+ DBUG_RETURN(r);
+}
+
+
+int vio_socket_timeout(Vio *vio,
+ uint which __attribute__((unused)),
+ my_bool old_mode __attribute__((unused)))
+{
+ int ret= 0;
+ DBUG_ENTER("vio_socket_timeout");
+ /*
+ 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);
+ }
+
+ DBUG_RETURN(ret);
+}
+
+/* Set TCP_NODELAY (disable Nagle's algorithm */
+int vio_nodelay(Vio *vio, my_bool on)
+{
+ int r;
+ int no_delay= MY_TEST(on);
+ DBUG_ENTER("vio_nodelay");
+
+ if (vio->type == VIO_TYPE_NAMEDPIPE || vio->type == VIO_TYPE_SOCKET)
+ {
+ DBUG_RETURN(0);
+ }
+
+ r = mysql_socket_setsockopt(vio->mysql_socket, IPPROTO_TCP, TCP_NODELAY,
+ IF_WIN((const char*), (void*)) &no_delay,
+ sizeof(no_delay));
+
+ if (r)
+ {
+ DBUG_PRINT("warning",
+ ("Couldn't set socket option for fast send, error %d",
+ socket_errno));
+ r = -1;
+ }
+ DBUG_PRINT("exit", ("%d", r));
+ DBUG_RETURN(r);
+}
+
+int vio_fastsend(Vio * vio)
+{
+ int r=0;
+ DBUG_ENTER("vio_fastsend");
+
+ if (vio->type == VIO_TYPE_NAMEDPIPE)
+ {
+ DBUG_RETURN(0);
+ }
+
+#if defined(IPTOS_THROUGHPUT)
+ {
+ int tos = IPTOS_THROUGHPUT;
+ r= mysql_socket_setsockopt(vio->mysql_socket, IPPROTO_IP, IP_TOS,
+ (void *)&tos, sizeof(tos));
+ }
+#endif /* IPTOS_THROUGHPUT */
+ if (!r)
+ r = vio_nodelay(vio, TRUE);
+ if (r)
+ {
+ DBUG_PRINT("warning",
+ ("Couldn't set socket option for fast send, error %d",
+ socket_errno));
+ r= -1;
+ }
+ DBUG_PRINT("exit", ("%d", r));
+ DBUG_RETURN(r);
+}
+
+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",
+ (int)mysql_socket_getfd(vio->mysql_socket),
+ (int)set_keep_alive));
+
+ if (vio->type != VIO_TYPE_NAMEDPIPE)
+ {
+ if (set_keep_alive)
+ opt = 1;
+ r = mysql_socket_setsockopt(vio->mysql_socket, SOL_SOCKET, SO_KEEPALIVE,
+ (char *)&opt, sizeof(opt));
+ }
+ DBUG_RETURN(r);
+}
+
+/*
+ Set socket options for keepalive e.g., TCP_KEEPCNT, TCP_KEEPIDLE/TCP_KEEPALIVE, TCP_KEEPINTVL
+*/
+int vio_set_keepalive_options(Vio* vio, const struct vio_keepalive_opts *opts)
+{
+#if defined _WIN32
+ struct tcp_keepalive s;
+ DWORD nbytes;
+
+ if (vio->type == VIO_TYPE_NAMEDPIPE)
+ return 0;
+
+ if (!opts->idle && !opts->interval)
+ return 0;
+
+ s.onoff= 1;
+ s.keepalivetime= opts->idle? opts->idle * 1000 : 7200;
+ s.keepaliveinterval= opts->interval?opts->interval * 1000 : 1;
+
+ return WSAIoctl(vio->mysql_socket.fd, SIO_KEEPALIVE_VALS, (LPVOID) &s, sizeof(s),
+ NULL, 0, &nbytes, NULL, NULL);
+
+#elif defined (TCP_KEEPIDLE) || defined (TCP_KEEPALIVE)
+
+ int ret= 0;
+ if (opts->idle)
+ {
+#ifdef TCP_KEEPIDLE // Linux only
+ ret= mysql_socket_setsockopt(vio->mysql_socket, IPPROTO_TCP, TCP_KEEPIDLE, (char *)&opts->idle, sizeof(opts->idle));
+#elif defined (TCP_KEEPALIVE)
+ ret= mysql_socket_setsockopt(vio->mysql_socket, IPPROTO_TCP, TCP_KEEPALIVE, (char *)&opts->idle, sizeof(opts->idle));
+#endif
+ if(ret)
+ return ret;
+ }
+
+#ifdef TCP_KEEPCNT // Linux only
+ if(opts->probes)
+ {
+ ret= mysql_socket_setsockopt(vio->mysql_socket, IPPROTO_TCP, TCP_KEEPCNT, (char *)&opts->probes, sizeof(opts->probes));
+ if(ret)
+ return ret;
+ }
+#endif
+
+#ifdef TCP_KEEPINTVL // Linux only
+ if(opts->interval)
+ {
+ ret= mysql_socket_setsockopt(vio->mysql_socket, IPPROTO_TCP, TCP_KEEPINTVL, (char *)&opts->interval, sizeof(opts->interval));
+ }
+#endif
+ return ret;
+#else /*TCP_KEEPIDLE || TCP_KEEPALIVE */
+ return -1;
+#endif
+}
+
+
+/**
+ 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_should_retry(Vio *vio)
+{
+ DBUG_ENTER("vio_should_retry");
+ DBUG_PRINT("info", ("vio_errno: %d", vio_errno(vio)));
+ DBUG_RETURN(vio_errno(vio) == SOCKET_EINTR);
+}
+
+
+/**
+ Indicate whether a I/O operation timed out.
+
+ @param vio A VIO object
+
+ @return Whether a I/O operation timed out.
+ @retval TRUE Operation timed out.
+ @retval FALSE Not a timeout failure.
+*/
+
+my_bool
+vio_was_timeout(Vio *vio)
+{
+ return (vio_errno(vio) == SOCKET_ETIMEDOUT);
+}
+
+
+int vio_close(Vio *vio)
+{
+ int r=0;
+ DBUG_ENTER("vio_close");
+ DBUG_PRINT("enter", ("sd: %d", (int)mysql_socket_getfd(vio->mysql_socket)));
+
+ if (vio->type != VIO_CLOSED)
+ {
+ DBUG_ASSERT(vio->type == VIO_TYPE_TCPIP ||
+ vio->type == VIO_TYPE_SOCKET ||
+ vio->type == VIO_TYPE_SSL);
+
+ DBUG_ASSERT(mysql_socket_getfd(vio->mysql_socket) >= 0);
+ if (mysql_socket_close(vio->mysql_socket))
+ r= -1;
+ }
+ if (r)
+ {
+ DBUG_PRINT("vio_error", ("close() failed, error: %d",socket_errno));
+ /* FIXME: error handling (not critical for MySQL) */
+ }
+ vio->type= VIO_CLOSED;
+ vio->mysql_socket= MYSQL_INVALID_SOCKET;
+ DBUG_RETURN(r);
+}
+
+
+const char *vio_description(Vio * vio)
+{
+ return vio->desc;
+}
+
+enum enum_vio_type vio_type(Vio* vio)
+{
+ return vio->type;
+}
+
+static const LEX_CSTRING vio_type_names[] =
+{
+ { STRING_WITH_LEN("") }, // internal threads
+ { STRING_WITH_LEN("TCP/IP") },
+ { STRING_WITH_LEN("Socket") },
+ { STRING_WITH_LEN("Named Pipe") },
+ { STRING_WITH_LEN("SSL/TLS") },
+ { STRING_WITH_LEN("Shared Memory") }
+};
+
+const char *vio_type_name(enum enum_vio_type vio_type, size_t *len)
+{
+ int index= vio_type >= FIRST_VIO_TYPE && vio_type <= LAST_VIO_TYPE
+ ? vio_type : 0;
+
+ *len= vio_type_names[index].length;
+ return vio_type_names[index].str;
+}
+
+
+my_socket vio_fd(Vio* vio)
+{
+ return mysql_socket_getfd(vio->mysql_socket);
+}
+
+/**
+ Convert a sock-address (AF_INET or AF_INET6) into the "normalized" form,
+ which is the IPv4 form for IPv4-mapped or IPv4-compatible IPv6 addresses.
+
+ @note Background: when IPv4 and IPv6 are used simultaneously, IPv4
+ addresses may be written in a form of IPv4-mapped or IPv4-compatible IPv6
+ addresses. That means, one address (a.b.c.d) can be written in three forms:
+ - IPv4: a.b.c.d;
+ - IPv4-compatible IPv6: ::a.b.c.d;
+ - IPv4-mapped IPv4: ::ffff:a.b.c.d;
+
+ Having three forms of one address makes it a little difficult to compare
+ addresses with each other (the IPv4-compatible IPv6-address of foo.bar
+ will be different from the IPv4-mapped IPv6-address of foo.bar).
+
+ @note This function can be made public when it's needed.
+
+ @param src [in] source IP address (AF_INET or AF_INET6).
+ @param src_length [in] length of the src.
+ @param dst [out] a buffer to store normalized IP address
+ (sockaddr_storage).
+ @param dst_length [out] optional - actual length of the normalized IP address.
+*/
+
+void vio_get_normalized_ip(const struct sockaddr *src, size_t src_length,
+ struct sockaddr *dst)
+{
+ switch (src->sa_family) {
+ case AF_INET:
+ memcpy(dst, src, src_length);
+ break;
+
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ {
+ const struct sockaddr_in6 *src_addr6= (const struct sockaddr_in6 *) src;
+ const struct in6_addr *src_ip6= &(src_addr6->sin6_addr);
+ const uint32 *src_ip6_int32= (uint32 *) src_ip6->s6_addr;
+
+ if (IN6_IS_ADDR_V4MAPPED(src_ip6) || IN6_IS_ADDR_V4COMPAT(src_ip6))
+ {
+ struct sockaddr_in *dst_ip4= (struct sockaddr_in *) dst;
+
+ /*
+ This is an IPv4-mapped or IPv4-compatible IPv6 address. It should
+ be converted to the IPv4 form.
+ */
+
+ memset(dst_ip4, 0, sizeof (struct sockaddr_in));
+ dst_ip4->sin_family= AF_INET;
+ dst_ip4->sin_port= src_addr6->sin6_port;
+
+ /*
+ In an IPv4 mapped or compatible address, the last 32 bits represent
+ the IPv4 address. The byte orders for IPv6 and IPv4 addresses are
+ the same, so a simple copy is possible.
+ */
+ dst_ip4->sin_addr.s_addr= src_ip6_int32[3];
+ }
+ else
+ {
+ /* This is a "native" IPv6 address. */
+ memcpy(dst, src, src_length);
+ }
+
+ break;
+ }
+#endif /* HAVE_IPV6 */
+ }
+}
+
+
+/**
+ Return the normalized IP address string for a sock-address.
+
+ The idea is to return an IPv4-address for an IPv4-mapped and
+ IPv4-compatible IPv6 address.
+
+ The function writes the normalized IP address to the given buffer.
+ The buffer should have enough space, otherwise error flag is returned.
+ The system constant INET6_ADDRSTRLEN can be used to reserve buffers of
+ the right size.
+
+ @param addr [in] sockaddr object (AF_INET or AF_INET6).
+ @param addr_length [in] length of the addr.
+ @param ip_string [out] buffer to write normalized IP address.
+ @param ip_string_size [in] size of the ip_string.
+
+ @return Error status.
+ @retval TRUE in case of error (the ip_string buffer is not enough).
+ @retval FALSE on success.
+*/
+
+my_bool vio_get_normalized_ip_string(const struct sockaddr *addr, size_t addr_length,
+ char *ip_string,
+ size_t ip_string_size)
+{
+ struct sockaddr_storage norm_addr_storage;
+ struct sockaddr *norm_addr= (struct sockaddr *) &norm_addr_storage;
+ int err_code;
+
+ vio_get_normalized_ip(addr, addr_length, norm_addr);
+
+ err_code= vio_getnameinfo(norm_addr, ip_string, ip_string_size, NULL, 0,
+ NI_NUMERICHOST);
+
+ if (!err_code)
+ return FALSE;
+
+ DBUG_PRINT("error", ("getnameinfo() failed with %d (%s).",
+ (int) err_code,
+ (const char *) gai_strerror(err_code)));
+ return TRUE;
+}
+
+
+/**
+ Return IP address and port of a VIO client socket.
+
+ The function returns an IPv4 address if IPv6 support is disabled.
+
+ The function returns an IPv4 address if the client socket is associated
+ with an IPv4-compatible or IPv4-mapped IPv6 address. Otherwise, the native
+ IPv6 address is returned.
+*/
+
+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)mysql_socket_getfd(vio->mysql_socket)));
+
+ if (vio->localhost)
+ {
+ /*
+ Initialize vio->remote and vio->addLen. Set vio->remote to IPv4 loopback
+ address.
+ */
+ struct in_addr *ip4= &((struct sockaddr_in *) &(vio->remote))->sin_addr;
+ vio->remote.ss_family= AF_INET;
+
+ ip4->s_addr= htonl(INADDR_LOOPBACK);
+
+ /* Initialize ip_buffer and port. */
+
+ strmov(ip_buffer, "127.0.0.1");
+ *port= 0;
+ }
+ else
+ {
+ int err_code;
+ char port_buffer[NI_MAXSERV];
+
+ struct sockaddr_storage addr_storage;
+ struct sockaddr *addr= (struct sockaddr *) &addr_storage;
+ size_socket addr_length= sizeof (addr_storage);
+ /* Get sockaddr by socked fd. */
+
+ err_code= mysql_socket_getpeername(vio->mysql_socket, addr, &addr_length);
+
+ if (err_code)
+ {
+ DBUG_PRINT("exit", ("getpeername() gave error: %d", socket_errno));
+ DBUG_RETURN(TRUE);
+ }
+
+ /* Normalize IP address. */
+
+ vio_get_normalized_ip(addr, addr_length,
+ (struct sockaddr *) &vio->remote);
+
+ /* Get IP address & port number. */
+
+ err_code= vio_getnameinfo((struct sockaddr *) &vio->remote,
+ ip_buffer, ip_buffer_size,
+ port_buffer, NI_MAXSERV,
+ NI_NUMERICHOST | NI_NUMERICSERV);
+
+ if (err_code)
+ {
+ DBUG_PRINT("exit", ("getnameinfo() gave error: %s",
+ gai_strerror(err_code)));
+ DBUG_RETURN(TRUE);
+ }
+
+ *port= (uint16) strtol(port_buffer, NULL, 10);
+ }
+
+ DBUG_PRINT("exit", ("Client IP address: %s; port: %d",
+ (const char *) ip_buffer,
+ (int) *port));
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Retrieve the amount of data that can be read from a socket.
+
+ @param vio A VIO object.
+ @param bytes[out] The amount of bytes available.
+
+ @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)
+ u_long 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(sd, FIONREAD, &len) < 0)
+ return TRUE;
+ *bytes= len;
+ return FALSE;
+#else
+ char buf[1024];
+ ssize_t res= recv(sd, &buf, sizeof(buf), MSG_PEEK);
+ if (res < 0)
+ return TRUE;
+ *bytes= res;
+ return FALSE;
+#endif /*_WIN32*/
+}
+
+#ifndef _WIN32
+
+/**
+ Set of event flags grouped by operations.
+*/
+
+/*
+ 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
+
+/* 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 */
+
+/**
+ Wait for an I/O event on a VIO socket.
+
+ @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.
+
+ @remark socket_errno is set to SOCKET_ETIMEDOUT on timeout.
+
+ @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.
+*/
+
+#ifndef _WIN32
+int vio_io_wait(Vio *vio, enum enum_vio_io_event event, int timeout)
+{
+ 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");
+ DBUG_PRINT("enter", ("timeout: %d", timeout));
+
+ memset(&pfd, 0, sizeof(pfd));
+
+ pfd.fd= sd;
+
+ /*
+ Set the poll bitmask describing the type of events.
+ The error flags are only valid in the revents bitmask.
+ */
+ switch (event)
+ {
+ 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;
+ }
+
+ 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)))
+ {
+ case -1:
+ DBUG_PRINT("error", ("poll returned -1"));
+ /* On error, -1 is returned. */
+ break;
+ case 0:
+ /*
+ Set errno to indicate a timeout error.
+ (This is not compiled in on WIN32.)
+ */
+ DBUG_PRINT("info", ("poll timeout"));
+ errno= SOCKET_ETIMEDOUT;
+ break;
+ default:
+ /* Ensure that the requested I/O event has completed. */
+ DBUG_ASSERT(pfd.revents & revents);
+ break;
+ }
+
+ END_SOCKET_WAIT(locker, timeout);
+ DBUG_RETURN(ret);
+}
+
+#else
+
+int vio_io_wait(Vio *vio, enum enum_vio_io_event event, int timeout)
+{
+ 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");
+
+ /* Convert the timeout, in milliseconds, to seconds and microseconds. */
+ if (timeout >= 0)
+ {
+ tm.tv_sec= timeout / 1000;
+ tm.tv_usec= (timeout % 1000) * 1000;
+ }
+
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ FD_ZERO(&exceptfds);
+
+ /* Always receive notification of exceptions. */
+ FD_SET(fd, &exceptfds);
+
+ switch (event)
+ {
+ 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;
+ }
+
+ START_SOCKET_WAIT(locker, &state, vio->mysql_socket, PSI_SOCKET_SELECT, timeout);
+
+ /* The first argument is ignored on Windows. */
+ ret= select(0, &readfds, &writefds, &exceptfds, (timeout >= 0) ? &tm : NULL);
+
+ END_SOCKET_WAIT(locker, timeout);
+
+ /* Set error code to indicate a timeout error. */
+ if (ret == 0)
+ WSASetLastError(SOCKET_ETIMEDOUT);
+
+ /* Error or timeout? */
+ if (ret <= 0)
+ DBUG_RETURN(ret);
+
+ /* The requested I/O event is ready? */
+ switch (event)
+ {
+ 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;
+ }
+
+ /* Error conditions pending? */
+ ret|= MY_TEST(FD_ISSET(fd, &exceptfds));
+
+ /* Not a timeout, ensure that a condition was met. */
+ DBUG_ASSERT(ret);
+
+ DBUG_RETURN(ret);
+}
+
+#endif /* _WIN32 */
+
+
+/**
+ Connect to a peer address.
+
+ @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.
+
+ @retval FALSE A connection was successfully established.
+ @retval TRUE A fatal error. See socket_errno.
+*/
+
+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");
+
+ /* Only for socket-based transport types. */
+ DBUG_ASSERT(vio->type == VIO_TYPE_SOCKET || vio->type == VIO_TYPE_TCPIP);
+
+ /* If timeout is not infinite, set socket to non-blocking mode. */
+ if ((timeout > -1) && vio_blocking(vio, FALSE, &not_used))
+ DBUG_RETURN(TRUE);
+
+ /* Initiate the connection. */
+ ret= mysql_socket_connect(vio->mysql_socket, addr, len);
+
+#ifdef _WIN32
+ wait= (ret == SOCKET_ERROR) &&
+ (WSAGetLastError() == WSAEINPROGRESS ||
+ WSAGetLastError() == WSAEWOULDBLOCK);
+#else
+ wait= (ret == -1) && (errno == EINPROGRESS || errno == EALREADY);
+#endif
+
+ /*
+ 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;
+
+ /*
+ 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)))
+ {
+#ifdef _WIN32
+ WSASetLastError(error);
+#else
+ errno= error;
+#endif
+ ret= MY_TEST(error);
+ }
+ }
+
+ /* 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));
+}
+
+
+/**
+ Determine if the endpoint of a connection is still available.
+
+ @remark The socket is assumed to be disconnected if an EOF
+ condition is encountered.
+
+ @param vio The VIO object.
+
+ @retval TRUE EOF condition not found.
+ @retval FALSE EOF condition is signaled.
+*/
+
+my_bool vio_is_connected(Vio *vio)
+{
+ uint bytes= 0;
+ DBUG_ENTER("vio_is_connected");
+
+ /*
+ 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);
+
+ /*
+ 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);
+ }
+
+#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);
+}
+
+
+/**
+ Number of bytes in the read or socket buffer
+
+ @remark An EOF condition might count as one readable byte.
+
+ @return number of bytes in one of the buffers or < 0 if error.
+*/
+
+ssize_t vio_pending(Vio *vio)
+{
+ uint bytes= 0;
+
+ /* Data pending on the read buffer. */
+ if (vio->read_pos < vio->read_end)
+ return vio->read_end - vio->read_pos;
+
+ /* Skip non-socket based transport types. */
+ switch (vio->type)
+ {
+ case VIO_TYPE_TCPIP:
+ /* fallthrough */
+ case VIO_TYPE_SOCKET:
+ /* Obtain number of readable bytes in the socket buffer. */
+ if (socket_peek_read(vio, &bytes))
+ return -1;
+ return bytes;
+
+ case VIO_TYPE_SSL:
+ bytes= (uint) SSL_pending(vio->ssl_arg);
+ if (bytes)
+ return bytes;
+ if (socket_peek_read(vio, &bytes))
+ return -1;
+ return bytes;
+
+#ifdef _WIN32
+ case VIO_TYPE_NAMEDPIPE:
+ bytes= vio_pending_pipe(vio);
+ return bytes;
+#endif
+ default:
+ return -1;
+ }
+}
+
+
+/**
+ Checks if the error code, returned by vio_getnameinfo(), means it was the
+ "No-name" error.
+
+ Windows-specific note: getnameinfo() returns WSANO_DATA instead of
+ EAI_NODATA or EAI_NONAME when no reverse mapping is available at the host
+ (i.e. Windows can't get hostname by IP-address). This error should be
+ treated as EAI_NONAME.
+
+ @return if the error code is actually EAI_NONAME.
+ @retval true if the error code is EAI_NONAME.
+ @retval false otherwise.
+*/
+
+my_bool vio_is_no_name_error(int err_code)
+{
+#ifdef _WIN32
+
+ return err_code == WSANO_DATA || err_code == EAI_NONAME;
+
+#else
+
+ return err_code == EAI_NONAME;
+
+#endif
+}
+
+
+/**
+ This is a wrapper for the system getnameinfo(), because different OS
+ differ in the getnameinfo() implementation:
+ - Solaris 10 requires that the 2nd argument (salen) must match the
+ actual size of the struct sockaddr_storage passed to it;
+ - Mac OS X has sockaddr_in::sin_len and sockaddr_in6::sin6_len and
+ requires them to be filled.
+*/
+
+int vio_getnameinfo(const struct sockaddr *sa,
+ char *hostname, size_t hostname_size,
+ char *port, size_t port_size,
+ int flags)
+{
+ int sa_length= 0;
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ sa_length= sizeof (struct sockaddr_in);
+#ifdef HAVE_SOCKADDR_IN_SIN_LEN
+ ((struct sockaddr_in *) sa)->sin_len= sa_length;
+#endif /* HAVE_SOCKADDR_IN_SIN_LEN */
+ break;
+
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ sa_length= sizeof (struct sockaddr_in6);
+# ifdef HAVE_SOCKADDR_IN6_SIN6_LEN
+ ((struct sockaddr_in6 *) sa)->sin6_len= sa_length;
+# endif /* HAVE_SOCKADDR_IN6_SIN6_LEN */
+ break;
+#endif /* HAVE_IPV6 */
+ }
+
+ return getnameinfo(sa, sa_length,
+ hostname, (uint)hostname_size,
+ port, (uint)port_size,
+ flags);
+}
diff --git a/vio/viossl.c b/vio/viossl.c
new file mode 100644
index 00000000..6fdebca1
--- /dev/null
+++ b/vio/viossl.c
@@ -0,0 +1,409 @@
+/* Copyright (c) 2000, 2013, 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-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"
+
+#ifdef HAVE_OPENSSL
+
+#define SSL_errno(X,Y) ERR_get_error()
+
+/**
+ Obtain the equivalent system error status for the last SSL I/O operation.
+
+ @param ssl_error The result code of the failed TLS/SSL I/O operation.
+*/
+
+static void ssl_set_sys_error(int ssl_error)
+{
+ int error= 0;
+
+ 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)
+ {
+#ifdef _WIN32
+ WSASetLastError(error);
+#else
+ errno= error;
+#endif
+ }
+}
+
+
+/**
+ 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.
+ @param should_wait[out] whether to wait for 'event'
+
+ @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, my_bool *should_wait)
+{
+ int ssl_error;
+ SSL *ssl= vio->ssl_arg;
+ my_bool should_retry= TRUE;
+
+#if defined(ERR_LIB_X509) && defined(X509_R_CERT_ALREADY_IN_HASH_TABLE)
+ /*
+ Ignore error X509_R_CERT_ALREADY_IN_HASH_TABLE.
+ This is a workaround for an OpenSSL bug in an older (< 1.1.1)
+ OpenSSL version.
+ */
+ unsigned long err = ERR_peek_error();
+ if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
+ ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE)
+ {
+ ERR_clear_error();
+ *should_wait= FALSE;
+ return TRUE;
+ }
+#endif
+
+ /* 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)
+ {
+ case SSL_ERROR_WANT_READ:
+ *event= VIO_IO_EVENT_READ;
+ *should_wait= TRUE;
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ *event= VIO_IO_EVENT_WRITE;
+ *should_wait= TRUE;
+ break;
+ default:
+ should_retry= FALSE;
+ *should_wait= FALSE;
+ ssl_set_sys_error(ssl_error);
+ ERR_clear_error();
+ break;
+ }
+
+ return should_retry;
+}
+
+
+/**
+ Handle SSL io error.
+
+ @param[in] vio Vio
+ @param[in] ret return from the failed IO operation
+
+ @return 0 - should retry last read/write operation
+ 1 - some error has occurred
+*/
+static int handle_ssl_io_error(Vio *vio, int ret)
+{
+ enum enum_vio_io_event event;
+ my_bool should_wait;
+
+ /* Process the SSL I/O error. */
+ if (!ssl_should_retry(vio, ret, &event, &should_wait))
+ return 1;
+
+ if (!should_wait)
+ return 1;
+
+ /* Attempt to wait for an I/O event. */
+ return vio_socket_io_wait(vio, event);
+}
+
+
+size_t vio_ssl_read(Vio *vio, uchar *buf, size_t size)
+{
+ int ret;
+ SSL *ssl= vio->ssl_arg;
+ DBUG_ENTER("vio_ssl_read");
+ DBUG_PRINT("enter", ("sd: %d buf: %p size: %zu ssl: %p",
+ (int)mysql_socket_getfd(vio->mysql_socket), buf, size,
+ vio->ssl_arg));
+
+
+ while ((ret= SSL_read(ssl, buf, (int)size)) < 0)
+ {
+ if (handle_ssl_io_error(vio, ret))
+ break;
+ }
+
+ DBUG_PRINT("exit", ("%d", ret));
+ DBUG_RETURN(ret < 0 ? -1 : ret);
+
+}
+
+
+size_t vio_ssl_write(Vio *vio, const uchar *buf, size_t size)
+{
+ int ret;
+ SSL *ssl= vio->ssl_arg;
+ DBUG_ENTER("vio_ssl_write");
+ DBUG_PRINT("enter", ("sd: %d buf: %p size: %zu",
+ (int)mysql_socket_getfd(vio->mysql_socket),
+ buf, size));
+ while ((ret= SSL_write(ssl, buf, (int)size)) < 0)
+ {
+ if (handle_ssl_io_error(vio,ret))
+ break;
+ }
+
+ DBUG_RETURN(ret < 0 ? -1 : ret);
+}
+
+int vio_ssl_close(Vio *vio)
+{
+ int r= 0;
+ SSL *ssl= (SSL*)vio->ssl_arg;
+ DBUG_ENTER("vio_ssl_close");
+
+ if (ssl)
+ {
+ /*
+ THE SSL standard says that SSL sockets must send and receive a close_notify
+ alert on socket shutdown to avoid truncation attacks. However, this can
+ cause problems since we often hold a lock during shutdown and this IO can
+ take an unbounded amount of time to complete. Since our packets are self
+ describing with length, we aren't vunerable to these attacks. Therefore,
+ we just shutdown by closing the socket (quiet shutdown).
+ */
+ SSL_set_quiet_shutdown(ssl, 1);
+
+ switch ((r= SSL_shutdown(ssl))) {
+ case 1:
+ /* Shutdown successful */
+ break;
+ case 0:
+ /*
+ Shutdown not yet finished - since the socket is going to
+ be closed there is no need to call SSL_shutdown() a second
+ time to wait for the other side to respond
+ */
+ break;
+ default: /* Shutdown failed */
+ DBUG_PRINT("vio_error", ("SSL_shutdown() failed, error: %d",
+ SSL_get_error(ssl, r)));
+ break;
+ }
+ }
+ DBUG_RETURN(vio_close(vio));
+}
+
+
+void vio_ssl_delete(Vio *vio)
+{
+ if (!vio)
+ return; /* It must be safe to delete null pointer */
+
+ if (vio->type == VIO_TYPE_SSL)
+ vio_ssl_close(vio); /* Still open, close connection first */
+
+ if (vio->ssl_arg)
+ {
+ SSL_free((SSL*) vio->ssl_arg);
+ vio->ssl_arg= 0;
+ }
+
+ vio_delete(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)
+ {
+ if (handle_ssl_io_error(vio,ret))
+ break;
+ }
+
+ vio->ssl_arg= NULL;
+
+ return ret;
+}
+
+
+static int ssl_do(struct st_VioSSLFd *ptr, Vio *vio, long timeout,
+ ssl_handshake_func_t func, unsigned long *errptr)
+{
+ int r;
+ SSL *ssl;
+ my_socket sd= mysql_socket_getfd(vio->mysql_socket);
+ DBUG_ENTER("ssl_do");
+ DBUG_PRINT("enter", ("ptr: %p, sd: %d ctx: %p",
+ ptr, (int)sd, ptr->ssl_context));
+
+
+ if (!(ssl= SSL_new(ptr->ssl_context)))
+ {
+ DBUG_PRINT("error", ("SSL_new failure"));
+ *errptr= ERR_get_error();
+ DBUG_RETURN(1);
+ }
+ DBUG_PRINT("info", ("ssl: %p timeout: %ld", ssl, timeout));
+ SSL_clear(ssl);
+ SSL_SESSION_set_timeout(SSL_get_session(ssl), timeout);
+ SSL_set_fd(ssl, (int)sd);
+
+#ifdef HAVE_WOLFSSL
+ /* Set first argument of the transport functions. */
+ wolfSSL_SetIOReadCtx(ssl, vio);
+ wolfSSL_SetIOWriteCtx(ssl, vio);
+#endif
+
+#if defined(SSL_OP_NO_COMPRESSION)
+ SSL_set_options(ssl, SSL_OP_NO_COMPRESSION);
+#endif
+
+ if ((r= ssl_handshake_loop(vio, ssl, func)) < 1)
+ {
+ DBUG_PRINT("error", ("SSL_connect/accept failure"));
+ *errptr= SSL_errno(ssl, r);
+ SSL_free(ssl);
+ DBUG_RETURN(1);
+ }
+
+ /*
+ Connection succeeded. Install new function handlers,
+ change type, set sd to the fd used when connecting
+ and set pointer to the SSL structure
+ */
+ if (vio_reset(vio, VIO_TYPE_SSL, SSL_get_fd(ssl), ssl, 0))
+ {
+ DBUG_RETURN(1);
+ }
+
+#ifndef DBUG_OFF
+ {
+ /* Print some info about the peer */
+ X509 *cert;
+ char buf[512];
+
+ DBUG_PRINT("info",("SSL connection succeeded"));
+ DBUG_PRINT("info",("Using cipher: '%s'" , SSL_get_cipher_name(ssl)));
+
+ if ((cert= SSL_get_peer_certificate (ssl)))
+ {
+ DBUG_PRINT("info",("Peer certificate:"));
+ X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
+ DBUG_PRINT("info",("\t subject: '%s'", buf));
+ X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof(buf));
+ DBUG_PRINT("info",("\t issuer: '%s'", buf));
+ X509_free(cert);
+ }
+ else
+ DBUG_PRINT("info",("Peer does not have certificate."));
+
+ if (SSL_get_shared_ciphers(ssl, buf, sizeof(buf)))
+ {
+ DBUG_PRINT("info",("shared_ciphers: '%s'", buf));
+ }
+ else
+ DBUG_PRINT("info",("no shared ciphers!"));
+ }
+#endif
+
+ DBUG_RETURN(0);
+}
+
+
+int sslaccept(struct st_VioSSLFd *ptr, Vio *vio, long timeout, unsigned long *errptr)
+{
+ DBUG_ENTER("sslaccept");
+ DBUG_RETURN(ssl_do(ptr, vio, timeout, SSL_accept, errptr));
+}
+
+
+int sslconnect(struct st_VioSSLFd *ptr, Vio *vio, long timeout, unsigned long *errptr)
+{
+ DBUG_ENTER("sslconnect");
+ DBUG_RETURN(ssl_do(ptr, vio, timeout, SSL_connect, errptr));
+}
+
+
+int vio_ssl_blocking(Vio *vio __attribute__((unused)),
+ my_bool set_blocking_mode,
+ my_bool *old_mode)
+{
+ /* Mode is always blocking */
+ *old_mode= 1;
+ /* Return error if we try to change to non_blocking mode */
+ return (set_blocking_mode ? 0 : 1);
+}
+
+my_bool vio_ssl_has_data(Vio *vio)
+{
+ return SSL_pending(vio->ssl_arg) > 0 ? TRUE : FALSE;
+}
+
+#endif /* HAVE_OPENSSL */
diff --git a/vio/viosslfactories.c b/vio/viosslfactories.c
new file mode 100644
index 00000000..289c28d4
--- /dev/null
+++ b/vio/viosslfactories.c
@@ -0,0 +1,526 @@
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates.
+ Copyright (c) 2011, 2016, MariaDB
+
+ 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 */
+
+#include "vio_priv.h"
+#include <ssl_compat.h>
+
+#ifdef HAVE_OPENSSL
+#include <openssl/dh.h>
+#include <openssl/bn.h>
+
+static my_bool ssl_algorithms_added = FALSE;
+static my_bool ssl_error_strings_loaded= FALSE;
+
+/* the function below was generated with "openssl dhparam -2 -C 2048" */
+#ifndef HAVE_WOLFSSL
+static
+DH *get_dh2048()
+{
+ static unsigned char dhp_2048[] = {
+ 0xA1,0xBB,0x7C,0x20,0xC5,0x5B,0xC0,0x7B,0x21,0x8B,0xD6,0xA8,
+ 0x15,0xFC,0x3B,0xBA,0xAB,0x9F,0xDF,0x68,0xC4,0x79,0x78,0x0D,
+ 0xC1,0x12,0x64,0xE4,0x15,0xC9,0x66,0xDB,0xF6,0xCB,0xB3,0x39,
+ 0x02,0x5B,0x78,0x62,0xFB,0x09,0xAE,0x09,0x6B,0xDD,0xD4,0x5D,
+ 0x97,0xBC,0xDC,0x7F,0xE6,0xD6,0xF1,0xCB,0xF5,0xEB,0xDA,0xA7,
+ 0x2E,0x5A,0x43,0x2B,0xE9,0x40,0xE2,0x85,0x00,0x1C,0xC0,0x0A,
+ 0x98,0x77,0xA9,0x31,0xDE,0x0B,0x75,0x4D,0x1E,0x1F,0x16,0x83,
+ 0xCA,0xDE,0xBD,0x21,0xFC,0xC1,0x82,0x37,0x36,0x33,0x0B,0x66,
+ 0x06,0x3C,0xF3,0xAF,0x21,0x57,0x57,0x80,0xF6,0x94,0x1B,0xA9,
+ 0xD4,0xF6,0x8F,0x18,0x62,0x0E,0xC4,0x22,0xF9,0x5B,0x62,0xCC,
+ 0x3F,0x19,0x95,0xCF,0x4B,0x00,0xA6,0x6C,0x0B,0xAF,0x9F,0xD5,
+ 0xFA,0x3D,0x6D,0xDA,0x30,0x83,0x07,0x91,0xAC,0x15,0xFF,0x8F,
+ 0x59,0x54,0xEA,0x25,0xBC,0x4E,0xEB,0x6A,0x54,0xDF,0x75,0x09,
+ 0x72,0x0F,0xEF,0x23,0x70,0xE0,0xA8,0x04,0xEA,0xFF,0x90,0x54,
+ 0xCD,0x84,0x18,0xC0,0x75,0x91,0x99,0x0F,0xA1,0x78,0x0C,0x07,
+ 0xB7,0xC5,0xDE,0x55,0x06,0x7B,0x95,0x68,0x2C,0x33,0x39,0xBC,
+ 0x2C,0xD0,0x6D,0xDD,0xFA,0xDC,0xB5,0x8F,0x82,0x39,0xF8,0x67,
+ 0x44,0xF1,0xD8,0xF7,0x78,0x11,0x9A,0x77,0x9B,0x53,0x47,0xD6,
+ 0x2B,0x5D,0x67,0xB8,0xB7,0xBC,0xC1,0xD7,0x79,0x62,0x15,0xC2,
+ 0xC5,0x83,0x97,0xA7,0xF8,0xB4,0x9C,0xF6,0x8F,0x9A,0xC7,0xDA,
+ 0x1B,0xBB,0x87,0x07,0xA7,0x71,0xAD,0xB2,0x8A,0x50,0xF8,0x26,
+ 0x12,0xB7,0x3E,0x0B,
+ };
+ static unsigned char dhg_2048[] = {
+ 0x02
+ };
+ DH *dh = DH_new();
+ BIGNUM *dhp_bn, *dhg_bn;
+
+ if (dh == NULL)
+ return NULL;
+ dhp_bn = BN_bin2bn(dhp_2048, sizeof (dhp_2048), NULL);
+ dhg_bn = BN_bin2bn(dhg_2048, sizeof (dhg_2048), NULL);
+ if (dhp_bn == NULL || dhg_bn == NULL
+ || !DH_set0_pqg(dh, dhp_bn, NULL, dhg_bn)) {
+ DH_free(dh);
+ BN_free(dhp_bn);
+ BN_free(dhg_bn);
+ return NULL;
+ }
+ return dh;
+}
+#endif
+
+static const char*
+ssl_error_string[] =
+{
+ "No error",
+ "Unable to get certificate",
+ "Unable to get private key",
+ "Private key does not match the certificate public key",
+ "SSL_CTX_set_default_verify_paths failed",
+ "Failed to set ciphers to use",
+ "SSL_CTX_new failed",
+ "SSL_CTX_set_tmp_dh failed",
+ "Unknown TLS version"
+};
+
+const char*
+sslGetErrString(enum enum_ssl_init_error e)
+{
+ DBUG_ASSERT(SSL_INITERR_NOERROR < e && e < SSL_INITERR_LASTERR);
+ return ssl_error_string[e];
+}
+
+static int
+vio_set_cert_stuff(SSL_CTX *ctx, const char *cert_file, const char *key_file,
+ my_bool is_client, enum enum_ssl_init_error* error)
+{
+ DBUG_ENTER("vio_set_cert_stuff");
+ DBUG_PRINT("enter", ("ctx: %p cert_file: %s key_file: %s",
+ ctx, cert_file, key_file));
+
+ if (!cert_file && key_file)
+ cert_file= key_file;
+
+ if (!key_file && cert_file)
+ key_file= cert_file;
+
+ if (cert_file &&
+ SSL_CTX_use_certificate_chain_file(ctx, cert_file) <= 0)
+ {
+ *error= SSL_INITERR_CERT;
+ DBUG_PRINT("error",("%s from file '%s'", sslGetErrString(*error), cert_file));
+ DBUG_EXECUTE("error", ERR_print_errors_fp(DBUG_FILE););
+ fprintf(stderr, "SSL error: %s from '%s'\n", sslGetErrString(*error),
+ cert_file);
+ fflush(stderr);
+ DBUG_RETURN(1);
+ }
+
+ if (key_file &&
+ SSL_CTX_use_PrivateKey_file(ctx, key_file, SSL_FILETYPE_PEM) <= 0)
+ {
+ *error= SSL_INITERR_KEY;
+ DBUG_PRINT("error", ("%s from file '%s'", sslGetErrString(*error), key_file));
+ DBUG_EXECUTE("error", ERR_print_errors_fp(DBUG_FILE););
+ fprintf(stderr, "SSL error: %s from '%s'\n", sslGetErrString(*error),
+ key_file);
+ fflush(stderr);
+ DBUG_RETURN(1);
+ }
+
+ /*
+ If certificate is used check if private key matches.
+ Note, that server side has to use certificate.
+ */
+ if ((cert_file != NULL || !is_client) && !SSL_CTX_check_private_key(ctx))
+ {
+ *error= SSL_INITERR_NOMATCH;
+ DBUG_PRINT("error", ("%s",sslGetErrString(*error)));
+ DBUG_EXECUTE("error", ERR_print_errors_fp(DBUG_FILE););
+ fprintf(stderr, "SSL error: %s\n", sslGetErrString(*error));
+ fflush(stderr);
+ DBUG_RETURN(1);
+ }
+
+ DBUG_RETURN(0);
+}
+
+
+void vio_check_ssl_init()
+{
+ if (!ssl_algorithms_added)
+ {
+ ssl_algorithms_added= TRUE;
+ OPENSSL_init_ssl(0, NULL);
+ }
+
+ if (!ssl_error_strings_loaded)
+ {
+ ssl_error_strings_loaded= TRUE;
+ SSL_load_error_strings();
+ }
+}
+
+#ifdef HAVE_WOLFSSL
+static int wolfssl_recv(WOLFSSL* ssl, char* buf, int sz, void* vio)
+{
+ size_t ret;
+ (void)ssl;
+ ret = vio_read((Vio *)vio, (uchar *)buf, sz);
+ /* check if connection was closed */
+ if (ret == 0)
+ return WOLFSSL_CBIO_ERR_CONN_CLOSE;
+
+ return (int)ret;
+}
+
+static int wolfssl_send(WOLFSSL* ssl, char* buf, int sz, void* vio)
+{
+ return (int)vio_write((Vio *)vio, (unsigned char*)buf, sz);
+}
+#endif /* HAVE_WOLFSSL */
+
+static long vio_tls_protocol_options(ulonglong tls_version)
+{
+ long tls_protocol_flags=
+#ifdef TLS1_3_VERSION
+ SSL_OP_NO_TLSv1_3 |
+#endif
+#if defined(TLS1_2_VERSION) || defined(HAVE_WOLFSSL)
+ SSL_OP_NO_TLSv1_2 |
+#endif
+ SSL_OP_NO_TLSv1_1 |
+ SSL_OP_NO_TLSv1;
+ long disabled_tls_protocols= tls_protocol_flags,
+ disabled_ssl_protocols= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
+
+ if (!tls_version)
+ return disabled_ssl_protocols;
+
+ if (tls_version & VIO_TLSv1_0)
+ disabled_tls_protocols&= ~SSL_OP_NO_TLSv1;
+ if (tls_version & VIO_TLSv1_1)
+ disabled_tls_protocols&= ~SSL_OP_NO_TLSv1_1;
+#if defined(TLS1_2_VERSION) || defined(HAVE_WOLFSSL)
+ if (tls_version & VIO_TLSv1_2)
+ disabled_tls_protocols&= ~SSL_OP_NO_TLSv1_2;
+#endif
+#ifdef TLS1_3_VERSION
+ if (tls_version & VIO_TLSv1_3)
+ disabled_tls_protocols&= ~SSL_OP_NO_TLSv1_3;
+#endif
+
+ /* some garbage was specified in tls_version option */
+ if (tls_protocol_flags == disabled_tls_protocols)
+ return -1;
+ return (disabled_tls_protocols | disabled_ssl_protocols);
+}
+
+/************************ VioSSLFd **********************************/
+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,
+ const char *crl_file, const char *crl_path, ulonglong tls_version)
+{
+ struct st_VioSSLFd *ssl_fd;
+ long ssl_ctx_options;
+ DBUG_ENTER("new_VioSSLFd");
+
+ /*
+ If some optional parameters indicate empty strings, then
+ for compatibility with SSL libraries, replace them with NULL,
+ otherwise these libraries will try to open files with an empty
+ name, etc., and they will return an error code instead performing
+ the necessary operations:
+ */
+ if (ca_file && !ca_file[0])
+ {
+ ca_file = NULL;
+ }
+ if (ca_path && !ca_path[0])
+ {
+ ca_path = NULL;
+ }
+ if (crl_file && !crl_file[0])
+ {
+ crl_file = NULL;
+ }
+ if (crl_path && !crl_path[0])
+ {
+ crl_path = NULL;
+ }
+
+ DBUG_PRINT("enter",
+ ("key_file: '%s' cert_file: '%s' ca_file: '%s' ca_path: '%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",
+ crl_file ? crl_file : "NULL",
+ crl_path ? crl_path : "NULL"));
+
+ vio_check_ssl_init();
+
+ if (!(ssl_fd= ((struct st_VioSSLFd*)
+ my_malloc(key_memory_vio_ssl_fd,
+ sizeof(struct st_VioSSLFd), MYF(0)))))
+ goto err0;
+ if (!(ssl_fd->ssl_context= SSL_CTX_new(is_client_method ?
+ SSLv23_client_method() :
+ SSLv23_server_method())))
+ {
+ *error= SSL_INITERR_MEMFAIL;
+ DBUG_PRINT("error", ("%s", sslGetErrString(*error)));
+ goto err1;
+ }
+
+ ssl_ctx_options= vio_tls_protocol_options(tls_version);
+ if (ssl_ctx_options == -1)
+ {
+ *error= SSL_INITERR_PROTOCOL;
+ DBUG_PRINT("error", ("%s", sslGetErrString(*error)));
+ goto err1;
+ }
+
+ SSL_CTX_set_options(ssl_fd->ssl_context, ssl_ctx_options);
+
+ /*
+ Set the ciphers that can be used
+ NOTE: SSL_CTX_set_cipher_list will return 0 if
+ none of the provided ciphers could be selected
+ */
+ if (cipher &&
+ SSL_CTX_set_ciphersuites(ssl_fd->ssl_context, cipher) == 0 &&
+ SSL_CTX_set_cipher_list(ssl_fd->ssl_context, cipher) == 0)
+ {
+ *error= SSL_INITERR_CIPHERS;
+ DBUG_PRINT("error", ("%s", sslGetErrString(*error)));
+ goto err2;
+ }
+
+ /* Load certs from the trusted ca */
+ if (SSL_CTX_load_verify_locations(ssl_fd->ssl_context, ca_file, ca_path) <= 0)
+ {
+ DBUG_PRINT("warning", ("SSL_CTX_load_verify_locations failed"));
+ if (ca_file || ca_path)
+ {
+ /* fail only if ca file or ca path were supplied and looking into
+ them fails. */
+ *error= SSL_INITERR_BAD_PATHS;
+ DBUG_PRINT("error", ("SSL_CTX_load_verify_locations failed : %s",
+ sslGetErrString(*error)));
+ goto err2;
+ }
+#ifndef HAVE_WOLFSSL
+ /* otherwise go use the defaults */
+ if (SSL_CTX_set_default_verify_paths(ssl_fd->ssl_context) == 0)
+ {
+ *error= SSL_INITERR_BAD_PATHS;
+ DBUG_PRINT("error", ("%s", sslGetErrString(*error)));
+ goto err2;
+ }
+#endif
+ }
+
+ if (crl_file || crl_path)
+ {
+#ifdef HAVE_WOLFSSL
+ /* CRL does not work with WolfSSL. */
+ DBUG_ASSERT(0);
+ goto err2;
+#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)));
+ goto err2;
+ }
+#endif
+ }
+
+ if (vio_set_cert_stuff(ssl_fd->ssl_context, cert_file, key_file,
+ is_client_method, error))
+ {
+ DBUG_PRINT("error", ("vio_set_cert_stuff failed"));
+ goto err2;
+ }
+
+#ifndef HAVE_WOLFSSL
+ /* DH stuff */
+ if (!is_client_method)
+ {
+ DH *dh= get_dh2048();
+ if (!SSL_CTX_set_tmp_dh(ssl_fd->ssl_context, dh))
+ {
+ *error= SSL_INITERR_DH;
+ DH_free(dh);
+ goto err2;
+ }
+
+ DH_free(dh);
+ }
+#endif
+
+#ifdef HAVE_WOLFSSL
+ /* set IO functions used by wolfSSL */
+ wolfSSL_SetIORecv(ssl_fd->ssl_context, wolfssl_recv);
+ wolfSSL_SetIOSend(ssl_fd->ssl_context, wolfssl_send);
+#endif
+
+ DBUG_PRINT("exit", ("OK 1"));
+
+ DBUG_RETURN(ssl_fd);
+
+err2:
+ SSL_CTX_free(ssl_fd->ssl_context);
+err1:
+ my_free(ssl_fd);
+err0:
+ DBUG_EXECUTE("error", ERR_print_errors_fp(DBUG_FILE););
+ DBUG_RETURN(0);
+}
+
+
+/************************ VioSSLConnectorFd **********************************/
+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 *crl_file, const char *crl_path)
+{
+ struct st_VioSSLFd *ssl_fd;
+ int verify= SSL_VERIFY_PEER;
+
+ if (ca_file && ! ca_file[0]) ca_file = NULL;
+ if (ca_path && ! ca_path[0]) ca_path = NULL;
+ if (crl_file && ! crl_file[0]) crl_file = NULL;
+ if (crl_path && ! crl_path[0]) crl_path = NULL;
+
+ /*
+ If some optional parameters indicate empty strings, then
+ for compatibility with SSL libraries, replace them with NULL,
+ otherwise these libraries will try to open files with an empty
+ name, etc., and they will return an error code instead performing
+ the necessary operations:
+ */
+ if (ca_file && !ca_file[0])
+ {
+ ca_file = NULL;
+ }
+ if (ca_path && !ca_path[0])
+ {
+ ca_path = NULL;
+ }
+ if (crl_file && !crl_file[0])
+ {
+ crl_file = NULL;
+ }
+ if (crl_path && !crl_path[0])
+ {
+ crl_path = NULL;
+ }
+
+ /*
+ Turn off verification of servers certificate if both
+ ca_file and ca_path is set to NULL
+ */
+ if (ca_file == 0 && ca_path == 0)
+ verify= SSL_VERIFY_NONE;
+
+ if (!(ssl_fd= new_VioSSLFd(key_file, cert_file, ca_file,
+ ca_path, cipher, TRUE, error,
+ crl_file, crl_path, 0)))
+ {
+ return 0;
+ }
+
+ /* Init the VioSSLFd as a "connector" ie. the client side */
+
+ SSL_CTX_set_verify(ssl_fd->ssl_context, verify, NULL);
+
+ return ssl_fd;
+}
+
+
+/************************ VioSSLAcceptorFd **********************************/
+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 *crl_file, const char *crl_path,
+ ulonglong tls_version)
+{
+ struct st_VioSSLFd *ssl_fd;
+ int verify= SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
+
+ /*
+ If some optional parameters indicate empty strings, then
+ for compatibility with SSL libraries, replace them with NULL,
+ otherwise these libraries will try to open files with an empty
+ name, etc., and they will return an error code instead performing
+ the necessary operations:
+ */
+ if (ca_file && !ca_file[0])
+ {
+ ca_file = NULL;
+ }
+ if (ca_path && !ca_path[0])
+ {
+ ca_path = NULL;
+ }
+ if (crl_file && !crl_file[0])
+ {
+ crl_file = NULL;
+ }
+ if (crl_path && !crl_path[0])
+ {
+ crl_path = NULL;
+ }
+
+ if (!(ssl_fd= new_VioSSLFd(key_file, cert_file, ca_file,
+ ca_path, cipher, FALSE, error,
+ crl_file, crl_path, tls_version)))
+ {
+ return 0;
+ }
+ /* Init the the VioSSLFd as a "acceptor" ie. the server side */
+
+ /* Set max number of cached sessions, returns the previous size */
+ SSL_CTX_sess_set_cache_size(ssl_fd->ssl_context, 128);
+
+ SSL_CTX_set_verify(ssl_fd->ssl_context, verify, NULL);
+
+ /*
+ Set session_id - an identifier for this server session
+ Use the ssl_fd pointer
+ */
+ SSL_CTX_set_session_id_context(ssl_fd->ssl_context,
+ (const unsigned char *)ssl_fd,
+ sizeof(ssl_fd));
+
+ return ssl_fd;
+}
+
+void free_vio_ssl_acceptor_fd(struct st_VioSSLFd *fd)
+{
+ DBUG_ENTER("free_vio_ssl_acceptor_fd");
+ SSL_CTX_free(fd->ssl_context);
+ my_free(fd);
+ DBUG_VOID_RETURN;
+}
+#endif /* HAVE_OPENSSL */
diff --git a/vio/viotest-ssl.c b/vio/viotest-ssl.c
new file mode 100644
index 00000000..36d67b9f
--- /dev/null
+++ b/vio/viotest-ssl.c
@@ -0,0 +1,154 @@
+/* Copyright (c) 2000, 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-1335 USA */
+
+#include <my_global.h>
+#ifdef HAVE_OPENSSL
+#include <my_sys.h>
+#include <m_string.h>
+#include <m_ctype.h>
+#include "mysql.h"
+#include "errmsg.h"
+#include <my_dir.h>
+#include <my_getopt.h>
+#include <signal.h>
+#include <violite.h>
+
+const char *VER="0.2";
+
+
+#ifndef DBUG_OFF
+const char *default_dbug_option="d:t:O,/tmp/viotest-ssl.trace";
+#endif
+
+void
+fatal_error(const char *r)
+{
+ perror(r);
+ exit(0);
+}
+
+void
+print_usage()
+{
+ printf("viossl-test: testing SSL virtual IO. Usage:\n");
+ printf("viossl-test server-key server-cert client-key client-cert [CAfile] [CApath]\n");
+}
+
+
+int main(int argc, char **argv)
+{
+ char* server_key = 0;
+ char* server_cert = 0;
+ char* client_key = 0;
+ char* client_cert = 0;
+ char* ca_file = 0;
+ char* ca_path = 0;
+ int child_pid,sv[2];
+ struct st_VioSSLAcceptorFd* ssl_acceptor=0;
+ struct st_VioSSLConnectorFd* ssl_connector=0;
+ Vio* client_vio=0;
+ Vio* server_vio=0;
+ enum enum_ssl_init_error ssl_init_error;
+ unsigned long ssl_error;
+
+ MY_INIT(argv[0]);
+ DBUG_PROCESS(argv[0]);
+ DBUG_PUSH(default_dbug_option);
+
+ if (argc<5)
+ {
+ print_usage();
+ return 1;
+ }
+
+ server_key = argv[1];
+ server_cert = argv[2];
+ client_key = argv[3];
+ client_cert = argv[4];
+ if (argc>5)
+ ca_file = argv[5];
+ if (argc>6)
+ ca_path = argv[6];
+ printf("Server key/cert : %s/%s\n", server_key, server_cert);
+ printf("Client key/cert : %s/%s\n", client_key, client_cert);
+ if (ca_file!=0)
+ printf("CAfile : %s\n", ca_file);
+ if (ca_path!=0)
+ printf("CApath : %s\n", ca_path);
+
+
+ if (socketpair(PF_UNIX, SOCK_STREAM, IPPROTO_IP, sv)==-1)
+ fatal_error("socketpair");
+
+ ssl_acceptor = new_VioSSLAcceptorFd(server_key, server_cert, ca_file,
+ ca_path);
+ ssl_connector = new_VioSSLConnectorFd(client_key, client_cert, ca_file,
+ ca_path, &ssl_init_error);
+
+ client_vio = (Vio*)my_malloc(sizeof(struct st_vio),MYF(0));
+ client_vio->sd = sv[0];
+ sslconnect(ssl_connector,client_vio,&ssl_error);
+ server_vio = (Vio*)my_malloc(sizeof(struct st_vio),MYF(0));
+ server_vio->sd = sv[1];
+ sslaccept(ssl_acceptor,server_vio,&ssl_error);
+
+ printf("Socketpair: %d , %d\n", client_vio->sd, server_vio->sd);
+
+ child_pid = fork();
+ if (child_pid==-1)
+ {
+ my_free(ssl_acceptor);
+ my_free(ssl_connector);
+ fatal_error("fork");
+ }
+ if (child_pid==0)
+ {
+ /* child, therefore, client */
+ char xbuf[100];
+ int r = vio_ssl_read(client_vio,xbuf, sizeof(xbuf));
+ if (r<=0) {
+ my_free(ssl_acceptor);
+ my_free(ssl_connector);
+ fatal_error("client:SSL_read");
+ }
+ xbuf[r] = 0;
+ printf("client:got %s\n", xbuf);
+ my_free(client_vio);
+ my_free(ssl_acceptor);
+ my_free(ssl_connector);
+ sleep(1);
+ }
+ else
+ {
+ const char* s = "Huhuhuh";
+ int r = vio_ssl_write(server_vio,(uchar*)s, strlen(s));
+ if (r<=0) {
+ my_free(ssl_acceptor);
+ my_free(ssl_connector);
+ fatal_error("server:SSL_write");
+ }
+ my_free(server_vio);
+ my_free(ssl_acceptor);
+ my_free(ssl_connector);
+ sleep(1);
+ }
+ return 0;
+}
+#else /* HAVE_OPENSSL */
+
+int main() {
+return 0;
+}
+#endif /* HAVE_OPENSSL */
diff --git a/vio/viotest-sslconnect.cc b/vio/viotest-sslconnect.cc
new file mode 100644
index 00000000..dee3e318
--- /dev/null
+++ b/vio/viotest-sslconnect.cc
@@ -0,0 +1,95 @@
+/* Copyright (c) 2000 MySQL AB
+ Use is subject to license terms.
+
+ 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 */
+
+/*
+** Virtual I/O library
+** Written by Andrei Errapart <andreie@no.spam.ee>
+*/
+
+#include "all.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <unistd.h>
+
+
+void
+fatal_error( const char* r)
+{
+ perror(r);
+ exit(0);
+}
+
+void
+print_usage()
+{
+ printf("viotest-sslconnect: testing SSL virtual IO. Usage:\n");
+ printf("viotest-sslconnect key cert\n");
+}
+
+int
+main( int argc,
+ char** argv)
+{
+ char* key = 0;
+ char* cert = 0;
+
+ if (argc<3)
+ {
+ print_usage();
+ return 1;
+ }
+
+ char ip[4] = {127, 0, 0, 1};
+ unsigned long addr = (unsigned long)
+ ((unsigned long)ip[0]<<24L)|
+ ((unsigned long)ip[1]<<16L)|
+ ((unsigned long)ip[2]<< 8L)|
+ ((unsigned long)ip[3]);
+ int fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (fd<0)
+ fatal_error("socket");
+ struct sockaddr_in sa;
+ sa.sin_family = AF_INET;
+ sa.sin_port=htons(4433);
+ sa.sin_addr.s_addr=htonl(addr);
+ int sa_size = sizeof sa;
+ if (connect(fd, reinterpret_cast<const sockaddr*>(&sa), sa_size)==-1)
+ fatal_error("connect");
+ key = argv[1];
+ cert = argv[2];
+ printf("Key : %s\n", key);
+ printf("Cert : %s\n", cert);
+
+ VIO_NS::VioSSLConnectorFd* ssl_connector = new VIO_NS::VioSSLConnectorFd(cert, key,0,0);
+
+ VIO_NS::VioSSL* vio = ssl_connector->connect(fd);
+
+ char xbuf[100];
+ int r = vio->read(xbuf, sizeof(xbuf));
+ if (r<=0) {
+ delete ssl_connector;
+ delete vio;
+ fatal_error("client:SSL_read");
+ }
+ xbuf[r] = 0;
+ printf("client:got %s\n", xbuf);
+ delete vio;
+ delete ssl_connector;
+ return 0;
+}
diff --git a/vio/viotest.cc b/vio/viotest.cc
new file mode 100644
index 00000000..cfb74cf3
--- /dev/null
+++ b/vio/viotest.cc
@@ -0,0 +1,63 @@
+/* Copyright (c) 2000 MySQL AB
+ Use is subject to license terms.
+
+ 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 */
+
+/*
+** Virtual I/O library
+** Written by Andrei Errapart <andreie@no.spam.ee>
+*/
+
+#include "all.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+
+#include <string.h>
+
+VIO_NS_USING;
+
+int
+main( int argc,
+ char** argv)
+{
+ VioFd* fs = 0;
+ VioSocket* ss = 0;
+ int fd = -1;
+ char* hh = "hshshsh\n";
+
+ DBUG_ENTER("main");
+ DBUG_PROCESS(argv[0]);
+ DBUG_PUSH("d:t");
+
+ fd = open("/dev/tty", O_WRONLY);
+ if (fd<0)
+ {
+ perror("open");
+ return 1;
+ }
+ fs = new VioFd(fd);
+ ss = new VioSocket(fd);
+ if (fs->write(hh,strlen(hh)) < 0)
+ perror("write");
+ ss->write(hh,strlen(hh));
+ printf("peer_name:%s\n", ss->peer_name());
+ printf("cipher_description:%s\n", ss->cipher_description());
+ delete fs;
+ delete ss;
+
+ DBUG_RETURN(0);
+}
+