Author: Bleep <tomh@inxpress.net>
[ircu2.10.12-pk.git] / ircd / s_auth.c
index 44f45eaa9779724240f39bddd6e7235e7e335d1d..acb2663caf94ec10142a6c9a49086dc4d7c25b79 100644 (file)
-/*
- * IRC - Internet Relay Chat, ircd/s_auth.c
- * Copyright (C) 1992 Darren Reed
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/s_auth.c
+ *   Copyright (C) 1992 Darren Reed
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 1, or (at your option)
- * any later version.
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ *   $Id$
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Changes:
+ *   July 6, 1999 - Rewrote most of the code here. When a client connects
+ *     to the server and passes initial socket validation checks, it
+ *     is owned by this module (auth) which returns it to the rest of the
+ *     server when dns and auth queries are finished. Until the client is
+ *     released, the server does not know it exists and does not process
+ *     any messages from it.
+ *     --Bleep  Thomas Helvey <tomh@inxpress.net>
  */
-
-#include "sys.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 UNIXPORT
-#include <sys/un.h>
-#endif
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#ifdef HPUX
-#include <arpa/inet.h>
-#endif /* HPUX */
-#if HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
-#ifdef USE_SYSLOG
-#include <syslog.h>
-#endif
-#include "h.h"
+#include "s_auth.h"
+#include "client.h"
+#include "IPcheck.h"
+#include "ircd.h"
+#include "ircd_alloc.h"
+#include "ircd_chattr.h"
+#include "ircd_log.h"
+#include "ircd_osdep.h"
+#include "ircd_string.h"
+#include "list.h"
+#include "numeric.h"
+#include "querycmds.h"
 #include "res.h"
-#include "struct.h"
-#include "common.h"
-#include "send.h"
 #include "s_bsd.h"
+#include "s_debug.h"
 #include "s_misc.h"
-#include "support.h"
-#include "ircd.h"
-#include "s_auth.h"
+#include "send.h"
 #include "sprintf_irc.h"
+#include "struct.h"
+#include "sys.h"               /* TRUE bleah */
 
-RCSTAG_CC("$Id$");
+#include <netdb.h>             /* struct hostent */
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
 
 /*
- * start_auth
- *
- * Flag the client to show that an attempt to contact the ident server on
- * the client's host.  The connect and subsequently the socket are all put
- * into 'non-blocking' mode.  Should the connect or any later phase of the
- * identifing process fail, it is aborted and the user is given a username
- * of "unknown".
+ * a bit different approach
+ * this replaces the original sendheader macros
+ */
+static struct {
+  const char* message;
+  size_t      length;
+} HeaderMessages [] = {
+  /* 123456789012345678901234567890123456789012345678901234567890 */
+  { "NOTICE AUTH :*** Looking up your hostname\r\n",       43 },
+  { "NOTICE AUTH :*** Found your hostname\r\n",            38 },
+  { "NOTICE AUTH :*** Found your hostname, cached\r\n",    46 },
+  { "NOTICE AUTH :*** Couldn't look up your hostname\r\n", 49 },
+  { "NOTICE AUTH :*** Checking Ident\r\n",                 33 },
+  { "NOTICE AUTH :*** Got ident response\r\n",             37 },
+  { "NOTICE AUTH :*** No ident response\r\n",              36 },
+  { "NOTICE AUTH :*** Your forward and reverse DNS do not match, " \
+    "ignoring hostname.\r\n",                              80 }
+};
+
+typedef enum {
+  REPORT_DO_DNS,
+  REPORT_FIN_DNS,
+  REPORT_FIN_DNSC,
+  REPORT_FAIL_DNS,
+  REPORT_DO_ID,
+  REPORT_FIN_ID,
+  REPORT_FAIL_ID,
+  REPORT_IP_MISMATCH
+} ReportType;
+
+#define sendheader(c, r) \
+   send((c)->fd, HeaderMessages[(r)].message, HeaderMessages[(r)].length, 0)
+
+struct AuthRequest* AuthPollList = 0; /* GLOBAL - auth queries pending io */
+static struct AuthRequest* AuthIncompleteList = 0;
+
+/*
+ * make_auth_request - allocate a new auth request
+ */
+static struct AuthRequest* make_auth_request(struct Client* client)
+{
+  struct AuthRequest* auth = 
+               (struct AuthRequest*) MyMalloc(sizeof(struct AuthRequest));
+  assert(0 != auth);
+  memset(auth, 0, sizeof(struct AuthRequest));
+  auth->fd      = -1;
+  auth->client  = client;
+  auth->timeout = CurrentTime + CONNECTTIMEOUT;
+  return auth;
+}
+
+/*
+ * free_auth_request - cleanup auth request allocations
+ */
+void free_auth_request(struct AuthRequest* auth)
+{
+  if (-1 < auth->fd)
+    close(auth->fd);
+  MyFree(auth);
+}
+
+/*
+ * unlink_auth_request - remove auth request from a list
+ */
+static void unlink_auth_request(struct AuthRequest* request,
+                                struct AuthRequest** list)
+{
+  if (request->next)
+    request->next->prev = request->prev;
+  if (request->prev)
+    request->prev->next = request->next;
+  else
+    *list = request->next;
+}
+
+/*
+ * link_auth_request - add auth request to a list
+ */
+static void link_auth_request(struct AuthRequest* request,
+                              struct AuthRequest** list)
+{
+  request->prev = 0;
+  request->next = *list;
+  if (*list)
+    (*list)->prev = request;
+  *list = request;
+}
+
+/*
+ * release_auth_client - release auth client from auth system
+ * this adds the client into the local client lists so it can be read by
+ * the main io processing loop
  */
