/************************************************************************
 *   IRC - Internet Relay Chat, src/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.
 */



#include "struct.h"
#include "common.h"
#include "sys.h"
#include "res.h"
#include "numeric.h"
#include "patchlevel.h"
#include "zlink.h"
#include "throttle.h"
#include "userban.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#if defined(SOL20)
#include <sys/filio.h>
#include <sys/select.h>
#include <unistd.h>
#endif
#include "inet.h"
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <utmp.h>
#include <sys/resource.h>
#include "hooks.h"

#ifdef  AIX
#include <time.h>
#include <arpa/nameser.h>
#else
#include "nameser.h"
#endif
#include "resolv.h"
#include "memcount.h"

/* If FD_ZERO isn't define up to this point,
 * define it (BSD4.2 needs this) */

#include "h.h"
#include "fdlist.h"
#include "fds.h"

extern void      engine_init();
extern fdlist default_fdlist;
extern int forked;
extern void free_port(aPort *);

#ifndef IN_LOOPBACKNET
#define IN_LOOPBACKNET  0x7f
#endif

#if defined(MAXBUFFERS)
int rcvbufmax = 0, sndbufmax = 0;
#endif

#ifdef MAXBUFFERS
void reset_sock_opts(int, int);
#endif

static void set_listener_sock_opts(int, aListener *);
static void set_listener_non_blocking(int, aListener *);
/* listener list, count */
aListener *listen_list = NULL;
int listen_count = 0;


aClient *local[MAXCONNECTIONS];
int highest_fd = 0, resfd = -1;
time_t timeofday;
static struct sockaddr_in mysk;

static struct sockaddr *connect_inet(aConnect *, aClient *, int *);
static int check_init(aClient *, char *);
static void set_sock_opts(int, aClient *);

#if defined(MAXBUFFERS)
static char *readbuf;
#else
static char readbuf[8192];
#endif

/* Silly macro to ignore certain report error statements */
#define silent_report_error(x,y)


/*
 * 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(char *hname, int size)
{
#ifdef RES_INIT
    /* try to fix up unqualified name */
    if (!strchr(hname, '.')) 
    {
        if (size < 3)
            return;
        if (!(_res.options & RES_INIT))
        {
            Debug((DEBUG_DNS, "res_init()"));
            res_init();
        }
        if (_res.defdname[0])
        {
            strncat(hname, ".", size - 1);
            strncat(hname, _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(char *text, aClient * cptr)
{
    int errtmp = errno;     /* debug may change 'errno' */
    char *host;
    int err;
	socklen_t len = sizeof(err);
    extern char *strerror();

    host = (cptr) ? get_client_name(cptr, (IsServer(cptr) ? HIDEME : 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, (char *) &err, &len))
            if (err)
                errtmp = err;
#endif
    sendto_realops_lev(DEBUG_LEV, text, host, strerror(errtmp));
#ifdef USE_SYSLOG
    syslog(LOG_WARNING, text, host, strerror(errtmp));
    if (!forked)
    {
        fprintf(stderr, text, host, strerror(errtmp));
        fprintf(stderr, "\n");
    }
#endif
    return;
}

void report_listener_error(char *text, aListener *lptr)
{
    int errtmp = errno;          /* debug may change 'errno' */
    char *host;
    int err;
	socklen_t len = sizeof(err);
    extern char *strerror();

    host = get_listener_name(lptr);

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

#ifdef  SO_ERROR
    if (lptr->fd >= 0)
        if (!getsockopt(lptr->fd, SOL_SOCKET, SO_ERROR, (char *) &err, &len))
            if (err)
                errtmp = err;
#endif
    sendto_realops_lev(DEBUG_LEV, text, host, strerror(errtmp));
#ifdef USE_SYSLOG
    syslog(LOG_WARNING, text, host, strerror(errtmp));
#endif
    if (!forked) 
    {
        fprintf(stderr, text, host, strerror(errtmp));
        fprintf(stderr, "\n");
    }
    return;
}

/*
 * open_listeners()
 *
 * cycle through our entire ports list and open them if they
 * arent already open.
 * Added Feb/04 -epi
 */

void
open_listeners()
{
    aPort *tmp;
    if(!ports)
        sendto_realops("Lost all port configurations!");
    for(tmp = ports; tmp; tmp = tmp->next)
    {
        if(tmp->lstn)
            continue;
        add_listener(tmp);
    }
    return;
}

/*
 * 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).
 * Backported from defunct 1.6 and updated for aPort structure in Feb04.
 * I'm assuming lucas rewrote this originally. -epi
 */
int add_listener(aPort *aport)
{
    aListener *lptr;
    aListener lstn;
    struct sockaddr_in server;
    int ad[4];
	socklen_t len = sizeof(server);
    char ipname[20];

    memset(&lstn, 0, sizeof(aListener));
    ad[0] = ad[1] = ad[2] = ad[3] = 0;

    if(!BadPtr(aport->allow))
    {
        strncpyzt(lstn.allow_string, aport->allow, sizeof(lstn.allow_string));
        sscanf(lstn.allow_string, "%d.%d.%d.%d", &ad[0], &ad[1], 
                                                 &ad[2], &ad[3]);
        ircsprintf(ipname, "%d.%d.%d.%d", ad[0], ad[1], ad[2], ad[3]);
        lstn.allow_ip.s_addr = inet_addr(ipname);
    }

    if (!BadPtr(aport->address) && (*aport->address != '*'))
    {
        lstn.vhost_ip.s_addr = inet_addr(aport->address);
        strncpyzt(lstn.vhost_string, aport->address, sizeof(lstn.vhost_string));
    }

    lstn.port = aport->port;

    if(lstn.port <= 0) /* stop stupidity cold */
        return -1;

    lstn.fd = socket(AF_INET, SOCK_STREAM, 0);
    if (lstn.fd < 0)
    {
        report_listener_error("opening stream socket %s:%s", &lstn);
        return -1;
    }

    set_listener_sock_opts(lstn.fd, &lstn);

    memset(&server, '\0', sizeof(server));
    server.sin_family = AF_INET;

    if (lstn.vhost_ip.s_addr)
        server.sin_addr.s_addr = lstn.vhost_ip.s_addr;
    else
        server.sin_addr.s_addr = INADDR_ANY;

    server.sin_port = htons(lstn.port);

    if (bind(lstn.fd, (struct sockaddr *) &server, sizeof(struct sockaddr_in)))
    {
        report_listener_error("binding stream socket %s:%s", &lstn);
        close(lstn.fd);
        return -1;
    }

    if (getsockname(lstn.fd, (struct sockaddr *) &server, &len)) 
    {
        report_listener_error("getsockname failed for %s:%s", &lstn);
        close(lstn.fd);
        return -1;
    }

    if (lstn.fd > highest_fd)
        highest_fd = lstn.fd;

#ifdef SOMAXCONN
    if(listen(lstn.fd, SOMAXCONN))
#else
    if(listen(lstn.fd, HYBRID_SOMAXCONN))
#endif
    {
        report_listener_error("error listening on FD %s:%s", &lstn);
        close(lstn.fd);
        return -1;
    }


    lptr = (aListener *) MyMalloc(sizeof(aListener));
    memcpy(lptr, &lstn, sizeof(aListener));

    if(local[lptr->fd])
    {
        report_listener_error("!!!! listener fd is held by client"
                              " in local[] array %s:%s", &lstn);
        abort();
    }

    lptr->aport = aport;
    aport->lstn = lptr;

    set_listener_non_blocking(lptr->fd, lptr);
#ifdef HAVE_SSL
    if ((aport->ptype!=NULL) && (mycmp("SSL", aport->ptype)==0)) {
        SetSSL(lptr);
        lptr->ssl = NULL;
        lptr->client_cert = NULL;
    }
#endif
    add_fd(lptr->fd, FDT_LISTENER, lptr);
    set_fd_flags(lptr->fd, FDF_WANTREAD);

    listen_count++;
    lptr->next = listen_list;
    listen_list = lptr;

    lptr->lasttime = timeofday;

    return 0;
}

void close_listener(aListener *lptr)
{
    aListener *alptr, *alptrprev = NULL;
    aPort *aport, *aportl, *aportn = NULL;

    del_fd(lptr->fd);
    close(lptr->fd);

    /* drop our conf link */
    aport = lptr->aport;
    aport->lstn = NULL;

    /* and now drop the conf itself */

    for(aportl = ports ; aportl ; aportl = aportl->next)
    {
        if(aportl == aport)
        {
            if(aportn)
                aportn->next = aportl->next;
            else
                ports = aportl->next;
            free_port(aportl);
            break;
        }
        aportn = aportl;
    }

    /* now drop the listener */

    for(alptr = listen_list; alptr; alptr = alptr->next)
    {
        if(alptr == lptr)
        {
            if(alptrprev)
                alptrprev->next = alptr->next;
            else
                listen_list = alptr->next;
            MyFree(lptr);
            listen_count--;
            break;
        }
        alptrprev = alptr;
    }
}

/*
 * 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()
{
    aListener *lptr, *lptrnext;

    lptr = listen_list;

    while(lptr)
    {
        lptrnext = lptr->next;
        if(lptr->clients <= 0)
            close_listener(lptr);
        else                
            /* if we cant close it, mark it for closing
             * when we loose all of our connections */
            lptr->aport->legal = -1;
        lptr = lptrnext;
    }
}

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

#ifdef RLIMIT_FD_MAX
    struct rlimit limit;

    if (!getrlimit(RLIMIT_FD_MAX, &limit))
    {
        if (limit.rlim_max < MAXCONNECTIONS)
        {
            printf("FATAL: System is only allowing %ld open files.\n",
                   (long)limit.rlim_max);
            printf("ircd requires at least %ld.\n", (long)MAXCONNECTIONS);
            printf("Fix the system account limits or recompile ircd.\n");
            printf("Aborting...\n");
            exit(-1);
        }
        /* set limit to exactly what we can handle */
        limit.rlim_cur = MAXCONNECTIONS;
        if (setrlimit(RLIMIT_FD_MAX, &limit) == -1)
        {
            printf("FATAL: Unable to set open file limit to %ld:\n%s\n",
                   (long)limit.rlim_cur, strerror(errno));
            printf("Aborting...\n");
            exit(-1);
        }
    }
#endif

    printf("\nIrcd is now becoming a daemon.\n");

#if !defined(SOL20)
    setlinebuf(stderr);
#endif

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

    if (bootopt & BOOT_TTY)
    {
        engine_init();

        /* debugging is going to a tty */
        resfd = init_resolver(0x1f);
        add_fd(resfd, FDT_RESOLVER, NULL);
        set_fd_flags(resfd, FDF_WANTREAD);
        return;
    }

    close(1);

    if (!(bootopt & BOOT_DEBUG) && !(bootopt & BOOT_STDERR))
        close(2);


    if ((isatty(0)) && !(bootopt & BOOT_OPER) && !(bootopt & BOOT_STDERR))
    {
        int pid;

        if ((pid = fork()) < 0)
        {
            if ((fd = open("/dev/tty", O_RDWR)) >= 0)
            write(fd, "Couldn't fork!\n", 15);  /* crude, but effective */
            exit(0);
        } 
        else if (pid > 0)
            exit(0);

        setsid();

        close(0);       /* fd 0 opened by inetd */
        local[0] = NULL;
    }

    engine_init();
    resfd = init_resolver(0x1f);
    add_fd(resfd, FDT_RESOLVER, NULL);
    set_fd_flags(resfd, FDF_WANTREAD);
    return;
}

