~drizzle-trunk/drizzle/development

971.7.10 by Eric Day
Duplicated oldlibdrizzle module, one for Drizzle protocol and one for MySQL, per Brian's request from merge proposal. Port options are now --drizzle-protocol-port and --mysql-protocol-port.
1
/* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2
 *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
3
 *
4
 *  Copyright (C) 2008 Sun Microsystems
5
 *
6
 * This program is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; version 2 of the License.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
 */
19
1241.9.12 by Monty Taylor
Trims more out of server_includes.h.
20
#include "config.h"
971.7.10 by Eric Day
Duplicated oldlibdrizzle module, one for Drizzle protocol and one for MySQL, per Brian's request from merge proposal. Port options are now --drizzle-protocol-port and --mysql-protocol-port.
21
#include <drizzled/gettext.h>
22
#include <drizzled/error.h>
23
#include <drizzled/query_id.h>
24
#include <drizzled/sql_state.h>
25
#include <drizzled/session.h>
1241.9.64 by Monty Taylor
Moved remaining non-public portions of mysys and mystrings to drizzled/internal.
26
#include "drizzled/internal/m_string.h"
971.7.10 by Eric Day
Duplicated oldlibdrizzle module, one for Drizzle protocol and one for MySQL, per Brian's request from merge proposal. Port options are now --drizzle-protocol-port and --mysql-protocol-port.
27
#include <algorithm>
28
29
#include "pack.h"
30
#include "errmsg.h"
31
#include "oldlibdrizzle.h"
32
#include "options.h"
33
34
using namespace std;
35
using namespace drizzled;
36
37
#define PROTOCOL_VERSION 10
38
1241.9.12 by Monty Taylor
Trims more out of server_includes.h.
39
extern uint32_t global_thread_id;
40
971.7.10 by Eric Day
Duplicated oldlibdrizzle module, one for Drizzle protocol and one for MySQL, per Brian's request from merge proposal. Port options are now --drizzle-protocol-port and --mysql-protocol-port.
41
static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024;
42
static uint32_t port;
43
static uint32_t connect_timeout;
44
static uint32_t read_timeout;
45
static uint32_t write_timeout;
46
static uint32_t retry_count;
47
static uint32_t buffer_length;
48
static char* bind_address;
49
50
const char* ListenMySQLProtocol::getHost(void) const
51
{
52
  return bind_address;
53
}
54
55
in_port_t ListenMySQLProtocol::getPort(void) const
56
{
57
  return (in_port_t) port;
58
}
59
60
plugin::Client *ListenMySQLProtocol::getClient(int fd)
61
{
62
  int new_fd;
63
  new_fd= acceptTcp(fd);
64
  if (new_fd == -1)
65
    return NULL;
66
67
  return new (nothrow) ClientMySQLProtocol(new_fd, using_mysql41_protocol);
68
}
69
70
ClientMySQLProtocol::ClientMySQLProtocol(int fd, bool using_mysql41_protocol_arg):
71
  using_mysql41_protocol(using_mysql41_protocol_arg)
