~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to plugin/mysql_protocol/mysql_protocol.cc

  • Committer: Jay Pipes
  • Date: 2008-07-17 18:48:58 UTC
  • mto: This revision was merged to the branch mainline in revision 182.
  • Revision ID: jay@mysql.com-20080717184858-2mbouxl8xi41gcge
Removed DBUG from CSV and Blackhole storage engines

Show diffs side-by-side

added added

removed removed

Lines of Context:
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, Inc.
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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
 
 */
19
 
 
20
 
#include <config.h>
21
 
#include <drizzled/gettext.h>
22
 
#include <drizzled/error.h>
23
 
#include <drizzled/query_id.h>
24
 
#include <drizzled/error/sql_state.h>
25
 
#include <drizzled/session.h>
26
 
#include <drizzled/internal/m_string.h>
27
 
#include <algorithm>
28
 
#include <boost/program_options.hpp>
29
 
#include <drizzled/module/option_map.h>
30
 
#include <drizzled/util/tokenize.h>
31
 
#include "errmsg.h"
32
 
#include "mysql_protocol.h"
33
 
#include "mysql_password.h"
34
 
#include "options.h"
35
 
 
36
 
#include <drizzled/identifier.h>
37
 
 
38
 
#include <libdrizzle/constants.h>
39
 
 
40
 
#define PROTOCOL_VERSION 10
41
 
 
42
 
namespace po= boost::program_options;
43
 
using namespace std;
44
 
using namespace drizzled;
45
 
 
46
 
namespace drizzle_plugin
47
 
