net_serv.cc 27.2 KB
Newer Older
unknown's avatar
unknown committed
1
/* Copyright (C) 2000 MySQL AB
2

unknown's avatar
unknown committed
3 4 5 6
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
7

unknown's avatar
unknown committed
8
   This program is distributed in the hope that it will be useful,
unknown's avatar
unknown committed
9
   but WITHOUT ANY WARRANTY; without even the implied warranty of
unknown's avatar
unknown committed
10 11
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
12

unknown's avatar
unknown committed
13 14 15
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
unknown's avatar
unknown committed
16

17 18 19 20 21 22 23
/*
  This file is the net layer API for the MySQL client/server protocol,
  which is a tightly coupled, proprietary protocol owned by MySQL AB.
  Any re-implementations of this protocol must also be under GPL
  unless one has got an license from MySQL AB stating otherwise.
*/

unknown's avatar
unknown committed
24 25
/*
  Write and read of logical packets to/from socket
unknown's avatar
unknown committed
26

unknown's avatar
unknown committed
27 28 29 30
  Writes are cached into net_buffer_length big packets.
  Read packets are reallocated dynamicly when reading big packets.
  Each logical packet has the following pre-info:
  3 byte length & 1 byte package-number.
31 32 33

  This file needs to be written in C as it's used by the libmysql client as a
  C file.
unknown's avatar
unknown committed
34
*/
35

unknown's avatar
SCRUM  
unknown committed
36 37 38 39
/*
  HFTODO this must be hidden if we don't want client capabilities in 
  embedded library
 */
unknown's avatar
unknown committed
40 41 42
#ifdef __WIN__
#include <winsock.h>
#endif
unknown's avatar
unknown committed
43
#include <my_global.h>
44 45
#include <mysql.h>
#include <mysql_embed.h>
unknown's avatar
unknown committed
46
#include <mysql_com.h>
47
#include <mysqld_error.h>
unknown's avatar
unknown committed
48 49
#include <my_sys.h>
#include <m_string.h>
50 51
#include <my_net.h>
#include <violite.h>
unknown's avatar
unknown committed
52 53 54
#include <signal.h>
#include <errno.h>

unknown's avatar
SCRUM  
unknown committed
55
#ifdef EMBEDDED_LIBRARY
56
#undef MYSQL_SERVER
57
#undef MYSQL_CLIENT
58
#define MYSQL_CLIENT
unknown's avatar
SCRUM  
unknown committed
59 60 61
#endif /*EMBEDDED_LIBRARY */


unknown's avatar
unknown committed
62 63 64 65 66 67 68 69 70
/*
  The following handles the differences when this is linked between the
  client and the server.

  This gives an error if a too big packet is found
  The server can change this with the -O switch, but because the client
  can't normally do this the client should have a bigger max_allowed_packet.
*/

unknown's avatar
unknown committed
71 72 73
#if defined(__WIN__) || !defined(MYSQL_SERVER)
  /* The following is because alarms doesn't work on windows. */
#define NO_ALARM
unknown's avatar
unknown committed
74
#endif
unknown's avatar
unknown committed
75 76
  
#ifndef NO_ALARM
unknown's avatar
unknown committed
77 78 79
#include "my_pthread.h"
void sql_print_error(const char *format,...);
#else
unknown's avatar
unknown committed
80
#define DONT_USE_THR_ALARM
unknown's avatar
unknown committed
81
#endif /* NO_ALARM */
unknown's avatar
unknown committed
82 83

#include "thr_alarm.h"
unknown's avatar
unknown committed
84

unknown's avatar
unknown committed
85 86
#ifdef MYSQL_SERVER
#define USE_QUERY_CACHE
87 88 89 90 91
/*
  The following variables/functions should really not be declared
  extern, but as it's hard to include mysql_priv.h here, we have to
  live with this for a while.
*/
unknown's avatar
unknown committed
92
extern uint test_flags;
93
extern ulong bytes_sent, bytes_received, net_big_packet_count;
unknown's avatar
unknown committed
94
extern pthread_mutex_t LOCK_bytes_sent , LOCK_bytes_received;
95
extern void query_cache_insert(NET *net, const char *packet, ulong length);
unknown's avatar
unknown committed
96 97
#else
#undef statistic_add
98
#undef statistic_increment
unknown's avatar
unknown committed
99
#define statistic_add(A,B,C)
100
#define statistic_increment(A,B)
unknown's avatar
unknown committed
101 102
#endif

unknown's avatar
unknown committed
103
#define TEST_BLOCKING		8
104
#define MAX_PACKET_LENGTH (256L*256L*256L-1)
unknown's avatar
unknown committed
105

106
static my_bool net_write_buff(NET *net,const char *packet,ulong len);
unknown's avatar
unknown committed
107 108 109 110


	/* Init with packet info */