-void start_auth(aClient *cptr)
+static void release_auth_client(struct Client* client)
+{
+  assert(0 != client);
+  client->lasttime = client->since = CurrentTime;
+  if (client->fd > HighestFd)
+    HighestFd = client->fd;
+  LocalClientArray[client->fd] = client;
+
+  add_client_to_list(client);
+  Debug((DEBUG_INFO, "Auth: release_auth_client %s@%s[%s]",
+         client->username, client->sockhost, client->sock_ip));
+}
+static void auth_kill_client(struct AuthRequest* auth)
 {
-  struct sockaddr_in sock;
-  int err;
-
-  Debug((DEBUG_NOTICE, "start_auth(%p) fd %d status %d",
-      cptr, cptr->fd, cptr->status));
-
-  alarm(2);
-  cptr->authfd = socket(AF_INET, SOCK_STREAM, 0);
-  err = errno;
-  alarm(0);
-
-  if (cptr->authfd < 0)
-  {
-#ifdef USE_SYSLOG
-    syslog(LOG_ERR, "Unable to create auth socket for %s:%m",
-       get_client_name(cptr, FALSE));
+  assert(0 != auth);
+
+  unlink_auth_request(auth, (IsDoingAuth(auth)) ? &AuthPollList : &AuthIncompleteList);
+
+  if (IsDNSPending(auth))
+    delete_resolver_queries(auth);
+  IPcheck_disconnect(auth->client);
+  Count_unknowndisconnects(UserStats);
+  free_client(auth->client);
+  free_auth_request(auth);
+}
+
+/*
+ * auth_dns_callback - called when resolver query finishes
+ * if the query resulted in a successful search, hp will contain
+ * a non-null pointer, otherwise hp will be null.
+ * set the client on it's way to a connection completion, regardless
+ * of success of failure
+ */
+static void auth_dns_callback(void* vptr, struct DNSReply* reply)
+{
+  struct AuthRequest* auth = (struct AuthRequest*) vptr;
+
+  assert(0 != auth);
+  /*
+   * need to do this here so auth_kill_client doesn't
+   * try have the resolver delete the query it's about
+   * to delete anyways. --Bleep
+   */
+  ClearDNSPending(auth);
+
+  if (reply) {
+    const struct hostent* hp = reply->hp;
+    int i;
+    assert(0 != hp);
+    /*
+     * Verify that the host to ip mapping is correct both ways and that
+     * the ip#(s) for the socket is listed for the host.
+     */
+    for (i = 0; hp->h_addr_list[i]; ++i) {
+      if (0 == memcmp(hp->h_addr_list[i], &auth->client->ip,
+                      sizeof(struct in_addr)))
+         break;
+    }
+    if (!hp->h_addr_list[i]) {
+      if (IsUserPort(auth->client))
+        sendheader(auth->client, REPORT_IP_MISMATCH);
+      sendto_op_mask(SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%s]",
+                     auth->client->sock_ip, hp->h_name, 
+                     ircd_ntoa(hp->h_addr_list[0]));
+#if defined(KILL_IPMISMATCH)
+      auth_kill_client(auth);
+      return;
 #endif
-    Debug((DEBUG_ERROR, "Unable to create auth socket for %s:%s",
-       get_client_name(cptr, FALSE), strerror(get_sockerr(cptr))));
-    if (!DoingDNS(cptr))
-      SetAccess(cptr);
-    ircstp->is_abad++;
-    return;
+    }
+    else {
+      ++reply->ref_count;
+      auth->client->dns_reply = reply;
+      ircd_strncpy(auth->client->sockhost, hp->h_name, HOSTLEN);
+      if (IsUserPort(auth->client))
+        sendheader(auth->client, REPORT_FIN_DNS);
+    }
   }
-  if (cptr->authfd >= (MAXCONNECTIONS - 2))
-  {
-    close(cptr->authfd);
-    cptr->authfd = -1;
-    return;
+  else {
+    /*
+     * this should have already been done by s_bsd.c in add_connection
+     *
+     * strcpy(auth->client->sockhost, auth->client->sock_ip);
+     */
+    if (IsUserPort(auth->client))
+      sendheader(auth->client, REPORT_FAIL_DNS);
+  }
+  if (!IsDoingAuth(auth)) {
+    release_auth_client(auth->client);
+    unlink_auth_request(auth, &AuthIncompleteList);
+    free_auth_request(auth);
   }
+}
 
-  set_non_blocking(cptr->authfd, cptr);
+/*
+ * authsenderr - handle auth send errors
+ */
+static void auth_error(struct AuthRequest* auth, int kill)
+{
+  ++ServerStats->is_abad;
 
-#ifdef VIRTUAL_HOST
-  if (bind(cptr->authfd, (struct sockaddr *)&vserv, sizeof(vserv)) == -1)
-  {
-    report_error("binding auth stream socket %s: %s", cptr);
-    close(cptr->authfd);
-    cptr->authfd = -1;
+  assert(0 != auth);
+  close(auth->fd);
+  auth->fd = -1;
+
+  if (IsUserPort(auth->client))
+    sendheader(auth->client, REPORT_FAIL_ID);
+
+  if (kill) {
+    /*
+     * we can't read the client info from the client socket,
+     * close the client connection and free the client
+     * Need to do this before we ClearAuth(auth) so we know
+     * which list to remove the query from. --Bleep
+     */
+    auth_kill_client(auth);
     return;
   }
+
+  ClearAuth(auth);
+  unlink_auth_request(auth, &AuthPollList);
+
+  if (IsDNSPending(auth))
+    link_auth_request(auth, &AuthIncompleteList);
+  else {
+    release_auth_client(auth->client);
+    free_auth_request(auth);
+  }
+}
+
+/*
+ * start_auth_query - Flag the client to show that an attempt to 
+ * contact the ident server on the client's host.  The connect and
+ * subsequently the socket are all put into 'non-blocking' mode.  
+ * Should the connect or any later phase of the identifing process fail,
+ * it is aborted and the user is given a username of "unknown".
+ */
+static int start_auth_query(struct AuthRequest* auth)
+{
+  struct sockaddr_in remote_addr;
+  struct sockaddr_in local_addr;
+  int                fd;
+
+  assert(0 != auth);
+  assert(0 != auth->client);
+
+  if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+#if 0
+    report_error(SOCKET_ERROR_MSG, get_client_name(auth->client, HIDE_IP), errno);
+#endif
+    ++ServerStats->is_abad;
+    return 0;
+  }
+  if ((MAXCONNECTIONS - 10) < fd) {
+#if 0
+    report_error(CONNLIMIT_ERROR_MSG, 
+                 get_client_name(auth->client, HIDE_IP), errno);
+#endif
+    close(fd);
+    return 0;
+  }
+  if (!os_set_nonblocking(fd)) {
+#if 0
+    report_error(NONB_ERROR_MSG, get_client_name(auth->client, HIDE_IP), errno);
+#endif
+    close(fd);
+    return 0;
+  }
+  if (IsUserPort(auth->client))
+    sendheader(auth->client, REPORT_DO_ID);
+  /* 
+   * get the local address of the client and bind to that to
+   * make the auth request.  This used to be done only for
+   * ifdef VIRTTUAL_HOST, but needs to be done for all clients
+   * since the ident request must originate from that same address--
+   * and machines with multiple IP addresses are common now
+   */
+  memset(&local_addr, 0, sizeof(struct sockaddr_in));
+  os_get_sockname(auth->client->fd, &local_addr);
+  local_addr.sin_port = htons(0);
+
+  if (bind(fd, (struct sockaddr*) &local_addr, sizeof(struct sockaddr_in))) {
+#if 0
+    report_error(BIND_ERROR_MSG,
+                 get_client_name(auth->client, HIDE_IP), errno);
 #endif
-  memcpy(&sock.sin_addr, &cptr->ip, sizeof(struct in_addr));
+    close(fd);
+    return 0;
+  }
 
-  sock.sin_port = htons(113);
-  sock.sin_family = AF_INET;
+  remote_addr.sin_addr.s_addr = auth->client->ip.s_addr;
+  remote_addr.sin_port = htons(113);
+  remote_addr.sin_family = AF_INET;
 
-  alarm((unsigned)4);
-  if (connect(cptr->authfd, (struct sockaddr *)&sock,
-      sizeof(sock)) == -1 && errno != EINPROGRESS)
-  {
-    ircstp->is_abad++;
+  if (!os_connect_nonb(fd, &remote_addr)) {
+    ServerStats->is_abad++;
     /*
      * No error report from this...
      */
-    alarm((unsigned)0);
-    close(cptr->authfd);
-    cptr->authfd = -1;
-    if (!DoingDNS(cptr))
-      SetAccess(cptr);
-    return;
+    close(fd);
+    if (IsUserPort(auth->client))
+      sendheader(auth->client, REPORT_FAIL_ID);
+    return 0;
+  }
+
+  auth->fd = fd;
+
+  SetAuthConnect(auth);
+  return 1;
+}
+
+
+enum IdentReplyFields {
+  IDENT_PORT_NUMBERS,
+  IDENT_REPLY_TYPE,
+  IDENT_OS_TYPE,
+  IDENT_INFO,
+  USERID_TOKEN_COUNT
+};
+
+static char* check_ident_reply(char* reply)
+{
+  char* token;
+  char* end;
+  char* vector[USERID_TOKEN_COUNT];
+  int count = token_vector(reply, ':', vector, USERID_TOKEN_COUNT);
+
+  if (USERID_TOKEN_COUNT != count)
+    return 0;
+  /*
+   * second token is the reply type
+   */
+  token = vector[IDENT_REPLY_TYPE];
+  if (EmptyString(token))
+    return 0;
+
+  while (IsSpace(*token))
+    ++token;
+
+  if (0 != strncmp(token, "USERID", 6))
+    return 0;
+
+  /*
+   * third token is the os type
+   */
+  token = vector[IDENT_OS_TYPE];
+  if (EmptyString(token))
+    return 0;
+  while (IsSpace(*token))
+   ++token;
+
+  /*
+   * Unless "OTHER" is specified as the operating system
+   * type, the server is expected to return the "normal"
+   * user identification of the owner of this connection.
+   * "Normal" in this context may be taken to mean a string
+   * of characters which uniquely identifies the connection
+   * owner such as a user identifier assigned by the system
+   * administrator and used by such user as a mail
+   * identifier, or as the "user" part of a user/password
+   * pair used to gain access to system resources.  When an
+   * operating system is specified (e.g., anything but
+   * "OTHER"), the user identifier is expected to be in a
+   * more or less immediately useful form - e.g., something
+   * that could be used as an argument to "finger" or as a
+   * mail address.
+   */
+  if (0 == strncmp(token, "OTHER", 5))
+    return 0;
+  /*
+   * fourth token is the username
+   */
+  token = vector[IDENT_INFO];
+  if (EmptyString(token))
+    return 0;
+  while (IsSpace(*token))
+    ++token;
+  /*
+   * look for the end of the username, terminators are '\0, @, <SPACE>, :'
+   */
+  for (end = token; *end; ++end) {
+    if (IsSpace(*end) || '@' == *end || ':' == *end)
+      break;
   }
-  alarm((unsigned)0);
-  cptr->flags |= (FLAGS_WRAUTH | FLAGS_AUTH);
-  if (cptr->authfd > highest_fd)
-    highest_fd = cptr->authfd;
-  return;
+  *end = '\0'; 
+  return token;
 }
 
+#if 0
 /*
- * send_authports
- *
- * Send the ident server a query giving "theirport , ourport".
+ * GetValidIdent - parse ident query reply from identd server
+ * 
+ * Inputs        - pointer to ident buf
+ * Output        - NULL if no valid ident found, otherwise pointer to name
+ * Side effects        -
+ */
+static char* GetValidIdent(char *buf)
+{
+  int   remp = 0;
+  int   locp = 0;
+  char* colon1Ptr;
+  char* colon2Ptr;
+  char* colon3Ptr;
+  char* commaPtr;
+  char* remotePortString;
+
+  /* All this to get rid of a sscanf() fun. */
+  remotePortString = buf;
+  
+  colon1Ptr = strchr(remotePortString,':');
+  if(!colon1Ptr)
+    return 0;
+
+  *colon1Ptr = '\0';
+  colon1Ptr++;
+  colon2Ptr = strchr(colon1Ptr,':');
+  if(!colon2Ptr)
+    return 0;
+
+  *colon2Ptr = '\0';
+  colon2Ptr++;
+  commaPtr = strchr(remotePortString, ',');
+
+  if(!commaPtr)
+    return 0;
+
+  *commaPtr = '\0';
+  commaPtr++;
+
+  remp = atoi(remotePortString);
+  if(!remp)
+    return 0;
+              
+  locp = atoi(commaPtr);
+  if(!locp)
+    return 0;
+
+  /* look for USERID bordered by first pair of colons */
+  if(!strstr(colon1Ptr, "USERID"))
+    return 0;
+
+  colon3Ptr = strchr(colon2Ptr,':');
+  if(!colon3Ptr)
+    return 0;
+  
+  *colon3Ptr = '\0';
+  colon3Ptr++;
+  return(colon3Ptr);
+}
+#endif
+
+/*
+ * start_auth - starts auth (identd) and dns queries for a client
+ */
+void start_auth(struct Client* client)
+{
+  struct DNSQuery     query;
+  struct AuthRequest* auth = 0;
+
+  assert(0 != client);
+
+  auth = make_auth_request(client);
+  assert(0 != auth);
+
+  query.vptr     = auth;
+  query.callback = auth_dns_callback;
+
+  if (IsUserPort(auth->client))
+    sendheader(client, REPORT_DO_DNS);
+
+#if !defined(NODNS)
+  client->dns_reply = gethost_byaddr((const char*) &client->ip, &query);
+  if (client->dns_reply) {
+    ++client->dns_reply->ref_count;
+    ircd_strncpy(client->sockhost, client->dns_reply->hp->h_name, HOSTLEN);
+    if (IsUserPort(auth->client))
+      sendheader(client, REPORT_FIN_DNSC);
+  }
+  else
+    SetDNSPending(auth);
+#endif
+
+  if (start_auth_query(auth))
+    link_auth_request(auth, &AuthPollList);
+  else if (IsDNSPending(auth))
+    link_auth_request(auth, &AuthIncompleteList);
+  else {
+    free_auth_request(auth);
+    release_auth_client(client);
+  }
+}
+
+/*
+ * timeout_auth_queries - timeout resolver and identd requests
+ * allow clients through if requests failed
+ */
+void timeout_auth_queries(time_t now)
+{
+  struct AuthRequest* auth;
+  struct AuthRequest* auth_next = 0;
+
+  for (auth = AuthPollList; auth; auth = auth_next) {
+    auth_next = auth->next;
+    if (auth->timeout < CurrentTime) {
+      if (-1 < auth->fd) {
+        close(auth->fd);
+        auth->fd = -1;
+      }
+
+      if (IsUserPort(auth->client))
+        sendheader(auth->client, REPORT_FAIL_ID);
+      if (IsDNSPending(auth)) {
+        delete_resolver_queries(auth);
+        if (IsUserPort(auth->client))
+          sendheader(auth->client, REPORT_FAIL_DNS);
+      }
+      ircd_log(L_INFO, "DNS/AUTH timeout %s",
+               get_client_name(auth->client, HIDE_IP));
+
+      release_auth_client(auth->client);
+      unlink_auth_request(auth, &AuthPollList);
+      free_auth_request(auth);
+    }
+  }
+  for (auth = AuthIncompleteList; auth; auth = auth_next) {
+    auth_next = auth->next;
+    if (auth->timeout < CurrentTime) {
+      delete_resolver_queries(auth);
+      if (IsUserPort(auth->client))
+        sendheader(auth->client, REPORT_FAIL_DNS);
+      ircd_log(L_INFO, "DNS timeout %s", get_client_name(auth->client, HIDE_IP));
+
+      release_auth_client(auth->client);
+      unlink_auth_request(auth, &AuthIncompleteList);
+      free_auth_request(auth);
+    }
+  }
+}
+
+/*
+ * send_auth_query - send the ident server a query giving "theirport , ourport"
  * The write is only attempted *once* so it is deemed to be a fail if the
  * entire write doesn't write all the data given.  This shouldnt be a
  * problem since the socket should have a write buffer far greater than
  * this message to store it in should problems arise. -avalon
  */
-void send_authports(aClient *cptr)
+void send_auth_query(struct AuthRequest* auth)
 {
-  struct sockaddr_in us, them;
-  char authbuf[32];
-  size_t ulen, tlen;
-
-  Debug((DEBUG_NOTICE, "write_authports(%p) fd %d authfd %d stat %d",
-      cptr, cptr->fd, cptr->authfd, cptr->status));
-  tlen = ulen = sizeof(us);
-  if (getsockname(cptr->fd, (struct sockaddr *)&us, &ulen) ||
-      getpeername(cptr->fd, (struct sockaddr *)&them, &tlen))
-  {
-#ifdef USE_SYSLOG
-    syslog(LOG_ERR, "auth get{sock,peer}name error for %s:%m",
-       get_client_name(cptr, FALSE));
-#endif
-    goto authsenderr;
+  struct sockaddr_in us;
+  struct sockaddr_in them;
+  char               authbuf[32];
+  size_t             count;
+
+  assert(0 != auth);
+  assert(0 != auth->client);
+
+  if (!os_get_sockname(auth->client->fd, &us) ||
+      !os_get_peername(auth->client->fd, &them)) {
+    auth_error(auth, 1);
+    return;
   }
+  sprintf_irc(authbuf, "%u , %u\r\n",
+             (unsigned int) ntohs(them.sin_port),
+             (unsigned int) ntohs(us.sin_port));
 
-  sprintf_irc(authbuf, "%u , %u\r\n", (unsigned int)ntohs(them.sin_port),
-      (unsigned int)ntohs(us.sin_port));
-
-  Debug((DEBUG_SEND, "sending [%s] to auth port %s.113",
-      authbuf, inetntoa(them.sin_addr)));
-  if (write(cptr->authfd, authbuf, strlen(authbuf)) != (int)strlen(authbuf))
-  {
-  authsenderr:
-    ircstp->is_abad++;
-    close(cptr->authfd);
-    if (cptr->authfd == highest_fd)
-      while (!loc_clients[highest_fd])
-       highest_fd--;
-    cptr->authfd = -1;
-    cptr->flags &= ~FLAGS_AUTH;
-    if (!DoingDNS(cptr))
-      SetAccess(cptr);
+  if (IO_SUCCESS == os_send_nonb(auth->fd, authbuf, strlen(authbuf), &count)) {
+    ClearAuthConnect(auth);
+    SetAuthPending(auth);
   }
-  cptr->flags &= ~FLAGS_WRAUTH;
-  return;
+  else
+    auth_error(auth, 0);
 }
 
+
 /*
- * read_authports
- *
- * read the reply (if any) from the ident server we connected to.
- * The actual read processijng here is pretty weak - no handling of the reply
- * if it is fragmented by IP.
+ * read_auth_reply - read the reply (if any) from the ident server 
+ * we connected to.
+ * We only give it one shot, if the reply isn't good the first time
+ * fail the authentication entirely. --Bleep
  */
-void read_authports(aClient *cptr)
+void read_auth_reply(struct AuthRequest* auth)
 {
-  Reg1 char *s, *t;
-  Reg2 int len;
-  char ruser[USERLEN + 1], system[8];
-  unsigned short int remp = 0, locp = 0;
-
-  *system = *ruser = '\0';
-  Debug((DEBUG_NOTICE, "read_authports(%p) fd %d authfd %d stat %d",
-      cptr, cptr->fd, cptr->authfd, cptr->status));
+  char*  username = 0;
+  size_t len;
   /*
-   * Nasty.  Cant allow any other reads from client fd while we're
-   * waiting on the authfd to return a full valid string.  Use the
-   * client's input buffer to buffer the authd reply.
-   * Oh. this is needed because an authd reply may come back in more
-   * than 1 read! -avalon
+   * rfc1453 sez we MUST accept 512 bytes
    */
-  if ((len = read(cptr->authfd, cptr->buffer + cptr->count,
-      sizeof(cptr->buffer) - 1 - cptr->count)) >= 0)
-  {
-    cptr->count += len;
-    cptr->buffer[cptr->count] = '\0';
+  char   buf[BUFSIZE + 1];
+
+  assert(0 != auth);
+  assert(0 != auth->client);
+
+  if (IO_SUCCESS == os_recv_nonb(auth->fd, buf, BUFSIZE, &len)) {
+    buf[len] = '\0';
+    username = check_ident_reply(buf);
   }
 
-  cptr->lasttime = now;
-  if ((len > 0) && (cptr->count != (sizeof(cptr->buffer) - 1)) &&
-      (sscanf(cptr->buffer, "%hd , %hd : USERID : %*[^:]: %10s",
-      &remp, &locp, ruser) == 3))
-  {
-    s = strrchr(cptr->buffer, ':');
-    *s++ = '\0';
-    for (t = (strrchr(cptr->buffer, ':') + 1); *t; t++)
-      if (!isSpace(*t))
-       break;
-    strncpy(system, t, sizeof(system) - 1);
-    system[sizeof(system) - 1] = 0;
-    for (t = ruser; *s && (t < ruser + sizeof(ruser)); s++)
-      if (!isSpace(*s) && *s != ':' && *s != '@')
-       *t++ = *s;
-    *t = '\0';
-    Debug((DEBUG_INFO, "auth reply ok [%s] [%s]", system, ruser));
+  close(auth->fd);
+  auth->fd = -1;
+  ClearAuth(auth);
+  
+  if (!EmptyString(username)) {
+    ircd_strncpy(auth->client->username, username, USERLEN);
+    /*
+     * Not needed, struct is zeroed by memset
+     * auth->client->username[USERLEN] = '\0';
+     */
+    SetGotId(auth->client);
+    ++ServerStats->is_asuc;
+    if (IsUserPort(auth->client))
+      sendheader(auth->client, REPORT_FIN_ID);
   }
-  else if (len != 0)
-  {
-    if (!strchr(cptr->buffer, '\n') && !strchr(cptr->buffer, '\r'))
-      return;
-    Debug((DEBUG_ERROR, "local %d remote %d", locp, remp));
-    Debug((DEBUG_ERROR, "bad auth reply in [%s]", cptr->buffer));
-    *ruser = '\0';
+  else {
+    ++ServerStats->is_abad;
+#if 0
+    strcpy(auth->client->username, "unknown");
+#endif
   }
-  close(cptr->authfd);
-  if (cptr->authfd == highest_fd)
-    while (!loc_clients[highest_fd])
-      highest_fd--;
-  cptr->count = 0;
-  cptr->authfd = -1;
-  ClearAuth(cptr);
-  if (!DoingDNS(cptr))
-    SetAccess(cptr);
-  if (len > 0)
-    Debug((DEBUG_INFO, "ident reply: [%s]", cptr->buffer));
-
-  if (!locp || !remp || !*ruser)
-  {
-    ircstp->is_abad++;
-    return;
+  unlink_auth_request(auth, &AuthPollList);
+
+  if (IsDNSPending(auth))
+    link_auth_request(auth, &AuthIncompleteList);
+  else {
+    release_auth_client(auth->client);
+    free_auth_request(auth);
   }
-  ircstp->is_asuc++;
-  strncpy(cptr->username, ruser, USERLEN);
-  cptr->username[USERLEN] = 0; /* This is the LA bug --Run */
-  if (strncmp(system, "OTHER", 5))
-    cptr->flags |= FLAGS_GOTID;
-  Debug((DEBUG_INFO, "got username [%s]", ruser));
-  return;
 }
+
+