void write_pidfile()
{
#ifdef IRCD_PIDFILE
    int fd;
    char buff[20];

    if ((fd = open(IRCD_PIDFILE, O_CREAT | O_WRONLY, 0600)) >= 0)
    {
        ircsprintf(buff, "%5d\n", (int) getpid());
        if (write(fd, buff, strlen(buff)) == -1)
            Debug((DEBUG_NOTICE, "Error writing to pid file %s", IRCD_PIDFILE));
        close(fd);
        return;
    }
#ifdef  DEBUGMODE
    else
        Debug((DEBUG_NOTICE, "Error opening pid file %s", IRCD_PIDFILE));
#endif
#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(aClient * cptr, char *sockn)
{
    struct sockaddr_in sk;
    socklen_t len = sizeof(struct sockaddr_in);

    /* If descriptor is a tty, special checking... * IT can't EVER be a tty */

    if (getpeername(cptr->fd, (struct sockaddr *) &sk, &len) == -1)
        return -1;

    strcpy(sockn, (char *) inetntoa((char *) &sk.sin_addr));
    if (inet_netof(sk.sin_addr) == IN_LOOPBACKNET)
    {
        cptr->hostp = NULL;
        strncpyzt(sockn, me.sockhost, HOSTLEN);
    }
    memcpy((char *) &cptr->ip, (char *) &sk.sin_addr, 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 = Access denied -2 =
 * Bad socket.
 */
int check_client(aClient *cptr)
{
    static char sockname[HOSTLEN + 1];
    struct hostent *hp = NULL;
    int i;

    Debug((DEBUG_DNS, "ch_cl: check access for %s[%s]",
           cptr->name, inetntoa((char *) &cptr->ip)));

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

    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.
     */
    if (hp)
    {
        for (i = 0; hp->h_addr_list[i]; i++)
            if (!memcmp(hp->h_addr_list[i], (char *) &cptr->ip,
                    sizeof(struct in_addr))) 
                break;

        if (!hp->h_addr_list[i])
        {
            sendto_one(cptr, "NOTICE AUTH :*** Your forward and reverse"
                             " DNS do not match, ignoring hostname.");
            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));

    if (inet_netof(cptr->ip) == IN_LOOPBACKNET ||
    inet_netof(cptr->ip) == inet_netof(mysk.sin_addr))
    {
        ircstp->is_loc++;
        cptr->flags |= FLAGS_LOCAL;
    }
    return 0;
}

#define CFLAG   CONF_CONNECT_SERVER
#define NFLAG   CONF_NOCONNECT_SERVER

/*
 * 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.
 *
 * This was terrible code.  Terrible!  Almost fucking scary! Rewritten into
 * a single function, much prettier.  Feb04 -epi
 */
int check_server_init(aClient * cptr)
{
    aConnect *aconn = NULL;
    struct hostent *hp = NULL;
    char sockname[HOSTLEN + 1], fullname[HOSTLEN + 1];
    char abuff[HOSTLEN + USERLEN + 2];
    int i = 0, ok = 0;

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

    if (!(aconn = find_aConnect(cptr->name)))
    {
        Debug((DEBUG_DNS, "No Connect block for %s", cptr->name));
        return -1;
    }

    /* 
     * * 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 (!cptr->hostp)
    {
        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 cant
         * interrupt that very well.
         */
        lin.value.aconn = aconn;
        lin.flags = ASYNC_CONF;
        nextdnscheck = 1;
        if ((s = strchr(aconn->host, '@')))
            s++;
        else
            s = aconn->host;
        Debug((DEBUG_DNS, "sv_ci:cache lookup (%s)", s));
        if((hp = gethost_byname(s, &lin)))
        {
            for (i = 0; hp->h_addr_list[i]; i++)
                if (!memcmp(hp->h_addr_list[i], (char *) &cptr->ip, 
                        sizeof(struct in_addr)))
                    break;
            if (!hp->h_addr_list[i])
            {
                sendto_realops_lev(ADMIN_LEV,
                    "Server IP# Mismatch: %s != %s[%08x]",
                    inetntoa((char *) &cptr->ip), hp->h_name,
                    *((unsigned long *) hp->h_addr));
                hp = NULL;
            }
        }
    }
    else
    {
        hp = cptr->hostp;
        for (i = 0; hp->h_addr_list[i]; i++)
            if (!memcmp(hp->h_addr_list[i], (char *) &cptr->ip,
                    sizeof(struct in_addr)))
                break;
    }

    if(hp)
    {
        strncpyzt(fullname, cptr->name, sizeof(fullname));
        add_local_domain(fullname, HOSTLEN - strlen(fullname));
        Debug((DEBUG_DNS, "sv_cl: gethostbyaddr: %s->%s",
                sockname, fullname));
        ircsprintf(abuff, "%s@%s", cptr->username, fullname);
        get_sockhost(cptr, fullname);
        for (i = 0; hp->h_addr_list[i]; i++)
        {
            if(!memcmp((char *) &aconn->ipnum, (char *) hp->h_addr_list[i],
                        sizeof(struct in_addr)))
                ok = 1;
            else
                ok = 0;
        }
    }
    else
    {
        /* having no luck finding a host.. check against IP */
        if(!memcmp((char *) &aconn->ipnum, (char *) &cptr->ip,
                   sizeof(struct in_addr)))
            ok = 1;
        else
            ok = 0;
    }

    /* if they dont match up, then away with them */
    if (!ok)
    {
        get_sockhost(cptr, sockname);
        return -1;
    }
    /* check for Ulined access and link it if nessecary */
    if(find_aUserver(cptr->name))
        cptr->flags |= FLAGS_ULINE;
    make_server(cptr);
    cptr->serv->aconn = aconn;
    aconn->acpt = cptr;
    set_effective_class(cptr);

    if ((aconn->ipnum.s_addr == -1))
    memcpy((char *) &aconn->ipnum, (char *) &cptr->ip,
           sizeof(struct in_addr));

    get_sockhost(cptr, aconn->host);
    
    Debug((DEBUG_DNS, "sv_cl: access ok: %s[%s]", cptr->name, cptr->sockhost));
    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
 */
int completed_connection(aClient * cptr)
{
    aConnect *aconn;

    if(!(cptr->flags & FLAGS_BLOCKED))
        unset_fd_flags(cptr->fd, FDF_WANTWRITE);
    unset_fd_flags(cptr->fd, FDF_WANTREAD);

    SetHandshake(cptr);

    if (!(aconn = find_aConnect(cptr->name)))
    {
        sendto_realops("Lost Config for %s", get_client_name(cptr, HIDEME));
        return -1;
    }
    if (!BadPtr(aconn->cpasswd))
        sendto_one(cptr, "PASS %s :TS", aconn->cpasswd);

    /* pass on our capabilities to the server we /connect'd */
#ifdef HAVE_ENCRYPTION_ON
    if(!(aconn->flags & CONN_DKEY))
        sendto_one(cptr, "CAPAB SSJOIN NOQUIT BURST UNCONNECT ZIP"
                         " NICKIP TSMODE");
    else
    {
        sendto_one(cptr, "CAPAB SSJOIN NOQUIT BURST UNCONNECT DKEY"
                         " ZIP NICKIP TSMODE");
        SetWantDKEY(cptr);
    }
#else
    sendto_one(cptr, "CAPAB SSJOIN NOQUIT BURST UNCONNECT ZIP NICKIP TSMODE");
#endif

    if(aconn->flags & CONN_ZIP)
        cptr->capabilities |= CAPAB_DOZIP;

    sendto_one(cptr, "SERVER %s 1 :%s", my_name_for_link(me.name, aconn), 
                                        me.info);
#ifdef DO_IDENTD
    /* Is this the right place to do this?  dunno... -Taner */
    if (!IsDead(cptr))
        start_auth(cptr);
#endif

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

/*
 * close_connection 
 * Close the physical connection. This function must make 
 * MyConnect(cptr) == FALSE, and set cptr->from == NULL.
 */
void close_connection(aClient *cptr)
{
    aConnect *aconn;

    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 > 2047)
        {
            ircstp->is_sks += (ircstp->is_sbs >> 10);
            ircstp->is_sbs &= 0x3ff;
        }
        if (ircstp->is_sbr > 2047)
        {
            ircstp->is_skr += (ircstp->is_sbr >> 10);
            ircstp->is_sbr &= 0x3ff;
        }
        /* schedule a quick reconnect if we've been connected a long time */
        if((aconn = find_aConnect_match(cptr->name, cptr->username,
                                    cptr->sockhost)))
        {
            aconn->hold = time(NULL);
            aconn->hold += (aconn->hold - cptr->since > HANGONGOODLINK) ?
                HANGONRETRYDELAY : aconn->class->connfreq;
            if (nextconnect > aconn->hold)
                nextconnect = aconn->hold;
        }
    } 
    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 > 2047)
        {
            ircstp->is_cks += (ircstp->is_cbs >> 10);
            ircstp->is_cbs &= 0x3ff;
        }
        if (ircstp->is_cbr > 2047)
        {
            ircstp->is_ckr += (ircstp->is_cbr >> 10);
            ircstp->is_cbr &= 0x3ff;
        }
    } 
    else
        ircstp->is_ni++;

    /* remove outstanding DNS queries. */
    del_queries((char *) cptr);

    if (cptr->authfd >= 0)
    {
        del_fd(cptr->authfd);
        close(cptr->authfd);
        cptr->authfd = -1;
    }

    if (cptr->fd >= 0)
    {
        dump_connections(cptr->fd);
        local[cptr->fd] = NULL;
#ifdef HAVE_SSL
        if(IsSSL(cptr) && cptr->ssl) {
            SSL_set_shutdown(cptr->ssl, SSL_RECEIVED_SHUTDOWN);
            SSL_smart_shutdown(cptr->ssl);
            SSL_free(cptr->ssl);
            cptr->ssl = NULL;
        }
#endif
        del_fd(cptr->fd);
        close(cptr->fd);
        cptr->fd = -2;
        SBufClear(&cptr->sendQ);
        SBufClear(&cptr->recvQ);
        memset(cptr->passwd, '\0', sizeof(cptr->passwd));
        if(cptr->lstn)
            cptr->lstn->clients--;
    }
    for (; highest_fd > 0; highest_fd--)
    if (local[highest_fd])
        break;

    clear_conflinks(cptr);

    cptr->from = NULL;      /* ...this should catch them! >:) --msa */

    /* if we're the last socket open on this listener,
     * check to make sure the listener is even supposed to be
     * open, and close it if its not -epi
     */
     if (cptr->lstn && (cptr->lstn->clients <= 0) && 
                (cptr->lstn->aport->legal == -1))
        close_listener(cptr->lstn);

    return;
}

