diff options
Diffstat (limited to 'sql/net_serv.cc')
-rw-r--r-- | sql/net_serv.cc | 265 |
1 files changed, 188 insertions, 77 deletions
diff --git a/sql/net_serv.cc b/sql/net_serv.cc index ea66bb53394..90a5da6a927 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -34,6 +34,7 @@ HFTODO this must be hidden if we don't want client capabilities in embedded library */ + #include <my_global.h> #include <mysql.h> #include <mysql_com.h> @@ -43,7 +44,6 @@ #include <my_net.h> #include <violite.h> #include <signal.h> -#include <errno.h> #include "probes_mysql.h" #ifdef EMBEDDED_LIBRARY @@ -108,26 +108,30 @@ extern void query_cache_insert(const char *packet, ulong length, unsigned pkt_nr); #endif // HAVE_QUERY_CACHE #define update_statistics(A) A +extern my_bool thd_net_is_killed(); +/* Additional instrumentation hooks for the server */ +#include "mysql_com_server.h" #else #define update_statistics(A) +#define thd_net_is_killed() 0 #endif #define TEST_BLOCKING 8 #define MAX_PACKET_LENGTH (256L*256L*256L-1) -static my_bool net_write_buff(NET *net,const uchar *packet,ulong len); - +static my_bool net_write_buff(NET *, const uchar *, ulong); /** Init with packet info. */ -my_bool my_net_init(NET *net, Vio* vio) +my_bool my_net_init(NET *net, Vio* vio, uint my_flags) { DBUG_ENTER("my_net_init"); + DBUG_PRINT("enter", ("my_flags: %u", my_flags)); net->vio = vio; my_net_local_init(net); /* Set some limits */ if (!(net->buff=(uchar*) my_malloc((size_t) net->max_packet+ NET_HEADER_SIZE + COMP_HEADER_SIZE +1, - MYF(MY_WME)))) + MYF(MY_WME | my_flags)))) DBUG_RETURN(1); net->buff_end=net->buff+net->max_packet; net->error=0; net->return_status=0; @@ -139,10 +143,15 @@ my_bool my_net_init(NET *net, Vio* vio) net->net_skip_rest_factor= 0; net->last_errno=0; net->unused= 0; + net->thread_specific_malloc= MY_TEST(my_flags & MY_THREAD_SPECIFIC); +#ifdef MYSQL_SERVER + net->extension= NULL; +#endif - if (vio != 0) /* If real connection */ + if (vio) { - net->fd = vio_fd(vio); /* For perl DBI/DBD */ + /* For perl DBI/DBD. */ + net->fd= vio_fd(vio); #if defined(MYSQL_SERVER) && !defined(__WIN__) if (!(test_flags & TEST_BLOCKING)) { @@ -193,7 +202,9 @@ my_bool net_realloc(NET *net, size_t length) */ if (!(buff= (uchar*) my_realloc((char*) net->buff, pkt_length + NET_HEADER_SIZE + COMP_HEADER_SIZE + 1, - MYF(MY_WME)))) + MYF(MY_WME | + (net->thread_specific_malloc ? + MY_THREAD_SPECIFIC : 0))))) { /* @todo: 1 and 2 codes are identical. */ net->error= 1; @@ -255,14 +266,17 @@ static int net_data_is_ready(my_socket sd) if ((res= select((int) (sd + 1), &sfds, NULL, NULL, &tv)) < 0) return 0; else - return test(res ? FD_ISSET(sd, &sfds) : 0); + return MY_TEST(res ? FD_ISSET(sd, &sfds) : 0); #endif /* HAVE_POLL */ } #endif /* EMBEDDED_LIBRARY */ /** - Intialize NET handler for new reads: + Clear (reinitialize) the NET structure for a new command. + + @remark Performs debug checking of the socket buffer to + ensure that the protocol sequence is correct. - Read from socket until there is nothing more to read. Discard what is read. @@ -295,7 +309,7 @@ void net_clear(NET *net, my_bool clear_buffer __attribute__((unused))) { size_t count; int ready; - while ((ready= net_data_is_ready(net->vio->sd)) > 0) + while ((ready= net_data_is_ready(vio_fd(net->vio))) > 0) { /* The socket is ready */ if ((long) (count= vio_read(net->vio, net->buff, @@ -345,9 +359,9 @@ my_bool net_flush(NET *net) DBUG_ENTER("net_flush"); if (net->buff != net->write_pos) { - error=test(net_real_write(net, net->buff, - (size_t) (net->write_pos - net->buff))); - net->write_pos=net->buff; + error= MY_TEST(net_real_write(net, net->buff, + (size_t) (net->write_pos - net->buff))); + net->write_pos= net->buff; } /* Sync packet number if using compression */ if (net->compress) @@ -363,15 +377,13 @@ my_bool net_flush(NET *net) /** Write a logical packet with packet header. - Format: Packet length (3 bytes), packet number(1 byte) - When compression is used a 3 byte compression length is added + Format: Packet length (3 bytes), packet number (1 byte) + When compression is used, a 3 byte compression length is added. - @note - If compression is used the original package is modified! + @note If compression is used, the original packet is modified! */ -my_bool -my_net_write(NET *net,const uchar *packet,size_t len) +my_bool my_net_write(NET *net, const uchar *packet, size_t len) { uchar buff[NET_HEADER_SIZE]; int rc; @@ -411,11 +423,12 @@ my_net_write(NET *net,const uchar *packet,size_t len) #ifndef DEBUG_DATA_PACKETS DBUG_DUMP("packet_header", buff, NET_HEADER_SIZE); #endif - rc= test(net_write_buff(net,packet,len)); + rc= MY_TEST(net_write_buff(net, packet, len)); MYSQL_NET_WRITE_DONE(rc); return rc; } + /** Send a command to the server. @@ -484,9 +497,9 @@ net_write_command(NET *net,uchar command, } int3store(buff,length); buff[3]= (uchar) net->pkt_nr++; - rc= test(net_write_buff(net, buff, header_size) || - (head_len && net_write_buff(net, header, head_len)) || - net_write_buff(net, packet, len) || net_flush(net)); + rc= MY_TEST(net_write_buff(net, buff, header_size) || + (head_len && net_write_buff(net, header, head_len)) || + net_write_buff(net, packet, len) || net_flush(net)); MYSQL_NET_WRITE_DONE(rc); DBUG_RETURN(rc); } @@ -527,7 +540,7 @@ net_write_buff(NET *net, const uchar *packet, ulong len) left_length= (ulong) (net->buff_end - net->write_pos); #ifdef DEBUG_DATA_PACKETS - DBUG_DUMP("data", packet, len); + DBUG_DUMP("data_written", packet, len); #endif if (len > left_length) { @@ -603,7 +616,10 @@ net_real_write(NET *net,const uchar *packet, size_t len) uchar *b; uint header_length=NET_HEADER_SIZE+COMP_HEADER_SIZE; if (!(b= (uchar*) my_malloc(len + NET_HEADER_SIZE + - COMP_HEADER_SIZE + 1, MYF(MY_WME)))) + COMP_HEADER_SIZE + 1, + MYF(MY_WME | + (net->thread_specific_malloc ? + MY_THREAD_SPECIFIC : 0))))) { net->error= 2; net->last_errno= ER_OUT_OF_RESOURCES; @@ -613,7 +629,8 @@ net_real_write(NET *net,const uchar *packet, size_t len) } memcpy(b+header_length,packet,len); - if (my_compress(b+header_length, &len, &complen)) + /* Don't compress error packets (compress == 2) */ + if (net->compress == 2 || my_compress(b+header_length, &len, &complen)) complen=0; int3store(&b[NET_HEADER_SIZE],complen); int3store(b,len); @@ -624,7 +641,7 @@ net_real_write(NET *net,const uchar *packet, size_t len) #endif /* HAVE_COMPRESS */ #ifdef DEBUG_DATA_PACKETS - DBUG_DUMP("data", packet, len); + DBUG_DUMP("data_written", packet, len); #endif #ifndef NO_ALARM @@ -775,7 +792,7 @@ static my_bool my_net_skip_rest(NET *net, uint32 remain, thr_alarm_t *alarmed, { while (remain > 0) { - size_t length= min(remain, net->max_packet); + size_t length= MY_MIN(remain, net->max_packet); if (net_safe_read(net, net->buff, length, alarmed)) DBUG_RETURN(1); update_statistics(thd_increment_bytes_received(length)); @@ -807,12 +824,14 @@ static my_bool my_net_skip_rest(NET *net, uint32 remain, thr_alarm_t *alarmed, */ static ulong -my_real_read(NET *net, size_t *complen) +my_real_read(NET *net, size_t *complen, + my_bool header __attribute__((unused))) { uchar *pos; size_t length; uint i,retry_count=0; ulong len=packet_error; + my_bool expect_error_packet __attribute__((unused))= 0; thr_alarm_t alarmed; #ifndef NO_ALARM ALARM alarm_buff; @@ -820,6 +839,21 @@ my_real_read(NET *net, size_t *complen) my_bool net_blocking=vio_is_blocking(net->vio); uint32 remain= (net->compress ? NET_HEADER_SIZE+COMP_HEADER_SIZE : NET_HEADER_SIZE); +#ifdef MYSQL_SERVER + size_t count= remain; + struct st_net_server *server_extension= 0; + + if (header) + { + server_extension= static_cast<st_net_server*> (net->extension); + if (server_extension != NULL) + { + void *user_data= server_extension->m_user_data; + server_extension->m_before_header(net, user_data, count); + } + } +#endif + *complen = 0; net->reading_or_writing=1; @@ -843,6 +877,17 @@ my_real_read(NET *net, size_t *complen) DBUG_PRINT("info",("vio_read returned %ld errno: %d", (long) length, vio_errno(net->vio))); + + if (i== 0 && thd_net_is_killed()) + { + DBUG_PRINT("info", ("thd is killed")); + len= packet_error; + net->error= 0; + net->last_errno= ER_CONNECTION_KILLED; + MYSQL_SERVER_my_error(net->last_errno, MYF(0)); + goto end; + } + #if !defined(__WIN__) && defined(MYSQL_SERVER) /* We got an error that there was no data on the socket. We now set up @@ -885,7 +930,7 @@ my_real_read(NET *net, size_t *complen) my_progname,vio_errno(net->vio)); } #ifndef MYSQL_SERVER - if ((long)length < 0 && vio_errno(net->vio) == SOCKET_EINTR) + if (length != 0 && vio_errno(net->vio) == SOCKET_EINTR) { DBUG_PRINT("warning",("Interrupted read. Retrying...")); continue; @@ -895,7 +940,7 @@ my_real_read(NET *net, size_t *complen) remain, vio_errno(net->vio), (long) length)); len= packet_error; net->error= 2; /* Close socket */ - net->last_errno= (vio_was_interrupted(net->vio) ? + net->last_errno= (vio_was_timeout(net->vio) ? ER_NET_READ_INTERRUPTED : ER_NET_READ_ERROR); MYSQL_SERVER_my_error(net->last_errno, MYF(0)); @@ -905,39 +950,34 @@ my_real_read(NET *net, size_t *complen) pos+= length; update_statistics(thd_increment_bytes_received(length)); } + +#ifdef DEBUG_DATA_PACKETS + DBUG_DUMP("data_read", net->buff+net->where_b, length); +#endif if (i == 0) { /* First parts is packet length */ ulong helping; +#ifndef DEBUG_DATA_PACKETS DBUG_DUMP("packet_header", net->buff+net->where_b, NET_HEADER_SIZE); +#endif if (net->buff[net->where_b + 3] != (uchar) net->pkt_nr) - { - if (net->buff[net->where_b] != (uchar) 255) - { - DBUG_PRINT("error", - ("Packets out of order (Found: %d, expected %u)", - (int) net->buff[net->where_b + 3], - net->pkt_nr)); - /* - We don't make noise server side, since the client is expected - to break the protocol for e.g. --send LOAD DATA .. LOCAL where - the server expects the client to send a file, but the client - may reply with a new command instead. - */ + { #ifndef MYSQL_SERVER - EXTRA_DEBUG_fflush(stdout); - EXTRA_DEBUG_fprintf(stderr,"Error: Packets out of order (Found: %d, expected %d)\n", - (int) net->buff[net->where_b + 3], - (uint) (uchar) net->pkt_nr); - EXTRA_DEBUG_fflush(stderr); + if (net->buff[net->where_b + 3] == (uchar) (net->pkt_nr -1)) + { + /* + If the server was killed then the server may have missed the + last sent client packet and the packet numbering may be one off. + */ + DBUG_PRINT("warning", ("Found possible out of order packets")); + expect_error_packet= 1; + } + else #endif - } - len= packet_error; - /* Not a NET error on the client. XXX: why? */ - MYSQL_SERVER_my_error(ER_NET_PACKETS_OUT_OF_ORDER, MYF(0)); - goto end; - } - net->compress_pkt_nr= ++net->pkt_nr; + goto packets_out_of_order; + } + net->compress_pkt_nr= ++net->pkt_nr; #ifdef HAVE_COMPRESS if (net->compress) { @@ -959,7 +999,7 @@ my_real_read(NET *net, size_t *complen) len=uint3korr(net->buff+net->where_b); if (!len) /* End of big multi-packet */ goto end; - helping = max(len,*complen) + net->where_b; + helping = MY_MAX(len,*complen) + net->where_b; /* The necessary size of net->buff */ if (helping >= net->max_packet) { @@ -976,7 +1016,30 @@ my_real_read(NET *net, size_t *complen) } pos=net->buff + net->where_b; remain = (uint32) len; +#ifdef MYSQL_SERVER + if (server_extension != NULL) + { + void *user_data= server_extension->m_user_data; + server_extension->m_after_header(net, user_data, count, 0); + server_extension= NULL; + } +#endif + } +#ifndef MYSQL_SERVER + else if (expect_error_packet) + { + /* + This check is safe both for compressed and not compressed protocol + as for the compressed protocol errors are not compressed anymore. + */ + if (net->buff[net->where_b] != (uchar) 255) + { + /* Restore pkt_nr to original value */ + net->pkt_nr--; + goto packets_out_of_order; + } } +#endif } end: @@ -990,9 +1053,53 @@ end: net->reading_or_writing=0; #ifdef DEBUG_DATA_PACKETS if (len != packet_error) - DBUG_DUMP("data", net->buff+net->where_b, len); + DBUG_DUMP("data_read", net->buff+net->where_b, len); +#endif +#ifdef MYSQL_SERVER + if (server_extension != NULL) + { + void *user_data= server_extension->m_user_data; + server_extension->m_after_header(net, user_data, count, 1); + DBUG_ASSERT(len == packet_error || len == 0); + } #endif return(len); + +packets_out_of_order: + { + DBUG_PRINT("error", + ("Packets out of order (Found: %d, expected %u)", + (int) net->buff[net->where_b + 3], + net->pkt_nr)); + DBUG_ASSERT(0); + /* + We don't make noise server side, since the client is expected + to break the protocol for e.g. --send LOAD DATA .. LOCAL where + the server expects the client to send a file, but the client + may reply with a new command instead. + */ +#ifndef MYSQL_SERVER + EXTRA_DEBUG_fflush(stdout); + EXTRA_DEBUG_fprintf(stderr,"Error: Packets out of order (Found: %d, expected %d)\n", + (int) net->buff[net->where_b + 3], + (uint) (uchar) net->pkt_nr); + EXTRA_DEBUG_fflush(stderr); +#endif + len= packet_error; + MYSQL_SERVER_my_error(ER_NET_PACKETS_OUT_OF_ORDER, MYF(0)); + goto end; + } +} + + + +/* Old interface. See my_net_read_packet() for function description */ + +#undef my_net_read + +ulong my_net_read(NET *net) +{ + return my_net_read_packet(net, 0); } @@ -1007,13 +1114,17 @@ end: If the packet was compressed, its uncompressed and the length of the uncompressed packet is returned. + read_from_server is set when the server is reading a new command + from the client. + @return The function returns the length of the found packet or packet_error. net->read_pos points to the read data. */ + ulong -my_net_read(NET *net) +my_net_read_packet(NET *net, my_bool read_from_server) { size_t len, complen; @@ -1023,7 +1134,7 @@ my_net_read(NET *net) if (!net->compress) { #endif - len = my_real_read(net,&complen); + len = my_real_read(net,&complen, read_from_server); if (len == MAX_PACKET_LENGTH) { /* First packet of a multi-packet. Concatenate the packets */ @@ -1033,7 +1144,7 @@ my_net_read(NET *net) { net->where_b += len; total_length += len; - len = my_real_read(net,&complen); + len = my_real_read(net,&complen, 0); } while (len == MAX_PACKET_LENGTH); if (len != packet_error) len+= total_length; @@ -1125,11 +1236,13 @@ my_net_read(NET *net) } net->where_b=buf_length; - if ((packet_len = my_real_read(net,&complen)) == packet_error) + if ((packet_len = my_real_read(net,&complen, read_from_server)) + == packet_error) { MYSQL_NET_READ_DONE(1, 0); return packet_error; } + read_from_server= 0; if (my_uncompress(net->buff + net->where_b, packet_len, &complen)) { @@ -1160,13 +1273,12 @@ void my_net_set_read_timeout(NET *net, uint timeout) { DBUG_ENTER("my_net_set_read_timeout"); DBUG_PRINT("enter", ("timeout: %d", timeout)); - if (net->read_timeout == timeout) - DBUG_VOID_RETURN; - net->read_timeout= timeout; -#ifdef NO_ALARM - if (net->vio) - vio_timeout(net->vio, 0, timeout); -#endif + if (net->read_timeout != timeout) + { + net->read_timeout= timeout; + if (net->vio) + vio_timeout(net->vio, 0, timeout); + } DBUG_VOID_RETURN; } @@ -1175,12 +1287,11 @@ void my_net_set_write_timeout(NET *net, uint timeout) { DBUG_ENTER("my_net_set_write_timeout"); DBUG_PRINT("enter", ("timeout: %d", timeout)); - if (net->write_timeout == timeout) - DBUG_VOID_RETURN; - net->write_timeout= timeout; -#ifdef NO_ALARM - if (net->vio) - vio_timeout(net->vio, 1, timeout); -#endif + if (net->write_timeout != timeout) + { + net->write_timeout= timeout; + if (net->vio) + vio_timeout(net->vio, 1, timeout); + } DBUG_VOID_RETURN; } |