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&lt;4&gt;"</td>
3963     <td>client_flag</td>
3964     <td>\ref group_cs_capabilities_flags</td></tr>
3965   <tr><td>@ref a_protocol_type_int4 "int&lt;4&gt;"</td>
3966     <td>max_packet_size</td>
3967     <td>maximum packet size</td></tr>
3968   <tr><td>@ref a_protocol_type_int1 "int&lt;1&gt;"</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&lt;2&gt;"</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&lt;3&gt;"</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&lt;2&gt;"</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&lt;3&gt;"</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&lt;NUL&gt;"</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&lt;NUL&gt;"</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&lt;NUL&gt;"</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&lt;EOF&gt;"</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&lt;4&gt;"</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&lt;4&gt;"</td>
4535     <td>max_packet_size</td>
4536     <td>maximum packet size</td></tr>
4537   <tr><td>@ref a_protocol_type_int1 "int&lt;1&gt;"</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&lt;NUL&gt;"</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&lt;length&gt;"</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&lt;1&gt;"</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&lt;length&gt;"</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&lt;NUL&gt;"</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&lt;NUL&gt;"</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&lt;lenenc&gt;"</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&lt;lenenc&gt;"</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&lt;lenenc&gt;"</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&lt;1&gt;"</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