
// Copyright 2005, Texas Instruments Incorporated
//
// This program has been modified from its original operation by Texas Instruments
// to do the following:
//
// 1. Prevent caching when AAAA query responses are received and don't lookup cache
//    when AAAA queries are received from clients
// 2. Allow error response with rsp code 1 & 3 to client
// 3. Cache only if a valid address or name is received in the response. There was
//    a possibility of caching incorrect values in ip and cname variables  if the
//    response is success, but there was no name or host address in the response
// 4. Use port 53 only for listening to DNS request and to send error response to
//    the clients. Use ephemeral port for forwarding request to server. Also
//    use ephemeral port for listening to responses from DNS server
// 5. NSP Policy Routing Framework
//
// THIS MODIFIED SOFTWARE AND DOCUMENTATION ARE PROVIDED
// "AS IS," AND TEXAS INSTRUMENTS MAKES NO REPRESENTATIONS
// OR WARRENTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
// PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR
// DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS,
// COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
// See The GNU General Public License for more details.
//
// These changes are covered under version 2 of the GNU General Public License,
// dated June 1991.
//-------------------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdarg.h>
#include <signal.h>
#include <syslog.h>
#include <sys/stat.h>
#include <malloc.h>

#include "dproxy.h"
#include "dns_decode.h"
#include "cache.h"
#include "conf.h"
#include "dns_list.h"
#include "dns_construct.h"
#include "dns_io.h"
//#include <linux/pr.h>

static int pppdemand_idle=0;
//cmm


 //cmm end

static char WarningEnable=0;
 
/*****************************************************************************/
/*****************************************************************************/
int dns_main_quit;
int dns_sock;
int dns_any_sock;
int dns_request_cnt;
fd_set rfds;
dns_request_t *dns_request_list;
struct stat conf_stat;



//CMM

static int g_iInit = 0;	/* 0 - uninitialized; 1 - initialized success; 2 - initialized failed */
static unsigned int g_uMsgId = 0;

int CMM_Init()
{
	MSG_RET iRet;
	if (g_iInit==CMM_INIT_NONE) {
		iRet = MSG_RegModule(MID_DPROXY_SELF, NULL);	/* 在CMM_Call()中自己接收消息，所以不需要消息循环 */
		if (RET_FAILED(iRet)) {
			g_iInit = CMM_INIT_FAILED;
			CMMIF_TRACE("err: register module STAT failed (%d)!\n", iRet);
		} else {
			MSG_AllModStartOK();
			g_iInit = CMM_INIT_SUCCESS;
			CMMIF_TRACE("msg: register module STAT successed.\n");
		} 
	}

	return g_iInit;
}

void CMM_Free()
{
	if (g_iInit==CMM_INIT_SUCCESS) {
		MSG_UnregModule(MID_DPROXY_SELF, NULL);
		g_iInit = CMM_INIT_NONE;
		CMMIF_TRACE("msg: unregistered module STAT.\n");
	}
}

