/** * Trim bytes form the end of a GWBUF structure. If the * buffer has n_bytes or less then it will be freed and * NULL will be returned. * * This routine assumes the buffer is not part of a chain * * @param buf The buffer to trim * @param n_bytes The number of bytes to trim off * @return The buffer chain or NULL if buffer has <= n_bytes */ GWBUF * gwbuf_trim(GWBUF *buf, unsigned int n_bytes) { ss_dassert(buf->next == NULL); if (GWBUF_LENGTH(buf) <= n_bytes) { gwbuf_consume(buf, GWBUF_LENGTH(buf)); return NULL; } buf->end = (void *)((char *)buf->end - n_bytes); return buf; }
/** * We have data from the client, we must route it to the backend. * This is simply a case of sending it to the connection that was * chosen when we started the client session. * * @param instance The router instance * @param router_session The router session returned from the newSession call * @param queue The queue of data buffers to route * @return The number of bytes sent */ static int execute(ROUTER *instance, void *router_session, GWBUF *queue) { CLI_SESSION *session = (CLI_SESSION *)router_session; /* Extract the characters */ while (queue) { strncat(session->cmdbuf, GWBUF_DATA(queue), MIN(GWBUF_LENGTH(queue),cmdbuflen-1)); queue = gwbuf_consume(queue, GWBUF_LENGTH(queue)); } execute_cmd(session); return 1; }
/** * 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; }
GWBUF *gwbuf_clone_portion( GWBUF *buf, size_t start_offset, size_t length) { GWBUF* clonebuf; CHK_GWBUF(buf); ss_dassert(start_offset+length <= GWBUF_LENGTH(buf)); if ((clonebuf = (GWBUF *)malloc(sizeof(GWBUF))) == NULL) { ss_dassert(clonebuf != NULL); LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Memory allocation failed due to %s.", strerror(errno)))); return NULL; } atomic_add(&buf->sbuf->refcount, 1); clonebuf->sbuf = buf->sbuf; clonebuf->gwbuf_type = buf->gwbuf_type; /*< clone info bits too */ clonebuf->start = (void *)((char*)buf->start+start_offset); clonebuf->end = (void *)((char *)clonebuf->start+length); clonebuf->gwbuf_type = buf->gwbuf_type; /*< clone the type for now */ clonebuf->properties = NULL; clonebuf->hint = NULL; clonebuf->gwbuf_info = buf->gwbuf_info; clonebuf->gwbuf_bufobj = buf->gwbuf_bufobj; clonebuf->next = NULL; clonebuf->tail = clonebuf; CHK_GWBUF(clonebuf); return clonebuf; }
/** * * @param my_instance * @param my_session * @param buffer * @return */ GWBUF* clone_query(TEE_INSTANCE* my_instance, TEE_SESSION* my_session, GWBUF* buffer) { GWBUF* clone = NULL; int length, residual = 0; char* ptr; if (my_session->branch_session && my_session->branch_session->state == SESSION_STATE_ROUTER_READY) { if (my_session->residual) { clone = gwbuf_clone_all(buffer); if (my_session->residual < GWBUF_LENGTH(clone)) { GWBUF_RTRIM(clone, GWBUF_LENGTH(clone) - residual); } my_session->residual -= GWBUF_LENGTH(clone); if (my_session->residual < 0) { my_session->residual = 0; } } else if (my_session->active && (ptr = modutil_get_SQL(buffer)) != NULL) { if ((my_instance->match == NULL || regexec(&my_instance->re, ptr, 0, NULL, 0) == 0) && (my_instance->nomatch == NULL || regexec(&my_instance->nore,ptr,0,NULL, 0) != 0)) { length = modutil_MySQL_query_len(buffer, &residual); clone = gwbuf_clone_all(buffer); my_session->residual = residual; } free(ptr); } else if (packet_is_required(buffer)) { clone = gwbuf_clone_all(buffer); } } return clone; }
/** * Check if a GWBUF structure is a MySQL COM_QUERY packet * * @param buf Buffer to check * @return True if GWBUF is a COM_QUERY packet */ int modutil_is_SQL(GWBUF *buf) { unsigned char *ptr; if (GWBUF_LENGTH(buf) < 5) return 0; ptr = GWBUF_DATA(buf); return ptr[4] == 0x03; // COM_QUERY }
/** * Check if a GWBUF structure is a MySQL COM_STMT_PREPARE packet * * @param buf Buffer to check * @return True if GWBUF is a COM_STMT_PREPARE packet */ int modutil_is_SQL_prepare(GWBUF *buf) { unsigned char *ptr; if (GWBUF_LENGTH(buf) < 5) return 0; ptr = GWBUF_DATA(buf); return ptr[4] == 0x16 ; // COM_STMT_PREPARE }
/** * We have data from the client, we must route it to the backend. * This is simply a case of sending it to the connection that was * chosen when we started the client session. * * @param instance The router instance * @param router_session The router session returned from the newSession call * @param queue The queue of data buffers to route * @return The number of bytes sent */ static int execute(ROUTER *instance, void *router_session, GWBUF *queue) { CLI_SESSION *session = (CLI_SESSION *)router_session; /* Extract the characters */ while (queue) { strncat(session->cmdbuf, GWBUF_DATA(queue), GWBUF_LENGTH(queue)); queue = gwbuf_consume(queue, GWBUF_LENGTH(queue)); } if (strrchr(session->cmdbuf, '\n')) { if (execute_cmd(session)) dcb_printf(session->session->client, "MaxScale> "); else session->session->client->func.close(session->session->client); } return 1; }
/** * Return the number of bytes of data in the linked list. * * @param head The current head of the linked list * @return The number of bytes of data in the linked list */ unsigned int gwbuf_length(GWBUF *head) { int rval = 0; CHK_GWBUF(head); while (head) { rval += GWBUF_LENGTH(head); head = head->next; } return rval; }
/** * We have data from the client, this is a SQL command, or other MySQL * packet type. * * @param instance The router instance * @param router_session The router session returned from the newSession call * @param queue The queue of data buffers to route * @return The number of bytes sent */ static int execute(ROUTER *rinstance, void *router_session, GWBUF *queue) { INFO_INSTANCE *instance = (INFO_INSTANCE *)rinstance; INFO_SESSION *session = (INFO_SESSION *)router_session; uint8_t *data; int length, len, residual; char *sql; if (GWBUF_TYPE(queue) == GWBUF_TYPE_HTTP) { return handle_url(instance, session, queue); } if (session->queue) { queue = gwbuf_append(session->queue, queue); session->queue = NULL; queue = gwbuf_make_contiguous(queue); } data = (uint8_t *)GWBUF_DATA(queue); length = data[0] + (data[1] << 8) + (data[2] << 16); if (length + 4 > GWBUF_LENGTH(queue)) { // Incomplete packet, must be buffered session->queue = queue; return 1; } // We have a complete request in a signle buffer if (modutil_MySQL_Query(queue, &sql, &len, &residual)) { sql = strndup(sql, len); int rc = maxinfo_execute_query(instance, session, sql); free(sql); return rc; } else { switch (MYSQL_COMMAND(queue)) { case COM_PING: return maxinfo_ping(instance, session, queue); case COM_STATISTICS: return maxinfo_statistics(instance, session, queue); default: MXS_ERROR("maxinfo: Unexpected MySQL command 0x%x", MYSQL_COMMAND(queue)); } } return 1; }
/** * Determine if the packet is a command that must be sent to the branch * to maintain the session consistancy. These are COM_INIT_DB, * COM_CHANGE_USER and COM_QUIT packets. * * @param queue The buffer to check * @return non-zero if the packet should be sent to the branch */ static int packet_is_required(GWBUF *queue) { uint8_t *ptr; int i; ptr = GWBUF_DATA(queue); if (GWBUF_LENGTH(queue) > 4) for (i = 0; required_packets[i]; i++) if (ptr[4] == required_packets[i]) return 1; return 0; }
/** * Returns pointer to GWBUF of a requested type. * As of 10.3.14 only MySQL to plain text conversion is supported. * Return NULL if conversion between types is not supported or due lacking * type information. */ GWBUF *gwbuf_clone_transform( GWBUF * head, gwbuf_type_t targettype) { gwbuf_type_t src_type; GWBUF* clonebuf; CHK_GWBUF(head); src_type = head->gwbuf_type; if (targettype == GWBUF_TYPE_UNDEFINED || src_type == GWBUF_TYPE_UNDEFINED || src_type == GWBUF_TYPE_PLAINSQL || targettype == src_type) { clonebuf = NULL; goto return_clonebuf; } switch (src_type) { case GWBUF_TYPE_MYSQL: if (targettype == GWBUF_TYPE_PLAINSQL) { /** Crete reference to string part of buffer */ clonebuf = gwbuf_clone_portion( head, 5, GWBUF_LENGTH(head)-5); ss_dassert(clonebuf != NULL); /** Overwrite the type with new format */ clonebuf->gwbuf_type = targettype; } else { clonebuf = NULL; } break; default: clonebuf = NULL; break; } /*< switch (src_type) */ return_clonebuf: return clonebuf; }
/** * Returns pointer to GWBUF of a requested type. * As of 10.3.14 only MySQL to plain text conversion is supported. * Return NULL if conversion between types is not supported or due lacking * type information. */ GWBUF *gwbuf_clone_transform( GWBUF * head, gwbuf_type_t targettype) { gwbuf_type_t src_type; GWBUF* clonebuf; CHK_GWBUF(head); src_type = head->gwbuf_type; if (targettype == GWBUF_TYPE_UNDEFINED || src_type == GWBUF_TYPE_UNDEFINED || src_type == GWBUF_TYPE_PLAINSQL || targettype == src_type) { clonebuf = NULL; goto return_clonebuf; } if (GWBUF_IS_TYPE_MYSQL(head)) { if (GWBUF_TYPE_PLAINSQL == targettype) { /** Crete reference to string part of buffer */ clonebuf = gwbuf_clone_portion( head, 5, GWBUF_LENGTH(head)-5); ss_dassert(clonebuf != NULL); /** Overwrite the type with new format */ gwbuf_set_type(clonebuf, targettype); } else { clonebuf = NULL; } } else { clonebuf = NULL; } return_clonebuf: return clonebuf; }
/** * Extract the SQL portion of a COM_QUERY packet * * NB This sets *sql to point into the packet and does not * allocate any new storage. The string pointed to by *sql is * not NULL terminated. * * The number of bytes pointed to *sql is returned in *length * * The remaining number of bytes required for the complete query string * are returned in *residual * * @param buf The packet buffer * @param sql Pointer that is set to point at the SQL data * @param length Length of the SQL query data pointed to by sql * @param residual Any remain part of the query in future packets * @return True if the packet is a COM_QUERY packet */ int modutil_MySQL_Query(GWBUF *buf, char **sql, int *length, int *residual) { unsigned char *ptr; if (!modutil_is_SQL(buf)) return 0; ptr = GWBUF_DATA(buf); *residual = *ptr++; *residual += (*ptr++ << 8); *residual += (*ptr++ << 16); ptr += 2; // Skip sequence id and COM_QUERY byte *residual = *residual - 1; *length = GWBUF_LENGTH(buf) - 5; *residual -= *length; *sql = (char *)ptr; return 1; }
/** * Remove the first mysql statement from buffer. Return pointer to the removed * statement or NULL if buffer is empty. * * Clone buf, calculate the length of included mysql stmt, and point the * statement with cloned buffer. Move the start pointer of buf accordingly * so that it only cover the remaining buffer. * */ GWBUF* gw_MySQL_get_next_stmt( GWBUF** p_readbuf) { GWBUF* stmtbuf; size_t buflen; size_t strlen; uint8_t* packet; if (*p_readbuf == NULL) { stmtbuf = NULL; goto return_stmtbuf; } CHK_GWBUF(*p_readbuf); if (GWBUF_EMPTY(*p_readbuf)) { stmtbuf = NULL; goto return_stmtbuf; } buflen = GWBUF_LENGTH((*p_readbuf)); packet = GWBUF_DATA((*p_readbuf)); strlen = MYSQL_GET_PACKET_LEN(packet); if (strlen+4 == buflen) { stmtbuf = *p_readbuf; *p_readbuf = NULL; goto return_stmtbuf; } /** vraa :Multi-packet stmt is not supported as of 7.3.14 */ if (strlen-1 > buflen-5) { stmtbuf = NULL; goto return_stmtbuf; } stmtbuf = gwbuf_clone_portion(*p_readbuf, 0, strlen+4); *p_readbuf = gwbuf_consume(*p_readbuf, strlen+4); return_stmtbuf: return stmtbuf; }
GWBUF *gwbuf_clone_portion( GWBUF *buf, size_t start_offset, size_t length) { GWBUF* clonebuf; CHK_GWBUF(buf); ss_dassert(start_offset+length <= GWBUF_LENGTH(buf)); if ((clonebuf = (GWBUF *)malloc(sizeof(GWBUF))) == NULL) { return NULL; } atomic_add(&buf->sbuf->refcount, 1); clonebuf->sbuf = buf->sbuf; clonebuf->start = (void *)((char*)buf->start)+start_offset; clonebuf->end = (void *)((char *)clonebuf->start)+length; clonebuf->gwbuf_type = buf->gwbuf_type; /*< clone the type for now */ clonebuf->next = NULL; CHK_GWBUF(clonebuf); return clonebuf; }
/** * The clientReply entry point. This is passed the response buffer * to which the filter should be applied. Once processed the * query is passed to the upstream component * (filter or router) in the filter chain. * * The function tries to extract a SQL query response out of the response buffer, * adds a timestamp to it and publishes the resulting string on the exchange. * The message is tagged with the same identifier that the query was. * * @param instance The filter instance data * @param session The filter session * @param reply The response data */ static int clientReply(FILTER* instance, void *session, GWBUF *reply) { MQ_SESSION *my_session = (MQ_SESSION *)session; MQ_INSTANCE *my_instance = (MQ_INSTANCE *)instance; char t_buf[128],*combined; unsigned int pkt_len = pktlen(reply->sbuf->data), offset = 0; amqp_basic_properties_t *prop; if (my_session->was_query){ int packet_ok = 0, was_last = 0; my_session->was_query = false; if(pkt_len > 0){ if((prop = malloc(sizeof(amqp_basic_properties_t)))){ prop->_flags = AMQP_BASIC_CONTENT_TYPE_FLAG | AMQP_BASIC_DELIVERY_MODE_FLAG | AMQP_BASIC_MESSAGE_ID_FLAG | AMQP_BASIC_CORRELATION_ID_FLAG; prop->content_type = amqp_cstring_bytes("text/plain"); prop->delivery_mode = AMQP_DELIVERY_PERSISTENT; prop->correlation_id = amqp_cstring_bytes(my_session->uid); prop->message_id = amqp_cstring_bytes("reply"); } if(!(combined = calloc(GWBUF_LENGTH(reply) + 256,sizeof(char)))){ skygw_log_write_flush(LOGFILE_ERROR, "Error : Out of memory"); } memset(t_buf,0,128); sprintf(t_buf,"%lu|",(unsigned long)time(NULL)); memcpy(combined + offset,t_buf,strnlen(t_buf,40)); offset += strnlen(t_buf,40); if(*(reply->sbuf->data + 4) == 0x00){ /**OK packet*/ unsigned int aff_rows = 0, l_id = 0, s_flg = 0, wrn = 0; unsigned char *ptr = (unsigned char*)(reply->sbuf->data + 5); pkt_len = pktlen(reply->sbuf->data); aff_rows = consume_leitoi(&ptr); l_id = consume_leitoi(&ptr); s_flg |= *ptr++; s_flg |= (*ptr++ << 8); wrn |= *ptr++; wrn |= (*ptr++ << 8); sprintf(combined + offset,"OK - affected_rows: %d " " last_insert_id: %d " " status_flags: %#0x " " warnings: %d ", aff_rows,l_id,s_flg,wrn); offset += strnlen(combined,GWBUF_LENGTH(reply) + 256) - offset; if(pkt_len > 7){ int plen = consume_leitoi(&ptr); if(plen > 0){ sprintf(combined + offset," message: %.*s\n",plen,ptr); } } packet_ok = 1; was_last = 1; }else if(*(reply->sbuf->data + 4) == 0xff){ /**ERR packet*/ sprintf(combined + offset,"ERROR - message: %.*s", (int)(reply->end - ((void*)(reply->sbuf->data + 13))), (char *)reply->sbuf->data + 13); packet_ok = 1; was_last = 1; }else if(*(reply->sbuf->data + 4) == 0xfb){ /**LOCAL_INFILE request packet*/ unsigned char *rset = (unsigned char*)reply->sbuf->data; strcpy(combined + offset,"LOCAL_INFILE: "); strncat(combined + offset,(const char*)rset+5,pktlen(rset)); packet_ok = 1; was_last = 1; }else{ /**Result set*/ unsigned char *rset = (unsigned char*)(reply->sbuf->data + 4); char *tmp; unsigned int col_cnt = consume_leitoi(&rset); tmp = calloc(256,sizeof(char)); sprintf(tmp,"Columns: %d",col_cnt); memcpy(combined + offset,tmp,strnlen(tmp,256)); offset += strnlen(tmp,256); memcpy(combined + offset,"\n",1); offset++; free(tmp); packet_ok = 1; was_last = 1; } if(packet_ok){ pushMessage(my_instance,prop,combined); if(was_last){ /**Successful reply received and sent, releasing uid*/ free(my_session->uid); my_session->uid = NULL; } } } } return my_session->up.clientReply(my_session->up.instance, my_session->up.session, reply); }
/** * Read the backend server MySQL handshake * * @param conn MySQL protocol structure * @return 0 on success, 1 on failure */ int gw_read_backend_handshake(MySQLProtocol *conn) { GWBUF *head = NULL; DCB *dcb = conn->owner_dcb; int n = -1; uint8_t *payload = NULL; int h_len = 0; int success = 0; int packet_len = 0; if ((n = dcb_read(dcb, &head)) != -1) { if (head) { payload = GWBUF_DATA(head); h_len = gwbuf_length(head); /* * The mysql packets content starts at byte fifth * just return with less bytes */ if (h_len <= 4) { /* log error this exit point */ conn->state = MYSQL_AUTH_FAILED; return 1; } //get mysql packet size, 3 bytes packet_len = gw_mysql_get_byte3(payload); if (h_len < (packet_len + 4)) { /* * data in buffer less than expected in the * packet. Log error this exit point */ conn->state = MYSQL_AUTH_FAILED; return 1; } // skip the 4 bytes header payload += 4; //Now decode mysql handshake success = gw_decode_mysql_server_handshake(conn, payload); if (success < 0) { /* MySQL handshake has not been properly decoded * we cannot continue * log error this exit point */ conn->state = MYSQL_AUTH_FAILED; return 1; } conn->state = MYSQL_AUTH_SENT; // consume all the data here head = gwbuf_consume(head, GWBUF_LENGTH(head)); return 0; } } // Nothing done here, log error this return 1; }
/** * Read event for EPOLLIN on the telnetd protocol module. * * @param dcb The descriptor control block * @return */ static int telnetd_read_event(DCB* dcb) { int n; GWBUF *head = NULL; SESSION *session = dcb->session; ROUTER_OBJECT *router = session->service->router; ROUTER *router_instance = session->service->router_instance; void *rsession = session->router_session; TELNETD *telnetd = (TELNETD *)dcb->protocol; char *password, *t; if ((n = dcb_read(dcb, &head)) != -1) { if (head) { unsigned char *ptr = GWBUF_DATA(head); ptr = GWBUF_DATA(head); while (GWBUF_LENGTH(head) && *ptr == TELNET_IAC) { telnetd_command(dcb, ptr + 1); GWBUF_CONSUME(head, 3); ptr = GWBUF_DATA(head); } if (GWBUF_LENGTH(head)) { switch (telnetd->state) { case TELNETD_STATE_LOGIN: telnetd->username = strndup(GWBUF_DATA(head), GWBUF_LENGTH(head)); /* Strip the cr/lf from the username */ t = strstr(telnetd->username, "\r\n"); if (t) *t = 0; telnetd->state = TELNETD_STATE_PASSWD; dcb_printf(dcb, "Password: "******"\r\n"); if (t) *t = 0; if (admin_verify(telnetd->username, password)) { telnetd_echo(dcb, 1); telnetd->state = TELNETD_STATE_DATA; dcb_printf(dcb, "\n\nMaxScale> "); } else { dcb_printf(dcb, "\n\rLogin incorrect\n\rLogin: "); telnetd_echo(dcb, 1); telnetd->state = TELNETD_STATE_LOGIN; free(telnetd->username); } gwbuf_consume(head, GWBUF_LENGTH(head)); free(password); break; case TELNETD_STATE_DATA: router->routeQuery(router_instance, rsession, head); break; } } else { // Force the free of the buffer header gwbuf_consume(head, 0); } } } return n; }
/** * Receive the MySQL authentication packet from backend, packet # is 2 * * @param protocol The MySQL protocol structure * @return -1 in case of failure, 0 if there was nothing to read, 1 if read * was successful. */ int gw_receive_backend_auth( MySQLProtocol *protocol) { int n = -1; GWBUF *head = NULL; DCB *dcb = protocol->owner_dcb; uint8_t *ptr = NULL; int rc = 0; n = dcb_read(dcb, &head); /*< * Read didn't fail and there is enough data for mysql packet. */ if (n != -1 && head != NULL && GWBUF_LENGTH(head) >= 5) { ptr = GWBUF_DATA(head); /*< * 5th byte is 0x0 if successful. */ if (ptr[4] == '\x00') { rc = 1; } else { uint8_t* tmpbuf = (uint8_t *)calloc(1, GWBUF_LENGTH(head)+1); memcpy(tmpbuf, ptr, GWBUF_LENGTH(head)); LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, "%lu [gw_receive_backend_auth] Invalid " "authentication message from backend dcb %p " "fd %d, ptr[4] = %p, msg %s.", pthread_self(), dcb, dcb->fd, tmpbuf[4], tmpbuf))); free(tmpbuf); rc = -1; } /*< * Remove data from buffer. */ head = gwbuf_consume(head, GWBUF_LENGTH(head)); } else if (n == 0) { /*< * This is considered as success because call didn't fail, * although no bytes was read. */ rc = 0; LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, "%lu [gw_receive_backend_auth] Read zero bytes from " "backend dcb %p fd %d in state %s. n %d, head %p, len %d", pthread_self(), dcb, dcb->fd, STRDCBSTATE(dcb->state), n, head, (head == NULL) ? 0 : GWBUF_LENGTH(head)))); } else { ss_dassert(n < 0 && head == NULL); rc = -1; LOGIF(LD, (skygw_log_write_flush( LOGFILE_DEBUG, "%lu [gw_receive_backend_auth] Reading from backend dcb %p " "fd %d in state %s failed. n %d, head %p, len %d", pthread_self(), dcb, dcb->fd, STRDCBSTATE(dcb->state), n, head, (head == NULL) ? 0 : GWBUF_LENGTH(head)))); } return rc; }
/** * The clientReply entry point. This is passed the response buffer * to which the filter should be applied. Once processed the * query is passed to the upstream component * (filter or router) in the filter chain. * * The function tries to extract a SQL query response out of the response buffer, * adds a timestamp to it and publishes the resulting string on the exchange. * The message is tagged with the same identifier that the query was. * * @param instance The filter instance data * @param session The filter session * @param reply The response data */ static int clientReply(FILTER* instance, void *session, GWBUF *reply) { MQ_SESSION *my_session = (MQ_SESSION *)session; MQ_INSTANCE *my_instance = (MQ_INSTANCE *)instance; char t_buf[128],*combined; unsigned int err_code = AMQP_STATUS_OK, pkt_len = pktlen(reply->sbuf->data), offset = 0; amqp_basic_properties_t prop; spinlock_acquire(my_instance->rconn_lock); if(my_instance->conn_stat != AMQP_STATUS_OK){ if(difftime(time(NULL),my_instance->last_rconn) > my_instance->rconn_intv){ my_instance->last_rconn = time(NULL); if(init_conn(my_instance,my_session)){ my_instance->rconn_intv = 1.0; my_instance->conn_stat = AMQP_STATUS_OK; }else{ my_instance->rconn_intv += 5.0; skygw_log_write(LOGFILE_ERROR, "Error : Failed to reconnect to the MQRabbit server "); } err_code = my_instance->conn_stat; } } spinlock_release(my_instance->rconn_lock); if (err_code == AMQP_STATUS_OK && my_session->was_query){ int packet_ok = 0, was_last = 0; my_session->was_query = 0; if(pkt_len > 0){ prop._flags = AMQP_BASIC_CONTENT_TYPE_FLAG | AMQP_BASIC_DELIVERY_MODE_FLAG | AMQP_BASIC_MESSAGE_ID_FLAG | AMQP_BASIC_CORRELATION_ID_FLAG; prop.content_type = amqp_cstring_bytes("text/plain"); prop.delivery_mode = AMQP_DELIVERY_PERSISTENT; prop.correlation_id = amqp_cstring_bytes(my_session->uid); prop.message_id = amqp_cstring_bytes("reply"); if(!(combined = calloc(GWBUF_LENGTH(reply) + 256,sizeof(char)))){ skygw_log_write_flush(LOGFILE_ERROR, "Error : Out of memory"); } memset(t_buf,0,128); sprintf(t_buf,"%lu|",(unsigned long)time(NULL)); memcpy(combined + offset,t_buf,strnlen(t_buf,40)); offset += strnlen(t_buf,40); if(*(reply->sbuf->data + 4) == 0x00){ /**OK packet*/ unsigned int aff_rows = 0, l_id = 0, s_flg = 0, wrn = 0; unsigned char *ptr = (unsigned char*)(reply->sbuf->data + 5); pkt_len = pktlen(reply->sbuf->data); aff_rows = consume_leitoi(&ptr); l_id = consume_leitoi(&ptr); s_flg |= *ptr++; s_flg |= (*ptr++ << 8); wrn |= *ptr++; wrn |= (*ptr++ << 8); sprintf(combined + offset,"OK - affected_rows: %d " " last_insert_id: %d " " status_flags: %#0x " " warnings: %d ", aff_rows,l_id,s_flg,wrn); offset += strnlen(combined,GWBUF_LENGTH(reply) + 256) - offset; if(pkt_len > 7){ int plen = consume_leitoi(&ptr); if(plen > 0){ sprintf(combined + offset," message: %.*s\n",plen,ptr); } } packet_ok = 1; was_last = 1; }else if(*(reply->sbuf->data + 4) == 0xff){ /**ERR packet*/ sprintf(combined + offset,"ERROR - message: %.*s", (int)(reply->end - ((void*)(reply->sbuf->data + 13))), (char *)reply->sbuf->data + 13); packet_ok = 1; was_last = 1; }else if(*(reply->sbuf->data + 4) == 0xfb){ /**LOCAL_INFILE request packet*/ unsigned char *rset = (unsigned char*)reply->sbuf->data; strcpy(combined + offset,"LOCAL_INFILE: "); strncat(combined + offset,(const char*)rset+5,pktlen(rset)); packet_ok = 1; was_last = 1; }else{ /**Result set*/ unsigned char *rset = (unsigned char*)(reply->sbuf->data + 4); char *tmp; unsigned int col_cnt = consume_leitoi(&rset); tmp = calloc(256,sizeof(char)); sprintf(tmp,"Columns: %d",col_cnt); memcpy(combined + offset,tmp,strnlen(tmp,256)); offset += strnlen(tmp,256); memcpy(combined + offset,"\n",1); offset++; free(tmp); packet_ok = 1; was_last = 1; } if(packet_ok){ if((err_code = amqp_basic_publish(my_session->conn,my_session->channel, amqp_cstring_bytes(my_instance->exchange), amqp_cstring_bytes(my_instance->key), 0,0,&prop,amqp_cstring_bytes(combined)) ) != AMQP_STATUS_OK){ spinlock_acquire(my_instance->rconn_lock); my_instance->conn_stat = err_code; spinlock_release(my_instance->rconn_lock); skygw_log_write_flush(LOGFILE_ERROR, "Error : Failed to publish message to MQRabbit server: " "%s",amqp_error_string2(err_code)); }else if(was_last){ /**Successful reply received and sent, releasing uid*/ free(my_session->uid); my_session->uid = NULL; } } free(combined); } } return my_session->up.clientReply(my_session->up.instance, my_session->up.session, reply); }