/*
 * server.c - mDNS Proxy, proxy server core
 */

/*
 * Copyright (c) 2000 Japan Network Information Center.  All rights reserved.
 *  
 * By using this file, you agree to the terms and conditions set forth bellow.
 * 
 * 			LICENSE TERMS AND CONDITIONS 
 * 
 * The following License Terms and Conditions apply, unless a different
 * license is obtained from Japan Network Information Center ("JPNIC"),
 * a Japanese association, Fuundo Bldg., 1-2 Kanda Ogawamachi, Chiyoda-ku,
 * Tokyo, Japan.
 * 
 * 1. Use, Modification and Redistribution (including distribution of any
 *    modified or derived work) in source and/or binary forms is permitted
 *    under this License Terms and Conditions.
 * 
 * 2. Redistribution of source code must retain the copyright notices as they
 *    appear in each source code file, this License Terms and Conditions.
 * 
 * 3. Redistribution in binary form must reproduce the Copyright Notice,
 *    this License Terms and Conditions, in the documentation and/or other
 *    materials provided with the distribution.  For the purposes of binary
 *    distribution the "Copyright Notice" refers to the following language:
 *    "Copyright (c) Japan Network Information Center.  All rights reserved."
 * 
 * 4. Neither the name of JPNIC may be used to endorse or promote products
 *    derived from this Software without specific prior written approval of
 *    JPNIC.
 * 
 * 5. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY JPNIC
 *    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 *    PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL JPNIC BE LIABLE
 *    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 *    BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *    WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 *    OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 *    ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 * 
 * 6. Indemnification by Licensee
 *    Any person or entities using and/or redistributing this Software under
 *    this License Terms and Conditions shall defend indemnify and hold
 *    harmless JPNIC from and against any and all judgements damages,
 *    expenses, settlement liabilities, cost and other liabilities of any
 *    kind as a result of use and redistribution of this Software or any
 *    claim, suite, action, litigation or proceeding by any third party
 *    arising out of or relates to this License Terms and Conditions.
 * 
 * 7. Governing Law, Jurisdiction and Venue
 *    This License Terms and Conditions shall be governed by and and
 *    construed in accordance with the law of Japan. Any person or entities
 *    using and/or redistributing this Software under this License Terms and
 *    Conditions hereby agrees and consent to the personal and exclusive
 *    jurisdiction and venue of Tokyo District Court of Japan.
 */

#ifndef lint
static char *rcsid = "$Id: server.c,v 1.1 2001/06/09 00:30:35 tale Exp $";
#endif

#include <config.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#ifdef  HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef  WIN32
#include <windows.h>
#include <winsock.h>
#else   /* for normal systems */
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#include <netinet/in.h>
#include <errno.h>
#endif

#include "mdnsproxy.h"

#ifdef  WIN32
#define close(s)    closesocket(s)
#define EWOULDBLOCK WSAEWOULDBLOCK
#endif

#ifndef max
#define max(x, y)   ((x) > (y) ? (x) : (y))
#endif

/*
 * Status of receiving data or accept a TCP connection.
 */
#define SUCCESS  0         /* succeeded        */ 
#define FAILURE  1         /* failed           */
#define DENIED   2         /* denied           */

/*
 * send buffer for TCP
 *      hold sending data when sending socket blocked
 */

typedef struct _SNDBUF {
    struct _SNDBUF      *prev ;
    struct _SNDBUF      *next ;
    int                 leng  ;     /* data length in the buffer    */
    int                 sent  ;     /* data have been sent          */
    int                 size  ;     /* size of this bufefr          */
    u_char              buff[1] ;   /* 'size' array follows         */
} SNDREC, *SNDPTR ;

/*
 * recv buffer for TCP
 *      hold incomplete message
 */

typedef struct _RCVBUF {
    int     stat  ;         /* what data receiving now      */
    int     leng  ;         /* length of the message        */
    int     recv  ;         /* message have been received   */
    int     size  ;         /* size of this buffer          */
    u_char  *buff ;         /* points 'size' array of char  */
} RCVREC, *RCVPTR ;

#define RCV_STAT_LEN1   0   /* waiting 1st byte of length   */
#define RCV_STAT_LEN2   1   /* waiting 2nd byte of length   */
#define RCV_STAT_DATA   2   /* waiting message data         */

/*
 * transport control block
 *      is used to handle pending recv/send data of the socket
 */

typedef struct _NETREC {
    struct _NETREC      *prev ;
    struct _NETREC      *next ;
    int                 sock  ;     /* socket to do network I/O */
    int                 proto ;     /* TCP or UDP               */
    int                 type  ;     /* see below                */
    struct sockaddr     peer  ;     /* peer of this transoport  */
    SNDREC              send  ;     /* pending send data (TPC)  */
    RCVREC              recv  ;     /* pending recv message     */
} NETREC, *NETPTR ;

#define NET_LISTEN  1       /* is proxy's listening socket  */
#define NET_CLIENT  2       /* is connection from client    */
#define NET_SERVER  3       /* is transport to server       */

static  NETREC  listNet = { 0 } ;   /* list of transports   */