#if 1
int CMM_Call(ST_MSG* pReq, ST_MSG** ppRsp)
{
	MSG_RET iRet;
	time_t t0, t1, t;


	iRet = MSG_SendMessage(pReq);
	if (iRet!=MSG_OK) {
		CMMIF_TRACE("err: send request to CMM failed (%d)!\n", iRet);
		return CMM_ERR_SEND;
	}
#ifdef CMMIF_DEBUG
	SNMPA_ShowMessage(pReq);
#endif

//注意，因为我们不设配置，所以才这样
//wulihua 2010-3-16 add 
	if(pReq->stMsgHead.usMsgType == MSG_CMM_SET_VAL)
	{
		return CMM_SUCCESS; 
	}

	
	t = t0 = time(NULL);
	t1 = t0+CALL_CMM_TIMEOUT;
	for (;;) {
		t = time(NULL);
		if (t>t1) {	/* 等待应答超时 */
			CMMIF_TRACE("err: wait CMM response timeout (%d seconds)!\n", CALL_CMM_TIMEOUT);
			return CMM_ERR_TIMEOUT;
		}

		iRet = MSG_ReceiveMessage(ppRsp, t1-t);
		if (iRet!=MSG_OK) {
			CMMIF_TRACE("err: receive response from CMM failed (%d)!\n", iRet);
			//return CMM_ERR_RECV; //wulihua 2010-3-10 这里老是退出了，不知道什么原因造成

			continue;
		}
		/*  收到一个消息 */
		if (*ppRsp) {
			ST_MSG* pRsp = *ppRsp;
			if (pRsp->stMsgHead.ulMsgID==pReq->stMsgHead.ulMsgID&&
				pRsp->stMsgHead.usDstMID==pReq->stMsgHead.usSrcMID&&
				pRsp->stMsgHead.usSrcMID==pReq->stMsgHead.usDstMID)
			{
				/* 收到对应于我们请求的应答消息，退出循环 */
#ifdef CMMIF_DEBUG
				SNMPA_ShowMessage(pRsp);
#endif
				break;
			} else {
#ifdef ZTE_HOT_AP_SNMP_IF
				/* 不是对应于我们请求的应答消息，看是否是snmpa_MsgProcess所需要的，否则丢弃并继续接收 */
#if 0 
			  if(snmpa_MsgProcess(pRsp) == TBS_SUCCESS)
                            {
                                safe_free_msg(pRsp);
                            }
                            else 
#endif
//			snmpa_MsgProcess(pRsp) ;
                        safe_free_msg(pRsp);
#endif
//                                MSG_ReleaseMessage(*ppRsp);
			}
		} else {
			CMMIF_TRACE("err: received a null response, why?!\n");
			return CMM_FAIL;
		}
	}
	return CMM_SUCCESS;
}



ST_MSG* CMM_CreateMessage(
	unsigned short usSrcMID, unsigned short usDstMID,
	unsigned short usMsgType, unsigned long ulBodyLength)
{
	ST_MSG* pMsg = (ST_MSG*)NULL;
	pMsg = MSG_CreateMessage(ulBodyLength);
	if (!pMsg) {
		CMMIF_TRACE("err: %s() failed - MSG_CreateMessage() returned NULL(SrcMID=%04x,DstMID=%04x,MsgType=%04x,BodySize=%08x)!\n",
			__func__, usSrcMID, usDstMID, usMsgType, (unsigned int)ulBodyLength);
		return  (ST_MSG* )NULL;
	}
	pMsg->stMsgHead.usSrcMID = usSrcMID;
	pMsg->stMsgHead.usDstMID = usDstMID;
	pMsg->stMsgHead.ulMsgID = ++g_uMsgId;
	pMsg->stMsgHead.usMsgType = usMsgType;
	pMsg->stMsgHead.ulBodyLength = ulBodyLength;
	return pMsg;
}	
ST_MSG* CMM_CreateGetValMessage(const char* szLeafName)
{
	unsigned long ulBodySize = (sizeof(unsigned long)+strlen(szLeafName)+1);
	ST_MSG* pMsg = CMM_CreateMessage(
		MID_DPROXY_SELF, MID_CMM,
		MSG_CMM_GET_VAL, ulBodySize);

	if (!pMsg) {
		return NULL;
	}

	/* Body */
	*((unsigned long*)pMsg->szMsgBody) = 1;	/* PathCount */
	strcpy(pMsg->szMsgBody+sizeof(unsigned long), szLeafName);		/* Path1 */

	return pMsg;
}