72
{
73
  net.vio= 0;
74
75
  if (fd == -1)
76
    return;
77
78
  if (drizzleclient_net_init_sock(&net, fd, 0, buffer_length))
79
    throw bad_alloc();
80
81
  drizzleclient_net_set_read_timeout(&net, read_timeout);
82
  drizzleclient_net_set_write_timeout(&net, write_timeout);
83
  net.retry_count=retry_count;
84
}
85
86
ClientMySQLProtocol::~ClientMySQLProtocol()
87
{
88
  if (net.vio)
89
    drizzleclient_vio_close(net.vio);
90
}
91
92
int ClientMySQLProtocol::getFileDescriptor(void)
93
{
94
  return drizzleclient_net_get_sd(&net);
95
}
96
97
bool ClientMySQLProtocol::isConnected()
98
{
99
  return net.vio != 0;
100
}
101
102
bool ClientMySQLProtocol::isReading(void)
103
{
104
  return net.reading_or_writing == 1;
105
}
106
107
bool ClientMySQLProtocol::isWriting(void)
108
{
109
  return net.reading_or_writing == 2;
110
}
111
112
bool ClientMySQLProtocol::flush()
113
{
114
  if (net.vio == NULL)
115
    return false;
116
  bool ret= drizzleclient_net_write(&net, (unsigned char*) packet.ptr(),
117
                           packet.length());
118
  packet.length(0);
119
  return ret;
120
}
121
122
void ClientMySQLProtocol::close(void)
123
{
124
  if (net.vio)
125
  { 
126
    drizzleclient_net_close(&net);
127
    drizzleclient_net_end(&net);
128
  }
129
}
130
131
bool ClientMySQLProtocol::authenticate()
132
{
133
  bool connection_is_valid;
134
135
  /* Use "connect_timeout" value during connection phase */
136
  drizzleclient_net_set_read_timeout(&net, connect_timeout);
137
  drizzleclient_net_set_write_timeout(&net, connect_timeout);
138
139
  connection_is_valid= checkConnection();
140
141
  if (connection_is_valid)
142
    sendOK();
143
  else
144
  {
145
    sendError(session->main_da.sql_errno(), session->main_da.message());
146
    return false;
147
  }
148
149
  /* Connect completed, set read/write timeouts back to default */
150
  drizzleclient_net_set_read_timeout(&net, read_timeout);
151
  drizzleclient_net_set_write_timeout(&net, write_timeout);
152
  return true;
153
}
154
155
bool ClientMySQLProtocol::readCommand(char **l_packet, uint32_t *packet_length)
156
{
157
  /*
158
    This thread will do a blocking read from the client which
159
    will be interrupted when the next command is received from
160
    the client, the connection is closed or "net_wait_timeout"
161
    number of seconds has passed
162
  */
163
#ifdef NEVER
164
  /* We can do this much more efficiently with poll timeouts or watcher thread,
165
     disabling for now, which means net_wait_timeout == read_timeout. */
166
  drizzleclient_net_set_read_timeout(&net,
167
                                     session->variables.net_wait_timeout);
168
#endif
169
170
  net.pkt_nr=0;
171
172
  *packet_length= drizzleclient_net_read(&net);
173
  if (*packet_length == packet_error)
174
  {
175
    /* Check if we can continue without closing the connection */
176
177
    if(net.last_errno== CR_NET_PACKET_TOO_LARGE)
178
      my_error(ER_NET_PACKET_TOO_LARGE, MYF(0));
179
    if (session->main_da.status() == Diagnostics_area::DA_ERROR)
180
      sendError(session->main_da.sql_errno(), session->main_da.message());
181
    else
182
      sendOK();
183
184
    if (net.error != 3)
185
      return false;                       // We have to close it.
186
187
    net.error= 0;
188
    *packet_length= 0;
189
    return true;
190
  }
191
192
  *l_packet= (char*) net.read_pos;
193
194
  /*
195
    'packet_length' contains length of data, as it was stored in packet
196
    header. In case of malformed header, drizzleclient_net_read returns zero.
197
    If packet_length is not zero, drizzleclient_net_read ensures that the returned
198
    number of bytes was actually read from network.
199
    There is also an extra safety measure in drizzleclient_net_read:
200
    it sets packet[packet_length]= 0, but only for non-zero packets.
201
  */
202
203
  if (*packet_length == 0)                       /* safety */
204
  {
205
    /* Initialize with COM_SLEEP packet */
206
    (*l_packet)[0]= (unsigned char) COM_SLEEP;
207
    *packet_length= 1;
208
  }
209
  else if (using_mysql41_protocol)
210
  {
211
    /* Map from MySQL commands to Drizzle commands. */
212
    switch ((int)(*l_packet)[0])
213
    {
214
    case 0: /* SLEEP */
215
    case 1: /* QUIT */
216
    case 2: /* INIT_DB */
217
    case 3: /* QUERY */
218
      break;
219
220
    case 8: /* SHUTDOWN */
221
      (*l_packet)[0]= (unsigned char) COM_SHUTDOWN;
222
      break;
223
224
    case 14: /* PING */
971.8.5 by Eric Day
Added test cases for --mysql flag.
225
      (*l_packet)[0]= (unsigned char) COM_PING;
971.7.10 by Eric Day
Duplicated oldlibdrizzle module, one for Drizzle protocol and one for MySQL, per Brian's request from merge proposal. Port options are now --drizzle-protocol-port and --mysql-protocol-port.
226
      break;
227
228
229
    default:
230
      /* Just drop connection for MySQL commands we don't support. */
231
      (*l_packet)[0]= (unsigned char) COM_QUIT;
232
      *packet_length= 1;
233
      break;
234
    }
235
  }
236
237
  /* Do not rely on drizzleclient_net_read, extra safety against programming errors. */
238
  (*l_packet)[*packet_length]= '\0';                  /* safety */
239
240
#ifdef NEVER
241
  /* See comment above. */
242
  /* Restore read timeout value */
243
  drizzleclient_net_set_read_timeout(&net,
244
                                     session->variables.net_read_timeout);
245
#endif
246
247
  return true;
248
}
249
250
/**
251
  Return ok to the client.
252
253
  The ok packet has the following structure:
254
255
  - 0               : Marker (1 byte)
256
  - affected_rows    : Stored in 1-9 bytes
257
  - id        : Stored in 1-9 bytes
258
  - server_status    : Copy of session->server_status;  Can be used by client
259
  to check if we are inside an transaction.
260
  New in 4.0 client
261
  - warning_count    : Stored in 2 bytes; New in 4.1 client
262
  - message        : Stored as packed length (1-9 bytes) + message.
263
  Is not stored if no message.
264
265
  @param session           Thread handler
266
  @param affected_rows       Number of rows changed by statement
267
  @param id           Auto_increment id for first row (if used)
268
  @param message       Message to send to the client (Used by mysql_status)
269
*/
270
271
void ClientMySQLProtocol::sendOK()
272
{
273
  unsigned char buff[DRIZZLE_ERRMSG_SIZE+10],*pos;
274
  const char *message= NULL;
275
  uint32_t tmp;
276
277
  if (!net.vio)    // hack for re-parsing queries
278
  {
279
    return;
280
  }
281
282
  buff[0]=0;                    // No fields
283
  if (session->main_da.status() == Diagnostics_area::DA_OK)
284
  {
285
    if (client_capabilities & CLIENT_FOUND_ROWS && session->main_da.found_rows())
286
      pos=drizzleclient_net_store_length(buff+1,session->main_da.found_rows());
287
    else
288
      pos=drizzleclient_net_store_length(buff+1,session->main_da.affected_rows());
289
    pos=drizzleclient_net_store_length(pos, session->main_da.last_insert_id());
290
    int2store(pos, session->main_da.server_status());
291
    pos+=2;
292
    tmp= min(session->main_da.total_warn_count(), (uint32_t)65535);
293
    message= session->main_da.message();
294
  }
295
  else
296
  {
297
    pos=drizzleclient_net_store_length(buff+1,0);
298
    pos=drizzleclient_net_store_length(pos, 0);
299
    int2store(pos, session->server_status);
300
    pos+=2;
301
    tmp= min(session->total_warn_count, (uint32_t)65535);
302
  }
303
304
  /* We can only return up to 65535 warnings in two bytes */
305
  int2store(pos, tmp);
306
  pos+= 2;
307
308
  session->main_da.can_overwrite_status= true;
309
310
  if (message && message[0])
311
  {
312
    size_t length= strlen(message);
313
    pos=drizzleclient_net_store_length(pos,length);
314
    memcpy(pos,(unsigned char*) message,length);
315
    pos+=length;
316
  }
317
  drizzleclient_net_write(&net, buff, (size_t) (pos-buff));
318
  drizzleclient_net_flush(&net);
319
320
  session->main_da.can_overwrite_status= false;
321
}
322
323
/**
324
  Send eof (= end of result set) to the client.
325
326
  The eof packet has the following structure:
327
328
  - 254    (DRIZZLE_PROTOCOL_NO_MORE_DATA)    : Marker (1 byte)
329
  - warning_count    : Stored in 2 bytes; New in 4.1 client
330
  - status_flag    : Stored in 2 bytes;
331
  For flags like SERVER_MORE_RESULTS_EXISTS.
332
333
  Note that the warning count will not be sent if 'no_flush' is set as
334
  we don't want to report the warning count until all data is sent to the
335
  client.
336
*/
337
338
void ClientMySQLProtocol::sendEOF()
339
{
340
  /* Set to true if no active vio, to work well in case of --init-file */
341
  if (net.vio != 0)
342
  {
343
    session->main_da.can_overwrite_status= true;
344
    writeEOFPacket(session->main_da.server_status(),
345
                   session->main_da.total_warn_count());
346
    drizzleclient_net_flush(&net);
347
    session->main_da.can_overwrite_status= false;
348
  }
349
  packet.shrink(buffer_length);
350
}
351
352
353
void ClientMySQLProtocol::sendError(uint32_t sql_errno, const char *err)
354
{
355
  uint32_t length;
356
  /*
357
    buff[]: sql_errno:2 + ('#':1 + SQLSTATE_LENGTH:5) + DRIZZLE_ERRMSG_SIZE:512
358
  */
359
  unsigned char buff[2+1+SQLSTATE_LENGTH+DRIZZLE_ERRMSG_SIZE], *pos;
360
361
  assert(sql_errno);
362
  assert(err && err[0]);
363
364
  /*
365
    It's one case when we can push an error even though there
366
    is an OK or EOF already.
367
  */
368
  session->main_da.can_overwrite_status= true;
369
370
  /* Abort multi-result sets */
371
  session->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
372
373
  /**
374
    Send a error string to client.
375
376
    For SIGNAL/RESIGNAL and GET DIAGNOSTICS functionality it's
377
    critical that every error that can be intercepted is issued in one
378
    place only, my_message_sql.
379
  */
380
381
  if (net.vio == 0)
382
  {
383
    return;
384
  }
385
386
  int2store(buff,sql_errno);
387
  pos= buff+2;
388
389
  /* The first # is to make the client backward compatible */
390
  buff[2]= '#';
391
  pos= (unsigned char*) strcpy((char*) buff+3, drizzle_errno_to_sqlstate(sql_errno));
392
  pos+= strlen(drizzle_errno_to_sqlstate(sql_errno));
393
394
  char *tmp= strncpy((char*)pos, err, DRIZZLE_ERRMSG_SIZE-1);
395
  tmp+= strlen((char*)pos);
396
  tmp[0]= '\0';
397
  length= (uint32_t)(tmp-(char*)buff);
398
  err= (char*) buff;
399
400
  drizzleclient_net_write_command(&net,(unsigned char) 255, (unsigned char*) "", 0, (unsigned char*) err, length);
401
402
  session->main_da.can_overwrite_status= false;
403
}
404
405
/**
406
  Send name and type of result to client.
407
408
  Sum fields has table name empty and field_name.
409
410
  @param Session        Thread data object
411
  @param list            List of items to send to client
412
  @param flag            Bit mask with the following functions:
413
                        - 1 send number of rows
414
                        - 2 send default values
415
                        - 4 don't write eof packet
416
417
  @retval
418
    0    ok
419
  @retval
420
    1    Error  (Note that in this case the error is not sent to the
421
    client)
422
*/
423
bool ClientMySQLProtocol::sendFields(List<Item> *list)
424
{
425
  List_iterator_fast<Item> it(*list);
426
  Item *item;
427
  unsigned char buff[80];
428
  String tmp((char*) buff,sizeof(buff),&my_charset_bin);
429
430
  unsigned char *row_pos= drizzleclient_net_store_length(buff, list->elements);
431
  (void) drizzleclient_net_write(&net, buff, (size_t) (row_pos-buff));
432
433
  while ((item=it++))
434
  {
435
    char *pos;
436
    SendField field;
437
    item->make_field(&field);
438
439
    packet.length(0);
440
441
    if (store(STRING_WITH_LEN("def")) ||
442
        store(field.db_name) ||
443
        store(field.table_name) ||
444
        store(field.org_table_name) ||
445
        store(field.col_name) ||
446
        store(field.org_col_name) ||
447
        packet.realloc(packet.length()+12))
448
      goto err;
449
450
    /* Store fixed length fields */
451
    pos= (char*) packet.ptr()+packet.length();
452
    *pos++= 12;                // Length of packed fields
453
    /* No conversion */
454
    int2store(pos, field.charsetnr);
455
    int4store(pos+2, field.length);
456
457
    if (using_mysql41_protocol)
458
    {
459
      /* Switch to MySQL field numbering. */
460
      switch (field.type)
461
      {
462
      case DRIZZLE_TYPE_LONG:
463
        pos[6]= 3;
464
        break;
465
466
      case DRIZZLE_TYPE_DOUBLE:
467
        pos[6]= 5;
468
        break;
469
470
      case DRIZZLE_TYPE_NULL:
471
        pos[6]= 6;
472
        break;
473
474
      case DRIZZLE_TYPE_TIMESTAMP:
475
        pos[6]= 7;
476
        break;
477
478
      case DRIZZLE_TYPE_LONGLONG:
479
        pos[6]= 8;
480
        break;
481
482
      case DRIZZLE_TYPE_DATETIME:
483
        pos[6]= 12;
484
        break;
485
486
      case DRIZZLE_TYPE_DATE:
487
        pos[6]= 14;
488
        break;
489
490
      case DRIZZLE_TYPE_VARCHAR:
491
        pos[6]= 15;
492
        break;
493
1218 by Brian Aker
Merge Eric
494
      case DRIZZLE_TYPE_DECIMAL:
971.7.10 by Eric Day
Duplicated oldlibdrizzle module, one for Drizzle protocol and one for MySQL, per Brian's request from merge proposal. Port options are now --drizzle-protocol-port and --mysql-protocol-port.
495
        pos[6]= (char)246;
496
        break;
497
498
      case DRIZZLE_TYPE_ENUM:
499
        pos[6]= (char)247;
500
        break;
501
502
      case DRIZZLE_TYPE_BLOB:
503
        pos[6]= (char)252;
504
        break;
505
      }
506
    }
507
    else
508
    {
509
      /* Add one to compensate for tinyint removal from enum. */
510
      pos[6]= field.type + 1;
511
    }
512
513
    int2store(pos+7,field.flags);
514
    pos[9]= (char) field.decimals;
515
    pos[10]= 0;                // For the future
516
    pos[11]= 0;                // For the future
517
    pos+= 12;
518
519
    packet.length((uint32_t) (pos - packet.ptr()));
520
    if (flush())
521
      break;
522
  }
523
524
  /*
525
    Mark the end of meta-data result set, and store session->server_status,
526
    to show that there is no cursor.
527
    Send no warning information, as it will be sent at statement end.
528
  */
529
  writeEOFPacket(session->server_status, session->total_warn_count);
530
  return 0;
531
532
err:
533
  my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES),
