/************************************************************************
 *   IRC - Internet Relay Chat, ircd/s_bsd.c
 *   Copyright (C) 1990 Jarkko Oikarinen and
 *                      University of Oulu, Computing Center
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 1, or (at your option)
 *   any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* -- Jto -- 07 Jul 1990
 * Added jlp@hamblin.byu.edu's debugtty fix
 */

/* -- Armin -- Jun 18 1990
 * Added setdtablesize() for more socket connections
 * (sequent OS Dynix only) -- maybe select()-call must be changed ...
 */

/* -- Jto -- 13 May 1990
 * Added several fixes from msa:
 *   Better error messages
 *   Changes in check_access
 * Added SO_REUSEADDR fix from zessel@informatik.uni-kl.de
 */

#ifndef lint
static  char rcsid[] = "@(#)$Id: s_bsd.c,v 1.73 1999/07/23 17:15:14 kalt Exp $";
#endif

#include "os.h"
#include "s_defines.h"
#define S_BSD_C
#include "s_externs.h"
#undef S_BSD_C

#ifndef IN_LOOPBACKNET
#define IN_LOOPBACKNET	0x7f
#endif

aClient	*local[MAXCONNECTIONS];
FdAry	fdas, fdall;
int	highest_fd = 0, readcalls = 0, udpfd = -1, resfd = -1, adfd = -1;
time_t	timeofday;
static	struct	SOCKADDR_IN	mysk;
static	void	polludp();

static	struct	SOCKADDR *connect_inet __P((aConfItem *, aClient *, int *));
static	int	completed_connection __P((aClient *));
static	int	check_init __P((aClient *, char *));
static	int	check_ping __P((char *, int));
static	void	do_dns_async __P(());
static	int	set_sock_opts __P((int, aClient *));
#ifdef	UNIXPORT
static	struct	SOCKADDR *connect_unix __P((aConfItem *, aClient *, int *));
static	void	add_unixconnection __P((aClient *, int));
static	char	unixpath[256];
#endif
static	char	readbuf[READBUF_SIZE];

#define	CFLAG	(CONF_CONNECT_SERVER|CONF_ZCONNECT_SERVER)
#define	NFLAG	CONF_NOCONNECT_SERVER

/*
 * Try and find the correct name to use with getrlimit() for setting the max.
 * number of files allowed to be open by this process.
 */
#ifdef RLIMIT_FDMAX
# define RLIMIT_FD_MAX   RLIMIT_FDMAX
#else
# ifdef RLIMIT_NOFILE
#  define RLIMIT_FD_MAX RLIMIT_NOFILE
# else
#  ifdef RLIMIT_OPEN_MAX
#   define RLIMIT_FD_MAX RLIMIT_OPEN_MAX
#  else
#   undef RLIMIT_FD_MAX
#  endif
# endif
#endif

/*
** add_local_domain()
** Add the domain to hostname, if it is missing
** (as suggested by eps@TOASTER.SFSU.EDU)
*/

void	add_local_domain(hname, size)
char	*hname;
int	size;
{
#ifdef RES_INIT
	/* try to fix up unqualified names */
	if (!index(hname, '.'))
	    {
		if (!(ircd_res.options & RES_INIT))
		    {
			Debug((DEBUG_DNS,"ircd_res_init()"));
			ircd_res_init();
		    }
		if (ircd_res.defdname[0])
		    {
			(void)strncat(hname, ".", size-1);
			(void)strncat(hname, ircd_res.defdname, size-2);
		    }
	    }
#endif
	return;
}

/*
** Cannot use perror() within daemon. stderr is closed in
** ircd and cannot be used. And, worse yet, it might have
** been reassigned to a normal connection...
*/

/*
** report_error
**	This a replacement for perror(). Record error to log and
**	also send a copy to all *LOCAL* opers online.
**
**	text	is a *format* string for outputting error. It must
**		contain only two '%s', the first will be replaced
**		by the sockhost from the cptr, and the latter will
**		be taken from sys_errlist[errno].
**
**	cptr	if not NULL, is the *LOCAL* client associated with
**		the error.
*/
void	report_error(text, cptr)
char	*text;
aClient *cptr;
{
	Reg	int	errtmp = errno; /* debug may change 'errno' */
	Reg	char	*host;
	int	err;
	SOCK_LEN_TYPE len = sizeof(err);
	extern	char	*strerror();

	host = (cptr) ? get_client_name(cptr, FALSE) : "";

	Debug((DEBUG_ERROR, text, host, strerror(errtmp)));

	/*
	 * Get the *real* error from the socket (well try to anyway..).
	 * This may only work when SO_DEBUG is enabled but its worth the
	 * gamble anyway.
	 */
#ifdef	SO_ERROR
	if (!IsMe(cptr) && cptr->fd >= 0)
		if (!GETSOCKOPT(cptr->fd, SOL_SOCKET, SO_ERROR, &err, &len))
			if (err)
				errtmp = err;
#endif
	sendto_flag(SCH_ERROR, text, host, strerror(errtmp));
#ifdef USE_SYSLOG
	syslog(LOG_WARNING, text, host, strerror(errtmp));
#endif
	return;
}

/*
 * inetport
 *
 * Create a socket in the AF_INET domain, bind it to the port given in
 * 'port' and listen to it. If 'ip' has a value, use it as vif to listen.
 * Connections are accepted to this socket depending on the IP# mask given
 * by 'ipmask'.  Returns the fd of the socket created or -1 on error.
 */
int	inetport(cptr, ip, ipmask, port)
aClient	*cptr;
char	*ipmask, *ip;
int	port;
{
	static	struct SOCKADDR_IN server;
	int	ad[4];
	SOCK_LEN_TYPE len = sizeof(server);
	char	ipname[20];

	ad[0] = ad[1] = ad[2] = ad[3] = 0;

	/*
	 * do it this way because building ip# from separate values for each
	 * byte requires endian knowledge or some nasty messing. Also means
	 * easy conversion of "*" 0.0.0.0 or 134.* to 134.0.0.0 :-)
	 */
	(void)sscanf(ipmask, "%d.%d.%d.%d", &ad[0], &ad[1], &ad[2], &ad[3]);
	(void)sprintf(ipname, "%d.%d.%d.%d", ad[0], ad[1], ad[2], ad[3]);

	(void)sprintf(cptr->sockhost, "%-.42s.%u", ip ? ip : ME,
		      (unsigned int)port);
	(void)strcpy(cptr->name, ME);
	DupString(cptr->auth, ipname);
	/*
	 * At first, open a new socket
	 */
	if (cptr->fd == -1)
		cptr->fd = socket(AFINET, SOCK_STREAM, 0);
	if (cptr->fd < 0)
	    {
		report_error("opening stream socket %s:%s", cptr);
		return -1;
	    }
	else if (cptr->fd >= MAXCLIENTS)
	    {
		sendto_flag(SCH_ERROR,
			    "No more connections allowed (%s)", cptr->name);
		(void)close(cptr->fd);
		return -1;
	    }
	(void)set_sock_opts(cptr->fd, cptr);
	/*
	 * Bind a port to listen for new connections if port is non-null,
	 * else assume it is already open and try get something from it.
	 */
	if (port)
	    {
		server.SIN_FAMILY = AFINET;
#ifdef INET6
		if (!ip || (!isxdigit(*ip) && *ip != ':'))
			server.sin6_addr = in6addr_any;
		else
			if(!inet_pton(AF_INET6, ip, server.sin6_addr.s6_addr))
				bcopy(minus_one, server.sin6_addr.s6_addr,
				      IN6ADDRSZ);
#else
		if (!ip || !isdigit(*ip))
			server.sin_addr.s_addr = INADDR_ANY;
		else
			server.sin_addr.s_addr = inetaddr(ip);
#endif
		server.SIN_PORT = htons(port);
		/*
		 * Try 10 times to bind the socket with an interval of 20
		 * seconds. Do this so we don't have to keep trying manually
		 * to bind. Why ? Because a port that has closed often lingers
		 * around for a short time.
		 * This used to be the case.  Now it no longer is.
		 * Could cause the server to hang for too long - avalon
		 */
		if (bind(cptr->fd, (SAP)&server, sizeof(server)) == -1)
		    {
			report_error("binding stream socket %s:%s", cptr);
			(void)close(cptr->fd);
			return -1;
		    }
	    }

	if (getsockname(cptr->fd, (struct SOCKADDR *)&server, &len))
	    {
		report_error("getsockname failed for %s:%s",cptr);
		(void)close(cptr->fd);
		return -1;
	    }

	if (cptr == &me) /* KLUDGE to get it work... */
	    {
		char	buf[1024];

		(void)sprintf(buf, rpl_str(RPL_MYPORTIS, "*"),
			ntohs(server.SIN_PORT));
		(void)write(0, buf, strlen(buf));
	    }

	if (cptr->fd > highest_fd)
		highest_fd = cptr->fd;
#ifdef INET6
	bcopy(server.sin6_addr.s6_addr, cptr->ip.s6_addr, IN6ADDRSZ);
#else
	cptr->ip.s_addr = server.sin_addr.s_addr; /* broken on linux at least*/
#endif
	cptr->port = port;
	(void)listen(cptr->fd, LISTENQUEUE);
	local[cptr->fd] = cptr;

	return 0;
}

/*
 * add_listener
 *
 * Create a new client which is essentially the stub like 'me' to be used
 * for a socket that is passive (listen'ing for connections to be accepted).
 */
int	add_listener(aconf)
aConfItem *aconf;
{
	aClient	*cptr;

	cptr = make_client(NULL);
	cptr->flags = FLAGS_LISTEN;
	cptr->acpt = cptr;
	cptr->from = cptr;
	cptr->firsttime = time(NULL);
	SetMe(cptr);
#ifdef	UNIXPORT
	if (*aconf->host == '/')
	    {
		strncpyzt(cptr->name, aconf->host, sizeof(cptr->name));
		if (unixport(cptr, aconf->host, aconf->port))
			cptr->fd = -2;
	    }
	else
#endif
		if (inetport(cptr, aconf->host, aconf->name, aconf->port))
			cptr->fd = -2;

	if (cptr->fd >= 0)
	    {
		cptr->confs = make_link();
		cptr->confs->next = NULL;
		cptr->confs->value.aconf = aconf;
		add_fd(cptr->fd, &fdas);
		add_fd(cptr->fd, &fdall);
		set_non_blocking(cptr->fd, cptr);
	    }
	else
		free_client(cptr);
	return 0;
}

#ifdef	UNIXPORT
/*
 * unixport
 *
 * Create a socket and bind it to a filename which is comprised of the path
 * (directory where file is placed) and port (actual filename created).
 * Set directory permissions as rwxr-xr-x so other users can connect to the
 * file which is 'forced' to rwxrwxrwx (different OS's have different need of
 * modes so users can connect to the socket).
 */
int	unixport(cptr, path, port)
aClient	*cptr;
char	*path;
int	port;
{
	struct sockaddr_un un;

	if ((cptr->fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
	    {
		report_error("error opening unix domain socket %s:%s", cptr);
		return -1;
	    }
	else if (cptr->fd >= MAXCLIENTS)
	    {
		sendto_flag(SCH_ERROR,
			    "No more connections allowed (%s)", cptr->name);
		(void)close(cptr->fd);
		return -1;
	    }

	un.sun_family = AF_UNIX;
	(void)mkdir(path, 0755);
	SPRINTF(unixpath, "%s/%d", path, port);
	(void)unlink(unixpath);
	strncpyzt(un.sun_path, unixpath, sizeof(un.sun_path));
	(void)strcpy(cptr->name, ME);
	errno = 0;
	get_sockhost(cptr, unixpath);

	if (bind(cptr->fd, (SAP)&un, strlen(unixpath)+2) == -1)
	    {
		report_error("error binding unix socket %s:%s", cptr);
		(void)close(cptr->fd);
		return -1;
	    }
	if (cptr->fd > highest_fd)
		highest_fd = cptr->fd;
	(void)listen(cptr->fd, LISTENQUEUE);
	(void)chmod(path, 0755);
	(void)chmod(unixpath, 0777);
	cptr->flags |= FLAGS_UNIX;
	cptr->port = 0;
	local[cptr->fd] = cptr;

	return 0;
}
#endif

/*
 * close_listeners
 *
 * Close and free all clients which are marked as having their socket open
 * and in a state where they can accept connections.  Unix sockets have
 * the path to the socket unlinked for cleanliness.
 */
void	close_listeners()
{
	Reg	aClient	*cptr;
	Reg	int	i;
	Reg	aConfItem *aconf;

	/*
	 * close all 'extra' listening ports we have and unlink the file
	 * name if it was a unix socket.
	 */
	for (i = highest_fd; i >= 0; i--)
	    {
		if (!(cptr = local[i]))
			continue;
		if (cptr == &me || !IsListening(cptr))
			continue;
		aconf = cptr->confs->value.aconf;

		if (IsIllegal(aconf) && aconf->clients == 0)
		    {
#ifdef	UNIXPORT
			if (IsUnixSocket(cptr))
			    {
				SPRINTF(unixpath, "%s/%d",
					aconf->host, aconf->port);
				(void)unlink(unixpath);
			    }
#endif
			close_connection(cptr);
		    }
	    }
}

void
start_iauth(rcvdsig)
int rcvdsig;
{
#if defined(USE_IAUTH)
	static time_t last = 0;
	static char first = 1;
	int sp[2], fd;

	if ((bootopt & BOOT_NOIAUTH) != 0)
		return;
	if (adfd >= 0)
	    {
		if (rcvdsig)
			sendto_flag(SCH_AUTH,
			    "iauth is already running, restart canceled");
		return;
	    }
	if ((time(NULL) - last) > 90 || rcvdsig)
	    {
		sendto_flag(SCH_AUTH, "Starting iauth...");
		last = time(NULL);
		read_iauth(); /* to reset olen */
		iauth_spawn += 1;
	    }
	else
		return;
	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) < 0)
	    {
		sendto_flag(SCH_ERROR, "socketpair() failed!");
		sendto_flag(SCH_AUTH, "Failed to restart iauth!");
	    }
	adfd = sp[0];
	set_non_blocking(sp[0], NULL);
	set_non_blocking(sp[1], NULL); /* less to worry about in iauth */
	switch (vfork())
	    {
	case -1:
		sendto_flag(SCH_ERROR, "vfork() failed!");
		sendto_flag(SCH_AUTH, "Failed to restart iauth!");
		close(sp[0]); close(sp[1]);
		adfd = -1;
		return;
	case 0:
		for (fd = 0; fd < MAXCONNECTIONS; fd++)
			if (fd != sp[1])
				(void)close(fd);
		if (sp[1] != 0)
		    {
			(void)dup2(sp[1], 0);
			close(sp[1]);
		    }
		if (execl(IAUTH_PATH, IAUTH, NULL) < 0)
			_exit(-1); /* should really not happen.. */
	default:
		close(sp[1]);
	    }

	if (first)
		first = 0;
	else
	    {
		int i;
		aClient *cptr;

		for (i = 0; i <= highest_fd; i++)
		    {   
			if (!(cptr = local[i]))
				continue;
			if (IsServer(cptr) || IsService(cptr))
				continue;
			sendto_iauth("%d O", i);
		    }
	    }
#endif
}