/*
 * allocate/dispose buffer for SND/RCV buffer managements
 *      simply mapped to malloc/free, but may be
 *      re-maped to spceific function if speed required
 */

#define xalloc(x)   malloc((x))
#define xfree(p)    free((p))

/*
 * Whether to log denied access from client.
 */
static int	logOnDenied = 0;

/*
 * transientError - utility for handling "soft" errors
 */

static BOOL	transientError(int eno)
{
    if (
#ifdef EAGAIN
	eno == EAGAIN ||
#endif
#ifdef EWOULDBLOCK
	eno == EWOULDBLOCK ||
#endif
#ifdef EINTR
	eno == EINTR ||
#endif
	eno == 0)
	return TRUE ;
    else
	return FALSE ;
}

/*
 * addrEq, addrFmt - utilities for handling address
 */

static  BOOL    addrEq(struct sockaddr *a1, struct sockaddr *a2)
{
    struct sockaddr_in  *ip1 = (struct sockaddr_in *) a1 ;
    struct sockaddr_in  *ip2 = (struct sockaddr_in *) a2 ;
    
    if (ip1->sin_addr.s_addr != ip2->sin_addr.s_addr) {
        return FALSE ;
    }
    if (ip1->sin_port != ip2->sin_port) {
        return FALSE ;
    }
    return TRUE ;
}

static  u_char  fmtbuff[64] ;

static  u_char  *addrFmt(struct sockaddr *addr, int proto)
{
    struct sockaddr_in  *iaddr = (struct sockaddr_in *) addr ;
    u_char  *ap ;
    u_char  *pp ;

    ap = (u_char *) &iaddr->sin_addr ;
    pp = (u_char *) &iaddr->sin_port ;
    
    sprintf(fmtbuff, "%s:%d.%d.%d.%d:%d",
        (proto == SOCK_STREAM ? "TCP" : "UDP"),
        (ap[0] & 0xff), (ap[1] & 0xff), (ap[2] & 0xff), (ap[3] & 0xff),
	((pp[0] & 0xff) * 256 + (pp[1] & 0xff)) ) ;

    return fmtbuff ;
}

/*
 * netCreate, netDispose - create/dispose transport control block
 */
 
#define SZRCVBUF    1024

static  NETPTR  netCreate(int sock, struct sockaddr *peer, int proto, int type)
{
    NETPTR  pNet, prev, next ;
    u_char  *pBuf ;
    
    if (listNet.prev == NULL || listNet.next == NULL) {
        WARN("netCreate - transport list is not initialized\n") ;
	listNet.prev = &listNet ;
	listNet.next = &listNet ;
    }
    
    pNet = (NETPTR)   xalloc(sizeof(NETREC)) ;
    pBuf = (u_char *) xalloc(SZRCVBUF) ;
    
    if (pNet == NULL || pBuf == NULL) {
        WARN("netCreate - cannot allocate buffer\n") ;
	if (pNet != NULL) xfree(pNet) ;
	if (pBuf != NULL) xfree(pBuf) ;
	return NULL ;
    }
    memset(pNet, 0, sizeof(NETREC)) ;

    pNet->sock  = sock  ;
    pNet->proto = proto ;
    pNet->type  = type  ;
    
    if (peer != NULL) {
        memcpy(&pNet->peer, peer, sizeof(struct sockaddr)) ;
    }

    pNet->send.prev = &pNet->send   ;
    pNet->send.next = &pNet->send   ;
    pNet->recv.stat = RCV_STAT_LEN1 ;
    pNet->recv.leng = 0             ;
    pNet->recv.recv = 0             ;
    pNet->recv.size = SZRCVBUF      ;
    pNet->recv.buff = pBuf         ;
    
    prev = listNet.prev ;
    next = prev->next   ;

    prev->next = pNet ;
    next->prev = pNet ;
    pNet->prev = prev ;
    pNet->next = next ;

    return pNet ;
}

static  void    netDispose(NETPTR pNet)
{
    NETPTR  p    ;
    SNDPTR  pSnd ;
    
    if (listNet.prev == NULL || listNet.next == NULL) {
        WARN("netDispose - transport list is not initialized\n") ;
	listNet.prev = &listNet ;
	listNet.next = &listNet ;
    }

    /*
     * unlink from transport list
     */
     
    for (p = listNet.next ; p != &listNet ; p = p->next) {
        if (p == pNet) {
	    break ;
	}
    }
    if (p == pNet) {        /* safe to unlink it */
        pNet->prev->next = pNet->next ;
	pNet->next->prev = pNet->prev ;
    }

    /*
     * dispose control block resources
     */
     
    if (pNet->send.prev == NULL || pNet->send.next == NULL) {
        WARN("netDispose - un-initialized SNDREC\n") ;
        pNet->send.prev = &pNet->send ;
        pNet->send.next = &pNet->send ;
    }
    while ((pSnd = pNet->send.next) != &pNet->send) {
	pSnd->prev->next = pSnd->next ;
	pSnd->next->prev = pSnd->prev ;
	xfree(pSnd) ;
    }
    if (pNet->recv.buff != NULL) {
        xfree(pNet->recv.buff) ;
    }
    close(pNet->sock) ;
    xfree(pNet) ;
}