{
48
 
 
49
 
std::vector<std::string> ClientMySQLProtocol::mysql_admin_ip_addresses;
50
 
static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024;
51
 
 
52
 
static port_constraint port;
53
 
static timeout_constraint connect_timeout;
54
 
static timeout_constraint read_timeout;
55
 
static timeout_constraint write_timeout;
56
 
static retry_constraint retry_count;
57
 
static buffer_constraint buffer_length;
58
 
 
59
 
static uint32_t random_seed1;
60
 
static uint32_t random_seed2;
61
 
static const uint32_t random_max= 0x3FFFFFFF;
62
 
static const double random_max_double= (double)0x3FFFFFFF;
63
 
 
64
 
 
65
 
ProtocolCounters *ListenMySQLProtocol::mysql_counters= new ProtocolCounters();
66
 
 
67
 
ListenMySQLProtocol::~ListenMySQLProtocol()
68
 
{ }
69
 
 
70
 
void ListenMySQLProtocol::addCountersToTable()
71
 
{
72
 
  counters.push_back(new drizzled::plugin::ListenCounter(new std::string("connection_count"), &getCounters()->connectionCount));
73
 
  counters.push_back(new drizzled::plugin::ListenCounter(new std::string("connected"), &getCounters()->connected));
74
 
  counters.push_back(new drizzled::plugin::ListenCounter(new std::string("failed_connections"), &getCounters()->failedConnections));
75
 
}
76
 
 
77
 
const std::string ListenMySQLProtocol::getHost(void) const
78
 
{
79
 
  return _hostname;
80
 
}
81
 
 
82
 
in_port_t ListenMySQLProtocol::getPort(void) const
83
 
{
84
 
  return port.get();
85
 
}
86
 
 
87
 
plugin::Client *ListenMySQLProtocol::getClient(int fd)
88
 
{
89
 
  int new_fd;
90
 
  new_fd= acceptTcp(fd);
91
 
  if (new_fd == -1)
92
 
    return NULL;
93
 
 
94
 
  return new ClientMySQLProtocol(new_fd, _using_mysql41_protocol, getCounters());
95
 
}
96
 
 
97
 
ClientMySQLProtocol::ClientMySQLProtocol(int fd, bool using_mysql41_protocol, ProtocolCounters *set_counters):
98
 
  is_admin_connection(false),
99
 
  _using_mysql41_protocol(using_mysql41_protocol),
100
 
  counters(set_counters)
101
 
{
102
 
  
103
 
  net.vio= 0;
104
 
 
105
 
  if (fd == -1)
106
 
    return;
107
 
 
108
 
  if (drizzleclient_net_init_sock(&net, fd, buffer_length.get()))
109
 
    throw bad_alloc();
110
 
 
111
 
  drizzleclient_net_set_read_timeout(&net, read_timeout.get());
112
 
  drizzleclient_net_set_write_timeout(&net, write_timeout.get());
113
 
  net.retry_count=retry_count.get();
114
 
}
115
 
 
116
 
ClientMySQLProtocol::~ClientMySQLProtocol()
117
 
{
118
 
  if (net.vio)
119
 
    net.vio->close();
120
 
}
121
 
 
122
 
int ClientMySQLProtocol::getFileDescriptor(void)
123
 
{
124
 
  return drizzleclient_net_get_sd(&net);
125
 
}
126
 
 
127
 
bool ClientMySQLProtocol::isConnected()
128
 
{
129
 
  return net.vio != 0;
130
 
}
131
 
 
132
 
bool ClientMySQLProtocol::isReading(void)
133
 
{
134
 
  return net.reading_or_writing == 1;
135
 
}
136
 
 
137
 
bool ClientMySQLProtocol::isWriting(void)
138
 
{
139
 
  return net.reading_or_writing == 2;
140
 
}
141
 
 
142
 
bool ClientMySQLProtocol::flush()
143
 
{
144
 
  if (net.vio == NULL)
145
 
    return false;
146
 
  bool ret= drizzleclient_net_write(&net, (unsigned char*) packet.ptr(),
147
 
                           packet.length());
148
 
  packet.length(0);
149
 
  return ret;
150
 
}
151
 
 
152
 
void ClientMySQLProtocol::close(void)
153
 
{
154
 
  if (net.vio)
155
 
  { 
156
 
    drizzleclient_net_close(&net);
157
 
    drizzleclient_net_end(&net);
158
 
    if (is_admin_connection)
159
 
      counters->adminConnected.decrement();
160
 
    else
161
 
      counters->connected.decrement();
162
 
  }
163
 
}
164
 
 
165
 
bool ClientMySQLProtocol::authenticate()
166
 
{
167
 
  bool connection_is_valid;
168
 
  if (is_admin_connection)
169
 
  {
170
 
    counters->adminConnectionCount.increment();
171
 
    counters->adminConnected.increment();
172
 
  }
173
 
  else
174
 
  {
175
 
    counters->connectionCount.increment();
176
 
    counters->connected.increment();
177
 
  }
178
 
 
179
 
  /* Use "connect_timeout" value during connection phase */
180
 
  drizzleclient_net_set_read_timeout(&net, connect_timeout.get());
181
 
  drizzleclient_net_set_write_timeout(&net, connect_timeout.get());
182
 
 
183
 
  connection_is_valid= checkConnection();
184
 
 
185
 
  if (connection_is_valid)
186
 
  {
187
 
    if (not is_admin_connection and (counters->connected > counters->max_connections))
188
 
    {
189
 
      std::string errmsg(ER(ER_CON_COUNT_ERROR));
190
 
      sendError(ER_CON_COUNT_ERROR, errmsg.c_str());
191
 
      counters->failedConnections.increment();
192
 
    }
193
 
    else
194
 
    {
195
 
      sendOK();
196
 
    }
197
 
  }
198
 
  else
199
 
  {
200
 
    sendError(session->main_da.sql_errno(), session->main_da.message());
201
 
    counters->failedConnections.increment();
202
 
    return false;
203
 
  }
204
 
 
205
 
  /* Connect completed, set read/write timeouts back to default */
206
 
  drizzleclient_net_set_read_timeout(&net, read_timeout.get());
207
 
  drizzleclient_net_set_write_timeout(&net, write_timeout.get());
208
 
  return true;
209
 
}
210
 
 
211
 
bool ClientMySQLProtocol::readCommand(char **l_packet, uint32_t *packet_length)
212
 
{
213
 
  /*
214
 
    This thread will do a blocking read from the client which
215
 
    will be interrupted when the next command is received from
216
 
    the client, the connection is closed or "net_wait_timeout"
217
 
    number of seconds has passed
218
 
  */
219
 
#ifdef NEVER
220
 
  /* We can do this much more efficiently with poll timeouts or watcher thread,
221
 
     disabling for now, which means net_wait_timeout == read_timeout. */
222
 
  drizzleclient_net_set_read_timeout(&net,
223
 
                                     session->variables.net_wait_timeout);
224
 
#endif
225
 
 
226
 
  net.pkt_nr=0;
227
 
 
228
 
  *packet_length= drizzleclient_net_read(&net);
229
 
  if (*packet_length == packet_error)
230
 
  {
231
 
    /* Check if we can continue without closing the connection */
232
 
 
233
 
    if(net.last_errno== ER_NET_PACKET_TOO_LARGE)
234
 
      my_error(ER_NET_PACKET_TOO_LARGE, MYF(0));
235
 
    if (session->main_da.status() == Diagnostics_area::DA_ERROR)
236
 
      sendError(session->main_da.sql_errno(), session->main_da.message());
237
 
    else
238
 
      sendOK();
239
 
 
240
 
    if (net.error != 3)
241
 
      return false;                       // We have to close it.
242
 
 
243
 
    net.error= 0;
244
 
  }
245
 
 
246
 
  *l_packet= (char*) net.read_pos;
247
 
 
248
 
  /*
249
 
    'packet_length' contains length of data, as it was stored in packet
250
 
    header. In case of malformed header, drizzleclient_net_read returns zero.
251
 
    If packet_length is not zero, drizzleclient_net_read ensures that the returned
252
 
    number of bytes was actually read from network.
253
 
    There is also an extra safety measure in drizzleclient_net_read:
254
 
    it sets packet[packet_length]= 0, but only for non-zero packets.
255
 
  */
256
 
 
257
 
  if (*packet_length == 0)                       /* safety */
258
 
  {
259
 
    /* Initialize with COM_SLEEP packet */
260
 
    (*l_packet)[0]= (unsigned char) COM_SLEEP;
261
 
    *packet_length= 1;
262
 
  }
263
 
  else if (_using_mysql41_protocol)
264
 
  {
265
 
    /* Map from MySQL commands to Drizzle commands. */
266
 
    switch ((int)(*l_packet)[0])
267
 
    {
268
 
    case 0: /* SLEEP */
269
 
    case 1: /* QUIT */
270
 
    case 2: /* INIT_DB */
271
 
    case 3: /* QUERY */
272
 
      break;
273
 
 
274
 
    case 8: /* SHUTDOWN */
275
 
      (*l_packet)[0]= (unsigned char) COM_SHUTDOWN;
276
 
      break;
277
 
 
278
 
    case 14: /* PING */
279
 
      (*l_packet)[0]= (unsigned char) COM_PING;
280
 
      break;
281
 
 
282
 
 
283
 
    default:
284
 
      /* Respond with unknown command for MySQL commands we don't support. */
285
 
      (*l_packet)[0]= (unsigned char) COM_END;
286
 
      *packet_length= 1;
287
 
      break;
288
 
    }
289
 
  }
290
 
 
291
 
  /* Do not rely on drizzleclient_net_read, extra safety against programming errors. */
292
 
  (*l_packet)[*packet_length]= '\0';                  /* safety */
293
 
 
294
 
#ifdef NEVER
295
 
  /* See comment above. */
296
 
  /* Restore read timeout value */
297
 
  drizzleclient_net_set_read_timeout(&net,
298
 
                                     session->variables.net_read_timeout);
299
 
#endif
300
 
 
301
 
  return true;
302
 
}
303
 
 
304
 
/**
305
 
  Return ok to the client.
306
 
 
307
 
  The ok packet has the following structure:
308
 
 
309
 
  - 0               : Marker (1 byte)
310
 
  - affected_rows    : Stored in 1-9 bytes
311
 
  - id        : Stored in 1-9 bytes
312
 
  - server_status    : Copy of session->server_status;  Can be used by client
313
 
  to check if we are inside an transaction.
314
 
  New in 4.0 client
315
 
  - warning_count    : Stored in 2 bytes; New in 4.1 client
316
 
  - message        : Stored as packed length (1-9 bytes) + message.
317
 
  Is not stored if no message.
318
 
 
319
 
  @param session           Thread handler
320
 
  @param affected_rows       Number of rows changed by statement
321
 
  @param id           Auto_increment id for first row (if used)
322
 
  @param message       Message to send to the client (Used by mysql_status)
323
 
*/
324
 
 
325
 
void ClientMySQLProtocol::sendOK()
326
 
{
327
 
  unsigned char buff[DRIZZLE_ERRMSG_SIZE+10],*pos;
328
 
  const char *message= NULL;
329
 
  uint32_t tmp;
330
 
 
331
 
  if (!net.vio)    // hack for re-parsing queries
332
 
  {
333
 
    return;
334
 
  }
335
 
 
336
 
  buff[0]=0;                    // No fields
337
 
  if (session->main_da.status() == Diagnostics_area::DA_OK)
338
 
  {
339
 
    if (client_capabilities & CLIENT_FOUND_ROWS && session->main_da.found_rows())
340
 
      pos=storeLength(buff+1,session->main_da.found_rows());
341
 
    else
342
 
      pos=storeLength(buff+1,session->main_da.affected_rows());
343
 
    pos=storeLength(pos, session->main_da.last_insert_id());
344
 
    int2store(pos, session->main_da.server_status());
345
 
    pos+=2;
346
 
    tmp= min(session->main_da.total_warn_count(), (uint32_t)65535);
347
 
    message= session->main_da.message();
348
 
  }
349
 
  else
350
 
  {
351
 
    pos=storeLength(buff+1,0);
352
 
    pos=storeLength(pos, 0);
353
 
    int2store(pos, session->server_status);
354
 
    pos+=2;
355
 
    tmp= min(session->total_warn_count, (uint32_t)65535);
356
 
  }
357
 
 
358
 
  /* We can only return up to 65535 warnings in two bytes */
359
 
  int2store(pos, tmp);
360
 
  pos+= 2;
361
 
 
362
 
  session->main_da.can_overwrite_status= true;
363
 
 
364
 
  if (message && message[0])
365
 
  {
366
 
    size_t length= strlen(message);
367
 
    pos=storeLength(pos,length);
368
 
    memcpy(pos,(unsigned char*) message,length);
369
 
    pos+=length;
370
 
  }
371
 
  drizzleclient_net_write(&net, buff, (size_t) (pos-buff));
372
 
  drizzleclient_net_flush(&net);
373
 
 
374
 
  session->main_da.can_overwrite_status= false;
375
 
}
376
 
 
377
 
/**
378
 
  Send eof (= end of result set) to the client.
379
 
 
380
 
  The eof packet has the following structure:
381
 
 
382
 
  - 254    (DRIZZLE_PROTOCOL_NO_MORE_DATA)    : Marker (1 byte)
383
 
  - warning_count    : Stored in 2 bytes; New in 4.1 client
384
 
  - status_flag    : Stored in 2 bytes;
385
 
  For flags like SERVER_MORE_RESULTS_EXISTS.
386
 
 
387
 
  Note that the warning count will not be sent if 'no_flush' is set as
388
 
  we don't want to report the warning count until all data is sent to the
389
 
  client.
390
 
*/
391
 
 
392
 
void ClientMySQLProtocol::sendEOF()
393
 
{
394
 
  /* Set to true if no active vio, to work well in case of --init-file */
395
 
  if (net.vio != 0)
396
 
  {
397
 
    session->main_da.can_overwrite_status= true;
398
 
    writeEOFPacket(session->main_da.server_status(),
399
 
                   session->main_da.total_warn_count());
400
 
    drizzleclient_net_flush(&net);
401
 
    session->main_da.can_overwrite_status= false;
402
 
  }
403
 
  packet.shrink(buffer_length.get());
404
 
}
405
 
 
406
 
 
407
 
void ClientMySQLProtocol::sendError(drizzled::error_t sql_errno, const char *err)
408
 
{
409
 
  uint32_t length;
410
 
  /*
411
 
    buff[]: sql_errno:2 + ('#':1 + SQLSTATE_LENGTH:5) + DRIZZLE_ERRMSG_SIZE:512
412
 
  */
413
 
  unsigned char buff[2+1+SQLSTATE_LENGTH+DRIZZLE_ERRMSG_SIZE], *pos;
414
 
 
415
 
  assert(sql_errno != EE_OK);
416
 
  assert(err && err[0]);
417
 
 
418
 
  /*
419
 
    It's one case when we can push an error even though there
420
 
    is an OK or EOF already.
421
 
  */
422
 
  session->main_da.can_overwrite_status= true;
423
 
 
424
 
  /* Abort multi-result sets */
425
 
  session->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
426
 
 
427
 
  /**
428
 
    Send a error string to client.
429
 
 
430
 
    For SIGNAL/RESIGNAL and GET DIAGNOSTICS functionality it's
431
 
    critical that every error that can be intercepted is issued in one
432
 
    place only, my_message_sql.
433
 
  */
434
 
 
435
 
  if (net.vio == 0)
436
 
  {
437
 
    return;
438
 
  }
439
 
 
440
 
  int2store(buff, static_cast<uint16_t>(sql_errno));
441
 
  pos= buff+2;
442
 
 
443
 
  /* The first # is to make the client backward compatible */
444
 
  buff[2]= '#';
445
 
  pos= (unsigned char*) strcpy((char*) buff+3, error::convert_to_sqlstate(sql_errno));
446
 
  pos+= strlen(error::convert_to_sqlstate(sql_errno));
447
 
 
448
 
  char *tmp= strncpy((char*)pos, err, DRIZZLE_ERRMSG_SIZE-1);
449
 
  tmp+= strlen((char*)pos);
450
 
  tmp[0]= '\0';
451
 
  length= (uint32_t)(tmp-(char*)buff);
452
 
  err= (char*) buff;
453
 
 
454
 
  drizzleclient_net_write_command(&net,(unsigned char) 255, (unsigned char*) "", 0, (unsigned char*) err, length);
455
 
 
456
 
  drizzleclient_net_flush(&net);
457
 
 
458
 
  session->main_da.can_overwrite_status= false;
459
 
}
460
 
 
461
 
/**
462
 
  Send name and type of result to client.
463
 
 
464
 
  Sum fields has table name empty and field_name.
465
 
 
466
 
  @param Session        Thread data object
467
 
  @param list            List of items to send to client
468
 
  @param flag            Bit mask with the following functions:
469
 
                        - 1 send number of rows
470
 
                        - 2 send default values
471
 
                        - 4 don't write eof packet
472
 
 
473
 
  @retval
474
 
    0    ok
475
 
  @retval
476
 
    1    Error  (Note that in this case the error is not sent to the
477
 
    client)
478
 
*/
479
 
bool ClientMySQLProtocol::sendFields(List<Item> *list)
480
 
{
481
 
  List<Item>::iterator it(list->begin());
482
 
  Item *item;
483
 
  unsigned char buff[80];
484
 
  String tmp((char*) buff,sizeof(buff),&my_charset_bin);
485
 
 
486
 
  unsigned char *row_pos= storeLength(buff, list->elements);
487
 
  (void) drizzleclient_net_write(&net, buff, (size_t) (row_pos-buff));
488
 
 
489
 
  while ((item=it++))
490
 
  {
491
 
    char *pos;
492
 
    SendField field;
493
 
    item->make_field(&field);
494
 
 
495
 
    packet.length(0);
496
 
 
497
 
    if (store(STRING_WITH_LEN("def")) ||
498
 
        store(field.db_name) ||
499
 
        store(field.table_name) ||
500
 
        store(field.org_table_name) ||
501
 
        store(field.col_name) ||
502
 
        store(field.org_col_name) ||
503
 
        packet.realloc(packet.length()+12))
504
 
      goto err;
505
 
 
506
 
    /* Store fixed length fields */
507
 
    pos= (char*) packet.ptr()+packet.length();
508
 
    *pos++= 12;                // Length of packed fields
509
 
    /* No conversion */
510
 
    int2store(pos, field.charsetnr);
511
 
    int4store(pos+2, field.length);
512
 
 
513
 
    if (_using_mysql41_protocol)
514
 
    {
515
 
      /* Switch to MySQL field numbering. */
516
 
      switch (field.type)
517
 
      {
518
 
      case DRIZZLE_TYPE_LONG:
519
 
        pos[6]= 3;
520
 
        break;
521
 
 
522
 
      case DRIZZLE_TYPE_DOUBLE:
523
 
        pos[6]= 5;
524
 
        break;
525
 
 
526
 
      case DRIZZLE_TYPE_NULL:
527
 
        pos[6]= 6;
528
 
        break;
529
 
 
530
 
      case DRIZZLE_TYPE_TIMESTAMP:
531
 
        pos[6]= 7;
532
 
        break;
533
 
 
534
 
      case DRIZZLE_TYPE_LONGLONG:
535
 
        pos[6]= 8;
536
 
        break;
537
 
 
538
 
      case DRIZZLE_TYPE_DATETIME:
539
 
        pos[6]= 12;
540
 
        break;
541
 
 
542
 
      case DRIZZLE_TYPE_TIME:
543
 
        pos[6]= 13;
544
 
        break;
545
 
 
546
 
      case DRIZZLE_TYPE_DATE:
547
 
        pos[6]= 14;
548
 
        break;
549
 
 
550
 
      case DRIZZLE_TYPE_VARCHAR:
551
 
        pos[6]= 15;
552
 
        break;
553
 
 
554
 
      case DRIZZLE_TYPE_MICROTIME:
555
 
        pos[6]= 15;
556
 
        break;
557
 
 
558
 
      case DRIZZLE_TYPE_UUID:
559
 
        pos[6]= 15;
560
 
        break;
561
 
 
562
 
      case DRIZZLE_TYPE_BOOLEAN:
563
 
        pos[6]= DRIZZLE_COLUMN_TYPE_TINY;
564
 
        break;
565
 
 
566
 
      case DRIZZLE_TYPE_DECIMAL:
567
 
        pos[6]= (char)246;
568
 
        break;
569
 
 
570
 
      case DRIZZLE_TYPE_ENUM:
571
 
        pos[6]= (char)247;
572
 
        break;
573
 
 
574
 
      case DRIZZLE_TYPE_BLOB:
575
 
        pos[6]= (char)252;
576
 
        break;
577
 
      }
578
 
    }
579
 
    else
580
 
    {
581
 
      /* Add one to compensate for tinyint removal from enum. */
582
 
      pos[6]= field.type + 1;
583
 
    }
584
 
 
585
 
    int2store(pos+7,field.flags);
586
 
    pos[9]= (char) field.decimals;
587
 
    pos[10]= 0;                // For the future
588
 
    pos[11]= 0;                // For the future
589
 
    pos+= 12;
590
 
 
591
 
    packet.length((uint32_t) (pos - packet.ptr()));
592
 
    if (flush())
593
 
      break;
594
 
  }
595
 
 
596
 
  /*
597
 
    Mark the end of meta-data result set, and store session->server_status,
598
 
    to show that there is no cursor.
599
 
    Send no warning information, as it will be sent at statement end.
600
 
  */
601
 
  writeEOFPacket(session->server_status, session->total_warn_count);
602
 
  return 0;
603
 
 
604
 
err:
605
 
  my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES),
606
 
             MYF(0));