/*
 * init_sys
 */
void	init_sys()
{
	Reg	int	fd;

#ifdef RLIMIT_FD_MAX
	struct rlimit limit;

	if (!getrlimit(RLIMIT_FD_MAX, &limit))
	    {
		if (limit.rlim_max < MAXCONNECTIONS)
		    {
			(void)fprintf(stderr, "ircd fd table is too big\n");
			(void)fprintf(stderr, "Hard Limit: %d IRC max: %d\n",
				      (int) limit.rlim_max, MAXCONNECTIONS);
			(void)fprintf(stderr,
				      "Fix MAXCONNECTIONS and recompile.\n");
			exit(-1);
		    }
		limit.rlim_cur = limit.rlim_max; /* make soft limit the max */
		if (setrlimit(RLIMIT_FD_MAX, &limit) == -1)
		    {
			(void)fprintf(stderr, "error setting max fd's to %d\n",
				      (int) limit.rlim_cur);
			exit(-1);
		    }
	    }
#endif
#if ! USE_POLL
# ifdef sequent
#  ifndef	DYNIXPTX
	int	fd_limit;

	fd_limit = setdtablesize(MAXCONNECTIONS + 1);
	if (fd_limit < MAXCONNECTIONS)
	    {
		(void)fprintf(stderr,"ircd fd table too big\n");
		(void)fprintf(stderr,"Hard Limit: %d IRC max: %d\n",
			fd_limit, MAXCONNECTIONS);
		(void)fprintf(stderr,"Fix MAXCONNECTIONS\n");
		exit(-1);
	    }
#  endif
# endif
#endif /* USE_POLL */

#if defined(PCS) || defined(DYNIXPTX) || defined(SVR3)
	char	logbuf[BUFSIZ];

	(void)setvbuf(stderr,logbuf,_IOLBF,sizeof(logbuf));
#else
# if defined(HPUX)
	(void)setvbuf(stderr, NULL, _IOLBF, 0);
# else
#  if !defined(SVR4)
	(void)setlinebuf(stderr);
#  endif
# endif
#endif

	bzero((char *)&fdas, sizeof(fdas));
	bzero((char *)&fdall, sizeof(fdall));
	fdas.highest = fdall.highest = -1;

	for (fd = 3; fd < MAXCONNECTIONS; fd++)
	    {
		(void)close(fd);
		local[fd] = NULL;
	    }
	local[1] = NULL;
	(void) fclose(stdout);
	(void)close(1);

	if (bootopt & BOOT_TTY)	/* debugging is going to a tty */
		goto init_dgram;
	if (!(bootopt & BOOT_DEBUG))
		(void)close(2);

	if (((bootopt & BOOT_CONSOLE) || isatty(0)) &&
	    !(bootopt & (BOOT_INETD|BOOT_OPER)))
	    {
#ifndef __CYGWIN32__
		if (fork())
			exit(0);
#endif
#ifdef TIOCNOTTY
		if ((fd = open("/dev/tty", O_RDWR)) >= 0)
		    {
			(void)ioctl(fd, TIOCNOTTY, (char *)NULL);
			(void)close(fd);
		    }
#endif
#if defined(HPUX) || defined(SVR4) || defined(DYNIXPTX) || \
    defined(_POSIX_SOURCE) || defined(SGI)
		(void)setsid();
#else
		(void)setpgrp(0, (int)getpid());
#endif
		(void)close(0);	/* fd 0 opened by inetd */
		local[0] = NULL;
	    }
init_dgram:
	resfd = init_resolver(0x1f);

	start_iauth(0);
}

void	write_pidfile()
{
	int fd;
	char buff[20];
	(void)truncate(IRCDPID_PATH, 0);
	if ((fd = open(IRCDPID_PATH, O_CREAT|O_WRONLY, 0600))>=0)
	    {
		bzero(buff, sizeof(buff));
		(void)sprintf(buff,"%5d\n", (int)getpid());
		if (write(fd, buff, strlen(buff)) == -1)
			Debug((DEBUG_NOTICE,"Error writing to pid file %s",
			      IRCDPID_PATH));
		(void)close(fd);
		return;
	    }
# ifdef	DEBUGMODE
	else
		Debug((DEBUG_NOTICE,"Error opening pid file %s",
			IRCDPID_PATH));
# endif
}
		
/*
 * Initialize the various name strings used to store hostnames. This is set
 * from either the server's sockhost (if client fd is a tty or localhost)
 * or from the ip# converted into a string. 0 = success, -1 = fail.
 */
static	int	check_init(cptr, sockn)
Reg	aClient	*cptr;
Reg	char	*sockn;
{
	struct	SOCKADDR_IN sk;
	SOCK_LEN_TYPE len = sizeof(struct SOCKADDR_IN);

#ifdef	UNIXPORT
	if (IsUnixSocket(cptr))
	    {
		strncpyzt(sockn, cptr->acpt->sockhost, HOSTLEN+1);
		get_sockhost(cptr, sockn);
		return 0;
	    }
#endif

	/* If descriptor is a tty, special checking... */
	if (isatty(cptr->fd))
	    {
		strncpyzt(sockn, me.sockhost, HOSTLEN);
		bzero((char *)&sk, sizeof(struct SOCKADDR_IN));
	    }
	else if (getpeername(cptr->fd, (SAP)&sk, &len) == -1)
	    {
		report_error("connect failure: %s %s", cptr);
		return -1;
	    }
#ifdef INET6
	inetntop(AF_INET6, (char *)&sk.sin6_addr, sockn, MYDUMMY_SIZE);
	Debug((DEBUG_DNS,"sockn %x",sockn));
	Debug((DEBUG_DNS,"sockn %s",sockn));
#else
	(void)strcpy(sockn, (char *)inetntoa((char *)&sk.sin_addr));
#endif
#ifdef INET6
	if (IN6_IS_ADDR_LOOPBACK(&sk.SIN_ADDR))
#else
	if (inetnetof(sk.SIN_ADDR) == IN_LOOPBACKNET)
#endif
	    {
		cptr->hostp = me.hostp;
	    }
	bcopy((char *)&sk.SIN_ADDR, (char *)&cptr->ip, sizeof(struct IN_ADDR));
	cptr->port = (int)(ntohs(sk.SIN_PORT));

	return 0;
}

/*
 * Ordinary client access check. Look for conf lines which have the same
 * status as the flags passed.
 *  0 = Success
 * -1 = Bad socket.
 * -2 = Access denied
 */
int	check_client(cptr)
Reg	aClient	*cptr;
{
	static	char	sockname[HOSTLEN+1];
	Reg	struct	hostent *hp = NULL;
	Reg	int	i;
 
#ifdef INET6
	Debug((DEBUG_DNS, "ch_cl: check access for %s[%s]",
		cptr->name, inet_ntop(AF_INET6, (char *)&cptr->ip, mydummy,
				      MYDUMMY_SIZE)));
#else
	Debug((DEBUG_DNS, "ch_cl: check access for %s[%s]",
		cptr->name, inetntoa((char *)&cptr->ip)));
#endif

	if (check_init(cptr, sockname))
		return -1;

	if (!IsUnixSocket(cptr))
		hp = cptr->hostp;
	/*
	 * Verify that the host to ip mapping is correct both ways and that
	 * the ip#(s) for the socket is listed for the host.
	 * We shouldn't check it for localhost, because hp is fake in that
	 * case. -Toor
	 */
	if (hp && (hp != me.hostp))
	    {
		for (i = 0; hp->h_addr_list[i]; i++)
			if (!bcmp(hp->h_addr_list[i], (char *)&cptr->ip,
				  sizeof(struct IN_ADDR)))
				break;
		if (!hp->h_addr_list[i])
		    {
#ifdef INET6
			sendto_flag(SCH_ERROR,
				    "IP# Mismatch: %s != %s[%08x%08x%08x%08x]",
				    inetntop(AF_INET6, (char *)&cptr->ip,
					      mydummy,MYDUMMY_SIZE),hp->h_name,
				    ((unsigned long *)hp->h_addr)[0],
				    ((unsigned long *)hp->h_addr)[1],
				    ((unsigned long *)hp->h_addr)[2],
				    ((unsigned long *)hp->h_addr)[3]); 
#else
			sendto_flag(SCH_ERROR, "IP# Mismatch: %s != %s[%08x]",
				    inetntoa((char *)&cptr->ip), hp->h_name,
				    *((unsigned long *)hp->h_addr));
#endif
			hp = NULL;
		    }
	    }

	if ((i = attach_Iline(cptr, hp, sockname)))
	    {
		Debug((DEBUG_DNS,"ch_cl: access denied: %s[%s]",
			cptr->name, sockname));
		return i;
	    }

	Debug((DEBUG_DNS, "ch_cl: access ok: %s[%s]",
		cptr->name, sockname));

#ifdef INET6
	if (IN6_IS_ADDR_LOOPBACK(&cptr->ip) || IsUnixSocket(cptr) ||
		(cptr->ip.s6_laddr[0]==mysk.sin6_addr.s6_laddr[0] &&
		 cptr->ip.s6_laddr[1]==mysk.sin6_addr.s6_laddr[1])
/* ||
	    IN6_ARE_ADDR_SAMEPREFIX(&cptr->ip, &mysk.SIN_ADDR))
 about the same, I think              NOT */
                                                              )
#else
        if (inetnetof(cptr->ip) == IN_LOOPBACKNET || IsUnixSocket(cptr) ||
            inetnetof(cptr->ip) == inetnetof(mysk.SIN_ADDR))
#endif
	    {

		ircstp->is_loc++;
		cptr->flags |= FLAGS_LOCAL;
	    }
	return 0;
}

/*
 * check_server_init(), check_server()
 *	check access for a server given its name (passed in cptr struct).
 *	Must check for all C/N lines which have a name which matches the
 *	name given and a host which matches. A host alias which is the
 *	same as the server name is also acceptable in the host field of a
 *	C/N line.
 *  0 = Success
 * -1 = Access denied
 * -2 = Bad socket.
 */
int	check_server_init(cptr)
aClient	*cptr;
{
	Reg	char	*name;
	Reg	aConfItem *c_conf = NULL, *n_conf = NULL;
	struct	hostent	*hp = NULL;
	Link	*lp;

	name = cptr->name;
	Debug((DEBUG_DNS, "sv_cl: check access for %s[%s]",
		name, cptr->sockhost));

	if (IsUnknown(cptr) && !attach_confs(cptr, name, CFLAG|NFLAG))
	    {
		Debug((DEBUG_DNS,"No C/N lines for %s", name));
		return -1;
	    }
	lp = cptr->confs;
	/*
	 * We initiated this connection so the client should have a C and N
	 * line already attached after passing through the connec_server()
	 * function earlier.
	 */
	if (IsConnecting(cptr) || IsHandshake(cptr))
	    {
		c_conf = find_conf(lp, name, CFLAG);
		n_conf = find_conf(lp, name, NFLAG);
		if (!c_conf || !n_conf)
		    {
			sendto_flag(SCH_ERROR, "Connecting Error: %s[%s]",
				   name, cptr->sockhost);
			det_confs_butmask(cptr, 0);
			return -1;
		    }
	    }
#ifdef	UNIXPORT
	if (IsUnixSocket(cptr))
	    {
		if (!c_conf)
			c_conf = find_conf(lp, name, CFLAG);
		if (!n_conf)
			n_conf = find_conf(lp, name, NFLAG);
	    }
#endif

	/*
	** If the servername is a hostname, either an alias (CNAME) or
	** real name, then check with it as the host. Use gethostbyname()
	** to check for servername as hostname.
	*/
	if (!IsUnixSocket(cptr) && !cptr->hostp)
	    {
		Reg	aConfItem *aconf;

		aconf = count_cnlines(lp);
		if (aconf)
		    {
			Reg	char	*s;
			Link	lin;

			/*
			** Do a lookup for the CONF line *only* and not
			** the server connection else we get stuck in a
			** nasty state since it takes a SERVER message to
			** get us here and we can't interrupt that very
			** well.
			*/
			lin.value.aconf = aconf;
			lin.flags = ASYNC_CONF;
			nextdnscheck = 1;
			if ((s = index(aconf->host, '@')))
				s++;
			else
				s = aconf->host;
			Debug((DEBUG_DNS,"sv_ci:cache lookup (%s)",s));
			hp = gethost_byname(s, &lin);
		    }
	    }
	return check_server(cptr, hp, c_conf, n_conf, 0);
}