#ifdef MAXBUFFERS
/* reset_sock_opts type =  0 = client, 1 = server */
void reset_sock_opts(int fd, int type)
{
#define CLIENT_BUFFER_SIZE  4096
#define SEND_BUF_SIZE       2920
    int opt;

    opt = type ? rcvbufmax : CLIENT_BUFFER_SIZE;
    if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &opt, sizeof(opt)) < 0) 
        sendto_realops("REsetsockopt(SO_RCVBUF) for fd %d (%s) failed",
                        fd, type ? "server" : "client");
    opt = type ? sndbufmax : SEND_BUF_SIZE;
    if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &opt, sizeof(opt)) < 0) 
        sendto_realops("REsetsockopt(SO_SNDBUF) for fd %d (%s) failed",
                        fd, type ? "server" : "client");
}
#endif              /* MAXBUFFERS */

/* set_sock_opts */
static void set_sock_opts(int fd, aClient * cptr)
{
     socklen_t opt;
    
#ifdef SO_REUSEADDR
    opt = 1;
    if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (char *) &opt,
            sizeof(opt)) < 0)
        silent_report_error("setsockopt(SO_REUSEADDR) %s:%s", cptr);
#endif
#if  defined(SO_DEBUG) && defined(DEBUGMODE) && 0
    /* Solaris with SO_DEBUG writes to syslog by default */