111
my_bool my_net_init(NET *net, Vio* vio)
unknown's avatar
unknown committed
112
{
113
  DBUG_ENTER("my_net_init");
unknown's avatar
unknown committed
114 115
  my_net_local_init(net);			/* Set some limits */
  if (!(net->buff=(uchar*) my_malloc((uint32) net->max_packet+
116 117
				     NET_HEADER_SIZE + COMP_HEADER_SIZE,
				     MYF(MY_WME))))
118
    DBUG_RETURN(1);
unknown's avatar
unknown committed
119
  net->buff_end=net->buff+net->max_packet;
unknown's avatar
unknown committed
120 121 122
  net->vio = vio;
  net->no_send_ok = 0;
  net->error=0; net->return_errno=0; net->return_status=0;
unknown's avatar
unknown committed
123
  net->pkt_nr=net->compress_pkt_nr=0;
unknown's avatar
unknown committed
124 125 126 127 128
  net->write_pos=net->read_pos = net->buff;
  net->last_error[0]=0;
  net->compress=0; net->reading_or_writing=0;
  net->where_b = net->remain_in_buf=0;
  net->last_errno=0;
unknown's avatar
unknown committed
129
  net->query_cache_query=0;
unknown's avatar
unknown committed
130
  net->report_error= 0;
unknown's avatar
unknown committed
131 132 133 134

  if (vio != 0)					/* If real connection */
  {
    net->fd  = vio_fd(vio);			/* For perl DBI/DBD */
unknown's avatar
unknown committed
135
#if defined(MYSQL_SERVER) && !defined(___WIN__) && !defined(__EMX__) && !defined(OS2)
unknown's avatar
unknown committed
136
    if (!(test_flags & TEST_BLOCKING))
137 138 139 140
    {
      my_bool old_mode;
      vio_blocking(vio, FALSE, &old_mode);
    }
unknown's avatar
unknown committed
141
#endif
unknown's avatar
unknown committed
142
    vio_fastsend(vio);
unknown's avatar
unknown committed
143
  }
144
  DBUG_RETURN(0);
unknown's avatar
unknown committed
145 146
}

unknown's avatar
unknown committed
147

unknown's avatar
unknown committed
148 149
void net_end(NET *net)
{
150
  DBUG_ENTER("net_end");
unknown's avatar
unknown committed
151 152
  my_free((gptr) net->buff,MYF(MY_ALLOW_ZERO_PTR));
  net->buff=0;
153
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
154 155
}

unknown's avatar
unknown committed
156

unknown's avatar
unknown committed
157 158
/* Realloc the packet buffer */

159
my_bool net_realloc(NET *net, ulong length)
unknown's avatar
unknown committed
160 161 162
{
  uchar *buff;
  ulong pkt_length;
163 164 165
  DBUG_ENTER("net_realloc");
  DBUG_PRINT("enter",("length: %lu", length));

unknown's avatar
unknown committed
166
  if (length >= net->max_packet_size)
unknown's avatar
unknown committed
167
  {
168 169
    DBUG_PRINT("error", ("Packet too large. Max size: %lu",
               net->max_packet_size));
unknown's avatar
unknown committed
170 171 172
    net->error= 1;
    net->report_error= 1;
    net->last_errno= ER_NET_PACKET_TOO_LARGE;
173
    DBUG_RETURN(1);
unknown's avatar
unknown committed
174
  }
175
  pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1); 
unknown's avatar
unknown committed
176 177 178 179
  /*
    We must allocate some extra bytes for the end 0 and to be able to
    read big compressed blocks
  */
unknown's avatar
unknown committed
180
  if (!(buff=(uchar*) my_realloc((char*) net->buff, (uint32) pkt_length +
181 182
				 NET_HEADER_SIZE + COMP_HEADER_SIZE,
				 MYF(MY_WME))))
unknown's avatar
unknown committed
183
  {
unknown's avatar
unknown committed
184 185 186
    net->error= 1;
    net->report_error= 1;
    net->last_errno= ER_OUT_OF_RESOURCES;
187
    DBUG_RETURN(1);
unknown's avatar
unknown committed
188 189 190
  }
  net->buff=net->write_pos=buff;
  net->buff_end=buff+(net->max_packet=pkt_length);
191
  DBUG_RETURN(0);
unknown's avatar
unknown committed
192 193 194 195 196 197
}

	/* Remove unwanted characters from connection */

void net_clear(NET *net)
{
unknown's avatar
unknown committed
198
  DBUG_ENTER("net_clear");
199
#if !defined(EXTRA_DEBUG) && !defined(EMBEDDED_LIBRARY)
unknown's avatar
unknown committed
200
  {
unknown's avatar
unknown committed
201 202 203 204 205 206 207 208 209 210
    int count;					/* One may get 'unused' warn */
    my_bool old_mode;
    if (!vio_blocking(net->vio, FALSE, &old_mode))
    {
      while ((count = vio_read(net->vio, (char*) (net->buff),
			       (uint32) net->max_packet)) > 0)
	DBUG_PRINT("info",("skipped %d bytes from file: %s",
			   count, vio_description(net->vio)));
      vio_blocking(net->vio, TRUE, &old_mode);
    }
unknown's avatar
unknown committed
211 212
  }
#endif /* EXTRA_DEBUG */
unknown's avatar
unknown committed
213
  net->pkt_nr=net->compress_pkt_nr=0;		/* Ready for new command */
unknown's avatar
unknown committed
214
  net->write_pos=net->buff;
unknown's avatar
unknown committed
215
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
216 217 218 219
}

	/* Flush write_buffer if not empty. */

220
my_bool net_flush(NET *net)
unknown's avatar
unknown committed
221
{
222
  my_bool error= 0;
unknown's avatar
unknown committed
223 224 225
  DBUG_ENTER("net_flush");
  if (net->buff != net->write_pos)
  {
226 227
    error=test(net_real_write(net,(char*) net->buff,
			      (ulong) (net->write_pos - net->buff)));
unknown's avatar
unknown committed
228 229
    net->write_pos=net->buff;
  }
unknown's avatar
unknown committed
230 231 232
  /* Sync packet number if using compression */
  if (net->compress)
    net->pkt_nr=net->compress_pkt_nr;
unknown's avatar
unknown committed
233 234 235 236 237 238 239 240 241
  DBUG_RETURN(error);
}


/*****************************************************************************
** Write something to server/client buffer
*****************************************************************************/