/*
 * netExpand - expand receive buffer
 */

static  BOOL    netExpand(NETPTR p)
{
    int     len ;
    u_char  *np ;
    
    if (p->recv.size > p->recv.leng) {
        return TRUE ;
    }
    
    len = 1024 * ((p->recv.leng + 1023) / 1024) ;
    
    TRACE("netExpand %d -> %d\n", p->recv.size, len) ;

    if ((np = xalloc(len)) == NULL) {
        WARN("netExpand - cannot allocate memory\n") ;
        return FALSE ;
    }
    if (p->recv.recv > 0) {
        memcpy(np, p->recv.buff, p->recv.recv) ;
    }
    xfree(p->recv.buff) ;
    p->recv.buff = np   ;
    p->recv.size = len  ;
    
    return TRUE ;
}

/*
 * tcpSend, tcpQueue, tcpFlush - send message over stream socket
 *
 *      tcpSend  - send message over TCP socket
 *      tcpQueue - en-queue message if stream blocked
 *      tcpFlush - flush pending messages
 */
 
static  int     tcpSend(int sock, u_char *msg, int len)
{
    int     n, cnt = 0 ;
    
    while (len > 0) {
        if ((n = send(sock, msg, len, 0)) > 0) {
	    msg += n ;
	    len -= n ;
	    cnt += n ;
	    continue ;
	}
	if (!transientError(errno)) {
	    WARN("tcpSend - send error %d\n", errno) ;
	    return -1 ;
	}
	break ;
    }
    return cnt ;
}

static  BOOL    tcpQueue(NETPTR p, u_char *msg, int len)
{
    SNDPTR  sp, prev, next ;

    if ((sp = (SNDPTR) xalloc(sizeof(SNDREC) + len)) == NULL) {
        WARN("tcpQueue - cannot allocate buffer\n") ;
	return FALSE ;
    }

    sp->sent = 0   ;
    sp->leng = len ;
    sp->size = len ;
    memcpy(sp->buff, msg, len) ;
    
    prev = p->send.prev ;
    next = prev->next   ;
    prev->next = sp ;
    next->prev = sp ;
    sp->next = next ;
    sp->prev = prev ;

    return TRUE ;
}

static  BOOL    tcpFlush(NETPTR p)
{
    SNDPTR  sp ;
    int     n, len ;
    
    if (p->proto != SOCK_STREAM) {
        WARN("tcpFlush - flushing on non-stream socket\n") ;
	return FALSE ;
    }
    if (p->send.prev == NULL || p->send.prev == NULL) {
        WARN("tcpFlush - send buffer is not initialized\n") ;
        p->send.prev = &p->send ;
        p->send.next = &p->send ;
    }

    while ((sp = p->send.next) != &p->send) {

        /*
	 * try to send data
	 */

        if ((len = sp->leng - sp->sent) > 0) {
	    if ((n = tcpSend(p->sock, &sp->buff[sp->sent], len)) < 0) {
	        WARN("tcpFlush - send error %d\n", errno) ;
		return FALSE ;
	    }
            if (n == 0) {
	        TRACE("tcpFlush - blocked\n") ;
	        return TRUE ;
	    }
	    sp->sent += n ;
	}
	
        /*
	 * if no more data in send buffer, unlink and free
	 */

        if ((len = sp->leng - sp->sent) <= 0) {
	    sp->prev->next = sp->next ;
	    sp->next->prev = sp->prev ;
	    xfree(sp) ;
        }
    }
    return TRUE ;
}

/*
 * Server Control Variables
 */

static  BOOL    servActive = FALSE ;

static  NETPTR  listenTcp = NULL ;  /* proxy's listening socket */
static  NETPTR  listenUdp = NULL ;  /* proxy's listening socket */

static  struct sockaddr serverDefaultAddr  = { 0 } ;
static  BOOL            serverRestrictPort = FALSE ;

/*
 * server_done - finalize server
 *
 *  is also used when 'server_init' failed in initialization sequence
 *
 *  using module level utilities
 *      
 *      netDispose      dispose transport control block
 */

void    server_done(void)
{
    NETPTR  p ;
    
    if (listNet.prev == NULL || listNet.next == NULL) {
        listNet.prev = &listNet ;
	listNet.next = &listNet ;
    }
    while ((p = listNet.next) != &listNet) {
        netDispose(p) ;
    }
}

/*
 * server_init - initialize server
 *
 *  using sub-functions
 *
 *      initTcp     create socket to listening TCP connection
 *      initUdp     create socket to listening UDP message
 *
 *  and also using module level utilities
 *      
 *      netCreate       create transport control block
 *
 *  also use 'server_done' to cleanup resources
 */

