/*
 * quic_process.c
 *
 *  Created on: 2019Äê4ÔÂ2ÈÕ
 *      Author: root
 */

#include <stdio.h>
#include <assert.h>
#include <stdbool.h>
#include <sys/time.h>
#include <MESA/stream.h>
#include <MESA/MESA_handle_logger.h>

#include "gquic_process.h"
#include "quic_analysis.h"
#include "parser_quic.h"

#ifndef	PRINTADDR
#define	PRINTADDR(a, b)	((b)<RLOG_LV_FATAL ? printaddr(&(a->addr), a->threadnum) : "")
#endif

#ifndef MIN
#define MIN(a, b)  	(((a) < (b)) ? (a) : (b))
#endif

const unsigned char PCAP_FILE_HEAD[24] = {0xD4, 0xC3, 0xB2, 0xA1, 0x02, 0x00, 0x04, 0x00,
								 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
								 0xFF, 0xFF, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00};

struct pcap_hdr
{
    unsigned int tv_sec;
    unsigned int tv_usec;
    unsigned int len;
    unsigned int caplen;
};

int dump_packet(struct streaminfo *pstream)
{
	int ret=0;
	char buff[2048]={0};
	char filename[512]={0};
	void *p_eth_rawpkt=NULL;
	int eth_rawpkt_len=0;
	struct pcap_hdr pcap_hdr;
	struct timeval current_time;

	if(g_quic_param.dump_packet_switch==0)
	{
		return 0;
	}

	ret=get_rawpkt_opt_from_streaminfo(pstream, RAW_PKT_GET_DATA, &p_eth_rawpkt);
	if(ret==0)
	{
		ret=get_rawpkt_opt_from_streaminfo(pstream, RAW_PKT_GET_TOT_LEN, &eth_rawpkt_len);
		if(ret<0)
		{
			return -1;
		}
		snprintf(filename, sizeof(filename), "%s-%s.pcap", g_quic_param.log_path, printaddr(&(pstream->addr), pstream->threadnum));
		FILE *fp=fopen(filename, "a+");
		if(fp)
		{
			gettimeofday(&current_time, NULL);
			pcap_hdr.tv_sec = current_time.tv_sec;
			pcap_hdr.tv_usec = current_time.tv_usec;
			pcap_hdr.caplen = eth_rawpkt_len;
			pcap_hdr.len = pcap_hdr.caplen;

			memcpy(buff, PCAP_FILE_HEAD, 24);
			memcpy(buff+24, &pcap_hdr, sizeof(pcap_hdr));
			memcpy(buff+24+sizeof(pcap_hdr), p_eth_rawpkt, eth_rawpkt_len);
			fwrite(buff, eth_rawpkt_len+24+sizeof(pcap_hdr), 1, fp);

			fclose(fp);
			fp=NULL;
		}
	}

	return 0;
}

int is_iquic(enum _QUIC_VERSION quic_version)
{
	switch(quic_version)
	{
		case	IQUIC_VERSION_I001:
		case	IQUIC_VERSION_I002:
		case	IQUIC_VERSION_I003:
		case	IQUIC_VERSION_I004:
		case	IQUIC_VERSION_I005:
		case	IQUIC_VERSION_I006:
		case	IQUIC_VERSION_I007:
		case	IQUIC_VERSION_I008:
		case	IQUIC_VERSION_I009:
		case	IQUIC_VERSION_I010:
		case	IQUIC_VERSION_I011:
		case	IQUIC_VERSION_I012:
		case	IQUIC_VERSION_I013:
		case	IQUIC_VERSION_I014:
		case	IQUIC_VERSION_I015:
		case	IQUIC_VERSION_I016:
		case	IQUIC_VERSION_I017:
		case	IQUIC_VERSION_I018:
		case	IQUIC_VERSION_I019:
		case	IQUIC_VERSION_I020:
		case	IQUIC_VERSION_I021:
		case	IQUIC_VERSION_I022:
		case	IQUIC_VERSION_I023:
		case	IQUIC_VERSION_I024:
		case	IQUIC_VERSION_I025:
		case	IQUIC_VERSION_I026:
		case	IQUIC_VERSION_I027:
		case	IQUIC_VERSION_I028:
		case	IQUIC_VERSION_I029:
		case	IQUIC_VERSION_I030:
		case	IQUIC_VERSION_I031:
		case	IQUIC_VERSION_I032:
			return TRUE;
			break;
		default:
				break;
	}
	
	return FALSE;
}

int check_port(unsigned short port)
{
	int i=0;
	for(i=0; i< g_quic_param.quic_port_num; i++)
	{
		if(g_quic_param.quic_port_list[i]==port)
		{
			return 1;
		}
	}

	return 0;
}

int is_quic_port(struct streaminfo *pstream)
{
	unsigned short source=0, dest=0;
	
	switch(pstream->addr.addrtype)
	{
		case	ADDR_TYPE_IPV4:
		case	__ADDR_TYPE_IP_PAIR_V4:
			source=(unsigned short)ntohs(pstream->addr.ipv4->source);
			dest=(unsigned short)ntohs(pstream->addr.ipv4->dest);
			break;
		case	ADDR_TYPE_IPV6:
		case	__ADDR_TYPE_IP_PAIR_V6:
			source=(unsigned short)ntohs(pstream->addr.ipv6->source);
			dest=(unsigned short)ntohs(pstream->addr.ipv6->dest);
			break;
		default:
			return 0;
			break;
	}

	if(check_port(source) || check_port(dest))
	{
		return 1;
	}

	return 0;
}

static long get_value(unsigned char *payload, int *offset, int len)
{
	switch(len)
	{
		case	1:
			return (long)(payload[(*offset)++]);
			break;
		case	2:
			(*offset)+=len;
			return (long)ntohs(*(unsigned short *)(payload+*offset-len));
			break;
		case	3:
			(*offset)+=len;
			return ((long)*(payload-2+*offset)<<16|
		           (long)*(payload-1+*offset)<<8|
		           (long)*(payload+*offset)<<0);
			break;
		case	4:
			(*offset)+=len;
			return (long)ntohl(*(unsigned int *)(payload+*offset-len));
			break;
		case	8:
			(*offset)+=len;
			return ((long)*(payload-7+*offset)<<56|
					(long)*(payload-6+*offset)<<48|
					(long)*(payload-5+*offset)<<40|
					(long)*(payload-4+*offset)<<32|
					(long)*(payload-3+*offset)<<24|
					(long)*(payload-2+*offset)<<16|
		            (long)*(payload-1+*offset)<<8|
		            (long)*(payload+*offset)<<0);
			break;
		case	32:
			(*offset)+=len;
			return 0;
			break;
		default:
			break;
	}

	return 0;
}


static int check_length(int last_len, int field_len)
{
	return ((last_len-field_len>0) ? 1 : 0);
}

int quic_getLinkState(struct _quic_context *_context)
{
	UCHAR state = 0;
	
	if(0==_context->link_state)
	{
		state=SESSION_STATE_PENDING|SESSION_STATE_DATA;
		_context->link_state=1;
	}
	else
	{
		state=SESSION_STATE_DATA;
	}
	
	return state;
}