#if !defined(SOL20) || defined(USE_SYSLOG)
    opt = 1;
    if (setsockopt(fd, SOL_SOCKET, SO_DEBUG, (char *) &opt, sizeof(opt)) < 0)
        silent_report_error("setsockopt(SO_DEBUG) %s:%s", cptr);
#endif              /* SOL20 */
#endif
#ifdef  SO_USELOOPBACK
    opt = 1;
    if (setsockopt(fd, SOL_SOCKET, SO_USELOOPBACK, (char *) &opt,
           sizeof(opt)) < 0)
        silent_report_error("setsockopt(SO_USELOOPBACK) %s:%s", cptr);
#endif
#ifdef  SO_RCVBUF
#if defined(MAXBUFFERS)
    if (rcvbufmax == 0)
    {
        socklen_t optlen;

        optlen = sizeof(rcvbufmax);
        getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &rcvbufmax, &optlen);
        while ((rcvbufmax < 16385) && (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, 
               (char *) (char *) &rcvbufmax,optlen) >= 0))
            rcvbufmax += 1024;
        getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &rcvbufmax, &optlen);
        readbuf = (char *) MyMalloc(rcvbufmax * sizeof(char));
    }
    if (IsServer(cptr))
        opt = rcvbufmax;
    else
        opt = 4096;
#else
    opt = 8192;
#endif
    if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &opt, sizeof(opt)) < 0)
        silent_report_error("setsockopt(SO_RCVBUF) %s:%s", cptr);
#endif
#ifdef  SO_SNDBUF
#if defined(MAXBUFFERS)
    if (sndbufmax == 0)
    {
        socklen_t optlen;
    
        optlen = sizeof(sndbufmax);
        getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &sndbufmax, &optlen);
        while ((sndbufmax < 16385) && (setsockopt (fd, SOL_SOCKET, SO_SNDBUF,
                           (char *) &sndbufmax, optlen) >= 0))
            sndbufmax += 1024;
        getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &sndbufmax, &optlen);
    }
    if (IsServer(cptr))
        opt = sndbufmax;
    else
        opt = 4096;
#else
    opt = 8192;
#endif
    if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &opt, sizeof(opt)) < 0)
        silent_report_error("setsockopt(SO_SNDBUF) %s:%s", cptr);
#endif
#if defined(IP_OPTIONS) && defined(IPPROTO_IP)
    {
#if defined(MAXBUFFERS)
        char *s = readbuf, *t = readbuf + (rcvbufmax * sizeof(char)) / 2;
        opt = (rcvbufmax * sizeof(char)) / 8;
#else
        char *s = readbuf, *t = readbuf + sizeof(readbuf) / 2;
    
        opt = sizeof(readbuf) / 8;
#endif
        if (getsockopt(fd, IPPROTO_IP, IP_OPTIONS, t, &opt) < 0)
            silent_report_error("getsockopt(IP_OPTIONS) %s:%s", cptr);
        else if (opt > 0)
        {
            for (*readbuf = '\0'; opt > 0; opt--, s += 3)
                ircsprintf(s, "%02.2x:", *t++);
            *s = '\0';
            sendto_realops("Connection %s using IP opts: (%s)",
                            get_client_name(cptr, HIDEME), readbuf);
        }
        if (setsockopt(fd, IPPROTO_IP, IP_OPTIONS, (char *) NULL, 0) < 0)
            silent_report_error("setsockopt(IP_OPTIONS) %s:%s", cptr);
    }
#endif
}

static void set_listener_sock_opts(int fd, aListener *lptr)
{
    int opt;

#ifdef SO_REUSEADDR
    opt = 1;
    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, 
                   sizeof(opt)) < 0)
        report_listener_error("setsockopt(SO_REUSEADDR) %s:%s", lptr);
#endif
#if  defined(SO_DEBUG) && defined(DEBUGMODE) && 0
   /*
    * Solaris with SO_DEBUG writes to syslog by default
    */
#if !defined(SOL20) || defined(USE_SYSLOG)
    opt = 1;
    if (setsockopt(fd, SOL_SOCKET, SO_DEBUG, (char *) &opt, sizeof(opt)) < 0) 
        report_listener_error("setsockopt(SO_DEBUG) %s:%s", lptr);
#endif                         /* SOL20 */
#endif
#ifdef  SO_USELOOPBACK
    opt = 1;
    if (setsockopt (fd, SOL_SOCKET, SO_USELOOPBACK, (char *) &opt, 
                    sizeof(opt)) < 0)
        report_listener_error("setsockopt(SO_USELOOPBACK) %s:%s", lptr);
#endif
#ifdef  SO_RCVBUF
# if defined(MAXBUFFERS)
    if (rcvbufmax == 0) 
    {
        socklen_t optlen;

        optlen = sizeof(rcvbufmax);
        getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &rcvbufmax, &optlen);
        while ((rcvbufmax < 16385) && (setsockopt (fd, SOL_SOCKET, SO_RCVBUF, 
               (char *) (char *) &rcvbufmax, optlen) >= 0)) 
            rcvbufmax += 1024;
        getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &rcvbufmax, &optlen);
        readbuf = (char *) MyMalloc(rcvbufmax * sizeof(char));
    }
    opt = 4096;
