Fix more IAuth bugs and add IAUTH log target.
[ircu2.10.12-pk.git] / ircd / ircd_auth.c
index c697e90fe45340746997085980551cedb96138e5..8b76a160cab521c84c94000b3b7d76e9e37e9b6a 100644 (file)
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
  * 02111-1307, USA.
- *
- * $Id$
+ */
+/** @file
+ * @brief IAuth client implementation for an IRC server.
+ * @version $Id$
  */
 
 #include "config.h"
 #include "msgq.h"
 #include "res.h"
 #include "s_bsd.h"
+#include "s_debug.h"
 #include "s_misc.h"
 #include "s_user.h"
 #include "send.h"
 
-#include <assert.h>
+/* #include <assert.h> -- Now using assert in ircd_log.h */
 #include <errno.h>
 #include <netdb.h>
 #include <string.h>
 #include <unistd.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
-#include <arpa/inet.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
 
+/** Describes state of a single pending IAuth request. */
 struct IAuthRequest {
-  struct IAuthRequest *iar_prev;        /* previous request struct */
-  struct IAuthRequest *iar_next;        /* next request struct */
-  struct Client *iar_client;            /* client being authenticated */
-  char iar_timed;                       /* if non-zero, using parent i_request_timer */
+  struct IAuthRequest *iar_prev;        /**< previous request struct */
+  struct IAuthRequest *iar_next;        /**< next request struct */
+  struct Client *iar_client;            /**< client being authenticated */
+  char iar_timed;                       /**< if non-zero, using parent i_request_timer */
 };
 
+/** Enumeration of IAuth connection flags. */
 enum IAuthFlag
 {
-  IAUTH_BLOCKED,                        /* socket buffer full */
-  IAUTH_CONNECTED,                      /* server greeting/handshake done */
-  IAUTH_ABORT,                          /* abort connection asap */
-  IAUTH_ICLASS,                         /* tell iauth about all local users */
-  IAUTH_CLOSING,                        /* candidate to be disposed */
-  IAUTH_LAST_FLAG
+  IAUTH_BLOCKED,                        /**< socket buffer full */
+  IAUTH_CONNECTED,                      /**< server greeting/handshake done */
+  IAUTH_ABORT,                          /**< abort connection asap */
+  IAUTH_ICLASS,                         /**< tell iauth about all local users */
+  IAUTH_CLOSING,                        /**< candidate to be disposed */
+  IAUTH_LAST_FLAG                       /**< total number of flags */
 };
+/** Declare a bitset structure indexed by IAuthFlag. */
 DECLARE_FLAGSET(IAuthFlags, IAUTH_LAST_FLAG);
 
