| 1 | /* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. |
| 2 | Copyright (c) 2012, Monty Program Ab |
| 3 | |
| 4 | This program is free software; you can redistribute it and/or modify |
| 5 | it under the terms of the GNU General Public License as published by |
| 6 | the Free Software Foundation; version 2 of the License. |
| 7 | |
| 8 | This program is distributed in the hope that it will be useful, |
| 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 | GNU General Public License for more details. |
| 12 | |
| 13 | You should have received a copy of the GNU General Public License |
| 14 | along with this program; if not, write to the Free Software |
| 15 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ |
| 16 | |
| 17 | /* |
| 18 | Note that we can't have assertion on file descriptors; The reason for |
| 19 | this is that during mysql shutdown, another thread can close a file |
| 20 | we are working on. In this case we should just return read errors from |
| 21 | the file descriptior. |
| 22 | */ |
| 23 | |
| 24 | #include "vio_priv.h" |
| 25 | #include "ssl_compat.h" |
| 26 | |
| 27 | #ifdef _WIN32 |
| 28 | |
| 29 | /** |
| 30 | Stub io_wait method that defaults to indicate that |
| 31 | requested I/O event is ready. |
| 32 | |
| 33 | Used for named pipe and shared memory VIO types. |
| 34 | |
| 35 | @param vio Unused. |
| 36 | @param event Unused. |
| 37 | @param timeout Unused. |
| 38 | |
| 39 | @retval 1 The requested I/O event has occurred. |
| 40 | */ |
| 41 | |
| 42 | static int no_io_wait(Vio *vio __attribute__((unused)), |
| 43 | enum enum_vio_io_event event __attribute__((unused)), |
| 44 | int timeout __attribute__((unused))) |
| 45 | { |
| 46 | return 1; |
| 47 | } |
| 48 | |
| 49 | #endif |
| 50 | |
| 51 | static my_bool has_no_data(Vio *vio __attribute__((unused))) |
| 52 | { |
| 53 | return FALSE; |
| 54 | } |
| 55 | |
| 56 | #ifdef _WIN32 |
| 57 | my_bool vio_shared_memory_has_data(Vio *vio) |
| 58 | { |
| 59 | return (vio->shared_memory_remain > 0); |
| 60 | } |
| 61 | |
| 62 | int vio_shared_memory_shutdown(Vio *vio, int how) |
| 63 | { |
| 64 | SetEvent(vio->event_conn_closed); |
| 65 | SetEvent(vio->event_server_wrote); |
| 66 | return 0; |
| 67 | } |
| 68 | |
| 69 | int vio_pipe_shutdown(Vio *vio, int how) |
| 70 | { |
| 71 | return CancelIoEx(vio->hPipe, NULL); |
| 72 | } |
| 73 | #endif |
| 74 | |
| 75 | /* |
| 76 | * Helper to fill most of the Vio* with defaults. |
| 77 | */ |
| 78 | |
| 79 | static void vio_init(Vio *vio, enum enum_vio_type type, |
| 80 | my_socket sd, uint flags) |
| 81 | { |
| 82 | DBUG_ENTER("vio_init" ); |
| 83 | DBUG_PRINT("enter" , ("type: %d sd: %d flags: %d" , type, (int)sd, flags)); |
| 84 | |
| 85 | #ifndef HAVE_VIO_READ_BUFF |
| 86 | flags&= ~VIO_BUFFERED_READ; |
| 87 | #endif |
| 88 | memset(vio, 0, sizeof(*vio)); |
| 89 | vio->type= type; |
| 90 | vio->mysql_socket= MYSQL_INVALID_SOCKET; |
| 91 | mysql_socket_setfd(&vio->mysql_socket, sd); |
| 92 | vio->localhost= flags & VIO_LOCALHOST; |
| 93 | vio->read_timeout= vio->write_timeout= -1; |
| 94 | if ((flags & VIO_BUFFERED_READ) && |
| 95 | !(vio->read_buffer= (char*)my_malloc(VIO_READ_BUFFER_SIZE, MYF(MY_WME)))) |
| 96 | flags&= ~VIO_BUFFERED_READ; |
| 97 | #ifdef _WIN32 |
| 98 | if (type == VIO_TYPE_NAMEDPIPE) |
| 99 | { |
| 100 | vio->viodelete =vio_delete; |
| 101 | vio->vioerrno =vio_errno; |
| 102 | vio->read =vio_read_pipe; |
| 103 | vio->write =vio_write_pipe; |
| 104 | vio->fastsend =vio_fastsend; |
| 105 | vio->viokeepalive =vio_keepalive; |
| 106 | vio->should_retry =vio_should_retry; |
| 107 | vio->was_timeout =vio_was_timeout; |
| 108 | vio->vioclose =vio_close_pipe; |
| 109 | vio->peer_addr =vio_peer_addr; |
| 110 | vio->vioblocking =vio_blocking; |
| 111 | vio->is_blocking =vio_is_blocking; |
| 112 | vio->io_wait =no_io_wait; |
| 113 | vio->is_connected =vio_is_connected_pipe; |
| 114 | vio->has_data =has_no_data; |
| 115 | vio->shutdown =vio_pipe_shutdown; |
| 116 | DBUG_VOID_RETURN; |
| 117 | } |
| 118 | #endif |
| 119 | #ifdef HAVE_SMEM |
| 120 | if (type == VIO_TYPE_SHARED_MEMORY) |
| 121 | { |
| 122 | vio->viodelete =vio_delete; |
| 123 | vio->vioerrno =vio_errno; |
| 124 | vio->read =vio_read_shared_memory; |
| 125 | vio->write =vio_write_shared_memory; |
| 126 | vio->fastsend =vio_fastsend; |
| 127 | vio->viokeepalive =vio_keepalive; |
| 128 | vio->should_retry =vio_should_retry; |
| 129 | vio->was_timeout =vio_was_timeout; |
| 130 | vio->vioclose =vio_close_shared_memory; |
| 131 | vio->peer_addr =vio_peer_addr; |
| 132 | vio->vioblocking =vio_blocking; |
| 133 | vio->is_blocking =vio_is_blocking; |
| 134 | vio->io_wait =no_io_wait; |
| 135 | vio->is_connected =vio_is_connected_shared_memory; |
| 136 | vio->has_data =vio_shared_memory_has_data; |
| 137 | vio->shutdown =vio_shared_memory_shutdown; |
| 138 | DBUG_VOID_RETURN; |
| 139 | } |
| 140 | #endif |
| 141 | #ifdef HAVE_OPENSSL |
| 142 | if (type == VIO_TYPE_SSL) |
| 143 | { |
| 144 | vio->viodelete =vio_ssl_delete; |
| 145 | vio->vioerrno =vio_errno; |
| 146 | vio->read =vio_ssl_read; |
| 147 | vio->write =vio_ssl_write; |
| 148 | vio->fastsend =vio_fastsend; |
| 149 | vio->viokeepalive =vio_keepalive; |
| 150 | vio->should_retry =vio_should_retry; |
| 151 | vio->was_timeout =vio_was_timeout; |
| 152 | vio->vioclose =vio_ssl_close; |
| 153 | vio->peer_addr =vio_peer_addr; |
| 154 | vio->vioblocking =vio_ssl_blocking; |
| 155 | vio->is_blocking =vio_is_blocking; |
| 156 | vio->io_wait =vio_io_wait; |
| 157 | vio->is_connected =vio_is_connected; |
| 158 | vio->has_data =vio_ssl_has_data; |
| 159 | vio->shutdown =vio_socket_shutdown; |
| 160 | vio->timeout =vio_socket_timeout; |
| 161 | DBUG_VOID_RETURN; |
| 162 | } |
| 163 | #endif /* HAVE_OPENSSL */ |
| 164 | vio->viodelete =vio_delete; |
| 165 | vio->vioerrno =vio_errno; |
| 166 | vio->read= (flags & VIO_BUFFERED_READ) ? vio_read_buff : vio_read; |
| 167 | vio->write =vio_write; |
| 168 | vio->fastsend =vio_fastsend; |
| 169 | vio->viokeepalive =vio_keepalive; |
| 170 | vio->should_retry =vio_should_retry; |
| 171 | vio->was_timeout =vio_was_timeout; |
| 172 | vio->vioclose =vio_close; |
| 173 | vio->peer_addr =vio_peer_addr; |
| 174 | vio->vioblocking =vio_blocking; |
| 175 | vio->is_blocking =vio_is_blocking; |
| 176 | vio->io_wait =vio_io_wait; |
| 177 | vio->is_connected =vio_is_connected; |
| 178 | vio->shutdown =vio_socket_shutdown; |
| 179 | vio->timeout =vio_socket_timeout; |
| 180 | vio->has_data = ((flags & VIO_BUFFERED_READ) ? |
| 181 | vio_buff_has_data : has_no_data); |
| 182 | DBUG_VOID_RETURN; |
| 183 | } |
| 184 | |
| 185 | |
| 186 | /** |
| 187 | Reinitialize an existing Vio object. |
| 188 | |
| 189 | @remark Used to rebind an initialized socket-based Vio object |
| 190 | to another socket-based transport type. For example, |
| 191 | rebind a TCP/IP transport to SSL. |
| 192 | |
| 193 | @param vio A VIO object. |
| 194 | @param type A socket-based transport type. |
| 195 | @param sd The socket. |
| 196 | @param ssl An optional SSL structure. |
| 197 | @param flags Flags passed to vio_init. |
| 198 | |
| 199 | @return Return value is zero on success. |
| 200 | */ |
| 201 | |
| 202 | my_bool vio_reset(Vio* vio, enum enum_vio_type type, |
| 203 | my_socket sd, void *ssl __attribute__((unused)), uint flags) |
| 204 | { |
| 205 | int ret= FALSE; |
| 206 | Vio old_vio= *vio; |
| 207 | DBUG_ENTER("vio_reset" ); |
| 208 | |
| 209 | /* The only supported rebind is from a socket-based transport type. */ |
| 210 | DBUG_ASSERT(vio->type == VIO_TYPE_TCPIP || vio->type == VIO_TYPE_SOCKET); |
| 211 | |
| 212 | /* |
| 213 | Will be reinitialized depending on the flags. |
| 214 | Nonetheless, already buffered inside the SSL layer. |
| 215 | */ |
| 216 | my_free(vio->read_buffer); |
| 217 | |
| 218 | vio_init(vio, type, sd, flags); |
| 219 | |
| 220 | /* Preserve perfschema info for this connection */ |
| 221 | vio->mysql_socket.m_psi= old_vio.mysql_socket.m_psi; |
| 222 | |
| 223 | #ifdef HAVE_OPENSSL |
| 224 | vio->ssl_arg= ssl; |
| 225 | #endif |
| 226 | |
| 227 | /* |
| 228 | Propagate the timeout values. Necessary to also propagate |
| 229 | the underlying proprieties associated with the timeout, |
| 230 | such as the socket blocking mode. |
| 231 | |
| 232 | note: old_vio.read_timeout/old_vio.write_timeout is stored in ms |
| 233 | but vio_timeout() takes seconds as argument, hence the / 1000 |
| 234 | */ |
| 235 | if (old_vio.read_timeout >= 0) |
| 236 | ret|= vio_timeout(vio, 0, old_vio.read_timeout / 1000); |
| 237 | |
| 238 | if (old_vio.write_timeout >= 0) |
| 239 | ret|= vio_timeout(vio, 1, old_vio.write_timeout / 1000); |
| 240 | |
| 241 | DBUG_RETURN(MY_TEST(ret)); |
| 242 | } |
| 243 | |
| 244 | |
| 245 | /* Create a new VIO for socket or TCP/IP connection. */ |
| 246 | |
| 247 | Vio *mysql_socket_vio_new(MYSQL_SOCKET mysql_socket, enum enum_vio_type type, uint flags) |
| 248 | { |
| 249 | Vio *vio; |
| 250 | my_socket sd= mysql_socket_getfd(mysql_socket); |
| 251 | DBUG_ENTER("mysql_socket_vio_new" ); |
| 252 | DBUG_PRINT("enter" , ("sd: %d" , (int)sd)); |
| 253 | if ((vio = (Vio*) my_malloc(sizeof(*vio),MYF(MY_WME)))) |
| 254 | { |
| 255 | vio_init(vio, type, sd, flags); |
| 256 | vio->desc= (vio->type == VIO_TYPE_SOCKET ? "socket" : "TCP/IP" ); |
| 257 | vio->mysql_socket= mysql_socket; |
| 258 | } |
| 259 | DBUG_RETURN(vio); |
| 260 | } |
| 261 | |
| 262 | /* Open the socket or TCP/IP connection and read the fnctl() status */ |
| 263 | |
| 264 | Vio *vio_new(my_socket sd, enum enum_vio_type type, uint flags) |
| 265 | { |
| 266 | Vio *vio; |
| 267 | MYSQL_SOCKET mysql_socket= MYSQL_INVALID_SOCKET; |
| 268 | DBUG_ENTER("vio_new" ); |
| 269 | DBUG_PRINT("enter" , ("sd: %d" , (int)sd)); |
| 270 | |
| 271 | mysql_socket_setfd(&mysql_socket, sd); |
| 272 | vio = mysql_socket_vio_new(mysql_socket, type, flags); |
| 273 | |
| 274 | DBUG_RETURN(vio); |
| 275 | } |
| 276 | |
| 277 | #ifdef _WIN32 |
| 278 | |
| 279 | Vio *vio_new_win32pipe(HANDLE hPipe) |
| 280 | { |
| 281 | Vio *vio; |
| 282 | DBUG_ENTER("vio_new_handle" ); |
| 283 | if ((vio = (Vio*) my_malloc(sizeof(Vio),MYF(MY_WME)))) |
| 284 | { |
| 285 | vio_init(vio, VIO_TYPE_NAMEDPIPE, 0, VIO_LOCALHOST); |
| 286 | vio->desc= "named pipe" ; |
| 287 | /* Create an object for event notification. */ |
| 288 | vio->overlapped.hEvent= CreateEvent(NULL, FALSE, FALSE, NULL); |
| 289 | if (vio->overlapped.hEvent == NULL) |
| 290 | { |
| 291 | my_free(vio); |
| 292 | DBUG_RETURN(NULL); |
| 293 | } |
| 294 | vio->hPipe= hPipe; |
| 295 | } |
| 296 | DBUG_RETURN(vio); |
| 297 | } |
| 298 | |
| 299 | #ifdef HAVE_SMEM |
| 300 | Vio *vio_new_win32shared_memory(HANDLE handle_file_map, HANDLE handle_map, |
| 301 | HANDLE event_server_wrote, HANDLE event_server_read, |
| 302 | HANDLE event_client_wrote, HANDLE event_client_read, |
| 303 | HANDLE event_conn_closed) |
| 304 | { |
| 305 | Vio *vio; |
| 306 | DBUG_ENTER("vio_new_win32shared_memory" ); |
| 307 | if ((vio = (Vio*) my_malloc(sizeof(Vio),MYF(MY_WME)))) |
| 308 | { |
| 309 | vio_init(vio, VIO_TYPE_SHARED_MEMORY, 0, VIO_LOCALHOST); |
| 310 | vio->desc= "shared memory" ; |
| 311 | vio->handle_file_map= handle_file_map; |
| 312 | vio->handle_map= handle_map; |
| 313 | vio->event_server_wrote= event_server_wrote; |
| 314 | vio->event_server_read= event_server_read; |
| 315 | vio->event_client_wrote= event_client_wrote; |
| 316 | vio->event_client_read= event_client_read; |
| 317 | vio->event_conn_closed= event_conn_closed; |
| 318 | vio->shared_memory_remain= 0; |
| 319 | vio->shared_memory_pos= handle_map; |
| 320 | } |
| 321 | DBUG_RETURN(vio); |
| 322 | } |
| 323 | #endif |
| 324 | #endif |
| 325 | |
| 326 | |
| 327 | /** |
| 328 | Set timeout for a network send or receive operation. |
| 329 | |
| 330 | @remark A non-infinite timeout causes the socket to be |
| 331 | set to non-blocking mode. On infinite timeouts, |
| 332 | the socket is set to blocking mode. |
| 333 | |
| 334 | @remark A negative timeout means an infinite timeout. |
| 335 | |
| 336 | @param vio A VIO object. |
| 337 | @param which Whether timeout is for send (1) or receive (0). |
| 338 | @param timeout Timeout interval in seconds. |
| 339 | |
| 340 | @return FALSE on success, TRUE otherwise. |
| 341 | */ |
| 342 | |
| 343 | int vio_timeout(Vio *vio, uint which, int timeout_sec) |
| 344 | { |
| 345 | int timeout_ms; |
| 346 | my_bool old_mode; |
| 347 | |
| 348 | /* |
| 349 | Vio timeouts are measured in milliseconds. Check for a possible |
| 350 | overflow. In case of overflow, set to infinite. |
| 351 | */ |
| 352 | if (timeout_sec > INT_MAX/1000) |
| 353 | timeout_ms= -1; |
| 354 | else |
| 355 | timeout_ms= (int) (timeout_sec * 1000); |
| 356 | |
| 357 | /* Deduce the current timeout status mode. */ |
| 358 | old_mode= vio->write_timeout < 0 && vio->read_timeout < 0; |
| 359 | |
| 360 | if (which) |
| 361 | vio->write_timeout= timeout_ms; |
| 362 | else |
| 363 | vio->read_timeout= timeout_ms; |
| 364 | |
| 365 | /* VIO-specific timeout handling. Might change the blocking mode. */ |
| 366 | return vio->timeout ? vio->timeout(vio, which, old_mode) : 0; |
| 367 | } |
| 368 | |
| 369 | |
| 370 | void vio_delete(Vio* vio) |
| 371 | { |
| 372 | if (!vio) |
| 373 | return; /* It must be safe to delete null pointers. */ |
| 374 | |
| 375 | if (vio->type != VIO_CLOSED) |
| 376 | vio->vioclose(vio); |
| 377 | my_free(vio->read_buffer); |
| 378 | my_free(vio); |
| 379 | } |
| 380 | |
| 381 | |
| 382 | /* |
| 383 | Cleanup memory allocated by vio or the |
| 384 | components below it when application finish |
| 385 | |
| 386 | */ |
| 387 | void vio_end(void) |
| 388 | { |
| 389 | #ifdef HAVE_YASSL |
| 390 | yaSSL_CleanUp(); |
| 391 | #elif defined(HAVE_OPENSSL) |
| 392 | // This one is needed on the client side |
| 393 | ERR_remove_state(0); |
| 394 | ERR_free_strings(); |
| 395 | EVP_cleanup(); |
| 396 | CRYPTO_cleanup_all_ex_data(); |
| 397 | #endif |
| 398 | } |
| 399 | |