# else
    opt = 8192;
# endif
    if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &opt, sizeof(opt)) < 0) 
        report_listener_error("setsockopt(SO_RCVBUF) %s:%s", lptr);
#endif
#ifdef  SO_SNDBUF
#if defined(MAXBUFFERS)
    if (sndbufmax == 0) 
    {
        socklen_t optlen;

        optlen = sizeof(sndbufmax);
        getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &sndbufmax, &optlen);
        while ((sndbufmax < 16385) && (setsockopt (fd, SOL_SOCKET, SO_SNDBUF, 
                (char *) &sndbufmax, optlen) >= 0)) 
            sndbufmax += 1024;
        getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &sndbufmax, &optlen);
    }
    opt = 4096;
#else
    opt = 8192;
#endif
    if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &opt, sizeof(opt)) < 0)
        report_listener_error("setsockopt(SO_SNDBUF) %s:%s", lptr);
#endif
}


int get_sockerr(aClient * cptr)
{
    int errtmp = errno, err = 0;
	socklen_t len = sizeof(err);
    
#ifdef  SO_ERROR
    if (cptr->fd >= 0)
    if (!getsockopt(cptr->fd, SOL_SOCKET, SO_ERROR, (char *) &err, &len))
        if (err)
            errtmp = err;
#endif
    return errtmp;
}

char *irc_get_sockerr(aClient *cptr)
{
    if(cptr->sockerr == 0)
        return "No error";
    
    if(cptr->sockerr > 0)
        return strerror(cptr->sockerr);
    
    switch(cptr->sockerr)
    { 
        case -1: /* this is the default */
            return "Unset error message [this is a bug!]";
        case IRCERR_BUFALLOC:
            return "dbuf allocation error";
        case IRCERR_ZIP:
            return "compression general failure";
        default:
            return "Unknown error!";
    }
    
    /* unreachable code, but the compiler is complaining.. */
    return NULL;
}

/*
 * * set_non_blocking 
 *       Set the client connection into non-blocking mode. 
 */
void set_non_blocking(int fd, aClient * cptr)
{
    int res, nonb = 0;

    nonb |= O_NONBLOCK;
    if ((res = fcntl(fd, F_GETFL, 0)) == -1)
        silent_report_error("fcntl(fd, F_GETFL) failed for %s:%s", cptr);
    else if (fcntl(fd, F_SETFL, res | nonb) == -1)
        silent_report_error("fcntl(fd, F_SETL, nonb) failed for %s:%s", cptr);
    return;
}

void set_listener_non_blocking(int fd, aListener *lptr)
{
    int res, nonb = 0;

    nonb |= O_NONBLOCK;
    if ((res = fcntl(fd, F_GETFL, 0)) == -1)
        report_listener_error("fcntl(fd, F_GETFL) failed for %s:%s", lptr);
    else if (fcntl(fd, F_SETFL, res | nonb) == -1)
        report_listener_error("fcntl(fd, F_SETL, nonb) failed for %s:%s", lptr);
    return;
}


/*
 * 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 yuet since it doesnt have a name.
 */
aClient *add_connection(aListener *lptr, int fd)
{
    Link lin;
    aClient *acptr = NULL;
    char *s, *t;
    struct sockaddr_in addr;
    socklen_t len = sizeof(struct sockaddr_in);
    struct userBan *ban;
    
    if (getpeername(fd, (struct sockaddr *) &addr, &len) == -1)
    { 
        ircstp->is_ref++;
        close(fd);
        return NULL;
    }

    acptr = make_client(NULL, &me);

    /* 
     * Copy ascii address to 'sockhost' just in case. Then we have
     * something valid to put into error messages...
     */
    get_sockhost(acptr, (char *) inetntoa((char *) &addr.sin_addr));
    memcpy((char *) &acptr->ip, (char *) &addr.sin_addr,
    sizeof(struct in_addr));
    
    acptr->port = ntohs(addr.sin_port);
    /* 
     * Check that this socket (client) is allowed to accept
     * connections from this IP#.
     */
    for (s = (char *) &lptr->allow_ip, t = (char *) &acptr->ip, len = 4;
         len > 0; len--, s++, t++)
    {
        if (!*s)
            continue;
        if (*s != *t)
            break;
    }
    if (len)
    {
        ircstp->is_ref++;
        acptr->fd = -2;
        free_client(acptr);
        close(fd);
        return NULL;
    }

    lptr->ccount++;
    lptr->clients++;
    Count.unknown++;
    add_fd(fd, FDT_CLIENT, acptr);
    local[fd] = acptr;

    acptr->fd = fd;
    if (fd > highest_fd)
        highest_fd = fd;

    /* sockets inherit the options of their parents.. do we need these? */
    set_non_blocking(acptr->fd, acptr);
    set_sock_opts(acptr->fd, acptr);

    acptr->lstn = lptr;
    add_client_to_list(acptr);

    ban = check_userbanned(acptr, UBAN_IP|UBAN_CIDR4|UBAN_WILDUSER, 0);
    if(ban)
    {
        char *reason, *ktype;
        int local;

        local = (ban->flags & UBAN_LOCAL) ? 1 : 0;
        ktype = local ? LOCAL_BANNED_NAME : NETWORK_BANNED_NAME;
        reason = ban->reason ? ban->reason : ktype;

        sendto_one(acptr, err_str(ERR_YOUREBANNEDCREEP), me.name, "*", ktype);
        sendto_one(acptr, ":%s NOTICE * :*** You are not welcome on this %s.",
                   me.name, local ? "server" : "network");
        sendto_one(acptr, ":%s NOTICE * :*** %s for %s", 
                   me.name, ktype, reason);
        sendto_one(acptr, ":%s NOTICE * :*** Your IP is %s",
                   me.name, inetntoa((char *)&acptr->ip.s_addr));
        sendto_one(acptr, ":%s NOTICE * :*** For assistance, please email %s"
                          " and include everything shown here.", me.name, 
                   local ? Local_Kline_Address : Network_Kline_Address);

        ircstp->is_ref++;
        ircstp->is_ref_1++;
        throttle_force(inetntoa((char *)&acptr->ip.s_addr));
        exit_client(acptr, acptr, &me, reason);

        return NULL;
    }

    if(call_hooks(CHOOK_PREACCESS, acptr) == FLUSH_BUFFER)
        return NULL;

#ifdef SHOW_HEADERS
    sendto_one(acptr, REPORT_DO_DNS);
#endif
    lin.flags = ASYNC_CLIENT;
    lin.value.cptr = acptr;
    Debug((DEBUG_DNS, "lookup %s", inetntoa((char *) &addr.sin_addr)));
    acptr->hostp = gethost_byaddr((char *) &acptr->ip, &lin);
    if (!acptr->hostp)
        SetDNS(acptr);
#ifdef SHOW_HEADERS
    else
        sendto_one(acptr, REPORT_FIN_DNSC);
#endif
    nextdnscheck = 1;
    
#ifdef DO_IDENTD
    start_auth(acptr);
#endif
    check_client_fd(acptr);
#ifdef HAVE_SSL
    if (IsSSL(lptr))
    {
        acptr->ssl = NULL;
        /* If for some reason we fail to create a new SSL object, 
         * notify the debug ops and reject the client. */
        if((acptr->ssl = SSL_new(CTX_Server)) == NULL)
        {
            sendto_realops_lev(DEBUG_LEV, "SSL_new() object failed [client %s]", acptr->sockhost); 
            ircstp->is_ref++;
            acptr->fd = -2;
            free_client(acptr);
            return NULL ;
        }
        SetSSL(acptr);               
        SSL_set_fd((SSL *)acptr->ssl, fd);
        set_non_blocking(fd, acptr);
        set_sock_opts(fd, acptr);
        if (!safe_SSL_accept(acptr, fd))
	    {
            SSL_set_shutdown(acptr->ssl, SSL_RECEIVED_SHUTDOWN);
            SSL_smart_shutdown(acptr->ssl);
            SSL_free(acptr->ssl);
            ircstp->is_ref++;
            acptr->fd = -2;
            free_client(acptr);
            (void) close(fd);
            return NULL;
	    }
    }
#endif
    return acptr;
}