static  NETPTR  initTcp(void)
{
    NETPTR  p ;
    int     sock ;
    int     one  = 1 ;
    struct  sockaddr addr ;
    
    if (config_query_listen(&addr) != TRUE) {
        WARN("initTcp - no listen address\n") ;
	return NULL ;
    }

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        WARN("initTcp - cannot create TCP socket\n") ;
	return NULL ;
    }
    
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) ;
    
    if (bind(sock, &addr, sizeof(addr)) < 0) {
        WARN("initTcp - cannot bind TCP socket\n") ;
	close(sock) ;
	return NULL ;
    }
    if (listen(sock, 5) < 0) {
        WARN("initTcp - cannot listen on TCP socket\n") ;
	close(sock) ;
	return NULL ;
    }
    if ((p = netCreate(sock, &addr, SOCK_STREAM, NET_LISTEN)) == NULL) {
        WARN("initTcp - cannot create control block\n") ;
	close(sock) ;
	return NULL ;
    }
    return p ;
}

static  NETPTR  initUdp(void)
{
    NETPTR  p ;
    int     sock ;
    int     one  = 1 ;
    struct  sockaddr addr ;
    
    if (config_query_listen(&addr) != TRUE) {
        WARN("initUdp - no listen address\n") ;
	return NULL ;
    }

    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        WARN("initUdp - cannot create UDP socket\n") ;
	return NULL ;
    }

    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) ;

    if (bind(sock, &addr, sizeof(addr)) < 0) {
        WARN("initUdp- cannot bind UDP socket\n") ;
	close(sock) ;
	return NULL ;
    }
    if ((p = netCreate(sock, &addr, SOCK_DGRAM, NET_LISTEN)) == NULL) {
        WARN("initUdp - cannot create control block\n") ;
	close(sock) ;
	return NULL ;
    }
    return p ;
}

/*
 * server_init - initialize proxy server
 */
 
BOOL    server_init(int ac, char *av[])
{
    /*
     * initialize transport list
     */
     
    listNet.prev = &listNet ;
    listNet.next = &listNet ;

    /*
     * setup transports
     */
     
    listenTcp = initTcp() ;
    listenUdp = initUdp() ;

    if (listenTcp == NULL || listenUdp == NULL) {
        WARN("server_init - cannot create proxy's listening port\n") ;
	server_done() ;
	return FALSE ;
    }
    
    if (config_query_forward(&serverDefaultAddr) != TRUE) {
        WARN("server_init - no DNS server address\n") ;
	server_done() ;
	return FALSE ;
    }
    if (config_query_restrict(&serverRestrictPort) != TRUE) {
        WARN("server_init - cannot query 'serverRestrictPort' flag\n") ;
	server_done() ;
	return FALSE ;
    }
    if (config_query_log_on_denied(&logOnDenied) != TRUE) {
        WARN("syntax error at log-on-denied line\n") ;
	server_done() ;
	return FALSE ;
    }
    
    /*
     * initialize translator
     */
     
    if (translate_initialize() != TRUE) {
	WARN("server_init - translation configuration failed\n") ;
	server_done() ;
	return FALSE ;
    }

    /*
     * initialize ACL.
     */
     
    if (acl_initialize() != TRUE) {
	WARN("server_init - access list configuration failed\n") ;
	server_done() ;
	return FALSE ;
    }

    /*
     * now server is ready, turn on active flags now
     */

    servActive = TRUE ;
    
    return TRUE ;
}

/*
 * server_stop - request to stop server
 *
 *      simply turn off 'servActive' control flag.
 *      then 'server_loop' terminate 
 */

void    server_stop(void)
{
    servActive = FALSE ;
}

/*
 * server_loop - proxy server's message loop
 *
 *  using sub-funcstions
 *
 *      setRdFds        listup sockets to check read  ready
 *      setWtFds        listup sockets to check write ready
 *      sockAccept      accept connection from client
 *      sockRecvTcp     receive message over TCP
 *      sockRecvUdp     receive message over UDP
 *      sockDispatch    dispatch on ready sockets
 *      sockValidate    validate sockets
 *      timerDispatch   entry of timer processing
 *
 *  and also using module level utilities
 *
 *      netCreate       create  transport control block
 *      netDispose      dispose transport control block
 *      tcpFlush        send pending message
 *      addrFmt         formatting address (& proto)
 */

/*
 * setRdFds, setWtFds - listup socket to check if ready
 */
 
static  int     setRdFds(fd_set *rfds)
{
    int     maxfd = 0 ;
    NETPTR  p ;
    
    /*
     * listup sockets to check read ready
     */
    
    if (listNet.prev == NULL || listNet.next == NULL) {
        WARN("setRdFds - listNet is not initialized\n") ;
	listNet.prev = &listNet ;
	listNet.next = &listNet ;
    }
    for (p = listNet.next ; p != &listNet ; p = p->next) {
        FD_SET(p->sock, rfds) ;
	maxfd = max(maxfd, p->sock) ;
    }
    return maxfd ;
}

static  int     setWtFds(fd_set *wfds)
{
    int     maxfd = 0 ;
    NETPTR  p ;
    
    /*
     * listup sockets to check read ready
     */
    
    if (listNet.prev == NULL || listNet.next == NULL) {
        WARN("setRdFds - listNet is not initialized\n") ;
	listNet.prev = &listNet ;
	listNet.next = &listNet ;
    }
    for (p = listNet.next ; p != &listNet ; p = p->next) {
        if (p->send.prev == NULL || p->send.next == NULL) {
	    WARN("setWtFds - send buffer is not initialized\n") ;
	    p->send.prev = &p->send ;
	    p->send.next = &p->send ;
	}
        if (p->send.next != &p->send) {
            FD_SET(p->sock, wfds) ;
	    maxfd = max(maxfd, p->sock) ;
	}
    }
    return maxfd ;
}

