#ifdef __KERNEL__
#include <linux/module.h>
#include <linux/skbuff.h>
#endif

#include <rtk_rg_struct.h>
#include <rtk_rg_alg_tool.h>
#include <rtk_rg_rtsp.h>
      

/* rtsp request format
 *    Request = 	Request-Line 
 * 			*( 	general-header 
 *			| 	request-header 
 *			| 	entity-header ) 
 *				CRLF
 *				[ message-body ] 
*/

#define RTSP_RESPOND_IDENTIFIER "RTSP/1.0 "

static int _rtk_rg_rtsp_nextline(unsigned char* p, unsigned int len, unsigned int * pOff, unsigned int * pLineOff, unsigned int * pLineLen) 
{ 
   	unsigned int    off = *pOff; 
   	unsigned int    physlen = 0; 
 
    if (off >= len) 
    { 
        return 0; 
    } 
 
    while (p[off] != '\n') 
    { 
        if (len-off <= 1) 
        { 
            return 0; 
        } 
 
        physlen++; 
        off++; 
    } 
 
    /* if we saw a crlf, physlen needs adjusted */ 
    if (physlen > 0 && p[off] == '\n' && p[off-1] == '\r') 
    { 
        physlen--; 
    } 
 
    /* advance past the newline */ 
    off++; 
 
    *pLineOff = *pOff; 
    *pLineLen = physlen; 
    *pOff = off; 
 
    return 1; 
} 

static int _rtk_rg_rtsp_mime_nextline(unsigned char* p, unsigned int len, unsigned int* pOff, uint* pLineOff, uint* pLineLen) 
{ 
    uint    off = *pOff; 
    uint    physlen = 0; 
    int     isFirstLine = 1; 
 
    if (off >= len) 
    { 
        return 0; 
    } 
 
    do 
    { 
        while (p[off] != '\n') 
        { 
            if (len-off <= 1) 
            { 
                return 0; 
            } 
 
            physlen++; 
            off++; 
        } 
 
        /* if we saw a crlf, physlen needs adjusted */ 
        if (physlen > 0 && p[off] == '\n' && p[off-1] == '\r') 
        { 
            physlen--; 
        } 
 
        /* advance past the newline */ 
        off++; 
 
        /* check for an empty line */ 
        if (physlen == 0) 
        { 
            break; 
        } 
 
        /* check for colon on the first physical line */ 
        if (isFirstLine) 
        { 
            isFirstLine = 0; 
            if (memchr(p+(*pOff), ':', physlen) == NULL) 
            { 
                return 0; 
            } 
        } 
    } 
    while (p[off] == ' ' || p[off] == '\t'); 
 
    *pLineOff = *pOff;
    *pLineLen = (physlen == 0) ? 0 : (off - *pOff); 
    *pOff = off; 
 
    return 1; 
} 

static rtk_rg_alg_expect_t * _rtk_rg_rtsp_expect(int direct, rtk_rg_alg_connection_t * pConn, rtk_rg_alg_newPort_t * pNewPort)
{
	//rtk_rg_alg_tuple_t tuple;
	rtk_rg_alg_expect_t * pExpect = NULL;
	//memset(&tuple, 0, sizeof(rtk_rg_alg_tuple_t));
#if 1	
	if(direct == NAPT_DIRECTION_OUTBOUND)
	{							
		//rtp or rtcp should use udp
		{	
			int ret;
			rtk_rg_upnpConnection_t upnpConn;
			rtk_rg_pktHdr_t *pPktHdr;	
			pPktHdr = (rtk_rg_pktHdr_t *)pConn->pPktHdr;
			
			upnpConn.is_tcp= 0; //pConn->tuple.isTcp;
			upnpConn.wan_intf_idx=pPktHdr->netifIdx;
			upnpConn.gateway_port=	pNewPort->newExtPort; //must use newExtPort
			upnpConn.local_port= pNewPort->newPort;
			upnpConn.local_ip=pConn->tuple.internalIp.ip;
			upnpConn.limit_remote_ip=0;		//1;remote source may be another server; not DMC
			upnpConn.limit_remote_port=0;		//can't restrict client using which port to connect
			upnpConn.remote_ip=pConn->tuple.remoteIp.ip;
			upnpConn.remote_port=0;
			upnpConn.type=UPNP_TYPE_ONESHOT;
			upnpConn.timeout=rg_db.algUserDefinedTimeout[RTK_RG_ALG_RTSP_TCP]; //auto time out if the client do not connect by this WAN
			
ALG("add upnp intf_idx=%d; upnp remot ip=0x%x port=%d; internal ip=0x%x port=%d\n",upnpConn.wan_intf_idx,pConn->tuple.remoteIp.ip,upnpConn.gateway_port,upnpConn.local_ip,upnpConn.local_port);
			assert_ok((pf.rtk_rg_upnpConnection_add)(&upnpConn,&ret));
		}
		pExpect = _rtk_rg_alg_expect_add(direct, &pConn->tuple, NULL);
	}
#else	
	if(direct == NAPT_DIRECTION_OUTBOUND)
	{							
		//rtp or rtcp should use udp
		tuple.isTcp = 0;
		tuple.isIp6 = pConn->tuple.isIp6;
		if(pConn->tuple.isIp6 == 0)
		{		
			tuple.internalIp.ip = pConn->tuple.internalIp.ip;
			tuple.internalPort = pNewPort->newPort;
			tuple.extIp.ip = pConn->tuple.extIp.ip;
			tuple.extPort = pNewPort->newExtPort;
			tuple.remoteIp.ip = pConn->tuple.remoteIp.ip;	
			pExpect = _rtk_rg_alg_expect_add(direct, &tuple, NULL);								
		}
		
	}
#endif
	return pExpect;
}