534
             MYF(0));
535
  return 1;
536
}
537
538
bool ClientMySQLProtocol::store(Field *from)
539
{
540
  if (from->is_null())
541
    return store();
542
  char buff[MAX_FIELD_WIDTH];
543
  String str(buff,sizeof(buff), &my_charset_bin);
544
545
  from->val_str(&str);
546
547
  return netStoreData((const unsigned char *)str.ptr(), str.length());
548
}
549
550
bool ClientMySQLProtocol::store(void)
551
{
552
  char buff[1];
553
  buff[0]= (char)251;
554
  return packet.append(buff, sizeof(buff), PACKET_BUFFER_EXTRA_ALLOC);
555
}
556
557
bool ClientMySQLProtocol::store(int32_t from)
558
{
559
  char buff[12];
560
  return netStoreData((unsigned char*) buff,
561
                      (size_t) (int10_to_str(from, buff, -10) - buff));
562
}
563
564
bool ClientMySQLProtocol::store(uint32_t from)
565
{
566
  char buff[11];
567
  return netStoreData((unsigned char*) buff,
568
                      (size_t) (int10_to_str(from, buff, 10) - buff));
569
}
570
571
bool ClientMySQLProtocol::store(int64_t from)
572
{
573
  char buff[22];
574
  return netStoreData((unsigned char*) buff,
575
                      (size_t) (int64_t10_to_str(from, buff, -10) - buff));
576
}
577
578
bool ClientMySQLProtocol::store(uint64_t from)
579
{
580
  char buff[21];
581
  return netStoreData((unsigned char*) buff,
582
                      (size_t) (int64_t10_to_str(from, buff, 10) - buff));
583
}
584
585
bool ClientMySQLProtocol::store(double from, uint32_t decimals, String *buffer)
586
{
587
  buffer->set_real(from, decimals, session->charset());
588
  return netStoreData((unsigned char*) buffer->ptr(), buffer->length());
589
}
590
591
bool ClientMySQLProtocol::store(const char *from, size_t length)
592
{
593
  return netStoreData((const unsigned char *)from, length);
594
}
595
596
bool ClientMySQLProtocol::wasAborted(void)
597
{
598
  return net.error && net.vio != 0;
599
}
600
601
bool ClientMySQLProtocol::haveMoreData(void)
602
{
603
  return drizzleclient_net_more_data(&net);
604
}
605
606
bool ClientMySQLProtocol::haveError(void)
607
{
608
  return net.error || net.vio == 0;
609
}
610
611
bool ClientMySQLProtocol::checkConnection(void)
612
{
613
  uint32_t pkt_len= 0;
614
  char *end;
615
616
  // TCP/IP connection
617
  {
618
    char ip[NI_MAXHOST];
619
    uint16_t peer_port;
620
621
    if (drizzleclient_net_peer_addr(&net, ip, &peer_port, NI_MAXHOST))
622
    {
623
      my_error(ER_BAD_HOST_ERROR, MYF(0), session->security_ctx.ip.c_str());
624
      return false;
625
    }
626
627
    session->security_ctx.ip.assign(ip);
628
  }
629
  drizzleclient_net_keepalive(&net, true);
630
631
  uint32_t server_capabilites;
632
  {
633
    /* buff[] needs to big enough to hold the server_version variable */
634
    char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH + 64];
635
636
    server_capabilites= CLIENT_BASIC_FLAGS;
637
638
    if (using_mysql41_protocol)
639
      server_capabilites|= CLIENT_PROTOCOL_MYSQL41;
640
641
#ifdef HAVE_COMPRESS
642
    server_capabilites|= CLIENT_COMPRESS;
643
#endif /* HAVE_COMPRESS */
644
645
    end= buff + strlen(VERSION);
646
    if ((end - buff) >= SERVER_VERSION_LENGTH)
647
      end= buff + (SERVER_VERSION_LENGTH - 1);
648
    memcpy(buff, VERSION, end - buff);
649
    *end= 0;
650
    end++;
651
652
    int4store((unsigned char*) end, global_thread_id);
653
    end+= 4;
654
655
    /* We don't use scramble anymore. */
656
    memset(end, 'X', SCRAMBLE_LENGTH_323);
657
    end+= SCRAMBLE_LENGTH_323;
658
    *end++= 0; /* an empty byte for some reason */
659
660
    int2store(end, server_capabilites);
661
    /* write server characteristics: up to 16 bytes allowed */
662
    end[2]=(char) default_charset_info->number;
663
    int2store(end+3, session->server_status);
664
    memset(end+5, 0, 13);
665
    end+= 18;
666
667
    /* Write scramble tail. */
668
    memset(end, 'X', SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323);
669
    end+= (SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323);
670
    *end++= 0; /* an empty byte for some reason */
671
672
    /* At this point we write connection message and read reply */
673
    if (drizzleclient_net_write_command(&net
674
          , (unsigned char) PROTOCOL_VERSION
675
          , (unsigned char*) ""
676
          , 0
677
          , (unsigned char*) buff
678
          , (size_t) (end-buff)) 
679
        ||    (pkt_len= drizzleclient_net_read(&net)) == packet_error 
680
        || pkt_len < MIN_HANDSHAKE_SIZE)
681
    {
682
      my_error(ER_HANDSHAKE_ERROR, MYF(0), session->security_ctx.ip.c_str());
683
      return false;
684
    }
685
  }