char quic_callPlugins(struct streaminfo *pstream, struct _quic_context *_context, void *buff, int buff_len, enum quic_interested_region region_mask, void *a_packet)
{
	char state=PROT_STATE_GIVEME;
	char app_state=APP_STATE_GIVEME;
	stSessionInfo session_info={0};

	if(region_mask==QUIC_INTEREST_KEY_MASK)
	{
		session_info.plugid=g_quic_param.quic_plugid;
		session_info.prot_flag=0;
		session_info.session_state=SESSION_STATE_CLOSE;
		session_info.app_info=NULL;
		session_info.buf=NULL;
		session_info.buflen=0;
	}
	else
	{
		session_info.plugid=g_quic_param.quic_plugid;
		session_info.prot_flag=(((unsigned long long)1)<<region_mask);
		session_info.session_state=quic_getLinkState(_context) ;
		session_info.app_info=(void*)(&_context->quic_info);
		session_info.buf=buff;
		session_info.buflen=buff_len;
	}
	state=PROT_PROCESS(&session_info, &(_context->business_pme), pstream->threadnum, pstream, a_packet);

	if(state&PROT_STATE_DROPPKT)
	{
		app_state=APP_STATE_DROPPKT;
	}

	return app_state;
}


unsigned long long get_variable_length(char *p, int offset, int v_len)
{
	switch(v_len)
	{
		case	1:
			return (unsigned long long)(p[offset]);
			break;
		case	2:
			return (unsigned long long)ntohs(*(unsigned short *)((char *)p+offset));
			break;
		case	3:
			return (unsigned long long)*(p+0+offset)<<16|
		           (unsigned long long)*(p+1+offset)<<8|
		           (unsigned long long)*(p+2+offset)<<0;
			break;
		case	4:
			return (unsigned long long)ntohl(*(unsigned int *)(p+offset));
			break;
		case	5:
			return (unsigned long long)*((unsigned char *)(p)+0+offset)<<32|
		           (unsigned long long)*((unsigned char *)(p)+1+offset)<<24|
		           (unsigned long long)*((unsigned char *)(p)+2+offset)<<16|
		           (unsigned long long)*((unsigned char *)(p)+3+offset)<<8|
		           (unsigned long long)*((unsigned char *)(p)+4+offset)<<0;
			break;
		case	6:
			return (unsigned long long)*((unsigned char *)(p)+0+offset)<<40|
		           (unsigned long long)*((unsigned char *)(p)+1+offset)<<32|
		           (unsigned long long)*((unsigned char *)(p)+2+offset)<<24|
		           (unsigned long long)*((unsigned char *)(p)+3+offset)<<16|
		           (unsigned long long)*((unsigned char *)(p)+4+offset)<<8|
		           (unsigned long long)*((unsigned char *)(p)+5+offset)<<0;
			break;
		case	7:
			return (unsigned long long)*((unsigned char *)(p)+0+offset)<<56|
		           (unsigned long long)*((unsigned char *)(p)+1+offset)<<40|
		           (unsigned long long)*((unsigned char *)(p)+2+offset)<<32|
		           (unsigned long long)*((unsigned char *)(p)+3+offset)<<24|
		           (unsigned long long)*((unsigned char *)(p)+4+offset)<<16|
		           (unsigned long long)*((unsigned char *)(p)+5+offset)<<8|
				   (unsigned long long)*((unsigned char *)(p)+6+offset)<<0;
			break;
		case	8:
			return (unsigned long long)*((unsigned char *)(p)+0+offset)<<56|
		           (unsigned long long)*((unsigned char *)(p)+1+offset)<<48|
		           (unsigned long long)*((unsigned char *)(p)+2+offset)<<40|
		           (unsigned long long)*((unsigned char *)(p)+3+offset)<<32|
		           (unsigned long long)*((unsigned char *)(p)+4+offset)<<24|
		           (unsigned long long)*((unsigned char *)(p)+5+offset)<<16|
		           (unsigned long long)*((unsigned char *)(p)+6+offset)<<8|
		           (unsigned long long)*((unsigned char *)(p)+7+offset)<<0;
			break;
		default:
			break;
	}

	return 0;
}

long long bit_to_value(char *payload, int payload_len, unsigned char flags, unsigned long long *out_value, int *used_len)
{
	switch(flags&0x3) // packet number
	{
		case	0x3: // 6 bytes
			if(!check_length(payload_len-*used_len, 6))
			{
				return -1;
			}
			*out_value=get_variable_length(payload, *used_len, 6);
			*used_len+=6;
			break;
		case	0x2: // 4 bytes
			if(!check_length(payload_len-*used_len, sizeof(unsigned int)))
			{
				return -1;
			}
			*out_value=(unsigned long long)ntohl(*(unsigned int *)(payload+*used_len));
			*used_len+=4;
			break;
		case	0x1: // 2bytes
			if(!check_length(payload_len-*used_len, sizeof(unsigned short)))
			{
				return -1;
			}
			*out_value=(unsigned long long)ntohs(*(unsigned short *)(payload+*used_len));
			*used_len+=2;
			break;
		default:	// 1 byte
			if(!check_length(payload_len-*used_len, sizeof(unsigned char)))
			{
				return -1;
			}
			*out_value=payload[*used_len];
			*used_len+=1;
			break;
	}

	return 0;
}

int get_quic_tlv(char *start_pos, quic_tlv_t *tlv, int len, int type, int thread_seq)
{
	if(tlv->value==NULL && len>0)
	{
		tlv->value=(char *)dictator_malloc(thread_seq, len+1);
		memset(tlv->value, 0, len+1);
		tlv->length=len;
		tlv->type=type;
		memcpy(tlv->value, start_pos, tlv->length);
	}
	
	return 0;
}

int get_stream_id(struct streaminfo *pstream, struct _quic_context* _context, char* payload, int payload_len, unsigned char frame_type, int *used_len)
{
	int stream_len=0,offset_len=0;

	_context->quic_info.frame_hdr.frame_type=frame_type;
	
	stream_len=(frame_type&GQUIC_SPECIAL_FRAME_STREAM_ID)+1;
	if(!check_length(payload_len-*used_len, stream_len))
	{
		return -1;
	}
		
	_context->quic_info.frame_hdr.stream_id=(unsigned int)get_variable_length(payload, *used_len, stream_len);
	*used_len+=stream_len;  // stream ID length

	if(frame_type&GQUIC_SPECIAL_FRAME_STREAM_DLEN)
	{
		if(!check_length(payload_len-*used_len, 2))
		{
			return -1;
		}
		_context->quic_info.frame_hdr.data_len=ntohs(*(unsigned short *)(payload+*used_len));
		*used_len+=2;  //data length
	}
			
	offset_len=(frame_type&GQUIC_SPECIAL_FRAME_STREAM_OFFSET) ? (((frame_type&GQUIC_SPECIAL_FRAME_STREAM_OFFSET))>>2)+1 : 0;
	if(!check_length(payload_len-*used_len, offset_len))
	{
		return -1;
	}
		
	_context->quic_info.frame_hdr.offset=get_variable_length(payload, *used_len, offset_len);
	*used_len+=offset_len;

	MESA_handle_runtime_log(g_quic_param.logger,
							RLOG_LV_DEBUG,
							"QUIC_FRAME",
							"frame_type: 0X%02X stream_id: %u data length: %u offset length: %u addr: %s",
							frame_type,
							_context->quic_info.frame_hdr.stream_id,
							_context->quic_info.frame_hdr.data_len,
							offset_len,
							PRINTADDR(pstream, g_quic_param.level)
						);

	return _context->quic_info.frame_hdr.stream_id;
}

unsigned long long get_packet_number(char* data, int offset, char pkn_len)
{
	switch(pkn_len)
	{
		case	1:
			return (unsigned long long)data[offset];
			break;
		case	2:
			return (unsigned long long)ntohs(*(unsigned short *)(data+offset));
			break;
		case	4:
			return (unsigned long long)ntohl(*(unsigned int *)(data+offset));
			break;
		case	8:
			return get_variable_length(data, offset, 8);;
			break;
	}

	return 0;
}

