net_serv.cc 27.3 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

unknown's avatar
unknown committed
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
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
unknown committed
55 56 57 58 59 60 61
#ifdef EMBEDDED_LIBRARY
#undef MYSQL_SERVER
#undef MYSQL_CLIENT
#define MYSQL_CLIENT
#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
unknown's avatar
unknown committed
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;
unknown's avatar
unknown committed
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 */

unknown's avatar
unknown committed
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 */

unknown's avatar
unknown committed
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
  {
unknown's avatar
unknown committed
168 169 170 171 172
    DBUG_PRINT("error", ("Packet too large. Max size: %lu",
               net->max_packet_size));
    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. */

unknown's avatar
unknown committed
220
my_bool net_flush(NET *net)
unknown's avatar
unknown committed
221
{
unknown's avatar
unknown committed
222
  my_bool error= 0;
unknown's avatar
unknown committed
223 224 225
  DBUG_ENTER("net_flush");
  if (net->buff != net->write_pos)
  {
unknown's avatar
unknown committed
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
*/

unknown's avatar
unknown committed
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
  if (unlikely(!net->vio)) /* nowhere to write */
255
    return 0;
256
  /*
257 258 259
    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)
260
  */
261
  while (len >= MAX_PACKET_LENGTH)
262
  {
263
    const ulong z_size = MAX_PACKET_LENGTH;
264
    int3store(buff, z_size);
unknown's avatar
unknown committed
265
    buff[3]= (uchar) net->pkt_nr++;
266 267 268 269 270 271 272
    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
273
  int3store(buff,len);
unknown's avatar
unknown committed
274
  buff[3]= (uchar) net->pkt_nr++;
unknown's avatar
unknown committed
275 276
  if (net_write_buff(net,(char*) buff,NET_HEADER_SIZE))
    return 1;
unknown's avatar
unknown committed
277
#ifndef DEBUG_DATA_PACKETS
unknown's avatar
unknown committed
278
  DBUG_DUMP("packet_header",(char*) buff,NET_HEADER_SIZE);
unknown's avatar
unknown committed
279
#endif
280
  return test(net_write_buff(net,packet,len));
unknown's avatar
unknown committed
281 282
}

283 284
/*
  Send a command to the server.
unknown's avatar
unknown committed
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308

  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
309 310
*/

unknown's avatar
unknown committed
311 312 313 314
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
315
{
unknown's avatar
unknown committed
316
  ulong length=len+1+head_len;			/* 1 extra byte for command */
317 318
  uchar buff[NET_HEADER_SIZE+1];
  uint header_size=NET_HEADER_SIZE+1;
319 320 321
  DBUG_ENTER("net_write_command");
  DBUG_PRINT("enter",("length: %lu", len));

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

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

351 352
/*
  Caching the data in a local buffer before sending it.
353

354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
  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
372 373
    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.
374 375 376 377

  RETURN
  0	ok
  1
378
*/
unknown's avatar
unknown committed
379

380
static my_bool
381
net_write_buff(NET *net,const char *packet,ulong len)
unknown's avatar
unknown committed
382
{
383 384 385 386 387
  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
388

unknown's avatar
unknown committed
389 390 391
#ifdef DEBUG_DATA_PACKETS
  DBUG_DUMP("data", packet, len);
#endif
392
  if (len > left_length)
unknown's avatar
unknown committed
393
  {
394
    if (net->write_pos != net->buff)
395
    {
396 397 398 399
      /* 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))
400
	return 1;
401
      net->write_pos= net->buff;
402 403 404
      packet+= left_length;
      len-= left_length;
    }
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
    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
423 424
  }
  memcpy((char*) net->write_pos,packet,len);
425
  net->write_pos+= len;
unknown's avatar
unknown committed
426 427 428
  return 0;
}

429 430 431 432 433

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

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

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

unknown's avatar
unknown committed
453 454 455 456 457 458 459 460 461 462
  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
463 464
    if (!(b=(uchar*) my_malloc((uint32) len + NET_HEADER_SIZE +
			       COMP_HEADER_SIZE, MYF(MY_WME))))
unknown's avatar
unknown committed
465 466
    {
#ifdef MYSQL_SERVER
unknown's avatar
unknown committed
467 468 469 470
      net->last_errno= ER_OUT_OF_RESOURCES;
      net->error= 2;
      /* TODO is it needed to set this variable if we have no socket */
      net->report_error= 1;
unknown's avatar
unknown committed
471
#endif
unknown's avatar
unknown committed
472
      net->reading_or_writing= 0;
unknown's avatar
unknown committed
473 474 475 476 477 478 479 480
      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
481
    b[3]=(uchar) (net->compress_pkt_nr++);
unknown's avatar
unknown committed
482 483 484 485 486 487
    len+= header_length;
    packet= (char*) b;
  }
#endif /* HAVE_COMPRESS */

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

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


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

unknown's avatar
unknown committed
581
#ifndef NO_ALARM
unknown's avatar
unknown committed
582

583 584
static my_bool net_safe_read(NET *net, char *buff, uint32 length,
			     thr_alarm_t *alarmed)
unknown's avatar
unknown committed
585 586
{
  uint retry_count=0;
587
  while (length > 0)
unknown's avatar
unknown committed
588
  {
589 590
    int tmp;
    if ((tmp=vio_read(net->vio,(char*) net->buff, length)) <= 0)
unknown's avatar
unknown committed
591 592
    {
      my_bool interrupted = vio_should_retry(net->vio);
593
      if (!thr_got_alarm(alarmed) && interrupted)
unknown's avatar
unknown committed
594
      {					/* Probably in MIT threads */
595
	if (retry_count++ < net->retry_count)
unknown's avatar
unknown committed
596 597
	  continue;
      }
598
      return 1;
unknown's avatar
unknown committed
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 625 626
    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));

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

630
  if (!thr_alarm_in_use(alarmed))
631 632
  {
    my_bool old_mode;
unknown's avatar
unknown committed
633
    if (thr_alarm(alarmed,net->read_timeout, alarm_buff) ||
634 635 636 637 638 639
	vio_blocking(net->vio, TRUE, &old_mode) < 0)
      DBUG_RETURN(1);				/* Can't setup, abort */
  }
  for (;;)
  {
    while (remain > 0)
640
    {
641 642 643 644 645
      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;
646
    }
647 648 649 650 651 652
    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
653
  }
654
  DBUG_RETURN(0);
unknown's avatar
unknown committed
655
}
unknown's avatar
unknown committed
656
#endif /* NO_ALARM */
unknown's avatar
unknown committed
657 658


659 660 661 662 663 664
/*
  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
665
static ulong
unknown's avatar
unknown committed
666 667 668 669 670 671 672
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
673
#ifndef NO_ALARM
unknown's avatar
unknown committed
674 675 676
  ALARM alarm_buff;
#endif
  my_bool net_blocking=vio_is_blocking(net->vio);
unknown's avatar
unknown committed
677 678
  uint32 remain= (net->compress ? NET_HEADER_SIZE+COMP_HEADER_SIZE :
		  NET_HEADER_SIZE);
unknown's avatar
unknown committed
679 680 681 682
  *complen = 0;

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

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

	len=uint3korr(net->buff+net->where_b);
810 811
	if (!len)				/* End of big multi-packet */
	  goto end;
unknown's avatar
unknown committed
812 813 814 815
	helping = max(len,*complen) + net->where_b;
	/* The necessary size of net->buff */
	if (helping >= net->max_packet)
	{
816
	  if (net_realloc(net,helping))
unknown's avatar
unknown committed
817
	  {
818 819 820 821
#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
822
#endif
823
	    len= packet_error;          /* Return error and close connection */
unknown's avatar
unknown committed
824 825 826 827
	    goto end;
	  }
	}
	pos=net->buff + net->where_b;
unknown's avatar
unknown committed
828
	remain = (uint32) len;
unknown's avatar
unknown committed
829 830 831 832
      }
    }

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

847 848 849 850 851 852 853 854 855 856 857 858 859 860 861

/*
  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
862 863 864 865 866 867 868 869
my_net_read(NET *net)
{
  ulong len,complen;

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

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

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

918
      if (buf_length - start_of_packet >= NET_HEADER_SIZE)
unknown's avatar
unknown committed
919
      {
920 921 922 923 924 925 926 927
	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
928
	{
929 930 931 932 933 934 935 936 937 938 939 940 941
	  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;

942
	  if (read_length != MAX_PACKET_LENGTH)	/* last package */
943
	  {
unknown's avatar
unknown committed
944
	    multi_byte_packet= 0;		/* No last zero len packet */
945 946 947 948 949 950 951 952 953 954 955 956 957
	    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
958 959 960
	}
      }
      /* Move data down to read next data packet after current one */
961
      if (first_packet_offset)
unknown's avatar
unknown committed
962
      {
963 964 965 966 967
	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
968 969
      }

970 971 972 973 974 975
      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
976 977
	net->error= 2;			/* caller will close socket */
	net->report_error= 1;
unknown's avatar
unknown committed
978
#ifdef MYSQL_SERVER
979
	net->last_errno=ER_NET_UNCOMPRESS_ERROR;
unknown's avatar
unknown committed
980
#endif
981 982 983
	return packet_error;
      }
      buf_length+=packet_len;
unknown's avatar
unknown committed
984
    }
985 986 987

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