/*
unknown's avatar
unknown committed
242 243 244 245 246 247
  Write a logical packet with packet header
  Format: Packet length (3 bytes), packet number(1 byte)
  When compression is used a 3 byte compression length is added

  NOTE
    If compression is used the original package is modified!
unknown's avatar
unknown committed
248 249
*/

250
my_bool
unknown's avatar
unknown committed
251 252 253
my_net_write(NET *net,const char *packet,ulong len)
{
  uchar buff[NET_HEADER_SIZE];
254
  /*
255 256 257
    Big packets are handled by splitting them in packets of MAX_PACKET_LENGTH
    length. The last packet is always a packet that is < MAX_PACKET_LENGTH.
    (The last packet may even have a length of 0)
258
  */
259
  while (len >= MAX_PACKET_LENGTH)
260
  {
261
    const ulong z_size = MAX_PACKET_LENGTH;
262
    int3store(buff, z_size);
unknown's avatar
unknown committed
263
    buff[3]= (uchar) net->pkt_nr++;
264 265 266 267 268 269 270
    if (net_write_buff(net, (char*) buff, NET_HEADER_SIZE) ||
	net_write_buff(net, packet, z_size))
      return 1;
    packet += z_size;
    len-=     z_size;
  }
  /* Write last packet */
unknown's avatar
unknown committed
271
  int3store(buff,len);
unknown's avatar
unknown committed
272
  buff[3]= (uchar) net->pkt_nr++;
unknown's avatar
unknown committed
273 274
  if (net_write_buff(net,(char*) buff,NET_HEADER_SIZE))
    return 1;
unknown's avatar
unknown committed
275
#ifndef DEBUG_DATA_PACKETS
unknown's avatar
unknown committed
276
  DBUG_DUMP("packet_header",(char*) buff,NET_HEADER_SIZE);
unknown's avatar
unknown committed
277
#endif
278
  return test(net_write_buff(net,packet,len));
unknown's avatar
unknown committed
279 280
}

281 282
/*
  Send a command to the server.
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306

  SYNOPSIS
    net_write_command()
    net			NET handler
    command		Command in MySQL server (enum enum_server_command)
    header		Header to write after command
    head_len		Length of header
    packet		Query or parameter to query
    len			Length of packet

  DESCRIPTION
    The reason for having both header and packet is so that libmysql
    can easy add a header to a special command (like prepared statements)
    without having to re-alloc the string.

    As the command is part of the first data packet, we have to do some data
    juggling to put the command in there, without having to create a new
    packet.
    This function will split big packets into sub-packets if needed.
    (Each sub packet can only be 2^24 bytes)

  RETURN VALUES
    0	ok
    1	error
307 308
*/

309 310 311 312
my_bool
net_write_command(NET *net,uchar command,
		  const char *header, ulong head_len,
		  const char *packet, ulong len)
unknown's avatar
unknown committed
313
{
314
  ulong length=len+1+head_len;			/* 1 extra byte for command */
315 316
  uchar buff[NET_HEADER_SIZE+1];
  uint header_size=NET_HEADER_SIZE+1;
317 318 319
  DBUG_ENTER("net_write_command");
  DBUG_PRINT("enter",("length: %lu", len));

320
  buff[4]=command;				/* For first packet */
unknown's avatar
unknown committed
321

322
  if (length >= MAX_PACKET_LENGTH)
323 324
  {
    /* Take into account that we have the command in the first header */
unknown's avatar
unknown committed
325
    len= MAX_PACKET_LENGTH - 1 - head_len;
326 327
    do
    {
328
      int3store(buff, MAX_PACKET_LENGTH);
unknown's avatar
unknown committed
329
      buff[3]= (uchar) net->pkt_nr++;
330
      if (net_write_buff(net,(char*) buff, header_size) ||
331 332
	  net_write_buff(net, header, head_len) ||
	  net_write_buff(net, packet, len))
333
	DBUG_RETURN(1);
334
      packet+= len;
335 336
      length-= MAX_PACKET_LENGTH;
      len= MAX_PACKET_LENGTH;
unknown's avatar
unknown committed
337
      head_len= 0;
338 339
      header_size= NET_HEADER_SIZE;
    } while (length >= MAX_PACKET_LENGTH);
340 341
    len=length;					/* Data left to be written */
  }
unknown's avatar
unknown committed
342
  int3store(buff,length);
unknown's avatar
unknown committed
343
  buff[3]= (uchar) net->pkt_nr++;
unknown's avatar
unknown committed
344
  DBUG_RETURN(test(net_write_buff(net, (char*) buff, header_size) ||
345
	      (head_len && net_write_buff(net, (char*) header, head_len)) ||
unknown's avatar
unknown committed
346
	      net_write_buff(net, packet, len) || net_flush(net)));
unknown's avatar
unknown committed
347 348
}

349 350
/*
  Caching the data in a local buffer before sending it.
351

352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
  SYNOPSIS
    net_write_buff()
    net		Network handler
    packet	Packet to send
    len		Length of packet

  DESCRIPTION
    Fill up net->buffer and send it to the client when full.

    If the rest of the to-be-sent-packet is bigger than buffer,
    send it in one big block (to avoid copying to internal buffer).
    If not, copy the rest of the data to the buffer and return without
    sending data.

  NOTES
    The cached buffer can be sent as it is with 'net_flush()'.

    In this code we have to be careful to not send a packet longer than
unknown's avatar
unknown committed
370 371
    MAX_PACKET_LENGTH to net_real_write() if we are using the compressed
    protocol as we store the length of the compressed packet in 3 bytes.
372 373 374 375

  RETURN
  0	ok
  1
376
*/
unknown's avatar
unknown committed
377