// GQUIC version from 0 to 43
static enum _QUIC_VERSION parse_q0to43_header(struct streaminfo *pstream, struct _quic_context* _context, char *payload, int payload_len, int *used_len)
{
	int ret=0;
	char public_flags=0;
	
	struct _quic_public_header *gquic_hdr=&(_context->quic_info.quic_hdr);

	public_flags=payload[*used_len];
	*used_len+=1;
	gquic_hdr->public_flags=public_flags;

	if((public_flags&GQUIC_PUBLIC_FLAG_RST) && _context->is_quic==TRUE)
	{
		gquic_hdr->is_reset=TRUE; //Public Reset Packet
		return QUIC_VERSION_UNKNOWN;
		
	}

	if(pstream->curdir==DIR_S2C && gquic_hdr->public_flags&GQUIC_PUBLIC_FLAG_VERSION)
	{
		return QUIC_VERSION_UNKNOWN;
	}

	//For Public Reset and Version Negotiation Packets (sent by the server) which don't have a packet number
	if(!public_flags&GQUIC_PUBLIC_FLAG_PKT_NUM)
	{
		if(public_flags&GQUIC_PUBLIC_FLAG_VERSION) //Public Reset Packet
		{
			return QUIC_VERSION_UNKNOWN;// todo
		}
		else // Version Negotiation Packet
		{
			return QUIC_VERSION_UNKNOWN;
		}
	}

	if(gquic_hdr->public_flags&GQUIC_PUBLIC_FLAG_CID)
	{
		*(unsigned long long *)gquic_hdr->server_CID=get_variable_length(payload, *used_len, sizeof(gquic_hdr->server_CID));
		*used_len+=sizeof(unsigned long long); // CID length

		_context->is_quic=TRUE;
	}
	
	if(gquic_hdr->public_flags&GQUIC_PUBLIC_FLAG_VERSION && (*(unsigned char *)(payload+*used_len)==0x51))
	{
		gquic_hdr->quic_version=(unsigned int)ntohl(*(unsigned int *)(payload+*used_len));
		*used_len+=sizeof(int); // skip version

		_context->is_quic=TRUE;
	}
	
	if(_context->is_quic==FALSE || gquic_hdr->quic_version<GQUIC_VERSION_Q001 || gquic_hdr->quic_version>GQUIC_VERSION_Q043)
	{
		_context->is_quic=FALSE;
		return QUIC_VERSION_UNKNOWN;
	}

	ret=bit_to_value(payload, payload_len, gquic_hdr->public_flags>>4, &gquic_hdr->packet_number, used_len);
	if(ret<0)
	{
		return (enum _QUIC_VERSION)gquic_hdr->quic_version;
	}
	
	if(gquic_hdr->public_flags==GQUIC_PUBLIC_FLAG_NONCE)
	{
		*used_len+=32; //diversification nonce
	}

	// Version 11 reduced the length of null encryption authentication tag  from 16 to 12 bytes
	if(gquic_hdr->quic_version > GQUIC_VERSION_Q010)
	{
		*used_len+=12;
	}
	else
	{
		*used_len+=16;
	}
	
	// Version 34 removed entropy bits from packets and ACK frames, 
	// removed private flag from packet header and changed the ACK format to specify ranges of packets acknowledged rather than missing ranges.
	if(gquic_hdr->quic_version < GQUIC_VERSION_Q034)
	{
		*used_len+=1;  //private flags
	}

	_context->is_quic=TRUE;
	
	MESA_handle_runtime_log(g_quic_param.logger,
							RLOG_LV_DEBUG,
							"QUIC_IDETIFY",
							"pub_flags: 0X%02X conection ID:[ destination: %llu ] version: Q%03u packet number: %llu dir(1: C2S;2: S2C): %d addr: %s",
							gquic_hdr->public_flags,
							*(unsigned long long *)gquic_hdr->server_CID,
							(((gquic_hdr->quic_version>>8)&0x0000000F)*10) + ((gquic_hdr->quic_version)&0x0000000F),
							gquic_hdr->packet_number,
							pstream->curdir,
							PRINTADDR(pstream, g_quic_param.level)
						);

	return (enum _QUIC_VERSION)gquic_hdr->quic_version;
}

enum _QUIC_VERSION parse_quic_header(struct streaminfo *pstream, struct _quic_context* _context, char *payload, int payload_len, int *used_len)
{
	int i=0,len=0;
	char client_CID[MAX_CONNECT_ID_LEN*2]={0};
	char server_CID[MAX_CONNECT_ID_LEN*2]={0};
	
	struct _quic_public_header *long_hdr=&(_context->quic_info.quic_hdr);

	long_hdr->public_flags=payload[*used_len];
	*used_len+=1; //skip public flags

	if(long_hdr->public_flags&0x80)
	{
		long_hdr->quic_version=(unsigned int)ntohl(*(unsigned int *)(payload+*used_len));
		*used_len+=sizeof(int); // skip version
		
		long_hdr->client_CID_len=(payload[*used_len]&0xF) ? (payload[*used_len]&0xF)+3 : 0;
		long_hdr->server_CID_len=((payload[*used_len]>>4)&0xF) ? ((payload[*used_len]>>4)&0xF)+3 : 0;
		*used_len+=sizeof(char);  // both connection_id length
		
		memcpy(long_hdr->server_CID, (void *)(payload+*used_len), long_hdr->server_CID_len);
		*used_len+=long_hdr->server_CID_len;  // Destination connection_id length
		memcpy(long_hdr->client_CID, (void *)(payload+*used_len), long_hdr->client_CID_len);
		*used_len+=long_hdr->client_CID_len;  // source connection_id length
	}
	else
	{
	  if(pstream->curdir==DIR_C2S)// short header only  destination connection ID
	  {
	  	*used_len+=long_hdr->server_CID_len;   // every packet destination connection ID is same
	  }
	}
	
	len=(long_hdr->public_flags&0x03)+1;
	long_hdr->packet_number=get_packet_number(payload, *used_len, len);
	*used_len+=len;

	*used_len+=12;  //message authentication hash

	_context->is_quic=TRUE;

	for(i=0,len=0;i<long_hdr->server_CID_len; i++) 
	{
		len+=snprintf(server_CID+len, sizeof(server_CID)-len, "%02X", long_hdr->server_CID[i]);
	}

	for(i=0,len=0;i<long_hdr->client_CID_len; i++) 
	{
		len+=snprintf(client_CID+len, sizeof(client_CID)-len, "%02X", long_hdr->client_CID[i]);
	}

	MESA_handle_runtime_log(g_quic_param.logger,
							RLOG_LV_DEBUG,
							"QUIC_IDETIFY",
							"pub_flags: 0X%02X conection ID:[ destination: %s source: %s ] version: Q%03u packet number: %llu dir(1: C2S;2: S2C): %d addr: %s",
							long_hdr->public_flags,
							server_CID,
							client_CID,
							(((long_hdr->quic_version>>8)&0x0000000F)*10) + ((long_hdr->quic_version)&0x0000000F),
							long_hdr->packet_number,
							pstream->curdir,
							PRINTADDR(pstream, g_quic_param.level)
						);
	
	return (enum _QUIC_VERSION)long_hdr->quic_version;
}

