added basic ssl support to ircu
[ircu2.10.12-pk.git] / ircd / s_bsd.c
index b38f61b493831fcf56a8c9da87dc2b112bb88b69..5fd6d9193eb1ed0e1d55fd2727754abfcb05ee4a 100644 (file)
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
+/** @file
+ * @brief Functions that now (or in the past) relied on BSD APIs.
+ * @version $Id$
+ */
+#include "config.h"
 
-#include "sys.h"
-#include <stdlib.h>
-#include <sys/socket.h>
-#if HAVE_SYS_FILE_H
-#include <sys/file.h>
-#endif
-#if HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-#ifdef SOL2
-#include <sys/filio.h>
-#endif
-#ifdef UNIXPORT
-#include <sys/un.h>
-#endif
-#include <stdio.h>
-#ifdef USE_POLL
-#ifndef HAVE_POLL_H
-#undef USE_POLL
-#else /* HAVE_POLL_H */
-#ifdef HAVE_STROPTS_H
-#include <stropts.h>
-#endif
-#include <poll.h>
-#endif /* HAVE_POLL_H */
-#endif /* USE_POLL */
-#include <signal.h>
-#if HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <sys/stat.h>
-#include <utmp.h>
-#include <sys/resource.h>
-#ifdef USE_SYSLOG
-#include <syslog.h>
-#endif
-#include <sys/utsname.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <arpa/nameser.h>
-#include <resolv.h>
-#include "h.h"
-#include "res.h"
-#include "struct.h"
-#include "s_bsd.h"
-#include "s_serv.h"
-#include "numeric.h"
-#include "send.h"
-#include "s_conf.h"
-#include "s_misc.h"
 #include "s_bsd.h"
+#include "client.h"
+#include "IPcheck.h"
+#include "channel.h"
+#include "class.h"
 #include "hash.h"
-#include "s_err.h"
+#include "ircd_alloc.h"
+#include "ircd_log.h"
+#include "ircd_features.h"
+#include "ircd_osdep.h"
+#include "ircd_reply.h"
+#include "ircd_snprintf.h"
+#include "ircd_string.h"
 #include "ircd.h"
-#include "support.h"
-#include "s_auth.h"
-#include "class.h"
+#include "list.h"
+#include "listener.h"
+#include "msg.h"
+#include "msgq.h"
+#include "numeric.h"
+#include "numnicks.h"
 #include "packet.h"
-#include "s_ping.h"
-#include "channel.h"
-#include "version.h"
 #include "parse.h"
-#include "common.h"
-#include "bsd.h"
-#include "numnicks.h"
-#include "s_user.h"
-#include "sprintf_irc.h"
 #include "querycmds.h"
-#include "IPcheck.h"
+#include "res.h"
+#include "s_auth.h"
+#include "s_conf.h"
+#include "s_debug.h"
+#include "s_misc.h"
+#include "s_user.h"
+#include "send.h"
+#include "ssl.h"
+#include "struct.h"
+#include "sys.h"
+#include "uping.h"
+#include "version.h"
 
-RCSTAG_CC("$Id$");
-
-#ifndef IN_LOOPBACKNET
-#define IN_LOOPBACKNET 0x7f
-#endif
-
-aClient *loc_clients[MAXCONNECTIONS];
-int highest_fd = 0, udpfd = -1, resfd = -1;
-unsigned int readcalls = 0;
-static struct sockaddr_in mysk;
-static void polludp();
-
-static struct sockaddr *connect_inet(aConfItem *, aClient *, int *);
-static int completed_connection(aClient *);
-static int check_init(aClient *, char *);
-static void do_dns_async(), set_sock_opts(int, aClient *);
-#ifdef UNIXPORT
-static struct sockaddr *connect_unix(aConfItem *, aClient *, int *);
-static void add_unixconnection(aClient *, int);
-static char unixpath[256];
-#endif
-static char readbuf[8192];
-#ifdef USE_POLL
-static struct pollfd poll_fds[MAXCONNECTIONS + 1];
-static aClient *poll_cptr[MAXCONNECTIONS + 1];
-#endif /* USE_POLL */
-struct sockaddr_in vserv;      /* Default address/interface to bind listen sockets to.
-                                   This is set with the -w commandline option OR whatever
-                                  the name in the M: line resolves to OR INADDR_ANY. */
-struct sockaddr_in cserv;      /* Default address/interface to bind connecting sockets to.
-                                  This is set with the -w commandline option OR whatever
-                                  the name in the M: line resolves to OR the first
-                                  interface specified in the ircd.conf file for the
-                                  server port. */
-static int running_in_background;
-
-#ifdef GODMODE
-#ifndef NOFLOODCONTROL
-#define NOFLOODCONTROL
-#endif
-#endif
+/* #include <assert.h> -- Now using assert in ircd_log.h */
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+/** Array of my own clients, indexed by file descriptor. */
+struct Client*            LocalClientArray[MAXCONNECTIONS];
+/** Maximum file descriptor in current use. */
+int                       HighestFd = -1;
+/** Default local address for outbound IPv4 connections. */
+struct irc_sockaddr       VirtualHost_v4;
+/** Default local address for outbound IPv6 connections. */
+struct irc_sockaddr       VirtualHost_v6;
+/** Temporary buffer for reading data from a peer. */
+static char               readbuf[SERVER_TCP_WINDOW];
 
 /*
- * 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
-
-#if !defined(USE_POLL)
-#if FD_SETSIZE < (MAXCONNECTIONS + 4)
-/*
- * Sanity check
- *
- * All operating systems work when MAXCONNECTIONS <= 252.
- * Most operating systems work when MAXCONNECTIONS <= 1020 and FD_SETSIZE is
- *   updated correctly in the system headers (on BSD systems our sys.h has
- *   defined FD_SETSIZE to MAXCONNECTIONS+4 before including the system's headers 
- *   but sys/types.h might have abruptly redefined it so the check is still 
- *   done), you might already need to recompile your kernel.
- * For larger FD_SETSIZE your milage may vary (kernel patches may be needed).
- * The check is _NOT_ done if we will not use FD_SETS at all (USE_POLL)
+ * report_error text constants
  */
-#error "FD_SETSIZE is too small or MAXCONNECTIONS too large."
-#endif
-#endif
+const char* const ACCEPT_ERROR_MSG    = "error accepting connection for %s: %s";
+const char* const BIND_ERROR_MSG      = "bind error for %s: %s";
+const char* const CONNECT_ERROR_MSG   = "connect to host %s failed: %s";
+const char* const CONNLIMIT_ERROR_MSG = "connect limit exceeded for %s: %s";
+const char* const LISTEN_ERROR_MSG    = "listen error for %s: %s";
+const char* const NONB_ERROR_MSG      = "error setting non-blocking for %s: %s";
+const char* const PEERNAME_ERROR_MSG  = "getpeername failed for %s: %s";
+const char* const POLL_ERROR_MSG      = "poll error for %s: %s";
+const char* const REGISTER_ERROR_MSG  = "registering %s: %s";
+const char* const REUSEADDR_ERROR_MSG = "error setting SO_REUSEADDR for %s: %s";
+const char* const SELECT_ERROR_MSG    = "select error for %s: %s";
+const char* const SETBUFS_ERROR_MSG   = "error setting buffer size for %s: %s";
+const char* const SOCKET_ERROR_MSG    = "error creating socket for %s: %s";
+const char* const TOS_ERROR_MSG              = "error setting TOS for %s: %s";
+
+
+static void client_sock_callback(struct Event* ev);
+static void client_timer_callback(struct Event* ev);
+
 
 /*
  * Cannot use perror() within daemon. stderr is closed in
@@ -175,1339 +112,549 @@ static int running_in_background;
  * 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.
+/** Replacement for perror(). Record error to log.  Send a copy to all
+ * *LOCAL* opers, but only if no errors were sent to them in the last
+ * 20 seconds.
+ * @param text 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].
+ * @param who The client associated with the error.
+ * @param err The errno value to display.
  */
-void report_error(char *text, aClient *cptr)
+void report_error(const char* text, const char* who, int err)
 {
-  Reg1 int errtmp = errno;     /* debug may change 'errno' */
-  Reg2 char *host;
-#if defined(SO_ERROR) && !defined(SOL2)
-  int err;
-  size_t len = sizeof(err);
-#endif
+  static time_t last_notice = 0;
+  int           errtmp = errno;   /* debug may change 'errno' */
+  const char*   errmsg = (err) ? strerror(err) : "";
 
-  host = (cptr) ? get_client_name(cptr, FALSE) : "";
+  if (!errmsg)
+    errmsg = "Unknown error"; 
 
-  Debug((DEBUG_ERROR, text, host, strerror(errtmp)));
+  if (EmptyString(who))
+    who = "unknown";
 
-  /*
-   * 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.
-   */
-#if defined(SO_ERROR) && !defined(SOL2)
-  if (cptr && !IsMe(cptr) && cptr->fd >= 0)
-    if (!getsockopt(cptr->fd, SOL_SOCKET, SO_ERROR, (OPT_TYPE *)&err, &len))
-      if (err)
-       errtmp = err;
-#endif
-  sendto_ops(text, host, strerror(errtmp));
-#ifdef USE_SYSLOG
-  syslog(LOG_WARNING, text, host, strerror(errtmp));
-#endif
-  if (!running_in_background)
-  {
-    fprintf(stderr, text, host, strerror(errtmp));
-    fprintf(stderr, "\n");
-    fflush(stderr);
-  }
-  return;
+  sendto_opmask_butone_ratelimited(0, SNO_OLDSNO, &last_notice, text, who, errmsg);
+  log_write(LS_SOCKET, L_ERROR, 0, text, who, errmsg);
+  errno = errtmp;
 }
 
-/*
- * inetport
- *
- * Create a socket in the AF_INET domain, bind it to the port given in
- * 'port' and listen to it.  Connections are accepted to this socket
- * depending on the IP# mask given by 'name'.  Returns the fd of the
- * socket created or -1 on error.
+
+/** Called when resolver query finishes.  If the DNS lookup was
+ * successful, start the connection; otherwise notify opers of the
+ * failure.
+ * @param vptr The struct ConfItem representing the Connect block.
+ * @param hp A pointer to the DNS lookup results (NULL on failure).
  */
-int inetport(aClient *cptr, char *name, char *bind_addr, unsigned short int port)
+static void connect_dns_callback(void* vptr, const struct irc_in_addr *addr, const char *h_name)
 {
-  unsigned short int sin_port;
-  int ad[4], opt;
-  char ipname[20];
-
-#ifdef TESTNET
-    sin_port = htons(port + 10000);
-#else
-    sin_port = htons(port);
-#endif
-
-  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 "*" to 0.0.0.0 or 134.* to 134.0.0.0 :-)
-   */
-  sscanf(name, "%d.%d.%d.%d", &ad[0], &ad[1], &ad[2], &ad[3]);
-  sprintf_irc(ipname, "%d.%d.%d.%d", ad[0], ad[1], ad[2], ad[3]);
-
-  if (cptr != &me)
-  {
-    sprintf(cptr->sockhost, "%-.42s.%u", name, port);
-    strcpy(cptr->name, me.name);
+  struct ConfItem* aconf = (struct ConfItem*) vptr;
+  assert(aconf);
+  aconf->dns_pending = 0;
+  if (addr) {
+    memcpy(&aconf->address, addr, sizeof(aconf->address));
+    connect_server(aconf, 0);
   }
-  /*
-   * At first, open a new socket
-   */
-  if (cptr->fd == -1)
-  {
-    alarm(2);
-    cptr->fd = socket(AF_INET, SOCK_STREAM, 0);
-    alarm(0);
-    if (cptr->fd < 0 && errno == EAGAIN)
-    {
-      sendto_ops("opening stream socket %s: No more sockets",
-         get_client_name(cptr, TRUE));
-      return -1;
-    }
-  }
-  if (cptr->fd < 0)
-  {
-    report_error("opening stream socket %s: %s", cptr);
-    return -1;
-  }
-  else if (cptr->fd >= MAXCLIENTS)
-  {
-    sendto_ops("No more connections allowed (%s)", cptr->name);
-    close(cptr->fd);
-    return -1;
-  }
-
-  opt = 1;
-  setsockopt(cptr->fd, SOL_SOCKET, SO_REUSEADDR, (OPT_TYPE *)&opt, sizeof(opt));
-
-  /*
-   * 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)
-  {
-    struct sockaddr_in bindaddr;
-    memset(&bindaddr, 0, sizeof(struct sockaddr_in));
-    if (*bind_addr == '*' && bind_addr[1] == 0)
-      bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);    /* Bind to all interfaces */
-    else if (*bind_addr)
-    {
-      bindaddr.sin_addr.s_addr = inet_addr(bind_addr); /* Use name from P: line */
-      /* If server port and bind_addr isn't localhost: */
-      if (port == portnum && strcmp("127.0.0.1", bind_addr))
-        cserv.sin_addr.s_addr = bindaddr.sin_addr.s_addr;      /* Initialize /connect port */
-    }
-    else
-      bindaddr.sin_addr = vserv.sin_addr;              /* Default */
-    bindaddr.sin_family = AF_INET;
-    bindaddr.sin_port = sin_port;
-    if (bind(cptr->fd, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) == -1)
-    {
-      report_error("binding stream socket %s: %s", cptr);
-      close(cptr->fd);
-      return -1;
-    }
-  }
-
-  if (cptr == &me)             /* KLUDGE to get it work... */
-  {
-    char buf[1024];
-    sprintf_irc(buf, rpl_str(RPL_MYPORTIS), me.name, "*", port);
-    write(1, buf, strlen(buf));
-  }
-  if (cptr->fd > highest_fd)
-    highest_fd = cptr->fd;
-  cptr->ip.s_addr = inet_addr(ipname);
-  cptr->port = port;
-  listen(cptr->fd, 128);       /* Use listen port backlog of 128 */
-  loc_clients[cptr->fd] = cptr;
-
-  return 0;
+  else
+    sendto_opmask_butone(0, SNO_OLDSNO, "Connect to %s failed: host lookup",
+                         aconf->name);
 }
 
