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