1 /* Copyright (c) 2000, 2021, Oracle and/or its affiliates.
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 @file
30
31 This file is the net layer API for the MySQL client/server protocol.
32
33 Write and read of logical packets to/from socket.
34
35 Writes are cached into net_buffer_length big packets.
36 Read packets are reallocated dynamicly when reading big packets.
37 Each logical packet has the following pre-info:
38 3 byte length & 1 byte package-number.
39
40 This file needs to be written in C as it's used by the libmysql client as a
41 C file.
42 */
43
44 /*
45 HFTODO this must be hidden if we don't want client capabilities in
46 embedded library
47 */
48 #include <my_global.h>
49 #include <mysql.h>
50 #include <mysql_com.h>
51 #include <mysqld_error.h>
52 #include <my_sys.h>
53 #include <m_string.h>
54 #include <violite.h>
55 #include <signal.h>
56 #include <errno.h>
57 #include "probes_mysql.h"
58 /* key_memory_NET_buff */
59 #include "mysqld.h"
60
61 #include <algorithm>
62
63 using std::min;
64 using std::max;
65
66 PSI_memory_key key_memory_NET_buff;
67 PSI_memory_key key_memory_NET_compress_packet;
68
69 #ifdef EMBEDDED_LIBRARY
70 #undef MYSQL_SERVER
71 #undef MYSQL_CLIENT
72 #define MYSQL_CLIENT
73 #endif /*EMBEDDED_LIBRARY */
74
75 // Workaround for compiler bug
76 // ld.so.1: mysqld: fatal: relocation error: file sql/mysqld:
77 // symbol OPENSSL_sk_new_null: referenced symbol not found
78 // openssl/safestack.h has lots of pragma weak <function>
79 // Taking the address of the function solves the problem.
80 // (note, do not make it static, it may be optimized away)
81 #if defined(HAVE_TLSv13) && defined(__SUNPRO_CC)
82 #include <openssl/ssl.h>
83 void *address_of_sk_new_null = &OPENSSL_sk_new_null;
84 #endif
85
86 /*
87 The following handles the differences when this is linked between the
88 client and the server.
89
90 This gives an error if a too big packet is found.
91 The server can change this, but because the client can't normally do this
92 the client should have a bigger max_allowed_packet.
93 */
94
95 #ifdef MYSQL_SERVER
96 /*
97 The following variables/functions should really not be declared
98 extern, but as it's hard to include sql_class.h here, we have to
99 live with this for a while.
100 */
101 extern void query_cache_insert(const char *packet, ulong length,
102 unsigned pkt_nr);
103 extern void thd_increment_bytes_sent(size_t length);
104 extern void thd_increment_bytes_received(size_t length);
105
106 /* Additional instrumentation hooks for the server */
107 #include "mysql_com_server.h"
108 #endif
109
110 #define VIO_SOCKET_ERROR ((size_t) -1)
111
112 static my_bool net_write_buff(NET *, const uchar *, size_t);
113
114 /** Init with packet info. */
115
my_net_init(NET * net,Vio * vio)116 my_bool my_net_init(NET *net, Vio* vio)
117 {
118 DBUG_ENTER("my_net_init");
119 net->vio = vio;
120 my_net_local_init(net); /* Set some limits */
121 if (!(net->buff=(uchar*) my_malloc(key_memory_NET_buff,
122 (size_t) net->max_packet+
123 NET_HEADER_SIZE + COMP_HEADER_SIZE,
124 MYF(MY_WME))))
125 DBUG_RETURN(1);
126 net->buff_end=net->buff+net->max_packet;
127 net->error=0; net->return_status=0;
128 net->pkt_nr=net->compress_pkt_nr=0;
129 net->write_pos=net->read_pos = net->buff;
130 net->last_error[0]=0;
131 net->compress=0; net->reading_or_writing=0;
132 net->where_b = net->remain_in_buf=0;
133 net->last_errno=0;
134 net->unused= 0;
135 #ifdef MYSQL_SERVER
136 net->extension= NULL;
137 #endif
138
139 if (vio)
140 {
141 /* For perl DBI/DBD. */
142 net->fd= vio_fd(vio);
143 vio_fastsend(vio);
144 }
145 DBUG_RETURN(0);
146 }
147
148
net_end(NET * net)149 void net_end(NET *net)
150 {
151 DBUG_ENTER("net_end");
152 my_free(net->buff);
153 net->buff=0;
154 DBUG_VOID_RETURN;
155 }
156
net_claim_memory_ownership(NET * net)157 void net_claim_memory_ownership(NET *net)
158 {
159 my_claim(net->buff);
160 }
161
162 /** Realloc the packet buffer. */
163
net_realloc(NET * net,size_t length)164 my_bool net_realloc(NET *net, size_t length)
165 {
166 uchar *buff;
167 size_t pkt_length;
168 DBUG_ENTER("net_realloc");
169 DBUG_PRINT("enter",("length: %lu", (ulong) length));
170
171 if (length >= net->max_packet_size)
172 {
173 DBUG_PRINT("error", ("Packet too large. Max size: %lu",
174 net->max_packet_size));
175 /* @todo: 1 and 2 codes are identical. */
176 net->error= 1;
177 net->last_errno= ER_NET_PACKET_TOO_LARGE;
178 #ifdef MYSQL_SERVER
179 my_error(ER_NET_PACKET_TOO_LARGE, MYF(0));
180 #endif
181 DBUG_RETURN(1);
182 }
183 pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1);
184 /*
185 We must allocate some extra bytes for the end 0 and to be able to
186 read big compressed blocks in
187 net_read_packet() may actually read 4 bytes depending on build flags and
188 platform.
189 */
190 if (!(buff= (uchar*) my_realloc(key_memory_NET_buff,
191 (char*) net->buff, pkt_length +
192 NET_HEADER_SIZE + COMP_HEADER_SIZE,
193 MYF(MY_WME))))
194 {
195 /* @todo: 1 and 2 codes are identical. */
196 net->error= 1;
197 net->last_errno= ER_OUT_OF_RESOURCES;
198 /* In the server the error is reported by MY_WME flag. */
199 DBUG_RETURN(1);
200 }
201 net->buff=net->write_pos=buff;
202 net->buff_end=buff+(net->max_packet= (ulong) pkt_length);
203 DBUG_RETURN(0);
204 }
205
206
207 /**
208 Clear (reinitialize) the NET structure for a new command.
209
210 @remark Performs debug checking of the socket buffer to
211 ensure that the protocol sequence is correct.
212
213 @param net NET handler
214 @param check_buffer Whether to check the socket buffer.
215 */
216
net_clear(NET * net,my_bool check_buffer MY_ATTRIBUTE ((unused)))217 void net_clear(NET *net,
218 my_bool check_buffer MY_ATTRIBUTE((unused)))
219 {
220 DBUG_ENTER("net_clear");
221
222 DBUG_EXECUTE_IF("simulate_bad_field_length_1", {
223 net->pkt_nr= net->compress_pkt_nr= 0;
224 net->write_pos= net->buff;
225 DBUG_VOID_RETURN;
226 });
227 DBUG_EXECUTE_IF("simulate_bad_field_length_2", {
228 net->pkt_nr= net->compress_pkt_nr= 0;
229 net->write_pos= net->buff;
230 DBUG_VOID_RETURN;
231 });
232 #if !defined(EMBEDDED_LIBRARY)
233 /* Ensure the socket buffer is empty, except for an EOF (at least 1). */
234 assert(!check_buffer || (vio_pending(net->vio) <= 1));
235 #endif
236
237 /* Ready for new command */
238 net->pkt_nr= net->compress_pkt_nr= 0;
239 net->write_pos= net->buff;
240
241 DBUG_VOID_RETURN;
242 }
243
244
245 /** Flush write_buffer if not empty. */
246
net_flush(NET * net)247 my_bool net_flush(NET *net)
248 {
249 my_bool error= 0;
250 DBUG_ENTER("net_flush");
251 if (net->buff != net->write_pos)
252 {
253 error= net_write_packet(net, net->buff,
254 (size_t) (net->write_pos - net->buff));
255 net->write_pos= net->buff;
256 }
257 /* Sync packet number if using compression */
258 if (net->compress)
259 net->pkt_nr=net->compress_pkt_nr;
260 DBUG_RETURN(error);
261 }
262
263
264 /**
265 Whether a I/O operation should be retried later.
266
267 @param net NET handler.
268 @param retry_count Maximum number of interrupted operations.
269
270 @retval TRUE Operation should be retried.
271 @retval FALSE Operation should not be retried. Fatal error.
272 */
273
274 static my_bool
net_should_retry(NET * net,uint * retry_count MY_ATTRIBUTE ((unused)))275 net_should_retry(NET *net, uint *retry_count MY_ATTRIBUTE((unused)))
276 {
277 my_bool retry;
278
279 #ifndef MYSQL_SERVER
280 /*
281 In the client library, interrupted I/O operations are always retried.
282 Otherwise, it's either a timeout or an unrecoverable error.
283 */
284 retry= vio_should_retry(net->vio);
285 #else
286 /*
287 In the server, interrupted I/O operations are retried up to a limit.
288 In this scenario, pthread_kill can be used to wake up
289 (interrupt) threads waiting for I/O.
290 */
291 retry= vio_should_retry(net->vio) && ((*retry_count)++ < net->retry_count);
292 #endif
293
294 return retry;
295 }
296
297
298 /*****************************************************************************
299 ** Write something to server/client buffer
300 *****************************************************************************/
301
302 /**
303 Write a logical packet with packet header.
304
305 Format: Packet length (3 bytes), packet number (1 byte)
306 When compression is used, a 3 byte compression length is added.
307
308 @note If compression is used, the original packet is modified!
309 */
310
my_net_write(NET * net,const uchar * packet,size_t len)311 my_bool my_net_write(NET *net, const uchar *packet, size_t len)
312 {
313 uchar buff[NET_HEADER_SIZE];
314 int rc;
315
316 if (unlikely(!net->vio)) /* nowhere to write */
317 return 0;
318
319 MYSQL_NET_WRITE_START(len);
320
321 DBUG_EXECUTE_IF("simulate_net_write_failure", {
322 my_error(ER_NET_ERROR_ON_WRITE, MYF(0));
323 return 1;
324 };
325 );
326
327 /*
328 Big packets are handled by splitting them in packets of MAX_PACKET_LENGTH
329 length. The last packet is always a packet that is < MAX_PACKET_LENGTH.
330 (The last packet may even have a length of 0)
331 */
332 while (len >= MAX_PACKET_LENGTH)
333 {
334 const ulong z_size = MAX_PACKET_LENGTH;
335 int3store(buff, z_size);
336 buff[3]= (uchar) net->pkt_nr++;
337 if (net_write_buff(net, buff, NET_HEADER_SIZE) ||
338 net_write_buff(net, packet, z_size))
339 {
340 MYSQL_NET_WRITE_DONE(1);
341 return 1;
342 }
343 packet += z_size;
344 len-= z_size;
345 }
346 /* Write last packet */
347 int3store(buff, static_cast<uint>(len));
348 buff[3]= (uchar) net->pkt_nr++;
349 if (net_write_buff(net, buff, NET_HEADER_SIZE))
350 {
351 MYSQL_NET_WRITE_DONE(1);
352 return 1;
353 }
354 #ifndef DEBUG_DATA_PACKETS
355 DBUG_DUMP("packet_header", buff, NET_HEADER_SIZE);
356 #endif
357 rc= MY_TEST(net_write_buff(net,packet,len));
358 MYSQL_NET_WRITE_DONE(rc);
359 return rc;
360 }
361
362
363 /**
364 Send a command to the server.
365
366 The reason for having both header and packet is so that libmysql
367 can easy add a header to a special command (like prepared statements)
368 without having to re-alloc the string.
369
370 As the command is part of the first data packet, we have to do some data
371 juggling to put the command in there, without having to create a new
372 packet.
373
374 This function will split big packets into sub-packets if needed.
375 (Each sub packet can only be 2^24 bytes)
376
377 @param net NET handler
378 @param command Command in MySQL server (enum enum_server_command)
379 @param header Header to write after command
380 @param head_len Length of header
381 @param packet Query or parameter to query
382 @param len Length of packet
383
384 @retval
385 0 ok
386 @retval
387 1 error
388 */
389
390 my_bool
net_write_command(NET * net,uchar command,const uchar * header,size_t head_len,const uchar * packet,size_t len)391 net_write_command(NET *net,uchar command,
392 const uchar *header, size_t head_len,
393 const uchar *packet, size_t len)
394 {
395 size_t length=len+1+head_len; /* 1 extra byte for command */
396 uchar buff[NET_HEADER_SIZE+1];
397 uint header_size=NET_HEADER_SIZE+1;
398 int rc;
399 DBUG_ENTER("net_write_command");
400 DBUG_PRINT("enter",("length: %lu", (ulong) len));
401
402 MYSQL_NET_WRITE_START(length);
403
404 buff[4]=command; /* For first packet */
405
406 if (length >= MAX_PACKET_LENGTH)
407 {
408 /* Take into account that we have the command in the first header */
409 len= MAX_PACKET_LENGTH - 1 - head_len;
410 do
411 {
412 int3store(buff, MAX_PACKET_LENGTH);
413 buff[3]= (uchar) net->pkt_nr++;
414 if (net_write_buff(net, buff, header_size) ||
415 net_write_buff(net, header, head_len) ||
416 net_write_buff(net, packet, len))
417 {
418 MYSQL_NET_WRITE_DONE(1);
419 DBUG_RETURN(1);
420 }
421 packet+= len;
422 length-= MAX_PACKET_LENGTH;
423 len= MAX_PACKET_LENGTH;
424 head_len= 0;
425 header_size= NET_HEADER_SIZE;
426 } while (length >= MAX_PACKET_LENGTH);
427 len=length; /* Data left to be written */
428 }
429 int3store(buff, static_cast<uint>(length));
430 buff[3]= (uchar) net->pkt_nr++;
431 rc= MY_TEST(net_write_buff(net, buff, header_size) ||
432 (head_len && net_write_buff(net, header, head_len)) ||
433 net_write_buff(net, packet, len) || net_flush(net));
434 MYSQL_NET_WRITE_DONE(rc);
435 DBUG_RETURN(rc);
436 }
437
438
439 /**
440 Caching the data in a local buffer before sending it.
441
442 Fill up net->buffer and send it to the client when full.
443
444 If the rest of the to-be-sent-packet is bigger than buffer,
445 send it in one big block (to avoid copying to internal buffer).
446 If not, copy the rest of the data to the buffer and return without
447 sending data.
448
449 @param net Network handler
450 @param packet Packet to send
451 @param len Length of packet
452
453 @note
454 The cached buffer can be sent as it is with 'net_flush()'.
455 In this code we have to be careful to not send a packet longer than
456 MAX_PACKET_LENGTH to net_write_packet() if we are using the compressed
457 protocol as we store the length of the compressed packet in 3 bytes.
458
459 @retval
460 0 ok
461 @retval
462 1
463 */
464
465 static my_bool
net_write_buff(NET * net,const uchar * packet,size_t len)466 net_write_buff(NET *net, const uchar *packet, size_t len)
467 {
468 ulong left_length;
469 if (net->compress && net->max_packet > MAX_PACKET_LENGTH)
470 left_length= (ulong) (MAX_PACKET_LENGTH - (net->write_pos - net->buff));
471 else
472 left_length= (ulong) (net->buff_end - net->write_pos);
473
474 #ifdef DEBUG_DATA_PACKETS
475 DBUG_DUMP("data", packet, len);
476 #endif
477 if (len > left_length)
478 {
479 if (net->write_pos != net->buff)
480 {
481 /* Fill up already used packet and write it */
482 memcpy(net->write_pos, packet, left_length);
483 if (net_write_packet(net, net->buff,
484 (size_t) (net->write_pos - net->buff) + left_length))
485 return 1;
486 net->write_pos= net->buff;
487 packet+= left_length;
488 len-= left_length;
489 }
490 if (net->compress)
491 {
492 /*
493 We can't have bigger packets than 16M with compression
494 Because the uncompressed length is stored in 3 bytes
495 */
496 left_length= MAX_PACKET_LENGTH;
497 while (len > left_length)
498 {
499 if (net_write_packet(net, packet, left_length))
500 return 1;
501 packet+= left_length;
502 len-= left_length;
503 }
504 }
505 if (len > net->max_packet)
506 return net_write_packet(net, packet, len);
507 /* Send out rest of the blocks as full sized blocks */
508 }
509 memcpy(net->write_pos, packet, len);
510 net->write_pos+= len;
511 return 0;
512 }
513
514
515 /**
516 Write a determined number of bytes to a network handler.
517
518 @param net NET handler.
519 @param buf Buffer containing the data to be written.
520 @param count The length, in bytes, of the buffer.
521
522 @return TRUE on error, FALSE on success.
523 */
524
525 static my_bool
net_write_raw_loop(NET * net,const uchar * buf,size_t count)526 net_write_raw_loop(NET *net, const uchar *buf, size_t count)
527 {
528 unsigned int retry_count= 0;
529
530 while (count)
531 {
532 size_t sentcnt= vio_write(net->vio, buf, count);
533
534 /* VIO_SOCKET_ERROR (-1) indicates an error. */
535 if (sentcnt == VIO_SOCKET_ERROR)
536 {
537 /* A recoverable I/O error occurred? */
538 if (net_should_retry(net, &retry_count))
539 continue;
540 else
541 break;
542 }
543
544 count-= sentcnt;
545 buf+= sentcnt;
546 #ifdef MYSQL_SERVER
547 thd_increment_bytes_sent(sentcnt);
548 #endif
549 }
550
551 /* On failure, propagate the error code. */
552 if (count)
553 {
554 /* Socket should be closed. */
555 net->error= 2;
556
557 /* Interrupted by a timeout? */
558 if (vio_was_timeout(net->vio))
559 net->last_errno= ER_NET_WRITE_INTERRUPTED;
560 else
561 net->last_errno= ER_NET_ERROR_ON_WRITE;
562
563 #ifdef MYSQL_SERVER
564 my_error(net->last_errno, MYF(0));
565 #endif
566 }
567
568 return MY_TEST(count);
569 }
570
571
572 /**
573 Compress and encapsulate a packet into a compressed packet.
574
575 @param net NET handler.
576 @param packet The packet to compress.
577 @param[in,out] length Length of the packet.
578
579 A compressed packet header is compromised of the packet
580 length (3 bytes), packet number (1 byte) and the length
581 of the original (uncompressed) packet.
582
583 @return Pointer to the (new) compressed packet.
584 */
585
586 static uchar *
compress_packet(NET * net,const uchar * packet,size_t * length)587 compress_packet(NET *net, const uchar *packet, size_t *length)
588 {
589 uchar *compr_packet;
590 size_t compr_length;
591 const uint header_length= NET_HEADER_SIZE + COMP_HEADER_SIZE;
592
593 compr_packet= (uchar *) my_malloc(key_memory_NET_compress_packet,
594 *length + header_length, MYF(MY_WME));
595
596 if (compr_packet == NULL)
597 return NULL;
598
599 memcpy(compr_packet + header_length, packet, *length);
600
601 /* Compress the encapsulated packet. */
602 if (my_compress(compr_packet + header_length, length, &compr_length))
603 {
604 /*
605 If the length of the compressed packet is larger than the
606 original packet, the original packet is sent uncompressed.
607 */
608 compr_length= 0;
609 }
610
611 /* Length of the compressed (original) packet. */
612 int3store(&compr_packet[NET_HEADER_SIZE], static_cast<uint>(compr_length));
613 /* Length of this packet. */
614 int3store(compr_packet, static_cast<uint>(*length));
615 /* Packet number. */
616 compr_packet[3]= (uchar) (net->compress_pkt_nr++);
617
618 *length+= header_length;
619
620 return compr_packet;
621 }
622
623
624 /**
625 Write a MySQL protocol packet to the network handler.
626
627 @param net NET handler.
628 @param packet The packet to write.
629 @param length Length of the packet.
630
631 @remark The packet might be encapsulated into a compressed packet.
632
633 @return TRUE on error, FALSE on success.
634 */
635
636 my_bool
net_write_packet(NET * net,const uchar * packet,size_t length)637 net_write_packet(NET *net, const uchar *packet, size_t length)
638 {
639 my_bool res;
640 DBUG_ENTER("net_write_packet");
641
642 #if defined(MYSQL_SERVER)
643 query_cache_insert((char*) packet, length, net->pkt_nr);
644 #endif
645
646 /* Socket can't be used */
647 if (net->error == 2)
648 DBUG_RETURN(TRUE);
649
650 net->reading_or_writing= 2;
651
652 #ifdef HAVE_COMPRESS
653 const bool do_compress= net->compress;
654 if (do_compress)
655 {
656 if ((packet= compress_packet(net, packet, &length)) == NULL)
657 {
658 net->error= 2;
659 net->last_errno= ER_OUT_OF_RESOURCES;
660 /* In the server, allocation failure raises a error. */
661 net->reading_or_writing= 0;
662 DBUG_RETURN(TRUE);
663 }
664 }
665 #endif /* HAVE_COMPRESS */
666
667 #ifdef DEBUG_DATA_PACKETS
668 DBUG_DUMP("data", packet, length);
669 #endif
670
671 res= net_write_raw_loop(net, packet, length);
672
673 #ifdef HAVE_COMPRESS
674 if (do_compress)
675 my_free((void *) packet);
676 #endif
677
678 net->reading_or_writing= 0;
679
680 DBUG_RETURN(res);
681 }
682
683 /*****************************************************************************
684 ** Read something from server/clinet
685 *****************************************************************************/
686
687 /**
688 Read a determined number of bytes from a network handler.
689
690 @param net NET handler.
691 @param count The number of bytes to read.
692
693 @return TRUE on error, FALSE on success.
694 */
695
net_read_raw_loop(NET * net,size_t count)696 static my_bool net_read_raw_loop(NET *net, size_t count)
697 {
698 bool eof= false;
699 unsigned int retry_count= 0;
700 uchar *buf= net->buff + net->where_b;
701
702 while (count)
703 {
704 size_t recvcnt= vio_read(net->vio, buf, count);
705
706 /* VIO_SOCKET_ERROR (-1) indicates an error. */
707 if (recvcnt == VIO_SOCKET_ERROR)
708 {
709 /* A recoverable I/O error occurred? */
710 if (net_should_retry(net, &retry_count))
711 continue;
712 else
713 break;
714 }
715 /* Zero indicates end of file. */
716 else if (!recvcnt)
717 {
718 eof= true;
719 break;
720 }
721
722 count-= recvcnt;
723 buf+= recvcnt;
724 #ifdef MYSQL_SERVER
725 thd_increment_bytes_received(recvcnt);
726 #endif
727 }
728
729 /* On failure, propagate the error code. */
730 if (count)
731 {
732 /* Socket should be closed. */
733 net->error= 2;
734
735 /* Interrupted by a timeout? */
736 if (!eof && vio_was_timeout(net->vio))
737 net->last_errno= ER_NET_READ_INTERRUPTED;
738 else
739 net->last_errno= ER_NET_READ_ERROR;
740
741 #ifdef MYSQL_SERVER
742 my_error(net->last_errno, MYF(0));
743 #endif
744 }
745
746 return MY_TEST(count);
747 }
748
749
750 /**
751 Read the header of a packet. The MySQL protocol packet header
752 consists of the length, in bytes, of the payload (packet data)
753 and a serial number.
754
755 @remark The encoded length is the length of the packet payload,
756 which does not include the packet header.
757
758 @remark The serial number is used to ensure that the packets are
759 received in order. If the packet serial number does not
760 match the expected value, a error is returned.
761
762 @param net NET handler.
763
764 @return TRUE on error, FALSE on success.
765 */
766
net_read_packet_header(NET * net)767 static my_bool net_read_packet_header(NET *net)
768 {
769 uchar pkt_nr;
770 size_t count= NET_HEADER_SIZE;
771 my_bool rc;
772
773 if (net->compress)
774 count+= COMP_HEADER_SIZE;
775
776 #ifdef MYSQL_SERVER
777 struct st_net_server *server_extension;
778
779 server_extension= static_cast<st_net_server*> (net->extension);
780
781 if (server_extension != NULL)
782 {
783 void *user_data= server_extension->m_user_data;
784 assert(server_extension->m_before_header != NULL);
785 assert(server_extension->m_after_header != NULL);
786
787 server_extension->m_before_header(net, user_data, count);
788 rc= net_read_raw_loop(net, count);
789 server_extension->m_after_header(net, user_data, count, rc);
790 }
791 else
792 #endif
793 {
794 rc= net_read_raw_loop(net, count);
795 }
796
797 if (rc)
798 return TRUE;
799
800 DBUG_DUMP("packet_header", net->buff + net->where_b, NET_HEADER_SIZE);
801
802 pkt_nr= net->buff[net->where_b + 3];
803
804 /*
805 Verify packet serial number against the truncated packet counter.
806 The local packet counter must be truncated since its not reset.
807 */
808 if (pkt_nr != (uchar) net->pkt_nr)
809 {
810 /* Not a NET error on the client. XXX: why? */
811 #if defined(MYSQL_SERVER)
812 my_error(ER_NET_PACKETS_OUT_OF_ORDER, MYF(0));
813 #elif defined(EXTRA_DEBUG)
814 /*
815 We don't make noise server side, since the client is expected
816 to break the protocol for e.g. --send LOAD DATA .. LOCAL where
817 the server expects the client to send a file, but the client
818 may reply with a new command instead.
819 */
820 my_message_local(ERROR_LEVEL,
821 "packets out of order (found %u, expected %u)",
822 (uint) pkt_nr, net->pkt_nr);
823 assert(pkt_nr == net->pkt_nr);
824 #endif
825 return TRUE;
826 }
827
828 net->pkt_nr++;
829
830 return FALSE;
831 }
832
833
834 /**
835 Read one (variable-length) MySQL protocol packet.
836 A MySQL packet consists of a header and a payload.
837
838 @remark Reads one packet to net->buff + net->where_b.
839 @remark Long packets are handled by my_net_read().
840 @remark The network buffer is expanded if necessary.
841
842 @return The length of the packet, or @packet_error on error.
843 */
844
net_read_packet(NET * net,size_t * complen)845 static size_t net_read_packet(NET *net, size_t *complen)
846 {
847 size_t pkt_len, pkt_data_len;
848
849 *complen= 0;
850
851 net->reading_or_writing= 1;
852
853 /* Retrieve packet length and number. */
854 if (net_read_packet_header(net))
855 goto error;
856
857 net->compress_pkt_nr= net->pkt_nr;
858
859 #ifdef HAVE_COMPRESS
860 if (net->compress)
861 {
862 /*
863 The right-hand expression
864 must match the size of the buffer allocated in net_realloc().
865 */
866 assert(net->where_b + NET_HEADER_SIZE + sizeof(uint32) <=
867 net->max_packet + NET_HEADER_SIZE + COMP_HEADER_SIZE);
868
869 /*
870 If the packet is compressed then complen > 0 and contains the
871 number of bytes in the uncompressed packet.
872 */
873 *complen= uint3korr(&(net->buff[net->where_b + NET_HEADER_SIZE]));
874 }
875 #endif
876
877 /* The length of the packet that follows. */
878 pkt_len= uint3korr(net->buff+net->where_b);
879
880 /* End of big multi-packet. */
881 if (!pkt_len)
882 goto end;
883
884 pkt_data_len = max(pkt_len, *complen) + net->where_b;
885
886 /* Expand packet buffer if necessary. */
887 if ((pkt_data_len >= net->max_packet) && net_realloc(net, pkt_data_len))
888 goto error;
889
890 /* Read the packet data (payload). */
891 if (net_read_raw_loop(net, pkt_len))
892 goto error;
893
894 end:
895 net->reading_or_writing= 0;
896 return pkt_len;
897
898 error:
899 net->reading_or_writing= 0;
900 return packet_error;
901 }
902
903
904 /**
905 Read a packet from the client/server and return it without the internal
906 package header.
907
908 If the packet is the first packet of a multi-packet packet
909 (which is indicated by the length of the packet = 0xffffff) then
910 all sub packets are read and concatenated.
911
912 If the packet was compressed, its uncompressed and the length of the
913 uncompressed packet is returned.
914
915 @return
916 The function returns the length of the found packet or packet_error.
917 net->read_pos points to the read data.
918 */
919
920 ulong
my_net_read(NET * net)921 my_net_read(NET *net)
922 {
923 size_t len, complen;
924
925 MYSQL_NET_READ_START();
926
927 #ifdef HAVE_COMPRESS
928 if (!net->compress)
929 {
930 #endif
931 len= net_read_packet(net, &complen);
932 if (len == MAX_PACKET_LENGTH)
933 {
934 /* First packet of a multi-packet. Concatenate the packets */
935 ulong save_pos = net->where_b;
936 size_t total_length= 0;
937 do
938 {
939 net->where_b += len;
940 total_length += len;
941 len= net_read_packet(net, &complen);
942 } while (len == MAX_PACKET_LENGTH);
943 if (len != packet_error)
944 len+= total_length;
945 net->where_b = save_pos;
946 }
947 net->read_pos = net->buff + net->where_b;
948 if (len != packet_error)
949 net->read_pos[len]=0; /* Safeguard for mysql_use_result */
950 MYSQL_NET_READ_DONE(0, len);
951 return static_cast<ulong>(len);
952 #ifdef HAVE_COMPRESS
953 }
954 else
955 {
956 /* We are using the compressed protocol */
957
958 size_t buf_length;
959 ulong start_of_packet;
960 ulong first_packet_offset;
961 uint read_length, multi_byte_packet=0;
962
963 if (net->remain_in_buf)
964 {
965 buf_length= net->buf_length; /* Data left in old packet */
966 first_packet_offset= start_of_packet= (net->buf_length -
967 net->remain_in_buf);
968 /* Restore the character that was overwritten by the end 0 */
969 net->buff[start_of_packet]= net->save_char;
970 }
971 else
972 {
973 /* reuse buffer, as there is nothing in it that we need */
974 buf_length= start_of_packet= first_packet_offset= 0;
975 }
976 for (;;)
977 {
978 size_t packet_len;
979
980 if (buf_length - start_of_packet >= NET_HEADER_SIZE)
981 {
982 read_length = uint3korr(net->buff+start_of_packet);
983 if (!read_length)
984 {
985 /* End of multi-byte packet */
986 start_of_packet += NET_HEADER_SIZE;
987 break;
988 }
989 if (read_length + NET_HEADER_SIZE <= buf_length - start_of_packet)
990 {
991 if (multi_byte_packet)
992 {
993 /*
994 It's never the buffer on the first loop iteration that will have
995 multi_byte_packet on.
996 Thus there shall never be a non-zero first_packet_offset here.
997 */
998 assert(first_packet_offset == 0);
999 /* Remove packet header for second packet */
1000 memmove(net->buff + start_of_packet,
1001 net->buff + start_of_packet + NET_HEADER_SIZE,
1002 buf_length - start_of_packet - NET_HEADER_SIZE);
1003 start_of_packet += read_length;
1004 buf_length -= NET_HEADER_SIZE;
1005 }
1006 else
1007 start_of_packet+= read_length + NET_HEADER_SIZE;
1008
1009 if (read_length != MAX_PACKET_LENGTH) /* last package */
1010 {
1011 multi_byte_packet= 0; /* No last zero len packet */
1012 break;
1013 }
1014 multi_byte_packet= NET_HEADER_SIZE;
1015 /* Move data down to read next data packet after current one */
1016 if (first_packet_offset)
1017 {
1018 memmove(net->buff,net->buff+first_packet_offset,
1019 buf_length-first_packet_offset);
1020 buf_length-=first_packet_offset;
1021 start_of_packet -= first_packet_offset;
1022 first_packet_offset=0;
1023 }
1024 continue;
1025 }
1026 }
1027 /* Move data down to read next data packet after current one */
1028 if (first_packet_offset)
1029 {
1030 memmove(net->buff,net->buff+first_packet_offset,
1031 buf_length-first_packet_offset);
1032 buf_length-=first_packet_offset;
1033 start_of_packet -= first_packet_offset;
1034 first_packet_offset=0;
1035 }
1036
1037 net->where_b=buf_length;
1038 if ((packet_len= net_read_packet(net, &complen)) == packet_error)
1039 {
1040 MYSQL_NET_READ_DONE(1, 0);
1041 return packet_error;
1042 }
1043 if (my_uncompress(net->buff + net->where_b, packet_len,
1044 &complen))
1045 {
1046 net->error= 2; /* caller will close socket */
1047 net->last_errno= ER_NET_UNCOMPRESS_ERROR;
1048 #ifdef MYSQL_SERVER
1049 my_error(ER_NET_UNCOMPRESS_ERROR, MYF(0));
1050 #endif
1051 MYSQL_NET_READ_DONE(1, 0);
1052 return packet_error;
1053 }
1054 buf_length+= complen;
1055 }
1056
1057 net->read_pos= net->buff+ first_packet_offset + NET_HEADER_SIZE;
1058 net->buf_length= buf_length;
1059 net->remain_in_buf= (ulong) (buf_length - start_of_packet);
1060 len = ((ulong) (start_of_packet - first_packet_offset) - NET_HEADER_SIZE -
1061 multi_byte_packet);
1062 net->save_char= net->read_pos[len]; /* Must be saved */
1063 net->read_pos[len]=0; /* Safeguard for mysql_use_result */
1064 }
1065 #endif /* HAVE_COMPRESS */
1066 MYSQL_NET_READ_DONE(0, len);
1067 return static_cast<ulong>(len);
1068 }
1069
1070
my_net_set_read_timeout(NET * net,uint timeout)1071 void my_net_set_read_timeout(NET *net, uint timeout)
1072 {
1073 DBUG_ENTER("my_net_set_read_timeout");
1074 DBUG_PRINT("enter", ("timeout: %d", timeout));
1075 if (net->read_timeout == timeout)
1076 DBUG_VOID_RETURN;
1077 net->read_timeout= timeout;
1078 if (net->vio)
1079 vio_timeout(net->vio, 0, timeout);
1080 DBUG_VOID_RETURN;
1081 }
1082
1083
my_net_set_write_timeout(NET * net,uint timeout)1084 void my_net_set_write_timeout(NET *net, uint timeout)
1085 {
1086 DBUG_ENTER("my_net_set_write_timeout");
1087 DBUG_PRINT("enter", ("timeout: %d", timeout));
1088 if (net->write_timeout == timeout)
1089 DBUG_VOID_RETURN;
1090 net->write_timeout= timeout;
1091 if (net->vio)
1092 vio_timeout(net->vio, 1, timeout);
1093 DBUG_VOID_RETURN;
1094 }
1095
1096