/*
 * sockAccept - accept connection from client
 */
 
static  int    sockAccept(NETPTR p)
{
    NETPTR  np ;
    int     ns, addrlen ;
    struct sockaddr     addr ;
    struct sockaddr     peer ;
    
    memset(&addr, 0, sizeof(addr)) ;
    memset(&peer, 0, sizeof(peer)) ;
    
    addrlen = sizeof(addr) ;

    if ((ns = accept(p->sock, &addr, &addrlen)) < 0) {
        WARN("sockAccept - cannot accept connection %d\n", errno) ;
	return FAILURE ;
    }
    
    addrlen = sizeof(peer) ;
    getpeername(ns, &peer, &addrlen) ;

    if (!acl_test(&peer)) {
	if (logOnDenied) {
	    WARN("sockAccept - deny access from %s\n",
		addrFmt(&peer, SOCK_STREAM));
	}
	shutdown(ns, 2) ;
	close(ns) ;
	return DENIED ;
    }
    
    if ((np = netCreate(ns, &peer, SOCK_STREAM, NET_CLIENT)) == NULL) {
        WARN("sockAccept - cannot create control block\n") ;
	close(ns) ;
	return FAILURE ;
    }

    TRACE("sockAccept - accept connection from %s on socket %d\n", addrFmt(&peer, SOCK_STREAM), ns) ;

    return SUCCESS ;
}

/*
 * sockRecvTcp, sockRecvUdp - receive message
 *
 *  when message complete, call 'notify_message' to
 *  notify message arrival.
 */
 
static  int    sockRecvTcp(NETPTR p)
{
    int     n, len ;
    u_char  buff[2] ;
    u_char  *bp ;
    
    /*
     * validate transport
     */

    if (p == NULL || p->proto != SOCK_STREAM) {
        WARN("sockRecvTcp - bad parameter\n") ;
	return FAILURE ;
    }
    if (p->recv.buff == NULL || p->recv.size == 0) {
        WARN("sockRecvTcp - no receiver buffer\n") ;
	return FAILURE ;
    }

    /*
     * when receiving message over TCP, constuct state machine with
     * RCVBUF's status.
     */

    if (p->recv.stat == RCV_STAT_LEN1) {
        if ((n = recv(p->sock, buff, 2, 0)) <= 0) {
	    if (errno == EWOULDBLOCK) {
	        return SUCCESS ;
	    }
	    WARN("sockRecvTcp - recv error %d on socket %d, STAT_LEN1\n", errno, p->sock) ;
	    return FAILURE ;
	}
	if (n == 1) {
	    p->recv.leng = ((int) (buff[0] & 0xff)) * 256 ;
	    p->recv.stat = RCV_STAT_LEN2 ;
	    return SUCCESS  ;   /* blocked, try later */
	}
	p->recv.leng = ((int) (buff[0] & 0xff)) * 256 + ((int) (buff[1] & 0xff)) ;
	p->recv.stat = RCV_STAT_DATA ;
	/* then fall through */
    }

    if (p->recv.stat == RCV_STAT_LEN2) {
        if ((n = recv(p->sock, buff, 1, 0)) <= 0) {
	    if (errno == EWOULDBLOCK) {
	        return SUCCESS ;
	    }
	    WARN("sockRecvTcp - recv error %d on socket %d, STAT_LEN2\n", errno, p->sock) ;
	    return FAILURE ;
	}
	p->recv.leng += ((int) (buff[0] & 0xff)) ;
	p->recv.stat = RCV_STAT_DATA ;
	/* then fall through */
    }

    if (p->recv.stat == RCV_STAT_DATA) {
        if (p->recv.size < p->recv.leng) {
            if (netExpand(p) != TRUE) {
	        WARN("sockRecvTcp - cannot expand recv buffer\n") ;
		return FAILURE ;
	    }
        }

	bp  = &p->recv.buff[p->recv.recv] ;
	len = p->recv.leng - p->recv.recv ;

	if ((n = recv(p->sock, bp, len, 0)) <= 0) {
	    if (errno == EWOULDBLOCK) {
	        return SUCCESS ;
	    }
	    WARN("sockRecvTcp - recv error %d on socket %d, "
		"STAT_DATA %d (%d/%d)\n", 
		errno, p->sock, len, p->recv.recv, p->recv.leng) ;
	    return FAILURE ;
	}
	if ((p->recv.recv += n) < p->recv.leng) {
	    return SUCCESS ;       /* still in-complete */
	}
	
	/*
	 * message complete, notify it
	 */
	 
	notify_message(&p->peer, SOCK_STREAM, p->recv.buff, p->recv.leng) ;

        /*
	 * reset recv buffer
	 */
	 
	p->recv.stat = RCV_STAT_LEN1 ;
	p->recv.leng = 0 ;
	p->recv.recv = 0 ;

	return SUCCESS ;
    }
    WARN("sockRecvTcp - something wrong\n") ;
    return FAILURE ;
}