/* handle taking care of the client's recvq here */
int do_client_queue(aClient *cptr)
{
    int dolen = 0, done;
    
    while (SBufLength(&cptr->recvQ) && !NoNewLine(cptr) &&
       ((cptr->status < STAT_UNKNOWN) || (cptr->since - timeofday < 10) ||
        IsNegoServer(cptr))) 
    {
        /* If it's become registered as a server, just parse the whole block */
        if (IsServer(cptr) || IsNegoServer(cptr)) 
        {
#if defined(MAXBUFFERS)
            dolen = sbuf_get(&cptr->recvQ, readbuf, rcvbufmax * sizeof(char));
#else
            dolen = sbuf_get(&cptr->recvQ, readbuf, sizeof(readbuf));
#endif
            if (dolen <= 0)
                break;
            if ((done = dopacket(cptr, readbuf, dolen)))
                return done;
            break;
        }
#if defined(MAXBUFFERS)
        dolen = sbuf_getmsg(&cptr->recvQ, readbuf, rcvbufmax * sizeof(char));
#else
        dolen = sbuf_getmsg(&cptr->recvQ, readbuf, sizeof(readbuf));
#endif
    
        if (dolen <= 0) 
        {
            if (dolen < 0)
                return exit_client(cptr, cptr, cptr, "sbuf_getmsg fail");
        
            if (SBufLength(&cptr->recvQ) < 510) 
            {
                cptr->flags |= FLAGS_NONL;
                break;
            }
            /* The buffer is full (more than 512 bytes) and it has no \n
             * Some user is trying to trick us. Kill their recvq. */
            SBufClear(&cptr->recvQ);
            break;
        }
        else if(client_dopacket(cptr, readbuf, dolen) == FLUSH_BUFFER)
            return FLUSH_BUFFER;
    }

    if(!(cptr->flags & FLAGS_HAVERECVQ) && SBufLength(&cptr->recvQ) && 
        !NoNewLine(cptr))
    {
       add_to_list(&recvq_clients, cptr);
       cptr->flags |= FLAGS_HAVERECVQ;
    }

    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
 */

#define MAX_CLIENT_RECVQ 8192

int read_packet(aClient * cptr)
{
    int length = 0, done;

    /* If data is ready, and the user is either not a person or
     * is a person and has a recvq of less than MAX_CLIENT_RECVQ,
     * read from this client
     */ 
    if (!(IsPerson(cptr) && SBufLength(&cptr->recvQ) > MAX_CLIENT_RECVQ)) 
    {
        errno = 0;
    
#ifdef HAVE_SSL
#if defined ( MAXBUFFERS )
        if (IsPerson(cptr))
            length = RECV_CHECK_SSL(cptr, readbuf, 8192 * sizeof(char));
        else
            length = RECV_CHECK_SSL(cptr, readbuf, rcvbufmax * sizeof(char));
#else
            length = RECV_CHECK_SSL(cptr, readbuf, sizeof(readbuf));
#endif

#else

#if defined(MAXBUFFERS)
        if (IsPerson(cptr))
            length = recv(cptr->fd, readbuf, 8192 * sizeof(char), 0);
        else
            length = recv(cptr->fd, readbuf, rcvbufmax * sizeof(char), 0);
#else
        length = recv(cptr->fd, readbuf, sizeof(readbuf), 0);
#endif

#endif   
        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)
        {
            cptr->sockerr = length ? errno : 0;
            return length;
        }
    }

    /* 
     * 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) ||
        IsNegoServer(cptr)) 
    {
        if (length > 0)
            if ((done = dopacket(cptr, readbuf, length)))
                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 (sbuf_put(&cptr->recvQ, readbuf, length) < 0)
            return exit_client(cptr, cptr, cptr, "sbuf_put fail");
    
        if (IsPerson(cptr) &&
#ifdef NO_OPER_FLOOD
        !IsAnOper(cptr) &&
#endif
        SBufLength(&cptr->recvQ) > CLIENT_FLOOD)
        {
            sendto_realops_lev(FLOOD_LEV, "Flood -- %s!%s@%s (%d) Exceeds %d"
                               " RecvQ", cptr->name[0] ? cptr->name : "*",
                               cptr->user ? cptr->user->username : "*",
                               cptr->user ? cptr->user->host : "*",
                               SBufLength(&cptr->recvQ), CLIENT_FLOOD);
            return exit_client(cptr, cptr, cptr, "Excess Flood");
        }
        return do_client_queue(cptr);
    }
    return 1;
}

void read_error_exit(aClient *cptr, int length, int err)
{
    char fbuf[512];
    char errmsg[512];
    
    if (IsServer(cptr) || IsHandshake(cptr) || IsConnecting(cptr)) 
    {
        if (length == 0) 
        {
            char *errtxt = "Server %s closed the connection";
        
            ircsprintf(fbuf, "from %s: %s", me.name, errtxt);
            sendto_gnotice(fbuf, get_client_name(cptr, HIDEME));
            ircsprintf(fbuf, ":%s GNOTICE :%s", me.name, errtxt);
            sendto_serv_butone(cptr, fbuf, get_client_name(cptr, HIDEME));
        }
        else 
        {
            char *errtxt = (IsConnecting(cptr) || IsHandshake(cptr)) ? 
                            "Connect error to %s (%s)" : 
                            "Read error from %s, closing link (%s)";

            ircsprintf(fbuf, "from %s: %s", me.name, errtxt);
            sendto_gnotice(fbuf, get_client_name(cptr, HIDEME), strerror(err));
            ircsprintf(fbuf, ":%s GNOTICE :%s", me.name, errtxt);
            sendto_serv_butone(cptr, fbuf, get_client_name(cptr, HIDEME), 
                               strerror(err));
        }
    }
    
    if (err)
        ircsprintf(errmsg, "Read error: %s", strerror(err));
    else
        ircsprintf(errmsg, "Client closed connection");
    
    exit_client(cptr, cptr, &me, errmsg);
}

void accept_connection(aListener *lptr)
{
    static struct sockaddr_in addr;
    socklen_t addrlen = sizeof(struct sockaddr_in);
    char host[HOSTLEN + 2];
    int newfd;
    int i;
    
    lptr->lasttime = timeofday;
    
    for (i = 0; i < 100; i++) /* accept up to 100 times per call 
                               * to deal with high connect rates */
    {
        if((newfd = accept(lptr->fd, (struct sockaddr *) &addr, &addrlen)) < 0) 
        {
            switch(errno)
            {
#ifdef EMFILE
                case EMFILE:
                    report_listener_error("Cannot accept connections %s:%s", 
                                           lptr);
                    break;
#endif
#ifdef ENFILE
                case ENFILE:
                    report_listener_error("Cannot accept connections %s:%s", 
                                           lptr);
                    break;
#endif
            }
        return;
        }

        strncpyzt(host, (char *) inetntoa((char *) &addr.sin_addr), 
                  sizeof(host));

        /* if they are throttled, drop them silently. */
        if (throttle_check(host, newfd, NOW) == 0) 
        {
            ircstp->is_ref++;
            ircstp->is_throt++;
            close(newfd);
            return;
        }

        if (newfd >= MAX_ACTIVECONN) 
        {
            ircstp->is_ref++;
            sendto_realops_lev(CCONN_LEV,"All connections in use. fd: %d (%s)",
                newfd,get_listener_name(lptr));
            send(newfd, "ERROR :All connections in use\r\n", 32, 0);
            close(newfd);
            return;
        }
        if((lptr->aport->legal == -1))
        {
            ircstp->is_ref++;
            send(newfd, "ERROR :This port is closed\r\n", 29, 0);
            close(newfd);
            return;
        }
        ircstp->is_ac++;

        add_connection(lptr, newfd);
#ifdef PINGNAZI
        nextping = timeofday;
#endif
    }
}