607
 
  return 1;
608
 
}
609
 
 
610
 
bool ClientMySQLProtocol::store(Field *from)
611
 
{
612
 
  if (from->is_null())
613
 
    return store();
614
 
  if (from->type() == DRIZZLE_TYPE_BOOLEAN)
615
 
  {
616
 
    return store(from->val_int());
617
 
  }
618
 
 
619
 
  char buff[MAX_FIELD_WIDTH];
620
 
  String str(buff,sizeof(buff), &my_charset_bin);
621
 
 
622
 
  from->val_str_internal(&str);
623
 
 
624
 
  return netStoreData((const unsigned char *)str.ptr(), str.length());
625
 
}
626
 
 
627
 
bool ClientMySQLProtocol::store(void)
628
 
{
629
 
  char buff[1];
630
 
  buff[0]= (char)251;
631
 
  return packet.append(buff, sizeof(buff), PACKET_BUFFER_EXTRA_ALLOC);
632
 
}
633
 
 
634
 
bool ClientMySQLProtocol::store(int32_t from)
635
 
{
636
 
  char buff[12];
637
 
  return netStoreData((unsigned char*) buff,
638
 
                      (size_t) (internal::int10_to_str(from, buff, -10) - buff));
639
 
}
640
 
 
641
 
bool ClientMySQLProtocol::store(uint32_t from)
642
 
