Drizzled Public API Documentation

mysql_protocol.cc
1 /* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
3  *
4  * Copyright (C) 2008 Sun Microsystems, Inc.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 #include <config.h>
21 
22 #include <boost/program_options.hpp>
23 
24 #include <algorithm>
25 #include <climits>
26 
27 #include <drizzled/gettext.h>
28 #include <drizzled/error.h>
29 #include <drizzled/error/sql_state.h>
30 #include <drizzled/session.h>
31 #include <drizzled/internal/m_string.h>
33 #include <drizzled/util/tokenize.h>
34 #include <plugin/mysql_protocol/errmsg.h>
35 #include <plugin/mysql_protocol/mysql_protocol.h>
36 #include <plugin/mysql_protocol/mysql_password.h>
37 #include <plugin/mysql_protocol/options.h>
38 #include <drizzled/identifier.h>
39 #include <drizzled/plugin/function.h>
40 #include <drizzled/diagnostics_area.h>
41 #include <drizzled/system_variables.h>
42 #include <libdrizzle-2.0/constants.h>
43 
44 #define MIN_HANDSHAKE_SIZE 6
45 #define PROTOCOL_VERSION 10
46 
47 namespace po= boost::program_options;
48 using namespace std;
49 using namespace drizzled;
50 
51 namespace drizzle_plugin {
52 
53 static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024;
54 
55 static port_constraint port;
56 static timeout_constraint connect_timeout;
57 static timeout_constraint read_timeout;
58 static timeout_constraint write_timeout;
59 static retry_constraint retry_count;
60 static buffer_constraint buffer_length;
61 
62 static uint32_t random_seed1;
63 static uint32_t random_seed2;
64 static const uint32_t random_max= 0x3FFFFFFF;
65 static const double random_max_double= (double)0x3FFFFFFF;
66 
67 ProtocolCounters ListenMySQLProtocol::mysql_counters;
68 
69 void ListenMySQLProtocol::addCountersToTable()
70 {
71  counters.push_back(new drizzled::plugin::ListenCounter(new std::string("connection_count"), &getCounters().connectionCount));
72  counters.push_back(new drizzled::plugin::ListenCounter(new std::string("connected"), &getCounters().connected));
73  counters.push_back(new drizzled::plugin::ListenCounter(new std::string("failed_connections"), &getCounters().failedConnections));
74 }
75 
76 const std::string ListenMySQLProtocol::getHost() const
77 {
78  return _hostname;
79 }
80 
81 in_port_t ListenMySQLProtocol::getPort() const
82 {
83  return port.get();
84 }
85 
86 plugin::Client *ListenMySQLProtocol::getClient(int fd)
87 {
88  int new_fd= acceptTcp(fd);
89  return new_fd == -1 ? NULL : new ClientMySQLProtocol(new_fd, getCounters());
90 }
91 
92 ClientMySQLProtocol::ClientMySQLProtocol(int fd, ProtocolCounters& set_counters) :
93  _is_interactive(false),
94  counters(set_counters)
95 {
96  net.vio= 0;
97 
98  if (fd == -1)
99  return;
100 
101  net.init(fd, buffer_length.get());
102  net.set_read_timeout(read_timeout.get());
103  net.set_write_timeout(write_timeout.get());
104  net.retry_count=retry_count.get();
105 }
106 
107 ClientMySQLProtocol::~ClientMySQLProtocol()
108 {
109  if (net.vio)
110  net.vio->close();
111 }
112 
114 {
115  return net.get_sd();
116 }
117 
119 {
120  return net.vio != 0;
121 }
122 
124 {
125  if (net.vio == NULL)
126  return false;
127  bool ret= net.write(packet.ptr(), packet.length());
128  packet.length(0);
129  return ret;
130 }
131 
133 {
134  if (net.vio)
135  {
136  net.close();
137  net.end();
138  counters.connected.decrement();
139  }
140 }
141 
143 {
144  counters.connectionCount.increment();
145  counters.connected.increment();
146 
147  /* Use "connect_timeout" value during connection phase */
148  net.set_read_timeout(connect_timeout.get());
149  net.set_write_timeout(connect_timeout.get());
150 
151  if (checkConnection())
152  {
153  if (counters.connected > counters.max_connections)
154  {
155  std::string errmsg(ER(ER_CON_COUNT_ERROR));
156  sendError(ER_CON_COUNT_ERROR, errmsg.c_str());
157  counters.failedConnections.increment();
158  }
159  else
160  {
161  sendOK();
162  }
163  }
164  else
165  {
166  sendError(session->main_da().sql_errno(), session->main_da().message());
167  counters.failedConnections.increment();
168  return false;
169  }
170 
171  /* Connect completed, set read/write timeouts back to default */
172  net.set_read_timeout(read_timeout.get());
173  net.set_write_timeout(write_timeout.get());
174  return true;
175 }
176 
177 bool ClientMySQLProtocol::readCommand(char **l_packet, uint32_t& packet_length)
178 {
179  /*
180  This thread will do a blocking read from the client which
181  will be interrupted when the next command is received from
182  the client, the connection is closed or "net_wait_timeout"
183  number of seconds has passed
184  */
185 #ifdef NEVER
186  /* We can do this much more efficiently with poll timeouts or watcher thread,
187  disabling for now, which means net_wait_timeout == read_timeout. */
188  drizzleclient_net_set_read_timeout(&net,
189  session->variables.net_wait_timeout);
190 #endif
191 
192  net.pkt_nr=0;
193  packet_length= net.read();
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  }
210 
211  *l_packet= (char*) net.read_pos;
212 
213  /*
214  'packet_length' contains length of data, as it was stored in packet
215  header. In case of malformed header, drizzleclient_net_read returns zero.
216  If packet_length is not zero, drizzleclient_net_read ensures that the returned
217  number of bytes was actually read from network.
218  There is also an extra safety measure in drizzleclient_net_read:
219  it sets packet[packet_length]= 0, but only for non-zero packets.
220  */
221 
222  if (packet_length == 0) /* safety */
223  {
224  /* Initialize with COM_SLEEP packet */
225  (*l_packet)[0]= COM_SLEEP;
226  packet_length= 1;
227  }
228  else
229  {
230  /* Map from MySQL commands to Drizzle commands. */
231  switch ((*l_packet)[0])
232  {
233  case 0: /* SLEEP */
234  case 1: /* QUIT */
235  case 2: /* INIT_DB */
236  case 3: /* QUERY */
237  break;
238 
239  case 8: /* SHUTDOWN */
240  (*l_packet)[0]= COM_SHUTDOWN;
241  break;
242 
243  case 12: /* KILL */
244  (*l_packet)[0]= COM_KILL;
245  break;
246 
247  case 14: /* PING */
248  (*l_packet)[0]= COM_PING;
249  break;
250 
251  default:
252  /* Respond with unknown command for MySQL commands we don't support. */
253  (*l_packet)[0]= COM_END;
254  packet_length= 1;
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, session->variables.net_read_timeout);
265 #endif
266 
267  return true;
268 }
269 
292 {
293  unsigned char buff[DRIZZLE_ERRMSG_SIZE+10],*pos;
294  const char *message= NULL;
295  uint32_t tmp;
296 
297  if (!net.vio) // hack for re-parsing queries
298  {
299  return;
300  }
301 
302  buff[0]=0; // No fields
303  if (session->main_da().status() == Diagnostics_area::DA_OK)
304  {
305  if (client_capabilities & CLIENT_FOUND_ROWS && session->main_da().found_rows())
306  pos=storeLength(buff+1,session->main_da().found_rows());
307  else
308  pos=storeLength(buff+1,session->main_da().affected_rows());
309  pos=storeLength(pos, session->main_da().last_insert_id());
310  int2store(pos, session->main_da().server_status());
311  pos+=2;
312  tmp= min(session->main_da().total_warn_count(), (uint32_t)65535);
313  message= session->main_da().message();
314  }
315  else
316  {
317  pos=storeLength(buff+1,0);
318  pos=storeLength(pos, 0);
319  int2store(pos, session->server_status);
320  pos+=2;
321  tmp= min(session->total_warn_count, (uint32_t)65535);
322  }
323 
324  /* We can only return up to 65535 warnings in two bytes */
325  int2store(pos, tmp);
326  pos+= 2;
327 
328  session->main_da().can_overwrite_status= true;
329 
330  if (message && message[0])
331  {
332  size_t length= strlen(message);
333  pos=storeLength(pos,length);
334  memcpy(pos,(unsigned char*) message,length);
335  pos+=length;
336  }
337  net.write(buff, pos - buff);
338  net.flush();
339 
340  session->main_da().can_overwrite_status= false;
341 }
342 
359 {
360  /* Set to true if no active vio, to work well in case of --init-file */
361  if (net.vio)
362  {
363  session->main_da().can_overwrite_status= true;
364  writeEOFPacket(session->main_da().server_status(), session->main_da().total_warn_count());
365  net.flush();
366  session->main_da().can_overwrite_status= false;
367  }
368  packet.shrink(buffer_length.get());
369 }
370 
371 
372 void ClientMySQLProtocol::sendError(drizzled::error_t sql_errno, const char *err)
373 {
374  // buff[]: sql_errno:2 + ('#':1 + SQLSTATE_LENGTH:5) + DRIZZLE_ERRMSG_SIZE:512
375  unsigned char buff[2 + 1 + SQLSTATE_LENGTH + DRIZZLE_ERRMSG_SIZE];
376 
377  assert(sql_errno != EE_OK);
378  assert(err && err[0]);
379 
380  /*
381  It's one case when we can push an error even though there
382  is an OK or EOF already.
383  */
384  session->main_da().can_overwrite_status= true;
385 
386  /* Abort multi-result sets */
387  session->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
388 
397  if (net.vio == 0)
398  {
399  return;
400  }
401 
402  int2store(buff, static_cast<uint16_t>(sql_errno));
403 
404  /* The first # is to make the client backward compatible */
405  buff[2]= '#';
406  unsigned char* pos= (unsigned char*) strcpy((char*) buff+3, error::convert_to_sqlstate(sql_errno));
407  pos+= strlen(error::convert_to_sqlstate(sql_errno));
408 
409  char *tmp= strncpy((char*)pos, err, DRIZZLE_ERRMSG_SIZE-1);
410  tmp+= strlen((char*)pos);
411  tmp[0]= '\0';
412  net.write_command(255, data_ref(), data_ref(buff, tmp));
413  net.flush();
414  session->main_da().can_overwrite_status= false;
415 }
416 
436 {
437  List<Item>::iterator it(list.begin());
438  unsigned char buff[80];
439  String tmp((char*) buff,sizeof(buff),&my_charset_bin);
440 
441  unsigned char *row_pos= storeLength(buff, list.size());
442  (void) net.write(buff, row_pos - buff);
443 
444  while (Item* item=it++)
445  {
446  SendField field;
447  item->make_field(&field);
448 
449  packet.length(0);
450 
451  store(STRING_WITH_LEN("def"));
452  store(field.db_name);
453  store(field.table_name);
454  store(field.org_table_name);
455  store(field.col_name);
456  store(field.org_col_name);
457  packet.realloc(packet.length()+12);
458 
459  /* Store fixed length fields */
460  char* pos= (char*) packet.ptr()+packet.length();
461  *pos++= 12; // Length of packed fields
462  /* No conversion */
463  int2store(pos, field.charsetnr);
464  int4store(pos+2, field.length);
465 
466  if (true) // _using_mysql41_protocol)
467  {
468  /* Switch to MySQL field numbering. */
469  switch (field.type)
470  {
471  case DRIZZLE_TYPE_LONG:
472  pos[6]= DRIZZLE_COLUMN_TYPE_LONG;
473  break;
474 
475  case DRIZZLE_TYPE_DOUBLE:
476  pos[6]= DRIZZLE_COLUMN_TYPE_DOUBLE;
477  break;
478 
479  case DRIZZLE_TYPE_NULL:
480  pos[6]= DRIZZLE_COLUMN_TYPE_NULL;
481  break;
482 
483  case DRIZZLE_TYPE_TIMESTAMP:
484  pos[6]= DRIZZLE_COLUMN_TYPE_TIMESTAMP;
485  break;
486 
487  case DRIZZLE_TYPE_LONGLONG:
488  pos[6]= DRIZZLE_COLUMN_TYPE_LONGLONG;
489  break;
490 
491  case DRIZZLE_TYPE_DATETIME:
492  pos[6]= DRIZZLE_COLUMN_TYPE_DATETIME;
493  break;
494 
495  case DRIZZLE_TYPE_TIME:
496  pos[6]= DRIZZLE_COLUMN_TYPE_TIME;
497  break;
498 
499  case DRIZZLE_TYPE_DATE:
500  pos[6]= DRIZZLE_COLUMN_TYPE_DATE;
501  break;
502 
503  case DRIZZLE_TYPE_VARCHAR:
504  pos[6]= DRIZZLE_COLUMN_TYPE_VARCHAR;
505  break;
506 
507  case DRIZZLE_TYPE_MICROTIME:
508  pos[6]= DRIZZLE_COLUMN_TYPE_VARCHAR;
509  break;
510 
511  case DRIZZLE_TYPE_UUID:
512  pos[6]= DRIZZLE_COLUMN_TYPE_VARCHAR;
513  break;
514 
515  case DRIZZLE_TYPE_IPV6:
516  pos[6]= DRIZZLE_COLUMN_TYPE_VARCHAR;
517  break;
518 
519  case DRIZZLE_TYPE_BOOLEAN:
520  pos[6]= DRIZZLE_COLUMN_TYPE_TINY;
521  break;
522 
523  case DRIZZLE_TYPE_DECIMAL:
524  pos[6]= (char)DRIZZLE_COLUMN_TYPE_NEWDECIMAL;
525  break;
526 
527  case DRIZZLE_TYPE_ENUM:
528  pos[6]= (char)DRIZZLE_COLUMN_TYPE_ENUM;
529  break;
530 
531  case DRIZZLE_TYPE_BLOB:
532  pos[6]= (char)DRIZZLE_COLUMN_TYPE_BLOB;
533  break;
534  }
535  }
536  else
537  {
538  /* Add one to compensate for tinyint removal from enum. */
539  pos[6]= field.type + 1;
540  }
541 
542  int2store(pos+7,field.flags);
543  pos[9]= (char) field.decimals;
544  pos[10]= 0; // For the future
545  pos[11]= 0; // For the future
546  pos+= 12;
547 
548  packet.length((uint32_t) (pos - packet.ptr()));
549  if (flush())
550  break;
551  }
552 
553  /*
554  Mark the end of meta-data result set, and store session->server_status,
555  to show that there is no cursor.
556  Send no warning information, as it will be sent at statement end.
557  */
558  writeEOFPacket(session->server_status, session->total_warn_count);
559 }
560 
561 void ClientMySQLProtocol::store(Field *from)
562 {
563  if (from->is_null())
564  return store();
565  if (from->type() == DRIZZLE_TYPE_BOOLEAN)
566  {
567  return store(from->val_int());
568  }
569 
570  char buff[MAX_FIELD_WIDTH];
571  String str(buff,sizeof(buff), &my_charset_bin);
572 
573  from->val_str_internal(&str);
574 
575  netStoreData(str.ptr(), str.length());
576 }
577 
578 void ClientMySQLProtocol::store()
579 {
580  char buff[1];
581  buff[0]= (char)251;
582  packet.append(buff, sizeof(buff), PACKET_BUFFER_EXTRA_ALLOC);
583 }
584 
585 void ClientMySQLProtocol::store(int32_t from)
586 {
587  char buff[12];
588  netStoreData(buff, internal::int10_to_str(from, buff, -10) - buff);
589 }
590 
591 void ClientMySQLProtocol::store(uint32_t from)
592 {
593  char buff[11];
594  netStoreData(buff, internal::int10_to_str(from, buff, 10) - buff);
595 }
596 
597 void ClientMySQLProtocol::store(int64_t from)
598 {
599  char buff[22];
600  netStoreData(buff, internal::int64_t10_to_str(from, buff, -10) - buff);
601 }
602 
603 void ClientMySQLProtocol::store(uint64_t from)
604 {
605  char buff[21];
606  netStoreData(buff, internal::int64_t10_to_str(from, buff, 10) - buff);
607 }
608 
609 void ClientMySQLProtocol::store(double from, uint32_t decimals, String *buffer)
610 {
611  buffer->set_real(from, decimals, session->charset());
612  netStoreData(buffer->ptr(), buffer->length());
613 }
614 
615 void ClientMySQLProtocol::store(const char *from, size_t length)
616 {
617  netStoreData(from, length);
618 }
619 
620 bool ClientMySQLProtocol::wasAborted()
621 {
622  return net.error_ && net.vio != 0;
623 }
624 
625 bool ClientMySQLProtocol::haveError()
626 {
627  return net.error_ || net.vio == 0;
628 }
629 
630 bool ClientMySQLProtocol::checkConnection()
631 {
632  char scramble[SCRAMBLE_LENGTH];
633  identifier::user::mptr user_identifier= identifier::User::make_shared();
634 
635  makeScramble(scramble);
636 
637  // TCP/IP connection
638  {
639  char ip[NI_MAXHOST];
640  uint16_t peer_port;
641 
642  if (net.peer_addr(ip, NI_MAXHOST, peer_port))
643  {
644  my_error(ER_BAD_HOST_ERROR, MYF(0), ip);
645  return false;
646  }
647 
648  user_identifier->setAddress(ip);
649  }
650  net.keepalive(true);
651 
652  char* end;
653  uint32_t pkt_len= 0;
654  uint32_t server_capabilites;
655  {
656  /* buff[] needs to big enough to hold the server_version variable */
657  char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH + 64];
658 
659  server_capabilites= CLIENT_BASIC_FLAGS | CLIENT_PROTOCOL_MYSQL41;
660 
661 #ifdef HAVE_COMPRESS
662  server_capabilites|= CLIENT_COMPRESS;
663 #endif /* HAVE_COMPRESS */
664 
665  end= buff + strlen(PANDORA_RELEASE_VERSION);
666  if ((end - buff) >= SERVER_VERSION_LENGTH)
667  end= buff + (SERVER_VERSION_LENGTH - 1);
668  memcpy(buff, PANDORA_RELEASE_VERSION, end - buff);
669  *end= 0;
670  end++;
671 
672  int4store((unsigned char*) end, session->variables.pseudo_thread_id);
673  end+= 4;
674 
675  /* We don't use scramble anymore. */
676  memcpy(end, scramble, SCRAMBLE_LENGTH_323);
677  end+= SCRAMBLE_LENGTH_323;
678  *end++= 0; /* an empty byte for some reason */
679 
680  int2store(end, server_capabilites);
681  /* write server characteristics: up to 16 bytes allowed */
682  end[2]=(char) default_charset_info->number;
683  int2store(end+3, session->server_status);
684  memset(end+5, 0, 13);
685  end+= 18;
686 
687  /* Write scramble tail. */
688  memcpy(end, scramble + SCRAMBLE_LENGTH_323, SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323);
689  end+= (SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323);
690  *end++= 0; /* an empty byte for some reason */
691 
692  /* At this point we write connection message and read reply */
693  if (net.write_command(PROTOCOL_VERSION, data_ref(), data_ref(buff, end))
694  || (pkt_len= net.read()) == packet_error
695  || pkt_len < MIN_HANDSHAKE_SIZE)
696  {
697  my_error(ER_HANDSHAKE_ERROR, MYF(0), user_identifier->address().c_str());
698  return false;
699  }
700  }
701  packet.alloc(buffer_length.get());
702 
703  client_capabilities= uint2korr(net.read_pos);
704  if (not (client_capabilities & CLIENT_PROTOCOL_MYSQL41))
705  {
706  my_error(ER_HANDSHAKE_ERROR, MYF(0), user_identifier->address().c_str());
707  return false;
708  }
709 
710  client_capabilities|= ((uint32_t) uint2korr(net.read_pos + 2)) << 16;
711  // session->max_client_packet_length= uint4korr(net.read_pos + 4);
712  end= (char*) net.read_pos + 32;
713 
714  /*
715  Disable those bits which are not supported by the server.
716  This is a precautionary measure, if the client lies. See Bug#27944.
717  */
718  client_capabilities&= server_capabilites;
719 
720  if (end >= (char*) net.read_pos + pkt_len + 2)
721  {
722  my_error(ER_HANDSHAKE_ERROR, MYF(0), user_identifier->address().c_str());
723  return false;
724  }
725 
726  char *user= end;
727  char *passwd= strchr(user, '\0')+1;
728  uint32_t user_len= passwd - user - 1;
729  char *l_db= passwd;
730 
731  /*
732  Only support new password format.
733 
734  Cast *passwd to an unsigned char, so that it doesn't extend the sign for
735  *passwd > 127 and become 2**32-127+ after casting to uint.
736  */
737  uint32_t passwd_len;
738  if (client_capabilities & CLIENT_SECURE_CONNECTION &&
739  passwd < (char *) net.read_pos + pkt_len)
740  {
741  passwd_len= (unsigned char)(*passwd++);
742  if (passwd_len > 0 and client_capabilities & CLIENT_CAPABILITIES_PLUGIN_AUTH)
743  {
744  user_identifier->setPasswordType(identifier::User::PLAIN_TEXT);
745  }
746  else if (passwd_len > 0)
747  {
748  user_identifier->setPasswordType(identifier::User::MYSQL_HASH);
749  user_identifier->setPasswordContext(scramble, SCRAMBLE_LENGTH);
750  }
751  }
752  else
753  {
754  passwd_len= 0;
755  }
756 
757  if (client_capabilities & CLIENT_CONNECT_WITH_DB &&
758  passwd < (char *) net.read_pos + pkt_len)
759  {
760  l_db= l_db + passwd_len + 1;
761  }
762  else
763  {
764  l_db= NULL;
765  }
766 
767  /* strlen() can't be easily deleted without changing client */
768  uint32_t db_len= l_db ? strlen(l_db) : 0;
769 
770  if (passwd + passwd_len + db_len > (char *) net.read_pos + pkt_len)
771  {
772  my_error(ER_HANDSHAKE_ERROR, MYF(0), user_identifier->address().c_str());
773  return false;
774  }
775 
776  /* If username starts and ends in "'", chop them off */
777  if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'')
778  {
779  user[user_len-1]= 0;
780  user++;
781  user_len-= 2;
782  }
783 
784  if (client_capabilities & CLIENT_INTERACTIVE)
785  {
786  _is_interactive= true;
787  }
788 
789  if (client_capabilities & CLIENT_CAPABILITIES_PLUGIN_AUTH)
790  {
791  passwd_len= strlen(passwd);
792  }
793 
794  user_identifier->setUser(user);
795  session->setUser(user_identifier);
796 
797  return session->checkUser(string(passwd, passwd_len), string(l_db ? l_db : ""));
798 
799 }
800 
801 void ClientMySQLProtocol::netStoreData(const void* from, size_t length)
802 {
803  size_t packet_length= packet.length();
804  /*
805  The +9 comes from that strings of length longer than 16M require
806  9 bytes to be stored (see storeLength).
807  */
808  if (packet_length+9+length > packet.alloced_length())
809  packet.realloc(packet_length+9+length);
810  unsigned char *to= storeLength((unsigned char*) packet.ptr()+packet_length, length);
811  memcpy(to,from,length);
812  packet.length((size_t) (to+length-(unsigned char*) packet.ptr()));
813 }
814 
820 void ClientMySQLProtocol::writeEOFPacket(uint32_t server_status, uint32_t total_warn_count)
821 {
822  unsigned char buff[5];
823  /*
824  Don't send warn count during SP execution, as the warn_list
825  is cleared between substatements, and mysqltest gets confused
826  */
827  uint32_t tmp= min(total_warn_count, (uint32_t)65535);
828  buff[0]= DRIZZLE_PROTOCOL_NO_MORE_DATA;
829  int2store(buff+1, tmp);
830  /*
831  The following test should never be true, but it's better to do it
832  because if 'is_fatal_error' is set the server is not going to execute
833  other queries (see the if test in dispatch_command / COM_QUERY)
834  */
835  if (session->is_fatal_error)
836  server_status&= ~SERVER_MORE_RESULTS_EXISTS;
837  int2store(buff + 3, server_status);
838  net.write(buff, 5);
839 }
840 
841 /*
842  Store an integer with simple packing into a output package
843 
844  buffer Store the packed integer here
845  length integers to store
846 
847  This is mostly used to store lengths of strings. We have to cast
848  the result for the LL() becasue of a bug in Forte CC compiler.
849 
850  RETURN
851  Position in 'buffer' after the packed length
852 */
853 
854 unsigned char *ClientMySQLProtocol::storeLength(unsigned char *buffer, uint64_t length)
855 {
856  if (length < 251)
857  {
858  *buffer= (unsigned char) length;
859  return buffer+1;
860  }
861  /* 251 is reserved for NULL */
862  if (length < 65536)
863  {
864  *buffer++= 252;
865  int2store(buffer, (uint32_t) length);
866  return buffer+2;
867  }
868  if (length < 16777216)
869  {
870  *buffer++=253;
871  int3store(buffer, (uint32_t) length);
872  return buffer+3;
873  }
874  *buffer++=254;
875  int8store(buffer, length);
876  return buffer+8;
877 }
878 
879 void ClientMySQLProtocol::makeScramble(char *scramble)
880 {
881  /* This is the MySQL algorithm with minimal changes. */
882  random_seed1= (random_seed1 * 3 + random_seed2) % random_max;
883  random_seed2= (random_seed1 + random_seed2 + 33) % random_max;
884  uint32_t seed= static_cast<uint32_t>((static_cast<double>(random_seed1) / random_max_double) * 0xffffffff);
885 
886  void *pointer= this;
887  uint32_t pointer_seed;
888  memcpy(&pointer_seed, &pointer, 4);
889  uint32_t random1= (seed + pointer_seed) % random_max;
890  uint32_t random2= (seed + session->variables.pseudo_thread_id + net.vio->get_fd()) % random_max;
891 
892  for (char *end= scramble + SCRAMBLE_LENGTH; scramble != end; scramble++)
893  {
894  random1= (random1 * 3 + random2) % random_max;
895  random2= (random1 + random2 + 33) % random_max;
896  *scramble= static_cast<char>((static_cast<double>(random1) / random_max_double) * 94 + 33);
897  }
898 }
899 
900 static int init(drizzled::module::Context &context)
901 {
902  /* Initialize random seeds for the MySQL algorithm with minimal changes. */
903  time_t seed_time= time(NULL);
904  random_seed1= seed_time % random_max;
905  random_seed2= (seed_time / 2) % random_max;
906 
907  const module::option_map &vm= context.getOptions();
908 
909  context.add(new plugin::Create_function<MySQLPassword>(MySQLPasswordName));
910 
911  ListenMySQLProtocol* listen_obj= new ListenMySQLProtocol("mysql_protocol", vm["bind-address"].as<std::string>());
912  listen_obj->addCountersToTable();
913  context.add(listen_obj);
914  context.registerVariable(new sys_var_constrained_value_readonly<in_port_t>("port", port));
915  context.registerVariable(new sys_var_constrained_value<uint32_t>("connect_timeout", connect_timeout));
916  context.registerVariable(new sys_var_constrained_value<uint32_t>("read_timeout", read_timeout));
917  context.registerVariable(new sys_var_constrained_value<uint32_t>("write_timeout", write_timeout));
918  context.registerVariable(new sys_var_constrained_value<uint32_t>("retry_count", retry_count));
919  context.registerVariable(new sys_var_constrained_value<uint32_t>("buffer_length", buffer_length));
920  context.registerVariable(new sys_var_const_string_val("bind_address", vm["bind-address"].as<std::string>()));
921  context.registerVariable(new sys_var_uint32_t_ptr("max-connections", &ListenMySQLProtocol::mysql_counters.max_connections));
922 
923  return 0;
924 }
925 
926 static void init_options(drizzled::module::option_context &context)
927 {
928  context("port",
929  po::value<port_constraint>(&port)->default_value(3306),
930  _("Port number to use for connection or 0 for default to with MySQL "
931  "protocol."));
932  context("connect-timeout",
933  po::value<timeout_constraint>(&connect_timeout)->default_value(10),
934  _("Connect Timeout."));
935  context("read-timeout",
936  po::value<timeout_constraint>(&read_timeout)->default_value(30),
937  _("Read Timeout."));
938  context("write-timeout",
939  po::value<timeout_constraint>(&write_timeout)->default_value(60),
940  _("Write Timeout."));
941  context("retry-count",
942  po::value<retry_constraint>(&retry_count)->default_value(10),
943  _("Retry Count."));
944  context("buffer-length",
945  po::value<buffer_constraint>(&buffer_length)->default_value(16384),
946  _("Buffer length."));
947  context("bind-address",
948  po::value<string>()->default_value("localhost"),
949  _("Address to bind to."));
950  context("max-connections",
951  po::value<uint32_t>(&ListenMySQLProtocol::mysql_counters.max_connections)->default_value(1000),
952  _("Maximum simultaneous connections."));
953 }
954 
955 } /* namespace drizzle_plugin */
956 
957 DRIZZLE_DECLARE_PLUGIN
958 {
959  DRIZZLE_VERSION_ID,
960  "mysql_protocol",
961  "0.1",
962  "Eric Day",
963  N_("MySQL network protocol"),
964  PLUGIN_LICENSE_GPL,
966  NULL,
968 }
969 DRIZZLE_DECLARE_PLUGIN_END;
void writeEOFPacket(uint32_t server_status, uint32_t total_warn_count)
bool checkUser(const std::string &passwd, const std::string &db)
Definition: session.cc:659
int get_fd() const
Definition: vio.cc:194
static void init_options(drizzled::module::option_context &context)
Initialize query-log command line options.
Definition: module.cc:157
An Proxy Wrapper around boost::program_options::variables_map.
virtual void sendFields(drizzled::List< drizzled::Item > &)
static int init(drizzled::module::Context &context)
Add query_log plugin to Drizzle and initalize query_log system variables.
Definition: module.cc:228
virtual bool readCommand(char **packet, uint32_t &packet_length)
virtual void sendError(const drizzled::error_t sql_errno, const char *err)
bool is_fatal_error
Definition: session.h:540
drizzle_system_variables & variables
Definition: session.h:199