Use main_config.name everywhere.
[freeradius.git] / src / main / unittest.c
1 /*
2  * unittest.c   Unit test wrapper for the RADIUS daemon.
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000-2013  The FreeRADIUS server project
21  * Copyright 2013  Alan DeKok <aland@ox.org>
22  */
23
24 RCSID("$Id$")
25
26 #include <freeradius-devel/radiusd.h>
27 #include <freeradius-devel/modules.h>
28 #include <freeradius-devel/state.h>
29 #include <freeradius-devel/rad_assert.h>
30
31 #ifdef HAVE_GETOPT_H
32 #       include <getopt.h>
33 #endif
34
35 #include <ctype.h>
36
37 /*
38  *  Global variables.
39  */
40 char const *radacct_dir = NULL;
41 char const *radlog_dir = NULL;
42 bool log_stripped_names = false;
43
44 static bool memory_report = false;
45 static bool filedone = false;
46
47 char const *radiusd_version = "FreeRADIUS Version " RADIUSD_VERSION_STRING
48 #ifdef RADIUSD_VERSION_COMMIT
49 " (git #" STRINGIFY(RADIUSD_VERSION_COMMIT) ")"
50 #endif
51 ", for host " HOSTINFO ", built on " __DATE__ " at " __TIME__;
52
53 /*
54  *      Static functions.
55  */
56 static void usage(int);
57
58 void listen_free(UNUSED rad_listen_t **head)
59 {
60         /* do nothing */
61 }
62
63
64 static rad_listen_t *listen_alloc(void *ctx)
65 {
66         rad_listen_t *this;
67
68         this = talloc_zero(ctx, rad_listen_t);
69         if (!this) return NULL;
70
71         this->type = RAD_LISTEN_AUTH;
72         this->recv = NULL;
73         this->send = NULL;
74         this->print = NULL;
75         this->encode = NULL;
76         this->decode = NULL;
77
78         /*
79          *      We probably don't care about this.  We can always add
80          *      fields later.
81          */
82         this->data = talloc_zero(this, listen_socket_t);
83         if (!this->data) {
84                 talloc_free(this);
85                 return NULL;
86         }
87
88         return this;
89 }
90
91 static RADCLIENT *client_alloc(void *ctx)
92 {
93         RADCLIENT *client;
94
95         client = talloc_zero(ctx, RADCLIENT);
96         if (!client) return NULL;
97
98         return client;
99 }
100
101 static REQUEST *request_setup(FILE *fp)
102 {
103         VALUE_PAIR      *vp;
104         REQUEST         *request;
105         vp_cursor_t     cursor;
106         struct timeval  now;
107
108         /*
109          *      Create and initialize the new request.
110          */
111         request = request_alloc(NULL);
112         gettimeofday(&now, NULL);
113         request->timestamp = now.tv_sec;
114
115         request->packet = rad_alloc(request, false);
116         if (!request->packet) {
117                 ERROR("No memory");
118                 talloc_free(request);
119                 return NULL;
120         }
121         request->packet->timestamp = now;
122
123         request->reply = rad_alloc(request, false);
124         if (!request->reply) {
125                 ERROR("No memory");
126                 talloc_free(request);
127                 return NULL;
128         }
129
130         request->listener = listen_alloc(request);
131         request->client = client_alloc(request);
132
133         request->number = 0;
134
135         request->master_state = REQUEST_ACTIVE;
136         request->child_state = REQUEST_RUNNING;
137         request->handle = NULL;
138         request->server = talloc_typed_strdup(request, "default");
139
140         request->root = &main_config;
141
142         /*
143          *      Read packet from fp
144          */
145         if (fr_pair_list_afrom_file(request->packet, &request->packet->vps, fp, &filedone) < 0) {
146                 fr_perror("unittest");
147                 talloc_free(request);
148                 return NULL;
149         }
150
151         /*
152          *      Set the defaults for IPs, etc.
153          */
154         request->packet->code = PW_CODE_ACCESS_REQUEST;
155
156         request->packet->src_ipaddr.af = AF_INET;
157         request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK);
158         request->packet->src_port = 18120;
159
160         request->packet->dst_ipaddr.af = AF_INET;
161         request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK);
162         request->packet->dst_port = 1812;
163
164         /*
165          *      Copied from radclient
166          *
167          *      Fix up Digest-Attributes issues
168          */
169         for (vp = fr_cursor_init(&cursor, &request->packet->vps);
170              vp;
171              vp = fr_cursor_next(&cursor)) {
172                 /*
173                  *      Double quoted strings get marked up as xlat expansions,
174                  *      but we don't support that here.
175                  */
176                 if (vp->type == VT_XLAT) {
177                         vp->vp_strvalue = vp->value.xlat;
178                         vp->value.xlat = NULL;
179                         vp->type = VT_DATA;
180                 }
181
182                 if (!vp->da->vendor) switch (vp->da->attr) {
183                 default:
184                         break;
185
186                         /*
187                          *      Allow it to set the packet type in
188                          *      the attributes read from the file.
189                          */
190                 case PW_PACKET_TYPE:
191                         request->packet->code = vp->vp_integer;
192                         break;
193
194                 case PW_PACKET_DST_PORT:
195                         request->packet->dst_port = (vp->vp_integer & 0xffff);
196                         break;
197
198                 case PW_PACKET_DST_IP_ADDRESS:
199                         request->packet->dst_ipaddr.af = AF_INET;
200                         request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
201                         request->packet->dst_ipaddr.prefix = 32;
202                         break;
203
204                 case PW_PACKET_DST_IPV6_ADDRESS:
205                         request->packet->dst_ipaddr.af = AF_INET6;
206                         request->packet->dst_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
207                         request->packet->dst_ipaddr.prefix = 128;
208                         break;
209
210                 case PW_PACKET_SRC_PORT:
211                         request->packet->src_port = (vp->vp_integer & 0xffff);
212                         break;
213
214                 case PW_PACKET_SRC_IP_ADDRESS:
215                         request->packet->src_ipaddr.af = AF_INET;
216                         request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
217                         request->packet->src_ipaddr.prefix = 32;
218                         break;
219
220                 case PW_PACKET_SRC_IPV6_ADDRESS:
221                         request->packet->src_ipaddr.af = AF_INET6;
222                         request->packet->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
223                         request->packet->src_ipaddr.prefix = 128;
224                         break;
225
226                 case PW_CHAP_PASSWORD: {
227                         int i, already_hex = 0;
228
229                         /*
230                          *      If it's 17 octets, it *might* be already encoded.
231                          *      Or, it might just be a 17-character password (maybe UTF-8)
232                          *      Check it for non-printable characters.  The odds of ALL
233                          *      of the characters being 32..255 is (1-7/8)^17, or (1/8)^17,
234                          *      or 1/(2^51), which is pretty much zero.
235                          */
236                         if (vp->vp_length == 17) {
237                                 for (i = 0; i < 17; i++) {
238                                         if (vp->vp_octets[i] < 32) {
239                                                 already_hex = 1;
240                                                 break;
241                                         }
242                                 }
243                         }
244
245                         /*
246                          *      Allow the user to specify ASCII or hex CHAP-Password
247                          */
248                         if (!already_hex) {
249                                 uint8_t *p;
250                                 size_t len, len2;
251
252                                 len = len2 = vp->vp_length;
253                                 if (len2 < 17) len2 = 17;
254
255                                 p = talloc_zero_array(vp, uint8_t, len2);
256
257                                 memcpy(p, vp->vp_strvalue, len);
258
259                                 rad_chap_encode(request->packet,
260                                                 p,
261                                                 fr_rand() & 0xff, vp);
262                                 vp->vp_octets = p;
263                                 vp->vp_length = 17;
264                         }
265                 }
266                         break;
267
268                 case PW_DIGEST_REALM:
269                 case PW_DIGEST_NONCE:
270                 case PW_DIGEST_METHOD:
271                 case PW_DIGEST_URI:
272                 case PW_DIGEST_QOP:
273                 case PW_DIGEST_ALGORITHM:
274                 case PW_DIGEST_BODY_DIGEST:
275                 case PW_DIGEST_CNONCE:
276                 case PW_DIGEST_NONCE_COUNT:
277                 case PW_DIGEST_USER_NAME:
278                         /* overlapping! */
279                 {
280                         DICT_ATTR const *da;
281                         uint8_t *p, *q;
282
283                         p = talloc_array(vp, uint8_t, vp->vp_length + 2);
284
285                         memcpy(p + 2, vp->vp_octets, vp->vp_length);
286                         p[0] = vp->da->attr - PW_DIGEST_REALM + 1;
287                         vp->vp_length += 2;
288                         p[1] = vp->vp_length;
289
290                         da = dict_attrbyvalue(PW_DIGEST_ATTRIBUTES, 0);
291                         rad_assert(da != NULL);
292                         vp->da = da;
293
294                         /*
295                          *      Re-do fr_pair_value_memsteal ourselves,
296                          *      because we play games with
297                          *      vp->da, and fr_pair_value_memsteal goes
298                          *      to GREAT lengths to sanitize
299                          *      and fix and change and
300                          *      double-check the various
301                          *      fields.
302                          */
303                         memcpy(&q, &vp->vp_octets, sizeof(q));
304                         talloc_free(q);
305
306                         vp->vp_octets = talloc_steal(vp, p);
307                         vp->type = VT_DATA;
308
309                         VERIFY_VP(vp);
310                 }
311
312                 break;
313                 }
314         } /* loop over the VP's we read in */
315
316         if (rad_debug_lvl) {
317                 for (vp = fr_cursor_init(&cursor, &request->packet->vps);
318                      vp;
319                      vp = fr_cursor_next(&cursor)) {
320                         /*
321                          *      Take this opportunity to verify all the VALUE_PAIRs are still valid.
322                          */
323                         if (!talloc_get_type(vp, VALUE_PAIR)) {
324                                 ERROR("Expected VALUE_PAIR pointer got \"%s\"", talloc_get_name(vp));
325
326                                 fr_log_talloc_report(vp);
327                                 rad_assert(0);
328                         }
329
330                         vp_print(fr_log_fp, vp);
331                 }
332                 fflush(fr_log_fp);
333         }
334
335         /*
336          *      Build the reply template from the request.
337          */
338         request->reply->sockfd = request->packet->sockfd;
339         request->reply->dst_ipaddr = request->packet->src_ipaddr;
340         request->reply->src_ipaddr = request->packet->dst_ipaddr;
341         request->reply->dst_port = request->packet->src_port;
342         request->reply->src_port = request->packet->dst_port;
343         request->reply->id = request->packet->id;
344         request->reply->code = 0; /* UNKNOWN code */
345         memcpy(request->reply->vector, request->packet->vector,
346                sizeof(request->reply->vector));
347         request->reply->vps = NULL;
348         request->reply->data = NULL;
349         request->reply->data_len = 0;
350
351         /*
352          *      Debugging
353          */
354         request->log.lvl = rad_debug_lvl;
355         request->log.func = vradlog_request;
356
357         request->username = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
358         request->password = fr_pair_find_by_num(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
359
360         return request;
361 }
362
363
364 static void print_packet(FILE *fp, RADIUS_PACKET *packet)
365 {
366         VALUE_PAIR *vp;
367         vp_cursor_t cursor;
368
369         if (!packet) {
370                 fprintf(fp, "\n");
371                 return;
372         }
373
374         fprintf(fp, "%s\n", fr_packet_codes[packet->code]);
375
376         for (vp = fr_cursor_init(&cursor, &packet->vps);
377              vp;
378              vp = fr_cursor_next(&cursor)) {
379                 /*
380                  *      Take this opportunity to verify all the VALUE_PAIRs are still valid.
381                  */
382                 if (!talloc_get_type(vp, VALUE_PAIR)) {
383                         ERROR("Expected VALUE_PAIR pointer got \"%s\"", talloc_get_name(vp));
384
385                         fr_log_talloc_report(vp);
386                         rad_assert(0);
387                 }
388
389                 vp_print(fp, vp);
390         }
391         fflush(fp);
392 }
393
394
395 #include <freeradius-devel/modpriv.h>
396
397 /*
398  *      %{poke:sql.foo=bar}
399  */
400 static ssize_t xlat_poke(UNUSED void *instance, REQUEST *request,
401                          char const *fmt, char *out, size_t outlen)
402 {
403         int i;
404         void *data, *base;
405         char *p, *q;
406         module_instance_t *mi;
407         char *buffer;
408         CONF_SECTION *modules;
409         CONF_PAIR *cp;
410         CONF_PARSER const *variables;
411         size_t len;
412
413         rad_assert(outlen > 1);
414         rad_assert(request != NULL);
415         rad_assert(fmt != NULL);
416         rad_assert(out != NULL);
417
418         *out = '\0';
419
420         modules = cf_section_sub_find(request->root->config, "modules");
421         if (!modules) return 0;
422
423         buffer = talloc_strdup(request, fmt);
424         if (!buffer) return 0;
425
426         p = strchr(buffer, '.');
427         if (!p) return 0;
428
429         *(p++) = '\0';
430
431         mi = module_find(modules, buffer);
432         if (!mi) {
433                 RDEBUG("Failed finding module '%s'", buffer);
434         fail:
435                 talloc_free(buffer);
436                 return 0;
437         }
438
439         q = strchr(p, '=');
440         if (!q) {
441                 RDEBUG("Failed finding '=' in string '%s'", fmt);
442                 goto fail;
443         }
444
445         *(q++) = '\0';
446
447         if (strchr(p, '.') != NULL) {
448                 RDEBUG("Can't do sub-sections right now");
449                 goto fail;
450         }
451
452         cp = cf_pair_find(mi->cs, p);
453         if (!cp) {
454                 RDEBUG("No such item '%s'", p);
455                 goto fail;
456         }
457
458         /*
459          *      Copy the old value to the output buffer, that way
460          *      tests can restore it later, if they need to.
461          */
462         len = strlcpy(out, cf_pair_value(cp), outlen);
463
464         if (cf_pair_replace(mi->cs, cp, q) < 0) {
465                 RDEBUG("Failed replacing pair");
466                 goto fail;
467         }
468
469         base = mi->insthandle;
470         variables = mi->entry->module->config;
471
472         /*
473          *      Handle the known configuration parameters.
474          */
475         for (i = 0; variables[i].name != NULL; i++) {
476                 int ret;
477
478                 if (variables[i].type == PW_TYPE_SUBSECTION) continue;
479                 /* else it's a CONF_PAIR */
480
481                 /*
482                  *      Not the pair we want.  Skip it.
483                  */
484                 if (strcmp(variables[i].name, p) != 0) continue;
485
486                 if (variables[i].data) {
487                         data = variables[i].data; /* prefer this. */
488                 } else if (base) {
489                         data = ((char *)base) + variables[i].offset;
490                 } else {
491                         DEBUG2("Internal sanity check 2 failed in cf_section_parse");
492                         goto fail;
493                 }
494
495                 /*
496                  *      Parse the pair we found, or a default value.
497                  */
498                 ret = cf_item_parse(mi->cs, variables[i].name, variables[i].type, data, variables[i].dflt);
499                 if (ret < 0) {
500                         DEBUG2("Failed inserting new value into module instance data");
501                         goto fail;
502                 }
503                 break;          /* we found it, don't do any more */
504         }
505
506         talloc_free(buffer);
507
508         return len;
509 }
510
511
512 /*
513  *      Read a file compose of xlat's and expected results
514  */
515 static bool do_xlats(char const *filename, FILE *fp)
516 {
517         int             lineno = 0;
518         ssize_t         len;
519         char            *p;
520         char            input[8192];
521         char            output[8192];
522         REQUEST         *request;
523         struct timeval  now;
524
525         /*
526          *      Create and initialize the new request.
527          */
528         request = request_alloc(NULL);
529         gettimeofday(&now, NULL);
530         request->timestamp = now.tv_sec;
531
532         request->log.lvl = rad_debug_lvl;
533         request->log.func = vradlog_request;
534
535         output[0] = '\0';
536
537         while (fgets(input, sizeof(input), fp) != NULL) {
538                 lineno++;
539
540                 /*
541                  *      Ignore blank lines and comments
542                  */
543                 p = input;
544                 while (isspace((int) *p)) p++;
545
546                 if (*p < ' ') continue;
547                 if (*p == '#') continue;
548
549                 p = strchr(p, '\n');
550                 if (!p) {
551                         if (!feof(fp)) {
552                                 fprintf(stderr, "Line %d too long in %s\n",
553                                         lineno, filename);
554                                 TALLOC_FREE(request);
555                                 return false;
556                         }
557                 } else {
558                         *p = '\0';
559                 }
560
561                 /*
562                  *      Look for "xlat"
563                  */
564                 if (strncmp(input, "xlat ", 5) == 0) {
565                         ssize_t slen;
566                         char const *error = NULL;
567                         char *fmt = talloc_typed_strdup(NULL, input + 5);
568                         xlat_exp_t *head;
569
570                         slen = xlat_tokenize(fmt, fmt, &head, &error);
571                         if (slen <= 0) {
572                                 talloc_free(fmt);
573                                 snprintf(output, sizeof(output), "ERROR offset %d '%s'", (int) -slen, error);
574                                 continue;
575                         }
576
577                         if (input[slen + 5] != '\0') {
578                                 talloc_free(fmt);
579                                 snprintf(output, sizeof(output), "ERROR offset %d 'Too much text' ::%s::", (int) slen, input + slen + 5);
580                                 continue;
581                         }
582
583                         len = radius_xlat_struct(output, sizeof(output), request, head, NULL, NULL);
584                         if (len < 0) {
585                                 snprintf(output, sizeof(output), "ERROR expanding xlat: %s", fr_strerror());
586                                 continue;
587                         }
588
589                         TALLOC_FREE(fmt); /* also frees 'head' */
590                         continue;
591                 }
592
593                 /*
594                  *      Look for "data".
595                  */
596                 if (strncmp(input, "data ", 5) == 0) {
597                         if (strcmp(input + 5, output) != 0) {
598                                 fprintf(stderr, "Mismatch at line %d of %s\n\tgot      : %s\n\texpected : %s\n",
599                                         lineno, filename, output, input + 5);
600                                 TALLOC_FREE(request);
601                                 return false;
602                         }
603                         continue;
604                 }
605
606                 fprintf(stderr, "Unknown keyword in %s[%d]\n", filename, lineno);
607                 TALLOC_FREE(request);
608                 return false;
609         }
610
611         TALLOC_FREE(request);
612         return true;
613 }
614
615
616 /*
617  *      The main guy.
618  */
619 int main(int argc, char *argv[])
620 {
621         int rcode = EXIT_SUCCESS;
622         int argval;
623         const char *input_file = NULL;
624         const char *output_file = NULL;
625         const char *filter_file = NULL;
626         FILE *fp;
627         REQUEST *request = NULL;
628         VALUE_PAIR *vp;
629         VALUE_PAIR *filter_vps = NULL;
630         bool xlat_only = false;
631         char *p;
632         fr_state_t *state = NULL;
633
634         fr_talloc_fault_setup();
635
636         /*
637          *      If the server was built with debugging enabled always install
638          *      the basic fatal signal handlers.
639          */
640 #ifndef NDEBUG
641         if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) {
642                 fr_perror("unittest");
643                 exit(EXIT_FAILURE);
644         }
645 #endif
646
647         p = strrchr(argv[0], FR_DIR_SEP);
648         if (!p) {
649                 main_config.name = argv[0];
650         } else {
651                 main_config.name = p + 1;
652         }
653
654         rad_debug_lvl = 0;
655         set_radius_dir(NULL, RADIUS_DIR);
656
657         /*
658          *      Ensure that the configuration is initialized.
659          */
660         memset(&main_config, 0, sizeof(main_config));
661         main_config.myip.af = AF_UNSPEC;
662         main_config.port = 0;
663         main_config.name = "radiusd";
664
665         /*
666          *      The tests should have only IPs, not host names.
667          */
668         fr_hostname_lookups = false;
669
670         /*
671          *      We always log to stdout.
672          */
673         fr_log_fp = stdout;
674         default_log.dst = L_DST_STDOUT;
675         default_log.fd = STDOUT_FILENO;
676
677         /*  Process the options.  */
678         while ((argval = getopt(argc, argv, "d:D:f:hi:mMn:o:O:xX")) != EOF) {
679
680                 switch (argval) {
681                         case 'd':
682                                 set_radius_dir(NULL, optarg);
683                                 break;
684
685                         case 'D':
686                                 main_config.dictionary_dir = talloc_typed_strdup(NULL, optarg);
687                                 break;
688
689                         case 'f':
690                                 filter_file = optarg;
691                                 break;
692
693                         case 'h':
694                                 usage(0);
695                                 break;
696
697                         case 'i':
698                                 input_file = optarg;
699                                 break;
700
701                         case 'm':
702                                 main_config.debug_memory = true;
703                                 break;
704
705                         case 'M':
706                                 memory_report = true;
707                                 main_config.debug_memory = true;
708                                 break;
709
710                         case 'n':
711                                 main_config.name = optarg;
712                                 break;
713
714                         case 'o':
715                                 output_file = optarg;
716                                 break;
717
718                         case 'O':
719                                 if (strcmp(optarg, "xlat_only") == 0) {
720                                         xlat_only = true;
721                                         break;
722                                 }
723
724                                 fprintf(stderr, "Unknown option '%s'\n", optarg);
725                                 exit(EXIT_FAILURE);
726
727                         case 'X':
728                                 rad_debug_lvl += 2;
729                                 main_config.log_auth = true;
730                                 main_config.log_auth_badpass = true;
731                                 main_config.log_auth_goodpass = true;
732                                 break;
733
734                         case 'x':
735                                 rad_debug_lvl++;
736                                 break;
737
738                         default:
739                                 usage(1);
740                                 break;
741                 }
742         }
743
744         if (rad_debug_lvl) version_print();
745         fr_debug_lvl = rad_debug_lvl;
746
747         /*
748          *      Mismatch between the binary and the libraries it depends on
749          */
750         if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
751                 fr_perror("radiusd");
752                 exit(EXIT_FAILURE);
753         }
754
755         /*
756          *  Initialising OpenSSL once, here, is safer than having individual modules do it.
757          */
758 #ifdef HAVE_OPENSSL_CRYPTO_H
759         tls_global_init();
760 #endif
761
762         if (xlat_register("poke", xlat_poke, NULL, NULL) < 0) {
763                 rcode = EXIT_FAILURE;
764                 goto finish;
765         }
766
767         /*  Read the configuration files, BEFORE doing anything else.  */
768         if (main_config_init() < 0) {
769                 rcode = EXIT_FAILURE;
770                 goto finish;
771         }
772
773         /*
774          *  Load the modules
775          */
776         if (modules_init(main_config.config) < 0) {
777                 rcode = EXIT_FAILURE;
778                 goto finish;
779         }
780
781         state =fr_state_init(NULL);
782
783         /*
784          *  Set the panic action (if required)
785          */
786         {
787                 char const *panic_action = NULL;
788
789                 panic_action = getenv("PANIC_ACTION");
790                 if (!panic_action) panic_action = main_config.panic_action;
791
792                 if (panic_action && (fr_fault_setup(panic_action, argv[0]) < 0)) {
793                         fr_perror("radiusd");
794                         exit(EXIT_FAILURE);
795                 }
796         }
797
798         setlinebuf(stdout); /* unbuffered output */
799
800         if (!input_file || (strcmp(input_file, "-") == 0)) {
801                 fp = stdin;
802         } else {
803                 fp = fopen(input_file, "r");
804                 if (!fp) {
805                         fprintf(stderr, "Failed reading %s: %s\n",
806                                 input_file, fr_syserror(errno));
807                         goto finish;
808                 }
809         }
810
811         /*
812          *      For simplicity, read xlat's.
813          */
814         if (xlat_only) {
815                 if (!do_xlats(input_file, fp)) rcode = EXIT_FAILURE;
816                 if (input_file) fclose(fp);
817                 goto finish;
818         }
819
820         /*
821          *      Grab the VPs from stdin, or from the file.
822          */
823         request = request_setup(fp);
824         if (!request) {
825                 fprintf(stderr, "Failed reading input: %s\n", fr_strerror());
826                 rcode = EXIT_FAILURE;
827                 goto finish;
828         }
829
830         /*
831          *      No filter file, OR there's no more input, OR we're
832          *      reading from a file, and it's different from the
833          *      filter file.
834          */
835         if (!filter_file || filedone ||
836             ((input_file != NULL) && (strcmp(filter_file, input_file) != 0))) {
837                 if (output_file) {
838                         fclose(fp);
839                         fp = NULL;
840                 }
841                 filedone = false;
842         }
843
844         /*
845          *      There is a filter file.  If necessary, open it.  If we
846          *      already are reading it via "input_file", then we don't
847          *      need to re-open it.
848          */
849         if (filter_file) {
850                 if (!fp) {
851                         fp = fopen(filter_file, "r");
852                         if (!fp) {
853                                 fprintf(stderr, "Failed reading %s: %s\n", filter_file, strerror(errno));
854                                 rcode = EXIT_FAILURE;
855                                 goto finish;
856                         }
857                 }
858
859
860                 if (fr_pair_list_afrom_file(request, &filter_vps, fp, &filedone) < 0) {
861                         fprintf(stderr, "Failed reading attributes from %s: %s\n",
862                                 filter_file, fr_strerror());
863                         rcode = EXIT_FAILURE;
864                         goto finish;
865                 }
866
867                 /*
868                  *      FIXME: loop over input packets.
869                  */
870                 fclose(fp);
871         }
872
873         rad_virtual_server(request);
874
875         if (!output_file || (strcmp(output_file, "-") == 0)) {
876                 fp = stdout;
877         } else {
878                 fp = fopen(output_file, "w");
879                 if (!fp) {
880                         fprintf(stderr, "Failed writing %s: %s\n",
881                                 output_file, fr_syserror(errno));
882                         exit(EXIT_FAILURE);
883                 }
884         }
885
886         print_packet(fp, request->reply);
887
888         if (output_file) fclose(fp);
889
890         /*
891          *      Update the list with the response type.
892          */
893         vp = radius_pair_create(request->reply, &request->reply->vps,
894                                PW_RESPONSE_PACKET_TYPE, 0);
895         vp->vp_integer = request->reply->code;
896
897         {
898                 VALUE_PAIR const *failed[2];
899
900                 if (filter_vps && !fr_pair_validate(failed, filter_vps, request->reply->vps)) {
901                         fr_pair_validate_debug(request, failed);
902                         fr_perror("Output file %s does not match attributes in filter %s (%s)",
903                                   output_file ? output_file : input_file, filter_file, fr_strerror());
904                         rcode = EXIT_FAILURE;
905                         goto finish;
906                 }
907         }
908
909         INFO("Exiting normally");
910
911 finish:
912         talloc_free(request);
913
914         /*
915          *      Detach any modules.
916          */
917         modules_free();
918
919         xlat_unregister("poke", xlat_poke, NULL);
920
921         xlat_free();            /* modules may have xlat's */
922
923         fr_state_delete(state);
924
925         /*
926          *      Free the configuration items.
927          */
928         main_config_free();
929
930         if (memory_report) {
931                 INFO("Allocated memory at time of report:");
932                 fr_log_talloc_report(NULL);
933         }
934
935         return rcode;
936 }
937
938
939 /*
940  *  Display the syntax for starting this program.
941  */
942 static void NEVER_RETURNS usage(int status)
943 {
944         FILE *output = status?stderr:stdout;
945
946         fprintf(output, "Usage: %s [options]\n", main_config.name);
947         fprintf(output, "Options:\n");
948         fprintf(output, "  -d raddb_dir  Configuration files are in \"raddb_dir/*\".\n");
949         fprintf(output, "  -D dict_dir   Dictionary files are in \"dict_dir/*\".\n");
950         fprintf(output, "  -f file       Filter reply against attributes in 'file'.\n");
951         fprintf(output, "  -h            Print this help message.\n");
952         fprintf(output, "  -i file       File containing request attributes.\n");
953         fprintf(output, "  -m            On SIGINT or SIGQUIT exit cleanly instead of immediately.\n");
954         fprintf(output, "  -n name       Read raddb/name.conf instead of raddb/radiusd.conf.\n");
955         fprintf(output, "  -X            Turn on full debugging.\n");
956         fprintf(output, "  -x            Turn on additional debugging. (-xx gives more debugging).\n");
957         exit(status);
958 }