Author: Kev <klmitch@mit.edu>
[ircu2.10.12-pk.git] / ircd / s_auth.c
1 /************************************************************************
2  *   IRC - Internet Relay Chat, src/s_auth.c
3  *   Copyright (C) 1992 Darren Reed
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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
18  *
19  *   $Id$
20  *
21  * Changes:
22  *   July 6, 1999 - Rewrote most of the code here. When a client connects
23  *     to the server and passes initial socket validation checks, it
24  *     is owned by this module (auth) which returns it to the rest of the
25  *     server when dns and auth queries are finished. Until the client is
26  *     released, the server does not know it exists and does not process
27  *     any messages from it.
28  *     --Bleep  Thomas Helvey <tomh@inxpress.net>
29  */
30 #include "config.h"
31
32 #include "s_auth.h"
33 #include "client.h"
34 #include "IPcheck.h"
35 #include "ircd.h"
36 #include "ircd_alloc.h"
37 #include "ircd_chattr.h"
38 #include "ircd_events.h"
39 #include "ircd_features.h"
40 #include "ircd_log.h"
41 #include "ircd_osdep.h"
42 #include "ircd_string.h"
43 #include "list.h"
44 #include "numeric.h"
45 #include "querycmds.h"
46 #include "res.h"
47 #include "s_bsd.h"
48 #include "s_debug.h"
49 #include "s_misc.h"
50 #include "send.h"
51 #include "sprintf_irc.h"
52 #include "struct.h"
53 #include "sys.h"               /* TRUE bleah */
54
55 #include <arpa/inet.h>         /* inet_netof */
56 #include <netdb.h>             /* struct hostent */
57 #include <string.h>
58 #include <stdlib.h>
59 #include <unistd.h>
60 #include <errno.h>
61 #include <fcntl.h>
62 #include <assert.h>
63 #include <sys/socket.h>
64 #include <sys/file.h>
65 #include <sys/ioctl.h>
66
67 /*
68  * a bit different approach
69  * this replaces the original sendheader macros
70  */
71 static struct {
72   const char*  message;
73   unsigned int length;
74 } HeaderMessages [] = {
75   /* 123456789012345678901234567890123456789012345678901234567890 */
76   { "NOTICE AUTH :*** Looking up your hostname\r\n",       43 },
77   { "NOTICE AUTH :*** Found your hostname\r\n",            38 },
78   { "NOTICE AUTH :*** Found your hostname, cached\r\n",    46 },
79   { "NOTICE AUTH :*** Couldn't look up your hostname\r\n", 49 },
80   { "NOTICE AUTH :*** Checking Ident\r\n",                 33 },
81   { "NOTICE AUTH :*** Got ident response\r\n",             37 },
82   { "NOTICE AUTH :*** No ident response\r\n",              36 },
83   { "NOTICE AUTH :*** Your forward and reverse DNS do not match, " \
84     "ignoring hostname.\r\n",                              80 }
85 };
86
87 typedef enum {
88   REPORT_DO_DNS,
89   REPORT_FIN_DNS,
90   REPORT_FIN_DNSC,
91   REPORT_FAIL_DNS,
92   REPORT_DO_ID,
93   REPORT_FIN_ID,
94   REPORT_FAIL_ID,
95   REPORT_IP_MISMATCH
96 } ReportType;
97
98 #define sendheader(c, r) \
99    send(cli_fd(c), HeaderMessages[(r)].message, HeaderMessages[(r)].length, 0)
100
101 struct AuthRequest* AuthPollList = 0; /* GLOBAL - auth queries pending io */
102 static struct AuthRequest* AuthIncompleteList = 0;
103
104 enum { AUTH_TIMEOUT = 60 };
105
106 static void release_auth_client(struct Client* client);
107 static void unlink_auth_request(struct AuthRequest* request,
108                                 struct AuthRequest** list);
109 void free_auth_request(struct AuthRequest* auth);
110
111 /*
112  * auth_timeout - timeout a given auth request
113  */
114 static void auth_timeout_callback(struct Event* ev)
115 {
116   struct AuthRequest* auth;
117
118   assert(0 != ev_timer(ev));
119   assert(0 != t_data(ev_timer(ev)));
120
121   auth = t_data(ev_timer(ev));
122
123   if (ev_type(ev) == ET_DESTROY) { /* being destroyed */
124     auth->flags &= ~AM_TIMEOUT;
125
126     if (!(auth->flags & AM_FREE_MASK)) {
127       Debug((DEBUG_LIST, "Freeing auth from timeout callback; %p [%p]", auth,
128              ev_timer(ev)));
129       MyFree(auth); /* done with it, finally */
130     }
131   } else {
132     assert(ev_type(ev) == ET_EXPIRE);
133
134     destroy_auth_request(auth, 1);
135   }
136 }
137
138 /*
139  * auth_sock_callback - called when an event occurs on the socket
140  */
141 static void auth_sock_callback(struct Event* ev)
142 {
143   struct AuthRequest* auth;
144
145   assert(0 != ev_socket(ev));
146   assert(0 != s_data(ev_socket(ev)));
147
148   auth = s_data(ev_socket(ev));
149
150   switch (ev_type(ev)) {
151   case ET_DESTROY: /* being destroyed */
152     auth->flags &= ~AM_SOCKET;
153
154     if (!(auth->flags & AM_FREE_MASK)) {
155       Debug((DEBUG_LIST, "Freeing auth from sock callback; %p [%p]", auth,
156              ev_socket(ev)));
157       MyFree(auth); /* done with it finally */
158     }
159     break;
160
161   case ET_CONNECT: /* socket connection completed */
162     Debug((DEBUG_LIST, "Connection completed for auth %p [%p]; sending query",
163            auth, ev_socket(ev)));
164     socket_state(&auth->socket, SS_CONNECTED);
165     send_auth_query(auth);
166     break;
167
168   case ET_READ: /* socket is readable */
169   case ET_EOF: /* end of file on socket */
170   case ET_ERROR: /* error on socket */
171     Debug((DEBUG_LIST, "Auth socket %p [%p] readable", auth, ev_socket(ev)));
172     read_auth_reply(auth);
173     break;
174
175   default:
176 #ifndef NDEBUG
177     abort(); /* unrecognized event */
178 #endif
179     break;
180   }
181 }
182
183 /*
184  * destroy_auth_request - stop an auth request completely
185  */
186 void destroy_auth_request(struct AuthRequest* auth, int send_reports)
187 {
188   struct AuthRequest** authList;
189
190   if (IsDoingAuth(auth)) {
191     authList = &AuthPollList;
192     if (-1 < auth->fd) {
193       close(auth->fd);
194       auth->fd = -1;
195       socket_del(&auth->socket);
196     }
197
198     if (send_reports && IsUserPort(auth->client))
199       sendheader(auth->client, REPORT_FAIL_ID);
200   } else
201     authList = &AuthIncompleteList;
202
203   if (IsDNSPending(auth)) {
204     delete_resolver_queries(auth);
205     if (send_reports && IsUserPort(auth->client))
206       sendheader(auth->client, REPORT_FAIL_DNS);
207   }
208
209   if (send_reports)
210     log_write(LS_RESOLVER, L_INFO, 0, "DNS/AUTH timeout %s",
211               get_client_name(auth->client, HIDE_IP));
212
213   release_auth_client(auth->client);
214   unlink_auth_request(auth, authList);
215   free_auth_request(auth);
216 }
217
218 /*
219  * make_auth_request - allocate a new auth request
220  */
221 static struct AuthRequest* make_auth_request(struct Client* client)
222 {
223   struct AuthRequest* auth = 
224                (struct AuthRequest*) MyMalloc(sizeof(struct AuthRequest));
225   assert(0 != auth);
226   memset(auth, 0, sizeof(struct AuthRequest));
227   auth->flags   = AM_TIMEOUT;
228   auth->fd      = -1;
229   auth->client  = client;
230   cli_auth(client) = auth;
231   timer_add(&auth->timeout, auth_timeout_callback, (void*) auth, TT_RELATIVE,
232             AUTH_TIMEOUT);
233   return auth;
234 }
235
236 /*
237  * free_auth_request - cleanup auth request allocations
238  */
239 void free_auth_request(struct AuthRequest* auth)
240 {
241   if (-1 < auth->fd) {
242     close(auth->fd);
243     Debug((DEBUG_LIST, "Deleting auth socket for %p", auth->client));
244     socket_del(&auth->socket);
245   }
246   Debug((DEBUG_LIST, "Deleting auth timeout timer for %p", auth->client));
247   timer_del(&auth->timeout);
248 }
249
250 /*
251  * unlink_auth_request - remove auth request from a list
252  */
253 static void unlink_auth_request(struct AuthRequest* request,
254                                 struct AuthRequest** list)
255 {
256   if (request->next)
257     request->next->prev = request->prev;
258   if (request->prev)
259     request->prev->next = request->next;
260   else
261     *list = request->next;
262 }
263
264 /*
265  * link_auth_request - add auth request to a list
266  */
267 static void link_auth_request(struct AuthRequest* request,
268                               struct AuthRequest** list)
269 {
270   request->prev = 0;
271   request->next = *list;
272   if (*list)
273     (*list)->prev = request;
274   *list = request;
275 }
276
277 /*
278  * release_auth_client - release auth client from auth system
279  * this adds the client into the local client lists so it can be read by
280  * the main io processing loop
281  */
282 static void release_auth_client(struct Client* client)
283 {
284   assert(0 != client);
285   cli_auth(client) = 0;
286   cli_lasttime(client) = cli_since(client) = CurrentTime;
287   if (cli_fd(client) > HighestFd)
288     HighestFd = cli_fd(client);
289   LocalClientArray[cli_fd(client)] = client;
290
291   add_client_to_list(client);
292   socket_events(&(cli_socket(client)), SOCK_ACTION_SET | SOCK_EVENT_READABLE);
293   Debug((DEBUG_INFO, "Auth: release_auth_client %s@%s[%s]",
294          cli_username(client), cli_sockhost(client), cli_sock_ip(client)));
295 }
296  
297 static void auth_kill_client(struct AuthRequest* auth)
298 {
299   assert(0 != auth);
300
301   unlink_auth_request(auth, (IsDoingAuth(auth)) ? &AuthPollList : &AuthIncompleteList);
302
303   if (IsDNSPending(auth))
304     delete_resolver_queries(auth);
305   IPcheck_disconnect(auth->client);
306   Count_unknowndisconnects(UserStats);
307   free_client(auth->client);
308   free_auth_request(auth);
309 }
310
311 /*
312  * auth_dns_callback - called when resolver query finishes
313  * if the query resulted in a successful search, hp will contain
314  * a non-null pointer, otherwise hp will be null.
315  * set the client on it's way to a connection completion, regardless
316  * of success of failure
317  */
318 static void auth_dns_callback(void* vptr, struct DNSReply* reply)
319 {
320   struct AuthRequest* auth = (struct AuthRequest*) vptr;
321
322   assert(0 != auth);
323   /*
324    * need to do this here so auth_kill_client doesn't
325    * try have the resolver delete the query it's about
326    * to delete anyways. --Bleep
327    */
328   ClearDNSPending(auth);
329
330   if (reply) {
331     const struct hostent* hp = reply->hp;
332     int i;
333     assert(0 != hp);
334     /*
335      * Verify that the host to ip mapping is correct both ways and that
336      * the ip#(s) for the socket is listed for the host.
337      */
338     for (i = 0; hp->h_addr_list[i]; ++i) {
339       if (0 == memcmp(hp->h_addr_list[i], &(cli_ip(auth->client)),
340                       sizeof(struct in_addr)))
341          break;
342     }
343     if (!hp->h_addr_list[i]) {
344       if (IsUserPort(auth->client))
345         sendheader(auth->client, REPORT_IP_MISMATCH);
346       sendto_opmask_butone(0, SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%s]",
347                            cli_sock_ip(auth->client), hp->h_name, 
348                            ircd_ntoa(hp->h_addr_list[0]));
349       if (feature_bool(FEAT_KILL_IPMISMATCH)) {
350         auth_kill_client(auth);
351         return;
352       }
353     }
354     else {
355       ++reply->ref_count;
356       cli_dns_reply(auth->client) = reply;
357       ircd_strncpy(cli_sockhost(auth->client), hp->h_name, HOSTLEN);
358       if (IsUserPort(auth->client))
359         sendheader(auth->client, REPORT_FIN_DNS);
360     }
361   }
362   else {
363     /*
364      * this should have already been done by s_bsd.c in add_connection
365      *
366      * strcpy(auth->client->sockhost, auth->client->sock_ip);
367      */
368     if (IsUserPort(auth->client))
369       sendheader(auth->client, REPORT_FAIL_DNS);
370   }
371   if (!IsDoingAuth(auth)) {
372     release_auth_client(auth->client);
373     unlink_auth_request(auth, &AuthIncompleteList);
374     free_auth_request(auth);
375   }
376 }
377
378 /*
379  * authsenderr - handle auth send errors
380  */
381 static void auth_error(struct AuthRequest* auth, int kill)
382 {
383   ++ServerStats->is_abad;
384
385   assert(0 != auth);
386   close(auth->fd);
387   auth->fd = -1;
388   socket_del(&auth->socket);
389
390   if (IsUserPort(auth->client))
391     sendheader(auth->client, REPORT_FAIL_ID);
392
393   if (kill) {
394     /*
395      * we can't read the client info from the client socket,
396      * close the client connection and free the client
397      * Need to do this before we ClearAuth(auth) so we know
398      * which list to remove the query from. --Bleep
399      */
400     auth_kill_client(auth);
401     return;
402   }
403
404   ClearAuth(auth);
405   unlink_auth_request(auth, &AuthPollList);
406
407   if (IsDNSPending(auth))
408     link_auth_request(auth, &AuthIncompleteList);
409   else {
410     release_auth_client(auth->client);
411     free_auth_request(auth);
412   }
413 }
414
415 /*
416  * start_auth_query - Flag the client to show that an attempt to 
417  * contact the ident server on the client's host.  The connect and
418  * subsequently the socket are all put into 'non-blocking' mode.  
419  * Should the connect or any later phase of the identifing process fail,
420  * it is aborted and the user is given a username of "unknown".
421  */
422 static int start_auth_query(struct AuthRequest* auth)
423 {
424   struct sockaddr_in remote_addr;
425   struct sockaddr_in local_addr;
426   int                fd;
427   IOResult           result;
428
429   assert(0 != auth);
430   assert(0 != auth->client);
431
432   if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
433 #if 0
434     report_error(SOCKET_ERROR_MSG, get_client_name(auth->client, HIDE_IP), errno);
435 #endif
436     ++ServerStats->is_abad;
437     return 0;
438   }
439   if ((MAXCONNECTIONS - 10) < fd) {
440 #if 0
441     report_error(CONNLIMIT_ERROR_MSG, 
442                  get_client_name(auth->client, HIDE_IP), errno);
443 #endif
444     close(fd);
445     return 0;
446   }
447   if (!os_set_nonblocking(fd)) {
448 #if 0
449     report_error(NONB_ERROR_MSG, get_client_name(auth->client, HIDE_IP), errno);
450 #endif
451     close(fd);
452     return 0;
453   }
454   if (IsUserPort(auth->client))
455     sendheader(auth->client, REPORT_DO_ID);
456   /* 
457    * get the local address of the client and bind to that to
458    * make the auth request.  This used to be done only for
459    * ifdef VIRTTUAL_HOST, but needs to be done for all clients
460    * since the ident request must originate from that same address--
461    * and machines with multiple IP addresses are common now
462    */
463   memset(&local_addr, 0, sizeof(struct sockaddr_in));
464   os_get_sockname(cli_fd(auth->client), &local_addr);
465   local_addr.sin_port = htons(0);
466
467   if (bind(fd, (struct sockaddr*) &local_addr, sizeof(struct sockaddr_in))) {
468 #if 0
469     report_error(BIND_ERROR_MSG,
470                  get_client_name(auth->client, HIDE_IP), errno);
471 #endif
472     close(fd);
473     return 0;
474   }
475
476   remote_addr.sin_addr.s_addr = (cli_ip(auth->client)).s_addr;
477   remote_addr.sin_port = htons(113);
478   remote_addr.sin_family = AF_INET;
479
480   if ((result = os_connect_nonb(fd, &remote_addr)) == IO_FAILURE ||
481       !socket_add(&auth->socket, auth_sock_callback, (void*) auth,
482                   result == IO_SUCCESS ? SS_CONNECTED : SS_CONNECTING,
483                   SOCK_EVENT_READABLE, fd)) {
484     ServerStats->is_abad++;
485     /*
486      * No error report from this...
487      */
488     close(fd);
489     if (IsUserPort(auth->client))
490       sendheader(auth->client, REPORT_FAIL_ID);
491     return 0;
492   }
493
494   auth->flags |= AM_SOCKET;
495   auth->fd = fd;
496
497   SetAuthConnect(auth);
498   if (result == IO_SUCCESS)
499     send_auth_query(auth); /* this does a SetAuthPending(auth) for us */
500
501   return 1;
502 }
503
504
505 enum IdentReplyFields {
506   IDENT_PORT_NUMBERS,
507   IDENT_REPLY_TYPE,
508   IDENT_OS_TYPE,
509   IDENT_INFO,
510   USERID_TOKEN_COUNT
511 };
512
513 static char* check_ident_reply(char* reply)
514 {
515   char* token;
516   char* end;
517   char* vector[USERID_TOKEN_COUNT];
518   int count = token_vector(reply, ':', vector, USERID_TOKEN_COUNT);
519
520   if (USERID_TOKEN_COUNT != count)
521     return 0;
522   /*
523    * second token is the reply type
524    */
525   token = vector[IDENT_REPLY_TYPE];
526   if (EmptyString(token))
527     return 0;
528
529   while (IsSpace(*token))
530     ++token;
531
532   if (0 != strncmp(token, "USERID", 6))
533     return 0;
534
535   /*
536    * third token is the os type
537    */
538   token = vector[IDENT_OS_TYPE];
539   if (EmptyString(token))
540     return 0;
541   while (IsSpace(*token))
542    ++token;
543
544   /*
545    * Unless "OTHER" is specified as the operating system
546    * type, the server is expected to return the "normal"
547    * user identification of the owner of this connection.
548    * "Normal" in this context may be taken to mean a string
549    * of characters which uniquely identifies the connection
550    * owner such as a user identifier assigned by the system
551    * administrator and used by such user as a mail
552    * identifier, or as the "user" part of a user/password
553    * pair used to gain access to system resources.  When an
554    * operating system is specified (e.g., anything but
555    * "OTHER"), the user identifier is expected to be in a
556    * more or less immediately useful form - e.g., something
557    * that could be used as an argument to "finger" or as a
558    * mail address.
559    */
560   if (0 == strncmp(token, "OTHER", 5))
561     return 0;
562   /*
563    * fourth token is the username
564    */
565   token = vector[IDENT_INFO];
566   if (EmptyString(token))
567     return 0;
568   while (IsSpace(*token))
569     ++token;
570   /*
571    * look for the end of the username, terminators are '\0, @, <SPACE>, :'
572    */
573   for (end = token; *end; ++end) {
574     if (IsSpace(*end) || '@' == *end || ':' == *end)
575       break;
576   }
577   *end = '\0'; 
578   return token;
579 }
580
581 #if 0
582 /*
583  * GetValidIdent - parse ident query reply from identd server
584  * 
585  * Inputs        - pointer to ident buf
586  * Output        - NULL if no valid ident found, otherwise pointer to name
587  * Side effects        -
588  */
589 static char* GetValidIdent(char *buf)
590 {
591   int   remp = 0;
592   int   locp = 0;
593   char* colon1Ptr;
594   char* colon2Ptr;
595   char* colon3Ptr;
596   char* commaPtr;
597   char* remotePortString;
598
599   /* All this to get rid of a sscanf() fun. */
600   remotePortString = buf;
601   
602   colon1Ptr = strchr(remotePortString,':');
603   if(!colon1Ptr)
604     return 0;
605
606   *colon1Ptr = '\0';
607   colon1Ptr++;
608   colon2Ptr = strchr(colon1Ptr,':');
609   if(!colon2Ptr)
610     return 0;
611
612   *colon2Ptr = '\0';
613   colon2Ptr++;
614   commaPtr = strchr(remotePortString, ',');
615
616   if(!commaPtr)
617     return 0;
618
619   *commaPtr = '\0';
620   commaPtr++;
621
622   remp = atoi(remotePortString);
623   if(!remp)
624     return 0;
625               
626   locp = atoi(commaPtr);
627   if(!locp)
628     return 0;
629
630   /* look for USERID bordered by first pair of colons */
631   if(!strstr(colon1Ptr, "USERID"))
632     return 0;
633
634   colon3Ptr = strchr(colon2Ptr,':');
635   if(!colon3Ptr)
636     return 0;
637   
638   *colon3Ptr = '\0';
639   colon3Ptr++;
640   return(colon3Ptr);
641 }
642 #endif
643
644 /*
645  * start_auth - starts auth (identd) and dns queries for a client
646  */
647 enum { LOOPBACK = 127 };
648
649 void start_auth(struct Client* client)
650 {
651   struct AuthRequest* auth = 0;
652
653   assert(0 != client);
654
655   auth = make_auth_request(client);
656   assert(0 != auth);
657
658   Debug((DEBUG_INFO, "Beginning auth request on client %p", client));
659
660   if (!feature_bool(FEAT_NODNS)) {
661     if (LOOPBACK == inet_netof(cli_ip(client)))
662       strcpy(cli_sockhost(client), cli_name(&me));
663     else {
664       struct DNSQuery query;
665
666       query.vptr     = auth;
667       query.callback = auth_dns_callback;
668
669       if (IsUserPort(auth->client))
670         sendheader(client, REPORT_DO_DNS);
671
672       cli_dns_reply(client) = gethost_byaddr((const char*) &(cli_ip(client)),
673                                              &query);
674
675       if (cli_dns_reply(client)) {
676         ++(cli_dns_reply(client))->ref_count;
677         ircd_strncpy(cli_sockhost(client), cli_dns_reply(client)->hp->h_name,
678                      HOSTLEN);
679         if (IsUserPort(auth->client))
680           sendheader(client, REPORT_FIN_DNSC);
681         Debug((DEBUG_LIST, "DNS entry for %p was cached", auth->client));
682       } else
683         SetDNSPending(auth);
684     }
685   }
686
687   if (start_auth_query(auth)) {
688     Debug((DEBUG_LIST, "identd query for %p initiated successfully",
689            auth->client));
690     link_auth_request(auth, &AuthPollList);
691   } else if (IsDNSPending(auth)) {
692     Debug((DEBUG_LIST, "identd query for %p not initiated successfully; "
693            "waiting on DNS", auth->client));
694     link_auth_request(auth, &AuthIncompleteList);
695   } else {
696     Debug((DEBUG_LIST, "identd query for %p not initiated successfully; "
697            "no DNS pending; releasing immediately", auth->client));
698     free_auth_request(auth);
699     release_auth_client(client);
700   }
701 }
702
703 /*
704  * send_auth_query - send the ident server a query giving "theirport , ourport"
705  * The write is only attempted *once* so it is deemed to be a fail if the
706  * entire write doesn't write all the data given.  This shouldnt be a
707  * problem since the socket should have a write buffer far greater than
708  * this message to store it in should problems arise. -avalon
709  */
710 void send_auth_query(struct AuthRequest* auth)
711 {
712   struct sockaddr_in us;
713   struct sockaddr_in them;
714   char               authbuf[32];
715   unsigned int       count;
716
717   assert(0 != auth);
718   assert(0 != auth->client);
719
720   if (!os_get_sockname(cli_fd(auth->client), &us) ||
721       !os_get_peername(cli_fd(auth->client), &them)) {
722     auth_error(auth, 1);
723     return;
724   }
725   sprintf_irc(authbuf, "%u , %u\r\n",
726              (unsigned int) ntohs(them.sin_port),
727              (unsigned int) ntohs(us.sin_port));
728
729   if (IO_SUCCESS == os_send_nonb(auth->fd, authbuf, strlen(authbuf), &count)) {
730     ClearAuthConnect(auth);
731     SetAuthPending(auth);
732   }
733   else
734     auth_error(auth, 0);
735 }
736
737
738 /*
739  * read_auth_reply - read the reply (if any) from the ident server 
740  * we connected to.
741  * We only give it one shot, if the reply isn't good the first time
742  * fail the authentication entirely. --Bleep
743  */
744 void read_auth_reply(struct AuthRequest* auth)
745 {
746   char*        username = 0;
747   unsigned int len;
748   /*
749    * rfc1453 sez we MUST accept 512 bytes
750    */
751   char   buf[BUFSIZE + 1];
752
753   assert(0 != auth);
754   assert(0 != auth->client);
755   assert(auth = cli_auth(auth->client));
756
757   if (IO_SUCCESS == os_recv_nonb(auth->fd, buf, BUFSIZE, &len)) {
758     buf[len] = '\0';
759     Debug((DEBUG_LIST, "Auth %p [%p] reply: %s", auth, &auth->socket, buf));
760     username = check_ident_reply(buf);
761     Debug((DEBUG_LIST, "Username: %s", username));
762   }
763
764   close(auth->fd);
765   auth->fd = -1;
766   Debug((DEBUG_LIST, "Deleting auth [%p] socket %p", auth, &auth->socket));
767   socket_del(&auth->socket);
768   ClearAuth(auth);
769   
770   if (!EmptyString(username)) {
771     ircd_strncpy(cli_username(auth->client), username, USERLEN);
772     /*
773      * Not needed, struct is zeroed by memset
774      * auth->client->username[USERLEN] = '\0';
775      */
776     SetGotId(auth->client);
777     ++ServerStats->is_asuc;
778     if (IsUserPort(auth->client))
779       sendheader(auth->client, REPORT_FIN_ID);
780   }
781   else {
782     ++ServerStats->is_abad;
783 #if 0
784     strcpy(cli_username(auth->client), "unknown");
785 #endif
786   }
787   unlink_auth_request(auth, &AuthPollList);
788
789   if (IsDNSPending(auth))
790     link_auth_request(auth, &AuthIncompleteList);
791   else {
792     release_auth_client(auth->client);
793     free_auth_request(auth);
794   }
795 }
796
797