~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to plugin/mysql_protocol/mysql_protocol.cc

Merged vcol stuff.

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