From c9c3fe7fbdc9f12648ae16a9ea50e886509c7238 Mon Sep 17 00:00:00 2001 From: "linuxrc@163.com" Date: Tue, 30 Nov 2021 12:55:56 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9B=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/conf/project_list.conf | 23 + bin/conf/qq_file_trans.conf | 33 + bin/plug/business/conflist_business.inf | 24 + .../business/qq_file_trans/qq_file_trans.inf | 13 + .../business/qq_file_trans/qq_file_trans.so | Bin 0 -> 67420 bytes src/Makefile | 21 + src/include/connector.h | 86 ++ src/qq_file_entry.c | 868 ++++++++++++++++++ src/qq_file_entry.h | 125 +++ src/qq_file_global.c | 308 +++++++ src/qq_file_global.h | 21 + 11 files changed, 1522 insertions(+) create mode 100644 bin/conf/project_list.conf create mode 100644 bin/conf/qq_file_trans.conf create mode 100644 bin/plug/business/conflist_business.inf create mode 100644 bin/plug/business/qq_file_trans/qq_file_trans.inf create mode 100644 bin/plug/business/qq_file_trans/qq_file_trans.so create mode 100644 src/Makefile create mode 100644 src/include/connector.h create mode 100644 src/qq_file_entry.c create mode 100644 src/qq_file_entry.h create mode 100644 src/qq_file_global.c create mode 100644 src/qq_file_global.h diff --git a/bin/conf/project_list.conf b/bin/conf/project_list.conf new file mode 100644 index 0000000..34829f0 --- /dev/null +++ b/bin/conf/project_list.conf @@ -0,0 +1,23 @@ +#project_requirement_name value_type +#current requirement definitions: +# terminal_tag, +# ipv4_frag_list, +# ipv6_frag_list, +# stream_id, +# value type definitions +# char, +# short, +# int, +# long, +# struct + +#terminal_tag struct +#ipv4_frag_list struct +#ipv6_frag_list struct +#stream_id struct +#test_project struct +#tcp_flow_stat struct +#udp_flow_stat struct + +JT_IP_PKT struct +STREAMID long diff --git a/bin/conf/qq_file_trans.conf b/bin/conf/qq_file_trans.conf new file mode 100644 index 0000000..d10ec29 --- /dev/null +++ b/bin/conf/qq_file_trans.conf @@ -0,0 +1,33 @@ +[MODULE] +#日志 +RUN_LOG_DIR=./log/qq_file_trans/ +RUN_LOG_LEVEL=40 + +#存本地文件开关 +DEBUG_SWITCH=0 + +#统计信息 +STAT_FIELD_CYCLE=300 +STAT_FIELD_TRIG=0 + + +[FEATURE] +#FEATURE_QQ_NUMBER_TCP=0:27,00,00,00,00,14;0:04,37,21 +#FEATURE_QQ_TCPFILENAME=0:0c,00,00,00,00,2b;31:0c,00,00,00,00,2b +#FEATURE_QQ_TCPFILE=0:04,36,43,00&39:65,65,00,00 + +FEATURE_QQ_NUMBER_TCP=0:04&13:00,01,00,00,00,00,00 +FEATURE_QQ_TCPFILENAME=0:04&13:00,01,00,00,00,00,00&31:0c,00,00,00,00,2b +FEATURE_QQ_TCPFILE=0:04&13:00,01,00,00,00,00,00&39:65,65,00,00 +FEATURE_QQ_TCPFILE_END=0:04&13:00,01,00,00,00,00,00&39:56,56,00,00 + +#文件应该也有特征13:00,01,00,00,00,00,00 +FEATURE_QQ_NUMBER_UDP=0:03,00,01,00,00,00,00,00;13:00,01,00,00,00,00,00 +FEATURE_QQ_UDPFILENAME=3:00,2b,52,00,00;34:00,2b,52,00,00 +FEATURE_QQ_UDPFILE_START=3:00,2d,63,00;34:00,2d,63,00 +FEATURE_QQ_UDPFILE_END=3:00,2b,56,00,00;34:00,2b,56,00,00 + +#其他QQ协议特征,上面的也会作为协议特征 +FEATURE_QQTCP_PROTOCOL= +FEATURE_QQUDP_PROTOCOL= + diff --git a/bin/plug/business/conflist_business.inf b/bin/plug/business/conflist_business.inf new file mode 100644 index 0000000..f5ca2a2 --- /dev/null +++ b/bin/plug/business/conflist_business.inf @@ -0,0 +1,24 @@ +##./plug/business/test_app/test_app.inf +##./plug/business/test_http/test_http.inf +##./plug/business/ipsec_biz_sample/ipsec_biz_sample.inf +##./plug/business/l2tp_biz_sample/l2tp_biz_sample.inf +##./plug/business/pptp_biz_sample/pptp_biz_sample.inf +##./plug/business/terminal_tag/terminal_tag.inf +##./plug/business/g_device_plug/g_device_plug.inf +# +#./plug/business/ycl_ftp/ycl_ftp.inf +#./plug/business/ycl_mail/ycl_mail.inf +#./plug/business/ycl_http_service/ycl_http_service.inf +##./plug/business/ycl_http_netdisk/ycl_http_netdisk.inf +#./plug/business/ycl_http_webmail/ycl_http_webmail.inf +##./plug/business/ycl_qq_biz/ycl_qq_biz.inf +##./plug/business/ycl_qq/ycl_qq_biz.inf +# +#./plug/business/http_jt_audit/http_jt_audit.inf +#./plug/business/dns_audit/dns_jt_audit.inf +#./plug/business/mail_audit/mail_jt_audit.inf +#./plug/business/ssl_audit/ssl_audit.inf +#./plug/business/ftp_audit/ftp_audit.inf +#./plug/business/ip_jt/ip_jt.inf +##./plug/business/MESA_capfile/MESA_capfile.inf +./plug/business/qq_file_trans/qq_file_trans.inf diff --git a/bin/plug/business/qq_file_trans/qq_file_trans.inf b/bin/plug/business/qq_file_trans/qq_file_trans.inf new file mode 100644 index 0000000..14f1d5e --- /dev/null +++ b/bin/plug/business/qq_file_trans/qq_file_trans.inf @@ -0,0 +1,13 @@ +[PLUGINFO] +PLUGNAME=QQ_FILE_TRANS +SO_PATH=./plug/business/qq_file_trans/qq_file_trans.so +INIT_FUNC=QQ_PROT_INIT +DESTROY_FUNC=QQ_PROT_DESTROY + +[UDP] +FUNC_FLAG=ALL +FUNC_NAME=QQ_PROT_UDP_ENTRY + +[TCP] +FUNC_FLAG=ALL +FUNC_NAME=QQ_PROT_TCP_ENTRY diff --git a/bin/plug/business/qq_file_trans/qq_file_trans.so b/bin/plug/business/qq_file_trans/qq_file_trans.so new file mode 100644 index 0000000000000000000000000000000000000000..f5b315c9a82758124254bba5c9da2111df1915a7 GIT binary patch literal 67420 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 U1S;)iJUvI~I&O$)HEc%kf1wkJYybcN literal 0 HcmV?d00001 diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..ee38172 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,21 @@ +CC=g++ +CCC=g++ + +INC_PATH = -I./include +CFLAGS = -Wall -g -fPIC $(INC_PATH) + +LIBS = -lMESA_prof_load -lMESA_handle_logger -ldocumentanalyze -lMESA_field_stat -lpthread + +OBJS = qq_file_global.o qq_file_entry.o + +TARGET = qq_file_trans.so + +$(TARGET):$(OBJS) + $(CCC) -fPIC -shared $^ -o $@ $(LIBS) + cp $@ /home/zhangcw/sapp/plug/business/qq_file_trans/ + +.c.o: + $(CC) -c $(CFLAGS) $< + +clean: + rm -f $(TARGET) $(OBJS) diff --git a/src/include/connector.h b/src/include/connector.h new file mode 100644 index 0000000..b1c10f1 --- /dev/null +++ b/src/include/connector.h @@ -0,0 +1,86 @@ +#ifndef CONNECTOR_H_ +#define CONNECTOR_H_ + +extern int CONNECTOR_VERSION_1_20170704; + +typedef unsigned long ulong64; +typedef unsigned int uint32; + +typedef enum +{ + APPD_SENDBACK=0, //ǰӦòݻش + APPD_FILE=1, //ļģӦòݷַ +}APPD_HEADER_TYPE; + +typedef enum +{ + DEST_APPD=0, //ݷַappd + DEST_WM=1, //webmailWM + DEST_MLFA=2, //ļmlfa +}APPD_DESTINATION; + +typedef enum +{ + COMPLETE=0, + FRAG=1, + FRAG_END=2, +}APPD_MSG_TYPE; + +typedef struct _appd_basic_t +{ + APPD_HEADER_TYPE appd_type; //Ӧòݻļ + const char* service_name; //,'\0' + APPD_DESTINATION destination; //ݽճ + ulong64* streamid; //ָ̬룬id + uint32 streamid_num; + struct ipaddr* addr; //ָ̬룬 Ԫ + uint32 addr_num; +}appd_basic_t; + + +#ifdef __cplusplus +extern "C" { +#endif + +/*ʼconnector ÿһһ*/ +//void* connector_start(appd_basic_t* baisc_info); +void* connector_start(appd_basic_t* baisc_info, const struct streaminfo *a_tcp); +/* msg_type: COMPLETE FRAG FRAG_END*/ +void connector(void* handler, APPD_MSG_TYPE msg_type, void* simple_opt_handle, void* nested_opt_handle); +/*ÿһ*/ +void connector_finish(void* handler); + + +/*ѡ*/ +/*1. üѡĸsimple_opt_handle*/ +void* connector_create_simple_opt(uint32 max_size); +/*2. ѡ*/ +int connector_append_simple_opt(void* simple_opt_handle, const char* opt_name, char* opt_value, uint32 opt_len); +/*3. ͷ*/ +void connector_free_simple_opt(void** simple_opt_handle); + + +/*Ƕѡ*/ +/*1. Ƕѡܸnested_opt_handle*/ +void* connector_create_nested_opt(uint32 max_size); +/*2. ijǶѡԼڲѡĸnested_unit_handle*/ +void* connector_create_nested_opt_unit(const char* nested_unit_name, uint32 max_size); +/*3.䵥Ƕѡڲѡ*/ +/*opt_unit_handle connector_create_nested_opt_unitķֵ*/ +/*ֵ 0 OK -1 ERROR*/ +int connector_append_nested_opt_unit(void* nested_unit_handle, const char* subopt_name, char* subopt_value, uint32 subopt_len); +/*4. Ƕѡ׷Ƕѡnested_opt_handle*/ +/*ֵ 0 OK -1 ERROR*/ +int connector_append_nested_opt(void* nested_opt_handle, void* nested_unit_handle); +/*5. ͷŵǶѡ*/ +void connector_free_nested_opt_unit(void** nested_unit_handle); +/*6. ͷ*/ +void connector_free_nested_opt(void** nested_opt_handle); + + +#ifdef __cplusplus +} +#endif +#endif + + diff --git a/src/qq_file_entry.c b/src/qq_file_entry.c new file mode 100644 index 0000000..ad49b50 --- /dev/null +++ b/src/qq_file_entry.c @@ -0,0 +1,868 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "qq_file_entry.h" +#include "qq_file_global.h" +#include "connector.h" + +qq_global_info_t g_qq_global_info; + +int data_hit_feature(const char *data, unsigned int datalen, unsigned int min_len, FEATURE_INDEX_T index) +{ + unsigned int i, j; + + if(datalen > min_len) + { + for(i=0; ithread_id = thread_id; + qq_pme->qq_state = QQ_STATE_INIT; + qq_pme->pending_flag = 1; + qq_pme->fileinfo.block_seq = -1; //UDPʼ-1ʶش + qq_pme->tcp_flag = tcp_flag; + + *pme = qq_pme; + + if(g_qq_global_info.stat_trig) + { + stat_field_operation(g_qq_global_info.stat_handle, g_qq_global_info.stat_id[STAT_ID_SESSION_NUM], FS_OP_TYPE_ADD, 1); + stat_field_operation(g_qq_global_info.stat_handle, g_qq_global_info.stat_id[STAT_ID_NEW_SESSION], FS_OP_TYPE_ADD, 1); + } + + return 0; +} + +static void clear_qq_pme_info(qq_proto_pme_t *qq_pme, struct streaminfo *a_udp, const void *raw_pkt) +{ + if(qq_pme->connector_hdl != NULL) + { + connector_finish(qq_pme->connector_hdl); + } + if(qq_pme->fileinfo.buf) + { + dictator_free(qq_pme->thread_id, qq_pme->fileinfo.buf); + } + + dictator_free(qq_pme->thread_id, qq_pme); + + if(g_qq_global_info.stat_trig) + { + stat_field_operation(g_qq_global_info.stat_handle, g_qq_global_info.stat_id[STAT_ID_SESSION_NUM], FS_OP_TYPE_ADD, -1); + stat_field_operation(g_qq_global_info.stat_handle, g_qq_global_info.stat_id[STAT_ID_DEL_SESSION], FS_OP_TYPE_ADD, 1); + } +} + +QQ_STATE_t analyse_udp_pdata_state(QQ_STATE_t state, const char *data, int datalen) +{ + if(state != QQ_STATE_INIT) + return state; + + return assign_qq_state(is_qq_protocol_udp(data, datalen)); +} + +QQ_STATE_t analyse_tcp_pdata_state(QQ_STATE_t state, const char *data, int datalen) +{ + if(state != QQ_STATE_INIT) + return state; + + return assign_qq_state(is_qq_protocol_tcp(data, datalen)); +} + +int process_udp_data_state(struct streaminfo *a_udp, qq_proto_pme_t *qq_pme, int thread_seq,const void *raw_pkt) +{ + char *data = (char *)a_udp->pudpdetail->pdata; + int datalen = a_udp->pudpdetail->datalen; + char filename[512]; + int tmp, i, j; + + switch(qq_pme->qq_state) + { + case QQ_STATE_QQNUM: + qq_pme->qq_state = QQ_STATE_INIT; + if(qq_pme->qqnum[a_udp->curdir-1] || qq_pme->qqnum_from) + break; + + if(*data == 0x03 && datalen==27) + { + qq_pme->qqnum[a_udp->curdir-1] = ntohl(*((unsigned int *)&data[23])); + } + else if(*data == 0x04) //TODO + { + qq_pme->qqnum_from = ntohl(*((unsigned int *)&data[9])); + } + break; + + case QQ_STATE_FNAME: + qq_pme->qq_state = QQ_STATE_INIT; + qq_pme->file_dir = a_udp->curdir - 1; + + if(*data == 0x05 && datalen < 800) //800---д + { + qq_pme->total_file_len = ntohl(*((unsigned int *)&data[11])); + for (i = 23, j = 0; i < datalen - 1; i+=2, j+=2) + { + if ((data[i] == 0x00) && (data[i + 1] == 0x00)) + break; + filename[j] = data[i]; + filename[j+1] = data[i+1]; + } + tmp = j; + } + else if(*data == 0x04 && datalen < 800) + { + if(qq_pme->qqnum_from == 0) + { + qq_pme->qqnum_from = ntohl(*((unsigned int *)&data[9])); + } + qq_pme->total_file_len = ntohl(*((unsigned int *)&data[42])); + for (i = 54, j = 0; i < datalen - 1; i+=2, j+=2) + { + if ((data[i] == 0x00) && (data[i + 1] == 0x00)) + break; + filename[j] = data[i]; + filename[j+1] = data[i+1]; + } + tmp = j; + } + else + { + return APP_STATE_GIVEME; + } + return protocol_process(qq_pme, QQ_FILENAME, filename, tmp, a_udp, raw_pkt); + + case QQ_STATE_FCONT_START: + qq_pme->qq_state = QQ_STATE_INIT; //ÿǰ涼 + if(*data == 0x05 && datalen>18) + { + tmp = ntohl(*((int *)&data[10])); //TODO֪intͻshort + if(qq_pme->fileinfo.block_seq >= tmp) + return APP_STATE_GIVEME; + + qq_pme->fileinfo.block_seq = tmp; + qq_pme->compress_flag = data[17]; + return protocol_process(qq_pme, QQ_FILECONT, data+18, datalen-18, a_udp, raw_pkt); + } + else if(*data==0x04 && datalen>50) + { + tmp = ntohl(*((int *)&data[41])); //TODO֪intͻshort + if(qq_pme->fileinfo.block_seq >= tmp) + return APP_STATE_GIVEME; + + qq_pme->fileinfo.block_seq = tmp; + qq_pme->compress_flag = data[48]; + return protocol_process(qq_pme, QQ_FILECONT, data+49, datalen-50, a_udp, raw_pkt); + } + break; + + case QQ_STATE_FCONT_END: + qq_pme->qq_state = QQ_STATE_INIT; + if(*data == 0x05 && datalen>18) + { + qq_pme->compress_flag = data[17]; + return protocol_process(qq_pme, QQ_FILECONT_END, data+18, datalen-18, a_udp, raw_pkt); + } + else if(*data == 0x04 && datalen>50) + { + qq_pme->compress_flag = data[48]; + return protocol_process(qq_pme, QQ_FILECONT_END, data+49, datalen-50, a_udp, raw_pkt); + } + break; + + case QQ_STATE_FCONT: + case QQ_STATE_INIT: + default: + break; + } + + return APP_STATE_GIVEME; +} + +extern "C" char QQ_PROT_UDP_ENTRY(struct streaminfo *a_udp, void **pme, int thread_seq,const void *raw_pkt) +{ + QQ_PACKET_TYPE_t type; + qq_proto_pme_t *qq_pme=(qq_proto_pme_t*)(*pme); + int rec = APP_STATE_GIVEME; + + switch(a_udp->opstate) + { + case OP_STATE_PENDING: + type = is_qq_protocol_udp((char *)a_udp->pudpdetail->pdata, a_udp->pudpdetail->datalen); + if(type) + { + if(init_qq_pme_info(&qq_pme, a_udp->threadnum, 0)) + return APP_STATE_DROPME; + qq_pme->qq_state = assign_qq_state(type); + *pme = qq_pme; + } + else + { + return APP_STATE_DROPME; + } + //no break here + + case OP_STATE_DATA: + qq_pme->qq_state = analyse_udp_pdata_state(qq_pme->qq_state, (char *)a_udp->pudpdetail->pdata, a_udp->pudpdetail->datalen); + + rec = process_udp_data_state(a_udp, qq_pme, thread_seq, raw_pkt); + break; + + case OP_STATE_CLOSE: + clear_qq_pme_info(qq_pme, a_udp, raw_pkt); + break; + + default: + return APP_STATE_DROPME; + } + + if(rec & APP_STATE_DROPME) + { + clear_qq_pme_info(qq_pme, a_udp, raw_pkt); + } + + return rec; +} + + +int process_tcp_data_state(struct streaminfo *a_tcp, qq_proto_pme_t *qq_pme, int thread_seq,const void *raw_pkt) +{ + char *data = (char *)a_tcp->pudpdetail->pdata; + int datalen = a_tcp->pudpdetail->datalen; + char filename[512]; + int cur_len=0, i, j, rec = APP_STATE_GIVEME, need_len; + + switch(qq_pme->qq_state) + { + case QQ_STATE_QQNUM: + qq_pme->qq_state = QQ_STATE_INIT; + if(qq_pme->qqnum_from) + break; + + if(*data == 0x04) //TODO + { + qq_pme->qqnum_from = ntohl(*((unsigned int *)&data[9])); + } + break; + + case QQ_STATE_FNAME: + qq_pme->qq_state = QQ_STATE_INIT; + qq_pme->file_dir = a_tcp->curdir - 1; + + //ȡļļUNICODE + if(*data == TCPFILE_START_CHAR0c && datalen < 800) //800---д + { + qq_pme->total_file_len = ntohl(*((unsigned int *)&data[27])); + for (i = 39, j = 0; i < datalen - 1; i+=2, j+=2) + { + if ((data[i] == 0x00) && (data[i + 1] == 0x00)) + break; + filename[j] = data[i]; + filename[j+1] = data[i+1]; + } + cur_len = j; + } + else if(*data == TCPFILE_START_CHAR04 && datalen < 800) + { + qq_pme->total_file_len = ntohl(*((unsigned int *)&data[90])); + for (i = 102, j = 0; i < datalen - 1; i+=2, j+=2) + { + if ((data[i] == 0x00) && (data[i + 1] == 0x00)) + break; + filename[j] = data[i]; + filename[j+1] = data[i+1]; + } + cur_len = j; + } + else + { + return APP_STATE_GIVEME; + } + return protocol_process(qq_pme, QQ_FILENAME, filename, cur_len, a_tcp, raw_pkt); + + case QQ_STATE_FCONT_START: + //UDPͬǣTCPͷһǰƥʽ䣻 + if(a_tcp->curdir != qq_pme->file_dir+1) + break; + + if(a_tcp->ptcpdetail->lostlen) //ѰҰͷ + { + qq_pme->fileinfo.len = 0; + qq_pme->qq_state = QQ_STATE_INIT; + break; + } + if(qq_pme->fileinfo.len > 0) + { + if(qq_pme->fileinfo.len < TCPFILE_HDR_LEN04+1) //жʱҪݳȴͷ + { + need_len = TCPFILE_HDR_LEN04+1 - qq_pme->fileinfo.len; + if(datalen < need_len) + { + if(save_initial_tcp_data(&qq_pme->fileinfo, data, datalen, qq_pme->thread_id) < 0) + { + return APP_STATE_DROPME; + } + return APP_STATE_GIVEME; + } + memcpy(qq_pme->fileinfo.buf+qq_pme->fileinfo.len, data, need_len); + qq_pme->fileinfo.len += need_len; + data += need_len; + datalen -= need_len; + if(data_hit_feature(qq_pme->fileinfo.buf, qq_pme->fileinfo.len, 0, FINDEX_TCPFILE)) + { + qq_pme->fileinfo.prot_flag = QQ_FILECONT; + } + else if(data_hit_feature(qq_pme->fileinfo.buf, qq_pme->fileinfo.len, 0, FINDEX_TCPFILE_END)) + { + qq_pme->fileinfo.prot_flag = QQ_FILECONT_END; + } + else + { + return APP_STATE_DROPME; + } + if(*(qq_pme->fileinfo.buf) == TCPFILE_START_CHAR04) + { + qq_pme->fileinfo.block_len = ntohs(*((unsigned short *)&qq_pme->fileinfo.buf[3])); + qq_pme->compress_flag = qq_pme->fileinfo.buf[TCPFILE_HDR_LEN04-1]; + } + } + + //һij + need_len = qq_pme->fileinfo.block_len - qq_pme->fileinfo.len; + if(need_len > datalen) + { + if(save_initial_tcp_data(&qq_pme->fileinfo, data, datalen, qq_pme->thread_id) < 0) + { + return APP_STATE_DROPME; + } + return APP_STATE_GIVEME; + } + if(save_initial_tcp_data(&qq_pme->fileinfo, data, need_len, qq_pme->thread_id) < 0) + { + return APP_STATE_DROPME; + } + qq_pme->fileinfo.len = 0; + data += need_len; + datalen -= need_len; + + if(*(qq_pme->fileinfo.buf) == TCPFILE_START_CHAR04 && qq_pme->fileinfo.block_len>TCPFILE_HDR_LEN04) + { + if(*(qq_pme->fileinfo.buf + qq_pme->fileinfo.block_len - 1) != TCPFILE_END_CHAR04) + return APP_STATE_DROPME; + rec = protocol_process(qq_pme, qq_pme->fileinfo.prot_flag, qq_pme->fileinfo.buf+TCPFILE_HDR_LEN04, qq_pme->fileinfo.block_len-TCPFILE_HDR_LEN04-1, a_tcp, raw_pkt); + } + if(rec & APP_STATE_DROPME) + { + return rec; + } + } + + while(datalen>0) + { + //ͷȲ + if(datalen < TCPFILE_HDR_LEN04+1) + { + if(save_initial_tcp_data(&qq_pme->fileinfo, data, datalen, qq_pme->thread_id) < 0) + { + return APP_STATE_DROPME; + } + return APP_STATE_GIVEME; + } + + if(data_hit_feature(data, datalen, 0, FINDEX_TCPFILE)) + { + qq_pme->fileinfo.prot_flag = QQ_FILECONT; + } + else if(data_hit_feature(data, datalen, 0, FINDEX_TCPFILE_END)) + { + qq_pme->fileinfo.prot_flag = QQ_FILECONT_END; + } + else + { + //еʱ򿴵мС飬轫߳ + unsigned short block_len = ntohs(*((unsigned short *)&data[3])); + if(*data == TCPFILE_START_CHAR04 && block_len < TCPFILE_HDR_LEN04) + { + data += block_len; + datalen -= block_len; + } + else + { + return APP_STATE_DROPME; + } + } + + //ͷȹͷȡ鳤ȣ鲻Ҳ + if(*data == TCPFILE_START_CHAR04 && datalen>TCPFILE_HDR_LEN04) + { + qq_pme->fileinfo.block_len = ntohs(*((unsigned short *)&data[3])); + qq_pme->compress_flag = data[TCPFILE_HDR_LEN04-1]; + if(datalen < qq_pme->fileinfo.block_len) + { + if(save_initial_tcp_data(&qq_pme->fileinfo, data, datalen, qq_pme->thread_id) < 0) + { + return APP_STATE_DROPME; + } + return APP_STATE_GIVEME; + } + + rec = protocol_process(qq_pme, qq_pme->fileinfo.prot_flag, data+TCPFILE_HDR_LEN04, qq_pme->fileinfo.block_len-TCPFILE_HDR_LEN04-1, a_tcp, raw_pkt); + if(rec & APP_STATE_DROPME) + { + return rec; + } + data += qq_pme->fileinfo.block_len; + datalen -= qq_pme->fileinfo.block_len; + } + } + break; + + case QQ_STATE_FCONT_END: + case QQ_STATE_FCONT: + case QQ_STATE_INIT: + default: + break; + } + + return APP_STATE_GIVEME; +} + +extern "C" char QQ_PROT_TCP_ENTRY(struct streaminfo *a_tcp, void **pme, int thread_seq,const void *raw_pkt) +{ + QQ_PACKET_TYPE_t type; + qq_proto_pme_t *qq_pme=(qq_proto_pme_t*)(*pme); + int rec = APP_STATE_GIVEME; + + switch(a_tcp->opstate) + { + case OP_STATE_PENDING: + type = is_qq_protocol_tcp((char *)a_tcp->ptcpdetail->pdata, a_tcp->ptcpdetail->datalen); + if(type) + { + if(init_qq_pme_info(&qq_pme, a_tcp->threadnum, 1)) + return APP_STATE_DROPME; + qq_pme->qq_state = assign_qq_state(type); + *pme = qq_pme; + } + else + { + return APP_STATE_DROPME; + } + //no break here + + case OP_STATE_DATA: + qq_pme->qq_state = analyse_tcp_pdata_state(qq_pme->qq_state, (char *)a_tcp->ptcpdetail->pdata, a_tcp->ptcpdetail->datalen); + + rec = process_tcp_data_state(a_tcp, qq_pme, thread_seq, raw_pkt); + break; + + case OP_STATE_CLOSE: + clear_qq_pme_info(qq_pme, a_tcp, raw_pkt); + break; + + default: + return APP_STATE_DROPME; + } + + if(rec & APP_STATE_DROPME) + { + clear_qq_pme_info(qq_pme, a_tcp, raw_pkt); + } + + return rec; +} + +int mkdir_according_path(const char * path) +{ + char buffer[256]; + const char *ps=path, *pc; + + if(*ps == '/') + ps += 1; + + while((pc = strchr(ps, '/')) != NULL) + { + while(*(pc+1) == '/') + pc++; + + memcpy(buffer, path, pc - path); + buffer[pc-path] = '\0'; + + if(access(buffer, F_OK)) + { + if(mkdir(buffer, 0777)) + { + return -1; + } + } + + ps = pc + 1; + } + if(access(path, F_OK)) + { + if(mkdir(path, 0777)) + { + return -1; + } + } + return 0; +} + +static int register_field_stat(const char *logpath) +{ + unsigned int stat_cycle; + int ret, i; + char stat_log[128]; + const char *region_name[STAT_ID_NUM]={"NEW_SESSION", "DEL_SESSION", "SESSION_NUM"}; + + snprintf(stat_log, 128, "%s/qq_stat.log", logpath); + + ret = MESA_load_profile_uint_def(CONF_FILENAME, "MODULE", "STAT_FIELD_CYCLE",&stat_cycle, 5); + if(ret < 0) + { + MESA_HANDLE_RUNTIME_LOG(g_qq_global_info.runtime_log, RLOG_LV_INFO, MODULE_NAME,"get [MODULE] STAT_FIELD_CYCLE failed, using default 5."); + } + ret = MESA_load_profile_int_def(CONF_FILENAME, "MODULE", "STAT_FIELD_TRIG",&g_qq_global_info.stat_trig, 0); + if(ret < 0) + { + MESA_HANDLE_RUNTIME_LOG(g_qq_global_info.runtime_log, RLOG_LV_INFO, MODULE_NAME,"get [MODULE] STAT_FIELD_TRIG failed, using default 0."); + } + + if(g_qq_global_info.stat_trig==0) + return 0; + + FILE* stat_fd = fopen(stat_log, "w"); + if(stat_fd == NULL) + { + MESA_HANDLE_RUNTIME_LOG(g_qq_global_info.runtime_log, RLOG_LV_INFO, MODULE_NAME,"fopen %s failed.", stat_log); + return -1; + } + + g_qq_global_info.stat_handle = init_screen_stat(stat_fd, stat_cycle, g_qq_global_info.stat_trig); + if(g_qq_global_info.stat_handle==NULL) + { + MESA_HANDLE_RUNTIME_LOG(g_qq_global_info.runtime_log, RLOG_LV_INFO, MODULE_NAME,"init_screen_stat failed."); + return -1; + } + + for(i=0;i= 0); + + srand(time(NULL)); + return 0; +} + +extern "C" void QQ_PROT_DESTROY(void) +{ +} + diff --git a/src/qq_file_entry.h b/src/qq_file_entry.h new file mode 100644 index 0000000..23e6a32 --- /dev/null +++ b/src/qq_file_entry.h @@ -0,0 +1,125 @@ +#ifndef __QQ_FILE_ENTRY_H__ +#define __QQ_FILE_ENTRY_H__ + +#include + +#include + +#define CONF_FILENAME "./conf/qq_file_trans.conf" +#define QQ_DEBUG_FILEPATH "./qq_files" +#define MODULE_NAME "QQ_FILE" + +#define MAX_QQ_FEATURE_NUM 16 +#define MAX_AND_RULE 32 + +#define PACKET_MIN_LEN_QQNUM 16 +#define PACKET_MIN_LEN_TCPNAME 551 +#define PACKET_MIN_LEN_UDPNAME 535 + +#define TCPFILE_START_CHAR04 0x04 +#define TCPFILE_END_CHAR04 0x03 +#define TCPFILE_HDR_LEN04 53 + +#define TCPFILE_START_CHAR0c 0x0c +#define TCPFILE_HDR_LEN0c 22 + +typedef enum __QQ_PACKET_TYPE +{ + QQ_PACKET_NOT=0, + QQ_PACKET_QQNUM, + QQ_PACKET_TCPFILE, + QQ_PACKET_TCPFILENAME, + QQ_PACKET_UDPFILE, + QQ_PACKET_UDPFILE_END, + QQ_PACKET_UDPFILENAME, + QQ_PACKET_OTHER, +}QQ_PACKET_TYPE_t; + +typedef enum __QQ_STATE +{ + QQ_STATE_INIT=0, + QQ_STATE_QQNUM, + QQ_STATE_FNAME, + QQ_STATE_FCONT_START, + QQ_STATE_FCONT, + QQ_STATE_FCONT_END, +}QQ_STATE_t; + +typedef enum __FEATURE_INDEX +{ + FINDEX_TCPPROT=0, + FINDEX_TCPNUM, + FINDEX_TCPNAME, + FINDEX_TCPFILE, + FINDEX_TCPFILE_END, + FINDEX_UDPPROT, + FINDEX_UDPNUM, + FINDEX_UDPNAME, + FINDEX_UDPFILE, + FINDEX_UDPFILE_END, + + FINDEX_NUM, +}FEATURE_INDEX_T; + +enum __FIELD_STAT_ID +{ + STAT_ID_NEW_SESSION, + STAT_ID_DEL_SESSION, + STAT_ID_SESSION_NUM, + STAT_ID_NUM, +}; + +typedef struct __trans_file_info +{ + char *buf; + unsigned int len; + unsigned int max_len; + long long prot_flag; + + int block_seq; //ݰţʱشUDP + unsigned short block_len; //һСĴСһһĴСTCP + unsigned short file_seq; //һСĴСһһĴСTCP +}trans_file_info_t; + +typedef struct __qq_proto_pme +{ + void *connector_hdl; + char filename[512]; + int filename_len; + QQ_STATE_t qq_state; + unsigned int qqnum[2]; + unsigned int qqnum_from; + unsigned int qqnum_to; + + trans_file_info_t fileinfo; + + int thread_id; + unsigned int file_dir; + unsigned int total_file_len; + unsigned int curr_file_len; + char pending_flag; + char end_flag; + char compress_flag; + char tcp_flag; + unsigned int random; +}qq_proto_pme_t; + +typedef struct __qq_global_info +{ + void *runtime_log; + void *stat_handle; + docanalyze_instance_t instanse; + char *feature[FINDEX_NUM][MAX_QQ_FEATURE_NUM][MAX_AND_RULE]; + unsigned int ftlen[FINDEX_NUM][MAX_QQ_FEATURE_NUM][MAX_AND_RULE]; + unsigned int ftoff[FINDEX_NUM][MAX_QQ_FEATURE_NUM][MAX_AND_RULE]; + unsigned int rulenum[FINDEX_NUM][MAX_QQ_FEATURE_NUM]; //ÿһڲ + unsigned int fnum[FINDEX_NUM]; + + int project_id; + int stat_trig; + int stat_id[STAT_ID_NUM]; + int debug_sw; +}qq_global_info_t; + +#endif + diff --git a/src/qq_file_global.c b/src/qq_file_global.c new file mode 100644 index 0000000..ce74f1c --- /dev/null +++ b/src/qq_file_global.c @@ -0,0 +1,308 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "qq_file_entry.h" +#include "qq_file_global.h" +#include "connector.h" + +#define QQ_OPT_SERVICE "SERVICE_IMQQ" +#define QQ_OPT_FILESET "opt_file_set" +#define QQ_OPT_FILENAME "opt_file_name" +#define QQ_OPT_FILECONT "opt_file_content" +#define QQ_OPT_NUMFROM "opt_im_from" +#define QQ_OPT_NUMTO "opt_im_to" +#define QQ_OPT_ACCOUNT "opt_im_account" + +extern qq_global_info_t g_qq_global_info; +const unsigned char gzip_feature[4]={0x1f, 0x8b, 0x08, 0x00}; + +int write_file_cont2disk(char *buffer, int filelen, char *filename, int seq, int random) +{ + FILE *fp; + int writelen, offset=0; + char filenamestr[512]; + + sprintf(filenamestr, "%s/%d_%u_%s", QQ_DEBUG_FILEPATH, seq, random, filename); + + fp = fopen(filenamestr, "ab+"); + if(fp == NULL) + { + printf("fopen file %s failed.\n", filenamestr); + return -1; + } + + while(offset < filelen) + { + writelen = fwrite(buffer + offset, 1, filelen - offset, fp); + if(writelen < 0) + { + printf("read error: %s\n", strerror(errno)); + return -1; + } + + offset += writelen; + } + fclose(fp); + return 0; +} + +int buf_cache_check_size(trans_file_info_t *pdstBuf, int wantedlen, int thread_seq) +{ + int ret=0, needlen, block_num=0; + + if(pdstBuf->max_len > wantedlen + pdstBuf->len) + return 0; + + needlen = wantedlen + pdstBuf->len - pdstBuf->max_len; + block_num = needlen/REALLOC_BLOCK_SIZE; + if(needlen%REALLOC_BLOCK_SIZE) + block_num += 1; + + pdstBuf->max_len += block_num*REALLOC_BLOCK_SIZE; + + if(pdstBuf->max_len > MAX_MALLOC_SIZE) + { + pdstBuf->max_len -= block_num*REALLOC_BLOCK_SIZE; + return -1; + } + + if(pdstBuf->buf == NULL) + { + pdstBuf->buf = (char *)dictator_malloc(thread_seq, pdstBuf->max_len); + } + else + { + pdstBuf->buf = (char *)dictator_realloc(thread_seq, pdstBuf->buf, pdstBuf->max_len); + ret = 1; + } + + return ret; +} + +int save_initial_tcp_data(trans_file_info_t *pdstBuf, char *data, int datalen, int thread_seq) +{ + if(buf_cache_check_size(pdstBuf, datalen, thread_seq) < 0) + { + return -1; + } + memcpy(pdstBuf->buf+pdstBuf->len, data, datalen); + pdstBuf->len += datalen; + + return 0; +} + + +int convert_from_unicode2utf8(char *in, int in_len, char *out, int *out_len) +{ + iconv_t cd; + size_t inlen = in_len, outlen=*out_len, ret; + + cd = iconv_open("utf-8", "unicode"); + if(cd == (iconv_t)-1) + { + return -1; + } + ret = iconv(cd, &in, &inlen, &out, &outlen); + if(ret == (size_t)-1) + { + iconv_close(cd); + return -1; + } + iconv_close(cd); + *out_len = outlen; + + return 0; +} + +int sendback_qq_data(qq_proto_pme_t *qq_pme, struct streaminfo *a_udp, char *filecont, int contlen) +{ + int nest_opt_num = 1; + void *nested_opt_handle, *nested_opt_unit_handle, *simple_opt=NULL; + + if(qq_pme->connector_hdl == NULL) + { + appd_basic_t basic; + ulong64 streamid[1]; + char qqfrom[32], qq_to[32]; + + streamid[0] = project_req_get_ulong(a_udp, g_qq_global_info.project_id); + + basic.addr = (struct ipaddr*)&a_udp->addr; + basic.addr_num = 1; + basic.appd_type = APPD_SENDBACK; + basic.destination = DEST_APPD; + basic.service_name = QQ_OPT_SERVICE; + basic.streamid = streamid; + basic.streamid_num = 1; + + qq_pme->connector_hdl = connector_start(&basic, a_udp); + if(qq_pme->connector_hdl == NULL) + return APP_STATE_DROPME; + + if(qq_pme->filename_len > 0) + { + nest_opt_num = 2; + } + sprintf(qqfrom, "%u", qq_pme->qqnum_from); + sprintf(qq_to, "%u", qq_pme->qqnum_to); + + simple_opt = connector_create_simple_opt(3); + connector_append_simple_opt(simple_opt, QQ_OPT_NUMFROM,qqfrom, strlen(qqfrom)); + connector_append_simple_opt(simple_opt, QQ_OPT_ACCOUNT,qqfrom, strlen(qqfrom)); + connector_append_simple_opt(simple_opt, QQ_OPT_NUMTO,qq_to, strlen(qq_to)); + connector(qq_pme->connector_hdl, COMPLETE, simple_opt, NULL); + connector_free_simple_opt(&simple_opt); + } + + nested_opt_handle = connector_create_nested_opt(1); + nested_opt_unit_handle = connector_create_nested_opt_unit(QQ_OPT_FILESET, nest_opt_num); + if(nest_opt_num == 2) + { + connector_append_nested_opt_unit(nested_opt_unit_handle, QQ_OPT_FILENAME,qq_pme->filename, qq_pme->filename_len); + } + connector_append_nested_opt_unit(nested_opt_unit_handle, QQ_OPT_FILECONT,filecont, contlen); + connector_append_nested_opt(nested_opt_handle,nested_opt_unit_handle); + + if(qq_pme->end_flag) + { + connector(qq_pme->connector_hdl, FRAG_END, NULL, nested_opt_handle); + connector_finish(qq_pme->connector_hdl); + qq_pme->connector_hdl = NULL; + } + else + { + connector(qq_pme->connector_hdl, FRAG, NULL, nested_opt_handle); + } + connector_free_nested_opt_unit(&nested_opt_unit_handle); + connector_free_nested_opt(&nested_opt_handle); + + return APP_STATE_GIVEME; +} + +int protocol_process(qq_proto_pme_t *qq_pme, long long prot_flag, char *buf_in, int inlen, struct streaminfo *a_udp, const void *raw_pkt) +{ + int rec = APP_STATE_GIVEME; + char buffer[512]={0}, *buf=buf_in; + int outlen=512, ret, buflen=inlen, k; + result_array_t result_array; + + switch(prot_flag) + { + case QQ_FILENAME: + if(qq_pme->pending_flag==0) + break; + else + { + qq_pme->fileinfo.block_seq = -1; //ݿ¼ + qq_pme->end_flag = 0; + qq_pme->pending_flag = 0; + qq_pme->fileinfo.file_seq += 1; + + if(qq_pme->qqnum_from == 0) + { + qq_pme->qqnum_to = qq_pme->qqnum[qq_pme->file_dir]; + if(qq_pme->qqnum_to == qq_pme->qqnum[0]) + qq_pme->qqnum_from = qq_pme->qqnum[1]; + else + qq_pme->qqnum_from = qq_pme->qqnum[0]; + } + + if(g_qq_global_info.debug_sw) + { + qq_pme->random = rand(); + } + } + if(!convert_from_unicode2utf8(buf_in, inlen, buffer, &outlen)) + { + snprintf(qq_pme->filename, sizeof(qq_pme->filename), "%s", buffer); + qq_pme->filename_len = strlen(qq_pme->filename); + MESA_HANDLE_RUNTIME_LOGV2(g_qq_global_info.runtime_log,RLOG_LV_DEBUG, MODULE_NAME, "DETECT_QQ_FILE %s, QQ_FROM: %u, QQ_TO: %u, FILE_LEN: %u, FILENAME: %s", \ + qq_pme->tcp_flag?"TCP":"UDP", qq_pme->qqnum_from, qq_pme->qqnum_to, qq_pme->total_file_len,qq_pme->filename); + } + else + { + MESA_HANDLE_RUNTIME_LOGV2(g_qq_global_info.runtime_log,RLOG_LV_INFO, MODULE_NAME, "convert_from_unicode2utf8 error: %s\n", strerror(errno)); + return APP_STATE_DROPME; + } + break; + + case QQ_FILECONT_END: + case QQ_FILECONT: + if(qq_pme->filename_len == 0) + return APP_STATE_DROPME; + + if(prot_flag==QQ_FILECONT_END) + { + if(qq_pme->end_flag || qq_pme->pending_flag) + break; + else + { + qq_pme->end_flag = 1; + qq_pme->pending_flag = 1; + } + } + else if(qq_pme->pending_flag) + { + qq_pme->end_flag = 0; + qq_pme->pending_flag = 0; + } + + if(inlen >= 4 && !memcmp(buf_in, gzip_feature, 4) && qq_pme->compress_flag) + { + docanalyze_streamparam_t doc_hanlde = docanalyze_startstream(DOC_GZIP_TYPE, g_qq_global_info.instanse, qq_pme->thread_id); + if(doc_hanlde ==NULL) + { + MESA_HANDLE_RUNTIME_LOGV2(g_qq_global_info.runtime_log,RLOG_LV_DEBUG, MODULE_NAME, "docanalyze_startstream() error."); + return APP_STATE_GIVEME; + } + memset(&result_array, 0, sizeof(result_array_t)); + ret = docanalyze_parsestream(doc_hanlde, buf_in, inlen, &result_array); + if(ret == DOC_PRO_OK) + { + for (k=0; k < result_array.result_num; k++) + { + buf = result_array.result_buff[k].presult; + buflen = result_array.result_buff[k].size; + } + } + qq_pme->curr_file_len += buflen; + + if(g_qq_global_info.debug_sw) + { + write_file_cont2disk(buf, buflen, qq_pme->filename, qq_pme->fileinfo.file_seq, qq_pme->random); + } + rec = sendback_qq_data(qq_pme, a_udp, buf, buflen); + + docanalyze_freeresult(&result_array); + docanalyze_endstream(doc_hanlde); + } + else + { + qq_pme->curr_file_len += buflen; + + if(g_qq_global_info.debug_sw) + { + write_file_cont2disk(buf, buflen, qq_pme->filename, qq_pme->fileinfo.file_seq, qq_pme->random); + } + rec = sendback_qq_data(qq_pme, a_udp, buf, buflen); + } + break; + + default: break; + } + + return rec; +} + diff --git a/src/qq_file_global.h b/src/qq_file_global.h new file mode 100644 index 0000000..2f1ad94 --- /dev/null +++ b/src/qq_file_global.h @@ -0,0 +1,21 @@ +#ifndef __QQ_FILE_GLOBAL_H__ +#define __QQ_FILE_GLOBAL_H__ + +#define QQ_FILENAME 1 +#define QQ_FILECONT 2 +#define QQ_FILECONT_END 4 + +#define MAX_MALLOC_SIZE (16*1024) +#define REALLOC_BLOCK_SIZE 1024 + +#define MESA_HANDLE_RUNTIME_LOGV2(handle, lv, mod, fmt, args...) \ + MESA_handle_runtime_log((handle), (lv), (mod), "%s:%d, " fmt, \ + __FILE__, __LINE__, ##args) + +int buf_cache_check_size(trans_file_info_t *pdstBuf, int wantedlen, int thread_seq); +int save_initial_tcp_data(trans_file_info_t *pdstBuf, char *data, int datalen, int thread_seq); + +int protocol_process(qq_proto_pme_t *qq_pme, long long prot_flag, char *buf, int buflen, struct streaminfo *a_udp, const void *raw_pkt); + +#endif +