1 /* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 Without limiting anything contained in the foregoing, this file,
15 which is part of C Driver for MySQL (Connector/C), is also subject to the
16 Universal FOSS Exception, version 1.0, a copy of which can be found at
17 http://oss.oracle.com/licenses/universal-foss-exception.
18
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License, version 2.0, for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
27
28 /*
29 This file is included by both libmysql.c (the MySQL client C API)
30 and the mysqld server to connect to another MYSQL server.
31
32 The differences for the two cases are:
33
34 - Things that only works for the client:
35 - Trying to automaticly determinate user name if not supplied to
36 mysql_real_connect()
37 - Support for reading local file with LOAD DATA LOCAL
38 - SHARED memory handling
39 - Prepared statements
40 - Things that only works for the server
41
42 In all other cases, the code should be idential for the client and
43 server.
44 */
45
46 #include "my_config.h"
47
48 #include <stdarg.h>
49 #include <sys/types.h>
50
51 #include "m_ctype.h"
52 #include "m_string.h"
53 #include "my_sys.h"
54 #include "mysys_err.h"
55 #ifndef _WIN32
56 #include <netdb.h>
57 #endif
58 #ifdef HAVE_NETINET_IN_H
59 #include <netinet/in.h>
60 #endif
61 #include <stdio.h>
62 #include <string>
63
64 #include <algorithm>
65
66 #include "client_async_authentication.h"
67 #include "compression.h" // validate_compression_attributes
68 #include "errmsg.h"
69 #include "lex_string.h"
70 #include "map_helpers.h"
71 #include "my_byteorder.h"
72 #include "my_compiler.h"
73 #include "my_dbug.h"
74 #include "my_default.h"
75 #include "my_inttypes.h"
76 #include "my_io.h"
77 #include "my_loglevel.h"
78 #include "my_macros.h"
79 #include "my_psi_config.h"
80 #include "my_shm_defaults.h"
81 #include "mysql.h"
82 #include "mysql/client_authentication.h"
83 #include "mysql/plugin_auth_common.h"
84 #include "mysql/psi/mysql_memory.h"
85 #include "mysql/service_mysql_alloc.h"
86 #include "mysql_version.h"
87 #include "mysqld_error.h"
88 #include "template_utils.h"
89 #include "typelib.h"
90 #include "violite.h"
91
92 #if !defined(_WIN32)
93 #include "my_thread.h" /* because of signal() */
94 #endif /* !defined(_WIN32) */
95
96 #include <signal.h>
97 #include <sys/stat.h>
98 #include <time.h>
99
100 #ifdef HAVE_PWD_H
101 #include <pwd.h>
102 #endif
103
104 #ifdef HAVE_SYS_SELECT_H
105 #include <sys/select.h>
106 #endif
107
108 #ifdef HAVE_SYS_UN_H
109 #include <sys/un.h>
110 #endif
111
112 #ifndef _WIN32
113 #include <errno.h>
114
115 #define SOCKET_ERROR -1
116 #endif
117
118 #include <openssl/x509v3.h>
119
120 #include <mysql/client_plugin.h>
121 #include <new>
122
123 #include "../libmysql/init_commands_array.h"
124 #include "../libmysql/mysql_trace.h" /* MYSQL_TRACE() instrumentation */
125 #include "sql_common.h"
126 #if defined(MYSQL_SERVER) && !defined(XTRABACKUP)
127 #include "mysql_com_server.h"
128 #include "sql/client_settings.h"
129 #else
130 #include "libmysql/client_settings.h"
131 #endif
132 #include "client_extensions_macros.h"
133 #include "sql/log_event.h" /* Log_event_type */
134 #include "sql/rpl_constants.h" /* mysql_binlog_XXX() */
135
136 using std::string;
137 using std::swap;
138
139 #define STATE_DATA(M) \
140 (NULL != (M) ? &(MYSQL_EXTENSION_PTR(M)->state_change) : NULL)
141
142 #define ADD_INFO(M, element, type) \
143 { \
144 M = STATE_DATA(mysql); \
145 M->info_list[type].head_node = \
146 list_add(M->info_list[type].head_node, element); \
147 }
148
149 #define native_password_plugin_name "mysql_native_password"
150 #define caching_sha2_password_plugin_name "caching_sha2_password"
151
152 PSI_memory_key key_memory_mysql_options;
153 PSI_memory_key key_memory_MYSQL_DATA;
154 PSI_memory_key key_memory_MYSQL;
155 PSI_memory_key key_memory_MYSQL_RES;
156 PSI_memory_key key_memory_MYSQL_ROW;
157 PSI_memory_key key_memory_MYSQL_state_change_info;
158 PSI_memory_key key_memory_MYSQL_HANDSHAKE;
159
160 #if defined(_WIN32)
161 PSI_memory_key key_memory_create_shared_memory;
162 #endif /* _WIN32 */
163
164 #ifdef HAVE_PSI_INTERFACE
165 /*
166 This code is common to the client and server,
167 and also used in the server when server A connects to server B,
168 for example with replication.
169 Therefore, the code is also instrumented.
170 */
171
172 static PSI_memory_info all_client_memory[] = {
173 #if defined(_WIN32)
174 {&key_memory_create_shared_memory, "create_shared_memory", 0, 0,
175 PSI_DOCUMENT_ME},
176 #endif /* _WIN32 */
177
178 {&key_memory_mysql_options, "mysql_options", 0, 0, PSI_DOCUMENT_ME},
179 {&key_memory_MYSQL_DATA, "MYSQL_DATA", 0, 0, PSI_DOCUMENT_ME},
180 {&key_memory_MYSQL, "MYSQL", 0, 0, PSI_DOCUMENT_ME},
181 {&key_memory_MYSQL_RES, "MYSQL_RES", 0, 0, PSI_DOCUMENT_ME},
182 {&key_memory_MYSQL_ROW, "MYSQL_ROW", 0, 0, PSI_DOCUMENT_ME},
183 {&key_memory_MYSQL_state_change_info, "MYSQL_STATE_CHANGE_INFO", 0, 0,
184 PSI_DOCUMENT_ME},
185 {&key_memory_MYSQL_HANDSHAKE, "MYSQL_HANDSHAKE", 0, 0, PSI_DOCUMENT_ME}};
186
init_client_psi_keys(void)187 void init_client_psi_keys(void) {
188 const char *category = "client";
189 int count;
190
191 count = static_cast<int>(array_elements(all_client_memory));
192 mysql_memory_register(category, all_client_memory, count);
193 }
194
195 #endif /* HAVE_PSI_INTERFACE */
196
197 uint mysql_port = 0;
198 char *mysql_unix_port = nullptr;
199 const char *unknown_sqlstate = "HY000";
200 const char *not_error_sqlstate = "00000";
201 const char *cant_connect_sqlstate = "08001";
202 #if defined(_WIN32)
203 static char *shared_memory_base_name = 0;
204 const char *def_shared_memory_base_name = default_shared_memory_base_name;
205 #endif
206
207 ulong g_net_buffer_length = 8192;
208 ulong g_max_allowed_packet = 1024L * 1024L * 1024L;
209
210 static void mysql_prune_stmt_list(MYSQL *mysql);
211 static int read_com_query_metadata(MYSQL *mysql, uchar *pos, ulong field_count);
212
213 CHARSET_INFO *default_client_charset_info = &my_charset_latin1;
214
215 /* Server error code and message */
216 unsigned int mysql_server_last_errno;
217 char mysql_server_last_error[MYSQL_ERRMSG_SIZE];
218 /* forward declaration */
219 static int read_one_row(MYSQL *mysql, uint fields, MYSQL_ROW row,
220 ulong *lengths);
221 static net_async_status read_one_row_nonblocking(MYSQL *mysql, uint fields,
222 MYSQL_ROW row, ulong *lengths,
223 int *res);
224
225 /**
226 Convert the connect timeout option to a timeout value for VIO
227 functions (vio_socket_connect() and vio_io_wait()).
228
229 @param mysql Connection handle (client side).
230
231 @return The timeout value in milliseconds, or -1 if no timeout.
232 */
233
get_vio_connect_timeout(MYSQL * mysql)234 static int get_vio_connect_timeout(MYSQL *mysql) {
235 int timeout_ms;
236 uint timeout_sec;
237
238 /*
239 A timeout of 0 means no timeout. Also, the connect_timeout
240 option value is in seconds, while VIO timeouts are measured
241 in milliseconds. Hence, check for a possible overflow. In
242 case of overflow, set to no timeout.
243 */
244 timeout_sec = mysql->options.connect_timeout;
245
246 if (!timeout_sec || (timeout_sec > INT_MAX / 1000))
247 timeout_ms = -1;
248 else
249 timeout_ms = (int)(timeout_sec * 1000);
250
251 return timeout_ms;
252 }
253
254 #ifdef _WIN32
255
256 /**
257 Convert the connect timeout option to a timeout value for WIN32
258 synchronization functions.
259
260 @remark Specific for WIN32 connection methods shared memory and
261 named pipe.
262
263 @param mysql Connection handle (client side).
264
265 @return The timeout value in milliseconds, or INFINITE if no timeout.
266 */
267
get_win32_connect_timeout(MYSQL * mysql)268 static DWORD get_win32_connect_timeout(MYSQL *mysql) {
269 DWORD timeout_ms;
270 uint timeout_sec;
271
272 /*
273 A timeout of 0 means no timeout. Also, the connect_timeout
274 option value is in seconds, while WIN32 timeouts are in
275 milliseconds. Hence, check for a possible overflow. In case
276 of overflow, set to no timeout.
277 */
278 timeout_sec = mysql->options.connect_timeout;
279
280 if (!timeout_sec || (timeout_sec > INT_MAX / 1000))
281 timeout_ms = INFINITE;
282 else
283 timeout_ms = (DWORD)(timeout_sec * 1000);
284
285 return timeout_ms;
286 }
287
288 #endif
289
290 /**
291 Set the internal error message to mysql handler
292
293 @param mysql connection handle (client side)
294 @param errcode CR_ error code, passed to ER macro to get
295 error text
296 @param sqlstate SQL standard sqlstate
297 */
298
set_mysql_error(MYSQL * mysql,int errcode,const char * sqlstate)299 void set_mysql_error(MYSQL *mysql, int errcode, const char *sqlstate) {
300 NET *net;
301 DBUG_TRACE;
302 DBUG_PRINT("enter", ("error :%d '%s'", errcode, ER_CLIENT(errcode)));
303 DBUG_ASSERT(mysql != nullptr);
304
305 if (mysql) {
306 net = &mysql->net;
307 net->last_errno = errcode;
308 my_stpcpy(net->last_error, ER_CLIENT(errcode));
309 my_stpcpy(net->sqlstate, sqlstate);
310 MYSQL_TRACE(ERROR, mysql, ());
311 } else {
312 mysql_server_last_errno = errcode;
313 my_stpcpy(mysql_server_last_error, ER_CLIENT(errcode));
314 }
315 }
316
317 /**
318 Is this NET instance initialized?
319 @c my_net_init() and net_end()
320 */
321
my_net_is_inited(NET * net)322 static bool my_net_is_inited(NET *net) { return net->buff != nullptr; }
323
324 /**
325 Clear possible error state of struct NET
326
327 @param net clear the state of the argument
328 */
329
net_clear_error(NET * net)330 void net_clear_error(NET *net) {
331 net->last_errno = 0;
332 net->last_error[0] = '\0';
333 my_stpcpy(net->sqlstate, not_error_sqlstate);
334 }
335
336 /**
337 Set an error message on the client.
338
339 @param mysql connection handle
340 @param errcode CR_* errcode, for client errors
341 @param sqlstate SQL standard sql state, unknown_sqlstate for the
342 majority of client errors.
343 @param format error message template, in sprintf format
344 @param ... variable number of arguments
345 */
346
set_mysql_extended_error(MYSQL * mysql,int errcode,const char * sqlstate,const char * format,...)347 void set_mysql_extended_error(MYSQL *mysql, int errcode, const char *sqlstate,
348 const char *format, ...) {
349 NET *net;
350 va_list args;
351 DBUG_TRACE;
352 DBUG_PRINT("enter", ("error :%d '%s'", errcode, format));
353 DBUG_ASSERT(mysql != nullptr);
354
355 net = &mysql->net;
356 net->last_errno = errcode;
357 va_start(args, format);
358 vsnprintf(net->last_error, sizeof(net->last_error) - 1, format, args);
359 va_end(args);
360 my_stpcpy(net->sqlstate, sqlstate);
361
362 MYSQL_TRACE(ERROR, mysql, ());
363 }
364
365 /*
366 Create a named pipe connection
367 */
368
369 #ifdef _WIN32
370
create_named_pipe(MYSQL * mysql,DWORD connect_timeout,const char ** arg_host,const char ** arg_unix_socket)371 static HANDLE create_named_pipe(MYSQL *mysql, DWORD connect_timeout,
372 const char **arg_host,
373 const char **arg_unix_socket) {
374 HANDLE hPipe = INVALID_HANDLE_VALUE;
375 char pipe_name[1024];
376 DWORD dwMode;
377 int i;
378 bool testing_named_pipes = 0;
379 const char *host = *arg_host, *unix_socket = *arg_unix_socket;
380
381 if (!unix_socket || (unix_socket)[0] == 0x00) unix_socket = mysql_unix_port;
382 if (!host || !strcmp(host, LOCAL_HOST)) host = LOCAL_HOST_NAMEDPIPE;
383
384 pipe_name[sizeof(pipe_name) - 1] = 0; /* Safety if too long string */
385 strxnmov(pipe_name, sizeof(pipe_name) - 1, "\\\\.\\pipe\\", unix_socket,
386 NullS);
387
388 DBUG_PRINT("info", ("Server name: '%s'. Named Pipe: %s", host, unix_socket));
389
390 for (i = 0; i < 100; i++) /* Don't retry forever */
391 {
392 if ((hPipe = CreateFile(pipe_name,
393 FILE_READ_ATTRIBUTES | FILE_READ_DATA |
394 FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA,
395 0, NULL, OPEN_EXISTING,
396 FILE_FLAG_OVERLAPPED | SECURITY_SQOS_PRESENT |
397 SECURITY_IDENTIFICATION,
398 NULL)) != INVALID_HANDLE_VALUE)
399 break;
400 if (GetLastError() != ERROR_PIPE_BUSY) {
401 set_mysql_extended_error(mysql, CR_NAMEDPIPEOPEN_ERROR, unknown_sqlstate,
402 ER_CLIENT(CR_NAMEDPIPEOPEN_ERROR), host,
403 unix_socket, (ulong)GetLastError());
404 return INVALID_HANDLE_VALUE;
405 }
406 /* wait for for an other instance */
407 if (!WaitNamedPipe(pipe_name, connect_timeout)) {
408 set_mysql_extended_error(mysql, CR_NAMEDPIPEWAIT_ERROR, unknown_sqlstate,
409 ER_CLIENT(CR_NAMEDPIPEWAIT_ERROR), host,
410 unix_socket, (ulong)GetLastError());
411 return INVALID_HANDLE_VALUE;
412 }
413 }
414 if (hPipe == INVALID_HANDLE_VALUE) {
415 set_mysql_extended_error(mysql, CR_NAMEDPIPEOPEN_ERROR, unknown_sqlstate,
416 ER_CLIENT(CR_NAMEDPIPEOPEN_ERROR), host,
417 unix_socket, (ulong)GetLastError());
418 return INVALID_HANDLE_VALUE;
419 }
420 dwMode = PIPE_READMODE_BYTE | PIPE_WAIT;
421 if (!SetNamedPipeHandleState(hPipe, &dwMode, NULL, NULL)) {
422 CloseHandle(hPipe);
423 set_mysql_extended_error(mysql, CR_NAMEDPIPESETSTATE_ERROR,
424 unknown_sqlstate,
425 ER_CLIENT(CR_NAMEDPIPESETSTATE_ERROR), host,
426 unix_socket, (ulong)GetLastError());
427 return INVALID_HANDLE_VALUE;
428 }
429 *arg_host = host;
430 *arg_unix_socket = unix_socket; /* connect arg */
431 return (hPipe);
432 }
433 #endif
434
435 /*
436 Create new shared memory connection, return handler of connection
437
438 @param mysql Pointer of mysql structure
439 @param net Pointer of net structure
440 @param connect_timeout Timeout of connection (in milliseconds)
441
442 @return HANDLE to the shared memory area.
443 */
444
445 #if defined(_WIN32)
create_shared_memory(MYSQL * mysql,NET * net,DWORD connect_timeout)446 static HANDLE create_shared_memory(MYSQL *mysql, NET *net,
447 DWORD connect_timeout) {
448 ulong smem_buffer_length = shared_memory_buffer_length + 4;
449 /*
450 event_connect_request is event object for start connection actions
451 event_connect_answer is event object for confirm, that server put data
452 handle_connect_file_map is file-mapping object, use for create shared
453 memory
454 handle_connect_map is pointer on shared memory
455 handle_map is pointer on shared memory for client
456 event_server_wrote,
457 event_server_read,
458 event_client_wrote,
459 event_client_read are events for transfer data between server and client
460 handle_file_map is file-mapping object, use for create shared memory
461 */
462 HANDLE event_connect_request = NULL;
463 HANDLE event_connect_answer = NULL;
464 HANDLE handle_connect_file_map = NULL;
465 char *handle_connect_map = NULL;
466
467 char *handle_map = NULL;
468 HANDLE event_server_wrote = NULL;
469 HANDLE event_server_read = NULL;
470 HANDLE event_client_wrote = NULL;
471 HANDLE event_client_read = NULL;
472 HANDLE event_conn_closed = NULL;
473 HANDLE handle_file_map = NULL;
474 HANDLE connect_named_mutex = NULL;
475 ulong connect_number;
476 char connect_number_char[22], *p;
477 char *tmp = NULL;
478 char *suffix_pos;
479 DWORD error_allow = 0;
480 DWORD error_code = 0;
481 DWORD event_access_rights = SYNCHRONIZE | EVENT_MODIFY_STATE;
482 char *shared_memory_base_name = mysql->options.shared_memory_base_name;
483 static const char *name_prefixes[] = {"", "Global\\"};
484 const char *prefix;
485 int i;
486
487 /*
488 If this is NULL, somebody freed the MYSQL* options. mysql_close()
489 is a good candidate. We don't just silently (re)set it to
490 def_shared_memory_base_name as that would create really confusing/buggy
491 behavior if the user passed in a different name on the command-line or
492 in a my.cnf.
493 */
494 DBUG_ASSERT(shared_memory_base_name != NULL);
495
496 /*
497 get enough space base-name + '_' + longest suffix we might ever send
498 */
499 if (!(tmp = (char *)my_malloc(key_memory_create_shared_memory,
500 strlen(shared_memory_base_name) + 32L,
501 MYF(MY_FAE))))
502 goto err;
503
504 /*
505 The name of event and file-mapping events create agree next rule:
506 shared_memory_base_name+unique_part
507 Where:
508 shared_memory_base_name is unique value for each server
509 unique_part is uniquel value for each object (events and file-mapping)
510 */
511 for (i = 0; i < array_elements(name_prefixes); i++) {
512 prefix = name_prefixes[i];
513 suffix_pos = strxmov(tmp, prefix, shared_memory_base_name, "_", NullS);
514 my_stpcpy(suffix_pos, "CONNECT_REQUEST");
515 event_connect_request = OpenEvent(event_access_rights, false, tmp);
516 if (event_connect_request) {
517 break;
518 }
519 }
520 if (!event_connect_request) {
521 error_allow = CR_SHARED_MEMORY_CONNECT_REQUEST_ERROR;
522 goto err;
523 }
524 my_stpcpy(suffix_pos, "CONNECT_ANSWER");
525 if (!(event_connect_answer = OpenEvent(event_access_rights, false, tmp))) {
526 error_allow = CR_SHARED_MEMORY_CONNECT_ANSWER_ERROR;
527 goto err;
528 }
529 my_stpcpy(suffix_pos, "CONNECT_DATA");
530 if (!(handle_connect_file_map =
531 OpenFileMapping(FILE_MAP_WRITE, false, tmp))) {
532 error_allow = CR_SHARED_MEMORY_CONNECT_FILE_MAP_ERROR;
533 goto err;
534 }
535 if (!(handle_connect_map = static_cast<char *>(MapViewOfFile(
536 handle_connect_file_map, FILE_MAP_WRITE, 0, 0, sizeof(DWORD))))) {
537 error_allow = CR_SHARED_MEMORY_CONNECT_MAP_ERROR;
538 goto err;
539 }
540
541 my_stpcpy(suffix_pos, "CONNECT_NAMED_MUTEX");
542 connect_named_mutex = OpenMutex(SYNCHRONIZE, false, tmp);
543 if (connect_named_mutex == NULL) {
544 error_allow = CR_SHARED_MEMORY_CONNECT_SET_ERROR;
545 goto err;
546 }
547
548 if (WaitForSingleObject(connect_named_mutex, connect_timeout) !=
549 WAIT_OBJECT_0) {
550 error_allow = CR_SHARED_MEMORY_CONNECT_ABANDONED_ERROR;
551 goto err;
552 }
553
554 /* Send to server request of connection */
555 if (!SetEvent(event_connect_request)) {
556 error_allow = CR_SHARED_MEMORY_CONNECT_SET_ERROR;
557 goto err;
558 }
559
560 /* Wait of answer from server */
561 if (WaitForSingleObject(event_connect_answer, connect_timeout) !=
562 WAIT_OBJECT_0) {
563 error_allow = CR_SHARED_MEMORY_CONNECT_ABANDONED_ERROR;
564 goto err;
565 }
566
567 /* Get number of connection */
568 connect_number = uint4korr(handle_connect_map); /*WAX2*/
569
570 ReleaseMutex(connect_named_mutex);
571 CloseHandle(connect_named_mutex);
572 connect_named_mutex = NULL;
573
574 p = longlong10_to_str(connect_number, connect_number_char, 10);
575
576 /*
577 The name of event and file-mapping events create agree next rule:
578 shared_memory_base_name+unique_part+number_of_connection
579
580 Where:
581 shared_memory_base_name is uniquel value for each server
582 unique_part is uniquel value for each object (events and file-mapping)
583 number_of_connection is number of connection between server and client
584 */
585 suffix_pos = strxmov(tmp, prefix, shared_memory_base_name, "_",
586 connect_number_char, "_", NullS);
587 my_stpcpy(suffix_pos, "DATA");
588 if ((handle_file_map = OpenFileMapping(FILE_MAP_WRITE, false, tmp)) == NULL) {
589 error_allow = CR_SHARED_MEMORY_FILE_MAP_ERROR;
590 goto err2;
591 }
592 if ((handle_map = static_cast<char *>(MapViewOfFile(
593 handle_file_map, FILE_MAP_WRITE, 0, 0, smem_buffer_length))) ==
594 NULL) {
595 error_allow = CR_SHARED_MEMORY_MAP_ERROR;
596 goto err2;
597 }
598
599 my_stpcpy(suffix_pos, "SERVER_WROTE");
600 if ((event_server_wrote = OpenEvent(event_access_rights, false, tmp)) ==
601 NULL) {
602 error_allow = CR_SHARED_MEMORY_EVENT_ERROR;
603 goto err2;
604 }
605
606 my_stpcpy(suffix_pos, "SERVER_READ");
607 if ((event_server_read = OpenEvent(event_access_rights, false, tmp)) ==
608 NULL) {
609 error_allow = CR_SHARED_MEMORY_EVENT_ERROR;
610 goto err2;
611 }
612
613 my_stpcpy(suffix_pos, "CLIENT_WROTE");
614 if ((event_client_wrote = OpenEvent(event_access_rights, false, tmp)) ==
615 NULL) {
616 error_allow = CR_SHARED_MEMORY_EVENT_ERROR;
617 goto err2;
618 }
619
620 my_stpcpy(suffix_pos, "CLIENT_READ");
621 if ((event_client_read = OpenEvent(event_access_rights, false, tmp)) ==
622 NULL) {
623 error_allow = CR_SHARED_MEMORY_EVENT_ERROR;
624 goto err2;
625 }
626
627 my_stpcpy(suffix_pos, "CONNECTION_CLOSED");
628 if ((event_conn_closed = OpenEvent(event_access_rights, false, tmp)) ==
629 NULL) {
630 error_allow = CR_SHARED_MEMORY_EVENT_ERROR;
631 goto err2;
632 }
633 /*
634 Set event that server should send data
635 */
636 SetEvent(event_server_read);
637
638 err2:
639 if (error_allow == 0) {
640 net->vio = vio_new_win32shared_memory(
641 handle_file_map, handle_map, event_server_wrote, event_server_read,
642 event_client_wrote, event_client_read, event_conn_closed);
643 } else {
644 error_code = GetLastError();
645 if (event_server_read) CloseHandle(event_server_read);
646 if (event_server_wrote) CloseHandle(event_server_wrote);
647 if (event_client_read) CloseHandle(event_client_read);
648 if (event_client_wrote) CloseHandle(event_client_wrote);
649 if (event_conn_closed) CloseHandle(event_conn_closed);
650 if (handle_map) UnmapViewOfFile(handle_map);
651 if (handle_file_map) CloseHandle(handle_file_map);
652 }
653 err:
654 my_free(tmp);
655 if (error_allow) error_code = GetLastError();
656 if (event_connect_request) CloseHandle(event_connect_request);
657 if (event_connect_answer) CloseHandle(event_connect_answer);
658 if (handle_connect_map) UnmapViewOfFile(handle_connect_map);
659 if (handle_connect_file_map) CloseHandle(handle_connect_file_map);
660 if (error_allow) {
661 if (connect_named_mutex) {
662 ReleaseMutex(connect_named_mutex);
663 CloseHandle(connect_named_mutex);
664 }
665
666 if (error_allow == CR_SHARED_MEMORY_EVENT_ERROR)
667 set_mysql_extended_error(mysql, error_allow, unknown_sqlstate,
668 ER_CLIENT(error_allow), suffix_pos, error_code);
669 else
670 set_mysql_extended_error(mysql, error_allow, unknown_sqlstate,
671 ER_CLIENT(error_allow), error_code);
672 return (INVALID_HANDLE_VALUE);
673 }
674 return (handle_map);
675 }
676 #endif
677
678 /*
679 Free all memory acquired to store state change information.
680 */
free_state_change_info(MYSQL_EXTENSION * ext)681 static void free_state_change_info(MYSQL_EXTENSION *ext) {
682 STATE_INFO *info;
683 int i;
684
685 if (ext)
686 info = &ext->state_change;
687 else
688 return;
689
690 for (i = SESSION_TRACK_SYSTEM_VARIABLES; i <= SESSION_TRACK_END; i++) {
691 if (list_length(info->info_list[i].head_node) != 0) {
692 list_free(info->info_list[i].head_node, (uint)0);
693 }
694 }
695 memset(info, 0, sizeof(STATE_INFO));
696 }
697
698 /**
699 Helper function to check if the buffer has at least bytes remaining
700
701 If the buffer is too small it raises CR_MALFORMED_PACKET_ERROR.
702
703 @param mysql the handle that has the buffer
704 @param packet the current position in the buffer
705 @param packet_length the size of the packet
706 @param bytes the bytes that we want available
707 @retval true the buffer has that many bytes
708 @retval false the buffer has less bytes remaining
709 */
buffer_check_remaining(MYSQL * mysql,uchar * packet,ulong packet_length,size_t bytes)710 inline bool buffer_check_remaining(MYSQL *mysql, uchar *packet,
711 ulong packet_length, size_t bytes) {
712 size_t remaining_bytes = packet_length - (packet - mysql->net.read_pos);
713 if (remaining_bytes < bytes) {
714 set_mysql_error(mysql, CR_MALFORMED_PACKET, unknown_sqlstate);
715 return false;
716 }
717 return true;
718 }
719
720 /*
721 Helper function to safely read a variable size from a buffer.
722 If the buffer is too small it raises CR_MALFORMED_PACKET_ERROR
723 and sets is_error to true.
724 Otherwise it sets is_error to false and calls @ref inet_field_length_ll.
725
726 @sa @ref net_field_length_ll
727
728 @param mysql the handle to return an error in
729 @param [in,out] packet pointer to the buffer to read the length from
730 @param packet_length remining bytes in packet
731 @param [out] is_error set to true if the buffer contains no room for a
732 full length, false otherwise.
733 @return the size read.
734 */
net_field_length_ll_safe(MYSQL * mysql,uchar ** packet,ulong packet_length,bool * is_error)735 inline my_ulonglong net_field_length_ll_safe(MYSQL *mysql, uchar **packet,
736 ulong packet_length,
737 bool *is_error) {
738 size_t sizeof_len = net_field_length_size(*packet);
739 if (!buffer_check_remaining(mysql, *packet, packet_length, sizeof_len)) {
740 *is_error = true;
741 return 0;
742 }
743
744 *is_error = false;
745 return net_field_length_ll(packet);
746 }
747
748 /**
749 Read Ok packet along with the server state change information.
750 */
read_ok_ex(MYSQL * mysql,ulong length)751 void read_ok_ex(MYSQL *mysql, ulong length) {
752 size_t total_len, len;
753 uchar *pos, *saved_pos;
754 my_ulonglong affected_rows, insert_id;
755 char *db;
756 char *data_str;
757
758 CHARSET_INFO *saved_cs;
759 bool is_charset;
760
761 STATE_INFO *info = nullptr;
762 enum enum_session_state_type type;
763 LIST *element = nullptr;
764 LEX_STRING *data = nullptr;
765 bool is_error;
766
767 pos = mysql->net.read_pos + 1;
768
769 affected_rows = net_field_length_ll_safe(mysql, &pos, length,
770 &is_error); /* affected rows */
771 if (is_error) return;
772 insert_id =
773 net_field_length_ll_safe(mysql, &pos, length, &is_error); /* insert id */
774 if (is_error) return;
775
776 /*
777 The following check ensures that we skip the assignment for the
778 above read fields (i.e. affected_rows and insert_id) wherein the
779 EOF packets are deprecated and the server sends OK packet instead
780 with a packet header of 0xFE (254) to identify it as an EOF packet.
781 We ignore this assignment as the valid contents of EOF packet include
782 packet marker, server status and warning count only. However, we would
783 assign these values to the connection handle if it was an OK packet
784 with a packet header of 0x00.
785 */
786
787 if (!((mysql->server_capabilities & CLIENT_DEPRECATE_EOF) &&
788 mysql->net.read_pos[0] == 254)) {
789 mysql->affected_rows = affected_rows;
790 mysql->insert_id = insert_id;
791
792 DBUG_PRINT("info", ("affected_rows: %lu insert_id: %lu",
793 (ulong)mysql->affected_rows, (ulong)mysql->insert_id));
794 }
795
796 if (!buffer_check_remaining(mysql, pos, length, 2)) return;
797 /* server status */
798 mysql->server_status = uint2korr(pos);
799 pos += 2;
800
801 if (protocol_41(mysql)) {
802 if (!buffer_check_remaining(mysql, pos, length, 2)) return;
803 mysql->warning_count = uint2korr(pos);
804 pos += 2;
805 } else
806 mysql->warning_count = 0; /* MySQL 4.0 protocol */
807
808 DBUG_PRINT("info", ("status: %u warning_count: %u", mysql->server_status,
809 mysql->warning_count));
810 if (mysql->server_capabilities & CLIENT_SESSION_TRACK) {
811 free_state_change_info(static_cast<MYSQL_EXTENSION *>(mysql->extension));
812
813 if (pos < mysql->net.read_pos + length) {
814 /* get the info field */
815 size_t length_msg_member =
816 (size_t)net_field_length_ll_safe(mysql, &pos, length, &is_error);
817 if (is_error) return;
818 if (!buffer_check_remaining(mysql, pos, length, length_msg_member))
819 return;
820 mysql->info = (length_msg_member ? (char *)pos : nullptr);
821 pos += (length_msg_member);
822
823 /* read session state changes info */
824 if (mysql->server_status & SERVER_SESSION_STATE_CHANGED) {
825 saved_pos = pos;
826 total_len =
827 (size_t)net_field_length_ll_safe(mysql, &pos, length, &is_error);
828 if (is_error) return;
829 /* ensure that mysql->info is zero-terminated */
830 if (mysql->info) *saved_pos = 0;
831
832 while (total_len > 0) {
833 saved_pos = pos;
834 type = (enum enum_session_state_type)net_field_length_ll_safe(
835 mysql, &pos, length, &is_error);
836 if (is_error) return;
837 switch (type) {
838 case SESSION_TRACK_SYSTEM_VARIABLES:
839 /* Move past the total length of the changed entity. */
840 (void)net_field_length_ll_safe(mysql, &pos, length, &is_error);
841 if (is_error) return;
842
843 /* Name of the system variable. */
844 len = (size_t)net_field_length_ll_safe(mysql, &pos, length,
845 &is_error);
846 if (is_error) return;
847 if (!buffer_check_remaining(mysql, pos, length, len)) return;
848
849 if (!my_multi_malloc(key_memory_MYSQL_state_change_info, MYF(0),
850 &element, sizeof(LIST), &data,
851 sizeof(LEX_STRING), &data_str, len, NullS)) {
852 set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
853 return;
854 }
855
856 data->str = data_str;
857 memcpy(data->str, (char *)pos, len);
858 data->length = len;
859 pos += len;
860
861 element->data = data;
862 ADD_INFO(info, element, SESSION_TRACK_SYSTEM_VARIABLES);
863
864 /*
865 Check if the changed variable was charset. In that case we need
866 to update mysql->charset.
867 */
868 if (!strncmp(data->str, "character_set_client", data->length))
869 is_charset = true;
870 else
871 is_charset = false;
872
873 /* Value of the system variable. */
874 len = (size_t)net_field_length_ll_safe(mysql, &pos, length,
875 &is_error);
876 if (is_error) return;
877 if (!buffer_check_remaining(mysql, pos, length, len)) return;
878
879 if (!my_multi_malloc(key_memory_MYSQL_state_change_info, MYF(0),
880 &element, sizeof(LIST), &data,
881 sizeof(LEX_STRING), &data_str, len, NullS)) {
882 set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
883 return;
884 }
885
886 data->str = data_str;
887 memcpy(data->str, (char *)pos, len);
888 data->length = len;
889 pos += len;
890
891 element->data = data;
892 ADD_INFO(info, element, SESSION_TRACK_SYSTEM_VARIABLES);
893
894 if (is_charset == 1) {
895 char charset_name[MY_CS_NAME_SIZE * 8]; // MY_CS_BUFFER_SIZE
896 size_t charset_name_length =
897 std::min(data->length, sizeof(charset_name) - 1);
898 saved_cs = mysql->charset;
899
900 memcpy(charset_name, data->str, charset_name_length);
901 charset_name[charset_name_length] = 0;
902
903 if (!(mysql->charset = get_charset_by_csname(
904 charset_name, MY_CS_PRIMARY, MYF(MY_WME)))) {
905 DBUG_PRINT(
906 "warning",
907 ("session tracker supplied %s is not a valid charset."
908 " Keeping the old one.",
909 charset_name));
910 mysql->charset = saved_cs;
911 }
912 }
913 break;
914 case SESSION_TRACK_TRANSACTION_STATE:
915 case SESSION_TRACK_TRANSACTION_CHARACTERISTICS:
916 case SESSION_TRACK_SCHEMA:
917
918 /* Move past the total length of the changed entity. */
919 (void)net_field_length_ll_safe(mysql, &pos, length, &is_error);
920 if (is_error) return;
921 len = (size_t)net_field_length_ll_safe(mysql, &pos, length,
922 &is_error);
923 if (is_error) return;
924 if (!buffer_check_remaining(mysql, pos, length, len)) return;
925
926 if (!my_multi_malloc(key_memory_MYSQL_state_change_info, MYF(0),
927 &element, sizeof(LIST), &data,
928 sizeof(LEX_STRING), &data_str, len, NullS)) {
929 set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
930 return;
931 }
932
933 data->str = data_str;
934 memcpy(data->str, (char *)pos, len);
935 data->length = len;
936 pos += len;
937
938 element->data = data;
939 ADD_INFO(info, element, type);
940
941 if (type == SESSION_TRACK_SCHEMA) {
942 if (!(db = (char *)my_malloc(key_memory_MYSQL_state_change_info,
943 data->length + 1, MYF(MY_WME)))) {
944 set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
945 return;
946 }
947
948 if (mysql->db) my_free(mysql->db);
949
950 memcpy(db, data->str, data->length);
951 db[data->length] = '\0';
952 mysql->db = db;
953 }
954
955 break;
956 case SESSION_TRACK_GTIDS:
957 /* Move past the total length of the changed entity. */
958 (void)net_field_length_ll_safe(mysql, &pos, length, &is_error);
959 if (is_error) return;
960
961 /* read (and ignore for now) the GTIDS encoding specification code
962 */
963 (void)net_field_length_ll_safe(mysql, &pos, length, &is_error);
964 if (is_error) return;
965
966 /*
967 For now we ignore the encoding specification, since only one
968 is supported. In the future the decoding of what comes next
969 depends on the specification code.
970 */
971
972 /* read the length of the encoded string. */
973 len = (size_t)net_field_length_ll_safe(mysql, &pos, length,
974 &is_error);
975 if (is_error) return;
976 if (!buffer_check_remaining(mysql, pos, length, len)) return;
977
978 if (!my_multi_malloc(key_memory_MYSQL_state_change_info, MYF(0),
979 &element, sizeof(LIST), &data,
980 sizeof(LEX_STRING), &data_str, len, NullS)) {
981 set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
982 return;
983 }
984
985 data->str = data_str;
986 memcpy(data->str, (char *)pos, len);
987 data->length = len;
988 pos += len;
989
990 element->data = data;
991 ADD_INFO(info, element, SESSION_TRACK_GTIDS);
992 break;
993 case SESSION_TRACK_STATE_CHANGE:
994 /* Get the length of the boolean tracker */
995 len = (size_t)net_field_length_ll_safe(mysql, &pos, length,
996 &is_error);
997 if (is_error) return;
998
999 /* length for boolean tracker is always 1 */
1000 DBUG_ASSERT(len == 1);
1001 if (!buffer_check_remaining(mysql, pos, length, len)) return;
1002
1003 if (!my_multi_malloc(key_memory_MYSQL_state_change_info, MYF(0),
1004 &element, sizeof(LIST), &data,
1005 sizeof(LEX_STRING), &data_str, len, NullS)) {
1006 set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
1007 return;
1008 }
1009
1010 data->str = data_str;
1011 memcpy(data->str, (char *)pos, len);
1012 data->length = len;
1013 pos += len;
1014
1015 element->data = data;
1016 ADD_INFO(info, element, SESSION_TRACK_STATE_CHANGE);
1017
1018 break;
1019 default:
1020 DBUG_ASSERT(type <= SESSION_TRACK_END);
1021 /*
1022 Unknown/unsupported type received, get the total length and move
1023 past it.
1024 */
1025
1026 len = (size_t)net_field_length_ll_safe(mysql, &pos, length,
1027 &is_error);
1028 if (is_error) return;
1029 pos += len;
1030 break;
1031 }
1032 total_len -= (pos - saved_pos);
1033 }
1034 if (info) {
1035 int itype;
1036 for (itype = SESSION_TRACK_BEGIN; itype < SESSION_TRACK_END;
1037 itype++) {
1038 if (info->info_list[itype].head_node) {
1039 info->info_list[itype].current_node =
1040 info->info_list[itype].head_node =
1041 list_reverse(info->info_list[itype].head_node);
1042 }
1043 }
1044 }
1045 }
1046 }
1047 } else if (pos < mysql->net.read_pos + length && net_field_length(&pos))
1048 mysql->info = (char *)pos;
1049 else
1050 mysql->info = nullptr;
1051 return;
1052 }
1053
1054 /* Helper for cli_safe_read and cli_safe_read_nonblocking */
1055 static ulong cli_safe_read_with_ok_complete(MYSQL *mysql, bool parse_ok,
1056 bool *is_data_packet, ulong len);
1057
1058 /**
1059 Read a packet from server in asynchronous way. This function can return
1060 without completly reading the packet, in such a case call this function
1061 again until complete packet is read.
1062
1063 @param[in] mysql connection handle
1064 @param[in] parse_ok if set to true then parse OK packet if it
1065 was sent by server
1066 @param[out] is_data_packet if set to true then the packet received
1067 was a "data packet".
1068 @param[out] res The length of the packet that was read or
1069 packet_error in case of error.
1070
1071 @retval NET_ASYNC_NOT_READY packet was not completely read
1072 @retval NET_ASYNC_COMPLETE finished reading packet
1073 */
cli_safe_read_with_ok_nonblocking(MYSQL * mysql,bool parse_ok,bool * is_data_packet,ulong * res)1074 net_async_status cli_safe_read_with_ok_nonblocking(MYSQL *mysql, bool parse_ok,
1075 bool *is_data_packet,
1076 ulong *res) {
1077 NET *net = &mysql->net;
1078 ulong len = 0;
1079 DBUG_TRACE;
1080 DBUG_ASSERT(net->vio);
1081
1082 if (NET_ASYNC_NOT_READY == my_net_read_nonblocking(net, &len)) {
1083 return NET_ASYNC_NOT_READY;
1084 }
1085
1086 DBUG_PRINT("info",
1087 ("total nb read: %lu, net->where_b: %lu", len, net->where_b));
1088
1089 *res = cli_safe_read_with_ok_complete(mysql, parse_ok, is_data_packet, len);
1090
1091 /*
1092 In case, packet is too large or connection is lost, net_end() is called to
1093 free up net->extention. Thus return NET_ASYNC_ERROR.
1094 */
1095 if ((*res == packet_error) && (NET_ASYNC_DATA(net) == nullptr)) {
1096 return NET_ASYNC_ERROR;
1097 }
1098 return NET_ASYNC_COMPLETE;
1099 }
1100
1101 /**
1102 Its a non blocking version of cli_safe_read
1103 */
cli_safe_read_nonblocking(MYSQL * mysql,bool * is_data_packet,ulong * res)1104 net_async_status cli_safe_read_nonblocking(MYSQL *mysql, bool *is_data_packet,
1105 ulong *res) {
1106 return cli_safe_read_with_ok_nonblocking(mysql, false, is_data_packet, res);
1107 }
1108
1109 /**
1110 Read a packet from server. Give error message if socket was down
1111 or packet is an error message
1112
1113 @param[in] mysql connection handle
1114 @param[in] parse_ok if set to true then parse OK packet
1115 if it is received
1116 @param[out] is_data_packet
1117 if set to true then packet received is
1118 a "data packet", that is not OK or ERR
1119 packet or EOF in case of old servers
1120
1121 @return The length of the packet that was read or packet_error in
1122 case of error. In case of error its description is stored
1123 in mysql handle.
1124 */
1125
cli_safe_read_with_ok(MYSQL * mysql,bool parse_ok,bool * is_data_packet)1126 ulong cli_safe_read_with_ok(MYSQL *mysql, bool parse_ok, bool *is_data_packet) {
1127 NET *net = &mysql->net;
1128 ulong len = 0;
1129
1130 MYSQL_TRACE(READ_PACKET, mysql, ());
1131
1132 if (is_data_packet) *is_data_packet = false;
1133
1134 if (net->vio != nullptr) len = my_net_read(net);
1135 return cli_safe_read_with_ok_complete(mysql, parse_ok, is_data_packet, len);
1136 }
1137
cli_safe_read_with_ok_complete(MYSQL * mysql,bool parse_ok,bool * is_data_packet,ulong len)1138 ulong cli_safe_read_with_ok_complete(MYSQL *mysql, bool parse_ok,
1139 bool *is_data_packet, ulong len) {
1140 NET *net = &mysql->net;
1141 DBUG_TRACE;
1142
1143 if (len == packet_error || len == 0) {
1144 #ifndef DBUG_OFF
1145 char desc[VIO_DESCRIPTION_SIZE];
1146 vio_description(net->vio, desc);
1147 DBUG_PRINT("error",
1148 ("Wrong connection or packet. fd: %s len: %lu", desc, len));
1149 #endif // DBUG_OFF
1150 #ifdef MYSQL_SERVER
1151 if (net->vio && (net->last_errno == ER_NET_READ_INTERRUPTED))
1152 return packet_error;
1153 #endif /*MYSQL_SERVER*/
1154 end_server(mysql);
1155 set_mysql_error(mysql,
1156 net->last_errno == ER_NET_PACKET_TOO_LARGE
1157 ? CR_NET_PACKET_TOO_LARGE
1158 : CR_SERVER_LOST,
1159 unknown_sqlstate);
1160 return packet_error;
1161 }
1162
1163 MYSQL_TRACE(PACKET_RECEIVED, mysql, (len, net->read_pos));
1164
1165 if (net->read_pos[0] == 255) {
1166 /*
1167 After server reprts an error, usually it is ready to accept new commands
1168 and we set stage to READY_FOR_COMMAND. This can be modified by the caller
1169 of cli_safe_read().
1170 */
1171 MYSQL_TRACE_STAGE(mysql, READY_FOR_COMMAND);
1172
1173 if (len > 3) {
1174 uchar *pos = net->read_pos + 1;
1175 net->last_errno = uint2korr(pos);
1176 pos += 2;
1177 len -= 2;
1178 if (protocol_41(mysql) && pos[0] == '#') {
1179 strmake(net->sqlstate, (char *)pos + 1, SQLSTATE_LENGTH);
1180 pos += SQLSTATE_LENGTH + 1;
1181 } else {
1182 /*
1183 The SQL state hasn't been received -- it should be reset to HY000
1184 (unknown error sql state).
1185 */
1186
1187 my_stpcpy(net->sqlstate, unknown_sqlstate);
1188 }
1189
1190 (void)strmake(net->last_error, (char *)pos,
1191 std::min<ulong>(len, sizeof(net->last_error) - 1));
1192 } else
1193 set_mysql_error(mysql, CR_UNKNOWN_ERROR, unknown_sqlstate);
1194 /*
1195 Cover a protocol design error: error packet does not
1196 contain the server status. Therefore, the client has no way
1197 to find out whether there are more result sets of
1198 a multiple-result-set statement pending. Luckily, in 5.0 an
1199 error always aborts execution of a statement, wherever it is
1200 a multi-statement or a stored procedure, so it should be
1201 safe to unconditionally turn off the flag here.
1202 */
1203 mysql->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
1204
1205 DBUG_PRINT("error", ("Got error: %d/%s (%s)", net->last_errno,
1206 net->sqlstate, net->last_error));
1207 return packet_error;
1208 } else {
1209 /* if it is OK packet irrespective of new/old server */
1210 if (net->read_pos[0] == 0) {
1211 if (parse_ok) {
1212 read_ok_ex(mysql, len);
1213 return len;
1214 }
1215 }
1216 /*
1217 Now we have a data packet, unless it is OK packet starting with
1218 0xFE - we detect that case below.
1219 */
1220 if (is_data_packet) *is_data_packet = true;
1221 /*
1222 For a packet starting with 0xFE detect if it is OK packet or a
1223 huge data packet. Note that old servers do not send OK packets
1224 starting with 0xFE.
1225 */
1226 if ((mysql->server_capabilities & CLIENT_DEPRECATE_EOF) &&
1227 (net->read_pos[0] == 254)) {
1228 /* detect huge data packet */
1229 if (len > MAX_PACKET_LENGTH) return len;
1230 /* otherwise we have OK packet starting with 0xFE */
1231 if (is_data_packet) *is_data_packet = false;
1232 /* parse it if requested */
1233 if (parse_ok) read_ok_ex(mysql, len);
1234 return len;
1235 }
1236 /* for old client detect EOF packet */
1237 if (!(mysql->server_capabilities & CLIENT_DEPRECATE_EOF) &&
1238 (net->read_pos[0] == 254) && (len < 8)) {
1239 if (is_data_packet) *is_data_packet = false;
1240 }
1241 }
1242 return len;
1243 }
1244
1245 /**
1246 Read a packet from server. Give error message if connection was broken or
1247 ERR packet was received. Detect if the packet received was an OK, ERR or
1248 something else (a "data packet").
1249
1250 @param[in] mysql connection handle
1251 @param[out] is_data_packet
1252 if set to true then the packet received
1253 was a "data packet".
1254
1255 @retval The length of the packet that was read or packet_error in case of
1256 error. In case of error its description is stored in mysql handle.
1257 */
cli_safe_read(MYSQL * mysql,bool * is_data_packet)1258 ulong cli_safe_read(MYSQL *mysql, bool *is_data_packet) {
1259 return cli_safe_read_with_ok(mysql, false, is_data_packet);
1260 }
1261
free_rows(MYSQL_DATA * cur)1262 void free_rows(MYSQL_DATA *cur) {
1263 if (cur) {
1264 free_root(cur->alloc, MYF(0));
1265 my_free(cur->alloc);
1266 my_free(cur);
1267 }
1268 }
1269
cli_advanced_command(MYSQL * mysql,enum enum_server_command command,const uchar * header,size_t header_length,const uchar * arg,size_t arg_length,bool skip_check,MYSQL_STMT * stmt)1270 bool cli_advanced_command(MYSQL *mysql, enum enum_server_command command,
1271 const uchar *header, size_t header_length,
1272 const uchar *arg, size_t arg_length, bool skip_check,
1273 MYSQL_STMT *stmt) {
1274 NET *net = &mysql->net;
1275 bool result = true;
1276 bool stmt_skip = stmt ? stmt->state != MYSQL_STMT_INIT_DONE : false;
1277 DBUG_TRACE;
1278
1279 if (mysql->net.vio == nullptr) { /* Do reconnect if possible */
1280 if (mysql_reconnect(mysql) || stmt_skip) return true;
1281 }
1282 /* turn off non blocking operations */
1283 if (!vio_is_blocking(mysql->net.vio))
1284 vio_set_blocking_flag(mysql->net.vio, true);
1285
1286 if (mysql->status != MYSQL_STATUS_READY ||
1287 mysql->server_status & SERVER_MORE_RESULTS_EXISTS) {
1288 DBUG_PRINT("error", ("state: %d", mysql->status));
1289 set_mysql_error(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate);
1290 return true;
1291 }
1292
1293 net_clear_error(net);
1294 mysql->info = nullptr;
1295 mysql->affected_rows = ~(my_ulonglong)0;
1296 /*
1297 Do not check the socket/protocol buffer on COM_QUIT as the
1298 result of a previous command might not have been read. This
1299 can happen if a client sends a query but does not reap the
1300 result before attempting to close the connection.
1301 */
1302 net_clear(&mysql->net, (command != COM_QUIT));
1303
1304 MYSQL_TRACE_STAGE(mysql, READY_FOR_COMMAND);
1305 MYSQL_TRACE(SEND_COMMAND, mysql,
1306 (command, header_length, arg_length, header, arg));
1307
1308 /*
1309 If auto-reconnect mode is enabled check if connection is still alive before
1310 sending new command. Otherwise, send() might not notice that connection was
1311 closed by the server (for example, due to KILL statement), and the fact that
1312 connection is gone will be noticed only on attempt to read command's result,
1313 when it is too late to reconnect. Note that such scenario can still occur if
1314 connection gets killed after this check but before command is sent to
1315 server. But this should be rare.
1316 */
1317 if ((command != COM_QUIT) && mysql->reconnect && !vio_is_connected(net->vio))
1318 net->error = 2;
1319
1320 if (net_write_command(net, (uchar)command, header, header_length, arg,
1321 arg_length)) {
1322 DBUG_PRINT("error",
1323 ("Can't send command to server. Error: %d", socket_errno));
1324 if (net->last_errno == ER_NET_PACKET_TOO_LARGE) {
1325 set_mysql_error(mysql, CR_NET_PACKET_TOO_LARGE, unknown_sqlstate);
1326 goto end;
1327 }
1328 end_server(mysql);
1329 if (mysql_reconnect(mysql) || stmt_skip) goto end;
1330
1331 MYSQL_TRACE(SEND_COMMAND, mysql,
1332 (command, header_length, arg_length, header, arg));
1333 if (net_write_command(net, (uchar)command, header, header_length, arg,
1334 arg_length)) {
1335 set_mysql_error(mysql, CR_SERVER_GONE_ERROR, unknown_sqlstate);
1336 goto end;
1337 }
1338 }
1339
1340 MYSQL_TRACE(PACKET_SENT, mysql, (header_length + arg_length));
1341
1342 #if defined(CLIENT_PROTOCOL_TRACING)
1343 switch (command) {
1344 case COM_STMT_PREPARE:
1345 MYSQL_TRACE_STAGE(mysql, WAIT_FOR_PS_DESCRIPTION);
1346 break;
1347
1348 case COM_STMT_FETCH:
1349 MYSQL_TRACE_STAGE(mysql, WAIT_FOR_ROW);
1350 break;
1351
1352 /*
1353 No server reply is expected after these commands so we reamin ready
1354 for the next command.
1355 */
1356 case COM_STMT_SEND_LONG_DATA:
1357 case COM_STMT_CLOSE:
1358 case COM_REGISTER_SLAVE:
1359 case COM_QUIT:
1360 break;
1361
1362 /*
1363 These replication commands are not supported and we bail out
1364 by pretending that connection has been closed.
1365 */
1366 case COM_BINLOG_DUMP:
1367 case COM_BINLOG_DUMP_GTID:
1368 case COM_TABLE_DUMP:
1369 MYSQL_TRACE(DISCONNECTED, mysql, ());
1370 break;
1371
1372 /*
1373 After COM_CHANGE_USER a regular authentication exchange
1374 is performed.
1375 */
1376 case COM_CHANGE_USER:
1377 MYSQL_TRACE_STAGE(mysql, AUTHENTICATE);
1378 break;
1379
1380 /*
1381 Server replies to COM_STATISTICS with a single packet
1382 containing a string with statistics information.
1383 */
1384 case COM_STATISTICS:
1385 MYSQL_TRACE_STAGE(mysql, WAIT_FOR_PACKET);
1386 break;
1387
1388 /*
1389 For all other commands we expect server to send regular reply which
1390 is either OK, ERR or a result-set header.
1391 */
1392 default:
1393 MYSQL_TRACE_STAGE(mysql, WAIT_FOR_RESULT);
1394 break;
1395 }
1396 #endif
1397
1398 result = false;
1399 if (!skip_check) {
1400 result = ((mysql->packet_length =
1401 cli_safe_read_with_ok(mysql, true, nullptr)) == packet_error
1402 ? 1
1403 : 0);
1404
1405 #if defined(CLIENT_PROTOCOL_TRACING)
1406 /*
1407 Return to READY_FOR_COMMAND protocol stage in case server reports error
1408 or sends OK packet.
1409 */
1410 if (result || mysql->net.read_pos[0] == 0x00)
1411 MYSQL_TRACE_STAGE(mysql, READY_FOR_COMMAND);
1412 #endif
1413 }
1414
1415 end:
1416 DBUG_PRINT("exit", ("result: %d", result));
1417 return result;
1418 }
1419
cli_advanced_command_nonblocking(MYSQL * mysql,enum enum_server_command command,const uchar * header,ulong header_length,const uchar * arg,ulong arg_length,bool skip_check,MYSQL_STMT * stmt,bool * ret)1420 net_async_status cli_advanced_command_nonblocking(
1421 MYSQL *mysql, enum enum_server_command command, const uchar *header,
1422 ulong header_length, const uchar *arg, ulong arg_length, bool skip_check,
1423 MYSQL_STMT *stmt, bool *ret) {
1424 NET *net = &mysql->net;
1425 NET_ASYNC *net_async = NET_ASYNC_DATA(net);
1426 bool result = true;
1427 *ret = result;
1428 bool stmt_skip = stmt ? stmt->state != MYSQL_STMT_INIT_DONE : false;
1429 DBUG_TRACE;
1430 DBUG_DUMP("sending", header, header_length);
1431 if (arg && arg_length) {
1432 DBUG_DUMP("sending arg", arg, arg_length);
1433 }
1434
1435 if (mysql->net.vio == nullptr) {
1436 set_mysql_error(mysql, CR_SERVER_GONE_ERROR, unknown_sqlstate);
1437 goto end;
1438 }
1439 /**
1440 When non blocking API execution is pending and did not complete then
1441 it can result in async context to be null. In such case if user executes
1442 any other API report command out of sync error.
1443 */
1444 if (net_async == nullptr) {
1445 set_mysql_error(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate);
1446 goto end;
1447 }
1448 if (net_async->async_send_command_status == NET_ASYNC_SEND_COMMAND_IDLE) {
1449 if (vio_is_blocking(mysql->net.vio)) {
1450 vio_set_blocking_flag(net->vio, false);
1451 }
1452
1453 if (mysql->status != MYSQL_STATUS_READY ||
1454 mysql->server_status & SERVER_MORE_RESULTS_EXISTS) {
1455 DBUG_PRINT("error", ("state: %d", mysql->status));
1456 set_mysql_error(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate);
1457 return NET_ASYNC_COMPLETE;
1458 }
1459
1460 net_clear_error(net);
1461 mysql->info = nullptr;
1462 mysql->affected_rows = ~(my_ulonglong)0;
1463 /*
1464 Do not check the socket/protocol buffer on COM_QUIT as the
1465 result of a previous command might not have been read. This
1466 can happen if a client sends a query but does not reap
1467 the result before attempting to close the connection.
1468 */
1469 DBUG_ASSERT(command <= COM_END);
1470 net_clear(&mysql->net, (command != COM_QUIT));
1471 net_async->async_send_command_status = NET_ASYNC_SEND_COMMAND_WRITE_COMMAND;
1472 }
1473
1474 MYSQL_TRACE_STAGE(mysql, READY_FOR_COMMAND);
1475 if (net_async->async_send_command_status ==
1476 NET_ASYNC_SEND_COMMAND_WRITE_COMMAND) {
1477 bool err;
1478 MYSQL_TRACE(SEND_COMMAND, mysql,
1479 (command, header_length, arg_length, header, arg));
1480 net_async_status status = net_write_command_nonblocking(
1481 net, (uchar)command, header, header_length, arg, arg_length, &err);
1482 if (status == NET_ASYNC_NOT_READY) {
1483 return NET_ASYNC_NOT_READY;
1484 }
1485 if (err) {
1486 DBUG_PRINT("error",
1487 ("Can't send command to server. Error: %d", socket_errno));
1488 if (net->last_errno == ER_NET_PACKET_TOO_LARGE) {
1489 set_mysql_error(mysql, CR_NET_PACKET_TOO_LARGE, unknown_sqlstate);
1490 goto end;
1491 }
1492 end_server(mysql);
1493 /* reset net_async to null as its reference has been freed */
1494 net_async = nullptr;
1495 if (stmt_skip) goto end;
1496 set_mysql_error(mysql, CR_SERVER_GONE_ERROR, unknown_sqlstate);
1497 goto end;
1498 }
1499 MYSQL_TRACE(PACKET_SENT, mysql, (header_length + arg_length));
1500 if (skip_check) {
1501 result = false;
1502 goto end;
1503 } else {
1504 net_async->async_send_command_status = NET_ASYNC_SEND_COMMAND_READ_STATUS;
1505 }
1506 }
1507
1508 if (net_async->async_send_command_status ==
1509 NET_ASYNC_SEND_COMMAND_READ_STATUS) {
1510 ulong pkt_len;
1511 net_async_status status =
1512 cli_safe_read_with_ok_nonblocking(mysql, true, nullptr, &pkt_len);
1513 if (status == NET_ASYNC_NOT_READY) {
1514 return NET_ASYNC_NOT_READY;
1515 }
1516 mysql->packet_length = pkt_len;
1517 result = (pkt_len == packet_error ? 1 : 0);
1518 #if defined(CLIENT_PROTOCOL_TRACING)
1519 /*
1520 Return to READY_FOR_COMMAND protocol stage in case server reports
1521 error or sends OK packet.
1522 */
1523 if (!result || mysql->net.read_pos[0] == 0x00)
1524 MYSQL_TRACE_STAGE(mysql, READY_FOR_COMMAND);
1525 #endif
1526 }
1527 end:
1528 if (net_async)
1529 net_async->async_send_command_status = NET_ASYNC_SEND_COMMAND_IDLE;
1530 DBUG_PRINT("exit", ("result: %d", result));
1531 *ret = result;
1532 return NET_ASYNC_COMPLETE;
1533 }
1534
free_old_query(MYSQL * mysql)1535 void free_old_query(MYSQL *mysql) {
1536 DBUG_TRACE;
1537 if (mysql->field_alloc) {
1538 free_root(mysql->field_alloc, MYF(0));
1539 init_alloc_root(PSI_NOT_INSTRUMENTED, mysql->field_alloc, 8192,
1540 0); /* Assume rowlength < 8192 */
1541 }
1542 mysql->fields = nullptr;
1543 mysql->field_count = 0; /* For API */
1544 mysql->warning_count = 0;
1545 mysql->info = nullptr;
1546 }
1547
1548 /**
1549 Finish reading of a partial result set from the server in asynchronous
1550 way. This function can return without completly flushing the result set,
1551 in such a case call this function again until result set in flushed.
1552 Read OK packet incase result set is not a data packet.
1553
1554 @param[in] mysql connection handle
1555 @param[out] res true in case of protocol error, false otherwise
1556
1557 @retval NET_ASYNC_NOT_READY result set not flushed yet
1558 @retval NET_ASYNC_COMPLETE finished flushing result set
1559 */
flush_one_result_nonblocking(MYSQL * mysql,bool * res)1560 static net_async_status flush_one_result_nonblocking(MYSQL *mysql, bool *res) {
1561 DBUG_TRACE;
1562
1563 *res = false;
1564 while (true) {
1565 ulong packet_length;
1566 bool is_data_packet;
1567 if (cli_safe_read_nonblocking(mysql, &is_data_packet, &packet_length) ==
1568 NET_ASYNC_NOT_READY) {
1569 return NET_ASYNC_NOT_READY;
1570 }
1571 mysql->packet_length = packet_length;
1572 if (packet_length == packet_error) {
1573 *res = true;
1574 break;
1575 }
1576 if (mysql->net.read_pos[0] != 0 && !is_data_packet) {
1577 if (protocol_41(mysql)) {
1578 uchar *pos = mysql->net.read_pos + 1;
1579 if (mysql->server_capabilities & CLIENT_DEPRECATE_EOF &&
1580 !is_data_packet) {
1581 read_ok_ex(mysql, packet_length);
1582 } else {
1583 mysql->warning_count = uint2korr(pos);
1584 pos += 2;
1585 mysql->server_status = uint2korr(pos);
1586 }
1587 pos += 2;
1588 }
1589 break;
1590 }
1591 }
1592 return NET_ASYNC_COMPLETE;
1593 }
1594
1595 /**
1596 Finish reading of a partial result set from the server.
1597 Get the EOF packet, and update mysql->status
1598 and mysql->warning_count.
1599
1600 @return true if a communication or protocol error, an error
1601 is set in this case, false otherwise.
1602 */
1603
flush_one_result(MYSQL * mysql)1604 static bool flush_one_result(MYSQL *mysql) {
1605 ulong packet_length;
1606 bool is_data_packet;
1607
1608 DBUG_ASSERT(mysql->status != MYSQL_STATUS_READY);
1609
1610 do {
1611 packet_length = cli_safe_read(mysql, &is_data_packet);
1612 /*
1613 There is an error reading from the connection,
1614 or (sic!) there were no error and no
1615 data in the stream, i.e. no more data from the server.
1616 Since we know our position in the stream (somewhere in
1617 the middle of a result set), this latter case is an error too
1618 -- each result set must end with a EOF packet.
1619 cli_safe_read() has set an error for us, just return.
1620 */
1621 if (packet_length == packet_error) return true;
1622 } while (mysql->net.read_pos[0] == 0 || is_data_packet);
1623
1624 /* Analyse final OK packet (EOF packet if it is old client) */
1625
1626 if (protocol_41(mysql)) {
1627 uchar *pos = mysql->net.read_pos + 1;
1628 if (mysql->server_capabilities & CLIENT_DEPRECATE_EOF && !is_data_packet)
1629 read_ok_ex(mysql, packet_length);
1630 else {
1631 mysql->warning_count = uint2korr(pos);
1632 pos += 2;
1633 mysql->server_status = uint2korr(pos);
1634 }
1635 pos += 2;
1636 }
1637 #if defined(CLIENT_PROTOCOL_TRACING)
1638 if (mysql->server_status & SERVER_MORE_RESULTS_EXISTS)
1639 MYSQL_TRACE_STAGE(mysql, WAIT_FOR_RESULT);
1640 else
1641 MYSQL_TRACE_STAGE(mysql, READY_FOR_COMMAND);
1642 #endif
1643 return false;
1644 }
1645
1646 /**
1647 Read a packet from network. If it's an OK packet, flush it.
1648
1649 @return true if error, false otherwise. In case of
1650 success, is_ok_packet is set to true or false,
1651 based on what we got from network.
1652 */
1653
opt_flush_ok_packet(MYSQL * mysql,bool * is_ok_packet)1654 static bool opt_flush_ok_packet(MYSQL *mysql, bool *is_ok_packet) {
1655 bool is_data_packet;
1656 ulong packet_length = cli_safe_read(mysql, &is_data_packet);
1657
1658 if (packet_length == packet_error) return true;
1659
1660 /* cli_safe_read always reads a non-empty packet. */
1661 DBUG_ASSERT(packet_length);
1662
1663 *is_ok_packet =
1664 ((mysql->net.read_pos[0] == 0) ||
1665 ((mysql->server_capabilities & CLIENT_DEPRECATE_EOF) &&
1666 mysql->net.read_pos[0] == 254 && packet_length < MAX_PACKET_LENGTH));
1667 if (*is_ok_packet) {
1668 read_ok_ex(mysql, packet_length);
1669 #if defined(CLIENT_PROTOCOL_TRACING)
1670 if (mysql->server_status & SERVER_MORE_RESULTS_EXISTS)
1671 MYSQL_TRACE_STAGE(mysql, WAIT_FOR_RESULT);
1672 else
1673 MYSQL_TRACE_STAGE(mysql, READY_FOR_COMMAND);
1674 #endif
1675 }
1676
1677 return false;
1678 }
1679
cli_flush_use_result_nonblocking(MYSQL * mysql,bool flush_all_results MY_ATTRIBUTE ((unused)))1680 static net_async_status cli_flush_use_result_nonblocking(
1681 MYSQL *mysql, bool flush_all_results MY_ATTRIBUTE((unused))) {
1682 DBUG_TRACE;
1683 /*
1684 flush_all_results is only used for mysql_stmt_close, and async is not
1685 supported for that.
1686 */
1687 DBUG_ASSERT(!flush_all_results);
1688 bool res;
1689 return flush_one_result_nonblocking(mysql, &res);
1690 }
1691
1692 /*
1693 Flush result set sent from server
1694 */
1695
cli_flush_use_result(MYSQL * mysql,bool flush_all_results)1696 static void cli_flush_use_result(MYSQL *mysql, bool flush_all_results) {
1697 /* Clear the current execution status */
1698 DBUG_TRACE;
1699 DBUG_PRINT("warning", ("Not all packets read, clearing them"));
1700
1701 if (flush_one_result(mysql)) return; /* An error occurred */
1702
1703 if (!flush_all_results) return;
1704
1705 while (mysql->server_status & SERVER_MORE_RESULTS_EXISTS) {
1706 bool is_ok_packet;
1707 if (opt_flush_ok_packet(mysql, &is_ok_packet))
1708 return; /* An error occurred. */
1709 if (is_ok_packet) {
1710 /*
1711 Indeed what we got from network was an OK packet, and we
1712 know that OK is the last one in a multi-result-set, so
1713 just return.
1714 */
1715 return;
1716 }
1717
1718 /*
1719 It's a result set, not an OK packet. A result set contains
1720 of two result set subsequences: field metadata, terminated
1721 with EOF packet, and result set data, again terminated with
1722 EOF packet. Read and flush them.
1723 */
1724 MYSQL_TRACE_STAGE(mysql, WAIT_FOR_FIELD_DEF);
1725 if (!(mysql->server_capabilities & CLIENT_DEPRECATE_EOF)) {
1726 if (flush_one_result(mysql)) return; /* An error occurred. */
1727 } else {
1728 uchar *pos = (uchar *)mysql->net.read_pos;
1729 ulong field_count = net_field_length(&pos);
1730 if (read_com_query_metadata(mysql, pos, field_count)) {
1731 return;
1732 } else {
1733 free_root(mysql->field_alloc, MYF(0));
1734 }
1735 }
1736 MYSQL_TRACE_STAGE(mysql, WAIT_FOR_ROW);
1737 if (flush_one_result(mysql)) return;
1738 }
1739 }
1740
1741 #ifdef _WIN32
is_NT(void)1742 static bool is_NT(void) {
1743 char *os = getenv("OS");
1744 return (os && !strcmp(os, "Windows_NT")) ? 1 : 0;
1745 }
1746 #endif
1747
1748 #ifdef CHECK_LICENSE
1749 /**
1750 Check server side variable 'license'.
1751
1752 If the variable does not exist or does not contain 'Commercial',
1753 we're talking to non-commercial server from commercial client.
1754
1755 @retval 0 success
1756 @retval !0 network error or the server is not commercial.
1757 Error code is saved in mysql->net.last_errno.
1758 */
1759
check_license(MYSQL * mysql)1760 static int check_license(MYSQL *mysql) {
1761 MYSQL_ROW row;
1762 MYSQL_RES *res;
1763 NET *net = &mysql->net;
1764 static const char query[] = "SELECT @@license";
1765 static const char required_license[] = STRINGIFY_ARG(LICENSE);
1766
1767 if (mysql_real_query(mysql, query, (ulong)(sizeof(query) - 1))) {
1768 if (net->last_errno == ER_UNKNOWN_SYSTEM_VARIABLE) {
1769 set_mysql_extended_error(mysql, CR_WRONG_LICENSE, unknown_sqlstate,
1770 ER_CLIENT(CR_WRONG_LICENSE), required_license);
1771 }
1772 return 1;
1773 }
1774 if (!(res = mysql_use_result(mysql))) return 1;
1775 row = mysql_fetch_row(res);
1776 /*
1777 If no rows in result set, or column value is NULL (none of these
1778 two is ever true for server variables now), or column value
1779 mismatch, set wrong license error.
1780 */
1781 if (!net->last_errno &&
1782 (!row || !row[0] ||
1783 strncmp(row[0], required_license, sizeof(required_license)))) {
1784 set_mysql_extended_error(mysql, CR_WRONG_LICENSE, unknown_sqlstate,
1785 ER_CLIENT(CR_WRONG_LICENSE), required_license);
1786 }
1787 mysql_free_result(res);
1788 return net->last_errno;
1789 }
1790 #endif /* CHECK_LICENSE */
1791
1792 /**************************************************************************
1793 Shut down connection
1794 **************************************************************************/
1795
end_server(MYSQL * mysql)1796 void end_server(MYSQL *mysql) {
1797 int save_errno = errno;
1798 DBUG_TRACE;
1799 if (mysql->net.vio != nullptr) {
1800 #ifndef DBUG_OFF
1801 char desc[VIO_DESCRIPTION_SIZE];
1802 vio_description(mysql->net.vio, desc);
1803 DBUG_PRINT("info", ("Net: %s", desc));
1804 #endif // DBUG_OFF
1805 #if defined(MYSQL_SERVER) && !defined(XTRABACKUP)
1806 slave_io_thread_detach_vio();
1807 #endif
1808 vio_delete(mysql->net.vio);
1809 mysql->net.vio = nullptr; /* Marker */
1810 mysql_prune_stmt_list(mysql);
1811 }
1812 net_end(&mysql->net);
1813 // net_extension_free(&mysql->net);
1814 free_old_query(mysql);
1815 errno = save_errno;
1816 MYSQL_TRACE(DISCONNECTED, mysql, ());
1817 }
1818
1819 /**
1820 Frees the memory allocated for a result, set by APIs which would have
1821 returned rows.
1822
1823 @param[in] result buffer which needs to be freed
1824
1825 @retval NET_ASYNC_NOT_READY operation not complete, retry again
1826 @retval NET_ASYNC_COMPLETE operation complete
1827 */
mysql_free_result_nonblocking(MYSQL_RES * result)1828 net_async_status STDCALL mysql_free_result_nonblocking(MYSQL_RES *result) {
1829 DBUG_TRACE;
1830 DBUG_PRINT("enter", ("mysql_res: %p", result));
1831 if (!result) return NET_ASYNC_COMPLETE;
1832
1833 MYSQL *mysql = result->handle;
1834 if (mysql) {
1835 if (mysql->unbuffered_fetch_owner == &result->unbuffered_fetch_cancelled)
1836 mysql->unbuffered_fetch_owner = nullptr;
1837 if (mysql->status == MYSQL_STATUS_USE_RESULT) {
1838 if (mysql->methods->flush_use_result_nonblocking(mysql, false) ==
1839 NET_ASYNC_NOT_READY) {
1840 return NET_ASYNC_NOT_READY;
1841 }
1842 mysql->status = MYSQL_STATUS_READY;
1843 if (mysql->unbuffered_fetch_owner) *mysql->unbuffered_fetch_owner = true;
1844 }
1845 }
1846 free_rows(result->data);
1847 if (result->field_alloc) {
1848 free_root(result->field_alloc, MYF(0));
1849 my_free(result->field_alloc);
1850 }
1851 my_free(result->row);
1852 my_free(result);
1853
1854 return NET_ASYNC_COMPLETE;
1855 }
1856
mysql_free_result(MYSQL_RES * result)1857 void STDCALL mysql_free_result(MYSQL_RES *result) {
1858 DBUG_TRACE;
1859 DBUG_PRINT("enter", ("mysql_res: %p", result));
1860 if (result) {
1861 MYSQL *mysql = result->handle;
1862 if (mysql) {
1863 if (mysql->unbuffered_fetch_owner == &result->unbuffered_fetch_cancelled)
1864 mysql->unbuffered_fetch_owner = nullptr;
1865 if (mysql->status == MYSQL_STATUS_USE_RESULT) {
1866 (*mysql->methods->flush_use_result)(mysql, false);
1867 mysql->status = MYSQL_STATUS_READY;
1868 if (mysql->unbuffered_fetch_owner)
1869 *mysql->unbuffered_fetch_owner = true;
1870 }
1871 }
1872 free_rows(result->data);
1873 if (result->field_alloc) {
1874 free_root(result->field_alloc, MYF(0));
1875 my_free(result->field_alloc);
1876 }
1877 my_free(result->row);
1878 my_free(result);
1879 }
1880 }
1881
1882 /****************************************************************************
1883 Get options from my.cnf
1884 ****************************************************************************/
1885
1886 static const char *default_options[] = {"port",
1887 "socket",
1888 "compress",
1889 "password",
1890 "pipe",
1891 "timeout",
1892 "user",
1893 "init-command",
1894 "host",
1895 "database",
1896 "debug",
1897 "return-found-rows",
1898 "ssl-key",
1899 "ssl-cert",
1900 "ssl-ca",
1901 "ssl-capath",
1902 "character-sets-dir",
1903 "default-character-set",
1904 "interactive-timeout",
1905 "connect-timeout",
1906 "local-infile",
1907 "disable-local-infile",
1908 "ssl-cipher",
1909 "max-allowed-packet",
1910 "protocol",
1911 "shared-memory-base-name",
1912 "multi-results",
1913 "multi-statements",
1914 "multi-queries",
1915 "report-data-truncation",
1916 "plugin-dir",
1917 "default-auth",
1918 "bind-address",
1919 "ssl-crl",
1920 "ssl-crlpath",
1921 "enable-cleartext-plugin",
1922 "tls-version",
1923 "ssl_mode",
1924 "optional-resultset-metadata",
1925 "ssl-fips-mode",
1926 "tls-ciphersuites",
1927 NullS};
1928 enum option_id {
1929 OPT_port = 1,
1930 OPT_socket,
1931 OPT_compress,
1932 OPT_password,
1933 OPT_pipe,
1934 OPT_timeout,
1935 OPT_user,
1936 OPT_init_command,
1937 OPT_host,
1938 OPT_database,
1939 OPT_debug,
1940 OPT_return_found_rows,
1941 OPT_ssl_key,
1942 OPT_ssl_cert,
1943 OPT_ssl_ca,
1944 OPT_ssl_capath,
1945 OPT_character_sets_dir,
1946 OPT_default_character_set,
1947 OPT_interactive_timeout,
1948 OPT_connect_timeout,
1949 OPT_local_infile,
1950 OPT_disable_local_infile,
1951 OPT_ssl_cipher,
1952 OPT_max_allowed_packet,
1953 OPT_protocol,
1954 OPT_shared_memory_base_name,
1955 OPT_multi_results,
1956 OPT_multi_statements,
1957 OPT_multi_queries,
1958 OPT_report_data_truncation,
1959 OPT_plugin_dir,
1960 OPT_default_auth,
1961 OPT_bind_address,
1962 OPT_ssl_crl,
1963 OPT_ssl_crlpath,
1964 OPT_enable_cleartext_plugin,
1965 OPT_tls_version,
1966 OPT_ssl_mode,
1967 OPT_optional_resultset_metadata,
1968 OPT_ssl_fips_mode,
1969 OPT_tls_ciphersuites,
1970 OPT_keep_this_one_last
1971 };
1972
1973 static TYPELIB option_types = {array_elements(default_options) - 1, "options",
1974 default_options, nullptr};
1975
1976 const char *sql_protocol_names_lib[] = {"TCP", "SOCKET", "PIPE", "MEMORY",
1977 NullS};
1978 TYPELIB sql_protocol_typelib = {array_elements(sql_protocol_names_lib) - 1, "",
1979 sql_protocol_names_lib, nullptr};
1980
add_init_command(struct st_mysql_options * options,const char * cmd)1981 static int add_init_command(struct st_mysql_options *options, const char *cmd) {
1982 char *tmp;
1983
1984 if (!options->init_commands) {
1985 void *rawmem = my_malloc(key_memory_mysql_options,
1986 sizeof(Init_commands_array), MYF(MY_WME));
1987 if (!rawmem) return 1;
1988 options->init_commands =
1989 new (rawmem) Init_commands_array(key_memory_mysql_options);
1990 }
1991
1992 if (!(tmp = my_strdup(key_memory_mysql_options, cmd, MYF(MY_WME))) ||
1993 options->init_commands->push_back(tmp)) {
1994 my_free(tmp);
1995 return 1;
1996 }
1997
1998 return 0;
1999 }
2000
set_ssl_option_unpack_path(const char * arg)2001 static char *set_ssl_option_unpack_path(const char *arg) {
2002 char *opt_var = nullptr;
2003 if (arg) {
2004 char *buff =
2005 (char *)my_malloc(key_memory_mysql_options, FN_REFLEN + 1, MYF(MY_WME));
2006 unpack_filename(buff, arg);
2007 opt_var = my_strdup(key_memory_mysql_options, buff, MYF(MY_WME));
2008 my_free(buff);
2009 }
2010 return opt_var;
2011 }
2012
mysql_read_default_options(struct st_mysql_options * options,const char * filename,const char * group)2013 void mysql_read_default_options(struct st_mysql_options *options,
2014 const char *filename, const char *group) {
2015 int argc;
2016 char *argv_buff[1], **argv;
2017 const char *groups[3];
2018 DBUG_TRACE;
2019 DBUG_PRINT("enter",
2020 ("file: %s group: %s", filename, group ? group : "NULL"));
2021
2022 static_assert(OPT_keep_this_one_last == array_elements(default_options),
2023 "OPT_keep_this_one_last needs to be the last element.");
2024
2025 argc = 1;
2026 argv = argv_buff;
2027 argv_buff[0] = const_cast<char *>("client");
2028 groups[0] = "client";
2029 groups[1] = group;
2030 groups[2] = nullptr;
2031
2032 MEM_ROOT alloc{PSI_NOT_INSTRUMENTED, 512};
2033 my_load_defaults(filename, groups, &argc, &argv, &alloc, nullptr);
2034 if (argc != 1) /* If some default option */
2035 {
2036 char **option = argv;
2037 while (*++option) {
2038 if (my_getopt_is_args_separator(option[0])) /* skip arguments separator */
2039 continue;
2040 /* DBUG_PRINT("info",("option: %s",option[0])); */
2041 if (option[0][0] == '-' && option[0][1] == '-') {
2042 char *end = strchr(*option, '=');
2043 char *opt_arg = nullptr;
2044 if (end != nullptr) {
2045 opt_arg = end + 1;
2046 *end = 0; /* Remove '=' */
2047 }
2048 /* Change all '_' in variable name to '-' */
2049 for (end = *option; end != nullptr; end = strchr(end, '_')) *end = '-';
2050 switch (find_type(*option + 2, &option_types, FIND_TYPE_BASIC)) {
2051 case OPT_port:
2052 if (opt_arg) options->port = atoi(opt_arg);
2053 break;
2054 case OPT_socket:
2055 if (opt_arg) {
2056 my_free(options->unix_socket);
2057 options->unix_socket =
2058 my_strdup(key_memory_mysql_options, opt_arg, MYF(MY_WME));
2059 }
2060 break;
2061 case OPT_compress:
2062 options->compress = true;
2063 options->client_flag |= CLIENT_COMPRESS;
2064 break;
2065 case OPT_password:
2066 if (opt_arg) {
2067 my_free(options->password);
2068 options->password =
2069 my_strdup(key_memory_mysql_options, opt_arg, MYF(MY_WME));
2070 }
2071 break;
2072 case OPT_pipe:
2073 options->protocol = MYSQL_PROTOCOL_PIPE;
2074 break;
2075 case OPT_connect_timeout:
2076 case OPT_timeout:
2077 if (opt_arg) options->connect_timeout = atoi(opt_arg);
2078 break;
2079 case OPT_user:
2080 if (opt_arg) {
2081 my_free(options->user);
2082 options->user =
2083 my_strdup(key_memory_mysql_options, opt_arg, MYF(MY_WME));
2084 }
2085 break;
2086 case OPT_init_command:
2087 add_init_command(options, opt_arg);
2088 break;
2089 case OPT_host:
2090 if (opt_arg) {
2091 my_free(options->host);
2092 options->host =
2093 my_strdup(key_memory_mysql_options, opt_arg, MYF(MY_WME));
2094 }
2095 break;
2096 case OPT_database:
2097 if (opt_arg) {
2098 my_free(options->db);
2099 options->db =
2100 my_strdup(key_memory_mysql_options, opt_arg, MYF(MY_WME));
2101 }
2102 break;
2103 case OPT_debug:
2104 #ifndef MYSQL_SERVER
2105 mysql_debug(opt_arg ? opt_arg : "d:t:o,/tmp/client.trace");
2106 break;
2107 #endif
2108 case OPT_return_found_rows:
2109 options->client_flag |= CLIENT_FOUND_ROWS;
2110 break;
2111 case OPT_ssl_key:
2112 my_free(options->ssl_key);
2113 options->ssl_key =
2114 my_strdup(key_memory_mysql_options, opt_arg, MYF(MY_WME));
2115 break;
2116 case OPT_ssl_cert:
2117 my_free(options->ssl_cert);
2118 options->ssl_cert =
2119 my_strdup(key_memory_mysql_options, opt_arg, MYF(MY_WME));
2120 break;
2121 case OPT_ssl_ca:
2122 my_free(options->ssl_ca);
2123 options->ssl_ca =
2124 my_strdup(key_memory_mysql_options, opt_arg, MYF(MY_WME));
2125 break;
2126 case OPT_ssl_capath:
2127 my_free(options->ssl_capath);
2128 options->ssl_capath =
2129 my_strdup(key_memory_mysql_options, opt_arg, MYF(MY_WME));
2130 break;
2131 case OPT_ssl_cipher:
2132 my_free(options->ssl_cipher);
2133 options->ssl_cipher =
2134 my_strdup(key_memory_mysql_options, opt_arg, MYF(MY_WME));
2135 break;
2136 case OPT_tls_ciphersuites:
2137 EXTENSION_SET_STRING(options, tls_ciphersuites, opt_arg);
2138 break;
2139 case OPT_tls_version:
2140 EXTENSION_SET_SSL_STRING(options, tls_version, opt_arg,
2141 SSL_MODE_PREFERRED);
2142 break;
2143 case OPT_ssl_crl:
2144 EXTENSION_SET_SSL_STRING(options, ssl_crl, opt_arg,
2145 SSL_MODE_PREFERRED);
2146 break;
2147 case OPT_ssl_crlpath:
2148 EXTENSION_SET_SSL_STRING(options, ssl_crlpath, opt_arg,
2149 SSL_MODE_PREFERRED);
2150 break;
2151 case OPT_character_sets_dir:
2152 my_free(options->charset_dir);
2153 options->charset_dir =
2154 my_strdup(key_memory_mysql_options, opt_arg, MYF(MY_WME));
2155 break;
2156 case OPT_default_character_set:
2157 my_free(options->charset_name);
2158 options->charset_name =
2159 my_strdup(key_memory_mysql_options, opt_arg, MYF(MY_WME));
2160 break;
2161 case OPT_interactive_timeout:
2162 options->client_flag |= CLIENT_INTERACTIVE;
2163 break;
2164 case OPT_local_infile:
2165 if (!opt_arg || atoi(opt_arg) != 0)
2166 options->client_flag |= CLIENT_LOCAL_FILES;
2167 else
2168 options->client_flag &= ~CLIENT_LOCAL_FILES;
2169 break;
2170 case OPT_disable_local_infile:
2171 options->client_flag &= ~CLIENT_LOCAL_FILES;
2172 break;
2173 case OPT_max_allowed_packet:
2174 if (opt_arg) options->max_allowed_packet = atoi(opt_arg);
2175 break;
2176 case OPT_protocol:
2177 if ((options->protocol = find_type(opt_arg, &sql_protocol_typelib,
2178 FIND_TYPE_BASIC)) <= 0) {
2179 my_message_local(ERROR_LEVEL, EE_UNKNOWN_PROTOCOL_OPTION,
2180 opt_arg);
2181 exit(1);
2182 }
2183 break;
2184 case OPT_shared_memory_base_name:
2185 #if defined(_WIN32)
2186 if (options->shared_memory_base_name != def_shared_memory_base_name)
2187 my_free(options->shared_memory_base_name);
2188 options->shared_memory_base_name =
2189 my_strdup(key_memory_mysql_options, opt_arg, MYF(MY_WME));
2190 #endif
2191 break;
2192 case OPT_multi_results:
2193 options->client_flag |= CLIENT_MULTI_RESULTS;
2194 break;
2195 case OPT_multi_statements:
2196 case OPT_multi_queries:
2197 options->client_flag |=
2198 CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS;
2199 break;
2200 case OPT_report_data_truncation:
2201 options->report_data_truncation =
2202 opt_arg ? (atoi(opt_arg) != 0) : true;
2203 break;
2204 case OPT_plugin_dir: {
2205 char buff[FN_REFLEN], buff2[FN_REFLEN];
2206 if (strlen(opt_arg) >= FN_REFLEN) opt_arg[FN_REFLEN] = '\0';
2207 if (my_realpath(buff, opt_arg, 0)) {
2208 DBUG_PRINT("warning",
2209 ("failed to normalize the plugin path: %s", opt_arg));
2210 break;
2211 }
2212 convert_dirname(buff2, buff, nullptr);
2213 EXTENSION_SET_STRING(options, plugin_dir, buff2);
2214 } break;
2215 case OPT_default_auth:
2216 EXTENSION_SET_STRING(options, default_auth, opt_arg);
2217 break;
2218 case OPT_bind_address:
2219 my_free(options->bind_address);
2220 options->bind_address =
2221 my_strdup(key_memory_mysql_options, opt_arg, MYF(MY_WME));
2222 break;
2223 case OPT_enable_cleartext_plugin:
2224 ENSURE_EXTENSIONS_PRESENT(options);
2225 options->extension->enable_cleartext_plugin =
2226 (!opt_arg || atoi(opt_arg) != 0) ? true : false;
2227 break;
2228 case OPT_optional_resultset_metadata:
2229 if (!opt_arg || atoi(opt_arg) != 0)
2230 options->client_flag |= CLIENT_OPTIONAL_RESULTSET_METADATA;
2231 else
2232 options->client_flag &= ~CLIENT_OPTIONAL_RESULTSET_METADATA;
2233 break;
2234
2235 default:
2236 DBUG_PRINT("warning", ("unknown option: %s", option[0]));
2237 }
2238 }
2239 }
2240 }
2241 }
2242
2243 /**************************************************************************
2244 Get column lengths of the current row
2245 If one uses mysql_use_result, res->lengths contains the length information,
2246 else the lengths are calculated from the offset between pointers.
2247 **************************************************************************/
2248
cli_fetch_lengths(ulong * to,MYSQL_ROW column,unsigned int field_count)2249 static void cli_fetch_lengths(ulong *to, MYSQL_ROW column,
2250 unsigned int field_count) {
2251 ulong *prev_length;
2252 char *start = nullptr;
2253 MYSQL_ROW end;
2254
2255 prev_length = nullptr; /* Keep gcc happy */
2256 for (end = column + field_count + 1; column != end; column++, to++) {
2257 if (!*column) {
2258 *to = 0; /* Null */
2259 continue;
2260 }
2261 if (start) /* Found end of prev string */
2262 *prev_length = (ulong)(*column - start - 1);
2263 start = *column;
2264 prev_length = to;
2265 }
2266 }
2267
2268 /**
2269 Read field metadata from field descriptor and store it in MYSQL_FIELD
2270 structure. String values in MYSQL_FIELD are allocated in a given allocator
2271 root.
2272
2273 @param mysql connection handle
2274 @param alloc memory allocator root
2275 @param default_value flag telling if default values should be read from
2276 descriptor
2277 @param server_capabilities protocol capability flags which determine format
2278 of the descriptor
2279 @param row field descriptor
2280 @param field address of MYSQL_FIELD structure to store metadata in.
2281
2282 @returns 0 on success.
2283 */
2284
unpack_field(MYSQL * mysql,MEM_ROOT * alloc,bool default_value,uint server_capabilities,MYSQL_ROWS * row,MYSQL_FIELD * field)2285 static int unpack_field(MYSQL *mysql, MEM_ROOT *alloc, bool default_value,
2286 uint server_capabilities, MYSQL_ROWS *row,
2287 MYSQL_FIELD *field) {
2288 ulong lengths[9]; /* Max length of each field */
2289 DBUG_TRACE;
2290
2291 if (!field) {
2292 set_mysql_error(mysql, CR_UNKNOWN_ERROR, unknown_sqlstate);
2293 return 1;
2294 }
2295
2296 memset(field, 0, sizeof(MYSQL_FIELD));
2297
2298 if (server_capabilities & CLIENT_PROTOCOL_41) {
2299 uchar *pos;
2300 /* fields count may be wrong */
2301 cli_fetch_lengths(&lengths[0], row->data, default_value ? 8 : 7);
2302 field->catalog = strmake_root(alloc, (char *)row->data[0], lengths[0]);
2303 field->db = strmake_root(alloc, (char *)row->data[1], lengths[1]);
2304 field->table = strmake_root(alloc, (char *)row->data[2], lengths[2]);
2305 field->org_table = strmake_root(alloc, (char *)row->data[3], lengths[3]);
2306 field->name = strmake_root(alloc, (char *)row->data[4], lengths[4]);
2307 field->org_name = strmake_root(alloc, (char *)row->data[5], lengths[5]);
2308
2309 field->catalog_length = lengths[0];
2310 field->db_length = lengths[1];
2311 field->table_length = lengths[2];
2312 field->org_table_length = lengths[3];
2313 field->name_length = lengths[4];
2314 field->org_name_length = lengths[5];
2315
2316 /* Unpack fixed length parts */
2317 if (lengths[6] != 12) {
2318 /* malformed packet. signal an error. */
2319 set_mysql_error(mysql, CR_MALFORMED_PACKET, unknown_sqlstate);
2320 return 1;
2321 }
2322
2323 pos = (uchar *)row->data[6];
2324 field->charsetnr = uint2korr(pos);
2325 field->length = (uint)uint4korr(pos + 2);
2326 field->type = (enum enum_field_types)pos[6];
2327 field->flags = uint2korr(pos + 7);
2328 field->decimals = (uint)pos[9];
2329
2330 if (IS_NUM(field->type)) field->flags |= NUM_FLAG;
2331 if (default_value && row->data[7]) {
2332 field->def = strmake_root(alloc, (char *)row->data[7], lengths[7]);
2333 field->def_length = lengths[7];
2334 } else
2335 field->def = nullptr;
2336 field->max_length = 0;
2337 }
2338 #ifndef DELETE_SUPPORT_OF_4_0_PROTOCOL
2339 else {
2340 /*
2341 If any of the row->data[] below is NULL, it can result in a
2342 crash. Error out early as it indicates a malformed packet.
2343 For data[0], data[1] and data[5], strmake_root() will handle
2344 NULL values.
2345 */
2346 if (!row->data[2] || !row->data[3] || !row->data[4]) {
2347 set_mysql_error(mysql, CR_MALFORMED_PACKET, unknown_sqlstate);
2348 return 1;
2349 }
2350
2351 cli_fetch_lengths(&lengths[0], row->data, default_value ? 6 : 5);
2352 field->org_table = field->table =
2353 strmake_root(alloc, (char *)row->data[0], lengths[0]);
2354 field->name = strmake_root(alloc, (char *)row->data[1], lengths[1]);
2355 field->length = (uint)uint3korr((uchar *)row->data[2]);
2356 field->type = (enum enum_field_types)(uchar)row->data[3][0];
2357
2358 field->catalog = const_cast<char *>("");
2359 field->db = const_cast<char *>("");
2360 field->catalog_length = 0;
2361 field->db_length = 0;
2362 field->org_table_length = field->table_length = lengths[0];
2363 field->name_length = lengths[1];
2364
2365 if (server_capabilities & CLIENT_LONG_FLAG) {
2366 if (lengths[4] != 3) {
2367 /* malformed packet. signal an error. */
2368 set_mysql_error(mysql, CR_MALFORMED_PACKET, unknown_sqlstate);
2369 return 1;
2370 }
2371 field->flags = uint2korr((uchar *)row->data[4]);
2372 field->decimals = (uint)(uchar)row->data[4][2];
2373 } else {
2374 if (lengths[4] != 2) {
2375 /* malformed packet. signal an error. */
2376 set_mysql_error(mysql, CR_MALFORMED_PACKET, unknown_sqlstate);
2377 return 1;
2378 }
2379 field->flags = (uint)(uchar)row->data[4][0];
2380 field->decimals = (uint)(uchar)row->data[4][1];
2381 }
2382 if (IS_NUM(field->type)) field->flags |= NUM_FLAG;
2383 if (default_value && row->data[5]) {
2384 field->def = strmake_root(alloc, (char *)row->data[5], lengths[5]);
2385 field->def_length = lengths[5];
2386 } else
2387 field->def = nullptr;
2388 field->max_length = 0;
2389 }
2390 #endif /* DELETE_SUPPORT_OF_4_0_PROTOCOL */
2391 return 0;
2392 }
2393
2394 /***************************************************************************
2395 Change field rows to field structs
2396 ***************************************************************************/
2397
unpack_fields(MYSQL * mysql,MYSQL_ROWS * data,MEM_ROOT * alloc,uint fields,bool default_value,uint server_capabilities)2398 MYSQL_FIELD *unpack_fields(MYSQL *mysql, MYSQL_ROWS *data, MEM_ROOT *alloc,
2399 uint fields, bool default_value,
2400 uint server_capabilities) {
2401 MYSQL_ROWS *row;
2402 MYSQL_FIELD *field, *result;
2403 DBUG_TRACE;
2404
2405 field = result = (MYSQL_FIELD *)alloc->Alloc((uint)sizeof(*field) * fields);
2406 if (!result) {
2407 set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
2408 return nullptr;
2409 }
2410 memset(field, 0, sizeof(MYSQL_FIELD) * fields);
2411 for (row = data; row; row = row->next, field++) {
2412 /* fields count may be wrong */
2413 if (field < result || static_cast<uint>(field - result) >= fields) {
2414 return nullptr;
2415 }
2416 if (unpack_field(mysql, alloc, default_value, server_capabilities, row,
2417 field)) {
2418 return nullptr;
2419 }
2420 }
2421 return result;
2422 }
2423
2424 /**
2425 Read metadata resultset from server in asynchronous way.
2426
2427 @param[in] mysql connection handle
2428 @param[in] alloc memory allocator root
2429 @param[in] field_count total number of fields
2430 @param[in] field number of columns in single field descriptor
2431 @param[out] ret an array of field rows
2432
2433 @retval NET_ASYNC_NOT_READY metadata resultset not read completely
2434 @retval NET_ASYNC_COMPLETE finished reading metadata resultset
2435 */
cli_read_metadata_ex_nonblocking(MYSQL * mysql,MEM_ROOT * alloc,ulong field_count,unsigned int field,MYSQL_FIELD ** ret)2436 net_async_status cli_read_metadata_ex_nonblocking(MYSQL *mysql, MEM_ROOT *alloc,
2437 ulong field_count,
2438 unsigned int field,
2439 MYSQL_FIELD **ret) {
2440 DBUG_TRACE;
2441 uchar *pos;
2442 ulong pkt_len;
2443 NET *net = &mysql->net;
2444 MYSQL_ASYNC *async_data = ASYNC_DATA(mysql);
2445 *ret = nullptr;
2446
2447 if (!async_data->async_read_metadata_field_len) {
2448 async_data->async_read_metadata_field_len =
2449 (ulong *)alloc->Alloc(sizeof(ulong) * field);
2450 }
2451 if (!async_data->async_read_metadata_fields) {
2452 async_data->async_read_metadata_fields =
2453 (MYSQL_FIELD *)alloc->Alloc((uint)sizeof(MYSQL_FIELD) * field_count);
2454 if (async_data->async_read_metadata_fields)
2455 memset(async_data->async_read_metadata_fields, 0,
2456 sizeof(MYSQL_FIELD) * field_count);
2457 }
2458
2459 if (!async_data->async_read_metadata_fields) {
2460 set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
2461 goto end;
2462 }
2463
2464 if (!async_data->async_read_metadata_data.data) {
2465 async_data->async_read_metadata_data.data =
2466 (MYSQL_ROW)alloc->Alloc(sizeof(char *) * (field + 1));
2467 memset(async_data->async_read_metadata_data.data, 0,
2468 sizeof(char *) * (field + 1));
2469 }
2470
2471 /*
2472 In this below loop we read each column info as 1 single row
2473 and save it in mysql->fields array
2474 */
2475 while (async_data->async_read_metadata_cur_field < field_count) {
2476 int res;
2477 if (read_one_row_nonblocking(mysql, field,
2478 async_data->async_read_metadata_data.data,
2479 async_data->async_read_metadata_field_len,
2480 &res) == NET_ASYNC_NOT_READY) {
2481 return NET_ASYNC_NOT_READY;
2482 }
2483
2484 if (res == -1) {
2485 goto end;
2486 }
2487
2488 if (unpack_field(mysql, alloc, false, mysql->server_capabilities,
2489 &async_data->async_read_metadata_data,
2490 async_data->async_read_metadata_fields +
2491 async_data->async_read_metadata_cur_field)) {
2492 goto end;
2493 }
2494 async_data->async_read_metadata_cur_field++;
2495 }
2496
2497 /* Read EOF packet in case of old client */
2498 if (!(mysql->server_capabilities & CLIENT_DEPRECATE_EOF)) {
2499 if (cli_safe_read_nonblocking(mysql, nullptr, &pkt_len) ==
2500 NET_ASYNC_NOT_READY) {
2501 return NET_ASYNC_NOT_READY;
2502 }
2503
2504 if (pkt_len == packet_error) {
2505 goto end;
2506 }
2507
2508 pos = net->read_pos;
2509 if (*pos == 254) {
2510 mysql->warning_count = uint2korr(pos + 1);
2511 mysql->server_status = uint2korr(pos + 3);
2512 }
2513 }
2514 *ret = async_data->async_read_metadata_fields;
2515
2516 end:
2517 async_data->async_read_metadata_field_len = nullptr;
2518 async_data->async_read_metadata_fields = nullptr;
2519 memset(&async_data->async_read_metadata_data, 0,
2520 sizeof(async_data->async_read_metadata_data));
2521 async_data->async_read_metadata_cur_field = 0;
2522 return NET_ASYNC_COMPLETE;
2523 }
2524
2525 /**
2526 Read metadata resultset from server
2527 Memory allocated in a given allocator root.
2528
2529 @param[in] mysql connection handle
2530 @param[in] alloc memory allocator root
2531 @param[in] field_count total number of fields
2532 @param[in] field number of columns in single field descriptor
2533
2534 @retval an array of field rows
2535
2536 */
cli_read_metadata_ex(MYSQL * mysql,MEM_ROOT * alloc,ulong field_count,unsigned int field)2537 MYSQL_FIELD *cli_read_metadata_ex(MYSQL *mysql, MEM_ROOT *alloc,
2538 ulong field_count, unsigned int field) {
2539 ulong *len;
2540 uint f;
2541 uchar *pos;
2542 MYSQL_FIELD *fields, *result;
2543 MYSQL_ROWS data;
2544 NET *net = &mysql->net;
2545 size_t size;
2546
2547 DBUG_TRACE;
2548
2549 len = (ulong *)alloc->Alloc(sizeof(ulong) * field);
2550 size = sizeof(MYSQL_FIELD) * field_count;
2551
2552 if (field_count != (size / sizeof(MYSQL_FIELD))) {
2553 set_mysql_error(mysql, CR_MALFORMED_PACKET, unknown_sqlstate);
2554 end_server(mysql);
2555 return nullptr;
2556 }
2557
2558 fields = result = (MYSQL_FIELD *)alloc->Alloc(size);
2559 if (!result) {
2560 set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
2561 end_server(mysql);
2562 return nullptr;
2563 }
2564 memset(fields, 0, sizeof(MYSQL_FIELD) * field_count);
2565
2566 data.data = (MYSQL_ROW)alloc->Alloc(sizeof(char *) * (field + 1));
2567 memset(data.data, 0, sizeof(char *) * (field + 1));
2568
2569 /*
2570 In this below loop we read each column info as 1 single row
2571 and save it in mysql->fields array
2572 */
2573 for (f = 0; f < field_count; ++f) {
2574 if (read_one_row(mysql, field, data.data, len) == -1) return nullptr;
2575 if (unpack_field(mysql, alloc, false, mysql->server_capabilities, &data,
2576 fields++))
2577 return nullptr;
2578 }
2579 /* Read EOF packet in case of old client */
2580 if (!(mysql->server_capabilities & CLIENT_DEPRECATE_EOF)) {
2581 if (packet_error == cli_safe_read(mysql, nullptr)) return nullptr;
2582 pos = net->read_pos;
2583 if (*pos == 254) {
2584 mysql->warning_count = uint2korr(pos + 1);
2585 mysql->server_status = uint2korr(pos + 3);
2586 }
2587 }
2588 return result;
2589 }
2590
alloc_field_alloc(MYSQL * mysql)2591 static int alloc_field_alloc(MYSQL *mysql) {
2592 if (mysql->field_alloc == nullptr) {
2593 mysql->field_alloc = (MEM_ROOT *)my_malloc(
2594 key_memory_MYSQL, sizeof(MEM_ROOT), MYF(MY_WME | MY_ZEROFILL));
2595 if (mysql->field_alloc == nullptr) {
2596 set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
2597 return 1;
2598 }
2599 init_alloc_root(PSI_NOT_INSTRUMENTED, mysql->field_alloc, 8192,
2600 0); /* Assume rowlength < 8192 */
2601 }
2602 /* At this point the NET is receiving a resultset. max packet should be set */
2603 DBUG_ASSERT(mysql->net.max_packet_size != 0);
2604 /* Limit the size of the columns buffer to MAX packet size or 1M */
2605 mysql->field_alloc->set_max_capacity(
2606 std::max(1024UL * 1024UL, mysql->net.max_packet_size));
2607 return 0;
2608 }
2609
2610 /**
2611 Read metadata resultset from server
2612
2613 @param[in] mysql connection handle
2614 @param[in] field_count total number of fields
2615 @param[in] field number of columns in single field descriptor
2616
2617 @retval an array of field rows
2618
2619 */
cli_read_metadata(MYSQL * mysql,ulong field_count,unsigned int field)2620 MYSQL_FIELD *cli_read_metadata(MYSQL *mysql, ulong field_count,
2621 unsigned int field) {
2622 alloc_field_alloc(mysql);
2623 return cli_read_metadata_ex(mysql, mysql->field_alloc, field_count, field);
2624 }
2625
2626 /**
2627 Helper method to read metadata in asynchronous way.
2628 */
cli_read_metadata_nonblocking(MYSQL * mysql,ulong field_count,unsigned int field,MYSQL_FIELD ** ret)2629 static net_async_status cli_read_metadata_nonblocking(MYSQL *mysql,
2630 ulong field_count,
2631 unsigned int field,
2632 MYSQL_FIELD **ret) {
2633 alloc_field_alloc(mysql);
2634 if (cli_read_metadata_ex_nonblocking(mysql, mysql->field_alloc, field_count,
2635 field, ret) == NET_ASYNC_NOT_READY) {
2636 return NET_ASYNC_NOT_READY;
2637 }
2638 return NET_ASYNC_COMPLETE;
2639 }
2640
2641 /**
2642 Read resultset metadata returned by COM_QUERY command.
2643
2644 @param[in] mysql Client connection handle.
2645 @param[in] pos Position in the packet where the metadata
2646 starts.
2647 @param[in] field_count Number of columns in the field descriptor.
2648
2649 @retval 0 Success.
2650 @retval 1 Error.
2651 */
read_com_query_metadata(MYSQL * mysql,uchar * pos,ulong field_count)2652 static int read_com_query_metadata(MYSQL *mysql, uchar *pos,
2653 ulong field_count) {
2654 /* Store resultset metadata flag. */
2655 if (mysql->client_flag & CLIENT_OPTIONAL_RESULTSET_METADATA) {
2656 mysql->resultset_metadata = static_cast<enum enum_resultset_metadata>(*pos);
2657 } else {
2658 mysql->resultset_metadata = RESULTSET_METADATA_FULL;
2659 }
2660
2661 switch (mysql->resultset_metadata) {
2662 case RESULTSET_METADATA_FULL:
2663 /* Read metadata. */
2664 MYSQL_TRACE_STAGE(mysql, WAIT_FOR_FIELD_DEF);
2665
2666 if (!(mysql->fields = cli_read_metadata(mysql, field_count,
2667 protocol_41(mysql) ? 7 : 5))) {
2668 free_root(mysql->field_alloc, MYF(0));
2669 return 1;
2670 }
2671 break;
2672
2673 case RESULTSET_METADATA_NONE:
2674 /* Skip metadata. */
2675 mysql->fields = nullptr;
2676 break;
2677
2678 default:
2679 /* Unknown metadata flag. */
2680 mysql->fields = nullptr;
2681 return 1;
2682 }
2683
2684 return 0;
2685 }
2686
2687 /**
2688 Read resultset metadata returned by COM_QUERY command in asynchronous way.
2689
2690 @param[in] mysql Client connection handle.
2691 @param[in] pos Position in the packet where the metadata
2692 starts.
2693 @param[in] field_count Number of columns in the field descriptor.
2694 @param[out] res set to false incase of success and true for
2695 error.
2696
2697 @retval NET_ASYNC_NOT_READY metadata resultset not read completely
2698 @retval NET_ASYNC_COMPLETE finished reading metadata resultset
2699 */
read_com_query_metadata_nonblocking(MYSQL * mysql,uchar * pos,ulong field_count,int * res)2700 static net_async_status read_com_query_metadata_nonblocking(MYSQL *mysql,
2701 uchar *pos,
2702 ulong field_count,
2703 int *res) {
2704 DBUG_TRACE;
2705 /* pos is only set on the first reentrant call. */
2706 if (pos) {
2707 /* Store resultset metadata flag. */
2708 if (mysql->client_flag & CLIENT_OPTIONAL_RESULTSET_METADATA) {
2709 mysql->resultset_metadata =
2710 static_cast<enum enum_resultset_metadata>(*pos);
2711 } else {
2712 mysql->resultset_metadata = RESULTSET_METADATA_FULL;
2713 }
2714 }
2715
2716 switch (mysql->resultset_metadata) {
2717 case RESULTSET_METADATA_FULL:
2718 /* Read metadata. */
2719 MYSQL_TRACE_STAGE(mysql, WAIT_FOR_FIELD_DEF);
2720
2721 if (cli_read_metadata_nonblocking(
2722 mysql, field_count, protocol_41(mysql) ? 7 : 5, &mysql->fields) ==
2723 NET_ASYNC_NOT_READY) {
2724 return NET_ASYNC_NOT_READY;
2725 }
2726
2727 if (!mysql->fields) {
2728 free_root(mysql->field_alloc, MYF(0));
2729 *res = 1;
2730 return NET_ASYNC_COMPLETE;
2731 }
2732 break;
2733
2734 case RESULTSET_METADATA_NONE:
2735 /* Skip metadata. */
2736 mysql->fields = nullptr;
2737 break;
2738
2739 default:
2740 /* Unknown metadata flag. */
2741 mysql->fields = nullptr;
2742 *res = 1;
2743 return NET_ASYNC_COMPLETE;
2744 }
2745
2746 *res = 0;
2747 return NET_ASYNC_COMPLETE;
2748 }
2749
cli_read_rows_nonblocking(MYSQL * mysql,MYSQL_FIELD * mysql_fields,unsigned int fields,MYSQL_DATA ** result_out)2750 net_async_status cli_read_rows_nonblocking(MYSQL *mysql,
2751 MYSQL_FIELD *mysql_fields,
2752 unsigned int fields,
2753 MYSQL_DATA **result_out) {
2754 uint field;
2755 ulong pkt_len;
2756 ulong len;
2757 uchar *cp;
2758 char *to, *end_to;
2759 MYSQL_ROWS *cur;
2760 NET *net = &mysql->net;
2761 bool is_data_packet;
2762 DBUG_TRACE;
2763 MYSQL_ASYNC *async_context = ASYNC_DATA(mysql);
2764 NET_ASYNC *net_async = NET_ASYNC_DATA(net);
2765 *result_out = nullptr;
2766
2767 if (cli_safe_read_nonblocking(mysql, &is_data_packet, &pkt_len) ==
2768 NET_ASYNC_NOT_READY) {
2769 return NET_ASYNC_NOT_READY;
2770 }
2771
2772 mysql->packet_length = pkt_len;
2773 if (pkt_len == packet_error) {
2774 if (net_async->read_rows_is_first_read) {
2775 free_rows(async_context->rows_result_buffer);
2776 async_context->rows_result_buffer = nullptr;
2777 }
2778 net_async->read_rows_is_first_read = true;
2779 return NET_ASYNC_COMPLETE;
2780 }
2781
2782 if (net_async->read_rows_is_first_read) {
2783 MYSQL_DATA *result;
2784 if (!(result =
2785 (MYSQL_DATA *)my_malloc(key_memory_MYSQL_DATA, sizeof(MYSQL_DATA),
2786 MYF(MY_WME | MY_ZEROFILL))) ||
2787 !(result->alloc =
2788 (MEM_ROOT *)my_malloc(key_memory_MYSQL_DATA, sizeof(MEM_ROOT),
2789 MYF(MY_WME | MY_ZEROFILL)))) {
2790 set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
2791 net_async->read_rows_is_first_read = true;
2792 free_rows(result);
2793 return NET_ASYNC_COMPLETE;
2794 }
2795 async_context->rows_result_buffer = result;
2796 init_alloc_root(PSI_NOT_INSTRUMENTED, result->alloc, 8192,
2797 0); /* Assume rowlength < 8192 */
2798 async_context->prev_row_ptr = &result->data;
2799 result->rows = 0;
2800 result->fields = fields;
2801
2802 net_async->read_rows_is_first_read = false;
2803 }
2804
2805 /*
2806 The last EOF packet is either a single 254 character or (in MySQL 4.1)
2807 254 followed by 1-7 status bytes or an OK packet starting with 0xFE
2808 */
2809 while (*(cp = net->read_pos) == 0 || is_data_packet) {
2810 MYSQL_DATA *result = async_context->rows_result_buffer;
2811 result->rows++;
2812 if (!(cur = (MYSQL_ROWS *)result->alloc->Alloc(sizeof(MYSQL_ROWS))) ||
2813 !(cur->data = ((MYSQL_ROW)result->alloc->Alloc(
2814 (fields + 1) * sizeof(char *) + pkt_len)))) {
2815 free_rows(result);
2816 async_context->rows_result_buffer = nullptr;
2817 set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
2818 net_async->read_rows_is_first_read = true;
2819 return NET_ASYNC_COMPLETE;
2820 }
2821 *async_context->prev_row_ptr = cur;
2822 async_context->prev_row_ptr = &cur->next;
2823 to = (char *)(cur->data + fields + 1);
2824 end_to = to + pkt_len - 1;
2825 for (field = 0; field < fields; field++) {
2826 if ((len = (ulong)net_field_length(&cp)) ==
2827 NULL_LENGTH) { /* null field */
2828 cur->data[field] = nullptr;
2829 } else {
2830 cur->data[field] = to;
2831 if (to > end_to || len > (ulong)(end_to - to)) {
2832 free_rows(result);
2833 async_context->rows_result_buffer = nullptr;
2834 set_mysql_error(mysql, CR_MALFORMED_PACKET, unknown_sqlstate);
2835 net_async->read_rows_is_first_read = true;
2836 return NET_ASYNC_COMPLETE;
2837 }
2838 memcpy(to, (char *)cp, len);
2839 to[len] = 0;
2840 to += len + 1;
2841 cp += len;
2842 if (mysql_fields) {
2843 if (mysql_fields[field].max_length < len)
2844 mysql_fields[field].max_length = len;
2845 }
2846 }
2847 }
2848 cur->data[field] = to; /* End of last field */
2849 if (cli_safe_read_nonblocking(mysql, &is_data_packet, &pkt_len) ==
2850 NET_ASYNC_NOT_READY) {
2851 return NET_ASYNC_NOT_READY;
2852 }
2853 mysql->packet_length = pkt_len;
2854 if (pkt_len == packet_error) {
2855 free_rows(async_context->rows_result_buffer);
2856 async_context->rows_result_buffer = nullptr;
2857 net_async->read_rows_is_first_read = true;
2858 return NET_ASYNC_COMPLETE;
2859 }
2860 }
2861
2862 *async_context->prev_row_ptr = nullptr; /* last pointer is null */
2863 /* read EOF packet or OK packet if it is new client */
2864 if (pkt_len > 1) {
2865 if (mysql->server_capabilities & CLIENT_DEPRECATE_EOF && !is_data_packet)
2866 read_ok_ex(mysql, pkt_len);
2867 else {
2868 mysql->warning_count = uint2korr(cp + 1);
2869 mysql->server_status = uint2korr(cp + 3);
2870 }
2871
2872 DBUG_PRINT("info", ("status: %u warning_count: %u", mysql->server_status,
2873 mysql->warning_count));
2874 }
2875
2876 #if defined(CLIENT_PROTOCOL_TRACING)
2877 if (mysql->server_status & SERVER_MORE_RESULTS_EXISTS)
2878 MYSQL_TRACE_STAGE(mysql, WAIT_FOR_RESULT);
2879 else
2880 MYSQL_TRACE_STAGE(mysql, READY_FOR_COMMAND);
2881 #endif
2882 DBUG_PRINT("exit",
2883 ("Got %lu rows", (ulong)async_context->rows_result_buffer->rows));
2884 *result_out = async_context->rows_result_buffer;
2885 async_context->rows_result_buffer = nullptr;
2886 net_async->read_rows_is_first_read = true;
2887 return NET_ASYNC_COMPLETE;
2888 }
2889
2890 /* Read all rows (data) from server */
2891
cli_read_rows(MYSQL * mysql,MYSQL_FIELD * mysql_fields,unsigned int fields)2892 MYSQL_DATA *cli_read_rows(MYSQL *mysql, MYSQL_FIELD *mysql_fields,
2893 unsigned int fields) {
2894 uint field;
2895 ulong pkt_len;
2896 ulong len;
2897 uchar *cp;
2898 char *to, *end_to;
2899 MYSQL_DATA *result;
2900 MYSQL_ROWS **prev_ptr, *cur;
2901 NET *net = &mysql->net;
2902 bool is_data_packet;
2903 DBUG_TRACE;
2904
2905 if ((pkt_len = cli_safe_read(mysql, &is_data_packet)) == packet_error)
2906 return nullptr;
2907
2908 if (pkt_len == 0) return nullptr;
2909 if (!(result =
2910 (MYSQL_DATA *)my_malloc(key_memory_MYSQL_DATA, sizeof(MYSQL_DATA),
2911 MYF(MY_WME | MY_ZEROFILL))) ||
2912 !(result->alloc =
2913 (MEM_ROOT *)my_malloc(key_memory_MYSQL_DATA, sizeof(MEM_ROOT),
2914 MYF(MY_WME | MY_ZEROFILL)))) {
2915 set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
2916 free_rows(result);
2917 return nullptr;
2918 }
2919 init_alloc_root(PSI_NOT_INSTRUMENTED, result->alloc, 8192,
2920 0); /* Assume rowlength < 8192 */
2921 prev_ptr = &result->data;
2922 result->rows = 0;
2923 result->fields = fields;
2924
2925 /*
2926 The last EOF packet is either a single 254 character or (in MySQL 4.1)
2927 254 followed by 1-7 status bytes or an OK packet starting with 0xFE
2928 */
2929
2930 while (*(cp = net->read_pos) == 0 || is_data_packet) {
2931 result->rows++;
2932 if (!(cur = (MYSQL_ROWS *)result->alloc->Alloc(sizeof(MYSQL_ROWS))) ||
2933 !(cur->data = ((MYSQL_ROW)result->alloc->Alloc(
2934 (fields + 1) * sizeof(char *) + pkt_len)))) {
2935 free_rows(result);
2936 set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
2937 return nullptr;
2938 }
2939 *prev_ptr = cur;
2940 prev_ptr = &cur->next;
2941 to = (char *)(cur->data + fields + 1);
2942 end_to = to + pkt_len - 1;
2943 for (field = 0; field < fields; field++) {
2944 if ((len = (ulong)net_field_length(&cp)) ==
2945 NULL_LENGTH) { /* null field */
2946 cur->data[field] = nullptr;
2947 } else {
2948 cur->data[field] = to;
2949 DBUG_EXECUTE_IF("simulate_invalid_packet_data", {
2950 to = end_to + 1;
2951 len = ULONG_MAX - 1;
2952 });
2953 if (to > end_to || len > (ulong)(end_to - to)) {
2954 free_rows(result);
2955 set_mysql_error(mysql, CR_MALFORMED_PACKET, unknown_sqlstate);
2956 return nullptr;
2957 }
2958 memcpy(to, (char *)cp, len);
2959 to[len] = 0;
2960 to += len + 1;
2961 cp += len;
2962 if (mysql_fields) {
2963 if (mysql_fields[field].max_length < len)
2964 mysql_fields[field].max_length = len;
2965 }
2966 }
2967 }
2968 cur->data[field] = to; /* End of last field */
2969 if ((pkt_len = cli_safe_read(mysql, &is_data_packet)) == packet_error) {
2970 free_rows(result);
2971 return nullptr;
2972 }
2973 }
2974 *prev_ptr = nullptr; /* last pointer is null */
2975 /* read EOF packet or OK packet if it is new client */
2976 if (pkt_len > 1) {
2977 if (mysql->server_capabilities & CLIENT_DEPRECATE_EOF && !is_data_packet)
2978 read_ok_ex(mysql, pkt_len);
2979 else {
2980 mysql->warning_count = uint2korr(cp + 1);
2981 mysql->server_status = uint2korr(cp + 3);
2982 }
2983
2984 DBUG_PRINT("info", ("status: %u warning_count: %u", mysql->server_status,
2985 mysql->warning_count));
2986 }
2987
2988 #if defined(CLIENT_PROTOCOL_TRACING)
2989 if (mysql->server_status & SERVER_MORE_RESULTS_EXISTS)
2990 MYSQL_TRACE_STAGE(mysql, WAIT_FOR_RESULT);
2991 else
2992 MYSQL_TRACE_STAGE(mysql, READY_FOR_COMMAND);
2993 #endif
2994 DBUG_PRINT("exit", ("Got %lu rows", (ulong)result->rows));
2995 return result;
2996 }
2997
read_one_row_complete(MYSQL * mysql,ulong pkt_len,bool is_data_packet,uint fields,MYSQL_ROW row,ulong * lengths)2998 static int read_one_row_complete(MYSQL *mysql, ulong pkt_len,
2999 bool is_data_packet, uint fields,
3000 MYSQL_ROW row, ulong *lengths) {
3001 DBUG_TRACE;
3002 uint field;
3003 ulong len;
3004 uchar *pos, *prev_pos, *end_pos;
3005 NET *net = &mysql->net;
3006
3007 if (net->read_pos[0] != 0x00 && !is_data_packet) {
3008 if (pkt_len > 1) /* MySQL 4.1 protocol */
3009 {
3010 if (mysql->server_capabilities & CLIENT_DEPRECATE_EOF)
3011 read_ok_ex(mysql, pkt_len);
3012 else {
3013 mysql->warning_count = uint2korr(net->read_pos + 1);
3014 mysql->server_status = uint2korr(net->read_pos + 3);
3015 }
3016 }
3017 #if defined(CLIENT_PROTOCOL_TRACING)
3018 if (mysql->server_status & SERVER_MORE_RESULTS_EXISTS)
3019 MYSQL_TRACE_STAGE(mysql, WAIT_FOR_RESULT);
3020 else
3021 MYSQL_TRACE_STAGE(mysql, READY_FOR_COMMAND);
3022 #endif
3023 return 1; /* End of data */
3024 }
3025 prev_pos = nullptr; /* allowed to write at packet[-1] */
3026 pos = net->read_pos;
3027 end_pos = pos + pkt_len;
3028 for (field = 0; field < fields; field++) {
3029 len = (ulong)net_field_length_checked(&pos, (ulong)(end_pos - pos));
3030 if (pos > end_pos) {
3031 set_mysql_error(mysql, CR_UNKNOWN_ERROR, unknown_sqlstate);
3032 return -1;
3033 }
3034
3035 if (len == NULL_LENGTH) {
3036 row[field] = nullptr;
3037 *lengths++ = 0;
3038 } else {
3039 row[field] = (char *)pos;
3040 pos += len;
3041 *lengths++ = len;
3042 }
3043 if (prev_pos) *prev_pos = 0; /* Terminate prev field */
3044 prev_pos = pos;
3045 }
3046 row[field] = (char *)prev_pos + 1; /* End of last field */
3047 *prev_pos = 0; /* Terminate last field */
3048 return 0;
3049 }
3050
3051 /*
3052 Read one row. Uses packet buffer as storage for fields.
3053 When next packet is read, the previous field values are destroyed
3054 */
3055
read_one_row(MYSQL * mysql,uint fields,MYSQL_ROW row,ulong * lengths)3056 static int read_one_row(MYSQL *mysql, uint fields, MYSQL_ROW row,
3057 ulong *lengths) {
3058 ulong pkt_len;
3059 bool is_data_packet;
3060
3061 if ((pkt_len = cli_safe_read(mysql, &is_data_packet)) == packet_error)
3062 return -1;
3063
3064 return read_one_row_complete(mysql, pkt_len, is_data_packet, fields, row,
3065 lengths);
3066 }
3067
read_one_row_nonblocking(MYSQL * mysql,uint fields,MYSQL_ROW row,ulong * lengths,int * res)3068 static net_async_status read_one_row_nonblocking(MYSQL *mysql, uint fields,
3069 MYSQL_ROW row, ulong *lengths,
3070 int *res) {
3071 DBUG_TRACE;
3072 ulong pkt_len;
3073 bool is_data_packet;
3074 net_async_status status;
3075
3076 status = cli_safe_read_nonblocking(mysql, &is_data_packet, &pkt_len);
3077 if (status == NET_ASYNC_NOT_READY) {
3078 return status;
3079 }
3080
3081 mysql->packet_length = pkt_len;
3082 if (pkt_len == packet_error) {
3083 *res = -1;
3084 return NET_ASYNC_COMPLETE;
3085 }
3086
3087 *res = read_one_row_complete(mysql, pkt_len, is_data_packet, fields, row,
3088 lengths);
3089 return NET_ASYNC_COMPLETE;
3090 }
3091
3092 /****************************************************************************
3093 Init MySQL structure or allocate one
3094 ****************************************************************************/
3095
mysql_init(MYSQL * mysql)3096 MYSQL *STDCALL mysql_init(MYSQL *mysql) {
3097 if (mysql_server_init(0, nullptr, nullptr)) return nullptr;
3098 if (!mysql) {
3099 if (!(mysql = (MYSQL *)my_malloc(key_memory_MYSQL, sizeof(*mysql),
3100 MYF(MY_WME | MY_ZEROFILL)))) {
3101 set_mysql_error(nullptr, CR_OUT_OF_MEMORY, unknown_sqlstate);
3102 return nullptr;
3103 }
3104 mysql->free_me = true;
3105 } else
3106 memset(mysql, 0, sizeof(*(mysql)));
3107 mysql->charset = default_client_charset_info;
3108 mysql->field_alloc = (MEM_ROOT *)my_malloc(
3109 key_memory_MYSQL, sizeof(*mysql->field_alloc), MYF(MY_WME | MY_ZEROFILL));
3110 if (!mysql->field_alloc) {
3111 set_mysql_error(nullptr, CR_OUT_OF_MEMORY, unknown_sqlstate);
3112 if (mysql->free_me) my_free(mysql);
3113 return nullptr;
3114 }
3115 my_stpcpy(mysql->net.sqlstate, not_error_sqlstate);
3116
3117 /*
3118 Only enable LOAD DATA INFILE by default if configured with option
3119 ENABLED_LOCAL_INFILE
3120 */
3121
3122 #if defined(ENABLED_LOCAL_INFILE) && !defined(MYSQL_SERVER)
3123 mysql->options.client_flag |= CLIENT_LOCAL_FILES;
3124 #endif
3125
3126 #if defined(_WIN32)
3127 mysql->options.shared_memory_base_name = (char *)def_shared_memory_base_name;
3128 #endif
3129
3130 mysql->options.report_data_truncation = true; /* default */
3131
3132 /* Initialize extensions. */
3133 if (!(mysql->extension = mysql_extension_init(mysql))) {
3134 set_mysql_error(nullptr, CR_OUT_OF_MEMORY, unknown_sqlstate);
3135 return nullptr;
3136 }
3137
3138 /*
3139 By default we don't reconnect because it could silently corrupt data (after
3140 reconnection you potentially lose table locks, user variables, session
3141 variables (transactions but they are specifically dealt with in
3142 mysql_reconnect()).
3143 This is a change: < 5.0.3 mysql->reconnect was set to 1 by default.
3144 How this change impacts existing apps:
3145 - existing apps which relyed on the default will see a behaviour change;
3146 they will have to set reconnect=1 after mysql_real_connect().
3147 - existing apps which explicitely asked for reconnection (the only way they
3148 could do it was by setting mysql.reconnect to 1 after mysql_real_connect())
3149 will not see a behaviour change.
3150 - existing apps which explicitely asked for no reconnection
3151 (mysql.reconnect=0) will not see a behaviour change.
3152 */
3153 mysql->reconnect = false;
3154 #if !defined(MYSQL_SERVER)
3155 ENSURE_EXTENSIONS_PRESENT(&mysql->options);
3156 mysql->options.extension->ssl_mode = SSL_MODE_PREFERRED;
3157 #endif
3158 /* by default connection_compressed should be OFF */
3159 ENSURE_EXTENSIONS_PRESENT(&mysql->options);
3160 mysql->options.extension->connection_compressed = false;
3161
3162 mysql->resultset_metadata = RESULTSET_METADATA_FULL;
3163 ASYNC_DATA(mysql)->async_op_status = ASYNC_OP_UNSET;
3164 return mysql;
3165 }
3166
3167 /*
3168 MYSQL::extension handling (see sql_common.h for declaration
3169 of MYSQL_EXTENSION structure).
3170 */
3171
mysql_extension_init(MYSQL * mysql MY_ATTRIBUTE ((unused)))3172 MYSQL_EXTENSION *mysql_extension_init(MYSQL *mysql MY_ATTRIBUTE((unused))) {
3173 MYSQL_EXTENSION *ext;
3174
3175 ext = static_cast<MYSQL_EXTENSION *>(my_malloc(PSI_NOT_INSTRUMENTED,
3176 sizeof(MYSQL_EXTENSION),
3177 MYF(MY_WME | MY_ZEROFILL)));
3178 ext->mysql_async_context = static_cast<MYSQL_ASYNC *>(
3179 my_malloc(PSI_NOT_INSTRUMENTED, sizeof(struct MYSQL_ASYNC),
3180 MYF(MY_WME | MY_ZEROFILL)));
3181 /* set default value */
3182 ext->mysql_async_context->async_op_status = ASYNC_OP_UNSET;
3183 #ifdef MYSQL_SERVER
3184 ext->server_extn = nullptr;
3185 #endif
3186 return ext;
3187 }
3188
mysql_extension_free(MYSQL_EXTENSION * ext)3189 void mysql_extension_free(MYSQL_EXTENSION *ext) {
3190 if (!ext) return;
3191 if (ext->trace_data) my_free(ext->trace_data);
3192 if (ext->mysql_async_context) {
3193 if (ext->mysql_async_context->connect_context) {
3194 if (ext->mysql_async_context->connect_context
3195 ->scramble_buffer_allocated) {
3196 my_free(ext->mysql_async_context->connect_context->scramble_buffer);
3197 ext->mysql_async_context->connect_context->scramble_buffer = nullptr;
3198 }
3199 my_free(ext->mysql_async_context->connect_context);
3200 ext->mysql_async_context->connect_context = nullptr;
3201 }
3202 my_free(ext->mysql_async_context);
3203 ext->mysql_async_context = nullptr;
3204 }
3205 // free state change related resources.
3206 free_state_change_info(ext);
3207
3208 my_free(ext);
3209 }
3210
3211 /*
3212 Fill in SSL part of MYSQL structure and set 'use_ssl' flag.
3213 NB! Errors are not reported until you do mysql_real_connect.
3214 */
3215
mysql_ssl_set(MYSQL * mysql MY_ATTRIBUTE ((unused)),const char * key MY_ATTRIBUTE ((unused)),const char * cert MY_ATTRIBUTE ((unused)),const char * ca MY_ATTRIBUTE ((unused)),const char * capath MY_ATTRIBUTE ((unused)),const char * cipher MY_ATTRIBUTE ((unused)))3216 bool STDCALL mysql_ssl_set(MYSQL *mysql MY_ATTRIBUTE((unused)),
3217 const char *key MY_ATTRIBUTE((unused)),
3218 const char *cert MY_ATTRIBUTE((unused)),
3219 const char *ca MY_ATTRIBUTE((unused)),
3220 const char *capath MY_ATTRIBUTE((unused)),
3221 const char *cipher MY_ATTRIBUTE((unused))) {
3222 bool result = false;
3223 DBUG_TRACE;
3224 result = mysql_options(mysql, MYSQL_OPT_SSL_KEY, key) +
3225 mysql_options(mysql, MYSQL_OPT_SSL_CERT, cert) +
3226 mysql_options(mysql, MYSQL_OPT_SSL_CA, ca) +
3227 mysql_options(mysql, MYSQL_OPT_SSL_CAPATH, capath) +
3228 mysql_options(mysql, MYSQL_OPT_SSL_CIPHER, cipher)
3229 ? true
3230 : false;
3231 return result;
3232 }
3233
3234 /*
3235 Free strings in the SSL structure and clear 'use_ssl' flag.
3236 NB! Errors are not reported until you do mysql_real_connect.
3237 */
3238
mysql_ssl_free(MYSQL * mysql)3239 static void mysql_ssl_free(MYSQL *mysql) {
3240 DBUG_TRACE;
3241
3242 my_free(mysql->options.ssl_key);
3243 my_free(mysql->options.ssl_cert);
3244 my_free(mysql->options.ssl_ca);
3245 my_free(mysql->options.ssl_capath);
3246 my_free(mysql->options.ssl_cipher);
3247 if (mysql->options.extension) {
3248 my_free(mysql->options.extension->tls_version);
3249 my_free(mysql->options.extension->ssl_crl);
3250 my_free(mysql->options.extension->ssl_crlpath);
3251 my_free(mysql->options.extension->tls_ciphersuites);
3252 my_free(mysql->options.extension->load_data_dir);
3253 }
3254 mysql->options.ssl_key = nullptr;
3255 mysql->options.ssl_cert = nullptr;
3256 mysql->options.ssl_ca = nullptr;
3257 mysql->options.ssl_capath = nullptr;
3258 mysql->options.ssl_cipher = nullptr;
3259 if (mysql->options.extension) {
3260 mysql->options.extension->ssl_crl = nullptr;
3261 mysql->options.extension->ssl_crlpath = nullptr;
3262 mysql->options.extension->ssl_ctx_flags = 0;
3263 mysql->options.extension->tls_version = nullptr;
3264 mysql->options.extension->ssl_mode = SSL_MODE_DISABLED;
3265 mysql->options.extension->ssl_fips_mode = SSL_FIPS_MODE_OFF;
3266 mysql->options.extension->tls_ciphersuites = nullptr;
3267 mysql->options.extension->load_data_dir = nullptr;
3268 }
3269 mysql->connector_fd = nullptr;
3270 }
3271
3272 /*
3273 Return the SSL cipher (if any) used for current
3274 connection to the server.
3275
3276 SYNOPSYS
3277 mysql_get_ssl_cipher()
3278 mysql pointer to the mysql connection
3279
3280 */
3281
mysql_get_ssl_cipher(MYSQL * mysql MY_ATTRIBUTE ((unused)))3282 const char *STDCALL mysql_get_ssl_cipher(MYSQL *mysql MY_ATTRIBUTE((unused))) {
3283 DBUG_TRACE;
3284 if (mysql->net.vio && mysql->net.vio->ssl_arg)
3285 return SSL_get_cipher_name((SSL *)mysql->net.vio->ssl_arg);
3286 return nullptr;
3287 }
3288
3289 /*
3290 Check the server's (subject) Common Name against the
3291 hostname we connected to
3292
3293 SYNOPSIS
3294 ssl_verify_server_cert()
3295 vio pointer to a SSL connected vio
3296 server_hostname name of the server that we connected to
3297 errptr if we fail, we'll return (a pointer to a string
3298 describing) the reason here
3299
3300 RETURN VALUES
3301 0 Success
3302 1 Failed to validate server
3303
3304 */
3305
ssl_verify_server_cert(Vio * vio,const char * server_hostname,const char ** errptr)3306 static int ssl_verify_server_cert(Vio *vio, const char *server_hostname,
3307 const char **errptr) {
3308 SSL *ssl;
3309 X509 *server_cert = nullptr;
3310 int ret_validation = 1;
3311
3312 #if !(OPENSSL_VERSION_NUMBER >= 0x10002000L)
3313 int cn_loc = -1;
3314 char *cn = NULL;
3315 ASN1_STRING *cn_asn1 = NULL;
3316 X509_NAME_ENTRY *cn_entry = NULL;
3317 X509_NAME *subject = NULL;
3318 #endif
3319
3320 DBUG_TRACE;
3321 DBUG_PRINT("enter", ("server_hostname: %s", server_hostname));
3322
3323 if (!(ssl = (SSL *)vio->ssl_arg)) {
3324 *errptr = "No SSL pointer found";
3325 goto error;
3326 }
3327
3328 if (!server_hostname) {
3329 *errptr = "No server hostname supplied";
3330 goto error;
3331 }
3332
3333 if (!(server_cert = SSL_get_peer_certificate(ssl))) {
3334 *errptr = "Could not get server certificate";
3335 goto error;
3336 }
3337
3338 if (X509_V_OK != SSL_get_verify_result(ssl)) {
3339 *errptr = "Failed to verify the server certificate";
3340 goto error;
3341 }
3342 /*
3343 We already know that the certificate exchanged was valid; the SSL library
3344 handled that. Now we need to verify that the contents of the certificate
3345 are what we expect.
3346 */
3347
3348 /* Use OpenSSL certificate matching functions instead of our own if we
3349 have OpenSSL. The X509_check_* functions return 1 on success.
3350 */
3351 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
3352 /*
3353 For OpenSSL 1.0.2 and up we already set certificate verification
3354 parameters in the new_VioSSLFd() to perform automatic checks.
3355 */
3356 ret_validation = 0;
3357 #else /* OPENSSL_VERSION_NUMBER < 0x10002000L */
3358 /*
3359 OpenSSL prior to 1.0.2 do not support X509_check_host() function.
3360 Use deprecated X509_get_subject_name() instead.
3361 */
3362 subject = X509_get_subject_name((X509 *)server_cert);
3363 // Find the CN location in the subject
3364 cn_loc = X509_NAME_get_index_by_NID(subject, NID_commonName, -1);
3365 if (cn_loc < 0) {
3366 *errptr = "Failed to get CN location in the certificate subject";
3367 goto error;
3368 }
3369
3370 // Get the CN entry for given location
3371 cn_entry = X509_NAME_get_entry(subject, cn_loc);
3372 if (cn_entry == NULL) {
3373 *errptr = "Failed to get CN entry using CN location";
3374 goto error;
3375 }
3376
3377 // Get CN from common name entry
3378 cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry);
3379 if (cn_asn1 == NULL) {
3380 *errptr = "Failed to get CN from CN entry";
3381 goto error;
3382 }
3383
3384 cn = (char *)ASN1_STRING_data(cn_asn1);
3385
3386 // There should not be any NULL embedded in the CN
3387 if ((size_t)ASN1_STRING_length(cn_asn1) != strlen(cn)) {
3388 *errptr = "NULL embedded in the certificate CN";
3389 goto error;
3390 }
3391
3392 DBUG_PRINT("info", ("Server hostname in cert: %s", cn));
3393 if (!strcmp(cn, server_hostname)) {
3394 /* Success */
3395 ret_validation = 0;
3396 }
3397 #endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
3398
3399 *errptr = "SSL certificate validation success";
3400
3401 error:
3402 if (server_cert != nullptr) X509_free(server_cert);
3403 return ret_validation;
3404 }
3405
3406 /*
3407 Note that the mysql argument must be initialized with mysql_init()
3408 before calling mysql_real_connect !
3409 */
3410
3411 static bool cli_read_query_result(MYSQL *mysql);
3412 static net_async_status cli_read_query_result_nonblocking(MYSQL *mysql);
3413 static MYSQL_RES *cli_use_result(MYSQL *mysql);
3414
cli_read_change_user_result(MYSQL * mysql)3415 int cli_read_change_user_result(MYSQL *mysql) {
3416 return cli_safe_read(mysql, nullptr);
3417 }
3418
cli_read_change_user_result_nonblocking(MYSQL * mysql,ulong * ret)3419 net_async_status cli_read_change_user_result_nonblocking(MYSQL *mysql,
3420 ulong *ret) {
3421 return cli_safe_read_nonblocking(mysql, nullptr, ret);
3422 }
3423
3424 static MYSQL_METHODS client_methods = {
3425 cli_read_query_result, /* read_query_result */
3426 cli_advanced_command, /* advanced_command */
3427 cli_read_rows, /* read_rows */
3428 cli_use_result, /* use_result */
3429 cli_fetch_lengths, /* fetch_lengths */
3430 cli_flush_use_result, /* flush_use_result */
3431 cli_read_change_user_result /* read_change_user_result */
3432 #if !defined(MYSQL_SERVER) || defined(XTRABACKUP)
3433 ,
3434 cli_list_fields, /* list_fields */
3435 cli_read_prepare_result, /* read_prepare_result */
3436 cli_stmt_execute, /* stmt_execute */
3437 cli_read_binary_rows, /* read_binary_rows */
3438 cli_unbuffered_fetch, /* unbuffered_fetch */
3439 cli_read_statistics, /* read_statistics */
3440 cli_read_query_result, /* next_result */
3441 cli_read_binary_rows, /* read_rows_from_cursor */
3442 free_rows
3443 #endif
3444 ,
3445 cli_read_query_result_nonblocking, /* read_query_result_nonblocking */
3446 cli_advanced_command_nonblocking, /* advanced_command_nonblocking */
3447 cli_read_rows_nonblocking, /* read_rows_nonblocking */
3448 cli_flush_use_result_nonblocking, /* flush_use_result_nonblocking */
3449 cli_read_query_result_nonblocking, /* next_result_nonblocking */
3450 cli_read_change_user_result_nonblocking /* read_change_user_result_nonblocking
3451 */
3452 };
3453
3454 typedef enum my_cs_match_type_enum {
3455 /* MySQL and OS charsets are fully compatible */
3456 my_cs_exact,
3457 /* MySQL charset is very close to OS charset */
3458 my_cs_approx,
3459 /*
3460 MySQL knows this charset, but it is not supported as client character set.
3461 */
3462 my_cs_unsupp
3463 } my_cs_match_type;
3464
3465 struct MY_CSET_OS_NAME {
3466 const char *os_name;
3467 const char *my_name;
3468 my_cs_match_type param;
3469 };
3470
3471 const MY_CSET_OS_NAME charsets[] = {
3472 #ifdef _WIN32
3473 {"cp437", "cp850", my_cs_approx}, {"cp850", "cp850", my_cs_exact},
3474 {"cp852", "cp852", my_cs_exact}, {"cp858", "cp850", my_cs_approx},
3475 {"cp866", "cp866", my_cs_exact}, {"cp874", "tis620", my_cs_approx},
3476 {"cp932", "cp932", my_cs_exact}, {"cp936", "gbk", my_cs_approx},
3477 {"cp949", "euckr", my_cs_approx}, {"cp950", "big5", my_cs_exact},
3478 {"cp1200", "utf16le", my_cs_unsupp}, {"cp1201", "utf16", my_cs_unsupp},
3479 {"cp1250", "cp1250", my_cs_exact}, {"cp1251", "cp1251", my_cs_exact},
3480 {"cp1252", "latin1", my_cs_exact}, {"cp1253", "greek", my_cs_exact},
3481 {"cp1254", "latin5", my_cs_exact}, {"cp1255", "hebrew", my_cs_approx},
3482 {"cp1256", "cp1256", my_cs_exact}, {"cp1257", "cp1257", my_cs_exact},
3483 {"cp10000", "macroman", my_cs_exact}, {"cp10001", "sjis", my_cs_approx},
3484 {"cp10002", "big5", my_cs_approx}, {"cp10008", "gb2312", my_cs_approx},
3485 {"cp10021", "tis620", my_cs_approx}, {"cp10029", "macce", my_cs_exact},
3486 {"cp12001", "utf32", my_cs_unsupp}, {"cp20107", "swe7", my_cs_exact},
3487 {"cp20127", "latin1", my_cs_approx}, {"cp20866", "koi8r", my_cs_exact},
3488 {"cp20932", "ujis", my_cs_exact}, {"cp20936", "gb2312", my_cs_approx},
3489 {"cp20949", "euckr", my_cs_approx}, {"cp21866", "koi8u", my_cs_exact},
3490 {"cp28591", "latin1", my_cs_approx}, {"cp28592", "latin2", my_cs_exact},
3491 {"cp28597", "greek", my_cs_exact}, {"cp28598", "hebrew", my_cs_exact},
3492 {"cp28599", "latin5", my_cs_exact}, {"cp28603", "latin7", my_cs_exact},
3493 {"cp38598", "hebrew", my_cs_exact}, {"cp51932", "ujis", my_cs_exact},
3494 {"cp51936", "gb2312", my_cs_exact}, {"cp51949", "euckr", my_cs_exact},
3495 {"cp51950", "big5", my_cs_exact}, {"cp54936", "gb18030", my_cs_exact},
3496 {"cp65001", "utf8mb4", my_cs_exact},
3497
3498 #else /* not Windows */
3499
3500 {"646", "latin1", my_cs_approx}, /* Default on Solaris */
3501 {"ANSI_X3.4-1968", "latin1", my_cs_approx},
3502 {"ansi1251", "cp1251", my_cs_exact},
3503 {"armscii8", "armscii8", my_cs_exact},
3504 {"armscii-8", "armscii8", my_cs_exact},
3505 {"ASCII", "latin1", my_cs_approx},
3506 {"Big5", "big5", my_cs_exact},
3507 {"cp1251", "cp1251", my_cs_exact},
3508 {"cp1255", "hebrew", my_cs_approx},
3509 {"CP866", "cp866", my_cs_exact},
3510 {"eucCN", "gb2312", my_cs_exact},
3511 {"euc-CN", "gb2312", my_cs_exact},
3512 {"eucJP", "ujis", my_cs_exact},
3513 {"euc-JP", "ujis", my_cs_exact},
3514 {"eucKR", "euckr", my_cs_exact},
3515 {"euc-KR", "euckr", my_cs_exact},
3516 {"gb18030", "gb18030", my_cs_exact},
3517 {"gb2312", "gb2312", my_cs_exact},
3518 {"gbk", "gbk", my_cs_exact},
3519 {"georgianps", "geostd8", my_cs_exact},
3520 {"georgian-ps", "geostd8", my_cs_exact},
3521 {"IBM-1252", "cp1252", my_cs_exact},
3522
3523 {"iso88591", "latin1", my_cs_approx},
3524 {"ISO_8859-1", "latin1", my_cs_approx},
3525 {"ISO8859-1", "latin1", my_cs_approx},
3526 {"ISO-8859-1", "latin1", my_cs_approx},
3527
3528 {"iso885913", "latin7", my_cs_exact},
3529 {"ISO_8859-13", "latin7", my_cs_exact},
3530 {"ISO8859-13", "latin7", my_cs_exact},
3531 {"ISO-8859-13", "latin7", my_cs_exact},
3532
3533 {"iso88592", "latin2", my_cs_exact},
3534 {"ISO_8859-2", "latin2", my_cs_exact},
3535 {"ISO8859-2", "latin2", my_cs_exact},
3536 {"ISO-8859-2", "latin2", my_cs_exact},
3537
3538 {"iso88597", "greek", my_cs_exact},
3539 {"ISO_8859-7", "greek", my_cs_exact},
3540 {"ISO8859-7", "greek", my_cs_exact},
3541 {"ISO-8859-7", "greek", my_cs_exact},
3542
3543 {"iso88598", "hebrew", my_cs_exact},
3544 {"ISO_8859-8", "hebrew", my_cs_exact},
3545 {"ISO8859-8", "hebrew", my_cs_exact},
3546 {"ISO-8859-8", "hebrew", my_cs_exact},
3547
3548 {"iso88599", "latin5", my_cs_exact},
3549 {"ISO_8859-9", "latin5", my_cs_exact},
3550 {"ISO8859-9", "latin5", my_cs_exact},
3551 {"ISO-8859-9", "latin5", my_cs_exact},
3552
3553 {"koi8r", "koi8r", my_cs_exact},
3554 {"KOI8-R", "koi8r", my_cs_exact},
3555 {"koi8u", "koi8u", my_cs_exact},
3556 {"KOI8-U", "koi8u", my_cs_exact},
3557
3558 {"roman8", "hp8", my_cs_exact}, /* Default on HP UX */
3559
3560 {"Shift_JIS", "sjis", my_cs_exact},
3561 {"SJIS", "sjis", my_cs_exact},
3562 {"shiftjisx0213", "sjis", my_cs_exact},
3563
3564 {"tis620", "tis620", my_cs_exact},
3565 {"tis-620", "tis620", my_cs_exact},
3566
3567 {"ujis", "ujis", my_cs_exact},
3568
3569 {"US-ASCII", "latin1", my_cs_approx},
3570
3571 {"utf8", "utf8mb4", my_cs_exact},
3572 {"utf-8", "utf8mb4", my_cs_exact},
3573 #endif
3574 {nullptr, nullptr, my_cs_exact}};
3575
my_os_charset_to_mysql_charset(const char * csname)3576 const char *my_os_charset_to_mysql_charset(const char *csname) {
3577 const MY_CSET_OS_NAME *csp;
3578 for (csp = charsets; csp->os_name; csp++) {
3579 if (!my_strcasecmp(&my_charset_latin1, csp->os_name, csname)) {
3580 switch (csp->param) {
3581 case my_cs_exact:
3582 return csp->my_name;
3583
3584 case my_cs_approx:
3585 /*
3586 Maybe we should print a warning eventually:
3587 character set correspondence is not exact.
3588 */
3589 return csp->my_name;
3590
3591 default:
3592 my_printf_error(ER_UNKNOWN_ERROR,
3593 "OS character set '%s'"
3594 " is not supported by MySQL client",
3595 MYF(0), csp->my_name);
3596 goto def;
3597 }
3598 }
3599 }
3600
3601 my_printf_error(ER_UNKNOWN_ERROR, "Unknown OS character set '%s'.", MYF(0),
3602 csname);
3603
3604 def:
3605 csname = MYSQL_DEFAULT_CHARSET_NAME;
3606 my_printf_error(ER_UNKNOWN_ERROR,
3607 "Switching to the default character set '%s'.", MYF(0),
3608 csname);
3609 return csname;
3610 }
3611
3612 #ifndef _WIN32
3613 #include <stdlib.h> /* for getenv() */
3614 #ifdef HAVE_LANGINFO_H
3615 #include <langinfo.h>
3616 #endif
3617 #include <locale.h>
3618 #endif /* _WIN32 */
3619
mysql_autodetect_character_set(MYSQL * mysql)3620 static int mysql_autodetect_character_set(MYSQL *mysql) {
3621 const char *csname = MYSQL_DEFAULT_CHARSET_NAME;
3622
3623 #ifdef _WIN32
3624 char cpbuf[64];
3625 {
3626 snprintf(cpbuf, sizeof(cpbuf), "cp%d", (int)GetConsoleCP());
3627 csname = my_os_charset_to_mysql_charset(cpbuf);
3628 }
3629 #elif defined(HAVE_NL_LANGINFO)
3630 {
3631 if (setlocale(LC_CTYPE, "") && (csname = nl_langinfo(CODESET)))
3632 csname = my_os_charset_to_mysql_charset(csname);
3633 }
3634 #endif
3635
3636 if (mysql->options.charset_name) my_free(mysql->options.charset_name);
3637 if (!(mysql->options.charset_name =
3638 my_strdup(key_memory_mysql_options, csname, MYF(MY_WME))))
3639 return 1;
3640 return 0;
3641 }
3642
mysql_set_character_set_with_default_collation(MYSQL * mysql)3643 static void mysql_set_character_set_with_default_collation(MYSQL *mysql) {
3644 const char *save = charsets_dir;
3645 if (mysql->options.charset_dir) {
3646 #ifdef MYSQL_SERVER
3647 // Do not change charsets_dir, it is not thread safe.
3648 DBUG_ASSERT(false);
3649 #else
3650 charsets_dir = mysql->options.charset_dir;
3651 #endif
3652 }
3653 if ((mysql->charset = get_charset_by_csname(mysql->options.charset_name,
3654 MY_CS_PRIMARY, MYF(MY_WME)))) {
3655 /* Try to set compiled default collation when it's possible. */
3656 CHARSET_INFO *collation;
3657 if ((collation =
3658 get_charset_by_name(MYSQL_DEFAULT_COLLATION_NAME, MYF(MY_WME))) &&
3659 my_charset_same(mysql->charset, collation)) {
3660 mysql->charset = collation;
3661 } else {
3662 /*
3663 Default compiled collation not found, or is not applicable
3664 to the requested character set.
3665 Continue with the default collation of the character set.
3666 */
3667 }
3668 }
3669 charsets_dir = save;
3670 }
3671
mysql_init_character_set(MYSQL * mysql)3672 int mysql_init_character_set(MYSQL *mysql) {
3673 /* Set character set */
3674 if (!mysql->options.charset_name) {
3675 if (!(mysql->options.charset_name =
3676 my_strdup(key_memory_mysql_options, MYSQL_DEFAULT_CHARSET_NAME,
3677 MYF(MY_WME))))
3678 return 1;
3679 } else if (!strcmp(mysql->options.charset_name,
3680 MYSQL_AUTODETECT_CHARSET_NAME) &&
3681 mysql_autodetect_character_set(mysql))
3682 return 1;
3683
3684 mysql_set_character_set_with_default_collation(mysql);
3685
3686 if (!mysql->charset) {
3687 if (mysql->options.charset_dir)
3688 set_mysql_extended_error(mysql, CR_CANT_READ_CHARSET, unknown_sqlstate,
3689 ER_CLIENT(CR_CANT_READ_CHARSET),
3690 mysql->options.charset_name,
3691 mysql->options.charset_dir);
3692 else {
3693 char cs_dir_name[FN_REFLEN];
3694 get_charsets_dir(cs_dir_name);
3695 set_mysql_extended_error(mysql, CR_CANT_READ_CHARSET, unknown_sqlstate,
3696 ER_CLIENT(CR_CANT_READ_CHARSET),
3697 mysql->options.charset_name, cs_dir_name);
3698 }
3699 return 1;
3700 }
3701 return 0;
3702 }
3703
3704 /*********** client side authentication support **************************/
3705
3706 static int client_mpvio_write_packet(MYSQL_PLUGIN_VIO *, const uchar *, int);
3707 static net_async_status client_mpvio_write_packet_nonblocking(
3708 struct MYSQL_PLUGIN_VIO *, const uchar *, int, int *);
3709 static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql);
3710 static net_async_status native_password_auth_client_nonblocking(
3711 MYSQL_PLUGIN_VIO *vio, MYSQL *mysql, int *result);
3712 static int clear_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql);
3713
3714 static auth_plugin_t native_password_client_plugin = {
3715 MYSQL_CLIENT_AUTHENTICATION_PLUGIN,
3716 MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION,
3717 native_password_plugin_name,
3718 MYSQL_CLIENT_PLUGIN_AUTHOR_ORACLE,
3719 "Native MySQL authentication",
3720 {1, 0, 0},
3721 "GPL",
3722 nullptr,
3723 nullptr,
3724 nullptr,
3725 nullptr,
3726 native_password_auth_client,
3727 native_password_auth_client_nonblocking};
3728
3729 static auth_plugin_t clear_password_client_plugin = {
3730 MYSQL_CLIENT_AUTHENTICATION_PLUGIN,
3731 MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION,
3732 "mysql_clear_password",
3733 MYSQL_CLIENT_PLUGIN_AUTHOR_ORACLE,
3734 "Clear password authentication plugin",
3735 {0, 1, 0},
3736 "GPL",
3737 nullptr,
3738 nullptr,
3739 nullptr,
3740 nullptr,
3741 clear_password_auth_client,
3742 nullptr};
3743
3744 static auth_plugin_t sha256_password_client_plugin = {
3745 MYSQL_CLIENT_AUTHENTICATION_PLUGIN,
3746 MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION,
3747 "sha256_password",
3748 MYSQL_CLIENT_PLUGIN_AUTHOR_ORACLE,
3749 "SHA256 based authentication with salt",
3750 {1, 0, 0},
3751 "GPL",
3752 nullptr,
3753 sha256_password_init,
3754 sha256_password_deinit,
3755 nullptr,
3756 sha256_password_auth_client,
3757 sha256_password_auth_client_nonblocking};
3758
3759 static auth_plugin_t caching_sha2_password_client_plugin = {
3760 MYSQL_CLIENT_AUTHENTICATION_PLUGIN,
3761 MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION,
3762 caching_sha2_password_plugin_name,
3763 MYSQL_CLIENT_PLUGIN_AUTHOR_ORACLE,
3764 "SHA2 based authentication with salt",
3765 {1, 0, 0},
3766 "GPL",
3767 nullptr,
3768 caching_sha2_password_init,
3769 caching_sha2_password_deinit,
3770 nullptr,
3771 caching_sha2_password_auth_client,
3772 caching_sha2_password_auth_client_nonblocking};
3773 #ifdef AUTHENTICATION_WIN
3774 extern "C" auth_plugin_t win_auth_client_plugin;
3775 #endif
3776
3777 /*
3778 Test trace plugin can be used only in debug builds. In non-debug ones
3779 it is ignored, even if it was enabled by build options (TEST_TRACE_PLUGIN
3780 macro).
3781 */
3782
3783 #if defined(CLIENT_PROTOCOL_TRACING) && defined(TEST_TRACE_PLUGIN) && \
3784 !defined(DBUG_OFF)
3785 extern auth_plugin_t test_trace_plugin;
3786 #endif
3787
3788 struct st_mysql_client_plugin *mysql_client_builtins[] = {
3789 (struct st_mysql_client_plugin *)&native_password_client_plugin,
3790 (struct st_mysql_client_plugin *)&clear_password_client_plugin,
3791 (struct st_mysql_client_plugin *)&sha256_password_client_plugin,
3792 (struct st_mysql_client_plugin *)&caching_sha2_password_client_plugin,
3793 #ifdef AUTHENTICATION_WIN
3794 (struct st_mysql_client_plugin *)&win_auth_client_plugin,
3795 #endif
3796 #if defined(CLIENT_PROTOCOL_TRACING) && defined(TEST_TRACE_PLUGIN) && \
3797 !defined(DBUG_OFF)
3798 (struct st_mysql_client_plugin *)&test_trace_plugin,
3799 #endif
3800 nullptr};
3801
write_length_encoded_string3(uchar * buf,const char * string,size_t length)3802 static uchar *write_length_encoded_string3(uchar *buf, const char *string,
3803 size_t length) {
3804 buf = net_store_length(buf, length);
3805 memcpy(buf, string, length);
3806 buf += length;
3807 return buf;
3808 }
3809
3810 /*
3811 The main purpose of this is to hide C++ from st_mysql_options_extention.
3812 */
3813 struct My_hash {
3814 malloc_unordered_map<string, string> hash{key_memory_mysql_options};
3815 };
3816
send_client_connect_attrs(MYSQL * mysql,uchar * buf)3817 uchar *send_client_connect_attrs(MYSQL *mysql, uchar *buf) {
3818 /* check if the server supports connection attributes */
3819 if (mysql->server_capabilities & CLIENT_CONNECT_ATTRS) {
3820 /* Always store the length if the client supports it */
3821 buf = net_store_length(
3822 buf, mysql->options.extension
3823 ? mysql->options.extension->connection_attributes_length
3824 : 0);
3825
3826 /* check if we have connection attributes */
3827 if (mysql->options.extension &&
3828 mysql->options.extension->connection_attributes) {
3829 /* loop over and dump the connection attributes */
3830 for (const auto &key_and_value :
3831 mysql->options.extension->connection_attributes->hash) {
3832 const string &key = key_and_value.first;
3833 const string &value = key_and_value.second;
3834
3835 /* we can't have zero length keys */
3836 DBUG_ASSERT(!key.empty());
3837
3838 buf = write_length_encoded_string3(buf, key.data(), key.size());
3839 buf = write_length_encoded_string3(buf, value.data(), value.size());
3840 }
3841 }
3842 }
3843 return buf;
3844 }
3845
get_length_store_length(size_t length)3846 static size_t get_length_store_length(size_t length) {
3847 /* as defined in net_store_length */
3848 #define MAX_VARIABLE_STRING_LENGTH 9
3849 uchar length_buffer[MAX_VARIABLE_STRING_LENGTH], *ptr;
3850
3851 ptr = net_store_length(length_buffer, length);
3852
3853 return ptr - &length_buffer[0];
3854 }
3855
3856 /*
3857 Write 1-8 bytes of string length header infromation to dest depending on
3858 value of src_len, then copy src_len bytes from src to dest.
3859
3860 @param dest Destination buffer of size src_len+8
3861 @param dest_end One byte past the end of the dest buffer
3862 @param src Source buff of size src_len
3863 @param src_end One byte past the end of the src buffer
3864
3865 @return pointer dest+src_len+header size or NULL if
3866 */
3867
write_length_encoded_string4(char * dest,char * dest_end,const uchar * src,const uchar * src_end)3868 static char *write_length_encoded_string4(char *dest, char *dest_end,
3869 const uchar *src,
3870 const uchar *src_end) {
3871 size_t src_len = (size_t)(src_end - src);
3872 uchar *to = net_store_length((uchar *)dest, src_len);
3873 if ((char *)(to + src_len) >= dest_end) return nullptr;
3874 memcpy(to, src, src_len);
3875 return (char *)(to + src_len);
3876 }
3877
3878 /*
3879 Write 1 byte of string length header information to dest and
3880 copy src_len bytes from src to dest.
3881 */
write_string(char * dest,char * dest_end,const uchar * src,const uchar * src_end)3882 static char *write_string(char *dest, char *dest_end, const uchar *src,
3883 const uchar *src_end) {
3884 size_t src_len = (size_t)(src_end - src);
3885 uchar *to = nullptr;
3886 if (src_len >= 251) return nullptr;
3887 *dest = (uchar)src_len;
3888 to = (uchar *)dest + 1;
3889 if ((char *)(to + src_len) >= dest_end) return nullptr;
3890 memcpy(to, src, src_len);
3891 return (char *)(to + src_len);
3892 }
3893 /**
3894 Sends a @ref page_protocol_com_change_user
3895 with a caller provided payload
3896
3897 @retval 0 ok
3898 @retval 1 error
3899 */
send_change_user_packet(MCPVIO_EXT * mpvio,const uchar * data,int data_len)3900 static int send_change_user_packet(MCPVIO_EXT *mpvio, const uchar *data,
3901 int data_len) {
3902 MYSQL *mysql = mpvio->mysql;
3903 char *buff, *end;
3904 int res = 1;
3905 size_t connect_attrs_len =
3906 (mysql->server_capabilities & CLIENT_CONNECT_ATTRS &&
3907 mysql->options.extension)
3908 ? mysql->options.extension->connection_attributes_length
3909 : 0;
3910
3911 buff = static_cast<char *>(
3912 my_alloca(USERNAME_LENGTH + data_len + 1 + NAME_LEN + 2 + NAME_LEN +
3913 connect_attrs_len + 9 /* for the length of the attrs */));
3914
3915 end = strmake(buff, mysql->user, USERNAME_LENGTH) + 1;
3916
3917 if (!data_len)
3918 *end++ = 0;
3919 else {
3920 DBUG_ASSERT(data_len <= 255);
3921 if (data_len > 255) {
3922 set_mysql_error(mysql, CR_MALFORMED_PACKET, unknown_sqlstate);
3923 goto error;
3924 }
3925 *end++ = data_len;
3926 memcpy(end, data, data_len);
3927 end += data_len;
3928 }
3929 end = strmake(end, mpvio->db ? mpvio->db : "", NAME_LEN) + 1;
3930
3931 if (mysql->server_capabilities & CLIENT_PROTOCOL_41) {
3932 int2store((uchar *)end, (ushort)mysql->charset->number);
3933 end += 2;
3934 }
3935
3936 if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
3937 end = strmake(end, mpvio->plugin->name, NAME_LEN) + 1;
3938
3939 end = (char *)send_client_connect_attrs(mysql, (uchar *)end);
3940
3941 res = simple_command(mysql, COM_CHANGE_USER, (uchar *)buff,
3942 (ulong)(end - buff), 1);
3943
3944 error:
3945 return res;
3946 }
3947
3948 /* clang-format off */
3949 /**
3950 @page page_protocol_connection_phase_packets_protocol_ssl_request Protocol::SSLRequest:
3951
3952 SSL Connection Request Packet. It is like
3953 @ref page_protocol_connection_phase_packets_protocol_handshake_response but is
3954 truncated right before username field. If server supports ::CLIENT_SSL
3955 capability, client can send this packet to request a secure SSL connection.
3956 The ::CLIENT_SSL capability flag must be set inside the SSL Connection Request Packet.
3957
3958 <table>
3959 <caption>Payload</caption>
3960 <tr><th>Type</th><th>Name</th><th>Description</th></tr>
3961 <tr><td colspan="3">if capabilities @& ::CLIENT_PROTOCOL_41 {</td></tr>
3962 <tr><td>@ref a_protocol_type_int4 "int<4>"</td>
3963 <td>client_flag</td>
3964 <td>\ref group_cs_capabilities_flags</td></tr>
3965 <tr><td>@ref a_protocol_type_int4 "int<4>"</td>
3966 <td>max_packet_size</td>
3967 <td>maximum packet size</td></tr>
3968 <tr><td>@ref a_protocol_type_int1 "int<1>"</td>
3969 <td>character_set</td>
3970 <td>client charset \ref a_protocol_character_set, only the lower 8-bits</td></tr>
3971 <tr><td>@ref sect_protocol_basic_dt_string_fix "string[23]"</td>
3972 <td>filler</td>
3973 <td>filler to the size of the handhshake response packet. All 0s.</td></tr>
3974 <tr><td colspan="3">} else {</td></tr>
3975 <tr><td>@ref a_protocol_type_int2 "int<2>"</td>
3976 <td>client_flag</td>
3977 <td>\ref group_cs_capabilities_flags, only the lower 16 bits</td></tr>
3978 <tr><td>@ref a_protocol_type_int3 "int<3>"</td>
3979 <td>max_packet_size</td>
3980 <td>maximum packet size, 0xFFFFFF max</td></tr>
3981 <tr><td colspan="3">}</td></tr>
3982 </table>
3983
3984 @sa int2store(), int3store(), int4store(), mysql_fill_packet_header()
3985 */
3986 /* clang-format on */
3987 /**
3988 Fill in the beginning of the client reply packet.
3989
3990 Used to fill in the beginning of the client reply packet
3991 or the ssl request packet.
3992
3993 @param mysql The mysql handler to operate
3994 @param[out] buff The buffer to receive the packet
3995 @param buff_size The max size of the buffer. Used in debug only.
3996 @return one past to where the buffer is filled
3997
3998 @sa page_protocol_conn_packets_protocol_ssl_request
3999 send_client_reply_packet()
4000 */
mysql_fill_packet_header(MYSQL * mysql,char * buff,size_t buff_size MY_ATTRIBUTE ((unused)))4001 static char *mysql_fill_packet_header(MYSQL *mysql, char *buff,
4002 size_t buff_size MY_ATTRIBUTE((unused))) {
4003 NET *net = &mysql->net;
4004 char *end;
4005 uchar *buff_p = (uchar *)buff;
4006 /*
4007 Always send CLIENT_LOCAL_FILES to the server.
4008 This needs to be done since the client can always decide to support
4009 local files even if this option is disabled by enabling the directory.
4010 But we can't turn it on in the client flag since it's used throughout the
4011 code base if the option is enabled or not.
4012 */
4013 unsigned long client_flag = mysql->client_flag | CLIENT_LOCAL_FILES;
4014
4015 if (client_flag & CLIENT_PROTOCOL_41) {
4016 /* 4.1 server and 4.1 client has a 32 byte option flag */
4017 DBUG_ASSERT(buff_size >= 32);
4018
4019 int4store(buff_p, client_flag);
4020 int4store(buff_p + 4, net->max_packet_size);
4021 buff[8] = (char)mysql->charset->number;
4022 memset(buff + 9, 0, 32 - 9);
4023 end = buff + 32;
4024 } else {
4025 DBUG_ASSERT(buff_size >= 5);
4026 DBUG_ASSERT(client_flag <= UINT_MAX16);
4027
4028 int2store(buff_p, (uint16)client_flag);
4029 int3store(buff_p + 2, net->max_packet_size);
4030 end = buff + 5;
4031 }
4032 return end;
4033 }
4034
4035 /**
4036 Calcualtes client capabilities in effect (mysql->client_flag)
4037
4038 Needs to be called immediately after receiving the server handshake packet.
4039
4040 @param mysql the connection context
4041 @param db The database specified by the client app
4042 @param client_flag The client flag as specified by the client app
4043 */
4044
cli_calculate_client_flag(MYSQL * mysql,const char * db,ulong client_flag)4045 static void cli_calculate_client_flag(MYSQL *mysql, const char *db,
4046 ulong client_flag) {
4047 mysql->client_flag = client_flag;
4048 mysql->client_flag |= mysql->options.client_flag;
4049 mysql->client_flag |= CLIENT_CAPABILITIES;
4050
4051 if (mysql->client_flag & CLIENT_MULTI_STATEMENTS)
4052 mysql->client_flag |= CLIENT_MULTI_RESULTS;
4053
4054 if (mysql->options.extension &&
4055 mysql->options.extension->ssl_mode != SSL_MODE_DISABLED)
4056 mysql->client_flag |= CLIENT_SSL;
4057
4058 if (db)
4059 mysql->client_flag |= CLIENT_CONNECT_WITH_DB;
4060 else
4061 mysql->client_flag &= ~CLIENT_CONNECT_WITH_DB;
4062
4063 /* Remove options that server doesn't support */
4064 mysql->client_flag = mysql->client_flag &
4065 (~(CLIENT_COMPRESS | CLIENT_SSL | CLIENT_PROTOCOL_41 |
4066 CLIENT_OPTIONAL_RESULTSET_METADATA) |
4067 mysql->server_capabilities);
4068
4069 if (mysql->options.protocol == MYSQL_PROTOCOL_SOCKET &&
4070 mysql->options.extension &&
4071 mysql->options.extension->ssl_mode <= SSL_MODE_PREFERRED) {
4072 mysql->client_flag &= ~CLIENT_SSL;
4073 mysql->options.extension->ssl_mode = SSL_MODE_DISABLED;
4074 }
4075 }
4076
4077 /**
4078 Establishes SSL if requested and supported.
4079
4080 @param mysql the connection handle
4081 @retval 0 success
4082 @retval 1 failure
4083 */
cli_establish_ssl(MYSQL * mysql)4084 static int cli_establish_ssl(MYSQL *mysql) {
4085 NET *net = &mysql->net;
4086
4087 /* Don't fallback on unencrypted connection if SSL required. */
4088 if (mysql->options.extension &&
4089 mysql->options.extension->ssl_mode >= SSL_MODE_REQUIRED &&
4090 !(mysql->server_capabilities & CLIENT_SSL)) {
4091 set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate,
4092 ER_CLIENT(CR_SSL_CONNECTION_ERROR),
4093 "SSL is required but the server doesn't "
4094 "support it");
4095 goto error;
4096 }
4097
4098 /*
4099 If the ssl_mode is VERIFY_CA or VERIFY_IDENTITY, make sure that the
4100 connection doesn't succeed without providing the CA certificate.
4101 */
4102 if (mysql->options.extension &&
4103 mysql->options.extension->ssl_mode > SSL_MODE_REQUIRED &&
4104 !(mysql->options.ssl_ca || mysql->options.ssl_capath)) {
4105 set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate,
4106 ER_CLIENT(CR_SSL_CONNECTION_ERROR),
4107 "CA certificate is required if ssl-mode "
4108 "is VERIFY_CA or VERIFY_IDENTITY");
4109 goto error;
4110 }
4111
4112 /*
4113 Attempt SSL connection if ssl_mode != SSL_MODE_DISABLED and the
4114 server supports SSL. Fallback on unencrypted connection otherwise.
4115 */
4116 if (mysql->options.extension &&
4117 mysql->options.extension->ssl_mode != SSL_MODE_DISABLED &&
4118 (mysql->server_capabilities & CLIENT_SSL)) {
4119 /* Do the SSL layering. */
4120 struct st_mysql_options *options = &mysql->options;
4121 struct st_VioSSLFd *ssl_fd;
4122 enum enum_ssl_init_error ssl_init_error = SSL_INITERR_NOERROR;
4123 const char *cert_error;
4124 unsigned long ssl_error;
4125 char buff[33], *end;
4126 const bool verify_identity =
4127 mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT;
4128
4129 /* check if server supports compression else turn off client capability */
4130 if (!(mysql->server_capabilities & CLIENT_ZSTD_COMPRESSION_ALGORITHM))
4131 mysql->client_flag &= ~CLIENT_ZSTD_COMPRESSION_ALGORITHM;
4132 if (!(mysql->server_capabilities & CLIENT_COMPRESS))
4133 mysql->client_flag &= ~CLIENT_COMPRESS;
4134
4135 end = mysql_fill_packet_header(mysql, buff, sizeof(buff));
4136
4137 /*
4138 Send mysql->client_flag, max_packet_size - unencrypted otherwise
4139 the server does not know we want to do SSL
4140 */
4141 MYSQL_TRACE(SEND_SSL_REQUEST, mysql,
4142 ((size_t)(end - buff), (const unsigned char *)buff));
4143 if (my_net_write(net, (uchar *)buff, (size_t)(end - buff)) ||
4144 net_flush(net)) {
4145 set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
4146 ER_CLIENT(CR_SERVER_LOST_EXTENDED),
4147 "sending connection information to server",
4148 errno);
4149 goto error;
4150 }
4151
4152 MYSQL_TRACE_STAGE(mysql, SSL_NEGOTIATION);
4153
4154 /* Create the VioSSLConnectorFd - init SSL and load certs */
4155 if (!(ssl_fd = new_VioSSLConnectorFd(
4156 options->ssl_key, options->ssl_cert, options->ssl_ca,
4157 options->ssl_capath, options->ssl_cipher,
4158 options->extension ? options->extension->tls_ciphersuites
4159 : nullptr,
4160 &ssl_init_error,
4161 options->extension ? options->extension->ssl_crl : nullptr,
4162 options->extension ? options->extension->ssl_crlpath : nullptr,
4163 options->extension ? options->extension->ssl_ctx_flags : 0,
4164 verify_identity ? mysql->host : nullptr))) {
4165 set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate,
4166 ER_CLIENT(CR_SSL_CONNECTION_ERROR),
4167 sslGetErrString(ssl_init_error));
4168 goto error;
4169 }
4170 mysql->connector_fd = (unsigned char *)ssl_fd;
4171
4172 /* Connect to the server */
4173 DBUG_PRINT("info", ("IO layer change in progress..."));
4174 MYSQL_TRACE(SSL_CONNECT, mysql, ());
4175 if (sslconnect(ssl_fd, net->vio, (long)(mysql->options.connect_timeout),
4176 &ssl_error, nullptr)) {
4177 char buf[512];
4178 ERR_error_string_n(ssl_error, buf, 512);
4179 buf[511] = 0;
4180 set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate,
4181 ER_CLIENT(CR_SSL_CONNECTION_ERROR), buf);
4182 goto error;
4183 }
4184 DBUG_PRINT("info", ("IO layer change done!"));
4185
4186 /* Verify server cert */
4187 if (verify_identity &&
4188 ssl_verify_server_cert(net->vio, mysql->host, &cert_error)) {
4189 set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate,
4190 ER_CLIENT(CR_SSL_CONNECTION_ERROR), cert_error);
4191 goto error;
4192 }
4193
4194 MYSQL_TRACE(SSL_CONNECTED, mysql, ());
4195 MYSQL_TRACE_STAGE(mysql, AUTHENTICATE);
4196 }
4197
4198 return 0;
4199
4200 error:
4201 return 1;
4202 }
4203
4204 /**
4205 This function will establish asynchronous ssl connection by completing 4
4206 different ssl connection states. Initial state is set to SSL_NONE during
4207 which this functions does priliminary checks like if server supports ssl
4208 or not, if CA certificate is required etc. Once preliminary checks are
4209 done state is changed to SSL_REQUEST. In this state ssl request packet
4210 is sent by client. If this network IO is complete, state is changed to
4211 SSL_CONNECT. During SSL_CONNECT sslconnect() is called which can return
4212 immediately or complete SSL handshake. If it returns immediately client
4213 will save all SSL context in struct mysql_async_auth, so that next call
4214 to this function will ensure that SSL_new() is not called twice. Once
4215 ssl connection is established state is changed to SSL_COMPLETE.
4216
4217 @param[in] mysql Client connection handle.
4218 @param[out] res set to false incase of success and true for
4219 error.
4220
4221 @retval NET_ASYNC_NOT_READY ssl connection not yet established
4222 @retval NET_ASYNC_COMPLETE ssl connection established
4223 */
cli_establish_ssl_nonblocking(MYSQL * mysql,int * res)4224 static net_async_status cli_establish_ssl_nonblocking(MYSQL *mysql, int *res) {
4225 DBUG_TRACE;
4226 NET *net = &mysql->net;
4227 NET_ASYNC *net_async = NET_ASYNC_DATA(net);
4228 mysql_async_connect *ctx = ASYNC_DATA(mysql)->connect_context;
4229
4230 if (ctx->ssl_state == SSL_NONE) {
4231 /* Don't fallback on unencrypted connection if SSL required. */
4232 if (mysql->options.extension &&
4233 mysql->options.extension->ssl_mode >= SSL_MODE_REQUIRED &&
4234 !(mysql->server_capabilities & CLIENT_SSL)) {
4235 set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate,
4236 ER_CLIENT(CR_SSL_CONNECTION_ERROR),
4237 "SSL is required but the server doesn't "
4238 "support it");
4239 goto error;
4240 }
4241
4242 /*
4243 If the ssl_mode is VERIFY_CA or VERIFY_IDENTITY, make sure
4244 that the connection doesn't succeed without providing the
4245 CA certificate.
4246 */
4247 if (mysql->options.extension &&
4248 mysql->options.extension->ssl_mode > SSL_MODE_REQUIRED &&
4249 !(mysql->options.ssl_ca || mysql->options.ssl_capath)) {
4250 set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate,
4251 ER_CLIENT(CR_SSL_CONNECTION_ERROR),
4252 "CA certificate is required if ssl-mode "
4253 "is VERIFY_CA or VERIFY_IDENTITY");
4254 goto error;
4255 }
4256
4257 /*
4258 Attempt SSL connection if ssl_mode != SSL_MODE_DISABLED and
4259 the server supports SSL. Fallback on unencrypted
4260 connection otherwise.
4261 */
4262 if (!mysql->options.extension ||
4263 mysql->options.extension->ssl_mode == SSL_MODE_DISABLED ||
4264 !(mysql->server_capabilities & CLIENT_SSL)) {
4265 goto done;
4266 }
4267 ctx->ssl_state = SSL_REQUEST;
4268 }
4269
4270 if (ctx->ssl_state == SSL_REQUEST) {
4271 char buff[33], *end;
4272
4273 end = mysql_fill_packet_header(mysql, buff, sizeof(buff));
4274
4275 /*
4276 Send mysql->client_flag, max_packet_size - unencrypted
4277 otherwise the server does not know we want to do SSL
4278 */
4279 MYSQL_TRACE(SEND_SSL_REQUEST, mysql,
4280 ((size_t)(end - buff), (const unsigned char *)buff));
4281 bool ret;
4282 if (my_net_write_nonblocking(net, (uchar *)buff, (size_t)(end - buff),
4283 &ret) == NET_ASYNC_NOT_READY) {
4284 return NET_ASYNC_NOT_READY;
4285 }
4286
4287 if (ret) {
4288 set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
4289 ER_CLIENT(CR_SERVER_LOST_EXTENDED),
4290 "sending connection information to server",
4291 errno);
4292 goto error;
4293 }
4294
4295 ctx->ssl_state = SSL_CONNECT;
4296 }
4297
4298 if (ctx->ssl_state == SSL_CONNECT) {
4299 /* Do the SSL layering. */
4300 struct st_mysql_options *options = &mysql->options;
4301 struct st_VioSSLFd *ssl_fd;
4302 enum enum_ssl_init_error ssl_init_error;
4303 const char *cert_error;
4304 unsigned long ssl_error;
4305 size_t ret;
4306 const bool verify_identity =
4307 mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT;
4308
4309 MYSQL_TRACE_STAGE(mysql, SSL_NEGOTIATION);
4310
4311 if (!mysql->connector_fd) {
4312 /* Create the VioSSLConnectorFd - init SSL and load certs */
4313 if (!(ssl_fd = new_VioSSLConnectorFd(
4314 options->ssl_key, options->ssl_cert, options->ssl_ca,
4315 options->ssl_capath, options->ssl_cipher,
4316 options->extension ? options->extension->tls_ciphersuites
4317 : nullptr,
4318 &ssl_init_error,
4319 options->extension ? options->extension->ssl_crl : nullptr,
4320 options->extension ? options->extension->ssl_crlpath : nullptr,
4321 options->extension ? options->extension->ssl_ctx_flags : 0,
4322 verify_identity ? mysql->host : nullptr))) {
4323 set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR,
4324 unknown_sqlstate,
4325 ER_CLIENT(CR_SSL_CONNECTION_ERROR),
4326 sslGetErrString(ssl_init_error));
4327 goto error;
4328 }
4329 mysql->connector_fd = (unsigned char *)ssl_fd;
4330 } else {
4331 ssl_fd = (struct st_VioSSLFd *)mysql->connector_fd;
4332 }
4333
4334 /* Connect to the server */
4335 DBUG_PRINT("info", ("IO layer change in progress..."));
4336 MYSQL_TRACE(SSL_CONNECT, mysql, ());
4337 if ((ret = sslconnect(ssl_fd, net->vio,
4338 (long)(mysql->options.connect_timeout), &ssl_error,
4339 &ctx->ssl))) {
4340 switch (ret) {
4341 case VIO_SOCKET_WANT_READ:
4342 net_async->async_blocking_state = NET_NONBLOCKING_READ;
4343 return NET_ASYNC_NOT_READY;
4344 case VIO_SOCKET_WANT_WRITE:
4345 net_async->async_blocking_state = NET_NONBLOCKING_WRITE;
4346 return NET_ASYNC_NOT_READY;
4347 default:
4348 break;
4349 /* continue for error handling */
4350 }
4351
4352 char buf[512];
4353 ERR_error_string_n(ssl_error, buf, 512);
4354 buf[511] = 0;
4355 set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate,
4356 ER_CLIENT(CR_SSL_CONNECTION_ERROR), buf);
4357 goto error;
4358 }
4359 DBUG_PRINT("info", ("IO layer change done!"));
4360
4361 /* sslconnect creates a new vio, so update it. */
4362 vio_set_blocking_flag(net->vio, !ctx->non_blocking);
4363
4364 /* Verify server cert */
4365 if (verify_identity &&
4366 ssl_verify_server_cert(net->vio, mysql->host, &cert_error)) {
4367 set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate,
4368 ER_CLIENT(CR_SSL_CONNECTION_ERROR), cert_error);
4369 goto error;
4370 }
4371
4372 MYSQL_TRACE(SSL_CONNECTED, mysql, ());
4373 MYSQL_TRACE_STAGE(mysql, AUTHENTICATE);
4374 }
4375
4376 done:
4377 *res = 0;
4378 ctx->ssl_state = SSL_COMPLETE;
4379 return NET_ASYNC_COMPLETE;
4380
4381 error:
4382 *res = 1;
4383 ctx->ssl_state = SSL_COMPLETE;
4384 return NET_ASYNC_COMPLETE;
4385 }
4386
4387 /**
4388 Asynchronous authentication phase is divided into several smaller chunks
4389 of subtasks like:
4390 1. Determine the default/initial plugin to use
4391 2. Call authentication plugin API
4392 3. Handle response from authentication plugin API
4393 4. Check if server asked to use a different authentication plugin
4394 5. In case server asked to use a different authentication plugin
4395 use that plugin to start the authentication process again.
4396 6. Complete authentication.
4397
4398 All above tasks are implemented in below authsm_* functions where
4399 authsm stads for authentication state machine.
4400 */
4401 static mysql_state_machine_status authsm_begin_plugin_auth(
4402 mysql_async_auth *ctx);
4403 static mysql_state_machine_status authsm_run_first_authenticate_user(
4404 mysql_async_auth *ctx);
4405 static mysql_state_machine_status authsm_handle_first_authenticate_user(
4406 mysql_async_auth *ctx);
4407 static mysql_state_machine_status authsm_read_change_user_result(
4408 mysql_async_auth *ctx);
4409 static mysql_state_machine_status authsm_handle_change_user_result(
4410 mysql_async_auth *ctx);
4411 static mysql_state_machine_status authsm_run_second_authenticate_user(
4412 mysql_async_auth *ctx);
4413 static mysql_state_machine_status authsm_handle_second_authenticate_user(
4414 mysql_async_auth *ctx);
4415 static mysql_state_machine_status authsm_finish_auth(mysql_async_auth *ctx);
4416
4417 /**
4418 Asynchronous connection phase is divided into several smaller modules
4419 where wach module does following:
4420 1. Begin the connection to the server, including any DNS resolution
4421 necessary, socket configuration, etc
4422 2. Complete the connection itself
4423 3. Connection established, read the first packet
4424 4. Parse the handshake from the server
4425 5. Establish SSL connection if needed
4426 6. Invoke the plugin to send the authentication data to the server
4427 7. Authenticated, set intial database if specified
4428 8. Send COM_INIT_DB.
4429 9. Prepare to send a sequence of init commands.
4430 10.Send an init command.
4431
4432 Below are the modules which does all above tasks.
4433 */
4434 static mysql_state_machine_status csm_begin_connect(mysql_async_connect *ctx);
4435 static mysql_state_machine_status csm_complete_connect(
4436 mysql_async_connect *ctx);
4437 static mysql_state_machine_status csm_wait_connect(mysql_async_connect *ctx);
4438 static mysql_state_machine_status csm_read_greeting(mysql_async_connect *ctx);
4439 static mysql_state_machine_status csm_parse_handshake(mysql_async_connect *ctx);
4440 static mysql_state_machine_status csm_establish_ssl(mysql_async_connect *ctx);
4441 static mysql_state_machine_status csm_authenticate(mysql_async_connect *ctx);
4442 static mysql_state_machine_status csm_prep_select_database(
4443 mysql_async_connect *ctx);
4444 #ifndef MYSQL_SERVER
4445 static mysql_state_machine_status csm_prep_init_commands(
4446 mysql_async_connect *ctx);
4447 static mysql_state_machine_status csm_send_one_init_command(
4448 mysql_async_connect *ctx);
4449 #endif
4450
4451 #define MAX_CONNECTION_ATTR_STORAGE_LENGTH 65536
4452
mysql_get_socket_descriptor(MYSQL * mysql)4453 int mysql_get_socket_descriptor(MYSQL *mysql) {
4454 if (mysql && mysql->net.vio) {
4455 return vio_fd(mysql->net.vio);
4456 }
4457 return -1;
4458 }
4459
4460 /* clang-format off */
4461 /**
4462 @page page_protocol_connection_phase_packets_protocol_handshake_response Protocol::HandshakeResponse:
4463
4464 Depending on the servers support for the ::CLIENT_PROTOCOL_41 capability and
4465 the clients understanding of that flag the client has to send either
4466 a @ref sect_protocol_connection_phase_packets_protocol_handshake_response320 or
4467 @ref sect_protocol_connection_phase_packets_protocol_handshake_response41.
4468
4469 @sa send_client_reply_packet
4470
4471 @section sect_protocol_connection_phase_packets_protocol_handshake_response320 Protocol::HandshakeResponse320
4472
4473 Old Handshake Response Packet used by old clients or if the server doesn't
4474 support ::CLIENT_PROTOCOL_41 @ref group_cs_capabilities_flags flag.
4475
4476 <table>
4477 <caption>Payload</caption>
4478 <tr><th>Type</th><th>Name</th><th>Description</th></tr>
4479 <tr><td>@ref a_protocol_type_int2 "int<2>"</td>
4480 <td>client_flag</td>
4481 <td>\ref group_cs_capabilities_flags, only the lower 16 bits. ::CLIENT_PROTOCOL_41 should never be set</td></tr>
4482 <tr><td>@ref a_protocol_type_int3 "int<3>"</td>
4483 <td>max_packet_size</td>
4484 <td>maximum packet size, 0xFFFFFF max</td></tr>
4485 <tr><td>@ref sect_protocol_basic_dt_string_null "string<NUL>"</td>
4486 <td>username</td>
4487 <td>login user name</td></tr>
4488 <tr><td colspan="3">if capabilities @& ::CLIENT_CONNECT_WITH_DB {</td></tr>
4489 <tr><td>@ref sect_protocol_basic_dt_string_null "string<NUL>"</td>
4490 <td>auth-response</td>
4491 <td>Opaque authentication response data generated by
4492 Authentication Method indicated by the plugin name field.</td></tr>
4493 <tr><td>@ref sect_protocol_basic_dt_string_null "string<NUL>"</td>
4494 <td>database</td>
4495 <td>initail database for the connection.
4496 This string should be interpreted using the character set indicated by
4497 character set field.</td></tr>
4498 <tr><td colspan="3">} else {</td></tr>
4499 <tr><td>@ref sect_protocol_basic_dt_string_eof "string<EOF>"</td>
4500 <td>auth-response</td>
4501 <td>Opaque authentication response data generated by
4502 Authentication Method indicated by the plugin name field.</td></tr>
4503 <tr><td colspan="3">}</td></tr>
4504 </table>
4505
4506 Example
4507 ========
4508
4509 ~~~~~~~~~~~~~~~~~~~~~
4510 11 00 00 01 85 24 00 00 00 6f 6c 64 00 47 44 53 .....$...old.GDS
4511 43 51 59 52 5f CQYR_
4512 ~~~~~~~~~~~~~~~~~~~~~
4513
4514 @note If auth-response is followed by a database field it must be
4515 NULL terminated.
4516
4517 @section sect_protocol_connection_phase_packets_protocol_handshake_response41 Protocol::HandshakeResponse41
4518
4519 Handshake Response Packet sent by 4.1+ clients supporting
4520 ::CLIENT_PROTOCOL_41 @ref group_cs_capabilities_flags flag,
4521 if the server announced it in its
4522 @ref page_protocol_connection_phase_packets_protocol_handshake.
4523 Otherwise (talking to an old server) the
4524 @ref sect_protocol_connection_phase_packets_protocol_handshake_response320
4525 packet must be used.
4526
4527
4528 <table>
4529 <caption>Payload</caption>
4530 <tr><th>Type</th><th>Name</th><th>Description</th></tr>
4531 <tr><td>@ref a_protocol_type_int4 "int<4>"</td>
4532 <td>client_flag</td>
4533 <td>\ref group_cs_capabilities_flags, ::CLIENT_PROTOCOL_41 always set.</td></tr>
4534 <tr><td>@ref a_protocol_type_int4 "int<4>"</td>
4535 <td>max_packet_size</td>
4536 <td>maximum packet size</td></tr>
4537 <tr><td>@ref a_protocol_type_int1 "int<1>"</td>
4538 <td>character_set</td>
4539 <td>client charset \ref a_protocol_character_set, only the lower 8-bits</td></tr>
4540 <tr><td>@ref sect_protocol_basic_dt_string_fix "string[23]"</td>
4541 <td>filler</td>
4542 <td>filler to the size of the handhshake response packet. All 0s.</td></tr>
4543 <tr><td>@ref sect_protocol_basic_dt_string_null "string<NUL>"</td>
4544 <td>username</td>
4545 <td>login user name</td></tr>
4546 <tr><td colspan="3">if capabilities @& ::CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA {</td></tr>
4547 <tr><td>@ref sect_protocol_basic_dt_string_le "string<length>"</td>
4548 <td>auth_response</td>
4549 <td>opaque authentication response data generated by
4550 Authentication Method indicated by the plugin name field. </td></tr>
4551 <tr><td colspan="3">} else {</td></tr>
4552 <tr><td>@ref a_protocol_type_int1 "int<1>"</td>
4553 <td>auth_response_length</td>
4554 <td>length of auth_response</td></tr>
4555 <tr><td>@ref sect_protocol_basic_dt_string_le "string<length>"</td>
4556 <td>auth_response</td>
4557 <td>opaque authentication response data generated by
4558 Authentication Method indicated by the plugin name field. </td></tr>
4559 <tr><td colspan="3">}</td></tr>
4560 <tr><td colspan="3">if capabilities @& ::CLIENT_CONNECT_WITH_DB {</td></tr>
4561 <tr><td>@ref sect_protocol_basic_dt_string_null "string<NUL>"</td>
4562 <td>database</td>
4563 <td>initail database for the connection.
4564 This string should be interpreted using the character set indicated by
4565 character set field.</td></tr>
4566 <tr><td colspan="3">}</td></tr>
4567 <tr><td colspan="3">if capabilities @& ::CLIENT_PLUGIN_AUTH {</td></tr>
4568 <tr><td>@ref sect_protocol_basic_dt_string_null "string<NUL>"</td>
4569 <td>client_plugin_name</td>
4570 <td>the Authentication Method used by the client to generate
4571 auth-response value in this packet. This is an UTF-8 string. </td></tr>
4572 <tr><td colspan="3">}</td></tr>
4573 <tr><td colspan="3">if capabilities @& ::CLIENT_CONNECT_ATTRS {</td></tr>
4574 <tr><td>@ref sect_protocol_basic_dt_int_le "int<lenenc>"</td>
4575 <td>length of all key-values</td>
4576 <td>affected rows</td></tr>
4577 <tr><td>@ref sect_protocol_basic_dt_string_le "string<lenenc>"</td>
4578 <td>key1</td>
4579 <td>Name of the 1st client attribute</td></tr>
4580 <tr><td>@ref sect_protocol_basic_dt_string_le "string<lenenc>"</td>
4581 <td>value1</td>
4582 <td>Value of the 1st client attribute</td></tr>
4583 <tr><td colspan="3">.. (if more data in length of all key-values, more keys and values parts)</td></tr>
4584 <tr><td colspan="3">}</td></tr>
4585 <tr><td>@ref a_protocol_type_int1 "int<1>"</td>
4586 <td>zstd_compression_level</td>
4587 <td>compression level for zstd compression algorithm</td></tr>
4588 </table>
4589
4590 Example
4591 ========
4592
4593 On MySQL 5.5.8 with ::CLIENT_PROTOCOL_41 ::CLIENT_PLUGIN_AUTH, CLIENT_SECURE_CONNECTION (removed in 8.0),
4594 and ::CLIENT_CONNECT_WITH_DB set, it may look like:
4595
4596 ~~~~~~~~~~~~~~~~~~~~~
4597 54 00 00 01 8d a6 0f 00 00 00 00 01 08 00 00 00 T...............
4598 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
4599 00 00 00 00 70 61 6d 00 14 ab 09 ee f6 bc b1 32 ....pam........2
4600 3e 61 14 38 65 c0 99 1d 95 7d 75 d4 47 74 65 73 >a.8e....}u.Gtes
4601 74 00 6d 79 73 71 6c 5f 6e 61 74 69 76 65 5f 70 t.mysql_native_p
4602 61 73 73 77 6f 72 64 00 assword.
4603 ~~~~~~~~~~~~~~~~~~~~~
4604
4605 Starting with MySQL 5.6.6 the client may send attributes
4606 if ::CLIENT_CONNECT_ATTRS is set:
4607
4608 ~~~~~~~~~~~~~~~~~~~~~
4609 b2 00 00 01 85 a2 1e 00 00 00 00 40 08 00 00 00 ...........@....
4610 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
4611 00 00 00 00 72 6f 6f 74 00 14 22 50 79 a2 12 d4 ....root.."Py...
4612 e8 82 e5 b3 f4 1a 97 75 6b c8 be db 9f 80 6d 79 .......uk.....my
4613 73 71 6c 5f 6e 61 74 69 76 65 5f 70 61 73 73 77 sql_native_passw
4614 6f 72 64 00 61 03 5f 6f 73 09 64 65 62 69 61 6e ord.a._os.debian
4615 36 2e 30 0c 5f 63 6c 69 65 6e 74 5f 6e 61 6d 65 6.0._client_name
4616 08 6c 69 62 6d 79 73 71 6c 04 5f 70 69 64 05 32 .libmysql._pid.2
4617 32 33 34 34 0f 5f 63 6c 69 65 6e 74 5f 76 65 72 2344._client_ver
4618 73 69 6f 6e 08 35 2e 36 2e 36 2d 6d 39 09 5f 70 sion.5.6.6-m9._p
4619 6c 61 74 66 6f 72 6d 06 78 38 36 5f 36 34 03 66 latform.x86_64.f
4620 6f 6f 03 62 61 72 oo.bar
4621 ~~~~~~~~~~~~~~~~~~~~~
4622
4623 @warning Currently, multibyte character sets such as UCS2, UTF16 and
4624 UTF32 are not supported.
4625
4626 @note If client wants to have a secure SSL connection and sets
4627 CLIENT_SSL flag it should first send the
4628 @ref page_protocol_connection_phase_packets_protocol_ssl_request packet
4629 and only then, after establishing the secure connection, it should send
4630 the @ref page_protocol_connection_phase_packets_protocol_handshake_response
4631 packet.
4632 */
4633 /* clang-format on */
4634
4635 /**
4636 sends a client authentication packet (second packet in the 3-way handshake)
4637
4638 @param mpvio The connection to use
4639 @param data The scramble to send
4640 @param data_len Length of data
4641 @param buff_out Buffer holding client handshake packet
4642 @param buff_len Length of buffer holding client handshake packet
4643 @retval 0 ok
4644 @retval 1 error
4645
4646 @sa mysql_fill_packet_header()
4647 page_protocol_conn_packets_protocol_handshake_response
4648 */
prep_client_reply_packet(MCPVIO_EXT * mpvio,const uchar * data,int data_len,char ** buff_out,int * buff_len)4649 static bool prep_client_reply_packet(MCPVIO_EXT *mpvio, const uchar *data,
4650 int data_len, char **buff_out,
4651 int *buff_len) {
4652 DBUG_TRACE;
4653 MYSQL *mysql = mpvio->mysql;
4654 char *buff, *end;
4655 size_t buff_size;
4656 size_t connect_attrs_len =
4657 (mysql->server_capabilities & CLIENT_CONNECT_ATTRS &&
4658 mysql->options.extension)
4659 ? mysql->options.extension->connection_attributes_length
4660 : 0;
4661 unsigned int compress_level = 0;
4662 bool server_zstd =
4663 (mysql->server_capabilities & CLIENT_ZSTD_COMPRESSION_ALGORITHM);
4664 bool client_zstd =
4665 (mysql->options.client_flag & CLIENT_ZSTD_COMPRESSION_ALGORITHM);
4666
4667 /* validate compression configuration */
4668 ENSURE_EXTENSIONS_PRESENT(&mysql->options);
4669 if (mysql->options.extension->compression_algorithm) {
4670 std::string algorithm = mysql->options.extension->compression_algorithm;
4671 if (!algorithm.empty() &&
4672 validate_compression_attributes(algorithm, std::string(), true)) {
4673 set_mysql_error(mysql, CR_COMPRESSION_WRONGLY_CONFIGURED,
4674 unknown_sqlstate);
4675 return true;
4676 }
4677 }
4678
4679 /**
4680 If server/client is configured to use zstd compression then set compression
4681 level if specified, else set level to a default value.
4682 */
4683 if (server_zstd && client_zstd) {
4684 if (mysql->options.extension &&
4685 mysql->options.extension->zstd_compression_level)
4686 compress_level = mysql->options.extension->zstd_compression_level;
4687 else
4688 compress_level = default_zstd_compression_level;
4689 }
4690 /* Remove those compression capabilities that server does not support. */
4691 if (!(mysql->server_capabilities & CLIENT_COMPRESS))
4692 mysql->client_flag &= ~CLIENT_COMPRESS;
4693 if (!(mysql->server_capabilities & CLIENT_ZSTD_COMPRESSION_ALGORITHM))
4694 mysql->client_flag &= ~CLIENT_ZSTD_COMPRESSION_ALGORITHM;
4695 /*
4696 If server and client have no compression algorithms in common, we must
4697 fall back to uncompressed. In that case, check that uncompressed is
4698 allowed by client.
4699 */
4700 if (!(mysql->client_flag & CLIENT_COMPRESS) &&
4701 !(mysql->client_flag & CLIENT_ZSTD_COMPRESSION_ALGORITHM) &&
4702 mysql->options.extension->connection_compressed) {
4703 set_mysql_error(mysql, CR_COMPRESSION_WRONGLY_CONFIGURED, unknown_sqlstate);
4704 return true;
4705 }
4706 DBUG_ASSERT(connect_attrs_len < MAX_CONNECTION_ATTR_STORAGE_LENGTH);
4707
4708 *buff_out = nullptr;
4709 *buff_len = 0;
4710
4711 /*
4712 Fixed size of the packet is 32 bytes. See mysql_fill_packet_header.
4713 +9 because data is a length encoded binary where meta data size is max 9.
4714 */
4715 buff_size = 33 + USERNAME_LENGTH + data_len + 9 + NAME_LEN + NAME_LEN +
4716 connect_attrs_len + 9 + ((server_zstd && client_zstd) ? 1 : 0);
4717
4718 buff = static_cast<char *>(
4719 my_malloc(PSI_NOT_INSTRUMENTED, buff_size, MYF(MY_WME | MY_ZEROFILL)));
4720
4721 /* The client_flags is already calculated. Just fill in the packet header */
4722 end = mysql_fill_packet_header(mysql, buff, buff_size);
4723
4724 DBUG_PRINT(
4725 "info",
4726 ("Server version = '%s' capabilites: %lu status: %u client_flag: %lu",
4727 mysql->server_version, mysql->server_capabilities, mysql->server_status,
4728 mysql->client_flag));
4729
4730 static_assert(MYSQL_USERNAME_LENGTH == USERNAME_LENGTH, "");
4731
4732 /* This needs to be changed as it's not useful with big packets */
4733 if (mysql->user[0])
4734 strmake(end, mysql->user, USERNAME_LENGTH);
4735 else {
4736 #if defined(KERBEROS_LIB_CONFIGURED)
4737 /*
4738 Kerberos user name should have already read inside LDAP SASL client
4739 plugin. If it is still empty we should return error.
4740 */
4741 if (strcmp(mpvio->plugin->name, "authentication_ldap_sasl_client") == 0) {
4742 if (!mysql->user[0]) {
4743 set_mysql_error(mysql, CR_KERBEROS_USER_NOT_FOUND, unknown_sqlstate);
4744 return true;
4745 }
4746 } else
4747 #endif
4748 read_user_name(end);
4749 }
4750
4751 /* We have to handle different version of handshake here */
4752 DBUG_PRINT("info", ("user: %s", end));
4753 end = strend(end) + 1;
4754 if (data_len) {
4755 /*
4756 Since the older versions of server do not have
4757 CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA capability,
4758 a check is performed on this before sending auth data.
4759 If lenenc support is not available, the data is sent
4760 in the format of first byte representing the length of
4761 the string followed by the actual string.
4762 */
4763 if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA)
4764 end = write_length_encoded_string4(end, (char *)(buff + buff_size), data,
4765 data + data_len);
4766 else
4767 end =
4768 write_string(end, (char *)(buff + buff_size), data, data + data_len);
4769 if (end == nullptr) goto error;
4770 } else
4771 *end++ = 0;
4772
4773 /* Add database if needed */
4774 if (mpvio->db && (mysql->server_capabilities & CLIENT_CONNECT_WITH_DB)) {
4775 end = strmake(end, mpvio->db, NAME_LEN) + 1;
4776 mysql->db = my_strdup(key_memory_MYSQL, mpvio->db, MYF(MY_WME));
4777 }
4778
4779 if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
4780 end = strmake(end, mpvio->plugin->name, NAME_LEN) + 1;
4781
4782 end = (char *)send_client_connect_attrs(mysql, (uchar *)end);
4783
4784 if (server_zstd && client_zstd) {
4785 /* send compression level if both client and server support it */
4786 *end = static_cast<unsigned char>(compress_level);
4787 end++;
4788 }
4789
4790 *buff_out = buff;
4791 *buff_len = end - buff;
4792
4793 return false;
4794
4795 error:
4796 my_free(buff);
4797 return true;
4798 }
4799
send_client_reply_packet(MCPVIO_EXT * mpvio,const uchar * data,int data_len)4800 static int send_client_reply_packet(MCPVIO_EXT *mpvio, const uchar *data,
4801 int data_len) {
4802 DBUG_TRACE;
4803 MYSQL *mysql = mpvio->mysql;
4804 NET *net = &mysql->net;
4805 char *buff = nullptr, *end = nullptr;
4806 int buff_len;
4807 int ret = 0;
4808 bool prep_err;
4809
4810 prep_err = prep_client_reply_packet(mpvio, data, data_len, &buff, &buff_len);
4811 if (prep_err) {
4812 return 1;
4813 }
4814
4815 end = buff + buff_len;
4816 /* Write authentication package */
4817 MYSQL_TRACE(SEND_AUTH_RESPONSE, mysql,
4818 ((size_t)(end - buff), (const unsigned char *)buff));
4819 if (my_net_write(net, (uchar *)buff, (size_t)(end - buff)) ||
4820 net_flush(net)) {
4821 set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
4822 ER_CLIENT(CR_SERVER_LOST_EXTENDED),
4823 "sending authentication information", errno);
4824 ret = 1;
4825 }
4826 MYSQL_TRACE(PACKET_SENT, mysql, ((size_t)(end - buff)));
4827 my_free(buff);
4828 return ret;
4829 }
4830
send_client_reply_packet_nonblocking(MCPVIO_EXT * mpvio,const uchar * pkt,int pkt_len,bool * result)4831 static net_async_status send_client_reply_packet_nonblocking(MCPVIO_EXT *mpvio,
4832 const uchar *pkt,
4833 int pkt_len,
4834 bool *result) {
4835 DBUG_TRACE;
4836 MYSQL *mysql = mpvio->mysql;
4837 mysql_async_auth *ctx = ASYNC_DATA(mysql)->connect_context->auth_context;
4838 net_async_status status;
4839
4840 bool error = false;
4841 if (!ctx->change_user_buff) {
4842 error =
4843 prep_client_reply_packet(mpvio, pkt, pkt_len, &ctx->change_user_buff,
4844 &ctx->change_user_buff_len);
4845 if (error) {
4846 goto end;
4847 }
4848 }
4849
4850 status = my_net_write_nonblocking(&mysql->net, (uchar *)ctx->change_user_buff,
4851 ctx->change_user_buff_len, &error);
4852
4853 if (status == NET_ASYNC_NOT_READY) {
4854 return NET_ASYNC_NOT_READY;
4855 }
4856
4857 end:
4858 *result = error;
4859 my_free(ctx->change_user_buff);
4860 ctx->change_user_buff = nullptr;
4861
4862 return NET_ASYNC_COMPLETE;
4863 }
4864
4865 #ifdef __clang__
4866 // Clang UBSAN false positive?
4867 // Call to function through pointer to incorrect function type
4868 static int client_mpvio_read_packet(MYSQL_PLUGIN_VIO *mpv,
4869 uchar **buf) SUPPRESS_UBSAN;
4870 #endif // __clang__
4871
4872 /**
4873 vio->read_packet() callback method for client authentication plugins
4874
4875 This function is called by a client authentication plugin, when it wants
4876 to read data from the server.
4877 */
client_mpvio_read_packet(MYSQL_PLUGIN_VIO * mpv,uchar ** buf)4878 static int client_mpvio_read_packet(MYSQL_PLUGIN_VIO *mpv, uchar **buf) {
4879 MCPVIO_EXT *mpvio = (MCPVIO_EXT *)mpv;
4880 MYSQL *mysql = mpvio->mysql;
4881 ulong pkt_len;
4882
4883 /* there are cached data left, feed it to a plugin */
4884 if (mpvio->cached_server_reply.pkt) {
4885 *buf = mpvio->cached_server_reply.pkt;
4886 mpvio->cached_server_reply.pkt = nullptr;
4887 mpvio->packets_read++;
4888 return mpvio->cached_server_reply.pkt_len;
4889 }
4890
4891 if (mpvio->packets_read == 0) {
4892 /*
4893 the server handshake packet came from the wrong plugin,
4894 or it's mysql_change_user(). Either way, there is no data
4895 for a plugin to read. send a dummy packet to the server
4896 to initiate a dialog.
4897 */
4898 if (client_mpvio_write_packet(mpv, nullptr, 0)) return (int)packet_error;
4899 }
4900
4901 /* otherwise read the data */
4902 pkt_len = (*mysql->methods->read_change_user_result)(mysql);
4903
4904 /* error while reading the change user request */
4905 if (pkt_len == packet_error) return (int)packet_error;
4906
4907 mpvio->last_read_packet_len = pkt_len;
4908 *buf = mysql->net.read_pos;
4909
4910 /* was it a request to change plugins ? */
4911 if (**buf == 254)
4912 return (int)packet_error; /* if yes, this plugin shan't continue */
4913
4914 /*
4915 the server sends \1\255 or \1\254 instead of just \255 or \254 -
4916 for us to not confuse it with an error or "change plugin" packets.
4917 We remove this escaping \1 here.
4918
4919 See also server_mpvio_write_packet() where the escaping is done.
4920 */
4921 if (pkt_len && **buf == 1) {
4922 (*buf)++;
4923 pkt_len--;
4924 }
4925 mpvio->packets_read++;
4926 return pkt_len;
4927 }
4928
4929 /**
4930 vio->read_packet() nonblocking callback method for client authentication
4931 plugins
4932 */
client_mpvio_read_packet_nonblocking(struct MYSQL_PLUGIN_VIO * mpv,uchar ** buf,int * result)4933 static net_async_status client_mpvio_read_packet_nonblocking(
4934 struct MYSQL_PLUGIN_VIO *mpv, uchar **buf, int *result) {
4935 DBUG_TRACE;
4936 MCPVIO_EXT *mpvio = (MCPVIO_EXT *)mpv;
4937 MYSQL *mysql = mpvio->mysql;
4938 ulong pkt_len;
4939 int error;
4940
4941 /* there are cached data left, feed it to a plugin */
4942 if (mpvio->cached_server_reply.pkt) {
4943 *buf = mpvio->cached_server_reply.pkt;
4944 mpvio->cached_server_reply.pkt = nullptr;
4945 mpvio->packets_read++;
4946 *result = mpvio->cached_server_reply.pkt_len;
4947 return NET_ASYNC_COMPLETE;
4948 }
4949
4950 if (mpvio->packets_read == 0) {
4951 /*
4952 the server handshake packet came from the wrong plugin,
4953 or it's mysql_change_user(). Either way, there is no data
4954 for a plugin to read. send a dummy packet to the server
4955 to initiate a dialog.
4956 */
4957 net_async_status status =
4958 client_mpvio_write_packet_nonblocking(mpv, nullptr, 0, &error);
4959 if (status == NET_ASYNC_NOT_READY) {
4960 return NET_ASYNC_NOT_READY;
4961 }
4962 if (error) {
4963 *result = (int)packet_error;
4964 return NET_ASYNC_COMPLETE;
4965 }
4966 }
4967
4968 /*
4969 packets_read needs to be set here to avoid entering above condition
4970 again.
4971 */
4972 mpvio->packets_read++;
4973 /* otherwise read the data */
4974 net_async_status status =
4975 mysql->methods->read_change_user_result_nonblocking(mysql, &pkt_len);
4976 if (status == NET_ASYNC_NOT_READY) {
4977 return NET_ASYNC_NOT_READY;
4978 }
4979
4980 mpvio->last_read_packet_len = pkt_len;
4981 *buf = mysql->net.read_pos;
4982
4983 /* was it a request to change plugins ? */
4984 if (**buf == 254) {
4985 *result = (int)packet_error;
4986 return NET_ASYNC_COMPLETE;
4987 }
4988
4989 /*
4990 the server sends \1\255 or \1\254 instead of just \255 or \254 -
4991 for us to not confuse it with an error or "change plugin" packets.
4992 We remove this escaping \1 here.
4993 See also server_mpvio_write_packet() where the escaping is
4994 done.
4995 */
4996 if (pkt_len && **buf == 1) {
4997 (*buf)++;
4998 pkt_len--;
4999 }
5000 *result = pkt_len;
5001 return NET_ASYNC_COMPLETE;
5002 }
5003
5004 #ifdef __clang__
5005 // Clang UBSAN false positive?
5006 // Call to function through pointer to incorrect function type
5007 static int client_mpvio_write_packet(MYSQL_PLUGIN_VIO *mpv, const uchar *pkt,
5008 int pkt_len) SUPPRESS_UBSAN;
5009 #endif // __clang__
5010
5011 /**
5012 vio->write_packet() callback method for client authentication plugins
5013
5014 This function is called by a client authentication plugin, when it wants
5015 to send data to the server.
5016
5017 It transparently wraps the data into a change user or authentication
5018 handshake packet, if neccessary.
5019 */
client_mpvio_write_packet(MYSQL_PLUGIN_VIO * mpv,const uchar * pkt,int pkt_len)5020 static int client_mpvio_write_packet(MYSQL_PLUGIN_VIO *mpv, const uchar *pkt,
5021 int pkt_len) {
5022 int res;
5023 MCPVIO_EXT *mpvio = (MCPVIO_EXT *)mpv;
5024
5025 if (mpvio->packets_written == 0) {
5026 if (mpvio->mysql_change_user)
5027 res = send_change_user_packet(mpvio, pkt, pkt_len);
5028 else
5029 res = send_client_reply_packet(mpvio, pkt, pkt_len);
5030 } else {
5031 NET *net = &mpvio->mysql->net;
5032
5033 MYSQL_TRACE(SEND_AUTH_DATA, mpvio->mysql, ((size_t)pkt_len, pkt));
5034
5035 if (mpvio->mysql->thd)
5036 res = 1; /* no chit-chat in embedded */
5037 else
5038 res = my_net_write(net, pkt, pkt_len) || net_flush(net);
5039
5040 if (!res) {
5041 MYSQL_TRACE(PACKET_SENT, mpvio->mysql, ((size_t)pkt_len));
5042 } else
5043 set_mysql_extended_error(mpvio->mysql, CR_SERVER_LOST, unknown_sqlstate,
5044 ER_CLIENT(CR_SERVER_LOST_EXTENDED),
5045 "sending authentication information", errno);
5046 }
5047 mpvio->packets_written++;
5048 return res;
5049 }
5050
5051 /**
5052 vio->write_packet() nonblocking callback method for client authentication
5053 plugins
5054 */
client_mpvio_write_packet_nonblocking(struct MYSQL_PLUGIN_VIO * mpv,const uchar * pkt,int pkt_len,int * result)5055 static net_async_status client_mpvio_write_packet_nonblocking(
5056 struct MYSQL_PLUGIN_VIO *mpv, const uchar *pkt, int pkt_len, int *result) {
5057 DBUG_TRACE;
5058 MCPVIO_EXT *mpvio = (MCPVIO_EXT *)mpv;
5059 bool error = false;
5060
5061 if (mpvio->packets_written == 0) {
5062 /* mysql_change_user_nonblocking not implemented yet. */
5063 DBUG_ASSERT(!mpvio->mysql_change_user);
5064 net_async_status status =
5065 send_client_reply_packet_nonblocking(mpvio, pkt, pkt_len, &error);
5066 if (status == NET_ASYNC_NOT_READY) {
5067 return NET_ASYNC_NOT_READY;
5068 }
5069 } else {
5070 NET *net = &mpvio->mysql->net;
5071
5072 MYSQL_TRACE(SEND_AUTH_DATA, mpvio->mysql, ((size_t)pkt_len, pkt));
5073
5074 if (mpvio->mysql->thd)
5075 *result = 1; /* no chit-chat in embedded */
5076 else {
5077 net_async_status status =
5078 my_net_write_nonblocking(net, pkt, pkt_len, &error);
5079 if (status == NET_ASYNC_NOT_READY) {
5080 return NET_ASYNC_NOT_READY;
5081 }
5082 *result = error;
5083
5084 if (error) {
5085 set_mysql_extended_error(mpvio->mysql, CR_SERVER_LOST, unknown_sqlstate,
5086 ER_CLIENT(CR_SERVER_LOST_EXTENDED),
5087 "sending authentication information", errno);
5088 } else {
5089 MYSQL_TRACE(PACKET_SENT, mpvio->mysql, ((size_t)pkt_len));
5090 }
5091 }
5092 }
5093 mpvio->packets_written++;
5094 *result = error ? -1 : 0;
5095 return NET_ASYNC_COMPLETE;
5096 }
5097
5098 /**
5099 fills MYSQL_PLUGIN_VIO_INFO structure with the information about the
5100 connection
5101 */
mpvio_info(Vio * vio,MYSQL_PLUGIN_VIO_INFO * info)5102 void mpvio_info(Vio *vio, MYSQL_PLUGIN_VIO_INFO *info) {
5103 memset(info, 0, sizeof(*info));
5104 switch (vio->type) {
5105 case VIO_TYPE_TCPIP:
5106 info->protocol = MYSQL_PLUGIN_VIO_INFO::MYSQL_VIO_TCP;
5107 info->socket = (int)vio_fd(vio);
5108 return;
5109 case VIO_TYPE_SOCKET:
5110 info->protocol = MYSQL_PLUGIN_VIO_INFO::MYSQL_VIO_SOCKET;
5111 info->socket = (int)vio_fd(vio);
5112 return;
5113 case VIO_TYPE_SSL: {
5114 struct sockaddr addr;
5115 socklen_t addrlen = sizeof(addr);
5116 if (getsockname(vio_fd(vio), &addr, &addrlen)) return;
5117 info->protocol = addr.sa_family == AF_UNIX
5118 ? MYSQL_PLUGIN_VIO_INFO::MYSQL_VIO_SOCKET
5119 : MYSQL_PLUGIN_VIO_INFO::MYSQL_VIO_TCP;
5120 info->socket = (int)vio_fd(vio);
5121 return;
5122 }
5123 #ifdef _WIN32
5124 case VIO_TYPE_NAMEDPIPE:
5125 info->protocol = MYSQL_PLUGIN_VIO_INFO::MYSQL_VIO_PIPE;
5126 info->handle = vio->hPipe;
5127 return;
5128 #if defined(_WIN32)
5129 case VIO_TYPE_SHARED_MEMORY:
5130 info->protocol = MYSQL_PLUGIN_VIO_INFO::MYSQL_VIO_MEMORY;
5131 info->handle = vio->handle_file_map; /* or what ? */
5132 return;
5133 #endif
5134 #endif
5135 default:
5136 DBUG_ASSERT(0);
5137 }
5138 }
5139
client_mpvio_info(MYSQL_PLUGIN_VIO * vio,MYSQL_PLUGIN_VIO_INFO * info)5140 static void client_mpvio_info(MYSQL_PLUGIN_VIO *vio,
5141 MYSQL_PLUGIN_VIO_INFO *info) {
5142 MCPVIO_EXT *mpvio = (MCPVIO_EXT *)vio;
5143 mpvio_info(mpvio->mysql->net.vio, info);
5144 }
5145
5146 bool libmysql_cleartext_plugin_enabled = false;
5147
check_plugin_enabled(MYSQL * mysql,mysql_async_auth * ctx)5148 static bool check_plugin_enabled(MYSQL *mysql, mysql_async_auth *ctx) {
5149 if (ctx->auth_plugin == &clear_password_client_plugin &&
5150 (!libmysql_cleartext_plugin_enabled &&
5151 (!mysql->options.extension ||
5152 !mysql->options.extension->enable_cleartext_plugin))) {
5153 set_mysql_extended_error(
5154 mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, unknown_sqlstate,
5155 ER_CLIENT(CR_AUTH_PLUGIN_CANNOT_LOAD),
5156 clear_password_client_plugin.name, "plugin not enabled");
5157 return true;
5158 }
5159 if (ctx->non_blocking && !ctx->auth_plugin->authenticate_user_nonblocking) {
5160 set_mysql_extended_error(
5161 mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, unknown_sqlstate,
5162 ER_CLIENT(CR_AUTH_PLUGIN_CANNOT_LOAD), ctx->auth_plugin->name,
5163 "plugin does not support nonblocking connect");
5164 }
5165 return false;
5166 }
5167
5168 /**
5169 Client side of the plugin driver authentication.
5170
5171 @note this is used by both the mysql_real_connect and mysql_change_user
5172
5173 @param mysql mysql
5174 @param data pointer to the plugin auth data (scramble) in the
5175 handshake packet
5176 @param data_len the length of the data
5177 @param data_plugin a plugin that data were prepared for
5178 or 0 if it's mysql_change_user()
5179 @param db initial db to use, can be 0
5180
5181 @retval 0 ok
5182 @retval 1 error
5183 */
run_plugin_auth(MYSQL * mysql,char * data,uint data_len,const char * data_plugin,const char * db)5184 int run_plugin_auth(MYSQL *mysql, char *data, uint data_len,
5185 const char *data_plugin, const char *db) {
5186 DBUG_TRACE;
5187 mysql_state_machine_status status;
5188 mysql_async_auth ctx;
5189 memset(&ctx, 0, sizeof(ctx));
5190
5191 ctx.mysql = mysql;
5192 ctx.data = data;
5193 ctx.data_len = data_len;
5194 ctx.data_plugin = data_plugin;
5195 ctx.db = db;
5196 ctx.non_blocking = false;
5197 ctx.state_function = authsm_begin_plugin_auth;
5198
5199 do {
5200 status = ctx.state_function(&ctx);
5201 } while (status != STATE_MACHINE_FAILED && status != STATE_MACHINE_DONE);
5202
5203 return status == STATE_MACHINE_FAILED;
5204 }
5205
5206 /**
5207 This functions drives the authentication on client side in a nonblocking
5208 way. This function will call differnt modules in a sequence where each
5209 module is responsible to acheive a particular part in entire authentication
5210 phase.
5211
5212 @note this is used by both the mysql_real_connect_nonblocking
5213
5214 @param mysql mysql
5215 @param data pointer to the plugin auth data (scramble) in the
5216 handshake packet
5217 @param data_len the length of the data
5218 @param data_plugin a plugin that data were prepared for
5219 or 0 if it's mysql_change_user()
5220 @param db initial db to use, can be 0
5221
5222 @retval NET_ASYNC_NOT_READY authentication not yet complete
5223 @retval NET_ASYNC_COMPLETE authentication done
5224 */
run_plugin_auth_nonblocking(MYSQL * mysql,char * data,uint data_len,const char * data_plugin,const char * db)5225 mysql_state_machine_status run_plugin_auth_nonblocking(MYSQL *mysql, char *data,
5226 uint data_len,
5227 const char *data_plugin,
5228 const char *db) {
5229 DBUG_TRACE;
5230 mysql_async_auth *ctx = ASYNC_DATA(mysql)->connect_context->auth_context;
5231 if (!ctx) {
5232 ctx = static_cast<mysql_async_auth *>(
5233 my_malloc(key_memory_MYSQL, sizeof(*ctx), MYF(MY_WME | MY_ZEROFILL)));
5234
5235 ctx->mysql = mysql;
5236 ctx->data = data;
5237 ctx->data_len = data_len;
5238 ctx->data_plugin = data_plugin;
5239 ctx->db = db;
5240 ctx->non_blocking = true;
5241 ctx->state_function = authsm_begin_plugin_auth;
5242 ASYNC_DATA(mysql)->connect_context->auth_context = ctx;
5243 }
5244
5245 mysql_state_machine_status ret = ctx->state_function(ctx);
5246 if (ret == STATE_MACHINE_FAILED || ret == STATE_MACHINE_DONE) {
5247 my_free(ctx);
5248 ASYNC_DATA(mysql)->connect_context->auth_context = nullptr;
5249 }
5250
5251 return ret;
5252 }
5253
5254 /**
5255 Determine the default/initial plugin to use
5256 */
authsm_begin_plugin_auth(mysql_async_auth * ctx)5257 static mysql_state_machine_status authsm_begin_plugin_auth(
5258 mysql_async_auth *ctx) {
5259 DBUG_TRACE;
5260 MYSQL *mysql = ctx->mysql;
5261 /* determine the default/initial plugin to use */
5262 if (mysql->options.extension && mysql->options.extension->default_auth &&
5263 mysql->server_capabilities & CLIENT_PLUGIN_AUTH) {
5264 ctx->auth_plugin_name = mysql->options.extension->default_auth;
5265 if (!(ctx->auth_plugin = (auth_plugin_t *)mysql_client_find_plugin(
5266 mysql, ctx->auth_plugin_name,
5267 MYSQL_CLIENT_AUTHENTICATION_PLUGIN)))
5268 return STATE_MACHINE_FAILED; /* oops, not found */
5269 } else {
5270 ctx->auth_plugin = &caching_sha2_password_client_plugin;
5271 ctx->auth_plugin_name = ctx->auth_plugin->name;
5272 }
5273
5274 if (check_plugin_enabled(mysql, ctx)) return STATE_MACHINE_FAILED;
5275
5276 DBUG_PRINT("info", ("using plugin %s", ctx->auth_plugin_name));
5277
5278 mysql->net.last_errno = 0; /* just in case */
5279
5280 if (ctx->data_plugin && strcmp(ctx->data_plugin, ctx->auth_plugin_name)) {
5281 /* data was prepared for a different plugin, don't show it to this one */
5282 ctx->data = nullptr;
5283 ctx->data_len = 0;
5284 }
5285
5286 ctx->mpvio.mysql_change_user = ctx->data_plugin == nullptr;
5287 ctx->mpvio.cached_server_reply.pkt = (uchar *)ctx->data;
5288 ctx->mpvio.cached_server_reply.pkt_len = ctx->data_len;
5289 ctx->mpvio.read_packet = client_mpvio_read_packet;
5290 ctx->mpvio.write_packet = client_mpvio_write_packet;
5291 ctx->mpvio.read_packet_nonblocking = client_mpvio_read_packet_nonblocking;
5292 ctx->mpvio.write_packet_nonblocking = client_mpvio_write_packet_nonblocking;
5293 ctx->mpvio.info = client_mpvio_info;
5294 ctx->mpvio.mysql = mysql;
5295 ctx->mpvio.packets_read = ctx->mpvio.packets_written = 0;
5296 ctx->mpvio.db = ctx->db;
5297 ctx->mpvio.plugin = ctx->auth_plugin;
5298 ctx->client_auth_plugin_state =
5299 (int)(client_auth_caching_sha2_password_plugin_status::
5300 CACHING_SHA2_READING_PASSWORD);
5301
5302 ctx->state_function = authsm_run_first_authenticate_user;
5303 return STATE_MACHINE_CONTINUE;
5304 }
5305
5306 /**
5307 Authentication can have two authenticate_user calls, depending on
5308 what the server responds with; this handles the first.
5309 */
authsm_run_first_authenticate_user(mysql_async_auth * ctx)5310 static mysql_state_machine_status authsm_run_first_authenticate_user(
5311 mysql_async_auth *ctx) {
5312 DBUG_TRACE;
5313 MYSQL *mysql = ctx->mysql;
5314 MYSQL_TRACE(AUTH_PLUGIN, mysql, (ctx->auth_plugin->name));
5315
5316 if (ctx->non_blocking && ctx->auth_plugin->authenticate_user_nonblocking) {
5317 net_async_status status = ctx->auth_plugin->authenticate_user_nonblocking(
5318 (struct MYSQL_PLUGIN_VIO *)&ctx->mpvio, mysql, &ctx->res);
5319 if (status == NET_ASYNC_NOT_READY) {
5320 return STATE_MACHINE_WOULD_BLOCK;
5321 }
5322 } else {
5323 ctx->res = ctx->auth_plugin->authenticate_user(
5324 (struct MYSQL_PLUGIN_VIO *)&ctx->mpvio, mysql);
5325 }
5326
5327 ctx->state_function = authsm_handle_first_authenticate_user;
5328 return STATE_MACHINE_CONTINUE;
5329 }
5330
5331 /**
5332 Handle the result of the first authenticate_user.
5333 */
authsm_handle_first_authenticate_user(mysql_async_auth * ctx)5334 static mysql_state_machine_status authsm_handle_first_authenticate_user(
5335 mysql_async_auth *ctx) {
5336 DBUG_TRACE;
5337 MYSQL *mysql = ctx->mysql;
5338 DBUG_PRINT("info",
5339 ("authenticate_user returned %s",
5340 ctx->res == CR_OK
5341 ? "CR_OK"
5342 : ctx->res == CR_ERROR ? "CR_ERROR"
5343 : ctx->res == CR_OK_HANDSHAKE_COMPLETE
5344 ? "CR_OK_HANDSHAKE_COMPLETE"
5345 : "error"));
5346
5347 static_assert(CR_OK == -1, "");
5348 static_assert(CR_ERROR == 0, "");
5349
5350 /*
5351 The connection may be closed. If so: do not try to read from the buffer.
5352 If server sends OK packet without sending auth-switch first, client side
5353 auth plugin may not be able to process it correctly.
5354 However, if server sends OK, it means server side authentication plugin
5355 already performed required checks. Further, server side plugin did not
5356 really care about plugin used by client in this case.
5357 */
5358 if (ctx->res > CR_OK &&
5359 (!my_net_is_inited(&mysql->net) ||
5360 (mysql->net.read_pos[0] != 0 && mysql->net.read_pos[0] != 254))) {
5361 /*
5362 the plugin returned an error. write it down in mysql,
5363 unless the error code is CR_ERROR and mysql->net.last_errno
5364 is already set (the plugin has done it)
5365 */
5366 DBUG_PRINT("info", ("res=%d", ctx->res));
5367 if (ctx->res > CR_ERROR)
5368 set_mysql_error(mysql, ctx->res, unknown_sqlstate);
5369 else if (!mysql->net.last_errno)
5370 set_mysql_error(mysql, CR_UNKNOWN_ERROR, unknown_sqlstate);
5371 return STATE_MACHINE_FAILED;
5372 }
5373 ctx->state_function = authsm_read_change_user_result;
5374 return STATE_MACHINE_CONTINUE;
5375 }
5376
5377 /**
5378 After the first authenticate_user comes a call to read the result of the
5379 implied change_user.
5380 */
authsm_read_change_user_result(mysql_async_auth * ctx)5381 static mysql_state_machine_status authsm_read_change_user_result(
5382 mysql_async_auth *ctx) {
5383 DBUG_TRACE;
5384 MYSQL *mysql = ctx->mysql;
5385 /* read the OK packet (or use the cached value in mysql->net.read_pos */
5386 if (ctx->res == CR_OK) {
5387 if (ctx->non_blocking) {
5388 net_async_status status =
5389 (*mysql->methods->read_change_user_result_nonblocking)(
5390 mysql, &ctx->pkt_length);
5391 if (status == NET_ASYNC_NOT_READY) {
5392 return STATE_MACHINE_WOULD_BLOCK;
5393 }
5394 } else {
5395 ctx->pkt_length = (*mysql->methods->read_change_user_result)(mysql);
5396 }
5397 } else /* res == CR_OK_HANDSHAKE_COMPLETE */
5398 ctx->pkt_length = ctx->mpvio.last_read_packet_len;
5399
5400 ctx->state_function = authsm_handle_change_user_result;
5401 return STATE_MACHINE_CONTINUE;
5402 }
5403
5404 /**
5405 Check if server asked to use a different authentication plugin
5406 */
authsm_handle_change_user_result(mysql_async_auth * ctx)5407 static mysql_state_machine_status authsm_handle_change_user_result(
5408 mysql_async_auth *ctx) {
5409 DBUG_TRACE;
5410 MYSQL *mysql = ctx->mysql;
5411 DBUG_PRINT("info", ("OK packet length=%lu", ctx->pkt_length));
5412 if (ctx->pkt_length == packet_error) {
5413 if (mysql->net.last_errno == CR_SERVER_LOST)
5414 set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
5415 ER_CLIENT(CR_SERVER_LOST_EXTENDED),
5416 "reading authorization packet", errno);
5417 return STATE_MACHINE_FAILED;
5418 }
5419
5420 if (mysql->net.read_pos[0] == 254) {
5421 ctx->state_function = authsm_run_second_authenticate_user;
5422 } else
5423 ctx->state_function = authsm_finish_auth;
5424
5425 return STATE_MACHINE_CONTINUE;
5426 }
5427
5428 /**
5429 Start the authentication process again with the plugin which
5430 server asked for.
5431 */
authsm_run_second_authenticate_user(mysql_async_auth * ctx)5432 static mysql_state_machine_status authsm_run_second_authenticate_user(
5433 mysql_async_auth *ctx) {
5434 DBUG_TRACE;
5435 MYSQL *mysql = ctx->mysql;
5436 /* The server asked to use a different authentication plugin */
5437 if (ctx->pkt_length < 2) {
5438 set_mysql_error(mysql, CR_MALFORMED_PACKET,
5439 unknown_sqlstate); /* purecov: inspected */
5440 return STATE_MACHINE_FAILED;
5441 } else {
5442 /* "use different plugin" packet */
5443 uint len;
5444 ctx->auth_plugin_name = (char *)mysql->net.read_pos + 1;
5445 len = (uint)strlen(
5446 ctx->auth_plugin_name); /* safe as my_net_read always appends \0 */
5447 ctx->mpvio.cached_server_reply.pkt_len = ctx->pkt_length - len - 2;
5448 ctx->mpvio.cached_server_reply.pkt = mysql->net.read_pos + len + 2;
5449 DBUG_PRINT("info", ("change plugin packet from server for plugin %s",
5450 ctx->auth_plugin_name));
5451 }
5452 if (!(ctx->auth_plugin = (auth_plugin_t *)mysql_client_find_plugin(
5453 mysql, ctx->auth_plugin_name, MYSQL_CLIENT_AUTHENTICATION_PLUGIN)))
5454 return STATE_MACHINE_FAILED;
5455
5456 if (check_plugin_enabled(mysql, ctx)) return STATE_MACHINE_FAILED;
5457
5458 MYSQL_TRACE(AUTH_PLUGIN, mysql, (ctx->auth_plugin->name));
5459
5460 ctx->mpvio.plugin = ctx->auth_plugin;
5461 ctx->res = ctx->auth_plugin->authenticate_user(
5462 (struct MYSQL_PLUGIN_VIO *)&ctx->mpvio, mysql);
5463
5464 ctx->state_function = authsm_handle_second_authenticate_user;
5465 return STATE_MACHINE_CONTINUE;
5466 }
5467
5468 /* Now read the results. */
authsm_handle_second_authenticate_user(mysql_async_auth * ctx)5469 static mysql_state_machine_status authsm_handle_second_authenticate_user(
5470 mysql_async_auth *ctx) {
5471 DBUG_TRACE;
5472 MYSQL *mysql = ctx->mysql;
5473 DBUG_PRINT("info",
5474 ("second authenticate_user returned %s",
5475 ctx->res == CR_OK
5476 ? "CR_OK"
5477 : ctx->res == CR_ERROR ? "CR_ERROR"
5478 : ctx->res == CR_OK_HANDSHAKE_COMPLETE
5479 ? "CR_OK_HANDSHAKE_COMPLETE"
5480 : "error"));
5481 if (ctx->res > CR_OK) {
5482 if (ctx->res > CR_ERROR)
5483 set_mysql_error(mysql, ctx->res, unknown_sqlstate);
5484 else if (!mysql->net.last_errno)
5485 set_mysql_error(mysql, CR_UNKNOWN_ERROR, unknown_sqlstate);
5486 return STATE_MACHINE_FAILED;
5487 }
5488
5489 if (ctx->res != CR_OK_HANDSHAKE_COMPLETE) {
5490 /* Read what server thinks about out new auth message report */
5491 if (cli_safe_read(mysql, nullptr) == packet_error) {
5492 if (mysql->net.last_errno == CR_SERVER_LOST)
5493 set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
5494 ER_CLIENT(CR_SERVER_LOST_EXTENDED),
5495 "reading final connect information", errno);
5496 return STATE_MACHINE_FAILED;
5497 }
5498 }
5499
5500 ctx->state_function = authsm_finish_auth;
5501 return STATE_MACHINE_CONTINUE;
5502 }
5503
5504 /* Final cleanup */
authsm_finish_auth(mysql_async_auth * ctx)5505 static mysql_state_machine_status authsm_finish_auth(mysql_async_auth *ctx) {
5506 DBUG_TRACE;
5507 MYSQL *mysql = ctx->mysql;
5508 /*
5509 net->read_pos[0] should always be 0 here if the server implements
5510 the protocol correctly
5511 */
5512 ctx->res = (mysql->net.read_pos[0] != 0);
5513
5514 MYSQL_TRACE(AUTHENTICATED, mysql, ());
5515 return ctx->res ? STATE_MACHINE_FAILED : STATE_MACHINE_DONE;
5516 }
5517
5518 /** set some default attributes */
set_connect_attributes(MYSQL * mysql,char * buff,size_t buf_len)5519 static int set_connect_attributes(MYSQL *mysql, char *buff, size_t buf_len) {
5520 int rc = 0;
5521
5522 /*
5523 Clean up any values set by the client code. We want these options as
5524 consistent as possible
5525 */
5526 rc += mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_client_name");
5527 rc += mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_os");
5528 rc += mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_platform");
5529 rc += mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_pid");
5530 rc += mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_thread");
5531 rc += mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_client_version");
5532
5533 /*
5534 Now let's set up some values
5535 */
5536 rc += mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_client_name",
5537 "libmysql");
5538 rc += mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_client_version",
5539 PACKAGE_VERSION);
5540 rc += mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_os", SYSTEM_TYPE);
5541 rc += mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_platform",
5542 MACHINE_TYPE);
5543 #ifdef _WIN32
5544 snprintf(buff, buf_len, "%lu", (ulong)GetCurrentProcessId());
5545 #else
5546 snprintf(buff, buf_len, "%lu", (ulong)getpid());
5547 #endif
5548 rc += mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_pid", buff);
5549
5550 #ifdef _WIN32
5551 snprintf(buff, buf_len, "%lu", (ulong)GetCurrentThreadId());
5552 rc += mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_thread", buff);
5553 #endif
5554
5555 return rc > 0 ? 1 : 0;
5556 }
5557
mysql_real_connect(MYSQL * mysql,const char * host,const char * user,const char * passwd,const char * db,uint port,const char * unix_socket,ulong client_flag)5558 MYSQL *STDCALL mysql_real_connect(MYSQL *mysql, const char *host,
5559 const char *user, const char *passwd,
5560 const char *db, uint port,
5561 const char *unix_socket, ulong client_flag) {
5562 DBUG_TRACE;
5563 mysql_state_machine_status status;
5564 mysql_async_connect ctx;
5565 memset(&ctx, 0, sizeof(ctx));
5566
5567 ctx.mysql = mysql;
5568 ctx.host = host;
5569 ctx.port = port;
5570 ctx.db = db;
5571 ctx.user = user;
5572 ctx.passwd = passwd;
5573 ctx.unix_socket = unix_socket;
5574 mysql->options.client_flag |= client_flag;
5575 ctx.client_flag = mysql->options.client_flag;
5576 ctx.state_function = csm_begin_connect;
5577 ctx.ssl_state = SSL_NONE;
5578
5579 do {
5580 status = ctx.state_function(&ctx);
5581 } while (status != STATE_MACHINE_FAILED && status != STATE_MACHINE_DONE);
5582
5583 if (status == STATE_MACHINE_DONE) {
5584 DBUG_PRINT("exit", ("Mysql handler: %p", mysql));
5585 return mysql;
5586 }
5587
5588 DBUG_PRINT("error", ("message: %u/%s (%s)", mysql->net.last_errno,
5589 mysql->net.sqlstate, mysql->net.last_error));
5590 {
5591 /* Free alloced memory */
5592 end_server(mysql);
5593 mysql_close_free(mysql);
5594 if (!(ctx.client_flag & CLIENT_REMEMBER_OPTIONS))
5595 mysql_close_free_options(mysql);
5596 if (ctx.scramble_buffer_allocated) my_free(ctx.scramble_buffer);
5597 }
5598 return nullptr;
5599 }
5600
5601 /**
5602 This API attempts to initialize all the context needed to make an asynchronous
5603 connection followed by establishing a connection to MySQL database. If this
5604 API returns NET_ASYNC_COMPLETE then connection is established else call this
5605 API from the client application until the status returned is
5606 NET_ASYNC_COMPLETE.
5607
5608 @param[in] mysql connection handle
5609 @param[in] host host name or IP address
5610 @param[in] user login ID used to connect to host
5611 @param[in] passwd password for this login ID
5612 @param[in] db default database to be set after connection
5613 @param[in] port port number to use for connection
5614 @param[in] unix_socket socket file to use for connection
5615 @param[in] client_flag flag to indidcate what client can handle
5616
5617 @retval NET_ASYNC_COMPLETE Success.
5618 @retval NET_ASYNC_ERROR Error.
5619 */
mysql_real_connect_nonblocking(MYSQL * mysql,const char * host,const char * user,const char * passwd,const char * db,uint port,const char * unix_socket,ulong client_flag)5620 net_async_status STDCALL mysql_real_connect_nonblocking(
5621 MYSQL *mysql, const char *host, const char *user, const char *passwd,
5622 const char *db, uint port, const char *unix_socket, ulong client_flag) {
5623 DBUG_TRACE;
5624
5625 mysql_state_machine_status status;
5626 mysql_async_connect *ctx = ASYNC_DATA(mysql)->connect_context;
5627
5628 if (!ctx) {
5629 ctx = static_cast<mysql_async_connect *>(
5630 my_malloc(key_memory_MYSQL, sizeof(*ctx), MYF(MY_WME | MY_ZEROFILL)));
5631 if (!ctx) return NET_ASYNC_ERROR;
5632
5633 ctx->mysql = mysql;
5634 ctx->host = host;
5635 ctx->port = port;
5636 ctx->db = db;
5637 ctx->user = user;
5638 ctx->passwd = passwd;
5639 ctx->unix_socket = unix_socket;
5640 mysql->options.client_flag |= client_flag;
5641 ctx->client_flag = mysql->options.client_flag;
5642 ctx->non_blocking = true;
5643 ctx->state_function = csm_begin_connect;
5644 ctx->ssl_state = SSL_NONE;
5645 ASYNC_DATA(mysql)->connect_context = ctx;
5646 ASYNC_DATA(mysql)->async_op_status = ASYNC_OP_CONNECT;
5647 }
5648
5649 status = ctx->state_function(ctx);
5650
5651 if (status == STATE_MACHINE_DONE) {
5652 my_free(ASYNC_DATA(mysql)->connect_context);
5653 ASYNC_DATA(mysql)->connect_context = nullptr;
5654 ASYNC_DATA(mysql)->async_op_status = ASYNC_OP_UNSET;
5655 return NET_ASYNC_COMPLETE;
5656 }
5657 if (status == STATE_MACHINE_FAILED) {
5658 DBUG_PRINT("error", ("message: %u/%s (%s)", mysql->net.last_errno,
5659 mysql->net.sqlstate, mysql->net.last_error));
5660 /* Free alloced memory */
5661 end_server(mysql);
5662 mysql_close_free(mysql);
5663 if (!(mysql->options.client_flag & CLIENT_REMEMBER_OPTIONS))
5664 mysql_close_free_options(mysql);
5665 return NET_ASYNC_ERROR;
5666 }
5667 return NET_ASYNC_NOT_READY;
5668 }
5669 /**
5670 Begin the connection to the server, including any DNS resolution
5671 necessary, socket configuration, etc.
5672 */
csm_begin_connect(mysql_async_connect * ctx)5673 static mysql_state_machine_status csm_begin_connect(mysql_async_connect *ctx) {
5674 MYSQL *mysql = ctx->mysql;
5675 const char *host = ctx->host;
5676 const char *user = ctx->user;
5677 const char *passwd = ctx->passwd;
5678 const char *db = ctx->db;
5679 uint port = ctx->port;
5680 const char *unix_socket = ctx->unix_socket;
5681 ulong client_flag = ctx->client_flag;
5682 bool connect_done =
5683 true; // this is true for most of the connect methods except sockets
5684
5685 DBUG_TRACE;
5686
5687 DBUG_PRINT("enter",
5688 ("host: %s db: %s user: %s (client)", host ? host : "(Null)",
5689 db ? db : "(Null)", user ? user : "(Null)"));
5690
5691 NET *net = &mysql->net;
5692 #ifdef _WIN32
5693 HANDLE hPipe = INVALID_HANDLE_VALUE;
5694 #endif
5695 #ifdef HAVE_SYS_UN_H
5696 struct sockaddr_un UNIXaddr;
5697 #endif
5698 /* Test whether we're already connected */
5699 if (net->vio) {
5700 set_mysql_error(mysql, CR_ALREADY_CONNECTED, unknown_sqlstate);
5701 return STATE_MACHINE_FAILED;
5702 }
5703
5704 if (set_connect_attributes(mysql, ctx->buff, sizeof(ctx->buff)))
5705 return STATE_MACHINE_FAILED;
5706
5707 mysql->methods = &client_methods;
5708 net->vio = nullptr; /* If something goes wrong */
5709 mysql->client_flag = 0; /* For handshake */
5710
5711 /* use default options */
5712 if (mysql->options.my_cnf_file || mysql->options.my_cnf_group) {
5713 mysql_read_default_options(
5714 &mysql->options,
5715 (mysql->options.my_cnf_file ? mysql->options.my_cnf_file : "my"),
5716 mysql->options.my_cnf_group);
5717 my_free(mysql->options.my_cnf_file);
5718 my_free(mysql->options.my_cnf_group);
5719 mysql->options.my_cnf_file = mysql->options.my_cnf_group = nullptr;
5720 }
5721
5722 /* Some empty-string-tests are done because of ODBC */
5723 if (!host || !host[0]) host = mysql->options.host;
5724 if (!user || !user[0]) {
5725 user = mysql->options.user;
5726 if (!user) user = "";
5727 }
5728 if (!passwd) {
5729 passwd = mysql->options.password;
5730 #if !defined(MYSQL_SERVER)
5731 if (!passwd) passwd = getenv("MYSQL_PWD"); /* get it from environment */
5732 #endif
5733 if (!passwd) passwd = "";
5734 }
5735 if (!db || !db[0]) db = mysql->options.db;
5736 if (!port) port = mysql->options.port;
5737 if (!unix_socket) unix_socket = mysql->options.unix_socket;
5738
5739 mysql->server_status = SERVER_STATUS_AUTOCOMMIT;
5740 DBUG_PRINT("info", ("Connecting"));
5741
5742 MYSQL_TRACE_STAGE(mysql, CONNECTING);
5743 MYSQL_TRACE(CONNECTING, mysql, ());
5744
5745 #if defined(_WIN32)
5746 if ((!mysql->options.protocol ||
5747 mysql->options.protocol == MYSQL_PROTOCOL_MEMORY) &&
5748 (!host || !strcmp(host, LOCAL_HOST))) {
5749 HANDLE handle_map;
5750 DBUG_PRINT("info", ("Using shared memory"));
5751
5752 handle_map =
5753 create_shared_memory(mysql, net, get_win32_connect_timeout(mysql));
5754
5755 if (handle_map == INVALID_HANDLE_VALUE) {
5756 DBUG_PRINT("error",
5757 ("host: '%s' socket: '%s' shared memory: %s have_tcpip: %d",
5758 host ? host : "<null>", unix_socket ? unix_socket : "<null>",
5759 mysql->options.shared_memory_base_name, (int)have_tcpip));
5760 if (mysql->options.protocol == MYSQL_PROTOCOL_MEMORY)
5761 return STATE_MACHINE_FAILED;
5762 /*
5763 Try also with PIPE or TCP/IP. Clear the error from
5764 create_shared_memory().
5765 */
5766
5767 net_clear_error(net);
5768 } else {
5769 mysql->options.protocol = MYSQL_PROTOCOL_MEMORY;
5770 unix_socket = 0;
5771 host = mysql->options.shared_memory_base_name;
5772 snprintf(ctx->host_info = ctx->buff, sizeof(ctx->buff) - 1,
5773 ER_CLIENT(CR_SHARED_MEMORY_CONNECTION), host);
5774 }
5775 }
5776 #endif /* _WIN32 */
5777 #if defined(HAVE_SYS_UN_H)
5778 if (!net->vio &&
5779 (!mysql->options.protocol ||
5780 mysql->options.protocol == MYSQL_PROTOCOL_SOCKET) &&
5781 (unix_socket || mysql_unix_port) &&
5782 (!host || !strcmp(host, LOCAL_HOST))) {
5783 my_socket sock = socket(AF_UNIX, SOCK_STREAM, 0);
5784 DBUG_PRINT("info", ("Using socket"));
5785 if (sock == SOCKET_ERROR) {
5786 set_mysql_extended_error(mysql, CR_SOCKET_CREATE_ERROR, unknown_sqlstate,
5787 ER_CLIENT(CR_SOCKET_CREATE_ERROR), socket_errno);
5788 return STATE_MACHINE_FAILED;
5789 }
5790
5791 net->vio =
5792 vio_new(sock, VIO_TYPE_SOCKET, VIO_LOCALHOST | VIO_BUFFERED_READ);
5793 if (!net->vio) {
5794 DBUG_PRINT("error", ("Unknow protocol %d ", mysql->options.protocol));
5795 set_mysql_error(mysql, CR_CONN_UNKNOW_PROTOCOL, unknown_sqlstate);
5796 closesocket(sock);
5797 return STATE_MACHINE_FAILED;
5798 }
5799
5800 if (ctx->non_blocking) vio_set_blocking_flag(net->vio, !ctx->non_blocking);
5801
5802 host = LOCAL_HOST;
5803 if (!unix_socket) unix_socket = mysql_unix_port;
5804 ctx->host_info = const_cast<char *>(ER_CLIENT(CR_LOCALHOST_CONNECTION));
5805 DBUG_PRINT("info", ("Using UNIX sock '%s'", unix_socket));
5806
5807 memset(&UNIXaddr, 0, sizeof(UNIXaddr));
5808 UNIXaddr.sun_family = AF_UNIX;
5809 strmake(UNIXaddr.sun_path, unix_socket, sizeof(UNIXaddr.sun_path) - 1);
5810
5811 if (mysql->options.extension && mysql->options.extension->retry_count)
5812 my_net_set_retry_count(net, mysql->options.extension->retry_count);
5813
5814 if (vio_socket_connect(net->vio, (struct sockaddr *)&UNIXaddr,
5815 sizeof(UNIXaddr), ctx->non_blocking,
5816 get_vio_connect_timeout(mysql), &connect_done)) {
5817 DBUG_PRINT("error",
5818 ("Got error %d on connect to local server", socket_errno));
5819 set_mysql_extended_error(mysql, CR_CONNECTION_ERROR, unknown_sqlstate,
5820 ER_CLIENT(CR_CONNECTION_ERROR), unix_socket,
5821 socket_errno);
5822 vio_delete(net->vio);
5823 net->vio = nullptr;
5824 return STATE_MACHINE_FAILED;
5825 }
5826 mysql->options.protocol = MYSQL_PROTOCOL_SOCKET;
5827 }
5828 #elif defined(_WIN32)
5829 if (!net->vio && (mysql->options.protocol == MYSQL_PROTOCOL_PIPE ||
5830 (host && !strcmp(host, LOCAL_HOST_NAMEDPIPE)) ||
5831 (!have_tcpip && (unix_socket || !host && is_NT())))) {
5832 hPipe = create_named_pipe(mysql, get_win32_connect_timeout(mysql), &host,
5833 &unix_socket);
5834
5835 if (hPipe == INVALID_HANDLE_VALUE) {
5836 DBUG_PRINT(
5837 "error",
5838 ("host: '%s' socket: '%s' have_tcpip: %d", host ? host : "<null>",
5839 unix_socket ? unix_socket : "<null>", (int)have_tcpip));
5840 if (mysql->options.protocol == MYSQL_PROTOCOL_PIPE ||
5841 (host && !strcmp(host, LOCAL_HOST_NAMEDPIPE)) ||
5842 (unix_socket && !strcmp(unix_socket, MYSQL_NAMEDPIPE)))
5843 return STATE_MACHINE_FAILED;
5844 /* Try also with TCP/IP */
5845 } else {
5846 net->vio = vio_new_win32pipe(hPipe);
5847 snprintf(ctx->host_info = ctx->buff, sizeof(ctx->buff) - 1,
5848 ER_CLIENT(CR_NAMEDPIPE_CONNECTION), unix_socket);
5849 }
5850 }
5851 #endif
5852 DBUG_PRINT("info",
5853 ("net->vio: %p protocol: %d", net->vio, mysql->options.protocol));
5854 if (!net->vio && (!mysql->options.protocol ||
5855 mysql->options.protocol == MYSQL_PROTOCOL_TCP)) {
5856 struct addrinfo *res_lst, *client_bind_ai_lst = nullptr, hints, *t_res;
5857 char port_buf[NI_MAXSERV];
5858 my_socket sock = SOCKET_ERROR;
5859 int gai_errno, saved_error = 0, status = -1, bind_result = 0;
5860 uint flags = VIO_BUFFERED_READ;
5861
5862 unix_socket = nullptr; /* This is not used */
5863
5864 if (!port) port = mysql_port;
5865
5866 if (!host) host = LOCAL_HOST;
5867
5868 snprintf(ctx->host_info = ctx->buff, sizeof(ctx->buff) - 1,
5869 ER_CLIENT(CR_TCP_CONNECTION), host);
5870 DBUG_PRINT("info", ("Server name: '%s'. TCP sock: %d", host, port));
5871
5872 memset(&hints, 0, sizeof(hints));
5873 hints.ai_socktype = SOCK_STREAM;
5874 hints.ai_protocol = IPPROTO_TCP;
5875 hints.ai_family = AF_UNSPEC;
5876
5877 DBUG_PRINT("info", ("IPV6 getaddrinfo %s", host));
5878 snprintf(port_buf, NI_MAXSERV, "%d", port);
5879 gai_errno = getaddrinfo(host, port_buf, &hints, &res_lst);
5880
5881 DBUG_EXECUTE_IF("vio_client_use_localhost", {
5882 DBUG_ASSERT(strlen(host) == 255);
5883 gai_errno = getaddrinfo(LOCAL_HOST, port_buf, &hints, &res_lst);
5884 });
5885
5886 if (gai_errno != 0) {
5887 /*
5888 For DBUG we are keeping the right message but for client we default to
5889 historical error message.
5890 */
5891 DBUG_PRINT("info", ("IPV6 getaddrinfo error %d", gai_errno));
5892 set_mysql_extended_error(mysql, CR_UNKNOWN_HOST, unknown_sqlstate,
5893 ER_CLIENT(CR_UNKNOWN_HOST), host, errno);
5894 return STATE_MACHINE_FAILED;
5895 }
5896
5897 /* Get address info for client bind name if it is provided */
5898 if (mysql->options.bind_address) {
5899 int bind_gai_errno = 0;
5900
5901 DBUG_PRINT("info", ("Resolving addresses for client bind: '%s'",
5902 mysql->options.bind_address));
5903 /* Lookup address info for name */
5904 bind_gai_errno = getaddrinfo(mysql->options.bind_address, nullptr, &hints,
5905 &client_bind_ai_lst);
5906 if (bind_gai_errno) {
5907 DBUG_PRINT("info",
5908 ("client bind getaddrinfo error %d", bind_gai_errno));
5909 set_mysql_extended_error(mysql, CR_UNKNOWN_HOST, unknown_sqlstate,
5910 ER_CLIENT(CR_UNKNOWN_HOST),
5911 mysql->options.bind_address, bind_gai_errno);
5912
5913 freeaddrinfo(res_lst);
5914 return STATE_MACHINE_FAILED;
5915 }
5916 DBUG_PRINT("info", (" got address info for client bind name"));
5917 }
5918
5919 /*
5920 A hostname might map to multiple IP addresses (IPv4/IPv6). Go over the
5921 list of IP addresses until a successful connection can be established.
5922 For each IP address, attempt to bind the socket to each client address
5923 for the client-side bind hostname until the bind is successful.
5924 */
5925 DBUG_PRINT("info", ("Try connect on all addresses for host."));
5926 for (t_res = res_lst; t_res; t_res = t_res->ai_next) {
5927 DBUG_PRINT("info",
5928 ("Create socket, family: %d type: %d proto: %d",
5929 t_res->ai_family, t_res->ai_socktype, t_res->ai_protocol));
5930
5931 sock = socket(t_res->ai_family, t_res->ai_socktype, t_res->ai_protocol);
5932 if (sock == SOCKET_ERROR) {
5933 DBUG_PRINT("info", ("Socket created was invalid"));
5934 /* Try next address if there is one */
5935 saved_error = socket_errno;
5936 continue;
5937 }
5938
5939 if (client_bind_ai_lst) {
5940 struct addrinfo *curr_bind_ai = nullptr;
5941 DBUG_PRINT("info", ("Attempting to bind socket to bind address(es)"));
5942
5943 /*
5944 We'll attempt to bind to each of the addresses returned, until
5945 we find one that works.
5946 If none works, we'll try the next destination host address
5947 (if any)
5948 */
5949 curr_bind_ai = client_bind_ai_lst;
5950
5951 while (curr_bind_ai != nullptr) {
5952 /* Attempt to bind the socket to the given address */
5953 bind_result = bind(sock, curr_bind_ai->ai_addr,
5954 static_cast<int>(curr_bind_ai->ai_addrlen));
5955 if (!bind_result) break; /* Success */
5956
5957 DBUG_PRINT("info", ("bind failed, attempting another bind address"));
5958 /* Problem with the bind, move to next address if present */
5959 curr_bind_ai = curr_bind_ai->ai_next;
5960 }
5961
5962 if (bind_result) {
5963 /*
5964 Could not bind to any client-side address with this destination
5965 Try the next destination address (if any)
5966 */
5967 DBUG_PRINT("info", ("All bind attempts with this address failed"));
5968 saved_error = socket_errno;
5969 closesocket(sock);
5970 continue;
5971 }
5972 DBUG_PRINT("info", ("Successfully bound client side of socket"));
5973 }
5974
5975 /* Create a new Vio object to abstract the socket. */
5976 if (!net->vio) {
5977 if (!(net->vio = vio_new(sock, VIO_TYPE_TCPIP, flags))) {
5978 set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
5979 closesocket(sock);
5980 freeaddrinfo(res_lst);
5981 if (client_bind_ai_lst) freeaddrinfo(client_bind_ai_lst);
5982 return STATE_MACHINE_FAILED;
5983 }
5984 }
5985 /* Just reinitialize if one is already allocated. */
5986 else if (vio_reset(net->vio, VIO_TYPE_TCPIP, sock, nullptr, flags)) {
5987 set_mysql_error(mysql, CR_UNKNOWN_ERROR, unknown_sqlstate);
5988 closesocket(sock);
5989 freeaddrinfo(res_lst);
5990 if (client_bind_ai_lst) freeaddrinfo(client_bind_ai_lst);
5991 return STATE_MACHINE_FAILED;
5992 }
5993
5994 if (ctx->non_blocking)
5995 vio_set_blocking_flag(net->vio, !ctx->non_blocking);
5996
5997 DBUG_PRINT("info", ("Connect socket"));
5998
5999 if (mysql->options.extension && mysql->options.extension->retry_count)
6000 my_net_set_retry_count(net, mysql->options.extension->retry_count);
6001
6002 status = vio_socket_connect(
6003 net->vio, t_res->ai_addr, (socklen_t)t_res->ai_addrlen,
6004 ctx->non_blocking, get_vio_connect_timeout(mysql), &connect_done);
6005 /*
6006 Here we rely on vio_socket_connect() to return success only if
6007 the connect attempt was really successful. Otherwise we would
6008 stop trying another address, believing we were successful.
6009 */
6010 if (!status) break;
6011
6012 /*
6013 Save either the socket error status or the error code of
6014 the failed vio_connection operation. It is necessary to
6015 avoid having it overwritten by later operations.
6016 */
6017 saved_error = socket_errno;
6018
6019 DBUG_PRINT("info", ("No success, try next address."));
6020 }
6021 DBUG_PRINT("info",
6022 ("End of connect attempts, sock: %d status: %d error: %d",
6023 sock, status, saved_error));
6024
6025 freeaddrinfo(res_lst);
6026 if (client_bind_ai_lst) freeaddrinfo(client_bind_ai_lst);
6027
6028 if (sock == SOCKET_ERROR) {
6029 set_mysql_extended_error(mysql, CR_IPSOCK_ERROR, unknown_sqlstate,
6030 ER_CLIENT(CR_IPSOCK_ERROR), saved_error);
6031 return STATE_MACHINE_FAILED;
6032 }
6033
6034 if (status) {
6035 DBUG_PRINT("error",
6036 ("Got error %d on connect to '%s'", saved_error, host));
6037 set_mysql_extended_error(mysql, CR_CONN_HOST_ERROR, unknown_sqlstate,
6038 ER_CLIENT(CR_CONN_HOST_ERROR), host,
6039 saved_error);
6040 return STATE_MACHINE_FAILED;
6041 }
6042 }
6043
6044 ctx->state_function = connect_done ? csm_complete_connect : csm_wait_connect;
6045 ctx->host = host;
6046 ctx->user = user;
6047 ctx->passwd = passwd;
6048 ctx->db = db;
6049 ctx->port = port;
6050 ctx->unix_socket = unix_socket;
6051 ctx->client_flag = client_flag;
6052 return STATE_MACHINE_CONTINUE;
6053 }
6054
6055 /**
6056 Wait for async connect attempt to complete.
6057 */
csm_wait_connect(mysql_async_connect * ctx)6058 static mysql_state_machine_status csm_wait_connect(mysql_async_connect *ctx) {
6059 NET *net = &(ctx->mysql->net);
6060 MYSQL_VIO vio = net->vio;
6061 int timeout_ms = 1; // this is 1ms: the smallest non-zero timeout we can use
6062 int ret;
6063
6064 DBUG_TRACE;
6065
6066 DBUG_PRINT(
6067 "enter",
6068 ("host: %s db: %s user: %s (client)", ctx->host ? ctx->host : "(Null)",
6069 ctx->db ? ctx->db : "(Null)", ctx->user ? ctx->user : "(Null)"));
6070
6071 if (!net->vio) {
6072 DBUG_PRINT("error", ("Unknown protocol %d", ctx->mysql->options.protocol));
6073 set_mysql_error(ctx->mysql, CR_CONN_UNKNOW_PROTOCOL, unknown_sqlstate);
6074 return STATE_MACHINE_FAILED;
6075 }
6076
6077 /*
6078 The connect() is in progress. The vio_io_wait() with the smallest non-zero
6079 timeout possible can be used to peek if connect() completed.
6080
6081 If vio_io_wait() returns 0,
6082 the socket never became writable and we'll return to caller.
6083 Otherwise, if vio_io_wait() returns 1, then one of two conditions
6084 exist:
6085
6086 1. An error occurred. Use getsockopt() to check for this.
6087 2. The connection was set up successfully: getsockopt() will
6088 return 0 as an error.
6089 */
6090 if (vio_io_wait(vio, VIO_IO_EVENT_CONNECT, timeout_ms) == 1) {
6091 int error;
6092 IF_WIN(int, socklen_t) optlen = sizeof(error);
6093 IF_WIN(char, void) *optval = (IF_WIN(char, void) *)&error;
6094
6095 /*
6096 At this point, we know that something happened on the socket.
6097 But this does not means that everything is alright. The connect
6098 might have failed. We need to retrieve the error code from the
6099 socket layer. We must return success only if we are sure that
6100 it was really a success. Otherwise we might prevent the caller
6101 from trying another address to connect to.
6102 */
6103 DBUG_PRINT("info", ("Connect to '%s' completed", ctx->host));
6104 ctx->state_function = csm_complete_connect;
6105 if (!(ret = mysql_socket_getsockopt(vio->mysql_socket, SOL_SOCKET, SO_ERROR,
6106 optval, &optlen))) {
6107 #ifdef _WIN32
6108 WSASetLastError(error);
6109 #else
6110 errno = error;
6111 #endif
6112 if (error != 0) {
6113 DBUG_PRINT("error",
6114 ("Got error %d on connect to '%s'", error, ctx->host));
6115 set_mysql_extended_error(
6116 ctx->mysql, CR_CONN_HOST_ERROR, unknown_sqlstate,
6117 ER_CLIENT(CR_CONN_HOST_ERROR), ctx->host, error);
6118 return STATE_MACHINE_FAILED;
6119 }
6120 }
6121 }
6122 return STATE_MACHINE_CONTINUE;
6123 }
6124
6125 /**
6126 Complete the connection itself, setting options on the now-connected socket.
6127 */
csm_complete_connect(mysql_async_connect * ctx)6128 static mysql_state_machine_status csm_complete_connect(
6129 mysql_async_connect *ctx) {
6130 DBUG_TRACE;
6131 MYSQL *mysql = ctx->mysql;
6132 NET *net = &mysql->net;
6133 DBUG_PRINT("info", ("net->vio: %p", net->vio));
6134 if (!net->vio) {
6135 DBUG_PRINT("error", ("Unknow protocol %d ", mysql->options.protocol));
6136 set_mysql_error(mysql, CR_CONN_UNKNOW_PROTOCOL, unknown_sqlstate);
6137 return STATE_MACHINE_FAILED;
6138 }
6139
6140 if (my_net_init(net, net->vio)) {
6141 vio_delete(net->vio);
6142 net->vio = nullptr;
6143 set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
6144 return STATE_MACHINE_FAILED;
6145 }
6146 vio_keepalive(net->vio, true);
6147
6148 /* If user set read_timeout, let it override the default */
6149 if (mysql->options.read_timeout)
6150 my_net_set_read_timeout(net, mysql->options.read_timeout);
6151
6152 /* If user set write_timeout, let it override the default */
6153 if (mysql->options.write_timeout)
6154 my_net_set_write_timeout(net, mysql->options.write_timeout);
6155
6156 /* If user set retry_count, let it override the default */
6157 if (mysql->options.extension && mysql->options.extension->retry_count)
6158 my_net_set_retry_count(net, mysql->options.extension->retry_count);
6159
6160 if (mysql->options.max_allowed_packet)
6161 net->max_packet_size = mysql->options.max_allowed_packet;
6162
6163 MYSQL_TRACE(CONNECTED, mysql, ());
6164 MYSQL_TRACE_STAGE(mysql, WAIT_FOR_INIT_PACKET);
6165
6166 /* Get version info */
6167 mysql->protocol_version = PROTOCOL_VERSION; /* Assume this */
6168 if (mysql->options.connect_timeout && !ctx->non_blocking &&
6169 (vio_io_wait(net->vio, VIO_IO_EVENT_READ,
6170 get_vio_connect_timeout(mysql)) < 1)) {
6171 set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
6172 ER_CLIENT(CR_SERVER_LOST_EXTENDED),
6173 "waiting for initial communication packet",
6174 socket_errno);
6175 return STATE_MACHINE_FAILED;
6176 }
6177 ctx->state_function = csm_read_greeting;
6178 return STATE_MACHINE_CONTINUE;
6179 }
6180
6181 /**
6182 Read the greeting from the server that is read the first packet
6183 */
csm_read_greeting(mysql_async_connect * ctx)6184 static mysql_state_machine_status csm_read_greeting(mysql_async_connect *ctx) {
6185 DBUG_TRACE;
6186 MYSQL *mysql = ctx->mysql;
6187 DBUG_PRINT("info", ("Read first packet."));
6188
6189 if (!ctx->non_blocking)
6190 ctx->pkt_length = cli_safe_read(mysql, nullptr);
6191 else {
6192 if (cli_safe_read_nonblocking(mysql, nullptr, &ctx->pkt_length) ==
6193 NET_ASYNC_NOT_READY) {
6194 return STATE_MACHINE_WOULD_BLOCK;
6195 }
6196 }
6197 if (ctx->pkt_length == packet_error) {
6198 if (mysql->net.last_errno == CR_SERVER_LOST)
6199 set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
6200 ER_CLIENT(CR_SERVER_LOST_EXTENDED),
6201 "reading initial communication packet",
6202 socket_errno);
6203 return STATE_MACHINE_FAILED;
6204 }
6205 ctx->state_function = csm_parse_handshake;
6206 return STATE_MACHINE_CONTINUE;
6207 }
6208
6209 /**
6210 Parse the handshake from the server.
6211 */
csm_parse_handshake(mysql_async_connect * ctx)6212 static mysql_state_machine_status csm_parse_handshake(
6213 mysql_async_connect *ctx) {
6214 DBUG_TRACE;
6215 MYSQL *mysql = ctx->mysql;
6216 NET *net = &mysql->net;
6217 int pkt_length = ctx->pkt_length;
6218 int pkt_scramble_len = 0;
6219 char *end, *server_version_end, *pkt_end;
6220 pkt_end = (char *)net->read_pos + pkt_length;
6221 /* Check if version of protocol matches current one */
6222 mysql->protocol_version = net->read_pos[0];
6223 DBUG_DUMP("packet", (uchar *)net->read_pos, 10);
6224 DBUG_PRINT("info", ("mysql protocol version %d, server=%d", PROTOCOL_VERSION,
6225 mysql->protocol_version));
6226 if (mysql->protocol_version != PROTOCOL_VERSION) {
6227 set_mysql_extended_error(mysql, CR_VERSION_ERROR, unknown_sqlstate,
6228 ER_CLIENT(CR_VERSION_ERROR),
6229 mysql->protocol_version, PROTOCOL_VERSION);
6230 return STATE_MACHINE_FAILED;
6231 }
6232 server_version_end = end = strend((char *)net->read_pos + 1);
6233 mysql->thread_id = uint4korr((uchar *)end + 1);
6234 end += 5;
6235 /*
6236 Scramble is split into two parts because old clients do not understand
6237 long scrambles; here goes the first part.
6238 */
6239 ctx->scramble_data = end;
6240 ctx->scramble_data_len = AUTH_PLUGIN_DATA_PART_1_LENGTH + 1;
6241 ctx->scramble_plugin = nullptr;
6242 end += ctx->scramble_data_len;
6243
6244 if (pkt_end >= end + 1) mysql->server_capabilities = uint2korr((uchar *)end);
6245 if (pkt_end >= end + 18) {
6246 /* New protocol with 16 bytes to describe server characteristics */
6247 mysql->server_language = end[2];
6248 mysql->server_status = uint2korr((uchar *)end + 3);
6249 mysql->server_capabilities |= uint2korr((uchar *)end + 5) << 16;
6250 pkt_scramble_len = end[7];
6251 if (pkt_scramble_len < 0) {
6252 set_mysql_error(mysql, CR_MALFORMED_PACKET,
6253 unknown_sqlstate); /* purecov: inspected */
6254 return STATE_MACHINE_FAILED;
6255 }
6256 }
6257 end += 18;
6258
6259 if (mysql_init_character_set(mysql)) return STATE_MACHINE_FAILED;
6260
6261 /* Save connection information */
6262 if (!my_multi_malloc(
6263 key_memory_MYSQL, MYF(0), &mysql->host_info,
6264 (uint)strlen(ctx->host_info) + 1, &mysql->host,
6265 (uint)strlen(ctx->host) + 1, &mysql->unix_socket,
6266 ctx->unix_socket ? (uint)strlen(ctx->unix_socket) + 1 : (uint)1,
6267 &mysql->server_version,
6268 (uint)(server_version_end - (char *)net->read_pos + 1), NullS) ||
6269 !(mysql->user = my_strdup(key_memory_MYSQL, ctx->user, MYF(0))) ||
6270 !(mysql->passwd = my_strdup(key_memory_MYSQL, ctx->passwd, MYF(0)))) {
6271 set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
6272 return STATE_MACHINE_FAILED;
6273 }
6274 my_stpcpy(mysql->host_info, ctx->host_info);
6275 my_stpcpy(mysql->host, ctx->host);
6276 if (ctx->unix_socket)
6277 my_stpcpy(mysql->unix_socket, ctx->unix_socket);
6278 else
6279 mysql->unix_socket = nullptr;
6280 my_stpcpy(mysql->server_version, (char *)net->read_pos + 1);
6281 mysql->port = ctx->port;
6282
6283 if (pkt_end >= end + SCRAMBLE_LENGTH - AUTH_PLUGIN_DATA_PART_1_LENGTH + 1)
6284
6285 {
6286 /*
6287 move the first scramble part - directly in the NET buffer -
6288 to get a full continuous scramble. We've read all the header,
6289 and can overwrite it now.
6290 */
6291 memmove(end - AUTH_PLUGIN_DATA_PART_1_LENGTH, ctx->scramble_data,
6292 AUTH_PLUGIN_DATA_PART_1_LENGTH);
6293 ctx->scramble_data = end - AUTH_PLUGIN_DATA_PART_1_LENGTH;
6294 if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH) {
6295 ctx->scramble_data_len = pkt_scramble_len;
6296 ctx->scramble_plugin = ctx->scramble_data + ctx->scramble_data_len;
6297 /*
6298 There is a possibility that we did not get a correct plugin name
6299 for some reason. For example, the packet was malformed and some
6300 of the fields had incorrect values. In such cases, we keep the
6301 plugin name empty so that the default authentication plugin
6302 gets used later on. Since we don't really know the plugin for which
6303 the scramble_data was prepared, we can discard it and set it's length
6304 to 0.
6305 */
6306 if (ctx->scramble_data + ctx->scramble_data_len > pkt_end) {
6307 ctx->scramble_data = 0;
6308 ctx->scramble_data_len = 0;
6309 ctx->scramble_plugin = const_cast<char *>("");
6310 }
6311 } else {
6312 ctx->scramble_data_len = (int)(pkt_end - ctx->scramble_data);
6313 ctx->scramble_plugin = caching_sha2_password_plugin_name;
6314 }
6315 } else {
6316 set_mysql_error(mysql, CR_MALFORMED_PACKET, unknown_sqlstate);
6317 return STATE_MACHINE_FAILED;
6318 }
6319 ctx->state_function = csm_establish_ssl;
6320 return STATE_MACHINE_CONTINUE;
6321 }
6322
6323 /**
6324 Establish SSL if needed.
6325 */
csm_establish_ssl(mysql_async_connect * ctx)6326 static mysql_state_machine_status csm_establish_ssl(mysql_async_connect *ctx) {
6327 DBUG_TRACE;
6328 MYSQL *mysql = ctx->mysql;
6329 /* This check happens to work for both sync and async. */
6330 if (ctx->ssl_state == SSL_NONE) {
6331 MYSQL_TRACE(INIT_PACKET_RECEIVED, mysql,
6332 (ctx->pkt_length, mysql->net.read_pos));
6333 MYSQL_TRACE_STAGE(mysql, AUTHENTICATE);
6334
6335 #if defined(_WIN32)
6336 if ((mysql->options.extension &&
6337 mysql->options.extension->ssl_mode <= SSL_MODE_PREFERRED) &&
6338 (mysql->options.protocol == MYSQL_PROTOCOL_MEMORY ||
6339 mysql->options.protocol == MYSQL_PROTOCOL_PIPE)) {
6340 mysql->options.extension->ssl_mode = SSL_MODE_DISABLED;
6341 }
6342 #endif
6343 /* try and bring up SSL if possible */
6344 cli_calculate_client_flag(mysql, ctx->db, ctx->client_flag);
6345
6346 /*
6347 Allocate separate buffer for scramble data if we are going
6348 to attempt TLS connection. This would prevent a possible
6349 overwrite through my_net_write.
6350 */
6351 if (ctx->scramble_data_len && mysql->options.extension &&
6352 mysql->options.extension->ssl_mode != SSL_MODE_DISABLED) {
6353 if (!(ctx->scramble_buffer =
6354 (char *)my_malloc(key_memory_MYSQL_HANDSHAKE,
6355 ctx->scramble_data_len, MYF(MY_WME)))) {
6356 set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
6357 return STATE_MACHINE_FAILED;
6358 }
6359 ctx->scramble_buffer_allocated = true;
6360 memcpy(ctx->scramble_buffer, ctx->scramble_data, ctx->scramble_data_len);
6361 } else {
6362 ctx->scramble_buffer = ctx->scramble_data;
6363 }
6364 }
6365 if (ctx->non_blocking) {
6366 int ret;
6367 if (cli_establish_ssl_nonblocking(mysql, &ret) == NET_ASYNC_NOT_READY) {
6368 return STATE_MACHINE_WOULD_BLOCK;
6369 }
6370 if (ret) {
6371 return STATE_MACHINE_FAILED;
6372 }
6373 } else {
6374 if (cli_establish_ssl(mysql)) {
6375 return STATE_MACHINE_FAILED;
6376 }
6377 }
6378
6379 ctx->state_function = csm_authenticate;
6380 return STATE_MACHINE_CONTINUE;
6381 }
6382
6383 /**
6384 Invoke the authentication client plugin API to send the authentication
6385 data to the server
6386 */
csm_authenticate(mysql_async_connect * ctx)6387 static mysql_state_machine_status csm_authenticate(mysql_async_connect *ctx) {
6388 DBUG_TRACE;
6389 MYSQL *mysql = ctx->mysql;
6390 if (ctx->non_blocking) {
6391 mysql_state_machine_status status = run_plugin_auth_nonblocking(
6392 ctx->mysql, ctx->scramble_data, ctx->scramble_data_len,
6393 ctx->scramble_plugin, ctx->db);
6394 if (status != STATE_MACHINE_DONE) {
6395 return status;
6396 }
6397 } else {
6398 if (run_plugin_auth(mysql, ctx->scramble_buffer, ctx->scramble_data_len,
6399 ctx->scramble_plugin, ctx->db)) {
6400 return STATE_MACHINE_FAILED;
6401 }
6402 }
6403
6404 if (ctx->scramble_buffer_allocated) {
6405 ctx->scramble_buffer_allocated = false;
6406 my_free(ctx->scramble_buffer);
6407 ctx->scramble_buffer = nullptr;
6408 }
6409
6410 ctx->state_function = csm_prep_select_database;
6411 return STATE_MACHINE_CONTINUE;
6412 }
6413
6414 /**
6415 Authenticated, set intial database if specified
6416 */
csm_prep_select_database(mysql_async_connect * ctx)6417 static mysql_state_machine_status csm_prep_select_database(
6418 mysql_async_connect *ctx) {
6419 DBUG_TRACE;
6420 MYSQL *mysql = ctx->mysql;
6421 NET *net = &mysql->net;
6422
6423 MYSQL_TRACE_STAGE(mysql, READY_FOR_COMMAND);
6424
6425 /* We will use compression */
6426 if ((mysql->client_flag & CLIENT_COMPRESS) ||
6427 (mysql->client_flag & CLIENT_ZSTD_COMPRESSION_ALGORITHM)) {
6428 net->compress = true;
6429 uint compress_level;
6430 enum enum_compression_algorithm algorithm =
6431 mysql->client_flag & CLIENT_COMPRESS ? MYSQL_ZLIB : MYSQL_ZSTD;
6432 if (mysql->options.extension &&
6433 mysql->options.extension->zstd_compression_level)
6434 compress_level = mysql->options.extension->zstd_compression_level;
6435 else
6436 compress_level = mysql_default_compression_level(algorithm);
6437 #ifndef MYSQL_SERVER
6438 NET_EXTENSION *ext = NET_EXTENSION_PTR(net);
6439 DBUG_ASSERT(ext != nullptr);
6440 mysql_compress_context_init(&ext->compress_ctx, algorithm, compress_level);
6441 #else
6442 NET_SERVER *server_ext = static_cast<NET_SERVER *>(net->extension);
6443 if (server_ext == nullptr) {
6444 server_ext =
6445 static_cast<NET_SERVER *>(MYSQL_EXTENSION_PTR(mysql)->server_extn);
6446 net->extension = server_ext;
6447 }
6448 DBUG_ASSERT(server_ext != nullptr);
6449 mysql_compress_context_init(&server_ext->compress_ctx, algorithm,
6450 compress_level);
6451 #endif
6452 }
6453 #ifdef CHECK_LICENSE
6454 if (check_license(mysql)) return STATE_MACHINE_FAILED;
6455 #endif
6456
6457 #ifdef MYSQL_SERVER
6458 return STATE_MACHINE_DONE;
6459 #else
6460 ctx->state_function = csm_prep_init_commands;
6461 #endif
6462
6463 return STATE_MACHINE_CONTINUE;
6464 }
6465
6466 #ifndef MYSQL_SERVER
6467 /**
6468 Prepare to send a sequence of init commands.
6469 */
csm_prep_init_commands(mysql_async_connect * ctx)6470 static mysql_state_machine_status csm_prep_init_commands(
6471 mysql_async_connect *ctx) {
6472 DBUG_TRACE;
6473 MYSQL *mysql = ctx->mysql;
6474 if (!mysql->options.init_commands) {
6475 return STATE_MACHINE_DONE;
6476 }
6477
6478 ctx->saved_reconnect = mysql->reconnect;
6479 mysql->reconnect = false;
6480 ctx->current_init_command = mysql->options.init_commands->begin();
6481
6482 ctx->state_function = csm_send_one_init_command;
6483 return STATE_MACHINE_CONTINUE;
6484 }
6485
6486 /**
6487 Send an init command. This is called once per init command until
6488 they've all been run (or a failure occurs).
6489 */
csm_send_one_init_command(mysql_async_connect * ctx)6490 static mysql_state_machine_status csm_send_one_init_command(
6491 mysql_async_connect *ctx) {
6492 DBUG_TRACE;
6493 MYSQL *mysql = ctx->mysql;
6494
6495 if (mysql_real_query(mysql, *ctx->current_init_command,
6496 (ulong)strlen(*ctx->current_init_command)))
6497 return STATE_MACHINE_FAILED;
6498 int status;
6499 do {
6500 if (mysql->fields) {
6501 MYSQL_RES *res;
6502 if (!(res = cli_use_result(mysql))) return STATE_MACHINE_FAILED;
6503 mysql_free_result(res);
6504 }
6505 if ((status = mysql_next_result(mysql)) > 0) return STATE_MACHINE_FAILED;
6506 } while (status == 0);
6507
6508 ++ctx->current_init_command;
6509 if (ctx->current_init_command < mysql->options.init_commands->end()) {
6510 return STATE_MACHINE_CONTINUE;
6511 }
6512 mysql->reconnect = ctx->saved_reconnect;
6513 DBUG_PRINT("exit", ("Mysql handler: %p", mysql));
6514 return STATE_MACHINE_DONE;
6515 }
6516 #endif
6517
mysql_reconnect(MYSQL * mysql)6518 bool mysql_reconnect(MYSQL *mysql) {
6519 MYSQL tmp_mysql;
6520 DBUG_TRACE;
6521 DBUG_ASSERT(mysql);
6522 DBUG_PRINT("enter", ("mysql->reconnect: %d", mysql->reconnect));
6523
6524 if (!mysql->reconnect || (mysql->server_status & SERVER_STATUS_IN_TRANS) ||
6525 !mysql->host_info) {
6526 /* Allow reconnect next time */
6527 mysql->server_status &= ~SERVER_STATUS_IN_TRANS;
6528 set_mysql_error(mysql, CR_SERVER_GONE_ERROR, unknown_sqlstate);
6529 return true;
6530 }
6531 mysql_init(&tmp_mysql);
6532 mysql_close_free_options(&tmp_mysql);
6533 tmp_mysql.options = mysql->options;
6534 tmp_mysql.options.my_cnf_file = tmp_mysql.options.my_cnf_group = nullptr;
6535 #ifdef MYSQL_SERVER
6536 NET_SERVER *server_extn = MYSQL_EXTENSION_PTR(&tmp_mysql)->server_extn =
6537 MYSQL_EXTENSION_PTR(mysql)->server_extn;
6538 MYSQL_EXTENSION_PTR(mysql)->server_extn = nullptr;
6539 #endif
6540 if (!mysql_real_connect(&tmp_mysql, mysql->host, mysql->user, mysql->passwd,
6541 mysql->db, mysql->port, mysql->unix_socket,
6542 mysql->client_flag | CLIENT_REMEMBER_OPTIONS)) {
6543 #ifdef MYSQL_SERVER
6544 MYSQL_EXTENSION_PTR(mysql)->server_extn = server_extn;
6545 #endif
6546 memset(&tmp_mysql.options, 0, sizeof(tmp_mysql.options));
6547 mysql_close(&tmp_mysql);
6548 mysql->net.last_errno = tmp_mysql.net.last_errno;
6549 my_stpcpy(mysql->net.last_error, tmp_mysql.net.last_error);
6550 my_stpcpy(mysql->net.sqlstate, tmp_mysql.net.sqlstate);
6551 return true;
6552 }
6553 if (mysql_set_character_set(&tmp_mysql, mysql->charset->csname)) {
6554 DBUG_PRINT("error", ("mysql_set_character_set() failed"));
6555 #ifdef MYSQL_SERVER
6556 MYSQL_EXTENSION_PTR(mysql)->server_extn = server_extn;
6557 #endif
6558 memset(&tmp_mysql.options, 0, sizeof(tmp_mysql.options));
6559 mysql_close(&tmp_mysql);
6560 mysql->net.last_errno = tmp_mysql.net.last_errno;
6561 my_stpcpy(mysql->net.last_error, tmp_mysql.net.last_error);
6562 my_stpcpy(mysql->net.sqlstate, tmp_mysql.net.sqlstate);
6563 return true;
6564 }
6565
6566 DBUG_PRINT("info", ("reconnect succeded"));
6567 tmp_mysql.reconnect = true;
6568 tmp_mysql.free_me = mysql->free_me;
6569
6570 /* Move prepared statements (if any) over to the new mysql object */
6571 tmp_mysql.stmts = mysql->stmts;
6572 mysql->stmts = nullptr;
6573
6574 /* Don't free options as these are now used in tmp_mysql */
6575 memset(&mysql->options, 0, sizeof(mysql->options));
6576 mysql->free_me = false;
6577 mysql_close(mysql);
6578 *mysql = std::move(tmp_mysql);
6579 net_clear(&mysql->net, true);
6580 mysql->affected_rows = ~(my_ulonglong)0;
6581 return false;
6582 }
6583
6584 /**
6585 Open a new replication stream.
6586
6587 Compose and send COM_BINLOG_DUMP[_GTID] command
6588 using information in the MYSQL_RPL structure.
6589
6590 Caller must set the following MYSQL_RPL's slots:
6591 file_name_length, file_name, start_positions, server_id, flags
6592 and in case of MYSQL_RPL_GTID: gtid_set_size, gtid_set
6593 or fix_gtid_set/fix_gtid_set_arg which is used to compose command packet.
6594
6595 Note: we treat NULL rpl->file_name as an empty string.
6596 If rpl->file_name_length is 0, strlen(rpl->file_name)
6597 will be called to set it.
6598 If rpl->fix_gtid_set is not NULL it will be called to fill
6599 packet gtid set data (rpl->gtid_set is ignored).
6600
6601 @param mysql Connection handle.
6602 @param rpl Replication stream information.
6603
6604 @retval -1 Error.
6605 @retval 0 Success.
6606 */
mysql_binlog_open(MYSQL * mysql,MYSQL_RPL * rpl)6607 int STDCALL mysql_binlog_open(MYSQL *mysql, MYSQL_RPL *rpl) {
6608 DBUG_TRACE;
6609 DBUG_ASSERT(mysql);
6610 DBUG_ASSERT(rpl);
6611
6612 enum enum_server_command command;
6613 uchar *command_buffer = nullptr;
6614 size_t command_size = 0;
6615
6616 /*
6617 No need to check mysql->net.vio here as
6618 it'll be checked in the simple_command().
6619 */
6620
6621 if (!rpl->file_name) {
6622 rpl->file_name = const_cast<char *>("");
6623 rpl->file_name_length = 0;
6624 } else if (rpl->file_name_length == 0)
6625 rpl->file_name_length = strlen(rpl->file_name);
6626
6627 if (rpl->file_name_length > UINT_MAX) {
6628 set_mysql_error(mysql, CR_FILE_NAME_TOO_LONG, unknown_sqlstate);
6629 return -1;
6630 }
6631
6632 if (rpl->flags & MYSQL_RPL_GTID) {
6633 command = COM_BINLOG_DUMP_GTID;
6634
6635 #define GTID_ENCODED_DATA_SIZE 8
6636
6637 size_t alloc_size = rpl->file_name_length + ::BINLOG_FLAGS_INFO_SIZE +
6638 ::BINLOG_SERVER_ID_INFO_SIZE +
6639 ::BINLOG_NAME_SIZE_INFO_SIZE + ::BINLOG_POS_INFO_SIZE +
6640 ::BINLOG_DATA_SIZE_INFO_SIZE +
6641 (rpl->gtid_set_encoded_size ? rpl->gtid_set_encoded_size
6642 : GTID_ENCODED_DATA_SIZE) +
6643 1;
6644
6645 if (!(command_buffer = (uchar *)my_malloc(PSI_NOT_INSTRUMENTED, alloc_size,
6646 MYF(MY_WME)))) {
6647 set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
6648 return -1;
6649 }
6650
6651 uchar *ptr = command_buffer;
6652
6653 int2store(ptr, rpl->flags); // Note: we use low 16 bits
6654 ptr += ::BINLOG_FLAGS_INFO_SIZE;
6655 int4store(ptr, rpl->server_id);
6656 ptr += ::BINLOG_SERVER_ID_INFO_SIZE;
6657 int4store(ptr, static_cast<uint32>(rpl->file_name_length));
6658 ptr += ::BINLOG_NAME_SIZE_INFO_SIZE;
6659 memcpy(ptr, rpl->file_name, rpl->file_name_length);
6660 ptr += rpl->file_name_length;
6661 int8store(ptr, rpl->start_position);
6662 ptr += ::BINLOG_POS_INFO_SIZE;
6663 if (rpl->gtid_set_encoded_size) {
6664 int4store(ptr, static_cast<uint32>(rpl->gtid_set_encoded_size));
6665 ptr += ::BINLOG_DATA_SIZE_INFO_SIZE;
6666 if (rpl->fix_gtid_set)
6667 rpl->fix_gtid_set(rpl, ptr);
6668 else
6669 memcpy(ptr, rpl->gtid_set_arg, rpl->gtid_set_encoded_size);
6670 ptr += rpl->gtid_set_encoded_size;
6671 } else {
6672 /* No GTID set data, store 0 as its length. */
6673 int4store(ptr, static_cast<uint32>(GTID_ENCODED_DATA_SIZE));
6674 ptr += ::BINLOG_DATA_SIZE_INFO_SIZE;
6675 int8store(ptr, static_cast<uint64>(0));
6676 ptr += GTID_ENCODED_DATA_SIZE;
6677 }
6678
6679 command_size = ptr - command_buffer;
6680 DBUG_ASSERT(command_size == (alloc_size - 1));
6681 } else {
6682 command = COM_BINLOG_DUMP;
6683 size_t alloc_size = rpl->file_name_length + ::BINLOG_POS_OLD_INFO_SIZE +
6684 ::BINLOG_FLAGS_INFO_SIZE +
6685 ::BINLOG_SERVER_ID_INFO_SIZE + 1;
6686
6687 if (!(command_buffer = (uchar *)my_malloc(PSI_NOT_INSTRUMENTED, alloc_size,
6688 MYF(MY_WME)))) {
6689 set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
6690 return -1;
6691 }
6692
6693 uchar *ptr = command_buffer;
6694
6695 /*
6696 COM_BINLOG_DUMP accepts only 4 bytes for the position, so
6697 we are forced to cast to uint32.
6698 */
6699 int4store(ptr, (uint32)rpl->start_position);
6700 ptr += ::BINLOG_POS_OLD_INFO_SIZE;
6701 int2store(ptr, rpl->flags); // note: we use low 16 bits
6702 ptr += ::BINLOG_FLAGS_INFO_SIZE;
6703 int4store(ptr, rpl->server_id);
6704 ptr += ::BINLOG_SERVER_ID_INFO_SIZE;
6705 memcpy(ptr, rpl->file_name, rpl->file_name_length);
6706 ptr += rpl->file_name_length;
6707
6708 command_size = ptr - command_buffer;
6709 DBUG_ASSERT(command_size == (alloc_size - 1));
6710 }
6711
6712 if (simple_command(mysql, command, command_buffer, command_size, 1)) {
6713 my_free(command_buffer);
6714 return -1;
6715 }
6716
6717 my_free(command_buffer);
6718
6719 return 0;
6720 }
6721
6722 /**
6723 Fetch one event from the server.
6724
6725 Read one packet and check its validity,
6726 set rpl->buffer and rpl->size accordingly.
6727
6728 @param mysql Connection handle.
6729 @param rpl Replication stream information.
6730
6731 @retval -1 Got error packet.
6732 @retval 0 Success.
6733 */
mysql_binlog_fetch(MYSQL * mysql,MYSQL_RPL * rpl)6734 int STDCALL mysql_binlog_fetch(MYSQL *mysql, MYSQL_RPL *rpl) {
6735 DBUG_TRACE;
6736 DBUG_ASSERT(mysql);
6737 DBUG_ASSERT(rpl);
6738
6739 for (;;) {
6740 /* Read a packet from the server. */
6741 ulong packet_len = cli_safe_read(mysql, nullptr);
6742
6743 NET *net = &mysql->net;
6744
6745 /* Check if error packet. */
6746 if (packet_len == packet_error || packet_len == 0) {
6747 return -1;
6748 }
6749 /* Check if EOF packet. */
6750 else if (packet_len < 8 && net->read_pos[0] == 254) {
6751 rpl->size = 0;
6752 return 0;
6753 }
6754
6755 /* Normal packet. */
6756 if (rpl->flags & MYSQL_RPL_SKIP_HEARTBEAT) {
6757 Log_event_type event_type =
6758 (Log_event_type)net->read_pos[1 + EVENT_TYPE_OFFSET];
6759 if (event_type == binary_log::HEARTBEAT_LOG_EVENT) continue;
6760 }
6761
6762 rpl->buffer = net->read_pos;
6763 rpl->size = packet_len;
6764 return 0;
6765 }
6766 }
6767
6768 /**
6769 Close replication stream.
6770
6771 @param mysql Connection handle.
6772 @param rpl Replication stream information.
6773 */
mysql_binlog_close(MYSQL * mysql,MYSQL_RPL * rpl)6774 void STDCALL mysql_binlog_close(MYSQL *mysql, MYSQL_RPL *rpl) {
6775 DBUG_TRACE;
6776 DBUG_ASSERT(mysql);
6777 DBUG_ASSERT(rpl);
6778
6779 end_server(mysql);
6780
6781 rpl->buffer = nullptr;
6782 rpl->size = 0;
6783 }
6784
6785 /**************************************************************************
6786 Set current database
6787 **************************************************************************/
6788
mysql_select_db(MYSQL * mysql,const char * db)6789 int STDCALL mysql_select_db(MYSQL *mysql, const char *db) {
6790 int error;
6791 DBUG_TRACE;
6792 DBUG_PRINT("enter", ("db: '%s'", db));
6793
6794 if ((error = simple_command(mysql, COM_INIT_DB, (const uchar *)db,
6795 (ulong)strlen(db), 0)))
6796 return error;
6797 my_free(mysql->db);
6798 mysql->db = my_strdup(key_memory_MYSQL, db, MYF(MY_WME));
6799 return 0;
6800 }
6801
6802 /*************************************************************************
6803 Send a QUIT to the server and close the connection
6804 If handle is alloced by mysql connect free it.
6805 *************************************************************************/
6806
mysql_close_free_options(MYSQL * mysql)6807 void mysql_close_free_options(MYSQL *mysql) {
6808 DBUG_TRACE;
6809
6810 my_free(mysql->options.user);
6811 my_free(mysql->options.host);
6812 my_free(mysql->options.password);
6813 my_free(mysql->options.unix_socket);
6814 my_free(mysql->options.db);
6815 my_free(mysql->options.my_cnf_file);
6816 my_free(mysql->options.my_cnf_group);
6817 my_free(mysql->options.charset_dir);
6818 my_free(mysql->options.charset_name);
6819 my_free(mysql->options.bind_address);
6820 if (mysql->options.init_commands) {
6821 char **ptr = mysql->options.init_commands->begin();
6822 char **end = mysql->options.init_commands->end();
6823 for (; ptr < end; ptr++) my_free(*ptr);
6824 mysql->options.init_commands->~Init_commands_array();
6825 my_free(mysql->options.init_commands);
6826 }
6827 mysql_ssl_free(mysql);
6828 #if defined(_WIN32)
6829 if (mysql->options.shared_memory_base_name != def_shared_memory_base_name)
6830 my_free(mysql->options.shared_memory_base_name);
6831 #endif /* _WIN32 */
6832 if (mysql->options.extension) {
6833 my_free(mysql->options.extension->plugin_dir);
6834 my_free(mysql->options.extension->default_auth);
6835 my_free(mysql->options.extension->server_public_key_path);
6836 delete mysql->options.extension->connection_attributes;
6837 my_free(mysql->options.extension->compression_algorithm);
6838 mysql->options.extension->total_configured_compression_algorithms = 0;
6839 my_free(mysql->options.extension);
6840 }
6841 memset(&mysql->options, 0, sizeof(mysql->options));
6842 }
6843
6844 /*
6845 Free all memory allocated in a MYSQL handle but preserve
6846 current options if any.
6847 */
6848
mysql_close_free(MYSQL * mysql)6849 void mysql_close_free(MYSQL *mysql) {
6850 my_free(mysql->host_info);
6851 my_free(mysql->user);
6852 my_free(mysql->passwd);
6853 my_free(mysql->db);
6854
6855 /* Free extension if any */
6856 if (mysql->extension)
6857 mysql_extension_free(static_cast<MYSQL_EXTENSION *>(mysql->extension));
6858
6859 my_free(mysql->field_alloc);
6860
6861 if (mysql->connector_fd)
6862 free_vio_ssl_acceptor_fd(
6863 reinterpret_cast<st_VioSSLFd *>(mysql->connector_fd));
6864 mysql->connector_fd = nullptr;
6865
6866 mysql->field_alloc = nullptr;
6867
6868 /* Clear pointers for better safety */
6869 mysql->host_info = nullptr;
6870 mysql->user = nullptr;
6871 mysql->passwd = nullptr;
6872 mysql->db = nullptr;
6873 mysql->extension = nullptr;
6874 }
6875
6876 /**
6877 For use when the connection to the server has been lost (in which case
6878 the server has discarded all information about prepared statements
6879 associated with the connection).
6880
6881 Mark all statements in mysql->stmts by setting stmt->mysql= 0 if the
6882 statement has transitioned beyond the MYSQL_STMT_INIT_DONE state, and
6883 unlink the statement from the mysql->stmts list.
6884
6885 The remaining pruned list of statements (if any) is kept in mysql->stmts.
6886
6887 @param mysql pointer to the MYSQL object
6888 */
mysql_prune_stmt_list(MYSQL * mysql)6889 static void mysql_prune_stmt_list(MYSQL *mysql) {
6890 LIST *pruned_list = nullptr;
6891
6892 while (mysql->stmts) {
6893 LIST *element = mysql->stmts;
6894 MYSQL_STMT *stmt;
6895
6896 mysql->stmts = list_delete(element, element);
6897 stmt = (MYSQL_STMT *)element->data;
6898 if (stmt->state != MYSQL_STMT_INIT_DONE) {
6899 stmt->mysql = nullptr;
6900 stmt->last_errno = CR_SERVER_LOST;
6901 my_stpcpy(stmt->last_error, ER_CLIENT(CR_SERVER_LOST));
6902 my_stpcpy(stmt->sqlstate, unknown_sqlstate);
6903 } else {
6904 pruned_list = list_add(pruned_list, element);
6905 }
6906 }
6907
6908 mysql->stmts = pruned_list;
6909 }
6910
6911 /*
6912 Clear connection pointer of every statement: this is necessary
6913 to give error on attempt to use a prepared statement of closed
6914 connection.
6915
6916 SYNOPSYS
6917 mysql_detach_stmt_list()
6918 stmt_list pointer to mysql->stmts
6919 func_name name of calling function
6920
6921 NOTE
6922 There is similar code in mysql_reconnect(), so changes here
6923 should also be reflected there.
6924 */
6925
mysql_detach_stmt_list(LIST ** stmt_list MY_ATTRIBUTE ((unused)),const char * func_name MY_ATTRIBUTE ((unused)))6926 void mysql_detach_stmt_list(LIST **stmt_list MY_ATTRIBUTE((unused)),
6927 const char *func_name MY_ATTRIBUTE((unused))) {
6928 #ifndef MYSQL_SERVER
6929 /* Reset connection handle in all prepared statements. */
6930 LIST *element = *stmt_list;
6931 char buff[MYSQL_ERRMSG_SIZE];
6932 DBUG_TRACE;
6933
6934 snprintf(buff, sizeof(buff) - 1, ER_CLIENT(CR_STMT_CLOSED), func_name);
6935 for (; element; element = element->next) {
6936 MYSQL_STMT *stmt = (MYSQL_STMT *)element->data;
6937 set_stmt_error(stmt, CR_STMT_CLOSED, unknown_sqlstate, buff);
6938 stmt->mysql = nullptr;
6939 /* No need to call list_delete for statement here */
6940 }
6941 *stmt_list = nullptr;
6942 return;
6943 #endif /* !MYSQL_SERVER */
6944 }
6945
mysql_close(MYSQL * mysql)6946 void STDCALL mysql_close(MYSQL *mysql) {
6947 DBUG_TRACE;
6948 if (mysql) /* Some simple safety */
6949 {
6950 /* If connection is still up, send a QUIT message */
6951 if (mysql->net.vio != nullptr) {
6952 free_old_query(mysql);
6953 mysql->status = MYSQL_STATUS_READY; /* Force command */
6954 if (vio_is_blocking(mysql->net.vio)) {
6955 simple_command(mysql, COM_QUIT, (uchar *)nullptr, 0, 1);
6956 } else {
6957 /*
6958 Best effort; try to toss a command on the wire, but we can't wait
6959 to hear back.
6960 */
6961 bool err; /* unused */
6962 simple_command_nonblocking(mysql, COM_QUIT, (uchar *)nullptr, 0, 1,
6963 &err);
6964 }
6965
6966 mysql->reconnect = false;
6967 end_server(mysql); /* Sets mysql->net.vio= 0 */
6968 }
6969 mysql_close_free(mysql);
6970 mysql_close_free_options(mysql);
6971 mysql_detach_stmt_list(&mysql->stmts, "mysql_close");
6972 if (mysql->free_me) {
6973 my_free(mysql);
6974 }
6975 }
6976 }
6977
cli_read_query_result(MYSQL * mysql)6978 static bool cli_read_query_result(MYSQL *mysql) {
6979 uchar *pos;
6980 ulong field_count;
6981 ulong length;
6982 DBUG_TRACE;
6983
6984 if ((length = cli_safe_read(mysql, nullptr)) == packet_error) return true;
6985 free_old_query(mysql); /* Free old result */
6986 #ifndef MYSQL_SERVER /* Avoid warn of unused labels*/
6987 get_info:
6988 #endif
6989 pos = (uchar *)mysql->net.read_pos;
6990 if ((field_count = net_field_length(&pos)) == 0) {
6991 read_ok_ex(mysql, length);
6992 #if defined(CLIENT_PROTOCOL_TRACING)
6993 if (mysql->server_status & SERVER_MORE_RESULTS_EXISTS)
6994 MYSQL_TRACE_STAGE(mysql, WAIT_FOR_RESULT);
6995 else
6996 MYSQL_TRACE_STAGE(mysql, READY_FOR_COMMAND);
6997 #endif
6998 return false;
6999 }
7000 #ifndef MYSQL_SERVER
7001 if (field_count == NULL_LENGTH) /* LOAD DATA LOCAL INFILE */
7002 {
7003 int error;
7004
7005 MYSQL_TRACE_STAGE(mysql, FILE_REQUEST);
7006
7007 error = handle_local_infile(mysql, (char *)pos);
7008
7009 MYSQL_TRACE_STAGE(mysql, WAIT_FOR_RESULT);
7010
7011 if ((length = cli_safe_read(mysql, nullptr)) == packet_error || error)
7012 return true;
7013 goto get_info; /* Get info packet */
7014 }
7015 #endif
7016 if (!(mysql->server_status & SERVER_STATUS_AUTOCOMMIT))
7017 mysql->server_status |= SERVER_STATUS_IN_TRANS;
7018
7019 if (read_com_query_metadata(mysql, pos, field_count)) return true;
7020
7021 mysql->status = MYSQL_STATUS_GET_RESULT;
7022 mysql->field_count = (uint)field_count;
7023
7024 MYSQL_TRACE_STAGE(mysql, WAIT_FOR_ROW);
7025
7026 DBUG_PRINT("exit", ("ok"));
7027 return false;
7028 }
cli_read_query_result_nonblocking(MYSQL * mysql)7029 static net_async_status cli_read_query_result_nonblocking(MYSQL *mysql) {
7030 DBUG_TRACE;
7031 NET *net = &mysql->net;
7032 NET_ASYNC *net_async = NET_ASYNC_DATA(net);
7033 uchar *pos = nullptr;
7034 ulong field_count;
7035 ulong length;
7036
7037 if (net_async->async_read_query_result_status ==
7038 NET_ASYNC_READ_QUERY_RESULT_IDLE) {
7039 net_async->async_read_query_result_status =
7040 NET_ASYNC_READ_QUERY_RESULT_FIELD_COUNT;
7041 }
7042
7043 if (net_async->async_read_query_result_status ==
7044 NET_ASYNC_READ_QUERY_RESULT_FIELD_COUNT) {
7045 net_async_status status =
7046 cli_safe_read_nonblocking(mysql, nullptr, &length);
7047 if (status == NET_ASYNC_NOT_READY) {
7048 return NET_ASYNC_NOT_READY;
7049 }
7050 if (length == packet_error) {
7051 if (NET_ASYNC_DATA(net) != nullptr)
7052 net_async->async_read_query_result_status =
7053 NET_ASYNC_READ_QUERY_RESULT_IDLE;
7054 return NET_ASYNC_ERROR;
7055 }
7056 mysql->packet_length = length;
7057
7058 free_old_query(mysql); /* Free old result */
7059 #ifndef MYSQL_SERVER /* Avoid warn of unused labels*/
7060 get_info:
7061 #endif
7062 pos = (uchar *)mysql->net.read_pos;
7063 if ((field_count = net_field_length(&pos)) == 0) {
7064 read_ok_ex(mysql, length);
7065 #if defined(CLIENT_PROTOCOL_TRACING)
7066 if (mysql->server_status & SERVER_MORE_RESULTS_EXISTS)
7067 MYSQL_TRACE_STAGE(mysql, WAIT_FOR_RESULT);
7068 else
7069 MYSQL_TRACE_STAGE(mysql, READY_FOR_COMMAND);
7070 #endif
7071 net_async->async_read_query_result_status =
7072 NET_ASYNC_READ_QUERY_RESULT_IDLE;
7073 return NET_ASYNC_COMPLETE;
7074 }
7075 #ifndef MYSQL_SERVER
7076 if (field_count == NULL_LENGTH) /* LOAD DATA LOCAL INFILE */
7077 {
7078 int error;
7079
7080 MYSQL_TRACE_STAGE(mysql, FILE_REQUEST);
7081
7082 if (!(mysql->options.client_flag & CLIENT_LOCAL_FILES)) {
7083 set_mysql_error(mysql, CR_MALFORMED_PACKET, unknown_sqlstate);
7084 net_async->async_read_query_result_status =
7085 NET_ASYNC_READ_QUERY_RESULT_IDLE;
7086 return NET_ASYNC_ERROR;
7087 }
7088
7089 error = handle_local_infile(mysql, (char *)pos);
7090
7091 MYSQL_TRACE_STAGE(mysql, WAIT_FOR_RESULT);
7092
7093 /* TODO: Make LOAD DATA LOCAL INFILE asynchronous. */
7094 if ((length = cli_safe_read(mysql, nullptr)) == packet_error || error) {
7095 net_async->async_read_query_result_status =
7096 NET_ASYNC_READ_QUERY_RESULT_IDLE;
7097 return NET_ASYNC_ERROR;
7098 }
7099 goto get_info; /* Get info packet */
7100 }
7101 #endif
7102 if (!(mysql->server_status & SERVER_STATUS_AUTOCOMMIT))
7103 mysql->server_status |= SERVER_STATUS_IN_TRANS;
7104
7105 mysql->field_count = (uint)field_count;
7106 net_async->async_read_query_result_status =
7107 NET_ASYNC_READ_QUERY_RESULT_FIELD_INFO;
7108 }
7109
7110 if (net_async->async_read_query_result_status ==
7111 NET_ASYNC_READ_QUERY_RESULT_FIELD_INFO) {
7112 int res;
7113 net_async_status status = read_com_query_metadata_nonblocking(
7114 mysql, pos, mysql->field_count, &res);
7115 if (status == NET_ASYNC_NOT_READY) {
7116 return NET_ASYNC_NOT_READY;
7117 }
7118
7119 if (res) {
7120 net_async->async_read_query_result_status =
7121 NET_ASYNC_READ_QUERY_RESULT_IDLE;
7122 return NET_ASYNC_ERROR;
7123 }
7124 }
7125
7126 mysql->status = MYSQL_STATUS_GET_RESULT;
7127 DBUG_PRINT("exit", ("ok, %u", mysql->field_count));
7128 net_async->async_read_query_result_status = NET_ASYNC_READ_QUERY_RESULT_IDLE;
7129 return NET_ASYNC_COMPLETE;
7130 }
7131
7132 /*
7133 Send the query and return so we can do something else.
7134 Needs to be followed by mysql_read_query_result() when we want to
7135 finish processing it.
7136 */
7137
mysql_send_query(MYSQL * mysql,const char * query,ulong length)7138 int STDCALL mysql_send_query(MYSQL *mysql, const char *query, ulong length) {
7139 STATE_INFO *info;
7140
7141 DBUG_TRACE;
7142
7143 if ((info = STATE_DATA(mysql)))
7144 free_state_change_info(static_cast<MYSQL_EXTENSION *>(mysql->extension));
7145
7146 return simple_command(mysql, COM_QUERY, pointer_cast<const uchar *>(query),
7147 length, 1);
7148 }
7149
7150 /**
7151 Executes the SQL statement pointed by query. This API is called by
7152 mysql_real_query_nonblocking to send query to server in asynchronous way.
7153
7154 @param[in] mysql connection handle
7155 @param[in] query query string to be executed
7156 @param[in] length length of query
7157
7158 @retval NET_ASYNC_ERROR query execution failed
7159 @retval NET_ASYNC_NOT_READY query not yet completed, call this API again
7160 @retval NET_ASYNC_COMPLETE query execution finished
7161 */
mysql_send_query_nonblocking(MYSQL * mysql,const char * query,ulong length)7162 net_async_status STDCALL mysql_send_query_nonblocking(MYSQL *mysql,
7163 const char *query,
7164 ulong length) {
7165 DBUG_TRACE;
7166 STATE_INFO *info;
7167
7168 if ((info = STATE_DATA(mysql)))
7169 free_state_change_info(static_cast<MYSQL_EXTENSION *>(mysql->extension));
7170
7171 bool ret;
7172 if (simple_command_nonblocking(mysql, COM_QUERY,
7173 pointer_cast<const uchar *>(query), length, 1,
7174 &ret) == NET_ASYNC_NOT_READY) {
7175 return NET_ASYNC_NOT_READY;
7176 }
7177 if (ret)
7178 return NET_ASYNC_ERROR;
7179 else
7180 return NET_ASYNC_COMPLETE;
7181 }
7182
mysql_real_query(MYSQL * mysql,const char * query,ulong length)7183 int STDCALL mysql_real_query(MYSQL *mysql, const char *query, ulong length) {
7184 int retval;
7185 DBUG_TRACE;
7186 DBUG_PRINT("enter", ("handle: %p", mysql));
7187 DBUG_PRINT("query", ("Query = '%-.*s'", (int)length, query));
7188 DBUG_EXECUTE_IF("inject_ER_NET_READ_INTERRUPTED", {
7189 mysql->net.last_errno = ER_NET_READ_INTERRUPTED;
7190 DBUG_SET("-d,inject_ER_NET_READ_INTERRUPTED");
7191 return 1;
7192 });
7193
7194 if (mysql_send_query(mysql, query, length)) return 1;
7195 retval = (int)(*mysql->methods->read_query_result)(mysql);
7196 return retval;
7197 }
7198
7199 /**
7200 Executes the SQL statement pointed by query. This sql statement length is set
7201 in length parameter. query string can contain multiple sql statements
7202 separated by semicolons. This function can return immediately with status set
7203 to NET_ASYNC_NOT_READY, in this case client application is expected to call
7204 this API until it returns NET_ASYNC_COMPLETE.
7205
7206 @param[in] mysql connection handle
7207 @param[in] query query string to be executed
7208 @param[in] length length of query
7209
7210 @retval NET_ASYNC_ERROR query execution failed
7211 @retval NET_ASYNC_NOT_READY query not yet completed, call this API again
7212 @retval NET_ASYNC_COMPLETE query execution finished
7213 */
mysql_real_query_nonblocking(MYSQL * mysql,const char * query,ulong length)7214 net_async_status STDCALL mysql_real_query_nonblocking(MYSQL *mysql,
7215 const char *query,
7216 ulong length) {
7217 DBUG_TRACE;
7218 DBUG_PRINT("enter", ("handle: %p", mysql));
7219 DBUG_PRINT("query", ("Query = '%-.*s'", (int)length, query));
7220 DBUG_EXECUTE_IF("inject_ER_NET_READ_INTERRUPTED", {
7221 mysql->net.last_errno = ER_NET_READ_INTERRUPTED;
7222 DBUG_SET("-d,inject_ER_NET_READ_INTERRUPTED");
7223 return NET_ASYNC_ERROR;
7224 });
7225 MYSQL_ASYNC *async_context = ASYNC_DATA(mysql);
7226 DBUG_ASSERT(async_context->async_op_status == ASYNC_OP_UNSET ||
7227 async_context->async_op_status == ASYNC_OP_QUERY);
7228
7229 net_async_status status = NET_ASYNC_NOT_READY;
7230 /* 1st phase: send query. */
7231 if (async_context->async_query_state == QUERY_IDLE) {
7232 async_context->async_query_length = length;
7233 async_context->async_op_status = ASYNC_OP_QUERY;
7234 async_context->async_query_state = QUERY_SENDING;
7235 }
7236
7237 if (async_context->async_query_state == QUERY_SENDING) {
7238 status = mysql_send_query_nonblocking(mysql, query, length);
7239 if (status == NET_ASYNC_NOT_READY)
7240 return NET_ASYNC_NOT_READY;
7241 else if (status == NET_ASYNC_ERROR)
7242 goto end;
7243 async_context->async_query_state = QUERY_READING_RESULT;
7244 }
7245
7246 /* 2nd phase: read query result (field count, field info) */
7247 if (async_context->async_query_state == QUERY_READING_RESULT) {
7248 status = (*mysql->methods->read_query_result_nonblocking)(mysql);
7249 if (status == NET_ASYNC_NOT_READY)
7250 return NET_ASYNC_NOT_READY;
7251 else if (status == NET_ASYNC_ERROR)
7252 goto end;
7253 }
7254
7255 end:
7256 async_context->async_op_status = ASYNC_OP_UNSET;
7257 async_context->async_query_state = QUERY_IDLE;
7258 async_context->async_query_length = 0;
7259 if (status == NET_ASYNC_ERROR)
7260 return NET_ASYNC_ERROR;
7261 else
7262 return NET_ASYNC_COMPLETE;
7263 }
7264
7265 /**************************************************************************
7266 Alloc result struct for buffered results. All rows are read to buffer.
7267 mysql_data_seek may be used.
7268 **************************************************************************/
7269
mysql_store_result(MYSQL * mysql)7270 MYSQL_RES *STDCALL mysql_store_result(MYSQL *mysql) {
7271 MYSQL_RES *result;
7272 DBUG_TRACE;
7273
7274 /*
7275 Some queries (e.g. "CALL") may return an empty resultset.
7276 mysql->field_count is 0 in such cases.
7277 */
7278 if (!mysql->field_count) return nullptr;
7279 if (mysql->status != MYSQL_STATUS_GET_RESULT) {
7280 set_mysql_error(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate);
7281 return nullptr;
7282 }
7283 mysql->status = MYSQL_STATUS_READY; /* server is ready */
7284 if (!(result = (MYSQL_RES *)my_malloc(
7285 key_memory_MYSQL_RES,
7286 (uint)(sizeof(MYSQL_RES) + sizeof(ulong) * mysql->field_count),
7287 MYF(MY_WME | MY_ZEROFILL)))) {
7288 set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
7289 return nullptr;
7290 }
7291 if (!(result->field_alloc = (MEM_ROOT *)my_malloc(
7292 key_memory_MYSQL, sizeof(MEM_ROOT), MYF(MY_WME | MY_ZEROFILL)))) {
7293 set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
7294 my_free(result);
7295 return nullptr;
7296 }
7297 result->methods = mysql->methods;
7298 result->eof = true; /* Marker for buffered */
7299 result->lengths = (ulong *)(result + 1);
7300 if (!(result->data = (*mysql->methods->read_rows)(mysql, mysql->fields,
7301 mysql->field_count))) {
7302 my_free(result->field_alloc);
7303 my_free(result);
7304 return nullptr;
7305 }
7306 mysql->affected_rows = result->row_count = result->data->rows;
7307 result->data_cursor = result->data->data;
7308 result->fields = mysql->fields;
7309 *result->field_alloc = std::move(*mysql->field_alloc);
7310 result->field_count = mysql->field_count;
7311 result->metadata = mysql->resultset_metadata;
7312 /* The rest of result members is zerofilled in my_malloc */
7313 mysql->fields = nullptr; /* fields is now in result */
7314 /* just in case this was mistakenly called after mysql_stmt_execute() */
7315 mysql->unbuffered_fetch_owner = nullptr;
7316 return result; /* Data fetched */
7317 }
7318
7319 /**
7320 This API reads all result set sent by server in an asynchronous way
7321
7322 @param[in] mysql connection handle
7323 @param[in] result buffer which holds all result sets.
7324
7325 @retval NET_ASYNC_NOT_READY reading of result sets not complete
7326 @retval NET_ASYNC_COMPLETE completed this asynchronous operation
7327 */
7328 enum net_async_status STDCALL
mysql_store_result_nonblocking(MYSQL * mysql,MYSQL_RES ** result)7329 mysql_store_result_nonblocking(MYSQL *mysql, MYSQL_RES **result) {
7330 DBUG_TRACE;
7331 MYSQL_ASYNC *async_context = ASYNC_DATA(mysql);
7332 *result = nullptr;
7333
7334 /*
7335 Some queries (e.g. "CALL") may return an empty resultset.
7336 mysql->field_count is 0 in such cases.
7337 */
7338 if (!mysql->field_count) {
7339 goto end;
7340 }
7341 if (!async_context->async_store_result_result) {
7342 if (mysql->status != MYSQL_STATUS_GET_RESULT) {
7343 set_mysql_error(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate);
7344 goto end;
7345 }
7346 mysql->status = MYSQL_STATUS_READY; /* server is ready */
7347
7348 if (!(async_context->async_store_result_result = (MYSQL_RES *)my_malloc(
7349 key_memory_MYSQL_RES,
7350 (uint)(sizeof(MYSQL_RES) + sizeof(ulong) * mysql->field_count),
7351 MYF(MY_WME | MY_ZEROFILL)))) {
7352 set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
7353 goto end;
7354 }
7355 if (!(async_context->async_store_result_result->field_alloc =
7356 (MEM_ROOT *)my_malloc(key_memory_MYSQL, sizeof(MEM_ROOT),
7357 MYF(MY_WME | MY_ZEROFILL)))) {
7358 set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
7359 my_free(async_context->async_store_result_result);
7360 goto end;
7361 }
7362 async_context->async_store_result_result->methods = mysql->methods;
7363 async_context->async_store_result_result->eof =
7364 true; /* Marker for buffered */
7365 async_context->async_store_result_result->lengths =
7366 (ulong *)(async_context->async_store_result_result + 1);
7367 }
7368
7369 if ((*mysql->methods->read_rows_nonblocking)(
7370 mysql, mysql->fields, mysql->field_count,
7371 &async_context->async_store_result_result->data) ==
7372 NET_ASYNC_NOT_READY) {
7373 return NET_ASYNC_NOT_READY;
7374 }
7375
7376 if (!async_context->async_store_result_result->data) {
7377 my_free(async_context->async_store_result_result->field_alloc);
7378 my_free(async_context->async_store_result_result);
7379 goto end;
7380 }
7381 mysql->affected_rows = async_context->async_store_result_result->row_count =
7382 async_context->async_store_result_result->data->rows;
7383 async_context->async_store_result_result->data_cursor =
7384 async_context->async_store_result_result->data->data;
7385 async_context->async_store_result_result->fields = mysql->fields;
7386 *async_context->async_store_result_result->field_alloc =
7387 std::move(*mysql->field_alloc);
7388 async_context->async_store_result_result->field_count = mysql->field_count;
7389 async_context->async_store_result_result->metadata =
7390 mysql->resultset_metadata;
7391 /* The rest of result members is zerofilled in my_malloc */
7392 mysql->fields = nullptr; /* fields is now in result */
7393 /* just in case this was mistakenly called after mysql_stmt_execute() */
7394 mysql->unbuffered_fetch_owner = nullptr;
7395 *result = async_context->async_store_result_result;
7396 end:
7397 async_context->async_store_result_result = nullptr;
7398 return NET_ASYNC_COMPLETE;
7399 }
7400
7401 /**************************************************************************
7402 Alloc struct for use with unbuffered reads. Data is fetched by domand
7403 when calling to mysql_fetch_row.
7404 mysql_data_seek is a noop.
7405
7406 No other queries may be specified with the same MYSQL handle.
7407 There shouldn't be much processing per row because mysql server shouldn't
7408 have to wait for the client (and will not wait more than 30 sec/packet).
7409 **************************************************************************/
7410
cli_use_result(MYSQL * mysql)7411 static MYSQL_RES *cli_use_result(MYSQL *mysql) {
7412 MYSQL_RES *result;
7413 DBUG_TRACE;
7414
7415 /*
7416 Some queries (e.g. "CALL") may return an empty resultset.
7417 mysql->field_count is 0 in such cases.
7418 */
7419 if (!mysql->field_count) return nullptr;
7420 if (mysql->status != MYSQL_STATUS_GET_RESULT) {
7421 set_mysql_error(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate);
7422 return nullptr;
7423 }
7424 if (!(result = (MYSQL_RES *)my_malloc(
7425 key_memory_MYSQL_RES,
7426 sizeof(*result) + sizeof(ulong) * mysql->field_count,
7427 MYF(MY_WME | MY_ZEROFILL))))
7428 return nullptr;
7429 result->lengths = (ulong *)(result + 1);
7430 result->methods = mysql->methods;
7431 if (!(result->row = (MYSQL_ROW)my_malloc(
7432 key_memory_MYSQL_ROW,
7433 sizeof(result->row[0]) * (mysql->field_count + 1),
7434 MYF(MY_WME)))) { /* Ptrs: to one row */
7435 my_free(result);
7436 return nullptr;
7437 }
7438 if (!(result->field_alloc = (MEM_ROOT *)my_malloc(
7439 key_memory_MYSQL, sizeof(MEM_ROOT), MYF(MY_WME | MY_ZEROFILL)))) {
7440 my_free(result->row);
7441 my_free(result);
7442 return nullptr;
7443 }
7444 result->fields = mysql->fields;
7445 *result->field_alloc = std::move(*mysql->field_alloc);
7446 result->field_count = mysql->field_count;
7447 result->metadata = mysql->resultset_metadata;
7448 result->current_field = 0;
7449 result->handle = mysql;
7450 result->current_row = nullptr;
7451 mysql->fields = nullptr; /* fields is now in result */
7452 mysql->status = MYSQL_STATUS_USE_RESULT;
7453 mysql->unbuffered_fetch_owner = &result->unbuffered_fetch_cancelled;
7454 return result; /* Data is read to be fetched */
7455 }
7456
7457 /**************************************************************************
7458 Return next row of the query results
7459 **************************************************************************/
7460
mysql_fetch_row(MYSQL_RES * res)7461 MYSQL_ROW STDCALL mysql_fetch_row(MYSQL_RES *res) {
7462 DBUG_TRACE;
7463 if (!res->data) { /* Unbufferred fetch */
7464 if (!res->eof) {
7465 MYSQL *mysql = res->handle;
7466 if (mysql->status != MYSQL_STATUS_USE_RESULT) {
7467 set_mysql_error(mysql,
7468 res->unbuffered_fetch_cancelled
7469 ? CR_FETCH_CANCELED
7470 : CR_COMMANDS_OUT_OF_SYNC,
7471 unknown_sqlstate);
7472 } else if (!(read_one_row(mysql, res->field_count, res->row,
7473 res->lengths))) {
7474 res->row_count++;
7475 return res->current_row = res->row;
7476 }
7477 DBUG_PRINT("info", ("end of data"));
7478 res->eof = true;
7479 mysql->status = MYSQL_STATUS_READY;
7480 /*
7481 Reset only if owner points to us: there is a chance that somebody
7482 started new query after mysql_stmt_close():
7483 */
7484 if (mysql->unbuffered_fetch_owner == &res->unbuffered_fetch_cancelled)
7485 mysql->unbuffered_fetch_owner = nullptr;
7486 /* Don't clear handle in mysql_free_result */
7487 res->handle = nullptr;
7488 }
7489 return (MYSQL_ROW) nullptr;
7490 }
7491 {
7492 MYSQL_ROW tmp;
7493 if (!res->data_cursor) {
7494 DBUG_PRINT("info", ("end of data"));
7495 return res->current_row = (MYSQL_ROW) nullptr;
7496 }
7497 tmp = res->data_cursor->data;
7498 res->data_cursor = res->data_cursor->next;
7499 return res->current_row = tmp;
7500 }
7501 }
7502 /**
7503 Reads next row of a result set in an asynchronous way.
7504
7505 @param[in] res buffer in which all rows are stored
7506 @param[out] row return pointer to one row from result set
7507
7508 @retval NET_ASYNC_NOT_READY fetch operation not complete, retry again
7509 @retval NET_ASYNC_COMPLETE fetch operation complete
7510 */
mysql_fetch_row_nonblocking(MYSQL_RES * res,MYSQL_ROW * row)7511 net_async_status STDCALL mysql_fetch_row_nonblocking(MYSQL_RES *res,
7512 MYSQL_ROW *row) {
7513 DBUG_TRACE;
7514 MYSQL *mysql = res->handle;
7515 *row = nullptr;
7516
7517 if (!res->data) { /* Unbufferred fetch */
7518 if (!res->eof) {
7519 /*
7520 Can be -1 (error), 0 (success) and 1 (eof).
7521 Init to -1 so if state is not MYSQL_STATUS_USE_RESULT we get
7522 out of sync error.
7523 */
7524 int read_row_result = -1;
7525 if (mysql->status == MYSQL_STATUS_USE_RESULT) {
7526 if (read_one_row_nonblocking(mysql, res->field_count, res->row,
7527 res->lengths,
7528 &read_row_result) == NET_ASYNC_NOT_READY) {
7529 return NET_ASYNC_NOT_READY;
7530 }
7531
7532 // we arrive here on NET_ASYNC_ERROR or NET_ASYNC_COMPLETE
7533
7534 if (read_row_result == 0) { // we've got a row: process it
7535 res->row_count++;
7536 *row = res->current_row = res->row;
7537 goto end;
7538 }
7539 }
7540
7541 if (read_row_result == -1) // on row reading error
7542 set_mysql_error(mysql,
7543 res->unbuffered_fetch_cancelled
7544 ? CR_FETCH_CANCELED
7545 : CR_COMMANDS_OUT_OF_SYNC,
7546 unknown_sqlstate);
7547 DBUG_PRINT("info", ("end of data"));
7548 res->eof = true;
7549 mysql->status = MYSQL_STATUS_READY;
7550 /*
7551 Reset only if owner points to us: there is a chance that
7552 somebody started new query after mysql_stmt_close():
7553 */
7554 if (mysql->unbuffered_fetch_owner == &res->unbuffered_fetch_cancelled)
7555 mysql->unbuffered_fetch_owner = nullptr;
7556 /* Don't clear handle in mysql_free_result */
7557 res->handle = nullptr;
7558 }
7559
7560 *row = nullptr;
7561 goto end;
7562 }
7563 {
7564 MYSQL_ROW tmp;
7565 if (!res->data_cursor) {
7566 DBUG_PRINT("info", ("end of data"));
7567 *row = nullptr;
7568 goto end;
7569 }
7570 tmp = res->data_cursor->data;
7571 res->data_cursor = res->data_cursor->next;
7572 *row = res->current_row = tmp;
7573 goto end;
7574 }
7575
7576 end:
7577 return NET_ASYNC_COMPLETE;
7578 }
7579
7580 /**************************************************************************
7581 Get column lengths of the current row
7582 If one uses mysql_use_result, res->lengths contains the length information,
7583 else the lengths are calculated from the offset between pointers.
7584 **************************************************************************/
7585
mysql_fetch_lengths(MYSQL_RES * res)7586 ulong *STDCALL mysql_fetch_lengths(MYSQL_RES *res) {
7587 MYSQL_ROW column;
7588
7589 if (!(column = res->current_row)) return nullptr; /* Something is wrong */
7590 if (res->data)
7591 (*res->methods->fetch_lengths)(res->lengths, column, res->field_count);
7592 return res->lengths;
7593 }
7594
7595 /**
7596 Validates, makes into an absolute path and sets @ref
7597 MYSQL_OPT_LOAD_DATA_LOCAL_DIR value
7598
7599 @param mysql connection handle
7600 @param arg the value to set. Can be null
7601
7602 @retval true failed
7603 @retval false success
7604 */
set_load_data_local_infile_option(MYSQL * mysql,const char * arg)7605 static bool set_load_data_local_infile_option(MYSQL *mysql, const char *arg) {
7606 char buff1[FN_REFLEN], buff2[FN_REFLEN];
7607
7608 ENSURE_EXTENSIONS_PRESENT(&mysql->options);
7609
7610 // NULL is a valid argument
7611 if (arg == nullptr || !arg[0]) {
7612 EXTENSION_SET_STRING(&mysql->options, load_data_dir, nullptr);
7613 return false;
7614 }
7615
7616 // make fully qualified name
7617 if (my_realpath(buff1, arg, 0)) {
7618 char errbuf[MYSYS_STRERROR_SIZE];
7619 set_mysql_extended_error(
7620 mysql, CR_LOAD_DATA_LOCAL_INFILE_REALPATH_FAIL, unknown_sqlstate,
7621 ER_CLIENT(CR_LOAD_DATA_LOCAL_INFILE_REALPATH_FAIL), arg, my_errno(),
7622 my_strerror(errbuf, sizeof(errbuf), my_errno()));
7623 return true;
7624 }
7625
7626 // with uniform directory separators
7627 convert_dirname(buff2, buff1, NullS);
7628 EXTENSION_SET_STRING(&mysql->options, load_data_dir, buff2);
7629 return false;
7630 }
7631
mysql_options(MYSQL * mysql,enum mysql_option option,const void * arg)7632 int STDCALL mysql_options(MYSQL *mysql, enum mysql_option option,
7633 const void *arg) {
7634 DBUG_TRACE;
7635 DBUG_PRINT("enter", ("option: %d", (int)option));
7636 switch (option) {
7637 case MYSQL_OPT_CONNECT_TIMEOUT:
7638 mysql->options.connect_timeout = *static_cast<const uint *>(arg);
7639 break;
7640 case MYSQL_OPT_READ_TIMEOUT:
7641 mysql->options.read_timeout = *static_cast<const uint *>(arg);
7642 break;
7643 case MYSQL_OPT_WRITE_TIMEOUT:
7644 mysql->options.write_timeout = *static_cast<const uint *>(arg);
7645 break;
7646 case MYSQL_OPT_COMPRESS:
7647 mysql->options.compress = true; /* Remember for connect */
7648 mysql->options.client_flag |= CLIENT_COMPRESS;
7649 break;
7650 case MYSQL_OPT_NAMED_PIPE: /* This option is depricated */
7651 mysql->options.protocol = MYSQL_PROTOCOL_PIPE; /* Force named pipe */
7652 break;
7653 case MYSQL_OPT_LOCAL_INFILE: /* Allow LOAD DATA LOCAL ?*/
7654 if (!arg || (*static_cast<const uint *>(arg) != 0))
7655 mysql->options.client_flag |= CLIENT_LOCAL_FILES;
7656 else
7657 mysql->options.client_flag &= ~CLIENT_LOCAL_FILES;
7658 break;
7659 case MYSQL_INIT_COMMAND:
7660 add_init_command(&mysql->options, static_cast<const char *>(arg));
7661 break;
7662 case MYSQL_READ_DEFAULT_FILE:
7663 my_free(mysql->options.my_cnf_file);
7664 mysql->options.my_cnf_file =
7665 my_strdup(key_memory_mysql_options, static_cast<const char *>(arg),
7666 MYF(MY_WME));
7667 break;
7668 case MYSQL_READ_DEFAULT_GROUP:
7669 my_free(mysql->options.my_cnf_group);
7670 mysql->options.my_cnf_group =
7671 my_strdup(key_memory_mysql_options, static_cast<const char *>(arg),
7672 MYF(MY_WME));
7673 break;
7674 case MYSQL_SET_CHARSET_DIR:
7675 my_free(mysql->options.charset_dir);
7676 mysql->options.charset_dir =
7677 my_strdup(key_memory_mysql_options, static_cast<const char *>(arg),
7678 MYF(MY_WME));
7679 break;
7680 case MYSQL_SET_CHARSET_NAME:
7681 my_free(mysql->options.charset_name);
7682 mysql->options.charset_name =
7683 my_strdup(key_memory_mysql_options, static_cast<const char *>(arg),
7684 MYF(MY_WME));
7685 break;
7686 case MYSQL_OPT_PROTOCOL:
7687 mysql->options.protocol = *static_cast<const uint *>(arg);
7688 break;
7689 case MYSQL_SHARED_MEMORY_BASE_NAME:
7690 #if defined(_WIN32)
7691 if (mysql->options.shared_memory_base_name != def_shared_memory_base_name)
7692 my_free(mysql->options.shared_memory_base_name);
7693 mysql->options.shared_memory_base_name =
7694 my_strdup(key_memory_mysql_options, static_cast<const char *>(arg),
7695 MYF(MY_WME));
7696 #endif
7697 break;
7698 case MYSQL_REPORT_DATA_TRUNCATION:
7699 mysql->options.report_data_truncation = *static_cast<const bool *>(arg);
7700 break;
7701 case MYSQL_OPT_RECONNECT:
7702 mysql->reconnect = *static_cast<const bool *>(arg);
7703 break;
7704 case MYSQL_OPT_BIND:
7705 my_free(mysql->options.bind_address);
7706 mysql->options.bind_address =
7707 my_strdup(key_memory_mysql_options, static_cast<const char *>(arg),
7708 MYF(MY_WME));
7709 break;
7710 case MYSQL_PLUGIN_DIR:
7711 EXTENSION_SET_STRING(&mysql->options, plugin_dir,
7712 static_cast<const char *>(arg));
7713 break;
7714 case MYSQL_DEFAULT_AUTH:
7715 EXTENSION_SET_STRING(&mysql->options, default_auth,
7716 static_cast<const char *>(arg));
7717 break;
7718 case MYSQL_OPT_SSL_KEY:
7719 if (mysql->options.ssl_key) my_free(mysql->options.ssl_key);
7720 mysql->options.ssl_key =
7721 set_ssl_option_unpack_path(static_cast<const char *>(arg));
7722 break;
7723 case MYSQL_OPT_SSL_CERT:
7724 if (mysql->options.ssl_cert) my_free(mysql->options.ssl_cert);
7725 mysql->options.ssl_cert =
7726 set_ssl_option_unpack_path(static_cast<const char *>(arg));
7727 break;
7728 case MYSQL_OPT_SSL_CA:
7729 if (mysql->options.ssl_ca) my_free(mysql->options.ssl_ca);
7730 mysql->options.ssl_ca =
7731 set_ssl_option_unpack_path(static_cast<const char *>(arg));
7732 break;
7733 case MYSQL_OPT_SSL_CAPATH:
7734 if (mysql->options.ssl_capath) my_free(mysql->options.ssl_capath);
7735 mysql->options.ssl_capath =
7736 set_ssl_option_unpack_path(static_cast<const char *>(arg));
7737 break;
7738 case MYSQL_OPT_SSL_CIPHER:
7739 SET_OPTION(ssl_cipher, static_cast<const char *>(arg));
7740 break;
7741 case MYSQL_OPT_TLS_CIPHERSUITES:
7742 EXTENSION_SET_STRING(&mysql->options, tls_ciphersuites,
7743 static_cast<const char *>(arg));
7744 break;
7745 case MYSQL_OPT_SSL_CRL:
7746 if (mysql->options.extension)
7747 my_free(mysql->options.extension->ssl_crl);
7748 else
7749 ALLOCATE_EXTENSIONS(&mysql->options);
7750 mysql->options.extension->ssl_crl =
7751 set_ssl_option_unpack_path(static_cast<const char *>(arg));
7752 break;
7753 case MYSQL_OPT_SSL_CRLPATH:
7754 if (mysql->options.extension)
7755 my_free(mysql->options.extension->ssl_crlpath);
7756 else
7757 ALLOCATE_EXTENSIONS(&mysql->options);
7758 mysql->options.extension->ssl_crlpath =
7759 set_ssl_option_unpack_path(static_cast<const char *>(arg));
7760 break;
7761 case MYSQL_OPT_TLS_VERSION:
7762 EXTENSION_SET_STRING(&mysql->options, tls_version,
7763 static_cast<const char *>(arg));
7764 if ((mysql->options.extension->ssl_ctx_flags = process_tls_version(
7765 mysql->options.extension->tls_version)) == -1)
7766 return 1;
7767 break;
7768 case MYSQL_OPT_SSL_FIPS_MODE: {
7769 char ssl_err_string[OPENSSL_ERROR_LENGTH] = {'\0'};
7770 ENSURE_EXTENSIONS_PRESENT(&mysql->options);
7771 mysql->options.extension->ssl_fips_mode = *static_cast<const uint *>(arg);
7772 if (set_fips_mode(mysql->options.extension->ssl_fips_mode,
7773 ssl_err_string) != 1) {
7774 DBUG_PRINT("error", ("fips mode set error %s:", ssl_err_string));
7775 set_mysql_extended_error(
7776 mysql, CR_SSL_FIPS_MODE_ERR, unknown_sqlstate,
7777 "Set Fips mode ON/STRICT failed, detail: '%s'.", ssl_err_string);
7778 return 1;
7779 }
7780 } break;
7781 case MYSQL_OPT_SSL_MODE:
7782 ENSURE_EXTENSIONS_PRESENT(&mysql->options);
7783 mysql->options.extension->ssl_mode = *static_cast<const uint *>(arg);
7784 if (mysql->options.extension->ssl_mode == SSL_MODE_VERIFY_IDENTITY)
7785 mysql->options.client_flag |= CLIENT_SSL_VERIFY_SERVER_CERT;
7786 else
7787 mysql->options.client_flag &= ~CLIENT_SSL_VERIFY_SERVER_CERT;
7788 break;
7789 case MYSQL_SERVER_PUBLIC_KEY:
7790 EXTENSION_SET_STRING(&mysql->options, server_public_key_path,
7791 static_cast<const char *>(arg));
7792 break;
7793
7794 case MYSQL_OPT_GET_SERVER_PUBLIC_KEY:
7795 ENSURE_EXTENSIONS_PRESENT(&mysql->options);
7796 mysql->options.extension->get_server_public_key =
7797 *static_cast<const bool *>(arg);
7798 break;
7799
7800 case MYSQL_OPT_CONNECT_ATTR_RESET:
7801 ENSURE_EXTENSIONS_PRESENT(&mysql->options);
7802 if (mysql->options.extension->connection_attributes) {
7803 delete mysql->options.extension->connection_attributes;
7804 mysql->options.extension->connection_attributes = nullptr;
7805 mysql->options.extension->connection_attributes_length = 0;
7806 }
7807 break;
7808 case MYSQL_OPT_CONNECT_ATTR_DELETE:
7809 ENSURE_EXTENSIONS_PRESENT(&mysql->options);
7810 if (mysql->options.extension->connection_attributes) {
7811 string key = arg ? pointer_cast<const char *>(arg) : "";
7812
7813 if (!key.empty()) {
7814 auto it =
7815 mysql->options.extension->connection_attributes->hash.find(key);
7816 if (it !=
7817 mysql->options.extension->connection_attributes->hash.end()) {
7818 const string &attr_key = it->first;
7819 const string &attr_value = it->second;
7820 mysql->options.extension->connection_attributes_length -=
7821 get_length_store_length(attr_key.size()) + attr_key.size() +
7822 get_length_store_length(attr_value.size()) + attr_value.size();
7823
7824 mysql->options.extension->connection_attributes->hash.erase(it);
7825 }
7826 }
7827 }
7828 break;
7829 case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
7830 ENSURE_EXTENSIONS_PRESENT(&mysql->options);
7831 mysql->options.extension->enable_cleartext_plugin =
7832 *static_cast<const bool *>(arg);
7833 break;
7834 case MYSQL_OPT_RETRY_COUNT:
7835 ENSURE_EXTENSIONS_PRESENT(&mysql->options);
7836 mysql->options.extension->retry_count = *static_cast<const uint *>(arg);
7837 break;
7838 case MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS:
7839 if (*static_cast<const bool *>(arg))
7840 mysql->options.client_flag |= CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;
7841 else
7842 mysql->options.client_flag &= ~CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;
7843 break;
7844
7845 case MYSQL_OPT_MAX_ALLOWED_PACKET:
7846 if (mysql)
7847 mysql->options.max_allowed_packet = *static_cast<const ulong *>(arg);
7848 else
7849 g_max_allowed_packet = *static_cast<const ulong *>(arg);
7850 break;
7851
7852 case MYSQL_OPT_NET_BUFFER_LENGTH:
7853 g_net_buffer_length = *static_cast<const ulong *>(arg);
7854 break;
7855
7856 case MYSQL_OPT_OPTIONAL_RESULTSET_METADATA:
7857 if (*static_cast<const bool *>(arg))
7858 mysql->options.client_flag |= CLIENT_OPTIONAL_RESULTSET_METADATA;
7859 else
7860 mysql->options.client_flag &= ~CLIENT_OPTIONAL_RESULTSET_METADATA;
7861 break;
7862
7863 case MYSQL_OPT_COMPRESSION_ALGORITHMS: {
7864 std::string compress_option(static_cast<const char *>(arg));
7865 std::vector<std::string> list;
7866 parse_compression_algorithms_list(compress_option, list);
7867 ENSURE_EXTENSIONS_PRESENT(&mysql->options);
7868 mysql->options.extension->connection_compressed = true;
7869 mysql->options.client_flag &=
7870 ~(CLIENT_COMPRESS | CLIENT_ZSTD_COMPRESSION_ALGORITHM);
7871 mysql->options.compress = false;
7872 auto it = list.begin();
7873 unsigned int cnt = 0;
7874 while (it != list.end() && cnt < COMPRESSION_ALGORITHM_COUNT_MAX) {
7875 std::string value = *it;
7876 switch (get_compression_algorithm(value)) {
7877 case enum_compression_algorithm::MYSQL_ZLIB:
7878 mysql->options.client_flag |= CLIENT_COMPRESS;
7879 mysql->options.compress = true;
7880 break;
7881 case enum_compression_algorithm::MYSQL_ZSTD:
7882 mysql->options.client_flag |= CLIENT_ZSTD_COMPRESSION_ALGORITHM;
7883 mysql->options.compress = true;
7884 break;
7885 case enum_compression_algorithm::MYSQL_UNCOMPRESSED:
7886 mysql->options.extension->connection_compressed = false;
7887 break;
7888 case enum_compression_algorithm::MYSQL_INVALID:
7889 break; // report error
7890 }
7891 it++;
7892 cnt++;
7893 }
7894 if (cnt)
7895 EXTENSION_SET_STRING(&mysql->options, compression_algorithm,
7896 static_cast<const char *>(arg));
7897 mysql->options.extension->total_configured_compression_algorithms = cnt;
7898 } break;
7899 case MYSQL_OPT_ZSTD_COMPRESSION_LEVEL:
7900 ENSURE_EXTENSIONS_PRESENT(&mysql->options);
7901 mysql->options.extension->zstd_compression_level =
7902 *static_cast<const unsigned int *>(arg);
7903 break;
7904
7905 case MYSQL_OPT_LOAD_DATA_LOCAL_DIR:
7906 if (set_load_data_local_infile_option(mysql,
7907 static_cast<const char *>(arg)))
7908 return 1;
7909 break;
7910
7911 default:
7912 return 1;
7913 }
7914 return 0;
7915 }
7916
7917 /**
7918 Return the current values for the options settable through mysql_options()
7919
7920 Returns the current values for all of the connection options.
7921 Callers should not manipulate the returned data !
7922 Data are valid at the time of returning them until the next C API CALL
7923 arg should always be a pointer to a variable of the appropriate type.
7924 type of variable, based on the parameter:
7925
7926 uint
7927 MYSQL_OPT_CONNECT_TIMEOUT, MYSQL_OPT_READ_TIMEOUT, MYSQL_OPT_WRITE_TIMEOUT,
7928 MYSQL_OPT_PROTOCOL, MYSQL_OPT_SSL_MODE, MYSQL_OPT_RETRY_COUNT
7929
7930 bool
7931 MYSQL_OPT_COMPRESS, MYSQL_OPT_LOCAL_INFILE,
7932 MYSQL_REPORT_DATA_TRUNCATION, MYSQL_OPT_RECONNECT,
7933 MYSQL_ENABLE_CLEARTEXT_PLUGIN, MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS,
7934 MYSQL_OPT_OPTIONAL_RESULTSET_METADATA
7935
7936 const char *
7937 MYSQL_READ_DEFAULT_FILE, MYSQL_READ_DEFAULT_GROUP,
7938 MYSQL_SET_CHARSET_DIR, MYSQL_SET_CHARSET_NAME,
7939 MYSQL_SHARED_MEMORY_BASE_NAME, MYSQL_SET_CLIENT_IP, MYSQL_OPT_BIND,
7940 MYSQL_PLUGIN_DIR, MYSQL_DEFAULT_AUTH, MYSQL_OPT_SSL_KEY, MYSQL_OPT_SSL_CERT,
7941 MYSQL_OPT_SSL_CA, MYSQL_OPT_SSL_CAPATH, MYSQL_OPT_SSL_CIPHER,
7942 MYSQL_OPT_TLS_CIPHERSUITES, MYSQL_OPT_SSL_CRL, MYSQL_OPT_SSL_CRLPATH,
7943 MYSQL_OPT_TLS_VERSION, MYSQL_SERVER_PUBLIC_KEY, MYSQL_OPT_SSL_FIPS_MODE
7944
7945 <none, error returned>
7946 MYSQL_OPT_NAMED_PIPE, MYSQL_OPT_CONNECT_ATTR_RESET,
7947 MYSQL_OPT_CONNECT_ATTR_DELETE, MYSQL_INIT_COMMAND
7948
7949 @param mysql The MYSQL connection to operate on
7950 @param option The option to return the value for
7951 @param [out] arg Must be non-null. Receives the current value.
7952 @return status
7953 @retval 0 SUCCESS
7954 */
7955
mysql_get_option(MYSQL * mysql,enum mysql_option option,const void * arg)7956 int STDCALL mysql_get_option(MYSQL *mysql, enum mysql_option option,
7957 const void *arg) {
7958 DBUG_TRACE;
7959 DBUG_PRINT("enter", ("option: %d", (int)option));
7960
7961 if (!arg) return 1;
7962
7963 switch (option) {
7964 case MYSQL_OPT_CONNECT_TIMEOUT:
7965 *(const_cast<uint *>(static_cast<const uint *>(arg))) =
7966 mysql->options.connect_timeout;
7967 break;
7968 case MYSQL_OPT_READ_TIMEOUT:
7969 *(const_cast<uint *>(static_cast<const uint *>(arg))) =
7970 mysql->options.read_timeout;
7971 break;
7972 case MYSQL_OPT_WRITE_TIMEOUT:
7973 *(const_cast<uint *>(static_cast<const uint *>(arg))) =
7974 mysql->options.write_timeout;
7975 break;
7976 case MYSQL_OPT_COMPRESS:
7977 *(const_cast<bool *>(static_cast<const bool *>(arg))) =
7978 mysql->options.compress;
7979 break;
7980 case MYSQL_OPT_LOCAL_INFILE: /* Allow LOAD DATA LOCAL ?*/
7981 *(const_cast<uint *>(static_cast<const uint *>(arg))) =
7982 (mysql->options.client_flag & CLIENT_LOCAL_FILES) != 0;
7983 break;
7984 case MYSQL_READ_DEFAULT_FILE:
7985 *(static_cast<char **>(const_cast<void *>(arg))) =
7986 mysql->options.my_cnf_file;
7987 break;
7988 case MYSQL_READ_DEFAULT_GROUP:
7989 *(static_cast<char **>(const_cast<void *>(arg))) =
7990 mysql->options.my_cnf_group;
7991 break;
7992 case MYSQL_SET_CHARSET_DIR:
7993 *(static_cast<char **>(const_cast<void *>(arg))) =
7994 mysql->options.charset_dir;
7995 break;
7996 case MYSQL_SET_CHARSET_NAME:
7997 *(static_cast<char **>(const_cast<void *>(arg))) =
7998 mysql->options.charset_name;
7999 break;
8000 case MYSQL_OPT_PROTOCOL:
8001 *(const_cast<uint *>(static_cast<const uint *>(arg))) =
8002 mysql->options.protocol;
8003 break;
8004 case MYSQL_SHARED_MEMORY_BASE_NAME:
8005 #if defined(_WIN32)
8006 *(static_cast<char **>(const_cast<void *>(arg))) =
8007 mysql->options.shared_memory_base_name;
8008 #else
8009 *(static_cast<char **>(const_cast<void *>(arg))) = const_cast<char *>("");
8010 #endif
8011 break;
8012 case MYSQL_REPORT_DATA_TRUNCATION:
8013 *(const_cast<bool *>(static_cast<const bool *>(arg))) =
8014 mysql->options.report_data_truncation;
8015 break;
8016 case MYSQL_OPT_RECONNECT:
8017 *(const_cast<bool *>(static_cast<const bool *>(arg))) = mysql->reconnect;
8018 break;
8019 case MYSQL_OPT_BIND:
8020 *(static_cast<char **>(const_cast<void *>(arg))) =
8021 mysql->options.bind_address;
8022 break;
8023 case MYSQL_OPT_SSL_MODE:
8024 *(const_cast<uint *>(static_cast<const uint *>(arg))) =
8025 mysql->options.extension ? mysql->options.extension->ssl_mode : 0;
8026 break;
8027 case MYSQL_OPT_SSL_FIPS_MODE:
8028 *(const_cast<uint *>(static_cast<const uint *>(arg))) =
8029 mysql->options.extension ? mysql->options.extension->ssl_fips_mode
8030 : 0;
8031 break;
8032 case MYSQL_PLUGIN_DIR:
8033 *(static_cast<char **>(const_cast<void *>(arg))) =
8034 mysql->options.extension ? mysql->options.extension->plugin_dir
8035 : nullptr;
8036 break;
8037 case MYSQL_DEFAULT_AUTH:
8038 *(static_cast<char **>(const_cast<void *>(arg))) =
8039 mysql->options.extension ? mysql->options.extension->default_auth
8040 : nullptr;
8041 break;
8042 case MYSQL_OPT_SSL_KEY:
8043 *(static_cast<char **>(const_cast<void *>(arg))) = mysql->options.ssl_key;
8044 break;
8045 case MYSQL_OPT_SSL_CERT:
8046 *(static_cast<char **>(const_cast<void *>(arg))) =
8047 mysql->options.ssl_cert;
8048 break;
8049 case MYSQL_OPT_SSL_CA:
8050 *(static_cast<char **>(const_cast<void *>(arg))) = mysql->options.ssl_ca;
8051 break;
8052 case MYSQL_OPT_SSL_CAPATH:
8053 *(static_cast<char **>(const_cast<void *>(arg))) =
8054 mysql->options.ssl_capath;
8055 break;
8056 case MYSQL_OPT_SSL_CIPHER:
8057 *(static_cast<char **>(const_cast<void *>(arg))) =
8058 mysql->options.ssl_cipher;
8059 break;
8060 case MYSQL_OPT_TLS_CIPHERSUITES:
8061 *(static_cast<char **>(const_cast<void *>(arg))) =
8062 mysql->options.extension ? mysql->options.extension->tls_ciphersuites
8063 : nullptr;
8064 break;
8065 case MYSQL_OPT_RETRY_COUNT:
8066 *(const_cast<uint *>(static_cast<const uint *>(arg))) =
8067 mysql->options.extension ? mysql->options.extension->retry_count : 1;
8068 break;
8069 case MYSQL_OPT_TLS_VERSION:
8070 *(static_cast<char **>(const_cast<void *>(arg))) =
8071 mysql->options.extension ? mysql->options.extension->tls_version
8072 : nullptr;
8073 break;
8074 case MYSQL_OPT_SSL_CRL:
8075 *(static_cast<char **>(const_cast<void *>(arg))) =
8076 mysql->options.extension ? mysql->options.extension->ssl_crl
8077 : nullptr;
8078 break;
8079 case MYSQL_OPT_SSL_CRLPATH:
8080 *(static_cast<char **>(const_cast<void *>(arg))) =
8081 mysql->options.extension ? mysql->options.extension->ssl_crlpath
8082 : nullptr;
8083 break;
8084 case MYSQL_SERVER_PUBLIC_KEY:
8085 *(static_cast<char **>(const_cast<void *>(arg))) =
8086 mysql->options.extension
8087 ? mysql->options.extension->server_public_key_path
8088 : nullptr;
8089 break;
8090 case MYSQL_OPT_GET_SERVER_PUBLIC_KEY:
8091 *(const_cast<bool *>(static_cast<const bool *>(arg))) =
8092 mysql->options.extension &&
8093 mysql->options.extension->get_server_public_key;
8094 break;
8095 case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
8096 *(const_cast<bool *>(static_cast<const bool *>(arg))) =
8097 mysql->options.extension &&
8098 mysql->options.extension->enable_cleartext_plugin;
8099 break;
8100 case MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS:
8101 *(const_cast<bool *>(static_cast<const bool *>(arg))) =
8102 (mysql->options.client_flag & CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS) !=
8103 0;
8104 break;
8105
8106 case MYSQL_OPT_MAX_ALLOWED_PACKET:
8107 if (mysql)
8108 *(const_cast<ulong *>(static_cast<const ulong *>(arg))) =
8109 mysql->options.max_allowed_packet;
8110 else
8111 *(const_cast<ulong *>(static_cast<const ulong *>(arg))) =
8112 g_max_allowed_packet;
8113 break;
8114
8115 case MYSQL_OPT_NET_BUFFER_LENGTH:
8116 *(const_cast<ulong *>(static_cast<const ulong *>(arg))) =
8117 g_net_buffer_length;
8118 break;
8119
8120 case MYSQL_OPT_OPTIONAL_RESULTSET_METADATA:
8121 *(const_cast<bool *>(static_cast<const bool *>(arg))) =
8122 (mysql->options.client_flag & CLIENT_OPTIONAL_RESULTSET_METADATA) !=
8123 0;
8124 break;
8125 case MYSQL_OPT_LOAD_DATA_LOCAL_DIR:
8126 *(static_cast<char **>(const_cast<void *>(arg))) =
8127 mysql->options.extension ? mysql->options.extension->load_data_dir
8128 : nullptr;
8129 break;
8130
8131 case MYSQL_OPT_NAMED_PIPE: /* This option is deprecated */
8132 case MYSQL_INIT_COMMAND: /* Cumulative */
8133 case MYSQL_OPT_CONNECT_ATTR_RESET: /* Cumulative */
8134 case MYSQL_OPT_CONNECT_ATTR_DELETE: /* Cumulative */
8135 default:
8136 return 1;
8137 }
8138 return 0;
8139 }
8140
mysql_options4(MYSQL * mysql,enum mysql_option option,const void * arg1,const void * arg2)8141 int STDCALL mysql_options4(MYSQL *mysql, enum mysql_option option,
8142 const void *arg1, const void *arg2) {
8143 DBUG_TRACE;
8144 DBUG_PRINT("enter", ("option: %d", (int)option));
8145
8146 switch (option) {
8147 case MYSQL_OPT_CONNECT_ATTR_ADD: {
8148 const char *key = static_cast<const char *>(arg1);
8149 const char *value = static_cast<const char *>(arg2);
8150 size_t key_len = arg1 ? strlen(key) : 0;
8151 size_t value_len = arg2 ? strlen(value) : 0;
8152 size_t attr_storage_length = key_len + value_len;
8153
8154 /* we can't have a zero length key */
8155 if (!key_len) {
8156 set_mysql_error(mysql, CR_INVALID_PARAMETER_NO, unknown_sqlstate);
8157 return 1;
8158 }
8159
8160 /* calculate the total storage length of the attribute */
8161 attr_storage_length += get_length_store_length(key_len);
8162 attr_storage_length += get_length_store_length(value_len);
8163
8164 ENSURE_EXTENSIONS_PRESENT(&mysql->options);
8165
8166 /*
8167 Throw and error if the maximum combined length of the attribute value
8168 will be greater than the maximum that we can safely transmit.
8169 */
8170 if (attr_storage_length +
8171 mysql->options.extension->connection_attributes_length >
8172 MAX_CONNECTION_ATTR_STORAGE_LENGTH) {
8173 set_mysql_error(mysql, CR_INVALID_PARAMETER_NO, unknown_sqlstate);
8174 return 1;
8175 }
8176
8177 if (!mysql->options.extension->connection_attributes) {
8178 mysql->options.extension->connection_attributes =
8179 new (std::nothrow) My_hash();
8180 if (!mysql->options.extension->connection_attributes) {
8181 set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
8182 return 1;
8183 }
8184 }
8185 if (!mysql->options.extension->connection_attributes->hash
8186 .emplace(key, value)
8187 .second) {
8188 /* can't insert the value */
8189 set_mysql_error(mysql, CR_DUPLICATE_CONNECTION_ATTR, unknown_sqlstate);
8190 return 1;
8191 }
8192
8193 mysql->options.extension->connection_attributes_length +=
8194 attr_storage_length;
8195
8196 break;
8197 }
8198
8199 default:
8200 return 1;
8201 }
8202 return 0;
8203 }
8204
8205 /****************************************************************************
8206 Functions to get information from the MySQL structure
8207 These are functions to make shared libraries more usable.
8208 ****************************************************************************/
8209
8210 /* MYSQL_RES */
mysql_num_rows(MYSQL_RES * res)8211 my_ulonglong STDCALL mysql_num_rows(MYSQL_RES *res) { return res->row_count; }
8212
mysql_num_fields(MYSQL_RES * res)8213 unsigned int STDCALL mysql_num_fields(MYSQL_RES *res) {
8214 return res->field_count;
8215 }
8216
mysql_errno(MYSQL * mysql)8217 uint STDCALL mysql_errno(MYSQL *mysql) {
8218 return mysql ? mysql->net.last_errno : mysql_server_last_errno;
8219 }
8220
mysql_error(MYSQL * mysql)8221 const char *STDCALL mysql_error(MYSQL *mysql) {
8222 return mysql ? mysql->net.last_error : mysql_server_last_error;
8223 }
8224
8225 /**
8226 Read data and its length from a LIST node.
8227
8228 Assumes LIST which stores data blobs in LEX_STRING structures,
8229 where LEX_STRING::str is pointer to the data and LEX_STRING::length
8230 is the length of this data.
8231
8232 If node is NULL then data and length are set to NULL and 0, respectively,
8233 and function returns 0, otherwise, if data has been read from the node,
8234 function returns 1.
8235 */
8236
get_data_and_length(LIST * node,const char ** data,size_t * length)8237 static int get_data_and_length(LIST *node, const char **data, size_t *length) {
8238 DBUG_ASSERT(!node || node->data);
8239 if (data) *data = node ? ((LEX_STRING *)(node->data))->str : nullptr;
8240 if (length) *length = node ? ((LEX_STRING *)(node->data))->length : 0;
8241
8242 return node ? 0 : 1;
8243 }
8244
8245 /**
8246 Get the first state change information received from the server.
8247
8248 @param [in] mysql mysql handle
8249 @param [in] type state change type
8250 @param [out] data buffer to store the data
8251 @param [out] length length of the data
8252
8253 @return
8254 0 - Valid data stored
8255 1 - No data
8256 */
8257
mysql_session_track_get_first(MYSQL * mysql,enum enum_session_state_type type,const char ** data,size_t * length)8258 int STDCALL mysql_session_track_get_first(MYSQL *mysql,
8259 enum enum_session_state_type type,
8260 const char **data, size_t *length) {
8261 STATE_INFO *info = STATE_DATA(mysql);
8262
8263 if (!info || !IS_SESSION_STATE_TYPE(type) ||
8264 !(info->info_list[type].head_node))
8265 return get_data_and_length(nullptr, data, length);
8266
8267 info->info_list[type].current_node = info->info_list[type].head_node;
8268
8269 return mysql_session_track_get_next(mysql, type, data, length);
8270 }
8271
8272 /**
8273 Get the subsequent state change information received from the server.
8274
8275 @param [in] mysql mysql handle
8276 @param [in] type state change type
8277 @param [out] data buffer to store the data
8278 @param [out] length length of the data
8279
8280 @return
8281 0 - Valid data stored
8282 1 - No data
8283 */
8284
mysql_session_track_get_next(MYSQL * mysql,enum enum_session_state_type type,const char ** data,size_t * length)8285 int STDCALL mysql_session_track_get_next(MYSQL *mysql,
8286 enum enum_session_state_type type,
8287 const char **data, size_t *length) {
8288 STATE_INFO *info = STATE_DATA(mysql);
8289 int ret;
8290
8291 if (!info || !IS_SESSION_STATE_TYPE(type) ||
8292 !(info->info_list[type].current_node))
8293 return get_data_and_length(nullptr, data, length);
8294
8295 ret = get_data_and_length(info->info_list[type].current_node, data, length);
8296
8297 info->info_list[type].current_node =
8298 list_rest(info->info_list[type].current_node);
8299
8300 return ret;
8301 }
8302
8303 /*
8304 Get version number for server in a form easy to test on
8305
8306 SYNOPSIS
8307 mysql_get_server_version()
8308 mysql Connection
8309
8310 EXAMPLE
8311 4.1.0-alfa -> 40100
8312
8313 NOTES
8314 We will ensure that a newer server always has a bigger number.
8315
8316 RETURN
8317 Signed number > 323000
8318 Zero if there is no connection
8319 */
8320
mysql_get_server_version(MYSQL * mysql)8321 ulong STDCALL mysql_get_server_version(MYSQL *mysql) {
8322 ulong major = 0, minor = 0, version = 0;
8323
8324 if (mysql->server_version) {
8325 char *pos = mysql->server_version, *end_pos;
8326 major = strtoul(pos, &end_pos, 10);
8327 pos = end_pos + 1;
8328 minor = strtoul(pos, &end_pos, 10);
8329 pos = end_pos + 1;
8330 version = strtoul(pos, &end_pos, 10);
8331 } else {
8332 set_mysql_error(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate);
8333 }
8334
8335 return major * 10000 + minor * 100 + version;
8336 }
8337
8338 /*
8339 mysql_set_character_set function sends SET NAMES cs_name to
8340 the server (which changes character_set_client, character_set_result
8341 and character_set_connection) and updates mysql->charset so other
8342 functions like mysql_real_escape will work correctly.
8343 */
mysql_set_character_set(MYSQL * mysql,const char * cs_name)8344 int STDCALL mysql_set_character_set(MYSQL *mysql, const char *cs_name) {
8345 CHARSET_INFO *cs;
8346 const char *save_csdir = charsets_dir;
8347
8348 if (mysql->options.charset_dir) {
8349 #ifdef MYSQL_SERVER
8350 // Do not change charsets_dir, it is not thread safe.
8351 DBUG_ASSERT(false);
8352 #else
8353 charsets_dir = mysql->options.charset_dir;
8354 #endif
8355 }
8356 if (!mysql->net.vio) {
8357 /* Initialize with automatic OS character set detection. */
8358 mysql_options(mysql, MYSQL_SET_CHARSET_NAME, cs_name);
8359 mysql_init_character_set(mysql);
8360 /*
8361 In case of automatic OS character set detection
8362 mysql_init_character_set changes mysql->options.charset_name
8363 from "auto" to the real character set name.
8364 Reset cs_name to the detected character set name, accordingly.
8365 */
8366 cs_name = mysql->options.charset_name;
8367 }
8368
8369 if (strlen(cs_name) < MY_CS_NAME_SIZE &&
8370 (cs = get_charset_by_csname(cs_name, MY_CS_PRIMARY, MYF(0)))) {
8371 char buff[MY_CS_NAME_SIZE + 10];
8372 charsets_dir = save_csdir;
8373 if (!mysql->net.vio) {
8374 /* If there is no connection yet we don't send "SET NAMES" query */
8375 mysql->charset = cs;
8376 return 0;
8377 }
8378 /* Skip execution of "SET NAMES" for pre-4.1 servers */
8379 if (mysql_get_server_version(mysql) < 40100) return 0;
8380 sprintf(buff, "SET NAMES %s", cs_name);
8381 if (!mysql_real_query(mysql, buff, (ulong)strlen(buff))) {
8382 mysql->charset = cs;
8383 }
8384 } else {
8385 char cs_dir_name[FN_REFLEN];
8386 get_charsets_dir(cs_dir_name);
8387 set_mysql_extended_error(mysql, CR_CANT_READ_CHARSET, unknown_sqlstate,
8388 ER_CLIENT(CR_CANT_READ_CHARSET), cs_name,
8389 cs_dir_name);
8390 }
8391 charsets_dir = save_csdir;
8392 return mysql->net.last_errno;
8393 }
8394
8395 /**
8396 Client authentication plugin that does native MySQL authentication
8397 using a 20-byte (4.1+) scramble
8398
8399 @param vio the channel to operate on
8400 @param mysql the MYSQL structure to operate on
8401
8402 @retval -1 ::CR_OK : Success
8403 @retval 1 ::CR_ERROR : error reading
8404 @retval 2012 ::CR_SERVER_HANDSHAKE_ERR : malformed handshake data
8405 */
native_password_auth_client(MYSQL_PLUGIN_VIO * vio,MYSQL * mysql)8406 static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) {
8407 int pkt_len;
8408 uchar *pkt;
8409
8410 DBUG_TRACE;
8411
8412 /* read the scramble */
8413 if ((pkt_len = vio->read_packet(vio, &pkt)) < 0) return CR_ERROR;
8414
8415 if (pkt_len != SCRAMBLE_LENGTH + 1) return CR_SERVER_HANDSHAKE_ERR;
8416
8417 /* save it in MYSQL */
8418 memcpy(mysql->scramble, pkt, SCRAMBLE_LENGTH);
8419 mysql->scramble[SCRAMBLE_LENGTH] = 0;
8420
8421 if (mysql->passwd[0]) {
8422 char scrambled[SCRAMBLE_LENGTH + 1];
8423 DBUG_PRINT("info", ("sending scramble"));
8424 scramble(scrambled, (char *)pkt, mysql->passwd);
8425 if (vio->write_packet(vio, (uchar *)scrambled, SCRAMBLE_LENGTH))
8426 return CR_ERROR;
8427 } else {
8428 DBUG_PRINT("info", ("no password"));
8429 if (vio->write_packet(vio, nullptr, 0)) /* no password */
8430 return CR_ERROR;
8431 }
8432
8433 return CR_OK;
8434 }
8435
8436 /**
8437 Client authentication plugin that does native MySQL authentication
8438 in a nonblocking way.
8439
8440 @param[in] vio the channel to operate on
8441 @param[in] mysql the MYSQL structure to operate on
8442 @param[out] result CR_OK : Success, CR_ERROR : error reading,
8443 CR_SERVER_HANDSHAKE_ERR : malformed handshake data
8444
8445 @retval NET_ASYNC_NOT_READY authentication not yet complete
8446 @retval NET_ASYNC_COMPLETE authentication done
8447 */
native_password_auth_client_nonblocking(MYSQL_PLUGIN_VIO * vio,MYSQL * mysql,int * result)8448 static net_async_status native_password_auth_client_nonblocking(
8449 MYSQL_PLUGIN_VIO *vio, MYSQL *mysql, int *result) {
8450 DBUG_TRACE;
8451 int io_result;
8452 uchar *pkt;
8453 mysql_async_auth *ctx = ASYNC_DATA(mysql)->connect_context->auth_context;
8454
8455 switch (static_cast<client_auth_native_password_plugin_status>(
8456 ctx->client_auth_plugin_state)) {
8457 case client_auth_native_password_plugin_status::NATIVE_READING_PASSWORD:
8458 if (((MCPVIO_EXT *)vio)->mysql_change_user) {
8459 /* mysql_change_user_nonblocking not implemented yet. */
8460 DBUG_ASSERT(false);
8461 } else {
8462 /* read the scramble */
8463 net_async_status status =
8464 vio->read_packet_nonblocking(vio, &pkt, &io_result);
8465 if (status == NET_ASYNC_NOT_READY) {
8466 return NET_ASYNC_NOT_READY;
8467 }
8468
8469 if (io_result < 0) {
8470 *result = CR_ERROR;
8471 return NET_ASYNC_COMPLETE;
8472 }
8473
8474 if (io_result != SCRAMBLE_LENGTH + 1) {
8475 *result = CR_SERVER_HANDSHAKE_ERR;
8476 return NET_ASYNC_COMPLETE;
8477 }
8478
8479 /* save it in MYSQL */
8480 memcpy(mysql->scramble, pkt, SCRAMBLE_LENGTH);
8481 mysql->scramble[SCRAMBLE_LENGTH] = 0;
8482 }
8483 ctx->client_auth_plugin_state = (int)
8484 client_auth_native_password_plugin_status::NATIVE_WRITING_RESPONSE;
8485
8486 /* fallthrough */
8487
8488 case client_auth_native_password_plugin_status::NATIVE_WRITING_RESPONSE:
8489 if (mysql->passwd[0]) {
8490 char scrambled[SCRAMBLE_LENGTH + 1];
8491 DBUG_PRINT("info", ("sending scramble"));
8492 scramble(scrambled, (char *)pkt, mysql->passwd);
8493 net_async_status status = vio->write_packet_nonblocking(
8494 vio, (uchar *)scrambled, SCRAMBLE_LENGTH, &io_result);
8495 if (status == NET_ASYNC_NOT_READY) {
8496 return NET_ASYNC_NOT_READY;
8497 }
8498
8499 if (io_result < 0) {
8500 *result = CR_ERROR;
8501 return NET_ASYNC_COMPLETE;
8502 }
8503 } else {
8504 DBUG_PRINT("info", ("no password"));
8505 net_async_status status = vio->write_packet_nonblocking(
8506 vio, nullptr, 0, &io_result); /* no password */
8507
8508 if (status == NET_ASYNC_NOT_READY) {
8509 return NET_ASYNC_NOT_READY;
8510 }
8511
8512 if (io_result < 0) {
8513 *result = CR_ERROR;
8514 return NET_ASYNC_COMPLETE;
8515 }
8516 }
8517 break;
8518 default:
8519 assert(0);
8520 }
8521
8522 *result = CR_OK;
8523 return NET_ASYNC_COMPLETE;
8524 }
8525 /* clang-format off */
8526 /**
8527 @page page_protocol_connection_phase_authentication_methods_clear_text_password Clear text client plugin
8528
8529 <ul>
8530 <li>
8531 This client side plugin is used by a number of server plugins:
8532 LDAP (*authentication_ldap_simple*) and PAM (*authentication_pam*) to name a few.
8533 </li>
8534 <li>
8535 The client name is *mysql_clear_password*
8536 </li>
8537 <li>
8538 Client side requires nothing from the server. But the server generates
8539 and sends a 20-byte
8540 @ref page_protocol_connection_phase_authentication_methods_native_password_authentication
8541 compatible scramble.
8542 </li>
8543 <li>
8544 Client side sends the password in clear text to the server
8545 </li>
8546 </ul>
8547
8548 @startuml
8549 Server->Client: 20 bytes of scramble to be ignored
8550 Client->Server: The clear text password. null terminated.
8551 @enduml
8552
8553 @note
8554 Sending the scramble is not necessary for the clear text
8555 method, but, since the server always initiates the exchange by
8556 sending @ref page_protocol_connection_phase_packets_protocol_handshake
8557 and that one has a placeholder for authentication plugin dependent data the
8558 server does fill that space with a scramble should it come to pass that
8559 it will back down to
8560 @ref page_protocol_connection_phase_authentication_methods_native_password_authentication.
8561 This is also why it's OK no to specifically read this in
8562 @ref clear_password_auth_client since it's already read as a part of
8563 the initial exchange.
8564
8565
8566 @sa ::clear_password_auth_client, ::server_mpvio_write_packet,
8567 ::send_server_handshake_packet
8568 */
8569 /* clang-format on */
8570
8571 /**
8572 The main function of the mysql_clear_password authentication plugin.
8573 */
8574
clear_password_auth_client(MYSQL_PLUGIN_VIO * vio,MYSQL * mysql)8575 static int clear_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) {
8576 int res;
8577
8578 /* send password in clear text */
8579 res = vio->write_packet(vio, (const unsigned char *)mysql->passwd,
8580 (int)strlen(mysql->passwd) + 1);
8581
8582 return res ? CR_ERROR : CR_OK;
8583 }
8584