static int _rtk_rg_rtsp_parse_message(unsigned char* pData, unsigned int dataLen, unsigned int * pOff, 
                   rtk_rg_rtsp_t * pRtsp) 
{ 
    unsigned int entityLen = 0; 
    unsigned int lineOff, lineLen, contentOff;  
 	unsigned char * end = NULL;
	
    if(!_rtk_rg_rtsp_nextline(pData, dataLen, pOff, &lineOff, &lineLen))
    	return 0;
    
    while(_rtk_rg_rtsp_mime_nextline(pData, dataLen, pOff, &lineOff, &lineLen)) 
	{ 
    	if(lineLen == 0) 
		{
        	if(entityLen > 0) 
            	*pOff += min(entityLen, dataLen - *pOff);            	
            break; 
        } 
        if(lineOff+lineLen > dataLen) { 
            ALG("!! overrun !!\n"); 
            break; 
        } 
 
        if(strnicmp(pData+lineOff, "CSeq:", 5) == 0) { 
            pRtsp->cseqOff = lineOff; 
            pRtsp->cseqLen = lineLen;
            ALG(">>>> cseqOff=%d;cseqLen=%d\n",lineOff,lineLen); 
        } 
 
        if(strnicmp(pData+lineOff, "Transport:", 10) == 0) { 
            pRtsp->transOff = lineOff; 
            pRtsp->transLen = lineLen;
            ALG("<<<< transOff=%d;transLen=%d\n",lineOff,lineLen); 
        } 

		if(strnicmp(pData+lineOff, "x-NAT:", 6) == 0) { 
            pRtsp->xnatOff = lineOff; 
            pRtsp->xnatLen = lineLen;
            ALG("<<<< xnatOff=%d;xnatLen=%d\n",lineOff,lineLen); 
        }

		if(strnicmp(pData+lineOff, "x-NAT_Address:", 14) == 0) { 
            pRtsp->xnatAddrOff = lineOff; 
            pRtsp->xnatAddrLen = lineLen;
            ALG("<<<< xnatAddrOff=%d;xnatAddrLen=%d\n",lineOff,lineLen); 
        }
 
        if(strnicmp(pData+lineOff, "Content-Length:", 15) == 0) { 
            contentOff = lineOff+15; 
            if(rtk_rg_skip_wspace_lws(pData, &contentOff, lineOff+lineLen))
          		entityLen = simple_strtoul(pData+contentOff, (char **)&end, 10);
        } 
    }
 
    return 1; 
} 

