1/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
2 2012-2016 SkySQL AB, MariaDB Corporation AB
3 This library is free software; you can redistribute it and/or
4 modify it under the terms of the GNU Library General Public
5 License as published by the Free Software Foundation; either
6 version 2 of the License, or (at your option) any later version.
7
8 This library is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 Library General Public License for more details.
12
13 You should have received a copy of the GNU Library General Public
14 License along with this library; if not, write to the Free
15 Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
16 MA 02111-1301, USA */
17
18/* Write and read of logical packets to/from socket
19 ** Writes are cached into net_buffer_length big packets.
20 ** Read packets are reallocated dynamically when reading big packets.
21 ** Each logical packet has the following pre-info:
22 ** 3 byte length & 1 byte package-number.
23 */
24
25
26#include <ma_global.h>
27#include <mysql.h>
28#include <ma_pvio.h>
29#include <ma_sys.h>
30#include <ma_string.h>
31#include "mysql.h"
32#include "ma_server_error.h"
33#include <signal.h>
34#include <errno.h>
35#include <sys/types.h>
36#include <ma_pvio.h>
37#include <ma_common.h>
38#ifndef _WIN32
39#include <poll.h>
40#endif
41
42#define MAX_PACKET_LENGTH (256L*256L*256L-1)
43
44/* net_buffer_length and max_allowed_packet are defined in mysql.h
45 See bug conc-57
46 */
47#undef net_buffer_length
48
49#undef max_allowed_packet
50ulong max_allowed_packet=1024L * 1024L * 1024L;
51ulong net_read_timeout= NET_READ_TIMEOUT;
52ulong net_write_timeout= NET_WRITE_TIMEOUT;
53ulong net_buffer_length= 8192; /* Default length. Enlarged if necessary */
54
55#if !defined(_WIN32) && !defined(MSDOS)
56#include <sys/socket.h>
57#else
58#undef MYSQL_SERVER /* Win32 can't handle interrupts */
59#endif
60#if !defined(MSDOS) && !defined(_WIN32) && !defined(HAVE_BROKEN_NETINET_INCLUDES) && !defined(__BEOS__)
61#include <netinet/in_systm.h>
62#include <netinet/in.h>
63#include <netinet/ip.h>
64#if !defined(alpha_linux_port)
65#include <netinet/tcp.h>
66#endif
67#endif
68
69
70/*
71 ** Give error if a too big packet is found
72 ** The server can change this with the -O switch, but because the client
73 ** can't normally do this the client should have a bigger max-buffer.
74 */
75
76static int ma_net_write_buff(NET *net,const char *packet, size_t len);
77
78
79/* Init with packet info */
80
81int ma_net_init(NET *net, MARIADB_PVIO* pvio)
82{
83 if (!(net->buff=(uchar*) malloc(net_buffer_length)))
84 return 1;
85 if (!net->extension)
86 return 1;
87
88 memset(net->buff, 0, net_buffer_length);
89
90 max_allowed_packet= net->max_packet_size= MAX(net_buffer_length, max_allowed_packet);
91 net->buff_end=net->buff+(net->max_packet=net_buffer_length);
92 net->pvio = pvio;
93 net->error=0; net->return_status=0;
94 net->read_timeout=(uint) net_read_timeout; /* Timeout for read */
95 net->compress_pkt_nr= net->pkt_nr= 0;
96 net->write_pos=net->read_pos = net->buff;
97 net->last_error[0]= net->sqlstate[0] =0;
98
99 net->compress=0; net->reading_or_writing=0;
100 net->where_b = net->remain_in_buf=0;
101 net->last_errno=0;
102
103 if (pvio != 0) /* If real connection */
104 {
105 ma_pvio_get_handle(pvio, &net->fd);
106 ma_pvio_blocking(pvio, 1, 0);
107 ma_pvio_fast_send(pvio);
108 }
109 return 0;
110}
111
112void ma_net_end(NET *net)
113{
114 free(net->buff);
115 net->buff=0;
116}
117
118/* Realloc the packet buffer */
119
120static my_bool net_realloc(NET *net, size_t length)
121{
122 uchar *buff;
123 size_t pkt_length;
124
125 if (length >= net->max_packet_size)
126 {
127 net->error=1;
128 net->last_errno=ER_NET_PACKET_TOO_LARGE;
129 return(1);
130 }
131 pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1);
132 /* reallocate buffer:
133 size= pkt_length + NET_HEADER_SIZE + COMP_HEADER_SIZE */
134 if (!(buff=(uchar*) realloc(net->buff,
135 pkt_length + NET_HEADER_SIZE + COMP_HEADER_SIZE)))
136 {
137 net->error=1;
138 return(1);
139 }
140 net->buff=net->write_pos=buff;
141 net->buff_end=buff+(net->max_packet=(unsigned long)pkt_length);
142 return(0);
143}
144
145/* Remove unwanted characters from connection */
146void ma_net_clear(NET *net)
147{
148 if (net->extension->multi_status > COM_MULTI_OFF)
149 return;
150 net->compress_pkt_nr= net->pkt_nr=0; /* Ready for new command */
151 net->write_pos=net->buff;
152 return;
153}
154
155/* Flush write_buffer if not empty. */
156int ma_net_flush(NET *net)
157{
158 int error=0;
159
160 /* don't flush if com_multi is in progress */
161 if (net->extension->multi_status > COM_MULTI_OFF)
162 return 0;
163
164 if (net->buff != net->write_pos)
165 {
166 error=ma_net_real_write(net,(char*) net->buff,
167 (size_t) (net->write_pos - net->buff));
168 net->write_pos=net->buff;
169 }
170 if (net->compress)
171 net->pkt_nr= net->compress_pkt_nr;
172 return(error);
173}
174
175/*****************************************************************************
176 ** Write something to server/client buffer
177 *****************************************************************************/
178
179/*
180 ** Write a logical packet with packet header
181 ** Format: Packet length (3 bytes), packet number(1 byte)
182 ** When compression is used a 3 byte compression length is added
183 ** NOTE: If compression is used the original package is destroyed!
184 */
185
186int ma_net_write(NET *net, const uchar *packet, size_t len)
187{
188 uchar buff[NET_HEADER_SIZE];
189 while (len >= MAX_PACKET_LENGTH)
190 {
191 const ulong max_len= MAX_PACKET_LENGTH;
192 int3store(buff,max_len);
193 buff[3]= (uchar)net->pkt_nr++;
194 if (ma_net_write_buff(net,(char*) buff,NET_HEADER_SIZE) ||
195 ma_net_write_buff(net, (char *)packet, max_len))
196 return 1;
197 packet+= max_len;
198 len-= max_len;
199 }
200 /* write last remaining packet, size can be zero */
201 int3store(buff, len);
202 buff[3]= (uchar)net->pkt_nr++;
203 if (ma_net_write_buff(net,(char*) buff,NET_HEADER_SIZE) ||
204 ma_net_write_buff(net, (char *)packet, len))
205 return 1;
206 return 0;
207}
208
209int ma_net_write_command(NET *net, uchar command,
210 const char *packet, size_t len,
211 my_bool disable_flush)
212{
213 uchar buff[NET_HEADER_SIZE+1];
214 size_t buff_size= NET_HEADER_SIZE + 1;
215 size_t length= 1 + len; /* 1 extra byte for command */
216 int rc;
217
218 buff[NET_HEADER_SIZE]= 0;
219 buff[4]=command;
220
221 if (length >= MAX_PACKET_LENGTH)
222 {
223 len= MAX_PACKET_LENGTH - 1;
224 do
225 {
226 int3store(buff, MAX_PACKET_LENGTH);
227 buff[3]= (net->compress) ? 0 : (uchar) (net->pkt_nr++);
228
229 if (ma_net_write_buff(net, (char *)buff, buff_size) ||
230 ma_net_write_buff(net, packet, len))
231 return(1);
232 packet+= len;
233 length-= MAX_PACKET_LENGTH;
234 len= MAX_PACKET_LENGTH;
235 buff_size= NET_HEADER_SIZE; /* don't send command for further packets */
236 } while (length >= MAX_PACKET_LENGTH);
237 len= length;
238 }
239 int3store(buff,length);
240 buff[3]= (net->compress) ? 0 :(uchar) (net->pkt_nr++);
241 rc= test (ma_net_write_buff(net,(char *)buff, buff_size) ||
242 ma_net_write_buff(net,packet,len));
243 if (!rc && !disable_flush)
244 return test(ma_net_flush(net));
245 return rc;
246}
247
248
249static int ma_net_write_buff(NET *net,const char *packet, size_t len)
250{
251 size_t left_length;
252
253 if (net->max_packet > MAX_PACKET_LENGTH &&
254 net->compress)
255 left_length= (size_t)(MAX_PACKET_LENGTH - (net->write_pos - net->buff));
256 else
257 left_length=(size_t) (net->buff_end - net->write_pos);
258
259 if (len > left_length)
260 {
261 if (net->write_pos != net->buff)
262 {
263 memcpy((char*) net->write_pos,packet,left_length);
264 if (ma_net_real_write(net,(char*) net->buff,
265 (size_t)(net->write_pos - net->buff) + left_length))
266 return 1;
267 packet+=left_length;
268 len-=left_length;
269 net->write_pos= net->buff;
270 }
271 if (net->compress)
272 {
273 /* uncompressed length is stored in 3 bytes,so
274 packet can't be > 0xFFFFFF */
275 left_length= MAX_PACKET_LENGTH;
276 while (len > left_length)
277 {
278 if (ma_net_real_write(net, packet, left_length))
279 return 1;
280 packet+= left_length;
281 len-= left_length;
282 }
283 }
284 if (len > net->max_packet)
285 return(test(ma_net_real_write(net, packet, len)));
286 }
287 memcpy((char*) net->write_pos,packet,len);
288 net->write_pos+=len;
289 return 0;
290}
291
292unsigned char *mysql_net_store_length(unsigned char *packet, size_t length);
293
294/* Read and write using timeouts */
295
296int ma_net_real_write(NET *net, const char *packet, size_t len)
297{
298 ssize_t length;
299 char *pos,*end;
300
301 if (net->error == 2)
302 return(-1); /* socket can't be used */
303
304 net->reading_or_writing=2;
305#ifdef HAVE_COMPRESS
306 if (net->compress)
307 {
308 size_t complen;
309 uchar *b;
310 uint header_length=NET_HEADER_SIZE+COMP_HEADER_SIZE;
311 if (!(b=(uchar*) malloc(len + NET_HEADER_SIZE + COMP_HEADER_SIZE + 1)))
312 {
313 net->last_errno=ER_OUT_OF_RESOURCES;
314 net->error=2;
315 net->reading_or_writing=0;
316 return(1);
317 }
318 memcpy(b+header_length,packet,len);
319
320 if (_mariadb_compress((unsigned char*) b+header_length,&len,&complen))
321 {
322 complen=0;
323 }
324 int3store(&b[NET_HEADER_SIZE],complen);
325 int3store(b,len);
326 b[3]=(uchar) (net->compress_pkt_nr++);
327 len+= header_length;
328 packet= (char*) b;
329 }
330#endif /* HAVE_COMPRESS */
331
332 pos=(char*) packet; end=pos+len;
333 while (pos != end)
334 {
335 if ((length=ma_pvio_write(net->pvio,(uchar *)pos,(size_t) (end-pos))) <= 0)
336 {
337 net->error=2; /* Close socket */
338 net->last_errno= ER_NET_ERROR_ON_WRITE;
339 net->reading_or_writing=0;
340 return(1);
341 }
342 pos+=length;
343 }
344#ifdef HAVE_COMPRESS
345 if (net->compress)
346 free((char*) packet);
347#endif
348 net->reading_or_writing=0;
349 return(((int) (pos != end)));
350}
351
352/*****************************************************************************
353 ** Read something from server/clinet
354 *****************************************************************************/
355static ulong ma_real_read(NET *net, size_t *complen)
356{
357 uchar *pos;
358 ssize_t length;
359 uint i;
360 ulong len=packet_error;
361 size_t remain= (net->compress ? NET_HEADER_SIZE+COMP_HEADER_SIZE :
362 NET_HEADER_SIZE);
363 *complen = 0;
364
365 net->reading_or_writing=1;
366
367 pos = net->buff + net->where_b; /* net->packet -4 */
368 for (i=0 ; i < 2 ; i++)
369 {
370 while (remain > 0)
371 {
372 /* First read is done with non blocking mode */
373 if ((length=ma_pvio_cache_read(net->pvio, pos,remain)) <= 0L)
374 {
375 len= packet_error;
376 net->error=2; /* Close socket */
377 goto end;
378 }
379 remain -= (ulong) length;
380 pos+= (ulong) length;
381 }
382
383 if (i == 0)
384 { /* First parts is packet length */
385 ulong helping;
386 net->pkt_nr= net->buff[net->where_b + 3];
387 net->compress_pkt_nr= ++net->pkt_nr;
388#ifdef HAVE_COMPRESS
389 if (net->compress)
390 {
391 /* complen is > 0 if package is really compressed */
392 *complen=uint3korr(&(net->buff[net->where_b + NET_HEADER_SIZE]));
393 }
394#endif
395
396 len=uint3korr(net->buff+net->where_b);
397 if (!len)
398 goto end;
399 helping = max(len,(ulong)*complen) + net->where_b;
400 /* The necessary size of net->buff */
401 if (helping >= net->max_packet)
402 {
403 if (net_realloc(net, helping))
404 {
405 len= packet_error; /* Return error */
406 goto end;
407 }
408 }
409 pos=net->buff + net->where_b;
410 remain = len;
411 }
412 }
413
414end:
415 net->reading_or_writing=0;
416 return(len);
417}
418
419ulong ma_net_read(NET *net)
420{
421 size_t len,complen;
422
423#ifdef HAVE_COMPRESS
424 if (!net->compress)
425 {
426#endif
427 len = ma_real_read (net,(size_t *)&complen);
428 if (len == MAX_PACKET_LENGTH)
429 {
430 /* multi packet read */
431 size_t length= 0;
432 ulong last_pos= net->where_b;
433
434 do
435 {
436 length+= len;
437 net->where_b+= (unsigned long)len;
438 len= ma_real_read(net, &complen);
439 } while (len == MAX_PACKET_LENGTH);
440 net->where_b= last_pos;
441 if (len != packet_error)
442 len+= length;
443 }
444 net->read_pos = net->buff + net->where_b;
445 if (len != packet_error)
446 net->read_pos[len]=0; /* Safeguard for mysql_use_result */
447 return (ulong)len;
448#ifdef HAVE_COMPRESS
449 }
450 else
451 {
452 /*
453 compressed protocol:
454
455 --------------------------------------
456 packet_length 3
457 sequence_id 1
458 uncompressed_length 3
459 --------------------------------------
460 compressed data packet_length - 7
461 --------------------------------------
462
463 Another packet will follow if:
464 packet_length == MAX_PACKET_LENGTH
465
466 Last package will be identified by
467 - packet_length is zero (special case)
468 - packet_length < MAX_PACKET_LENGTH
469 */
470
471 size_t packet_length,
472 buffer_length;
473 size_t current= 0, start= 0;
474 my_bool is_multi_packet= 0;
475
476 /* check if buffer is empty */
477 if (!net->remain_in_buf)
478 {
479 buffer_length= 0;
480 }
481 else
482 {
483 /* save position and restore \0 character */
484 buffer_length= net->buf_length;
485 current= net->buf_length - net->remain_in_buf;
486 start= current;
487 net->buff[net->buf_length - net->remain_in_buf]=net->save_char;
488 }
489 for (;;)
490 {
491 if (buffer_length - current >= 4)
492 {
493 uchar *pos= net->buff + current;
494 packet_length= uint3korr(pos);
495
496 /* check if we have last package (special case: zero length) */
497 if (!packet_length)
498 {
499 current+= 4; /* length + sequence_id,
500 no more data will follow */
501 break;
502 }
503 if (packet_length + 4 <= buffer_length - current)
504 {
505 if (!is_multi_packet)
506 {
507 current= current + packet_length + 4;
508 }
509 else
510 {
511 /* remove packet_header */
512 memmove(net->buff + current,
513 net->buff + current + 4,
514 buffer_length - current);
515 buffer_length-= 4;
516 current+= packet_length;
517 }
518 /* do we have last packet ? */
519 if (packet_length != MAX_PACKET_LENGTH)
520 {
521 is_multi_packet= 0;
522 break;
523 }
524 else
525 is_multi_packet= 1;
526 if (start)
527 {
528 memmove(net->buff, net->buff + start,
529 buffer_length - start);
530 /* decrease buflen*/
531 buffer_length-= start;
532 start= 0;
533 }
534 continue;
535 }
536 }
537 if (start)
538 {
539 memmove(net->buff, net->buff + start, buffer_length - start);
540 /* decrease buflen and current */
541 current -= start;
542 buffer_length-= start;
543 start= 0;
544 }
545
546 net->where_b=(unsigned long)buffer_length;
547
548 if ((packet_length = ma_real_read(net,(size_t *)&complen)) == packet_error)
549 return packet_error;
550 if (_mariadb_uncompress((unsigned char*) net->buff + net->where_b, &packet_length, &complen))
551 {
552 net->error=2; /* caller will close socket */
553 net->last_errno=ER_NET_UNCOMPRESS_ERROR;
554 break;
555 return packet_error;
556 }
557 buffer_length+= complen;
558 }
559 /* set values */
560 net->buf_length= (unsigned long)buffer_length;
561 net->remain_in_buf= (unsigned long)(buffer_length - current);
562 net->read_pos= net->buff + start + 4;
563 len= current - start - 4;
564 if (is_multi_packet)
565 len-= 4;
566 net->save_char= net->read_pos[len]; /* Must be saved */
567 net->read_pos[len]=0; /* Safeguard for mysql_use_result */
568 }
569#endif
570 return (ulong)len;
571}
572
573int net_add_multi_command(NET *net, uchar command, const uchar *packet,
574 size_t length)
575{
576 if (net->extension->multi_status == COM_MULTI_OFF)
577 {
578 return(1);
579 }
580 /* don't increase packet number */
581 net->compress_pkt_nr= net->pkt_nr= 0;
582 return ma_net_write_command(net, command, (const char *)packet, length, 1);
583}
584
585