e9441625bb297e7ad93235eb27b747829d0e89ac
[ircu2.10.12-pk.git] / ircd / ircd_auth.c
1 /*
2  * IRC - Internet Relay Chat, ircd/ircd_auth.c
3  * Copyright 2004 Michael Poole <mdpoole@troilus.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18  * 02111-1307, USA.
19  */
20 /** @file
21  * @brief IAuth client implementation for an IRC server.
22  * @version $Id$
23  */
24
25 #include "config.h"
26 #include "client.h"
27 #include "ircd_alloc.h"
28 #include "ircd_auth.h"
29 #include "ircd_events.h"
30 #include "ircd_features.h"
31 #include "ircd_log.h"
32 #include "ircd_osdep.h"
33 #include "ircd_snprintf.h"
34 #include "ircd_string.h"
35 #include "ircd.h"
36 #include "msg.h"
37 #include "msgq.h"
38 #include "res.h"
39 #include "s_bsd.h"
40 #include "s_debug.h"
41 #include "s_misc.h"
42 #include "s_user.h"
43 #include "send.h"
44
45 /* #include <assert.h> -- Now using assert in ircd_log.h */
46 #include <errno.h>
47 #include <netdb.h>
48 #include <string.h>
49 #include <unistd.h>
50 #include <sys/socket.h>
51 #include <netinet/in.h>
52 #ifdef HAVE_STDINT_H
53 #include <stdint.h>
54 #endif
55
56 /** Describes state of a single pending IAuth request. */
57 struct IAuthRequest {
58   struct IAuthRequest *iar_prev;        /**< previous request struct */
59   struct IAuthRequest *iar_next;        /**< next request struct */
60   struct Client *iar_client;            /**< client being authenticated */
61   char iar_timed;                       /**< if non-zero, using parent i_request_timer */
62 };
63
64 /** Enumeration of IAuth connection flags. */
65 enum IAuthFlag
66 {
67   IAUTH_BLOCKED,                        /**< socket buffer full */
68   IAUTH_CONNECTED,                      /**< server greeting/handshake done */
69   IAUTH_ABORT,                          /**< abort connection asap */
70   IAUTH_ICLASS,                         /**< tell iauth about all local users */
71   IAUTH_CLOSING,                        /**< candidate to be disposed */
72   IAUTH_LAST_FLAG                       /**< total number of flags */
73 };
74 /** Declare a bitset structure indexed by IAuthFlag. */
75 DECLARE_FLAGSET(IAuthFlags, IAUTH_LAST_FLAG);
76
77 /** Describes state of an IAuth connection. */
78 struct IAuth {
79   struct IAuthRequest i_list_head;      /**< doubly linked list of requests */
80   struct MsgQ i_sendQ;                  /**< messages queued to send */
81   struct Socket i_socket;               /**< connection to server */
82   struct Timer i_reconn_timer;          /**< when to reconnect the connection */
83   struct Timer i_request_timer;         /**< when the current request times out */
84   struct IAuthFlags i_flags;            /**< connection state/status/flags */
85   struct DNSQuery i_query;              /**< DNS lookup for iauth server */
86   unsigned int i_recvM;                 /**< messages received */
87   unsigned int i_sendM;                 /**< messages sent */
88   unsigned int i_recvK;                 /**< kilobytes received */
89   unsigned int i_sendK;                 /**< kilobytes sent */
90   unsigned short i_recvB;               /**< bytes received modulo 1024 */
91   unsigned short i_sendB;               /**< bytes sent modulo 1024 */
92   time_t i_reconnect;                   /**< seconds to wait before reconnecting */
93   time_t i_timeout;                     /**< seconds to wait for a request */
94   unsigned int i_count;                 /**< characters used in i_buffer */
95   char i_buffer[BUFSIZE+1];             /**< partial unprocessed line from server */
96   char i_passwd[PASSWDLEN+1];           /**< password for connection */
97   char i_host[HOSTLEN+1];               /**< iauth server hostname */
98   struct irc_sockaddr i_addr;           /**< iauth server ip address and port */
99   struct IAuth *i_next;                 /**< next connection in list */
100 };
101
102 /** Return flags element of \a iauth. */
103 #define i_flags(iauth) ((iauth)->i_flags)
104 /** Return whether flag \a flag is set on \a iauth. */
105 #define IAuthGet(iauth, flag) FlagHas(&i_flags(iauth), flag)
106 /** Set flag \a flag on \a iauth. */
107 #define IAuthSet(iauth, flag) FlagSet(&i_flags(iauth), flag)
108 /** Clear flag \a flag from \a iauth. */
109 #define IAuthClr(iauth, flag) FlagClr(&i_flags(iauth), flag)
110 /** Get blocked state for \a iauth. */
111 #define i_GetBlocked(iauth) IAuthGet(iauth, IAUTH_BLOCKED)
112 /** Set blocked state for \a iauth. */
113 #define i_SetBlocked(iauth) IAuthSet(iauth, IAUTH_BLOCKED)
114 /** Clear blocked state for \a iauth. */
115 #define i_ClrBlocked(iauth) IAuthClr(iauth, IAUTH_BLOCKED)
116 /** Get connected flag for \a iauth. */
117 #define i_GetConnected(iauth) IAuthGet(iauth, IAUTH_CONNECTED)
118 /** Set connected flag for \a iauth. */
119 #define i_SetConnected(iauth) IAuthSet(iauth, IAUTH_CONNECTED)
120 /** Clear connected flag for \a iauth. */
121 #define i_ClrConnected(iauth) IAuthClr(iauth, IAUTH_CONNECTED)
122 /** Get abort flag for \a iauth. */
123 #define i_GetAbort(iauth) IAuthGet(iauth, IAUTH_ABORT)
124 /** Set abort flag for \a iauth. */
125 #define i_SetAbort(iauth) IAuthSet(iauth, IAUTH_ABORT)
126 /** Clear abort flag for \a iauth. */
127 #define i_ClrAbort(iauth) IAuthClr(iauth, IAUTH_ABORT)
128 /** Get IClass flag for \a iauth. */
129 #define i_GetIClass(iauth) IAuthGet(iauth, IAUTH_ICLASS)
130 /** Set IClass flag for \a iauth. */
131 #define i_SetIClass(iauth) IAuthSet(iauth, IAUTH_ICLASS)
132 /** Clear IClass flag for \a iauth. */
133 #define i_ClrIClass(iauth) IAuthClr(iauth, IAUTH_ICLASS)
134 /** Get closing flag for \a iauth. */
135 #define i_GetClosing(iauth) IAuthGet(iauth, IAUTH_CLOSING)
136 /** Set closing flag for \a iauth. */
137 #define i_SetClosing(iauth) IAuthSet(iauth, IAUTH_CLOSING)
138 /** Clear closing flag for \a iauth. */
139 #define i_ClrClosing(iauth) IAuthClr(iauth, IAUTH_CLOSING)
140
141 /** Return head of request linked list for \a iauth. */
142 #define i_list_head(iauth) ((iauth)->i_list_head)
143 /** Return socket event generator for \a iauth. */
144 #define i_socket(iauth) ((iauth)->i_socket)
145 /** Return reconnect timer for \a iauth. */
146 #define i_reconn_timer(iauth) ((iauth)->i_reconn_timer)
147 /** Return request timeout timer for \a iauth. */
148 #define i_request_timer(iauth) ((iauth)->i_request_timer)
149 /** Return DNS query for \a iauth. */
150 #define i_query(iauth) ((iauth)->i_query)
151 /** Return received bytes (modulo 1024) for \a iauth. */
152 #define i_recvB(iauth) ((iauth)->i_recvB)
153 /** Return received kilobytes (modulo 1024) for \a iauth. */
154 #define i_recvK(iauth) ((iauth)->i_recvK)
155 /** Return received megabytes for \a iauth. */
156 #define i_recvM(iauth) ((iauth)->i_recvM)
157 /** Return sent bytes (modulo 1024) for \a iauth. */
158 #define i_sendB(iauth) ((iauth)->i_sendB)
159 /** Return sent kilobytes (modulo 1024) for \a iauth. */
160 #define i_sendK(iauth) ((iauth)->i_sendK)
161 /** Return sent megabytes for \a iauth. */
162 #define i_sendM(iauth) ((iauth)->i_sendM)
163 /** Return outbound message queue for \a iauth. */
164 #define i_sendQ(iauth) ((iauth)->i_sendQ)
165 /** Return reconnection interval for \a iauth. */
166 #define i_reconnect(iauth) ((iauth)->i_reconnect)
167 /** Return request timeout interval for \a iauth. */
168 #define i_timeout(iauth) ((iauth)->i_timeout)
169 /** Return length of unprocessed message data for \a iauth. */
170 #define i_count(iauth) ((iauth)->i_count)
171 /** Return start of unprocessed message data for \a iauth. */
172 #define i_buffer(iauth) ((iauth)->i_buffer)
173 /** Return password we send for \a iauth. */
174 #define i_passwd(iauth) ((iauth)->i_passwd)
175 /** Return server hostname for \a iauth. */
176 #define i_host(iauth) ((iauth)->i_host)
177 /** Return address of IAuth server for \a iauth. */
178 #define i_addr(iauth) ((iauth)->i_addr)
179 /** Return server port for \a iauth. */
180 #define i_port(iauth) ((iauth)->i_addr.port)
181 /** Return next IAuth connection after \a iauth. */
182 #define i_next(iauth) ((iauth)->i_next)
183
184 /** Command table entry. */
185 struct IAuthCmd {
186   const char *iac_name; /**< Name of command. */
187   void (*iac_func)(struct IAuth *iauth, int, char *[]); /**< Handler function. */
188 };
189
190 /** Active %IAuth connection(s). */
191 struct IAuth *iauth_active;
192
193 static void iauth_write(struct IAuth *iauth);
194 static void iauth_reconnect(struct IAuth *iauth);
195 static void iauth_disconnect(struct IAuth *iauth);
196 static void iauth_sock_callback(struct Event *ev);
197 static void iauth_send_request(struct IAuth *iauth, struct IAuthRequest *iar);
198 static void iauth_dispose_request(struct IAuth *iauth, struct IAuthRequest *iar);
199 static void iauth_cmd_doneauth(struct IAuth *iauth, int argc, char *argv[]);
200 static void iauth_cmd_badauth(struct IAuth *iauth, int argc, char *argv[]);
201
202 /** Table of responses we might get from the IAuth server. */
203 static const struct IAuthCmd iauth_cmdtab[] = {
204   { "DoneAuth", iauth_cmd_doneauth },
205   { "BadAuth", iauth_cmd_badauth },
206   { NULL, NULL }
207 };
208
209 /** Start (or update) a connection to an %IAuth server.
210  * If a connection already exists for the specified server name and
211  * port, update it with the other parameters; otherwise allocate a new
212  * IAuth record.
213  * @param[in] host %IAuth server hostname.
214  * @param[in] port %IAuth server port.
215  * @param[in] passwd Password to send.
216  * @param[in] reconnect Reconnect interval.
217  * @param[in] timeout Request timeout interval.
218  * @return IAuth structure for that connection.
219  */
220 struct IAuth *iauth_connect(char *host, unsigned short port, char *passwd, time_t reconnect, time_t timeout)
221 {
222   struct IAuth *iauth;
223
224   for (iauth = iauth_active; iauth; iauth = i_next(iauth)) {
225     if (!ircd_strncmp(i_host(iauth), host, HOSTLEN)
226         && (i_port(iauth) == port)) {
227       i_ClrClosing(iauth);
228       i_reconnect(iauth) = reconnect;
229       if (t_active(&i_reconn_timer(iauth)) && (t_expire(&i_reconn_timer(iauth)) > CurrentTime + i_reconnect(iauth)))
230         timer_chg(&i_reconn_timer(iauth), TT_RELATIVE, i_reconnect(iauth));
231       break;
232     }
233   }
234   if (NULL == iauth) {
235     if (iauth_active && !i_GetClosing(iauth_active)) {
236       log_write(LS_CONFIG, L_WARNING, 0, "Creating extra active IAuth connection to %s:%d.", host, port);
237     }
238     iauth = MyCalloc(1, sizeof(*iauth));
239     i_list_head(iauth).iar_prev = &i_list_head(iauth);
240     i_list_head(iauth).iar_next = &i_list_head(iauth);
241     msgq_init(&i_sendQ(iauth));
242     ircd_strncpy(i_host(iauth), host, HOSTLEN);
243     memset(&i_addr(iauth), 0, sizeof(i_addr(iauth)));
244     i_port(iauth) = port;
245     iauth_active = iauth;
246     timer_init(&i_reconn_timer(iauth));
247     i_reconnect(iauth) = reconnect;
248     s_fd(&i_socket(iauth)) = -1;
249     iauth_reconnect(iauth);
250   }
251   if (passwd)
252     ircd_strncpy(i_passwd(iauth), passwd, PASSWDLEN);
253   else
254     i_passwd(iauth)[0] = '\0';
255   i_timeout(iauth) = timeout;
256   i_SetIClass(iauth);
257   return iauth;
258 }
259
260 /** Mark all %IAuth connections as closing. */
261 void iauth_mark_closing(void)
262 {
263   struct IAuth *iauth;
264   for (iauth = iauth_active; iauth; iauth = i_next(iauth))
265     i_SetClosing(iauth);
266 }
267
268 /** Close a particular %IAuth connection.
269  * @param[in] iauth %Connection to close.
270  */
271 void iauth_close(struct IAuth *iauth)
272 {
273   /* Figure out what to do with the closing connection's requests. */
274   if (i_list_head(iauth).iar_next != &i_list_head(iauth)) {
275     struct IAuthRequest *iar;
276     if (iauth_active || i_next(iauth)) {
277       /* If iauth_active != NULL, send requests to it; otherwise if
278        * i_next(iauth) != NULL, we can hope it or some later
279        * connection will be active.
280        */
281       struct IAuth *target = iauth_active ? iauth_active : i_next(iauth);
282
283       /* Append iauth->i_list_head to end of target->i_list_head. */
284       iar = i_list_head(iauth).iar_next;
285       iar->iar_prev = i_list_head(target).iar_prev;
286       i_list_head(target).iar_prev->iar_next = iar;
287       iar = i_list_head(iauth).iar_prev;
288       iar->iar_next = &i_list_head(target);
289       i_list_head(target).iar_prev = iar;
290
291       /* If the target is not closing, send the requests. */
292       for (iar = i_list_head(iauth).iar_next;
293            iar != &i_list_head(target);
294            iar = iar->iar_next) {
295         if (!i_GetClosing(target))
296           iauth_send_request(target, iar);
297       }
298     } else {
299       /* No active connections - approve the requests and drop them. */
300       while ((iar = i_list_head(iauth).iar_next) != &i_list_head(iauth)) {
301         struct Client *client = iar->iar_client;
302         iauth_dispose_request(iauth, iar);
303         register_user(client, client, cli_name(client), cli_username(client));
304       }
305     }
306   }
307   /* Make sure the connection closes with an empty request list. */
308   i_list_head(iauth).iar_prev = &i_list_head(iauth);
309   i_list_head(iauth).iar_next = &i_list_head(iauth);
310   /* Cancel the timer, if it is active. */
311   if (t_active(&i_reconn_timer(iauth)))
312     timer_del(&i_reconn_timer(iauth));
313   if (t_active(&i_request_timer(iauth)))
314     timer_del(&i_request_timer(iauth));
315   /* Disconnect from the server. */
316   if (s_fd(&i_socket(iauth)) != -1)
317     iauth_disconnect(iauth);
318   /* Free memory. */
319   MyFree(iauth);
320 }
321
322 /** Close all %IAuth connections marked as closing. */
323 void iauth_close_unused(void)
324 {
325   struct IAuth *prev, *iauth, *next;
326
327   for (prev = NULL, iauth = iauth_active; iauth; iauth = next) {
328     next = i_next(iauth);
329     if (i_GetClosing(iauth)) {
330       /* Update iauth_active linked list. */
331       if (prev)
332         i_next(prev) = next;
333       else
334         iauth_active = next;
335       /* Close and destroy the connection. */
336       iauth_close(iauth);
337     } else {
338       prev = iauth;
339     }
340   }
341 }
342
343 /** Send a line to an %IAuth server.
344  * @param[in] iauth %Connection to send on.
345  * @param[in] format Format string for message.
346  */
347 static void iauth_send(struct IAuth *iauth, const char *format, ...)
348 {
349   va_list vl;
350   struct MsgBuf *mb;
351
352   va_start(vl, format);
353   mb = msgq_vmake(0, format, vl);
354   va_end(vl);
355   msgq_add(&i_sendQ(iauth), mb, 0);
356   msgq_clean(mb);
357 }
358
359 /** Report a protocol violation from the %IAuth server.
360  * @param[in] iauth %Connection that experienced the violation.
361  * @param[in] format Format string for message to operators.
362  */
363 static void iauth_protocol_violation(struct IAuth *iauth, const char *format, ...)
364 {
365   struct VarData vd;
366   assert(iauth != 0);
367   assert(format != 0);
368   vd.vd_format = format;
369   va_start(vd.vd_args, format);
370   sendwallto_group_butone(&me, WALL_DESYNCH, NULL, "IAuth protocol violation: %v", &vd);
371   va_end(vd.vd_args);
372 }
373
374 /** Send on-connect burst to an %IAuth server.
375  * @param[in] iauth %Connection that has completed.
376  */
377 static void iauth_on_connect(struct IAuth *iauth)
378 {
379   struct IAuthRequest *iar;
380   if (EmptyString(i_passwd(iauth)))
381     iauth_send(iauth, "Server %s", cli_name(&me));
382   else
383     iauth_send(iauth, "Server %s %s", cli_name(&me), i_passwd(iauth));
384   if (i_GetIClass(iauth)) {
385     /* TODO: report local users to iauth */
386     iauth_send(iauth, "EndUsers");
387   }
388   i_SetConnected(iauth);
389   for (iar = i_list_head(iauth).iar_next;
390        iar != &i_list_head(iauth);
391        iar = iar->iar_next)
392     iauth_send_request(iauth, iar);
393   iauth_write(iauth);
394 }
395
396 /** Complete disconnection of an %IAuth connection.
397  * @param[in] iauth %Connection to fully close.
398  */
399 static void iauth_disconnect(struct IAuth *iauth)
400 {
401   close(s_fd(&i_socket(iauth)));
402   socket_del(&i_socket(iauth));
403   s_fd(&i_socket(iauth)) = -1;
404 }
405
406 /** DNS completion callback for an %IAuth connection.
407  * @param[in] vptr Pointer to the IAuth struct.
408  * @param[in] he DNS reply parameters.
409  */
410 static void iauth_dns_callback(void *vptr, struct DNSReply *he)
411 {
412   struct IAuth *iauth = vptr;
413   if (!he) {
414     sendto_opmask_butone(0, SNO_OLDSNO, "IAuth connection to %s failed: host lookup failed", i_host(iauth));
415   } else {
416     memcpy(&i_addr(iauth).addr, &he->addr, sizeof(i_addr(iauth).addr));
417     if (!irc_in_addr_valid(&i_addr(iauth).addr)) {
418       sendto_opmask_butone(0, SNO_OLDSNO, "IAuth connection to %s failed: host came back as unresolved", i_host(iauth));
419       return;
420     }
421     iauth_reconnect(iauth);
422   }
423 }
424
425 /** Timer callback for reconnecting to %IAuth.
426  * @param[in] ev Timer event for reconnect.
427  */
428 static void iauth_reconnect_ev(struct Event *ev)
429 {
430   if (ev_type(ev) == ET_EXPIRE)
431     iauth_reconnect(t_data(ev_timer(ev)));
432 }
433
434 /** Schedule a reconnection for \a iauth.
435  * @param[in] iauth %Connection that needs to be reconnected.
436  */
437 static void iauth_schedule_reconnect(struct IAuth *iauth)
438 {
439   struct Timer *timer;
440   timer = &i_reconn_timer(iauth);
441   if (t_onqueue(timer))
442     timer_chg(timer, TT_RELATIVE, i_reconnect(iauth));
443   else
444     timer_add(&i_reconn_timer(iauth), iauth_reconnect_ev,
445               iauth, TT_RELATIVE, i_reconnect(iauth));
446 }
447
448 /** Initiate a (re-)connection to \a iauth.
449  * @param[in] iauth %Connection that should be initiated.
450  */
451 static void iauth_reconnect(struct IAuth *iauth)
452 {
453   struct irc_sockaddr *local;
454   IOResult result;
455   int fd;
456
457   if (s_fd(&i_socket(iauth)) != -1)
458     iauth_disconnect(iauth);
459   Debug((DEBUG_INFO, "IAuth attempt connection to %s port %p.", i_host(iauth), i_port(iauth)));
460   if (!irc_in_addr_valid(&i_addr(iauth).addr)
461       && !ircd_aton(&i_addr(iauth).addr, i_host(iauth))) {
462     i_query(iauth).vptr = iauth;
463     i_query(iauth).callback = iauth_dns_callback;
464     gethost_byname(i_host(iauth), &i_query(iauth));
465     return;
466   }
467   local = irc_in_addr_is_ipv4(&i_addr(iauth).addr) ? &VirtualHost_v4 : &VirtualHost_v6;
468   fd = os_socket(local, SOCK_STREAM, "IAuth");
469   if (fd < 0)
470     return;
471   if (!os_set_sockbufs(fd, SERVER_TCP_WINDOW, SERVER_TCP_WINDOW)) {
472     close(fd);
473     sendto_opmask_butone(0, SNO_OLDSNO, "IAuth reconnect unable to set socket buffers: %s", strerror(errno));
474     return;
475   }
476   result = os_connect_nonb(fd, &i_addr(iauth));
477   if (result == IO_FAILURE) {
478     close(fd);
479     sendto_opmask_butone(0, SNO_OLDSNO, "IAuth reconnect unable to initiate connection: %s", strerror(errno));
480     return;
481   }
482   s_fd(&i_socket(iauth)) = fd;
483   if (!socket_add(&i_socket(iauth), iauth_sock_callback, iauth,
484                   (result == IO_SUCCESS) ? SS_CONNECTED : SS_CONNECTING,
485                   SOCK_EVENT_READABLE | SOCK_EVENT_WRITABLE, fd)) {
486     close(fd);
487     sendto_opmask_butone(0, SNO_OLDSNO, "IAuth reconnect unable to add socket: %s", strerror(errno));
488     return;
489   }
490 }
491
492 /** Read input from \a iauth.
493  * Reads up to SERVER_TCP_WINDOW bytes per pass.
494  * @param[in] iauth Readable connection.
495  */
496 static void iauth_read(struct IAuth *iauth)
497 {
498   char *src, *endp, *old_buffer, *argv[MAXPARA + 1];
499   unsigned int length, argc, ii;
500   char readbuf[SERVER_TCP_WINDOW];
501
502   length = 0;
503   if (IO_FAILURE == os_recv_nonb(s_fd(&i_socket(iauth)), readbuf, sizeof(readbuf), &length))
504     return;
505   if (length == 0) {
506       iauth_reconnect(iauth);
507       return;
508   }
509   i_recvB(iauth) += length;
510   if (i_recvB(iauth) > 1023) {
511     i_recvK(iauth) += i_recvB(iauth) >> 10;
512     i_recvB(iauth) &= 1023;
513   }
514   old_buffer = i_buffer(iauth);
515   endp = old_buffer + i_count(iauth);
516   for (src = readbuf; length > 0; --length) {
517     *endp = *src++;
518     if (IsEol(*endp)) {
519       /* Skip blank lines. */
520       if (endp == old_buffer)
521         continue;
522       /* NUL-terminate line and split parameters. */
523       *endp = '\0';
524       for (argc = 0, endp = old_buffer; *endp && (argc < MAXPARA); ) {
525         while (*endp == ' ')
526           *endp++ = '\0';
527         if (*endp == '\0')
528           break;
529         if (*endp == ':')
530         {
531           argv[argc++] = endp + 1;
532           break;
533         }
534         argv[argc++] = endp;
535         for (; *endp && *endp != ' '; ++endp) ;
536       }
537       argv[argc] = NULL;
538       /* Count line and reset endp to start of buffer. */
539       i_recvM(iauth)++;
540       endp = old_buffer;
541       /* Look up command and try to dispatch. */
542       if (argc > 0) {
543         for (ii = 0; iauth_cmdtab[ii].iac_name; ++ii) {
544           if (!ircd_strcmp(iauth_cmdtab[ii].iac_name, argv[0])) {
545             iauth_cmdtab[ii].iac_func(iauth, argc, argv);
546             if (i_GetAbort(iauth))
547               iauth_disconnect(iauth);
548             break;
549           }
550         }
551       }
552     }
553     else if (endp < old_buffer + BUFSIZE)
554       endp++;
555   }
556   i_count(iauth) = endp - old_buffer;
557 }
558
559 /** Send queued output to \a iauth.
560  * @param[in] iauth Writable connection with queued data.
561  */
562 static void iauth_write(struct IAuth *iauth)
563 {
564   unsigned int bytes_tried, bytes_sent;
565   IOResult iores;
566
567   if (i_GetBlocked(iauth))
568     return;
569   while (MsgQLength(&i_sendQ(iauth)) > 0) {
570     iores = os_sendv_nonb(s_fd(&i_socket(iauth)), &i_sendQ(iauth), &bytes_tried, &bytes_sent);
571     switch (iores) {
572     case IO_SUCCESS:
573       msgq_delete(&i_sendQ(iauth), bytes_sent);
574       i_sendB(iauth) += bytes_sent;
575       if (i_sendB(iauth) > 1023) {
576         i_sendK(iauth) += i_sendB(iauth) >> 10;
577         i_sendB(iauth) &= 1023;
578       }
579       if (bytes_tried == bytes_sent)
580         break;
581       /* If bytes_sent < bytes_tried, fall through to IO_BLOCKED. */
582     case IO_BLOCKED:
583       i_SetBlocked(iauth);
584       socket_events(&i_socket(iauth), SOCK_ACTION_ADD | SOCK_EVENT_WRITABLE);
585       return;
586     case IO_FAILURE:
587       iauth_disconnect(iauth);
588       return;
589     }
590   }
591   /* We were able to flush all events, so remove notification. */
592   socket_events(&i_socket(iauth), SOCK_ACTION_DEL | SOCK_EVENT_WRITABLE);
593 }
594
595 /** Handle socket activity for an %IAuth connection.
596  * @param[in] ev &Socket event; the IAuth connection is the user data pointer for the socket.
597  */
598 static void iauth_sock_callback(struct Event *ev)
599 {
600   struct IAuth *iauth;
601
602   assert(0 != ev_socket(ev));
603   iauth = (struct IAuth*) s_data(ev_socket(ev));
604   assert(0 != iauth);
605
606   switch (ev_type(ev)) {
607   case ET_CONNECT:
608     socket_state(ev_socket(ev), SS_CONNECTED);
609     iauth_on_connect(iauth);
610     break;
611   case ET_DESTROY:
612     if (!i_GetClosing(iauth))
613       iauth_schedule_reconnect(iauth);
614     break;
615   case ET_READ:
616     iauth_read(iauth);
617     break;
618   case ET_WRITE:
619     i_ClrBlocked(iauth);
620     iauth_write(iauth);
621     break;
622   case ET_EOF:
623     iauth_disconnect(iauth);
624     break;
625   case ET_ERROR:
626     sendto_opmask_butone(0, SNO_OLDSNO, "IAuth socket error: %s", strerror(ev_data(ev)));
627     log_write(LS_SOCKET, L_ERROR, 0, "IAuth socket error: %s", strerror(ev_data(ev)));
628     iauth_disconnect(iauth);
629     break;
630   default:
631     assert(0 && "Unrecognized event type");
632     break;
633   }
634 }
635
636 /* Functions related to IAuthRequest structs */
637
638 /** Handle timeout while waiting for a response.
639  * @param[in] ev Timer event that expired.
640  */
641 static void iauth_request_ev(struct Event *ev)
642 {
643   /* TODO: this could probably be more intelligent */
644   if (ev_type(ev) == ET_EXPIRE) {
645     sendto_opmask_butone(0, SNO_OLDSNO, "IAuth request timed out; reconnecting");
646     iauth_reconnect(t_data(ev_timer(ev)));
647   }
648 }
649
650 /** Send a authorization request to an %IAuth server.
651  * @param[in] iauth %Connection to send request on.
652  * @param[in] iar Request to send.
653  */
654 static void iauth_send_request(struct IAuth *iauth, struct IAuthRequest *iar)
655 {
656   struct Client *client;
657
658   /* If iauth is not connected, we must defer the request. */
659   if (!i_GetConnected(iauth)) {
660     Debug((DEBUG_SEND, "IAuth deferring request for %s because we are not connected.", cli_name(iar->iar_client)));
661     return;
662   }
663
664   /* If no timed request, set up expiration timer. */
665   if (!t_active(&i_request_timer(iauth))) {
666     struct Timer *timer = timer_init(&i_request_timer(iauth));
667     timer_add(timer, iauth_request_ev, iauth, TT_RELATIVE, i_timeout(iauth));
668     iar->iar_timed = 1;
669   } else
670     iar->iar_timed = 0;
671
672   /* Send the FullAuth request. */
673   client = iar->iar_client;
674   assert(iar->iar_client != NULL);
675   iauth_send(iauth, "FullAuth %x %s %s %s %s %s :%s",
676              client, cli_name(client), cli_username(client),
677              cli_user(client)->host, cli_sock_ip(client),
678              cli_passwd(client), cli_info(client));
679
680   /* Write to the socket if we can. */
681   iauth_write(iauth);
682 }
683
684 /** Start independent authorization check for a client.
685  * @param[in] iauth %Connection to send request on.
686  * @param[in] cptr Client to check.
687  * @return Zero, or CPTR_KILLED in case of memory allocation failure.
688  */
689 int iauth_start_client(struct IAuth *iauth, struct Client *cptr)
690 {
691   struct IAuthRequest *iar;
692
693   /* Allocate and initialize IAuthRequest struct. */
694   if (!(iar = MyCalloc(1, sizeof(*iar))))
695     return exit_client(cptr, cptr, &me, "IAuth memory allocation failed");
696   iar->iar_next = &i_list_head(iauth);
697   iar->iar_prev = i_list_head(iauth).iar_prev;
698   iar->iar_client = cptr;
699   iar->iar_prev->iar_next = iar;
700   iar->iar_next->iar_prev = iar;
701
702   /* Send request. */
703   iauth_send_request(iauth, iar);
704
705   return 0;
706 }
707
708 /** Handle a client that is disconnecting.
709  * If there is a pending %IAuth request for the client, close it.
710  * @param[in] cptr Client that is disconnecting.
711  */
712 void iauth_exit_client(struct Client *cptr)
713 {
714   if (cli_iauth(cptr)) {
715     iauth_dispose_request(iauth_active, cli_iauth(cptr));
716     cli_iauth(cptr) = NULL;
717   } else if (IsIAuthed(cptr) && i_GetIClass(iauth_active)) {
718     /* TODO: report quit to iauth */
719   }
720 }
721
722 /** Find pending request with a particular ID.
723  * @param[in] iauth %Connection context for the ID.
724  * @param[in] id Identifier to look up.
725  * @return IAuthRequest with that ID, or NULL.
726  */
727 static struct IAuthRequest *iauth_find_request(struct IAuth *iauth, char *id)
728 {
729   struct IAuthRequest *curr;
730   struct Client *target;
731   target = (struct Client*)strtoul(id, NULL, 16);
732   for (curr = i_list_head(iauth).iar_next;
733        curr != &i_list_head(iauth);
734        curr = curr->iar_next) {
735     assert(curr->iar_client != NULL);
736     if (target == curr->iar_client)
737       return curr;
738   }
739   return NULL;
740 }
741
742 /** Unlink and free a request.
743  * @param[in] iauth Connection that owns the request.
744  * @param[in] iar Request to free.
745  */
746 static void iauth_dispose_request(struct IAuth *iauth, struct IAuthRequest *iar)
747 {
748   assert(iar->iar_client != NULL);
749   if (iar->iar_timed)
750     timer_del(&i_request_timer(iauth));
751   cli_iauth(iar->iar_client) = NULL;
752   iar->iar_prev->iar_next = iar->iar_next;
753   iar->iar_next->iar_prev = iar->iar_prev;
754   MyFree(iar);
755 }
756
757 /** Handle a DoneAuth response from %IAuth.
758  * This means the client is authorized, so let them in.
759  * @param[in] iauth Connection that sent the message.
760  * @param[in] argc Argument count.
761  * @param[in] argv Argument list.
762  */
763 static void iauth_cmd_doneauth(struct IAuth *iauth, int argc, char *argv[])
764 {
765   struct IAuthRequest *iar;
766   struct Client *client;
767   char *id;
768   char *username;
769   char *hostname;
770   char *c_class;
771   char *account;
772
773   if (argc < 5) {
774     iauth_protocol_violation(iauth, "Only %d parameters for DoneAuth (expected >=5)", argc);
775     return;
776   }
777   id = argv[1];
778   username = argv[2];
779   hostname = argv[3];
780   c_class = argv[4];
781   account = (argc > 5) ? argv[5] : 0;
782   iar = iauth_find_request(iauth, id);
783   if (!iar) {
784     iauth_protocol_violation(iauth, "Got unexpected DoneAuth for id %s", id);
785     return;
786   }
787   client = iar->iar_client;
788   ircd_strncpy(cli_username(client), username, USERLEN);
789   ircd_strncpy(cli_user(client)->host, hostname, HOSTLEN);
790   if (account) {
791     ircd_strncpy(cli_user(client)->account, account, ACCOUNTLEN);
792     SetAccount(client);
793   }
794   SetIAuthed(client);
795   iauth_dispose_request(iauth, iar);
796   register_user(client, client, cli_name(client), username);
797 }
798
799 /** Handle a BadAuth response from %IAuth.
800  * This means the client is not authorized, so dump them.
801  * @param[in] iauth Connection that sent the message.
802  * @param[in] argc Argument count.
803  * @param[in] argv Argument list.
804  */
805 static void iauth_cmd_badauth(struct IAuth *iauth, int argc, char *argv[])
806 {
807   struct IAuthRequest *iar;
808   struct Client *client;
809   char *id;
810   char *reason;
811
812   if (argc < 3) {
813     iauth_protocol_violation(iauth, "Only %d parameters for BadAuth (expected >=3)", argc);
814     return;
815   }
816   id = argv[1];
817   reason = argv[2];
818   if (EmptyString(reason)) {
819     iauth_protocol_violation(iauth, "Empty BadAuth reason for id %s", id);
820     return;
821   }
822   iar = iauth_find_request(iauth, id);
823   if (!iar) {
824     iauth_protocol_violation(iauth, "Got unexpected BadAuth for id %s", id);
825     return;
826   }
827   client = iar->iar_client;
828   iauth_dispose_request(iauth, iar);
829   exit_client(client, client, &me, reason);
830 }