~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to plugin/mysql_protocol/mysql_protocol.cc

  • Committer: Stewart Smith
  • Date: 2008-07-13 06:56:15 UTC
  • mto: (210.1.1 drizzle)
  • mto: This revision was merged to the branch mainline in revision 211.
  • Revision ID: stewart@flamingspork.com-20080713065615-vzok75kgnnviokl9
Move MD5() into a UDF

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