#include #include #include #include #include #include #include #include #include #include #include "http.h" #define LOG_PATH "./log/qq_file/runtime.log" #define ONLINE_SEND 0 #define OFFLINE_SEND 1 #define NAME_SIZE 128 #define MAX_HTABLE_NUM 96 #define UUID_LENGTH 35 #define NIRVANA int QQ_FILE_SEND_VERSION_1_20201124 = 0; void qq_file_send_version_1_20201124() { //20201124 } typedef struct { uint8_t send_type; // OFFLINE_SEND char uuid[UUID_LENGTH+1]; uint16_t content_len; // content length in current packet char filename[NAME_SIZE*2]; struct nirvana_streaming_ctx *ctx; uint8_t next_flag; // 1: next packet contains separator // 0: not contain uint64_t *file_len; // file length uchar curdir; uint id; // same like file count , from 0 to start }qq_pme_info,*qq_pme_info_p; typedef struct { uint8_t feature_type; char *separator_header; uint header_len; uint uuid_deviation; char *uuid_suffix; uint uuid_suffix_len; uint content_deviation; char *end_suffix; uint end_suffix_len; }struct_separator_feature; typedef struct { uint id; uint8_t send_type; char filename[NAME_SIZE*2]; struct nirvana_streaming_ctx *ctx; uint64_t *file_len; // file length }file_info; uint capture_file_cnt = 0; struct_separator_feature online_feature,offline_feature; MESA_htable_handle file_htable; char save_directory[NAME_SIZE]; int locally_save; // save file to local machine void *runtime_log; struct nirvana_instance *instance_asyn; /* * @Description: Use this function instead of strncmp ,because strncmp may have problems matching features * @param: the main string * @param: the pattern string to be searched * @param: the len of pattern sting * @return: 0 : success -1 : fail */ int bicmp(char *str,char *ptn,int len) { int i; for(i = 0; i < len; i++) { if(str[i] != ptn[i]) return -1; } return 0; } /* * @Description: Extract the information of private protocols in payload * @param: payload * @param: payload_len * @param: features of separators * @param: pme * @return: =0 : success <0 : fail */ int parse_separator(char **payload,int payload_len,struct_separator_feature separator_feature,qq_pme_info **pme) { int uuid_len = UUID_LENGTH; //match by header if( bicmp(*payload,separator_feature.separator_header, separator_feature.header_len) != 0) { return -1; } // match by the suffix which is after uuid if( bicmp( (*payload)+separator_feature.uuid_deviation + uuid_len + 1, separator_feature.uuid_suffix,separator_feature.uuid_suffix_len) != 0) { return -2; } qq_pme_info_p qq_pme = *pme; strncpy(qq_pme->uuid,(*payload) + separator_feature.uuid_deviation,uuid_len); qq_pme->uuid[uuid_len] = '\0'; //printf("this uuid:%s\n",qq_pme->uuid); (*payload)+=separator_feature.content_deviation; qq_pme->content_len = payload_len-separator_feature.content_deviation; qq_pme->send_type = separator_feature.feature_type; return 0; } /* * @Description: find end suffix in response payload if found,means the end of the current file transfer * @param: payload * @param: payload_len * @param: separator_feature * @param: file_id * @return: =0 : success <0 : fail */ int parse_end_suffix(char *payload,int payload_len,struct_separator_feature separator_feature,uint file_id) { if( bicmp(separator_feature.end_suffix,payload,separator_feature.end_suffix_len) != 0) { MESA_handle_runtime_log(runtime_log,RLOG_LV_DEBUG,"match","file %u not finish",file_id); return -1; } MESA_handle_runtime_log(runtime_log,RLOG_LV_DEBUG,"match","file %u finish",file_id); return 0; } /* * @Description: Converts a string in hex to decimal(int) * @param: a hex char ,range : 0-9a-f * @return: int , range : 0-15 */ int hexch_to_int(char ch) { if ((ch >= 'A') && (ch <='F')) { return 10+ch-'A'; } else if ((ch >= 'a') && (ch <='f')) { return 10+ch-'a'; } else{ return ch-'0'; } } /* * @Description: 'abcd00' -> {0xab,0xcd,0x00} * @param: in_str * @param: the len of out_str * @param: out_str * @return: None */ void str_to_ascii(char *in_str,int str_len,char* out_str) { int i; for (i=0;i0: fail */ int read_profile_of_separator(const char *inf_file,struct_separator_feature *p_separator_feature,char *section,int feature_type) { char *separator_header_array,*uuid_suffix_array,*end_suffix_array; int read_res = 0; p_separator_feature->feature_type = feature_type; //header_len read_res += MESA_load_profile_uint_nodef(inf_file,section,"header_len",&(p_separator_feature->header_len)); //header p_separator_feature->separator_header = (char *)malloc(p_separator_feature->header_len+1); separator_header_array = (char *)malloc((p_separator_feature->header_len)<<1|1); if(MESA_load_profile_string_nodef(inf_file,section,"separator_header",separator_header_array,p_separator_feature->header_len<<1|1)<0) { read_res += -1; MESA_handle_runtime_log(runtime_log,RLOG_LV_FATAL,"init","[%s] separator_header error",section); } str_to_ascii(separator_header_array,p_separator_feature->header_len,p_separator_feature->separator_header); free(separator_header_array); //uuid_deviation read_res += MESA_load_profile_uint_nodef(inf_file,section,"uuid_deviation",&(p_separator_feature->uuid_deviation)); //uuid_suffix_len read_res += MESA_load_profile_uint_nodef(inf_file,section,"uuid_suffix_len",&(p_separator_feature->uuid_suffix_len)); //uuid_suffix p_separator_feature->uuid_suffix = (char *)malloc(p_separator_feature->uuid_suffix_len+1); uuid_suffix_array = (char *)malloc(p_separator_feature->uuid_suffix_len<<2|1); if(MESA_load_profile_string_nodef(inf_file,section,"uuid_suffix",uuid_suffix_array,p_separator_feature->uuid_suffix_len<<1|1)<0) { read_res += -1; MESA_handle_runtime_log(runtime_log,RLOG_LV_FATAL,"init","[%s] uuid_suffix error",section); } str_to_ascii(uuid_suffix_array,p_separator_feature->uuid_suffix_len,p_separator_feature->uuid_suffix); free(uuid_suffix_array); //content_deviation read_res += MESA_load_profile_uint_nodef(inf_file,section,"content_deviation",&(p_separator_feature->content_deviation)); //end_suffix_len read_res += MESA_load_profile_uint_nodef(inf_file,section,"end_suffix_len",&(p_separator_feature->end_suffix_len)); //end_suffix p_separator_feature->end_suffix = (char *)malloc(p_separator_feature->end_suffix_len+1); end_suffix_array = (char *)malloc(p_separator_feature->end_suffix_len<<1|1); if(MESA_load_profile_string_nodef(inf_file,section,"end_suffix",end_suffix_array,p_separator_feature->end_suffix_len<<1|1)<0) { read_res += -1; MESA_handle_runtime_log(runtime_log,RLOG_LV_FATAL,"init","[%s] end_suffix error",section); } str_to_ascii(end_suffix_array,p_separator_feature->end_suffix_len,p_separator_feature->end_suffix); free(end_suffix_array); return read_res; } /* * @Description: Used to delete value from hash table * @param: file_info * * @return: Null */ void free_fileinfo(void *thisfile) { //thisfile = thisfile; file_info *p = (file_info *)thisfile; #ifdef NIRVANA nirvana_streaming_update_end(p->ctx); #endif if (p->send_type == OFFLINE_SEND) MESA_handle_runtime_log(runtime_log,RLOG_LV_INFO,"offline","save file %d , size: %lld bytes",p->id, *(p->file_len)); else MESA_handle_runtime_log(runtime_log,RLOG_LV_INFO,"online","save file %d , size: %lld bytes",p->id, *(p->file_len)); if(p->file_len) free(p->file_len); free(p); } /* * @Description: different files have different uuids. Fragments with the same UUID belong to the same file get the pme according to the current uuid * @param: pme * @param: content len * @param: send_type * @return: 0: not exist 1: exist */ int match_uuid(qq_pme_info **pme) { qq_pme_info_p qq_pme = *pme; file_info* thisfile = (file_info *) MESA_htable_search(file_htable,(u_char *)qq_pme->uuid,UUID_LENGTH); if (thisfile == NULL) { // new file create file_info *newfile; newfile = (file_info *)malloc(sizeof(file_info)); qq_pme->id = capture_file_cnt; capture_file_cnt++; newfile->id = qq_pme->id; sprintf(qq_pme->filename,"%s/%d-%s",save_directory,qq_pme->id,qq_pme->uuid); strncpy(newfile->filename,qq_pme->filename,NAME_SIZE); newfile->filename[strlen(qq_pme->filename)] = '\0'; uint64_t *file_len = (uint64_t *)malloc(sizeof(uint64_t)); *file_len = 0; qq_pme->file_len = file_len; newfile->file_len = file_len; newfile->send_type = qq_pme->send_type; #ifdef NIRVANA // nirvana section struct nirvana_streaming_ctx *ctx; union nirvana_progid progid; struct streaming_meta streammeta; struct nvn_opt_unit opt[1]; int opt_num = 0; memset(&streammeta,0,sizeof(streammeta)); streammeta.msg_type = NVN_MSG_TYPE_DOC_IM; if(qq_pme->send_type == ONLINE_SEND) streammeta.msg_subtype = NVN_MSG_SUBTYPE_IM_QQ_ONLINE; else streammeta.msg_subtype = NVN_MSG_SUBTYPE_IM_QQ_OFFLINE; streammeta.protocol = NVN_PROTOCOL_QQ; streammeta.cont_code = 0; streammeta.live_streaming = 0; streammeta.cap_IP = 0; streammeta.dir = DIR_C2S; streammeta.request_single_flow = 0; calculate_md5_for_progid(newfile->filename, strlen(newfile->filename), &progid); streammeta.balance_seed = caculate_seed_using_sdbm_hash((char*)&progid, sizeof(progid)); ctx = nirvana_streaming_update_start(instance_asyn, &progid, &streammeta); if(ctx == NULL) { return PROT_STATE_DROPME; } nirvana_streaming_update_meta(ctx, opt, opt_num); qq_pme->ctx = ctx; newfile->ctx = ctx; #endif MESA_handle_runtime_log(runtime_log,RLOG_LV_INFO,"match","found %d file\tuuid:%s ",qq_pme->id,qq_pme->uuid); MESA_htable_add(file_htable,(u_char *) qq_pme->uuid, UUID_LENGTH, (void *)newfile); return 0; } else { // there is a file point in htable // so change the pme qq_pme->id = thisfile->id; qq_pme->file_len = thisfile->file_len; qq_pme->send_type = thisfile->send_type; strncpy(qq_pme->filename,thisfile->filename,strlen(thisfile->filename)); #ifdef NIRVANA qq_pme->ctx = thisfile->ctx; #endif return 1; } } /* * @Description: free memory of pme */ void free_pme(qq_pme_info **pme) { qq_pme_info_p qq_pme = *pme; //free(qq_pme->ctx); free(qq_pme); } uchar qq_file_send_entry(stSessionInfo* session_info,void **pme,int thread_seq,struct streaminfo *a_tcp,void *a_packet) { int res = 0; uchar rec = PROT_STATE_GIVEME; //int payload_len = tcp_detail->datalen; //char *payload =(char *)tcp_detail->pdata; qq_pme_info_p qq_pme = (qq_pme_info_p) *pme; http_infor* a_http = (http_infor *)(session_info->app_info); switch(session_info->prot_flag) { case HTTP_REQ_LINE: if(a_http->method!=HTTP_METHOD_POST) { rec = PROT_STATE_DROPME; } if (*pme == NULL) { *pme =(qq_pme_info_p)malloc(sizeof(qq_pme_info)); qq_pme = *pme; memset(qq_pme->filename,0,NAME_SIZE); } qq_pme->curdir = a_http->curdir; break; case HTTP_RES_LINE: if (qq_pme == NULL) { rec = PROT_STATE_DROPME; } break; case HTTP_MESSAGE_URL: { char bmd5[33]; int url_decoded_len; url_decoded_len = session_info->buflen; char *url_decoded = (char*)calloc(url_decoded_len+1,sizeof(char)); memcpy(url_decoded, session_info->buf, session_info->buflen); res = sscanf(url_decoded,"%*d.%*d.%*d.%*d:%*d/ftn_handler?bmd5=%32s",bmd5); if(res>0) { qq_pme->next_flag = 0; // separator_flag is 1 means that the next TCP packet contains a separator } else { rec = PROT_STATE_DROPME; } free(url_decoded); break; } case HTTP_CONTENT: { int content_len = session_info->buflen; char *payload = (char *)malloc(content_len); memcpy(payload,session_info->buf,content_len); char *content = payload; if (content_len==0) { free(payload); break; } if (qq_pme->curdir != a_http->curdir) { // looking for end suffix if(qq_pme->send_type == ONLINE_SEND) { res = parse_end_suffix(content,content_len,online_feature,qq_pme->id); } else { res = parse_end_suffix(content,content_len,offline_feature,qq_pme->id); } if(res == 0) { MESA_htable_del(file_htable,(u_char *)qq_pme->uuid,UUID_LENGTH,free_fileinfo); } } else { if(qq_pme->next_flag == 0) { res = parse_separator(&content,content_len,offline_feature,&qq_pme); if(res < 0) { // If offline matching fails, the online rule will be matched again res = parse_separator(&content,content_len,online_feature,&qq_pme); if(res < 0) { // If both matches fail, the rule sis wrong or the traffic is wrong if (res == -1) MESA_handle_runtime_log(runtime_log,RLOG_LV_FATAL,"match","feature fail,check rule"); else MESA_handle_runtime_log(runtime_log,RLOG_LV_FATAL,"match","feature fail,unkown type"); // if the current http stream is transferring a file with a half,and the rule matches fail // there will be a streamctx still open and no way to close // but this situation seems won't happen rec = PROT_STATE_DROPME; free(payload); break; } else MESA_handle_runtime_log(runtime_log,RLOG_LV_DEBUG,"match","online feature success,uuid:%s",qq_pme->uuid); } else { MESA_handle_runtime_log(runtime_log,RLOG_LV_DEBUG,"match","offline feature success,uuid:%s",qq_pme->uuid); } qq_pme->next_flag = 1; content_len = qq_pme->content_len; match_uuid(&qq_pme); } *(qq_pme->file_len) += content_len; MESA_handle_runtime_log(runtime_log,RLOG_LV_DEBUG,"match","file %d saved %d Bytes, content_len:%d",qq_pme->id,*(qq_pme->file_len),content_len); if(locally_save == 1) { //save file to disk MESA_handle_runtime_log(runtime_log,RLOG_LV_DEBUG,"match","file %d saved as %s",qq_pme->id,qq_pme->filename); FILE *fp = fopen(qq_pme->filename,"ab"); fwrite(content,content_len,1,fp); fclose(fp); } #ifdef NIRVANA nirvana_streaming_update_data(qq_pme->ctx, content, content_len); #endif } free(payload); break; } } if(session_info->session_state&SESSION_STATE_CLOSE || rec == PROT_STATE_DROPME) { if(pme != NULL) { free_pme((qq_pme_info **)pme); } } return rec; } int suggestion_inform_callback(struct suggest_receive_info *info, struct nirvana_message_survey *detail, const char *snap_URL, struct nvn_opt_unit *opts, void *privdata) { char fromip_str[64]; inet_ntop(AF_INET, &info->from_ip, fromip_str, 64); printf("Receive suggest from: %s, progid=0x%lu_%lu\n", fromip_str, detail->progid.progid_long.progidH, detail->progid.progid_long.progidL); return 0; } void nirvana_client_init() { struct nirvana_parameter *parameter; parameter = nirvana_client_parameter_new("./plug/business/qq_file_send/qq_file_send.conf", "NIRVANA", runtime_log); //assert(parameter != NULL); instance_asyn = nirvana_evbase_instance_new(parameter, runtime_log, suggestion_inform_callback, NULL); //assert(instance_asyn!=NULL); } void QQ_FILE_SEND_DESTROY() { MESA_handle_runtime_log(runtime_log,RLOG_LV_INFO,"destroy","------------exit------------"); } int qq_file_send_init() { printf("qq_file_send init\n"); int read_res = 0; runtime_log = MESA_create_runtime_log_handle(LOG_PATH, 20); char *inf_file = "./plug/business/qq_file_send/qq_file_send.conf"; read_res += read_profile_of_separator(inf_file,&online_feature,"ONLINE_FEATURE",ONLINE_SEND); read_res += read_profile_of_separator(inf_file,&offline_feature,"OFFLINE_FEATURE",OFFLINE_SEND); //read_res += MESA_load_profile_string_nodef(inf_file,"CAPTURE","post_header",post_header,NAME_SIZE); MESA_load_profile_int_def(inf_file,"CAPTURE","locally_save",&locally_save,0); if(locally_save == 1) { if(MESA_load_profile_string_def(inf_file,"CAPTURE","save_directory",save_directory,NAME_SIZE,"./plug/business/qq_file_send/")<0) { read_res += -1; MESA_handle_runtime_log(runtime_log,RLOG_LV_FATAL,"init","load save directory failed"); } } else { strcpy(save_directory,"./plug/business/qq_file_send/"); } if(access(save_directory,W_OK) != 0 && locally_save == 1) { MESA_handle_runtime_log(runtime_log,RLOG_LV_FATAL,"init","save directory %s not found or cannot write\n",save_directory); return -1; } else { MESA_handle_runtime_log(runtime_log,RLOG_LV_DEBUG,"init","save directory is %s",save_directory); } //free(inf_file); if(read_res == 0) MESA_handle_runtime_log(runtime_log,RLOG_LV_INFO,"init","------------load feature success------------"); else { MESA_handle_runtime_log(runtime_log,RLOG_LV_FATAL,"init","------------load feature failed:%d------------",read_res); return -1; } #ifdef NIRVANA nirvana_client_init(); #endif MESA_htable_create_args_t *htable_args; htable_args = (MESA_htable_create_args_t *)malloc(sizeof(MESA_htable_create_args_t)); htable_args->thread_safe = 1; htable_args->recursive = 0; htable_args->hash_slot_size = 1048576; htable_args->max_elem_num = MAX_HTABLE_NUM; htable_args->key_comp = NULL; htable_args->key2index = NULL; htable_args->data_free = free_fileinfo; htable_args->data_expire_with_condition = NULL; htable_args->eliminate_type = 0; htable_args->expire_time = 60; file_htable = MESA_htable_create(htable_args,1); MESA_htable_print_crtl(file_htable,0); return 0; }