-#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).
+/** Closes all file descriptors.
+ * @param close_stderr If non-zero, also close stderr.
  */
-int unixport(aClient *cptr, char *path, unsigned short int port)
+void close_connections(int close_stderr)
 {
-  struct sockaddr_un un;
-
-  alarm(2);
-  cptr->fd = socket(AF_UNIX, SOCK_STREAM, 0);
-  alarm(0);
-  if (cptr->fd == -1 && errno == EAGAIN)
-  {
-    sendto_ops("error opening unix domain socket %s: No more sockets",
-       get_client_name(cptr, TRUE));
-    return -1;
-  }
-  if (cptr->fd == -1)
-  {
-    report_error("error opening unix domain socket %s: %s", cptr);
-    return -1;
-  }
-  else if (cptr->fd >= MAXCLIENTS)
-  {
-    sendto_ops("No more connections allowed (%s)", cptr->name);
-    close(cptr->fd);
-    cptr->fd = -1;
-    return -1;
-  }
-
-  un.sun_family = AF_UNIX;
-#if HAVE_MKDIR
-  mkdir(path, 0755);
-#else
-  if (chmod(path, 0755) == -1)
-  {
-    sendto_ops("error 'chmod 0755 %s': %s", path, strerror(errno));
-#ifdef USE_SYSLOG
-    syslog(LOG_WARNING, "error 'chmod 0755 %s': %s", path, strerror(errno));
-#endif
-    close(cptr->fd);
-    cptr->fd = -1;
-    return -1;
-  }
-#endif
-  sprintf_irc(unixpath, "%s/%u", path, port);
-  unlink(unixpath);
-  strncpy(un.sun_path, unixpath, sizeof(un.sun_path) - 1);
-  un.sun_path[sizeof(un.sun_path) - 1] = 0;
-  strcpy(cptr->name, me.name);
-  errno = 0;
-  get_sockhost(cptr, unixpath);
-
-  if (bind(cptr->fd, (struct sockaddr *)&un, strlen(unixpath) + 2) == -1)
+  int i;
+  if (close_stderr)
   {
-    report_error("error binding unix socket %s: %s", cptr);
-    close(cptr->fd);
-    return -1;
+    close(0);
+    close(1);
+    close(2);
   }
-  if (cptr->fd > highest_fd)
-    highest_fd = cptr->fd;
-  listen(cptr->fd, 5);
-  chmod(unixpath, 0777);
-  cptr->flags |= FLAGS_UNIX;
-  cptr->port = 0;
-  loc_clients[cptr->fd] = cptr;
-
-  return 0;
+  for (i = 3; i < MAXCONNECTIONS; ++i)
+    close(i);
 }
-#endif
 
-/*
- * 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).
+/** Initialize process fd limit to MAXCONNECTIONS.
  */