enum _QUIC_VERSION is_quic_protocol(struct streaminfo *pstream, struct _quic_context* _context, char *payload, int payload_len, int *used_len)
{
	enum _QUIC_VERSION quic_version=QUIC_VERSION_UNKNOWN;
	
	if(_context->quic_info.quic_hdr.quic_version!=QUIC_VERSION_UNKNOWN)
	{
		if(is_iquic((enum _QUIC_VERSION)(_context->quic_info.quic_hdr.quic_version)))
		{
			return (enum _QUIC_VERSION)(_context->quic_info.quic_hdr.quic_version);
		}
		 
		quic_version=(enum _QUIC_VERSION)(_context->quic_info.quic_hdr.quic_version);
	}
	else
	{
		// The most significant bit (0x80) of byte 0 (the first byte) is set to 1 for long headers
		(payload[*used_len]&0x80) ? (quic_version=(enum _QUIC_VERSION)ntohl(*(unsigned int *)(payload+(*used_len+1)))) : QUIC_VERSION_UNKNOWN;
	}

	switch(quic_version) // +1 meaning: skip public flags
	{
		case	GQUIC_VERSION_Q044:
		case	GQUIC_VERSION_Q045:
		case	GQUIC_VERSION_Q046:
		case	GQUIC_VERSION_Q047:
		case	GQUIC_VERSION_Q048:
			quic_version=parse_quic_header(pstream, _context, payload, payload_len, used_len);
			return quic_version;
			break;
		default:
			if(
				(quic_version==GQUIC_VERSION_Q099) ||
				(quic_version==PICOQUIC_VERSION_30) ||
				(quic_version==PQUIC_VERSION_PROX) ||
				(quic_version==GQUIC_VERSION_T099) ||
				(quic_version>=GQUIC_VERSION_Q049 && quic_version<=GQUIC_VERSION_Q050) ||
				(quic_version>=GQUIC_VERSION_Q051 && quic_version<=GQUIC_VERSION_Q059) ||
				(quic_version>=GQUIC_VERSION_T048 && quic_version<=GQUIC_VERSION_T049) ||
				(quic_version>=GQUIC_VERSION_T050 && quic_version<=GQUIC_VERSION_T059) ||
				(quic_version>=QUANT_VERSION_00 && quic_version<=QUANT_VERSION_FF) ||
				(quic_version>=QUIC_GO_VERSION_00 && quic_version<=QUIC_GO_VERSION_FF) ||
				(quic_version>=QUICLY_VERSION_00 && quic_version<=QUICLY_VERSION_FF) ||
				(quic_version>=MSQUIC_VERSION_00 && quic_version<=MSQUIC_VERSION_0F) ||
				(quic_version>=MOZQUIC_VERSION_00 && quic_version<=MOZQUIC_VERSION_0F) ||
				(quic_version>=MVFST_VERSION_00 && quic_version<=MVFST_VERSION_0F) ||
				(quic_version>=IQUIC_VERSION_I001 && quic_version<=IQUIC_VERSION_I032) ||
				(quic_version==IQUIC_VERSION_RFC9000)
			)
			{
				MESA_handle_runtime_log(g_quic_param.logger, 
											RLOG_LV_DEBUG,
											"QUIC",
											"version: 0x%x addr: %s",
											quic_version, PRINTADDR(pstream, g_quic_param.level)
										);
				
				_context->is_quic=TRUE;
				_context->quic_info.quic_hdr.quic_version=quic_version;
				return quic_version;
			}			
			break;
	}

	// Q001~Q043: 0x80 is currently unused, and must be set to 0
	if(payload[*used_len]>0x80)
	{
		return QUIC_VERSION_UNKNOWN;
	}
	
	return parse_q0to43_header(pstream, _context, payload, payload_len, used_len);
}

int parse_extension_tag(struct streaminfo *pstream, struct _quic_stream **quic_stream, void *a_packet, char *payload, int payload_len, int *used_len, int tag_num)
{
	int tag_used_num=0;
	int tag_type=0;
	int total_tag_len=0,tag_len=0;
	int tag_offset_end=0,pre_tag_offset_end=0;

	if(tag_num>64 || tag_num<0)
	{
		(*used_len)=payload_len;
		
		dump_packet(pstream);
		MESA_handle_runtime_log(g_quic_param.logger, RLOG_LV_FATAL, "QUIC_TAG_NUM", "QUIC_TAG_NUM:%d addr: %s", tag_num, printaddr(&pstream->addr, pstream->threadnum));		
		return -1;
	}
		
	struct _quic_stream *stream=*quic_stream;
	int tag_value_start=tag_num*4*2+(*used_len);  // skip length of type and offset, type(offset)=szieof(int)

	if(stream==NULL)
	{
		stream=(struct _quic_stream *)dictator_malloc(pstream->threadnum, sizeof(struct _quic_stream));
		memset(stream, 0, sizeof(struct _quic_stream));
		stream->ext_tags=(quic_tlv_t *)dictator_malloc(pstream->threadnum, tag_num*sizeof(quic_tlv_t));
		memset(stream->ext_tags, 0, tag_num*sizeof(quic_tlv_t));
		*quic_stream=stream;
		stream->sni_idx=0xFF;
		stream->ver_idx=0xFF;
		stream->ua_idx=0xFF;
	}
	else
	{
		quic_release_exts(pstream->threadnum, stream->ext_tags, stream->ext_tag_num);
		stream->ext_tags=(quic_tlv_t *)dictator_malloc(pstream->threadnum, tag_num*sizeof(quic_tlv_t));
		memset(stream->ext_tags, 0, tag_num*sizeof(quic_tlv_t));
		*quic_stream=stream;
		stream->ext_tag_num=0;
		stream->count++;
		stream->sni_idx=0xFF;
		stream->ver_idx=0xFF;
		stream->ua_idx=0xFF;
	}

	while(tag_num>tag_used_num)
	{
		tag_type=ntohl(*(unsigned int *)(payload+*used_len));
		*used_len+=sizeof(int);
		
		tag_offset_end=*(unsigned int *)(payload+*used_len);
		*used_len+=sizeof(int);
		
		tag_len=tag_offset_end-pre_tag_offset_end;
		if(tag_len<0 || (tag_offset_end>=payload_len) || (tag_len>payload_len-tag_value_start))
		{
			return -1;
		}

		switch(tag_type)
		{
			case	TAG_PAD:
				break;
			case	TAG_VER:
				stream->ver_idx=stream->ext_tag_num;
				get_quic_tlv(payload+tag_value_start, &stream->ext_tags[stream->ext_tag_num], tag_len, tag_type, pstream->threadnum);
				*(unsigned int *)(stream->ext_tags[stream->ext_tag_num].value)=(unsigned int)ntohl(*(unsigned int *)(stream->ext_tags[stream->ext_tag_num].value));
				MESA_handle_runtime_log(g_quic_param.logger,
											RLOG_LV_DEBUG,
											"QUIC_VERSION",
											"Quic version: 0X%X addr: %s",
											*(unsigned int *)(stream->ext_tags[stream->ext_tag_num].value),
											PRINTADDR(pstream, g_quic_param.level)
										);
				break;
			case	TAG_UAID:
				stream->ua_idx=stream->ext_tag_num;
				get_quic_tlv(payload+tag_value_start, &stream->ext_tags[stream->ext_tag_num], tag_len, tag_type, pstream->threadnum);
				MESA_handle_runtime_log(g_quic_param.logger,
											RLOG_LV_DEBUG,
											"QUIC_UA",
											"User Agent: %s addr: %s",
											stream->ext_tags[stream->ext_tag_num].value,
											PRINTADDR(pstream, g_quic_param.level)
										);
				stream->ext_tag_num++;
				break;
			case	TAG_SNI:
				stream->sni_idx=stream->ext_tag_num;
				get_quic_tlv(payload+tag_value_start, &stream->ext_tags[stream->ext_tag_num], tag_len, tag_type, pstream->threadnum);
				MESA_handle_runtime_log(g_quic_param.logger,
											RLOG_LV_DEBUG,
											"QUIC_SNI",
											"SNI: %s addr: %s",
											stream->ext_tags[stream->ext_tag_num].value,
											PRINTADDR(pstream, g_quic_param.level)
										);
				stream->ext_tag_num++;
				break;
			default:
				get_quic_tlv(payload+tag_value_start, &stream->ext_tags[stream->ext_tag_num], tag_len, tag_type, pstream->threadnum);
				stream->ext_tag_num++;
				break;
		}
			
		tag_used_num++;
		tag_value_start+=tag_len;
		total_tag_len+=tag_len;
		pre_tag_offset_end=tag_offset_end;
	}
	
	*used_len += total_tag_len;
	
	return 0;
}

