int mysql_dissector(struct tcp_stream* tcp, void** no_need_param){ struct half_stream* stream; int msg_type; int ret = 0; if(tcp->client.count_new){ stream = &tcp->client; /* the msg is sent by server so the message is server type*/ msg_type = MYSQL_SERVER_MSG; }else{ stream = &tcp->server; msg_type = MYSQL_CLIENT_MSG; } mysql_session* sess = get_mysql_session(&tcp->addr); /* FIXME: it shouldn't be null, but it can be null in some case.*/ //DEBUG_ASSERT(sess != NULL); if(sess != NULL){ ret = handle_resume_state(sess, msg_type); if(ret == SESSION_STATE_PROCESS_CONTINUE){ ret = handle_msg(stream, msg_type, sess); if(ret == STREAM_DISCARD){ /* leave the data we have received but not unhandled to next call */ nids_discard(tcp, sess->handled_len); sess->handled_len = 0; } }else{ log_runtime_error("handle canceled due to resume state"); } }else{ log_runtime_error("Cannot get a session"); } return ret; }
int mysql_dissect_query_result(mysql_session* sess, half_stream* stream){ int ret = 0; char* msg = PACKET_MSG(stream->data); u_char first_byte = *msg; query_info_t* info = sess->query_info; gettimeofday(&info->result_start, NULL); log_runtime_error("Server: Query Result New data: %d", stream->count - stream->offset); /* Response: the length of OK packet is supposed to be greater than 7(header counts in).*/ if(first_byte == 0 && PACKET_LEN(stream->data) >= 3){ info->result_end = info->result_start; sess->state = SESSION_STATE_QUERY; /* the size of affect row is not fixed. */ sess->query_info->row_num = decode_mysql_lenenc_int((const u_char*)msg + 1, PACKET_LEN(stream->data) - 1); return 0; /* Response: Error packet */ }else if(first_byte == MYSQL_LENENC_INT_ERR && PACKET_LEN(stream->data) >= 3){ sess->state = SESSION_STATE_QUERY; int err_code = *(short*)(msg + 1); /* ERROR 1064 means there is a parsing error * we won't record such query. */ return err_code == 1064? SESSION_IGNORE_WRONG_SYNTAX : 0; } /* if the results have data rows, we will see TWO packet with EOF marker. * The first one is to hint that all the columns have been sent, * and the second one means that all rows have been sent. */ int first_eof = query_res_find_eof(sess, stream, stream->data, &sess->query_info->col_num); if(first_eof){ log_runtime_error("Entering Server Result Phase2 from QueryResult state..."); int second_eof = query_res_find_eof(sess, stream, stream->data + sess->handled_len,&sess->query_info->row_num); /* if we have not handled all data, we should return STREAM_DISCARD, and set handled_len * to data length we have handled. * otherwise, we return 0 means we have handled all data and set handled_len to 0 */ if(!second_eof){ sess->state = SESSION_STATE_RESULT_PHASE2; ret = STREAM_DISCARD; }else{ sess->state = SESSION_STATE_QUERY; sess->handled_len = 0; info->result_end = info->result_start; ret = 0; } }else{ /* the state will not change, but we may haven't handled all data * so we should use nids_discard() to notify that we have data left in stream->data. * the length is stored in sess->handled_len */ ret = STREAM_DISCARD; } return ret; }
int handle_resume_state(mysql_session* sess, int msg_type){ int go = SESSION_STATE_PROCESS_DISCARD; if(sess->state < SESSION_STATE_RESUME_START){ /* we are at correct state */ return SESSION_STATE_PROCESS_CONTINUE; } log_runtime_error("handle resume state: current state: %s msg_type: %s ", states[sess->state], msg_type?"server":"client"); switch(sess->state){ case SESSION_STATE_RESUME_START: if(msg_type == MYSQL_SERVER_MSG){ sess->state = SESSION_STATE_RESUME_WAIT_CLIENT; }else{ sess->state = SESSION_STATE_RESUME_WAIT_SERVER; } break; case SESSION_STATE_RESUME_WAIT_CLIENT: if(msg_type == MYSQL_CLIENT_MSG){ /* only at here can we go to SESSION_STATE_QUERY to handle the client query */ sess->state = SESSION_STATE_QUERY; go = SESSION_STATE_PROCESS_CONTINUE; } break; case SESSION_STATE_RESUME_WAIT_SERVER: if(msg_type == MYSQL_SERVER_MSG){ sess->state = SESSION_STATE_RESUME_WAIT_CLIENT; } break; default: break; } return go; }
int mysql_dissect_login_resp(mysql_session* sess, half_stream* stream){ log_runtime_error("Server: Login Response "); debug_print_stream(stream); sess->state = SESSION_STATE_QUERY; /* TODO: add login failed handling */ return 0; }
int mysql_dissect_greet(mysql_session* sess, half_stream* stream){ log_runtime_error("Server: Greet "); debug_print_stream(stream); sess->state = SESSION_STATE_LOGIN_REQUEST; /*TODO: * record the protocol version */ return 0; }
void TxtFile::writeAnsi(const std::list<std::string>& content) const { setAnsiStamp(); std::ofstream file(m_path.string().c_str()); if (file.bad()) throw log_runtime_error("can't create text file"); BOOST_FOREACH(const std::string& line, content) { file << line << std::endl; }
int mysql_dissect_login_request(mysql_session* sess, half_stream* stream){ log_runtime_error("Client: Login "); //debug_print_stream(stream); sess->state = SESSION_STATE_LOGIN_RESPONSE; if(!mysql_dissect_is_complete(stream, sess)){ sess->handled_len = 0; return STREAM_DISCARD; } user_info_t* user_info = (user_info_t*)malloc(sizeof(user_info_t)); memset(user_info, 0, sizeof(user_info_t)); /* Refer: http://dev.mysql.com/doc/internals/en/connection-phase-packets.html */ mysql_login_client_info* client_info = (mysql_login_client_info*)PACKET_MSG(stream->data); if(PACKET_LEN(stream->data) > sizeof(mysql_login_client_info)){ /* get the user name*/ char* user_name = (char*)client_info + sizeof(mysql_login_client_info); int name_len = strlen(user_name); strncpy(user_info->username, user_name, MYSQL_USERNAME_MAX_LEN); user_info->username[MYSQL_USERNAME_MAX_LEN - 1] = '\0'; /* get the database name */ if(client_info->capability & CLIENT_CONNECT_WITH_DB){ /* version 4.1 protocol */ // TODO: can not assume the mysql is using version 4.1, // see https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::HandshakeResponse // 1. first, we need to skip auth field, but auth field is not just the CLIENT_SECURE_CONNECTION flags set, // it may be CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA or others. // if capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA { // lenenc-int length of auth-response // string[n] auth-response // } else if capabilities & CLIENT_SECURE_CONNECTION { // 1 length of auth-response // string[n] auth-response // } else { // string[NUL] auth-response // } if(client_info->capability & CLIENT_SECURE_CONNECTION){ int auth_resp_len = *(user_name + name_len + 1); char* dbname = user_name + name_len + auth_resp_len + 1 + 1; strncpy(sess->dbname, dbname, MYSQL_DB_NAME_LEN); sess->dbname[MYSQL_DB_NAME_LEN - 1] = '\0'; } } } sess->user_info = user_info; return 0; }
/*looking for the second eof */ int mysql_dissect_query_result_phase2(mysql_session* sess, half_stream* stream){ log_runtime_error("Entering Server Result Phase2..."); int ret = 0; int second_eof = query_res_find_eof(sess, stream, stream->data, &sess->query_info->row_num); if(!second_eof){ sess->state = SESSION_STATE_RESULT_PHASE2; ret = STREAM_DISCARD; }else{ query_info_t* info = sess->query_info; gettimeofday(&info->result_end, NULL); sess->state = SESSION_STATE_QUERY; } return ret; }
const std::list<std::string> TxtFile::readUtf8withoutPref() const { std::ifstream file(m_path.string().c_str()); if (file.bad()) throw log_runtime_error("can't open text file"); std::list<std::string> result; while (!file.eof()) { std::string str; std::getline(file, str); result.push_back(str); } return result; }
const std::list<std::wstring> TxtFile::readUnicode() const { std::wifstream file(m_path.string().c_str(), std::ios_base::binary); if (file.bad()) throw log_runtime_error("can't open text file"); file.imbue(std::locale(std::locale(), new ucs2_conversion())); std::list<std::wstring> result; while (!file.eof()) { std::wstring str; file.ignore(); std::getline(file, str, L'\x0D'); result.push_back(str); } return result; }
/* * handle the message send by server */ int handle_server_msg(half_stream* stream, mysql_session* sess){ int ret = 0; if(sess->ignore_flag){ sess->state = SESSION_STATE_QUERY; return 0; } switch(sess->state){ case SESSION_STATE_SERVER_GREET: /* expected to be a greeting msg */ ret = mysql_dissect_greet(sess, stream); break; case SESSION_STATE_LOGIN_RESPONSE: ret = mysql_dissect_login_resp(sess, stream); break; case SESSION_STATE_QUERY_RESULT: ret = mysql_dissect_query_result(sess, stream); break; case SESSION_STATE_RESULT_PHASE2: ret = mysql_dissect_query_result_phase2(sess, stream); break; /* PANIC: we have gone to the wrong state */ case SESSION_STATE_LOGIN_REQUEST: case SESSION_STATE_QUERY: default: /* go to the recovery start state */ log_runtime_error("get a wrong state when handling server msg. current state: %s", states[sess->state]); sess->state = SESSION_STATE_RESUME_START; break; } if(sess->state == SESSION_STATE_QUERY){ if(/* ret != SESSION_IGNORE_WRONG_SYNTAX &&*/ sess->query_info->data[0] != '\0'){ log_session_query(sess); } /*VERY IMPORTANT: clean the temporary session data of current query*/ memset(sess->query_info, 0, offsetof(query_info_t, alloc_size)); sess->query_info->data[0] = '\0'; sess->handled_len = 0; } return ret; }
const std::list<std::string> TxtFile::readAnsi() const { std::ifstream file(m_path.string().c_str()); if (file.bad()) throw log_runtime_error("can't open text file"); std::list<std::string> result; while (!file.eof()) { std::string str; std::getline(file, str); size_t pos = str.find_first_of(BadSymbols); while(pos != str.npos) { str.erase(pos, 1); pos = str.find_first_of(BadSymbols, pos); } /* boost::erase_all(str, "\x0A"); boost::erase_all(str, "\x0B"); boost::erase_all(str, "\x0C"); boost::erase_all(str, "\x0D"); boost::erase_all(str, "\x0E"); boost::erase_all(str, "\x0F"); boost::erase_all(str, "\x10"); boost::erase_all(str, "\x11"); boost::erase_all(str, "\x12"); boost::erase_all(str, "\x13"); boost::erase_all(str, "\x14"); boost::erase_all(str, "\x15"); boost::erase_all(str, "\x16"); boost::erase_all(str, "\x17"); boost::erase_all(str, "\x18"); boost::erase_all(str, "\x19"); */ result.push_back(str); } return result; }
/* * 0 if it is an incomplete mysql packet * 1 if it is a complete mysql packet */ int mysql_dissect_is_complete(half_stream* stream, mysql_session* sess){ int pkt_len = PACKET_LEN(stream->data); int received_data_len = stream->count - stream->offset; if(pkt_len > received_data_len - MYSQL_PACKET_HEADER_LEN){ /* * this is an incomplete packet, we should wait until the libnids * has buffered all packets and gives us a notification. */ log_runtime_error("Incomplete Query packet! pkt-len: %d received-len:%d", pkt_len, received_data_len); //debug_print_stream(stream); return 0; }else if(pkt_len == received_data_len - MYSQL_PACKET_HEADER_LEN){ /* that is exactly we want! */ return 1; }else{ /*we have received more data than one complete packet !!! */ /* may not happend ? */ return 1; } return 1; }
int handle_client_msg(half_stream* stream, mysql_session* sess){ int ret = 0; switch(sess->state){ case SESSION_STATE_LOGIN_REQUEST: ret = mysql_dissect_login_request(sess, stream); break; case SESSION_STATE_QUERY: ret = mysql_dissect_query(sess, stream); break; /* PANIC: we have gone to the wrong state */ case SESSION_STATE_SERVER_GREET: case SESSION_STATE_LOGIN_RESPONSE: case SESSION_STATE_QUERY_RESULT: case SESSION_STATE_RESULT_PHASE2: default: /* go to the recovery start state */ log_runtime_error("get a wrong state when handling client msg. current state: %s", states[sess->state]); sess->state = SESSION_STATE_RESUME_START; break; } return ret; }