Drizzled Public API Documentation

mysql_protocol.cc

00001 /* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
00002  *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
00003  *
00004  *  Copyright (C) 2008 Sun Microsystems, Inc.
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; version 2 of the License.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00018  */
00019 
00020 #include <config.h>
00021 #include <drizzled/gettext.h>
00022 #include <drizzled/error.h>
00023 #include <drizzled/query_id.h>
00024 #include <drizzled/error/sql_state.h>
00025 #include <drizzled/session.h>
00026 #include <drizzled/internal/m_string.h>
00027 #include <algorithm>
00028 #include <boost/program_options.hpp>
00029 #include <drizzled/module/option_map.h>
00030 #include <drizzled/util/tokenize.h>
00031 #include "errmsg.h"
00032 #include "mysql_protocol.h"
00033 #include "mysql_password.h"
00034 #include "options.h"
00035 #include <drizzled/identifier.h>
00036 #include <drizzled/plugin/function.h>
00037 #include <libdrizzle/constants.h>
00038 
00039 #define PROTOCOL_VERSION 10
00040 
00041 namespace po= boost::program_options;
00042 using namespace std;
00043 using namespace drizzled;
00044 
00045 namespace drizzle_plugin
00046 {
00047 
00048 std::vector<std::string> ClientMySQLProtocol::mysql_admin_ip_addresses;
00049 static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024;
00050 
00051 static port_constraint port;
00052 static timeout_constraint connect_timeout;
00053 static timeout_constraint read_timeout;
00054 static timeout_constraint write_timeout;
00055 static retry_constraint retry_count;
00056 static buffer_constraint buffer_length;
00057 
00058 static uint32_t random_seed1;
00059 static uint32_t random_seed2;
00060 static const uint32_t random_max= 0x3FFFFFFF;
00061 static const double random_max_double= (double)0x3FFFFFFF;
00062 
00063 
00064 ProtocolCounters *ListenMySQLProtocol::mysql_counters= new ProtocolCounters();
00065 
00066 ListenMySQLProtocol::~ListenMySQLProtocol()
00067 { }
00068 
00069 void ListenMySQLProtocol::addCountersToTable()
00070 {
00071   counters.push_back(new drizzled::plugin::ListenCounter(new std::string("connection_count"), &getCounters()->connectionCount));
00072   counters.push_back(new drizzled::plugin::ListenCounter(new std::string("connected"), &getCounters()->connected));
00073   counters.push_back(new drizzled::plugin::ListenCounter(new std::string("failed_connections"), &getCounters()->failedConnections));
00074 }
00075 
00076 const std::string ListenMySQLProtocol::getHost(void) const
00077 {
00078   return _hostname;
00079 }
00080 
00081 in_port_t ListenMySQLProtocol::getPort(void) const
00082 {
00083   return port.get();
00084 }
00085 
00086 plugin::Client *ListenMySQLProtocol::getClient(int fd)
00087 {
00088   int new_fd;
00089   new_fd= acceptTcp(fd);
00090   if (new_fd == -1)
00091     return NULL;
00092 
00093   return new ClientMySQLProtocol(new_fd, _using_mysql41_protocol, getCounters());
00094 }
00095 
00096 ClientMySQLProtocol::ClientMySQLProtocol(int fd, bool using_mysql41_protocol, ProtocolCounters *set_counters):
00097   is_admin_connection(false),
00098   _using_mysql41_protocol(using_mysql41_protocol),
00099   _is_interactive(false),
00100   counters(set_counters)
00101 {
00102   
00103   net.vio= 0;
00104 
00105   if (fd == -1)
00106     return;
00107 
00108   if (drizzleclient_net_init_sock(&net, fd, buffer_length.get()))
00109     throw bad_alloc();
00110 
00111   drizzleclient_net_set_read_timeout(&net, read_timeout.get());
00112   drizzleclient_net_set_write_timeout(&net, write_timeout.get());
00113   net.retry_count=retry_count.get();
00114 }
00115 
00116 ClientMySQLProtocol::~ClientMySQLProtocol()
00117 {
00118   if (net.vio)
00119     net.vio->close();
00120 }
00121 
00122 int ClientMySQLProtocol::getFileDescriptor(void)
00123 {
00124   return drizzleclient_net_get_sd(&net);
00125 }
00126 
00127 bool ClientMySQLProtocol::isConnected()
00128 {
00129   return net.vio != 0;
00130 }
00131 
00132 bool ClientMySQLProtocol::isReading(void)
00133 {
00134   return net.reading_or_writing == 1;
00135 }
00136 
00137 bool ClientMySQLProtocol::isWriting(void)
00138 {
00139   return net.reading_or_writing == 2;
00140 }
00141 
00142 bool ClientMySQLProtocol::flush()
00143 {
00144   if (net.vio == NULL)
00145     return false;
00146   bool ret= drizzleclient_net_write(&net, (unsigned char*) packet.ptr(),
00147                            packet.length());
00148   packet.length(0);
00149   return ret;
00150 }
00151 
00152 void ClientMySQLProtocol::close(void)
00153 {
00154   if (net.vio)
00155   { 
00156     drizzleclient_net_close(&net);
00157     drizzleclient_net_end(&net);
00158     if (is_admin_connection)
00159     {
00160       counters->adminConnected.decrement();
00161     }
00162     else
00163     {
00164       counters->connected.decrement();
00165     }
00166   }
00167 }
00168 
00169 bool ClientMySQLProtocol::authenticate()
00170 {
00171   bool connection_is_valid;
00172   if (is_admin_connection)
00173   {
00174     counters->adminConnectionCount.increment();
00175     counters->adminConnected.increment();
00176   }
00177   else
00178   {
00179     counters->connectionCount.increment();
00180     counters->connected.increment();
00181   }
00182 
00183   /* Use "connect_timeout" value during connection phase */
00184   drizzleclient_net_set_read_timeout(&net, connect_timeout.get());
00185   drizzleclient_net_set_write_timeout(&net, connect_timeout.get());
00186 
00187   connection_is_valid= checkConnection();
00188 
00189   if (connection_is_valid)
00190   {
00191     if (not is_admin_connection and (counters->connected > counters->max_connections))
00192     {
00193       std::string errmsg(ER(ER_CON_COUNT_ERROR));
00194       sendError(ER_CON_COUNT_ERROR, errmsg.c_str());
00195       counters->failedConnections.increment();
00196     }
00197     else
00198     {
00199       sendOK();
00200     }
00201   }
00202   else
00203   {
00204     sendError(session->main_da.sql_errno(), session->main_da.message());
00205     counters->failedConnections.increment();
00206     return false;
00207   }
00208 
00209   /* Connect completed, set read/write timeouts back to default */
00210   drizzleclient_net_set_read_timeout(&net, read_timeout.get());
00211   drizzleclient_net_set_write_timeout(&net, write_timeout.get());
00212   return true;
00213 }
00214 
00215 bool ClientMySQLProtocol::readCommand(char **l_packet, uint32_t *packet_length)
00216 {
00217   /*
00218     This thread will do a blocking read from the client which
00219     will be interrupted when the next command is received from
00220     the client, the connection is closed or "net_wait_timeout"
00221     number of seconds has passed
00222   */
00223 #ifdef NEVER
00224   /* We can do this much more efficiently with poll timeouts or watcher thread,
00225      disabling for now, which means net_wait_timeout == read_timeout. */
00226   drizzleclient_net_set_read_timeout(&net,
00227                                      session->variables.net_wait_timeout);
00228 #endif
00229 
00230   net.pkt_nr=0;
00231 
00232   *packet_length= drizzleclient_net_read(&net);
00233   if (*packet_length == packet_error)
00234   {
00235     /* Check if we can continue without closing the connection */
00236 
00237     if(net.last_errno== ER_NET_PACKET_TOO_LARGE)
00238       my_error(ER_NET_PACKET_TOO_LARGE, MYF(0));
00239     if (session->main_da.status() == Diagnostics_area::DA_ERROR)
00240       sendError(session->main_da.sql_errno(), session->main_da.message());
00241     else
00242       sendOK();
00243 
00244     if (net.error != 3)
00245       return false;                       // We have to close it.
00246 
00247     net.error= 0;
00248   }
00249 
00250   *l_packet= (char*) net.read_pos;
00251 
00252   /*
00253     'packet_length' contains length of data, as it was stored in packet
00254     header. In case of malformed header, drizzleclient_net_read returns zero.
00255     If packet_length is not zero, drizzleclient_net_read ensures that the returned
00256     number of bytes was actually read from network.
00257     There is also an extra safety measure in drizzleclient_net_read:
00258     it sets packet[packet_length]= 0, but only for non-zero packets.
00259   */
00260 
00261   if (*packet_length == 0)                       /* safety */
00262   {
00263     /* Initialize with COM_SLEEP packet */
00264     (*l_packet)[0]= (unsigned char) COM_SLEEP;
00265     *packet_length= 1;
00266   }
00267   else if (_using_mysql41_protocol)
00268   {
00269     /* Map from MySQL commands to Drizzle commands. */
00270     switch ((int)(*l_packet)[0])
00271     {
00272     case 0: /* SLEEP */
00273     case 1: /* QUIT */
00274     case 2: /* INIT_DB */
00275     case 3: /* QUERY */
00276       break;
00277 
00278     case 8: /* SHUTDOWN */
00279       (*l_packet)[0]= (unsigned char) COM_SHUTDOWN;
00280       break;
00281 
00282     case 12: /* KILL */
00283       (*l_packet)[0]= (unsigned char) COM_KILL;
00284       break;
00285 
00286     case 14: /* PING */
00287       (*l_packet)[0]= (unsigned char) COM_PING;
00288       break;
00289 
00290 
00291     default:
00292       /* Respond with unknown command for MySQL commands we don't support. */
00293       (*l_packet)[0]= (unsigned char) COM_END;
00294       *packet_length= 1;
00295       break;
00296     }
00297   }
00298 
00299   /* Do not rely on drizzleclient_net_read, extra safety against programming errors. */
00300   (*l_packet)[*packet_length]= '\0';                  /* safety */
00301 
00302 #ifdef NEVER
00303   /* See comment above. */
00304   /* Restore read timeout value */
00305   drizzleclient_net_set_read_timeout(&net,
00306                                      session->variables.net_read_timeout);
00307 #endif
00308 
00309   return true;
00310 }
00311 
00333 void ClientMySQLProtocol::sendOK()
00334 {
00335   unsigned char buff[DRIZZLE_ERRMSG_SIZE+10],*pos;
00336   const char *message= NULL;
00337   uint32_t tmp;
00338 
00339   if (!net.vio)    // hack for re-parsing queries
00340   {
00341     return;
00342   }
00343 
00344   buff[0]=0;                    // No fields
00345   if (session->main_da.status() == Diagnostics_area::DA_OK)
00346   {
00347     if (client_capabilities & CLIENT_FOUND_ROWS && session->main_da.found_rows())
00348       pos=storeLength(buff+1,session->main_da.found_rows());
00349     else
00350       pos=storeLength(buff+1,session->main_da.affected_rows());
00351     pos=storeLength(pos, session->main_da.last_insert_id());
00352     int2store(pos, session->main_da.server_status());
00353     pos+=2;
00354     tmp= min(session->main_da.total_warn_count(), (uint32_t)65535);
00355     message= session->main_da.message();
00356   }
00357   else
00358   {
00359     pos=storeLength(buff+1,0);
00360     pos=storeLength(pos, 0);
00361     int2store(pos, session->server_status);
00362     pos+=2;
00363     tmp= min(session->total_warn_count, (uint32_t)65535);
00364   }
00365 
00366   /* We can only return up to 65535 warnings in two bytes */
00367   int2store(pos, tmp);
00368   pos+= 2;
00369 
00370   session->main_da.can_overwrite_status= true;
00371 
00372   if (message && message[0])
00373   {
00374     size_t length= strlen(message);
00375     pos=storeLength(pos,length);
00376     memcpy(pos,(unsigned char*) message,length);
00377     pos+=length;
00378   }
00379   drizzleclient_net_write(&net, buff, (size_t) (pos-buff));
00380   drizzleclient_net_flush(&net);
00381 
00382   session->main_da.can_overwrite_status= false;
00383 }
00384 
00400 void ClientMySQLProtocol::sendEOF()
00401 {
00402   /* Set to true if no active vio, to work well in case of --init-file */
00403   if (net.vio != 0)
00404   {
00405     session->main_da.can_overwrite_status= true;
00406     writeEOFPacket(session->main_da.server_status(),
00407                    session->main_da.total_warn_count());
00408     drizzleclient_net_flush(&net);
00409     session->main_da.can_overwrite_status= false;
00410   }
00411   packet.shrink(buffer_length.get());
00412 }
00413 
00414 
00415 void ClientMySQLProtocol::sendError(drizzled::error_t sql_errno, const char *err)
00416 {
00417   uint32_t length;
00418   /*
00419     buff[]: sql_errno:2 + ('#':1 + SQLSTATE_LENGTH:5) + DRIZZLE_ERRMSG_SIZE:512
00420   */
00421   unsigned char buff[2+1+SQLSTATE_LENGTH+DRIZZLE_ERRMSG_SIZE], *pos;
00422 
00423   assert(sql_errno != EE_OK);
00424   assert(err && err[0]);
00425 
00426   /*
00427     It's one case when we can push an error even though there
00428     is an OK or EOF already.
00429   */
00430   session->main_da.can_overwrite_status= true;
00431 
00432   /* Abort multi-result sets */
00433   session->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
00434 
00443   if (net.vio == 0)
00444   {
00445     return;
00446   }
00447 
00448   int2store(buff, static_cast<uint16_t>(sql_errno));
00449   pos= buff+2;
00450 
00451   /* The first # is to make the client backward compatible */
00452   buff[2]= '#';
00453   pos= (unsigned char*) strcpy((char*) buff+3, error::convert_to_sqlstate(sql_errno));
00454   pos+= strlen(error::convert_to_sqlstate(sql_errno));
00455 
00456   char *tmp= strncpy((char*)pos, err, DRIZZLE_ERRMSG_SIZE-1);
00457   tmp+= strlen((char*)pos);
00458   tmp[0]= '\0';
00459   length= (uint32_t)(tmp-(char*)buff);
00460   err= (char*) buff;
00461 
00462   drizzleclient_net_write_command(&net,(unsigned char) 255, (unsigned char*) "", 0, (unsigned char*) err, length);
00463 
00464   drizzleclient_net_flush(&net);
00465 
00466   session->main_da.can_overwrite_status= false;
00467 }
00468 
00487 bool ClientMySQLProtocol::sendFields(List<Item> *list)
00488 {
00489   List<Item>::iterator it(list->begin());
00490   Item *item;
00491   unsigned char buff[80];
00492   String tmp((char*) buff,sizeof(buff),&my_charset_bin);
00493 
00494   unsigned char *row_pos= storeLength(buff, list->size());
00495   (void) drizzleclient_net_write(&net, buff, (size_t) (row_pos-buff));
00496 
00497   while ((item=it++))
00498   {
00499     char *pos;
00500     SendField field;
00501     item->make_field(&field);
00502 
00503     packet.length(0);
00504 
00505     if (store(STRING_WITH_LEN("def")) ||
00506         store(field.db_name) ||
00507         store(field.table_name) ||
00508         store(field.org_table_name) ||
00509         store(field.col_name) ||
00510         store(field.org_col_name) ||
00511         packet.realloc(packet.length()+12))
00512       goto err;
00513 
00514     /* Store fixed length fields */
00515     pos= (char*) packet.ptr()+packet.length();
00516     *pos++= 12;                // Length of packed fields
00517     /* No conversion */
00518     int2store(pos, field.charsetnr);
00519     int4store(pos+2, field.length);
00520 
00521     if (_using_mysql41_protocol)
00522     {
00523       /* Switch to MySQL field numbering. */
00524       switch (field.type)
00525       {
00526       case DRIZZLE_TYPE_LONG:
00527         pos[6]= DRIZZLE_COLUMN_TYPE_LONG;
00528         break;
00529 
00530       case DRIZZLE_TYPE_DOUBLE:
00531         pos[6]= DRIZZLE_COLUMN_TYPE_DOUBLE;
00532         break;
00533 
00534       case DRIZZLE_TYPE_NULL:
00535         pos[6]= DRIZZLE_COLUMN_TYPE_NULL;
00536         break;
00537 
00538       case DRIZZLE_TYPE_TIMESTAMP:
00539         pos[6]= DRIZZLE_COLUMN_TYPE_TIMESTAMP;
00540         break;
00541 
00542       case DRIZZLE_TYPE_LONGLONG:
00543         pos[6]= DRIZZLE_COLUMN_TYPE_LONGLONG;
00544         break;
00545 
00546       case DRIZZLE_TYPE_DATETIME:
00547         pos[6]= DRIZZLE_COLUMN_TYPE_DATETIME;
00548         break;
00549 
00550       case DRIZZLE_TYPE_TIME:
00551         pos[6]= DRIZZLE_COLUMN_TYPE_TIME;
00552         break;
00553 
00554       case DRIZZLE_TYPE_DATE:
00555         pos[6]= DRIZZLE_COLUMN_TYPE_DATE;
00556         break;
00557 
00558       case DRIZZLE_TYPE_VARCHAR:
00559         pos[6]= DRIZZLE_COLUMN_TYPE_VARCHAR;
00560         break;
00561 
00562       case DRIZZLE_TYPE_MICROTIME:
00563         pos[6]= DRIZZLE_COLUMN_TYPE_VARCHAR;
00564         break;
00565 
00566       case DRIZZLE_TYPE_UUID:
00567         pos[6]= DRIZZLE_COLUMN_TYPE_VARCHAR;
00568         break;
00569 
00570       case DRIZZLE_TYPE_BOOLEAN:
00571         pos[6]= DRIZZLE_COLUMN_TYPE_TINY;
00572         break;
00573 
00574       case DRIZZLE_TYPE_DECIMAL:
00575         pos[6]= (char)DRIZZLE_COLUMN_TYPE_NEWDECIMAL;
00576         break;
00577 
00578       case DRIZZLE_TYPE_ENUM:
00579         pos[6]= (char)DRIZZLE_COLUMN_TYPE_ENUM;
00580         break;
00581 
00582       case DRIZZLE_TYPE_BLOB:
00583         pos[6]= (char)DRIZZLE_COLUMN_TYPE_BLOB;
00584         break;
00585       }
00586     }
00587     else
00588     {
00589       /* Add one to compensate for tinyint removal from enum. */
00590       pos[6]= field.type + 1;
00591     }
00592 
00593     int2store(pos+7,field.flags);
00594     pos[9]= (char) field.decimals;
00595     pos[10]= 0;                // For the future
00596     pos[11]= 0;                // For the future
00597     pos+= 12;
00598 
00599     packet.length((uint32_t) (pos - packet.ptr()));
00600     if (flush())
00601       break;
00602   }
00603 
00604   /*
00605     Mark the end of meta-data result set, and store session->server_status,
00606     to show that there is no cursor.
00607     Send no warning information, as it will be sent at statement end.
00608   */
00609   writeEOFPacket(session->server_status, session->total_warn_count);
00610   return 0;
00611 
00612 err:
00613   my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES),
00614              MYF(0));
00615   return 1;
00616 }
00617 
00618 bool ClientMySQLProtocol::store(Field *from)
00619 {
00620   if (from->is_null())
00621     return store();
00622   if (from->type() == DRIZZLE_TYPE_BOOLEAN)
00623   {
00624     return store(from->val_int());
00625   }
00626 
00627   char buff[MAX_FIELD_WIDTH];
00628   String str(buff,sizeof(buff), &my_charset_bin);
00629 
00630   from->val_str_internal(&str);
00631 
00632   return netStoreData((const unsigned char *)str.ptr(), str.length());
00633 }
00634 
00635 bool ClientMySQLProtocol::store(void)
00636 {
00637   char buff[1];
00638   buff[0]= (char)251;
00639   return packet.append(buff, sizeof(buff), PACKET_BUFFER_EXTRA_ALLOC);
00640 }
00641 
00642 bool ClientMySQLProtocol::store(int32_t from)
00643 {
00644   char buff[12];
00645   return netStoreData((unsigned char*) buff,
00646                       (size_t) (internal::int10_to_str(from, buff, -10) - buff));
00647 }
00648 
00649 bool ClientMySQLProtocol::store(uint32_t from)
00650 {
00651   char buff[11];
00652   return netStoreData((unsigned char*) buff,
00653                       (size_t) (internal::int10_to_str(from, buff, 10) - buff));
00654 }
00655 
00656 bool ClientMySQLProtocol::store(int64_t from)
00657 {
00658   char buff[22];
00659   return netStoreData((unsigned char*) buff,
00660                       (size_t) (internal::int64_t10_to_str(from, buff, -10) - buff));
00661 }
00662 
00663 bool ClientMySQLProtocol::store(uint64_t from)
00664 {
00665   char buff[21];
00666   return netStoreData((unsigned char*) buff,
00667                       (size_t) (internal::int64_t10_to_str(from, buff, 10) - buff));
00668 }
00669 
00670 bool ClientMySQLProtocol::store(double from, uint32_t decimals, String *buffer)
00671 {
00672   buffer->set_real(from, decimals, session->charset());
00673   return netStoreData((unsigned char*) buffer->ptr(), buffer->length());
00674 }
00675 
00676 bool ClientMySQLProtocol::store(const char *from, size_t length)
00677 {
00678   return netStoreData((const unsigned char *)from, length);
00679 }
00680 
00681 bool ClientMySQLProtocol::wasAborted(void)
00682 {
00683   return net.error && net.vio != 0;
00684 }
00685 
00686 bool ClientMySQLProtocol::haveMoreData(void)
00687 {
00688   return drizzleclient_net_more_data(&net);
00689 }
00690 
00691 bool ClientMySQLProtocol::haveError(void)
00692 {
00693   return net.error || net.vio == 0;
00694 }
00695 
00696 bool ClientMySQLProtocol::checkConnection(void)
00697 {
00698   uint32_t pkt_len= 0;
00699   char *end;
00700   char scramble[SCRAMBLE_LENGTH];
00701   identifier::User::shared_ptr user_identifier= identifier::User::make_shared();
00702 
00703   makeScramble(scramble);
00704 
00705   // TCP/IP connection
00706   {
00707     char ip[NI_MAXHOST];
00708     uint16_t peer_port;
00709 
00710     if (drizzleclient_net_peer_addr(&net, ip, &peer_port, NI_MAXHOST))
00711     {
00712       my_error(ER_BAD_HOST_ERROR, MYF(0), ip);
00713       return false;
00714     }
00715 
00716     user_identifier->setAddress(ip);
00717   }
00718   drizzleclient_net_keepalive(&net, true);
00719 
00720   uint32_t server_capabilites;
00721   {
00722     /* buff[] needs to big enough to hold the server_version variable */
00723     char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH + 64];
00724 
00725     server_capabilites= CLIENT_BASIC_FLAGS;
00726 
00727     if (_using_mysql41_protocol)
00728     {
00729       server_capabilites|= CLIENT_PROTOCOL_MYSQL41;
00730     }
00731 
00732 #ifdef HAVE_COMPRESS
00733     server_capabilites|= CLIENT_COMPRESS;
00734 #endif /* HAVE_COMPRESS */
00735 
00736     end= buff + strlen(PANDORA_RELEASE_VERSION);
00737     if ((end - buff) >= SERVER_VERSION_LENGTH)
00738       end= buff + (SERVER_VERSION_LENGTH - 1);
00739     memcpy(buff, PANDORA_RELEASE_VERSION, end - buff);
00740     *end= 0;
00741     end++;
00742 
00743     int4store((unsigned char*) end, session->variables.pseudo_thread_id);
00744     end+= 4;
00745 
00746     /* We don't use scramble anymore. */
00747     memcpy(end, scramble, SCRAMBLE_LENGTH_323);
00748     end+= SCRAMBLE_LENGTH_323;
00749     *end++= 0; /* an empty byte for some reason */
00750 
00751     int2store(end, server_capabilites);
00752     /* write server characteristics: up to 16 bytes allowed */
00753     end[2]=(char) default_charset_info->number;
00754     int2store(end+3, session->server_status);
00755     memset(end+5, 0, 13);
00756     end+= 18;
00757 
00758     /* Write scramble tail. */
00759     memcpy(end, scramble + SCRAMBLE_LENGTH_323, SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323);
00760     end+= (SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323);
00761     *end++= 0; /* an empty byte for some reason */
00762 
00763     /* At this point we write connection message and read reply */
00764     if (drizzleclient_net_write_command(&net
00765           , (unsigned char) PROTOCOL_VERSION
00766           , (unsigned char*) ""
00767           , 0
00768           , (unsigned char*) buff
00769           , (size_t) (end-buff)) 
00770         ||    (pkt_len= drizzleclient_net_read(&net)) == packet_error 
00771         || pkt_len < MIN_HANDSHAKE_SIZE)
00772     {
00773       my_error(ER_HANDSHAKE_ERROR, MYF(0), user_identifier->address().c_str());
00774       return false;
00775     }
00776   }
00777   if (packet.alloc(buffer_length.get()))
00778     return false; /* The error is set by alloc(). */
00779 
00780   client_capabilities= uint2korr(net.read_pos);
00781   if (!(client_capabilities & CLIENT_PROTOCOL_MYSQL41))
00782   {
00783     my_error(ER_HANDSHAKE_ERROR, MYF(0), user_identifier->address().c_str());
00784     return false;
00785   }
00786 
00787   client_capabilities|= ((uint32_t) uint2korr(net.read_pos + 2)) << 16;
00788   session->max_client_packet_length= uint4korr(net.read_pos + 4);
00789   end= (char*) net.read_pos + 32;
00790 
00791   /*
00792     Disable those bits which are not supported by the server.
00793     This is a precautionary measure, if the client lies. See Bug#27944.
00794   */
00795   client_capabilities&= server_capabilites;
00796 
00797   if (end >= (char*) net.read_pos + pkt_len + 2)
00798   {
00799     my_error(ER_HANDSHAKE_ERROR, MYF(0), user_identifier->address().c_str());
00800     return false;
00801   }
00802 
00803   net.return_status= &session->server_status;
00804 
00805   char *user= end;
00806   char *passwd= strchr(user, '\0')+1;
00807   uint32_t user_len= passwd - user - 1;
00808   char *l_db= passwd;
00809 
00810   /*
00811     Only support new password format.
00812 
00813     Cast *passwd to an unsigned char, so that it doesn't extend the sign for
00814     *passwd > 127 and become 2**32-127+ after casting to uint.
00815   */
00816   uint32_t passwd_len;
00817   if (client_capabilities & CLIENT_SECURE_CONNECTION &&
00818       passwd < (char *) net.read_pos + pkt_len)
00819   {
00820     passwd_len= (unsigned char)(*passwd++);
00821     if (passwd_len > 0 and client_capabilities & CLIENT_CAPABILITIES_PLUGIN_AUTH)
00822     {
00823       user_identifier->setPasswordType(identifier::User::PLAIN_TEXT);
00824     }
00825     else if (passwd_len > 0)
00826     {
00827       user_identifier->setPasswordType(identifier::User::MYSQL_HASH);
00828       user_identifier->setPasswordContext(scramble, SCRAMBLE_LENGTH);
00829     }
00830   }
00831   else
00832   {
00833     passwd_len= 0;
00834   }
00835 
00836   if (client_capabilities & CLIENT_CONNECT_WITH_DB &&
00837       passwd < (char *) net.read_pos + pkt_len)
00838   {
00839     l_db= l_db + passwd_len + 1;
00840   }
00841   else
00842   {
00843     l_db= NULL;
00844   }
00845 
00846   /* strlen() can't be easily deleted without changing client */
00847   uint32_t db_len= l_db ? strlen(l_db) : 0;
00848 
00849   if (passwd + passwd_len + db_len > (char *) net.read_pos + pkt_len)
00850   {
00851     my_error(ER_HANDSHAKE_ERROR, MYF(0), user_identifier->address().c_str());
00852     return false;
00853   }
00854 
00855   /* If username starts and ends in "'", chop them off */
00856   if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'')
00857   {
00858     user[user_len-1]= 0;
00859     user++;
00860     user_len-= 2;
00861   }
00862 
00863   if (client_capabilities & CLIENT_ADMIN)
00864   {
00865     if ((strncmp(user, "root", 4) == 0) and isAdminAllowed())
00866     {
00867       is_admin_connection= true;
00868     }
00869     else
00870     {
00871       my_error(ER_ADMIN_ACCESS, MYF(0));
00872       return false;
00873     }
00874   }
00875 
00876   if (client_capabilities & CLIENT_INTERACTIVE)
00877   {
00878     _is_interactive= true;
00879   }
00880 
00881   if (client_capabilities & CLIENT_CAPABILITIES_PLUGIN_AUTH)
00882   {
00883     passwd_len= strlen(passwd);
00884   }
00885 
00886   user_identifier->setUser(user);
00887   session->setUser(user_identifier);
00888 
00889   return session->checkUser(string(passwd, passwd_len),
00890                             string(l_db ? l_db : ""));
00891 
00892 }
00893 
00894 bool ClientMySQLProtocol::isAdminAllowed(void)
00895 {
00896   if (std::find(mysql_admin_ip_addresses.begin(), mysql_admin_ip_addresses.end(), session->user()->address()) != mysql_admin_ip_addresses.end())
00897     return true;
00898 
00899   return false;
00900 }
00901 
00902 bool ClientMySQLProtocol::netStoreData(const unsigned char *from, size_t length)
00903 {
00904   size_t packet_length= packet.length();
00905   /*
00906      The +9 comes from that strings of length longer than 16M require
00907      9 bytes to be stored (see storeLength).
00908   */
00909   if (packet_length+9+length > packet.alloced_length() &&
00910       packet.realloc(packet_length+9+length))
00911     return 1;
00912   unsigned char *to= storeLength((unsigned char*) packet.ptr()+packet_length, length);
00913   memcpy(to,from,length);
00914   packet.length((size_t) (to+length-(unsigned char*) packet.ptr()));
00915   return 0;
00916 }
00917 
00923 void ClientMySQLProtocol::writeEOFPacket(uint32_t server_status,
00924                                          uint32_t total_warn_count)
00925 {
00926   unsigned char buff[5];
00927   /*
00928     Don't send warn count during SP execution, as the warn_list
00929     is cleared between substatements, and mysqltest gets confused
00930   */
00931   uint32_t tmp= min(total_warn_count, (uint32_t)65535);
00932   buff[0]= DRIZZLE_PROTOCOL_NO_MORE_DATA;
00933   int2store(buff+1, tmp);
00934   /*
00935     The following test should never be true, but it's better to do it
00936     because if 'is_fatal_error' is set the server is not going to execute
00937     other queries (see the if test in dispatch_command / COM_QUERY)
00938   */
00939   if (session->is_fatal_error)
00940     server_status&= ~SERVER_MORE_RESULTS_EXISTS;
00941   int2store(buff + 3, server_status);
00942   drizzleclient_net_write(&net, buff, 5);
00943 }
00944 
00945 /*
00946   Store an integer with simple packing into a output package
00947 
00948   buffer      Store the packed integer here
00949   length    integers to store
00950 
00951   This is mostly used to store lengths of strings.  We have to cast
00952   the result for the LL() becasue of a bug in Forte CC compiler.
00953 
00954   RETURN
00955   Position in 'buffer' after the packed length
00956 */
00957 
00958 unsigned char *ClientMySQLProtocol::storeLength(unsigned char *buffer, uint64_t length)
00959 {
00960   if (length < (uint64_t) 251LL)
00961   {
00962     *buffer=(unsigned char) length;
00963     return buffer+1;
00964   }
00965   /* 251 is reserved for NULL */
00966   if (length < (uint64_t) 65536LL)
00967   {
00968     *buffer++=252;
00969     int2store(buffer,(uint32_t) length);
00970     return buffer+2;
00971   }
00972   if (length < (uint64_t) 16777216LL)
00973   {
00974     *buffer++=253;
00975     int3store(buffer,(uint32_t) length);
00976     return buffer+3;
00977   }
00978   *buffer++=254;
00979   int8store(buffer,length);
00980   return buffer+8;
00981 }
00982 
00983 void ClientMySQLProtocol::makeScramble(char *scramble)
00984 {
00985   /* This is the MySQL algorithm with minimal changes. */
00986   random_seed1= (random_seed1 * 3 + random_seed2) % random_max;
00987   random_seed2= (random_seed1 + random_seed2 + 33) % random_max;
00988   uint32_t seed= static_cast<uint32_t>((static_cast<double>(random_seed1) / random_max_double) * 0xffffffff);
00989 
00990   void *pointer= this;
00991   uint32_t pointer_seed;
00992   memcpy(&pointer_seed, &pointer, 4);
00993   uint32_t random1= (seed + pointer_seed) % random_max;
00994   uint32_t random2= (seed + session->variables.pseudo_thread_id + net.vio->get_fd()) % random_max;
00995 
00996   for (char *end= scramble + SCRAMBLE_LENGTH; scramble != end; scramble++)
00997   {
00998     random1= (random1 * 3 + random2) % random_max;
00999     random2= (random1 + random2 + 33) % random_max;
01000     *scramble= static_cast<char>((static_cast<double>(random1) / random_max_double) * 94 + 33);
01001   }
01002 }
01003 
01004 void ClientMySQLProtocol::mysql_compose_ip_addresses(vector<string> options)
01005 {
01006   for (vector<string>::iterator it= options.begin();
01007        it != options.end();
01008        ++it)
01009   {
01010     tokenize(*it, mysql_admin_ip_addresses, ",", true);
01011   }
01012 }
01013 
01014 static ListenMySQLProtocol *listen_obj= NULL;
01015 plugin::Create_function<MySQLPassword> *mysql_password= NULL;
01016 
01017 static int init(drizzled::module::Context &context)
01018 {  
01019   /* Initialize random seeds for the MySQL algorithm with minimal changes. */
01020   time_t seed_time= time(NULL);
01021   random_seed1= seed_time % random_max;
01022   random_seed2= (seed_time / 2) % random_max;
01023 
01024   const module::option_map &vm= context.getOptions();
01025 
01026   mysql_password= new plugin::Create_function<MySQLPassword>(MySQLPasswordName);
01027   context.add(mysql_password);
01028 
01029   listen_obj= new ListenMySQLProtocol("mysql_protocol", vm["bind-address"].as<std::string>(), true);
01030   listen_obj->addCountersToTable();
01031   context.add(listen_obj); 
01032   context.registerVariable(new sys_var_constrained_value_readonly<in_port_t>("port", port));
01033   context.registerVariable(new sys_var_constrained_value<uint32_t>("connect_timeout", connect_timeout));
01034   context.registerVariable(new sys_var_constrained_value<uint32_t>("read_timeout", read_timeout));
01035   context.registerVariable(new sys_var_constrained_value<uint32_t>("write_timeout", write_timeout));
01036   context.registerVariable(new sys_var_constrained_value<uint32_t>("retry_count", retry_count));
01037   context.registerVariable(new sys_var_constrained_value<uint32_t>("buffer_length", buffer_length));
01038   context.registerVariable(new sys_var_const_string_val("bind_address",
01039                                                         vm["bind-address"].as<std::string>()));
01040 
01041   context.registerVariable(new sys_var_uint32_t_ptr("max-connections", &ListenMySQLProtocol::mysql_counters->max_connections));
01042 
01043   return 0;
01044 }
01045 
01046 static void init_options(drizzled::module::option_context &context)
01047 {
01048   context("port",
01049           po::value<port_constraint>(&port)->default_value(3306),
01050           _("Port number to use for connection or 0 for default to with MySQL "
01051                               "protocol."));
01052   context("connect-timeout",
01053           po::value<timeout_constraint>(&connect_timeout)->default_value(10),
01054           _("Connect Timeout."));
01055   context("read-timeout",
01056           po::value<timeout_constraint>(&read_timeout)->default_value(30),
01057           _("Read Timeout."));
01058   context("write-timeout",
01059           po::value<timeout_constraint>(&write_timeout)->default_value(60),
01060           _("Write Timeout."));
01061   context("retry-count",
01062           po::value<retry_constraint>(&retry_count)->default_value(10),
01063           _("Retry Count."));
01064   context("buffer-length",
01065           po::value<buffer_constraint>(&buffer_length)->default_value(16384),
01066           _("Buffer length."));
01067   context("bind-address",
01068           po::value<string>()->default_value("localhost"),
01069           _("Address to bind to."));
01070   context("max-connections",
01071           po::value<uint32_t>(&ListenMySQLProtocol::mysql_counters->max_connections)->default_value(1000),
01072           _("Maximum simultaneous connections."));
01073   context("admin-ip-addresses",
01074           po::value<vector<string> >()->composing()->notifier(&ClientMySQLProtocol::mysql_compose_ip_addresses),
01075           _("A restrictive IP address list for incoming admin connections."));
01076 }
01077 
01078 } /* namespace drizzle_plugin */
01079 
01080 DRIZZLE_DECLARE_PLUGIN
01081 {
01082   DRIZZLE_VERSION_ID,
01083   "mysql-protocol",
01084   "0.1",
01085   "Eric Day",
01086   "MySQL Protocol Module",
01087   PLUGIN_LICENSE_GPL,
01088   drizzle_plugin::init,             /* Plugin Init */
01089   NULL, /* depends */
01090   drizzle_plugin::init_options    /* config options */
01091 }
01092 DRIZZLE_DECLARE_PLUGIN_END;