686
  if (packet.alloc(buffer_length))
687
    return false; /* The error is set by alloc(). */
688
689
  client_capabilities= uint2korr(net.read_pos);
690
691
692
  client_capabilities|= ((uint32_t) uint2korr(net.read_pos + 2)) << 16;
693
  session->max_client_packet_length= uint4korr(net.read_pos + 4);
694
  end= (char*) net.read_pos + 32;
695
696
  /*
697
    Disable those bits which are not supported by the server.
698
    This is a precautionary measure, if the client lies. See Bug#27944.
699
  */
700
  client_capabilities&= server_capabilites;
701
702
  if (end >= (char*) net.read_pos + pkt_len + 2)
703
  {
704
    my_error(ER_HANDSHAKE_ERROR, MYF(0), session->security_ctx.ip.c_str());
705
    return false;
706
  }
707
708
  net.return_status= &session->server_status;
709
710
  char *user= end;
711
  char *passwd= strchr(user, '\0')+1;
712
  uint32_t user_len= passwd - user - 1;
713
  char *l_db= passwd;
714
715
  /*
716
    Old clients send null-terminated string as password; new clients send
717
    the size (1 byte) + string (not null-terminated). Hence in case of empty
718
    password both send '\0'.
719
720
    This strlen() can't be easily deleted without changing client.
721
722
    Cast *passwd to an unsigned char, so that it doesn't extend the sign for
723
    *passwd > 127 and become 2**32-127+ after casting to uint.
724
  */