int	check_server(cptr, hp, c_conf, n_conf, estab)
aClient	*cptr;
Reg	aConfItem	*n_conf, *c_conf;
Reg	struct	hostent	*hp;
int	estab;
{
	Reg	char	*name;
	char	abuff[HOSTLEN+USERLEN+2];
	char	sockname[HOSTLEN+1], fullname[HOSTLEN+1];
	Link	*lp = cptr->confs;
	int	i;

	if (check_init(cptr, sockname))
		return -2;

check_serverback:
	if (hp)
	    {
		for (i = 0; hp->h_addr_list[i]; i++)
			if (!bcmp(hp->h_addr_list[i], (char *)&cptr->ip,
				  sizeof(struct IN_ADDR)))
				break;
		if (!hp->h_addr_list[i])
		    {
#ifdef INET6
			sendto_flag(SCH_ERROR,
				    "IP# Mismatch: %s != %s[%08x%08x%08x%08x]",
				    inetntop(AF_INET6, (char *)&cptr->ip,
					      mydummy,MYDUMMY_SIZE),hp->h_name,
				    ((unsigned long *)hp->h_addr)[0],
				    ((unsigned long *)hp->h_addr)[1],
				    ((unsigned long *)hp->h_addr)[2],
				    ((unsigned long *)hp->h_addr)[3]); 
#else
			sendto_flag(SCH_ERROR, "IP# Mismatch: %s != %s[%08x]",
				    inetntoa((char *)&cptr->ip), hp->h_name,
				    *((unsigned long *)hp->h_addr));
#endif
			hp = NULL;
		    }
	    }
	else if (cptr->hostp)
	    {
		hp = cptr->hostp;
		goto check_serverback;
	    }

	if (hp)
		/*
		 * if we are missing a C or N line from above, search for
		 * it under all known hostnames we have for this ip#.
		 */
		for (i=0,name = hp->h_name; name ; name = hp->h_aliases[i++])
		    {
			strncpyzt(fullname, name, sizeof(fullname));
			add_local_domain(fullname, HOSTLEN-strlen(fullname));
			Debug((DEBUG_DNS, "sv_cl: gethostbyaddr: %s->%s",
				sockname, fullname));
			SPRINTF(abuff, "%s@%s", cptr->username, fullname);
			if (!c_conf)
				c_conf = find_conf_host(lp, abuff, CFLAG);
			if (!n_conf)
				n_conf = find_conf_host(lp, abuff, NFLAG);
			if (c_conf && n_conf)
			    {
				get_sockhost(cptr, fullname);
				break;
			    }
		    }
	name = cptr->name;

	/*
	 * Check for C and N lines with the hostname portion the ip number
	 * of the host the server runs on. This also checks the case where
	 * there is a server connecting from 'localhost'.
	 */
	if (IsUnknown(cptr) && (!c_conf || !n_conf))
	    {
		SPRINTF(abuff, "%s@%s", cptr->username, sockname);
		if (!c_conf)
			c_conf = find_conf_host(lp, abuff, CFLAG);
		if (!n_conf)
			n_conf = find_conf_host(lp, abuff, NFLAG);
	    }
	/*
	 * Attach by IP# only if all other checks have failed.
	 * It is quite possible to get here with the strange things that can
	 * happen when using DNS in the way the irc server does. -avalon
	 */
	if (!hp)
	    {
		if (!c_conf)
			c_conf = find_conf_ip(lp, (char *)&cptr->ip,
					      cptr->username, CFLAG);
		if (!n_conf)
			n_conf = find_conf_ip(lp, (char *)&cptr->ip,
					      cptr->username, NFLAG);
	    }
	else
		for (i = 0; hp->h_addr_list[i]; i++)
		    {
			if (!c_conf)
				c_conf = find_conf_ip(lp, hp->h_addr_list[i],
						      cptr->username, CFLAG);
			if (!n_conf)
				n_conf = find_conf_ip(lp, hp->h_addr_list[i],
						      cptr->username, NFLAG);
		    }
	/*
	 * detach all conf lines that got attached by attach_confs()
	 */
	det_confs_butmask(cptr, 0);
	/*
	 * if no C or no N lines, then deny access
	 */
	if (!c_conf || !n_conf)
	    {
		get_sockhost(cptr, sockname);
		Debug((DEBUG_DNS, "sv_cl: access denied: %s[%s@%s] c %x n %x",
			name, cptr->auth, cptr->sockhost,
			c_conf, n_conf));
		return -1;
	    }
	/*
	 * attach the C and N lines to the client structure for later use.
	 */
	(void)attach_conf(cptr, n_conf);
	(void)attach_conf(cptr, c_conf);
	(void)attach_confs(cptr, name, CONF_HUB|CONF_LEAF);

#ifdef INET6
	if ((AND16(c_conf->ipnum.s6_addr) == 255) && !IsUnixSocket(cptr))
#else
	if ((c_conf->ipnum.s_addr == -1) && !IsUnixSocket(cptr))
#endif
		bcopy((char *)&cptr->ip, (char *)&c_conf->ipnum,
			sizeof(struct IN_ADDR));
	if (!IsUnixSocket(cptr))
		get_sockhost(cptr, c_conf->host);

	Debug((DEBUG_DNS,"sv_cl: access ok: %s[%s]",
		name, cptr->sockhost));
	if (estab)
		return m_server_estab(cptr);
	return 0;
}

/*
** completed_connection
**	Complete non-blocking connect()-sequence. Check access and
**	terminate connection, if trouble detected.
**
**	Return	TRUE, if successfully completed
**		FALSE, if failed and ClientExit
*/
static	int completed_connection(cptr)
aClient	*cptr;
{
	aConfItem *aconf;

	SetHandshake(cptr);
	
	aconf = find_conf(cptr->confs, cptr->name, CFLAG);
	if (!aconf)
	    {
		sendto_flag(SCH_NOTICE,
			    "Lost C-Line for %s", get_client_name(cptr,FALSE));
		return -1;
	    }
	if (!BadPtr(aconf->passwd))
#ifndef	ZIP_LINKS
		sendto_one(cptr, "PASS %s %s IRC|%s %s", aconf->passwd,
			   pass_version, serveropts,
			   (bootopt & BOOT_STRICTPROT) ? "P" : "");
#else
		sendto_one(cptr, "PASS %s %s IRC|%s %s%s", aconf->passwd,
			   pass_version, serveropts,
			   (bootopt & BOOT_STRICTPROT) ? "P" : "",
			   (aconf->status == CONF_ZCONNECT_SERVER) ? "Z" : "");
#endif

	aconf = find_conf(cptr->confs, cptr->name, CONF_NOCONNECT_SERVER);
	if (!aconf)
	    {
		sendto_flag(SCH_NOTICE,
			    "Lost N-Line for %s", get_client_name(cptr,FALSE));
		return -1;
	    }
	sendto_one(cptr, "SERVER %s 1 :%s",
		   my_name_for_link(ME, aconf->port), me.info);
	if (!IsDead(cptr))
	    {
		start_auth(cptr);
#if defined(USE_IAUTH)
		/*
		** This could become a bug.. but I don't think iauth needs the
		** hostname/aliases in this case. -kalt
		*/
		sendto_iauth("%d d", cptr->fd);
#endif
	    }

	return (IsDead(cptr)) ? -1 : 0;
}

int	hold_server(cptr)
aClient	*cptr;
{
	return -1; /* needs to be fixed, don't forget virtual hosts */

#if 0              /* code and variables declarations are removed, this
		      avoids compiler warnings */

	struct	SOCKADDR_IN	sin;
	aConfItem	*aconf;
	aClient	*acptr;
	int	fd;

#ifdef	ZIP_LINKS
	/*
	 * reconnecting will not work with compressed links,
	 * unless someones fixes reconnect and implements what's needed
	 * to have it work for compressed links. -krys
	 */
	return -1;
#else
	if (!IsServer(cptr) ||
	    !(aconf = find_conf_name(cptr->name, CFLAG)))
		return -1;

	if (!aconf->port)
		return -1;

	fd = socket(AFINET, SOCK_STREAM, 0);

	if (fd >= MAXCLIENTS)
	    {
		(void)close(fd);
		sendto_flag(SCH_ERROR,
			    "Can't reconnect - all connections in use");
		return -1;
	    }

	cptr->flags |= FLAGS_HELD;
	(void)close(cptr->fd);
	del_fd(cptr->fd, &fdall);
	del_fd(cptr->fd, &fdas);
	cptr->fd = -2;

	acptr = make_client(NULL);
	acptr->fd = fd;
	acptr->port = aconf->port;
	set_non_blocking(acptr->fd, acptr);
	(void)set_sock_opts(acptr->fd, acptr);
	bzero((char *)&sin, sizeof(sin));
	sin.SIN_FAMILY = AFINET;
	sin.SIN_PORT = htons(aconf->port);
	bcopy((char *)&cptr->ip, (char *)&sin.SIN_ADDR, sizeof(cptr->ip));
	bcopy((char *)&cptr->ip, (char *)&acptr->ip, sizeof(cptr->ip));

	if (connect(acptr->fd, (SAP)&sin, sizeof(sin)) < 0 &&
	   errno != EINPROGRESS)
	    {
		report_error("Connect to host %s failed: %s", acptr); /*buggy*/
		(void)close(acptr->fd);
		MyFree((char *)acptr);
		return -1;
	    }

	acptr->status = STAT_RECONNECT;
	if (acptr->fd > highest_fd)
		highest_fd = acptr->fd;
	add_fd(acptr->fd, &fdall);
	local[acptr->fd] = acptr;
	acptr->acpt = &me;
	add_client_to_list(acptr);
	(void)strcpy(acptr->name, cptr->name);
	/* broken syntax
	sendto_one(acptr, "PASS %s %s", aconf->passwd, pass_version);
	*/
	sendto_one(acptr, "RECONNECT %s %d", acptr->name, cptr->sendM);
	sendto_flag(SCH_NOTICE, "Reconnecting to %s", acptr->name);
	Debug((DEBUG_NOTICE, "Reconnect %s %#x via %#x %d", cptr->name, cptr,
		acptr, acptr->fd));
	return 0;
#endif
#endif
}

