implement IAUTH client code to query a separate server before allowing
[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  * $Id$
21  */
22
23 #include "config.h"
24 #include "client.h"
25 #include "ircd_alloc.h"
26 #include "ircd_auth.h"
27 #include "ircd_events.h"
28 #include "ircd_features.h"
29 #include "ircd_log.h"
30 #include "ircd_osdep.h"
31 #include "ircd_snprintf.h"
32 #include "ircd_string.h"
33 #include "ircd.h"
34 #include "msg.h"
35 #include "msgq.h"
36 #include "res.h"
37 #include "s_bsd.h"
38 #include "s_misc.h"
39 #include "s_user.h"
40 #include "send.h"
41
42 #include <assert.h>
43 #include <errno.h>
44 #include <netdb.h>
45 #include <string.h>
46 #include <unistd.h>
47 #include <sys/socket.h>
48 #include <netinet/in.h>
49 #include <arpa/inet.h>
50
51 struct IAuthRequest {
52   struct IAuthRequest *iar_prev;        /* previous request struct */
53   struct IAuthRequest *iar_next;        /* next request struct */
54   struct Client *iar_client;            /* client being authenticated */
55   char iar_timed;                       /* if non-zero, using parent i_request_timer */
56 };
57
58 enum IAuthFlag
59 {
60   IAUTH_BLOCKED,                        /* socket buffer full */
61   IAUTH_CONNECTED,                      /* server greeting/handshake done */
62   IAUTH_ABORT,                          /* abort connection asap */
63   IAUTH_ICLASS,                         /* tell iauth about all local users */
64   IAUTH_CLOSING,                        /* candidate to be disposed */
65   IAUTH_LAST_FLAG
66 };
67 DECLARE_FLAGSET(IAuthFlags, IAUTH_LAST_FLAG);
68
69 struct IAuth {
70   struct IAuthRequest i_list_head;      /* doubly linked list of requests */
71   struct MsgQ i_sendQ;                  /* messages queued to send */
72   struct Socket i_socket;               /* connection to server */
73   struct Timer i_reconn_timer;          /* when to reconnect the connection */
74   struct Timer i_request_timer;         /* when the current request times out */
75   struct IAuthFlags i_flags;            /* connection state/status/flags */
76   struct DNSQuery i_query;              /* DNS lookup for iauth server */
77   unsigned int i_recvM;                 /* messages received */
78   unsigned int i_sendM;                 /* messages sent */
79   unsigned int i_recvK;                 /* kilobytes received */
80   unsigned int i_sendK;                 /* kilobytes sent */
81   unsigned short i_recvB;               /* bytes received modulo 1024 */
82   unsigned short i_sendB;               /* bytes sent modulo 1024 */
83   time_t i_reconnect;                   /* seconds to wait before reconnecting */
84   time_t i_timeout;                     /* seconds to wait for a request */
85   unsigned int i_count;                 /* characters used in i_buffer */
86   char i_buffer[BUFSIZE+1];             /* partial unprocessed line from server */
87   char i_passwd[PASSWDLEN+1];           /* password for connection */
88   char i_host[HOSTLEN+1];               /* iauth server hostname */
89   in_addr_t i_addr;                     /* iauth server ip address */
90   unsigned short i_port;                /* iauth server port */
91   struct IAuth *i_next;                 /* next connection in list */
92 };
93
94 #define i_flags(iauth) ((iauth)->i_flags)
95 #define IAuthGet(iauth, flag) FlagHas(&i_flags(iauth), flag)
96 #define IAuthSet(iauth, flag) FlagSet(&i_flags(iauth), flag)
97 #define IAuthClr(iauth, flag) FlagClr(&i_flags(iauth), flag)
98 #define i_GetBlocked(iauth) IAuthGet(iauth, IAUTH_BLOCKED)
99 #define i_SetBlocked(iauth) IAuthSet(iauth, IAUTH_BLOCKED)
100 #define i_ClrBlocked(iauth) IAuthClr(iauth, IAUTH_BLOCKED)
101 #define i_GetConnected(iauth) IAuthGet(iauth, IAUTH_CONNECTED)
102 #define i_SetConnected(iauth) IAuthSet(iauth, IAUTH_CONNECTED)
103 #define i_ClrConnected(iauth) IAuthClr(iauth, IAUTH_CONNECTED)
104 #define i_GetAbort(iauth) IAuthGet(iauth, IAUTH_ABORT)
105 #define i_SetAbort(iauth) IAuthSet(iauth, IAUTH_ABORT)
106 #define i_ClrAbort(iauth) IAuthClr(iauth, IAUTH_ABORT)
107 #define i_GetIClass(iauth) IAuthGet(iauth, IAUTH_ICLASS)
108 #define i_SetIClass(iauth) IAuthSet(iauth, IAUTH_ICLASS)
109 #define i_ClrIClass(iauth) IAuthClr(iauth, IAUTH_ICLASS)
110 #define i_GetClosing(iauth) IAuthGet(iauth, IAUTH_CLOSING)
111 #define i_SetClosing(iauth) IAuthSet(iauth, IAUTH_CLOSING)
112 #define i_ClrClosing(iauth) IAuthClr(iauth, IAUTH_CLOSING)
113
114 #define i_list_head(iauth) ((iauth)->i_list_head)
115 #define i_socket(iauth) ((iauth)->i_socket)
116 #define i_reconn_timer(iauth) ((iauth)->i_reconn_timer)
117 #define i_request_timer(iauth) ((iauth)->i_request_timer)
118 #define i_query(iauth) ((iauth)->i_query)
119 #define i_recvB(iauth) ((iauth)->i_recvB)
120 #define i_recvK(iauth) ((iauth)->i_recvK)
121 #define i_recvM(iauth) ((iauth)->i_recvM)
122 #define i_sendB(iauth) ((iauth)->i_sendB)
123 #define i_sendK(iauth) ((iauth)->i_sendK)
124 #define i_sendM(iauth) ((iauth)->i_sendM)
125 #define i_sendQ(iauth) ((iauth)->i_sendQ)
126 #define i_reconnect(iauth) ((iauth)->i_reconnect)
127 #define i_timeout(iauth) ((iauth)->i_timeout)
128 #define i_count(iauth) ((iauth)->i_count)
129 #define i_buffer(iauth) ((iauth)->i_buffer)
130 #define i_passwd(iauth) ((iauth)->i_passwd)
131 #define i_host(iauth) ((iauth)->i_host)
132 #define i_addr(iauth) ((iauth)->i_addr)
133 #define i_port(iauth) ((iauth)->i_port)
134 #define i_next(iauth) ((iauth)->i_next)
135
136 struct IAuthCmd {
137   const char *iac_name;
138   void (*iac_func)(struct IAuth *iauth, int, char *[]);
139 };
140
141 struct IAuth *iauth_active;
142
143 static const struct IAuthCmd iauth_cmdtab[];
144
145 static void iauth_write(struct IAuth *iauth);
146 static void iauth_reconnect(struct IAuth *iauth);
147 static void iauth_disconnect(struct IAuth *iauth);
148 static void iauth_sock_callback(struct Event *ev);
149 static void iauth_send_request(struct IAuth *iauth, struct IAuthRequest *iar);
150 static void iauth_dispose_request(struct IAuth *iauth, struct IAuthRequest *iar);
151
152 struct IAuth *iauth_connect(char *host, unsigned short port, char *passwd, time_t reconnect, time_t timeout)
153 {
154   struct IAuth *iauth;
155
156   for (iauth = iauth_active; iauth; iauth = i_next(iauth)) {
157     if (!ircd_strncmp(i_host(iauth), host, HOSTLEN)
158         && (i_port(iauth) == port)) {
159       i_ClrClosing(iauth);
160       i_reconnect(iauth) = reconnect;
161       if (t_active(&i_reconn_timer(iauth)) && (t_expire(&i_reconn_timer(iauth)) > CurrentTime + i_reconnect(iauth)))
162         timer_chg(&i_reconn_timer(iauth), TT_RELATIVE, i_reconnect(iauth));
163       break;
164     }
165   }
166   if (NULL == iauth) {
167     if (iauth_active && !i_GetClosing(iauth_active)) {
168       log_write(LS_CONFIG, L_WARNING, 0, "Creating extra active IAuth connection to %s:%d.", host, port);
169     }
170     iauth = MyCalloc(1, sizeof(*iauth));
171     i_list_head(iauth).iar_prev = &i_list_head(iauth);
172     i_list_head(iauth).iar_next = &i_list_head(iauth);
173     msgq_init(&i_sendQ(iauth));
174     ircd_strncpy(i_host(iauth), host, HOSTLEN);
175     i_port(iauth) = port;
176     i_addr(iauth) = INADDR_NONE;
177     i_next(iauth) = iauth_active;
178     iauth_active = iauth;
179     i_reconnect(iauth) = reconnect;
180     iauth_reconnect(iauth);
181   }
182   if (passwd)
183     ircd_strncpy(i_passwd(iauth), passwd, PASSWDLEN);
184   else
185     i_passwd(iauth)[0] = '\0';
186   i_timeout(iauth) = timeout;
187   return iauth;
188 }
189
190 void iauth_mark_closing(void)
191 {
192   struct IAuth *iauth;
193   for (iauth = iauth_active; iauth; iauth = i_next(iauth))
194     i_SetClosing(iauth);
195 }
196
197 void iauth_close(struct IAuth *iauth)
198 {
199   /* Figure out what to do with the closing connection's requests. */
200   if (i_list_head(iauth).iar_next != &i_list_head(iauth)) {
201     struct IAuthRequest *iar;
202     if (iauth_active || i_next(iauth)) {
203       /* If iauth_active != NULL, send requests to it; otherwise if
204        * i_next(iauth) != NULL, we can hope it or some later
205        * connection will be active.
206        */
207       struct IAuth *target = iauth_active ? iauth_active : i_next(iauth);
208
209       /* Append iauth->i_list_head to end of target->i_list_head. */
210       iar = i_list_head(iauth).iar_next;
211       iar->iar_prev = i_list_head(target).iar_prev;
212       i_list_head(target).iar_prev->iar_next = iar;
213       iar = i_list_head(iauth).iar_prev;
214       iar->iar_next = &i_list_head(target);
215       i_list_head(target).iar_prev = iar;
216
217       /* If the target is not closing, send the requests. */
218       for (iar = i_list_head(iauth).iar_next;
219            iar != &i_list_head(target);
220            iar = iar->iar_next) {
221         if (!i_GetClosing(target))
222           iauth_send_request(target, iar);
223       }
224     } else {
225       /* No active connections - approve the requests and drop them. */
226       while ((iar = i_list_head(iauth).iar_next) != &i_list_head(iauth)) {
227         struct Client *client = iar->iar_client;
228         iauth_dispose_request(iauth, iar);
229         register_user(client, client, cli_name(client), cli_username(client));
230       }
231     }
232   }
233   /* Make sure the connection closes with an empty request list. */
234   i_list_head(iauth).iar_prev = &i_list_head(iauth);
235   i_list_head(iauth).iar_next = &i_list_head(iauth);
236   /* Cancel the timer, if it is active. */
237   if (t_active(&i_reconn_timer(iauth)))
238     timer_del(&i_reconn_timer(iauth));
239   if (t_active(&i_request_timer(iauth)))
240     timer_del(&i_request_timer(iauth));
241   /* Disconnect from the server. */
242   if (s_fd(&i_socket(iauth)) != -1)
243     iauth_disconnect(iauth);
244   /* Free memory. */
245   MyFree(iauth);
246 }
247
248 void iauth_close_unused(void)
249 {
250   struct IAuth *prev, *iauth, *next;
251   
252   for (prev = NULL, iauth = iauth_active; iauth; iauth = next) {
253     next = i_next(iauth);
254     if (i_GetClosing(iauth)) {
255       /* Update iauth_active linked list. */
256       if (prev)
257         i_next(prev) = next;
258       else
259         iauth_active = next;
260       /* Close and destroy the connection. */
261       iauth_close(iauth);
262     } else {
263       prev = iauth;
264     }
265   }
266 }
267
268 static void iauth_send(struct IAuth *iauth, const char *format, ...)
269 {
270   va_list vl;
271   struct MsgBuf *mb;
272
273   va_start(vl, format);
274   mb = msgq_vmake(0, format, vl);
275   va_end(vl);
276   msgq_add(&i_sendQ(iauth), mb, 0);
277   msgq_clean(mb);
278 }
279
280 static void iauth_protocol_violation(struct IAuth *iauth, const char *format, ...)
281 {
282   struct VarData vd;
283   assert(iauth != 0);
284   assert(format != 0);
285   vd.vd_format = format;
286   va_start(vd.vd_args, format);
287   sendwallto_group_butone(&me, WALL_DESYNCH, NULL, "IAuth protocol violation: %v", &vd);
288   va_end(vd.vd_args);
289 }
290
291 static void iauth_on_connect(struct IAuth *iauth)
292 {
293   struct IAuthRequest *iar;
294   if (EmptyString(i_passwd(iauth)))
295     iauth_send(iauth, "Server %s", cli_name(&me));
296   else
297     iauth_send(iauth, "Server %s %s", cli_name(&me), i_passwd(iauth));
298   if (i_GetIClass(iauth)) {
299     /* TODO: report local users to iauth */
300     iauth_send(iauth, "EndUsers");
301   }
302   i_SetConnected(iauth);
303   for (iar = i_list_head(iauth).iar_next;
304        iar != &i_list_head(iauth);
305        iar = iar->iar_next)
306     iauth_send_request(iauth, iar);
307   iauth_write(iauth);
308 }
309
310 static void iauth_disconnect(struct IAuth *iauth)
311 {
312   close(s_fd(&i_socket(iauth)));
313   socket_del(&i_socket(iauth));
314   s_fd(&i_socket(iauth)) = -1;
315 }
316
317 static void iauth_dns_callback(void *vptr, struct hostent *he)
318 {
319   struct IAuth *iauth = vptr;
320   if (!he) {
321     sendto_opmask_butone(0, SNO_OLDSNO, "IAuth connection to %s failed: host lookup failed", i_host(iauth));
322   } else if (he->h_addrtype != AF_INET) {
323     sendto_opmask_butone(0, SNO_OLDSNO, "IAuth connection to %s failed: bad host type %d", i_host(iauth), he->h_addrtype);
324   } else {
325     assert(he->h_addrtype == sizeof(i_addr(iauth)));
326     memcpy(&i_addr(iauth), he->h_addr_list[0], sizeof(i_addr(iauth)));
327     if (INADDR_NONE == i_addr(iauth)) {
328       sendto_opmask_butone(0, SNO_OLDSNO, "IAuth connection to %s failed: host came back as INADDR_NONE", i_host(iauth));
329       return;
330     }
331     iauth_reconnect(iauth);
332   }
333 }
334
335 static void iauth_reconnect_ev(struct Event *ev)
336 {
337   if (ev_type(ev) == ET_EXPIRE)
338     iauth_reconnect(t_data(ev_timer(ev)));
339 }
340
341 static void iauth_schedule_reconnect(struct IAuth *iauth)
342 {
343   struct Timer *timer;
344   assert(!t_active(&i_reconn_timer(iauth)));
345   timer = timer_init(&i_reconn_timer(iauth));
346   timer_add(timer, iauth_reconnect_ev, iauth, TT_RELATIVE, i_reconnect(iauth));
347 }
348
349 static void iauth_reconnect(struct IAuth *iauth)
350 {
351   extern struct sockaddr_in VirtualHost;
352   struct sockaddr_in sin;
353   IOResult result;
354   int fd;
355
356   if (INADDR_NONE == i_addr(iauth)) {
357     i_addr(iauth) = inet_addr(i_host(iauth));
358     if (INADDR_NONE == i_addr(iauth)) {
359       i_query(iauth).vptr = iauth;
360       i_query(iauth).callback = iauth_dns_callback;
361       gethost_byname(i_host(iauth), &i_query(iauth));
362       return;
363     }
364   }
365   fd = socket(AF_INET, SOCK_STREAM, 0);
366   if (0 > fd) {
367     sendto_opmask_butone(0, SNO_OLDSNO, "IAuth reconnect unable to allocate socket: %s", strerror(errno));
368     return;
369   }
370   if (feature_bool(FEAT_VIRTUAL_HOST)
371       && bind(fd, (struct sockaddr*)&VirtualHost, sizeof(VirtualHost)) != 0) {
372     close(fd);
373     sendto_opmask_butone(0, SNO_OLDSNO, "IAuth reconnect unable to bind vhost: %s", strerror(errno));
374     return;
375   }
376   if (!os_set_sockbufs(fd, SERVER_TCP_WINDOW, SERVER_TCP_WINDOW)) {
377     close(fd);
378     sendto_opmask_butone(0, SNO_OLDSNO, "IAuth reconnect unable to set socket buffers: %s", strerror(errno));
379     return;
380   }
381   if (!os_set_nonblocking(fd)) {
382     close(fd);
383     sendto_opmask_butone(0, SNO_OLDSNO, "IAuth reconnect unable to make socket non-blocking: %s", strerror(errno));
384     return;
385   }
386   memset(&sin, 0, sizeof(sin));
387   sin.sin_family = AF_INET;
388   sin.sin_addr.s_addr = i_addr(iauth);
389   sin.sin_port = htons(i_port(iauth));
390   result = os_connect_nonb(fd, &sin);
391   if (result == IO_FAILURE) {
392     close(fd);
393     sendto_opmask_butone(0, SNO_OLDSNO, "IAuth reconnect unable to initiate connection: %s", strerror(errno));
394     return;
395   }
396   if (!socket_add(&i_socket(iauth), iauth_sock_callback, iauth,
397                   (result == IO_SUCCESS) ? SS_CONNECTED : SS_CONNECTING,
398                   SOCK_EVENT_READABLE | SOCK_EVENT_WRITABLE, fd)) {
399     close(fd);
400     sendto_opmask_butone(0, SNO_OLDSNO, "IAuth reconnect unable to add socket: %s", strerror(errno));
401     return;
402   }
403 }
404
405 static void iauth_read(struct IAuth *iauth)
406 {
407   char *src, *endp, *old_buffer, *argv[MAXPARA + 1];
408   unsigned int length, argc, ii;
409   char readbuf[SERVER_TCP_WINDOW];
410
411   length = 0;
412   if (IO_FAILURE == os_recv_nonb(s_fd(&i_socket(iauth)), readbuf, sizeof(readbuf), &length))
413     return;
414   i_recvB(iauth) += length;
415   if (i_recvB(iauth) > 1023) {
416     i_recvK(iauth) += i_recvB(iauth) >> 10;
417     i_recvB(iauth) &= 1023;
418   }
419   old_buffer = i_buffer(iauth);
420   endp = old_buffer + i_count(iauth);
421   for (src = readbuf; length > 0; --length) {
422     *endp = *src++;
423     if (IsEol(*endp)) {
424       /* Skip blank lines. */
425       if (endp == old_buffer)
426         continue;
427       /* NUL-terminate line and split parameters. */
428       *endp = '\0';
429       for (argc = 0, endp = old_buffer; *endp && (argc < MAXPARA); ) {
430         while (*endp == ' ')
431           *endp++ = '\0';
432         if (*endp == '\0')
433           break;
434         if (*endp == ':')
435         {
436           argv[argc++] = endp + 1;
437           break;
438         }
439         argv[argc++] = endp;
440         for (; *endp && *endp != ' '; ++endp) ;
441       }
442       argv[argc] = NULL;
443       /* Count line and reset endp to start of buffer. */
444       i_recvM(iauth)++;
445       endp = old_buffer;
446       /* Look up command and try to dispatch. */
447       if (argc > 0) {
448         for (ii = 0; iauth_cmdtab[ii].iac_name; ++ii) {
449           if (!ircd_strcmp(iauth_cmdtab[ii].iac_name, argv[0])) {
450             iauth_cmdtab[ii].iac_func(iauth, argc, argv);
451             if (i_GetAbort(iauth))
452               iauth_disconnect(iauth);
453             break;
454           }
455         }
456       }
457     }
458     else if (endp < old_buffer + BUFSIZE)
459       endp++;
460   }
461   i_count(iauth) = endp - old_buffer;
462 }
463
464 static void iauth_write(struct IAuth *iauth)
465 {
466   unsigned int bytes_tried, bytes_sent;
467   IOResult iores;
468
469   if (i_GetBlocked(iauth))
470     return;
471   while (MsgQLength(&i_sendQ(iauth)) > 0) {
472     iores = os_sendv_nonb(s_fd(&i_socket(iauth)), &i_sendQ(iauth), &bytes_tried, &bytes_sent);
473     switch (iores) {
474     case IO_SUCCESS:
475       msgq_delete(&i_sendQ(iauth), bytes_sent);
476       i_sendB(iauth) += bytes_sent;
477       if (i_sendB(iauth) > 1023) {
478         i_sendK(iauth) += i_sendB(iauth) >> 10;
479         i_sendB(iauth) &= 1023;
480       }
481       if (bytes_tried == bytes_sent)
482         break;
483       /* If bytes_sent < bytes_tried, fall through to IO_BLOCKED. */
484     case IO_BLOCKED:
485       i_SetBlocked(iauth);
486       socket_events(&i_socket(iauth), SOCK_ACTION_ADD | SOCK_EVENT_WRITABLE);
487       return;
488     case IO_FAILURE:
489       iauth_disconnect(iauth);
490       return;
491     }
492   }
493   /* We were able to flush all events, so remove notification. */
494   socket_events(&i_socket(iauth), SOCK_ACTION_DEL | SOCK_EVENT_WRITABLE);
495 }
496
497 static void iauth_sock_callback(struct Event *ev)
498 {
499   struct IAuth *iauth;
500
501   assert(0 != ev_socket(ev));
502   iauth = (struct IAuth*) s_data(ev_socket(ev));
503   assert(0 != iauth);
504
505   switch (ev_type(ev)) {
506   case ET_CONNECT:
507     socket_state(ev_socket(ev), SS_CONNECTED);
508     iauth_on_connect(iauth);
509     break;
510   case ET_DESTROY:
511     if (!i_GetClosing(iauth))
512       iauth_schedule_reconnect(iauth);
513     break;
514   case ET_READ:
515     iauth_read(iauth);
516     break;
517   case ET_WRITE:
518     i_ClrBlocked(iauth);
519     iauth_write(iauth);
520     break;
521   case ET_EOF:
522     iauth_disconnect(iauth);
523     break;
524   case ET_ERROR:
525     sendto_opmask_butone(0, SNO_OLDSNO, "IAuth socket error: %s", strerror(ev_data(ev)));
526     log_write(LS_SOCKET, L_ERROR, 0, "IAuth socket error: %s", strerror(ev_data(ev)));
527     iauth_disconnect(iauth);
528     break;
529   default:
530     assert(0 && "Unrecognized event type");
531     break;
532   }
533 }
534
535 /* Functions related to IAuthRequest structs */
536
537 static void iauth_request_ev(struct Event *ev)
538 {
539   /* TODO: this could probably be more intelligent */
540   if (ev_type(ev) == ET_EXPIRE) {
541     sendto_opmask_butone(0, SNO_OLDSNO, "IAuth request timed out; reconnecting");
542     iauth_reconnect(t_data(ev_timer(ev)));
543   }
544 }
545
546 static void iauth_send_request(struct IAuth *iauth, struct IAuthRequest *iar)
547 {
548   struct Client *client;
549
550   /* If iauth is not connected, we must defer the request. */
551   if (!i_GetConnected(iauth))
552     return;
553
554   /* If no timed request, set up expiration timer. */
555   if (!t_active(&i_request_timer(iauth))) {
556     struct Timer *timer = timer_init(&i_request_timer(iauth));
557     timer_add(timer, iauth_request_ev, iauth, TT_RELATIVE, i_timeout(iauth));
558     iar->iar_timed = 1;
559   } else
560     iar->iar_timed = 0;
561
562   /* Send the FullAuth request. */
563   client = iar->iar_client;
564   assert(iar->iar_client != NULL);
565   iauth_send(iauth, "FullAuth %x %s %s %s %s %s :%s",
566              client, cli_name(client), cli_username(client),
567              cli_user(client)->host, cli_sock_ip(client),
568              cli_passwd(client), cli_info(client));
569
570   /* Write to the socket if we can. */
571   iauth_write(iauth);
572 }
573
574 int iauth_start_client(struct IAuth *iauth, struct Client *cptr)
575 {
576   struct IAuthRequest *iar;
577
578   /* Allocate and initialize IAuthRequest struct. */
579   if (!(iar = MyCalloc(1, sizeof(*iar))))
580     return exit_client(cptr, cptr, &me, "IAuth memory allocation failed");
581   iar->iar_next = &i_list_head(iauth);
582   iar->iar_prev = i_list_head(iauth).iar_prev;
583   iar->iar_client = cptr;
584   iar->iar_prev->iar_next = iar;
585   iar->iar_next->iar_prev = iar;
586
587   /* Send request. */
588   iauth_send_request(iauth, iar);
589
590   return 0;
591 }
592
593 void iauth_exit_client(struct Client *cptr)
594 {
595   if (cli_iauth(cptr)) {
596     iauth_dispose_request(iauth_active, cli_iauth(cptr));
597     cli_iauth(cptr) = NULL;
598   } else if (IsIAuthed(cptr) && i_GetIClass(iauth_active)) {
599     /* TODO: report quit to iauth */
600   }
601 }
602
603 static struct IAuthRequest *iauth_find_request(struct IAuth *iauth, char *id)
604 {
605   struct IAuthRequest *curr;
606   struct Client *target;
607   target = (struct Client*)strtoul(id, NULL, 16);
608   for (curr = i_list_head(iauth).iar_next;
609        curr != &i_list_head(iauth);
610        curr = curr->iar_next) {
611     assert(curr->iar_client != NULL);
612     if (target == curr->iar_client)
613       return curr;
614   }
615   return NULL;
616 }
617
618 static void iauth_dispose_request(struct IAuth *iauth, struct IAuthRequest *iar)
619 {
620   assert(iar->iar_client != NULL);
621   if (iar->iar_timed)
622     timer_del(&i_request_timer(iauth));
623   cli_iauth(iar->iar_client) = NULL;
624   iar->iar_prev->iar_next = iar->iar_next;
625   iar->iar_next->iar_prev = iar->iar_prev;
626   MyFree(iar);
627 }
628
629 static void iauth_cmd_doneauth(struct IAuth *iauth, int argc, char *argv[])
630 {
631   struct IAuthRequest *iar;
632   struct Client *client;
633   char *id;
634   char *username;
635   char *hostname;
636   char *c_class;
637   char *account;
638
639   if (argc < 5) {
640     iauth_protocol_violation(iauth, "Only %d parameters for DoneAuth (expected >=5)", argc);
641     return;
642   }
643   id = argv[1];
644   username = argv[2];
645   hostname = argv[3];
646   c_class = argv[4];
647   account = (argc > 5) ? argv[5] : 0;
648   iar = iauth_find_request(iauth, id);
649   if (!iar) {
650     iauth_protocol_violation(iauth, "Got unexpected DoneAuth for id %s", id);
651     return;
652   }
653   client = iar->iar_client;
654   ircd_strncpy(cli_username(client), username, USERLEN);
655   ircd_strncpy(cli_user(client)->host, hostname, HOSTLEN);
656   if (account) {
657     ircd_strncpy(cli_user(client)->account, account, ACCOUNTLEN);
658     SetAccount(client);
659   }
660   SetIAuthed(client);
661   iauth_dispose_request(iauth, iar);
662   register_user(client, client, cli_name(client), username);
663 }
664
665 static void iauth_cmd_badauth(struct IAuth *iauth, int argc, char *argv[])
666 {
667   struct IAuthRequest *iar;
668   struct Client *client;
669   char *id;
670   char *reason;
671
672   if (argc < 3) {
673     iauth_protocol_violation(iauth, "Only %d parameters for BadAuth (expected >=3)", argc);
674     return;
675   }
676   id = argv[1];
677   reason = argv[2];
678   if (EmptyString(reason)) {
679     iauth_protocol_violation(iauth, "Empty BadAuth reason for id %s", id);
680     return;
681   }
682   iar = iauth_find_request(iauth, id);
683   if (!iar) {
684     iauth_protocol_violation(iauth, "Got unexpected BadAuth for id %s", id);
685     return;
686   }
687   client = iar->iar_client;
688   iauth_dispose_request(iauth, iar);
689   exit_client(client, client, &me, reason);
690 }
691
692 static const struct IAuthCmd iauth_cmdtab[] = {
693   { "DoneAuth", iauth_cmd_doneauth },
694   { "BadAuth", iauth_cmd_badauth },
695   { NULL, NULL }
696 };