378
static my_bool
379
net_write_buff(NET *net,const char *packet,ulong len)
unknown's avatar
unknown committed
380
{
381 382 383 384 385
  ulong left_length;
  if (net->compress && net->max_packet > MAX_PACKET_LENGTH)
    left_length= MAX_PACKET_LENGTH - (net->write_pos - net->buff);
  else
    left_length= (ulong) (net->buff_end - net->write_pos);
unknown's avatar
unknown committed
386

unknown's avatar
unknown committed
387 388 389
#ifdef DEBUG_DATA_PACKETS
  DBUG_DUMP("data", packet, len);
#endif
390
  if (len > left_length)
unknown's avatar
unknown committed
391
  {
392
    if (net->write_pos != net->buff)
393
    {
394 395 396 397
      /* Fill up already used packet and write it */
      memcpy((char*) net->write_pos,packet,left_length);
      if (net_real_write(net,(char*) net->buff, 
			 (ulong) (net->write_pos - net->buff) + left_length))
398
	return 1;
399
      net->write_pos= net->buff;
400 401 402
      packet+= left_length;
      len-= left_length;
    }
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
    if (net->compress)
    {
      /*
	We can't have bigger packets than 16M with compression
	Because the uncompressed length is stored in 3 bytes
      */
      left_length= MAX_PACKET_LENGTH;
      while (len > left_length)
      {
	if (net_real_write(net, packet, left_length))
	  return 1;
	packet+= left_length;
	len-= left_length;
      }
    }
    if (len > net->max_packet)
      return net_real_write(net, packet, len) ? 1 : 0;
    /* Send out rest of the blocks as full sized blocks */
unknown's avatar
unknown committed
421 422
  }
  memcpy((char*) net->write_pos,packet,len);
423
  net->write_pos+= len;
unknown's avatar
unknown committed
424 425 426
  return 0;
}

427 428 429 430 431

/*
  Read and write one packet using timeouts.
  If needed, the packet is compressed before sending.
*/
unknown's avatar
unknown committed
432 433 434 435

int
net_real_write(NET *net,const char *packet,ulong len)
{
unknown's avatar
unknown committed
436
  long int length;
unknown's avatar
unknown committed
437 438
  char *pos,*end;
  thr_alarm_t alarmed;
unknown's avatar
unknown committed
439
#ifndef NO_ALARM
unknown's avatar
unknown committed
440 441 442 443 444 445
  ALARM alarm_buff;
#endif
  uint retry_count=0;
  my_bool net_blocking = vio_is_blocking(net->vio);
  DBUG_ENTER("net_real_write");

446
#if defined(MYSQL_SERVER) && defined(HAVE_QUERY_CACHE)
447 448
  if (net->query_cache_query != 0)
    query_cache_insert(net, packet, len);
unknown's avatar
unknown committed
449 450
#endif

unknown's avatar
unknown committed
451 452 453 454 455 456 457 458 459 460
  if (net->error == 2)
    DBUG_RETURN(-1);				/* socket can't be used */

  net->reading_or_writing=2;
#ifdef HAVE_COMPRESS
  if (net->compress)
  {
    ulong complen;
    uchar *b;
    uint header_length=NET_HEADER_SIZE+COMP_HEADER_SIZE;
unknown's avatar
unknown committed
461 462
    if (!(b=(uchar*) my_malloc((uint32) len + NET_HEADER_SIZE +
			       COMP_HEADER_SIZE, MYF(MY_WME))))
unknown's avatar
unknown committed
463 464
    {
#ifdef MYSQL_SERVER
unknown's avatar
unknown committed
465 466
      net->last_errno= ER_OUT_OF_RESOURCES;
      net->error= 2;
467
      /* TODO is it needed to set this variable if we have no socket */
unknown's avatar
unknown committed
468
      net->report_error= 1;
unknown's avatar
unknown committed
469
#endif
unknown's avatar
unknown committed
470
      net->reading_or_writing= 0;
unknown's avatar
unknown committed
471 472 473 474 475 476 477 478
      DBUG_RETURN(1);
    }
    memcpy(b+header_length,packet,len);

    if (my_compress((byte*) b+header_length,&len,&complen))
      complen=0;
    int3store(&b[NET_HEADER_SIZE],complen);
    int3store(b,len);
unknown's avatar
unknown committed
479
    b[3]=(uchar) (net->compress_pkt_nr++);
unknown's avatar
unknown committed
480 481 482 483 484 485
    len+= header_length;
    packet= (char*) b;
  }
#endif /* HAVE_COMPRESS */

  /* DBUG_DUMP("net",packet,len); */
unknown's avatar
unknown committed
486
#ifndef NO_ALARM
unknown's avatar
unknown committed
487 488
  thr_alarm_init(&alarmed);
  if (net_blocking)
unknown's avatar
unknown committed
489
    thr_alarm(&alarmed,(uint) net->write_timeout,&alarm_buff);
unknown's avatar
unknown committed
490 491
#else
  alarmed=0;
492
  vio_timeout(net->vio, net->write_timeout);
unknown's avatar
unknown committed
493
#endif /* NO_ALARM */
unknown's avatar
unknown committed
494 495 496 497

  pos=(char*) packet; end=pos+len;
  while (pos != end)
  {
unknown's avatar
unknown committed
498
    if ((long) (length=vio_write(net->vio,pos,(uint32) (end-pos))) <= 0)
unknown's avatar
unknown committed
499 500
    {
      my_bool interrupted = vio_should_retry(net->vio);
unknown's avatar
unknown committed
501
#if (!defined(__WIN__) && !defined(__EMX__) && !defined(OS2))
502
      if ((interrupted || length==0) && !thr_alarm_in_use(&alarmed))
unknown's avatar
unknown committed
503
      {
unknown's avatar
unknown committed
504
        if (!thr_alarm(&alarmed,(uint) net->write_timeout,&alarm_buff))
unknown's avatar
unknown committed
505
        {                                       /* Always true for client */
506 507
	  my_bool old_mode;
	  while (vio_blocking(net->vio, TRUE, &old_mode) < 0)
unknown's avatar
unknown committed
508
	  {
509
	    if (vio_should_retry(net->vio) && retry_count++ < net->retry_count)
510
	      continue;
unknown's avatar
unknown committed
511
#ifdef EXTRA_DEBUG
512 513 514
	    fprintf(stderr,
		    "%s: my_net_write: fcntl returned error %d, aborting thread\n",
		    my_progname,vio_errno(net->vio));
unknown's avatar
unknown committed
515
#endif /* EXTRA_DEBUG */
516
#ifdef MYSQL_SERVER	    
unknown's avatar
unknown committed
517
	    net->last_errno= ER_NET_ERROR_ON_WRITE;
518
#endif
unknown's avatar
unknown committed
519 520
	    net->error= 2;                     /* Close socket */
            net->report_error= 1;
521
	    goto end;
unknown's avatar
unknown committed
522 523 524 525 526 527 528
	  }
	  retry_count=0;
	  continue;
	}
      }
      else
#endif /* (!defined(__WIN__) && !defined(__EMX__)) */
529
	if (thr_alarm_in_use(&alarmed) && !thr_got_alarm(&alarmed) &&
unknown's avatar
unknown committed
530 531
	    interrupted)
      {
532
	if (retry_count++ < net->retry_count)
unknown's avatar
unknown committed
533 534 535 536 537 538 539
	    continue;
#ifdef EXTRA_DEBUG
	  fprintf(stderr, "%s: write looped, aborting thread\n",
		  my_progname);
#endif /* EXTRA_DEBUG */
      }
#if defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER)
unknown's avatar
unknown committed
540
      if (vio_errno(net->vio) == SOCKET_EINTR)
unknown's avatar
unknown committed
541 542 543 544 545
      {
	DBUG_PRINT("warning",("Interrupted write. Retrying..."));
	continue;
      }
#endif /* defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER) */
unknown's avatar
unknown committed
546 547
      net->error= 2;				/* Close socket */
      net->report_error= 1;
unknown's avatar
unknown committed
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
#ifdef MYSQL_SERVER
      net->last_errno= (interrupted ? ER_NET_WRITE_INTERRUPTED :
			ER_NET_ERROR_ON_WRITE);
#endif /* MYSQL_SERVER */
      break;
    }
    pos+=length;
    statistic_add(bytes_sent,length,&LOCK_bytes_sent);
  }
#ifndef __WIN__
 end:
#endif
#ifdef HAVE_COMPRESS
  if (net->compress)
    my_free((char*) packet,MYF(0));
#endif
564
  if (thr_alarm_in_use(&alarmed))
unknown's avatar
unknown committed
565
  {
566
    my_bool old_mode;
unknown's avatar
unknown committed
567
    thr_end_alarm(&alarmed);
568
    vio_blocking(net->vio, net_blocking, &old_mode);
unknown's avatar
unknown committed
569 570 571 572 573 574 575 576 577 578
  }
  net->reading_or_writing=0;
  DBUG_RETURN(((int) (pos != end)));
}