static int _rtk_rg_rtsp_parse_transport(int direct, unsigned char ** ppData,  unsigned int * pDataLen, rtk_rg_alg_connection_t * pConn,
										rtk_rg_rtsp_t * pRtsp, unsigned int * matchOff, unsigned int * matchLen) 
{
	int ret = 0; 
	unsigned int off = pRtsp->transOff, lenOff, nextParamOff, nextFieldOff; 
	const unsigned char * pParamEnd, *pFieldEnd;  
	uint16 port;
	unsigned char * end = NULL;
	unsigned char * pData = *ppData;
	unsigned int portLen;
	uint16 rtpExtPort = 0, rtcpExtPort = 0;
	rtk_rg_alg_newPort_t newPort;
	unsigned int bufLen = 0;
	char buffer[sizeof("nnn.nnn.nnn.nnn")];
	
	lenOff = pRtsp->transOff + pRtsp->transLen;
	
	if(pRtsp->transLen < 10  || strnicmp(pData+off, "Transport:", 10) != 0) 
	{ 
		ALG("sanity check failed\n"); 
		return 0; 
	} 
	 
	ALG("Transport: '%.*s'\n", (int)pRtsp->transLen, pData+off); 
	
	off += 10; 
	if(!rtk_rg_skip_wspace_lws(pData, &off, lenOff))
		return 0;
	 
	/* Transport: tran;field;field=val,tran;field;field=val,... */ 
	while(off < lenOff) 
	{  
		pParamEnd = memchr(pData+off, ',', lenOff-off); 
		nextParamOff = (pParamEnd == NULL) ? lenOff : (pParamEnd- pData + 1); 
	 
		while(off < nextParamOff) 
		{ 
			pFieldEnd = memchr(pData+off, ';', nextParamOff-off); 
			nextFieldOff = (pFieldEnd == NULL) ? nextParamOff : (pFieldEnd-pData+1); 
			if(strncmp(pData+off, "client_port=", 12) == 0) 
			{ 
				off += 12; 
				port = simple_strtoul(pData+off, (char **)&end, 10);
				ALG("low port found : %hu\n", port);
				if (port < 1024 || port > 65535)
					return 0;
				
				portLen = end - pData -off;
					
				if(pRtsp->lowPort != 0 && pRtsp->lowPort != port) 
				{
					ALG("multiple ports found, port %hu ignored\n", port); 
				}
				else
				{ 
					ALG("low port found : %hu\n", port);
					pRtsp->lowPort = pRtsp->highPort = port; 
					if(pData[off+portLen] == '-') 
					{  
						port = simple_strtoul(pData+off+portLen+1, (char **)&end, 10); 
						if (port < 1024 || port > 65535)
						{
							return 0; 
						}
						pRtsp->portType = PORT_RANGE;
						pRtsp->highPort = port; 
	 					
						// If we have a range, assume rtp: 
						// loport must be even, hiport must be loport+1 
						if ((pRtsp->lowPort & 0x0001) != 0 || 
							pRtsp->highPort != pRtsp->lowPort+1) 
						{ 
							ALG("incorrect range: %hu-%hu, correcting\n", pRtsp->lowPort, pRtsp->highPort); 
							pRtsp->lowPort &= ~1; 
							pRtsp->highPort = pRtsp->lowPort + 1; 
						}
						*matchLen = end - pData -off;

						//only do upnp for outbound packet 
						if(direct == NAPT_DIRECTION_OUTBOUND)
						{
							rtpExtPort = _rtk_rg_extPortPair_get(FALSE, pRtsp->lowPort);
							if(rtpExtPort == FAIL)
								return 0;
							
							ALG("range: using ports %hu-%hu\n", rtpExtPort, rtpExtPort+1);
							bufLen = sprintf(buffer, "%hu-%hu", rtpExtPort, rtpExtPort+1); 

							//add expect
							newPort.newPort = pRtsp->lowPort;
							newPort.newExtPort = rtpExtPort;
							_rtk_rg_rtsp_expect(direct, pConn, &newPort);

							//add another expect
							newPort.newPort = pRtsp->lowPort+1;
							newPort.newExtPort = rtpExtPort+1;
							_rtk_rg_rtsp_expect(direct, pConn, &newPort);
							pConn->app.rtsp.lowPort=pRtsp->lowPort;
							pConn->app.rtsp.highPort=pRtsp->highPort;
						}else{
							ALG("range: using ports %hu-%hu\n", pConn->app.rtsp.lowPort, pConn->app.rtsp.highPort);
							bufLen = sprintf(buffer, "%hu-%hu", pConn->app.rtsp.lowPort, pConn->app.rtsp.highPort); 
						}
					} 
					else if (pData[off+portLen] == '/') 
					{ 
						port = simple_strtoul(pData+off+portLen+1, (char **)&end, 10); 
						if (port < 1024 || port > 65535)
							return 0;  
						pRtsp->portType = PORT_DISCON;						
						pRtsp->highPort = port; 
						*matchLen = end - pData -off;

						//only do port nat modification for outbound packet 
						if(direct == NAPT_DIRECTION_OUTBOUND)
						{
							rtpExtPort = _rtk_rg_extPort_get(FALSE, pRtsp->lowPort);
							if(rtpExtPort == FAIL)
								return 0;

							rtcpExtPort = _rtk_rg_extPort_get(FALSE, pRtsp->highPort);
							if(rtcpExtPort == FAIL)
								return 0;
							
							ALG("discon: using ports %hu/%hu\n", rtpExtPort, rtcpExtPort); 
							
							if(rtcpExtPort ==  rtpExtPort+1)
								bufLen = sprintf(buffer, "%hu-%hu", rtpExtPort, rtpExtPort+1);
							else
								bufLen = sprintf(buffer, "%hu/%hu", rtpExtPort, rtcpExtPort);

							//add expect
							newPort.newPort = pRtsp->lowPort;
							newPort.newExtPort = rtpExtPort;
							_rtk_rg_rtsp_expect(direct, pConn, &newPort);

							//add another expect
							newPort.newPort = pRtsp->highPort;
							newPort.newExtPort = rtcpExtPort;
							_rtk_rg_rtsp_expect(direct, pConn, &newPort);
							pConn->app.rtsp.lowPort=pRtsp->lowPort;
							pConn->app.rtsp.highPort=pRtsp->highPort;
						}else{
							ALG("discon: using ports %hu/%hu\n", pConn->app.rtsp.lowPort, pConn->app.rtsp.highPort); 
							
							if(pRtsp->highPort ==  pRtsp->lowPort+1)
								bufLen = sprintf(buffer, "%hu-%hu", pConn->app.rtsp.lowPort, pConn->app.rtsp.highPort);
							else
								bufLen = sprintf(buffer, "%hu/%hu", pConn->app.rtsp.lowPort, pConn->app.rtsp.highPort);
						}
					} 
					else	//single
					{
						*matchLen = portLen;
						//only do port nat modification for outbound packet 
						if(direct == NAPT_DIRECTION_OUTBOUND)
						{
							rtpExtPort = _rtk_rg_extPort_get(FALSE, pRtsp->lowPort);
							if(rtpExtPort == FAIL)
								return 0;
							
							ALG("single: using port %hu\n", rtpExtPort);
					        bufLen = sprintf(buffer, "%hu", rtpExtPort);

							//add expect
							newPort.newPort = pRtsp->lowPort;
							newPort.newExtPort = rtpExtPort;
							_rtk_rg_rtsp_expect(direct, pConn, &newPort);
							pConn->app.rtsp.lowPort=pRtsp->lowPort;
							pConn->app.rtsp.highPort=0;
						}else{
							ALG("single: using port %hu\n", pConn->app.rtsp.lowPort);
					        bufLen = sprintf(buffer, "%hu", pConn->app.rtsp.lowPort);
						}
					}
					
					*matchOff = off;
					ret = 1;
					if(direct == NAPT_DIRECTION_OUTBOUND)
						ALG("****** lowport=%d; highport=%d\n",pRtsp->lowPort,pRtsp->highPort);
					else
						ALG("****** lowport=%d; highport=%d\n",pConn->app.rtsp.lowPort,pConn->app.rtsp.highPort);

					if(!_rtk_rg_mangle_packet(ppData, pDataLen, pConn, *matchOff, *matchLen, buffer, bufLen))
						return 0;
				} 
			} 
	 		
			off = nextFieldOff; 
		} 
	 
		off = nextParamOff; 
	} 
	 
	return ret; 

}