int readwrite_client(aClient *cptr, int isread, int iswrite)
{
    /*
     * NOTE
     * We now do this in a more logical way.
     * We request a write poll on a socket for two reasons
     * - the socket is waiting for a connect() call
     * - the socket is blocked
     */

#ifdef HAVE_SSL
    if (cptr->ssl != NULL && IsSSL(cptr) && !SSL_is_init_finished(cptr->ssl))
    {
        if(IsDead(cptr) || (!safe_SSL_accept(cptr, cptr->fd)))
            close_connection(cptr);
        return 1;
    }
#endif

    if(iswrite)
    {
        if (IsConnecting(cptr) && completed_connection(cptr))
        {
            char errmsg[512];

            ircsprintf(errmsg, "Connect Error: %s", irc_get_sockerr(cptr));
            return exit_client(cptr, cptr, &me, errmsg);
        }

        if(cptr->flags & FLAGS_BLOCKED)
        {
            cptr->flags &= ~FLAGS_BLOCKED;
            unset_fd_flags(cptr->fd, FDF_WANTWRITE);
        }
        else 
        {
            /* this may be our problem with occational 100% cpu looping
             * we've experienced.  jason suggested this, here we will try
             * this and see if it happens at all -epi */
            sendto_realops_lev(DEBUG_LEV, "Removing socket %d: reported ready"
                               " for write, but not blocking", cptr->fd);
            /* This unset_fd_flags() does not appear to make any difference
             * to the write set.  The socket appears stuck, and there has
             * to be a reason for it.  Since we're experiencing a very low
             * number of these, simply drop the client entirely, and treat
             * this as a socket handling error.  This is essentially a kludge
             * however tracking down this bug will take a serious amount of
             * time and testing - since its not easily reproducable.  This 
             * will in the meantime prevent maxing the CPU.  -epi 
             *
             * unset_fd_flags(cptr->fd, FDF_WANTWRITE);
             */
            exit_client(cptr, cptr, &me, "Socket error (write)");
            return FLUSH_BUFFER;
        }

    }

    if (isread)
    {
        int length = read_packet(cptr);

        if(length == FLUSH_BUFFER)
            return length;

        if(length <= 0)
        {
            read_error_exit(cptr, length, cptr->sockerr);
            return FLUSH_BUFFER;
        }
    }

    if (IsDead(cptr))
    {
        char errmsg[512];

        ircsprintf(errmsg, "Write Error: %s", (cptr->flags & FLAGS_SENDQEX) ?
                   "SendQ Exceeded" : irc_get_sockerr(cptr));
        return exit_client(cptr, cptr, &me, errmsg);
    }

    return 1;
}

/* connect_server */
int connect_server(aConnect *aconn, aClient * by, struct hostent *hp)
{
    struct sockaddr *svp;
    aClient *cptr, *c2ptr;
    char *s;
    int errtmp, len;

    Debug((DEBUG_NOTICE, "Connect to %s[%s] @%s", aconn->name, aconn->host, 
                                    inetntoa((char *) &aconn->ipnum)));
    
    if ((c2ptr = find_server(aconn->name, NULL)))
    {
        sendto_ops("Server %s already present from %s",
                    aconn->name, get_client_name(c2ptr, HIDEME));
        if (by && IsPerson(by) && !MyClient(by))
            sendto_one(by, ":%s NOTICE %s :Server %s already present from %s",
                       me.name, by->name, aconn->name,
                       get_client_name(c2ptr, HIDEME));
        return -1;
    }
    /* 
     * If we dont know the IP# for this host and itis a hostname and not
     * a ip# string, then try and find the appropriate host record.
     */
    if ((!aconn->ipnum.s_addr))
    {
        Link lin;

        lin.flags = ASYNC_CONNECT;
        lin.value.aconn = aconn;
        nextdnscheck = 1;
        s = (char *) strchr(aconn->host, '@');
        s++;            /* should NEVER be NULL */
        if ((aconn->ipnum.s_addr = inet_addr(s)) == -1) 
        {
            aconn->ipnum.s_addr = 0;
            hp = gethost_byname(s, &lin);
            Debug((DEBUG_NOTICE, "co_sv: hp %x ac %x na %s ho %s",
                                 hp, aconn, aconn->name, s));
            if (!hp)
                return 0;
            memcpy((char *) &aconn->ipnum, hp->h_addr, sizeof(struct in_addr));
        }
    }
    cptr = make_client(NULL, &me);
    cptr->hostp = hp;
    /* Copy these in so we have something for error detection. */
    strncpyzt(cptr->name, aconn->name, sizeof(cptr->name));
    strncpyzt(cptr->sockhost, aconn->host, HOSTLEN + 1);
    svp = connect_inet(aconn, cptr, &len);

    if (!svp)
    {
        if (cptr->fd >= 0)
            close(cptr->fd);
        cptr->fd = -2;
        free_client(cptr);
        return -1;
    }
    
    set_non_blocking(cptr->fd, cptr);
    set_sock_opts(cptr->fd, cptr);
    signal(SIGALRM, dummy);
    if (connect(cptr->fd, svp, len) < 0 && errno != EINPROGRESS) 
    {
        errtmp = errno;     /* other system calls may eat errno */
        report_error("Connect to host %s failed: %s", cptr);
        if (by && IsPerson(by) && !MyClient(by))
            sendto_one(by, ":%s NOTICE %s :Connect to server %s failed.",
                       me.name, by->name, cptr->name);
        close(cptr->fd);
        cptr->fd = -2;
        free_client(cptr);
        errno = errtmp;
        if (errno == EINTR)
            errno = ETIMEDOUT;
        return -1;
    }
    
    make_server(cptr);
    cptr->serv->aconn = aconn;
    
    /* The socket has been connected or connect is in progress. */
    if (by && IsPerson(by))
    {
        strcpy(cptr->serv->bynick, by->name);
        strcpy(cptr->serv->byuser, by->user->username);
        strcpy(cptr->serv->byhost, by->user->host);
    }
    else
    {
        strcpy(cptr->serv->bynick, "AutoConn.");
        *cptr->serv->byuser = '\0';
        *cptr->serv->byhost = '\0';
    }
    cptr->serv->up = me.name;
    if (cptr->fd > highest_fd)
        highest_fd = cptr->fd;
    local[cptr->fd] = cptr;
    SetConnecting(cptr);

    get_sockhost(cptr, aconn->host);
    add_client_to_list(cptr);
#ifdef PINGNAZI
    nextping = timeofday;
#endif

    add_fd(cptr->fd, FDT_CLIENT, cptr);
    cptr->flags |= FLAGS_BLOCKED;
    set_fd_flags(cptr->fd, FDF_WANTREAD|FDF_WANTWRITE);

    return 0;
}