725
  uint32_t passwd_len= client_capabilities & CLIENT_SECURE_CONNECTION ?
726
    (unsigned char)(*passwd++) : strlen(passwd);
727
  l_db= client_capabilities & CLIENT_CONNECT_WITH_DB ? l_db + passwd_len + 1 : 0;
728
729
  /* strlen() can't be easily deleted without changing client */
730
  uint32_t db_len= l_db ? strlen(l_db) : 0;
731
732
  if (passwd + passwd_len + db_len > (char *) net.read_pos + pkt_len)
733
  {
734
    my_error(ER_HANDSHAKE_ERROR, MYF(0), session->security_ctx.ip.c_str());
735
    return false;
736
  }
737
738
  /* If username starts and ends in "'", chop them off */
739
  if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'')
740
  {
741
    user[user_len-1]= 0;
742
    user++;
743
    user_len-= 2;
744
  }
745
746
  session->security_ctx.user.assign(user);
747
748
  return session->checkUser(passwd, passwd_len, l_db);
749
}
750
751
bool ClientMySQLProtocol::netStoreData(const unsigned char *from, size_t length)
752
{
753
  size_t packet_length= packet.length();
754
  /*
755
     The +9 comes from that strings of length longer than 16M require
756
     9 bytes to be stored (see drizzleclient_net_store_length).
757
  */
758
  if (packet_length+9+length > packet.alloced_length() &&
759
      packet.realloc(packet_length+9+length))
760
    return 1;
761
  unsigned char *to= drizzleclient_net_store_length((unsigned char*) packet.ptr()+packet_length, length);
762
  memcpy(to,from,length);
763
  packet.length((size_t) (to+length-(unsigned char*) packet.ptr()));
764
  return 0;
765
}
766
767
/**
768
  Format EOF packet according to the current client and
769
  write it to the network output buffer.
770
*/
771
772
void ClientMySQLProtocol::writeEOFPacket(uint32_t server_status,
773
                                         uint32_t total_warn_count)