-int add_listener(aConfItem *aconf)
+int init_connection_limits(void)
 {
-  aClient *cptr;
-
-  cptr = make_client(NULL, STAT_ME);
-  cptr->flags = FLAGS_LISTEN;
-  cptr->acpt = cptr;
-  cptr->from = cptr;
-  strncpy(cptr->name, aconf->host, sizeof(cptr->name) - 1);
-  cptr->name[sizeof(cptr->name) - 1] = 0;
-#ifdef UNIXPORT
-  if (*aconf->host == '/')
-  {
-    if (unixport(cptr, aconf->host, aconf->port))
-      cptr->fd = -2;
+  int limit = os_set_fdlimit(MAXCONNECTIONS);
+  if (0 == limit)
+    return 1;
+  if (limit < 0) {
+    fprintf(stderr, "error setting max fds to %d: %s\n", limit, strerror(errno));
   }
-  else
-#endif
-  if (inetport(cptr, aconf->host, aconf->passwd, aconf->port))
-    cptr->fd = -2;
-
-  if (cptr->fd >= 0)
-  {
-    cptr->confs = make_link();
-    cptr->confs->next = NULL;
-    cptr->confs->value.aconf = aconf;
-    set_non_blocking(cptr->fd, cptr);
-    if (aconf->port == portnum)
-      have_server_port = 1;
+  else if (limit > 0) {
+    fprintf(stderr, "ircd fd table too big\nHard Limit: %d IRC max: %d\n",
+            limit, MAXCONNECTIONS);
+    fprintf(stderr, "set MAXCONNECTIONS to a smaller value");
   }
-  else
-    free_client(cptr);
   return 0;
 }
 
-/*
- * 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.
+/** Set up address and port and make a connection.
+ * @param aconf Provides the connection information.
+ * @param cptr Client structure for the peer.
+ * @return Non-zero on success; zero on failure.
  */
-void close_listeners(void)
+static int connect_inet(struct ConfItem* aconf, struct Client* cptr)
 {
-  Reg1 aClient *cptr;
-  Reg2 int i;
-  Reg3 aConfItem *aconf;
+  const struct irc_sockaddr *local;
+  IOResult result;
+  int family = 0;
 
+  assert(0 != aconf);
+  assert(0 != cptr);
   /*
-   * close all 'extra' listening ports we have and unlink the file
-   * name if it was a unix socket.
+   * Might as well get sockhost from here, the connection is attempted
+   * with it so if it fails its useless.
    */
-  for (i = highest_fd; i >= 0; i--)
-  {
-    if (!(cptr = loc_clients[i]))
-      continue;
-    if (!IsMe(cptr) || cptr == &me || !IsListening(cptr))
-      continue;
-    aconf = cptr->confs->value.aconf;
-
-    if (IsIllegal(aconf) && aconf->clients == 0)
-    {
-#ifdef UNIXPORT
-      if (IsUnixSocket(cptr))
-      {
-       sprintf_irc(unixpath, "%s/%u", aconf->host, aconf->port);
-       unlink(unixpath);
-      }
-#endif
-      close_connection(cptr);
-    }
-  }
-}
-
-/*
- * init_sys
- */
-void init_sys(void)
-{
-  Reg1 int fd;
-#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_FD_MAX)
-  struct rlimit limit;
+  if (irc_in_addr_valid(&aconf->origin.addr))
+    local = &aconf->origin;
+  else if (irc_in_addr_is_ipv4(&aconf->address.addr)) {
+    local = &VirtualHost_v4;
+    family = AF_INET;
+  } else
+    local = &VirtualHost_v6;
+  cli_fd(cptr) = os_socket(local, SOCK_STREAM, cli_name(cptr), family);
+  if (cli_fd(cptr) < 0)
+    return 0;
 
-  if (!getrlimit(RLIMIT_FD_MAX, &limit))
-  {
-#ifdef pyr
-    if (limit.rlim_cur < MAXCONNECTIONS)
-#else
-    if (limit.rlim_max < MAXCONNECTIONS)
-#endif
-    {
-      fprintf(stderr, "ircd fd table too big\n");
-      fprintf(stderr, "Hard Limit: " LIMIT_FMT " IRC max: %d\n",
-#ifdef pyr
-         limit.rlim_cur,
-#else
-         limit.rlim_max,
-#endif
-         (int)MAXCONNECTIONS);
-      fprintf(stderr, "Fix MAXCONNECTIONS\n");
-      exit(-1);
-    }
-#ifndef pyr
-    limit.rlim_cur = limit.rlim_max;   /* make soft limit the max */
-    if (setrlimit(RLIMIT_FD_MAX, &limit) == -1)
-    {
-      fprintf(stderr, "error setting max fd's to " LIMIT_FMT "\n",
-         limit.rlim_cur);
-      exit(-1);
-    }
-#endif
-  }
-#endif /* defined(HAVE_SETRLIMIT) && defined(RLIMIT_FD_MAX) */
-#ifdef DEBUGMODE
-  if (1)
-  {
-    static char logbuf[BUFSIZ];
-#if SETVBUF_REVERSED
-    setvbuf(stderr, _IOLBF, logbuf, sizeof(logbuf));
-#else
-    setvbuf(stderr, logbuf, _IOLBF, sizeof(logbuf));
-#endif
+  /*
+   * save connection info in client
+   */
+  memcpy(&cli_ip(cptr), &aconf->address.addr, sizeof(cli_ip(cptr)));
+  ircd_ntoa_r(cli_sock_ip(cptr), &cli_ip(cptr));
+  /*
+   * we want a big buffer for server connections
+   */
+  if (!os_set_sockbufs(cli_fd(cptr), feature_int(FEAT_SOCKSENDBUF), feature_int(FEAT_SOCKRECVBUF))) {
+    cli_error(cptr) = errno;
+    report_error(SETBUFS_ERROR_MSG, cli_name(cptr), errno);
+    close(cli_fd(cptr));
+    cli_fd(cptr) = -1;
+    return 0;
   }
-#endif
-
-  for (fd = 3; fd < MAXCONNECTIONS; fd++)
-  {
-    close(fd);
-    loc_clients[fd] = NULL;
+  /*
+   * Set the TOS bits - this is nonfatal if it doesn't stick.
+   */
+  if (!os_set_tos(cli_fd(cptr), feature_int(FEAT_TOS_SERVER))) {
+    report_error(TOS_ERROR_MSG, cli_name(cptr), errno);
   }
-  loc_clients[1] = NULL;
-  close(1);
-
-  if (bootopt & BOOT_TTY)      /* debugging is going to a tty */
-    goto init_dgram;
-  if (!(bootopt & BOOT_DEBUG))
-    close(2);
-
-  if (((bootopt & BOOT_CONSOLE) || isatty(0)) &&
-      !(bootopt & BOOT_INETD))
-  {
-    if (fork())
-      exit(0);
-    running_in_background = 1;
-#ifdef TIOCNOTTY
-    if ((fd = open("/dev/tty", O_RDWR)) >= 0)
-    {
-      ioctl(fd, TIOCNOTTY, (char *)NULL);
-      close(fd);
-    }
-#endif
-#if defined(HPUX) || defined(SOL2) || defined(_SEQUENT_) || \
-    defined(_POSIX_SOURCE) || defined(SVR4)
-    setsid();
-#else
-    setpgid(0, 0);
-#endif
-    close(0);                  /* fd 0 opened by inetd */
-    loc_clients[0] = NULL;
+  if ((result = os_connect_nonb(cli_fd(cptr), &aconf->address)) == IO_FAILURE) {
+    cli_error(cptr) = errno;
+    report_error(CONNECT_ERROR_MSG, cli_name(cptr), errno);
+    close(cli_fd(cptr));
+    cli_fd(cptr) = -1;
+    return 0;
   }
-init_dgram:
-  resfd = init_resolver();
-
-  return;
-}
-
-void write_pidfile(void)
-{
-#ifdef PPATH
-  int fd;
-  char buff[20];
-  if ((fd = open(PPATH, O_CREAT | O_WRONLY, 0600)) >= 0)
-  {
-    memset(buff, 0, sizeof(buff));
-    sprintf(buff, "%5d\n", (int)getpid());
-    if (write(fd, buff, strlen(buff)) == -1)
-      Debug((DEBUG_NOTICE, "Error writing to pid file %s", PPATH));
-    close(fd);
-    return;
+  
+  if (!socket_add(&(cli_socket(cptr)), client_sock_callback,
+                 (void*) cli_connect(cptr),
+                 (result == IO_SUCCESS) ? SS_CONNECTED : SS_CONNECTING,
+                 SOCK_EVENT_READABLE, cli_fd(cptr))) {
+    cli_error(cptr) = ENFILE;
+    report_error(REGISTER_ERROR_MSG, cli_name(cptr), ENFILE);
+    close(cli_fd(cptr));
+    cli_fd(cptr) = -1;
+    return 0;
   }
-#ifdef DEBUGMODE
-  else
-    Debug((DEBUG_NOTICE, "Error opening pid file \"%s\": %s",
-       PPATH, strerror(errno)));
-#endif
-#endif
+  
+  if(aconf->usessl) {
+    struct SSLConnection *ssl = ssl_create_connect(cli_fd(cptr), cptr, SSLData_Client);
+    cli_connect(cptr)->con_ssl = ssl;
+    if(ssl_handshake(ssl)) {
+      unsigned int events = 0;
+      if(ssl_wantread(ssl))
+        events |= SOCK_EVENT_READABLE;
+      if(ssl_wantwrite(ssl))
+        events |= SOCK_EVENT_WRITABLE;
+      socket_events(&(cli_socket(cptr)), SOCK_ACTION_SET | events);
+      result = IO_BLOCKED;
+    }
+  }
+  
+  cli_freeflag(cptr) |= FREEFLAG_SOCKET;
+  return 1;
 }
 
-/*
- * 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.
+/** Attempt to send a sequence of bytes to the connection.
+ * As a side effect, updates \a cptr's FLAG_BLOCKED setting
+ * and sendB/sendK fields.
+ * @param cptr Client that should receive data.
+ * @param buf Message buffer to send to client.
+ * @return Negative on connection-fatal error; otherwise
+ *  number of bytes sent.
  */
-static int check_init(aClient *cptr, char *sockn)
+unsigned int deliver_it(struct Client *cptr, struct MsgQ *buf)
 {
-  struct sockaddr_in sk;
-  size_t len = sizeof(struct sockaddr_in);
-  sockn[HOSTLEN] = 0;
-
-#ifdef UNIXPORT
-  if (IsUnixSocket(cptr))
-  {
-    strncpy(sockn, cptr->acpt->sockhost, HOSTLEN);
-    get_sockhost(cptr, sockn);
-    return 0;
-  }
-#endif
-
-  /* If descriptor is a tty, special checking... */
-  if (isatty(cptr->fd))
-  {
-    strncpy(sockn, me.sockhost, HOSTLEN);
-    memset(&sk, 0, sizeof(struct sockaddr_in));
-  }
-  else if (getpeername(cptr->fd, (struct sockaddr *)&sk, &len) == -1)
-  {
-    report_error("connect failure: %s %s", cptr);
-    return -1;
-  }
-  strcpy(sockn, inetntoa(sk.sin_addr));
-  if (inet_netof(sk.sin_addr) == IN_LOOPBACKNET)
-  {
-    cptr->hostp = NULL;
-    strncpy(sockn, me.sockhost, HOSTLEN);
-  }
-  memcpy(&cptr->ip, &sk.sin_addr, sizeof(struct in_addr));
-#ifdef TESTNET
-  cptr->port = ntohs(sk.sin_port) - 10000;
-#else
-  cptr->port = ntohs(sk.sin_port);
-#endif
-
-  return 0;
+  unsigned int bytes_written = 0;
+  unsigned int bytes_count = 0;
+  IOResult result;
+  assert(0 != cptr);
+
+  if(cli_connect(cptr)->con_ssl) {
+    result = ssl_send_encrypt(cli_connect(cptr)->con_ssl, buf, &bytes_count, &bytes_written);
+  } else {
+    result = os_sendv_nonb(cli_fd(cptr), buf, &bytes_count, &bytes_written);
+  }
+  
+  switch (result) {
+  case IO_SUCCESS:
+    ClrFlag(cptr, FLAG_BLOCKED);
+
+    cli_sendB(cptr) += bytes_written;
+    cli_sendB(&me)  += bytes_written;
+    /* A partial write implies that future writes will block. */
+    if (bytes_written < bytes_count)
+      SetFlag(cptr, FLAG_BLOCKED);
+    break;
+  case IO_BLOCKED:
+    SetFlag(cptr, FLAG_BLOCKED);
+    break;
+  case IO_FAILURE:
+    cli_error(cptr) = errno;
+    SetFlag(cptr, FLAG_DEADSOCKET);
+    break;
+  }
+  return bytes_written;
 }
 
-/*
- * Ordinary client access check. Look for conf lines which have the same
- * status as the flags passed.
+/** Complete non-blocking connect()-sequence. Check access and
+ * terminate connection, if trouble detected.
+ * @param cptr Client to which we have connected, with all ConfItem structs attached.
+ * @return Zero on failure (caller should exit_client()), non-zero on success.
  */
-enum AuthorizationCheckResult check_client(aClient *cptr)
+int completed_connection(struct Client* cptr)
 {
-  static char sockname[HOSTLEN + 1];
-  Reg2 struct hostent *hp = NULL;
-  Reg3 int i;
-  enum AuthorizationCheckResult acr;
-
-  ClearAccess(cptr);
-  Debug((DEBUG_DNS, "ch_cl: check access for %s[%s]",
-      cptr->name, inetntoa(cptr->ip)));
+  struct ConfItem *aconf;
+  time_t newts;
+  struct Client *acptr;
+  int i;
 
-  if (check_init(cptr, sockname))
-    return ACR_BAD_SOCKET;
+  assert(0 != cptr);
 
-  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.
+   * get the socket status from the fd first to check if
+   * connection actually succeeded
    */
-  if (hp)
-  {
-    for (i = 0; hp->h_addr_list[i]; i++)
-      if (!memcmp(hp->h_addr_list[i], &cptr->ip, sizeof(struct in_addr)))
-       break;
-    if (!hp->h_addr_list[i])
-    {
-      sendto_op_mask(SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%08x]",
-         inetntoa(cptr->ip), hp->h_name, *((unsigned int *)hp->h_addr));
-      hp = NULL;
-    }
-  }
-
-  if ((acr = attach_Iline(cptr, hp, sockname)))
-  {
-    Debug((DEBUG_DNS, "ch_cl: access denied: %s[%s]", cptr->name, sockname));
-    return acr;
+  if ((cli_error(cptr) = os_get_sockerr(cli_fd(cptr)))) {
+    const char* msg = strerror(cli_error(cptr));
+    if (!msg)
+      msg = "Unknown error";
+    sendto_opmask_butone(0, SNO_OLDSNO, "Connection failed to %s: %s",
+                         cli_name(cptr), msg);
+    return 0;
   }
-
-  Debug((DEBUG_DNS, "ch_cl: access ok: %s[%s]", cptr->name, sockname));
-
-  if (inet_netof(cptr->ip) == IN_LOOPBACKNET || IsUnixSocket(cptr) ||
-      inet_netof(cptr->ip) == inet_netof(mysk.sin_addr))
-  {
-    ircstp->is_loc++;
-    cptr->flags |= FLAGS_LOCAL;
+  if (!(aconf = find_conf_byname(cli_confs(cptr), cli_name(cptr), CONF_SERVER))) {
+    sendto_opmask_butone(0, SNO_OLDSNO, "Lost Server Line for %s", cli_name(cptr));
+    return 0;
   }
-  return ACR_OK;
-}
-
-#define CFLAG  CONF_CONNECT_SERVER
-#define NFLAG  CONF_NOCONNECT_SERVER
-
-/*
- * 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.
- *
- * Returns
- *  0 = Success
- * -1 = Access denied
- * -2 = Bad socket.
- */
-int check_server(aClient *cptr)
-{
-  Reg1 const char *name;
-  Reg2 aConfItem *c_conf = NULL, *n_conf = NULL;
-  struct hostent *hp = NULL;
-  Link *lp;
-  char abuff[HOSTLEN + USERLEN + 2];
-  char sockname[HOSTLEN + 1], fullname[HOSTLEN + 1];
-  int i;
+  if (s_state(&(cli_socket(cptr))) == SS_CONNECTING)
+    socket_state(&(cli_socket(cptr)), SS_CONNECTED);
 
-  name = cptr->name;
-  Debug((DEBUG_DNS, "sv_cl: check access for %s[%s]", name, cptr->sockhost));
+  if (!EmptyString(aconf->passwd))
+    sendrawto_one(cptr, MSG_PASS " :%s", aconf->passwd);
 
-  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.
+   * Create a unique timestamp
    */
-  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_ops("Connecting Error: %s[%s]", name, cptr->sockhost);
-      det_confs_butmask(cptr, 0);
-      return -1;
+  newts = TStime();
+  for (i = HighestFd; i > -1; --i) {
+    if ((acptr = LocalClientArray[i]) && 
+        (IsServer(acptr) || IsHandshake(acptr))) {
+      if (cli_serv(acptr)->timestamp >= newts)
+        newts = cli_serv(acptr)->timestamp + 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
+  assert(0 != cli_serv(cptr));
 
+  cli_serv(cptr)->timestamp = newts;
+  SetHandshake(cptr);
   /*
-   * 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.
+   * Make us timeout after twice the timeout for DNS look ups
    */
-  if (!IsUnixSocket(cptr) && !cptr->hostp)
-  {
-    Reg1 aConfItem *aconf;
+  cli_lasttime(cptr) = CurrentTime;
+  ClearPingSent(cptr);
 
-    aconf = count_cnlines(lp);
-    if (aconf)
-    {
-      Reg1 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.
-       */
-      ClearAccess(cptr);
-      lin.value.aconf = aconf;
-      lin.flags = ASYNC_CONF;
-      nextdnscheck = 1;
-      if ((s = strchr(aconf->host, '@')))
-       s++;
-      else
-       s = aconf->host;
-      Debug((DEBUG_DNS, "sv_ci:cache lookup (%s)", s));
-      hp = gethost_byname(s, &lin);
-    }
-  }
+  sendrawto_one(cptr, MSG_SERVER " %s 1 %Tu %Tu J%s %s%s +%s6 :%s",
+                cli_name(&me), cli_serv(&me)->timestamp, newts,
+               MAJOR_PROTOCOL, NumServCap(&me),
+               feature_bool(FEAT_HUB) ? "h" : "", cli_info(&me));
 
-  lp = cptr->confs;
-
-  ClearAccess(cptr);
-  if (check_init(cptr, sockname))
-    return -2;
+  return (IsDead(cptr)) ? 0 : 1;
+}
 
-check_serverback:
-  if (hp)
-  {
-    for (i = 0; hp->h_addr_list[i]; i++)
-      if (!memcmp(hp->h_addr_list[i], &cptr->ip, sizeof(struct in_addr)))
-       break;
-    if (!hp->h_addr_list[i])
-    {
-      sendto_op_mask(SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%08x]",
-         inetntoa(cptr->ip), hp->h_name, *((unsigned int *)hp->h_addr));
-      hp = NULL;
-    }
-  }
-  else if (cptr->hostp)
-  {
-    hp = cptr->hostp;
-    goto check_serverback;
-  }
+/** Close the physical connection.  Side effects: MyConnect(cptr)
+ * becomes false and cptr->from becomes NULL.
+ * @param cptr Client to disconnect.
+ */
+void close_connection(struct Client *cptr)
+{
+  struct ConfItem* aconf;
 
-  if (hp)
+  if (IsServer(cptr)) {
+    ServerStats->is_sv++;
+    ServerStats->is_sbs += cli_sendB(cptr);
+    ServerStats->is_sbr += cli_receiveB(cptr);
+    ServerStats->is_sti += CurrentTime - cli_firsttime(cptr);
     /*
-     * If we are missing a C or N line from above, search for
-     * it under all known hostnames we have for this ip#.
+     * If the connection has been up for a long amount of time, schedule
+     * a 'quick' reconnect, else reset the next-connect cycle.
      */
-    for (i = 0, name = hp->h_name; name; name = hp->h_aliases[i++])
-    {
-      strncpy(fullname, name, sizeof(fullname) - 1);
-      fullname[sizeof(fullname) - 1] = 0;
-      add_local_domain(fullname, HOSTLEN - strlen(fullname));
-      Debug((DEBUG_DNS, "sv_cl: gethostbyaddr: %s->%s", sockname, fullname));
-      sprintf_irc(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;
-      }
+    if ((aconf = find_conf_exact(cli_name(cptr), cptr, CONF_SERVER))) {
+      /*
+       * Reschedule a faster reconnect, if this was a automatically
+       * 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 = CurrentTime;
+      aconf->hold += ((aconf->hold - cli_since(cptr) >
+                      feature_int(FEAT_HANGONGOODLINK)) ?
+                     feature_int(FEAT_HANGONRETRYDELAY) : ConfConFreq(aconf));
+/*        if (nextconnect > aconf->hold) */
+/*          nextconnect = aconf->hold; */
     }
-  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_irc(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 if (IsUser(cptr)) {
+    ServerStats->is_cl++;
+    ServerStats->is_cbs += cli_sendB(cptr);
+    ServerStats->is_cbr += cli_receiveB(cptr);
+    ServerStats->is_cti += CurrentTime - cli_firsttime(cptr);
   }
   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 %p n %p",
-       name, cptr->username, cptr->sockhost, c_conf, n_conf));
-    return -1;
-  }
-  /*
-   * attach the C and N lines to the client structure for later use.
-   */
-  attach_conf(cptr, n_conf);
-  attach_conf(cptr, c_conf);
-  attach_confs(cptr, name, CONF_HUB | CONF_LEAF | CONF_UWORLD);
-
-  if ((c_conf->ipnum.s_addr == INADDR_NONE) && !IsUnixSocket(cptr))
-    memcpy(&c_conf->ipnum, &cptr->ip, 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));
-  return 0;
-}
-#undef CFLAG
-#undef NFLAG
+    ServerStats->is_ni++;
+
+  if(cli_connect(cptr)->con_ssl) {
+    ssl_free_connection(cli_connect(cptr)->con_ssl);
+    cli_connect(cptr)->con_ssl = NULL;
+  }
+  
+  if (-1 < cli_fd(cptr)) {
+    flush_connections(cptr);
+    LocalClientArray[cli_fd(cptr)] = 0;
+    close(cli_fd(cptr));
+    socket_del(&(cli_socket(cptr))); /* queue a socket delete */
+    cli_fd(cptr) = -1;
+  }
+  SetFlag(cptr, FLAG_DEADSOCKET);
+
+  MsgQClear(&(cli_sendQ(cptr)));
+  client_drop_sendq(cli_connect(cptr));
+  DBufClear(&(cli_recvQ(cptr)));
+  memset(cli_passwd(cptr), 0, sizeof(cli_passwd(cptr)));
+  set_snomask(cptr, 0, SNO_SET);
 
-/*
- * 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(aClient *cptr)
-{
-  aConfItem *aconf;
-  time_t newts;
-  aClient *acptr;
-  int i;
+  det_confs_butmask(cptr, 0);
 
-  aconf = find_conf(cptr->confs, cptr->name, CONF_CONNECT_SERVER);
-  if (!aconf)
-  {
-    sendto_ops("Lost C-Line for %s", get_client_name(cptr, FALSE));
-    return -1;
+  if (cli_listener(cptr)) {
+    release_listener(cli_listener(cptr));
+    cli_listener(cptr) = 0;
   }
-  if (!BadPtr(aconf->passwd))
-    sendto_one(cptr, "PASS :%s", aconf->passwd);
 
-  aconf = find_conf(cptr->confs, cptr->name, CONF_NOCONNECT_SERVER);
-  if (!aconf)
-  {
-    sendto_ops("Lost N-Line for %s", get_client_name(cptr, FALSE));
-    return -1;
-  }
-  make_server(cptr);
-  /* Create a unique timestamp */
-  newts = TStime();
-  for (i = highest_fd; i >= 0; i--)
-  {
-    if (!(acptr = loc_clients[i]) || (!IsServer(acptr) && !IsHandshake(acptr)))
-      continue;
-    if (acptr->serv->timestamp >= newts)
-      newts = acptr->serv->timestamp + 1;
+  for ( ; HighestFd > 0; --HighestFd) {
+    if (LocalClientArray[HighestFd])
+      break;
   }
-  cptr->serv->timestamp = newts;
-  SetHandshake(cptr);
-  /* Make us timeout after twice the timeout for DNS look ups */
-  cptr->lasttime = now;
-  cptr->flags |= FLAGS_PINGSENT;
-  sendto_one(cptr, "SERVER %s 1 " TIME_T_FMT " " TIME_T_FMT " J%s %s%s :%s",
-      my_name_for_link(me.name, aconf), me.serv->timestamp,
-      newts, MAJOR_PROTOCOL, NumServCap(&me), me.info);
-  if (!IsDead(cptr))
-    start_auth(cptr);
-
-  return (IsDead(cptr)) ? -1 : 0;
 }
 
-/*
- * close_connection
- *
- * Close the physical connection. This function must make
- * MyConnect(cptr) == FALSE, and set cptr->from == NULL.
+/** Close all unregistered connections.
+ * @param source Oper who requested the close.
+ * @return Number of closed connections.
  */
-void close_connection(aClient *cptr)
+int net_close_unregistered_connections(struct Client* source)
 {
-  Reg1 aConfItem *aconf;
-  Reg2 int i, j;
-  int empty = cptr->fd;
+  int            i;
+  struct Client* cptr;
+  int            count = 0;
+  assert(0 != source);
 
-  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 += now - 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 (IsUser(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 += now - 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;
+  for (i = HighestFd; i > 0; --i) {
+    if ((cptr = LocalClientArray[i]) && !IsRegistered(cptr)) {
+      send_reply(source, RPL_CLOSING, get_client_name(source, HIDE_IP));
+      exit_client(source, cptr, &me, "Oper Closing");
+      ++count;
     }
   }
-  else
-    ircstp->is_ni++;
+  return count;
+}
 
-  /*
-   * 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.
-   */
+/** 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 not added to the linked list of clients, it is
+ * passed off to the auth handler for dns and ident queries.
+ * @param listener Listening socket that received the connection.
+ * @param fd File descriptor of new connection.
+ */
+void add_connection(struct Listener* listener, int fd) {
+  struct irc_sockaddr addr;
+  struct Client      *new_client;
+  time_t             next_target = 0;
 
-  if ((aconf = find_conf_exact(cptr->name, cptr->username,
-      cptr->sockhost, CONF_CONNECT_SERVER)))
-  {
-    /*
-     * 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 = now;
-    aconf->hold += (aconf->hold - cptr->since > HANGONGOODLINK) ?
-       HANGONRETRYDELAY : ConfConFreq(aconf);
-    if (nextconnect > aconf->hold)
-      nextconnect = aconf->hold;
-  }
+  const char* const throttle_message =
+         "ERROR :Your host is trying to (re)connect too fast -- throttled\r\n";
+       /* 12345678901234567890123456789012345679012345678901234567890123456 */
+  const char* const register_message =
+         "ERROR :Unable to complete your registration\r\n";
 
-  if (cptr->authfd >= 0)
-    close(cptr->authfd);
+  assert(0 != listener);
 
-  if (cptr->fd >= 0)
-  {
-    flush_connections(cptr->fd);
-    loc_clients[cptr->fd] = NULL;
-    close(cptr->fd);
-    cptr->fd = -2;
-  }
-
-  DBufClear(&cptr->sendQ);
-  DBufClear(&cptr->recvQ);
-  memset(cptr->passwd, 0, sizeof(cptr->passwd));
-  set_snomask(cptr, 0, SNO_SET);
   /*
-   * Clean up extra sockets from P-lines which have been discarded.
+   * 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 (cptr->acpt != &me && cptr->acpt != cptr)
-  {
-    aconf = cptr->acpt->confs->value.aconf;
-    if (aconf->clients > 0)
-      aconf->clients--;
-    if (!aconf->clients && IsIllegal(aconf))
-      close_connection(cptr->acpt);
+  if (!os_get_peername(fd, &addr) || !os_set_nonblocking(fd)) {
+    ++ServerStats->is_ref;
+    close(fd);
+    return;
   }
-
-  for (; highest_fd > 0; highest_fd--)
-    if (loc_clients[highest_fd])
-      break;
-
-  det_confs_butmask(cptr, 0);
-
   /*
-   * fd remap to keep loc_clients[i] filled at the bottom.
+   * Disable IP (*not* TCP) options.  In particular, this makes it impossible
+   * to use source routing to connect to the server.  If we didn't do this
+   * (and if intermediate networks didn't drop source-routed packets), an
+   * attacker could successfully IP spoof us...and even return the anti-spoof
+   * ping, because the options would cause the packet to be routed back to
+   * the spoofer's machine.  When we disable the IP options, we delete the
+   * source route, and the normal routing takes over.
    */
-  if (empty > 0)
-    if ((j = highest_fd) > (i = empty) && !IsLog(loc_clients[j]))
-    {
-      if (IsListening(loc_clients[j]))
-       return;
-      if (dup2(j, i) == -1)
-       return;
-      loc_clients[i] = loc_clients[j];
-      loc_clients[i]->fd = i;
-      loc_clients[j] = NULL;
-      close(j);
-      while (!loc_clients[highest_fd])
-       highest_fd--;
-    }
+  os_disable_options(fd);
 
-  return;
-}
-
-/*
- *  set_sock_opts
- */
-static void set_sock_opts(int fd, aClient *cptr)
-{
-  size_t opt;
-#ifdef SO_REUSEADDR
-  opt = 1;
-  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
-      (OPT_TYPE *)&opt, sizeof(opt)) < 0)
-    report_error("setsockopt(SO_REUSEADDR) %s: %s", cptr);
-#endif
-#ifdef SO_USELOOPBACK
-  opt = 1;
-  if (setsockopt(fd, SOL_SOCKET, SO_USELOOPBACK,
-      (OPT_TYPE *)&opt, sizeof(opt)) < 0)
-    report_error("setsockopt(SO_USELOOPBACK) %s: %s", cptr);
-#endif
-#ifdef SO_RCVBUF
-  opt = 8192;
-  if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (OPT_TYPE *)&opt, sizeof(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).
- */
-  opt = 8192;
-#else
-  opt = 8192;
-#endif
-  if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (OPT_TYPE *)&opt, sizeof(opt)) < 0)
-    report_error("setsockopt(SO_SNDBUF) %s: %s", cptr);
-#endif
-#if defined(IP_OPTIONS) && defined(IPPROTO_IP)
+  if (listener_server(listener))
   {
-    char *s = readbuf, *t = readbuf + sizeof(readbuf) / 2;
-
-    opt = sizeof(readbuf) / 8;
-    if (getsockopt(fd, IPPROTO_IP, IP_OPTIONS, (OPT_TYPE *)t, &opt) < 0)
-      report_error("getsockopt(IP_OPTIONS) %s: %s", cptr);
-    else if (opt > 0 && opt != sizeof(readbuf) / 8)
-    {
-      for (*readbuf = '\0'; opt > 0; opt--, s += 3)
-       sprintf(s, "%02x:", *t++);
-      *s = '\0';
-      sendto_ops("Connection %s using IP opts: (%s)",
-         get_client_name(cptr, TRUE), readbuf);
-    }
-    if (setsockopt(fd, IPPROTO_IP, IP_OPTIONS, (OPT_TYPE *)NULL, 0) < 0)
-      report_error("setsockopt(IP_OPTIONS) %s: %s", cptr);
+    new_client = make_client(0, STAT_UNKNOWN_SERVER);
   }
-#endif
-}
-
-int get_sockerr(aClient *cptr)
-{
-  int errtmp = errno;
-#if defined(SO_ERROR) && !defined(SOL2)
-  int err = 0;
-  size_t len = sizeof(err);
-  if (cptr->fd >= 0)
-    if (!getsockopt(cptr->fd, SOL_SOCKET, SO_ERROR, (OPT_TYPE *)&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(int fd, aClient *cptr)
-{
-  int res;
-#ifndef NBLOCK_SYSV
-  int nonb = 0;
-#endif
-
-  /*
-   * 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.
-   */
-#ifdef NBLOCK_POSIX
-  nonb |= O_NONBLOCK;
-#endif
-#ifdef NBLOCK_BSD
-  nonb |= O_NDELAY;
-#endif
-#ifdef 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;
-}
-
-/*
- * 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 doesn't have a name.
- */
-aClient *add_connection(aClient *cptr, int fd, int type)
-{
-  Link lin;
-  aClient *acptr;
-  aConfItem *aconf = NULL;
-  acptr =
-      make_client(NULL,
-      (cptr->port == portnum) ? STAT_UNKNOWN_SERVER : STAT_UNKNOWN_USER);
-
-  if (cptr != &me)
-    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 (type == ADCON_TTY)       /* If descriptor is a tty,
-                                  special checking... */
-    get_sockhost(acptr, cptr->sockhost);
   else
   {
-    Reg1 char *s, *t;
-    struct sockaddr_in addr;
-    size_t len = sizeof(struct sockaddr_in);
-
-    if (getpeername(fd, (struct sockaddr *)&addr, &len) == -1)
-    {
-      report_error("Failed in connecting to %s: %s", cptr);
-    add_con_refuse:
-      ircstp->is_ref++;
-      acptr->fd = -2;
-      free_client(acptr);
-      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...
+     * Add this local client to the IPcheck registry.
+     *
+     * If they're throttled, murder them, but tell them why first.
      */
-    get_sockhost(acptr, inetntoa(addr.sin_addr));
-    memcpy(&acptr->ip, &addr.sin_addr, sizeof(struct in_addr));
-#ifdef TESTNET
-    acptr->port = ntohs(addr.sin_port) - 10000;
-#else
-    acptr->port = ntohs(addr.sin_port);
-#endif
-
-    /*
-     * Check that this socket (client) is allowed to accept
-     * connections from this IP#.
-     */
-    for (s = (char *)&cptr->ip, t = (char *)&acptr->ip, len = 4;
-       len > 0; len--, s++, t++)
+    if (!IPcheck_local_connect(&addr.addr, &next_target))
     {
-      if (!*s)
-       continue;
-      if (*s != *t)
-       break;
+      ++ServerStats->is_ref;
+      write(fd, throttle_message, strlen(throttle_message));
+      close(fd);
+      return;
     }
-
-    if (len)
-      goto add_con_refuse;
-
-    lin.flags = ASYNC_CLIENT;
-    lin.value.cptr = acptr;
-    Debug((DEBUG_DNS, "lookup %s", inetntoa(addr.sin_addr)));
-    acptr->hostp = gethost_byaddr(&acptr->ip, &lin);
-    if (!acptr->hostp)
-      SetDNS(acptr);
-    nextdnscheck = 1;
+    new_client = make_client(0, STAT_UNKNOWN_USER);
+    SetIPChecked(new_client);
   }
 
-  if (aconf)
-    aconf->clients++;
-  acptr->fd = fd;
-  if (fd > highest_fd)
-    highest_fd = fd;
-  loc_clients[fd] = acptr;
-  acptr->acpt = cptr;
-  Count_newunknown(nrof);
-  add_client_to_list(acptr);
-  set_non_blocking(acptr->fd, acptr);
-  set_sock_opts(acptr->fd, acptr);
-
   /*
-   * Add this local client to the IPcheck registry.
-   * If it is a connection to a user port and if the site has been throttled,
-   * reject the user.
+   * Copy ascii address to 'sockhost' just in case. Then we have something
+   * valid to put into error messages...
    */
-  if (IPcheck_local_connect(acptr) == -1 && IsUserPort(acptr))
-  {
-    ircstp->is_ref++;
-    exit_client(cptr, acptr, &me,
-       "Your host is trying to (re)connect too fast -- throttled");
-    return NULL;
-  }
-
-  start_auth(acptr);
-  return acptr;
-}
-
-#ifdef UNIXPORT
-static void add_unixconnection(aClient *cptr, int fd)
-{
-  aClient *acptr;
-  aConfItem *aconf = NULL;
-
-  acptr = make_client(NULL, STAT_UNKNOWN);
-
-  /*
-   * Copy ascii address to 'sockhost' just in case. Then we
-   * have something valid to put into error messages...
-   */
-  get_sockhost(acptr, me.sockhost);
-  if (cptr != &me)
-    aconf = cptr->confs->value.aconf;
-  if (aconf)
-  {
-    if (IsIllegal(aconf))
-    {
-      ircstp->is_ref++;
-      acptr->fd = -2;
-      free_client(acptr);
-      close(fd);
-      return;
-    }
-    else
-      aconf->clients++;
+  ircd_ntoa_r(cli_sock_ip(new_client), &addr.addr);
+  strcpy(cli_sockhost(new_client), cli_sock_ip(new_client));
+  memcpy(&cli_ip(new_client), &addr.addr, sizeof(cli_ip(new_client)));
+
+  if (next_target)
+    cli_nexttarget(new_client) = next_target;
+
+  cli_fd(new_client) = fd;
+  if (!socket_add(&(cli_socket(new_client)), client_sock_callback,
+                 (void*) cli_connect(new_client), SS_CONNECTED, 0, fd)) {
+    ++ServerStats->is_ref;
+    write(fd, register_message, strlen(register_message));
+    close(fd);
+    cli_fd(new_client) = -1;
+    return;
+  }
+  cli_freeflag(new_client) |= FREEFLAG_SOCKET;
+  cli_listener(new_client) = listener;
+  ++listener->ref_count;
+
+  Count_newunknown(UserStats);
+  
+  if(listener_ssl(listener)) {
+    struct Connection* con = cli_connect(new_client);
+    con->con_ssl = ssl_start_handshake_listener(listener->ssl_listener, fd, new_client, SSLData_Client);
+    unsigned int events = 0;
+    if(ssl_wantread(con->con_ssl))
+      events |= SOCK_EVENT_READABLE;
+    if(ssl_wantwrite(con->con_ssl))
+      events |= SOCK_EVENT_WRITABLE;
+    socket_events(&(cli_socket(new_client)), SOCK_ACTION_SET | events);
+  } else {
+    /* if we've made it this far we can put the client on the auth query pile */
+    start_auth(new_client);
   }
-  acptr->fd = fd;
-  if (fd > highest_fd)
-    highest_fd = fd;
-  loc_clients[fd] = acptr;
-  acptr->acpt = cptr;
-  SetUnixSock(acptr);
-  memcpy(&acptr->ip, &me.ip, sizeof(struct in_addr));
-
-  Count_newunknown(nrof);
-  add_client_to_list(acptr);
-  set_non_blocking(acptr->fd, acptr);
-  set_sock_opts(acptr->fd, acptr);
-  SetAccess(acptr);
-  return;
 }
-#endif
 
-/*
- * select/poll convert macro's by Run.
- *
- * The names are chosen to reflect what they means when NOT using poll().
+/** Determines whether to tell the events engine we're interested in
+ * writable events.
+ * @param cptr Client for which to decide this.
  */
-#ifndef USE_POLL
-typedef fd_set *fd_setp_t;
-#define RFD_ISSET(fd, rfd, index) FD_ISSET((fd), (rfd))
-#define WFD_ISSET(fd, wfd, index) FD_ISSET((fd), (wfd))
-#define RFD_SET(fd, rfd, index, cptr) FD_SET((fd), (rfd))
-#define WFD_SET(fd, wfd, index, cptr) FD_SET((fd), (wfd))
-#define RWFD_SET(fd, wfd, index) FD_SET((fd), (wfd))
-#define RFD_CLR_OUT(fd, rfd, index) FD_CLR((fd), (rfd))
-#define WFD_CLR_OUT(fd, wfd, index) FD_CLR((fd), (wfd))
-#define LOC_FD(index) (index)
-#define LOC_CLIENTS(index) loc_clients[index]
-#define HIGHEST_INDEX highest_fd
-#else /* USE_POLL */
-typedef unsigned int fd_setp_t;        /* Actually, an index to poll_fds[] */
-#ifdef _AIX
-#define POLLREADFLAGS (POLLIN|POLLMSG)
-#else
-#  if defined(POLLMSG) && defined(POLLIN) && defined(POLLRDNORM)
-#    define POLLREADFLAGS (POLLMSG|POLLIN|POLLRDNORM)
-#  else
-#    if defined(POLLIN) && defined(POLLRDNORM)
-#      define POLLREADFLAGS (POLLIN|POLLRDNORM)
-#    else
-#      if defined(POLLIN)
-#        define POLLREADFLAGS POLLIN
-#      else
-#        if defined(POLLRDNORM)
-#          define POLLREADFLAGS POLLRDNORM
-#        endif
-#      endif
-#    endif
-#  endif
-#endif
-#if defined(POLLOUT) && defined(POLLWRNORM)
-#define POLLWRITEFLAGS (POLLOUT|POLLWRNORM)
-#else
-#  if defined(POLLOUT)
-#    define POLLWRITEFLAGS POLLOUT
-#  else
-#    if defined(POLLWRNORM)
-#      define POLLWRITEFLAGS POLLWRNORM
-#    endif
-#  endif
-#endif
-#ifdef POLLHUP
-#define POLLERRORS (POLLHUP|POLLERR)
-#else
-#define POLLERRORS POLLERR
-#endif
-#define RFD_ISSET(fd, rfd, index) \
-  ((poll_fds[index].revents & POLLREADFLAGS) || \
-  ((poll_fds[index].events & POLLREADFLAGS) && \
-    (poll_fds[index].revents & POLLERRORS)))
-#define WFD_ISSET(fd, wfd, index) \
-  ((poll_fds[index].revents & POLLWRITEFLAGS) || \
-  ((poll_fds[index].events & POLLWRITEFLAGS) && \
-    (poll_fds[index].revents & POLLERRORS)))
-#define RFD_SET(fdes, rfd, index, cptr) \
-  do { \
-    poll_fds[index].fd = fdes; \
-    poll_cptr[index] = cptr; \
-    poll_fds[index].events = POLLREADFLAGS; \
-    added = TRUE; \
-  } while(0)
-#define WFD_SET(fdes, wfd, index, cptr) \
-  do { \
-    poll_fds[index].fd = fdes; \
-    poll_cptr[index] = cptr; \
-    if (added) \
-      poll_fds[index].events |= POLLWRITEFLAGS; \
-    else \
-    { \
-      poll_fds[index].events = POLLWRITEFLAGS; \
-      added = TRUE; \
-    } \
-  } while(0)
-/* This identical to WFD_SET() when used after a call to RFD_SET(): */
-#define RWFD_SET(fd, wfd, index) poll_fds[index].events |= POLLWRITEFLAGS
-/* [RW]FD_CLR_OUT() clears revents, not events */
-#define RFD_CLR_OUT(fd, rfd, index) poll_fds[index].revents &= ~POLLREADFLAGS
-#define WFD_CLR_OUT(fd, wfd, index) poll_fds[index].revents &= ~POLLWRITEFLAGS
-#define LOC_FD(index) (poll_fds[index].fd)
-#define LOC_CLIENTS(index) (poll_cptr[index])
-#define HIGHEST_INDEX (currfd_index - 1)
-#endif /* USE_POLL */
+void update_write(struct Client* cptr)
+{
+  /* If there are messages that need to be sent along, or if the client
+   * is in the middle of a /list, then we need to tell the engine that
+   * we're interested in writable events--otherwise, we need to drop
+   * that interest.
+   */
+  socket_events(&(cli_socket(cptr)),
+               ((MsgQLength(&cli_sendQ(cptr)) || cli_listing(cptr)) ?
+                SOCK_ACTION_ADD : SOCK_ACTION_DEL) | SOCK_EVENT_WRITABLE);
+}
 
-/*
- * 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
+/** 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
+ * @param cptr Client from which to read data.
+ * @param socket_ready If non-zero, more data can be read from the client's socket.
+ * @return Positive number on success, zero on connection-fatal failure, negative
+ *   if user is killed.
  */
-static int read_packet(aClient *cptr, fd_setp_t rfd)
+static int read_packet(struct Client *cptr, int socket_ready)
 {
-  size_t dolen = 0;
-  int length = 0;
-  int done;
-
-  if (RFD_ISSET(cptr->fd, rfd, rfd) &&
-      !(IsUser(cptr) && DBufLength(&cptr->recvQ) > 6090))
-  {
-    errno = 0;
-    length = recv(cptr->fd, readbuf, sizeof(readbuf), 0);
-
-    cptr->lasttime = now;
-    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;
+  unsigned int dolen = 0;
+  unsigned int length = 0;
+
+  if (socket_ready &&
+      !(IsUser(cptr) &&
+       DBufLength(&(cli_recvQ(cptr))) > feature_int(FEAT_CLIENT_FLOOD))) {
+    
+    /* Handle SSL Sockets
+    */
+    int recvret;
+    if(cli_connect(cptr)->con_ssl) {
+      recvret = ssl_recv_decrypt(cli_connect(cptr)->con_ssl, readbuf, sizeof(readbuf), &length);
+    } else {
+      recvret = os_recv_nonb(cli_fd(cptr), readbuf, sizeof(readbuf), &length);
+    }
+    switch (recvret) {
+    case IO_SUCCESS:
+      if (length)
+      {
+        cli_lasttime(cptr) = CurrentTime;
+        ClearPingSent(cptr);
+        ClrFlag(cptr, FLAG_NONL);
+        if (cli_lasttime(cptr) > cli_since(cptr))
+          cli_since(cptr) = cli_lasttime(cptr);
+      }
+      break;
+    case IO_BLOCKED:
+      break;
+    case IO_FAILURE:
+      cli_error(cptr) = errno;
+      /* SetFlag(cptr, FLAG_DEADSOCKET); */
+      return 0;
+    }
   }
-
+  
   /*
    * 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))
-  {
-    if (length > 0)
-      if ((done = dopacket(cptr, readbuf, length)))
-       return done;
-  }
+  if (length > 0 && IsServer(cptr))
+    return server_dopacket(cptr, readbuf, length);
+  else if (length > 0 && (IsHandshake(cptr) || IsConnecting(cptr)))
+    return connect_dopacket(cptr, readbuf, length);
   else
   {
     /*
@@ -1515,39 +662,16 @@ static int read_packet(aClient *cptr, fd_setp_t rfd)
      * it on the end of the receive queue and do it when its
      * turn comes around.
      */
-    if (!dbuf_put(&cptr->recvQ, readbuf, length))
+    if (length > 0 && dbuf_put(&(cli_recvQ(cptr)), readbuf, length) == 0)
       return exit_client(cptr, cptr, &me, "dbuf_put fail");
 
-#ifndef NOFLOODCONTROL
-    if (IsUser(cptr) && DBufLength(&cptr->recvQ) > CLIENT_FLOOD)
+    if (DBufLength(&(cli_recvQ(cptr))) > feature_int(FEAT_CLIENT_FLOOD))
       return exit_client(cptr, cptr, &me, "Excess Flood");
-#endif
 
-    while (DBufLength(&cptr->recvQ) && !NoNewLine(cptr)
-#ifndef NOFLOODCONTROL
-       && (IsTrusted(cptr) || cptr->since - now < 10)
-#endif
-       )
+    while (DBufLength(&(cli_recvQ(cptr))) && !NoNewLine(cptr) && 
+           (IsTrusted(cptr) || cli_since(cptr) - CurrentTime < 10))
     {
-      /*
-       * If it has become registered as a Server
-       * then skip the per-message parsing below.
-       */
-      if (IsServer(cptr))
-      {
-       /*
-        * XXX - this blindly deletes data if no cr/lf is received at
-        * the end of a lot of messages and the data stored in the 
-        * dbuf is greater than sizeof(readbuf)
-        */
-       dolen = dbuf_get(&cptr->recvQ, readbuf, sizeof(readbuf));
-       if (0 == dolen)
-         break;
-       if ((done = dopacket(cptr, readbuf, dolen)))
-         return done;
-       break;
-      }
-      dolen = dbuf_getmsg(&cptr->recvQ, cptr->buffer, BUFSIZE);
+      dolen = dbuf_getmsg(&(cli_recvQ(cptr)), cli_buffer(cptr), BUFSIZE);
       /*
        * Devious looking...whats it do ? well..if a client
        * sends a *long* message without any CR or LF, then
@@ -1556,1004 +680,332 @@ static int read_packet(aClient *cptr, fd_setp_t rfd)
        * deletes the rest of the buffer contents.
        * -avalon
        */
-      if (0 == dolen)
-      {
-       if (DBufLength(&cptr->recvQ) < 510)
-       {
-         cptr->flags |= FLAGS_NONL;
-         break;
-       }
-       DBufClear(&cptr->recvQ);
-       break;
-      }
-      else if (CPTR_KILLED == client_dopacket(cptr, dolen))
-       return CPTR_KILLED;
-    }
-  }
-  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.
- *
- * Don't ever use ZERO for `delay', unless you mean to poll and then
- * you have to have sleep/wait somewhere else in the code.--msa
- */
-int read_message(time_t delay)
-{
-  Reg1 aClient *cptr;
-  Reg2 int nfds;
-  struct timeval wait;
-#ifdef pyr
-  struct timeval nowt;
-  unsigned long us;
-#endif
-  time_t delay2 = delay;
-  unsigned long usec = 0;
-  int res, length, fd, i;
-  int auth = 0, ping = 0;
-#ifndef USE_POLL
-  fd_set read_set, write_set;
-#else /* USE_POLL */
-  unsigned int currfd_index = 0;
-  unsigned int udpfdindex = 0;
-  unsigned int resfdindex = 0;
-  unsigned long timeout;
-  int added;
-#endif /* USE_POLL */
-
-#ifdef pyr
-  gettimeofday(&nowt, NULL);
-  now = nowt.tv_sec;
-#endif
-
-  for (res = 0;;)
-  {
-#ifndef USE_POLL
-    FD_ZERO(&read_set);
-    FD_ZERO(&write_set);
-#endif /* not USE_POLL */
-    for (i = highest_fd; i >= 0; i--)
-    {
-#ifdef USE_POLL
-      added = FALSE;
-#endif /* USE_POLL */
-      if (!(cptr = loc_clients[i]))
-       continue;
-      if (IsLog(cptr))
-       continue;
-      if (DoingAuth(cptr))
-      {
-       auth++;
-       Debug((DEBUG_NOTICE, "auth on %p %d", cptr, i));
-       RFD_SET(cptr->authfd, &read_set, currfd_index, cptr);
-       if (cptr->flags & FLAGS_WRAUTH)
-         RWFD_SET(cptr->authfd, &write_set, currfd_index);
-      }
-      if (IsPing(cptr))
-      {
-       ping++;
-       Debug((DEBUG_NOTICE, "open ping on %p %d", cptr, i));
-       if (!cptr->firsttime || now <= cptr->firsttime)
-       {
-         RFD_SET(i, &read_set, currfd_index, cptr);
-         delay2 = 1;
-         if (DoPing(cptr) && now > cptr->lasttime)
-           RWFD_SET(i, &write_set, currfd_index);
-       }
-       else
-       {
-         del_queries((char *)cptr);
-         end_ping(cptr);
-       }
-#ifdef USE_POLL
-       if (added)
-         currfd_index++;
-#endif /* USE_POLL */
-       continue;
-      }
-      if (DoingDNS(cptr) || DoingAuth(cptr))
-      {
-#ifdef USE_POLL
-       if (added)
-         currfd_index++;
-#endif /* USE_POLL */
-       continue;
-      }
-      if (IsMe(cptr) && IsListening(cptr))
-       RFD_SET(i, &read_set, currfd_index, cptr);
-      else if (!IsMe(cptr))
+      if (dolen == 0)
       {
-       if (DBufLength(&cptr->recvQ) && delay2 > 2)
-         delay2 = 1;
-       if (DBufLength(&cptr->recvQ) < 4088)
-         RFD_SET(i, &read_set, currfd_index, cptr);
-       if (DBufLength(&cptr->sendQ) || IsConnecting(cptr) ||
-           (cptr->listing && DBufLength(&cptr->sendQ) < 2048))
-#ifndef pyr
-         WFD_SET(i, &write_set, currfd_index, cptr);
-#else /* pyr */
-       {
-         if (!(cptr->flags & FLAGS_BLOCKED))
-           WFD_SET(i, &write_set, currfd_index, cptr);
-         else
-           delay2 = 0, usec = 500000;
-       }
-       if (now - cptr->lw.tv_sec && nowt.tv_usec - cptr->lw.tv_usec < 0)
-         us = 1000000;
-       else
-         us = 0;
-       us += nowt.tv_usec;
-       if (us - cptr->lw.tv_usec > 500000)
-         cptr->flags &= ~FLAGS_BLOCKED;
-#endif /* pyr */
+        if (DBufLength(&(cli_recvQ(cptr))) < 510)
+          SetFlag(cptr, FLAG_NONL);
+        else
+        {
+          /* More than 512 bytes in the line - drop the input and yell
+           * at the client.
+           */
+          DBufClear(&(cli_recvQ(cptr)));
+          send_reply(cptr, ERR_INPUTTOOLONG);
+        }
       }
-#ifdef USE_POLL
-      if (added)
-       currfd_index++;
-#endif /* USE_POLL */
-    }
-
-    if (udpfd >= 0)
-    {
-      RFD_SET(udpfd, &read_set, currfd_index, NULL);
-#ifdef USE_POLL
-      udpfdindex = currfd_index;
-      currfd_index++;
-#endif /* USE_POLL */
-    }
-    if (resfd >= 0)
-    {
-      RFD_SET(resfd, &read_set, currfd_index, NULL);
-#ifdef USE_POLL
-      resfdindex = currfd_index;
-      currfd_index++;
-#endif /* USE_POLL */
-    }
-
-    wait.tv_sec = MIN(delay2, delay);
-    wait.tv_usec = usec;
-#ifndef USE_POLL
-#ifdef HPUX
-    nfds = select(FD_SETSIZE, (int *)&read_set, (int *)&write_set, 0, &wait);
-#else
-    nfds = select(FD_SETSIZE, &read_set, &write_set, 0, &wait);
-#endif
-#else /* USE_POLL */
-    timeout = (wait.tv_sec * 1000) + (wait.tv_usec / 1000);
-    nfds = poll(poll_fds, currfd_index, timeout);
-#endif /* USE_POLL */
-    now = time(NULL);
-    if (nfds == -1 && errno == EINTR)
-      return -1;
-    else if (nfds >= 0)
-      break;
-    report_error("select %s: %s", &me);
-    res++;
-    if (res > 5)
-      restart("too many select errors");
-    sleep(10);
-    now += 10;
-  }
-
-  if (udpfd >= 0 && RFD_ISSET(udpfd, &read_set, udpfdindex))
-  {
-    polludp();
-    nfds--;
-    RFD_CLR_OUT(udpfd, &read_set, udpfdindex);
-  }
-  /*
-   * Check fd sets for the ping fd's (if set and valid!) first
-   * because these can not be processed using the normal loops below.
-   * And we want them to be as fast as possible.
-   * -Run
-   */
-  for (i = HIGHEST_INDEX; (ping > 0) && (i >= 0); i--)
-  {
-    if (!(cptr = LOC_CLIENTS(i)))
-      continue;
-    if (!IsPing(cptr))
-      continue;
-    ping--;
-    if ((nfds > 0) && RFD_ISSET(cptr->fd, &read_set, i))
-    {
-      nfds--;
-      RFD_CLR_OUT(cptr->fd, &read_set, i);
-      read_ping(cptr);         /* This can RunFree(cptr) ! */
-    }
-    else if ((nfds > 0) && WFD_ISSET(cptr->fd, &write_set, i))
-    {
-      nfds--;
-      cptr->lasttime = now;
-      WFD_CLR_OUT(cptr->fd, &write_set, i);
-      send_ping(cptr);         /* This can RunFree(cptr) ! */
-    }
-  }
-  if (resfd >= 0 && RFD_ISSET(resfd, &read_set, resfdindex))
-  {
-    do_dns_async();
-    nfds--;
-    RFD_CLR_OUT(resfd, &read_set, resfdindex);
-  }
-  /*
-   * Check fd sets for the auth fd's (if set and valid!) first
-   * because these can not be processed using the normal loops below.
-   * -avalon
-   */
-  for (i = HIGHEST_INDEX; (auth > 0) && (i >= 0); i--)
-  {
-    if (!(cptr = LOC_CLIENTS(i)))
-      continue;
-    if (cptr->authfd < 0)
-      continue;
-    auth--;
-    if ((nfds > 0) && WFD_ISSET(cptr->authfd, &write_set, i))
-    {
-      nfds--;
-      send_authports(cptr);
-    }
-    else if ((nfds > 0) && RFD_ISSET(cptr->authfd, &read_set, i))
-    {
-      nfds--;
-      read_authports(cptr);
-    }
-  }
-  for (i = HIGHEST_INDEX; i >= 0; i--)
-    if ((cptr = LOC_CLIENTS(i)) && RFD_ISSET(i, &read_set, i) &&
-       IsListening(cptr))
-    {
-      RFD_CLR_OUT(i, &read_set, i);
-      nfds--;
-      cptr->lasttime = now;
+      else if (client_dopacket(cptr, dolen) == CPTR_KILLED)
+        return CPTR_KILLED;
       /*
-       * 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 it has become registered as a Server
+       * then skip the per-message parsing below.
        */
-      if ((fd = accept(LOC_FD(i), NULL, NULL)) < 0)
-      {
-       if (errno != EWOULDBLOCK)
-         report_error("accept() failed%s: %s", NULL);
-       break;
-      }
-#if defined(USE_SYSLOG) && defined(SYSLOG_CONNECTS)
-      {                                /* get an early log of all connections   --dl */
-       static struct sockaddr_in peer;
-       static int len;
-       len = sizeof(peer);
-       getpeername(fd, (struct sockaddr *)&peer, &len);
-       syslog(LOG_DEBUG, "Conn: %s", inetntoa(peer.sin_addr));
-      }
-#endif
-      ircstp->is_ac++;
-      if (fd >= MAXCLIENTS)
+      if (IsHandshake(cptr) || IsServer(cptr))
       {
-       /* Don't send more messages then one every 10 minutes */
-       static int count;
-       static time_t last_time;
-       ircstp->is_ref++;
-       ++count;
-       if (last_time < now - (time_t) 600)
-       {
-         if (count > 0)
-         {
-           if (!last_time)
-             last_time = me.since;
-           sendto_ops
-               ("All connections in use!  Had to refuse %d clients in the last "
-               STIME_T_FMT " minutes", count, (now - last_time) / 60);
-         }
-         else
-           sendto_ops("All connections in use. (%s)", get_client_name(cptr,
-               TRUE));
-         count = 0;
-         last_time = now;
-       }
-       send(fd, "ERROR :All connections in use\r\n", 32, 0);
-       close(fd);
-       break;
+        while (-1)
+        {
+          dolen = dbuf_get(&(cli_recvQ(cptr)), readbuf, sizeof(readbuf));
+          if (dolen <= 0)
+            return 1;
+          else if (dolen == 0)
+          {
+            if (DBufLength(&(cli_recvQ(cptr))) < 510)
+              SetFlag(cptr, FLAG_NONL);
+            else
+              DBufClear(&(cli_recvQ(cptr)));
+          }
+          else if ((IsServer(cptr) &&
+                    server_dopacket(cptr, readbuf, dolen) == CPTR_KILLED) ||
+                   (!IsServer(cptr) &&
+                    connect_dopacket(cptr, readbuf, dolen) == CPTR_KILLED))
+            return CPTR_KILLED;
+        }
       }
-      /*
-       * Use of add_connection (which never fails :) meLazy
-       */
-#ifdef UNIXPORT
-      if (IsUnixSocket(cptr))
-       add_unixconnection(cptr, fd);
-      else
-#endif
-      if (!add_connection(cptr, fd, ADCON_SOCKET))
-       continue;
-      nextping = now;
-      if (!cptr->acpt)
-       cptr->acpt = &me;
     }
 
-  for (i = HIGHEST_INDEX; i >= 0; i--)
-  {
-    if (!(cptr = LOC_CLIENTS(i)) || IsMe(cptr))
-      continue;
-#ifdef USE_POLL
-    if (DoingDNS(cptr) || DoingAuth(cptr) || !(cptr = loc_clients[LOC_FD(i)]))
-      continue;
-#endif /* USE_POLL */
-#ifdef DEBUGMODE
-    if (IsLog(cptr))
-      continue;
-#endif
-    if (WFD_ISSET(i, &write_set, i))
+    /* If there's still data to process, wait 2 seconds first */
+    if (DBufLength(&(cli_recvQ(cptr))) && !NoNewLine(cptr) &&
+       !t_onqueue(&(cli_proc(cptr))))
     {
-      int write_err = 0;
-      nfds--;
-      /*
-       *  ...room for writing, empty some queue then...
-       */
-      cptr->flags &= ~FLAGS_BLOCKED;
-      if (IsConnecting(cptr))
-       write_err = completed_connection(cptr);
-      if (!write_err)
-      {
-       if (cptr->listing && DBufLength(&cptr->sendQ) < 2048)
-         list_next_channels(cptr, 64);
-       send_queued(cptr);
-      }
-      if (IsDead(cptr) || write_err)
-      {
-      deadsocket:
-       if (RFD_ISSET(i, &read_set, i))
-       {
-         nfds--;
-         RFD_CLR_OUT(i, &read_set, i);
-       }
-       exit_client(cptr, cptr, &me,
-           IsDead(cptr) ? LastDeadComment(cptr) : strerror(get_sockerr(cptr)));
-       continue;
-      }
+      Debug((DEBUG_LIST, "Adding client process timer for %C", cptr));
+      cli_freeflag(cptr) |= FREEFLAG_TIMER;
+      timer_add(&(cli_proc(cptr)), client_timer_callback, cli_connect(cptr),
+               TT_RELATIVE, 2);
     }
-    length = 1;                        /* for fall through case */
-    if ((!NoNewLine(cptr) || RFD_ISSET(i, &read_set, i)) && !IsDead(cptr))
-#ifndef USE_POLL
-      length = read_packet(cptr, &read_set);
-#else /* USE_POLL */
-      length = read_packet(cptr, i);
-#endif /* USE_POLL */
-#if 0
-    /* Bullshit, why would we want to flush sockets while using non-blocking?
-     * This uses > 4% cpu! --Run */
-    if (length > 0)
-      flush_connections(LOC_FD(i));
-#endif
-    if ((length != CPTR_KILLED) && IsDead(cptr))
-      goto deadsocket;
-    if (!RFD_ISSET(i, &read_set, i) && length > 0)
-      continue;
-    nfds--;
-    readcalls++;
-    if (length > 0 || length == CPTR_KILLED)
-      continue;
-
-    /*
-     * ...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", LOC_FD(i), errno, length));
-
-    if ((IsServer(cptr) || IsHandshake(cptr)) && errno == 0 && length == 0)
-      exit_client_msg(cptr, cptr, &me, "Server %s closed the connection (%s)",
-         get_client_name(cptr, FALSE), cptr->serv->last_error_msg);
-    else
-      exit_client_msg(cptr, cptr, &me, "Read error to %s: %s",
-         get_client_name(cptr, FALSE), (length < 0) ?
-         strerror(get_sockerr(cptr)) : "EOF from client");
   }
-  return 0;
+  return 1;
 }
 
-/*
- * connect_server
+/** Start a connection to another server.
+ * @param aconf Connect block data for target server.
+ * @param by Client who requested the connection (if any).
+ * @return Non-zero on success; zero on failure.
  */
-int connect_server(aConfItem *aconf, aClient *by, struct hostent *hp)
+int connect_server(struct ConfItem* aconf, struct Client* by)
 {
-  Reg1 struct sockaddr *svp;
-  Reg2 aClient *cptr, *c2ptr;
-  Reg3 char *s;
-  int errtmp, len;
+  struct Client*   cptr = 0;
+  assert(0 != aconf);
 
-  Debug((DEBUG_NOTICE, "Connect to %s[%s] @%s",
-      aconf->name, aconf->host, inetntoa(aconf->ipnum)));
+  if (aconf->dns_pending) {
+    sendto_opmask_butone(0, SNO_OLDSNO, "Server %s connect DNS pending",
+                         aconf->name);
+    return 0;
+  }
+  Debug((DEBUG_NOTICE, "Connect to %s[@%s]", aconf->name,
+         ircd_ntoa(&aconf->address.addr)));
 
-  if ((c2ptr = FindClient(aconf->name)))
-  {
-    if (IsServer(c2ptr) || IsMe(c2ptr))
-    {
-      sendto_ops("Server %s already present from %s",
-         aconf->name, c2ptr->from->name);
-      if (by && IsUser(by) && !MyUser(by))
-      {
-#ifndef NO_PROTOCOL9
-       if (Protocol(by->from) < 10)
-         sendto_one(by, ":%s NOTICE %s :Server %s already present from %s",
-             me.name, by->name, aconf->name, c2ptr->from->name);
-       else
-#endif
-         sendto_one(by, "%s NOTICE %s%s :Server %s already present from %s",
-             NumServ(&me), NumNick(by), aconf->name, c2ptr->from->name);
+  if ((cptr = FindClient(aconf->name))) {
+    if (IsServer(cptr) || IsMe(cptr)) {
+      sendto_opmask_butone(0, SNO_OLDSNO, "Server %s already present from %s", 
+                           aconf->name, cli_name(cli_from(cptr)));
+      if (by && IsUser(by) && !MyUser(by)) {
+        sendcmdto_one(&me, CMD_NOTICE, by, "%C :Server %s already present "
+                      "from %s", by, aconf->name, cli_name(cli_from(cptr)));
       }
-      return -1;
+      return 0;
     }
-    else if (IsHandshake(c2ptr) || IsConnecting(c2ptr))
-    {
-      if (by && IsUser(by))
-      {
-       if (MyUser(by) || Protocol(by->from) < 10)
-         sendto_one(by, ":%s NOTICE %s :Connection to %s already in progress",
-             me.name, by->name, get_client_name(c2ptr, TRUE));
-       else
-         sendto_one(by,
-             "%s NOTICE %s%s :Connection to %s already in progress",
-             NumServ(&me), NumNick(by), get_client_name(c2ptr, TRUE));
+    else if (IsHandshake(cptr) || IsConnecting(cptr)) {
+      if (by && IsUser(by)) {
+        sendcmdto_one(&me, CMD_NOTICE, by, "%C :Connection to %s already in "
+                      "progress", by, cli_name(cptr));
       }
-      return -1;
+      return 0;
     }
   }
-
   /*
-   * If we dont know the IP# for this host and itis a hostname and
+   * 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)
-#ifdef UNIXPORT
-      && ((aconf->host[2]) != '/')     /* needed for Unix domain -- dl */
-#endif
-      )
-  {
-    Link lin;
-
-    lin.flags = ASYNC_CONNECT;
-    lin.value.aconf = aconf;
-    nextdnscheck = 1;
-    s = strchr(aconf->host, '@');
-    s++;                       /* should NEVER be NULL */
-    if ((aconf->ipnum.s_addr = inet_addr(s)) == INADDR_NONE)
-    {
-      aconf->ipnum.s_addr = INADDR_ANY;
-      hp = gethost_byname(s, &lin);
-      Debug((DEBUG_NOTICE, "co_sv: hp %p ac %p na %s ho %s",
-         hp, aconf, aconf->name, s));
-      if (!hp)
-       return 0;
-      memcpy(&aconf->ipnum, hp->h_addr, sizeof(struct in_addr));
-    }
+  if (!irc_in_addr_valid(&aconf->address.addr)
+      && !ircd_aton(&aconf->address.addr, aconf->host)) {
+    char buf[HOSTLEN + 1];
+
+    host_from_uh(buf, aconf->host, HOSTLEN);
+    gethost_byname(buf, connect_dns_callback, aconf);
+    aconf->dns_pending = 1;
+    return 0;
   }
-  cptr = make_client(NULL, STAT_UNKNOWN);
-  cptr->hostp = hp;
+  cptr = make_client(NULL, STAT_UNKNOWN_SERVER);
+
   /*
    * Copy these in so we have something for error detection.
    */
-  strncpy(cptr->name, aconf->name, sizeof(cptr->name) - 1);
-  cptr->name[sizeof(cptr->name) - 1] = 0;
-  strncpy(cptr->sockhost, aconf->host, HOSTLEN);
-  cptr->sockhost[HOSTLEN] = 0;
-
-#ifdef UNIXPORT
-  if (aconf->host[2] == '/')   /* (/ 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
+  ircd_strncpy(cli_name(cptr), aconf->name, HOSTLEN);
+  ircd_strncpy(cli_sockhost(cptr), aconf->host, HOSTLEN);
 
-  if (!svp)
-  {
-    if (cptr->fd >= 0)
-      close(cptr->fd);
-    cptr->fd = -2;
-    if (by && IsUser(by) && !MyUser(by))
-    {
-#ifndef NO_PROTOCOL9
-      if (Protocol(by->from) < 10)
-       sendto_one(by, ":%s NOTICE %s :Couldn't connect to %s",
-           me.name, by->name, get_client_name(cptr, TRUE));
-      else
-#endif
-       sendto_one(by, "%s NOTICE %s%s :Couldn't connect to %s",
-           NumServ(&me), NumNick(by), get_client_name(cptr, TRUE));
-    }
-    free_client(cptr);
-    return -1;
-  }
+  /*
+   * Attach config entries to client here rather than in
+   * completed_connection. This to avoid null pointer references
+   */
+  attach_confs_byhost(cptr, aconf->host, CONF_SERVER);
 
-  set_non_blocking(cptr->fd, cptr);
-  set_sock_opts(cptr->fd, cptr);
-  signal(SIGALRM, dummy);
-  alarm(4);
-  if (connect(cptr->fd, svp, len) < 0 && errno != EINPROGRESS)
-  {
-    int err = get_sockerr(cptr);
-    errtmp = errno;            /* other system calls may eat errno */
-    alarm(0);
-    report_error("Connect to host %s failed: %s", cptr);
-    if (by && IsUser(by) && !MyUser(by))
-    {
-#ifndef NO_PROTOCOL9
-      if (Protocol(by->from) < 10)
-       sendto_one(by, ":%s NOTICE %s :Connect to host %s failed: %s",
-           me.name, by->name, get_client_name(cptr, TRUE), strerror(err));
-      else
-#endif
-       sendto_one(by, "%s NOTICE %s%s :Connect to host %s failed: %s",
-           NumServ(&me), NumNick(by), get_client_name(cptr, TRUE),
-           strerror(err));
+  if (!find_conf_byhost(cli_confs(cptr), aconf->host, CONF_SERVER)) {
+    sendto_opmask_butone(0, SNO_OLDSNO, "Host %s is not enabled for "
+                         "connecting: no Connect block", aconf->name);
+    if (by && IsUser(by) && !MyUser(by)) {
+      sendcmdto_one(&me, CMD_NOTICE, by, "%C :Connect to host %s failed: no "
+                    "Connect block", by, aconf->name);
     }
-    close(cptr->fd);
-    cptr->fd = -2;
+    det_confs_butmask(cptr, 0);
     free_client(cptr);
-    errno = errtmp;
-    if (errno == EINTR)
-      errno = ETIMEDOUT;
-    return -1;
+    return 0;
   }
-  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
+   * attempt to connect to the server in the conf line
    */
-  attach_confs_host(cptr, aconf->host,
-      CONF_NOCONNECT_SERVER | CONF_CONNECT_SERVER);
-
-  if (!find_conf_host(cptr->confs, aconf->host, CONF_NOCONNECT_SERVER) ||
-      !find_conf_host(cptr->confs, aconf->host, CONF_CONNECT_SERVER))
-  {
-    sendto_ops("Host %s is not enabled for connecting:no C/N-line",
-       aconf->host);
-    if (by && IsUser(by) && !MyUser(by))
-    {
-#ifndef NO_PROTOCOL9
-      if (Protocol(by->from) < 10)
-       sendto_one(by,
-           ":%s NOTICE %s :Connect to host %s failed: no C/N-lines",
-           me.name, by->name, get_client_name(cptr, TRUE));
-      else
-#endif
-       sendto_one(by,
-           "%s NOTICE %s%s :Connect to host %s failed: no C/N-lines",
-           NumServ(&me), NumNick(by), get_client_name(cptr, TRUE));
+  if (!connect_inet(aconf, cptr)) {
+    if (by && IsUser(by) && !MyUser(by)) {
+      sendcmdto_one(&me, CMD_NOTICE, by, "%C :Couldn't connect to %s", by,
+                    cli_name(cptr));
     }
     det_confs_butmask(cptr, 0);
-    close(cptr->fd);
-    cptr->fd = -2;
     free_client(cptr);
-    return (-1);
+    return 0;
   }
   /*
+   * NOTE: if we're here we have a valid C:Line and the client should
+   * have started the connection and stored the remote address/port and
+   * ip address name in itself
+   *
    * The socket has been connected or connect is in progress.
    */
   make_server(cptr);
-  if (by && IsUser(by))
-  {
-    sprintf_irc(cptr->serv->by, "%s%s", NumNick(by));
-    if (cptr->serv->user)
-      free_user(cptr->serv->user, NULL);
-    cptr->serv->user = by->user;
-    by->user->refcnt++;
-  }
-  else
-  {
-    *cptr->serv->by = '\0';
-    if (cptr->serv->user)
-      free_user(cptr->serv->user, NULL);
-    cptr->serv->user = NULL;
-  }
-  cptr->serv->up = &me;
-  if (cptr->fd > highest_fd)
-    highest_fd = cptr->fd;
-  loc_clients[cptr->fd] = cptr;
-  cptr->acpt = &me;
+  if (by && IsUser(by)) {
+    ircd_snprintf(0, cli_serv(cptr)->by, sizeof(cli_serv(cptr)->by), "%s%s",
+                 NumNick(by));
+    assert(0 == cli_serv(cptr)->user);
+    cli_serv(cptr)->user = cli_user(by);
+    cli_user(by)->refcnt++;
+  }
+  else {
+    *(cli_serv(cptr))->by = '\0';
+    /* strcpy(cptr->serv->by, "Auto"); */
+  }
+  cli_serv(cptr)->up = &me;
   SetConnecting(cptr);
 
-  get_sockhost(cptr, aconf->host);
-  Count_newunknown(nrof);
+  if (cli_fd(cptr) > HighestFd)
+    HighestFd = cli_fd(cptr);
+
+  LocalClientArray[cli_fd(cptr)] = cptr;
+
+  Count_newunknown(UserStats);
+  /* Actually we lie, the connect hasn't succeeded yet, but we have a valid
+   * cptr, so we register it now.
+   * Maybe these two calls should be merged.
+   */
   add_client_to_list(cptr);
   hAddClient(cptr);
-  nextping = now;
+/*    nextping = CurrentTime; */
 
-  return 0;
+  return (s_state(&cli_socket(cptr)) == SS_CONNECTED) ?
+    completed_connection(cptr) : 1;
 }
 
-static struct sockaddr *connect_inet(aConfItem *aconf, aClient *cptr, int *lenp)
+/** 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 init_server_identity(void)
 {
-  static struct sockaddr_in server;
-  Reg3 struct hostent *hp;
-  struct sockaddr_in bindaddr;
-
-  /*
-   * Might as well get sockhost from here, the connection is attempted
-   * with it so if it fails its useless.
-   */
-  alarm(2);
-  cptr->fd = socket(AF_INET, SOCK_STREAM, 0);
-  alarm(0);
-  if (cptr->fd == -1 && errno == EAGAIN)
-  {
-    sendto_ops("opening stream socket to server %s: No more sockets",
-       get_client_name(cptr, TRUE));
-    return NULL;
-  }
-  if (cptr->fd == -1)
-  {
-    report_error("opening stream socket to server %s: %s", cptr);
-    return NULL;
-  }
-  if (cptr->fd >= MAXCLIENTS)
-  {
-    sendto_ops("No more connections allowed (%s)", cptr->name);
-    return NULL;
-  }
-  mysk.sin_port = 0;
-  memset(&server, 0, sizeof(server));
-  server.sin_family = AF_INET;
-  get_sockhost(cptr, aconf->host);
+  const struct LocalConf* conf = conf_get_local();
+  assert(0 != conf);
 
-  /*
-   * 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#.
-   */
-  memcpy(&bindaddr, &cserv, sizeof(bindaddr));
-  if (aconf->ipnum.s_addr == 0x100007f)
-    bindaddr.sin_addr.s_addr = 0x100007f;      /* bind with localhost when we are connecting to localhost */
-  if (bind(cptr->fd, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) == -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 dont know it, then the connect fails.
-   */
-  if (isDigit(*aconf->host) && (aconf->ipnum.s_addr == INADDR_NONE))
-    aconf->ipnum.s_addr = inet_addr(aconf->host);
-  if (aconf->ipnum.s_addr == INADDR_NONE)
-  {
-    hp = cptr->hostp;
-    if (!hp)
-    {
-      Debug((DEBUG_FATAL, "%s: unknown host", aconf->host));
-      return NULL;
-    }
-    memcpy(&aconf->ipnum, hp->h_addr, sizeof(struct in_addr));
-  }
-  memcpy(&server.sin_addr, &aconf->ipnum, sizeof(struct in_addr));
-  memcpy(&cptr->ip, &aconf->ipnum, sizeof(struct in_addr));
-#ifdef TESTNET
-  server.sin_port = htons(((aconf->port > 0) ? aconf->port : portnum) + 10000);
-#else
-  server.sin_port = htons(((aconf->port > 0) ? aconf->port : portnum));
-#endif
-  *lenp = sizeof(server);
-  return (struct sockaddr *)&server;
+  ircd_strncpy(cli_name(&me), conf->name, HOSTLEN);
+  SetYXXServerName(&me, conf->numeric);
 }
 
-#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.
+/** Process events on a client socket.
+ * @param ev Socket event structure that has a struct Connection as
+ *   its associated data.
  */
-static struct sockaddr *connect_unix(aConfItem *aconf, aClient *cptr, int *lenp)
+static void client_sock_callback(struct Event* ev)
 {
-  static struct sockaddr_un sock;
+  struct Client* cptr;
+  struct Connection* con;
+  char *fmt = "%s";
+  char *fallback = 0;
 
-  alarm(2);
-  cptr->fd = socket(AF_UNIX, SOCK_STREAM, 0);
-  alarm(0);
-  if (cptr->fd == -1 && errno == EAGAIN)
-  {
-    sendto_ops("Unix domain connect to host %s failed: No more sockets",
-       get_client_name(cptr, TRUE));
-    return NULL;
-  }
-  if (cptr->fd == -1)
-  {
-    report_error("Unix domain connect to host %s failed: %s", cptr);
-    return NULL;
-  }
-  else if (cptr->fd >= MAXCLIENTS)
-  {
-    sendto_ops("No more connections allowed (%s)", cptr->name);
-    return NULL;
-  }
+  assert(0 != ev_socket(ev));
+  assert(0 != s_data(ev_socket(ev)));
 
-  get_sockhost(cptr, aconf->host);
-  /* +2 needed for working Unix domain -- dl */
-  strncpy(sock.sun_path, aconf->host + 2, sizeof(sock.sun_path) - 1);
-  sock.sun_path[sizeof(sock.sun_path) - 1] = 0;
-  sock.sun_family = AF_UNIX;
-  *lenp = strlen(sock.sun_path) + 2;
+  con = (struct Connection*) s_data(ev_socket(ev));
 
-  SetUnixSock(cptr);
-  return (struct sockaddr *)&sock;
-}
+  assert(0 != con_client(con) || ev_type(ev) == ET_DESTROY);
 
-#endif
+  cptr = con_client(con);
 
-/*
- * 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, size_t len)
-{
-  static char tmp[HOSTLEN + 1];
-#if HAVE_UNAME
-  struct utsname utsn;
-#endif
-  struct hostent *hp;
-  char *cname = cptr->name;
-  size_t len2;
+  assert(0 == cptr || con == cli_connect(cptr));
 
-  /*
-   * Setup local socket structure to use for binding to.
-   */
-  memset(&mysk, 0, sizeof(mysk));
-  mysk.sin_family = AF_INET;
+  switch (ev_type(ev)) {
+  case ET_DESTROY:
+    con_freeflag(con) &= ~FREEFLAG_SOCKET;
 
-#if HAVE_UNAME
-  if (uname(&utsn) == -1)
-    return;
-  len2 = strlen(utsn.nodename);
-  if (len2 > len)
-    len2 = len;
-  strncpy(name, utsn.nodename, len2);
-#else /* HAVE_GETHOSTNAME */
-  if (gethostname(name, len) == -1)
-    return;
-#endif
-  name[len] = '\0';
+    if (!con_freeflag(con) && !cptr)
+      free_connection(con);
+    break;
 
-  /* Assume that a name containing '.' is a FQDN */
-  if (!strchr(name, '.'))
-    add_local_domain(name, len - strlen(name));
+  case ET_CONNECT: /* socket connection completed */
+    if(cli_connect(cptr)->con_ssl) {
+      ssl_start_handshake_connect(cli_connect(cptr)->con_ssl);
+    }
+    else if (!completed_connection(cptr) || IsDead(cptr))
+      fallback = cli_info(cptr);
+    break;
 
-  /*
-   * 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)))
-  {
-    const char *hname;
-    int i = 0;
+  case ET_ERROR: /* an error occurred */
+    fallback = cli_info(cptr);
+    cli_error(cptr) = ev_data(ev);
+    /* If the OS told us we have a bad file descriptor, we should
+     * record that for future reference.
+     */
+    if (cli_error(cptr) == EBADF)
+      cli_fd(cptr) = -1;
+    if (s_state(&(con_socket(con))) == SS_CONNECTING) {
+      completed_connection(cptr);
+      /* for some reason, the os_get_sockerr() in completed_connect()
+       * can return 0 even when ev_data(ev) indicates a real error, so
+       * re-assign the client error here.
+       */
+      cli_error(cptr) = ev_data(ev);
+      break;
+    }
+    /*FALLTHROUGH*/
+  case ET_EOF: /* end of file on socket */
+    Debug((DEBUG_ERROR, "READ ERROR: fd = %d %d", cli_fd(cptr),
+          cli_error(cptr)));
+    SetFlag(cptr, FLAG_DEADSOCKET);
+    if ((IsServer(cptr) || IsHandshake(cptr)) && cli_error(cptr) == 0) {
+      exit_client_msg(cptr, cptr, &me, "Server %s closed the connection (%s)",
+                     cli_name(cptr), cli_serv(cptr)->last_error_msg);
+      return;
+    } else {
+      fmt = "Read error: %s";
+      fallback = "EOF from client";
+    }
+    break;
 
-    for (hname = hp->h_name; hname; hname = hp->h_aliases[i++])
-    {
-      strncpy(tmp, hname, sizeof(tmp) - 1);
-      add_local_domain(tmp, sizeof(tmp) - 1 - strlen(tmp));
+  case ET_WRITE: /* socket is writable */
+    ClrFlag(cptr, FLAG_BLOCKED);
+    if (cli_listing(cptr) && MsgQLength(&(cli_sendQ(cptr))) < 2048)
+      list_next_channels(cptr);
+    Debug((DEBUG_SEND, "Sending queued data to %C", cptr));
+    send_queued(cptr);
+    break;
 
-      /*
-       * 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 (!strCasediff(me.name, tmp))
-       break;
+  case ET_READ: /* socket is readable */
+    if (!IsDead(cptr)) {
+      Debug((DEBUG_DEBUG, "Reading data from %C", cptr));
+      if (read_packet(cptr, 1) == 0) /* error while reading packet */
+       fallback = "EOF from client";
     }
-    if (strCasediff(me.name, tmp))
-      strncpy(name, hp->h_name, len);
-    else
-      strncpy(name, tmp, len);
-    memcpy(&mysk.sin_addr, hp->h_addr, sizeof(struct in_addr));
-    Debug((DEBUG_DEBUG, "local name is %s", get_client_name(&me, TRUE)));
-  }
-  return;
-}
+    break;
 
-/*
- * Setup a UDP socket and listen for incoming packets
- */
-int setup_ping(void)
-{
-  struct sockaddr_in from;
-  int on = 1;
-
-  memset(&from, 0, sizeof(from));
-  from.sin_addr = cserv.sin_addr;
-#ifdef TESTNET
-  from.sin_port = htons(atoi(UDP_PORT) + 10000);
-#else
-  from.sin_port = htons(atoi(UDP_PORT));
-#endif
-  from.sin_family = AF_INET;
-
-  if ((udpfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
-  {
-    Debug((DEBUG_ERROR, "socket udp : %s", strerror(errno)));
-    return -1;
-  }
-  if (setsockopt(udpfd, SOL_SOCKET, SO_REUSEADDR,
-      (OPT_TYPE *)&on, sizeof(on)) == -1)
-  {
-#ifdef USE_SYSLOG
-    syslog(LOG_ERR, "setsockopt udp fd %d : %m", udpfd);
-#endif
-    Debug((DEBUG_ERROR, "setsockopt so_reuseaddr : %s", strerror(errno)));
-    close(udpfd);
-    udpfd = -1;
-    return -1;
+  default:
+    assert(0 && "Unrecognized socket event in client_sock_callback()");
+    break;
   }
-  on = 0;
-  setsockopt(udpfd, SOL_SOCKET, SO_BROADCAST, (char *)&on, sizeof(on));
-  if (bind(udpfd, (struct sockaddr *)&from, sizeof(from)) == -1)
-  {
-#ifdef USE_SYSLOG
-    syslog(LOG_ERR, "bind udp.%d fd %d : %m", from.sin_port, udpfd);
-#endif
-    Debug((DEBUG_ERROR, "bind : %s", strerror(errno)));
-    close(udpfd);
-    udpfd = -1;
-    return -1;
-  }
-  if (fcntl(udpfd, F_SETFL, FNDELAY) == -1)
-  {
-    Debug((DEBUG_ERROR, "fcntl fndelay : %s", strerror(errno)));
-    close(udpfd);
-    udpfd = -1;
-    return -1;
+
+  assert(0 == cptr || 0 == cli_connect(cptr) || con == cli_connect(cptr));
+
+  if (fallback) {
+    const char* msg = (cli_error(cptr)) ? strerror(cli_error(cptr)) : fallback;
+    if (!msg)
+      msg = "Unknown error";
+    exit_client_msg(cptr, cptr, &me, fmt, msg);
   }
-  return udpfd;
 }
 
-/*
- * max # of pings set to 15/sec.
+/** Process a timer on client socket.
+ * @param ev Timer event that has a struct Connection as its
+ * associated data.
  */
-static void polludp(void)
+static void client_timer_callback(struct Event* ev)
 {
-  Reg1 char *s;
-  struct sockaddr_in from;
-  int n;
-  size_t fromlen = sizeof(from);
-  static time_t last = 0;
-  static int cnt = 0, mlen = 0;
-
-  /*
-   * find max length of data area of packet.
-   */
-  if (!mlen)
-  {
-    mlen = sizeof(readbuf) - strlen(me.name) - strlen(version);
-    mlen -= 6;
-    if (mlen < 0)
-      mlen = 0;
-  }
-  Debug((DEBUG_DEBUG, "udp poll"));
+  struct Client* cptr;
+  struct Connection* con;
 
-  n = recvfrom(udpfd, readbuf, mlen, 0, (struct sockaddr *)&from, &fromlen);
-  if (now == last)
-    if (++cnt > 14)
-      return;
-  cnt = 0;
-  last = now;
+  assert(0 != ev_timer(ev));
+  assert(0 != t_data(ev_timer(ev)));
+  assert(ET_DESTROY == ev_type(ev) || ET_EXPIRE == ev_type(ev));
 
-  if (n == -1)
-  {
-    if ((errno == EWOULDBLOCK) || (errno == EAGAIN))
-      return;
-    else
-    {
-      report_error("udp port recvfrom (%s): %s", &me);
-      return;
-    }
-  }
-  ircstp->is_udp++;
-  if (n < 19)
-    return;
+  con = (struct Connection*) t_data(ev_timer(ev));
 
-  s = readbuf + n;
-  /*
-   * attach my name and version for the reply
-   */
-  *readbuf |= 1;
-  strcpy(s, me.name);
-  s += strlen(s) + 1;
-  strcpy(s, version);
-  s += strlen(s);
-  sendto(udpfd, readbuf, s - readbuf, 0,
-      (struct sockaddr *)&from, sizeof(from));
-  return;
-}
+  assert(0 != con_client(con) || ev_type(ev) == ET_DESTROY);
 
-/*
- * do_dns_async
- *
- * Called when the fd returned from init_resolver() has been selected for
- * reading.
- */
-static void do_dns_async(void)
-{
-  static Link ln;
-  aClient *cptr;
-  aConfItem *aconf;
-  struct hostent *hp;
+  cptr = con_client(con);
 
-  ln.flags = ASYNC_NONE;
-  hp = get_res((char *)&ln);
+  assert(0 == cptr || con == cli_connect(cptr));
 
-  Debug((DEBUG_DNS, "%p = get_res(%d,%p)", hp, ln.flags, ln.value.cptr));
+  if (ev_type(ev)== ET_DESTROY) {
+    con_freeflag(con) &= ~FREEFLAG_TIMER; /* timer has expired... */
 
-  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);
-       if (!DoingAuth(cptr))
-         SetAccess(cptr);
-       cptr->hostp = hp;
-      }
-      break;
-    case ASYNC_CONNECT:
-      aconf = ln.value.aconf;
-      if (hp && aconf)
-      {
-       memcpy(&aconf->ipnum, hp->h_addr, sizeof(struct in_addr));
-       connect_server(aconf, NULL, hp);
-      }
-      else
-       sendto_ops("Connect to %s failed: host lookup",
-           (aconf) ? aconf->host : "unknown");
-      break;
-    case ASYNC_PING:
-      cptr = ln.value.cptr;
-      del_queries((char *)cptr);
-      if (hp)
-      {
-       memcpy(&cptr->ip, hp->h_addr, sizeof(struct in_addr));
-       if (ping_server(cptr) == -1)
-         end_ping(cptr);
-      }
-      else
-      {
-       sendto_ops("Udp ping to %s failed: host lookup", cptr->sockhost);
-       end_ping(cptr);
-      }
-      break;
-    case ASYNC_CONF:
-      aconf = ln.value.aconf;
-      if (hp && aconf)
-       memcpy(&aconf->ipnum, hp->h_addr, sizeof(struct in_addr));
-      break;
-    default:
-      break;
+    if (!con_freeflag(con) && !cptr)
+      free_connection(con); /* client is being destroyed */
+  } else {
+    Debug((DEBUG_LIST, "Client process timer for %C expired; processing",
+          cptr));
+    read_packet(cptr, 0); /* read_packet will re-add timer if needed */
   }
+
+  assert(0 == cptr || 0 == cli_connect(cptr) || con == cli_connect(cptr));
 }