int CMM_GetStr(const char* szLeafName, char* szValue, unsigned int uSize, char **pv, int pvLen)
{
	MSG_RET iRet = CMM_SUCCESS;
	ST_MSG* pReq = NULL;
	ST_MSG* pRsp = NULL;
	char* pcItem = NULL;
	char* pc = NULL;
	u_long ulResult = 0;

	pReq = CMM_CreateGetValMessage(szLeafName);
	if (!pReq) {
		iRet = CMM_ERR_MEM;
		goto EXIT1;
	}

	iRet = CMM_Call(pReq, &pRsp);
	if (iRet!=CMM_SUCCESS) {
		goto EXIT2;
	}

	ulResult = *((u_long*)pRsp->szMsgBody);
	if (0!=ulResult) {
		iRet = CMM_FAIL;
		CMMIF_TRACE("err: get node value failed (%s result=%lu)!\n",
			szLeafName, ulResult);
		goto EXIT3;
	}

	pcItem = pRsp->szMsgBody+sizeof(unsigned long)+sizeof(unsigned long);	/* Result + Status */
	CMMIF_TRACE("msg: %s()--->%s\n", __func__, pcItem);
	pc = strtok(pcItem, "=");
	pc = strtok(NULL, "\n\r");
	if (pc) {
		strncpy(szValue, pc, uSize-1);
		szValue[uSize-1] = 0;
	} else {
		*szValue = 0;
	}
	CMMIF_TRACE("msg: value='%s'\n", szValue);

       /*map the pv and try to find the matched value*/
       if(pv && pvLen > 1) /*at least pvlen is 2*/
       {
            int found = 0;
            int i = 0;
            char *p = pv[i];
            while(1)
            {
                if(strcasecmp(szValue, pv[i]) == 0) /*compare name: value read from CMM*/
                {
                    found = 1;
                    strcpy(szValue, pv[i+1]); /*retrieve the value: value transfered to SNMP manager*/
                    break;
                }

                i += 2;
                if(i >= pvLen) break;
                
                p = pv[i];
            }
            
            if(found == 0) 
            {
               // SNMPA_TRACE("Failed to get %s, because being unmathced!\n", szLeafName);
                //iRet = CMM_FAIL; /*If no item matched the value, return it as successful*/
            }
       }

EXIT3:
	MSG_ReleaseMessage(pRsp);
EXIT2:
	MSG_ReleaseMessage(pReq);
EXIT1:
	return iRet;
}

#endif
//cmm end