/*
** close_connection
**	Close the physical connection. This function must make
**	MyConnect(cptr) == FALSE, and set cptr->from == NULL.
*/
void	close_connection(cptr)
aClient *cptr;
{
	Reg	aConfItem *aconf;
	Reg	int	i,j;
#ifdef SO_LINGER
	struct 	linger	sockling;

	sockling.l_onoff = 0;
#endif

	if (IsServer(cptr))
	    {
		ircstp->is_sv++;
		ircstp->is_sbs += cptr->sendB;
		ircstp->is_sbr += cptr->receiveB;
		ircstp->is_sks += cptr->sendK;
		ircstp->is_skr += cptr->receiveK;
		ircstp->is_sti += timeofday - cptr->firsttime;
		if (ircstp->is_sbs > 1023)
		    {
			ircstp->is_sks += (ircstp->is_sbs >> 10);
			ircstp->is_sbs &= 0x3ff;
		    }
		if (ircstp->is_sbr > 1023)
		    {
			ircstp->is_skr += (ircstp->is_sbr >> 10);
			ircstp->is_sbr &= 0x3ff;
		    }
	    }
	else if (IsClient(cptr))
	    {
		ircstp->is_cl++;
		ircstp->is_cbs += cptr->sendB;
		ircstp->is_cbr += cptr->receiveB;
		ircstp->is_cks += cptr->sendK;
		ircstp->is_ckr += cptr->receiveK;
		ircstp->is_cti += timeofday - cptr->firsttime;
		if (ircstp->is_cbs > 1023)
		    {
			ircstp->is_cks += (ircstp->is_cbs >> 10);
			ircstp->is_cbs &= 0x3ff;
		    }
		if (ircstp->is_cbr > 1023)
		    {
			ircstp->is_ckr += (ircstp->is_cbr >> 10);
			ircstp->is_cbr &= 0x3ff;
		    }
	    }
	else
		ircstp->is_ni++;

	/*
	 * remove outstanding DNS queries.
	 */
	del_queries((char *)cptr);
	/*
	 * If the connection has been up for a long amount of time, schedule
	 * a 'quick' reconnect, else reset the next-connect cycle.
	 */
	if ((aconf = find_conf_exact(cptr->name, cptr->username,
				    cptr->sockhost, CFLAG)))
	    {
		/*
		 * Reschedule a faster reconnect, if this was a automaticly
		 * connected configuration entry. (Note that if we have had
		 * a rehash in between, the status has been changed to
		 * CONF_ILLEGAL). But only do this if it was a "good" link.
		 */
		aconf->hold = timeofday;
		aconf->hold += (aconf->hold - cptr->since > HANGONGOODLINK) ?
				HANGONRETRYDELAY : ConfConFreq(aconf);
		if (nextconnect > aconf->hold)
			nextconnect = aconf->hold;
	    }

	if (cptr->authfd >= 0)
	    {
#ifdef	SO_LINGER
		if (cptr->exitc == EXITC_PING)
			if (SETSOCKOPT(cptr->authfd, SOL_SOCKET, SO_LINGER,
				       &sockling, sockling))
				report_error("setsockopt(SO_LINGER) %s:%s",
					     cptr);
#endif
		(void)close(cptr->authfd);
	    }

	if ((i = cptr->fd) >= 0)
	    {
#if defined(USE_IAUTH)
		sendto_iauth("%d D", cptr->fd);
#endif
		flush_connections(i);
		if (IsServer(cptr) || IsListening(cptr))
		    {
			del_fd(i, &fdas);
#ifdef	ZIP_LINKS
			/*
			** the connection might have zip data (even if
			** FLAGS_ZIP is not set)
			*/
			zip_free(cptr);
#endif
		    }
		else if (IsClient(cptr))
		    {
#ifdef	SO_LINGER
			if (cptr->exitc == EXITC_PING)
				if (SETSOCKOPT(i, SOL_SOCKET, SO_LINGER,
					       &sockling, sockling))
					report_error("setsockopt(SO_LINGER) %s:%s",
						     cptr);
#endif
		    }
		del_fd(i, &fdall);
		local[i] = NULL;
		(void)close(i);

		/*
		 * fd remap to keep local[i] filled at the bottom.
		 *	don't *ever* move descriptors for 
		 *		+ log file
		 *		+ sockets bound to listen() ports
		 *	--Yegg
		 */
		if (i >= 0 && (j = highest_fd) > i)
		    {
			while (!local[j])
				j--;
			if (j > i && local[j] &&
			    !(IsLog(local[j]) || IsMe(local[j])))
			    {
				if (dup2(j,i) == -1)
					return;
				local[i] = local[j];
				local[i]->fd = i;
				local[j] = NULL;
				(void)close(j);
				del_fd(j, &fdall);
				add_fd(i, &fdall);
				if (IsServer(local[i]) || IsMe(local[i]))
				    {
					del_fd(j, &fdas);
					add_fd(i, &fdas);
				    }
				while (!local[highest_fd])
					highest_fd--;
#if defined(USE_IAUTH)
				sendto_iauth("%d R %d", j, i);
#endif
			    }
		    }
		cptr->fd = -2;
		DBufClear(&cptr->sendQ);
		DBufClear(&cptr->recvQ);
		bzero(cptr->passwd, sizeof(cptr->passwd));
		/*
		 * clean up extra sockets from P-lines which have been
		 * discarded.
		 */
		if (cptr->acpt != &me)
		    {
			aconf = cptr->acpt->confs->value.aconf;
			if (aconf->clients > 0)
				aconf->clients--;
			if (!aconf->clients && IsIllegal(aconf))
				close_connection(cptr->acpt);
		    }
	    }

	det_confs_butmask(cptr, 0);
	cptr->from = NULL; /* ...this should catch them! >:) --msa */
	return;
}

/*
** set_sock_opts
*/
static	int	set_sock_opts(fd, cptr)
int	fd;
aClient	*cptr;
{
	int	opt, ret = 0;
#ifdef SO_REUSEADDR
	opt = 1;
	if (SETSOCKOPT(fd, SOL_SOCKET, SO_REUSEADDR, &opt, opt) < 0)
		report_error("setsockopt(SO_REUSEADDR) %s:%s", cptr);
#endif
#if  defined(SO_DEBUG) && defined(DEBUGMODE) && 0
/* Solaris 2.x with SO_DEBUG writes to syslog by default */
#if ! SOLARIS_2 || defined(USE_SYSLOG)
	opt = 1;
	if (SETSOCKOPT(fd, SOL_SOCKET, SO_DEBUG, &opt, opt) < 0)
		report_error("setsockopt(SO_DEBUG) %s:%s", cptr);
#endif /* SOLARIS_2 */
#endif
#ifdef	SO_USELOOPBACK
	opt = 1;
	if (SETSOCKOPT(fd, SOL_SOCKET, SO_USELOOPBACK, &opt, opt) < 0)
		report_error("setsockopt(SO_USELOOPBACK) %s:%s", cptr);
#endif
#ifdef	SO_RCVBUF
	opt = 8192;
	if (SETSOCKOPT(fd, SOL_SOCKET, SO_RCVBUF, &opt, opt) < 0)
		report_error("setsockopt(SO_RCVBUF) %s:%s", cptr);
#endif
#ifdef	SO_SNDBUF
# ifdef	_SEQUENT_
/* seems that Sequent freezes up if the receving buffer is a different size
 * to the sending buffer (maybe a tcp window problem too).
 */
# endif
	opt = 8192;
	if (SETSOCKOPT(fd, SOL_SOCKET, SO_SNDBUF, &opt, opt) < 0)
		report_error("setsockopt(SO_SNDBUF) %s:%s", cptr);
# ifdef	SO_SNDLOWAT
	/*
	 * Setting the low water mark should improve performence by avoiding
	 * early returns from select()/poll().  It shouldn't delay sending
	 * data, provided that io_loop() combines read_message() and
	 * flush_fdary/connections() calls properly. -kalt
	 * This call isn't always implemented, even when defined.. so be quiet
	 * about errors. -kalt
	 */
	opt = 8192;
	SETSOCKOPT(fd, SOL_SOCKET, SO_SNDLOWAT, &opt, opt);
# endif
#endif
#if defined(IP_OPTIONS) && defined(IPPROTO_IP) && !defined(AIX) && \
    !defined(SUN_GSO_BUG) && !defined(INET6)
	/*
	 * Mainly to turn off and alert us to source routing, here.
	 * Method borrowed from Wietse Venema's TCP wrapper.
	 */
	{
	    if (!IsUnixSocket(cptr) && !IsListening(cptr))
		{
		    u_char	opbuf[256], *t = opbuf;
		    char	*s = readbuf;
	      
		    opt = sizeof(opbuf);
		    if (GETSOCKOPT(fd, IPPROTO_IP, IP_OPTIONS, t, &opt) == -1)
			    report_error("getsockopt(IP_OPTIONS) %s:%s", cptr);
		    else if (opt > 0)
			{
			    for (; opt > 0; opt--, s+= 3)
				    (void)sprintf(s, " %02x", *t++);
			    *s = '\0';
			    sendto_flag(SCH_NOTICE,
					"Connection %s with IP opts%s",
					get_client_name(cptr, TRUE), readbuf);
			    Debug((DEBUG_NOTICE,
				   "Connection %s with IP opts%s",
				   get_client_name(cptr, TRUE), readbuf));
			    ret = -1;
			}
		}
	}
#endif
	return ret;
}

int	get_sockerr(cptr)
aClient	*cptr;
{
	int errtmp = errno, err = 0;
	SOCK_LEN_TYPE len = sizeof(err);

#ifdef	SO_ERROR
	if (cptr->fd >= 0)
		if (!GETSOCKOPT(cptr->fd, SOL_SOCKET, SO_ERROR, &err, &len))
			if (err)
				errtmp = err;
#endif
	return errtmp;
}

/*
** set_non_blocking
**	Set the client connection into non-blocking mode. If your
**	system doesn't support this, you can make this a dummy
**	function (and get all the old problems that plagued the
**	blocking version of IRC--not a problem if you are a
**	lightly loaded node...)
*/
void	set_non_blocking(fd, cptr)
int	fd;
aClient *cptr;
{
	int	res, nonb = 0;

	/*
	** NOTE: consult ALL your relevant manual pages *BEFORE* changing
	**	 these ioctl's.  There are quite a few variations on them,
	**	 as can be seen by the PCS one.  They are *NOT* all the same.
	**	 Heed this well. - Avalon.
	*/
#if NBLOCK_POSIX
	nonb |= O_NONBLOCK;
#endif
#if NBLOCK_BSD
	nonb |= O_NDELAY;
#endif
#if NBLOCK_SYSV
	/* This portion of code might also apply to NeXT.  -LynX */
	res = 1;

	if (ioctl (fd, FIONBIO, &res) < 0)
		report_error("ioctl(fd,FIONBIO) failed for %s:%s", cptr);
#else
	if ((res = fcntl(fd, F_GETFL, 0)) == -1)
		report_error("fcntl(fd, F_GETFL) failed for %s:%s",cptr);
	else if (fcntl(fd, F_SETFL, res | nonb) == -1)
		report_error("fcntl(fd, F_SETL, nonb) failed for %s:%s",cptr);
#endif
	return;
}

#ifdef	CLONE_CHECK
/* 
 * check_clones
 * adapted by jecete 4 IRC Ptnet
 */
static  int     check_clones(cptr)
aClient *cptr;
{
	struct abacklog {
		struct  IN_ADDR ip;
		time_t  PT;
		struct abacklog *next;
	};
	static		struct abacklog *backlog = NULL;
	register	struct abacklog **blscn = &backlog,
					*blptr;
	register	int count = 0;

	/* First, ditch old entries */
	while (*blscn != NULL)
	    {
		if ((*blscn)->PT+CLONE_PERIOD < timeofday)
		    {
			blptr= *blscn;
			*blscn=blptr->next;
			MyFree((char *)blptr);
		    }
		else
			blscn = &(*blscn)->next;
	    }
	/* Now add new item to the list */
	blptr = (struct abacklog *) MyMalloc(sizeof(struct abacklog));
#ifdef INET6
	bcopy(cptr->ip.s6_addr, blptr->ip.s6_addr, IN6ADDRSZ);
#else
	blptr->ip.s_addr = cptr->ip.s_addr;
#endif
	blptr->PT = timeofday;
	blptr->next = backlog;
	backlog = blptr;
	
	/* Count the number of entries from the same host */
	blptr = backlog;
	while (blptr != NULL)
	    {
#ifdef INET6
		if (bcmp(blptr->ip.s6_addr, cptr->ip.s6_addr, IN6ADDRSZ) == 0)
#else
		if (blptr->ip.s_addr == cptr->ip.s_addr)
#endif
			count++;
		blptr = blptr->next;
	    }
       return (count);
}
#endif

/*
 * Creates a client which has just connected to us on the given fd.
 * The sockhost field is initialized with the ip# of the host.
 * The client is added to the linked list of clients but isnt added to any
 * hash tables yet since it doesnt have a name.
 */
aClient	*add_connection(cptr, fd)
aClient	*cptr;
int	fd;
{
	Link	lin;
	aClient *acptr;
	aConfItem *aconf = NULL;
	acptr = make_client(NULL);

	aconf = cptr->confs->value.aconf;
	/* Removed preliminary access check. Full check is performed in
	 * m_server and m_user instead. Also connection time out help to
	 * get rid of unwanted connections.
	 */
	if (isatty(fd)) /* If descriptor is a tty, special checking... */
		get_sockhost(acptr, cptr->sockhost);
	else
	    {
		struct	SOCKADDR_IN addr;
		SOCK_LEN_TYPE len = sizeof(struct SOCKADDR_IN);

		if (getpeername(fd, (SAP)&addr, &len) == -1)
		    {
#if defined(linux)
			if (errno != ENOTCONN)
#endif
				report_error("Failed in connecting to %s :%s",
					     cptr);
add_con_refuse:
			ircstp->is_ref++;
			acptr->fd = -2;
			free_client(acptr);
			(void)close(fd);
			return NULL;
		    }
		/* don't want to add "Failed in connecting to" here.. */
		if (aconf && IsIllegal(aconf))
			goto add_con_refuse;
		/* Copy ascii address to 'sockhost' just in case. Then we
		 * have something valid to put into error messages...
		 */
#ifdef INET6
		inetntop(AF_INET6, (char *)&addr.sin6_addr, mydummy,
			  MYDUMMY_SIZE);
		get_sockhost(acptr, (char *)mydummy);
#else
		get_sockhost(acptr, (char *)inetntoa((char *)&addr.sin_addr));
#endif
		bcopy ((char *)&addr.SIN_ADDR, (char *)&acptr->ip,
			sizeof(struct IN_ADDR));
		acptr->port = ntohs(addr.SIN_PORT);

		lin.flags = ASYNC_CLIENT;
		lin.value.cptr = acptr;
#ifdef INET6
		Debug((DEBUG_DNS, "lookup %s",
		       inet_ntop(AF_INET6, (char *)&addr.sin6_addr,
				 mydummy, MYDUMMY_SIZE)));
#else
		Debug((DEBUG_DNS, "lookup %s",
		       inetntoa((char *)&addr.sin_addr)));
#endif
		acptr->hostp = gethost_byaddr((char *)&acptr->ip, &lin);
		if (!acptr->hostp)
			SetDNS(acptr);
		nextdnscheck = 1;
	    }

#ifdef	CLONE_CHECK
	if (check_clones(acptr) > CLONE_MAX)
	    {
		sendto_flag(SCH_LOCAL, "Rejecting connection from %s[%s].",
			    (acptr->hostp) ? acptr->hostp->h_name : "",
			    acptr->sockhost);
		sendto_flog(acptr, " ?Clone? ", 0, "<none>",
			    (acptr->hostp) ? acptr->hostp->h_name :
			    acptr->sockhost);
		del_queries((char *)acptr);
# ifdef INET6
		(void)sendto(acptr->fd,
			     "ERROR :Too rapid connections from your host\r\n",
			     46, 0, 0, 0);
# else
		(void)send(acptr->fd,
			   "ERROR :Too rapid connections from your host\r\n",
			   46, 0);
# endif
		goto add_con_refuse;
	    }
#endif
	acptr->fd = fd;
	set_non_blocking(acptr->fd, acptr);
	if (set_sock_opts(acptr->fd, acptr) == -1)
		goto add_con_refuse;
	if (aconf)
		aconf->clients++;
	if (fd > highest_fd)
		highest_fd = fd;
	local[fd] = acptr;
	add_fd(fd, &fdall);
	acptr->acpt = cptr;
	add_client_to_list(acptr);
	start_auth(acptr);
#if defined(USE_IAUTH)
	if (!isatty(fd) && !DoingDNS(acptr))
	    {
		int i = 0;
		
		while (acptr->hostp->h_aliases[i])
			sendto_iauth("%d A %s", acptr->fd,
				     acptr->hostp->h_aliases[i++]);
		if (acptr->hostp->h_name)
			sendto_iauth("%d N %s",acptr->fd,acptr->hostp->h_name);
		else if (acptr->hostp->h_aliases[0])
			sendto_iauth("%d n", acptr->fd);
	    }
#endif
	return acptr;
}