static  int    sockRecvUdp(NETPTR p)
{
    int             n, fromlen ;
    struct sockaddr fromaddr   ;
    
    /*
     * validate transport
     */

    if (p == NULL || p->proto != SOCK_DGRAM) {
        WARN("sockRecvUdp - bad parameter\n") ;
	return FAILURE ;
    }
    if (p->recv.buff == NULL || p->recv.size == 0) {
        WARN("sockRecvUdp - no receiver buffer\n") ;
	return FAILURE ;
    }

    /*
     * receive over UDP
     */
     
    fromlen = sizeof(fromaddr) ;
    
    n = recvfrom(p->sock, p->recv.buff, p->recv.size, 0, &fromaddr, &fromlen) ;

    if (n == 0) {
	WARN("sockRecvUdp - no data\n") ;
	return SUCCESS ;
    } else if (n < 0) {
	if (transientError(errno))
	    return SUCCESS ;
	switch (errno) {
#ifdef ECONNREFUSED
	case ECONNREFUSED:
#endif
#ifdef ENETUNREACH
	case ENETUNREACH:
#endif
#ifdef EHOSTUNREACH
	case EHOSTUNREACH:
#endif
	    WARN("sockRecvUdp - recv error %d (ignored)\n", errno) ;
	    return SUCCESS ;
	default:
	    WARN("sockRecvUdp - recv error %d\n", errno) ;
	    return FAILURE ;
	}
    }

    if (!addrEq(&fromaddr, &serverDefaultAddr) && !acl_test(&fromaddr)) {
	if (logOnDenied) {
	    WARN("sockRecvUdp - deny access from %s\n", 
		addrFmt(&fromaddr, SOCK_DGRAM));
	}
	return DENIED ;
    }
                
    /*
     * notify message arrival
     */

    notify_message(&fromaddr, SOCK_DGRAM, p->recv.buff, n) ;

    return SUCCESS ;
}

/*
 * sockDispatch - process on ready sockets
 */
 
static  void    sockDispatch(int nfds, fd_set *rfds, fd_set *wfds)
{
    NETPTR  p, np ;
    int     stat ;
    
    /*
     * process on ready sockets
     */
     
    if (listNet.prev == NULL || listNet.next == NULL) {
        WARN("sockDispatch - listNet is not initialized\n") ;
        listNet.prev = &listNet ;
        listNet.next = &listNet ;
    }
    for (p = listNet.next ; p != &listNet ; p = np) {

        np = p->next ;
	stat = SUCCESS ;  /* assume no problem */
        
        /*
	 * if receiving socket ready, then
	 *
	 *      LISTEN TCP      accept connection
	 *      LISTEN UDP      receive message
	 *      CLIENT          receive message
	 *      SERVER          receive message
	 */
	 
	if (FD_ISSET(p->sock, rfds)) {
	    if (p->type != NET_LISTEN) {
	        if (p->proto == SOCK_STREAM) {
		    stat = sockRecvTcp(p) ;
		} else {
		    stat = sockRecvUdp(p) ;
		}
            } else if (p->proto == SOCK_DGRAM) {
	        stat = sockRecvUdp(p) ;
	    } else {        /* connect request on listening TCP socket */
	        stat = sockAccept(p) ;
	    }
	}

        /*
	 * if send socket ready, then send pending message
	 *
	 *      don't check transport type, protocol, but
	 *      this happens only for TCP socket to CLIENT/SERVER
	 */
	 
	if (stat == SUCCESS && FD_ISSET(p->sock, wfds)) {
	    tcpFlush(p) ;
	}
	
	/*
	 * if something wrong on socket, dispose it
	 */

        if (stat == FAILURE) {
            if (p->type == NET_LISTEN || p->proto == SOCK_DGRAM) {
    	        WARN("sockDispatch - error on listening socket\n") ;
	        server_stop() ;
	    } else {
	        WARN("sockDispatch - error on socket %d\n", p->sock) ;
	    }
	    netDispose(p) ;
        }	
    }
}

/*
 * sockValidate - validate socket when select error
 */
 
static  void    sockValidate(int nfds, fd_set *rfds, fd_set *wfds)
{
    NETPTR  p, np ;
    fd_set  fds ;
    struct timeval  tm ;

    TRACE("sockValidate\n") ;

    FD_ZERO(&fds) ;
    tm.tv_sec  = 0 ;
    tm.tv_usec = 0 ;
    
    if (listNet.prev == NULL || listNet.next == NULL) {
        WARN("sockValidate - listNet is not initialized\n") ;
        listNet.prev = &listNet ;
        listNet.next = &listNet ;
    }

    for (p = listNet.next ; p != &listNet ; p = np) {

        np = p->next ;
        
        /*
	 * check if socket is still available
	 */

        FD_SET(p->sock, &fds) ;

        if (!FD_ISSET(p->sock, rfds) && !FD_ISSET(p->sock, wfds)) {
	    FD_CLR(p->sock, &fds) ;
	    continue ;
	}
	if (select(p->sock + 1, &fds, NULL, NULL, &tm) >= 0) {
	    FD_CLR(p->sock, &fds) ;
	    continue ;
	}
        FD_CLR(p->sock, &fds) ;

        /*
	 * something wrong on socket
	 */
	 
        if (p->type == NET_LISTEN) {
	    WARN("sockValidate - closed listening socket\n") ;
	    server_stop() ;
	} else {
	    WARN("sockValidate - closed socket %d\n", p->sock) ;
	}
	netDispose(p) ;
    }
}

