Files
bind9/contrib/idn/mdnkit/mdnsproxy/server.c
2001-06-09 00:30:55 +00:00

1458 lines
35 KiB
C

/*
* 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) ;
}
}