#ifdef	UNIXPORT
static	void	add_unixconnection(cptr, fd)
aClient	*cptr;
int	fd;
{
	aClient *acptr;
	aConfItem *aconf = NULL;

	acptr = make_client(NULL);

	/* Copy ascii address to 'sockhost' just in case. Then we
	 * have something valid to put into error messages...
	 */
	get_sockhost(acptr, me.sockhost);
	aconf = cptr->confs->value.aconf;
	if (aconf)
	    {
		if (IsIllegal(aconf))
		    {
			ircstp->is_ref++;
			acptr->fd = -2;
			free_client(acptr);
			(void)close(fd);
			return;
		    }
		else
			aconf->clients++;
	    }
	acptr->fd = fd;
	if (fd > highest_fd)
		highest_fd = fd;
	local[fd] = acptr;
	add_fd(fd, &fdall);
	acptr->acpt = cptr;
	SetUnixSock(acptr);
	bcopy((char *)&me.ip, (char *)&acptr->ip, sizeof(struct IN_ADDR));

	add_client_to_list(acptr);
	set_non_blocking(acptr->fd, acptr);
	(void)set_sock_opts(acptr->fd, acptr);
	return;
}
#endif

/*
** read_listener
**
** Accept incoming connections, extracted from read_message() 98/12 -kalt
** Up to 10 connections will be accepted, unless SLOW_ACCEPT is defined.
*/
static void
read_listener(cptr)
aClient *cptr;
{
	int fdnew, max = 10;

#if defined(SLOW_ACCEPT)
	max = 1;
#endif
	while (max--)
	    {
		/*
		** There may be many reasons for error return, but in otherwise
		** correctly working environment the probable cause is running
		** out of file descriptors (EMFILE, ENFILE or others?). The
		** man pages for accept don't seem to list these as possible,
		** although it's obvious that it may happen here.
		** Thus no specific errors are tested at this point, just
		** assume that connections cannot be accepted until some old
		** is closed first.
		*/
		if ((fdnew = accept(cptr->fd, NULL, NULL)) < 0)
		    {
			if (errno != EWOULDBLOCK)
				report_error("Cannot accept connections %s:%s",
					     cptr);
			break;
		    }
		ircstp->is_ac++;
		if (fdnew >= MAXCLIENTS)
		    {
			ircstp->is_ref++;
			sendto_flag(SCH_ERROR, "All connections in use. (%s)",
				    get_client_name(cptr, TRUE));
			find_bounce(NULL, 0, fdnew);
#ifdef INET6
			(void)sendto(fdnew,
				     "ERROR :All connections in use\r\n",
				     32, 0, 0, 0);
#else
			(void)send(fdnew, "ERROR :All connections in use\r\n",
				   32, 0);
#endif
			(void)close(fdnew);
			continue;
		    }
		/*
		 * Use of add_connection (which never fails :) meLazy
		 * Never say never. MrMurphy visited here. -Vesa
		 */
#ifdef	UNIXPORT
		if (IsUnixSocket(cptr))
			add_unixconnection(cptr, fdnew);
		else
#endif
			if (!add_connection(cptr, fdnew))
				continue;
		nextping = timeofday; /* isn't this abusive? -kalt */
		istat.is_unknown++;
	    }
}

/*
** client_packet
**
** Process data from receive buffer to client.
** Extracted from read_packet() 960804/291p3/Vesa
*/
static	int	client_packet(cptr)
Reg	aClient *cptr;
{
	Reg	int	dolen = 0;

	while (DBufLength(&cptr->recvQ) && !NoNewLine(cptr) &&
	       ((cptr->status < STAT_UNKNOWN) ||
		(cptr->since - timeofday < MAXPENALTY)))
	    {
		/*
		** If it has become registered as a Service or Server
		** then skip the per-message parsing below.
		*/
		if (IsService(cptr) || IsServer(cptr))
		    {
			dolen = dbuf_get(&cptr->recvQ, readbuf,
					 sizeof(readbuf));
			if (dolen <= 0)
				break;
			dolen = dopacket(cptr, readbuf, dolen);
			if (dolen == 2 && cptr->since == cptr->lasttime)
				cptr->since += 5;
			if (dolen)
				return dolen;
			break;
		    }
		dolen = dbuf_getmsg(&cptr->recvQ, readbuf,
				    sizeof(readbuf));
		/*
		** Devious looking...whats it do ? well..if a client
		** sends a *long* message without any CR or LF, then
		** dbuf_getmsg fails and we pull it out using this
		** loop which just gets the next 512 bytes and then
		** deletes the rest of the buffer contents.
		** -avalon
		*/
		while (dolen <= 0)
		    {
			if (dolen < 0)
				return exit_client(cptr, cptr, &me,
						   "dbuf_getmsg fail");
			if (DBufLength(&cptr->recvQ) < 510)
			    {	/* hmm? */
				cptr->flags |= FLAGS_NONL;
				break;
			    }
			dolen = dbuf_get(&cptr->recvQ, readbuf, 511);
			if (dolen > 0 && DBufLength(&cptr->recvQ))
				DBufClear(&cptr->recvQ);
		    }

		/* Is it okay not to test for other return values? -krys */
		if (dolen > 0 &&
		    (dopacket(cptr, readbuf, dolen) == FLUSH_BUFFER))
			return FLUSH_BUFFER;
	    }
	return 1;
}

/*
** read_packet
**
** Read a 'packet' of data from a connection and process it.  Read in 8k
** chunks to give a better performance rating (for server connections).
** Do some tricky stuff for client connections to make sure they don't do
** any flooding >:-) -avalon
*/
static	int	read_packet(cptr, msg_ready)
Reg	aClient *cptr;
int	msg_ready;
{
	Reg	int	length = 0, done;

	if (msg_ready &&
	    !(IsPerson(cptr) && DBufLength(&cptr->recvQ) > 6090))
	    {
		errno = 0;
#ifdef INET6
		length = recvfrom(cptr->fd, readbuf, sizeof(readbuf), 0, 0, 0);
#else
		length = recv(cptr->fd, readbuf, sizeof(readbuf), 0);
#endif
#if defined(DEBUGMODE) && defined(DEBUG_READ)
		if (length > 0)
			Debug((DEBUG_READ,
				"recv = %d bytes from %d[%s]:[%*.*s]\n",
				length, cptr->fd, cptr->name, length, length,
				readbuf));
#endif

		Debug((DEBUG_DEBUG, "Received %d(%d-%s) bytes from %d %s",
			length, errno, strerror(errno),
			cptr->fd, get_client_name(cptr, TRUE)));
		cptr->lasttime = timeofday;
		if (cptr->lasttime > cptr->since)
			cptr->since = cptr->lasttime;
		cptr->flags &= ~(FLAGS_PINGSENT|FLAGS_NONL);
		/*
		 * If not ready, fake it so it isnt closed
		 */
		if (length == -1 &&
			((errno == EWOULDBLOCK) || (errno == EAGAIN)))
			return 1;
		if (length <= 0)
			return length;
	    }
	else if (msg_ready)
		return exit_client(cptr, cptr, &me, "EOF From Client");

	/*
	** For server connections, we process as many as we can without
	** worrying about the time of day or anything :)
	*/
	if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr) ||
	    IsService(cptr))
	    {
		if (length > 0)
		    {
			done = dopacket(cptr, readbuf, length);
			if (done && done != 2)
				return done;
		    }
	    }
	else
	    {
		/*
		** Before we even think of parsing what we just read, stick
		** it on the end of the receive queue and do it when its
		** turn comes around.
		*/
		if (length && dbuf_put(&cptr->recvQ, readbuf, length) < 0)
			return exit_client(cptr, cptr, &me, "dbuf_put fail");

		if (IsPerson(cptr) &&
		    DBufLength(&cptr->recvQ) > CLIENT_FLOOD)
		    {
			cptr->exitc = EXITC_FLOOD;
			return exit_client(cptr, cptr, &me, "Excess Flood");
		    }

		return client_packet(cptr);
	    }
	return 1;
}


/*
 * Check all connections for new connections and input data that is to be
 * processed. Also check for connections with data queued and whether we can
 * write it out.
 */
int	read_message(delay, fdp, ro)
time_t	delay; /* Don't ever use ZERO here, unless you mean to poll and then
		* you have to have sleep/wait somewhere else in the code.--msa
		* Actually, ZERO is NOT ZERO anymore.. see below -kalt
		*/
FdAry	*fdp;
int	ro;
{
#if ! USE_POLL
# define SET_READ_EVENT( thisfd )	FD_SET( thisfd, &read_set)
# define SET_WRITE_EVENT( thisfd )	FD_SET( thisfd, &write_set)
# define CLR_READ_EVENT( thisfd )	FD_CLR( thisfd, &read_set)
# define CLR_WRITE_EVENT( thisfd )	FD_CLR( thisfd, &write_set)
# define TST_READ_EVENT( thisfd )	FD_ISSET( thisfd, &read_set)
# define TST_WRITE_EVENT( thisfd )	FD_ISSET( thisfd, &write_set)

	fd_set	read_set, write_set;
	int	highfd = -1;
#else
/* most of the following use pfd */
# define POLLSETREADFLAGS	(POLLIN|POLLRDNORM)
# define POLLREADFLAGS		(POLLSETREADFLAGS|POLLHUP|POLLERR)
# define POLLSETWRITEFLAGS	(POLLOUT|POLLWRNORM)
# define POLLWRITEFLAGS		(POLLOUT|POLLWRNORM|POLLHUP|POLLERR)

# define SET_READ_EVENT( thisfd ){  CHECK_PFD( thisfd );\
				   pfd->events |= POLLSETREADFLAGS;}
# define SET_WRITE_EVENT( thisfd ){ CHECK_PFD( thisfd );\
				   pfd->events |= POLLSETWRITEFLAGS;}

# define CLR_READ_EVENT( thisfd )	pfd->revents &= ~POLLSETREADFLAGS
# define CLR_WRITE_EVENT( thisfd )	pfd->revents &= ~POLLSETWRITEFLAGS
# define TST_READ_EVENT( thisfd )	pfd->revents & POLLREADFLAGS
# define TST_WRITE_EVENT( thisfd )	pfd->revents & POLLWRITEFLAGS

# define CHECK_PFD( thisfd ) 			\
	if ( pfd->fd != thisfd ) {		\
		pfd = &poll_fdarray[nbr_pfds++];\
		pfd->fd     = thisfd;		\
		pfd->events = 0;		\
	}

	struct pollfd   poll_fdarray[MAXCONNECTIONS];
	struct pollfd * pfd     = poll_fdarray;
	struct pollfd * res_pfd = NULL;
	struct pollfd * udp_pfd = NULL;
	struct pollfd * ad_pfd = NULL;
	aClient	 * authclnts[MAXCONNECTIONS];	/* mapping of auth fds to client ptrs */
	int	   nbr_pfds = 0;