//CMM END
/*****************************************************************************/
int is_connected()
{
    FILE *fp;

    if(!config.ppp_detect)return 1;

    fp = fopen( config.ppp_device_file, "r" );
    if(!fp)return 0;
    fclose(fp);
    return 1;
}
/*****************************************************************************/
int dns_init()
{
    struct sockaddr_in sa;
    struct in_addr ip;

    //unsigned int pr_mark = DPROXY_PR_MARK;

    /* Clear it out */
    memset((void *)&sa, 0, sizeof(sa));

    dns_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    /* Error */
    if( dns_sock < 0 ){
        debug_perror("Could not create dns socket");
        exit(1);
    }

    ip.s_addr = INADDR_ANY;
    sa.sin_family = AF_INET;
    memcpy((void *)&sa.sin_addr, (void *)&ip, sizeof(struct in_addr));
    sa.sin_port = htons(PORT);

    /* bind() the socket to the interface */
    if (bind(dns_sock, (struct sockaddr *)&sa, sizeof(struct sockaddr)) < 0){
        debug_perror("dns_init: bind: Could not bind to port");
        exit(1);
    }

    /* Use ANY port to forward request to the DNS server */
    /* Clear it out */
    memset((void *)&sa, 0, sizeof(sa));

    dns_any_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    /* Error */
    if( dns_any_sock < 0 ){
        debug_perror("Could not create dns any socket");
        exit(1);
    }

    ip.s_addr = INADDR_ANY;
    sa.sin_family = AF_INET;
    memcpy((void *)&sa.sin_addr, (void *)&ip, sizeof(struct in_addr));
    sa.sin_port = 0; /* Use any port */

    /* bind() the socket to the interface */
    if (bind(dns_any_sock, (struct sockaddr *)&sa, sizeof(struct sockaddr)) < 0){
        debug_perror("dns_init: bind: Could not bind to ANY port");
        exit(1);
    }

#if 0
    /* Adding setsockopt to set a mark value for a particular socket. */
    if (setsockopt(dns_any_sock, SOL_IP, POLICY_ROUTING_MARK, (void *)&pr_mark, sizeof(pr_mark)) != 0)
    {
        printf ("Failed to set PR mark for socket %d\n", dns_any_sock);
    }
#endif

    dns_main_quit = 0;

    FD_ZERO( &rfds );
    FD_SET( dns_sock, &rfds );
    FD_SET( dns_any_sock, &rfds );

    dns_request_list = NULL;

    cache_purge( config.purge_time );

    return 1;
}
/*****************************************************************************/
void dns_handle_new_query(dns_request_t *m)
{
    struct in_addr in;
    int retval = -1;
    if( m->message.question[0].type == A){
        /* standard query */
        retval = cache_lookup_name( m->cname, m->ip );
    }else if( m->message.question[0].type == AAA ){
        /* IPv6 AAAA query in IPv4 */
        /* This query is not cached */
        retval = 0 ;
    }else if( m->message.question[0].type == PTR ){
        /* reverse lookup */
        retval = cache_lookup_ip( m->ip, m->cname );
    }

    debug(".......... %s ---- %s\n", m->cname, m->ip );

	if(0)
	{
    dns_construct_reply( m );
   dns_write_packet( dns_sock, m->src_addr, m->src_port, m );
	return;
	}

#if 0
	char szWarning[40]={0};
CMM_GetStr("InternetGatewayDevice.X_TWSZ-COM_TrafficMeter.TrffcLimitWarningReached",szWarning,sizeof(szWarning)-1, NULL, 0);
if((!strcmp(szWarning,LIMIT_WARNING_STR_TIME_OVER))||(!strcmp(szWarning,LIMIT_WARNING_STR_VLM_OVER)))

	{
    dns_construct_reply( m );
   dns_write_packet( dns_sock, m->src_addr, m->src_port, m );
	return;
	}
#endif
    switch( retval )
    {
        case 0:
		if(pppdemand_idle==1)
			{
				//如果是按需拨号且网络连接空闲，在处理DNS请求时触发PPP连接
				system("killall -SIGUSR1 pppd");
			}
            if( is_connected() ){
                debug("Adding to request list-> id: %d\n", m->message.header.id);
				if(dns_request_cnt < REQUEST_CNT)
				{
					dns_request_cnt++;
                	dns_request_list = dns_list_add( dns_request_list, m );
            	}
				else
				{
					break;
				}
#ifdef  SENDTO_ALL_DNSSERVER_AT_ONCE
				int i;
				for(i=0; i<MAX_NAME_SERVER; i++)
				{
					if(config.name_server[i][0] != NULL)
					{
						 inet_aton( config.name_server[i], &in );
               			 debug("Sent Request To %s\n",config.name_server[i]);
               			 dns_write_packet( dns_any_sock, in, PORT, m );
					}
					else
					{
						break;
					}
				}
#else
                /*!!! relay the query untouched */
                inet_aton( config.name_server[0], &in );
                debug("Sent Request To %s\n",config.name_server[0]);
                dns_write_packet( dns_any_sock, in, PORT, m );
#endif

            }else{
                debug("Not connected **\n");
                dns_construct_error_reply(m);
				/*Modify by sdh 2012-6-27，不给客户端返回失败的，这样客户端不会重新在DNS域名后加上.belkin后缀再请求，
				从而引起解释再次失败，打开网页会更慢*/
				#if 0
                dns_write_packet( dns_sock, m->src_addr, m->src_port, m );
				#endif
				/*End by sdh 2012-6-27*/
            }
            break;
        case 1:
            dns_construct_reply( m );
            dns_write_packet( dns_sock, m->src_addr, m->src_port, m );
            debug("Cache hit -  name %s -ip %s\n", m->cname, m->ip);
            break;
        default:
            debug("Unknown query type: %d\n", m->message.question[0].type );
    }

}
/*****************************************************************************/
void dns_handle_request(dns_request_t *m)
{
    dns_request_t *ptr = NULL;
    int forward_resp=0;

	char szWarning[40]={0};
	if(WarningEnable==1)
	{
		CMM_GetStr("InternetGatewayDevice.X_TWSZ-COM_TrafficMeter.TrffcLimitWarningReached",szWarning,sizeof(szWarning)-1, NULL, 0);
		if((!strcmp(szWarning,LIMIT_WARNING_STR_TIME_OVER))||(!strcmp(szWarning,LIMIT_WARNING_STR_VLM_OVER)))
		{
		      dns_construct_warning_reply( m );
		      dns_write_packet( dns_sock, m->src_addr, m->src_port, m );
			return;
		}
	}
	
    /* request may be a new query or a answer from the upstream server */
    ptr = dns_list_find_by_id( dns_request_list, m );

    if( ptr != NULL )
    {
        debug("Found query in list\n");
        /* message may be a response */
        if( m->message.header.flags.f.question == 1 )
        {
            switch( m->message.header.flags.f.rcode )
            {
                case DNS_NO_ERROR:
                    {
                        /* Append to the cache if not a AAA query and cache flag indicates
                           something available for cache
                           */
                        if((m->message.question[0].type != AAA ) &&
                                (m->cache))
                        {
                            debug("Cache append: %s ----> %s\n", m->cname, m->ip );
                            //cache_name_append( m->cname, m->ip );
                        }
                        forward_resp=1;
                        break;
                    }
                case DNS_FMT_ERROR:
                    {
						/*Modify by sdh 2012-6-27，不给客户端返回失败的，这样客户端不会重新在DNS域名后加上.belkin后缀再请求，
						从而引起解释再次失败，打开网页会更慢*/
                        forward_resp=0;
						/*End by sdh 2012-6-27*/
                        break;
                    }
                case DNS_NAME_ERR:
                    {
                        if(m->message.header.flags.f.authorative)
                        {
                        	/*Modify by sdh 2012-6-27，不给客户端返回失败的，这样客户端不会重新在DNS域名后加上.belkin后缀再请求，
							从而引起解释再次失败，打开网页会更慢*/
                            forward_resp=0;
							/*End by sdh 2012-6-27*/
                        }
                        break;
                    }
                default:
                    /* do nothing */
                    break;
            }
            if (forward_resp)
            {
                //dns_write_packet( dns_any_sock, ptr->src_addr, ptr->src_port, m );
                dns_write_packet( dns_sock, ptr->src_addr, ptr->src_port, m );
                debug("Rsp Code: %d - Replying with answer from %s\n",(int)(m->message.header.flags.f.rcode),inet_ntoa( m->src_addr ));
                dns_request_list = dns_list_remove( dns_request_list, ptr );
				dns_request_cnt--;
            }
            else
            {
                debug("Rsp Code: %d - answer from %s dropped\n",(int)(m->message.header.flags.f.rcode),inet_ntoa( m->src_addr ));
            }
        }
        else
        {
            debug("Duplicate query\n");
        }
    }
    else
    {
#ifdef SENDTO_ALL_DNSSERVER_AT_ONCE
    	if(m->message.header.flags.f.question == 1)
		{
			debug("Duplicate response\n");
			return;
		}
#endif
        dns_handle_new_query( m );
    }

}
/*****************************************************************************/
int dns_main_loop()
{
    struct timeval tv;
    fd_set active_rfds;
    int retval;
    dns_request_t m;
    dns_request_t *ptr, *next;
    int purge_time = config.purge_time / 60;
    struct stat temp_stat ;
	
#ifndef SENDTO_ALL_DNSSERVER_AT_ONCE
    int next_server_index = 0;
#endif

    struct in_addr in;
	dns_request_cnt =0;

    while( !dns_main_quit )
    {

        /* set the one second time out */
        // tv.tv_sec = 1;
        //tv.tv_usec = 0;

		/*Modify by sdh, 2012-6-27, 适当减小超时时间，
			目的是想提高proxy处理dns_request_list队列任务的效率*/
		tv.tv_sec = 0;
		tv.tv_usec = 500000;
		/*End by sdh 2012-6-27*/

        /* now copy the main rfds in the active one as it gets modified by select*/
        active_rfds = rfds;

        retval = select( FD_SETSIZE, &active_rfds, NULL, NULL, &tv );

        if (retval)
        {
            /* data is now available */
            if (FD_ISSET(dns_sock,  &active_rfds))
            {
                dns_read_packet( dns_sock, &m );
                dns_handle_request( &m );
            }
            else
            {
                if (FD_ISSET(dns_any_sock,  &active_rfds))
                {
                    dns_read_packet( dns_any_sock, &m );
                    dns_handle_request( &m );
                }
            }
            m.time_pending = 0;
        }
        else
        {
            /* If resolv.conf has changed read it again*/
            if( !stat(config.config_file, &temp_stat))
            {
                if( conf_stat.st_ctime != temp_stat.st_ctime)
                {
                    conf_load(config.config_file);
                    conf_stat = temp_stat;
                }
            }
            /* select time out */
            ptr = dns_request_list;
            while( ptr )
            {
                next = ptr->next;
                /* Resend query to a new nameserver if response
                 * has not been received from the current nameserver */

                ptr->time_pending++;
                if( ptr->time_pending > DNS_TIMEOUT ){
                    debug("Request timed out\n");
                    /* send error back */
                    dns_construct_error_reply(ptr);

					/*Modify by sdh 2012-6-27，不给客户端返回失败的，这样客户端不会重新在DNS域名后加上.belkin后缀再请求，
						从而引起解释再次失败，打开网页会更慢*/
					#if 0
                    dns_write_packet( dns_sock, ptr->src_addr, ptr->src_port, ptr );
					#endif
                    dns_request_list = dns_list_remove( dns_request_list, ptr );
					dns_request_cnt --;
                }
                //if( ( ptr->time_pending % DNS_SERVER_TIMEOUT == 0) &&
                //        ( ptr->time_pending < DNS_TIMEOUT) )
                {
#ifdef SENDTO_ALL_DNSSERVER_AT_ONCE
					{
						/* send error back */
	                    debug("All Servers Tried: No Response\n");
	                    dns_construct_error_reply(ptr);

						/*Modify by sdh 2012-6-27，不给客户端返回失败的，这样客户端不会重新在DNS域名后加上.belkin后缀再请求，
						从而引起解释再次失败，打开网页会更慢*/
						#if 0
	                    dns_write_packet( dns_sock, ptr->src_addr, ptr->src_port, ptr );
						#endif
						/*End by sdh 2012-6-27*/
						
	                    dns_request_list = dns_list_remove( dns_request_list, ptr );
						dns_request_cnt --;
	                    debug("Sent a DNS error message to Client \n");
					}
#else
                    /* Send a request to the next nameserver */
                    next_server_index = ptr->time_pending/DNS_SERVER_TIMEOUT;
                    /* If already the maximum number of supported servers have
                       been tried out then remove the request from the list and
                       send a response  back
                       */
                    if(next_server_index >= MAX_NAME_SERVER ||
                            (config.name_server[next_server_index][0] == NULL ))
                            // (config.name_server[next_server_index] == NULL ))
                    {
                        /* send error back */
                        debug("All Servers Tried: No Response\n");
                        dns_construct_error_reply(ptr);
						#if 0
                        dns_write_packet( dns_sock, ptr->src_addr, ptr->src_port, ptr );
						#endif
                        dns_request_list = dns_list_remove( dns_request_list, ptr );
						dns_request_cnt --;
                        debug("Sent a DNS error message to Client \n");
                    }
                    else
                    {
                        /* Create the new server IP address and relay the query untouched */
                        inet_aton( config.name_server[next_server_index], &in );
                        /*!!!*/
                        debug("Didn't get a response from last server\n");
                        debug("Sending a new request to %s\n", config.name_server[next_server_index]);
                        /* Send the new request to the next name server */
                        dns_write_packet( dns_any_sock, in, PORT, ptr );
                    }
#endif
                }
                ptr = next;
            } /* while(ptr) */

            /* purge cache */
            purge_time--;
            if( !purge_time ){
                cache_purge( config.purge_time );
                purge_time = config.purge_time / 60;
            }

        } /* if (retval) */
    }
    return 0;
}

