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