{
643
 
  char buff[11];
644
 
  return netStoreData((unsigned char*) buff,
645
 
                      (size_t) (internal::int10_to_str(from, buff, 10) - buff));
646
 
}
647
 
 
648
 
bool ClientMySQLProtocol::store(int64_t from)
649
 
{
650
 
  char buff[22];
651
 
  return netStoreData((unsigned char*) buff,
652
 
                      (size_t) (internal::int64_t10_to_str(from, buff, -10) - buff));
653
 
}
654
 
 
655
 
bool ClientMySQLProtocol::store(uint64_t from)
656
 
{
657
 
  char buff[21];
658
 
  return netStoreData((unsigned char*) buff,
659
 
                      (size_t) (internal::int64_t10_to_str(from, buff, 10) - buff));
660
 
}
661
 
 
662
 
bool ClientMySQLProtocol::store(double from, uint32_t decimals, String *buffer)
663
 
{
664
 
  buffer->set_real(from, decimals, session->charset());
665
 
  return netStoreData((unsigned char*) buffer->ptr(), buffer->length());
666
 
}
667
 
 
668
 
bool ClientMySQLProtocol::store(const char *from, size_t length)
669
 
{
670
 
  return netStoreData((const unsigned char *)from, length);
671
 
}
672
 
 
673
 