/*****************************************************************************
** Read something from server/clinet
*****************************************************************************/

unknown's avatar
unknown committed
579
#ifndef NO_ALARM
unknown's avatar
unknown committed
580

581 582
static my_bool net_safe_read(NET *net, char *buff, uint32 length,
			     thr_alarm_t *alarmed)
unknown's avatar
unknown committed
583 584
{
  uint retry_count=0;
585
  while (length > 0)
unknown's avatar
unknown committed
586
  {
587 588
    int tmp;
    if ((tmp=vio_read(net->vio,(char*) net->buff, length)) <= 0)
unknown's avatar
unknown committed
589 590
    {
      my_bool interrupted = vio_should_retry(net->vio);
591
      if (!thr_got_alarm(alarmed) && interrupted)
unknown's avatar
unknown committed
592
      {					/* Probably in MIT threads */
593
	if (retry_count++ < net->retry_count)
unknown's avatar
unknown committed
594 595
	  continue;
      }
596
      return 1;
unknown's avatar
unknown committed
597
    }
598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
    length-= tmp;
  }
  return 0;
}

/*
  Help function to clear the commuication buffer when we get a too big packet.

  SYNOPSIS
    my_net_skip_rest()
    net		Communication handle
    remain	Bytes to read
    alarmed	Parameter for thr_alarm()
    alarm_buff	Parameter for thr_alarm()

  RETURN VALUES
   0	Was able to read the whole packet
   1	Got mailformed packet from client
*/