static struct sockaddr *
connect_inet(aConnect *aconn, aClient *cptr, int *lenp)
{
    static struct sockaddr_in server;
    struct hostent *hp;
    struct sockaddr_in sin;
    
    /* 
     * Might as well get sockhost from here, the connection is attempted
     * with it so if it fails its useless.
     */
    cptr->fd = socket(AF_INET, SOCK_STREAM, 0);
    if (cptr->fd >= MAX_ACTIVECONN)
    {
        sendto_realops("No more connections allowed (%s)", cptr->name);
        return NULL;
    }
    memset((char *) &server, '\0', sizeof(server));
    memset((char *) &sin, '\0', sizeof(sin));
    server.sin_family = sin.sin_family = AF_INET;
    get_sockhost(cptr, aconn->host);

    if (aconn->source)
        sin.sin_addr.s_addr = inet_addr(aconn->source);

    if (cptr->fd < 0)
    {
        report_error("opening stream socket to server %s:%s", cptr);
        cptr->fd = -2;
        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#.
     * 
     * No we don't bind it, not all OS's can handle connecting with an
     * already bound socket, different ip# might occur anyway leading to
     * a freezing select() on this side for some time.
     */
    if (aconn->source)
    {
        /* 
         * * No, we do bind it if we have virtual host support. If we
         * don't explicitly bind it, it will default to IN_ADDR_ANY and
         * we lose due to the other server not allowing our base IP
         * --smg
         */
        if (bind(cptr->fd, (struct sockaddr *) &sin, sizeof(sin)) == -1)
        {
            report_error("error binding to local port for %s:%s", cptr);
            close(cptr->fd);
            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 dont know it, then the connect
     * fails.
     */
    if (IsDigit(*aconn->host) && (aconn->ipnum.s_addr == -1))
        aconn->ipnum.s_addr = inet_addr(aconn->host);
    if (aconn->ipnum.s_addr == -1)
    {
        hp = cptr->hostp;
        if (!hp)
        {
            Debug((DEBUG_FATAL, "%s: unknown host", aconn->host));
            return NULL;
        }
        memcpy((char *) &aconn->ipnum, hp->h_addr, sizeof(struct in_addr));
    }
    memcpy((char *) &server.sin_addr, (char *) &aconn->ipnum, 
            sizeof(struct in_addr));
    
    memcpy((char *) &cptr->ip, (char *) &aconn->ipnum,
            sizeof(struct in_addr));

    server.sin_port = htons((aconn->port > 0) ? aconn->port : PORTNUM);
    *lenp = sizeof(server);
    return (struct sockaddr *) &server;
}

/*
 * 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(aClient * cptr, char *name, int len)
{
    static char tmp[HOSTLEN + 1];
    struct hostent *hp;

    /* 
     * The following conflicts with both AIX and linux prototypes oh
     * well, we can put up with the errors from other systems -Dianora
     */

    char *cname = cptr->name;
    
    /* Setup local socket structure to use for binding to. */
    memset((char *) &mysk, '\0', sizeof(mysk));
    mysk.sin_family = AF_INET;

    if (gethostname(name, len) == -1)
        return;
    name[len] = '\0';
    
    /* assume that a name containing '.' is a FQDN */
    if (!strchr(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.name, tmp))
                break;
        }
        if (mycmp(me.name, tmp))
            strncpyzt(name, hp->h_name, len);
        else
            strncpyzt(name, tmp, len);
        memcpy((char *) &mysk.sin_addr, hp->h_addr, sizeof(struct in_addr));

        Debug((DEBUG_DEBUG, "local name is %s", get_client_name(&me, TRUE)));
    }
    return;
}

/*
 * do_dns_async
 *
 * Called when the fd returned from init_resolver() has been selected for
 * reading.
 */
void do_dns_async()
{
    static Link ln;
    aClient *cptr;
    aConnect *aconn;
    struct hostent *hp;
    int bytes, packets = 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);
#ifdef SHOW_HEADERS
                    sendto_one(cptr, REPORT_FIN_DNS);
#endif
                    ClearDNS(cptr);
                    cptr->hostp = hp;
                    check_client_fd(cptr);
                }
                break;
            case ASYNC_CONNECT:
                aconn = ln.value.aconn;
                if (hp && aconn)
                {
                    memcpy((char *) &aconn->ipnum, hp->h_addr,
                           sizeof(struct in_addr));
        
                    connect_server(aconn, NULL, hp);
                } 
                else
                    sendto_ops("Connect to %s failed: host lookup",
                                (aconn) ? aconn->host : "unknown");
                break;
            case ASYNC_CONF:
                aconn = ln.value.aconn;
                if (hp && aconn)
                    memcpy((char *) &aconn->ipnum, hp->h_addr,
                            sizeof(struct in_addr));

                break;
            default:
                break;
        }
        if (ioctl(resfd, FIONREAD, &bytes) == -1)
            bytes = 0;
        packets++;
    } while ((bytes > 0) && (packets < 512));
}

u_long
memcount_s_bsd(MCs_bsd *mc)
{
    aListener   *lptr;

    mc->file = __FILE__;

    for (lptr = listen_list; lptr; lptr = lptr->next)
    {
        mc->listeners.c++;
        mc->listeners.m += sizeof(*lptr);
    }
    mc->total.c += mc->listeners.c;
    mc->total.m += mc->listeners.m;

    mc->s_local.c = sizeof(local)/sizeof(local[0]);
    mc->s_local.m = sizeof(local);

    mc->s_readbuf.c = 1;
#ifndef MAXBUFFERS
    mc->s_readbuf.m = sizeof(readbuf);
#else
    mc->s_readbuf.m = rcvbufmax;
    mc->total.c += mc->s_readbuf.c;
    mc->total.m += mc->s_readbuf.m;
#endif

    return mc->total.m;
}



syntax highlighted by Code2HTML, v. 0.9.1