From dad6f0f344617be1f965b89c1b348aa1ae113ac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Sun, 21 Apr 2024 14:10:08 +0000 Subject: [PATCH] Split of MySQL_Protocol.cpp into multiple files --- include/MySQL_Prepared_Stmt_info.h | 25 + include/MySQL_Protocol.h | 11 +- include/MySQL_encode.h | 22 + lib/Makefile | 1 + lib/MySQL_Protocol.cpp | 753 +---------------------------- lib/MySQL_ResultSet.cpp | 549 +++++++++++++++++++++ lib/MySQL_encode.cpp | 239 +++++++++ 7 files changed, 840 insertions(+), 760 deletions(-) create mode 100644 include/MySQL_Prepared_Stmt_info.h create mode 100644 include/MySQL_encode.h create mode 100644 lib/MySQL_ResultSet.cpp create mode 100644 lib/MySQL_encode.cpp diff --git a/include/MySQL_Prepared_Stmt_info.h b/include/MySQL_Prepared_Stmt_info.h new file mode 100644 index 0000000000..ad221720a5 --- /dev/null +++ b/include/MySQL_Prepared_Stmt_info.h @@ -0,0 +1,25 @@ +#ifndef CLASS_MySQL_Prepared_Stmt_info_H +#define CLASS_MySQL_Prepared_Stmt_info_H +class MySQL_Prepared_Stmt_info { + public: + uint32_t statement_id; + uint16_t num_columns; + uint16_t num_params; + uint16_t warning_count; + uint16_t pending_num_columns; + uint16_t pending_num_params; + 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); + pending_num_columns=num_columns; + pending_num_params=num_params; + } +}; +#endif // CLASS_MySQL_Prepared_Stmt_info_H diff --git a/include/MySQL_Protocol.h b/include/MySQL_Protocol.h index 0d30b223bf..a95251f8bf 100644 --- a/include/MySQL_Protocol.h +++ b/include/MySQL_Protocol.h @@ -4,6 +4,7 @@ #include "proxysql.h" #include "cpp.h" #include "MySQL_Variables.h" +#include "MySQL_Prepared_Stmt_info.h" #define RESULTSET_BUFLEN 16300 @@ -70,16 +71,6 @@ class MySQL_ResultSet { unsigned long long current_size(); }; -class MySQL_Prepared_Stmt_info { - public: - uint32_t statement_id; - uint16_t num_columns; - uint16_t num_params; - uint16_t warning_count; - uint16_t pending_num_columns; - uint16_t pending_num_params; - MySQL_Prepared_Stmt_info(unsigned char *, unsigned int); -}; uint8_t mysql_decode_length(unsigned char *ptr, uint64_t *len); diff --git a/include/MySQL_encode.h b/include/MySQL_encode.h new file mode 100644 index 0000000000..27273d6127 --- /dev/null +++ b/include/MySQL_encode.h @@ -0,0 +1,22 @@ +#ifndef CLASS_MySQL_encode_H +#define CLASS_MySQL_encode_H +#ifdef DEBUG +void __dump_pkt(const char *func, unsigned char *_ptr, unsigned int len); +#endif // DEBUG +char *sha1_pass_hex(char *sha1_pass); +double proxy_my_rnd(struct rand_struct *rand_st); +void proxy_create_random_string(char *_to, uint length, struct rand_struct *rand_st); +int write_encoded_length(unsigned char *p, uint64_t val, uint8_t len, char prefix); +int write_encoded_length_and_string(unsigned char *p, uint64_t val, uint8_t len, char prefix, char *string); +void proxy_compute_sha1_hash_multi(uint8_t *digest, const char *buf1, int len1, const char *buf2, int len2); +void proxy_compute_sha1_hash(uint8_t *digest, const char *buf, int len); +void proxy_compute_two_stage_sha1_hash(const char *password, size_t pass_len, uint8_t *hash_stage1, uint8_t *hash_stage2); +void proxy_my_crypt(char *to, const uint8_t *s1, const uint8_t *s2, uint len); +unsigned char decode_char(char x); +void unhex_pass(uint8_t *out, const char *in); +void proxy_scramble(char *to, const char *message, const char *password); +bool proxy_scramble_sha1(char *pass_reply, const char *message, const char *sha1_sha1_pass, char *sha1_pass); +unsigned int CPY3(unsigned char *ptr); +uint64_t CPY8(unsigned char *ptr); +uint8_t mysql_encode_length(uint64_t len, char *hd); +#endif // CLASS_MySQL_encode_H diff --git a/lib/Makefile b/lib/Makefile index 8ab3daea5f..58b17c29eb 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -129,6 +129,7 @@ _OBJ_CXX := ProxySQL_GloVars.oo network.oo debug.oo configfile.oo Query_Cache.oo sha256crypt.oo \ QP_rule_text.oo QP_query_digest_stats.oo \ GTID_Server_Data.oo MyHGC.oo MySrvConnList.oo MySrvList.oo MySrvC.oo \ + MySQL_encode.oo MySQL_ResultSet.oo \ proxysql_find_charset.oo ProxySQL_Poll.oo OBJ_CXX := $(patsubst %,$(ODIR)/%,$(_OBJ_CXX)) HEADERS := ../include/*.h ../include/*.hpp diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp index 527c87bc93..cba71c6a8a 100644 --- a/lib/MySQL_Protocol.cpp +++ b/lib/MySQL_Protocol.cpp @@ -26,8 +26,6 @@ extern ClickHouse_Authentication *GloClickHouseAuth; #undef max_allowed_packet #endif -//#define RESULTSET_BUFLEN 16300 - #ifndef CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA #define CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA 0x00200000 #endif @@ -43,240 +41,7 @@ static const char *plugins[3] = { "caching_sha2_password", }; -#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 = 0; - 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) { - unsigned char * to = (unsigned char *)_to; - int rc = 0; - uint i; - rc = RAND_bytes((unsigned char *)to,length); -#ifdef DEBUG - if (rc==1) { - // For code coverage (to test the following code and other function) - // in DEBUG mode we pretend that RAND_bytes() fails 1% of the time - if(rand()%100==0) { - rc=0; - } - } -#endif // DEBUG - if (rc!=1) { - for (i=0; i 127) { - *to -= 128; - } - if (*to == 0) { - *to = 'a'; - } - to++; - } - } - *to= '\0'; -} - -static inline int write_encoded_length(unsigned char *p, uint64_t val, uint8_t len, char prefix) { - if (len==1) { - *p=(char)val; - return 1; - } - *p=prefix; - p++; - memcpy(p,&val,len-1); - return len; -} - -static inline int write_encoded_length_and_string(unsigned char *p, uint64_t val, uint8_t len, char prefix, char *string) { - int l=write_encoded_length(p,val,len,prefix); - if (val) { - memcpy(p+l,string,val); - } - return l+val; -} - -void proxy_compute_sha1_hash_multi(uint8_t *digest, const char *buf1, int len1, const char *buf2, int len2) { - PROXY_TRACE(); - const EVP_MD *evp_digest = EVP_get_digestbyname("sha1"); - assert(evp_digest != NULL); - EVP_MD_CTX *ctx = EVP_MD_CTX_new(); - EVP_MD_CTX_init(ctx); - EVP_DigestInit_ex(ctx, evp_digest, NULL); - EVP_DigestUpdate(ctx, buf1, len1); - EVP_DigestUpdate(ctx, buf2, len2); - unsigned int olen = 0; - EVP_DigestFinal(ctx, digest, &olen); - EVP_MD_CTX_free(ctx); -} - -void proxy_compute_sha1_hash(uint8_t *digest, const char *buf, int len) { - PROXY_TRACE(); - const EVP_MD *evp_digest = EVP_get_digestbyname("sha1"); - assert(evp_digest != NULL); - EVP_MD_CTX *ctx = EVP_MD_CTX_new(); - EVP_MD_CTX_init(ctx); - EVP_DigestInit_ex(ctx, evp_digest, NULL); - EVP_DigestUpdate(ctx, buf, len); - unsigned int olen = 0; - EVP_DigestFinal(ctx, digest, &olen); - EVP_MD_CTX_free(ctx); -} - -void proxy_compute_two_stage_sha1_hash(const char *password, size_t pass_len, uint8_t *hash_stage1, uint8_t *hash_stage2) { - proxy_compute_sha1_hash(hash_stage1, password, pass_len); - proxy_compute_sha1_hash(hash_stage2, (const char *) hash_stage1, SHA_DIGEST_LENGTH); -} - -void proxy_my_crypt(char *to, const uint8_t *s1, const uint8_t *s2, uint len) { - const uint8_t *s1_end= s1 + len; - while (s1 < s1_end) - *to++= *s1++ ^ *s2++; -} - -unsigned char decode_char(char x) { - if (x >= '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;ibuffer + myrs->buffer_used; myrs->buffer_used += size; } - memcpy(_ptr, &myhdr, sizeof(mysql_hdr)); - int l=sizeof(mysql_hdr); + memcpy(_ptr, &myhdr, sizeof(mysql_hdr)); + int l=sizeof(mysql_hdr); _ptr[l]=0xfe; l++; int16_t internal_status = status; if (sess) { @@ -2977,501 +2725,6 @@ bool MySQL_Protocol::generate_COM_QUERY_from_COM_FIELD_LIST(PtrSize_t *pkt) { return true; } -MySQL_ResultSet::MySQL_ResultSet() { - buffer = NULL; - //reset_pid = true; -} - -void MySQL_ResultSet::buffer_init(MySQL_Protocol* myproto) { - if (buffer==NULL) { - buffer=(unsigned char *)malloc(RESULTSET_BUFLEN); - } - - buffer_used=0; - myprot = myproto; -} - -void MySQL_ResultSet::init(MySQL_Protocol *_myprot, MYSQL_RES *_res, MYSQL *_my, MYSQL_STMT *_stmt) { - PROXY_TRACE2(); - transfer_started=false; - resultset_completed=false; - myprot=_myprot; - mysql=_my; - stmt=_stmt; - if (buffer==NULL) { - //if (_stmt==NULL) { // we allocate this buffer only for not prepared statements - // removing the previous assumption. We allocate this buffer also for prepared statements - buffer=(unsigned char *)malloc(RESULTSET_BUFLEN); - //} - } - buffer_used=0; - myds=NULL; - if (myprot) { // if myprot = NULL , this is a mirror - myds=myprot->get_myds(); - } - //if (reset_pid==true) { - sid=0; - //PSarrayOUT = NULL; - if (myprot) { // if myprot = NULL , this is a mirror - sid=myds->pkt_sid+1; - //PSarrayOUT = new PtrSizeArray(8); - } - //} - //reset_pid=true; - 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 - } - MySQL_Data_Stream * c_myds = *(myprot->myds); - if (c_myds->com_field_list==false) { - myprot->generate_pkt_column_count(false,&pkt.ptr,&pkt.size,sid,num_fields,this); - sid++; - resultset_size+=pkt.size; - } - // columns description - for (unsigned int i=0; icom_field_list==false) { - // we are replacing generate_pkt_field() with a more efficient version - //myprot->generate_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,this); - myprot->generate_pkt_field2(&pkt.ptr,&pkt.size,sid,field,this); - resultset_size+=pkt.size; - sid++; - } else { - if (c_myds->com_field_wild==NULL || mywildcmp(c_myds->com_field_wild,field->name)) { - myprot->generate_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,true,4,(char *)"null",this); - resultset_size+=pkt.size; - sid++; - } - } - } - - deprecate_eof_active = c_myds->myconn && (c_myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF); - - // 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; - setStatus |= ( mysql->server_status & ~SERVER_STATUS_AUTOCOMMIT ); // get flags from server_status but ignore autocommit - setStatus = setStatus & ~SERVER_STATUS_CURSOR_EXISTS; // Do not send cursor #1128 -// if (_stmt) { // binary protocol , we also assume we have ALL the resultset -// 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; - //} else { - if (RESULTSET_BUFLEN <= (buffer_used + 9)) { - buffer_to_PSarrayOut(); - } - if (!deprecate_eof_active && myds->com_field_list==false) { - // up to 2.2.0 we used to add an EOF here. - // due to bug #3547 we move the logic into add_eof() that can now handle also prepared statements - PROXY_TRACE2(); - // if the backend server has CLIENT_DEPRECATE_EOF enabled, and the client does not support - // CLIENT_DEPRECATE_EOF, warning_count will be excluded from the intermediate EOF packet - add_eof((mysql->server_capabilities & CLIENT_DEPRECATE_EOF)); - } -} - - -// due to bug #3547 , in case of an error we remove the EOF -// and replace it with an ERR -// note that EOF is added on a packet on its own, instead of using a buffer, -// so that can be removed using remove_last_eof() -void MySQL_ResultSet::remove_last_eof() { - PROXY_TRACE2(); - PtrSize_t pkt; - if (PSarrayOUT.len) { - unsigned int l = PSarrayOUT.len-1; - PtrSize_t * pktp = PSarrayOUT.index(l); - if (pktp->size == 9) { - PROXY_TRACE2(); - PSarrayOUT.remove_index(l,&pkt); - l_free(pkt.size, pkt.ptr); - sid--; - } - } -} - -void MySQL_ResultSet::init_with_stmt(MySQL_Connection *myconn) { - PROXY_TRACE2(); - assert(stmt); - MYSQL_STMT *_stmt = stmt; - MySQL_Data_Stream * c_myds = *(myprot->myds); - buffer_to_PSarrayOut(); - 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); - } -#define MAXBUFFSTMT 12*1024*1024 // hardcoded to LESS *very important* than 16MB - if (total_size < MAXBUFFSTMT) { - 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); - if (resultset_size/0xFFFFFFF != ((resultset_size+pkt.size)/0xFFFFFFF)) { - // generate a heartbeat every 256MB - unsigned long long curtime=monotonic_time(); - c_myds->sess->thread->atomic_curtime=curtime; - } - resultset_size+=pkt.size; - } else { // this code fixes a bug: resultset larger than 4GB would cause a crash - unsigned long long tmp_pkt_size = 0; - r=_stmt->result.data; - MYSQL_ROWS * r2 = NULL; - while (r) { - if (r->length >= MAXBUFFSTMT) { - // we have a large row - // we will send just that - tmp_pkt_size = r->length; - if (r->length > 0xFFFFFF) { - tmp_pkt_size+=(r->length / 0xFFFFFF) * sizeof(mysql_hdr); - } - tmp_pkt_size += sizeof(mysql_hdr); - PtrSize_t pkt; - pkt.size=tmp_pkt_size; - pkt.ptr=malloc(pkt.size); - add_row2(r,(unsigned char *)pkt.ptr); - PSarrayOUT.add(pkt.ptr,pkt.size); - if (resultset_size/0xFFFFFFF != ((resultset_size+pkt.size)/0xFFFFFFF)) { - // generate a heartbeat every 256MB - unsigned long long curtime=monotonic_time(); - c_myds->sess->thread->atomic_curtime=curtime; - } - resultset_size+=pkt.size; - r=r->next; // next row - } else { // we have small row - r2 = r; - tmp_pkt_size = 0; - unsigned int a = 0; - while (r && (tmp_pkt_size + r->length) < MAXBUFFSTMT) { - a++; - tmp_pkt_size += r->length; - tmp_pkt_size += sizeof(mysql_hdr); - //if (r->next) { - r = r->next; - //} - } - r = r2; // we reset it back to the beginning - if (tmp_pkt_size) { // this should always be true - unsigned long long tmp2 = 0; - PtrSize_t pkt; - pkt.size=tmp_pkt_size; - pkt.ptr=malloc(pkt.size); - while (tmp2 < tmp_pkt_size) { - add_row2(r,(unsigned char *)pkt.ptr+tmp2); - tmp2 += r->length; - tmp2 += sizeof(mysql_hdr); - r = r->next; - } - PSarrayOUT.add(pkt.ptr,pkt.size); - if (resultset_size/0xFFFFFFF != ((resultset_size+pkt.size)/0xFFFFFFF)) { - // generate a heartbeat every 256MB - unsigned long long curtime=monotonic_time(); - c_myds->sess->thread->atomic_curtime=curtime; - } - resultset_size+=pkt.size; - } - } - } - } - } - // up to 2.2.0 we were always adding an EOF - // due to bug #3547 , in case of an error we remove the EOF - // and replace it with an ERR - // note that EOF is added on a packet on its own, instead of using a buffer, - // so that can be removed - // - // NOTE: After 2.4.5 previous behavior is modified in favor of the following: - // - // When CLIENT_DEPRECATE_EOF two EOF packets are two be expected in the response: - // 1. After the columns definitions (This is added directly by 'MySQL_ResultSet::init'). - // 2. After the rows values, this can either be and EOF packet or a ERR packet in case of error. - // - // First EOF packet isn't optional, and it's just the second the one that is optionaly either an EOF - // or an ERR packet. The following code adds either the final EOF or ERR packet. This is equally valid - // for when CLIENT_DEPRECATE_EOF is enabled or not. If CLIENT_DEPRECATE_EOF is: - // * DISABLED: The behavior is as described before. - // * ENABLED: Code is identical for this case. The initial EOF packet is conditionally added by - // 'MySQL_ResultSet::init', thus, this packet should not be present if not needed at this point. - // In case of error an ERR packet needs to be added, otherwise `add_eof` handles the generation of - // the equivalent OK packet replacing the final EOF packet. - int myerr = mysql_stmt_errno(_stmt); - if (myerr) { - PROXY_TRACE2(); - add_err(myconn->myds); - } else { - PROXY_TRACE2(); - 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; -} - -// this function is used for binary protocol -// maybe later on can be adapted for text protocol too -unsigned int MySQL_ResultSet::add_row(MYSQL_ROWS *rows) { - unsigned int pkt_length=0; - MYSQL_ROW row = rows->data; - unsigned long row_length = rows->length; - // we call generate_pkt_row3 passing row_length - sid=myprot->generate_pkt_row3(this, &pkt_length, sid, 0, NULL, row, row_length); - sid++; - resultset_size+=pkt_length; - num_rows++; - return pkt_length; -} - - -// this function is used for text protocol -unsigned int MySQL_ResultSet::add_row(MYSQL_ROW row) { - unsigned long *lengths=mysql_fetch_lengths(result); - unsigned int pkt_length=0; - if (myprot) { - // we call generate_pkt_row3 without passing row_length - sid=myprot->generate_pkt_row3(this, &pkt_length, sid, num_fields, lengths, row, 0); - } else { - unsigned int col=0; - for (col=0; collength; - num_rows++; - 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); - char *o = (char *) row->data; - o += copied; - memcpy(offset, o, 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); - char *o = (char *) row->data; - o += copied; - memcpy(offset, o, 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(bool suppress_warning_count) { - if (myprot) { - unsigned int nTrx=myds->sess->NumActiveTransactions(); - uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); - if (myds->sess->autocommit) setStatus += SERVER_STATUS_AUTOCOMMIT; - setStatus |= ( mysql->server_status & ~SERVER_STATUS_AUTOCOMMIT ); // get flags from server_status but ignore autocommit - setStatus = setStatus & ~SERVER_STATUS_CURSOR_EXISTS; // Do not send cursor #1128 - //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; - - // Note: warnings count will only be sent to the client if mysql-query_digests is enabled - const MySQL_Backend* _mybe = myds->sess->mybe; - const MySQL_Data_Stream* _server_myds = (_mybe && _mybe->server_myds) ? _mybe->server_myds : nullptr; - const MySQL_Connection* _myconn = (_server_myds && _server_myds->myds_type == MYDS_BACKEND && _server_myds->myconn) ? - _server_myds->myconn : nullptr; - const unsigned int warning_count = (_myconn && suppress_warning_count == false) ? _myconn->warning_count : 0; - if (deprecate_eof_active) { - PtrSize_t pkt; - buffer_to_PSarrayOut(); - myprot->generate_pkt_OK(false, &pkt.ptr, &pkt.size, sid, 0, 0, setStatus, warning_count, NULL, true); - PSarrayOUT.add(pkt.ptr, pkt.size); - resultset_size += pkt.size; - } - else { - // due to bug #3547 , in case of an error we remove the EOF - // and replace it with an ERR - // note that EOF is added on a packet on its own, instead of using a buffer, - // so that can be removed using remove_last_eof() - buffer_to_PSarrayOut(); - myprot->generate_pkt_EOF(false, NULL, NULL, sid, warning_count, setStatus, this); - resultset_size += 9; - buffer_to_PSarrayOut(); - } - sid++; - } - resultset_completed=true; -} - -void MySQL_ResultSet::add_err(MySQL_Data_Stream *_myds) { - PtrSize_t pkt; - if (myprot) { - MYSQL *_mysql=_myds->myconn->mysql; - buffer_to_PSarrayOut(); - char sqlstate[10]; - sprintf(sqlstate,"%s",mysql_sqlstate(_mysql)); - if (_myds && _myds->killed_at) { // see case #750 - if (_myds->kill_type == 0) { - myprot->generate_pkt_ERR(false,&pkt.ptr,&pkt.size,sid,1907,sqlstate,(char *)"Query execution was interrupted, query_timeout exceeded"); - MyHGM->p_update_mysql_error_counter(p_mysql_error_type::proxysql, _myds->myconn->parent->myhgc->hid, _myds->myconn->parent->address, _myds->myconn->parent->port, 1907); - } else { - myprot->generate_pkt_ERR(false,&pkt.ptr,&pkt.size,sid,1317,sqlstate,(char *)"Query execution was interrupted"); - MyHGM->p_update_mysql_error_counter(p_mysql_error_type::proxysql, _myds->myconn->parent->myhgc->hid, _myds->myconn->parent->address, _myds->myconn->parent->port, 1317); - } - } else { - int myerr = 0; - // the error code is returned from: - // - mysql_stmt_errno() if using a prepared statement - // - mysql_errno() if not using a prepared statement - if (stmt) { - myerr = mysql_stmt_errno(stmt); - myprot->generate_pkt_ERR(false,&pkt.ptr,&pkt.size,sid,myerr,sqlstate,mysql_stmt_error(stmt)); - } else { - myerr = mysql_errno(_mysql); - myprot->generate_pkt_ERR(false,&pkt.ptr,&pkt.size,sid,myerr,sqlstate,mysql_error(_mysql)); - } - // TODO: Check this is a mysql error - MyHGM->p_update_mysql_error_counter(p_mysql_error_type::mysql, _myds->myconn->parent->myhgc->hid, _myds->myconn->parent->address, _myds->myconn->parent->port, myerr); - } - PSarrayOUT.add(pkt.ptr,pkt.size); - sid++; - resultset_size+=pkt.size; - } - resultset_completed=true; -} - -/* -bool MySQL_ResultSet::get_COM_FIELD_LIST_response(PtrSizeArray *PSarrayFinal) { - transfer_started=true; - if (myprot) { - } - return resultset_completed; -} -*/ - -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(bool _last) { - if (buffer_used==0) - return; // exit immediately if the buffer is empty - if (buffer_used < RESULTSET_BUFLEN/2) { - if (_last == false) { - buffer=(unsigned char *)realloc(buffer,buffer_used); - } - } - PSarrayOUT.add(buffer,buffer_used); - if (_last) { - buffer = NULL; - } else { - 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.len==0) // see bug #699 - return intsize; - intsize+=sizeof(PtrSizeArray); - intsize+=(PSarrayOUT.size*sizeof(PtrSize_t *)); - unsigned int i; - for (i=0; isize>RESULTSET_BUFLEN) { - intsize+=pkt->size; - } else { - intsize+=RESULTSET_BUFLEN; - } - } - return intsize; -} - my_bool proxy_mysql_stmt_close(MYSQL_STMT* stmt) { // Clean internal structures for 'stmt->mysql->stmts'. if (stmt->mysql) { diff --git a/lib/MySQL_ResultSet.cpp b/lib/MySQL_ResultSet.cpp new file mode 100644 index 0000000000..87e713aa76 --- /dev/null +++ b/lib/MySQL_ResultSet.cpp @@ -0,0 +1,549 @@ +#include +#include "proxysql.h" +#include "cpp.h" +#include "re2/re2.h" +#include "re2/regexp.h" + +#include "MySQL_PreparedStatement.h" +#include "MySQL_Data_Stream.h" +#include "MySQL_Authentication.hpp" +#include "MySQL_LDAP_Authentication.hpp" +#include "MySQL_Variables.h" + +#include + +//#include + +extern MySQL_Authentication *GloMyAuth; +extern MySQL_LDAP_Authentication *GloMyLdapAuth; +extern MySQL_Threads_Handler *GloMTH; + +#ifdef PROXYSQLCLICKHOUSE +extern ClickHouse_Authentication *GloClickHouseAuth; +#endif /* PROXYSQLCLICKHOUSE */ + +#ifdef max_allowed_packet +#undef max_allowed_packet +#endif + +#ifndef CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA +#define CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA 0x00200000 +#endif + +#include "proxysql_find_charset.h" + + +extern "C" char * sha256_crypt_r (const char *key, const char *salt, char *buffer, int buflen); + + +uint8_t mysql_encode_length(uint64_t len, char *hd); + + +MySQL_ResultSet::MySQL_ResultSet() { + buffer = NULL; + //reset_pid = true; +} + +void MySQL_ResultSet::buffer_init(MySQL_Protocol* myproto) { + if (buffer==NULL) { + buffer=(unsigned char *)malloc(RESULTSET_BUFLEN); + } + + buffer_used=0; + myprot = myproto; +} + +void MySQL_ResultSet::init(MySQL_Protocol *_myprot, MYSQL_RES *_res, MYSQL *_my, MYSQL_STMT *_stmt) { + PROXY_TRACE2(); + transfer_started=false; + resultset_completed=false; + myprot=_myprot; + mysql=_my; + stmt=_stmt; + if (buffer==NULL) { + //if (_stmt==NULL) { // we allocate this buffer only for not prepared statements + // removing the previous assumption. We allocate this buffer also for prepared statements + buffer=(unsigned char *)malloc(RESULTSET_BUFLEN); + //} + } + buffer_used=0; + myds=NULL; + if (myprot) { // if myprot = NULL , this is a mirror + myds=myprot->get_myds(); + } + //if (reset_pid==true) { + sid=0; + //PSarrayOUT = NULL; + if (myprot) { // if myprot = NULL , this is a mirror + sid=myds->pkt_sid+1; + //PSarrayOUT = new PtrSizeArray(8); + } + //} + //reset_pid=true; + 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 + } + MySQL_Data_Stream * c_myds = *(myprot->myds); + if (c_myds->com_field_list==false) { + myprot->generate_pkt_column_count(false,&pkt.ptr,&pkt.size,sid,num_fields,this); + sid++; + resultset_size+=pkt.size; + } + // columns description + for (unsigned int i=0; icom_field_list==false) { + // we are replacing generate_pkt_field() with a more efficient version + //myprot->generate_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,this); + myprot->generate_pkt_field2(&pkt.ptr,&pkt.size,sid,field,this); + resultset_size+=pkt.size; + sid++; + } else { + if (c_myds->com_field_wild==NULL || mywildcmp(c_myds->com_field_wild,field->name)) { + myprot->generate_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,true,4,(char *)"null",this); + resultset_size+=pkt.size; + sid++; + } + } + } + + deprecate_eof_active = c_myds->myconn && (c_myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF); + + // 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; + setStatus |= ( mysql->server_status & ~SERVER_STATUS_AUTOCOMMIT ); // get flags from server_status but ignore autocommit + setStatus = setStatus & ~SERVER_STATUS_CURSOR_EXISTS; // Do not send cursor #1128 +// if (_stmt) { // binary protocol , we also assume we have ALL the resultset +// 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; + //} else { + if (RESULTSET_BUFLEN <= (buffer_used + 9)) { + buffer_to_PSarrayOut(); + } + if (!deprecate_eof_active && myds->com_field_list==false) { + // up to 2.2.0 we used to add an EOF here. + // due to bug #3547 we move the logic into add_eof() that can now handle also prepared statements + PROXY_TRACE2(); + // if the backend server has CLIENT_DEPRECATE_EOF enabled, and the client does not support + // CLIENT_DEPRECATE_EOF, warning_count will be excluded from the intermediate EOF packet + add_eof((mysql->server_capabilities & CLIENT_DEPRECATE_EOF)); + } +} + + +// due to bug #3547 , in case of an error we remove the EOF +// and replace it with an ERR +// note that EOF is added on a packet on its own, instead of using a buffer, +// so that can be removed using remove_last_eof() +void MySQL_ResultSet::remove_last_eof() { + PROXY_TRACE2(); + PtrSize_t pkt; + if (PSarrayOUT.len) { + unsigned int l = PSarrayOUT.len-1; + PtrSize_t * pktp = PSarrayOUT.index(l); + if (pktp->size == 9) { + PROXY_TRACE2(); + PSarrayOUT.remove_index(l,&pkt); + l_free(pkt.size, pkt.ptr); + sid--; + } + } +} + +void MySQL_ResultSet::init_with_stmt(MySQL_Connection *myconn) { + PROXY_TRACE2(); + assert(stmt); + MYSQL_STMT *_stmt = stmt; + MySQL_Data_Stream * c_myds = *(myprot->myds); + buffer_to_PSarrayOut(); + 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); + } +#define MAXBUFFSTMT 12*1024*1024 // hardcoded to LESS *very important* than 16MB + if (total_size < MAXBUFFSTMT) { + 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); + if (resultset_size/0xFFFFFFF != ((resultset_size+pkt.size)/0xFFFFFFF)) { + // generate a heartbeat every 256MB + unsigned long long curtime=monotonic_time(); + c_myds->sess->thread->atomic_curtime=curtime; + } + resultset_size+=pkt.size; + } else { // this code fixes a bug: resultset larger than 4GB would cause a crash + unsigned long long tmp_pkt_size = 0; + r=_stmt->result.data; + MYSQL_ROWS * r2 = NULL; + while (r) { + if (r->length >= MAXBUFFSTMT) { + // we have a large row + // we will send just that + tmp_pkt_size = r->length; + if (r->length > 0xFFFFFF) { + tmp_pkt_size+=(r->length / 0xFFFFFF) * sizeof(mysql_hdr); + } + tmp_pkt_size += sizeof(mysql_hdr); + PtrSize_t pkt; + pkt.size=tmp_pkt_size; + pkt.ptr=malloc(pkt.size); + add_row2(r,(unsigned char *)pkt.ptr); + PSarrayOUT.add(pkt.ptr,pkt.size); + if (resultset_size/0xFFFFFFF != ((resultset_size+pkt.size)/0xFFFFFFF)) { + // generate a heartbeat every 256MB + unsigned long long curtime=monotonic_time(); + c_myds->sess->thread->atomic_curtime=curtime; + } + resultset_size+=pkt.size; + r=r->next; // next row + } else { // we have small row + r2 = r; + tmp_pkt_size = 0; + unsigned int a = 0; + while (r && (tmp_pkt_size + r->length) < MAXBUFFSTMT) { + a++; + tmp_pkt_size += r->length; + tmp_pkt_size += sizeof(mysql_hdr); + //if (r->next) { + r = r->next; + //} + } + r = r2; // we reset it back to the beginning + if (tmp_pkt_size) { // this should always be true + unsigned long long tmp2 = 0; + PtrSize_t pkt; + pkt.size=tmp_pkt_size; + pkt.ptr=malloc(pkt.size); + while (tmp2 < tmp_pkt_size) { + add_row2(r,(unsigned char *)pkt.ptr+tmp2); + tmp2 += r->length; + tmp2 += sizeof(mysql_hdr); + r = r->next; + } + PSarrayOUT.add(pkt.ptr,pkt.size); + if (resultset_size/0xFFFFFFF != ((resultset_size+pkt.size)/0xFFFFFFF)) { + // generate a heartbeat every 256MB + unsigned long long curtime=monotonic_time(); + c_myds->sess->thread->atomic_curtime=curtime; + } + resultset_size+=pkt.size; + } + } + } + } + } + // up to 2.2.0 we were always adding an EOF + // due to bug #3547 , in case of an error we remove the EOF + // and replace it with an ERR + // note that EOF is added on a packet on its own, instead of using a buffer, + // so that can be removed + // + // NOTE: After 2.4.5 previous behavior is modified in favor of the following: + // + // When CLIENT_DEPRECATE_EOF two EOF packets are two be expected in the response: + // 1. After the columns definitions (This is added directly by 'MySQL_ResultSet::init'). + // 2. After the rows values, this can either be and EOF packet or a ERR packet in case of error. + // + // First EOF packet isn't optional, and it's just the second the one that is optionaly either an EOF + // or an ERR packet. The following code adds either the final EOF or ERR packet. This is equally valid + // for when CLIENT_DEPRECATE_EOF is enabled or not. If CLIENT_DEPRECATE_EOF is: + // * DISABLED: The behavior is as described before. + // * ENABLED: Code is identical for this case. The initial EOF packet is conditionally added by + // 'MySQL_ResultSet::init', thus, this packet should not be present if not needed at this point. + // In case of error an ERR packet needs to be added, otherwise `add_eof` handles the generation of + // the equivalent OK packet replacing the final EOF packet. + int myerr = mysql_stmt_errno(_stmt); + if (myerr) { + PROXY_TRACE2(); + add_err(myconn->myds); + } else { + PROXY_TRACE2(); + 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; +} + +// this function is used for binary protocol +// maybe later on can be adapted for text protocol too +unsigned int MySQL_ResultSet::add_row(MYSQL_ROWS *rows) { + unsigned int pkt_length=0; + MYSQL_ROW row = rows->data; + unsigned long row_length = rows->length; + // we call generate_pkt_row3 passing row_length + sid=myprot->generate_pkt_row3(this, &pkt_length, sid, 0, NULL, row, row_length); + sid++; + resultset_size+=pkt_length; + num_rows++; + return pkt_length; +} + + +// this function is used for text protocol +unsigned int MySQL_ResultSet::add_row(MYSQL_ROW row) { + unsigned long *lengths=mysql_fetch_lengths(result); + unsigned int pkt_length=0; + if (myprot) { + // we call generate_pkt_row3 without passing row_length + sid=myprot->generate_pkt_row3(this, &pkt_length, sid, num_fields, lengths, row, 0); + } else { + unsigned int col=0; + for (col=0; collength; + num_rows++; + 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); + char *o = (char *) row->data; + o += copied; + memcpy(offset, o, 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); + char *o = (char *) row->data; + o += copied; + memcpy(offset, o, 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(bool suppress_warning_count) { + if (myprot) { + unsigned int nTrx=myds->sess->NumActiveTransactions(); + uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); + if (myds->sess->autocommit) setStatus += SERVER_STATUS_AUTOCOMMIT; + setStatus |= ( mysql->server_status & ~SERVER_STATUS_AUTOCOMMIT ); // get flags from server_status but ignore autocommit + setStatus = setStatus & ~SERVER_STATUS_CURSOR_EXISTS; // Do not send cursor #1128 + //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; + + // Note: warnings count will only be sent to the client if mysql-query_digests is enabled + const MySQL_Backend* _mybe = myds->sess->mybe; + const MySQL_Data_Stream* _server_myds = (_mybe && _mybe->server_myds) ? _mybe->server_myds : nullptr; + const MySQL_Connection* _myconn = (_server_myds && _server_myds->myds_type == MYDS_BACKEND && _server_myds->myconn) ? + _server_myds->myconn : nullptr; + const unsigned int warning_count = (_myconn && suppress_warning_count == false) ? _myconn->warning_count : 0; + if (deprecate_eof_active) { + PtrSize_t pkt; + buffer_to_PSarrayOut(); + myprot->generate_pkt_OK(false, &pkt.ptr, &pkt.size, sid, 0, 0, setStatus, warning_count, NULL, true); + PSarrayOUT.add(pkt.ptr, pkt.size); + resultset_size += pkt.size; + } + else { + // due to bug #3547 , in case of an error we remove the EOF + // and replace it with an ERR + // note that EOF is added on a packet on its own, instead of using a buffer, + // so that can be removed using remove_last_eof() + buffer_to_PSarrayOut(); + myprot->generate_pkt_EOF(false, NULL, NULL, sid, warning_count, setStatus, this); + resultset_size += 9; + buffer_to_PSarrayOut(); + } + sid++; + } + resultset_completed=true; +} + +void MySQL_ResultSet::add_err(MySQL_Data_Stream *_myds) { + PtrSize_t pkt; + if (myprot) { + MYSQL *_mysql=_myds->myconn->mysql; + buffer_to_PSarrayOut(); + char sqlstate[10]; + sprintf(sqlstate,"%s",mysql_sqlstate(_mysql)); + if (_myds && _myds->killed_at) { // see case #750 + if (_myds->kill_type == 0) { + myprot->generate_pkt_ERR(false,&pkt.ptr,&pkt.size,sid,1907,sqlstate,(char *)"Query execution was interrupted, query_timeout exceeded"); + MyHGM->p_update_mysql_error_counter(p_mysql_error_type::proxysql, _myds->myconn->parent->myhgc->hid, _myds->myconn->parent->address, _myds->myconn->parent->port, 1907); + } else { + myprot->generate_pkt_ERR(false,&pkt.ptr,&pkt.size,sid,1317,sqlstate,(char *)"Query execution was interrupted"); + MyHGM->p_update_mysql_error_counter(p_mysql_error_type::proxysql, _myds->myconn->parent->myhgc->hid, _myds->myconn->parent->address, _myds->myconn->parent->port, 1317); + } + } else { + int myerr = 0; + // the error code is returned from: + // - mysql_stmt_errno() if using a prepared statement + // - mysql_errno() if not using a prepared statement + if (stmt) { + myerr = mysql_stmt_errno(stmt); + myprot->generate_pkt_ERR(false,&pkt.ptr,&pkt.size,sid,myerr,sqlstate,mysql_stmt_error(stmt)); + } else { + myerr = mysql_errno(_mysql); + myprot->generate_pkt_ERR(false,&pkt.ptr,&pkt.size,sid,myerr,sqlstate,mysql_error(_mysql)); + } + // TODO: Check this is a mysql error + MyHGM->p_update_mysql_error_counter(p_mysql_error_type::mysql, _myds->myconn->parent->myhgc->hid, _myds->myconn->parent->address, _myds->myconn->parent->port, myerr); + } + PSarrayOUT.add(pkt.ptr,pkt.size); + sid++; + resultset_size+=pkt.size; + } + resultset_completed=true; +} + +/* +bool MySQL_ResultSet::get_COM_FIELD_LIST_response(PtrSizeArray *PSarrayFinal) { + transfer_started=true; + if (myprot) { + } + return resultset_completed; +} +*/ + +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(bool _last) { + if (buffer_used==0) + return; // exit immediately if the buffer is empty + if (buffer_used < RESULTSET_BUFLEN/2) { + if (_last == false) { + buffer=(unsigned char *)realloc(buffer,buffer_used); + } + } + PSarrayOUT.add(buffer,buffer_used); + if (_last) { + buffer = NULL; + } else { + 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.len==0) // see bug #699 + return intsize; + intsize+=sizeof(PtrSizeArray); + intsize+=(PSarrayOUT.size*sizeof(PtrSize_t *)); + unsigned int i; + for (i=0; isize>RESULTSET_BUFLEN) { + intsize+=pkt->size; + } else { + intsize+=RESULTSET_BUFLEN; + } + } + return intsize; +} + +/* +my_bool proxy_mysql_stmt_close(MYSQL_STMT* stmt) { + // Clean internal structures for 'stmt->mysql->stmts'. + if (stmt->mysql) { + stmt->mysql->stmts = + list_delete(stmt->mysql->stmts, &stmt->list); + } + // Nullify 'mysql' field to avoid sending a blocking command to the server. + stmt->mysql = NULL; + // Perform the regular close operation. + return mysql_stmt_close(stmt); +} +*/ diff --git a/lib/MySQL_encode.cpp b/lib/MySQL_encode.cpp new file mode 100644 index 0000000000..13a4f0461b --- /dev/null +++ b/lib/MySQL_encode.cpp @@ -0,0 +1,239 @@ +#include +#include "proxysql.h" +#include "cpp.h" + +#ifdef DEBUG +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 = 0; + 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) { + unsigned char * to = (unsigned char *)_to; + int rc = 0; + uint i; + rc = RAND_bytes((unsigned char *)to,length); +#ifdef DEBUG + if (rc==1) { + // For code coverage (to test the following code and other function) + // in DEBUG mode we pretend that RAND_bytes() fails 1% of the time + if(rand()%100==0) { + rc=0; + } + } +#endif // DEBUG + if (rc!=1) { + for (i=0; i 127) { + *to -= 128; + } + if (*to == 0) { + *to = 'a'; + } + to++; + } + } + *to= '\0'; +} + +int write_encoded_length(unsigned char *p, uint64_t val, uint8_t len, char prefix) { + if (len==1) { + *p=(char)val; + return 1; + } + *p=prefix; + p++; + memcpy(p,&val,len-1); + return len; +} + +int write_encoded_length_and_string(unsigned char *p, uint64_t val, uint8_t len, char prefix, char *string) { + int l=write_encoded_length(p,val,len,prefix); + if (val) { + memcpy(p+l,string,val); + } + return l+val; +} + +void proxy_compute_sha1_hash_multi(uint8_t *digest, const char *buf1, int len1, const char *buf2, int len2) { + PROXY_TRACE(); + const EVP_MD *evp_digest = EVP_get_digestbyname("sha1"); + assert(evp_digest != NULL); + EVP_MD_CTX *ctx = EVP_MD_CTX_new(); + EVP_MD_CTX_init(ctx); + EVP_DigestInit_ex(ctx, evp_digest, NULL); + EVP_DigestUpdate(ctx, buf1, len1); + EVP_DigestUpdate(ctx, buf2, len2); + unsigned int olen = 0; + EVP_DigestFinal(ctx, digest, &olen); + EVP_MD_CTX_free(ctx); +} + +void proxy_compute_sha1_hash(uint8_t *digest, const char *buf, int len) { + PROXY_TRACE(); + const EVP_MD *evp_digest = EVP_get_digestbyname("sha1"); + assert(evp_digest != NULL); + EVP_MD_CTX *ctx = EVP_MD_CTX_new(); + EVP_MD_CTX_init(ctx); + EVP_DigestInit_ex(ctx, evp_digest, NULL); + EVP_DigestUpdate(ctx, buf, len); + unsigned int olen = 0; + EVP_DigestFinal(ctx, digest, &olen); + EVP_MD_CTX_free(ctx); +} + +void proxy_compute_two_stage_sha1_hash(const char *password, size_t pass_len, uint8_t *hash_stage1, uint8_t *hash_stage2) { + proxy_compute_sha1_hash(hash_stage1, password, pass_len); + proxy_compute_sha1_hash(hash_stage2, (const char *) hash_stage1, SHA_DIGEST_LENGTH); +} + +void proxy_my_crypt(char *to, const uint8_t *s1, const uint8_t *s2, uint len) { + const uint8_t *s1_end= s1 + len; + while (s1 < s1_end) + *to++= *s1++ ^ *s2++; +} + +unsigned char decode_char(char x) { + if (x >= '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;i