bool ClientMySQLProtocol::wasAborted(void)
674
 
{
675
 
  return net.error && net.vio != 0;
676
 
}
677
 
 
678
 
bool ClientMySQLProtocol::haveMoreData(void)
679
 
{
680
 
  return drizzleclient_net_more_data(&net);
681
 
}
682
 
 
683
 
bool ClientMySQLProtocol::haveError(void)
684
 
{
685
 
  return net.error || net.vio == 0;
686
 
}
687
 
 
688
 
bool ClientMySQLProtocol::checkConnection(void)
689
 
{
690
 
  uint32_t pkt_len= 0;
691
 
  char *end;
692
 
  char scramble[SCRAMBLE_LENGTH];
693
 
  identifier::User::shared_ptr user_identifier= identifier::User::make_shared();
694
 
 
695
 
  makeScramble(scramble);
696
 
 
697
 
  // TCP/IP connection
698
 
  {
699
 
    char ip[NI_MAXHOST];
700
 
    uint16_t peer_port;
701
 
 
702
 
    if (drizzleclient_net_peer_addr(&net, ip, &peer_port, NI_MAXHOST))
703
 
    {
704
 
      my_error(ER_BAD_HOST_ERROR, MYF(0), ip);
705
 
      return false;
706
 
    }
707
 
 
708
 
    user_identifier->setAddress(ip);
709
 
  }
710
 
  drizzleclient_net_keepalive(&net, true);
711
 
 
712
 
  uint32_t server_capabilites;
713
 
  {
714
 
    /* buff[] needs to big enough to hold the server_version variable */
715
 
    char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH + 64];
716
 
 
717
 
    server_capabilites= CLIENT_BASIC_FLAGS;
718
 
 
719
 
    if (_using_mysql41_protocol)
720
 
      server_capabilites|= CLIENT_PROTOCOL_MYSQL41;
721
 
 
722
 
#ifdef HAVE_COMPRESS
723
 
    server_capabilites|= CLIENT_COMPRESS;
724
 
#endif /* HAVE_COMPRESS */
725
 
 
726
 
    end= buff + strlen(PANDORA_RELEASE_VERSION);
727
 
    if ((end - buff) >= SERVER_VERSION_LENGTH)
728
 
      end= buff + (SERVER_VERSION_LENGTH - 1);
729
 
    memcpy(buff, PANDORA_RELEASE_VERSION, end - buff);
730
 
    *end= 0;
731
 
    end++;
732
 
 
733
 
    int4store((unsigned char*) end, session->variables.pseudo_thread_id);
734
 
    end+= 4;
735
 
 
736
 
    /* We don't use scramble anymore. */
737
 
    memcpy(end, scramble, SCRAMBLE_LENGTH_323);
738
 
    end+= SCRAMBLE_LENGTH_323;
739
 
    *end++= 0; /* an empty byte for some reason */
740
 
 
741
 
    int2store(end, server_capabilites);
742
 
    /* write server characteristics: up to 16 bytes allowed */
743
 
    end[2]=(char) default_charset_info->number;
744
 
    int2store(end+3, session->server_status);
745
 
    memset(end+5, 0, 13);
746
 
    end+= 18;
747
 
 
748
 
    /* Write scramble tail. */
749
 
    memcpy(end, scramble + SCRAMBLE_LENGTH_323, SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323);
750
 
    end+= (SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323);
751
 
    *end++= 0; /* an empty byte for some reason */
752
 
 
753
 
    /* At this point we write connection message and read reply */
754
 
    if (drizzleclient_net_write_command(&net
755
 
          , (unsigned char) PROTOCOL_VERSION
756
 
          , (unsigned char*) ""
757
 
          , 0
758
 
          , (unsigned char*) buff
759
 
          , (size_t) (end-buff)) 
760
 
        ||    (pkt_len= drizzleclient_net_read(&net)) == packet_error 
761
 
        || pkt_len < MIN_HANDSHAKE_SIZE)
762
 
    {
763
 
      my_error(ER_HANDSHAKE_ERROR, MYF(0), user_identifier->address().c_str());
764
 
      return false;
765
 
    }
766
 
  }