+/** Describes state of an IAuth connection. */
 struct IAuth {
-  struct IAuthRequest i_list_head;      /* doubly linked list of requests */
-  struct MsgQ i_sendQ;                  /* messages queued to send */
-  struct Socket i_socket;               /* connection to server */
-  struct Timer i_reconn_timer;          /* when to reconnect the connection */
-  struct Timer i_request_timer;         /* when the current request times out */
-  struct IAuthFlags i_flags;            /* connection state/status/flags */
-  struct DNSQuery i_query;              /* DNS lookup for iauth server */
-  unsigned int i_recvM;                 /* messages received */
-  unsigned int i_sendM;                 /* messages sent */
-  unsigned int i_recvK;                 /* kilobytes received */
-  unsigned int i_sendK;                 /* kilobytes sent */
-  unsigned short i_recvB;               /* bytes received modulo 1024 */
-  unsigned short i_sendB;               /* bytes sent modulo 1024 */
-  time_t i_reconnect;                   /* seconds to wait before reconnecting */
-  time_t i_timeout;                     /* seconds to wait for a request */
-  unsigned int i_count;                 /* characters used in i_buffer */
-  char i_buffer[BUFSIZE+1];             /* partial unprocessed line from server */
-  char i_passwd[PASSWDLEN+1];           /* password for connection */
-  char i_host[HOSTLEN+1];               /* iauth server hostname */
-  in_addr_t i_addr;                     /* iauth server ip address */
-  unsigned short i_port;                /* iauth server port */
-  struct IAuth *i_next;                 /* next connection in list */
+  struct IAuthRequest i_list_head;      /**< doubly linked list of requests */
+  struct MsgQ i_sendQ;                  /**< messages queued to send */
+  struct Socket i_socket;               /**< connection to server */
+  struct Timer i_reconn_timer;          /**< when to reconnect the connection */
+  struct Timer i_request_timer;         /**< when the current request times out */
+  struct IAuthFlags i_flags;            /**< connection state/status/flags */
+  struct DNSQuery i_query;              /**< DNS lookup for iauth server */
+  unsigned int i_recvM;                 /**< messages received */
+  unsigned int i_sendM;                 /**< messages sent */
+  unsigned int i_recvK;                 /**< kilobytes received */
+  unsigned int i_sendK;                 /**< kilobytes sent */
+  unsigned short i_recvB;               /**< bytes received modulo 1024 */
+  unsigned short i_sendB;               /**< bytes sent modulo 1024 */
+  time_t i_reconnect;                   /**< seconds to wait before reconnecting */
+  time_t i_timeout;                     /**< seconds to wait for a request */
+  unsigned int i_count;                 /**< characters used in i_buffer */
+  char i_buffer[BUFSIZE+1];             /**< partial unprocessed line from server */
+  char i_passwd[PASSWDLEN+1];           /**< password for connection */
+  char i_host[HOSTLEN+1];               /**< iauth server hostname */
+  struct irc_sockaddr i_addr;           /**< iauth server ip address and port */
+  struct IAuth *i_next;                 /**< next connection in list */
 };
 
+/** Return flags element of \a iauth. */
 #define i_flags(iauth) ((iauth)->i_flags)
+/** Return whether flag \a flag is set on \a iauth. */
 #define IAuthGet(iauth, flag) FlagHas(&i_flags(iauth), flag)
+/** Set flag \a flag on \a iauth. */
 #define IAuthSet(iauth, flag) FlagSet(&i_flags(iauth), flag)
+/** Clear flag \a flag from \a iauth. */
 #define IAuthClr(iauth, flag) FlagClr(&i_flags(iauth), flag)
+/** Get blocked state for \a iauth. */
 #define i_GetBlocked(iauth) IAuthGet(iauth, IAUTH_BLOCKED)
+/** Set blocked state for \a iauth. */
 #define i_SetBlocked(iauth) IAuthSet(iauth, IAUTH_BLOCKED)
+/** Clear blocked state for \a iauth. */
 #define i_ClrBlocked(iauth) IAuthClr(iauth, IAUTH_BLOCKED)
+/** Get connected flag for \a iauth. */
 #define i_GetConnected(iauth) IAuthGet(iauth, IAUTH_CONNECTED)
+/** Set connected flag for \a iauth. */
 #define i_SetConnected(iauth) IAuthSet(iauth, IAUTH_CONNECTED)
+/** Clear connected flag for \a iauth. */
 #define i_ClrConnected(iauth) IAuthClr(iauth, IAUTH_CONNECTED)
+/** Get abort flag for \a iauth. */
 #define i_GetAbort(iauth) IAuthGet(iauth, IAUTH_ABORT)
+/** Set abort flag for \a iauth. */
 #define i_SetAbort(iauth) IAuthSet(iauth, IAUTH_ABORT)
+/** Clear abort flag for \a iauth. */
 #define i_ClrAbort(iauth) IAuthClr(iauth, IAUTH_ABORT)
+/** Get IClass flag for \a iauth. */
 #define i_GetIClass(iauth) IAuthGet(iauth, IAUTH_ICLASS)
+/** Set IClass flag for \a iauth. */
 #define i_SetIClass(iauth) IAuthSet(iauth, IAUTH_ICLASS)
+/** Clear IClass flag for \a iauth. */
 #define i_ClrIClass(iauth) IAuthClr(iauth, IAUTH_ICLASS)
+/** Get closing flag for \a iauth. */
 #define i_GetClosing(iauth) IAuthGet(iauth, IAUTH_CLOSING)
