2a6c59b391ea95d8d66686c9c1cbbae0dc54d860
[freeradius.git] / src / main / radattr.c
1 /*
2  * radattr.c    RADIUS Attribute debugging tool.
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 2010  Alan DeKok <aland@freeradius.org>
21  */
22
23 RCSID("$Id$")
24
25 #include <freeradius-devel/libradius.h>
26
27 typedef struct REQUEST REQUEST;
28
29 #include <freeradius-devel/parser.h>
30 #include <freeradius-devel/xlat.h>
31 #include <freeradius-devel/conf.h>
32 #include <freeradius-devel/radpaths.h>
33 #include <freeradius-devel/dhcp.h>
34
35 #include <ctype.h>
36
37 #ifdef HAVE_GETOPT_H
38 #       include <getopt.h>
39 #endif
40
41 #include <assert.h>
42
43 #include <freeradius-devel/log.h>
44 extern log_lvl_t rad_debug_lvl;
45
46 #include <sys/wait.h>
47 pid_t rad_fork(void);
48 pid_t rad_waitpid(pid_t pid, int *status);
49
50 pid_t rad_fork(void)
51 {
52         return fork();
53 }
54
55 pid_t rad_waitpid(pid_t pid, int *status)
56 {
57         return waitpid(pid, status, 0);
58 }
59
60 static ssize_t xlat_test(UNUSED void *instance, UNUSED REQUEST *request,
61                          UNUSED char const *fmt, UNUSED char *out, UNUSED size_t outlen)
62 {
63         return 0;
64 }
65
66 static RADIUS_PACKET my_original = {
67         .sockfd = -1,
68         .id = 0,
69         .code = PW_CODE_ACCESS_REQUEST,
70         .vector = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
71 };
72
73
74 static RADIUS_PACKET my_packet = {
75         .sockfd = -1,
76         .id = 0,
77         .code = PW_CODE_ACCESS_ACCEPT,
78         .vector = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
79 };
80
81
82 static char const *my_secret = "testing123";
83
84 /*
85  *      End of hacks for xlat
86  *
87  **********************************************************************/
88
89 static int encode_tlv(char *buffer, uint8_t *output, size_t outlen);
90
91 static char const hextab[] = "0123456789abcdef";
92
93 static int encode_data_string(char *buffer,
94                               uint8_t *output, size_t outlen)
95 {
96         int length = 0;
97         char *p;
98
99         p = buffer + 1;
100
101         while (*p && (outlen > 0)) {
102                 if (*p == '"') {
103                         return length;
104                 }
105
106                 if (*p != '\\') {
107                         *(output++) = *(p++);
108                         outlen--;
109                         length++;
110                         continue;
111                 }
112
113                 switch (p[1]) {
114                 default:
115                         *(output++) = p[1];
116                         break;
117
118                 case 'n':
119                         *(output++) = '\n';
120                         break;
121
122                 case 'r':
123                         *(output++) = '\r';
124                         break;
125
126                 case 't':
127                         *(output++) = '\t';
128                         break;
129                 }
130
131                 outlen--;
132                 length++;
133         }
134
135         fprintf(stderr, "String is not terminated\n");
136         return 0;
137 }
138
139 static int encode_data_tlv(char *buffer, char **endptr,
140                            uint8_t *output, size_t outlen)
141 {
142         int depth = 0;
143         int length;
144         char *p;
145
146         for (p = buffer; *p != '\0'; p++) {
147                 if (*p == '{') depth++;
148                 if (*p == '}') {
149                         depth--;
150                         if (depth == 0) break;
151                 }
152         }
153
154         if (*p != '}') {
155                 fprintf(stderr, "No trailing '}' in string starting "
156                         "with \"%s\"\n",
157                         buffer);
158                 return 0;
159         }
160
161         *endptr = p + 1;
162         *p = '\0';
163
164         p = buffer + 1;
165         while (isspace((int) *p)) p++;
166
167         length = encode_tlv(p, output, outlen);
168         if (length == 0) return 0;
169
170         return length;
171 }
172
173 static int encode_hex(char *p, uint8_t *output, size_t outlen)
174 {
175         int length = 0;
176         while (*p) {
177                 char *c1, *c2;
178
179                 while (isspace((int) *p)) p++;
180
181                 if (!*p) break;
182
183                 if(!(c1 = memchr(hextab, tolower((int) p[0]), 16)) ||
184                    !(c2 = memchr(hextab, tolower((int)  p[1]), 16))) {
185                         fprintf(stderr, "Invalid data starting at "
186                                 "\"%s\"\n", p);
187                         return 0;
188                 }
189
190                 *output = ((c1 - hextab) << 4) + (c2 - hextab);
191                 output++;
192                 length++;
193                 p += 2;
194
195                 outlen--;
196                 if (outlen == 0) {
197                         fprintf(stderr, "Too much data\n");
198                         return 0;
199                 }
200         }
201
202         return length;
203 }
204
205
206 static int encode_data(char *p, uint8_t *output, size_t outlen)
207 {
208         int length;
209
210         if (!isspace((int) *p)) {
211                 fprintf(stderr, "Invalid character following attribute "
212                         "definition\n");
213                 return 0;
214         }
215
216         while (isspace((int) *p)) p++;
217
218         if (*p == '{') {
219                 int sublen;
220                 char *q;
221
222                 length = 0;
223
224                 do {
225                         while (isspace((int) *p)) p++;
226                         if (!*p) {
227                                 if (length == 0) {
228                                         fprintf(stderr, "No data\n");
229                                         return 0;
230                                 }
231
232                                 break;
233                         }
234
235                         sublen = encode_data_tlv(p, &q, output, outlen);
236                         if (sublen == 0) return 0;
237
238                         length += sublen;
239                         output += sublen;
240                         outlen -= sublen;
241                         p = q;
242                 } while (*q);
243
244                 return length;
245         }
246
247         if (*p == '"') {
248                 length = encode_data_string(p, output, outlen);
249                 return length;
250         }
251
252         length = encode_hex(p, output, outlen);
253
254         if (length == 0) {
255                 fprintf(stderr, "Empty string\n");
256                 return 0;
257         }
258
259         return length;
260 }
261
262 static int decode_attr(char *buffer, char **endptr)
263 {
264         long attr;
265
266         attr = strtol(buffer, endptr, 10);
267         if (*endptr == buffer) {
268                 fprintf(stderr, "No valid number found in string "
269                         "starting with \"%s\"\n", buffer);
270                 return 0;
271         }
272
273         if (!**endptr) {
274                 fprintf(stderr, "Nothing follows attribute number\n");
275                 return 0;
276         }
277
278         if ((attr <= 0) || (attr > 256)) {
279                 fprintf(stderr, "Attribute number is out of valid "
280                         "range\n");
281                 return 0;
282         }
283
284         return (int) attr;
285 }
286
287 static int decode_vendor(char *buffer, char **endptr)
288 {
289         long vendor;
290
291         if (*buffer != '.') {
292                 fprintf(stderr, "Invalid separator before vendor id\n");
293                 return 0;
294         }
295
296         vendor = strtol(buffer + 1, endptr, 10);
297         if (*endptr == (buffer + 1)) {
298                 fprintf(stderr, "No valid vendor number found\n");
299                 return 0;
300         }
301
302         if (!**endptr) {
303                 fprintf(stderr, "Nothing follows vendor number\n");
304                 return 0;
305         }
306
307         if ((vendor <= 0) || (vendor > (1 << 24))) {
308                 fprintf(stderr, "Vendor number is out of valid range\n");
309                 return 0;
310         }
311
312         if (**endptr != '.') {
313                 fprintf(stderr, "Invalid data following vendor number\n");
314                 return 0;
315         }
316         (*endptr)++;
317
318         return (int) vendor;
319 }
320
321 static int encode_tlv(char *buffer, uint8_t *output, size_t outlen)
322 {
323         int attr;
324         int length;
325         char *p;
326
327         attr = decode_attr(buffer, &p);
328         if (attr == 0) return 0;
329
330         output[0] = attr;
331         output[1] = 2;
332
333         if (*p == '.') {
334                 p++;
335                 length = encode_tlv(p, output + 2, outlen - 2);
336
337         } else {
338                 length = encode_data(p, output + 2, outlen - 2);
339         }
340
341         if (length == 0) return 0;
342         if (length > (255 - 2)) {
343                 fprintf(stderr, "TLV data is too long\n");
344                 return 0;
345         }
346
347         output[1] += length;
348
349         return length + 2;
350 }
351
352 static int encode_vsa(char *buffer, uint8_t *output, size_t outlen)
353 {
354         int vendor;
355         int length;
356         char *p;
357
358         vendor = decode_vendor(buffer, &p);
359         if (vendor == 0) return 0;
360
361         output[0] = 0;
362         output[1] = (vendor >> 16) & 0xff;
363         output[2] = (vendor >> 8) & 0xff;
364         output[3] = vendor & 0xff;
365
366         length = encode_tlv(p, output + 4, outlen - 4);
367         if (length == 0) return 0;
368         if (length > (255 - 6)) {
369                 fprintf(stderr, "VSA data is too long\n");
370                 return 0;
371         }
372
373
374         return length + 4;
375 }
376
377 static int encode_evs(char *buffer, uint8_t *output, size_t outlen)
378 {
379         int vendor;
380         int attr;
381         int length;
382         char *p;
383
384         vendor = decode_vendor(buffer, &p);
385         if (vendor == 0) return 0;
386
387         attr = decode_attr(p, &p);
388         if (attr == 0) return 0;
389
390         output[0] = 0;
391         output[1] = (vendor >> 16) & 0xff;
392         output[2] = (vendor >> 8) & 0xff;
393         output[3] = vendor & 0xff;
394         output[4] = attr;
395
396         length = encode_data(p, output + 5, outlen - 5);
397         if (length == 0) return 0;
398
399         return length + 5;
400 }
401
402 static int encode_extended(char *buffer,
403                            uint8_t *output, size_t outlen)
404 {
405         int attr;
406         int length;
407         char *p;
408
409         attr = decode_attr(buffer, &p);
410         if (attr == 0) return 0;
411
412         output[0] = attr;
413
414         if (attr == 26) {
415                 length = encode_evs(p, output + 1, outlen - 1);
416         } else {
417                 length = encode_data(p, output + 1, outlen - 1);
418         }
419         if (length == 0) return 0;
420         if (length > (255 - 3)) {
421                 fprintf(stderr, "Extended Attr data is too long\n");
422                 return 0;
423         }
424
425         return length + 1;
426 }
427
428 static int encode_long_extended(char *buffer,
429                                  uint8_t *output, size_t outlen)
430 {
431         int attr;
432         int length, total;
433         char *p;
434
435         attr = decode_attr(buffer, &p);
436         if (attr == 0) return 0;
437
438         /* output[0] is the extended attribute */
439         output[1] = 4;
440         output[2] = attr;
441         output[3] = 0;
442
443         if (attr == 26) {
444                 length = encode_evs(p, output + 4, outlen - 4);
445                 if (length == 0) return 0;
446
447                 output[1] += 5;
448                 length -= 5;
449         } else {
450                 length = encode_data(p, output + 4, outlen - 4);
451         }
452         if (length == 0) return 0;
453
454         total = 0;
455         while (1) {
456                 int sublen = 255 - output[1];
457
458                 if (length <= sublen) {
459                         output[1] += length;
460                         total += output[1];
461                         break;
462                 }
463
464                 length -= sublen;
465
466                 memmove(output + 255 + 4, output + 255, length);
467                 memcpy(output + 255, output, 4);
468
469                 output[1] = 255;
470                 output[3] |= 0x80;
471
472                 output += 255;
473                 output[1] = 4;
474                 total += 255;
475         }
476
477         return total;
478 }
479
480 static int encode_rfc(char *buffer, uint8_t *output, size_t outlen)
481 {
482         int attr;
483         int length, sublen;
484         char *p;
485
486         attr = decode_attr(buffer, &p);
487         if (attr == 0) return 0;
488
489         length = 2;
490         output[0] = attr;
491         output[1] = 2;
492
493         if (attr == 26) {
494                 sublen = encode_vsa(p, output + 2, outlen - 2);
495
496         } else if ((attr < 241) || (attr > 246)) {
497                 sublen = encode_data(p, output + 2, outlen - 2);
498
499         } else {
500                 if (*p != '.') {
501                         fprintf(stderr, "Invalid data following "
502                                 "attribute number\n");
503                         return 0;
504                 }
505
506                 if (attr < 245) {
507                         sublen = encode_extended(p + 1,
508                                                  output + 2, outlen - 2);
509                 } else {
510
511                         /*
512                          *      Not like the others!
513                          */
514                         return encode_long_extended(p + 1, output, outlen);
515                 }
516         }
517         if (sublen == 0) return 0;
518         if (sublen > (255 -2)) {
519                 fprintf(stderr, "RFC Data is too long\n");
520                 return 0;
521         }
522
523         output[1] += sublen;
524         return length + sublen;
525 }
526
527 static void parse_condition(char const *input, char *output, size_t outlen)
528 {
529         ssize_t slen;
530         char const *error = NULL;
531         fr_cond_t *cond;
532
533         slen = fr_condition_tokenize(NULL, NULL, input, &cond, &error, FR_COND_ONE_PASS);
534         if (slen <= 0) {
535                 snprintf(output, outlen, "ERROR offset %d %s", (int) -slen, error);
536                 return;
537         }
538
539         input += slen;
540         if (*input != '\0') {
541                 talloc_free(cond);
542                 snprintf(output, outlen, "ERROR offset %d 'Too much text'", (int) slen);
543                 return;
544         }
545
546         fr_cond_sprint(output, outlen, cond);
547
548         talloc_free(cond);
549 }
550
551 static void parse_xlat(char const *input, char *output, size_t outlen)
552 {
553         ssize_t slen;
554         char const *error = NULL;
555         char *fmt = talloc_typed_strdup(NULL, input);
556         xlat_exp_t *head;
557
558         slen = xlat_tokenize(fmt, fmt, &head, &error);
559         if (slen <= 0) {
560                 snprintf(output, outlen, "ERROR offset %d '%s'", (int) -slen, error);
561                 return;
562         }
563
564         if (input[slen] != '\0') {
565                 snprintf(output, outlen, "ERROR offset %d 'Too much text'", (int) slen);
566                 return;
567         }
568
569         xlat_sprint(output, outlen, head);
570         talloc_free(fmt);
571 }
572
573 static void process_file(const char *root_dir, char const *filename)
574 {
575         int lineno;
576         size_t i, outlen;
577         ssize_t len, data_len;
578         FILE *fp;
579         char input[8192], buffer[8192];
580         char output[8192];
581         char directory[8192];
582         uint8_t *attr, data[2048];
583
584         if (strcmp(filename, "-") == 0) {
585                 fp = stdin;
586                 directory[0] = '\0';
587
588         } else {
589                 if (root_dir && *root_dir) {
590                         snprintf(directory, sizeof(directory), "%s/%s", root_dir, filename);
591                 } else {
592                         strlcpy(directory, filename, sizeof(directory));
593                 }
594
595                 fp = fopen(directory, "r");
596                 if (!fp) {
597                         fprintf(stderr, "Error opening %s: %s\n",
598                                 directory, fr_syserror(errno));
599                         exit(1);
600                 }
601
602                 filename = directory;
603         }
604
605         lineno = 0;
606         *output = '\0';
607         data_len = 0;
608
609         while (fgets(buffer, sizeof(buffer), fp) != NULL) {
610                 char *p = strchr(buffer, '\n');
611                 VALUE_PAIR *vp, *head;
612                 VALUE_PAIR **tail = &head;
613
614                 lineno++;
615                 head = NULL;
616
617                 if (!p) {
618                         if (!feof(fp)) {
619                                 fprintf(stderr, "Line %d too long in %s\n",
620                                         lineno, directory);
621                                 exit(1);
622                         }
623                 } else {
624                         *p = '\0';
625                 }
626
627                 /*
628                  *      Comments, with hacks for User-Name[#]
629                  */
630                 p = strchr(buffer, '#');
631                 if (p && ((p == buffer) ||
632                           ((p > buffer) && (p[-1] != '[')))) *p = '\0';
633
634                 p = buffer;
635                 while (isspace((int) *p)) p++;
636                 if (!*p) continue;
637
638                 DEBUG2("%s[%d]: %s\n", filename, lineno, buffer);
639
640                 strlcpy(input, p, sizeof(input));
641
642                 if (strncmp(p, "raw ", 4) == 0) {
643                         outlen = encode_rfc(p + 4, data, sizeof(data));
644                         if (outlen == 0) {
645                                 fprintf(stderr, "Parse error in line %d of %s\n",
646                                         lineno, directory);
647                                 exit(1);
648                         }
649
650                 print_hex:
651                         if (outlen == 0) {
652                                 output[0] = 0;
653                                 continue;
654                         }
655
656                         if (outlen > sizeof(data)) outlen = sizeof(data);
657
658                         if (outlen >= (sizeof(output) / 2)) {
659                                 outlen = (sizeof(output) / 2) - 1;
660                         }
661
662                         data_len = outlen;
663                         for (i = 0; i < outlen; i++) {
664                                 if (sizeof(output) < (3*i)) break;
665
666                                 snprintf(output + 3*i, sizeof(output) - (3*i) - 1,
667                                          "%02x ", data[i]);
668                         }
669                         outlen = strlen(output);
670                         output[outlen - 1] = '\0';
671                         continue;
672                 }
673
674                 if (strncmp(p, "data ", 5) == 0) {
675                         if (strcmp(p + 5, output) != 0) {
676                                 fprintf(stderr, "Mismatch at line %d of %s\n\tgot      : %s\n\texpected : %s\n",
677                                         lineno, directory, output, p + 5);
678                                 exit(1);
679                         }
680                         continue;
681                 }
682
683                 if (strncmp(p, "encode ", 7) == 0) {
684                         if (strcmp(p + 7, "-") == 0) {
685                                 p = output;
686                         } else {
687                                 p += 7;
688                         }
689
690                         if (fr_pair_list_afrom_str(NULL, p, &head) != T_EOL) {
691                                 strlcpy(output, fr_strerror(), sizeof(output));
692                                 continue;
693                         }
694
695                         attr = data;
696                         vp = head;
697                         while (vp) {
698                                 len = rad_vp2attr(&my_packet, &my_original, my_secret, (VALUE_PAIR const **)(void **)&vp,
699                                                   attr, data + sizeof(data) - attr);
700                                 if (len < 0) {
701                                         fprintf(stderr, "Failed encoding %s: %s\n",
702                                                 vp->da->name, fr_strerror());
703                                         exit(1);
704                                 }
705
706                                 attr += len;
707                                 if (len == 0) break;
708                         }
709
710                         fr_pair_list_free(&head);
711                         outlen = attr - data;
712                         goto print_hex;
713                 }
714
715                 if (strncmp(p, "decode ", 7) == 0) {
716                         ssize_t my_len;
717
718                         if (strcmp(p + 7, "-") == 0) {
719                                 attr = data;
720                                 len = data_len;
721                         } else {
722                                 attr = data;
723                                 len = encode_hex(p + 7, data, sizeof(data));
724                                 if (len == 0) {
725                                         fprintf(stderr, "Failed decoding hex string at line %d of %s\n", lineno, directory);
726                                         exit(1);
727                                 }
728                         }
729
730                         my_len = 0;
731                         while (len > 0) {
732                                 vp = NULL;
733                                 my_len = rad_attr2vp(NULL, &my_packet, &my_original, my_secret, attr, len, &vp);
734                                 if (my_len < 0) {
735                                         fr_pair_list_free(&head);
736                                         break;
737                                 }
738
739                                 if (my_len > len) {
740                                         fprintf(stderr, "Internal sanity check failed at %d\n", __LINE__);
741                                         exit(1);
742                                 }
743
744                                 *tail = vp;
745                                 while (vp) {
746                                         tail = &(vp->next);
747                                         vp = vp->next;
748                                 }
749
750                                 attr += my_len;
751                                 len -= my_len;
752                         }
753
754                         /*
755                          *      Output may be an error, and we ignore
756                          *      it if so.
757                          */
758                         if (head) {
759                                 vp_cursor_t cursor;
760                                 p = output;
761                                 for (vp = fr_cursor_init(&cursor, &head);
762                                      vp;
763                                      vp = fr_cursor_next(&cursor)) {
764                                         vp_prints(p, sizeof(output) - (p - output), vp);
765                                         p += strlen(p);
766
767                                         if (vp->next) {strcpy(p, ", ");
768                                                 p += 2;
769                                         }
770                                 }
771
772                                 fr_pair_list_free(&head);
773                         } else if (my_len < 0) {
774                                 strlcpy(output, fr_strerror(), sizeof(output));
775
776                         } else { /* zero-length attribute */
777                                 *output = '\0';
778                         }
779                         continue;
780                 }
781
782                 /*
783                  *      And some DHCP tests
784                  */
785                 if (strncmp(p, "encode-dhcp ", 12) == 0) {
786                         vp_cursor_t cursor;
787
788                         if (strcmp(p + 12, "-") == 0) {
789                                 p = output;
790                         } else {
791                                 p += 12;
792                         }
793
794                         if (fr_pair_list_afrom_str(NULL, p, &head) != T_EOL) {
795                                 strlcpy(output, fr_strerror(), sizeof(output));
796                                 continue;
797                         }
798
799                         fr_cursor_init(&cursor, &head);
800
801
802                         attr = data;
803                         vp = head;
804
805                         while ((vp = fr_cursor_current(&cursor))) {
806                                 len = fr_dhcp_encode_option(NULL, attr, data + sizeof(data) - attr, &cursor);
807                                 if (len < 0) {
808                                         fprintf(stderr, "Failed encoding %s: %s\n",
809                                                 vp->da->name, fr_strerror());
810                                         exit(1);
811                                 }
812                                 attr += len;
813                         };
814
815                         fr_pair_list_free(&head);
816                         outlen = attr - data;
817                         goto print_hex;
818                 }
819
820                 if (strncmp(p, "decode-dhcp ", 12) == 0) {
821                         ssize_t my_len;
822
823                         if (strcmp(p + 12, "-") == 0) {
824                                 attr = data;
825                                 len = data_len;
826                         } else {
827                                 attr = data;
828                                 len = encode_hex(p + 12, data, sizeof(data));
829                                 if (len == 0) {
830                                         fprintf(stderr, "Failed decoding hex string at line %d of %s\n", lineno, directory);
831                                         exit(1);
832                                 }
833                         }
834
835                         my_len = fr_dhcp_decode_options(NULL, &head, attr, len);
836
837                         /*
838                          *      Output may be an error, and we ignore
839                          *      it if so.
840                          */
841                         if (head) {
842                                 vp_cursor_t cursor;
843                                 p = output;
844                                 for (vp = fr_cursor_init(&cursor, &head);
845                                      vp;
846                                      vp = fr_cursor_next(&cursor)) {
847                                         vp_prints(p, sizeof(output) - (p - output), vp);
848                                         p += strlen(p);
849
850                                         if (vp->next) {strcpy(p, ", ");
851                                                 p += 2;
852                                         }
853                                 }
854
855                                 fr_pair_list_free(&head);
856                         } else if (my_len < 0) {
857                                 strlcpy(output, fr_strerror(), sizeof(output));
858
859                         } else { /* zero-length attribute */
860                                 *output = '\0';
861                         }
862                         continue;
863                 }
864
865                 if (strncmp(p, "attribute ", 10) == 0) {
866                         p += 10;
867
868                         if (fr_pair_list_afrom_str(NULL, p, &head) != T_EOL) {
869                                 strlcpy(output, fr_strerror(), sizeof(output));
870                                 continue;
871                         }
872
873                         vp_prints(output, sizeof(output), head);
874                         continue;
875                 }
876
877                 if (strncmp(p, "$INCLUDE ", 9) == 0) {
878                         char *q;
879
880                         p += 9;
881                         while (isspace((int) *p)) p++;
882
883                         q = strrchr(directory, '/');
884                         if (q) {
885                                 *q = '\0';
886                                 process_file(directory, p);
887                                 *q = '/';
888                         } else {
889                                 process_file(NULL, p);
890                         }
891                         continue;
892                 }
893
894                 if (strncmp(p, "condition ", 10) == 0) {
895                         p += 10;
896                         parse_condition(p, output, sizeof(output));
897                         continue;
898                 }
899
900                 if (strncmp(p, "xlat ", 5) == 0) {
901                         p += 5;
902                         parse_xlat(p, output, sizeof(output));
903                         continue;
904                 }
905
906                 fprintf(stderr, "Unknown input at line %d of %s\n",
907                         lineno, directory);
908                 exit(1);
909         }
910
911         if (fp != stdin) fclose(fp);
912 }
913
914 static void NEVER_RETURNS usage(void)
915 {
916         fprintf(stderr, "usage: radattr [OPTS] filename\n");
917         fprintf(stderr, "  -d <raddb>             Set user dictionary directory (defaults to " RADDBDIR ").\n");
918         fprintf(stderr, "  -D <dictdir>           Set main dictionary directory (defaults to " DICTDIR ").\n");
919         fprintf(stderr, "  -x                     Debugging mode.\n");
920         fprintf(stderr, "  -M                     Show talloc memory report.\n");
921
922         exit(1);
923 }
924
925 int main(int argc, char *argv[])
926 {
927         int c;
928         bool report = false;
929         char const *radius_dir = RADDBDIR;
930         char const *dict_dir = DICTDIR;
931         int *inst = &c;
932
933         cf_new_escape = true;   /* fix the tests */
934
935 #ifndef NDEBUG
936         if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) {
937                 fr_perror("radattr");
938                 exit(EXIT_FAILURE);
939         }
940 #endif
941
942         while ((c = getopt(argc, argv, "d:D:xMh")) != EOF) switch (c) {
943                 case 'd':
944                         radius_dir = optarg;
945                         break;
946                 case 'D':
947                         dict_dir = optarg;
948                         break;
949                 case 'x':
950                         fr_debug_lvl++;
951                         rad_debug_lvl = fr_debug_lvl;
952                         break;
953                 case 'M':
954                         report = true;
955                         break;
956                 case 'h':
957                 default:
958                         usage();
959         }
960         argc -= (optind - 1);
961         argv += (optind - 1);
962
963         /*
964          *      Mismatch between the binary and the libraries it depends on
965          */
966         if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
967                 fr_perror("radattr");
968                 return 1;
969         }
970
971         if (dict_init(dict_dir, RADIUS_DICTIONARY) < 0) {
972                 fr_perror("radattr");
973                 return 1;
974         }
975
976         if (dict_read(radius_dir, RADIUS_DICTIONARY) == -1) {
977                 fr_perror("radattr");
978                 return 1;
979         }
980
981         if (xlat_register("test", xlat_test, NULL, inst) < 0) {
982                 fprintf(stderr, "Failed registering xlat");
983                 return 1;
984         }
985
986         if (argc < 2) {
987                 process_file(NULL, "-");
988
989         } else {
990                 process_file(NULL, argv[1]);
991         }
992
993         if (report) {
994                 dict_free();
995                 fr_log_talloc_report(NULL);
996         }
997
998         return 0;
999 }