#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <linux/netfilter.h>        /* for NF_ACCEPT */
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
#include <uci.h>
#include <sys/syslog.h>
#include <linux/ip.h>
#include <linux/tcp.h>

#include "commtouch.h"

#define DEBUG_FLAG 0
#if DEBUG_FLAG
#define   DEBUG(fmt,args...) printf("DEBUG: " fmt, ##args)
#else
#define   DEBUG(fmt,args...)
#endif

#define IPQUAD(addr) \
  ((unsigned char *)&addr)[0],                  \
    ((unsigned char *)&addr)[1],                \
    ((unsigned char *)&addr)[2],                \
    ((unsigned char *)&addr)[3]
    
int syslogc=0;

struct uci_context *ctx;
struct uci_package *pkg = NULL; 

unsigned long checked_time=0;

short parse_url_code(char *buffer)
{
	char *row, *tmp_code="";
	char code[8];
	int i = 10;
	
	row = strtok (buffer, "\r\n");
	while(i>0)
	{
		row = strtok (NULL, "\r\n");
		if(row!=NULL)
		{
			if(strstr(row, "x-ctch-categories:") != NULL)
			{
				tmp_code = strtok (row, ":");
				tmp_code = strtok (NULL, ":");
				strncpy(code, &tmp_code[1], strlen(tmp_code)-1);
				tmp_code = strtok(code, "\r\n");
				break;
			}
		}	
		--i;
	}
	if(!strcmp(tmp_code, ""))
		return -1;
	return atoi(tmp_code);
}

void init_url_cache_table()
{
	int i, j;

	DEBUG("####CommTouch: Flush URL Cache Table!\n");
	
	for(i=0;i<URL_HASH_SIZE;i++)
	{
		for(j=0;j<CACHE_ENTRY_LENGTH;j++)
		{
			url_cache_table[i][j].url_addr[0] = '\0';
			url_cache_table[i][j].category = 0;
		}
	}

	for(j=0;j<CACHE_ENTRY_LENGTH;j++)
		cache_index[j] = 0;
}
unsigned int calculate_url_hash_value(char *host_url)
{
	unsigned int mult = 567629137;        // a large (32 bit) prime number;
	unsigned int pos = 0;    
    	unsigned int accumulator = 0;    
   	unsigned int HashValue =0;    
    	unsigned char ch;

	if((host_url[3] == '.') && (0 == strncmp(host_url, "www", 3))) {
		host_url = host_url + 4;
	}

        
    	ch = host_url[0];    
    
    	while (ch !='\0'){
       	pos++;
        	accumulator+=(mult*ch) + pos;
        	ch=*(host_url++);
    	}

    	return (unsigned int)(accumulator % URL_HASH_SIZE);
}
int search_url_cache_table(char *host_url, short *code, int *hash_result)
{
	int i, hash_value;

	hash_value = calculate_url_hash_value(host_url);
	*hash_result = hash_value;


	DEBUG("CommTouch: Req URL -> %s\n", host_url);
	for(i=0;i<CACHE_ENTRY_LENGTH;i++)
	{
		DEBUG("CommTouch: Trg URL -> %s\n", url_cache_table[hash_value][i].url_addr);
		if(url_cache_table[hash_value][i].url_addr[0] == '\0')
			return 0;

		if(!strcmp(host_url, url_cache_table[hash_value][i].url_addr))
		{
			*code = url_cache_table[hash_value][i].category;
			return 1;
		}
	}
	return 0;
}

void add_host_to_cache_table(char *host_url, short *code, int hash_result)
{
	strcpy(url_cache_table[hash_result][cache_index[hash_result]].url_addr, host_url);
	url_cache_table[hash_result][cache_index[hash_result]].category = *code;
	cache_index[hash_result] = (cache_index[hash_result]+1) % CACHE_ENTRY_LENGTH;
}


int SetSocketTimeout(int connectSocket, int milliseconds)
{
	struct timeval tv;

	tv.tv_sec = milliseconds / 1000 ;
	tv.tv_usec = ( milliseconds % 1000) * 1000  ;

    	return setsockopt (connectSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof tv);
}

