~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to plugin/mysql_protocol/mysql_protocol.cc

code clean move Item_func_num1 and Item_func_connection_id to functions directory

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