767
 
  if (packet.alloc(buffer_length.get()))
768
 
    return false; /* The error is set by alloc(). */
769
 
 
770
 
  client_capabilities= uint2korr(net.read_pos);
771
 
  if (!(client_capabilities & CLIENT_PROTOCOL_MYSQL41))
772
 
  {
773
 
    my_error(ER_HANDSHAKE_ERROR, MYF(0), user_identifier->address().c_str());
774
 
    return false;
775
 
  }
776
 
 
777
 
  client_capabilities|= ((uint32_t) uint2korr(net.read_pos + 2)) << 16;
778
 
  session->max_client_packet_length= uint4korr(net.read_pos + 4);
779
 
  end= (char*) net.read_pos + 32;
780
 
 
781
 
  /*
782
 
    Disable those bits which are not supported by the server.
783
 
    This is a precautionary measure, if the client lies. See Bug#27944.
784
 
  */
785
 
  client_capabilities&= server_capabilites;
786
 
 
787
 
  if (end >= (char*) net.read_pos + pkt_len + 2)
788
 
  {
789
 
    my_error(ER_HANDSHAKE_ERROR, MYF(0), user_identifier->address().c_str());
790
 
    return false;
791
 
  }
792
 
 
793
 
  net.return_status= &session->server_status;
794
 
 
795
 
  char *user= end;
796
 
  char *passwd= strchr(user, '\0')+1;
797
 
  uint32_t user_len= passwd - user - 1;
798
 
  char *l_db= passwd;
799
 
 
800
 
  /*
801
 
    Only support new password format.
802
 
 
803
 
    Cast *passwd to an unsigned char, so that it doesn't extend the sign for
804
 
    *passwd > 127 and become 2**32-127+ after casting to uint.
805
 
  */
806
 
  uint32_t passwd_len;
807
 
  if (client_capabilities & CLIENT_SECURE_CONNECTION &&
808
 
      passwd < (char *) net.read_pos + pkt_len)
809
 
  {
810
 
    passwd_len= (unsigned char)(*passwd++);
811
 
    if (passwd_len > 0)
812
 
    {
813
 
      user_identifier->setPasswordType(identifier::User::MYSQL_HASH);
814
 
      user_identifier->setPasswordContext(scramble, SCRAMBLE_LENGTH);
815
 
    }
816
 
  }
817
 
  else
818
 
  {
819
 
    passwd_len= 0;
820
 
  }
821
 
 
822
 
  if (client_capabilities & CLIENT_CONNECT_WITH_DB &&
823
 
      passwd < (char *) net.read_pos + pkt_len)
824
 
  {
825
 
    l_db= l_db + passwd_len + 1;
826
 
  }
827
 
  else
828
 
  {
829
 
    l_db= NULL;
830
 
  }
831
 
 
832
 
  /* strlen() can't be easily deleted without changing client */