static my_bool my_net_skip_rest(NET *net, uint32 remain, thr_alarm_t *alarmed,
				ALARM *alarm_buff)
{
  uint32 old=remain;
  DBUG_ENTER("my_net_skip_rest");
  DBUG_PRINT("enter",("bytes_to_skip: %u", (uint) remain));

625 626 627
  /* The following is good for debugging */
  statistic_increment(net_big_packet_count,&LOCK_bytes_received);

628
  if (!thr_alarm_in_use(alarmed))
629 630
  {
    my_bool old_mode;
unknown's avatar
unknown committed
631
    if (thr_alarm(alarmed,net->read_timeout, alarm_buff) ||
632 633 634 635 636 637
	vio_blocking(net->vio, TRUE, &old_mode) < 0)
      DBUG_RETURN(1);				/* Can't setup, abort */
  }
  for (;;)
  {
    while (remain > 0)
638
    {
639 640 641 642 643
      uint length= min(remain, net->max_packet);
      if (net_safe_read(net, (char*) net->buff, length, alarmed))
	DBUG_RETURN(1);
      statistic_add(bytes_received, length, &LOCK_bytes_received);
      remain -= (uint32) length;
unknown's avatar
unknown committed
644
    }
645 646 647 648 649 650
    if (old != MAX_PACKET_LENGTH)
      break;
    if (net_safe_read(net, (char*) net->buff, NET_HEADER_SIZE, alarmed))
      DBUG_RETURN(1);
    old=remain= uint3korr(net->buff);
    net->pkt_nr++;
unknown's avatar
unknown committed
651
  }
652
  DBUG_RETURN(0);
unknown's avatar
unknown committed
653
}
unknown's avatar
unknown committed
654
#endif /* NO_ALARM */
unknown's avatar
unknown committed
655 656


657 658 659 660 661 662
/*
  Reads one packet to net->buff + net->where_b
  Returns length of packet.  Long packets are handled by my_net_read().
  This function reallocates the net->buff buffer if necessary.
*/

