You've already forked mariadb-connector-c
mirror of
https://github.com/mariadb-corporation/mariadb-connector-c.git
synced 2025-08-08 14:02:17 +03:00
Fix for CONC-70: Unknown error when reading large packets via conpressed protocol
This commit is contained in:
@@ -83,6 +83,7 @@ my_bool my_uncompress (unsigned char *packet, size_t *len, size_t *complen)
|
|||||||
memcpy(packet,compbuf,*len);
|
memcpy(packet,compbuf,*len);
|
||||||
my_free((gptr)compbuf,MYF(MY_WME));
|
my_free((gptr)compbuf,MYF(MY_WME));
|
||||||
}
|
}
|
||||||
|
else *complen= *len;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif /* HAVE_COMPRESS */
|
#endif /* HAVE_COMPRESS */
|
||||||
|
122
libmariadb/net.c
122
libmariadb/net.c
@@ -37,7 +37,7 @@
|
|||||||
|
|
||||||
#define MAX_PACKET_LENGTH (256L*256L*256L-1)
|
#define MAX_PACKET_LENGTH (256L*256L*256L-1)
|
||||||
|
|
||||||
/* net_buffer_length and max_allowec_packet are defined in mysql.h
|
/* net_buffer_length and max_allowed_packet are defined in mysql.h
|
||||||
See bug conc-57
|
See bug conc-57
|
||||||
*/
|
*/
|
||||||
#undef net_buffer_length
|
#undef net_buffer_length
|
||||||
@@ -713,54 +713,120 @@ ulong my_net_read(NET *net)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (net->remain_in_buf)
|
/*
|
||||||
|
compressed protocol:
|
||||||
|
|
||||||
|
--------------------------------------
|
||||||
|
packet_lengt h 3
|
||||||
|
sequence_id 1
|
||||||
|
uncompressed_length 3
|
||||||
|
--------------------------------------
|
||||||
|
compressed data packet_length - 7
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
|
Another packet will follow if:
|
||||||
|
packet_length == MAX_PACKET_LENGTH
|
||||||
|
|
||||||
|
Last package will be identified by
|
||||||
|
- packet_length is zero (special case)
|
||||||
|
- packet_length < MAX_PACKET_LENGTH
|
||||||
|
*/
|
||||||
|
|
||||||
|
size_t packet_length,
|
||||||
|
buffer_length;
|
||||||
|
size_t current= 0, start= 0;
|
||||||
|
my_bool is_multi_packet= 0;
|
||||||
|
|
||||||
|
/* check if buffer is empty */
|
||||||
|
if (!net->remain_in_buf)
|
||||||
{
|
{
|
||||||
/* restore 0 character */
|
buffer_length= 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* save position and restore \0 character */
|
||||||
|
buffer_length= net->buf_length;
|
||||||
|
current= net->buf_length - net->remain_in_buf;
|
||||||
|
start= current;
|
||||||
net->buff[net->buf_length - net->remain_in_buf]=net->save_char;
|
net->buff[net->buf_length - net->remain_in_buf]=net->save_char;
|
||||||
}
|
}
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
if (net->remain_in_buf)
|
if (buffer_length - current >= 4)
|
||||||
{
|
{
|
||||||
uchar *pos = net->buff + net->buf_length - net->remain_in_buf;
|
uchar *pos= net->buff + current;
|
||||||
if (net->remain_in_buf >= 4)
|
packet_length= uint3korr(pos);
|
||||||
|
|
||||||
|
/* check if we have last package (special case: zero length) */
|
||||||
|
if (!packet_length)
|
||||||
{
|
{
|
||||||
net->length = uint3korr(pos);
|
current+= 4; /* length + sequence_id,
|
||||||
if (net->length <= net->remain_in_buf - 4)
|
no more data will follow */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (packet_length + 4 <= buffer_length - current)
|
||||||
|
{
|
||||||
|
if (!is_multi_packet)
|
||||||
{
|
{
|
||||||
/* We have a full packet */
|
current= current + packet_length + 4;
|
||||||
len=net->length;
|
|
||||||
net->remain_in_buf -= net->length + 4;
|
|
||||||
net->read_pos=pos + 4;
|
|
||||||
break; /* We have a full packet */
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* remove packet_header */
|
||||||
|
memmove(net->buff + current,
|
||||||
|
net->buff + current + 4,
|
||||||
|
buffer_length - current);
|
||||||
|
buffer_length-= 4;
|
||||||
|
current+= packet_length;
|
||||||
|
}
|
||||||
|
/* do we have last packet ? */
|
||||||
|
if (packet_length != MAX_PACKET_LENGTH)
|
||||||
|
{
|
||||||
|
is_multi_packet= 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
is_multi_packet= 1;
|
||||||
|
if (start)
|
||||||
|
{
|
||||||
|
memmove(net->buff, net->buff + start,
|
||||||
|
buffer_length - start);
|
||||||
|
/* decrease buflen*/
|
||||||
|
buffer_length-= start;
|
||||||
|
start= 0;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
/* Move data down to read next data packet after current one */
|
|
||||||
if (net->buf_length != net->remain_in_buf)
|
|
||||||
{
|
|
||||||
memmove(net->buff,pos,net->remain_in_buf);
|
|
||||||
net->buf_length=net->remain_in_buf;
|
|
||||||
}
|
|
||||||
net->where_b=net->buf_length;
|
|
||||||
}
|
}
|
||||||
else
|
if (start)
|
||||||
{
|
{
|
||||||
net->where_b=0;
|
memmove(net->buff, net->buff + start, buffer_length - start);
|
||||||
net->buf_length=0;
|
/* decrease buflen and current */
|
||||||
|
current -= start;
|
||||||
|
buffer_length-= start;
|
||||||
|
start= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((len = my_real_read(net,(size_t *)&complen)) == packet_error)
|
net->where_b=buffer_length;
|
||||||
|
|
||||||
|
if ((packet_length = my_real_read(net,(size_t *)&complen)) == packet_error)
|
||||||
break;
|
break;
|
||||||
if (my_uncompress((unsigned char*) net->buff + net->where_b, &len, &complen))
|
if (my_uncompress((unsigned char*) net->buff + net->where_b, &packet_length, &complen))
|
||||||
{
|
{
|
||||||
len= packet_error;
|
len= packet_error;
|
||||||
net->error=2; /* caller will close socket */
|
net->error=2; /* caller will close socket */
|
||||||
net->last_errno=ER_NET_UNCOMPRESS_ERROR;
|
net->last_errno=ER_NET_UNCOMPRESS_ERROR;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
net->buf_length+=(unsigned long)len;
|
buffer_length+= complen;
|
||||||
net->remain_in_buf+=(unsigned long)len;
|
|
||||||
}
|
}
|
||||||
|
/* set values */
|
||||||
|
net->buf_length= buffer_length;
|
||||||
|
net->remain_in_buf= buffer_length - current;
|
||||||
|
net->read_pos= net->buff + start + 4;
|
||||||
|
len= current - start - 4;
|
||||||
|
if (is_multi_packet)
|
||||||
|
len-= 4;
|
||||||
if (len != packet_error)
|
if (len != packet_error)
|
||||||
{
|
{
|
||||||
net->save_char= net->read_pos[len]; /* Must be saved */
|
net->save_char= net->read_pos[len]; /* Must be saved */
|
||||||
|
@@ -30,11 +30,21 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "my_test.h"
|
#include "my_test.h"
|
||||||
#include "ma_common.h"
|
#include "ma_common.h"
|
||||||
|
|
||||||
static int test_conc68(MYSQL *mysql)
|
static int test_conc70(MYSQL *my)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
MYSQL_RES *res;
|
MYSQL_RES *res;
|
||||||
MYSQL_ROW row;
|
MYSQL_ROW row;
|
||||||
|
MYSQL *mysql= mysql_init(NULL);
|
||||||
|
|
||||||
|
mysql_query(my, "SET @a:=@@max_allowed_packet");
|
||||||
|
check_mysql_rc(rc, my);
|
||||||
|
|
||||||
|
mysql_query(my, "SET global max_allowed_packet=1024*1024*22");
|
||||||
|
|
||||||
|
mysql_options(mysql, MYSQL_OPT_COMPRESS, (void *)1);
|
||||||
|
FAIL_IF(!mysql_real_connect(mysql, hostname, username, password, schema,
|
||||||
|
port, socketname, 0), mysql_error(my));
|
||||||
|
|
||||||
rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
|
rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
|
||||||
check_mysql_rc(rc, mysql);
|
check_mysql_rc(rc, mysql);
|
||||||
@@ -43,7 +53,7 @@ static int test_conc68(MYSQL *mysql)
|
|||||||
check_mysql_rc(rc, mysql);
|
check_mysql_rc(rc, mysql);
|
||||||
|
|
||||||
rc= mysql_query(mysql, "INSERT INTO t1 VALUES (REPEAT('A', 1024 * 1024 * 20))");
|
rc= mysql_query(mysql, "INSERT INTO t1 VALUES (REPEAT('A', 1024 * 1024 * 20))");
|
||||||
check_mysql_rc(rc, mysql);
|
check_mysql_rc(rc, mysql);
|
||||||
|
|
||||||
rc= mysql_query(mysql, "SELECT a FROM t1");
|
rc= mysql_query(mysql, "SELECT a FROM t1");
|
||||||
check_mysql_rc(rc, mysql);
|
check_mysql_rc(rc, mysql);
|
||||||
@@ -55,47 +65,65 @@ static int test_conc68(MYSQL *mysql)
|
|||||||
}
|
}
|
||||||
|
|
||||||
row= mysql_fetch_row(res);
|
row= mysql_fetch_row(res);
|
||||||
|
diag("Length: %ld", strlen(row[0]));
|
||||||
FAIL_IF(strlen(row[0]) != 1024 * 1024 * 20, "Wrong length");
|
FAIL_IF(strlen(row[0]) != 1024 * 1024 * 20, "Wrong length");
|
||||||
|
|
||||||
mysql_free_result(res);
|
mysql_free_result(res);
|
||||||
|
mysql_close(mysql);
|
||||||
|
|
||||||
|
rc= mysql_query(my, "SET global max_allowed_packet=@a");
|
||||||
|
check_mysql_rc(rc, my);
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int test_conc66(MYSQL *my)
|
static int test_conc68(MYSQL *my)
|
||||||
{
|
{
|
||||||
MYSQL *mysql= mysql_init(NULL);
|
|
||||||
int rc;
|
int rc;
|
||||||
FILE *fp;
|
MYSQL_RES *res;
|
||||||
|
MYSQL_ROW row;
|
||||||
|
MYSQL *mysql= mysql_init(NULL);
|
||||||
|
|
||||||
fp= fopen("./my.cnf", "w");
|
mysql_query(my, "SET @a:=@@max_allowed_packet");
|
||||||
fprintf(fp, "[conc-66]\nuser=conc-66\npassword=\"my#pass;word\"");
|
check_mysql_rc(rc, my);
|
||||||
fclose(fp);
|
|
||||||
|
|
||||||
rc= mysql_query(my, "GRANT ALL ON test.* to 'conc-66'@'%' identified by 'my#pass;word'");
|
mysql_query(my, "SET global max_allowed_packet=1024*1024*22");
|
||||||
check_mysql_rc(rc, mysql);
|
|
||||||
rc= mysql_query(my, "FLUSH PRIVILEGES");
|
FAIL_IF(!mysql_real_connect(mysql, hostname, username, password, schema,
|
||||||
|
port, socketname, 0), mysql_error(my));
|
||||||
|
|
||||||
|
rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
|
||||||
check_mysql_rc(rc, mysql);
|
check_mysql_rc(rc, mysql);
|
||||||
|
|
||||||
rc= mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, "conc-66");
|
rc= mysql_query(mysql, "CREATE TABLE t1 (a LONGBLOB)");
|
||||||
check_mysql_rc(rc, mysql);
|
|
||||||
rc= mysql_options(mysql, MYSQL_READ_DEFAULT_FILE, "./my.cnf");
|
|
||||||
check_mysql_rc(rc, mysql);
|
check_mysql_rc(rc, mysql);
|
||||||
|
|
||||||
rc= mysql_real_connect(mysql, hostname, NULL, NULL, schema,
|
rc= mysql_query(mysql, "INSERT INTO t1 VALUES (REPEAT('A', 1024 * 1024 * 20))");
|
||||||
port, socketname, 0);
|
check_mysql_rc(rc, mysql);
|
||||||
if (!rc) {
|
|
||||||
diag("Error: %s\n", mysql_error(mysql));
|
rc= mysql_query(mysql, "SELECT a FROM t1");
|
||||||
mysql_close(mysql);
|
check_mysql_rc(rc, mysql);
|
||||||
|
|
||||||
|
if (!(res= mysql_store_result(mysql)))
|
||||||
|
{
|
||||||
|
diag("Error: %s", mysql_error(mysql));
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
row= mysql_fetch_row(res);
|
||||||
|
diag("Length: %ld", strlen(row[0]));
|
||||||
|
FAIL_IF(strlen(row[0]) != 1024 * 1024 * 20, "Wrong length");
|
||||||
|
|
||||||
|
mysql_free_result(res);
|
||||||
mysql_close(mysql);
|
mysql_close(mysql);
|
||||||
|
|
||||||
rc= mysql_query(my, "DROP USER 'conc-66'");
|
rc= mysql_query(my, "SET global max_allowed_packet=@a");
|
||||||
check_mysql_rc(rc, mysql);
|
check_mysql_rc(rc, my);
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int basic_connect(MYSQL *mysql)
|
static int basic_connect(MYSQL *mysql)
|
||||||
{
|
{
|
||||||
MYSQL_ROW row;
|
MYSQL_ROW row;
|
||||||
@@ -572,7 +600,6 @@ static int test_reconnect_maxpackage(MYSQL *my)
|
|||||||
memset(query + 8, 'A', max_packet);
|
memset(query + 8, 'A', max_packet);
|
||||||
strcat(query, "' FROM DUAL");
|
strcat(query, "' FROM DUAL");
|
||||||
|
|
||||||
|
|
||||||
rc= mysql_query(mysql, query);
|
rc= mysql_query(mysql, query);
|
||||||
free(query);
|
free(query);
|
||||||
if (!rc)
|
if (!rc)
|
||||||
@@ -621,8 +648,8 @@ static int test_compressed(MYSQL *my)
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct my_tests_st my_tests[] = {
|
struct my_tests_st my_tests[] = {
|
||||||
|
{"test_conc70", test_conc70, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
||||||
{"test_conc68", test_conc68, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
{"test_conc68", test_conc68, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
||||||
{"test_conc66", test_conc66, TEST_CONNECTION_NEW, 0, NULL, NULL},
|
|
||||||
{"test_compressed", test_compressed, TEST_CONNECTION_NONE, 0, NULL, NULL},
|
{"test_compressed", test_compressed, TEST_CONNECTION_NONE, 0, NULL, NULL},
|
||||||
{"test_reconnect_maxpackage", test_reconnect_maxpackage, TEST_CONNECTION_NONE, 0, NULL, NULL},
|
{"test_reconnect_maxpackage", test_reconnect_maxpackage, TEST_CONNECTION_NONE, 0, NULL, NULL},
|
||||||
{"basic_connect", basic_connect, TEST_CONNECTION_NONE, 0, NULL, NULL},
|
{"basic_connect", basic_connect, TEST_CONNECTION_NONE, 0, NULL, NULL},
|
||||||
|
@@ -57,7 +57,8 @@ static int test_conc66(MYSQL *my)
|
|||||||
diag("Error: %s", mysql_error(mysql));
|
diag("Error: %s", mysql_error(mysql));
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
rc= mysql_query(my, "DROP USER conc66");
|
rc= mysql_query(my, "DROP USER conc66@localhost");
|
||||||
|
|
||||||
check_mysql_rc(rc, my);
|
check_mysql_rc(rc, my);
|
||||||
mysql_close(mysql);
|
mysql_close(mysql);
|
||||||
return OK;
|
return OK;
|
||||||
|
@@ -25,29 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "ma_common.h"
|
#include "ma_common.h"
|
||||||
|
|
||||||
|
|
||||||
static int test_conc60(MYSQL *mysql)
|
|
||||||
{
|
|
||||||
MYSQL_STMT *stmt= mysql_stmt_init(mysql);
|
|
||||||
char *stmtstr= "SELECT * FROM agendas";
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
rc= mysql_stmt_prepare(stmt, stmtstr, strlen(stmtstr));
|
|
||||||
check_stmt_rc(rc, stmt);
|
|
||||||
|
|
||||||
rc= mysql_stmt_execute(stmt);
|
|
||||||
check_stmt_rc(rc, stmt);
|
|
||||||
|
|
||||||
rc= mysql_stmt_store_result(stmt);
|
|
||||||
check_stmt_rc(rc, stmt);
|
|
||||||
|
|
||||||
diag("rows: %u", mysql_stmt_num_rows(stmt));
|
|
||||||
|
|
||||||
while (mysql_stmt_fetch(stmt));
|
|
||||||
|
|
||||||
mysql_stmt_close(stmt);
|
|
||||||
|
|
||||||
return(OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Bug#28075 "COM_DEBUG crashes mysqld"
|
Bug#28075 "COM_DEBUG crashes mysqld"
|
||||||
@@ -984,7 +962,6 @@ static int test_connect_attrs(MYSQL *my)
|
|||||||
|
|
||||||
struct my_tests_st my_tests[] = {
|
struct my_tests_st my_tests[] = {
|
||||||
{"test_connect_attrs", test_connect_attrs, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
{"test_connect_attrs", test_connect_attrs, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
||||||
{"test_conc60", test_conc60, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
|
||||||
{"test_conc49", test_conc49, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
{"test_conc49", test_conc49, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
||||||
{"test_bug28075", test_bug28075, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
{"test_bug28075", test_bug28075, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
||||||
{"test_bug28505", test_bug28505, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
{"test_bug28505", test_bug28505, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
||||||
|
Reference in New Issue
Block a user