#if 0
/*****************************************************************************/
void debug_perror( char * msg ) {
    debug( "%s : %s\n" , msg , strerror(errno) );
}
/*****************************************************************************/
void debug(char *fmt, ...)
{
#define MAX_MESG_LEN 1024

    va_list args;
    char text[ MAX_MESG_LEN ];

    sprintf( text, "[ %d ]: ", getpid());
    va_start (args, fmt);
    vsnprintf( &text[strlen(text)], MAX_MESG_LEN - strlen(text), fmt, args);
    va_end (args);

    if( config.debug_file[0] ){
        FILE *fp;
        fp = fopen( config.debug_file, "a");
        if(!fp){
            syslog( LOG_ERR, "could not open log file %m" );
            return;
        }
        fprintf( fp, "%s", text);
        fclose(fp);
    }

    /** if not in daemon-mode stderr was not closed, use it. */
    if( ! config.daemon_mode ) {
        fprintf( stderr, "%s", text);
    }
}
#endif

/*****************************************************************************
 * print usage informations to stderr.
 *
 *****************************************************************************/
void usage(char * program , char * message ) {
    fprintf(stderr,"%s\n" , message );
    fprintf(stderr,"usage : %s [-c <config-file>] [-d] [-h] [-P]\n", program );
    fprintf(stderr,"\t-c <config-file>\tread configuration from <config-file>\n");
    fprintf(stderr,"\t-d \t\trun in debug (=non-daemon) mode.\n");
    fprintf(stderr,"\t-P \t\tprint configuration on stdout and exit.\n");
    fprintf(stderr,"\t-h \t\tthis message.\n");
}
/*****************************************************************************
 * get commandline options.
 *
 * @return 0 on success, < 0 on error.
 *****************************************************************************/