int init_socket_to_commtouch()
{
	struct sockaddr_in serv_addr;
	struct hostent *hostptr;
	int resolve_state = 1;
	
	if ((hostptr = gethostbyname(COMMTOUCH_SERVER)) == NULL)
	{
		DEBUG("CommTouch log: Get Server IP from Host Name Failed!\n");
		resolve_state = 0;
	}

	memset(&serv_addr, 0, sizeof(struct sockaddr_in));

	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(5678);
	
	if(resolve_state)
		memcpy((char *)&serv_addr.sin_addr,(char *)hostptr->h_addr,hostptr->h_length);
	else
	{
		DEBUG("CommTouch log: Use server ip directly!\n");
		serv_addr.sin_addr.s_addr = inet_addr(COMMTOUCH_SERVER_IP);
	}
	
	if ((commtouch_socket = socket(AF_INET,SOCK_DGRAM,0)) < 0)
	{
		DEBUG("CommTouch log: Create Socket Failed!\n");
		return -2;
	}
	if (connect(commtouch_socket,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
	{
		DEBUG("CommTouch log: Connect to Server Failed!\n");
		close(commtouch_socket);
		return -3;
	}

	SetSocketTimeout(commtouch_socket, 1000); //set socket timeout in 3 sec

	return 1;
}


int get_url_code_from_commtouch(char *host_url, short *code)
{
	char buffer[256];
	char rcv_buffer[1024];
	int rcv_len=0;
	int snd_len=0;
	
	if(strlen(host_url)>=256)
		return -2;

	sprintf(buffer,
		"x-ctch-request-id: %d\r\nx-ctch-request-type: classifyurl\r\nx-ctch-pver: %s\r\nx-ctch-key: %s\r\n\r\nx-ctch-url: %s\r\n",
		commtouch_comid, commtouch_pver, commtouch_key, host_url);
    DEBUG("buffer=%s",buffer);
	if((snd_len = send(commtouch_socket, &buffer, strlen(buffer), 0)) < 0)
	{
		DEBUG("CommTouch log: Send URL Request to Server Failed!\n");
		close(commtouch_socket);
		return -1;		
	}
		if((rcv_len = recv(commtouch_socket, &rcv_buffer, sizeof(rcv_buffer), 0)) > 0)
		{
			DEBUG("rcv_buffer=%s\n", rcv_buffer);
			if((*code = parse_url_code(rcv_buffer)) == -1)
				return -3;
			DEBUG("CommTouch log: URL Code is %d\n", code);
			close(commtouch_socket);
			return 0;
	    }
	DEBUG("CommTouch log: Receive URL Code failed!\n");
	close(commtouch_socket);
	return -2;
}


//find pscode from default category list   
int check_pscode(char* category)
{
	int i=0;
	for(i=0;i<256;i++)
	{
		if(!strcmp(category_list[i].category,category))
		{
			DEBUG("check_pscode=%d\n",atoi(category_list[i].pscode));
			return 	atoi(category_list[i].pscode);
		}	
	}
	return -1;
}

void StrToLower(char *str,int len){
    int i;
    for(i=0;i<len;i++) {
        if(*str > 0x40 && *str < 0x5B ) {
            *str += 0x20;
        }
        str++;
    }
}

// 0: check ok
// 1: check fail or not check yet
int check_license()
{
	struct uci_context *ctx2;
	struct uci_package *pkg2 = NULL; 
	struct uci_section *s;    
	struct uci_option  *o;
	int ret=1;
	
	ctx2 = uci_alloc_context();
	uci_load(ctx2, "fw_cf_license", &pkg2);
	if (pkg2 == NULL)
	{
		uci_free_context(ctx2);
		return 1;
	}
	
	s=uci_lookup_section(&pkg2->sections,"fwlicense");
	o=uci_lookup_option(&s->options,"status");

	if(!strcasecmp("enable",o->v.string))
		ret=0;

	uci_free_context(ctx2);
	return ret;
}


// 1: check ok
// 0: check fail or not check yet
int check_expire_time()
{
	char *token1, *token2;
	char start[32],expire[32];
	struct tm start_tm, expire_tm;
	time_t start_timep, expire_timep, current_timep;
	int auth_status = 1;
	struct uci_context *ctx2;
	struct uci_package *pkg2 = NULL; 
	struct uci_section *s;    
	struct uci_option  *o;

	ctx2 = uci_alloc_context();
	uci_load(ctx2, "fw_cf_license", &pkg2);
	if (pkg2 == NULL)
	{
		uci_free_context(ctx2);
		return 0;
	}
	
	s=uci_lookup_section(&pkg2->sections,"fwlicense");
	
	o=uci_lookup_option(&s->options,"start_date");
	strcpy(start,o->v.string);
	
	o=uci_lookup_option(&s->options,"exp_date");
	strcpy(expire,o->v.string);
	
	//process start time
	token1 = strtok(start, "-");

	start_tm.tm_year = atoi(token1) - 1900;
	token1 = strtok(NULL, "-");
	start_tm.tm_mon = atoi(token1) - 1;
	token1 = strtok(NULL, "-");
	start_tm.tm_mday = atoi(token1);
	start_tm.tm_hour = 0;
	start_tm.tm_min = 0;
	start_tm.tm_sec = 0;
	start_tm.tm_isdst = -1;
	start_timep = mktime(&start_tm);


	//process expire time
	token2 = strtok(expire, "-");
	expire_tm.tm_year = atoi(token2) - 1900;
	token2 = strtok(NULL, "-");
	expire_tm.tm_mon = atoi(token2) - 1;
	token2 = strtok(NULL, "-");
	expire_tm.tm_mday = atoi(token2);
	expire_tm.tm_hour = 0;
	expire_tm.tm_min = 0;
	expire_tm.tm_sec = 0;
	expire_tm.tm_isdst = -1;
	expire_timep = mktime(&expire_tm);

	//get currnt time
	time(&current_timep);

	
	if(start_timep < current_timep && current_timep < expire_timep)
		auth_status = 0;
	
	return auth_status;
	
}

// block file check
//  0 : pass
//-1 : block java
//-2 : block activex
//-3 : block compressed file
//-4 : block execution file
//-5 : block mms file


int block_file_check(struct uci_section *s,char *token1)
{
	struct uci_option  *o;
	char *filename;
	int i=0;

	StrToLower(token1,strlen(token1));
	
	filename=strtok(token1," ");
	filename=strtok(NULL," ");
	DEBUG("filename %s \n",filename);

	if(filename!=NULL)
	{
		o=uci_lookup_option(&s->options,"block_java");
		if(strcasecmp(o->v.string,"enable")==0)
		{
			for(i=0 ; i< sizeof(BK_JAVA)/sizeof(char*) ; i++)	
			{
				char *tmp=strstr(filename,BK_JAVA[i]);
				if(tmp!=NULL && tmp[sizeof(BK_JAVA[i])]=='\0')
					return -1;
			}
		}	
		o=uci_lookup_option(&s->options,"block_activex");
		if(strcasecmp(o->v.string,"enable")!=0)
		{
			for(i=0 ; i< sizeof(BK_OLE)/sizeof(char*) ; i++)	
			{
				char *tmp=strstr(filename,BK_OLE[i]);
				if(tmp!=NULL && tmp[sizeof(BK_OLE[i])]=='\0')
					return -2;
			}
		}	
		o=uci_lookup_option(&s->options,"block_compressed");
		if(strcasecmp(o->v.string,"enable")==0)
		{
			for(i=0 ; i< sizeof(BK_ZIPFILE)/sizeof(char*) ; i++)	
			{
				char *tmp=strstr(filename,BK_ZIPFILE[i]);
				if(tmp!=NULL && tmp[sizeof(BK_ZIPFILE[i])]=='\0')
					return -3;
			}
		}	
		o=uci_lookup_option(&s->options,"block_exec");
		if(strcasecmp(o->v.string,"enable")==0)
		{
			for(i=0 ; i< sizeof(BK_EXEFILE)/sizeof(char*) ; i++)	
			{
				char *tmp=strstr(filename,BK_EXEFILE[i]);
				if(tmp!=NULL && tmp[sizeof(BK_EXEFILE[i])]=='\0')
					return -4;
			}
		}	
		o=uci_lookup_option(&s->options,"block_mms");
		if(strcasecmp(o->v.string,"enable")==0)
		{
			for(i=0 ; i< sizeof(BK_MMSFILE)/sizeof(char*) ; i++)	
			{
				char *tmp=strstr(filename,BK_MMSFILE[i]);
				if(tmp!=NULL && tmp[sizeof(BK_MMSFILE[i])]=='\0')
					return -5;
			}
		}	
	}
	return 0;
}
int CovStrToIp(char  *pchString, unsigned long  *pIpAddress)
{
    unsigned char        u8MaxQuotient = 0xff/10;
    unsigned char        u8MaxRemainder = 0xff%10;
    unsigned char        u8NextDig;
    unsigned char        u8NextByte = 0;

    *pIpAddress = 0;
	
    if (NULL == pchString)
    {
        return -1;
    }

    while (*pchString != 0)
    {
        if (*pchString != '.')
        {
            u8NextDig = (*pchString - '0');
            if ((u8NextByte > u8MaxQuotient) || 
                ((u8NextByte == u8MaxQuotient) &&
                 (u8NextDig > u8MaxRemainder)))
            {
                return -1;
            }
            u8NextByte = u8NextByte*10 + u8NextDig;
        }
        else
        {
            *pIpAddress = *pIpAddress << 8;
            *pIpAddress += u8NextByte;
            u8NextByte = 0;
        }
        pchString++;
    }
    *pIpAddress = *pIpAddress << 8;
    *pIpAddress += u8NextByte;

    return 0;
}
char* check_ip(char *payload)
{
	struct iphdr *iphdrp = (struct iphdr *)payload;
	struct uci_option  *o;
	struct uci_section *s; 
	struct uci_element *es;
	
	char tmp[16];
	unsigned long cli_ip=0,cli_mask=0;
	int equal=0;
	  
	DEBUG("iphdr %u.%u.%u.%u ->",
         IPQUAD(iphdrp->saddr));
	DEBUG(" %u.%u.%u.%u \n",
         IPQUAD(iphdrp->daddr));

	uci_foreach_element(&pkg->sections, es)
	{	
		s = uci_to_section(es);
		o=uci_lookup_option(&s->options,"src_ip");     
		DEBUG("cli_ip=%s\n",o->v.string);

		//0.0.0.0 = any
		if(!strcmp(o->v.string,"0.0.0.0"))
			return s->e.name;
		
		strcpy(tmp,o->v.string);
		CovStrToIp(tmp,&cli_ip);
		DEBUG("cli_ip %u.%u.%u.%u\n",
			 IPQUAD(cli_ip));
		o=uci_lookup_option(&s->options,"mask");
		DEBUG("cli mask=%s\n",o->v.string)  ;
		strcpy(tmp,o->v.string);
		CovStrToIp(tmp,&cli_mask);
		DEBUG("cli_mask %u.%u.%u.%u\n",
			 IPQUAD(cli_mask));
		
		int i=0;
		for(i=0;i<4;i++)
		{
			DEBUG("cip i %d %d\n",i,((unsigned char *)&cli_ip)[3-i]&((unsigned char *)&cli_mask)[3-i]);		
			DEBUG("sip i %d %d\n",i,((unsigned char *)&iphdrp->saddr)[i]&((unsigned char *)&cli_mask)[3-i]);
			
			if((((unsigned char *)&cli_ip)[3-i]&((unsigned char *)&cli_mask)[3-i]) == 
			   (((unsigned char *)&iphdrp->saddr)[i]&((unsigned char *)&cli_mask)[3-i]))
			{
				equal++;
			}   
		}		
		
		if(equal==4)
			return s->e.name;
	}	
	
	return NULL;	
}
// call back function, check packet and decide to drop or accept
static int cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,
          struct nfq_data *nfa, void *data)
{      

	int status=0,len=0;
	struct nfqnl_msg_packet_hdr *ph;
	char *payload,*payload2,HTTP_H[1024],*token1,*token2,*q,*p;
	char *proxy,*cookie,*string;
	char page[256],host[2048],tmppage[256];
	int   cache_result, hash_result,pagelen=0;
	recType tmprec;
	int keyword_match=0;
	struct uci_element *es, *eo;    
	struct uci_section *s;    
	struct uci_option  *o;
	time_t  current_timep;
	char *section_name;

    u_int32_t id;
	
	memset(HTTP_H,0,1024);
	ph = nfq_get_msg_packet_hdr(nfa);
	
	if (ph) {
		id = ntohl(ph->packet_id);
		len=nfq_get_payload(nfa, &payload);
		nfq_get_payload(nfa, &payload2);
		
		section_name = check_ip(payload2);
		DEBUG("section_name=%s\n",section_name);
		if(NULL == section_name)
		{
			DEBUG("NULL section NAME\n");
			goto ACCEPT;
		}
		DEBUG("RUNNING\n");
		//load uci config --wcf
		s=uci_lookup_section(&pkg->sections,section_name);

		memcpy(HTTP_H,payload+40,1024);
		StrToLower(HTTP_H,strlen(HTTP_H));
		if(strncmp(HTTP_H,"get ",4)==0)	
		{	
			token1=strtok(HTTP_H,"\r\n");
			do
			{	
				token2=strtok(NULL,"\r\n");
				if(token2==NULL)
					break;
				if(strncmp(token2,"host: ",4)==0)
					string=token2;
				else if(strncmp(token2,"cookie: ",8)==0)
					cookie=token2;
				else if(strncmp(token2,"proxy-connection: ",18)==0)
					proxy=token2;
			}while(token2!=NULL);

			o=uci_lookup_option(&s->options,"block_cookie");
			if(strcasecmp(o->v.string,"enable")==0)
			{	
				if(cookie!=NULL)
					goto DROP;
			}
			o=uci_lookup_option(&s->options,"block_proxy");
			if(strcasecmp(o->v.string,"enable")==0)
			{	
				if(proxy!=NULL)
					goto DROP;
			}
			
			string=string+6;
						
			q= strstr(string, "/");
			if(q) {
				q++;                    
				strcpy(page, q);
				p = strtok(string, "/");                     
				strcpy(host, p);                   
			} else {
				strcpy(host, string);	
				page[0] = '\0';
			}
			DEBUG("host=%s\n",host);
			DEBUG("page=%s\n",page);


			if(block_file_check(s,token1)<0)
				goto DROP;
			
			//check url keyword
			o=uci_lookup_option(&s->options,"keyword_action"); //allow,drop, disable
			DEBUG("action=%s\n",o->v.string);
			if(strcasecmp(o->v.string,"disable")!=0)
			{
				if(strcasecmp(o->v.string,"allow")==0)
				{
					o=uci_lookup_option(&s->options,"keyword");
					
					if(o->type==UCI_TYPE_LIST)
					{	
						uci_foreach_element(&o->v.list, eo) 
						{	
							DEBUG("keyword=%s host=%s\n",eo->name,host);
							if(strstr(host,eo->name)!=NULL)
							{
								goto ACCEPT;
							}
						}
					}
					else if(o->type==UCI_TYPE_STRING)
					{
						if(strstr(host,o->v.string)!=NULL)
						{
							goto ACCEPT;
						}
					}
				}
				else if(strcasecmp(o->v.string,"block")==0)
				{
					o=uci_lookup_option(&s->options,"keyword");
					if(o->type==UCI_TYPE_LIST)
					{
						uci_foreach_element(&o->v.list, eo) 
						{
							if(strstr(host,eo->name)!=NULL)
							{
 								if(syslogc!=0)  syslog(LOG_LOCAL0|LOG_DEBUG,"BLOCK %s with keyword %s",host,eo->name);
								goto DROP;
							}
						}
					}
					else if(o->type==UCI_TYPE_STRING)
					{
						if(strstr(host,o->v.string)!=NULL)
						{
							syslog(LOG_LOCAL0|LOG_DEBUG,"BLOCK %s with keyword %s",host,o->v.string);
							goto DROP;
						}
					}
				}
			}	 
			
	
			//check exception allow
			o=uci_lookup_option(&s->options,"expt_a");
			if( o!= NULL)
			{
				if(o->type==UCI_TYPE_LIST)
				{
					uci_foreach_element(&o->v.list, eo) 
					{
						DEBUG("expt_a =%s\n", eo->name);
						
						memset(tmppage,0,sizeof(tmppage));
						if(strstr(eo->name,"/")!=NULL)
							strcpy(tmppage,strstr(eo->name,"/")+1);
						pagelen = strlen(tmppage);
						if(!strcmp(eo->name, host) && tmppage[pagelen-1] == '/' && !strncmp(tmppage, page, pagelen))
						{
							 goto ACCEPT;
						}	
						else if(!strcmp(eo->name, host) && (!strcmp(tmppage, "") || !strcmp(tmppage, page))){
							 goto ACCEPT;
						}
					}
				}
				else if(o->type==UCI_TYPE_STRING)
				{
					DEBUG("expt_a =%s\n", o->v.string);
					memset(tmppage,0,sizeof(tmppage));
					if(strstr(o->v.string,"/")!=NULL)
						strcpy(tmppage,strstr(o->v.string,"/")+1);
					pagelen = strlen(tmppage);
					if(!strcmp(o->v.string, host) && tmppage[pagelen-1] == '/' && !strncmp(tmppage, page, pagelen))
					{
						 goto ACCEPT;
					}	
					else if(!strcmp(o->v.string, host) && (!strcmp(tmppage, "") || !strcmp(tmppage, page))){
						 goto ACCEPT;
					}
				}
			}			
			//check exception deny
			o=uci_lookup_option(&s->options,"expt_d");
			if( o!= NULL)
			{
				if(o->type==UCI_TYPE_LIST)	
				{
					uci_foreach_element(&o->v.list, eo) 
					{
						DEBUG("exot_d =%s\n", eo->name);

						memset(tmppage,0,sizeof(tmppage));
						if(strstr(eo->name,"/")!=NULL)
							strcpy(tmppage,strstr(eo->name,"/")+1);
						pagelen = strlen(tmppage);
						if(!strcmp(eo->name, host) && tmppage[pagelen-1] == '/' && !strncmp(tmppage, page, pagelen))
						{
							if(syslogc!=0)	 syslog(LOG_LOCAL0|LOG_DEBUG,"BLOCK %s in Exception Deny %s",host,eo->name);
							 goto DROP;
						}	
						else if(!strcmp(eo->name, host) && (!strcmp(eo->name, "") || !strcmp(tmppage, page))){
							 if(syslogc!=0)	 syslog(LOG_LOCAL0|LOG_DEBUG,"BLOCK %s in Exception Deny %s",host,eo->name);
							 goto DROP;
						}
					}
				}
				else if(o->type==UCI_TYPE_STRING)
				{
					DEBUG("expt_d =%s\n", o->v.string);
					memset(tmppage,0,sizeof(tmppage));
					if(strstr(o->v.string,"/")!=NULL)
						strcpy(tmppage,strstr(o->v.string,"/")+1);
					pagelen = strlen(tmppage);
					if(!strcmp(o->v.string, host) && tmppage[pagelen-1] == '/' && !strncmp(tmppage, page, pagelen))
					{
						 if(syslogc!=0)	syslog(LOG_LOCAL0|LOG_DEBUG,"BLOCK %s in Exception Deny %s",host,o->v.string);
						 goto DROP;
					}	
					else if(!strcmp(o->v.string, host) && (!strcmp(o->v.string, "") || !strcmp(tmppage, page)))
					{
						if(syslogc!=0)	syslog(LOG_LOCAL0|LOG_DEBUG,"BLOCK %s in Exception Deny %s",host,o->v.string);	
						 goto DROP;
					}
				}
			}
			if(0!=check_license())
			{
				goto ACCEPT;
			}
			//check expire or not			
			time(&current_timep);
			
			if(current_timep>checked_time)
			{
				checked_time=checked_time+43200;
				//system("/www/cgi-bin/activate.cgi 0");
				if((0!=check_expire_time()) || (0!=check_license()))
					goto ACCEPT;
			}		
			
			//check forbidden category
			strcpy(page, "");		
			if(search_url_cache_table(host, &psCode, &hash_result)==1)
				sta = 0;
			else
			{
				int ret=0;
				sta = -1;
				if((ret=init_socket_to_commtouch()) == 1)
					sta = get_url_code_from_commtouch(host, &psCode);
					DEBUG("ret=%d sta=%d psCode=%d\n",ret,sta,psCode);
			}
			
			if(sta == 0) 
			{
				add_host_to_cache_table(host, &psCode, hash_result);
				o=uci_lookup_option(&s->options,"forbidden_cat");
				if( o!= NULL)
				{
					if(o->type==UCI_TYPE_LIST)
					{
						uci_foreach_element(&o->v.list, eo) 
						{
							if(psCode==check_pscode(eo->name))
							{
								if(syslogc!=0)	syslog(LOG_LOCAL0|LOG_DEBUG,"BLOCK %s in Forbidden Category %s",host,eo->name);	
								goto DROP;
							}	
						}
					}
					else if(o->type==UCI_TYPE_STRING)
					{
						if(psCode==check_pscode(o->v.string))
						{
							if(syslogc!=0)	syslog(LOG_LOCAL0|LOG_DEBUG,"BLOCK %s in Forbidden Category %s",host,o->v.string);	
							goto DROP;
						}	
					}
				}
			}
			else
			{
				if(syslogc!=0)	syslog(LOG_LOCAL0|LOG_DEBUG,"Sc_CategorizeURL failed");
				DEBUG("Sc_CategorizeURL failed, ret:%d\n", sta);	
			}	
		}	
	}	
	
ACCEPT:	
	DEBUG("ACCEPT\n");
    //return nfq_set_verdict_mark(qh, id, NF_ACCEPT,0x6216,0, NULL);
    return nfq_set_verdict(qh, id, NF_ACCEPT,0, NULL);
DROP:
	DEBUG("DROP\n");
    //return nfq_set_verdict_mark(qh, id, NF_DROP,0x6216,0, NULL);	
    return nfq_set_verdict(qh, id, NF_DROP,0, NULL);	
}
   
int main(int argc, char **argv)
{
    struct nfq_handle *h;
    struct nfq_q_handle *qh;
    struct nfnl_handle *nh;
    int fd,rv;
    int ret=0,i=0;
    char buf[4096],tmp[128];
    FILE *fp;
	time_t  current_timep;
//set check time
	time(&current_timep);
	checked_time=current_timep+43200;

	if(argc==2)
		syslogc=atoi(argv[1]);
	DEBUG("syslogc=%d \n",syslogc);
// open nfq_handle
    h = nfq_open();
    if (!h) {
        fprintf(stderr, "error during nfq_open()\n");
        exit(1);
    }
// unbind AF_INET
    if ((ret=nfq_unbind_pf(h, AF_INET)) < 0) {
       fprintf(stderr, "error during nfq_unbind_pf()\n");
       exit(1);
    }
	
// bind with AF_INET
    if ((ret=nfq_bind_pf(h, AF_INET)) < 0) {
        fprintf(stderr, "error during nfq_bind_pf()\n");
        exit(1);
    }

// binding this socket to queue '30'
    qh = nfq_create_queue(h, 30, &cb, NULL);
    if (!qh) {
        fprintf(stderr, "error during nfq_create_queue()\n");
        exit(1);
    }
   

    if (nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff) < 0) {
        fprintf(stderr, "can't set packet_copy mode\n");
        exit(1);
    }
   
    nh = nfq_nfnlh(h);
    fd = nfnl_fd(nh);

   	init_url_cache_table();

	//initial category_list 
	memset(category_list,0,sizeof(category_list));
	if ((fp = fopen(COMMTOUCH_GATEGORY_LIST_FILE, "r")) != NULL)
	{
		while(fgets(tmp, sizeof(tmp), fp) != NULL)
		{
			if(tmp[0]=='#')
				continue;
			else
			{
				sprintf(category_list[i].category,"%s",strtok (tmp, ","));	
				sprintf(category_list[i].pscode,"%s",strtok(NULL, ","));
				i++;
			}		
		}	
		fclose(fp);
	}
	//load uci config 'content filter'
	ctx = uci_alloc_context();
	uci_load(ctx, "content_filter", &pkg);
	if (pkg == NULL)
	{
		uci_free_context(ctx);
		return 1;
	}
	
// receieve data from netlink
    while ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0) {
        nfq_handle_packet(h, buf, rv);
    }  
       
    DEBUG("unbinding from queue 30\n");
// queue release
    nfq_destroy_queue(qh);
   
#ifdef INSANE
    /* normally, applications SHOULD NOT issue this command, since
     * it detaches other programs/sockets from AF_INET, too ! */
    DEBUG("unbinding from AF_INET\n");
    nfq_unbind_pf(h, AF_INET);
#endif
   	uci_free_context(ctx);
    DEBUG("closing library handle\n");
// close nfq_handle
    nfq_close(h);
    exit(0);
}