int gquic_frame_type_stream(struct streaminfo *pstream, struct _quic_context* _context, char *payload, int payload_len, int *used_len, void *a_packet)
{
	int ret=0;
	char state=APP_STATE_GIVEME;
	int tag_num = 0;
	unsigned int message_tag;

	if(!check_length(payload_len-*used_len, 8))
	{
		return state;
	}
	
	switch(_context->quic_info.quic_hdr.quic_version)
	{
		case	GQUIC_VERSION_Q041:
			*used_len+=1; // unknown
		case	GQUIC_VERSION_Q044:
			message_tag=(unsigned int)ntohl(*(unsigned int *)(payload+*used_len));
			*used_len+=4;

			tag_num=*(int *)(payload+*used_len);
			*used_len+=4; //tag_num
			break;
		default:
			message_tag=(unsigned int)ntohl(*(unsigned int *)(payload+*used_len));
			*used_len+=4;

			tag_num=*(unsigned short *)(payload+*used_len);
			*used_len+=2; //tag_num
			*used_len+=2; //padding
			break;
	}
	
	switch(message_tag)
	{
		case CHLO: //MTAG_CHLO;
			ret=parse_extension_tag(pstream, &(_context->quic_info.client_hello), a_packet, payload, payload_len, used_len, tag_num);
			if(ret>=0 && _context->call_business)
			{
				state=quic_callPlugins(pstream, _context, (void *)(_context->quic_info.client_hello), sizeof(void *), QUIC_CLIENT_HELLO_MASK, a_packet);
			}
			break;
		case SHLO:  //MTAG_SHLO;
			ret=parse_extension_tag(pstream, &(_context->quic_info.server_hello), a_packet, payload, payload_len, used_len, tag_num);
			if(ret>=0 && _context->call_business)
			{
				state=quic_callPlugins(pstream, _context, (void *)(_context->quic_info.server_hello), sizeof(void *), QUIC_SERVER_HELLO_MASK, a_packet);
			}
			break;
		case REJ: //MTAG_REJ;
			ret=parse_extension_tag(pstream, &(_context->quic_info.rejection), a_packet, payload, payload_len, used_len, tag_num);
			if(ret>=0 && _context->call_business)
			{
				state=quic_callPlugins(pstream, _context, (void *)(_context->quic_info.rejection), sizeof(void *), QUIC_REJECTION_MASK, a_packet);
			}
			break;
		default:
			break;
	}

	return state;
}

//frame type->stream->offset->data length
int gquic_proc_unencrypt(struct streaminfo *pstream, struct _quic_context* _context, void *a_packet, char * payload, int payload_len, int *used_len)
{
	int ret=0;
	unsigned char frame_type=0;
	unsigned int stream_id=0;
	unsigned int error_code=0;
	unsigned short reason_phrase_length=0;
	unsigned long long byte_offset=0;
	unsigned long long least_unacked_delta=0;
				
	while(*used_len<payload_len)
	{	 
		frame_type=payload[*used_len];
		*used_len+=1;  //skip frame_type
		
		switch(frame_type)
		{
			case	GQUIC_REGULAR_FRAME_PADDING:
				return APP_STATE_GIVEME; // PADDING frame
				break;
			case	GQUIC_REGULAR_FRAME_RST_STREAM:
				if(!check_length(payload_len-*used_len, sizeof(unsigned int)+sizeof(unsigned long long)+sizeof(unsigned int)))
				{
					return APP_STATE_GIVEME;
				}
				stream_id=(unsigned int)get_variable_length(payload, *used_len, sizeof(unsigned int));
				*used_len+=sizeof(unsigned int);

				byte_offset=get_variable_length(payload, *used_len, sizeof(unsigned long long));
				*used_len+=sizeof(unsigned long long);

				error_code=(unsigned int)get_variable_length(payload, *used_len, sizeof(unsigned int));
				*used_len+=sizeof(unsigned int);

				MESA_handle_runtime_log(g_quic_param.logger,
										RLOG_LV_DEBUG,
										"QUIC_RST_STREAM",
										"stream_id: %u byte_offset: %llu error_code: %u addr: %s",
										stream_id,
										byte_offset,
										error_code,
										printaddr(&pstream->addr, pstream->threadnum));

				return quic_callPlugins(pstream, _context, NULL, 0, QUIC_INTEREST_KEY_MASK, a_packet);
				break;
			case	GQUIC_REGULAR_FRAME_CONNECTION_CLOSE:
				if(!check_length(payload_len-*used_len, sizeof(unsigned int)+sizeof(unsigned short)))
				{
					return APP_STATE_GIVEME;
				}
				error_code=(unsigned int)get_variable_length(payload, *used_len, sizeof(unsigned int));
				*used_len+=sizeof(unsigned int);

				reason_phrase_length=(unsigned short)get_variable_length(payload, *used_len, sizeof(unsigned short));
				*used_len+=sizeof(unsigned short);

				if(!check_length(payload_len-*used_len, reason_phrase_length))
				{
					return APP_STATE_GIVEME;
				}

				*used_len+=reason_phrase_length; // skip Reason Phrase

				MESA_handle_runtime_log(g_quic_param.logger,
										RLOG_LV_DEBUG,
										"QUIC_CONNECTION_CLOSE",
										"error_code: %u reason_phrase_length: %d addr: %s",
										error_code,
										reason_phrase_length,
										printaddr(&pstream->addr, pstream->threadnum));

				return quic_callPlugins(pstream, _context, NULL, 0, QUIC_INTEREST_KEY_MASK, a_packet);
				break;
			case	GQUIC_REGULAR_FRAME_GOAWAY:
				if(!check_length(payload_len-*used_len, sizeof(unsigned int)+sizeof(unsigned int)+sizeof(unsigned short)))
				{
					return APP_STATE_GIVEME;
				}
				error_code=(unsigned int)get_variable_length(payload, *used_len, sizeof(unsigned int));
				*used_len+=sizeof(unsigned int);

				//Last Good Stream ID
				stream_id=(unsigned int)get_variable_length(payload, *used_len, sizeof(unsigned int));
				*used_len+=sizeof(unsigned int);

				reason_phrase_length=(unsigned short)get_variable_length(payload, *used_len, sizeof(unsigned short));
				*used_len+=sizeof(unsigned short);
				
				if(!check_length(payload_len-*used_len, reason_phrase_length))
				{
					return APP_STATE_GIVEME;
				}
				*used_len+=reason_phrase_length; // skip Reason Phrase

				MESA_handle_runtime_log(g_quic_param.logger,
										RLOG_LV_DEBUG,
										"QUIC_GOAWAY",
										"error_code: %u Last Good Stream ID: %u reason_phrase_length: %d addr: %s",
										error_code,
										stream_id,
										reason_phrase_length,
										printaddr(&pstream->addr, pstream->threadnum));
				break;
			case	GQUIC_REGULAR_FRAME_WINDOW_UPDATE:
				if(!check_length(payload_len-*used_len, sizeof(unsigned int)+sizeof(unsigned long long)))
				{
					return APP_STATE_GIVEME;
				}
				stream_id=(unsigned int)get_variable_length(payload, *used_len, sizeof(unsigned int));
				*used_len+=sizeof(unsigned int);

				byte_offset=get_variable_length(payload, *used_len, sizeof(unsigned long long));
				*used_len+=sizeof(unsigned long long);

				MESA_handle_runtime_log(g_quic_param.logger,
										RLOG_LV_DEBUG,
										"QUIC_WINDOW_UPDATE",
										"stream_id: %u byte_offset: %llu addr: %s",
										stream_id,
										byte_offset,
										printaddr(&pstream->addr, pstream->threadnum));
				break;
			case	GQUIC_REGULAR_FRAME_BLOCKED:
				if(!check_length(payload_len-*used_len, sizeof(unsigned int)))
				{
					return APP_STATE_GIVEME;
				}
				stream_id=(unsigned int)get_variable_length(payload, *used_len, sizeof(unsigned int));
				*used_len+=sizeof(unsigned int);

				MESA_handle_runtime_log(g_quic_param.logger,
										RLOG_LV_DEBUG,
										"QUIC_STOP_WAITING",
										"stream_id: %u addr: %s",
										stream_id,
										printaddr(&pstream->addr, pstream->threadnum));
				break;
			case	GQUIC_REGULAR_FRAME_STOP_WAITING:
				ret=bit_to_value(payload, payload_len, _context->quic_info.quic_hdr.public_flags>>4, &least_unacked_delta, used_len);
				if(ret<0)
				{
					return APP_STATE_GIVEME;
				}

				MESA_handle_runtime_log(g_quic_param.logger,
										RLOG_LV_DEBUG,
										"QUIC_STOP_WAITING",
										"least_unacked_delta: %llu addr: %s",
										least_unacked_delta,
										printaddr(&pstream->addr, pstream->threadnum));
				break;
			case	GQUIC_REGULAR_FRAME_PING:
				 //The PING frame contains no payload. 
				 //The receiver of a PING frame simply needs to ACK the packet containing this frame
				break;
			default: //Regular Frame Types
				if(frame_type&GQUIC_SPECIAL_FRAME_STREAM || (frame_type&0xC0)==GQUIC_SPECIAL_FRAME_ACK)
				{					
					stream_id=get_stream_id(pstream, _context, payload, payload_len, frame_type, used_len);
					if(stream_id<0)
					{
						return APP_STATE_GIVEME;
					}
					ret=gquic_frame_type_stream(pstream, _context, payload, payload_len, used_len, a_packet);
				}
				else if((frame_type&0xE0)==GQUIC_SPECIAL_FRAME_CONGEST_FB)  // high two bits set 0; (frame_type: 01nullmmB)
				{
					//not used
				}
				else
				{
					return APP_STATE_GIVEME;
				}
				break;
		}

		if(ret&APP_STATE_DROPME || ret&APP_STATE_DROPPKT)
		{
			return ret;
		}
	}
	
	return APP_STATE_GIVEME;
}