#endif

	aClient	*cptr;
	int	nfds, ret = 0;
	struct	timeval	wait;
	time_t	delay2 = delay;
	int	res, length, fd, i;
	int	auth;

	for (res = 0;;)
	    {
#if ! USE_POLL
		FD_ZERO(&read_set);
		FD_ZERO(&write_set);
#else
		/* set up such that CHECK_FD works */
		nbr_pfds = 0;
		pfd 	 = poll_fdarray;
		pfd->fd  = -1;
		res_pfd  = NULL;
		udp_pfd  = NULL;
		ad_pfd = NULL;
#endif	/* USE_POLL */
		auth = 0;

#if USE_POLL
		if ( auth == 0 )
			bzero((char *) authclnts, sizeof( authclnts ));
#endif
		for (i = fdp->highest; i >= 0; i--)
		    {
			if (!(cptr = local[fd = fdp->fd[i]]) ||
			    IsLog(cptr) || IsHeld(cptr))
				continue;
			Debug((DEBUG_L11, "fd %d cptr %#x %d %#x %s",
				fd, cptr, cptr->status, cptr->flags,
				get_client_name(cptr,TRUE)));
			/* authentication fd's */
			if (DoingAuth(cptr))
			    {
				auth++;
				SET_READ_EVENT(cptr->authfd);
				Debug((DEBUG_NOTICE,"auth on %x %d", cptr,
					fd));
				if (cptr->flags & FLAGS_WRAUTH)
					SET_WRITE_EVENT(cptr->authfd);
#if USE_POLL
				authclnts[cptr->authfd] = cptr;
#else
				if (cptr->authfd > highfd)
					highfd = cptr->authfd;
#endif
			    }
			/*
			** if any of these is true, data won't be parsed
			** so no need to check for anything!
			*/
#if defined(USE_IAUTH)
			if (DoingDNS(cptr) || DoingAuth(cptr) ||
			    WaitingXAuth(cptr) ||
			    (DoingXAuth(cptr) &&
			     !(iauth_options & XOPT_EARLYPARSE)))
#else
			if (DoingDNS(cptr) || DoingAuth(cptr))
#endif
				continue;
#if ! USE_POLL
			if (fd > highfd)
				highfd = fd;
#endif
			/*
			** Checking for new connections is only done up to
			** once per second.
			*/
			if (IsListening(cptr))
			    {
				if (timeofday > cptr->lasttime + 1 && ro == 0)
				    {
					SET_READ_EVENT( fd );
				    }
				else if (delay2 > 1)
					delay2 = 1;
				continue;
			    }
			
			/*
			** This is very approximate, it should take
			** cptr->since into account. -kalt
			*/
			if (DBufLength(&cptr->recvQ) && delay2 > 2)
				delay2 = 1;

			if (IsRegisteredUser(cptr))
			    {
				if (cptr->since - timeofday < MAXPENALTY+1)
					SET_READ_EVENT( fd );
			    }
			else if (DBufLength(&cptr->recvQ) < 4088)
				SET_READ_EVENT( fd );

			/*
			** If we have anything in the sendQ, check if there is
			** room to write data.
			*/
			if (DBufLength(&cptr->sendQ) || IsConnecting(cptr) ||
#ifdef	ZIP_LINKS
			    ((cptr->flags & FLAGS_ZIP) &&
			     (cptr->zip->outcount > 0)) ||
#endif
			    IsReconnect(cptr))
				if (IsServer(cptr) || IsConnecting(cptr) ||
				    ro == 0)
					SET_WRITE_EVENT( fd );
		    }

		if (udpfd >= 0)
		    {
			SET_READ_EVENT(udpfd);
#if ! USE_POLL
			if (udpfd > highfd)
				highfd = udpfd;
#else
			udp_pfd = pfd;
#endif
		    }
		if (resfd >= 0)
		    {
			SET_READ_EVENT(resfd);
#if ! USE_POLL
			if (resfd > highfd)
				highfd = resfd;
#else
			res_pfd = pfd;
#endif			
		    }
#if defined(USE_IAUTH)
		if (adfd >= 0)
		    {
			SET_READ_EVENT(adfd);
# if ! USE_POLL
			if (adfd > highfd)
				highfd = adfd;
# else
			ad_pfd = pfd;
# endif			
		    }
#endif
		Debug((DEBUG_L11, "udpfd %d resfd %d adfd %d", udpfd, resfd,
		       adfd));
#if ! USE_POLL
		Debug((DEBUG_L11, "highfd %d", highfd));
#endif
		
		wait.tv_sec = MIN(delay2, delay);
		wait.tv_usec = (delay == 0) ? 200000 : 0;
#if ! USE_POLL
		nfds = select(highfd + 1, (SELECT_FDSET_TYPE *)&read_set,
			      (SELECT_FDSET_TYPE *)&write_set, 0, &wait);
#else
		nfds = poll( poll_fdarray, nbr_pfds,
			     wait.tv_sec * 1000 + wait.tv_usec/1000 );
#endif
		ret = nfds;
		if (nfds == -1 && errno == EINTR)
			return -1;
		else if (nfds >= 0)
			break;
#if ! USE_POLL
		report_error("select %s:%s", &me);
#else
		report_error("poll %s:%s", &me);
#endif
		res++;
		if (res > 5)
			restart("too many select()/poll() errors");
		sleep(10);
		timeofday = time(NULL);
	    } /* for(res=0;;) */
	
	timeofday = time(NULL);
	if (nfds > 0 &&
#if ! USE_POLL
	    resfd >= 0 &&
#else
	    (pfd = res_pfd) &&
#endif
	    TST_READ_EVENT(resfd))
	    {
		CLR_READ_EVENT(resfd);
		nfds--;
		do_dns_async();
	    }
	if (nfds > 0 &&
#if ! USE_POLL
	    udpfd >= 0 &&
#else
	    (pfd = udp_pfd) &&
#endif
	    TST_READ_EVENT(udpfd))
	    {
		CLR_READ_EVENT(udpfd);
		nfds--;
		polludp();
	    }
#if defined(USE_IAUTH)
	if (nfds > 0 &&
# if ! USE_POLL
	    adfd >= 0 &&
# else
	    (pfd = ad_pfd) &&
# endif
	    TST_READ_EVENT(adfd))
	    {
		CLR_READ_EVENT(adfd);
		nfds--;
		read_iauth();
	    }
#endif

#if ! USE_POLL
	for (i = fdp->highest; i >= 0; i--)
#else
	for (pfd = poll_fdarray, i = 0; i < nbr_pfds; i++, pfd++ )
#endif
	    {
#if ! USE_POLL
		if (!(cptr = local[fd = fdp->fd[i]]))
			continue;
#else
		fd = pfd->fd;
		if ((cptr = authclnts[fd]))
		    {
#endif
			/*
			 * check for the auth fd's
			 */
			if (auth > 0 && nfds > 0
#if ! USE_POLL
			    && cptr->authfd >= 0
#endif
			    )
			    {
				auth--;
				if (TST_WRITE_EVENT(cptr->authfd))
				    {
					nfds--;
					send_authports(cptr);
				    }
				else if (TST_READ_EVENT(cptr->authfd))
				    {
					nfds--;
					read_authports(cptr);
				    }
				continue;
			    }
#if USE_POLL
		    }
		fd = pfd->fd;
		if (!(cptr = local[fd]))
			continue;
#else
		fd = cptr->fd;
#endif
		/*
		 * accept connections
		 */
		if (TST_READ_EVENT(fd) && IsListening(cptr))
		    {
			CLR_READ_EVENT(fd);
			cptr->lasttime = timeofday;
			read_listener(cptr);
			continue;
		    }
		if (IsMe(cptr))
			continue;
		if (TST_WRITE_EVENT(fd))
		    {
			int	write_err = 0;
			/*
			** ...room for writing, empty some queue then...
			*/
			if (IsConnecting(cptr))
				write_err = completed_connection(cptr);
			if (!write_err)
				(void)send_queued(cptr);
			if (IsDead(cptr) || write_err)
			    {
deadsocket:
				if (TST_READ_EVENT(fd))
					CLR_READ_EVENT(fd);
				cptr->exitc = EXITC_ERROR;
				(void)exit_client(cptr, cptr, &me,
						  strerror(get_sockerr(cptr)));
				continue;
			    }
		    }
		length = 1;	/* for fall through case */
		if (!NoNewLine(cptr) || TST_READ_EVENT(fd))
		    {
			if (!DoingAuth(cptr))
				length = read_packet(cptr, TST_READ_EVENT(fd));
		    }
		readcalls++;
		if (length == FLUSH_BUFFER)
			continue;
		else if (length > 0)
			flush_connections(cptr->fd);
		if (IsDead(cptr))
			goto deadsocket;
		if (length > 0)
			continue;
		
		/* Ghost! Unknown users are tagged in parse() since 2.9.
		 * Let's not drop the uplink but just the ghost's message.
		 */
		if (length == -3)
			continue;
		
		/*
		** NB: This following section has been modified to *expect*
		**     cptr to be valid (ie if (length == FLUSH_BUFFER) is
		**     above and stays there). - avalon 24/9/94
		*/
		/*
		** ...hmm, with non-blocking sockets we might get
		** here from quite valid reasons, although.. why
		** would select report "data available" when there
		** wasn't... so, this must be an error anyway...  --msa
		** actually, EOF occurs when read() returns 0 and
		** in due course, select() returns that fd as ready
		** for reading even though it ends up being an EOF. -avalon
		*/
		Debug((DEBUG_ERROR, "READ ERROR: fd = %d %d %d",
		       cptr->fd, errno, length));
		
		if (IsServer(cptr) || IsHandshake(cptr))
		    {
			int timeconnected = timeofday - cptr->firsttime;
			
			if (length == 0)
				sendto_flag(SCH_NOTICE,
		     "Server %s closed the connection (%d, %2d:%02d:%02d)",
					     get_client_name(cptr, FALSE),
					     timeconnected / 86400,
					     (timeconnected % 86400) / 3600,
					     (timeconnected % 3600)/60, 
					     timeconnected % 60);
			else	/* this must be for -1 */
			    {
				report_error("Lost connection to %s:%s",cptr);
				sendto_flag(SCH_NOTICE,
			     "%s had been connected for %d, %2d:%02d:%02d",
					     get_client_name(cptr, FALSE),
					     timeconnected / 86400,
					     (timeconnected % 86400) / 3600,
					     (timeconnected % 3600)/60, 
					     timeconnected % 60);
				if (hold_server(cptr) == 0)
					continue;
			    }
		    }
		(void)exit_client(cptr, cptr, &me, length >= 0 ?
				  "EOF From client" :
				  strerror(get_sockerr(cptr)));
	    } /* for(i) */
	return ret;
}

/*
 * connect_server
 */
int	connect_server(aconf, by, hp)
aConfItem *aconf;
aClient	*by;
struct	hostent	*hp;
{
	Reg	struct	SOCKADDR *svp;
	Reg	aClient *cptr, *c2ptr;
	Reg	char	*s;
	int	i, len;

#ifdef INET6
	Debug((DEBUG_NOTICE,"Connect to %s[%s] @%s",
	       aconf->name, aconf->host,
	       inet_ntop(AF_INET6, (char *)&aconf->ipnum, mydummy,
			 MYDUMMY_SIZE)));
#else
	Debug((DEBUG_NOTICE,"Connect to %s[%s] @%s",
	       aconf->name, aconf->host,
	       inetntoa((char *)&aconf->ipnum)));
#endif

	if ((c2ptr = find_server(aconf->name, NULL)))
	    {
		sendto_flag(SCH_NOTICE, "Server %s already present from %s",
			    aconf->name, get_client_name(c2ptr, TRUE));
		if (by && IsPerson(by) && !MyClient(by))
		  sendto_one(by,
			     ":%s NOTICE %s :Server %s already present from %s",
			     ME, by->name, aconf->name,
			     get_client_name(c2ptr, TRUE));
		return -1;
	    }

	/*
	 * If we don't know the IP# for this host and it is a hostname and
	 * not a ip# string, then try and find the appropriate host record.
	 */
	if (!aconf->ipnum.S_ADDR && *aconf->host != '/')
	    {
		Link    lin;

		lin.flags = ASYNC_CONNECT;
		lin.value.aconf = aconf;
		nextdnscheck = 1;
		s = (char *)index(aconf->host, '@');
		s++; /* should NEVER be NULL */
#ifdef INET6
		if (!inet_pton(AF_INET6, s, aconf->ipnum.s6_addr))
#else
		if ((aconf->ipnum.s_addr = inetaddr(s)) == -1)
#endif
		    {
#ifdef INET6
			bzero(aconf->ipnum.s6_addr, IN6ADDRSZ);
#else
			aconf->ipnum.s_addr = 0;
#endif
			hp = gethost_byname(s, &lin);
			Debug((DEBUG_NOTICE, "co_sv: hp %x ac %x na %s ho %s",
				hp, aconf, aconf->name, s));
			if (!hp)
				return 0;
			bcopy(hp->h_addr, (char *)&aconf->ipnum,
				sizeof(struct IN_ADDR));
		    }
	    }
	cptr = make_client(NULL);
	cptr->hostp = hp;
	/*
	 * Copy these in so we have something for error detection.
	 */
	strncpyzt(cptr->name, aconf->name, sizeof(cptr->name));
	strncpyzt(cptr->sockhost, aconf->host, HOSTLEN+1);

#ifdef	UNIXPORT
	if (*aconf->host == '/') /* (/ starts a 2), Unix domain -- dl*/
		svp = connect_unix(aconf, cptr, &len);
	else
		svp = connect_inet(aconf, cptr, &len);
#else
	svp = connect_inet(aconf, cptr, &len);
#endif

	if (!svp)
	    {
		if (cptr->fd != -1)
			(void)close(cptr->fd);
		cptr->fd = -2;
		free_client(cptr);
		return -1;
	    }