static int _rtk_rg_rtsp_parse_NAT(unsigned char * pData, rtk_rg_alg_connection_t * pConn,
										rtk_rg_rtsp_t * pRtsp, unsigned int * matchOff, unsigned int * matchLen) 
{
	int 	ret = 0; 
	unsigned int off = pRtsp->xnatOff, lenOff, nextParamOff, nextFieldOff; 
	const unsigned char * pParamEnd, *pFieldEnd;  
	uint16 port;
	unsigned char * end = NULL;
	unsigned int portLen;
	
	lenOff = pRtsp->xnatOff + pRtsp->xnatLen;
	
	if(pRtsp->xnatLen < 6  || strnicmp(pData+off, "x-NAT:", 6) != 0) 
	{ 
		ALG("sanity check failed\n"); 
		return 0; 
	} 
	 
	ALG("x-NAT: '%.*s'\n", (int)pRtsp->xnatLen, pData+off); 
	
	off += 6; 
	if(!rtk_rg_skip_wspace_lws(pData, &off, lenOff))
		return 0;
	
	/* x-NAT: aaa.bbb.ccc.ddd:eeeee/r/n */
	while(off < lenOff) 
	{
		pParamEnd = memchr(pData+off, '\r', lenOff-off); 
		nextParamOff = (pParamEnd == NULL) ? lenOff : (pParamEnd- pData + 1); 
	  
		while(off < nextParamOff) 
		{ 
			pFieldEnd = memchr(pData+off, ':', nextParamOff-off); 
			nextFieldOff = (pFieldEnd == NULL) ? nextParamOff : (pFieldEnd-pData+1); 
			off-=1;
			if(strncmp(pData+off, ":", 1) == 0) 
			{ 
				off += 1; 
				port = simple_strtoul(pData+off, (char **)&end, 10);
				ALG("port found : %hu\n", port);
				if (port < 1024 || port > 65535)
					return 0;
				
				portLen = end - pData -off;
					
				if(pRtsp->xnatPort!= 0 && pRtsp->xnatPort!= port) 
				{
					ALG("multiple ports found, port %hu ignored\n", port); 
				}
				else
				{ 
					ALG("xnat port found : %hu\n", port);
					pRtsp->xnatPort = port; 
					
					*matchLen = portLen;
					
					*matchOff = off;
					ret = 1;
					ALG("****** xnatport=%d\n",pRtsp->xnatPort);
				} 
			} 
	 		
			off = nextFieldOff; 
		} 
	 
		off = nextParamOff; 
	} 
	 
	return ret; 

}

static int _rtk_rg_rtsp_parse_NATADDR(unsigned char * pData, rtk_rg_alg_connection_t * pConn,
										rtk_rg_rtsp_t * pRtsp, unsigned int * matchOff, unsigned int * matchLen) 
{
	int 	ret = 0; 
	unsigned int off = pRtsp->xnatAddrOff, lenOff, nextParamOff, nextFieldOff; 
	const unsigned char * pParamEnd, *pFieldEnd;  
	uint16 port;
	unsigned char * end = NULL;
	unsigned int portLen;
	
	lenOff = pRtsp->xnatAddrOff + pRtsp->xnatAddrLen;

	if(pRtsp->xnatAddrLen < 14  || strnicmp(pData+off, "x-NAT_Address:", 14) != 0) 
	{ 
		ALG("sanity check failed\n"); 
		return 0; 
	} 
	 
	ALG("x-NAT_Address: '%.*s'\n", (int)pRtsp->xnatAddrLen, pData+off); 
	
	off += 14; 
	if(!rtk_rg_skip_wspace_lws(pData, &off, lenOff))
		return 0;
	
	/* x-NAT_Address: aaa.bbb.ccc.ddd:eeeee/r/n */
	while(off < lenOff) 
	{
		pParamEnd = memchr(pData+off, '\r', lenOff-off); 
		nextParamOff = (pParamEnd == NULL) ? lenOff : (pParamEnd- pData + 1); 
	  
		while(off < nextParamOff) 
		{ 
			pFieldEnd = memchr(pData+off, ':', nextParamOff-off); 
			nextFieldOff = (pFieldEnd == NULL) ? nextParamOff : (pFieldEnd-pData+1); 
			off-=1;
			if(strncmp(pData+off, ":", 1) == 0) 
			{ 
				off += 1; 
				port = simple_strtoul(pData+off, (char **)&end, 10);
				ALG("port found : %hu\n", port);
				if (port < 1024 || port > 65535)
					return 0;
				
				portLen = end - pData -off;
					
				if(pRtsp->xnatAddrPort!= 0 && pRtsp->xnatAddrPort!= port) 
				{
					ALG("multiple ports found, port %hu ignored\n", port); 
				}
				else
				{ 
					ALG("xnatAddr port found : %hu\n", port);
					pRtsp->xnatAddrPort = port; 
					
					*matchLen = portLen;
					
					*matchOff = off;
					ret = 1;
					ALG("****** xnatAddrport=%d\n",pRtsp->xnatAddrPort);
				} 
			} 
	 		
			off = nextFieldOff; 
		} 
	 
		off = nextParamOff; 
	} 
	 
	return ret; 

}

