a02edefad0e4cf020af0c0de1552ee92026d4f7d
[freeradius.git] / src / main / request_list.c
1 /*
2  * request_list.c       Hide the handling of the REQUEST list from
3  *                      the main server.
4  *
5  * Version:     $Id$
6  *
7  *   This program is free software; you can redistribute it and/or modify
8  *   it under the terms of the GNU General Public License as published by
9  *   the Free Software Foundation; either version 2 of the License, or
10  *   (at your option) any later version.
11  *
12  *   This program is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *
17  *   You should have received a copy of the GNU General Public License
18  *   along with this program; if not, write to the Free Software
19  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  *
21  * Copyright 2003-2004,2006  The FreeRADIUS server project
22  */
23
24 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include <freeradius-devel/autoconf.h>
28
29 #include <stdlib.h>
30 #include <string.h>
31 #include <signal.h>
32
33 #include <freeradius-devel/radiusd.h>
34 #include <freeradius-devel/rad_assert.h>
35 #include <freeradius-devel/request_list.h>
36 #include <freeradius-devel/radius_snmp.h>
37
38 struct request_list_t {
39         lrad_packet_list_t *pl;
40 };
41
42 #ifdef HAVE_PTHREAD_H
43 static pthread_mutex_t  proxy_mutex;
44 #else
45 /*
46  *      This is easier than ifdef's throughout the code.
47  */
48 #define pthread_mutex_lock(_x)
49 #define pthread_mutex_unlock(_x)
50 #endif
51
52 static lrad_packet_list_t *proxy_list = NULL;
53
54 /*
55  *      We keep the proxy FD's here.  The RADIUS Id's are marked
56  *      "allocated" per Id, via a bit per proxy FD.
57  */
58 static int              proxy_fds[32];
59 static rad_listen_t     *proxy_listeners[32];
60
61 /*
62  *      Initialize the request list.
63  */
64 request_list_t *rl_init(void)
65 {
66         request_list_t *rl = rad_malloc(sizeof(*rl));
67
68         /*
69          *      Initialize the request_list[] array.
70          */
71         memset(rl, 0, sizeof(*rl));
72
73         rl->pl = lrad_packet_list_create(0);
74         if (!rl->pl) {
75                 rad_assert("FAIL" == NULL);
76         }
77
78         return rl;
79 }
80
81 int rl_init_proxy(void)
82 {
83         /*
84          *      Hacks, so that multiple users can call rl_init,
85          *      and it won't get excited.
86          *
87          *      FIXME: Move proxy stuff to another struct entirely.
88          */
89         if (proxy_list) return 0;
90
91         /*
92          *      Create the tree for managing proxied requests and
93          *      responses.
94          */
95         proxy_list = lrad_packet_list_create(1);
96         if (!proxy_list) {
97                 rad_assert("FAIL" == NULL);
98         }
99
100 #ifdef HAVE_PTHREAD_H
101         /*
102          *      For now, always create the mutex.
103          *
104          *      Later, we can only create it if there are multiple threads.
105          */
106         if (pthread_mutex_init(&proxy_mutex, NULL) != 0) {
107                 radlog(L_ERR, "FATAL: Failed to initialize proxy mutex: %s",
108                        strerror(errno));
109                 exit(1);
110         }
111 #endif
112
113         {
114                 int i;
115                 rad_listen_t *listener;
116
117                 /*
118                  *      Mark the Fd's as unused.
119                  */
120                 for (i = 0; i < 32; i++) proxy_fds[i] = -1;
121
122                 for (listener = mainconfig.listen;
123                      listener != NULL;
124                      listener = listener->next) {
125                         if (listener->type == RAD_LISTEN_PROXY) {
126                                 /*
127                                  *      FIXME: This works only because we
128                                  *      start off with one proxy socket.
129                                  */
130                                 proxy_fds[listener->fd & 0x1f] = listener->fd;
131                                 proxy_listeners[listener->fd & 0x1f] = listener;
132                                 lrad_packet_list_socket_add(proxy_list, listener->fd);
133                                 break;
134                         }
135                 }
136         }
137
138         return 1;
139 }
140
141 static int rl_free_entry(void *ctx, void *data)
142 {
143         REQUEST *request = data;
144         
145         ctx = ctx;              /* -Wunused */
146
147 #ifdef HAVE_PTHREAD_H 
148         /*
149          *      If someone is processing this request, kill
150          *      them, and mark the request as not being used.
151          *
152          *      FIXME: Move the request to the "dead pool",
153          *      and don't kill the thread.
154          */
155         if (request->child_pid != NO_SUCH_CHILD_PID) {
156                 pthread_kill(request->child_pid, SIGKILL);
157                 request->child_pid = NO_SUCH_CHILD_PID;
158         }
159 #endif
160         request_free(&request);
161
162         return 0;
163 }
164
165
166 /*
167  *      Delete everything in the request list.
168  *
169  *      This should be called only when debugging the server...
170  */
171 void rl_deinit(request_list_t *rl)
172 {
173         if (!rl) return;
174
175         if (proxy_list) {
176                 lrad_packet_list_free(proxy_list);
177                 proxy_list = NULL;
178         }
179
180         /*
181          *      Delete everything in the table, too.
182          */
183         lrad_packet_list_walk(rl->pl, rl_free_entry, NULL);
184
185         lrad_packet_list_free(rl->pl);
186
187         /*
188          *      Just to ensure no one is using the memory.
189          */
190         memset(rl, 0, sizeof(*rl));
191         free(rl);
192 }
193
194
195 /*
196  *      Yank a request from the tree, without free'ing it.
197  */
198 void rl_yank(request_list_t *rl, REQUEST *request)
199 {
200 #ifdef WITH_SNMP
201         /*
202          *      Update the SNMP statistics.
203          *
204          *      Note that we do NOT do this in rad_respond(),
205          *      as that function is called from child threads.
206          *      Instead, we update the stats when a request is
207          *      deleted, because only the main server thread calls
208          *      this function...
209          */
210         if (mainconfig.do_snmp) {
211                 switch (request->reply->code) {
212                 case PW_AUTHENTICATION_ACK:
213                   rad_snmp.auth.total_responses++;
214                   rad_snmp.auth.total_access_accepts++;
215                   break;
216
217                 case PW_AUTHENTICATION_REJECT:
218                   rad_snmp.auth.total_responses++;
219                   rad_snmp.auth.total_access_rejects++;
220                   break;
221
222                 case PW_ACCESS_CHALLENGE:
223                   rad_snmp.auth.total_responses++;
224                   rad_snmp.auth.total_access_challenges++;
225                   break;
226
227                 case PW_ACCOUNTING_RESPONSE:
228                   rad_snmp.acct.total_responses++;
229                   break;
230
231                 default:
232                         break;
233                 }
234         }
235 #endif
236
237         /*
238          *      Delete the request from the list.
239          */
240         lrad_packet_list_yank(rl->pl, request->packet);
241         
242         /*
243          *      If there's a proxied packet, and we're still
244          *      waiting for a reply, then delete the packet
245          *      from the list of outstanding proxied requests.
246          */
247         if (request->proxy &&
248             (request->proxy_outstanding > 0)) {
249                 pthread_mutex_lock(&proxy_mutex);
250                 lrad_packet_list_yank(proxy_list, request->proxy);
251                 lrad_packet_list_id_free(proxy_list, request->proxy);
252                 pthread_mutex_unlock(&proxy_mutex);
253         }
254 }
255
256
257 /*
258  *      Delete a request from the tree.
259  */
260 void rl_delete(request_list_t *rl, REQUEST *request)
261 {
262         rl_yank(rl, request);
263         request_free(&request);
264 }
265
266
267 /*
268  *      Add a request to the request list.
269  */
270 int rl_add(request_list_t *rl, REQUEST *request)
271 {
272         return lrad_packet_list_insert(rl->pl, &request->packet);
273 }
274
275 /*
276  *      Look up a particular request, using:
277  *
278  *      Request ID, request code, source IP, source port,
279  *
280  *      Note that we do NOT use the request vector to look up requests.
281  *
282  *      We MUST NOT have two requests with identical (id/code/IP/port), and
283  *      different vectors.  This is a serious error!
284  */
285 REQUEST *rl_find(request_list_t *rl, RADIUS_PACKET *packet)
286 {
287         RADIUS_PACKET **packet_p;
288
289         packet_p = lrad_packet_list_find(rl->pl, packet);
290         if (!packet_p) return NULL;
291
292         return lrad_packet2myptr(REQUEST, packet, packet_p);
293 }
294
295 /*
296  *      Add an entry to the proxy tree.
297  *
298  *      This is the ONLY function in this source file which may be called
299  *      from a child thread.  It therefore needs mutexes...
300  */
301 int rl_add_proxy(REQUEST *request)
302 {
303         int i, proxy;
304         char buf[128];
305
306         request->proxy_outstanding = 1;
307         request->proxy->sockfd = -1;
308
309         pthread_mutex_lock(&proxy_mutex);
310
311         if (!lrad_packet_list_id_alloc(proxy_list, request->proxy)) {
312                 int found;
313                 rad_listen_t *proxy_listener;
314
315                 /*
316                  *      Allocate a new proxy Fd.  This function adds it
317                  *      into the list of listeners.
318                  */
319                 proxy_listener = proxy_new_listener();
320                 if (!proxy_listener) {
321                         pthread_mutex_unlock(&proxy_mutex);
322                         DEBUG2("ERROR: Failed to create a new socket for proxying requests.");
323                         return 0;
324                 }
325
326                 /*
327                  *      Cache it locally.
328                  */
329                 found = -1;
330                 proxy = proxy_listener->fd;
331                 for (i = 0; i < 32; i++) {
332                         /*
333                          *      Found a free entry.  Save the socket,
334                          *      and remember where we saved it.
335                          */
336                         if (proxy_fds[(proxy + i) & 0x1f] == -1) {
337                                 found = (proxy + i) & 0x1f;
338                                 proxy_fds[found] = proxy;
339                                 proxy_listeners[found] = proxy_listener;
340                                 break;
341                         }
342                 }
343                 rad_assert(found >= 0);
344
345                 if (!lrad_packet_list_socket_add(proxy_list, proxy_listener->fd)) {
346                         pthread_mutex_unlock(&proxy_mutex);
347                         DEBUG2("ERROR: Failed to create a new socket for proxying requests.");
348                         return 0; /* leak proxy_listener */
349                         
350                 }
351                     
352                 if (!lrad_packet_list_id_alloc(proxy_list, request->proxy)) {
353                         pthread_mutex_unlock(&proxy_mutex);
354                         DEBUG2("ERROR: Failed to create a new socket for proxying requests.");
355                         return 0;
356                 }
357         }
358
359         /*
360          *      FIXME: Hack until we get rid of rad_listen_t, and put
361          *      the information into the packet_list.
362          */
363         proxy = -1;
364         for (i = 0; i < 32; i++) {
365                 if (proxy_fds[i] == request->proxy->sockfd) {
366                         proxy = i;
367                         break;
368                 }
369         }
370         rad_assert(proxy >= 0);
371
372         rad_assert(proxy_fds[proxy] != -1);
373         request->proxy_listener = proxy_listeners[proxy];
374
375         if (!lrad_packet_list_insert(proxy_list, &request->proxy)) {
376                 pthread_mutex_unlock(&proxy_mutex);
377                 DEBUG2("ERROR: Failed to insert entry into proxy list");
378                 return 0;
379         }
380         
381         pthread_mutex_unlock(&proxy_mutex);
382
383         DEBUG3(" proxy: allocating destination %s port %d - Id %d",
384                inet_ntop(request->proxy->dst_ipaddr.af,
385                          &request->proxy->dst_ipaddr.ipaddr, buf, sizeof(buf)),
386                request->proxy->dst_port,
387                request->proxy->id);
388         
389         return 1;
390 }
391
392
393 /*
394  *      Look up a particular request, using:
395  *
396  *      Request Id, request code, source IP, source port,
397  *
398  *      Note that we do NOT use the request vector to look up requests.
399  *
400  *      We MUST NOT have two requests with identical (id/code/IP/port), and
401  *      different vectors.  This is a serious error!
402  */
403 REQUEST *rl_find_proxy(RADIUS_PACKET *reply)
404 {
405         RADIUS_PACKET **proxy_p;
406         REQUEST *request;
407
408         pthread_mutex_lock(&proxy_mutex);
409         proxy_p = lrad_packet_list_find_byreply(proxy_list, reply);
410
411         if (!proxy_p) {
412                 pthread_mutex_unlock(&proxy_mutex);
413                 return NULL;
414         }
415
416         request = lrad_packet2myptr(REQUEST, proxy, proxy_p);
417         rad_assert(request->proxy_outstanding > 0);
418         request->proxy_outstanding--;
419                 
420         /*
421          *      Received all of the replies we expect.
422          *      delete it from the managed list.
423          */
424         if (request->proxy_outstanding == 0) {
425                 lrad_packet_list_yank(proxy_list, request->proxy);
426                 lrad_packet_list_id_free(proxy_list, request->proxy);
427         }
428         pthread_mutex_unlock(&proxy_mutex);
429
430         return request;
431 }
432
433
434 /*
435  *      Return the number of requests in the request list.
436  */
437 int rl_num_requests(request_list_t *rl)
438 {
439         return lrad_packet_list_num_elements(rl->pl);
440 }
441
442
443 /*
444  *      See also radiusd.c
445  */
446 #define SLEEP_FOREVER (65536)
447 typedef struct rl_walk_t {
448         time_t  now;
449         int     sleep_time;
450         request_list_t *rl;
451 } rl_walk_t;
452
453
454 /*
455  *  Refresh a request, by using cleanup_delay, max_request_time, etc.
456  *
457  *  When walking over the request list, all of the per-request
458  *  magic is done here.
459  */
460 static int refresh_request(void *ctx, void *data)
461 {
462         int time_passed;
463         rl_walk_t *info = (rl_walk_t *) ctx;
464         child_pid_t child_pid;
465         request_list_t *rl = info->rl;
466         REQUEST *request = lrad_packet2myptr(REQUEST, packet, data);
467
468         rad_assert(request->magic == REQUEST_MAGIC);
469
470         time_passed = (int) (info->now - request->timestamp);
471         
472         /*
473          *      If the request is marked as a delayed reject, AND it's
474          *      time to send the reject, then do so now.
475          */
476         if (request->finished &&
477             ((request->options & RAD_REQUEST_OPTION_DELAYED_REJECT) != 0)) {
478                 rad_assert(request->child_pid == NO_SUCH_CHILD_PID);
479                 if (time_passed < mainconfig.reject_delay) {
480                         goto reject_delay;
481                 }
482
483         reject_packet:
484                 /*
485                  *      Clear the 'delayed reject' bit, so that we
486                  *      don't do this again, and fall through to
487                  *      setting cleanup delay.
488                  */
489                 request->listener->send(request->listener, request);
490                 request->options &= ~RAD_REQUEST_OPTION_DELAYED_REJECT;
491
492                 /*
493                  *      FIXME: Beware interaction with cleanup_delay,
494                  *      where we might send a reject, and immediately
495                  *      there-after clean it up!
496                  */
497         }
498
499         /*
500          *      If the request is finished, THEN
501          *      check that more than cleanup_delay seconds have passed
502          *      since it was received
503          *      OR, if this is a request which had the "don't cache"
504          *      option set, then it CANNOT have a duplicate
505          *      SO, clean it up
506          */
507         if (request->finished &&
508             ((time_passed >= mainconfig.cleanup_delay) ||
509             ((request->options & RAD_REQUEST_OPTION_DONT_CACHE) != 0))) {
510                 rad_assert(request->child_pid == NO_SUCH_CHILD_PID);
511                 /*
512                  *  Request completed, delete it, and unlink it
513                  *  from the currently 'alive' list of requests.
514                  */
515         cleanup:
516                 DEBUG2("Cleaning up request %d ID %d with timestamp %08lx",
517                                 request->number, request->packet->id,
518                                 (unsigned long) request->timestamp);
519
520                 /*
521                  *  Delete the request.
522                  */
523                 rl_delete(rl, request);
524                 return 0;
525         }
526
527         /*
528          *      If more than max_request_time has passed since
529          *      we received the request, kill it.
530          */
531         if (time_passed >= mainconfig.max_request_time) {
532                 int number;
533
534                 child_pid = request->child_pid;
535                 number = request->number;
536
537                 /*
538                  *      There MUST be a RAD_PACKET reply.
539                  */
540                 rad_assert(request->reply != NULL);
541
542                 /*
543                  *      If we've tried to proxy the request, and
544                  *      the proxy server hasn't responded, then
545                  *      we send a REJECT back to the caller.
546                  *
547                  *      For safety, we assert that there is no child
548                  *      handling the request.  If the assertion fails,
549                  *      it means that we've sent a proxied request to
550                  *      the home server, and the child thread is still
551                  *      sitting on the request!
552                  */
553                 if (request->proxy && !request->proxy_reply) {
554                         rad_assert(request->child_pid == NO_SUCH_CHILD_PID);
555
556                         radlog(L_ERR, "Rejecting request %d due to lack of any response from home server %s port %d",
557                                request->number,
558                                client_name_old(&request->packet->src_ipaddr),
559                                request->packet->src_port);
560                         request_reject(request, REQUEST_FAIL_HOME_SERVER);
561                         request->finished = TRUE;
562                         return 0;
563                 }
564
565                 if (mainconfig.kill_unresponsive_children) {
566                         if (child_pid != NO_SUCH_CHILD_PID) {
567                                 /*
568                                  *  This request seems to have hung
569                                  *   - kill it
570                                  */
571 #ifdef HAVE_PTHREAD_H
572                                 radlog(L_ERR, "Killing unresponsive thread for request %d",
573                                        request->number);
574                                 pthread_cancel(child_pid);
575 #endif
576                         } /* else no proxy reply, quietly fail */
577
578                         /*
579                          *      Maybe we haven't killed it.  In that
580                          *      case, print a warning.
581                          */
582                 } else if ((child_pid != NO_SUCH_CHILD_PID) &&
583                            ((request->options & RAD_REQUEST_OPTION_LOGGED_CHILD) == 0)) {
584                         radlog(L_ERR, "WARNING: Unresponsive child (id %lu) for request %d",
585                                (unsigned long)child_pid, number);
586
587                         /*
588                          *  Set the option that we've sent a log message,
589                          *  so that we don't send more than one message
590                          *  per request.
591                          */
592                         request->options |= RAD_REQUEST_OPTION_LOGGED_CHILD;
593                 }
594
595                 /*
596                  *      Send a reject message for the request, mark it
597                  *      finished, and forget about the child.
598                  */
599                 request_reject(request, REQUEST_FAIL_SERVER_TIMEOUT);
600                 
601                 request->child_pid = NO_SUCH_CHILD_PID;
602
603                 if (mainconfig.kill_unresponsive_children)
604                         request->finished = TRUE;
605                 return 0;
606         } /* else the request is still allowed to be in the queue */
607
608         /*
609          *      If the request is finished, set the cleanup delay.
610          */
611         if (request->finished) {
612                 time_passed = mainconfig.cleanup_delay - time_passed;
613                 goto setup_timeout;
614         }
615
616         /*
617          *      Accounting request.  Don't re-send them, since the NAS
618          *      will take care of doing that, and we're not a NAS.
619          *      Instead, simply clean them up once we're pretty sure
620          *      that the home server won't be responding.
621          */
622         if ((request->packet->code == PW_ACCOUNTING_REQUEST) &&
623             request->proxy && !request->proxy_reply &&
624             (request->child_pid == NO_SUCH_CHILD_PID) &&
625             ((info->now - request->proxy_start_time) > (mainconfig.proxy_retry_delay * 2))) {
626                 goto cleanup;
627         }
628
629
630         /*
631          *      Set reject delay, if appropriate.
632          */
633         if ((request->packet->code == PW_AUTHENTICATION_REQUEST) &&
634             (mainconfig.reject_delay > 0)) {
635         reject_delay:
636                 time_passed = mainconfig.reject_delay - time_passed;
637                 
638                 /*
639                  *      This catches a corner case, apparently.
640                  */
641                 if ((request->reply->code == PW_AUTHENTICATION_REJECT) &&
642                     (time_passed == 0)) goto reject_packet;
643                 if (time_passed <= 0) time_passed = 1;
644                 goto setup_timeout;
645         }
646
647         /*
648          *      The request is still alive, wake up when it's
649          *      taken too long.
650          */
651         time_passed = mainconfig.max_request_time - time_passed;
652
653 setup_timeout:          
654         if (time_passed < 0) time_passed = 1;
655
656         if (time_passed < info->sleep_time) {
657                 info->sleep_time = time_passed;
658         }
659
660         return 0;
661 }
662
663
664 /*
665  *  Clean up the request list, every so often.
666  *
667  *  This is done by walking through ALL of the list, and
668  *  - marking any requests which are finished, and expired
669  *  - killing any processes which are NOT finished after a delay
670  *  - deleting any marked requests.
671  *
672  *      Returns the number of millisends to sleep, before processing
673  *      something.
674  */
675 int rl_clean_list(request_list_t *rl, time_t now)
676 {
677         rl_walk_t info;
678
679         info.now = now;
680         info.sleep_time = SLEEP_FOREVER;
681         info.rl = rl;
682
683         lrad_packet_list_walk(rl->pl, &info, refresh_request);
684
685         if (info.sleep_time < 0) info.sleep_time = 0;
686
687         return info.sleep_time;
688 }