~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to plugin/mysql_protocol/mysql_protocol.cc

Merged in latest plugin-slot-reorg.

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