static int _rtk_rg_rtsp_mangle_transport(int direct, unsigned char ** ppData,  unsigned int * pDataLen, 
										rtk_rg_alg_connection_t * pConn, rtk_rg_rtsp_t * pRtsp)
{ 
	
	char buffer[sizeof("nnn.nnn.nnn.nnn")];
	unsigned int bufLen = 0, addrOff, addrLen;
	unsigned int off = pRtsp->transOff, lenOff, nextParamOff, nextFieldOff; 
	const unsigned char * pParamEnd, *pFieldEnd;  
	unsigned char * pData = *ppData;
	union rtk_rg_alg_addr addr;
	
	uint32 newIp;
	
	lenOff = pRtsp->transOff+ pRtsp->transLen;
	
	//only do ipv4 address nat modification
	if(pConn->tuple.isIp6 == 1)
		return 1;
	
	while(off < lenOff) 
	{  
		pParamEnd = memchr(pData+off, ',', lenOff-off); 
		nextParamOff = (pParamEnd == NULL) ? lenOff : (pParamEnd- pData + 1); 
	 	
		while(off < nextParamOff) 
		{ 
			pFieldEnd = memchr(pData+off, ';', nextParamOff-off); 
			nextFieldOff = (pFieldEnd == NULL) ? nextParamOff : (pFieldEnd-pData+1); 
			if(strncmp(pData+off, "destination=", 12) == 0)
			{
				off += 12;
				if (!_rtk_rg_parse_addr(pData+off, &addrLen, &addr, nextParamOff-off, pConn)) 
					return 0;

				ALG("found destination= : len:%d, %8x\n",addrLen,ntohl(addr.ip)); 
				if(direct == NAPT_DIRECTION_OUTBOUND &&
					pConn->tuple.internalIp.ip == ntohl(addr.ip))
				{
					newIp = htonl(pConn->tuple.extIp.ip);							
				}else if(direct == NAPT_DIRECTION_INBOUND &&
					pConn->tuple.extIp.ip == ntohl(addr.ip))
				{
						newIp = htonl(pConn->tuple.internalIp.ip);
				}
				else
					continue;

				addrOff = off;				
				bufLen = sprintf(buffer, "%pI4", &newIp);
				if(!_rtk_rg_mangle_packet(ppData, pDataLen, pConn, addrOff, addrLen, buffer, bufLen))
						return 0;
			}
			off = nextFieldOff; 
		}
		off = nextParamOff; 
	}
	if(addrLen!=bufLen)	//there is delta length
		_rtk_rg_update_tcp_seqNdelta(pConn->direct,(rtk_rg_pktHdr_t *)pConn->pPktHdr, pConn);
 	return 1;
}

static int _rtk_rg_rtsp_mangle_NAT(int direct, unsigned char ** ppData,  unsigned int * pDataLen, 
										rtk_rg_alg_connection_t * pConn, rtk_rg_rtsp_t * pRtsp,
										unsigned int  matchOff, unsigned int matchLen)
{ 
	char buffer[sizeof("nnn.nnn.nnn.nnn")];
	unsigned int bufLen = 0, addrOff, addrLen;
	unsigned int off = pRtsp->xnatOff, lenOff, nextParamOff, nextFieldOff; 
	const unsigned char * pParamEnd, *pFieldEnd;  
	unsigned char * pData = *ppData;
	union rtk_rg_alg_addr addr;
	uint32 newIp;
	
	lenOff = pRtsp->xnatOff+ pRtsp->xnatLen;

	//only do port nat modification for outbound packet 
	if(direct == NAPT_DIRECTION_OUTBOUND)
	{	
		ALG("x-NAT: using port %hu\n", pConn->tuple.extPort);
        bufLen = sprintf(buffer, "%hu", pConn->tuple.extPort);
		if(!_rtk_rg_mangle_packet(ppData, pDataLen, pConn, matchOff, matchLen, buffer, bufLen))
			return 0;
	}
	
	//only do ipv4 address nat modification
	if(pConn->tuple.isIp6 == 1)
		return 1;
	
	while(off < lenOff) 
	{  
		pParamEnd = memchr(pData+off, '\r', lenOff-off); 
		nextParamOff = (pParamEnd == NULL) ? lenOff : (pParamEnd- pData + 1); 
	 	
		while(off < nextParamOff) 
		{ 
			pFieldEnd = memchr(pData+off, ':', nextParamOff-off); 
			nextFieldOff = (pFieldEnd == NULL) ? nextParamOff : (pFieldEnd-pData+1); 
			if(strncmp(pData+off, "x-NAT: ", 7) == 0)
			{
				off += 7;
				if (!_rtk_rg_parse_addr(pData+off, &addrLen, &addr, nextParamOff-off, pConn)) 
					return 0;

				ALG("found ip= : len:%d, %8x\n",addrLen,ntohl(addr.ip)); 
				if(direct == NAPT_DIRECTION_OUTBOUND &&
					pConn->tuple.internalIp.ip == ntohl(addr.ip))
				{
					newIp = htonl(pConn->tuple.extIp.ip);							
				}else
					continue;

				addrOff = off;				
				bufLen = sprintf(buffer, "%pI4", &newIp);
				if(!_rtk_rg_mangle_packet(ppData, pDataLen, pConn, addrOff, addrLen, buffer, bufLen))
						return 0;
			}
			off = nextFieldOff; 
		}
		off = nextParamOff; 
	}
	if(addrLen!=bufLen)	//there is delta length
		_rtk_rg_update_tcp_seqNdelta(pConn->direct,(rtk_rg_pktHdr_t *)pConn->pPktHdr, pConn);
 	return 1;
}