//QUIC_DATA:is quic data pcap;QUIC_TRUE:is handshake pcap;QUIC_RETURN_DROPME:not quic protocol;
int parse_gquic_Q046(struct streaminfo *pstream, struct _quic_context* _context, void *a_packet, char * payload, int payload_len, int *used_len)
{
	int stream_id=0;
	int ret=APP_STATE_GIVEME;
	unsigned char frame_type;
	
	while(*used_len < payload_len)
	{
		frame_type=payload[*used_len];
		*used_len+=1;  //skip frame_type
		
		if((frame_type>>4)&IQUIC_FRAME_STREAM_HEX08) //0x08=Q048
		{			
			stream_id=get_stream_id(pstream, _context, payload, payload_len, frame_type, used_len);
			if(stream_id<0)
			{
				return APP_STATE_GIVEME;
			}
			ret=gquic_frame_type_stream(pstream, _context, payload, payload_len, used_len, a_packet);
		}
		else
		{
			return APP_STATE_GIVEME; //todo
		}

		if(ret&APP_STATE_DROPME || ret&APP_STATE_DROPPKT)
		{
			return ret;
		}
	}
	
	return APP_STATE_GIVEME;
}

int parameter_type_len(unsigned char flags)
{
	switch(flags)
	{
		case	0:
			return 1;
			break;
		case	1:
			return 2;
			break;
		case	2:
			return 4;
			break;
		case	3:
			return 8;
			break;
		default:
			break;
	}

	return 1;
}

int parse_encrypt_parameter(struct _quic_stream *quic_stream, unsigned char *payload, int payload_len, int thread_seq)
{
	int used_len=0,length=0;
	int para_type_len=0;
	unsigned long para_type=0;
	
	while(payload_len>used_len)
	{
		para_type_len=parameter_type_len(payload[used_len]>>6);
		para_type=get_value(payload, &used_len, para_type_len);  //type=
		switch(para_type&0xFFFF)
		{
			case	EXT_QUIC_PARAM_USER_AGENT: // 2021-10-20 deprecated
					quic_stream->ua_idx=quic_stream->ext_tag_num++;
					length=get_value(payload, &used_len, 1); // length=1
					get_quic_tlv((char *)payload+used_len, &(quic_stream->ext_tags[quic_stream->ua_idx]), length, EXT_QUIC_PARAM_USER_AGENT, thread_seq);
					used_len+=length;
					break;
			case	EXT_QUIC_PARAM_QUIC_VERSION:
					quic_stream->ver_idx=quic_stream->ext_tag_num++;
					length=get_value(payload, &used_len, 1); // length=1
					get_quic_tlv((char *)payload+used_len, &(quic_stream->ext_tags[quic_stream->ver_idx]), length, EXT_QUIC_PARAM_QUIC_VERSION, thread_seq);
					*(unsigned int *)quic_stream->ext_tags[quic_stream->ver_idx].value=(unsigned int)htonl(*(unsigned int *)quic_stream->ext_tags[quic_stream->ver_idx].value);
					used_len+=length;
				break;
			case	EXT_QUIC_PARAM_INIT_RTT:
			case	EXT_QUIC_PARAM_GOOGLE_CONN_OPTIONS:
			case	EXT_QUIC_PARAM_ORIGINAL_DST_CONN_ID:
			case	EXT_QUIC_PARAM_MAX_IDLE_TIMEOUT:
			case	EXT_QUIC_PARAM_STATELESS_RST_TOKEN:
			case	EXT_QUIC_PARAM_MAX_UDP_PAYLOAD:
			case	EXT_QUIC_PARAM_MAX_INIT_DATA:
			case	EXT_QUIC_PARAM_MAX_STREAM_BIDI_LOCAL:
			case	EXT_QUIC_PARAM_MAX_STREAM_BIDI_REMOTE:
			case	EXT_QUIC_PARAM_MAX_STREAM_UNI:
			case	EXT_QUIC_PARAM_MAX_STREAMS_BIDI:
			case	EXT_QUIC_PARAM_MAX_STREAMS_UNI:
			case	EXT_QUIC_PARAM_ACK_DELAY_EXPONENT:
			case	EXT_QUIC_PARAM_MAX_ACK_DELAY:
			case	EXT_QUIC_PARAM_DISABLE_ACTIVE_MIGRATION:
			case	EXT_QUIC_PARAM_PREFERRED_ADDRESS:
			case	EXT_QUIC_PARAM_ACTIVE_CONN_ID_LINIT:
			case	EXT_QUIC_PARAM_INIT_SRC_CONN_ID:
			case	EXT_QUIC_PARAM_RETRY_SRC_CONN_ID:
			case	EXT_QUIC_PARAM_MAX_DATAGRAM_FRAME_SIZE:
			default:
				length=get_value(payload, &used_len, 1); // length=1
				used_len+=length;
				break;
		}
	}

	return 0;
}
int parse_encrypt_server_name(struct _quic_stream *quic_stream, unsigned char *payload, int payload_len, int thread_seq)
{
	int ext_len=0,used_len=0;
	
	ext_len=get_value(payload, &used_len, 2);  //Server Name List length
	if(ext_len<=0 || ext_len>payload_len)
	{
		return 0;
	}
	
	if(get_value(payload, &used_len, 1)==0)  //Server Name type
	{
		ext_len=get_value(payload, &used_len, 2);  //Server Name length
		if(ext_len<=0 || ext_len>payload_len)
		{
			return 0;
		}
		
		quic_stream->sni_idx=quic_stream->ext_tag_num++;
		get_quic_tlv((char *)payload+used_len, &(quic_stream->ext_tags[quic_stream->sni_idx]), ext_len, EXTENSION_SERVER_NAME, thread_seq);
	}

	return 1;
}