833
 
  uint32_t db_len= l_db ? strlen(l_db) : 0;
834
 
 
835
 
  if (passwd + passwd_len + db_len > (char *) net.read_pos + pkt_len)
836
 
  {
837
 
    my_error(ER_HANDSHAKE_ERROR, MYF(0), user_identifier->address().c_str());
838
 
    return false;
839
 
  }
840
 
 
841
 
  /* If username starts and ends in "'", chop them off */
842
 
  if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'')
843
 
  {
844
 
    user[user_len-1]= 0;
845
 
    user++;
846
 
    user_len-= 2;
847
 
  }
848
 
 
849
 
  if (client_capabilities & CLIENT_ADMIN)
850
 
  {
851
 
    if ((strncmp(user, "root", 4) == 0) and isAdminAllowed())
852
 
    {
853
 
      is_admin_connection= true;
854
 
    }
855
 
    else
856
 
    {
857
 
      my_error(ER_ADMIN_ACCESS, MYF(0));
858
 
      return false;
859
 
    }
860
 
  }
861
 
 
862
 
  user_identifier->setUser(user);
863
 
  session->setUser(user_identifier);
864
 
 
865
 
  return session->checkUser(string(passwd, passwd_len),
866
 
                            string(l_db ? l_db : ""));
867
 
 
868
 
}
869
 
 
870
 
bool ClientMySQLProtocol::isAdminAllowed(void)
871
 
{
872
 
  if (std::find(mysql_admin_ip_addresses.begin(), mysql_admin_ip_addresses.end(), session->user()->address()) != mysql_admin_ip_addresses.end())
873
 
    return true;
874
 
 
875
 
  return false;
876
 
}
877
 
 
878
 
bool ClientMySQLProtocol::netStoreData(const unsigned char *from, size_t length)
879
 
{
880
 
  size_t packet_length= packet.length();
881
 
  /*
882
 
     The +9 comes from that strings of length longer than 16M require
883
 
     9 bytes to be stored (see storeLength).
884
 
  */
885
 
  if (packet_length+9+length > packet.alloced_length() &&
886
 
      packet.realloc(packet_length+9+length))
887
 
    return 1;
888
 
  unsigned char *to= storeLength((unsigned char*) packet.ptr()+packet_length, length);
889
 
  memcpy(to,from,length);
890
 
  packet.length((size_t) (to+length-(unsigned char*) packet.ptr()));
891
 
  return 0;
892
 
}
893
 
 
894
 
/**
895
 
  Format EOF packet according to the current client and
896
 
  write it to the network output buffer.
897
 
*/
898
 
 
899
 
void ClientMySQLProtocol::writeEOFPacket(uint32_t server_status,
900
 
                                         uint32_t total_warn_count)
901
 
{
902
 
  unsigned char buff[5];
903
 
  /*
904
 
    Don't send warn count during SP execution, as the warn_list
905
 
    is cleared between substatements, and mysqltest gets confused
906
 
  */
907
 
  uint32_t tmp= min(total_warn_count, (uint32_t)65535);
908
 
  buff[0]= DRIZZLE_PROTOCOL_NO_MORE_DATA;
909
 
  int2store(buff+1, tmp);
910
 
  /*
911
 
    The following test should never be true, but it's better to do it
912
 
    because if 'is_fatal_error' is set the server is not going to execute
913
 
    other queries (see the if test in dispatch_command / COM_QUERY)
914
 
  */
915
 
  if (session->is_fatal_error)
916
 
    server_status&= ~SERVER_MORE_RESULTS_EXISTS;
917
 
  int2store(buff + 3, server_status);
918
 
  drizzleclient_net_write(&net, buff, 5);
919
 
}
920
 
 
921
 
/*
922
 
  Store an integer with simple packing into a output package
923
 
 
924
 
  buffer      Store the packed integer here
925
 
  length    integers to store
926
 
 
927
 
  This is mostly used to store lengths of strings.  We have to cast
928
 
  the result for the LL() becasue of a bug in Forte CC compiler.
929
 
 
930
 
  RETURN
931
 
  Position in 'buffer' after the packed length
932
 
*/
933
 
 
934
 
unsigned char *ClientMySQLProtocol::storeLength(unsigned char *buffer, uint64_t length)
935
 
{
936
 
  if (length < (uint64_t) 251LL)
937
 
  {
938
 
    *buffer=(unsigned char) length;
939
 
    return buffer+1;
940
 
  }
941
 
  /* 251 is reserved for NULL */
942
 
  if (length < (uint64_t) 65536LL)
943
 
  {
944
 
    *buffer++=252;
945
 
    int2store(buffer,(uint32_t) length);
946
 
    return buffer+2;
947
 
  }
948
 
  if (length < (uint64_t) 16777216LL)
949
 
  {
950
 
    *buffer++=253;
951
 
    int3store(buffer,(uint32_t) length);
952
 
    return buffer+3;
953
 
  }
954
 
  *buffer++=254;
955
 
  int8store(buffer,length);
956
 
  return buffer+8;
957
 
}
958
 
 
959
 
void ClientMySQLProtocol::makeScramble(char *scramble)
960
 