/*
 * timerDispatch - entry of timer processing
 */
 
static  time_t  timeLast = 0 ;      /* last time of timer process   */
static  time_t  timeWait = 10 ;     /* wait till next timer process */

static  void    timerDispatch(void)
{
    time_t  t = time(NULL) ;
    
    /*
     * do timer process every 'timeWait' interval
     */

    if ((t - timeLast) < timeWait) {
        return ;
    }

    /*
     * call timer processes here
     */
    
    notify_timer() ;
    
    /*
     * update timer
     */
     
    timeLast = t ;
}

/*
 * server_loop - proxy's main, select loop
 */
 
void    server_loop(void)
{
    struct timeval  tm ;
    fd_set  rfds, wfds ;
    int     maxrfd, maxwfd ;
    int     n, nfds ;

    while (servActive) {

        FD_ZERO(&rfds) ;
	FD_ZERO(&wfds) ;
	
	maxrfd = setRdFds(&rfds) ;
	maxwfd = setWtFds(&wfds) ;
	nfds = max(maxrfd, maxwfd) + 1 ;
	
	tm.tv_sec  = 10 ;
	tm.tv_usec = 0 ;
	
	if (maxwfd == 0) {
	    n = select(nfds, &rfds, NULL, NULL, &tm) ;
	} else {
	    n = select(nfds, &rfds, &wfds, NULL, &tm) ;
	}

	if (n > 0) {
	    sockDispatch(nfds, &rfds, &wfds) ;
	} else if (n < 0) {
	    if (!transientError(errno)) {
		sockValidate(nfds, &rfds, &wfds) ;
	    }
	}
        timerDispatch() ;
#ifdef  DEBUG
        fflush(stdout) ;
	fflush(stderr) ;
#endif
	log_turnover();
    }
}

/*
 * Server's entries for send message (forward or response)
 *
 *      server_forward      forward request to DNS server
 *      server_response     send back response to client
 *
 * using sub-functions,
 *
 *      openTcp      open TCP connection to the upstream server
 *      openUdp      open UDP transport  to the upstream server
 *      getServer    get transport to the upstream server
 *      getClient    get transport to responding client 
 *      sendTcp      send message via TCP
 *      sendUdp      send message via UDP
 *
 * and also using module level utilities,
 *      
 *      netCreate   create transport control block
 *      tcpSend     send message over TCP socket
 *      tcpQueue    enqueue if TCP blocked
 *      addrEq      check if addresses are same
 *      addrFmt     formatting address (& proto)
 */

/*
 * openTcp, openUdp - create TCP/UDP transport to DNS server
 */
 
static  NETPTR  openTcp(struct sockaddr *to)
{
    int     sock ;
    NETPTR  p    ;
    
    TRACE("openTcp - creating transport to %s\n", addrFmt(to, SOCK_STREAM)) ;
    
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        WARN("openTcp - cannote create socket %d\n", errno) ;
	return NULL ;
    }
    if (connect(sock, to, sizeof(struct sockaddr)) < 0) {
        WARN("openTcp - cannot connect to server %d\n", errno) ;
        close(sock) ;
	return NULL ;
    }
    if ((p = netCreate(sock, to, SOCK_STREAM, NET_SERVER)) == NULL) {
        WARN("openTcp - cannot create control block\n") ;
	close(sock) ;
	return NULL ;
    }
    return p ;
}

static  NETPTR  openUdp(struct sockaddr *to)
{
    int     sock ;
    NETPTR  p    ;
    struct sockaddr_in  saddr ;     /* source port */
    
    TRACE("openUdp - creating transport to %s\n", addrFmt(to, SOCK_DGRAM)) ;
    
    memset(&saddr, 0, sizeof(saddr)) ;
    saddr.sin_family = AF_INET                ;
    saddr.sin_addr.s_addr = htonl(INADDR_ANY) ;
    saddr.sin_port = htons(0)                 ;
    
    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        WARN("openUdp - cannote create socket %d\n", errno) ;
	return NULL ;
    }
    if (bind(sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) {
        WARN("openUdp - cannot bind source port\n") ;
	close(sock) ;
	return NULL ;
    }
    if (connect(sock, to, sizeof(struct sockaddr)) < 0) {
        WARN("openUdp - cannot connect to server\n") ;
	close(sock) ;
	return NULL ;
    }
    if ((p = netCreate(sock, to, SOCK_DGRAM, NET_SERVER)) == NULL) {
        WARN("openUdp - cannot create control block\n") ;
	close(sock) ;
	return NULL ;
    }
    return p ;
}

/*
 * getServer - search/create transport to the DNS server
 */
 
