/** * The routeQuery entry point. This is passed the query buffer * to which the filter should be applied. Once processed the * query is passed to the downstream component * (filter or router) in the filter chain. * * The function checks whether required logging trigger conditions are met and if so, * tries to extract a SQL query out of the query buffer, canonize the query, add * a timestamp to it and publish the resulting string on the exchange. * The message is tagged with an unique identifier and the clientReply will * use the same identifier for the reply from the backend to form a query-reply pair. * * @param instance The filter instance data * @param session The filter session * @param queue The query data */ static int routeQuery(FILTER *instance, void *session, GWBUF *queue) { MQ_SESSION *my_session = (MQ_SESSION *)session; MQ_INSTANCE *my_instance = (MQ_INSTANCE *)instance; char *ptr, t_buf[128], *combined,*canon_q,*sesshost,*sessusr; bool success = false, src_ok = false,schema_ok = false,obj_ok = false; int length, i, j,dbcount = 0; char** sesstbls; unsigned int plen = 0; amqp_basic_properties_t *prop; /**The user is changing databases*/ if(*((char*)(queue->start + 4)) == 0x02){ if(my_session->db){ free(my_session->db); } plen = pktlen(queue->start); my_session->db = calloc(plen,sizeof(char)); memcpy(my_session->db,queue->start + 5,plen - 1); } if(modutil_is_SQL(queue)){ /**Parse the query*/ if (!query_is_parsed(queue)){ success = parse_query(queue); } if(!success){ skygw_log_write(LOGFILE_ERROR,"Error: Parsing query failed."); goto send_downstream; } if(!my_instance->log_all){ if(!skygw_is_real_query(queue)){ goto send_downstream; } } if(my_instance->trgtype == TRG_ALL){ skygw_log_write_flush(LOGFILE_TRACE,"Trigger is TRG_ALL"); schema_ok = true; src_ok = true; obj_ok = true; goto validate_triggers; } if(my_instance->trgtype & TRG_SOURCE && my_instance->src_trg){ if(session_isvalid(my_session->session)){ sessusr = session_getUser(my_session->session); sesshost = session_get_remote(my_session->session); /**Username was configured*/ if(my_instance->src_trg->usize > 0){ for(i = 0;i<my_instance->src_trg->usize;i++){ if(strcmp(my_instance->src_trg->user[i],sessusr) == 0) { skygw_log_write_flush(LOGFILE_TRACE,"Trigger is TRG_SOURCE: user: %s = %s",my_instance->src_trg->user[i],sessusr); src_ok = true; break; } } } /**If username was not matched, try to match hostname*/ if(!src_ok && my_instance->src_trg->hsize > 0){ for(i = 0;i<my_instance->src_trg->hsize;i++){ if(strcmp(my_instance->src_trg->host[i],sesshost) == 0) { skygw_log_write_flush(LOGFILE_TRACE,"Trigger is TRG_SOURCE: host: %s = %s",my_instance->src_trg->host[i],sesshost); src_ok = true; break; } } } } if(src_ok && !my_instance->strict_logging){ schema_ok = true; obj_ok = true; goto validate_triggers; } }else{ src_ok = true; } if(my_instance->trgtype & TRG_SCHEMA && my_instance->shm_trg){ int tbsz = 0,z; char** tblnames = skygw_get_table_names(queue,&tbsz,true); char* tmp; bool all_remotes = true; for(z = 0;z<tbsz;z++){ if((tmp = strchr(tblnames[z],'.')) != NULL){ char *lasts; tmp = strtok_r(tblnames[z],".",&lasts); for(i = 0; i<my_instance->shm_trg->size; i++){ if(strcmp(tmp,my_instance->shm_trg->objects[i]) == 0){ skygw_log_write_flush(LOGFILE_TRACE,"Trigger is TRG_SCHEMA: %s = %s",tmp,my_instance->shm_trg->objects[i]); schema_ok = true; break; } } }else{ all_remotes = false; } free(tblnames[z]); } free(tblnames); if(!schema_ok && !all_remotes && my_session->db && strlen(my_session->db)>0){ for(i = 0; i<my_instance->shm_trg->size; i++){ if(strcmp(my_session->db,my_instance->shm_trg->objects[i]) == 0){ skygw_log_write_flush(LOGFILE_TRACE,"Trigger is TRG_SCHEMA: %s = %s",my_session->db,my_instance->shm_trg->objects[i]); schema_ok = true; break; } } } if(schema_ok && !my_instance->strict_logging){ src_ok = true; obj_ok = true; goto validate_triggers; } }else{ schema_ok = true; } if(my_instance->trgtype & TRG_OBJECT && my_instance->obj_trg){ sesstbls = skygw_get_table_names(queue,&dbcount,false); for(j = 0; j<dbcount; j++){ char* tbnm = NULL; if((strchr(sesstbls[j],'.')) != NULL){ char *lasts; tbnm = strtok_r(sesstbls[j],".",&lasts); tbnm = strtok_r(NULL,".",&lasts); }else{ tbnm = sesstbls[j]; } for(i = 0; i<my_instance->obj_trg->size; i++){ if(!strcmp(tbnm,my_instance->obj_trg->objects[i])){ obj_ok = true; skygw_log_write_flush(LOGFILE_TRACE,"Trigger is TRG_OBJECT: %s = %s",my_instance->obj_trg->objects[i],sesstbls[j]); break; } } } if(dbcount > 0){ for(j = 0; j<dbcount; j++){ free(sesstbls[j]); } free(sesstbls); dbcount = 0; } if(obj_ok && !my_instance->strict_logging){ src_ok = true; schema_ok = true; goto validate_triggers; } }else{ obj_ok = true; } validate_triggers: if(src_ok&&schema_ok&&obj_ok){ /** * Something matched the trigger, log the query */ skygw_log_write_flush(LOGFILE_TRACE,"Routing message to: %s:%d %s as %s/%s, exchange: %s<%s> key:%s queue:%s", my_instance->hostname,my_instance->port, my_instance->vhost,my_instance->username, my_instance->password,my_instance->exchange, my_instance->exchange_type,my_instance->key, my_instance->queue); if(my_session->uid == NULL){ my_session->uid = calloc(33,sizeof(char)); if(!my_session->uid){ skygw_log_write(LOGFILE_ERROR,"Error : Out of memory."); }else{ genkey(my_session->uid,32); } } if (queue->next != NULL) { queue = gwbuf_make_contiguous(queue); } if(modutil_extract_SQL(queue, &ptr, &length)){ my_session->was_query = true; 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("query"); } if(success){ /**Try to convert to a canonical form and use the plain query if unsuccessful*/ if((canon_q = skygw_get_canonical(queue)) == NULL){ skygw_log_write_flush(LOGFILE_ERROR, "Error: Cannot form canonical query."); } } memset(t_buf,0,128); sprintf(t_buf, "%lu|",(unsigned long)time(NULL)); int qlen = strnlen(canon_q,length) + strnlen(t_buf,128); if((combined = malloc((qlen+1)*sizeof(char))) == NULL){ skygw_log_write_flush(LOGFILE_ERROR, "Error: Out of memory"); } strcpy(combined,t_buf); strncat(combined,canon_q,length); pushMessage(my_instance,prop,combined); free(canon_q); } } /** Pass the query downstream */ } send_downstream: return my_session->down.routeQuery(my_session->down.instance, my_session->down.session, queue); }
/** * 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); }
/** * 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); }