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