static int _rtk_rg_rtsp_mangle_NATADDR(int direct, unsigned char ** ppData,  unsigned int * pDataLen, 
										rtk_rg_alg_connection_t * pConn, rtk_rg_rtsp_t * pRtsp,
										unsigned int  matchOff, unsigned int matchLen)
{ 
	char buffer[sizeof("nnn.nnn.nnn.nnn")];
	unsigned int bufLen = 0, addrOff, addrLen;
	unsigned int off = pRtsp->xnatAddrOff, lenOff, nextParamOff, nextFieldOff; 
	const unsigned char * pParamEnd, *pFieldEnd;  
	unsigned char * pData = *ppData;
	union rtk_rg_alg_addr addr;
	uint32 newIp;
	
	lenOff = pRtsp->xnatAddrOff + pRtsp->xnatAddrLen;

	//only do port nat modification for outbound packet 
	if(direct == NAPT_DIRECTION_OUTBOUND)
	{	
		ALG("x-NAT_Address: using port %hu\n", pConn->tuple.extPort);
        bufLen = sprintf(buffer, "%hu", pConn->tuple.extPort);
		if(!_rtk_rg_mangle_packet(ppData, pDataLen, pConn, matchOff, matchLen, buffer, bufLen))
			return 0;
	}
	
	//only do ipv4 address nat modification
	if(pConn->tuple.isIp6 == 1)
		return 1;
	
	while(off < lenOff) 
	{  
		pParamEnd = memchr(pData+off, '\r', lenOff-off); 
		nextParamOff = (pParamEnd == NULL) ? lenOff : (pParamEnd- pData + 1); 
	 	
		while(off < nextParamOff) 
		{ 
			pFieldEnd = memchr(pData+off, ':', nextParamOff-off); 
			nextFieldOff = (pFieldEnd == NULL) ? nextParamOff : (pFieldEnd-pData+1); 
			if(strncmp(pData+off, "x-NAT_Address: ", 15) == 0)
			{
				off += 15;
				if (!_rtk_rg_parse_addr(pData+off, &addrLen, &addr, nextParamOff-off, pConn)) 
					return 0;

				ALG("found ip= : len:%d, %8x\n",addrLen,ntohl(addr.ip)); 
				if(direct == NAPT_DIRECTION_OUTBOUND &&
					pConn->tuple.internalIp.ip == ntohl(addr.ip))
				{
					newIp = htonl(pConn->tuple.extIp.ip);							
				}else
					continue;

				addrOff = off;				
				bufLen = sprintf(buffer, "%pI4", &newIp);
				if(!_rtk_rg_mangle_packet(ppData, pDataLen, pConn, addrOff, addrLen, buffer, bufLen))
						return 0;
			}
			off = nextFieldOff; 
		}
		off = nextParamOff; 
	}
	if(addrLen!=bufLen)	//there is delta length
		_rtk_rg_update_tcp_seqNdelta(pConn->direct,(rtk_rg_pktHdr_t *)pConn->pPktHdr, pConn);
 	return 1;
}

static int _rtk_rg_rtsp_reply_process(int direct, unsigned char ** ppData, unsigned int * pDataLen, 
										rtk_rg_alg_connection_t * pConn)
{
	unsigned int matchOff=0, matchLen=0;
	unsigned int off = 0, dataLen = *pDataLen, ret=0;
	unsigned char * pData = *ppData;
    rtk_rg_rtsp_t  rtsp;
	
	
	if (strncmp(pData, "RTSP/1.0 200 OK", 15) == 0) 	//reply only
	{
		memset(&rtsp, 0, sizeof(rtsp));
		if (!_rtk_rg_rtsp_parse_message(pData, dataLen, &off, &rtsp))
		{
        	return 0;      /* not a valid message */
        }
        if(rtsp.transLen!=0)
        {
#if 0        
        	int i;
	        int j,localip_len;
			char tmpip[16];
			char localip[16];
        	for(i=0;i<rtsp.transLen;i++)
        	{
        		if (strncmp(pData+rtsp.transOff+i, "destination=", 12) == 0)
        		{
        			memset(localip,0,16);
					sprintf(localip,"%u.%u.%u.%u",(pConn->tuple.internalIp.ip>>24)&0xff,(pConn->tuple.internalIp.ip>>16)&0xff,(pConn->tuple.internalIp.ip>>8)&0xff,(pConn->tuple.internalIp.ip)&0xff);
					localip_len=strlen(localip);

        			for(j=7;j<17;j++)	//"rrr.xxx.yyy.zzz" max length is 15
        			{
        				if(*(pData+rtsp.transOff+i+12+j) == 0x3b)	//0x3b = ";"
        				{
        					//int delta_len;
        					int k,tail;
        					memcpy(tmpip,pData+rtsp.transOff+i+12,j);
        					tmpip[j] = 0;
        					if(j==localip_len)	//cool!
        					{
        						ALG("#0 case(%p): oldip=%s; newip=%s\n",pConn,tmpip,localip);
        						memcpy(pData+rtsp.transOff+i+12,localip,localip_len);
        					}
        					else if(j>localip_len)
        					{
        						ALG("#1 case(%p): oldip=%s; newip=%s\n",pConn,tmpip,localip);			
        						tail = *(pDataLen) - rtsp.transOff - i - j - 1;
        						for(k=0;k<tail;k++)	//move data
        							*(pData+rtsp.transOff+i+12+localip_len+1+k)=*(pData+rtsp.transOff+i+12+j+1+k);
        						memcpy(pData+rtsp.transOff+i+12,localip,localip_len);
        						*(pData+rtsp.transOff+i+12+localip_len) = 0x3b;
        						*pDataLen += (localip_len-j);
        						
        					}
        					else //(j<localip_len)
        					{        						
        						ALG("#2 case(%p): oldip=%s; newip=%s\n",pConn,tmpip,localip);
        						tail = *(pDataLen) - rtsp.transOff - i - j -1;
        						for(k=0;k<tail;k++)	//move data
        							*(pData+dataLen+(localip_len-j)-k) = *(pData+dataLen-k);	//move delta length...not one byte only
        						memcpy(pData+rtsp.transOff+i+12,localip,localip_len);
        						*(pData+rtsp.transOff+i+12+localip_len) = 0x3b;
        						*pDataLen += (localip_len-j);
        					}
        					ret=1;	//replaced
        					break;
        				}
        			}
        		}
        	}
#else
			_rtk_rg_rtsp_parse_transport(direct, ppData, pDataLen, pConn, &rtsp, &matchOff, &matchLen); 

			ALG("udp transport found, ports=(%d,%hu,%hu)\n", (int)rtsp.portType, pConn->app.rtsp.lowPort, pConn->app.rtsp.highPort);
			_rtk_rg_rtsp_mangle_transport(direct, ppData, pDataLen, pConn, &rtsp);
#endif
		}
	}
	return ret;
}