int parse_encrypt_client_hello(struct streaminfo *pstream, struct _quic_stream *quic_stream, void *a_packet, unsigned char *payload, int payload_len)
{
	int skip_len=0;
	int used_len=0;
	int flags=0,ret=0;
	int ext_type=0, extension_total_len=0;

	get_value(payload, &used_len, 1);  //handshake type
	get_value(payload, &used_len, 3); //client hello length
	get_value(payload, &used_len, 2); //ssl_version

	get_value(payload, &used_len, 32);  //Random

	skip_len=(int)get_value(payload, &used_len, 1);  //Session ID length
	if(!check_length(payload_len-used_len, skip_len))
	{
		return flags;
	}
	used_len+=skip_len;

	skip_len=(int)get_value(payload, &used_len, 2);  //Ciper Suites length
	if(!check_length(payload_len-used_len, skip_len))
	{
		return flags;
	}
	used_len+=skip_len;

	skip_len=(int)get_value(payload, &used_len, 1);  //Compression Methods
	if(!check_length(payload_len-used_len, skip_len))
	{
		return flags;
	}
	used_len+=skip_len;

	extension_total_len=(int)get_value(payload, &used_len, 2);  //Extension length
	if(!check_length(payload_len-used_len, extension_total_len) && (payload_len-used_len!=extension_total_len))
	{
		return flags;
	}

	quic_stream->ext_tags=(quic_tlv_t *)dictator_malloc(pstream->threadnum, sizeof(quic_tlv_t)*3);
	memset(quic_stream->ext_tags, 0, sizeof(quic_tlv_t)*3);
	quic_stream->sni_idx=0xFF;
	quic_stream->ua_idx=0xFF;
	quic_stream->ver_idx=0xFF;
	
	while(extension_total_len>used_len)
	{
		ext_type=get_value(payload, &used_len, 2);  //Extension type		
		skip_len=get_value(payload, &used_len, 2);	//length
		if(!check_length(payload_len-used_len, skip_len))
		{
			return flags;
		}
				
		switch(ext_type)
		{
			case	EXTENSION_SERVER_NAME:
				ret=parse_encrypt_server_name(quic_stream, payload+used_len, skip_len, pstream->threadnum);
				if(ret<=0)
				{
					break;
				}
				flags=1;
				break;
			case	EXTENSION_QUIC_PARAM_TLS_13:
			case	EXTENSION_QUIC_PARAM_TLS_33:
				parse_encrypt_parameter(quic_stream, payload+used_len, skip_len, pstream->threadnum);
				break;
			case	EXTENSION_SUPPORT_GROUP:
			case	EXTENSION_APP_PROT_NEGO:
			case	EXTENSION_SIG_ALGORITHM:
			case	EXTENSION_KEY_SHARE:
			case	EXTENSION_PSK_EXCHANGE:
			case	EXTENSION_SUPP_SSL_VER:
			case	EXTENSION_COMPRESS_CERT:
				break;
			default:
				break;
		}
		
		used_len+=skip_len;
	}
	
	return flags;
}

static int get_decrypt_payload(unsigned char * payload, int payload_len, unsigned char *join_payload, int *join_payload_len, int *used_len)
{
	int join_length=0;
	unsigned char frame_type=0;
	unsigned short offset=0;
	unsigned short length=0;
	
	for(; *used_len<payload_len; )
	{
		frame_type=get_value(payload, used_len, 1);	// Frame Type=1
		if(frame_type==IQUIC_FRAME_PADDING || frame_type==IQUIC_FRAME_PING)
		{
			continue;
		}

		if((payload+(*used_len))[0]&0x40)
		{
			offset=(get_value(payload, used_len, 2))&0x0FFF;	// offset=12bit
		}
		else
		{			
			offset=get_value(payload, used_len, 1);	// offset=8bit
		}

		if((payload+(*used_len))[0]&0x40)
		{
			length=(get_value(payload, used_len, 2))&0x0FFF;	// length=12bit
		}
		else
		{			
			length=get_value(payload, used_len, 1);	// length=8bit
		}

		if((*join_payload_len)<join_length+length)
		{
			break;
		}

		#if 0
		if(frame_type==IQUIC_FRAME_CRYPTO && offset==0 && join_length==0)
		{
			memcpy(join_payload, payload+(*used_len), length);
			join_length+=length;
			break;
		}
#endif
		memcpy(join_payload+offset, payload+(*used_len), length);
		join_length+=length;
		*used_len+=length;
	}

	(*join_payload_len)=join_length;

	return join_length;
}

int parse_decrypt_quic(struct streaminfo *pstream, struct _quic_context* _context, void *a_packet, unsigned char * payload, int payload_len, int *used_len)
{
	unsigned char join_payload[2048]={0};
	int join_payload_len=sizeof(join_payload);

	int ret=0,state=APP_STATE_GIVEME;
	unsigned int quic_version=_context->quic_info.quic_hdr.quic_version;


	if( (quic_version>=MVFST_VERSION_00 && quic_version<=MVFST_VERSION_0F)		||
		(quic_version>=GQUIC_VERSION_T050 && quic_version<=GQUIC_VERSION_T059)	||
		(quic_version>=IQUIC_VERSION_I022 && quic_version<=IQUIC_VERSION_I029)	||
		(quic_version==IQUIC_VERSION_RFC9000)
		)
	{
		join_payload_len=get_decrypt_payload(payload, payload_len, join_payload, &join_payload_len, used_len);
		if(join_payload_len<=0)
		{
			return state;
		}
		if(join_payload[0] == 0x01)
		{
			if(_context->quic_info.client_hello==NULL)
			{
				_context->quic_info.client_hello=(struct _quic_stream *)dictator_malloc(pstream->threadnum, sizeof(struct _quic_stream));
				memset(_context->quic_info.client_hello, 0, sizeof(struct _quic_stream));
				_context->quic_info.client_hello->sni_idx=0xFF;
				_context->quic_info.client_hello->ua_idx=0xFF;
				_context->quic_info.client_hello->ver_idx=0xFF;
			}
			ret=parse_encrypt_client_hello(pstream, _context->quic_info.client_hello, a_packet, join_payload, join_payload_len);
			if(ret>0 && _context->call_business)
			{
				state=quic_callPlugins(pstream, _context, (void *)(_context->quic_info.client_hello), sizeof(void *), QUIC_CLIENT_HELLO_MASK, a_packet);
			}
		}
	}
	else if(quic_version>=GQUIC_VERSION_Q047 && quic_version<=GQUIC_VERSION_Q059)
	{
		get_value(payload, used_len, 4);	// Frame type=1,offset=1,length=2
		state=gquic_frame_type_stream(pstream, _context, (char *)payload, payload_len, used_len, a_packet);
	}
	else
	{
		state=APP_STATE_DROPME;
	}

	return state;
}