774
{
775
  unsigned char buff[5];
776
  /*
777
    Don't send warn count during SP execution, as the warn_list
778
    is cleared between substatements, and mysqltest gets confused
779
  */
780
  uint32_t tmp= min(total_warn_count, (uint32_t)65535);
781
  buff[0]= DRIZZLE_PROTOCOL_NO_MORE_DATA;
782
  int2store(buff+1, tmp);
783
  /*
784
    The following test should never be true, but it's better to do it
785
    because if 'is_fatal_error' is set the server is not going to execute
786
    other queries (see the if test in dispatch_command / COM_QUERY)
787
  */
788
  if (session->is_fatal_error)
789
    server_status&= ~SERVER_MORE_RESULTS_EXISTS;
790
  int2store(buff + 3, server_status);
791
  drizzleclient_net_write(&net, buff, 5);
792
}
793
794
static ListenMySQLProtocol *listen_obj= NULL;
795
796
static int init(drizzled::plugin::Registry &registry)
797
{
798
  listen_obj= new ListenMySQLProtocol("mysql_protocol", true);
799
  registry.add(listen_obj); 
800
  return 0;
801
}
802
803
static int deinit(drizzled::plugin::Registry &registry)
804
{
805
  registry.remove(listen_obj);
806
  delete listen_obj;
807
  return 0;
808
}
809
810
static DRIZZLE_SYSVAR_UINT(port, port, PLUGIN_VAR_RQCMDARG,
811
                           N_("Port number to use for connection or 0 for default to with MySQL "
812
                              "protocol."),
813
                           NULL, NULL, 3306, 0, 65535, 0);