unknown's avatar
unknown committed
663
static ulong
unknown's avatar
unknown committed
664 665 666 667 668 669 670
my_real_read(NET *net, ulong *complen)
{
  uchar *pos;
  long length;
  uint i,retry_count=0;
  ulong len=packet_error;
  thr_alarm_t alarmed;
unknown's avatar
unknown committed
671
#ifndef NO_ALARM
unknown's avatar
unknown committed
672 673 674
  ALARM alarm_buff;
#endif
  my_bool net_blocking=vio_is_blocking(net->vio);
unknown's avatar
unknown committed
675 676
  uint32 remain= (net->compress ? NET_HEADER_SIZE+COMP_HEADER_SIZE :
		  NET_HEADER_SIZE);
unknown's avatar
unknown committed
677 678 679 680
  *complen = 0;

  net->reading_or_writing=1;
  thr_alarm_init(&alarmed);
unknown's avatar
unknown committed
681
#ifndef NO_ALARM
unknown's avatar
unknown committed
682
  if (net_blocking)
unknown's avatar
unknown committed
683
    thr_alarm(&alarmed,net->read_timeout,&alarm_buff);
684 685
#else
  vio_timeout(net->vio, net->read_timeout);
unknown's avatar
unknown committed
686
#endif /* NO_ALARM */
unknown's avatar
unknown committed
687 688 689 690 691 692 693 694 695 696 697 698 699

    pos = net->buff + net->where_b;		/* net->packet -4 */
    for (i=0 ; i < 2 ; i++)
    {
      while (remain > 0)
      {
	/* First read is done with non blocking mode */
        if ((int) (length=vio_read(net->vio,(char*) pos,remain)) <= 0L)
        {
          my_bool interrupted = vio_should_retry(net->vio);

	  DBUG_PRINT("info",("vio_read returned %d,  errno: %d",
			     length, vio_errno(net->vio)));
unknown's avatar
unknown committed
700
#if (!defined(__WIN__) && !defined(__EMX__) && !defined(OS2)) || defined(MYSQL_SERVER)
unknown's avatar
unknown committed
701 702 703 704 705
	  /*
	    We got an error that there was no data on the socket. We now set up
	    an alarm to not 'read forever', change the socket to non blocking
	    mode and try again
	  */
706
	  if ((interrupted || length == 0) && !thr_alarm_in_use(&alarmed))
unknown's avatar
unknown committed
707
	  {
unknown's avatar
unknown committed
708
	    if (!thr_alarm(&alarmed,net->read_timeout,&alarm_buff)) /* Don't wait too long */
unknown's avatar
unknown committed
709
	    {
710 711 712 713
	      my_bool old_mode;
	      while (vio_blocking(net->vio, TRUE, &old_mode) < 0)
	      {
		if (vio_should_retry(net->vio) &&
714
		    retry_count++ < net->retry_count)
715 716 717 718
		  continue;
		DBUG_PRINT("error",
			   ("fcntl returned error %d, aborting thread",
			    vio_errno(net->vio)));
unknown's avatar
unknown committed
719
#ifdef EXTRA_DEBUG
720 721 722
		fprintf(stderr,
			"%s: read: fcntl returned error %d, aborting thread\n",
			my_progname,vio_errno(net->vio));
unknown's avatar
unknown committed
723
#endif /* EXTRA_DEBUG */
724
		len= packet_error;
unknown's avatar
unknown committed
725 726
		net->error= 2;                 /* Close socket */
	        net->report_error= 1;
unknown's avatar
unknown committed
727
#ifdef MYSQL_SERVER
unknown's avatar
unknown committed
728
		net->last_errno= ER_NET_FCNTL_ERROR;
unknown's avatar
unknown committed
729
#endif
730 731
		goto end;
	      }
unknown's avatar
unknown committed
732 733 734 735 736
	      retry_count=0;
	      continue;
	    }
	  }
#endif /* (!defined(__WIN__) && !defined(__EMX__)) || defined(MYSQL_SERVER) */
737
	  if (thr_alarm_in_use(&alarmed) && !thr_got_alarm(&alarmed) &&
unknown's avatar
unknown committed
738 739
	      interrupted)
	  {					/* Probably in MIT threads */
740
	    if (retry_count++ < net->retry_count)
unknown's avatar
unknown committed
741 742 743 744 745 746 747 748 749 750 751 752 753
	      continue;
#ifdef EXTRA_DEBUG
	    fprintf(stderr, "%s: read looped with error %d, aborting thread\n",
		    my_progname,vio_errno(net->vio));
#endif /* EXTRA_DEBUG */
	  }
#if defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER)
	  if (vio_should_retry(net->vio))
	  {
	    DBUG_PRINT("warning",("Interrupted read. Retrying..."));
	    continue;
	  }
#endif
754 755
	  DBUG_PRINT("error",("Couldn't read packet: remain: %u  errno: %d  length: %ld",
			      remain, vio_errno(net->vio), length));
unknown's avatar
unknown committed
756
	  len= packet_error;
unknown's avatar
unknown committed
757 758
	  net->error= 2;				/* Close socket */
	  net->report_error= 1;
unknown's avatar
unknown committed
759 760 761 762 763 764
#ifdef MYSQL_SERVER
	  net->last_errno= (interrupted ? ER_NET_READ_INTERRUPTED :
			    ER_NET_READ_ERROR);
#endif
	  goto end;
	}
unknown's avatar
unknown committed
765
	remain -= (uint32) length;
unknown's avatar
unknown committed
766 767 768 769 770 771
	pos+= (ulong) length;
	statistic_add(bytes_received,(ulong) length,&LOCK_bytes_received);
      }
      if (i == 0)
      {					/* First parts is packet length */
	ulong helping;
unknown's avatar
unknown committed
772 773
        DBUG_DUMP("packet_header",(char*) net->buff+net->where_b,
                  NET_HEADER_SIZE);
unknown's avatar
unknown committed
774 775 776 777 778
	if (net->buff[net->where_b + 3] != (uchar) net->pkt_nr)
	{
	  if (net->buff[net->where_b] != (uchar) 255)
	  {
	    DBUG_PRINT("error",
unknown's avatar
unknown committed
779
		       ("Packets out of order (Found: %d, expected %u)",
unknown's avatar
unknown committed
780
			(int) net->buff[net->where_b + 3],
unknown's avatar
unknown committed
781
			net->pkt_nr));
unknown's avatar
unknown committed
782 783 784 785 786 787 788
#ifdef EXTRA_DEBUG
	    fprintf(stderr,"Packets out of order (Found: %d, expected %d)\n",
		    (int) net->buff[net->where_b + 3],
		    (uint) (uchar) net->pkt_nr);
#endif
	  }
	  len= packet_error;
unknown's avatar
unknown committed
789
	  net->report_error= 1;
unknown's avatar
unknown committed
790 791 792 793 794
#ifdef MYSQL_SERVER
	  net->last_errno=ER_NET_PACKETS_OUT_OF_ORDER;
#endif
	  goto end;
	}
unknown's avatar
unknown committed
795
	net->compress_pkt_nr= ++net->pkt_nr;
unknown's avatar
unknown committed
796 797 798
#ifdef HAVE_COMPRESS
	if (net->compress)
	{
799 800 801 802
	  /*
	    If the packet is compressed then complen > 0 and contains the
	    number of bytes in the uncompressed packet
	  */
unknown's avatar
unknown committed
803 804 805 806 807
	  *complen=uint3korr(&(net->buff[net->where_b + NET_HEADER_SIZE]));
	}
#endif

	len=uint3korr(net->buff+net->where_b);
808 809
	if (!len)				/* End of big multi-packet */
	  goto end;
unknown's avatar
unknown committed
810 811 812 813
	helping = max(len,*complen) + net->where_b;
	/* The necessary size of net->buff */
	if (helping >= net->max_packet)
	{
814
	  if (net_realloc(net,helping))
unknown's avatar
unknown committed
815
	  {
816 817 818 819
#if defined(MYSQL_SERVER) && !defined(NO_ALARM)
	    if (!net->compress &&
		!my_net_skip_rest(net, (uint32) len, &alarmed, &alarm_buff))
	      net->error= 3;		/* Successfully skiped packet */
unknown's avatar
unknown committed
820
#endif
821
	    len= packet_error;          /* Return error and close connection */
unknown's avatar
unknown committed
822 823 824 825
	    goto end;
	  }
	}
	pos=net->buff + net->where_b;
unknown's avatar
unknown committed
826
	remain = (uint32) len;
unknown's avatar
unknown committed
827 828 829 830
      }
    }

end:
831
  if (thr_alarm_in_use(&alarmed))
unknown's avatar
unknown committed
832
  {
833
    my_bool old_mode;
unknown's avatar
unknown committed
834
    thr_end_alarm(&alarmed);
835
    vio_blocking(net->vio, net_blocking, &old_mode);
unknown's avatar
unknown committed
836 837
  }
  net->reading_or_writing=0;
unknown's avatar
unknown committed
838 839 840 841
#ifdef DEBUG_DATA_PACKETS
  if (len != packet_error)
    DBUG_DUMP("data",(char*) net->buff+net->where_b, len);
#endif
unknown's avatar
unknown committed
842 843 844
  return(len);
}

845 846 847 848 849 850 851 852 853 854 855 856 857 858 859

/*
  Read a packet from the client/server and return it without the internal
  package header.
  If the packet is the first packet of a multi-packet packet
  (which is indicated by the length of the packet = 0xffffff) then
  all sub packets are read and concatenated.
  If the packet was compressed, its uncompressed and the length of the
  uncompressed packet is returned.

  The function returns the length of the found packet or packet_error.
  net->read_pos points to the read data.
*/

