#include "proxysql.h" #include "cpp.h" extern MySQL_Authentication *GloMyAuth; extern MySQL_Threads_Handler *GloMTH; #ifdef max_allowed_packet #undef max_allowed_packet #endif //#define RESULTSET_BUFLEN 16300 #ifdef DEBUG static void __dump_pkt(const char *func, unsigned char *_ptr, unsigned int len) { if (GloVars.global.gdbg==0) return; if (GloVars.global.gdbg_lvl[PROXY_DEBUG_MYSQL_PROTOCOL].verbosity < 8 ) return; unsigned int i; fprintf(stderr,"DUMP %d bytes FROM %s\n", len, func); for(i = 0; i < len; i++) { if(isprint(_ptr[i])) fprintf(stderr,"%c", _ptr[i]); else fprintf(stderr,"."); if (i>0 && (i%16==15 || i==len-1)) { unsigned int j; if (i%16!=15) { j=15-i%16; while (j--) fprintf(stderr," "); } fprintf(stderr," --- "); for (j=(i==len-1 ? ((int)(i/16))*16 : i-15 ) ; j<=i; j++) { fprintf(stderr,"%02x ", _ptr[j]); } fprintf(stderr,"\n"); } } fprintf(stderr,"\n\n"); } #endif char *sha1_pass_hex(char *sha1_pass) { if (sha1_pass==NULL) return NULL; char *buff=(char *)malloc(SHA_DIGEST_LENGTH*2+2); buff[0]='*'; buff[SHA_DIGEST_LENGTH*2+1]='\0'; int i; uint8_t a; for (i=0;iseed1= (rand_st->seed1*3+rand_st->seed2) % rand_st->max_value; rand_st->seed2= (rand_st->seed1+rand_st->seed2+33) % rand_st->max_value; return (((double) rand_st->seed1) / rand_st->max_value_dbl); } void proxy_create_random_string(char *to, uint length, struct rand_struct *rand_st) { uint i; for (i=0; i= '0' && x <= '9') return (x - 0x30); else if (x >= 'A' && x <= 'F') return(x - 0x37); else if (x >= 'a' && x <= 'f') return(x - 0x57); else { proxy_error("Invalid char"); return 0; } } void unhex_pass(uint8_t *out, const char *in) { int i=0; for (i=0;iprot_status=CPY2(pkt); pkt+=sizeof(uint16_t); p+=sizeof(uint16_t); #ifdef DEBUG warns=CPY2(pkt); #endif /* DEBUG */ pkt+=sizeof(uint16_t); p+=sizeof(uint16_t); pkt++; p++; if (length>p) { memcpy(msg,pkt,length-p); msg[length-p]=0; } else { msg[0]=0; } proxy_debug(PROXY_DEBUG_MYSQL_PROTOCOL,1,"OK Packet \n", (uint32_t)affected_rows, (uint32_t)insert_id, (uint16_t)mp->prot_status, (uint16_t)warns, msg); return PKT_PARSED; } int pkt_end(unsigned char *pkt, unsigned int length, MySQL_Protocol *mp) { if(*pkt != 0xFE || length > 5) return PKT_ERROR; #ifdef DEBUG uint16_t warns = 0; #endif /* DEBUG */ if(length > 1) { // 4.1+ pkt++; #ifdef DEBUG warns = CPY2(pkt); #endif /* DEBUG */ pkt += 2; mp->prot_status = CPY2(pkt); /* if((tag->state == STATE_TXT_ROW || tag->state == STATE_BIN_ROW) && status & SERVER_MORE_RESULTS_EXISTS && tag->event != EVENT_END_MULTI_RESULT) return PKT_WRONG_TYPE; } */ } proxy_debug(PROXY_DEBUG_MYSQL_PROTOCOL,1,"End Packet \n", mp->prot_status, warns); // if(status & SERVER_MORE_RESULTS_EXISTS) { // proxy_debug(PROXY_DEBUG_MYSQL_PROTOCOL,1,"End Packet \n"); // } return PKT_PARSED; } MySQL_Prepared_Stmt_info::MySQL_Prepared_Stmt_info(unsigned char *pkt, unsigned int length) { pkt += 5; statement_id = CPY4(pkt); pkt += sizeof(uint32_t); num_columns = CPY2(pkt); pkt += sizeof(uint16_t); num_params = CPY2(pkt); pkt += sizeof(uint16_t); pkt++; // reserved_1 warning_count = CPY2(pkt); // fprintf(stderr,"Generating prepared statement with id=%d, cols=%d, params=%d, warns=%d\n", statement_id, num_columns, num_params, warning_count); pending_num_columns=num_columns; pending_num_params=num_params; } void MySQL_Protocol::init(MySQL_Data_Stream **__myds, MySQL_Connection_userinfo *__userinfo, MySQL_Session *__sess) { myds=__myds; userinfo=__userinfo; sess=__sess; current_PreStmt=NULL; // prot_status=0; } //int parse_mysql_pkt(unsigned char *pkt, enum session_states *states, int from_client) { int MySQL_Protocol::parse_mysql_pkt(PtrSize_t *PS_entry, MySQL_Data_Stream *__myds) { unsigned char *pkt=(unsigned char *)PS_entry->ptr; // unsigned int size=PS_entry->size; //myds=__myds; enum mysql_data_stream_status *DSS=&(*myds)->DSS; mysql_hdr hdr; unsigned char cmd; unsigned char *payload; int from=(*myds)->myds_type; // if the packet is from client or server enum MySQL_response_type c; payload=pkt+sizeof(mysql_hdr); memcpy(&hdr,pkt,sizeof(mysql_hdr)); //Copy4B(&hdr,pkt); proxy_debug(PROXY_DEBUG_MYSQL_PROTOCOL,1,"MySQL Packet length=%d, senquence_id=%d, addr=%p\n", hdr.pkt_length, hdr.pkt_id, payload); switch (*DSS) { // client is not connected yet case STATE_NOT_CONNECTED: if (from==MYDS_FRONTEND) { // at this stage we expect a packet from the server, not from client return PKT_ERROR; } break; // client has sent the handshake case STATE_CLIENT_HANDSHAKE: if (from==MYDS_FRONTEND) { // at this stage we expect a packet from the server, not from client return PKT_ERROR; } c=mysql_response(payload, hdr.pkt_length); switch (c) { case OK_Packet: if (pkt_ok(payload, hdr.pkt_length, this)==PKT_PARSED) { *DSS=STATE_SLEEP; return PKT_PARSED; } break; default: return PKT_ERROR; // from the server we expect either an OK or an ERR. Everything else is wrong } break; // connection is idle. Client should be send a command case STATE_SLEEP: // if (!from_client) { // return PKT_ERROR; // } cmd=*payload; switch (cmd) { case MYSQL_COM_QUERY: if (pkt_com_query(payload, hdr.pkt_length)==PKT_PARSED) { //*states=STATE_CLIENT_COM_QUERY; return PKT_PARSED; } break; } //break; default: // TO BE REMOVED: begin if (from==MYDS_FRONTEND) { // at this stage we expect a packet from the server, not from client return PKT_ERROR; } c=mysql_response(payload, hdr.pkt_length); switch (c) { case OK_Packet: if (pkt_ok(payload, hdr.pkt_length, this)==PKT_PARSED) { *DSS=STATE_SLEEP; return PKT_PARSED; } break; case EOF_Packet: pkt_end(payload, hdr.pkt_length, this); break; default: return PKT_ERROR; // from the server we expect either an OK or an ERR. Everything else is wrong } // TO BE REMOVED: end break; } return PKT_ERROR; } static unsigned char protocol_version=10; //static uint16_t server_capabilities=CLIENT_FOUND_ROWS | CLIENT_PROTOCOL_41 | CLIENT_IGNORE_SIGPIPE | CLIENT_TRANSACTIONS | CLIENT_SECURE_CONNECTION | CLIENT_CONNECT_WITH_DB | CLIENT_SSL; //static uint8_t server_language=33; static uint16_t server_status=SERVER_STATUS_AUTOCOMMIT; //static char *mysql_server_version = (char *)"5.1.30"; //bool MySQL_Protocol::generate_statistics_response(MySQL_Data_Stream *myds, bool send, void **ptr, unsigned int *len) { bool MySQL_Protocol::generate_statistics_response(bool send, void **ptr, unsigned int *len) { // FIXME : this function generates a not useful string. It is a placeholder for now char buf1[1000]; unsigned long long t1=monotonic_time(); //const char *stats=(char *)"Uptime: 1000 Threads: 1 Questions: 34221015 Slow queries: 0 Opens: 757 Flush tables: 1 Open tables: 185 Queries per second avg: 22.289"; sprintf(buf1,"Uptime: %llu Threads: %d Questions: %llu Slow queries: %llu", (t1-GloVars.global.start_time)/1000/1000, MyHGM->status.client_connections , GloMTH->get_total_queries() , GloMTH->get_slow_queries() ); unsigned char statslen=strlen(buf1); mysql_hdr myhdr; myhdr.pkt_id=1; //myhdr.pkt_length=statslen+1; myhdr.pkt_length=statslen; unsigned int size=myhdr.pkt_length+sizeof(mysql_hdr); unsigned char *_ptr=(unsigned char *)l_alloc(size); memcpy(_ptr, &myhdr, sizeof(mysql_hdr)); //Copy4B(_ptr, &myhdr); int l=sizeof(mysql_hdr); //_ptr[l++]=statslen; memcpy(_ptr+l,buf1,statslen); if (send==true) { (*myds)->PSarrayOUT->add((void *)_ptr,size); } if (len) { *len=size; } if (ptr) { *ptr=(void *)_ptr; } #ifdef DEBUG if (dump_pkt) { __dump_pkt(__func__,_ptr,size); } #endif return true; } //bool MySQL_Protocol::generate_pkt_EOF(MySQL_Data_Stream *myds, bool send, void **ptr, unsigned int *len, uint8_t sequence_id, uint16_t warnings, uint16_t status) { bool MySQL_Protocol::generate_pkt_EOF(bool send, void **ptr, unsigned int *len, uint8_t sequence_id, uint16_t warnings, uint16_t status) { if ((*myds)->sess->mirror==true) { return true; } mysql_hdr myhdr; myhdr.pkt_id=sequence_id; myhdr.pkt_length=5; unsigned int size=myhdr.pkt_length+sizeof(mysql_hdr); unsigned char *_ptr=(unsigned char *)l_alloc(size); memcpy(_ptr, &myhdr, sizeof(mysql_hdr)); //Copy4B(_ptr, &myhdr); int l=sizeof(mysql_hdr); _ptr[l]=0xfe; l++; memcpy(_ptr+l, &warnings, sizeof(uint16_t)); l+=sizeof(uint16_t); memcpy(_ptr+l, &status, sizeof(uint16_t)); if (send==true) { (*myds)->PSarrayOUT->add((void *)_ptr,size); switch ((*myds)->DSS) { case STATE_COLUMN_DEFINITION: (*myds)->DSS=STATE_EOF1; break; case STATE_ROW: (*myds)->DSS=STATE_EOF2; break; default: //assert(0); break; } } if (len) { *len=size; } if (ptr) { *ptr=(void *)_ptr; } #ifdef DEBUG if (dump_pkt) { __dump_pkt(__func__,_ptr,size); } #endif return true; } //bool MySQL_Protocol::generate_pkt_ERR(MySQL_Data_Stream *myds, bool send, void **ptr, unsigned int *len, uint8_t sequence_id, uint16_t error_code, char *sql_state, char *sql_message) { bool MySQL_Protocol::generate_pkt_ERR(bool send, void **ptr, unsigned int *len, uint8_t sequence_id, uint16_t error_code, char *sql_state, char *sql_message) { if ((*myds)->sess->mirror==true) { return true; } mysql_hdr myhdr; uint32_t sql_message_len=( sql_message ? strlen(sql_message) : 0 ); myhdr.pkt_id=sequence_id; myhdr.pkt_length=1+sizeof(uint16_t)+1+5+sql_message_len; unsigned int size=myhdr.pkt_length+sizeof(mysql_hdr); unsigned char *_ptr=(unsigned char *)l_alloc(size); memcpy(_ptr, &myhdr, sizeof(mysql_hdr)); //Copy4B(_ptr, &myhdr); int l=sizeof(mysql_hdr); _ptr[l]=0xff; l++; memcpy(_ptr+l, &error_code, sizeof(uint16_t)); l+=sizeof(uint16_t); _ptr[l]='#'; l++; memcpy(_ptr+l, sql_state, 5); l+=5; if (sql_message) memcpy(_ptr+l, sql_message, sql_message_len); if (send==true) { (*myds)->PSarrayOUT->add((void *)_ptr,size); switch ((*myds)->DSS) { case STATE_CLIENT_HANDSHAKE: case STATE_QUERY_SENT_DS: case STATE_QUERY_SENT_NET: (*myds)->DSS=STATE_ERR; break; case STATE_OK: break; default: assert(0); } } if (len) { *len=size; } if (ptr) { *ptr=(void *)_ptr; } #ifdef DEBUG if (dump_pkt) { __dump_pkt(__func__,_ptr,size); } #endif return true; } //bool MySQL_Protocol::generate_pkt_OK(MySQL_Data_Stream *myds, bool send, void **ptr, unsigned int *len, uint8_t sequence_id, unsigned int affected_rows, unsigned int last_insert_id, uint16_t status, uint16_t warnings, char *msg) { bool MySQL_Protocol::generate_pkt_OK(bool send, void **ptr, unsigned int *len, uint8_t sequence_id, unsigned int affected_rows, uint64_t last_insert_id, uint16_t status, uint16_t warnings, char *msg) { if ((*myds)->sess->mirror==true) { return true; } char affected_rows_prefix; uint8_t affected_rows_len=mysql_encode_length(affected_rows, &affected_rows_prefix); char last_insert_id_prefix; uint8_t last_insert_id_len=mysql_encode_length(last_insert_id, &last_insert_id_prefix); uint32_t msg_len=( msg ? strlen(msg) : 0 ); char msg_prefix; uint8_t msg_len_len=mysql_encode_length(msg_len, &msg_prefix); mysql_hdr myhdr; myhdr.pkt_id=sequence_id; myhdr.pkt_length=1+affected_rows_len+last_insert_id_len+sizeof(uint16_t)+sizeof(uint16_t)+msg_len; if (msg_len) myhdr.pkt_length+=msg_len_len; unsigned int size=myhdr.pkt_length+sizeof(mysql_hdr); unsigned char *_ptr=(unsigned char *)l_alloc(size); memcpy(_ptr, &myhdr, sizeof(mysql_hdr)); //Copy4B(_ptr, &myhdr); int l=sizeof(mysql_hdr); _ptr[l]=0x00; l++; /* if (affected_rows_len > 1) { _ptr[l]=affected_rows_prefix; l++; } memcpy(_ptr+l, &affected_rows, affected_rows_len); l+=( affected_rows_len > 1 ? affected_rows_len - 1 : 1 ); if (last_insert_id_len > 1) { _ptr[l]=last_insert_id_prefix; l++; } memcpy(_ptr+l, &last_insert_id, last_insert_id_len); l+=( last_insert_id_len > 1 ? last_insert_id_len -1 : 1 ); */ l+=write_encoded_length(_ptr+l, affected_rows, affected_rows_len, affected_rows_prefix); l+=write_encoded_length(_ptr+l, last_insert_id, last_insert_id_len, last_insert_id_prefix); memcpy(_ptr+l, &status, sizeof(uint16_t)); l+=sizeof(uint16_t); memcpy(_ptr+l, &warnings, sizeof(uint16_t)); l+=sizeof(uint16_t); if (msg) { l+=write_encoded_length(_ptr+l, msg_len, msg_len_len, msg_prefix); memcpy(_ptr+l, msg, msg_len); } if (send==true) { (*myds)->PSarrayOUT->add((void *)_ptr,size); switch ((*myds)->DSS) { case STATE_CLIENT_HANDSHAKE: case STATE_QUERY_SENT_DS: case STATE_QUERY_SENT_NET: (*myds)->DSS=STATE_OK; break; case STATE_OK: break; default: assert(0); } } if (len) { *len=size; } if (ptr) { *ptr=(void *)_ptr; } #ifdef DEBUG if (dump_pkt) { __dump_pkt(__func__,_ptr,size); } #endif return true; } //bool MySQL_Protocol::generate_pkt_column_count(MySQL_Data_Stream *myds, bool send, void **ptr, unsigned int *len, uint8_t sequence_id, uint64_t count) { bool MySQL_Protocol::generate_pkt_column_count(bool send, void **ptr, unsigned int *len, uint8_t sequence_id, uint64_t count) { if ((*myds)->sess->mirror==true) { return true; } char count_prefix=0; uint8_t count_len=mysql_encode_length(count, &count_prefix); mysql_hdr myhdr; myhdr.pkt_id=sequence_id; myhdr.pkt_length=count_len; unsigned int size=myhdr.pkt_length+sizeof(mysql_hdr); unsigned char *_ptr=(unsigned char *)l_alloc(size); memcpy(_ptr, &myhdr, sizeof(mysql_hdr)); //Copy4B(_ptr, &myhdr); int l=sizeof(mysql_hdr); /* if (count_len > 1) { _ptr[l]=count_prefix; l++; } memcpy(_ptr+l, &count, count_len); l+=( count_len > 1 ? count_len -1 : 1 ); */ l+=write_encoded_length(_ptr+l, count, count_len, count_prefix); if (send==true) { (*myds)->PSarrayOUT->add((void *)_ptr,size); } if (len) { *len=size; } if (ptr) { *ptr=(void *)_ptr; } #ifdef DEBUG if (dump_pkt) { __dump_pkt(__func__,_ptr,size); } #endif return true; } //bool MySQL_Protocol::generate_pkt_field(MySQL_Data_Stream *myds, bool send, void **ptr, unsigned int *len, uint8_t sequence_id, char *schema, char *table, char *org_table, char *name, char *org_name, uint16_t charset, uint32_t column_length, uint8_t type, uint16_t flags, uint8_t decimals, bool field_list, uint64_t defvalue_length, char *defvalue) { bool MySQL_Protocol::generate_pkt_field(bool send, void **ptr, unsigned int *len, uint8_t sequence_id, char *schema, char *table, char *org_table, char *name, char *org_name, uint16_t charset, uint32_t column_length, uint8_t type, uint16_t flags, uint8_t decimals, bool field_list, uint64_t defvalue_length, char *defvalue) { if ((*myds)->sess->mirror==true) { return true; } char *def=(char *)"def"; uint32_t def_strlen=strlen(def); char def_prefix; uint8_t def_len=mysql_encode_length(def_strlen, &def_prefix); uint32_t schema_strlen=strlen(schema); char schema_prefix; uint8_t schema_len=mysql_encode_length(schema_strlen, &schema_prefix); uint32_t table_strlen=strlen(table); char table_prefix; uint8_t table_len=mysql_encode_length(table_strlen, &table_prefix); uint32_t org_table_strlen=strlen(org_table); char org_table_prefix; uint8_t org_table_len=mysql_encode_length(org_table_strlen, &org_table_prefix); uint32_t name_strlen=strlen(name); char name_prefix; uint8_t name_len=mysql_encode_length(name_strlen, &name_prefix); uint32_t org_name_strlen=strlen(org_name); char org_name_prefix; uint8_t org_name_len=mysql_encode_length(org_name_strlen, &org_name_prefix); char defvalue_length_prefix; uint8_t defvalue_length_len=mysql_encode_length(defvalue_length, &defvalue_length_prefix); mysql_hdr myhdr; myhdr.pkt_id=sequence_id; myhdr.pkt_length = def_len + def_strlen + schema_len + schema_strlen + table_len + table_strlen + org_table_len + org_table_strlen + name_len + name_strlen + org_name_len + org_name_strlen + 1 // filler + sizeof(uint16_t) // charset + sizeof(uint32_t) // column_length + sizeof(uint8_t) // type + sizeof(uint16_t) // flags + sizeof(uint8_t) // decimals + 2; // filler if (field_list) { myhdr.pkt_length += defvalue_length_len + strlen(defvalue); } //else myhdr.pkt_length++; unsigned int size=myhdr.pkt_length+sizeof(mysql_hdr); unsigned char *_ptr=(unsigned char *)l_alloc(size); memcpy(_ptr, &myhdr, sizeof(mysql_hdr)); int l=sizeof(mysql_hdr); l+=write_encoded_length_and_string(_ptr+l, def_strlen, def_len, def_prefix, def); l+=write_encoded_length_and_string(_ptr+l, schema_strlen, schema_len, schema_prefix, schema); l+=write_encoded_length_and_string(_ptr+l, table_strlen, table_len, table_prefix, table); l+=write_encoded_length_and_string(_ptr+l, org_table_strlen, org_table_len, org_table_prefix, org_table); l+=write_encoded_length_and_string(_ptr+l, name_strlen, name_len, name_prefix, name); l+=write_encoded_length_and_string(_ptr+l, org_name_strlen, org_name_len, org_name_prefix, org_name); _ptr[l]=0x0c; l++; memcpy(_ptr+l,&charset,sizeof(uint16_t)); l+=sizeof(uint16_t); memcpy(_ptr+l,&column_length,sizeof(uint32_t)); l+=sizeof(uint32_t); _ptr[l]=type; l++; memcpy(_ptr+l,&flags,sizeof(uint16_t)); l+=sizeof(uint16_t); _ptr[l]=decimals; l++; _ptr[l]=0x00; l++; _ptr[l]=0x00; l++; if (field_list) { l+=write_encoded_length_and_string(_ptr+l, strlen(defvalue), defvalue_length_len, defvalue_length_prefix, defvalue); } //else _ptr[l]=0x00; //else fprintf(stderr,"current deflen=%d, defstrlen=%d, namelen=%d, namestrlen=%d, l=%d\n", def_len, def_strlen, name_len, name_strlen, l); if (send==true) { (*myds)->PSarrayOUT->add((void *)_ptr,size); } if (len) { *len=size; } if (ptr) { *ptr=(void *)_ptr; } #ifdef DEBUG if (dump_pkt) { __dump_pkt(__func__,_ptr,size); } #endif return true; } // FIXME FIXME function not completed yet! // see https://dev.mysql.com/doc/internals/en/com-stmt-prepare-response.html bool MySQL_Protocol::generate_STMT_PREPARE_RESPONSE(uint8_t sequence_id, MySQL_STMT_Global_info *stmt_info) { uint8_t sid=sequence_id; uint16_t i; char *okpack=(char *)malloc(16); // first packet mysql_hdr hdr; hdr.pkt_id=sid; hdr.pkt_length=12; memcpy(okpack,&hdr,sizeof(mysql_hdr)); // copy header okpack[4]=0; okpack[13]=0; memcpy(okpack+5,&stmt_info->statement_id,sizeof(uint32_t)); memcpy(okpack+9,&stmt_info->num_columns,sizeof(uint16_t)); memcpy(okpack+11,&stmt_info->num_params,sizeof(uint16_t)); memcpy(okpack+14,&stmt_info->warning_count,sizeof(uint16_t)); (*myds)->PSarrayOUT->add((void *)okpack,16); sid++; if (stmt_info->num_params) { for (i=0; inum_params; i++) { generate_pkt_field(true,NULL,NULL,sid, (char *)"", (char *)"", (char *)"", (char *)"?", (char *)"", 63,0,253,128,0,false,0,NULL); // NOTE: charset is 63 = binary ! sid++; } generate_pkt_EOF(true,NULL,NULL,sid,0,SERVER_STATUS_AUTOCOMMIT); // FIXME : for now we pass a very broken flag sid++; } if (stmt_info->num_columns) { for (i=0; inum_columns; i++) { MYSQL_FIELD *fd=stmt_info->fields[i]; //bool MySQL_Protocol::generate_pkt_field(bool send, void **ptr, unsigned int *len, uint8_t sequence_id, char *schema, char *table, char *org_table, char *name, char *org_name, uint16_t charset, uint32_t column_length, uint8_t type, uint16_t flags, uint8_t decimals, bool field_list, uint64_t defvalue_length, char *defvalue) { generate_pkt_field(true,NULL,NULL,sid, fd->db, fd->table, fd->org_table, fd->name, fd->org_name, fd->charsetnr, 0, fd->type, fd->flags, fd->decimals, false,0,NULL); sid++; } generate_pkt_EOF(true,NULL,NULL,sid,0,SERVER_STATUS_AUTOCOMMIT); // FIXME : for now we pass a very broken flag sid++; } return true; } bool MySQL_Protocol::generate_pkt_row(bool send, void **ptr, unsigned int *len, uint8_t sequence_id, int colnums, unsigned long *fieldslen, char **fieldstxt) { int col=0; int rowlen=0; for (col=0; colPSarrayOUT->add((void *)_ptr,size); } if (len) { *len=size; } if (ptr) { *ptr=(void *)_ptr; } #ifdef DEBUG if (dump_pkt) { __dump_pkt(__func__,_ptr,size); } #endif return true; } uint8_t MySQL_Protocol::generate_pkt_row3(MySQL_ResultSet *myrs, unsigned int *len, uint8_t sequence_id, int colnums, unsigned long *fieldslen, char **fieldstxt) { if ((*myds)->sess->mirror==true) { return true; } int col=0; unsigned int rowlen=0; uint8_t pkt_sid=sequence_id; for (col=0; colbuffer_used) ) { // there is space in the buffer, add the data to it pkt.ptr = myrs->buffer + myrs->buffer_used; myrs->buffer_used += pkt.size; } else { // there is no space in the buffer, we flush the buffer and recreate it myrs->buffer_to_PSarrayOut(); // now we can check again if there is space in the buffer if ( pkt.size<=(RESULTSET_BUFLEN-myrs->buffer_used) ) { // there is space in the NEW buffer, add the data to it pkt.ptr = myrs->buffer + myrs->buffer_used; myrs->buffer_used += pkt.size; } else { // a new buffer is not enough to store the new row pkt.ptr=l_alloc(pkt.size); } } int l=sizeof(mysql_hdr); for (col=0; col= myrs->buffer && pkt.ptr < myrs->buffer+RESULTSET_BUFLEN) { // we are writing within the buffer, do not add to PSarrayOUT } else { // we are writing outside the buffer, add to PSarrayOUT myrs->PSarrayOUT->add(pkt.ptr,pkt.size); } } else { unsigned int left=pkt.size; unsigned int copied=0; while (left>=(0xFFFFFF+sizeof(mysql_hdr))) { PtrSize_t pkt2; pkt2.size=0xFFFFFF+sizeof(mysql_hdr); pkt2.ptr=l_alloc(pkt2.size); memcpy((char *)pkt2.ptr+sizeof(mysql_hdr), (char *)pkt.ptr+sizeof(mysql_hdr)+copied, 0xFFFFFF); mysql_hdr myhdr; myhdr.pkt_id=pkt_sid; pkt_sid++; myhdr.pkt_length=0xFFFFFF; memcpy(pkt2.ptr, &myhdr, sizeof(mysql_hdr)); // we are writing a large packet (over 16MB), we assume we are always outside the buffer myrs->PSarrayOUT->add(pkt2.ptr,pkt2.size); copied+=0xFFFFFF; left-=0xFFFFFF; } PtrSize_t pkt2; pkt2.size=left; pkt2.ptr=l_alloc(pkt2.size); memcpy((char *)pkt2.ptr+sizeof(mysql_hdr), (char *)pkt.ptr+sizeof(mysql_hdr)+copied, left-sizeof(mysql_hdr)); mysql_hdr myhdr; myhdr.pkt_id=pkt_sid; myhdr.pkt_length=left-sizeof(mysql_hdr); memcpy(pkt2.ptr, &myhdr, sizeof(mysql_hdr)); // we are writing a large packet (over 16MB), we assume we are always outside the buffer myrs->PSarrayOUT->add(pkt2.ptr,pkt2.size); } if (len) { *len=pkt.size+(pkt_sid-sequence_id)*sizeof(mysql_hdr); } if (pkt.size >= (0xFFFFFF+sizeof(mysql_hdr))) { l_free(pkt.size,pkt.ptr); } return pkt_sid; } bool MySQL_Protocol::generate_pkt_auth_switch_request(bool send, void **ptr, unsigned int *len) { proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 7, "Generating auth switch request pkt\n"); mysql_hdr myhdr; myhdr.pkt_id=1; myhdr.pkt_length=1 // fe + (strlen("mysql_native_password")+1) + 20 // scramble + 1; // 00 unsigned int size=myhdr.pkt_length+sizeof(mysql_hdr); unsigned char *_ptr=(unsigned char *)malloc(size); memset(_ptr,0,size); memcpy(_ptr, &myhdr, sizeof(mysql_hdr)); int l; l=sizeof(mysql_hdr); _ptr[l]=0xfe; l++; //0xfe memcpy(_ptr+l,"mysql_native_password",strlen("mysql_native_password")); l+=strlen("mysql_native_password"); _ptr[l]=0x00; l++; memcpy(_ptr+l, (*myds)->myconn->scramble_buff+0, 20); l+=20; _ptr[l]=0x00; //l+=1; //0x00 if (send==true) { (*myds)->PSarrayOUT->add((void *)_ptr,size); (*myds)->DSS=STATE_SERVER_HANDSHAKE; (*myds)->sess->status=CONNECTING_CLIENT; } if (len) { *len=size; } if (ptr) { *ptr=(void *)_ptr; } #ifdef DEBUG if (dump_pkt) { __dump_pkt(__func__,_ptr,size); } #endif return true; } //bool MySQL_Protocol::generate_pkt_initial_handshake(MySQL_Data_Stream *myds, bool send, void **ptr, unsigned int *len) { bool MySQL_Protocol::generate_pkt_initial_handshake(bool send, void **ptr, unsigned int *len, uint32_t *_thread_id) { proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 7, "Generating handshake pkt\n"); mysql_hdr myhdr; myhdr.pkt_id=0; myhdr.pkt_length=sizeof(protocol_version) + (strlen(mysql_thread___server_version)+1) + sizeof(uint32_t) // thread_id + 8 // scramble1 + 1 // 0x00 //+ sizeof(glovars.server_capabilities) //+ sizeof(glovars.server_language) //+ sizeof(glovars.server_status) + sizeof(mysql_thread___server_capabilities) + sizeof(mysql_thread___default_charset) + sizeof(server_status) + 3 // unknown stuff + 10 // filler + 12 // scramble2 + 1 // 0x00 + (strlen("mysql_native_password")+1); unsigned int size=myhdr.pkt_length+sizeof(mysql_hdr); //mypkt->data=g_slice_alloc0(mypkt->length); //mypkt->data=l_alloc0(thrLD->sfp, mypkt->length); unsigned char *_ptr=(unsigned char *)malloc(size); memset(_ptr,0,size); memcpy(_ptr, &myhdr, sizeof(mysql_hdr)); //Copy4B(_ptr, &myhdr); int l; l=sizeof(mysql_hdr); //srand(pthread_self()); //uint32_t thread_id=rand()%100000; uint32_t thread_id=__sync_fetch_and_add(&glovars.thread_id,1); if (thread_id==0) { thread_id=__sync_fetch_and_add(&glovars.thread_id,1); // again! } *_thread_id=thread_id; //uint32_t thread_id=pthread_self(); rand_struct rand_st; //randominit(&rand_st,rand(),rand()); rand_st.max_value= 0x3FFFFFFFL; rand_st.max_value_dbl=0x3FFFFFFFL; rand_st.seed1=rand()%rand_st.max_value; rand_st.seed2=rand()%rand_st.max_value; memcpy(_ptr+l, &protocol_version, sizeof(protocol_version)); l+=sizeof(protocol_version); memcpy(_ptr+l, mysql_thread___server_version, strlen(mysql_thread___server_version)); l+=strlen(mysql_thread___server_version)+1; memcpy(_ptr+l, &thread_id, sizeof(uint32_t)); l+=sizeof(uint32_t); //#ifdef MARIADB_BASE_VERSION // proxy_create_random_string(myds->myconn->myconn.scramble_buff+0,8,(struct my_rnd_struct *)&rand_st); //#else proxy_create_random_string((*myds)->myconn->scramble_buff+0,8,(struct rand_struct *)&rand_st); //#endif int i; for (i=0;i<8;i++) { if ((*myds)->myconn->scramble_buff[i]==0) { (*myds)->myconn->scramble_buff[i]='a'; } } memcpy(_ptr+l, (*myds)->myconn->scramble_buff+0, 8); l+=8; _ptr[l]=0x00; l+=1; //0x00 if (mysql_thread___have_compress) { mysql_thread___server_capabilities |= CLIENT_COMPRESS; // FIXME: shouldn't be here //(*myds)->myconn->options.compression_min_length=50; } (*myds)->myconn->options.server_capabilities=mysql_thread___server_capabilities; memcpy(_ptr+l,&mysql_thread___server_capabilities, sizeof(mysql_thread___server_capabilities)); l+=sizeof(mysql_thread___server_capabilities); memcpy(_ptr+l,&mysql_thread___default_charset, sizeof(mysql_thread___default_charset)); l+=sizeof(mysql_thread___default_charset); memcpy(_ptr+l,&server_status, sizeof(server_status)); l+=sizeof(server_status); memcpy(_ptr+l,"\x0f\x80\x15",3); l+=3; for (i=0;i<10; i++) { _ptr[l]=0x00; l++; } //filler //create_random_string(mypkt->data+l,12,(struct my_rnd_struct *)&rand_st); l+=12; //#ifdef MARIADB_BASE_VERSION // proxy_create_random_string(myds->myconn->myconn.scramble_buff+8,12,(struct my_rnd_struct *)&rand_st); //#else proxy_create_random_string((*myds)->myconn->scramble_buff+8,12,(struct rand_struct *)&rand_st); //#endif //create_random_string(scramble_buf+8,12,&rand_st); for (i=8;i<20;i++) { if ((*myds)->myconn->scramble_buff[i]==0) { (*myds)->myconn->scramble_buff[i]='a'; } } memcpy(_ptr+l, (*myds)->myconn->scramble_buff+8, 12); l+=12; l+=1; //0x00 memcpy(_ptr+l,"mysql_native_password",strlen("mysql_native_password")); if (send==true) { (*myds)->PSarrayOUT->add((void *)_ptr,size); (*myds)->DSS=STATE_SERVER_HANDSHAKE; (*myds)->sess->status=CONNECTING_CLIENT; } if (len) { *len=size; } if (ptr) { *ptr=(void *)_ptr; } #ifdef DEBUG if (dump_pkt) { __dump_pkt(__func__,_ptr,size); } #endif return true; } //bool MySQL_Protocol::process_pkt_OK(MySQL_Data_Stream *myds, unsigned char *pkt, unsigned int len) { bool MySQL_Protocol::process_pkt_OK(unsigned char *pkt, unsigned int len) { if (len < 11) return false; mysql_hdr hdr; memcpy(&hdr,pkt,sizeof(mysql_hdr)); pkt += sizeof(mysql_hdr); if (*pkt) return false; if (len!=hdr.pkt_length+sizeof(mysql_hdr)) return false; //MYSQL &myc=(*myds)->myconn->myconn; uint64_t affected_rows; uint64_t insert_id; #ifdef DEBUG uint16_t warns; #endif /* DEBUG */ unsigned char msg[len]; unsigned int p=0; int rc; //field_count = (u_int)*pkt++; pkt++; p++; rc=mysql_decode_length(pkt,&affected_rows); pkt += rc; p+=rc; rc=mysql_decode_length(pkt,&insert_id); pkt += rc; p+=rc; prot_status=CPY2(pkt); pkt+=sizeof(uint16_t); p+=sizeof(uint16_t); #ifdef DEBUG warns=CPY2(pkt); #endif /* DEBUG */ pkt+=sizeof(uint16_t); p+=sizeof(uint16_t); pkt++; p++; if (len>p) { memcpy(msg,pkt,len-p); msg[len-p]=0; } else { msg[0]=0; } proxy_debug(PROXY_DEBUG_MYSQL_PROTOCOL,1,"OK Packet \n", (uint32_t)affected_rows, (uint32_t)insert_id, (uint16_t)prot_status, (uint16_t)warns, msg); return true; } bool MySQL_Protocol::process_pkt_EOF(unsigned char *pkt, unsigned int len) { int ret; mysql_hdr hdr; unsigned char *payload; memcpy(&hdr,pkt,sizeof(mysql_hdr)); payload=pkt+sizeof(mysql_hdr); ret=pkt_end(payload, hdr.pkt_length, this); return ( ret==PKT_PARSED ? true : false ); } //bool MySQL_Protocol::process_pkt_COM_QUERY(MySQL_Data_Stream *myds, unsigned char *pkt, unsigned int len) { bool MySQL_Protocol::process_pkt_COM_QUERY(unsigned char *pkt, unsigned int len) { bool ret=false; unsigned int _len=len-sizeof(mysql_hdr)-1; unsigned char *query=(unsigned char *)l_alloc(_len+1); memcpy(query,pkt+1+sizeof(mysql_hdr),_len); query[_len]=0x00; //printf("%s\n",query); l_free(_len+1,query); ret=true; return ret; } bool MySQL_Protocol::process_pkt_auth_swich_response(unsigned char *pkt, unsigned int len) { bool ret=false; char *password=NULL; if (len!=sizeof(mysql_hdr)+20) { return ret; } mysql_hdr hdr; memcpy(&hdr,pkt,sizeof(mysql_hdr)); int default_hostgroup=-1; bool transaction_persistent; bool _ret_use_ssl=false; unsigned char pass[128]; memset(pass,0,128); pkt+=sizeof(mysql_hdr); memcpy(pass, pkt, 20); char reply[SHA_DIGEST_LENGTH+1]; reply[SHA_DIGEST_LENGTH]='\0'; void *sha1_pass=NULL; password=GloMyAuth->lookup((char *)userinfo->username, USERNAME_FRONTEND, &_ret_use_ssl, &default_hostgroup, NULL, NULL, &transaction_persistent, NULL, NULL, &sha1_pass); // FIXME: add support for default schema and fast forward , issues #255 and #256 if (password==NULL) { ret=false; } else { // if (pass_len==0 && strlen(password)==0) { // ret=true; // } else { if (password[0]!='*') { // clear text password proxy_scramble(reply, (*myds)->myconn->scramble_buff, password); if (memcmp(reply, pass, SHA_DIGEST_LENGTH)==0) { ret=true; } } else { ret=proxy_scramble_sha1((char *)pass,(*myds)->myconn->scramble_buff,password+1, reply); if (ret) { if (sha1_pass==NULL) { // currently proxysql doesn't know any sha1_pass for that specific user, let's set it! GloMyAuth->set_SHA1((char *)userinfo->username, USERNAME_FRONTEND,reply); } if (userinfo->sha1_pass) free(userinfo->sha1_pass); userinfo->sha1_pass=sha1_pass_hex(reply); } } // } // if (_ret_use_ssl==true) { // ret=false; // } } if (sha1_pass) { free(sha1_pass); sha1_pass=NULL; } return ret; } bool MySQL_Protocol::process_pkt_COM_CHANGE_USER(unsigned char *pkt, unsigned int len) { // FIXME: very buggy function, it doesn't perform any real check bool ret=false; int cur=sizeof(mysql_hdr); unsigned char *user=NULL; char *password=NULL; char *db=NULL; mysql_hdr hdr; memcpy(&hdr,pkt,sizeof(mysql_hdr)); int default_hostgroup=-1; bool transaction_persistent; bool _ret_use_ssl=false; cur++; user=pkt+cur; cur+=strlen((const char *)user); cur++; unsigned char pass_len=pkt[cur]; cur++; unsigned char pass[128]; memset(pass,0,128); //pkt+=sizeof(mysql_hdr); memcpy(pass, pkt+cur, pass_len); char reply[SHA_DIGEST_LENGTH+1]; reply[SHA_DIGEST_LENGTH]='\0'; cur+=pass_len; db=(char *)pkt+cur; void *sha1_pass=NULL; password=GloMyAuth->lookup((char *)user, USERNAME_FRONTEND, &_ret_use_ssl, &default_hostgroup, NULL, NULL, &transaction_persistent, NULL, NULL, &sha1_pass); // FIXME: add support for default schema and fast forward, see issue #255 and #256 (*myds)->sess->default_hostgroup=default_hostgroup; (*myds)->sess->transaction_persistent=transaction_persistent; if (password==NULL) { ret=false; } else { if (pass_len==0 && strlen(password)==0) { ret=true; } else { if (password[0]!='*') { // clear text password proxy_scramble(reply, (*myds)->myconn->scramble_buff, password); if (memcmp(reply, pass, SHA_DIGEST_LENGTH)==0) { ret=true; } else { ret=proxy_scramble_sha1((char *)pass,(*myds)->myconn->scramble_buff,password+1, reply); if (ret) { if (sha1_pass==NULL) { // currently proxysql doesn't know any sha1_pass for that specific user, let's set it! GloMyAuth->set_SHA1((char *)user, USERNAME_FRONTEND,reply); } if (userinfo->sha1_pass) free(userinfo->sha1_pass); userinfo->sha1_pass=sha1_pass_hex(reply); } } } } if (_ret_use_ssl==true) { // if we reached here, use_ssl is false , but _ret_use_ssl is true // it means that a client is required to use SSL , but it is not ret=false; } } if (userinfo->username) free(userinfo->username); if (userinfo->password) free(userinfo->password); if (ret==true) { //(*myds)->myconn->options.max_allowed_pkt=max_pkt; (*myds)->DSS=STATE_CLIENT_HANDSHAKE; userinfo->username=strdup((const char *)user); userinfo->password=strdup((const char *)password); if (db) userinfo->set_schemaname(db,strlen(db)); } else { // we always duplicate username and password, or crashes happen userinfo->username=strdup((const char *)user); /*if (pass_len) */ userinfo->password=strdup((const char *)""); } //if (password) free(password); if (password) { free(password); password=NULL; } if (sha1_pass) { free(sha1_pass); sha1_pass=NULL; } return ret; } //bool MySQL_Protocol::process_pkt_handshake_response(MySQL_Data_Stream *myds, unsigned char *pkt, unsigned int len) { bool MySQL_Protocol::process_pkt_handshake_response(unsigned char *pkt, unsigned int len) { bool ret=false; uint8_t charset; uint32_t capabilities; uint32_t max_pkt; uint32_t pass_len; unsigned char *user=NULL; char *db=NULL; unsigned char pass[128]; char *password=NULL; bool use_ssl=false; bool _ret_use_ssl=false; memset(pass,0,128); void *sha1_pass=NULL; #ifdef DEBUG unsigned char *_ptr=pkt; #endif mysql_hdr hdr; memcpy(&hdr,pkt,sizeof(mysql_hdr)); //Copy4B(&hdr,pkt); pkt += sizeof(mysql_hdr); capabilities = CPY4(pkt); pkt += sizeof(uint32_t); max_pkt = CPY4(pkt); pkt += sizeof(uint32_t); charset = *(uint8_t *)pkt; pkt += 24; if (len==sizeof(mysql_hdr)+32) { (*myds)->encrypted=true; use_ssl=true; } else { user = pkt; pkt += strlen((char *)user) + 1; pass_len = (capabilities & CLIENT_SECURE_CONNECTION ? *pkt++ : strlen((char *)pkt)); memcpy(pass, pkt, pass_len); pass[pass_len] = 0; pkt += pass_len; db = (capabilities & CLIENT_CONNECT_WITH_DB ? (char *)pkt : NULL); char reply[SHA_DIGEST_LENGTH+1]; reply[SHA_DIGEST_LENGTH]='\0'; int default_hostgroup=-1; char *default_schema=NULL; bool schema_locked; bool transaction_persistent; bool fast_forward; int max_connections; password=GloMyAuth->lookup((char *)user, USERNAME_FRONTEND, &_ret_use_ssl, &default_hostgroup, &default_schema, &schema_locked, &transaction_persistent, &fast_forward, &max_connections, &sha1_pass); //assert(default_hostgroup>=0); (*myds)->sess->default_hostgroup=default_hostgroup; (*myds)->sess->default_schema=default_schema; // just the pointer is passed (*myds)->sess->schema_locked=schema_locked; (*myds)->sess->transaction_persistent=transaction_persistent; (*myds)->sess->session_fast_forward=fast_forward; (*myds)->sess->user_max_connections=max_connections; if (password==NULL) { // this is a workaround for bug #603 if ((*myds)->sess->admin==true) { if (strcmp((const char *)user,mysql_thread___monitor_username)==0) { proxy_scramble(reply, (*myds)->myconn->scramble_buff, mysql_thread___monitor_password); if (memcmp(reply, pass, SHA_DIGEST_LENGTH)==0) { (*myds)->sess->default_hostgroup=STATS_HOSTGROUP; (*myds)->sess->default_schema=strdup((char *)"main"); // just the pointer is passed (*myds)->sess->schema_locked=false; (*myds)->sess->transaction_persistent=false; (*myds)->sess->session_fast_forward=false; (*myds)->sess->user_max_connections=0; password=l_strdup(mysql_thread___monitor_password); ret=true; } } else { ret=false; } } else { ret=false; } } else { if (pass_len==0 && strlen(password)==0) { ret=true; } else { if (password[0]!='*') { // clear text password proxy_scramble(reply, (*myds)->myconn->scramble_buff, password); if (memcmp(reply, pass, SHA_DIGEST_LENGTH)==0) { ret=true; } } else { ret=proxy_scramble_sha1((char *)pass,(*myds)->myconn->scramble_buff,password+1, reply); if (ret) { if (sha1_pass==NULL) { // currently proxysql doesn't know any sha1_pass for that specific user, let's set it! GloMyAuth->set_SHA1((char *)user, USERNAME_FRONTEND,reply); } if (userinfo->sha1_pass) free(userinfo->sha1_pass); userinfo->sha1_pass=sha1_pass_hex(reply); } } } } if (_ret_use_ssl==true) { // if we reached here, use_ssl is false , but _ret_use_ssl is true // it means that a client is required to use SSL , but it is not ret=false; } } proxy_debug(PROXY_DEBUG_MYSQL_PROTOCOL,1,"Handshake (%s auth) , capabilities:%u char:%u, use_ssl:%s\n", (capabilities & CLIENT_SECURE_CONNECTION ? "new" : "old"), user, password, pass, db, max_pkt, capabilities, charset, ((*myds)->encrypted ? "yes" : "no")); assert(sess); assert(sess->client_myds); MySQL_Connection *myconn=sess->client_myds->myconn; assert(myconn); myconn->set_charset(charset); // enable compression if (capabilities & CLIENT_COMPRESS) { if (myconn->options.server_capabilities & CLIENT_COMPRESS) { myconn->options.compression_min_length=50; //myconn->set_status_compression(true); // don't enable this here. It needs to be enabled after the OK is sent } } #ifdef DEBUG if (dump_pkt) { __dump_pkt(__func__,_ptr,len); } #endif if (use_ssl) { ret=true; goto __exit_process_pkt_handshake_response; } if (ret==true) { (*myds)->myconn->options.max_allowed_pkt=max_pkt; (*myds)->DSS=STATE_CLIENT_HANDSHAKE; userinfo->username=strdup((const char *)user); userinfo->password=strdup((const char *)password); if (db) userinfo->set_schemaname(db,strlen(db)); } else { // we always duplicate username and password, or crashes happen userinfo->username=strdup((const char *)user); if (pass_len) userinfo->password=strdup((const char *)""); } __exit_process_pkt_handshake_response: //if (password) free(password); if (password) { free(password); password=NULL; } if (sha1_pass) { free(sha1_pass); sha1_pass=NULL; } //l_free(len,pkt); return ret; } void * MySQL_Protocol::Query_String_to_packet(uint8_t sid, std::string *s, unsigned int *l) { mysql_hdr hdr; hdr.pkt_id=sid; hdr.pkt_length=1+s->length(); *l=hdr.pkt_length+sizeof(mysql_hdr); void *pkt=malloc(*l); memcpy(pkt,&hdr,sizeof(mysql_hdr)); uint8_t c=_MYSQL_COM_QUERY; memcpy((char *)pkt+4,&c,1); memcpy((char *)pkt+5,s->c_str(),s->length()); return pkt; } // get_binds_from_pkt() process an STMT_EXECUTE packet, and extract binds value // and optionally metadata // if stmt_meta is NULL, it means it is the first time that the client run // STMT_EXECUTE and therefore stmt_meta needs to be build // // returns stmt_meta, or a new one // See https://dev.mysql.com/doc/internals/en/com-stmt-execute.html for reference stmt_execute_metadata_t * MySQL_Protocol::get_binds_from_pkt(void *ptr, unsigned int size, MySQL_STMT_Global_info *stmt_info, stmt_execute_metadata_t **stmt_meta) { stmt_execute_metadata_t *ret=NULL; //return NULL in case of failure if (size<14) { // some error! return ret; } uint16_t num_params=stmt_info->num_params; if (num_params==2) { PROXY_TRACE(); } char *p=(char *)ptr+5; if (*stmt_meta) { // this PS was executed at least once, and we already have metadata ret=*stmt_meta; } else { // this is the first time that this PS is executed ret= new stmt_execute_metadata_t(); } if (*stmt_meta==NULL) { memcpy(&ret->stmt_id,p,4); // stmt-id } p+=4; // stmt-id memcpy(&ret->flags,p,1); p+=1; // flags p+=4; // iteration-count ret->num_params=num_params; // ret->binds=NULL; // ret->is_nulls=NULL; // ret->lengths=NULL; // we keep a pointer to the packet // this is extremely important because: // * binds[X].buffer does NOT point to a new allocated buffer // * binds[X].buffer points to offset inside the original packet // FIXME: there is still no free for pkt, so that will be a memory leak that needs to be fixed ret->pkt=ptr; uint8_t new_params_bound_flag; if (num_params) { uint16_t i; size_t null_bitmap_length=(num_params+7)/8; if (size < (14+1+null_bitmap_length)) { // some data missing? delete ret; return NULL; } memcpy(&new_params_bound_flag,p+null_bitmap_length,1); if (new_params_bound_flag==1) { // something wrong // delete ret; // return NULL; // the client is sending us the params type. We ignore it //p+=(2*num_params); } uint8_t *null_bitmap=NULL; null_bitmap=(uint8_t *)malloc(null_bitmap_length); memcpy(null_bitmap,p,null_bitmap_length); p+=null_bitmap_length; p+=1; // new_params_bound_flag MYSQL_BIND *binds=NULL; my_bool *is_nulls=NULL; unsigned long *lengths=NULL; // now we create bind structures only if needed if (*stmt_meta==NULL) { binds=(MYSQL_BIND *)malloc(sizeof(MYSQL_BIND)*num_params); memset(binds,0,sizeof(MYSQL_BIND)*num_params); ret->binds=binds; is_nulls=(my_bool *)malloc(sizeof(my_bool)*num_params); ret->is_nulls=is_nulls; lengths=(unsigned long *)malloc(sizeof(unsigned long)*num_params); ret->lengths=lengths; } else { // if STMT_EXECUTE was already executed once binds=ret->binds; is_nulls=ret->is_nulls; lengths=ret->lengths; } // process packet and set NULLs for (i=0;i> idx; is_nulls[i]=is_null; binds[i].is_null=&is_nulls[i]; } free(null_bitmap); // we are done with it if (new_params_bound_flag) { // the client is rebinding the parameters // the client is sending again the type of each parameter for (i=0;i= 32768) { // is_unsigned bit buffer_type-=32768; binds[i].is_unsigned=1; } binds[i].buffer_type=(enum enum_field_types)buffer_type; p+=2; // set length, defaults to 0 // for parameters with not fixed length, that will be assigned later lengths[i]=0; // unsigned long l=0; // uint8_t ll=mysql_decode_length((unsigned char *)p,&l); // lengths[i]=l; // p+=ll; binds[i].length=&lengths[i]; //stmt_info->params[i]->buffer_type=binds[i].buffer_type; } } else { //new_params_bound_flag==0 // the client is NOT rebinding the parameters // the client is NOT sending again the type of each parameter // we should ALREADY know the type of each parameter // nothing should be done here // for (i=0;iparams[i]->buffer_type; // lengths[i]=0; // binds[i].length=&lengths[i]; // } } for (i=0;i1) { PROXY_TRACE(); } p+=l; binds[i].buffer=p; p+=len; lengths[i]=len; } break; default: assert(0); break; } } } /* #ifdef DEBUG // debug fprintf(stderr,"STMT_EXEC: %d\n",ret->stmt_id); if (num_params==2) { PROXY_TRACE(); } for (int i=0;ibinds[i].is_null), ret->binds[i].buffer_type); } #endif */ if (ret) ret->size=size; return ret; } MySQL_ResultSet::MySQL_ResultSet(MySQL_Protocol *_myprot, MYSQL_RES *_res, MYSQL *_my, MYSQL_STMT *_stmt) { transfer_started=false; resultset_completed=false; myprot=_myprot; mysql=_my; buffer=NULL; if (_stmt==NULL) { // we allocate this buffer only for not prepared statements buffer=(unsigned char *)malloc(RESULTSET_BUFLEN); } buffer_used=0; myds=NULL; sid=0; PSarrayOUT = NULL; if (myprot) { // if myprot = NULL , this is a mirror myds=myprot->get_myds(); sid=myds->pkt_sid+1; PSarrayOUT = new PtrSizeArray(8); } result=_res; resultset_size=0; num_rows=0; num_fields=mysql_field_count(mysql); PtrSize_t pkt; // immediately generate the first set of packets // columns count if (myprot==NULL) { return; // this is a mirror } myprot->generate_pkt_column_count(false,&pkt.ptr,&pkt.size,sid,num_fields); sid++; PSarrayOUT->add(pkt.ptr,pkt.size); resultset_size+=pkt.size; // columns description for (unsigned int i=0; igenerate_pkt_field(false,&pkt.ptr,&pkt.size,sid,field->db,field->table,field->org_table,field->name,field->org_name,field->charsetnr,field->length,field->type,field->flags,field->decimals,false,0,NULL); PSarrayOUT->add(pkt.ptr,pkt.size); resultset_size+=pkt.size; sid++; } // first EOF unsigned int nTrx=myds->sess->NumActiveTransactions(); uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); if (myds->sess->autocommit) setStatus += SERVER_STATUS_AUTOCOMMIT; myprot->generate_pkt_EOF(false,&pkt.ptr,&pkt.size,sid,0,mysql->server_status|setStatus); sid++; PSarrayOUT->add(pkt.ptr,pkt.size); resultset_size+=pkt.size; if (_stmt) { // binary protocol , we also assume we have ALL the resultset //MYSQL_RES * prepare_meta_result = mysql_stmt_result_metadata(_stmt); // int column_count=mysql_num_fields(prepare_meta_result); // MYSQL_BIND *binds=(MYSQL_BIND *)malloc(sizeof(MYSQL_BIND)*column_count); // mysql_stmt_bind_result(_stmt, binds); //fprintf(stdout, "Fetching results ...\n"); // int row_count=0; // while (!mysql_stmt_fetch(_stmt)) { // row_count++; // fprintf(stdout, " row %d\n", row_count); // } // free (binds); unsigned long long total_size=0; MYSQL_ROWS *r=_stmt->result.data; if (r) { total_size+=r->length; if (r->length > 0xFFFFFF) { total_size+=(r->length / 0xFFFFFF) * sizeof(mysql_hdr); } total_size+=sizeof(mysql_hdr); while(r->next) { r=r->next; total_size+=r->length; if (r->length > 0xFFFFFF) { total_size+=(r->length / 0xFFFFFF) * sizeof(mysql_hdr); } total_size+=sizeof(mysql_hdr); } PtrSize_t pkt; pkt.size=total_size; pkt.ptr=malloc(pkt.size); total_size=0; r=_stmt->result.data; add_row2(r,(unsigned char *)pkt.ptr); total_size+=r->length; if (r->length > 0xFFFFFF) { total_size+=(r->length / 0xFFFFFF) * sizeof(mysql_hdr); } total_size+=sizeof(mysql_hdr); while(r->next) { r=r->next; add_row2(r,(unsigned char *)pkt.ptr+total_size); total_size+=r->length; if (r->length > 0xFFFFFF) { total_size+=(r->length / 0xFFFFFF) * sizeof(mysql_hdr); } total_size+=sizeof(mysql_hdr); } PSarrayOUT->add(pkt.ptr,pkt.size); resultset_size+=pkt.size; } add_eof(); } } MySQL_ResultSet::~MySQL_ResultSet() { PtrSize_t pkt; if (PSarrayOUT) { while (PSarrayOUT->len) { PSarrayOUT->remove_index_fast(0,&pkt); l_free(pkt.size, pkt.ptr); } delete PSarrayOUT; } if (buffer) { free(buffer); buffer=NULL; } if (myds) myds->pkt_sid=sid-1; } unsigned int MySQL_ResultSet::add_row(MYSQL_ROW row) { unsigned long *lengths=mysql_fetch_lengths(result); unsigned int pkt_length=0; if (myprot) { sid=myprot->generate_pkt_row3(this, &pkt_length, sid, num_fields, lengths, row); } else { unsigned int col=0; for (col=0; collength; uint8_t pkt_sid=sid; if (length < (0xFFFFFF+sizeof(mysql_hdr))) { mysql_hdr myhdr; myhdr.pkt_length=length; myhdr.pkt_id=pkt_sid; memcpy(offset, &myhdr, sizeof(mysql_hdr)); memcpy(offset+sizeof(mysql_hdr), row->data, row->length); pkt_sid++; } else { unsigned int left=length; unsigned int copied=0; while (left>=0xFFFFFF) { mysql_hdr myhdr; myhdr.pkt_length=0xFFFFFF; myhdr.pkt_id=pkt_sid; pkt_sid++; memcpy(offset, &myhdr, sizeof(mysql_hdr)); offset+=sizeof(mysql_hdr); memcpy(offset, row->data+copied, myhdr.pkt_length); offset+=0xFFFFFF; // we are writing a large packet (over 16MB), we assume we are always outside the buffer copied+=0xFFFFFF; left-=0xFFFFFF; } mysql_hdr myhdr; myhdr.pkt_length=left; myhdr.pkt_id=pkt_sid; pkt_sid++; memcpy(offset, &myhdr, sizeof(mysql_hdr)); offset+=sizeof(mysql_hdr); memcpy(offset, row->data+copied, myhdr.pkt_length); // we are writing a large packet (over 16MB), we assume we are always outside the buffer } sid=pkt_sid; return length; } void MySQL_ResultSet::add_eof() { PtrSize_t pkt; if (myprot) { buffer_to_PSarrayOut(); unsigned int nTrx=myds->sess->NumActiveTransactions(); uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); if (myds->sess->autocommit) setStatus += SERVER_STATUS_AUTOCOMMIT; myprot->generate_pkt_EOF(false,&pkt.ptr,&pkt.size,sid,0,mysql->server_status|setStatus); PSarrayOUT->add(pkt.ptr,pkt.size); sid++; resultset_size+=pkt.size; } resultset_completed=true; } bool MySQL_ResultSet::get_resultset(PtrSizeArray *PSarrayFinal) { transfer_started=true; if (myprot) { PSarrayFinal->copy_add(PSarrayOUT,0,PSarrayOUT->len); while (PSarrayOUT->len) PSarrayOUT->remove_index(PSarrayOUT->len-1,NULL); } return resultset_completed; } void MySQL_ResultSet::buffer_to_PSarrayOut() { if (buffer_used==0) return; // exit immediately if the buffer is empty if (buffer_used < RESULTSET_BUFLEN/2) { buffer=(unsigned char *)realloc(buffer,buffer_used); } PSarrayOUT->add(buffer,buffer_used); buffer=(unsigned char *)malloc(RESULTSET_BUFLEN); buffer_used=0; } unsigned long long MySQL_ResultSet::current_size() { unsigned long long intsize=0; intsize+=sizeof(MySQL_ResultSet); intsize+=RESULTSET_BUFLEN; // size of buffer if (PSarrayOUT==NULL) // see bug #699 return intsize; intsize+=sizeof(PtrSizeArray); intsize+=(PSarrayOUT->size*sizeof(PtrSize_t *)); unsigned int i; for (i=0; ilen; i++) { PtrSize_t *pkt=PSarrayOUT->index(i); if (pkt->size>RESULTSET_BUFLEN) { intsize+=pkt->size; } else { intsize+=RESULTSET_BUFLEN; } } return intsize; }