static int _rtk_rg_rtsp_request_process(int direct, unsigned char ** ppData, unsigned int * pDataLen, 
										rtk_rg_alg_connection_t * pConn)
{
	unsigned int cmdoff, off = 0, dataLen = *pDataLen;
	unsigned char * pData = *ppData;
    rtk_rg_rtsp_t  rtsp;           
	unsigned int matchOff=0, matchLen=0;
	
	while (off < dataLen) { 
		cmdoff = off;
		memset(&rtsp, 0, sizeof(rtsp)); 
		
        if (!_rtk_rg_rtsp_parse_message(pData, dataLen, &off, &rtsp)) 
        	return 0; //break;      /* not a valid message */ 

		ALG("Found a message:'%.*s'\n", 8, pData+cmdoff);
		//method name is case-sensitive
        if (strncmp(pData+cmdoff, "SETUP ", 6) == 0) 
        {
	        if(rtsp.transLen) 
			{ 
	          	_rtk_rg_rtsp_parse_transport(direct, ppData, pDataLen, pConn, &rtsp, &matchOff, &matchLen); 
	        } 

			if (rtsp.lowPort == 0) 
			{ 
	            ALG("no udp transports found\n"); 
	            continue;   /* no udp transports found */ 
	        } 

			//20161103LUKE: cut original delta for later add new delta, and keep old seq for update delta.
			if(rtsp.xnatAddrLen)
			{
				rtsp.xnatAddrOff-=pConn->outDelta;
				rtsp.xnatAddrOldSeq=pConn->oldOutSeq;
			}
			
	        ALG("udp transport found, ports=(%d,%hu,%hu)\n", (int)rtsp.portType, rtsp.lowPort, rtsp.highPort);
			_rtk_rg_rtsp_mangle_transport(direct, ppData, pDataLen, pConn, &rtsp);

			if(rtsp.xnatAddrLen)
			{
				rtsp.xnatAddrOff+=pConn->outDelta;
				pConn->oldOutSeq=rtsp.xnatAddrOldSeq;	//recover for update delta if has
				_rtk_rg_rtsp_parse_NATADDR(pData, pConn, &rtsp, &matchOff, &matchLen);	
			}

			if (rtsp.xnatAddrPort == 0) 
			{ 
	            ALG("no xnatAddrPort found\n"); 
	            continue;   /* no xnatAddrPort found */ 
	        }

			ALG("xnatAddrPort found, port=(%hu)\n",rtsp.xnatAddrPort);
			_rtk_rg_rtsp_mangle_NATADDR(direct, ppData, pDataLen, pConn, &rtsp, matchOff, matchLen);
        }else if (strncmp(pData+cmdoff, "DESCRIBE ", 9) == 0)
        {
        	if(rtsp.xnatLen)
			{
				_rtk_rg_rtsp_parse_NAT(pData, pConn, &rtsp, &matchOff, &matchLen);	
			}

			if (rtsp.xnatPort == 0) 
			{ 
	            ALG("no xnatPort found\n"); 
	            continue;   /* no xnatPort found */ 
	        }

			ALG("xnatPort found, port=(%hu)\n",rtsp.xnatPort);
			_rtk_rg_rtsp_mangle_NAT(direct, ppData, pDataLen, pConn, &rtsp, matchOff, matchLen);
        }else
        	continue;
	}
 	return 1;
}

int _rtk_rg_rtsp_handler(int direct, int after, unsigned char *pSkb,unsigned char *pPktInfo, unsigned char * pConnInfo)
{
	int ret = 0; //= SUCCESS;
#ifdef __KERNEL__
	unsigned char * pData, *pAppData;
	unsigned int appLen=0,dataOff=0;
	rtk_rg_pktHdr_t *pPktHdr;
	struct sk_buff *skb;
	rtk_rg_alg_connection_t * pConn;
	
	pPktHdr = (rtk_rg_pktHdr_t *)pPktInfo;
	pConn = (rtk_rg_alg_connection_t *)pConnInfo;
	skb=(struct sk_buff *)pSkb;
	
	pData=skb->data;
	if(pPktHdr->tagif&TCP_TAGIF)
		dataOff = pPktHdr->l4Offset + pPktHdr->headerLen;
	else
		dataOff = pPktHdr->l4Offset + 8; /*udp header length is 8 bytes*/

#if 1
	appLen = pPktHdr->l3Len + pPktHdr->l3Offset - dataOff;
	ALG(" _rtk_rg_rtsp_handle appLen(%d-%d-%d) = %d\n",pPktHdr->l3Len,pPktHdr->l3Offset,dataOff,appLen);
#else
	appLen = skb->len - dataOff;
#endif
	pAppData = pData + dataOff;	
	
	if (appLen < 0) //may be zero, since KMplayer will send interleave rtsp pkt
		return FAIL;
	
	//do nothing before napt modification
	if(after == 0)
		return SUCCESS;
	if(direct==NAPT_DIRECTION_OUTBOUND)
	{		
		if(pConn->tuple.isIp6 == 0)
		{
			pConn->tuple.extIp.ip = ntohl(*pPktHdr->pIpv4Sip);
			pConn->tuple.extPort = ntohs(*pPktHdr->pSport);
		}
	}

	pConn->skb = pSkb;
	pConn->pPktHdr = pPktInfo;
	pConn->appOff = dataOff;
	pConn->direct = direct;
	//RTSP_RESPOND_IDENTIFIER: RTSP/1.0
	if (strnicmp(pAppData, RTSP_RESPOND_IDENTIFIER, strlen(RTSP_RESPOND_IDENTIFIER)) != 0)
	{
		ret = _rtk_rg_rtsp_request_process(direct, &pAppData, &appLen, pConn);
	}
	else
	{
		if(direct==NAPT_DIRECTION_INBOUND)	//RTSP RESPONSE only
		{
			int len;
			len = appLen;
			ret = _rtk_rg_rtsp_reply_process(direct, &pAppData, &len, pConn);
			if((ret == 1) && (len != appLen))
			{
				ALG("\nlen=%d;appLen=%d\n",len,appLen);
				skb->len += (len - appLen);
				skb->tail += (len - appLen);
				pPktHdr->l3Len += (len - appLen);
				*pPktHdr->pL3Len=htons(pPktHdr->l3Len);
				ALG("before pConn->inDelta=%d\n",pConn->inDelta);
				pConn->inDelta+=(len - appLen);
				_rtk_rg_update_tcp_seqNdelta(pConn->direct, pPktHdr, pConn);
				ALG("after pConn->inDelta=%d\n",pConn->inDelta);

				if(pPktHdr->egressTagif & PPPOE_TAGIF)
				{
					*pPktHdr->pPppoeLength = htons(ntohs(*pPktHdr->pPppoeLength) + (len - appLen));
				}
			}
		}
	}
	//rtsp reponse message is not necessary to be processed	
#endif	
	return ret;
}