{
961
 
  /* This is the MySQL algorithm with minimal changes. */
962
 
  random_seed1= (random_seed1 * 3 + random_seed2) % random_max;
963
 
  random_seed2= (random_seed1 + random_seed2 + 33) % random_max;
964
 
  uint32_t seed= static_cast<uint32_t>((static_cast<double>(random_seed1) / random_max_double) * 0xffffffff);
965
 
 
966
 
  void *pointer= this;
967
 
  uint32_t pointer_seed;
968
 
  memcpy(&pointer_seed, &pointer, 4);
969
 
  uint32_t random1= (seed + pointer_seed) % random_max;
970
 
  uint32_t random2= (seed + session->variables.pseudo_thread_id + net.vio->get_fd()) % random_max;
971
 
 
972
 
  for (char *end= scramble + SCRAMBLE_LENGTH; scramble != end; scramble++)
973
 
  {
974
 
    random1= (random1 * 3 + random2) % random_max;
975
 
    random2= (random1 + random2 + 33) % random_max;
976
 
    *scramble= static_cast<char>((static_cast<double>(random1) / random_max_double) * 94 + 33);
977
 
  }
978
 
}
979
 
 
980
 
void ClientMySQLProtocol::mysql_compose_ip_addresses(vector<string> options)
981
 
{
982
 
  for (vector<string>::iterator it= options.begin();
983
 
       it != options.end();
984
 
       ++it)
985
 
  {
986
 
    tokenize(*it, mysql_admin_ip_addresses, ",", true);
987
 
  }
988
 
}
989
 
 
990
 
static ListenMySQLProtocol *listen_obj= NULL;
991
 
plugin::Create_function<MySQLPassword> *mysql_password= NULL;
992
 
 
993
 
static int init(drizzled::module::Context &context)
994
 
{  
995
 
  /* Initialize random seeds for the MySQL algorithm with minimal changes. */
996
 
  time_t seed_time= time(NULL);
997
 
  random_seed1= seed_time % random_max;
998
 
  random_seed2= (seed_time / 2) % random_max;
999
 
 
1000
 
  const module::option_map &vm= context.getOptions();
1001
 
 
1002
 
  mysql_password= new plugin::Create_function<MySQLPassword>(MySQLPasswordName);
1003
 
  context.add(mysql_password);
1004
 
 
1005
 
  listen_obj= new ListenMySQLProtocol("mysql_protocol", vm["bind-address"].as<std::string>(), true);
1006
 
  listen_obj->addCountersToTable();
1007
 
  context.add(listen_obj); 
1008
 
  context.registerVariable(new sys_var_constrained_value_readonly<in_port_t>("port", port));
1009
 
  context.registerVariable(new sys_var_constrained_value<uint32_t>("connect_timeout", connect_timeout));
1010
 
  context.registerVariable(new sys_var_constrained_value<uint32_t>("read_timeout", read_timeout));
1011
 
  context.registerVariable(new sys_var_constrained_value<uint32_t>("write_timeout", write_timeout));
1012
 
  context.registerVariable(new sys_var_constrained_value<uint32_t>("retry_count", retry_count));
1013
 
  context.registerVariable(new sys_var_constrained_value<uint32_t>("buffer_length", buffer_length));
1014
 
  context.registerVariable(new sys_var_const_string_val("bind_address",
1015
 
                                                        vm["bind-address"].as<std::string>()));
1016
 
 
1017
 
  context.registerVariable(new sys_var_uint32_t_ptr("max-connections", &ListenMySQLProtocol::mysql_counters->max_connections));
1018
 
 
1019
 
  return 0;
1020
 
}
1021
 
 
1022
 
static void init_options(drizzled::module::option_context &context)
1023
 
{
1024
 
  context("port",
1025
 
          po::value<port_constraint>(&port)->default_value(3306),
1026
 
          _("Port number to use for connection or 0 for default to with MySQL "
1027
 
                              "protocol."));
1028
 
  context("connect-timeout",
1029
 
          po::value<timeout_constraint>(&connect_timeout)->default_value(10),
1030
 
          _("Connect Timeout."));
1031
 
  context("read-timeout",
1032
 
          po::value<timeout_constraint>(&read_timeout)->default_value(30),
1033
 
          _("Read Timeout."));
1034
 
  context("write-timeout",
1035
 
          po::value<timeout_constraint>(&write_timeout)->default_value(60),
1036
 
          _("Write Timeout."));
1037
 
  context("retry-count",
1038
 
          po::value<retry_constraint>(&retry_count)->default_value(10),
1039
 
          _("Retry Count."));
1040
 
  context("buffer-length",
1041
 
          po::value<buffer_constraint>(&buffer_length)->default_value(16384),
1042
 
          _("Buffer length."));
1043
 
  context("bind-address",
1044
 
          po::value<string>()->default_value(""),
1045
 
          _("Address to bind to."));
1046
 
  context("max-connections",
1047
 
          po::value<uint32_t>(&ListenMySQLProtocol::mysql_counters->max_connections)->default_value(1000),
1048
 
          _("Maximum simultaneous connections."));
1049
 
  context("admin-ip-addresses",
1050
 
          po::value<vector<string> >()->composing()->notifier(&ClientMySQLProtocol::mysql_compose_ip_addresses),
1051
 
          _("A restrictive IP address list for incoming admin connections."));
1052
 
}
1053
 
 
1054
 
} /* namespace drizzle_plugin */
1055
 
 
1056
 
DRIZZLE_DECLARE_PLUGIN
1057
 
{
1058
 
  DRIZZLE_VERSION_ID,
1059
 
  "mysql-protocol",
1060
 
  "0.1",
1061
 
  "Eric Day",
1062
 
  "MySQL Protocol Module",
1063
 
  PLUGIN_LICENSE_GPL,
1064
 
  drizzle_plugin::init,             /* Plugin Init */
1065
 
  NULL, /* depends */
1066
 
  drizzle_plugin::init_options    /* config options */
1067
 
}
1068
 
DRIZZLE_DECLARE_PLUGIN_END;