814
static DRIZZLE_SYSVAR_UINT(connect_timeout, connect_timeout,
815
                           PLUGIN_VAR_RQCMDARG, N_("Connect Timeout."),
816
                           NULL, NULL, 10, 1, 300, 0);
817
static DRIZZLE_SYSVAR_UINT(read_timeout, read_timeout, PLUGIN_VAR_RQCMDARG,
818
                           N_("Read Timeout."), NULL, NULL, 30, 1, 300, 0);
819
static DRIZZLE_SYSVAR_UINT(write_timeout, write_timeout, PLUGIN_VAR_RQCMDARG,
820
                           N_("Write Timeout."), NULL, NULL, 60, 1, 300, 0);
821
static DRIZZLE_SYSVAR_UINT(retry_count, retry_count, PLUGIN_VAR_RQCMDARG,
822
                           N_("Retry Count."), NULL, NULL, 10, 1, 100, 0);
823
static DRIZZLE_SYSVAR_UINT(buffer_length, buffer_length, PLUGIN_VAR_RQCMDARG,
824
                           N_("Buffer length."), NULL, NULL, 16384, 1024,
825
                           1024*1024, 0);
826
static DRIZZLE_SYSVAR_STR(bind_address, bind_address, PLUGIN_VAR_READONLY,
827
                          N_("Address to bind to."), NULL, NULL, NULL);
