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