int get_options( int argc, char ** argv )
{
    int c = 0;
    int not_daemon = 0;
    int want_printout = 0;
    char * progname = argv[0];

    conf_defaults();

    while( (c = getopt( argc, argv, "c:dhP")) != -1 ) {
        switch(c) {
            case 'c':
                conf_load(optarg);
                if(config.config_file)
                    stat(config.config_file, &conf_stat);
                break;
            case 'd':
                not_daemon = 1;
                break;
            case 'h':
                usage(progname,"");
                return -1;
            case 'P':
                want_printout = 1;
                break;
            default:
                usage(progname,"");
                return -1;
        }
    }

    /** unset daemon-mode if -d was given. */
    if( not_daemon ) {
        config.daemon_mode = 0;
    }

    if( want_printout ) {
        conf_print();
        exit(0);
    }
    return 0;
}
/*****************************************************************************/
void sig_hup (int signo)
{
    signal(SIGHUP, sig_hup); /* set this for the next sighup */
    conf_load (config.config_file);
}

void sig_USR1 (int signo)
	{
		WarningEnable=1;
		//system("echo =========dproxy get signal usr1=======\n");
	}

void sig_USR2 (int signo)
	{
		WarningEnable=0;
		//system("echo =========dproxy get signal usr2=======\n");
	}