ulong
unknown's avatar
unknown committed
860 861 862 863 864 865 866 867
my_net_read(NET *net)
{
  ulong len,complen;

#ifdef HAVE_COMPRESS
  if (!net->compress)
  {
#endif
868
    len = my_real_read(net,&complen);
869
    if (len == MAX_PACKET_LENGTH)
870 871
    {
      /* First packet of a multi-packet.  Concatenate the packets */
unknown's avatar
unknown committed
872
      ulong save_pos = net->where_b;
873 874 875 876 877
      ulong total_length=0;
      do
      {
	net->where_b += len;
	total_length += len;
878
	len = my_real_read(net,&complen);
879
      } while (len == MAX_PACKET_LENGTH);
880 881 882 883
      if (len != packet_error)
	len+= total_length;
      net->where_b = save_pos;
    }
unknown's avatar
unknown committed
884 885 886 887 888 889
    net->read_pos = net->buff + net->where_b;
    if (len != packet_error)
      net->read_pos[len]=0;		/* Safeguard for mysql_use_result */
    return len;
#ifdef HAVE_COMPRESS
  }
890
  else
unknown's avatar
unknown committed
891
  {
892 893
    /* We are using the compressed protocol */

unknown's avatar
unknown committed
894 895 896
    ulong buf_length;
    ulong start_of_packet;
    ulong first_packet_offset;
897 898
    uint read_length, multi_byte_packet=0;

unknown's avatar
unknown committed
899 900
    if (net->remain_in_buf)
    {
901
      buf_length= net->buf_length;		/* Data left in old packet */
unknown's avatar
unknown committed
902 903
      first_packet_offset= start_of_packet= (net->buf_length -
					     net->remain_in_buf);
904
      /* Restore the character that was overwritten by the end 0 */
unknown's avatar
unknown committed
905
      net->buff[start_of_packet]= net->save_char;
906 907 908
    }
    else
    {
909
      /* reuse buffer, as there is nothing in it that we need */
unknown's avatar
unknown committed
910
      buf_length= start_of_packet= first_packet_offset= 0;
911 912 913 914
    }
    for (;;)
    {
      ulong packet_len;
unknown's avatar
unknown committed
915

916
      if (buf_length - start_of_packet >= NET_HEADER_SIZE)
unknown's avatar
unknown committed
917
      {
918 919 920 921 922 923 924 925
	read_length = uint3korr(net->buff+start_of_packet);
	if (!read_length)
	{ 
	  /* End of multi-byte packet */
	  start_of_packet += NET_HEADER_SIZE;
	  break;
	}
	if (read_length + NET_HEADER_SIZE <= buf_length - start_of_packet)
unknown's avatar
unknown committed
926
	{
927 928 929 930 931 932 933 934 935 936 937 938 939
	  if (multi_byte_packet)
	  {
	    /* Remove packet header for second packet */
	    memmove(net->buff + first_packet_offset + start_of_packet,
		    net->buff + first_packet_offset + start_of_packet +
		    NET_HEADER_SIZE,
		    buf_length - start_of_packet);
	    start_of_packet += read_length;
	    buf_length -= NET_HEADER_SIZE;
	  }
	  else
	    start_of_packet+= read_length + NET_HEADER_SIZE;

940
	  if (read_length != MAX_PACKET_LENGTH)	/* last package */
941
	  {
unknown's avatar
unknown committed
942
	    multi_byte_packet= 0;		/* No last zero len packet */
943 944 945 946 947 948 949 950 951 952 953 954 955
	    break;
	  }
	  multi_byte_packet= NET_HEADER_SIZE;
	  /* Move data down to read next data packet after current one */
	  if (first_packet_offset)
	  {
	    memmove(net->buff,net->buff+first_packet_offset,
		    buf_length-first_packet_offset);
	    buf_length-=first_packet_offset;
	    start_of_packet -= first_packet_offset;
	    first_packet_offset=0;
	  }
	  continue;
unknown's avatar
unknown committed
956 957 958
	}
      }
      /* Move data down to read next data packet after current one */
959
      if (first_packet_offset)
unknown's avatar
unknown committed
960
      {
961 962 963 964 965
	memmove(net->buff,net->buff+first_packet_offset,
		buf_length-first_packet_offset);
	buf_length-=first_packet_offset;
	start_of_packet -= first_packet_offset;
	first_packet_offset=0;
unknown's avatar
unknown committed
966 967
      }

968 969 970 971 972 973
      net->where_b=buf_length;
      if ((packet_len = my_real_read(net,&complen)) == packet_error)
	return packet_error;
      if (my_uncompress((byte*) net->buff + net->where_b, &packet_len,
			&complen))
      {
unknown's avatar
unknown committed
974 975
	net->error= 2;			/* caller will close socket */
	net->report_error= 1;
unknown's avatar
unknown committed
976
#ifdef MYSQL_SERVER
977
	net->last_errno=ER_NET_UNCOMPRESS_ERROR;
unknown's avatar
unknown committed
978
#endif
979 980 981
	return packet_error;
      }
      buf_length+=packet_len;
unknown's avatar
unknown committed
982
    }
983 984 985

    net->read_pos=      net->buff+ first_packet_offset + NET_HEADER_SIZE;
    net->buf_length=    buf_length;
unknown's avatar
unknown committed
986 987
    net->remain_in_buf= (ulong) (buf_length - start_of_packet);
    len = ((ulong) (start_of_packet - first_packet_offset) - NET_HEADER_SIZE -
988
           multi_byte_packet);
unknown's avatar
unknown committed
989 990 991
    net->save_char= net->read_pos[len];	/* Must be saved */
    net->read_pos[len]=0;		/* Safeguard for mysql_use_result */
  }
992
#endif /* HAVE_COMPRESS */
unknown's avatar
unknown committed
993 994
  return len;
}
995