+/** Set closing flag for \a iauth. */
 #define i_SetClosing(iauth) IAuthSet(iauth, IAUTH_CLOSING)
+/** Clear closing flag for \a iauth. */
 #define i_ClrClosing(iauth) IAuthClr(iauth, IAUTH_CLOSING)
 
+/** Return head of request linked list for \a iauth. */
 #define i_list_head(iauth) ((iauth)->i_list_head)
+/** Return socket event generator for \a iauth. */
 #define i_socket(iauth) ((iauth)->i_socket)
+/** Return reconnect timer for \a iauth. */
 #define i_reconn_timer(iauth) ((iauth)->i_reconn_timer)
+/** Return request timeout timer for \a iauth. */
 #define i_request_timer(iauth) ((iauth)->i_request_timer)
+/** Return DNS query for \a iauth. */
 #define i_query(iauth) ((iauth)->i_query)
+/** Return received bytes (modulo 1024) for \a iauth. */
 #define i_recvB(iauth) ((iauth)->i_recvB)
+/** Return received kilobytes (modulo 1024) for \a iauth. */
 #define i_recvK(iauth) ((iauth)->i_recvK)
+/** Return received megabytes for \a iauth. */
 #define i_recvM(iauth) ((iauth)->i_recvM)
+/** Return sent bytes (modulo 1024) for \a iauth. */
 #define i_sendB(iauth) ((iauth)->i_sendB)
+/** Return sent kilobytes (modulo 1024) for \a iauth. */
 #define i_sendK(iauth) ((iauth)->i_sendK)
+/** Return sent megabytes for \a iauth. */
 #define i_sendM(iauth) ((iauth)->i_sendM)
+/** Return outbound message queue for \a iauth. */
 #define i_sendQ(iauth) ((iauth)->i_sendQ)
+/** Return reconnection interval for \a iauth. */
 #define i_reconnect(iauth) ((iauth)->i_reconnect)
+/** Return request timeout interval for \a iauth. */
 #define i_timeout(iauth) ((iauth)->i_timeout)
+/** Return length of unprocessed message data for \a iauth. */
 #define i_count(iauth) ((iauth)->i_count)
+/** Return start of unprocessed message data for \a iauth. */
 #define i_buffer(iauth) ((iauth)->i_buffer)
+/** Return password we send for \a iauth. */
 #define i_passwd(iauth) ((iauth)->i_passwd)
+/** Return server hostname for \a iauth. */
 #define i_host(iauth) ((iauth)->i_host)
+/** Return address of IAuth server for \a iauth. */
 #define i_addr(iauth) ((iauth)->i_addr)
-#define i_port(iauth) ((iauth)->i_port)
+/** Return server port for \a iauth. */
+#define i_port(iauth) ((iauth)->i_addr.port)
+/** Return next IAuth connection after \a iauth. */
 #define i_next(iauth) ((iauth)->i_next)
 
+/** Command table entry. */
 struct IAuthCmd {
-  const char *iac_name;
-  void (*iac_func)(struct IAuth *iauth, int, char *[]);
+  const char *iac_name; /**< Name of command. */
+  void (*iac_func)(struct IAuth *iauth, int, char *[]); /**< Handler function. */
 };
 
+/** Active %IAuth connection(s). */
 struct IAuth *iauth_active;
 
-static const struct IAuthCmd iauth_cmdtab[];
-
 static void iauth_write(struct IAuth *iauth);
 static void iauth_reconnect(struct IAuth *iauth);
 static void iauth_disconnect(struct IAuth *iauth);
 static void iauth_sock_callback(struct Event *ev);
 static void iauth_send_request(struct IAuth *iauth, struct IAuthRequest *iar);
 static void iauth_dispose_request(struct IAuth *iauth, struct IAuthRequest *iar);
+static void iauth_cmd_doneauth(struct IAuth *iauth, int argc, char *argv[]);
+static void iauth_cmd_badauth(struct IAuth *iauth, int argc, char *argv[]);
+
+/** Table of responses we might get from the IAuth server. */
+static const struct IAuthCmd iauth_cmdtab[] = {
+  { "DoneAuth", iauth_cmd_doneauth },
+  { "BadAuth", iauth_cmd_badauth },
+  { NULL, NULL }
+};
 