void sig_SIGINT(int signo)
	{
		//ppp是按需连接，且处于idle(断开)状态
		pppdemand_idle=1;
	}

void sig_SIGURG(int signo)
	{
		//ppp是按需连接，且处于连接状态
		pppdemand_idle=0;
	}
/*****************************************************************************/
int main(int argc, char **argv)
{
    mallopt(M_MMAP_THRESHOLD,4096);

    /* get commandline options, load config if needed. */
    if(get_options( argc, argv ) < 0 ) {
        exit(1);
    }

	//sigaction用来替代除了SIGKILL和SIGSTOP之外的其它任何信号编码
	struct sigaction actint;
	actint.sa_handler = sig_SIGINT;
	sigemptyset(&actint.sa_mask);
	actint.sa_flags = 0;
	sigaction(SIGINT, &actint, 0);

	struct sigaction acturg;
	acturg.sa_handler = sig_SIGURG;
	sigemptyset(&acturg.sa_mask);
	acturg.sa_flags = 0;
	sigaction(SIGURG, &acturg, 0);

    signal(SIGHUP, sig_hup);
    signal(SIGUSR1, sig_USR1);
    signal(SIGUSR2, sig_USR2);

	//注册模块
	if (CMM_INIT_SUCCESS != CMM_Init())
	{
		printf("[stat]:%s:%d:CMM_Init failed , existing ...\n",
		__FUNCTION__,__LINE__);
		CMM_Free();
		exit(-1);			
	}
	
    dns_init();

    if (config.daemon_mode) {
        /* Standard fork and background code */
        switch (fork()) {
            case -1:	/* Oh shit, something went wrong */
                debug_perror("fork");
                exit(-1);
            case 0:	/* Child: close off stdout, stdin and stderr */
                close(0);
                close(1);
                close(2);
                break;
            default:	/* Parent: Just exit */
                exit(0);
        }
    }

    dns_main_loop();

	//注销模块
	CMM_Free();
    return 0;
}