/*According to Linux netfilter nf_conntrack_rtsp.c implementation, 
 * only process the content of header field "Transport" in setup request message
*/
rtk_rg_fwdEngineAlgReturn_t rtk_rg_algRegFunc_rtsp(int direct, int after, unsigned char *pSkb,unsigned char *pPktInfo)
{
	//Attention: caller function needs to make sure it needs to do napt modification
	//ipv6 address and port doesn't need to do napt modification now
#ifdef __KERNEL__
		int ret;
		rtk_rg_pktHdr_t *pPktHdr;
		rtk_rg_alg_connection_t * pConn;
		rtk_rg_alg_tuple_t tuple;
		pPktHdr = (rtk_rg_pktHdr_t *)pPktInfo;
		memset(&tuple, 0, sizeof(rtk_rg_alg_tuple_t));

		if(after==0)
		{
			//Pre function
			if(direct==NAPT_DIRECTION_OUTBOUND)
			{
				_rtk_rg_alg_init_tuple(direct, 1, pPktHdr, &tuple);
				
				pConn = _rtk_rg_alg_connection_find(&tuple);
				if(pConn == NULL)
				{
					pConn = _rtk_rg_alg_connection_add(&tuple);
				}
				else
				{
					pConn->pPktHdr = pPktInfo;
					_rtk_rg_sync_tcp_ack_seq(direct, pPktHdr, pConn);
				}				
			}
		}
		else
		{
			//Post function
			if(direct==NAPT_DIRECTION_OUTBOUND)
			{		
				_rtk_rg_alg_init_tuple(direct, after, pPktHdr, &tuple);
				
				pConn = _rtk_rg_alg_connection_find(&tuple);		
				if(pConn == NULL)
					return RG_FWDENGINE_ALG_RET_FAIL;
				pConn->pPktHdr = pPktInfo;
				ret = _rtk_rg_rtsp_handler(direct, after, pSkb, pPktInfo, (unsigned char *)pConn);
				
			}
			else
			{
				//Attention, pPktHdr->ipv4Dip is not the original external ip after napt modification, it is the internal ip
				_rtk_rg_alg_init_tuple(direct, after, pPktHdr, &tuple);
		
				pConn = _rtk_rg_alg_connection_find(&tuple);		
				if(pConn == NULL)
					return RG_FWDENGINE_ALG_RET_FAIL;
				pConn->pPktHdr = pPktInfo;
				
				//20160923LUKE: we should always update if delta is not zero and should not influence current one if we need to modify it.
				_rtk_rg_sync_tcp_ack_seq(direct, pPktHdr, pConn);
				
				ret = _rtk_rg_rtsp_handler(direct, after, pSkb, pPktInfo, (unsigned char *)pConn);				
			}
			
		}
	
#endif
#if defined(CONFIG_RTL9600_SERIES)
	//20160331LUKE: checksum by sw offload
	if((pPktHdr->egressTagif&PPPOE_TAGIF) && (after==1))
	{
		struct sk_buff *skb;
		skb= (struct sk_buff *)pSkb;
			// set pppoe length = IP total length + 2
			//skb->data[pPktHdr->l3Offset-4]=htons(ntohs(spip->tot_len)+2)>>8;
			//skb->data[pPktHdr->l3Offset-3]=htons(ntohs(spip->tot_len)+2)&0xff;

			//re-cal l3 checksum
			*pPktHdr->pIpv4Checksum=0;
			*pPktHdr->pIpv4Checksum=htons(inet_chksum(skb->data+pPktHdr->l3Offset,pPktHdr->l4Offset-pPktHdr->l3Offset));
		
			//re-cal l4 checksum
			*pPktHdr->pL4Checksum=0;
			*pPktHdr->pL4Checksum=htons(inet_chksum_pseudo(skb->data+pPktHdr->l4Offset,pPktHdr->l3Offset+(*pPktHdr->pL3Len)-pPktHdr->l4Offset,
			ntohl(*pPktHdr->pIpv4Sip),ntohl(*pPktHdr->pIpv4Dip),pPktHdr->ipProtocol));
	}
#endif
	return RG_FWDENGINE_ALG_RET_SUCCESS;

}

#ifdef __KERNEL__
static int __init init(void)
{
	return SUCCESS;
}


static void __exit exit(void)
{
}

module_init(init);
module_exit(exit);

#endif