+/** Start (or update) a connection to an %IAuth server.
+ * If a connection already exists for the specified server name and
+ * port, update it with the other parameters; otherwise allocate a new
+ * IAuth record.
+ * @param[in] host %IAuth server hostname.
+ * @param[in] port %IAuth server port.
+ * @param[in] passwd Password to send.
+ * @param[in] reconnect Reconnect interval.
+ * @param[in] timeout Request timeout interval.
+ * @return IAuth structure for that connection.
+ */
 struct IAuth *iauth_connect(char *host, unsigned short port, char *passwd, time_t reconnect, time_t timeout)
 {
   struct IAuth *iauth;
@@ -172,10 +240,10 @@ struct IAuth *iauth_connect(char *host, unsigned short port, char *passwd, time_
     i_list_head(iauth).iar_next = &i_list_head(iauth);
     msgq_init(&i_sendQ(iauth));
     ircd_strncpy(i_host(iauth), host, HOSTLEN);
+    memset(&i_addr(iauth), 0, sizeof(i_addr(iauth)));
     i_port(iauth) = port;
-    i_addr(iauth) = INADDR_NONE;
-    i_next(iauth) = iauth_active;
     iauth_active = iauth;
+    timer_init(&i_reconn_timer(iauth));
     i_reconnect(iauth) = reconnect;
     iauth_reconnect(iauth);
   }
@@ -184,9 +252,11 @@ struct IAuth *iauth_connect(char *host, unsigned short port, char *passwd, time_
   else
     i_passwd(iauth)[0] = '\0';
   i_timeout(iauth) = timeout;
+  i_SetIClass(iauth);
   return iauth;
 }
 
+/** Mark all %IAuth connections as closing. */
 void iauth_mark_closing(void)
 {
   struct IAuth *iauth;
@@ -194,6 +264,9 @@ void iauth_mark_closing(void)
     i_SetClosing(iauth);
 }
 
+/** Close a particular %IAuth connection.
+ * @param[in] iauth %Connection to close.
+ */
 void iauth_close(struct IAuth *iauth)
 {
   /* Figure out what to do with the closing connection's requests. */
@@ -239,16 +312,17 @@ void iauth_close(struct IAuth *iauth)
   if (t_active(&i_request_timer(iauth)))
     timer_del(&i_request_timer(iauth));
   /* Disconnect from the server. */
-  if (s_fd(&i_socket(iauth)) != -1)
+  if (i_GetConnected(iauth))
     iauth_disconnect(iauth);
   /* Free memory. */
   MyFree(iauth);
 }
 
+/** Close all %IAuth connections marked as closing. */
 void iauth_close_unused(void)
 {
   struct IAuth *prev, *iauth, *next;
-  
+
   for (prev = NULL, iauth = iauth_active; iauth; iauth = next) {
     next = i_next(iauth);
     if (i_GetClosing(iauth)) {
@@ -265,6 +339,10 @@ void iauth_close_unused(void)
   }
 }
 
+/** Send a line to an %IAuth server.
+ * @param[in] iauth %Connection to send on.
+ * @param[in] format Format string for message.
+ */
 static void iauth_send(struct IAuth *iauth, const char *format, ...)
 {
   va_list vl;
@@ -277,6 +355,10 @@ static void iauth_send(struct IAuth *iauth, const char *format, ...)
   msgq_clean(mb);
 }
 
+/** Report a protocol violation from the %IAuth server.
+ * @param[in] iauth %Connection that experienced the violation.
+ * @param[in] format Format string for message to operators.
+ */
 static void iauth_protocol_violation(struct IAuth *iauth, const char *format, ...)
 {
   struct VarData vd;
@@ -288,6 +370,9 @@ static void iauth_protocol_violation(struct IAuth *iauth, const char *format, ..
   va_end(vd.vd_args);
 }
 
+/** Send on-connect burst to an %IAuth server.
+ * @param[in] iauth %Connection that has completed.
+ */
 static void iauth_on_connect(struct IAuth *iauth)
 {
   struct IAuthRequest *iar;
@@ -307,101 +392,114 @@ static void iauth_on_connect(struct IAuth *iauth)
   iauth_write(iauth);
 }
 
+/** Complete disconnection of an %IAuth connection.
+ * @param[in] iauth %Connection to fully close.
+ */
 static void iauth_disconnect(struct IAuth *iauth)
 {
   close(s_fd(&i_socket(iauth)));
   socket_del(&i_socket(iauth));
-  s_fd(&i_socket(iauth)) = -1;
+  i_ClrConnected(iauth);
 }
 
+/** DNS completion callback for an %IAuth connection.
+ * @param[in] vptr Pointer to the IAuth struct.
+ * @param[in] he DNS reply parameters.
+ */
 static void iauth_dns_callback(void *vptr, struct DNSReply *he)
 {
   struct IAuth *iauth = vptr;
   if (!he) {
-    sendto_opmask_butone(0, SNO_OLDSNO, "IAuth connection to %s failed: host lookup failed", i_host(iauth));
-  } else if (he->h_addrtype != AF_INET) {
-    sendto_opmask_butone(0, SNO_OLDSNO, "IAuth connection to %s failed: bad host type %d", i_host(iauth), he->h_addrtype);
+    log_write(LS_IAUTH, L_NOTICE, 0, "IAuth connection to %s failed: host lookup failed", i_host(iauth));
   } else {
-    struct sockaddr_in *sin = (struct sockaddr_in*)&he->addr;
-    i_addr(iauth) = sin->sin_addr.s_addr;
-    if (INADDR_NONE == i_addr(iauth)) {
-      sendto_opmask_butone(0, SNO_OLDSNO, "IAuth connection to %s failed: host came back as INADDR_NONE", i_host(iauth));
+    memcpy(&i_addr(iauth).addr, &he->addr, sizeof(i_addr(iauth).addr));
+    if (!irc_in_addr_valid(&i_addr(iauth).addr)) {
+      log_write(LS_IAUTH, L_NOTICE, 0, "IAuth connection to %s failed: host came back as unresolved", i_host(iauth));
       return;
     }
     iauth_reconnect(iauth);
   }
 }
 
+/** Timer callback for reconnecting to %IAuth.
+ * @param[in] ev Timer event for reconnect.
+ */
 static void iauth_reconnect_ev(struct Event *ev)
 {
   if (ev_type(ev) == ET_EXPIRE)
     iauth_reconnect(t_data(ev_timer(ev)));
 }
 
+/** Schedule a reconnection for \a iauth.
+ * @param[in] iauth %Connection that needs to be reconnected.
+ */
 static void iauth_schedule_reconnect(struct IAuth *iauth)
 {
   struct Timer *timer;
-  assert(!t_active(&i_reconn_timer(iauth)));
-  timer = timer_init(&i_reconn_timer(iauth));
-  timer_add(timer, iauth_reconnect_ev, iauth, TT_RELATIVE, i_reconnect(iauth));
+  timer = &i_reconn_timer(iauth);
+  if (t_onqueue(timer))
+    timer_chg(timer, TT_RELATIVE, i_reconnect(iauth));
+  else
+    timer_add(&i_reconn_timer(iauth), iauth_reconnect_ev,
+              iauth, TT_RELATIVE, i_reconnect(iauth));
 }
 
+/** Initiate a (re-)connection to \a iauth.
+ * @param[in] iauth %Connection that should be initiated.
+ */
 static void iauth_reconnect(struct IAuth *iauth)
 {
-  extern struct sockaddr_in VirtualHost;
-  struct sockaddr_in sin;
+  struct irc_sockaddr *local;
   IOResult result;
   int fd;
 
-  if (INADDR_NONE == i_addr(iauth)) {
-    i_addr(iauth) = inet_addr(i_host(iauth));
-    if (INADDR_NONE == i_addr(iauth)) {
-      i_query(iauth).vptr = iauth;
-      i_query(iauth).callback = iauth_dns_callback;
-      gethost_byname(i_host(iauth), &i_query(iauth));
-      return;
-    }
-  }
-  fd = socket(AF_INET, SOCK_STREAM, 0);
-  if (0 > fd) {
-    sendto_opmask_butone(0, SNO_OLDSNO, "IAuth reconnect unable to allocate socket: %s", strerror(errno));
+  if (i_GetConnected(iauth)) {
+    iauth_disconnect(iauth);
+    iauth_schedule_reconnect(iauth);
     return;
   }
-  if (feature_bool(FEAT_VIRTUAL_HOST)
-      && bind(fd, (struct sockaddr*)&VirtualHost, sizeof(VirtualHost)) != 0) {
-    close(fd);
-    sendto_opmask_butone(0, SNO_OLDSNO, "IAuth reconnect unable to bind vhost: %s", strerror(errno));
+  log_write(LS_IAUTH, L_DEBUG, 0, "IAuth attempt connection to %s port %p.", i_host(iauth), i_port(iauth));
+  if (!irc_in_addr_valid(&i_addr(iauth).addr)
+      && !ircd_aton(&i_addr(iauth).addr, i_host(iauth))) {
+    i_query(iauth).vptr = iauth;
+    i_query(iauth).callback = iauth_dns_callback;
+    gethost_byname(i_host(iauth), &i_query(iauth));
     return;
   }
-  if (!os_set_sockbufs(fd, SERVER_TCP_WINDOW, SERVER_TCP_WINDOW)) {
-    close(fd);
-    sendto_opmask_butone(0, SNO_OLDSNO, "IAuth reconnect unable to set socket buffers: %s", strerror(errno));
+  local = irc_in_addr_is_ipv4(&i_addr(iauth).addr) ? &VirtualHost_v4 : &VirtualHost_v6;
+  fd = os_socket(local, SOCK_STREAM, "IAuth");
+  if (fd < 0) {
+    iauth_schedule_reconnect(iauth);
     return;
   }
-  if (!os_set_nonblocking(fd)) {
-    close(fd);
-    sendto_opmask_butone(0, SNO_OLDSNO, "IAuth reconnect unable to make socket non-blocking: %s", strerror(errno));
-    return;
+  if (!os_set_sockbufs(fd, SERVER_TCP_WINDOW, SERVER_TCP_WINDOW)) {
+    log_write(LS_IAUTH, L_WARNING, 0, "IAuth reconnect unable to set socket buffers: %s", strerror(errno));
+    goto failure;
   }
-  memset(&sin, 0, sizeof(sin));
-  sin.sin_family = AF_INET;
-  sin.sin_addr.s_addr = i_addr(iauth);
-  sin.sin_port = htons(i_port(iauth));
-  result = os_connect_nonb(fd, &sin);
+  s_fd(&i_socket(iauth)) = fd;
+  result = os_connect_nonb(fd, &i_addr(iauth));
   if (result == IO_FAILURE) {
-    close(fd);
-    sendto_opmask_butone(0, SNO_OLDSNO, "IAuth reconnect unable to initiate connection: %s", strerror(errno));
-    return;
+    log_write(LS_IAUTH, L_NOTICE, 0, "IAuth reconnect unable to initiate connection: %s", strerror(errno));
+    goto failure;
   }
   if (!socket_add(&i_socket(iauth), iauth_sock_callback, iauth,
                   (result == IO_SUCCESS) ? SS_CONNECTED : SS_CONNECTING,
                   SOCK_EVENT_READABLE | SOCK_EVENT_WRITABLE, fd)) {
-    close(fd);
-    sendto_opmask_butone(0, SNO_OLDSNO, "IAuth reconnect unable to add socket: %s", strerror(errno));
-    return;
+    log_write(LS_IAUTH, L_WARNING, 0, "IAuth reconnect unable to add socket: %s", strerror(errno));
+    goto failure;
   }
+  return;
+failure:
+  close(fd);
+  i_ClrConnected(iauth);
+  iauth_schedule_reconnect(iauth);
+  return;
 }
 
+/** Read input from \a iauth.
+ * Reads up to SERVER_TCP_WINDOW bytes per pass.
+ * @param[in] iauth Readable connection.
+ */
 static void iauth_read(struct IAuth *iauth)
 {
   char *src, *endp, *old_buffer, *argv[MAXPARA + 1];
@@ -409,8 +507,11 @@ static void iauth_read(struct IAuth *iauth)
   char readbuf[SERVER_TCP_WINDOW];
 
   length = 0;
-  if (IO_FAILURE == os_recv_nonb(s_fd(&i_socket(iauth)), readbuf, sizeof(readbuf), &length))
-    return;
+  if (IO_FAILURE == os_recv_nonb(s_fd(&i_socket(iauth)), readbuf, sizeof(readbuf), &length)
+      || length == 0) {
+      iauth_reconnect(iauth);
+      return;
+  }
   i_recvB(iauth) += length;
   if (i_recvB(iauth) > 1023) {
     i_recvK(iauth) += i_recvB(iauth) >> 10;
@@ -461,6 +562,9 @@ static void iauth_read(struct IAuth *iauth)
   i_count(iauth) = endp - old_buffer;
 }
 
+/** Send queued output to \a iauth.
+ * @param[in] iauth Writable connection with queued data.
+ */
 static void iauth_write(struct IAuth *iauth)
 {
   unsigned int bytes_tried, bytes_sent;
@@ -494,6 +598,9 @@ static void iauth_write(struct IAuth *iauth)
   socket_events(&i_socket(iauth), SOCK_ACTION_DEL | SOCK_EVENT_WRITABLE);
 }
 
+/** Handle socket activity for an %IAuth connection.
+ * @param[in] ev &Socket event; the IAuth connection is the user data pointer for the socket.
+ */
 static void iauth_sock_callback(struct Event *ev)
 {
   struct IAuth *iauth;
@@ -518,13 +625,12 @@ static void iauth_sock_callback(struct Event *ev)
     i_ClrBlocked(iauth);
     iauth_write(iauth);
     break;
-  case ET_EOF:
-    iauth_disconnect(iauth);
-    break;
   case ET_ERROR:
-    sendto_opmask_butone(0, SNO_OLDSNO, "IAuth socket error: %s", strerror(ev_data(ev)));
-    log_write(LS_SOCKET, L_ERROR, 0, "IAuth socket error: %s", strerror(ev_data(ev)));
+    log_write(LS_IAUTH, L_ERROR, 0, "IAuth socket error: %s", strerror(ev_data(ev)));
+    /* and fall through to the ET_EOF case */
+  case ET_EOF:
     iauth_disconnect(iauth);
+    iauth_schedule_reconnect(iauth);
     break;
   default:
     assert(0 && "Unrecognized event type");
@@ -534,22 +640,31 @@ static void iauth_sock_callback(struct Event *ev)
 
 /* Functions related to IAuthRequest structs */
 
+/** Handle timeout while waiting for a response.
+ * @param[in] ev Timer event that expired.
+ */
 static void iauth_request_ev(struct Event *ev)
 {
   /* TODO: this could probably be more intelligent */
   if (ev_type(ev) == ET_EXPIRE) {
-    sendto_opmask_butone(0, SNO_OLDSNO, "IAuth request timed out; reconnecting");
+    log_write(LS_IAUTH, L_NOTICE, 0, "IAuth request timed out; reconnecting");
     iauth_reconnect(t_data(ev_timer(ev)));
   }
 }
 
+/** Send a authorization request to an %IAuth server.
+ * @param[in] iauth %Connection to send request on.
+ * @param[in] iar Request to send.
+ */
 static void iauth_send_request(struct IAuth *iauth, struct IAuthRequest *iar)
 {
   struct Client *client;
 
   /* If iauth is not connected, we must defer the request. */
-  if (!i_GetConnected(iauth))
+  if (!i_GetConnected(iauth)) {
+    Debug((DEBUG_SEND, "IAuth deferring request for %s because we are not connected.", cli_name(iar->iar_client)));
     return;
+  }
 
   /* If no timed request, set up expiration timer. */
   if (!t_active(&i_request_timer(iauth))) {
@@ -571,6 +686,11 @@ static void iauth_send_request(struct IAuth *iauth, struct IAuthRequest *iar)
   iauth_write(iauth);
 }
 
+/** Start independent authorization check for a client.
+ * @param[in] iauth %Connection to send request on.
+ * @param[in] cptr Client to check.
+ * @return Zero, or CPTR_KILLED in case of memory allocation failure.
+ */
 int iauth_start_client(struct IAuth *iauth, struct Client *cptr)
 {
   struct IAuthRequest *iar;
@@ -578,6 +698,7 @@ int iauth_start_client(struct IAuth *iauth, struct Client *cptr)
   /* Allocate and initialize IAuthRequest struct. */
   if (!(iar = MyCalloc(1, sizeof(*iar))))
     return exit_client(cptr, cptr, &me, "IAuth memory allocation failed");
+  cli_iauth(cptr) = iar;
   iar->iar_next = &i_list_head(iauth);
   iar->iar_prev = i_list_head(iauth).iar_prev;
   iar->iar_client = cptr;
@@ -590,16 +711,27 @@ int iauth_start_client(struct IAuth *iauth, struct Client *cptr)
   return 0;
 }
 
+/** Handle a client that is disconnecting.
+ * If there is a pending %IAuth request for the client, close it.
+ * @param[in] cptr Client that is disconnecting.
+ */
 void iauth_exit_client(struct Client *cptr)
 {
   if (cli_iauth(cptr)) {
     iauth_dispose_request(iauth_active, cli_iauth(cptr));
     cli_iauth(cptr) = NULL;
-  } else if (IsIAuthed(cptr) && i_GetIClass(iauth_active)) {
-    /* TODO: report quit to iauth */
   }
+  if (!i_GetConnected(iauth_active))
+    return;
+  iauth_send(iauth_active, "ExitUser %x", cptr);
+  iauth_write(iauth_active);
 }
 
+/** Find pending request with a particular ID.
+ * @param[in] iauth %Connection context for the ID.
+ * @param[in] id Identifier to look up.
+ * @return IAuthRequest with that ID, or NULL.
+ */
 static struct IAuthRequest *iauth_find_request(struct IAuth *iauth, char *id)
 {
   struct IAuthRequest *curr;
@@ -615,6 +747,10 @@ static struct IAuthRequest *iauth_find_request(struct IAuth *iauth, char *id)
   return NULL;
 }
 
+/** Unlink and free a request.
+ * @param[in] iauth Connection that owns the request.
+ * @param[in] iar Request to free.
+ */
 static void iauth_dispose_request(struct IAuth *iauth, struct IAuthRequest *iar)
 {
   assert(iar->iar_client != NULL);
@@ -626,6 +762,12 @@ static void iauth_dispose_request(struct IAuth *iauth, struct IAuthRequest *iar)
   MyFree(iar);
 }
 
+/** Handle a DoneAuth response from %IAuth.
+ * This means the client is authorized, so let them in.
+ * @param[in] iauth Connection that sent the message.
+ * @param[in] argc Argument count.
+ * @param[in] argv Argument list.
+ */
 static void iauth_cmd_doneauth(struct IAuth *iauth, int argc, char *argv[])
 {
   struct IAuthRequest *iar;
@@ -662,6 +804,12 @@ static void iauth_cmd_doneauth(struct IAuth *iauth, int argc, char *argv[])
   register_user(client, client, cli_name(client), username);
 }
 
+/** Handle a BadAuth response from %IAuth.
+ * This means the client is not authorized, so dump them.
+ * @param[in] iauth Connection that sent the message.
+ * @param[in] argc Argument count.
+ * @param[in] argv Argument list.
+ */
 static void iauth_cmd_badauth(struct IAuth *iauth, int argc, char *argv[])
 {
   struct IAuthRequest *iar;
@@ -688,9 +836,3 @@ static void iauth_cmd_badauth(struct IAuth *iauth, int argc, char *argv[])
   iauth_dispose_request(iauth, iar);
   exit_client(client, client, &me, reason);
 }
-
-static const struct IAuthCmd iauth_cmdtab[] = {
-  { "DoneAuth", iauth_cmd_doneauth },
-  { "BadAuth", iauth_cmd_badauth },
-  { NULL, NULL }
-};