static  NETPTR  getServer(struct sockaddr *to, int proto)
{
    NETPTR  p ;

    /*
     * if proxy should use DNS port to communicate DNS server,
     * use proxy's listen port to forward request to the server
     */
    
    if (proto == SOCK_DGRAM && serverRestrictPort) {
        return listenUdp ;
    }
    
    /*
     * search transport to specified DNS server
     */

    if (listNet.prev == NULL || listNet.next == NULL) {
        WARN("getServer - listNet is not initialized\n") ;
	listNet.prev = &listNet ;
	listNet.next = &listNet ;
    }
    for (p = listNet.next ; p != &listNet ; p = p->next) {
        if (p->proto != proto || p->type != NET_SERVER) {
	    continue ;
	}
	if (addrEq(&p->peer, to)) {
	    return p ;
	}
    }
    
    /*
     * if not exist, create new one
     */

    if (proto == SOCK_STREAM) {
        p = openTcp(to) ;
    } else {
        p = openUdp(to) ;
    }
    return p ;
}

/*
 * getClient - search transport to the client
 */
 
static  NETPTR  getClient(struct sockaddr *to, int proto)
{
    NETPTR  p ;

    /*
     * for response using UDP, use proxy's UDP socket
     */

    if (proto == SOCK_DGRAM) {
        return listenUdp ;
    }

    /*
     * otherwise (TCP), use existing transport to the client
     */
     
    if (listNet.prev == NULL || listNet.next == NULL) {
        WARN("getClient - listNet is not initialized\n") ;
	listNet.prev = &listNet ;
	listNet.next = &listNet ;
    }
    for (p = listNet.next ; p != &listNet ; p = p->next) {
        if (p->proto != proto || p->type != NET_CLIENT) {
	    continue ;
	}
	if (addrEq(&p->peer, to)) {
	    return p ;
	}
    }
    return NULL ;
}

/*
 * sendTcp, sendUdp - send message over TCP/UDP
 */
 
static  void    sendTcp(NETPTR p, u_char *msg, int len)
{
    int     n ;
    u_char  buff[1024] ;
    u_char  *bp ;
    
    /*
     * insert 2 bytes message length field before the message
     */
     
    if (len < (1024 - 2)) {
        bp = buff ;
    } else {
        bp = malloc(len + 2) ;
    }
    if (bp == NULL) {
        WARN("sendTcp - cannot alocate send buffer\n") ;
	return ;
    }
    bp[0] = len / 256 ;
    bp[1] = len % 256 ;
    memcpy(&bp[2], msg, len) ;
    len += 2 ;
    
    /*
     * if there is pending data, enqueue sending message
     */

    if (p->send.prev == NULL || p->send.next == NULL) {
        WARN("sendTcp - send buffer is not initialized\n") ;
	p->send.prev = &p->send ;
	p->send.next = &p->send ;
    }
    if (p->send.next != &p->send) {
        tcpQueue(p, bp, len) ;
	if (bp != buff) free(bp) ;
	return ;
    }
    
    /*
     * then, try to send message
     */

    if ((n = tcpSend(p->sock, bp, len)) < 0) {
        WARN("sendTcp - cannot send message\n") ;
	if (bp != buff) free(bp) ;
	return ;
    }
    if (n != len) {
        tcpQueue(p, (bp + n), (len - n)) ;
    }
    if (bp != buff) free(bp) ;
}

static  void    sendUdp(NETPTR p, u_char *msg, int len, struct sockaddr *to)
{
    int     n ;
    
    if (p->type == NET_SERVER) {    /* target is binded */
        n = send(p->sock, msg, len, 0) ;
    } else {                        /* need to specify target */
        n = sendto(p->sock, msg, len, 0, to, sizeof(struct sockaddr)) ;
    }
    if (n < 0) {
	if (!transientError(errno)) {
	    WARN("sendUdp - cannot send message %d\n", errno) ;
	}
	return ;
    }
    if (n != len) {
        WARN("sendUdp - cannot send entire message %d/%d\n", n, len) ;
	return ;
    }
}

/*
 * server_forward - forward request to the DNS server
 */
 
void    server_forward(struct sockaddr *to, int proto, u_char *msg, int len)
{
    NETPTR  p ;
    
    if (to == NULL) {
        to = &serverDefaultAddr ;
    }
    if ((p = getServer(to, proto)) == NULL) {
        WARN("server_forward - no transport to server %s\n",
	    addrFmt(to, proto)) ;
	return ;
    }
    if (proto == SOCK_STREAM) {
        sendTcp(p, msg, len) ;
    } else {
        sendUdp(p, msg, len, to) ;
    }
}

/*
 * server_response - response to client
 */
 
void    server_response(struct sockaddr *to, int proto, u_char *msg, int len)
{
    NETPTR  p ;
    
    if ((p = getClient(to, proto)) == NULL) {
        WARN("server_response - no transport to client %s\n",
	    addrFmt(to, proto)) ;
        return ;
    }
    if (proto == SOCK_STREAM) {
        sendTcp(p, msg, len) ;
    } else {
        sendUdp(p, msg, len, to) ;
    }
}
