/** * Convert a chain of GWBUF structures into a single GWBUF structure * * @param orig The chain to convert * @return The contiguous buffer */ GWBUF * gwbuf_make_contiguous(GWBUF *orig) { GWBUF *newbuf; char *ptr; int len; if (orig->next == NULL) return orig; if ((newbuf = gwbuf_alloc(gwbuf_length(orig))) != NULL) { newbuf->gwbuf_type = orig->gwbuf_type; newbuf->hint = hint_dup(orig->hint); ptr = GWBUF_DATA(newbuf); while (orig) { len = GWBUF_LENGTH(orig); memcpy(ptr, GWBUF_DATA(orig), len); ptr += len; orig = gwbuf_consume(orig, len); } } return newbuf; }
void manual_query() { char query[1024]; unsigned int qlen; GWBUF** tmpbuf; free_buffers(); printf("Enter query: "); fgets(query,1024,stdin); qlen = strnlen(query, 1024); if((tmpbuf = malloc(sizeof(GWBUF*)))== NULL){ printf("Error: cannot allocate enough memory.\n"); skygw_log_write(LOGFILE_ERROR,"Error: cannot allocate enough memory.\n"); return; } instance.buffer = tmpbuf; instance.buffer_count = 1; instance.buffer[0] = gwbuf_alloc(qlen + 5); gwbuf_set_type(instance.buffer[0],GWBUF_TYPE_MYSQL); memcpy(instance.buffer[0]->sbuf->data + 5,query,qlen); instance.buffer[0]->sbuf->data[0] = (qlen); instance.buffer[0]->sbuf->data[1] = (qlen << 8); instance.buffer[0]->sbuf->data[2] = (qlen << 16); instance.buffer[0]->sbuf->data[3] = 0x00; instance.buffer[0]->sbuf->data[4] = 0x03; }
int test2() { GWBUF *buffer; char len = 128; char query[129]; buffer = gwbuf_alloc(132); ss_info_dassert((buffer != NULL),"Buffer should not be null"); memset(query,';',128); memset(query+128,'\0',1); *((unsigned char*)buffer->start) = len; *((unsigned char*)buffer->start+1) = 0; *((unsigned char*)buffer->start+2) = 0; *((unsigned char*)buffer->start+3) = 1; *((unsigned char*)buffer->start+4) = 0x03; memcpy(buffer->start + 5,query,strlen(query)); char* result = modutil_get_SQL(buffer); ss_dassert(strcmp(result,query) == 0); gwbuf_free(buffer); free(result); ss_dfprintf(stderr, "\t..done\n"); return 0; }
static int test1() { GWBUF *buffer; char *(sql[100]); int result, length, residual; /* Poll tests */ ss_dfprintf(stderr, "testmodutil : Rudimentary tests."); buffer = gwbuf_alloc(100); ss_info_dassert(0 == modutil_is_SQL(buffer), "Default buffer should be diagnosed as not SQL"); /* There would ideally be some straightforward way to create a SQL buffer? */ ss_dfprintf(stderr, "\t..done\nExtract SQL from buffer"); ss_info_dassert(0 == modutil_extract_SQL(buffer, sql, &length), "Default buffer should fail"); ss_dfprintf(stderr, "\t..done\nExtract SQL from buffer different way?"); ss_info_dassert(0 == modutil_MySQL_Query(buffer, sql, &length, &residual), "Default buffer should fail"); ss_dfprintf(stderr, "\t..done\nReplace SQL in buffer"); ss_info_dassert(0 == modutil_replace_SQL(buffer, "select * from some_table;"), "Default buffer should fail"); ss_dfprintf(stderr, "\t..done\nTidy up."); gwbuf_free(buffer); ss_dfprintf(stderr, "\t..done\n"); return 0; }
/** * Send a response to a "SELECT UNIX_TIMESTAMP()" request. This differs from the other * requests since we do not save a copy of the original interaction with the master * and simply replay it. We want to always send the current time. We have stored a typcial * response, which gives us the schema information normally returned. This is sent to the * client and then we add a dynamic part that will insert the current timestamp data. * Finally we send a preprepaed EOF packet to end the response stream. * * @param router The binlog router instance * @param slave The slave server to which we are sending the response * @return Non-zero if data was sent */ static int blr_slave_send_timestamp(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave) { GWBUF *pkt; char timestamp[20]; uint8_t *ptr; int len, ts_len; sprintf(timestamp, "%ld", time(0)); ts_len = strlen(timestamp); len = sizeof(timestamp_def) + sizeof(timestamp_eof) + 5 + ts_len; if ((pkt = gwbuf_alloc(len)) == NULL) return 0; ptr = GWBUF_DATA(pkt); memcpy(ptr, timestamp_def, sizeof(timestamp_def)); // Fixed preamble ptr += sizeof(timestamp_def); encode_value(ptr, ts_len + 1, 24); // Add length of data packet ptr += 3; *ptr++ = 0x04; // Sequence number in response *ptr++ = ts_len; // Length of result string strncpy((char *)ptr, timestamp, ts_len); // Result string ptr += ts_len; memcpy(ptr, timestamp_eof, sizeof(timestamp_eof)); // EOF packet to terminate result return slave->dcb->func.write(slave->dcb, pkt); }
/** * Process a slave replication registration message. * * We store the various bits of information the slave gives us and generate * a reply message. * * @param router The router instance * @param slave The slave server * @param queue The BINLOG_DUMP packet * @return Non-zero if data was sent */ static int blr_slave_register(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue) { GWBUF *resp; uint8_t *ptr; int len, slen; ptr = GWBUF_DATA(queue); len = extract_field(ptr, 24); ptr += 4; // Skip length and sequence number if (*ptr++ != COM_REGISTER_SLAVE) return 0; slave->serverid = extract_field(ptr, 32); ptr += 4; slen = *ptr++; if (slen != 0) { slave->hostname = strndup((char *)ptr, slen); ptr += slen; } else slave->hostname = NULL; slen = *ptr++; if (slen != 0) { ptr += slen; slave->user = strndup((char *)ptr, slen); } else slave->user = NULL; slen = *ptr++; if (slen != 0) { slave->passwd = strndup((char *)ptr, slen); ptr += slen; } else slave->passwd = NULL; slave->port = extract_field(ptr, 16); ptr += 2; slave->rank = extract_field(ptr, 32); /* * Now construct a response */ if ((resp = gwbuf_alloc(11)) == NULL) return 0; ptr = GWBUF_DATA(resp); encode_value(ptr, 7, 24); // Payload length ptr += 3; *ptr++ = 1; // Sequence number encode_value(ptr, 0, 24); ptr += 3; encode_value(ptr, slave->serverid, 32); slave->state = BLRS_REGISTERED; return slave->dcb->func.write(slave->dcb, resp); }
/** * Enable or disable telnet protocol echo * * @param dcb DCB of the telnet connection * @param enable Enable or disable echo functionality */ static void telnetd_echo(DCB *dcb, int enable) { GWBUF *gwbuf; char *buf; if ((gwbuf = gwbuf_alloc(3)) == NULL) return; buf = GWBUF_DATA(gwbuf); buf[0] = TELNET_IAC; buf[1] = enable ? TELNET_WONT : TELNET_WILL; buf[2] = TELNET_ECHO; dcb_write(dcb, gwbuf); }
/** * Send the field count packet in a response packet sequence. * * @param router The router * @param slave The slave connection * @param count Number of columns in the result set * @return Non-zero on success */ static int blr_slave_send_fieldcount(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, int count) { GWBUF *pkt; uint8_t *ptr; if ((pkt = gwbuf_alloc(5)) == NULL) return 0; ptr = GWBUF_DATA(pkt); encode_value(ptr, 1, 24); // Add length of data packet ptr += 3; *ptr++ = 0x01; // Sequence number in response *ptr++ = count; // Length of result string return slave->dcb->func.write(slave->dcb, pkt); }
GWBUF* gen_packet(PACKET pkt) { unsigned int psize = 0; GWBUF* buff = NULL; unsigned char* ptr; switch(pkt){ case PACKET_OK: psize = 11; break; default: break; } if(psize > 0){ buff = gwbuf_alloc(psize); ptr = (unsigned char*)buff->start; switch(pkt){ case PACKET_OK: ptr[0] = 7; /**Packet size*/ ptr[1] = 0; ptr[2] = 0; ptr[3] = 1; /**sequence_id*/ ptr[4] = 0; /**OK header*/ ptr[5] = 0; /**affected_rows*/ ptr[6] = 0; /**last_insert_id*/ ptr[7] = 0; /**status_flags*/ ptr[8] = 0; ptr[9] = 0; /**warnings*/ ptr[10] = 0; break; default: break; } } return buff; }
int main(int argc, char** argv) { unsigned int psize; GWBUF* qbuff; char *tok; char readbuff[4092]; FILE* infile; FILE* outfile; if(argc != 3){ printf("Usage: canonizer <input file> <output file>\n"); return 1; } if(mysql_library_init(num_elements, server_options, server_groups)){ printf("Embedded server init failed.\n"); return 1; } infile = fopen(argv[1],"rb"); outfile = fopen(argv[2],"wb"); if(infile == NULL || outfile == NULL){ printf("Opening files failed.\n"); return 1; } while(!feof(infile)) { fgets(readbuff,4092,infile); psize = strlen(readbuff); if(psize < 4092){ qbuff = gwbuf_alloc(psize + 7); *(qbuff->sbuf->data + 0) = (unsigned char)psize; *(qbuff->sbuf->data + 1) = (unsigned char)(psize>>8); *(qbuff->sbuf->data + 2) = (unsigned char)(psize>>16); *(qbuff->sbuf->data + 4) = 0x03; memcpy(qbuff->start + 5,readbuff,psize + 1); parse_query(qbuff); tok = skygw_get_canonical(qbuff); fprintf(outfile,"%s\n",tok); free(tok); gwbuf_free(qbuff); } }
/** * Send an OK packet to the client. * @param dcb The DCB that connects to the client */ void maxinfo_send_ok(DCB *dcb) { static const char ok_packet[] ={ 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; GWBUF* buffer = gwbuf_alloc(sizeof(ok_packet)); if (buffer) { memcpy(buffer->start, ok_packet, sizeof(ok_packet)); dcb->func.write(dcb, buffer); } }
/** * Respond to a COM_PING command * * @param router The router instance * @param session The connection that requested the ping * @param queue The ping request */ static int maxinfo_ping(INFO_INSTANCE *router, INFO_SESSION *session, GWBUF *queue) { char *ptr; GWBUF *ret; int len; if ((ret = gwbuf_alloc(5)) == NULL) return 0; ptr = GWBUF_DATA(ret); *ptr++ = 0x01; *ptr++ = 0; *ptr++ = 0; *ptr++ = 1; *ptr = 0; // OK return session->dcb->func.write(session->dcb, ret); }
/** * Send a "fake" format description event to the newly connected slave * * @param router The router instance * @param slave The slave to send the event to */ static void blr_slave_send_fde(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave) { BLFILE *file; REP_HEADER hdr; GWBUF *record, *head; uint8_t *ptr; uint32_t chksum; if ((file = blr_open_binlog(router, slave->binlogfile)) == NULL) return; if ((record = blr_read_binlog(router, file, 4, &hdr)) == NULL) { blr_close_binlog(router, file); return; } blr_close_binlog(router, file); head = gwbuf_alloc(5); ptr = GWBUF_DATA(head); encode_value(ptr, hdr.event_size + 1, 24); // Payload length ptr += 3; *ptr++ = slave->seqno++; *ptr++ = 0; // OK head = gwbuf_append(head, record); ptr = GWBUF_DATA(record); encode_value(ptr, time(0), 32); // Overwrite timestamp ptr += 13; encode_value(ptr, 0, 32); // Set next position to 0 /* * Since we have changed the timestamp we must recalculate the CRC * * Position ptr to the start of the event header, * calculate a new checksum * and write it into the header */ ptr = GWBUF_DATA(record) + hdr.event_size - 4; chksum = crc32(0L, NULL, 0); chksum = crc32(chksum, GWBUF_DATA(record), hdr.event_size - 4); encode_value(ptr, chksum, 32); slave->dcb->func.write(slave->dcb, head); }
/** * Replace the contents of a GWBUF with the new SQL statement passed as a text string. * The routine takes care of the modification needed to the MySQL packet, * returning a GWBUF chain that can be used to send the data to a MySQL server * * @param orig The original request in a GWBUF * @param sql The SQL text to replace in the packet * @return A newly formed GWBUF containing the MySQL packet. */ GWBUF * modutil_replace_SQL(GWBUF *orig, char *sql) { unsigned char *ptr; int length, newlength; GWBUF *addition; if (!modutil_is_SQL(orig)) return NULL; ptr = GWBUF_DATA(orig); length = *ptr++; length += (*ptr++ << 8); length += (*ptr++ << 16); ptr += 2; // Skip sequence id and COM_QUERY byte newlength = strlen(sql); if (length - 1 == newlength) { /* New SQL is the same length as old */ memcpy(ptr, sql, newlength); } else if (length - 1 > newlength) { /* New SQL is shorter */ memcpy(ptr, sql, newlength); GWBUF_RTRIM(orig, (length - 1) - newlength); } else { memcpy(ptr, sql, length - 1); addition = gwbuf_alloc(newlength - (length - 1)); memcpy(GWBUF_DATA(addition), &sql[length - 1], newlength - (length - 1)); ptr = GWBUF_DATA(orig); *ptr++ = (newlength + 1) & 0xff; *ptr++ = ((newlength + 1) >> 8) & 0xff; *ptr++ = ((newlength + 1) >> 16) & 0xff; orig->next = addition; } return orig; }
/** * Construct an error response * * @param router The router instance * @param slave The slave server instance * @param msg The error message to send */ static void blr_slave_send_error(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *msg) { GWBUF *pkt; unsigned char *data; int len; if ((pkt = gwbuf_alloc(strlen(msg) + 13)) == NULL) return; data = GWBUF_DATA(pkt); len = strlen(msg) + 1; encode_value(&data[0], len, 24); // Payload length data[3] = 0; // Sequence id // Payload data[4] = 0xff; // Error indicator data[5] = 0; // Error Code data[6] = 0; // Error Code strncpy((char *)&data[7], "#00000", 6); memcpy(&data[13], msg, strlen(msg)); // Error Message slave->dcb->func.write(slave->dcb, pkt); }
/** * Send a MySQL OK packet to the DCB * * @param dcb The DCB to send the OK packet to * @return result of a write call, non-zero if write was successful */ static int maxinfo_send_ok(DCB *dcb) { GWBUF *buf; uint8_t *ptr; if ((buf = gwbuf_alloc(11)) == NULL) return 0; ptr = GWBUF_DATA(buf); *ptr++ = 7; // Payload length *ptr++ = 0; *ptr++ = 0; *ptr++ = 1; // Seqno *ptr++ = 0; // ok *ptr++ = 0; *ptr++ = 0; *ptr++ = 2; *ptr++ = 0; *ptr++ = 0; *ptr++ = 0; return dcb->func.write(dcb, buf); }
/** * Send the column definition packet in a response packet sequence. * * @param router The router * @param slave The slave connection * @param name Name of the column * @param type Column type * @param len Column length * @param seqno Packet sequence number * @return Non-zero on success */ static int blr_slave_send_columndef(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *name, int type, int len, uint8_t seqno) { GWBUF *pkt; uint8_t *ptr; if ((pkt = gwbuf_alloc(26 + strlen(name))) == NULL) return 0; ptr = GWBUF_DATA(pkt); encode_value(ptr, 22 + strlen(name), 24); // Add length of data packet ptr += 3; *ptr++ = seqno; // Sequence number in response *ptr++ = 3; // Catalog is always def *ptr++ = 'd'; *ptr++ = 'e'; *ptr++ = 'f'; *ptr++ = 0; // Schema name length *ptr++ = 0; // virtal table name length *ptr++ = 0; // Table name length *ptr++ = strlen(name); // Column name length; while (*name) *ptr++ = *name++; // Copy the column name *ptr++ = 0; // Orginal column name *ptr++ = 0x0c; // Length of next fields always 12 *ptr++ = 0x3f; // Character set *ptr++ = 0; encode_value(ptr, len, 32); // Add length of column ptr += 4; *ptr++ = type; *ptr++ = 0x81; // Two bytes of flags if (type == 0xfd) *ptr++ = 0x1f; else *ptr++ = 0x00; *ptr++= 0; *ptr++= 0; *ptr++= 0; return slave->dcb->func.write(slave->dcb, pkt); }
/** * Return some basic statistics from the router in response to a COM_STATISTICS * request. * * @param router The router instance * @param session The connection that requested the statistics * @param queue The statistics request * * @return non-zero on sucessful send */ static int maxinfo_statistics(INFO_INSTANCE *router, INFO_SESSION *session, GWBUF *queue) { char result[1000], *ptr; GWBUF *ret; int len; snprintf(result, 1000, "Uptime: %u Threads: %u Sessions: %u ", maxscale_uptime(), config_threadcount(), serviceSessionCountAll()); if ((ret = gwbuf_alloc(4 + strlen(result))) == NULL) return 0; len = strlen(result); ptr = GWBUF_DATA(ret); *ptr++ = len & 0xff; *ptr++ = (len & 0xff00) >> 8; *ptr++ = (len & 0xff0000) >> 16; *ptr++ = 1; strncpy(ptr, result, len); return session->dcb->func.write(session->dcb, ret); }
/** * Loads a query from a file *@return 0 if successful, 1 if an error occurred */ int load_query() { char** query_list = NULL; char* buffer = NULL; int i, qcount = 0, qbuff_sz = 10, rval = 0; int offset = 0; unsigned int qlen = 0; buffer = (char*)calloc(4092,sizeof(char)); if(buffer == NULL){ printf("Error: cannot allocate enough memory.\n"); MXS_ERROR("cannot allocate enough memory.\n"); return 1; } query_list = calloc(qbuff_sz,sizeof(char*)); if(query_list == NULL){ printf("Error: cannot allocate enough memory.\n"); MXS_ERROR("cannot allocate enough memory.\n"); free(buffer); return 1; } while((offset = fdgets(instance.infile,buffer,4092))){ if(qbuff_sz <= qcount){ char** tmpbuff = realloc(query_list,sizeof(char*)*qbuff_sz*2); if(tmpbuff == NULL){ printf("Error: cannot allocate enough memory.\n"); MXS_ERROR("cannot allocate enough memory.\n"); rval = 1; goto retblock; } query_list = tmpbuff; qbuff_sz *= 2; } query_list[qcount] = calloc((offset + 1),sizeof(char)); strcpy(query_list[qcount],buffer); offset = 0; qcount++; } /**TODO check what messes up the first querystring*/ GWBUF** tmpbff = malloc(sizeof(GWBUF*)*(qcount + 1)); if(tmpbff){ for(i = 0;i<qcount;i++){ tmpbff[i] = gwbuf_alloc(strlen(query_list[i]) + 6); if(tmpbff[i] == NULL) { printf("Error: cannot allocate a new buffer.\n"); MXS_ERROR("cannot allocate a new buffer.\n"); int x; for(x = 0;x<i;x++) { gwbuf_free(tmpbff[x]); } free(tmpbff); rval = 1; goto retblock; } gwbuf_set_type(tmpbff[i],GWBUF_TYPE_MYSQL); strcpy((char*)(tmpbff[i]->start + 5),query_list[i]); qlen = strlen(query_list[i]) + 1; tmpbff[i]->sbuf->data[0] = qlen; tmpbff[i]->sbuf->data[1] = (qlen << 8); tmpbff[i]->sbuf->data[2] = (qlen << 16); tmpbff[i]->sbuf->data[3] = 0x00; tmpbff[i]->sbuf->data[4] = 0x03; } tmpbff[qcount] = NULL; instance.buffer = tmpbff; }else{ printf("Error: cannot allocate enough memory for buffers.\n"); MXS_ERROR("cannot allocate enough memory for buffers.\n"); free_buffers(); rval = 1; goto retblock; } if(qcount < 1){ rval = 1; goto retblock; } instance.buffer_count = qcount; retblock: for(i = 0;i<qcount;i++) { free(query_list[i]); } free(query_list); free(buffer); return rval; }
/** * mysql_send_auth_error * * Send a MySQL protocol ERR message, for gateway authentication error to the dcb * * @param dcb descriptor Control Block for the connection to which the OK is sent * @param packet_number * @param in_affected_rows * @param mysql_message * @return packet length * */ int mysql_send_auth_error (DCB *dcb, int packet_number, int in_affected_rows, const char* mysql_message) { uint8_t *outbuf = NULL; uint8_t mysql_payload_size = 0; uint8_t mysql_packet_header[4]; uint8_t *mysql_payload = NULL; uint8_t field_count = 0; uint8_t mysql_err[2]; uint8_t mysql_statemsg[6]; unsigned int mysql_errno = 0; const char *mysql_error_msg = NULL; const char *mysql_state = NULL; GWBUF *buf; if (dcb->state != DCB_STATE_POLLING) { LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, "%lu [mysql_send_auth_error] dcb %p is in a state %s, " "and it is not in epoll set anymore. Skip error sending.", pthread_self(), dcb, STRDCBSTATE(dcb->state)))); return 0; } mysql_errno = 1045; mysql_error_msg = "Access denied!"; mysql_state = "2800"; field_count = 0xff; gw_mysql_set_byte2(mysql_err, mysql_errno); mysql_statemsg[0]='#'; memcpy(mysql_statemsg+1, mysql_state, 5); if (mysql_message != NULL) { mysql_error_msg = mysql_message; } mysql_payload_size = sizeof(field_count) + sizeof(mysql_err) + sizeof(mysql_statemsg) + strlen(mysql_error_msg); // allocate memory for packet header + payload if ((buf = gwbuf_alloc(sizeof(mysql_packet_header) + mysql_payload_size)) == NULL) { return 0; } outbuf = GWBUF_DATA(buf); // write packet header with packet number gw_mysql_set_byte3(mysql_packet_header, mysql_payload_size); mysql_packet_header[3] = packet_number; // write header memcpy(outbuf, mysql_packet_header, sizeof(mysql_packet_header)); mysql_payload = outbuf + sizeof(mysql_packet_header); // write field memcpy(mysql_payload, &field_count, sizeof(field_count)); mysql_payload = mysql_payload + sizeof(field_count); // write errno memcpy(mysql_payload, mysql_err, sizeof(mysql_err)); mysql_payload = mysql_payload + sizeof(mysql_err); // write sqlstate memcpy(mysql_payload, mysql_statemsg, sizeof(mysql_statemsg)); mysql_payload = mysql_payload + sizeof(mysql_statemsg); // write err messg memcpy(mysql_payload, mysql_error_msg, strlen(mysql_error_msg)); // writing data in the Client buffer queue dcb->func.write(dcb, buf); return sizeof(mysql_packet_header) + mysql_payload_size; }
/** * mysql_send_custom_error * * Send a MySQL protocol Generic ERR message, to the dcb * Note the errno and state are still fixed now * * @param dcb Owner_Dcb Control Block for the connection to which the OK is sent * @param packet_number * @param in_affected_rows * @param mysql_message * @return packet length * */ int mysql_send_custom_error (DCB *dcb, int packet_number, int in_affected_rows, const char* mysql_message) { uint8_t *outbuf = NULL; uint8_t mysql_payload_size = 0; uint8_t mysql_packet_header[4]; uint8_t *mysql_payload = NULL; uint8_t field_count = 0; uint8_t mysql_err[2]; uint8_t mysql_statemsg[6]; unsigned int mysql_errno = 0; const char *mysql_error_msg = NULL; const char *mysql_state = NULL; GWBUF *buf = NULL; if (dcb == NULL || dcb->state != DCB_STATE_POLLING) { return 0; } mysql_errno = 2003; mysql_error_msg = "An errorr occurred ..."; mysql_state = "HY000"; field_count = 0xff; gw_mysql_set_byte2(mysql_err, mysql_errno); mysql_statemsg[0]='#'; memcpy(mysql_statemsg+1, mysql_state, 5); if (mysql_message != NULL) { mysql_error_msg = mysql_message; } mysql_payload_size = sizeof(field_count) + sizeof(mysql_err) + sizeof(mysql_statemsg) + strlen(mysql_error_msg); // allocate memory for packet header + payload buf = gwbuf_alloc(sizeof(mysql_packet_header) + mysql_payload_size); ss_dassert(buf != NULL); if (buf == NULL) { return 0; } outbuf = GWBUF_DATA(buf); // write packet header with packet number gw_mysql_set_byte3(mysql_packet_header, mysql_payload_size); mysql_packet_header[3] = packet_number; // write header memcpy(outbuf, mysql_packet_header, sizeof(mysql_packet_header)); mysql_payload = outbuf + sizeof(mysql_packet_header); // write field memcpy(mysql_payload, &field_count, sizeof(field_count)); mysql_payload = mysql_payload + sizeof(field_count); // write errno memcpy(mysql_payload, mysql_err, sizeof(mysql_err)); mysql_payload = mysql_payload + sizeof(mysql_err); // write sqlstate memcpy(mysql_payload, mysql_statemsg, sizeof(mysql_statemsg)); mysql_payload = mysql_payload + sizeof(mysql_statemsg); // write err messg memcpy(mysql_payload, mysql_error_msg, strlen(mysql_error_msg)); // writing data in the Client buffer queue dcb->func.write(dcb, buf); return sizeof(mysql_packet_header) + mysql_payload_size; }
/** * Write a MySQL CHANGE_USER packet to backend server * * @param conn MySQL protocol structure * @param dbname The selected database * @param user The selected user * @param passwd The SHA1(real_password): Note real_password is unknown * @return 1 on success, 0 on failure */ int gw_send_change_user_to_backend(char *dbname, char *user, uint8_t *passwd, MySQLProtocol *conn) { int compress = 0; int rv; uint8_t *payload = NULL; uint8_t *payload_start = NULL; long bytes; uint8_t client_scramble[GW_MYSQL_SCRAMBLE_SIZE]; uint8_t client_capabilities[4]; uint32_t server_capabilities; uint32_t final_capabilities; char dbpass[MYSQL_USER_MAXLEN + 1]=""; GWBUF *buffer; DCB *dcb; char *curr_db = NULL; uint8_t *curr_passwd = NULL; if (strlen(dbname)) curr_db = dbname; if (strlen((char *)passwd)) curr_passwd = passwd; dcb = conn->owner_dcb; // Zero the vars memset(&server_capabilities, '\0', sizeof(server_capabilities)); memset(&final_capabilities, '\0', sizeof(final_capabilities)); final_capabilities = gw_mysql_get_byte4((uint8_t *)&server_capabilities); final_capabilities |= GW_MYSQL_CAPABILITIES_PROTOCOL_41; final_capabilities |= GW_MYSQL_CAPABILITIES_CLIENT; if (compress) { final_capabilities |= GW_MYSQL_CAPABILITIES_COMPRESS; #ifdef DEBUG_MYSQL_CONN fprintf(stderr, ">>>> Backend Connection with compression\n"); #endif } if (curr_passwd != NULL) { uint8_t hash1[GW_MYSQL_SCRAMBLE_SIZE]=""; uint8_t hash2[GW_MYSQL_SCRAMBLE_SIZE]=""; uint8_t new_sha[GW_MYSQL_SCRAMBLE_SIZE]=""; // hash1 is the function input, SHA1(real_password) memcpy(hash1, passwd, GW_MYSQL_SCRAMBLE_SIZE); // hash2 is the SHA1(input data), where input_data = SHA1(real_password) gw_sha1_str(hash1, GW_MYSQL_SCRAMBLE_SIZE, hash2); // dbpass is the HEX form of SHA1(SHA1(real_password)) gw_bin2hex(dbpass, hash2, GW_MYSQL_SCRAMBLE_SIZE); // new_sha is the SHA1(CONCAT(scramble, hash2) gw_sha1_2_str(conn->scramble, GW_MYSQL_SCRAMBLE_SIZE, hash2, GW_MYSQL_SCRAMBLE_SIZE, new_sha); // compute the xor in client_scramble gw_str_xor(client_scramble, new_sha, hash1, GW_MYSQL_SCRAMBLE_SIZE); } if (curr_db == NULL) { // without db final_capabilities &= ~GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB; } else { final_capabilities |= GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB; } final_capabilities |= GW_MYSQL_CAPABILITIES_PLUGIN_AUTH; gw_mysql_set_byte4(client_capabilities, final_capabilities); // Protocol MySQL COM_CHANGE_USER for CLIENT_PROTOCOL_41 // 1 byte COMMAND bytes = 1; // add the user bytes += strlen(user); // the NULL bytes++; // next will be + 1 (scramble_len) + 20 (fixed_scramble) + (dbname + NULL term) + 2 bytes charset if (curr_passwd != NULL) { bytes += GW_MYSQL_SCRAMBLE_SIZE; bytes++; } else { bytes++; } if (curr_db != NULL) { bytes += strlen(curr_db); bytes++; } // the charset bytes += 2; bytes += strlen("mysql_native_password"); bytes++; // the packet header bytes += 4; // allocating the GWBUF buffer = gwbuf_alloc(bytes); payload = GWBUF_DATA(buffer); // clearing data memset(payload, '\0', bytes); // save the start pointer payload_start = payload; // set packet # = 1 payload[3] = 0x00; payload += 4; // set the command COM_CHANGE_USER \x11 payload[0] = 0x11; payload++; memcpy(payload, user, strlen(user)); payload += strlen(user); payload++; if (curr_passwd != NULL) { // set the auth-length *payload = GW_MYSQL_SCRAMBLE_SIZE; payload++; //copy the 20 bytes scramble data after packet_buffer+36+user+NULL+1 (byte of auth-length) memcpy(payload, client_scramble, GW_MYSQL_SCRAMBLE_SIZE); payload += GW_MYSQL_SCRAMBLE_SIZE; } else { // skip the auth-length and write a NULL payload++; } // if the db is not NULL append it if (curr_db != NULL) { memcpy(payload, curr_db, strlen(curr_db)); payload += strlen(curr_db); payload++; } // set the charset, 2 bytes!!!! *payload = '\x08'; payload++; *payload = '\x00'; payload++; memcpy(payload, "mysql_native_password", strlen("mysql_native_password")); payload += strlen("mysql_native_password"); payload++; // put here the paylod size: bytes to write - 4 bytes packet header gw_mysql_set_byte3(payload_start, (bytes-4)); rv = dcb->func.write(dcb, buffer); if (rv == 0) return 0; else return 1; }
/** * Read event for EPOLLIN on the httpd protocol module. * * @param dcb The descriptor control block * @return */ static int httpd_read_event(DCB* dcb) { SESSION *session = dcb->session; ROUTER_OBJECT *router = session->service->router; ROUTER *router_instance = session->service->router_instance; void *rsession = session->router_session; int numchars = 1; char buf[HTTPD_REQUESTLINE_MAXLEN-1] = ""; char *query_string = NULL; char method[HTTPD_METHOD_MAXLEN-1] = ""; char url[HTTPD_SMALL_BUFFER] = ""; size_t i, j; int headers_read = 0; HTTPD_session *client_data = NULL; GWBUF *uri; client_data = dcb->data; /** * get the request line * METHOD URL HTTP_VER\r\n */ numchars = httpd_get_line(dcb->fd, buf, sizeof(buf)); i = 0; j = 0; while (!ISspace(buf[j]) && (i < sizeof(method) - 1)) { method[i] = buf[j]; i++; j++; } method[i] = '\0'; strcpy(client_data->method, method); /* check allowed http methods */ if (strcasecmp(method, "GET") && strcasecmp(method, "POST")) { //httpd_unimplemented(dcb->fd); return 0; } i = 0; while ( (j < sizeof(buf)) && ISspace(buf[j])) { j++; } while ((j < sizeof(buf) - 1) && !ISspace(buf[j]) && (i < sizeof(url) - 1)) { url[i] = buf[j]; i++; j++; } url[i] = '\0'; /** * Get the query string if availble */ if (strcasecmp(method, "GET") == 0) { query_string = url; while ((*query_string != '?') && (*query_string != '\0')) { query_string++; } if (*query_string == '?') { *query_string = '\0'; query_string++; } } /** * Get the request headers */ while ((numchars > 0) && strcmp("\n", buf)) { char *value = NULL; char *end = NULL; numchars = httpd_get_line(dcb->fd, buf, sizeof(buf)); if ((value = strchr(buf, ':'))) { *value = '\0'; value++; end = &value[strlen(value) -1]; *end = '\0'; if (strncasecmp(buf, "Hostname", 6) == 0) { strcpy(client_data->hostname, value); } if (strncasecmp(buf, "useragent", 9) == 0) { strcpy(client_data->useragent, value); } } } if (numchars) { headers_read = 1; memcpy(&client_data->headers_received, &headers_read, sizeof(int)); } /** * Now begins the server reply */ /* send all the basic headers and close with \r\n */ httpd_send_headers(dcb, 1); #if 0 /** * ToDO: launch proper content handling based on the requested URI, later REST interface * */ if (strcmp(url, "/show") == 0) { if (query_string && strlen(query_string)) { if (strcmp(query_string, "dcb") == 0) { dprintAllDCBs(dcb); } if (strcmp(query_string, "session") == 0) { dprintAllSessions(dcb); } } } if (strcmp(url, "/services") == 0) { RESULTSET *set, *seviceGetList(); if ((set = serviceGetList()) != NULL) { resultset_stream_json(set, dcb); resultset_free(set); } } #endif if ((uri = gwbuf_alloc(strlen(url) + 1)) != NULL) { strcpy((char *)GWBUF_DATA(uri), url); gwbuf_set_type(uri, GWBUF_TYPE_HTTP); SESSION_ROUTE_QUERY(session, uri); } /* force the client connecton close */ dcb_close(dcb); return 0; }
int main(int argc, char** argv) { if(argc < 3){ fprintf(stderr,"Usage: classify <input> <expected output>"); return 1; } int rd = 0,buffsz = getpagesize(),strsz = 0,ex_val = 0; char buffer[1024], *strbuff = (char*)calloc(buffsz,sizeof(char)); FILE *input,*expected; if(mysql_library_init(num_elements, server_options, server_groups)) { printf("Error: Cannot initialize Embedded Library."); return 1; } input = fopen(argv[1],"rb"); if(input == NULL) { printf("Error: Failed to open input file %s", argv[1]); return 1; } expected = fopen(argv[2],"rb"); if(expected == NULL) { fclose(input); printf("Error: Failed to open expected output file %s", argv[2]); return 1; } while((rd = fread(buffer,sizeof(char),1023,input))){ /**Fill the read buffer*/ if(strsz + rd >= buffsz){ char* tmp = realloc(strbuff,(buffsz*2)*sizeof(char)); if(tmp == NULL){ free(strbuff); fclose(input); fclose(expected); mysql_library_end(); fprintf(stderr,"Error: Memory allocation failed."); return 1; } strbuff = tmp; buffsz *= 2; } memcpy(strbuff+strsz,buffer,rd); strsz += rd; *(strbuff+strsz) = '\0'; char *tok,*nlptr; /**Remove newlines*/ while((nlptr = strpbrk(strbuff,"\n")) != NULL && (nlptr - strbuff) < strsz){ memmove(nlptr,nlptr+1,strsz - (nlptr + 1 - strbuff)); strsz -= 1; } /**Parse read buffer for full queries*/ while(strpbrk(strbuff,";") != NULL){ tok = strpbrk(strbuff,";"); unsigned int qlen = tok - strbuff + 1; GWBUF* buff = gwbuf_alloc(qlen+6); *((unsigned char*)(buff->start)) = qlen; *((unsigned char*)(buff->start + 1)) = (qlen >> 8); *((unsigned char*)(buff->start + 2)) = (qlen >> 16); *((unsigned char*)(buff->start + 3)) = 0x00; *((unsigned char*)(buff->start + 4)) = 0x03; memcpy(buff->start+5, strbuff, qlen); memmove(strbuff,tok + 1, strsz - qlen); strsz -= qlen; memset(strbuff + strsz,0,buffsz - strsz); skygw_query_type_t type = query_classifier_get_type(buff); char qtypestr[64]; char expbuff[256]; int expos = 0; while((rd = fgetc(expected)) != '\n' && !feof(expected)){ expbuff[expos++] = rd; } expbuff[expos] = '\0'; if(type == QUERY_TYPE_UNKNOWN){ sprintf(qtypestr,"QUERY_TYPE_UNKNOWN"); } if(type & QUERY_TYPE_LOCAL_READ){ sprintf(qtypestr,"QUERY_TYPE_LOCAL_READ"); } if(type & QUERY_TYPE_READ){ sprintf(qtypestr,"QUERY_TYPE_READ"); } if(type & QUERY_TYPE_WRITE){ sprintf(qtypestr,"QUERY_TYPE_WRITE"); } if(type & QUERY_TYPE_MASTER_READ){ sprintf(qtypestr,"QUERY_TYPE_MASTER_READ"); } if(type & QUERY_TYPE_SESSION_WRITE){ sprintf(qtypestr,"QUERY_TYPE_SESSION_WRITE"); } if(type & QUERY_TYPE_USERVAR_READ){ sprintf(qtypestr,"QUERY_TYPE_USERVAR_READ"); } if(type & QUERY_TYPE_SYSVAR_READ){ sprintf(qtypestr,"QUERY_TYPE_SYSVAR_READ"); } if(type & QUERY_TYPE_GSYSVAR_READ){ sprintf(qtypestr,"QUERY_TYPE_GSYSVAR_READ"); } if(type & QUERY_TYPE_GSYSVAR_WRITE){ sprintf(qtypestr,"QUERY_TYPE_GSYSVAR_WRITE"); } if(type & QUERY_TYPE_BEGIN_TRX){ sprintf(qtypestr,"QUERY_TYPE_BEGIN_TRX"); } if(type & QUERY_TYPE_ENABLE_AUTOCOMMIT){ sprintf(qtypestr,"QUERY_TYPE_ENABLE_AUTOCOMMIT"); } if(type & QUERY_TYPE_DISABLE_AUTOCOMMIT){ sprintf(qtypestr,"QUERY_TYPE_DISABLE_AUTOCOMMIT"); } if(type & QUERY_TYPE_ROLLBACK){ sprintf(qtypestr,"QUERY_TYPE_ROLLBACK"); } if(type & QUERY_TYPE_COMMIT){ sprintf(qtypestr,"QUERY_TYPE_COMMIT"); } if(type & QUERY_TYPE_PREPARE_NAMED_STMT){ sprintf(qtypestr,"QUERY_TYPE_PREPARE_NAMED_STMT"); } if(type & QUERY_TYPE_PREPARE_STMT){ sprintf(qtypestr,"QUERY_TYPE_PREPARE_STMT"); } if(type & QUERY_TYPE_EXEC_STMT){ sprintf(qtypestr,"QUERY_TYPE_EXEC_STMT"); } if(type & QUERY_TYPE_CREATE_TMP_TABLE){ sprintf(qtypestr,"QUERY_TYPE_CREATE_TMP_TABLE"); } if(type & QUERY_TYPE_READ_TMP_TABLE){ sprintf(qtypestr,"QUERY_TYPE_READ_TMP_TABLE"); } if(strcmp(qtypestr,expbuff) != 0){ printf("Error in output: '%s' was expected but got '%s'",expbuff,qtypestr); ex_val = 1; } gwbuf_free(buff); } } fclose(input); fclose(expected); mysql_library_end(); free(strbuff); return ex_val; }
/** * Generate an internal rotate event that we can use to cause the slave to move beyond * a binlog file that is misisng the rotate eent at the end. * * @param router The router instance * @param slave The slave to rotate * @return Non-zero if the rotate took place */ static int blr_slave_fake_rotate(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave) { char *sptr; int filenum; GWBUF *resp; uint8_t *ptr; int len, binlognamelen; REP_HEADER hdr; uint32_t chksum; if ((sptr = strrchr(slave->binlogfile, '.')) == NULL) return 0; blr_close_binlog(router, slave->file); filenum = atoi(sptr + 1); sprintf(slave->binlogfile, BINLOG_NAMEFMT, router->fileroot, filenum + 1); slave->binlog_pos = 4; if ((slave->file = blr_open_binlog(router, slave->binlogfile)) == NULL) return 0; binlognamelen = strlen(slave->binlogfile); if (slave->nocrc) len = 19 + 8 + binlognamelen; else len = 19 + 8 + 4 + binlognamelen; // Build a fake rotate event resp = gwbuf_alloc(len + 5); hdr.payload_len = len + 1; hdr.seqno = slave->seqno++; hdr.ok = 0; hdr.timestamp = 0L; hdr.event_type = ROTATE_EVENT; hdr.serverid = router->masterid; hdr.event_size = len; hdr.next_pos = 0; hdr.flags = 0x20; ptr = blr_build_header(resp, &hdr); encode_value(ptr, slave->binlog_pos, 64); ptr += 8; memcpy(ptr, slave->binlogfile, binlognamelen); ptr += binlognamelen; if (!slave->nocrc) { /* * Now add the CRC to the fake binlog rotate event. * * The algorithm is first to compute the checksum of an empty buffer * and then the checksum of the event portion of the message, ie we do not * include the length, sequence number and ok byte that makes up the first * 5 bytes of the message. We also do not include the 4 byte checksum itself. */ chksum = crc32(0L, NULL, 0); chksum = crc32(chksum, GWBUF_DATA(resp) + 5, hdr.event_size - 4); encode_value(ptr, chksum, 32); } slave->dcb->func.write(slave->dcb, resp); return 1; }
/** * We have a registered slave that is behind the current leading edge of the * binlog. We must replay the log entries to bring this node up to speed. * * There may be a large number of records to send to the slave, the process * is triggered by the slave COM_BINLOG_DUMP message and all the events must * be sent without receiving any new event. This measn there is no trigger into * MaxScale other than this initial message. However, if we simply send all the * events we end up with an extremely long write queue on the DCB and risk * running the server out of resources. * * The slave catchup routine will send a burst of replication events per single * call. The paramter "long" control the number of events in the burst. The * short burst is intended to be used when the master receive an event and * needs to put the slave into catchup mode. This prevents the slave taking * too much tiem away from the thread that is processing the master events. * * At the end of the burst a fake EPOLLOUT event is added to the poll event * queue. This ensures that the slave callback for processing DCB write drain * will be called and future catchup requests will be handled on another thread. * * @param router The binlog router * @param slave The slave that is behind * @param large Send a long or short burst of events * @return The number of bytes written */ int blr_slave_catchup(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, bool large) { GWBUF *head, *record; REP_HEADER hdr; int written, rval = 1, burst; int rotating; unsigned long burst_size; uint8_t *ptr; if (large) burst = router->long_burst; else burst = router->short_burst; burst_size = router->burst_size; spinlock_acquire(&slave->catch_lock); if (slave->cstate & CS_BUSY) { spinlock_release(&slave->catch_lock); return 0; } slave->cstate |= CS_BUSY; spinlock_release(&slave->catch_lock); if (slave->file == NULL) { rotating = router->rotating; if ((slave->file = blr_open_binlog(router, slave->binlogfile)) == NULL) { if (rotating) { spinlock_acquire(&slave->catch_lock); slave->cstate |= CS_EXPECTCB; slave->cstate &= ~CS_BUSY; spinlock_release(&slave->catch_lock); poll_fake_write_event(slave->dcb); return rval; } LOGIF(LE, (skygw_log_write( LOGFILE_ERROR, "blr_slave_catchup failed to open binlog file %s", slave->binlogfile))); slave->cstate &= ~CS_BUSY; slave->state = BLRS_ERRORED; dcb_close(slave->dcb); return 0; } } slave->stats.n_bursts++; while (burst-- && burst_size > 0 && (record = blr_read_binlog(router, slave->file, slave->binlog_pos, &hdr)) != NULL) { head = gwbuf_alloc(5); ptr = GWBUF_DATA(head); encode_value(ptr, hdr.event_size + 1, 24); ptr += 3; *ptr++ = slave->seqno++; *ptr++ = 0; // OK head = gwbuf_append(head, record); if (hdr.event_type == ROTATE_EVENT) { unsigned long beat1 = hkheartbeat; blr_close_binlog(router, slave->file); if (hkheartbeat - beat1 > 1) LOGIF(LE, (skygw_log_write( LOGFILE_ERROR, "blr_close_binlog took %d beats", hkheartbeat - beat1))); blr_slave_rotate(slave, GWBUF_DATA(record)); beat1 = hkheartbeat; if ((slave->file = blr_open_binlog(router, slave->binlogfile)) == NULL) { if (rotating) { spinlock_acquire(&slave->catch_lock); slave->cstate |= CS_EXPECTCB; slave->cstate &= ~CS_BUSY; spinlock_release(&slave->catch_lock); poll_fake_write_event(slave->dcb); return rval; } LOGIF(LE, (skygw_log_write( LOGFILE_ERROR, "blr_slave_catchup failed to open binlog file %s", slave->binlogfile))); slave->state = BLRS_ERRORED; dcb_close(slave->dcb); break; } if (hkheartbeat - beat1 > 1) LOGIF(LE, (skygw_log_write( LOGFILE_ERROR, "blr_open_binlog took %d beats", hkheartbeat - beat1))); } slave->stats.n_bytes += gwbuf_length(head); written = slave->dcb->func.write(slave->dcb, head); if (written && hdr.event_type != ROTATE_EVENT) { slave->binlog_pos = hdr.next_pos; } rval = written; slave->stats.n_events++; burst_size -= hdr.event_size; } if (record == NULL) slave->stats.n_failed_read++; spinlock_acquire(&slave->catch_lock); slave->cstate &= ~CS_BUSY; spinlock_release(&slave->catch_lock); if (record) { slave->stats.n_flows++; spinlock_acquire(&slave->catch_lock); slave->cstate |= CS_EXPECTCB; spinlock_release(&slave->catch_lock); poll_fake_write_event(slave->dcb); } else if (slave->binlog_pos == router->binlog_position && strcmp(slave->binlogfile, router->binlog_name) == 0) { int state_change = 0; spinlock_acquire(&router->binlog_lock); spinlock_acquire(&slave->catch_lock); /* * Now check again since we hold the router->binlog_lock * and slave->catch_lock. */ if (slave->binlog_pos != router->binlog_position || strcmp(slave->binlogfile, router->binlog_name) != 0) { slave->cstate &= ~CS_UPTODATE; slave->cstate |= CS_EXPECTCB; spinlock_release(&slave->catch_lock); spinlock_release(&router->binlog_lock); poll_fake_write_event(slave->dcb); } else { if ((slave->cstate & CS_UPTODATE) == 0) { slave->stats.n_upd++; slave->cstate |= CS_UPTODATE; spinlock_release(&slave->catch_lock); spinlock_release(&router->binlog_lock); state_change = 1; } } if (state_change) { slave->stats.n_caughtup++; if (slave->stats.n_caughtup == 1) { LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, "%s: Slave %s is up to date %s, %u.", router->service->name, slave->dcb->remote, slave->binlogfile, slave->binlog_pos))); } else if ((slave->stats.n_caughtup % 50) == 0) { LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, "%s: Slave %s is up to date %s, %u.", router->service->name, slave->dcb->remote, slave->binlogfile, slave->binlog_pos))); } } } else { if (slave->binlog_pos >= blr_file_size(slave->file) && router->rotating == 0 && strcmp(router->binlog_name, slave->binlogfile) != 0 && blr_master_connected(router)) { /* We may have reached the end of file of a non-current * binlog file. * * Note if the master is rotating there is a window during * which the rotate event has been written to the old binlog * but the new binlog file has not yet been created. Therefore * we ignore these issues during the rotate processing. */ LOGIF(LE, (skygw_log_write(LOGFILE_ERROR, "Slave reached end of file for binlong file %s at %u " "which is not the file currently being downloaded. " "Master binlog is %s, %lu.", slave->binlogfile, slave->binlog_pos, router->binlog_name, router->binlog_position))); if (blr_slave_fake_rotate(router, slave)) { spinlock_acquire(&slave->catch_lock); slave->cstate |= CS_EXPECTCB; spinlock_release(&slave->catch_lock); poll_fake_write_event(slave->dcb); } else { slave->state = BLRS_ERRORED; dcb_close(slave->dcb); } } else { spinlock_acquire(&slave->catch_lock); slave->cstate |= CS_EXPECTCB; spinlock_release(&slave->catch_lock); poll_fake_write_event(slave->dcb); } } return rval; }
/** * Process a COM_BINLOG_DUMP message from the slave. This is the * final step in the process of registration. The new master, MaxScale * must send a response packet and generate a fake BINLOG_ROTATE event * with the binlog file requested by the slave. And then send a * FORMAT_DESCRIPTION_EVENT that has been saved from the real master. * * Once send MaxScale must continue to send binlog events to the slave. * * @param router The router instance * @param slave The slave server * @param queue The BINLOG_DUMP packet * @return The number of bytes written to the slave */ static int blr_slave_binlog_dump(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue) { GWBUF *resp; uint8_t *ptr; int len, flags, serverid, rval, binlognamelen; REP_HEADER hdr; uint32_t chksum; ptr = GWBUF_DATA(queue); len = extract_field(ptr, 24); binlognamelen = len - 11; ptr += 4; // Skip length and sequence number if (*ptr++ != COM_BINLOG_DUMP) { LOGIF(LE, (skygw_log_write( LOGFILE_ERROR, "blr_slave_binlog_dump expected a COM_BINLOG_DUMP but received %d", *(ptr-1)))); return 0; } slave->binlog_pos = extract_field(ptr, 32); ptr += 4; flags = extract_field(ptr, 16); ptr += 2; serverid = extract_field(ptr, 32); ptr += 4; strncpy(slave->binlogfile, (char *)ptr, binlognamelen); slave->binlogfile[binlognamelen] = 0; slave->seqno = 1; if (slave->nocrc) len = 19 + 8 + binlognamelen; else len = 19 + 8 + 4 + binlognamelen; // Build a fake rotate event resp = gwbuf_alloc(len + 5); hdr.payload_len = len + 1; hdr.seqno = slave->seqno++; hdr.ok = 0; hdr.timestamp = 0L; hdr.event_type = ROTATE_EVENT; hdr.serverid = router->masterid; hdr.event_size = len; hdr.next_pos = 0; hdr.flags = 0x20; ptr = blr_build_header(resp, &hdr); encode_value(ptr, slave->binlog_pos, 64); ptr += 8; memcpy(ptr, slave->binlogfile, binlognamelen); ptr += binlognamelen; if (!slave->nocrc) { /* * Now add the CRC to the fake binlog rotate event. * * The algorithm is first to compute the checksum of an empty buffer * and then the checksum of the event portion of the message, ie we do not * include the length, sequence number and ok byte that makes up the first * 5 bytes of the message. We also do not include the 4 byte checksum itself. */ chksum = crc32(0L, NULL, 0); chksum = crc32(chksum, GWBUF_DATA(resp) + 5, hdr.event_size - 4); encode_value(ptr, chksum, 32); } rval = slave->dcb->func.write(slave->dcb, resp); /* Send the FORMAT_DESCRIPTION_EVENT */ if (slave->binlog_pos != 4) blr_slave_send_fde(router, slave); slave->dcb->low_water = router->low_water; slave->dcb->high_water = router->high_water; dcb_add_callback(slave->dcb, DCB_REASON_DRAINED, blr_slave_callback, slave); slave->state = BLRS_DUMPING; LOGIF(LM, (skygw_log_write( LOGFILE_MESSAGE, "%s: New slave %s, server id %d, requested binlog file %s from position %lu", router->service->name, slave->dcb->remote, slave->serverid, slave->binlogfile, slave->binlog_pos))); if (slave->binlog_pos != router->binlog_position || strcmp(slave->binlogfile, router->binlog_name) != 0) { spinlock_acquire(&slave->catch_lock); slave->cstate &= ~CS_UPTODATE; slave->cstate |= CS_EXPECTCB; spinlock_release(&slave->catch_lock); poll_fake_write_event(slave->dcb); } return rval; }