828
1228.1.5 by Monty Taylor
Merged in some naming things.
829
static drizzle_sys_var* system_variables[]= {
971.7.10 by Eric Day
Duplicated oldlibdrizzle module, one for Drizzle protocol and one for MySQL, per Brian's request from merge proposal. Port options are now --drizzle-protocol-port and --mysql-protocol-port.
830
  DRIZZLE_SYSVAR(port),
831
  DRIZZLE_SYSVAR(connect_timeout),
832
  DRIZZLE_SYSVAR(read_timeout),
833
  DRIZZLE_SYSVAR(write_timeout),
834
  DRIZZLE_SYSVAR(retry_count),
835
  DRIZZLE_SYSVAR(buffer_length),
836
  DRIZZLE_SYSVAR(bind_address),
837
  NULL
838
};
839
1228.1.5 by Monty Taylor
Merged in some naming things.
840
DRIZZLE_DECLARE_PLUGIN
971.7.10 by Eric Day
Duplicated oldlibdrizzle module, one for Drizzle protocol and one for MySQL, per Brian's request from merge proposal. Port options are now --drizzle-protocol-port and --mysql-protocol-port.
841
{
1241.10.2 by Monty Taylor
Added support for embedding the drizzle version number in the plugin file.
842
  DRIZZLE_VERSION_ID,
971.7.10 by Eric Day
Duplicated oldlibdrizzle module, one for Drizzle protocol and one for MySQL, per Brian's request from merge proposal. Port options are now --drizzle-protocol-port and --mysql-protocol-port.
843
  "mysql_protocol",
844
  "0.1",
845
  "Eric Day",
846
  "MySQL Protocol Module",
847
  PLUGIN_LICENSE_GPL,
848
  init,             /* Plugin Init */
849
  deinit,           /* Plugin Deinit */
850
  NULL,             /* status variables */
851
  system_variables, /* system variables */
852
  NULL              /* config options */
853
}
1228.1.5 by Monty Taylor
Merged in some naming things.
854
DRIZZLE_DECLARE_PLUGIN_END;