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