1#include <ma_global.h>
2#include <ma_sys.h>
3#include <errmsg.h>
4#include <string.h>
5#include <ma_common.h>
6#include <mysql/client_plugin.h>
7
8typedef struct st_mysql_client_plugin_AUTHENTICATION auth_plugin_t;
9static int client_mpvio_write_packet(struct st_plugin_vio*, const uchar*, size_t);
10static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql);
11static int dummy_fallback_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql __attribute__((unused)));
12extern void read_user_name(char *name);
13extern char *ma_send_connect_attr(MYSQL *mysql, unsigned char *buffer);
14extern int ma_read_ok_packet(MYSQL *mysql, uchar *pos, ulong length);
15
16typedef struct {
17 int (*read_packet)(struct st_plugin_vio *vio, uchar **buf);
18 int (*write_packet)(struct st_plugin_vio *vio, const uchar *pkt, size_t pkt_len);
19 void (*info)(struct st_plugin_vio *vio, struct st_plugin_vio_info *info);
20 /* -= end of MYSQL_PLUGIN_VIO =- */
21 MYSQL *mysql;
22 auth_plugin_t *plugin; /**< what plugin we're under */
23 const char *db;
24 struct {
25 uchar *pkt; /**< pointer into NET::buff */
26 uint pkt_len;
27 } cached_server_reply;
28 uint packets_read, packets_written; /**< counters for send/received packets */
29 my_bool mysql_change_user; /**< if it's mysql_change_user() */
30 int last_read_packet_len; /**< the length of the last *read* packet */
31} MCPVIO_EXT;
32/*
33#define compile_time_assert(A) \
34do {\
35 typedef char constraint[(A) ? 1 : -1];\
36} while (0);
37*/
38
39auth_plugin_t mysql_native_password_client_plugin=
40{
41 MYSQL_CLIENT_AUTHENTICATION_PLUGIN,
42 MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION,
43 native_password_plugin_name,
44 "R.J.Silk, Sergei Golubchik",
45 "Native MySQL authentication",
46 {1, 0, 0},
47 "LGPL",
48 NULL,
49 NULL,
50 NULL,
51 NULL,
52 native_password_auth_client
53};
54
55
56static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
57{
58 int pkt_len;
59 uchar *pkt;
60
61 if (((MCPVIO_EXT *)vio)->mysql_change_user)
62 {
63 /*
64 in mysql_change_user() the client sends the first packet.
65 we use the old scramble.
66 */
67 pkt= (uchar*)mysql->scramble_buff;
68 pkt_len= SCRAMBLE_LENGTH + 1;
69 }
70 else
71 {
72 /* read the scramble */
73 if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)
74 return CR_ERROR;
75
76 if (pkt_len != SCRAMBLE_LENGTH + 1)
77 return CR_SERVER_HANDSHAKE_ERR;
78
79 /* save it in MYSQL */
80 memmove(mysql->scramble_buff, pkt, SCRAMBLE_LENGTH);
81 mysql->scramble_buff[SCRAMBLE_LENGTH] = 0;
82 }
83
84 if (mysql && mysql->passwd[0])
85 {
86 char scrambled[SCRAMBLE_LENGTH + 1];
87 ma_scramble_41((uchar *)scrambled, (char*)pkt, mysql->passwd);
88 if (vio->write_packet(vio, (uchar*)scrambled, SCRAMBLE_LENGTH))
89 return CR_ERROR;
90 }
91 else
92 if (vio->write_packet(vio, 0, 0)) /* no password */
93 return CR_ERROR;
94
95 return CR_OK;
96}
97
98auth_plugin_t dummy_fallback_client_plugin=
99{
100 MYSQL_CLIENT_AUTHENTICATION_PLUGIN,
101 MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION,
102 "dummy_fallback_auth",
103 "Sergei Golubchik",
104 "Dummy fallback plugin",
105 {1, 0, 0},
106 "LGPL",
107 NULL,
108 NULL,
109 NULL,
110 NULL,
111 dummy_fallback_auth_client
112};
113
114
115static int dummy_fallback_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql __attribute__((unused)))
116{
117 char last_error[MYSQL_ERRMSG_SIZE];
118 unsigned int i, last_errno= ((MCPVIO_EXT *)vio)->mysql->net.last_errno;
119 if (last_errno)
120 strncpy(last_error, ((MCPVIO_EXT *)vio)->mysql->net.last_error,
121 sizeof(last_error));
122
123 /* safety-wise we only do 10 round-trips */
124 for (i=0; i < 10; i++)
125 {
126 uchar *pkt;
127 if (vio->read_packet(vio, &pkt) < 0)
128 break;
129 if (vio->write_packet(vio, 0, 0))
130 break;
131 }
132 if (last_errno)
133 strncpy(((MCPVIO_EXT *)vio)->mysql->net.last_error, last_error,
134 sizeof(((MCPVIO_EXT *)vio)->mysql->net.last_error));
135 return CR_ERROR;
136}
137
138static int send_change_user_packet(MCPVIO_EXT *mpvio,
139 const uchar *data, int data_len)
140{
141 MYSQL *mysql= mpvio->mysql;
142 char *buff, *end;
143 int res= 1;
144 size_t conn_attr_len= (mysql->options.extension) ?
145 mysql->options.extension->connect_attrs_len : 0;
146
147 buff= malloc(USERNAME_LENGTH+1 + data_len+1 + NAME_LEN+1 + 2 + NAME_LEN+1 + 9 + conn_attr_len);
148
149 end= ma_strmake(buff, mysql->user, USERNAME_LENGTH) + 1;
150
151 if (!data_len)
152 *end++= 0;
153 else
154 {
155 if (mysql->client_flag & CLIENT_SECURE_CONNECTION)
156 {
157 DBUG_ASSERT(data_len <= 255);
158 if (data_len > 255)
159 {
160 my_set_error(mysql, CR_MALFORMED_PACKET, SQLSTATE_UNKNOWN, 0);
161 goto error;
162 }
163 *end++= data_len;
164 }
165 else
166 {
167 DBUG_ASSERT(data_len == SCRAMBLE_LENGTH_323 + 1);
168 DBUG_ASSERT(data[SCRAMBLE_LENGTH_323] == 0);
169 }
170 memcpy(end, data, data_len);
171 end+= data_len;
172 }
173 end= ma_strmake(end, mpvio->db ? mpvio->db : "", NAME_LEN) + 1;
174
175 if (mysql->server_capabilities & CLIENT_PROTOCOL_41)
176 {
177 int2store(end, (ushort) mysql->charset->nr);
178 end+= 2;
179 }
180
181 if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
182 end= ma_strmake(end, mpvio->plugin->name, NAME_LEN) + 1;
183
184 end= ma_send_connect_attr(mysql, (unsigned char *)end);
185
186 res= ma_simple_command(mysql, COM_CHANGE_USER,
187 buff, (ulong)(end-buff), 1, NULL);
188
189error:
190 free(buff);
191 return res;
192}
193
194
195
196static int send_client_reply_packet(MCPVIO_EXT *mpvio,
197 const uchar *data, int data_len)
198{
199 MYSQL *mysql= mpvio->mysql;
200 NET *net= &mysql->net;
201 char *buff, *end;
202 size_t conn_attr_len= (mysql->options.extension) ?
203 mysql->options.extension->connect_attrs_len : 0;
204
205 /* see end= buff+32 below, fixed size of the packet is 32 bytes */
206 buff= malloc(33 + USERNAME_LENGTH + data_len + NAME_LEN + NAME_LEN + conn_attr_len + 9);
207 end= buff;
208
209 mysql->client_flag|= mysql->options.client_flag;
210 mysql->client_flag|= CLIENT_CAPABILITIES;
211
212 if (mysql->client_flag & CLIENT_MULTI_STATEMENTS)
213 mysql->client_flag|= CLIENT_MULTI_RESULTS;
214
215#if defined(HAVE_TLS) && !defined(EMBEDDED_LIBRARY)
216 if (mysql->options.ssl_key || mysql->options.ssl_cert ||
217 mysql->options.ssl_ca || mysql->options.ssl_capath ||
218 mysql->options.ssl_cipher || mysql->options.use_ssl ||
219 (mysql->options.client_flag & CLIENT_SSL_VERIFY_SERVER_CERT))
220 mysql->options.use_ssl= 1;
221 if (mysql->options.use_ssl)
222 mysql->client_flag|= CLIENT_SSL;
223#endif /* HAVE_TLS && !EMBEDDED_LIBRARY*/
224 if (mpvio->db)
225 mysql->client_flag|= CLIENT_CONNECT_WITH_DB;
226
227 /* if server doesn't support SSL and verification of server certificate
228 was set to mandatory, we need to return an error */
229 if (mysql->options.use_ssl && !(mysql->server_capabilities & CLIENT_SSL))
230 {
231 if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) ||
232 (mysql->options.extension && (mysql->options.extension->tls_fp ||
233 mysql->options.extension->tls_fp_list)))
234 {
235 my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
236 ER(CR_SSL_CONNECTION_ERROR),
237 "SSL is required, but the server does not support it");
238 goto error;
239 }
240 }
241
242
243 /* Remove options that server doesn't support */
244 mysql->client_flag= mysql->client_flag &
245 (~(CLIENT_COMPRESS | CLIENT_SSL | CLIENT_PROTOCOL_41)
246 | mysql->server_capabilities);
247
248#ifndef HAVE_COMPRESS
249 mysql->client_flag&= ~CLIENT_COMPRESS;
250#endif
251
252 if (mysql->client_flag & CLIENT_PROTOCOL_41)
253 {
254 /* 4.1 server and 4.1 client has a 32 byte option flag */
255 if (!(mysql->server_capabilities & CLIENT_MYSQL))
256 mysql->client_flag&= ~CLIENT_MYSQL;
257 int4store(buff,mysql->client_flag);
258 int4store(buff+4, net->max_packet_size);
259 buff[8]= (char) mysql->charset->nr;
260 memset(buff + 9, 0, 32-9);
261 if (!(mysql->server_capabilities & CLIENT_MYSQL))
262 {
263 mysql->extension->mariadb_client_flag = MARIADB_CLIENT_SUPPORTED_FLAGS >> 32;
264 int4store(buff + 28, mysql->extension->mariadb_client_flag);
265 }
266 end= buff+32;
267 }
268 else
269 {
270 int2store(buff, mysql->client_flag);
271 int3store(buff+2, net->max_packet_size);
272 end= buff+5;
273 }
274#ifdef HAVE_TLS
275 if (mysql->options.ssl_key ||
276 mysql->options.ssl_cert ||
277 mysql->options.ssl_ca ||
278 mysql->options.ssl_capath ||
279 mysql->options.ssl_cipher
280#ifdef CRL_IMPLEMENTED
281 || (mysql->options.extension &&
282 (mysql->options.extension->ssl_crl ||
283 mysql->options.extension->ssl_crlpath))
284#endif
285 )
286 mysql->options.use_ssl= 1;
287 if (mysql->options.use_ssl &&
288 (mysql->client_flag & CLIENT_SSL))
289 {
290 /*
291 Send mysql->client_flag, max_packet_size - unencrypted otherwise
292 the server does not know we want to do SSL
293 */
294 if (ma_net_write(net, (unsigned char *)buff, (size_t) (end-buff)) || ma_net_flush(net))
295 {
296 my_set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN,
297 ER(CR_SERVER_LOST_EXTENDED),
298 "sending connection information to server",
299 errno);
300 goto error;
301 }
302 if (ma_pvio_start_ssl(mysql->net.pvio))
303 goto error;
304 }
305#endif /* HAVE_TLS */
306
307 /* This needs to be changed as it's not useful with big packets */
308 if (mysql->user && mysql->user[0])
309 ma_strmake(end, mysql->user, USERNAME_LENGTH);
310 else
311 read_user_name(end);
312
313 /* We have to handle different version of handshake here */
314 end+= strlen(end) + 1;
315 if (data_len)
316 {
317 if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
318 {
319 *end++= data_len;
320 memcpy(end, data, data_len);
321 end+= data_len;
322 }
323 else
324 {
325 DBUG_ASSERT(data_len == SCRAMBLE_LENGTH_323 + 1); /* incl. \0 at the end */
326 memcpy(end, data, data_len);
327 end+= data_len;
328 }
329 }
330 else
331 *end++= 0;
332
333 /* Add database if needed */
334 if (mpvio->db && (mysql->server_capabilities & CLIENT_CONNECT_WITH_DB))
335 {
336 end= ma_strmake(end, mpvio->db, NAME_LEN) + 1;
337 mysql->db= strdup(mpvio->db);
338 }
339
340 if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
341 end= ma_strmake(end, mpvio->plugin->name, NAME_LEN) + 1;
342
343 end= ma_send_connect_attr(mysql, (unsigned char *)end);
344
345 /* Write authentication package */
346 if (ma_net_write(net, (unsigned char *)buff, (size_t) (end-buff)) || ma_net_flush(net))
347 {
348 my_set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN,
349 ER(CR_SERVER_LOST_EXTENDED),
350 "sending authentication information",
351 errno);
352 goto error;
353 }
354 free(buff);
355 return 0;
356
357error:
358 free(buff);
359 return 1;
360}
361
362/**
363 vio->read_packet() callback method for client authentication plugins
364
365 This function is called by a client authentication plugin, when it wants
366 to read data from the server.
367*/
368
369static int client_mpvio_read_packet(struct st_plugin_vio *mpv, uchar **buf)
370{
371 MCPVIO_EXT *mpvio= (MCPVIO_EXT*)mpv;
372 MYSQL *mysql= mpvio->mysql;
373 ulong pkt_len;
374
375 /* there are cached data left, feed it to a plugin */
376 if (mpvio->cached_server_reply.pkt)
377 {
378 *buf= mpvio->cached_server_reply.pkt;
379 mpvio->cached_server_reply.pkt= 0;
380 mpvio->packets_read++;
381 return mpvio->cached_server_reply.pkt_len;
382 }
383
384 if (mpvio->packets_read == 0)
385 {
386 /*
387 the server handshake packet came from the wrong plugin,
388 or it's mysql_change_user(). Either way, there is no data
389 for a plugin to read. send a dummy packet to the server
390 to initiate a dialog.
391 */
392 if (client_mpvio_write_packet(mpv, 0, 0))
393 return (int)packet_error;
394 }
395
396 /* otherwise read the data */
397 if ((pkt_len= ma_net_safe_read(mysql)) == packet_error)
398 return (int)packet_error;
399
400 mpvio->last_read_packet_len= pkt_len;
401 *buf= mysql->net.read_pos;
402
403 /* was it a request to change plugins ? */
404 if (pkt_len && **buf == 254)
405 return (int)packet_error; /* if yes, this plugin shan't continue */
406
407 /*
408 the server sends \1\255 or \1\254 instead of just \255 or \254 -
409 for us to not confuse it with an error or "change plugin" packets.
410 We remove this escaping \1 here.
411
412 See also server_mpvio_write_packet() where the escaping is done.
413 */
414 if (pkt_len && **buf == 1)
415 {
416 (*buf)++;
417 pkt_len--;
418 }
419 mpvio->packets_read++;
420 return pkt_len;
421}
422
423/**
424 vio->write_packet() callback method for client authentication plugins
425
426 This function is called by a client authentication plugin, when it wants
427 to send data to the server.
428
429 It transparently wraps the data into a change user or authentication
430 handshake packet, if necessary.
431*/
432
433static int client_mpvio_write_packet(struct st_plugin_vio *mpv,
434 const uchar *pkt, size_t pkt_len)
435{
436 int res;
437 MCPVIO_EXT *mpvio= (MCPVIO_EXT*)mpv;
438
439 if (mpvio->packets_written == 0)
440 {
441 if (mpvio->mysql_change_user)
442 res= send_change_user_packet(mpvio, pkt, (int)pkt_len);
443 else
444 res= send_client_reply_packet(mpvio, pkt, (int)pkt_len);
445 }
446 else
447 {
448 NET *net= &mpvio->mysql->net;
449 if (mpvio->mysql->thd)
450 res= 1; /* no chit-chat in embedded */
451 else
452 res= ma_net_write(net, (unsigned char *)pkt, pkt_len) || ma_net_flush(net);
453 }
454
455 if (res)
456 {
457 /* don't overwrite errors */
458 if (!mysql_errno(mpvio->mysql))
459 my_set_error(mpvio->mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN,
460 ER(CR_SERVER_LOST_EXTENDED),
461 "sending authentication information",
462 errno);
463 }
464 mpvio->packets_written++;
465 return res;
466}
467
468/**
469 fills MYSQL_PLUGIN_VIO_INFO structure with the information about the
470 connection
471*/
472
473void mpvio_info(MARIADB_PVIO *pvio, MYSQL_PLUGIN_VIO_INFO *info)
474{
475 memset(info, 0, sizeof(*info));
476 switch (pvio->type) {
477 case PVIO_TYPE_SOCKET:
478 info->protocol= MYSQL_VIO_TCP;
479 ma_pvio_get_handle(pvio, &info->socket);
480 return;
481 case PVIO_TYPE_UNIXSOCKET:
482 info->protocol= MYSQL_VIO_SOCKET;
483 ma_pvio_get_handle(pvio, &info->socket);
484 return;
485 /*
486 case VIO_TYPE_SSL:
487 {
488 struct sockaddr addr;
489 SOCKET_SIZE_TYPE addrlen= sizeof(addr);
490 if (getsockname(vio->sd, &addr, &addrlen))
491 return;
492 info->protocol= addr.sa_family == AF_UNIX ?
493 MYSQL_VIO_SOCKET : MYSQL_VIO_TCP;
494 info->socket= vio->sd;
495 return;
496 }
497 */
498#ifdef _WIN32
499 /*
500 case VIO_TYPE_NAMEDPIPE:
501 info->protocol= MYSQL_VIO_PIPE;
502 info->handle= vio->hPipe;
503 return;
504 */
505/* not supported yet
506 case VIO_TYPE_SHARED_MEMORY:
507 info->protocol= MYSQL_VIO_MEMORY;
508 info->handle= vio->handle_file_map;
509 return;
510*/
511#endif
512 default: DBUG_ASSERT(0);
513 }
514}
515
516static void client_mpvio_info(MYSQL_PLUGIN_VIO *vio,
517 MYSQL_PLUGIN_VIO_INFO *info)
518{
519 MCPVIO_EXT *mpvio= (MCPVIO_EXT*)vio;
520 mpvio_info(mpvio->mysql->net.pvio, info);
521}
522
523/**
524 Client side of the plugin driver authentication.
525
526 @note this is used by both the mysql_real_connect and mysql_change_user
527
528 @param mysql mysql
529 @param data pointer to the plugin auth data (scramble) in the
530 handshake packet
531 @param data_len the length of the data
532 @param data_plugin a plugin that data were prepared for
533 or 0 if it's mysql_change_user()
534 @param db initial db to use, can be 0
535
536 @retval 0 ok
537 @retval 1 error
538*/
539
540int run_plugin_auth(MYSQL *mysql, char *data, uint data_len,
541 const char *data_plugin, const char *db)
542{
543 const char *auth_plugin_name;
544 auth_plugin_t *auth_plugin;
545 MCPVIO_EXT mpvio;
546 ulong pkt_length;
547 int res;
548
549 /* determine the default/initial plugin to use */
550 if (mysql->options.extension && mysql->options.extension->default_auth &&
551 mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
552 {
553 auth_plugin_name= mysql->options.extension->default_auth;
554 if (!(auth_plugin= (auth_plugin_t*) mysql_client_find_plugin(mysql,
555 auth_plugin_name, MYSQL_CLIENT_AUTHENTICATION_PLUGIN)))
556 auth_plugin= &dummy_fallback_client_plugin;
557 }
558 else
559 {
560 if (mysql->server_capabilities & CLIENT_PROTOCOL_41)
561 auth_plugin= &mysql_native_password_client_plugin;
562 else
563 {
564 if (!(auth_plugin= (auth_plugin_t*)mysql_client_find_plugin(mysql,
565 "mysql_old_password", MYSQL_CLIENT_AUTHENTICATION_PLUGIN)))
566 auth_plugin= &dummy_fallback_client_plugin;
567 }
568 auth_plugin_name= auth_plugin->name;
569 }
570
571 mysql->net.last_errno= 0; /* just in case */
572
573 if (data_plugin && strcmp(data_plugin, auth_plugin_name))
574 {
575 /* data was prepared for a different plugin, don't show it to this one */
576 data= 0;
577 data_len= 0;
578 }
579
580 mpvio.mysql_change_user= data_plugin == 0;
581 mpvio.cached_server_reply.pkt= (uchar*)data;
582 mpvio.cached_server_reply.pkt_len= data_len;
583 mpvio.read_packet= client_mpvio_read_packet;
584 mpvio.write_packet= client_mpvio_write_packet;
585 mpvio.info= client_mpvio_info;
586 mpvio.mysql= mysql;
587 mpvio.packets_read= mpvio.packets_written= 0;
588 mpvio.db= db;
589
590retry:
591 mpvio.plugin= auth_plugin;
592
593 mysql->net.read_pos[0]= 0;
594 res= auth_plugin->authenticate_user((struct st_plugin_vio *)&mpvio, mysql);
595
596 if ((res == CR_ERROR && !mysql->net.buff) ||
597 (res > CR_OK && mysql->net.read_pos[0] != 254))
598 {
599 /*
600 the plugin returned an error. write it down in mysql,
601 unless the error code is CR_ERROR and mysql->net.last_errno
602 is already set (the plugin has done it)
603 */
604 if (res > CR_ERROR)
605 my_set_error(mysql, res, SQLSTATE_UNKNOWN, 0);
606 else
607 if (!mysql->net.last_errno) {
608 my_set_error(mysql, CR_UNKNOWN_ERROR, SQLSTATE_UNKNOWN, 0);
609 }
610 return 1;
611 }
612
613 /* read the OK packet (or use the cached value in mysql->net.read_pos */
614 if (res == CR_OK)
615 pkt_length= ma_net_safe_read(mysql);
616 else /* res == CR_OK_HANDSHAKE_COMPLETE or an error */
617 pkt_length= mpvio.last_read_packet_len;
618
619 if (pkt_length == packet_error)
620 {
621 if (mysql->net.last_errno == CR_SERVER_LOST)
622 my_set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN,
623 ER(CR_SERVER_LOST_EXTENDED),
624 "reading authorization packet",
625 errno);
626 return 1;
627 }
628 if (mysql->net.read_pos[0] == 254)
629 {
630 /* The server asked to use a different authentication plugin */
631 if (pkt_length == 1)
632 {
633 /* old "use short scramble" packet */
634 auth_plugin_name= old_password_plugin_name;
635 mpvio.cached_server_reply.pkt= (uchar*)mysql->scramble_buff;
636 mpvio.cached_server_reply.pkt_len= SCRAMBLE_LENGTH + 1;
637 }
638 else
639 {
640 /* new "use different plugin" packet */
641 uint len;
642 auth_plugin_name= (char*)mysql->net.read_pos + 1;
643 len= (uint)strlen(auth_plugin_name); /* safe as ma_net_read always appends \0 */
644 mpvio.cached_server_reply.pkt_len= pkt_length - len - 2;
645 mpvio.cached_server_reply.pkt= mysql->net.read_pos + len + 2;
646 }
647 if (!(auth_plugin= (auth_plugin_t *) mysql_client_find_plugin(mysql,
648 auth_plugin_name, MYSQL_CLIENT_AUTHENTICATION_PLUGIN)))
649 auth_plugin= &dummy_fallback_client_plugin;
650
651 goto retry;
652
653 }
654 /*
655 net->read_pos[0] should always be 0 here if the server implements
656 the protocol correctly
657 */
658 if (mysql->net.read_pos[0] == 0)
659 return ma_read_ok_packet(mysql, mysql->net.read_pos + 1, pkt_length);
660 return 1;
661}
662
663