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