	set_non_blocking(cptr->fd, cptr);
	(void)set_sock_opts(cptr->fd, cptr);
	(void)signal(SIGALRM, dummy);
	(void)alarm(4);
	if (connect(cptr->fd, (SAP)svp, len) < 0 && errno != EINPROGRESS)
	    {
		i = errno; /* other system calls may eat errno */
		(void)alarm(0);
		report_error("Connect to host %s failed: %s",cptr);
		if (by && IsPerson(by) && !MyClient(by))
		  sendto_one(by,
			     ":%s NOTICE %s :Connect to host %s failed.",
			     ME, by->name, cptr);
		(void)close(cptr->fd);
		cptr->fd = -2;
		free_client(cptr);
		errno = i;
		if (errno == EINTR)
			errno = ETIMEDOUT;
		return -1;
	    }
	(void)alarm(0);

	/* Attach config entries to client here rather than in
	 * completed_connection. This to avoid null pointer references
	 * when name returned by gethostbyaddr matches no C lines
	 * (could happen in 2.6.1a when host and servername differ).
	 * No need to check access and do gethostbyaddr calls.
	 * There must at least be one as we got here C line...  meLazy
	 */
	(void)attach_confs_host(cptr, aconf->host, CFLAG|NFLAG);

	if (!find_conf_host(cptr->confs, aconf->host, NFLAG) ||
	    !find_conf_host(cptr->confs, aconf->host, CFLAG))
	    {
      		sendto_flag(SCH_NOTICE,
			    "Host %s is not enabled for connecting:no C/N-line",
			    aconf->host);
		if (by && IsPerson(by) && !MyClient(by))
		  sendto_one(by,
			     ":%s NOTICE %s :Connect to host %s failed.",
			     ME, by->name, cptr);
		det_confs_butmask(cptr, 0);
		(void)close(cptr->fd);
		cptr->fd = -2;
		free_client(cptr);
		return(-1);
	    }
	/*
	** The socket has been connected or connect is in progress.
	*/
	(void)make_server(cptr);
	if (by && IsPerson(by))
	    {
		(void)strcpy(cptr->serv->by, by->name);
		cptr->serv->user = by->user;
		by->user->refcnt++;
	    }
	else
		(void)strcpy(cptr->serv->by, "AutoConn.");
	cptr->serv->up = ME;
	cptr->serv->nline = aconf;
	if (cptr->fd > highest_fd)
		highest_fd = cptr->fd;
	add_fd(cptr->fd, &fdall);
	local[cptr->fd] = cptr;
	cptr->acpt = &me;
	SetConnecting(cptr);

	get_sockhost(cptr, aconf->host);
	add_client_to_list(cptr);
	nextping = timeofday;
	istat.is_unknown++;

	return 0;
}

static	struct	SOCKADDR *connect_inet(aconf, cptr, lenp)
Reg	aConfItem	*aconf;
Reg	aClient	*cptr;
int	*lenp;
{
	static	struct	SOCKADDR_IN	server;
	Reg	struct	hostent	*hp;
	aClient	*acptr;
	int	i;

	/*
	 * Might as well get sockhost from here, the connection is attempted
	 * with it so if it fails its useless.
	 */
	cptr->fd = socket(AFINET, SOCK_STREAM, 0);
	if (cptr->fd >= MAXCLIENTS)
	    {
		sendto_flag(SCH_NOTICE,
			    "No more connections allowed (%s)", cptr->name);
		return NULL;
	    }
	mysk.SIN_PORT = 0;
	bzero((char *)&server, sizeof(server));
	server.SIN_FAMILY = AFINET;
	get_sockhost(cptr, aconf->host);

	if (cptr->fd == -1)
	    {
		report_error("opening stream socket to server %s:%s", cptr);
		return NULL;
	    }
	/*
	** Bind to a local IP# (with unknown port - let unix decide) so
	** we have some chance of knowing the IP# that gets used for a host
	** with more than one IP#.
	** With VIFs, M:line defines outgoing IP# and initialises mysk.
	*/
	if (bind(cptr->fd, (SAP)&mysk, sizeof(mysk)) == -1)
	    {
		report_error("error binding to local port for %s:%s", cptr);
		return NULL;
	    }
	/*
	 * By this point we should know the IP# of the host listed in the
	 * conf line, whether as a result of the hostname lookup or the ip#
	 * being present instead. If we don't know it, then the connect fails.
	 */
#ifdef INET6
	if (isdigit(*aconf->host) && (AND16(aconf->ipnum.s6_addr) == 255))
		if (!inet_pton(AF_INET6, aconf->host,aconf->ipnum.s6_addr))
			bcopy(minus_one, aconf->ipnum.s6_addr, IN6ADDRSZ);
	if (AND16(aconf->ipnum.s6_addr) == 255)
#else
	if (isdigit(*aconf->host) && (aconf->ipnum.s_addr == -1))
		aconf->ipnum.s_addr = inetaddr(aconf->host);
	if (aconf->ipnum.s_addr == -1)
#endif
	    {
		hp = cptr->hostp;
		if (!hp)
		    {
			Debug((DEBUG_FATAL, "%s: unknown host", aconf->host));
			return NULL;
		    }
		bcopy(hp->h_addr, (char *)&aconf->ipnum,
		      sizeof(struct IN_ADDR));
 	    }
	bcopy((char *)&aconf->ipnum, (char *)&server.SIN_ADDR,
		sizeof(struct IN_ADDR));
	bcopy((char *)&aconf->ipnum, (char *)&cptr->ip,
		sizeof(struct IN_ADDR));
	cptr->port = (aconf->port > 0) ? aconf->port : portnum;
	server.SIN_PORT = htons(cptr->port);
	/*
	 * Look for a duplicate IP#,port pair among already open connections
	 * (This caters for unestablished connections).
	 */
	for (i = highest_fd; i >= 0; i--)
		if ((acptr = local[i]) &&
		    !bcmp((char *)&cptr->ip, (char *)&acptr->ip,
			  sizeof(cptr->ip)) && server.SIN_PORT == acptr->port)
			return NULL;
	*lenp = sizeof(server);
	return	(struct SOCKADDR *)&server;
}

#ifdef	UNIXPORT
/* connect_unix
 *
 * Build a socket structure for cptr so that it can connet to the unix
 * socket defined by the conf structure aconf.
 */