//cid->version->nounce->pkt num->ahn hash(12)
int quic_process(struct streaminfo *pstream, struct _quic_context* _context, int thread_seq, void* a_packet)
{
	int used_len=0;
	int ret=APP_STATE_GIVEME;
	unsigned char decrypt_payload[1500]={0};
	unsigned int decrypt_payload_len=sizeof(decrypt_payload);

	enum _QUIC_VERSION is_gquic=QUIC_VERSION_UNKNOWN;
	struct udpdetail *udp_detail=pstream->pudpdetail;
	
	if(udp_detail->pdata==NULL || udp_detail->datalen<=0)
	{
		return APP_STATE_GIVEME;
	}

	is_gquic=is_quic_protocol(pstream, _context, (char *)udp_detail->pdata, udp_detail->datalen, &used_len);
	if(is_gquic!=QUIC_VERSION_UNKNOWN)
	{
		if(_context->cb_version==0 && _context->call_business)
		{
			_context->cb_version=1;
			ret=quic_callPlugins(pstream, _context, &(_context->quic_info.quic_hdr.quic_version), sizeof(_context->quic_info.quic_hdr.quic_version), QUIC_USEING_VERSION_MASK, a_packet);
			if((ret&APP_STATE_DROPME) || (ret&APP_STATE_DROPPKT))
			{
				return ret;
			}
		}
		
		switch(is_gquic)
		{
			case	GQUIC_VERSION_Q044:
			case	GQUIC_VERSION_Q045:
			case	GQUIC_VERSION_Q046:
			case	GQUIC_VERSION_Q047:
			case	GQUIC_VERSION_Q048:
				ret=parse_gquic_Q046(pstream, _context, a_packet, (char *)udp_detail->pdata, udp_detail->datalen, &used_len);
				break;
			default:
				if(is_gquic>=GQUIC_VERSION_Q001 && is_gquic<=GQUIC_VERSION_Q046)
				{
					ret=gquic_proc_unencrypt(pstream, _context, a_packet, (char *)udp_detail->pdata, udp_detail->datalen, &used_len);
					break;
				}
				
				if(	((is_gquic>=MVFST_VERSION_00 && is_gquic<=MVFST_VERSION_0F)		||
						(is_gquic>=GQUIC_VERSION_Q049 && is_gquic<=GQUIC_VERSION_Q059)	||
						(is_gquic>=GQUIC_VERSION_T050 && is_gquic<=GQUIC_VERSION_T059)	||
						(is_gquic>=GQUIC_VERSION_T050 && is_gquic<=GQUIC_VERSION_T059)	||
						(is_gquic>=IQUIC_VERSION_I022 && is_gquic<=IQUIC_VERSION_I029)	||
						(is_gquic==IQUIC_VERSION_RFC9000)
						)
					&& _context->is_decrypt==0
					)
				{
					_context->is_decrypt=1;
					ret=dissect_quic((char *)udp_detail->pdata, udp_detail->datalen, decrypt_payload, &decrypt_payload_len);
					if(ret!=1)
					{
						return APP_STATE_GIVEME;
					}
					ret=parse_decrypt_quic(pstream, _context, a_packet, decrypt_payload, decrypt_payload_len, &used_len);
					break;
				}

				if(_context->call_business)
				{
					ret=quic_callPlugins(pstream, _context, (char *)udp_detail->pdata, udp_detail->datalen, QUIC_APPLICATION_DATA_MASK, a_packet);
					if((ret&APP_STATE_DROPME) || (ret&APP_STATE_DROPPKT))
					{
						return ret;
					}
				}
				break;
		}

		if((ret&APP_STATE_DROPME) || (ret&APP_STATE_DROPPKT))
		{
			return ret;
		}
	}

	if(_context->is_quic==TRUE)
	{
		if(_context->quic_info.quic_hdr.is_reset && _context->call_business)
		{
			return quic_callPlugins(pstream, _context, NULL, 0, QUIC_INTEREST_KEY_MASK, a_packet);			
		}
		
		if(_context->quic_info.quic_hdr.is_version_negotiation && _context->call_business)
		{
			return quic_callPlugins(pstream, _context, NULL, 0, QUIC_NEGOTIATION_VERSION_MASK, a_packet);
		}
		return APP_STATE_GIVEME;
	}
	
	return APP_STATE_DROPME;;
}

unsigned int quic_protocol_identify(struct streaminfo *a_stream, void *a_packet, char *out_sni, int *out_sni_len, char *out_ua, int *out_ua_len)
{
	int ret=APP_STATE_GIVEME;
	int len=0;
	void *pme=NULL;
	struct _quic_context *_context=NULL;
	unsigned int quic_version=QUIC_VERSION_UNKNOWN;

	if(!is_quic_port(a_stream))
	{
		return quic_version;
	}
	
	quic_init_stream(&pme, a_stream->threadnum);
	_context=(struct _quic_context *)pme;
	
	ret=quic_process(a_stream, _context, a_stream->threadnum, a_packet);
	if(ret!=PROT_STATE_DROPME && _context->is_quic!=QUIC_VERSION_UNKNOWN)
	{
		if(_context->quic_info.client_hello!=NULL && _context->quic_info.client_hello->ext_tags!=NULL)
		{
			if(_context->quic_info.client_hello->sni_idx!=0xFF)
			{
				len=MIN((int)_context->quic_info.client_hello->ext_tags[_context->quic_info.client_hello->sni_idx].length, (*out_sni_len)-1);
				memcpy(out_sni, _context->quic_info.client_hello->ext_tags[_context->quic_info.client_hello->sni_idx].value, len);
				(*out_sni_len)=len;
			}
			else
			{
				(*out_sni_len)=0;
			}

			if(_context->quic_info.client_hello->ua_idx!=0xFF)
			{
				len=MIN((int)_context->quic_info.client_hello->ext_tags[_context->quic_info.client_hello->ua_idx].length, (*out_ua_len)-1);
				memcpy(out_ua, _context->quic_info.client_hello->ext_tags[_context->quic_info.client_hello->ua_idx].value, len);
				(*out_ua_len)=len;
			}
			else
			{
				(*out_ua_len)=0;
			}
			
			quic_version=_context->quic_info.quic_hdr.quic_version;
		}
		else
		{
			if(_context->is_quic==TRUE)
			{
				quic_version=_context->quic_info.quic_hdr.quic_version;
			}
		}
	}

	quic_release_stream(&pme, a_stream->threadnum);

	return quic_version;
}