static	struct	SOCKADDR *connect_unix(aconf, cptr, lenp)
aConfItem	*aconf;
aClient	*cptr;
int	*lenp;
{
	static	struct	sockaddr_un	sock;

	if ((cptr->fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
	    {
		report_error("Unix domain connect to host %s failed: %s", cptr);
		return NULL;
	    }
	else if (cptr->fd >= MAXCLIENTS)
	    {
		sendto_flag(SCH_NOTICE,
			    "No more connections allowed (%s)", cptr->name);
		return NULL;
	    }

	get_sockhost(cptr, aconf->host);
	strncpyzt(sock.sun_path, aconf->host + 2, sizeof(sock.sun_path));
	sock.sun_family = AF_UNIX;
	*lenp = strlen(sock.sun_path) + 2;

	SetUnixSock(cptr);
	return (struct sockaddr *)&sock;
}
#endif

/*
 * The following section of code performs summoning of users to irc.
 */
#if defined(ENABLE_SUMMON) || defined(ENABLE_USERS)
int	utmp_open()
{
#ifdef O_NOCTTY
	return (open(UTMP, O_RDONLY|O_NOCTTY));
#else
	return (open(UTMP, O_RDONLY));
#endif
}

int	utmp_read(fd, name, line, host, hlen)
int	fd, hlen;
char	*name, *line, *host;
{
	struct	utmp	ut;
	while (read(fd, (char *)&ut, sizeof (struct utmp))
	       == sizeof (struct utmp))
	    {
		strncpyzt(name, ut.ut_name, 9);
		strncpyzt(line, ut.ut_line, 10);
#ifdef USER_PROCESS
#  if defined(HPUX) || defined(AIX)
		strncpyzt(host,(ut.ut_host[0]) ? (ut.ut_host) : ME, 16);
#  else
		strncpyzt(host, ME, 9);
#  endif
		if (ut.ut_type == USER_PROCESS)
			return 0;
#else
		strncpyzt(host, (ut.ut_host[0]) ? (ut.ut_host) : ME,
			hlen);
		if (ut.ut_name[0])
			return 0;
#endif
	    }
	return -1;
}

int	utmp_close(fd)
int	fd;
{
	return(close(fd));
}

#ifdef ENABLE_SUMMON
void	summon(who, namebuf, linebuf, chname)
aClient *who;
char	*namebuf, *linebuf, *chname;
{
	static	char	wrerr[] = "NOTICE %s :Write error. Couldn't summon.";
	int	fd;
	char	line[120];
	struct	tm	*tp;

	tp = localtime(&timeofday);
	if (strlen(linebuf) > (size_t) 9)
	    {
		sendto_one(who,"NOTICE %s :Serious fault in SUMMON.",
			   who->name);
		sendto_one(who,
			   "NOTICE %s :linebuf too long. Inform Administrator",
			   who->name);
		return;
	    }
	/*
	 * Following line added to prevent cracking to e.g. /dev/kmem if
	 * UTMP is for some silly reason writable to everyone...
	 */
	if ((linebuf[0] != 't' || linebuf[1] != 't' || linebuf[2] != 'y')
	    && (linebuf[0] != 'c' || linebuf[1] != 'o' || linebuf[2] != 'n')
#ifdef HPUX
	    && (linebuf[0] != 'p' || linebuf[1] != 't' || linebuf[2] != 'y' ||
		linebuf[3] != '/')
#endif
	    )
	    {
		sendto_one(who,
		      "NOTICE %s :Looks like mere mortal souls are trying to",
			   who->name);
		sendto_one(who,"NOTICE %s :enter the twilight zone... ",
			   who->name);
		Debug((0, "%s (%s@%s, nick %s, %s)",
		      "FATAL: major security hack. Notify Administrator !",
		      who->username, who->user->host,
		      who->name, who->info));
		return;
	    }

	SPRINTF(line,"/dev/%s", linebuf);
	(void)alarm(5);
#ifdef	O_NOCTTY
	if ((fd = open(line, O_WRONLY | O_NDELAY | O_NOCTTY)) == -1)
#else
	if ((fd = open(line, O_WRONLY | O_NDELAY)) == -1)
#endif
	    {
		(void)alarm(0);
		sendto_one(who,
			   "NOTICE %s :%s seems to have disabled summoning...",
			   who->name, namebuf);
		return;
	    }
#if !defined(O_NOCTTY) && defined(TIOCNOTTY)
	(void)ioctl(fd, TIOCNOTTY, NULL);
#endif
	(void)alarm(0);
	(void)sprintf(line,"\n\r\007Message from IRC_Daemon@%s at %d:%02d\n\r",
			ME, tp->tm_hour, tp->tm_min);
	if (write(fd, line, strlen(line)) != strlen(line))
	    {
		(void)alarm(0);
		(void)close(fd);
		sendto_one(who, wrerr, who->name);
		return;
	    }
	(void)alarm(0);
	(void)strcpy(line, "ircd: You are being summoned to Internet Relay \
Chat on\n\r");
	(void)alarm(5);
	if (write(fd, line, strlen(line)) != strlen(line))
	    {
		(void)alarm(0);
		(void)close(fd);
		sendto_one(who, wrerr, who->name);
		return;
	    }
	(void)alarm(0);
	SPRINTF(line, "ircd: Channel %s, by %s@%s (%s) %s\n\r", chname,
		who->user->username, who->user->host, who->name, who->info);
	(void)alarm(5);
	if (write(fd, line, strlen(line)) != strlen(line))
	    {
		(void)alarm(0);
		(void)close(fd);
		sendto_one(who, wrerr, who->name);
		return;
	    }
	(void)alarm(0);
	(void)strcpy(line,"ircd: Respond with irc\n\r");
	(void)alarm(5);
	if (write(fd, line, strlen(line)) != strlen(line))
	    {
		(void)alarm(0);
		(void)close(fd);
		sendto_one(who, wrerr, who->name);
		return;
	    }
	(void)close(fd);
	(void)alarm(0);
	sendto_one(who, rpl_str(RPL_SUMMONING, who->name), namebuf);
	return;
}
#  endif
#endif /* ENABLE_SUMMON */

/*
** find the real hostname for the host running the server (or one which
** matches the server's name) and its primary IP#.  Hostname is stored
** in the client structure passed as a pointer.
*/
void	get_my_name(cptr, name, len)
aClient	*cptr;
char	*name;
int	len;
{
	static	char tmp[HOSTLEN+1];
	struct	hostent	*hp;
	char	*cname = cptr->name;
	aConfItem	*aconf;

	/*
	** Setup local socket structure to use for binding to.
	*/
	bzero((char *)&mysk, sizeof(mysk));
	mysk.SIN_FAMILY = AFINET;
	
	if ((aconf = find_me())->passwd && isdigit(*aconf->passwd))
#ifdef INET6
		if(!inet_pton(AF_INET6, aconf->passwd, mysk.sin6_addr.s6_addr))
			bcopy(minus_one, mysk.sin6_addr.s6_addr, IN6ADDRSZ);
#else
		mysk.sin_addr.s_addr = inetaddr(aconf->passwd);
#endif

	if (gethostname(name, len) == -1)
		return;
	name[len] = '\0';

	/* assume that a name containing '.' is a FQDN */
	if (!index(name,'.'))
		add_local_domain(name, len - strlen(name));

	/*
	** If hostname gives another name than cname, then check if there is
	** a CNAME record for cname pointing to hostname. If so accept
	** cname as our name.   meLazy
	*/
	if (BadPtr(cname))
		return;
	if ((hp = gethostbyname(cname)) || (hp = gethostbyname(name)))
	    {
		char	*hname;
		int	i = 0;

		for (hname = hp->h_name; hname; hname = hp->h_aliases[i++])
  		    {
			strncpyzt(tmp, hname, sizeof(tmp));
			add_local_domain(tmp, sizeof(tmp) - strlen(tmp));

			/*
			** Copy the matching name over and store the
			** 'primary' IP# as 'myip' which is used
			** later for making the right one is used
			** for connecting to other hosts.
			*/
			if (!mycmp(ME, tmp))
				break;
 		    }
		if (mycmp(ME, tmp))
			strncpyzt(name, hp->h_name, len);
		else
			strncpyzt(name, tmp, len);
		if (!aconf->passwd)
			bcopy(hp->h_addr, (char *)&mysk.SIN_ADDR,
			      sizeof(struct IN_ADDR));
		Debug((DEBUG_DEBUG,"local name is %s",
				get_client_name(&me,TRUE)));
	    }
	return;
}

/*
** setup a UDP socket and listen for incoming packets
*/
int	setup_ping(aconf)
aConfItem	*aconf;
{
	struct	SOCKADDR_IN	from;
	int	on = 1;

	if (udpfd != -1)
		return udpfd;
	bzero((char *)&from, sizeof(from));
	if (aconf->passwd && isdigit(*aconf->passwd))
#ifdef INET6
	  if(!inet_pton(AF_INET6, aconf->passwd,from.sin6_addr.s6_addr))
		bcopy(minus_one, from.sin6_addr.s6_addr, IN6ADDRSZ);
#else
	  from.sin_addr.s_addr = inetaddr(aconf->passwd);
#endif
	else
#ifdef INET6
	  from.SIN_ADDR = in6addr_any;
#else
	  from.sin_addr.s_addr = htonl(INADDR_ANY); /* hmmpf */
#endif
	from.SIN_PORT = htons((u_short) aconf->port);
	from.SIN_FAMILY = AFINET;

	if ((udpfd = socket(AFINET, SOCK_DGRAM, 0)) == -1)
	    {
		Debug((DEBUG_ERROR, "socket udp : %s", strerror(errno)));
		return -1;
	    }
	if (SETSOCKOPT(udpfd, SOL_SOCKET, SO_REUSEADDR, &on, on) == -1)
	    {
#ifdef	USE_SYSLOG
		syslog(LOG_ERR, "setsockopt udp fd %d : %m", udpfd);
#endif
		Debug((DEBUG_ERROR, "setsockopt so_reuseaddr : %s",
			strerror(errno)));
		(void)close(udpfd);
		return udpfd = -1;
	    }
	on = 0;
	(void) SETSOCKOPT(udpfd, SOL_SOCKET, SO_BROADCAST, &on, on);
	if (bind(udpfd, (SAP)&from, sizeof(from))==-1)
	    {
#ifdef	USE_SYSLOG
		syslog(LOG_ERR, "bind udp.%d fd %d : %m",
		       ntohs(from.SIN_PORT), udpfd);
#endif
		Debug((DEBUG_ERROR, "bind : %s", strerror(errno)));
		(void)close(udpfd);
		return udpfd = -1;
	    }
	if (fcntl(udpfd, F_SETFL, FNDELAY)==-1)
	    {
		Debug((DEBUG_ERROR, "fcntl fndelay : %s", strerror(errno)));
		(void)close(udpfd);
		return udpfd = -1;
	    }
	Debug((DEBUG_INFO, "udpfd = %d, port %d", udpfd,ntohs(from.SIN_PORT)));
	return udpfd;
}


void	send_ping(aconf)
aConfItem *aconf;
{
	Ping	pi;
	struct	SOCKADDR_IN	sin;
	aCPing	*cp = aconf->ping;

#ifdef INET6
	if (!aconf->ipnum.s6_addr || AND16(aconf->ipnum.s6_addr) == 255 || !cp->port)
#else
	if (!aconf->ipnum.s_addr || aconf->ipnum.s_addr == -1 || !cp->port)
#endif
		return;
	if (aconf->class->conFreq == 0) /* avoid flooding */
		return;
	pi.pi_cp = aconf;
	pi.pi_id = htonl(PING_CPING);
	pi.pi_seq = cp->lseq++;
	cp->seq++;
	/*
	 * Only recognise stats from the last 20 minutes as significant...
	 * Try and fake sliding along a "window" here.
	 */
	if (cp->seq > 1 && cp->seq * aconf->class->conFreq > 1200)
	    {
		if (cp->recvd)
		    {
			cp->ping -= (cp->ping / cp->recvd);
			if (cp->recvd == cp->seq)
				cp->recvd--;
		    }
		else
			cp->ping = 0;
		cp->seq--;
	    }

	bzero((char *)&sin, sizeof(sin));
#ifdef INET6
	bcopy(aconf->ipnum.s6_addr, sin.sin6_addr.s6_addr, IN6ADDRSZ);
#else
	sin.sin_addr.s_addr = aconf->ipnum.s_addr;
#endif
	sin.SIN_PORT = htons(cp->port);
	sin.SIN_FAMILY = AFINET;
	(void)gettimeofday(&pi.pi_tv, NULL);
#ifdef INET6
	Debug((DEBUG_SEND,"Send ping to %s,%d fd %d, %d bytes",
	       inet_ntop(AF_INET6, (char *)&aconf->ipnum,mydummy,MYDUMMY_SIZE),
	       cp->port, udpfd, sizeof(pi)));
#else
	Debug((DEBUG_SEND,"Send ping to %s,%d fd %d, %d bytes",
	       inetntoa((char *)&aconf->ipnum),
	       cp->port, udpfd, sizeof(pi)));
#endif
	(void)sendto(udpfd, (char *)&pi, sizeof(pi), 0,(SAP)&sin,sizeof(sin));
}

static	int	check_ping(buf, len)
char	*buf;
int	len;
{
	Ping	pi;
	aConfItem	*aconf;
	struct	timeval	tv;
	double	d;
	aCPing	*cp = NULL;
	u_long	rtt;

	(void)gettimeofday(&tv, NULL);

	if (len < sizeof(pi) + 8)
		return -1;

	bcopy(buf, (char *)&pi, sizeof(pi));	/* ensure nice byte align. */

	for (aconf = conf; aconf; aconf = aconf->next)
		if (pi.pi_cp == aconf && (cp = aconf->ping))
			break;
	if (!aconf || match(aconf->name, buf + sizeof(pi)))
		return -1;

	cp->recvd++;
	cp->lrecvd++;
	rtt = ((tv.tv_sec - pi.pi_tv.tv_sec) * 1000 +
		(tv.tv_usec - pi.pi_tv.tv_usec) / 1000);
	cp->ping += rtt;
	cp->rtt += rtt;
	if (cp->rtt > 1000000)
	    {
		cp->ping = (cp->rtt /= cp->lrecvd);
		cp->recvd = cp->lrecvd = 1;
		cp->seq = cp->lseq = 1;
	    }
	d = (double)cp->recvd / (double)cp->seq;
	d = pow(d, (double)20.0);
	d = (double)cp->ping / (double)cp->recvd / d;
	if (d > 10000.0)
		d = 10000.0;
	aconf->pref = (int) (d * 100.0);

	return 0;
}

/*
 * max # of pings set to 15/sec.
 */
static	void	polludp()
{
	static	time_t	last = 0;
	static	int	cnt = 0, mlen = 0, lasterr = 0;
	Reg	char	*s;
	struct	SOCKADDR_IN	from;
	Ping	pi;
	int	n;
	SOCK_LEN_TYPE fromlen = sizeof(from);

	/*
	 * find max length of data area of packet.
	 */
	if (!mlen)
	    {
		mlen = sizeof(readbuf) - strlen(ME) - strlen(version);
		mlen -= 6;
		if (mlen < 0)
			mlen = 0;
	    }
	Debug((DEBUG_DEBUG,"udp poll"));

	n = recvfrom(udpfd, readbuf, mlen, 0, (SAP)&from, &fromlen);
	if (n == -1)
	    {
		ircstp->is_udperr++;
		if ((errno == EWOULDBLOCK) || (errno == EAGAIN))
			return;
		else
		    {
			report_error("udp port recvfrom (%s): %s", &me);
			return;
		    }
	    }

	if (timeofday == last)
	    {
		if (++cnt > 14)
		    {
			if (timeofday > lasterr + 30)
			    {
				sendto_flag(SCH_NOTICE,
				    "udp packet dropped: %d bytes from %s.%d",
#ifdef INET6
					    n, inetntop(AF_INET6,
					 (char *)&from.sin6_addr, mydummy,
							 MYDUMMY_SIZE),
#else
					    n,inetntoa((char *)&from.sin_addr),
#endif
					    ntohs(from.SIN_PORT));
				lasterr = timeofday;
			    }
			ircstp->is_udpdrop++;
			return;
		    }
	    }
	else
		cnt = 0, last = timeofday;

#ifdef INET6
	Debug((DEBUG_NOTICE, "udp (%d) %d bytes from %s,%d", cnt, n,
	       inet_ntop(AF_INET6, (char *)&from.sin6_addr, mydummy,
			 MYDUMMY_SIZE),
	       ntohs(from.SIN_PORT)));
#else
	Debug((DEBUG_NOTICE, "udp (%d) %d bytes from %s,%d", cnt, n,
	       inetntoa((char *)&from.sin_addr),
	       ntohs(from.SIN_PORT)));
#endif

	readbuf[n] = '\0';
	ircstp->is_udpok++;
	if (n  < 8)
		return;

	bcopy(s = readbuf, (char *)&pi, MIN(n, sizeof(pi)));
	pi.pi_id = ntohl(pi.pi_id);
	Debug((DEBUG_INFO, "\tpi_id %#x pi_seq %d pi_cp %#x",
		pi.pi_id, pi.pi_seq, pi.pi_cp));

	if ((pi.pi_id == (PING_CPING|PING_REPLY) ||
	     pi.pi_id == (PING_CPING|(PING_REPLY << 24))) && n >= sizeof(pi))
	    {
		check_ping(s, n);
		return;
	    }
	else if (pi.pi_id & PING_REPLY)
		return;
	/*
	 * attach my name and version for the reply
	 */
	pi.pi_id |= PING_REPLY;
	pi.pi_id = htonl(pi.pi_id);
	bcopy((char *)&pi, s, MIN(n, sizeof(pi)));
	s += n;
	(void)strcpy(s, ME);
	s += strlen(s)+1;
	(void)strcpy(s, version);
	s += strlen(s);
	(void)sendto(udpfd, readbuf, s-readbuf, 0, (SAP)&from ,sizeof(from));
	return;
}

/*
 * do_dns_async
 *
 * Called when the fd returned from init_resolver() has been selected for
 * reading.
 */
static	void	do_dns_async()
{
	static	Link	ln;
	aClient	*cptr;
	aConfItem	*aconf;
	struct	hostent	*hp;
	int	bytes, pkts;

	pkts = 0;

	do {
		ln.flags = -1;
		hp = get_res((char *)&ln);

		Debug((DEBUG_DNS,"%#x = get_res(%d,%#x)", hp, ln.flags,
			ln.value.cptr));

		switch (ln.flags)
		{
		case ASYNC_NONE :
			/*
			 * no reply was processed that was outstanding or
			 * had a client still waiting.
			 */
			break;
		case ASYNC_CLIENT :
			if ((cptr = ln.value.cptr))
			    {
				del_queries((char *)cptr);
				ClearDNS(cptr);
				cptr->hostp = hp;
#if defined(USE_IAUTH)
				if (hp)
				    {
					int i = 0;

					while (hp->h_aliases[i])
						sendto_iauth("%d A %s",
							     cptr->fd,
							hp->h_aliases[i++]);
					if (hp->h_name)
						sendto_iauth("%d N %s",
							cptr->fd, hp->h_name);
					else if (hp->h_aliases[0])
						sendto_iauth("%d n", cptr->fd);
				    }
				else
					sendto_iauth("%d d", cptr->fd);
				if (iauth_options & XOPT_EXTWAIT)
					cptr->lasttime = timeofday;
#endif
			    }
			break;
		case ASYNC_CONNECT :
			aconf = ln.value.aconf;
			if (hp && aconf)
			    {
				bcopy(hp->h_addr, (char *)&aconf->ipnum,
				      sizeof(struct IN_ADDR));
				(void)connect_server(aconf, NULL, hp);
			    }
			else
				sendto_flag(SCH_ERROR,
					    "Connect to %s failed: host lookup",
					   (aconf) ? aconf->host : "unknown");
			break;
		case ASYNC_CONF :
			aconf = ln.value.aconf;
			if (hp && aconf)
				bcopy(hp->h_addr, (char *)&aconf->ipnum,
				      sizeof(struct IN_ADDR));
			break;
		default :
			break;
		}
		pkts++;
		if (ioctl(resfd, FIONREAD, &bytes) == -1)
			bytes = 0;
	} while ((bytes > 0) && (pkts < 10));
}
