]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/ipfilter/parse.c
This commit was generated by cvs2svn to compensate for changes in r52746,
[FreeBSD/FreeBSD.git] / contrib / ipfilter / parse.c
1 /*
2  * Copyright (C) 1993-1997 by Darren Reed.
3  *
4  * Redistribution and use in source and binary forms are permitted
5  * provided that this notice is preserved and due credit is given
6  * to the original author and the contributors.
7  */
8 #include <stdio.h>
9 #include <string.h>
10 #include <sys/types.h>
11 #if !defined(__SVR4) && !defined(__svr4__)
12 #include <strings.h>
13 #else
14 #include <sys/byteorder.h>
15 #endif
16 #include <sys/param.h>
17 #include <sys/time.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <stddef.h>
21 #include <sys/socket.h>
22 #include <netinet/in.h>
23 #include <netinet/in_systm.h>
24 #include <netinet/ip.h>
25 #include <netinet/tcp.h>
26 #include <net/if.h>
27 #include <netdb.h>
28 #include <arpa/nameser.h>
29 #include <arpa/inet.h>
30 #include <resolv.h>
31 #include <ctype.h>
32 #include "ip_compat.h"
33 #include "ip_fil.h"
34 #include "ipf.h"
35
36 #if !defined(lint)
37 static const char sccsid[] ="@(#)parse.c        1.44 6/5/96 (C) 1993-1996 Darren Reed";
38 static const char rcsid[] = "@(#)$Id: parse.c,v 2.0.2.18.2.5 1998/05/23 19:20:33 darrenr Exp $";
39 #endif
40
41 extern  struct  ipopt_names     ionames[], secclass[];
42 extern  int     opts;
43
44 u_short portnum __P((char *));
45 u_char  tcp_flags __P((char *, u_char *));
46 int     addicmp __P((char ***, struct frentry *));
47 int     extras __P((char ***, struct frentry *));
48 char    ***seg;
49 u_long  *sa, *msk;
50 u_short *pp, *tp;
51 u_char  *cp;
52
53 int     hostmask __P((char ***, u_32_t *, u_32_t *, u_short *, u_char *,
54                       u_short *));
55 int     ports __P((char ***, u_short *, u_char *, u_short *));
56 int     icmpcode __P((char *)), addkeep __P((char ***, struct frentry *));
57 int     to_interface __P((frdest_t *, char *));
58 void    print_toif __P((char *, frdest_t *));
59 void    optprint __P((u_short, u_short, u_long, u_long));
60 int     countbits __P((u_32_t));
61 char    *portname __P((int, int));
62
63
64 char    *proto = NULL;
65 char    flagset[] = "FSRPAU";
66 u_char  flags[] = { TH_FIN, TH_SYN, TH_RST, TH_PUSH, TH_ACK, TH_URG };
67
68 static  char    thishost[64];
69
70
71 void initparse()
72 {
73         gethostname(thishost, sizeof(thishost));
74         thishost[sizeof(thishost) - 1] = '\0';
75 }
76
77
78 /* parse()
79  *
80  * parse a line read from the input filter rule file
81  */
82 struct  frentry *parse(line)
83 char    *line;
84 {
85         static  struct  frentry fil;
86         struct  protoent        *p = NULL;
87         char    *cps[31], **cpp;
88         u_char  ch;
89         int     i, cnt = 1;
90
91         while (*line && isspace(*line))
92                 line++;
93         if (!*line)
94                 return NULL;
95
96         bzero((char *)&fil, sizeof(fil));
97         fil.fr_mip.fi_v = 0xf;
98         fil.fr_ip.fi_v = 4;
99         /*
100          * break line up into max of 20 segments
101          */
102         if (opts & OPT_DEBUG)
103                 fprintf(stderr, "parse [%s]\n", line);
104         for (i = 0, *cps = strtok(line, " \b\t\r\n"); cps[i] && i < 30; cnt++)
105                 cps[++i] = strtok(NULL, " \b\t\r\n");
106         cps[i] = NULL;
107
108         if (cnt < 3) {
109                 (void)fprintf(stderr,"not enough segments in line\n");
110                 return NULL;
111         }
112
113         cpp = cps;
114         if (**cpp == '@')
115                 fil.fr_hits = (U_QUAD_T)atoi(*cpp++ + 1) + 1;
116
117
118         if (!strcasecmp("block", *cpp)) {
119                 fil.fr_flags |= FR_BLOCK;
120                 if (!strncasecmp(*(cpp+1), "return-icmp", 11)) {
121                         fil.fr_flags |= FR_RETICMP;
122                         cpp++;
123                         if (*(*cpp + 11) == '(') {
124                                 i = icmpcode(*cpp + 12);
125                                 if (i == -1) {
126                                         fprintf(stderr,
127                                                 "uncrecognised icmp code %s\n",
128                                                 *cpp + 12);
129                                         return NULL;
130                                 }
131                                 fil.fr_icode = i;
132                         }
133                 } else if (!strncasecmp(*(cpp+1), "return-rst", 10)) {
134                         fil.fr_flags |= FR_RETRST;
135                         cpp++;
136                 }
137         } else if (!strcasecmp("count", *cpp)) {
138                 fil.fr_flags |= FR_ACCOUNT;
139         } else if (!strcasecmp("pass", *cpp)) {
140                 fil.fr_flags |= FR_PASS;
141         } else if (!strcasecmp("auth", *cpp)) {
142                  fil.fr_flags |= FR_AUTH;
143         } else if (!strcasecmp("preauth", *cpp)) {
144                  fil.fr_flags |= FR_PREAUTH;
145         } else if (!strcasecmp("skip", *cpp)) {
146                 cpp++;
147                 if (!isdigit(**cpp)) {
148                         (void)fprintf(stderr, "integer must follow skip\n");
149                         return NULL;
150                 }
151                 fil.fr_skip = atoi(*cpp);
152         } else if (!strcasecmp("log", *cpp)) {
153                 fil.fr_flags |= FR_LOG;
154                 if (!strcasecmp(*(cpp+1), "body")) {
155                         fil.fr_flags |= FR_LOGBODY;
156                         cpp++;
157                 }
158                 if (!strcasecmp(*(cpp+1), "first")) {
159                         fil.fr_flags |= FR_LOGFIRST;
160                         cpp++;
161                 }
162         } else {
163                 /*
164                  * Doesn't start with one of the action words
165                  */
166                 (void)fprintf(stderr, "unknown keyword (%s)\n", *cpp);
167                 return NULL;
168         }
169         cpp++;
170
171         if (!strcasecmp("in", *cpp))
172                 fil.fr_flags |= FR_INQUE;
173         else if (!strcasecmp("out", *cpp)) {
174                 fil.fr_flags |= FR_OUTQUE;
175                 if (fil.fr_flags & FR_RETICMP) {
176                         (void)fprintf(stderr,
177                                 "Can only use return-icmp with 'in'\n");
178                         return NULL;
179                 } else if (fil.fr_flags & FR_RETRST) {
180                         (void)fprintf(stderr,
181                                 "Can only use return-rst with 'in'\n");
182                         return NULL;
183                 }
184         } else {
185                 (void)fprintf(stderr,
186                         "missing 'in'/'out' keyword (%s)\n", *cpp);
187                 return NULL;
188         }
189         if (!*++cpp)
190                 return NULL;
191
192         if (!strcasecmp("log", *cpp)) {
193                 cpp++;
194                 if (fil.fr_flags & FR_PASS)
195                         fil.fr_flags |= FR_LOGP;
196                 else if (fil.fr_flags & FR_BLOCK)
197                         fil.fr_flags |= FR_LOGB;
198                 if (!strcasecmp(*cpp, "body")) {
199                         fil.fr_flags |= FR_LOGBODY;
200                         cpp++;
201                 }
202                 if (!strcasecmp(*cpp, "first")) {
203                         fil.fr_flags |= FR_LOGFIRST;
204                         cpp++;
205                 }
206                 if (!strcasecmp(*cpp, "or-block")) {
207                         if (!(fil.fr_flags & FR_PASS)) {
208                                 (void)fprintf(stderr,
209                                         "or-block must be used with pass\n");
210                                 return NULL;
211                         }
212                         fil.fr_flags |= FR_LOGORBLOCK;
213                         cpp++;
214                 }
215         }
216
217         if (!strcasecmp("quick", *cpp)) {
218                 cpp++;
219                 fil.fr_flags |= FR_QUICK;
220         }
221
222         *fil.fr_ifname = '\0';
223         if (*cpp && !strcasecmp(*cpp, "on")) {
224                 if (!*++cpp) {
225                         (void)fprintf(stderr, "interface name missing\n");
226                         return NULL;
227                 }
228                 (void)strncpy(fil.fr_ifname, *cpp, IFNAMSIZ-1);
229                 fil.fr_ifname[IFNAMSIZ-1] = '\0';
230                 cpp++;
231                 if (!*cpp) {
232                         if (fil.fr_flags & FR_RETRST) {
233                                 (void)fprintf(stderr,
234                                         "%s can only be used with TCP\n",
235                                         "return-rst");
236                                 return NULL;
237                         }
238                         return &fil;
239                 }
240
241                 if (*cpp) {
242                         if (!strcasecmp(*cpp, "dup-to") && *(cpp + 1)) {
243                                 cpp++;
244                                 if (to_interface(&fil.fr_dif, *cpp))
245                                         return NULL;
246                                 cpp++;
247                         }
248                         if (!strcasecmp(*cpp, "to") && *(cpp + 1)) {
249                                 cpp++;
250                                 if (to_interface(&fil.fr_tif, *cpp))
251                                         return NULL;
252                                 cpp++;
253                         } else if (!strcasecmp(*cpp, "fastroute")) {
254                                 fil.fr_flags |= FR_FASTROUTE;
255                                 cpp++;
256                         }
257                 }
258         }
259         if (*cpp && !strcasecmp(*cpp, "tos")) {
260                 if (!*++cpp) {
261                         (void)fprintf(stderr, "tos missing value\n");
262                         return NULL;
263                 }
264                 fil.fr_tos = strtol(*cpp, NULL, 0);
265                 fil.fr_mip.fi_tos = 0xff;
266                 cpp++;
267         }
268
269         if (*cpp && !strcasecmp(*cpp, "ttl")) {
270                 if (!*++cpp) {
271                         (void)fprintf(stderr, "ttl missing hopcount value\n");
272                         return NULL;
273                 }
274                 fil.fr_ttl = atoi(*cpp);
275                 fil.fr_mip.fi_ttl = 0xff;
276                 cpp++;
277         }
278
279         /*
280          * check for "proto <protoname>" only decode udp/tcp/icmp as protoname
281          */
282         proto = NULL;
283         if (*cpp && !strcasecmp(*cpp, "proto")) {
284                 if (!*++cpp) {
285                         (void)fprintf(stderr, "protocol name missing\n");
286                         return NULL;
287                 }
288                 if (!strcasecmp(*cpp, "tcp/udp")) {
289                         fil.fr_ip.fi_fl |= FI_TCPUDP;
290                         fil.fr_mip.fi_fl |= FI_TCPUDP;
291                 } else {
292                         if (!(p = getprotobyname(*cpp)) && !isdigit(**cpp)) {
293                                 (void)fprintf(stderr,
294                                         "unknown protocol (%s)\n", *cpp);
295                                 return NULL;
296                         }
297                         if (p)
298                                 fil.fr_proto = p->p_proto;
299                         else if (isdigit(**cpp))
300                                 fil.fr_proto = atoi(*cpp);
301                         fil.fr_mip.fi_p = 0xff;
302                 }
303                 proto = *cpp;
304                 if (fil.fr_proto != IPPROTO_TCP && fil.fr_flags & FR_RETRST) {
305                         (void)fprintf(stderr,
306                                 "%s can only be used with TCP\n",
307                                 "return-rst");
308                         return NULL;
309                 }
310                 if (!*++cpp)
311                         return &fil;
312         }
313         if (fil.fr_proto != IPPROTO_TCP && fil.fr_flags & FR_RETRST) {
314                 (void)fprintf(stderr, "%s can only be used with TCP\n",
315                         "return-rst");
316                 return NULL;
317         }
318
319         /*
320          * get the from host and bit mask to use against packets
321          */
322
323         if (!*cpp) {
324                 fprintf(stderr, "missing source specification\n");
325                 return NULL;
326         }
327         if (!strcasecmp(*cpp, "all")) {
328                 cpp++;
329                 if (!*cpp)
330                         return &fil;
331         } else {
332                 if (strcasecmp(*cpp, "from")) {
333                         (void)fprintf(stderr,
334                                 "unexpected keyword (%s) - from\n", *cpp);
335                         return NULL;
336                 }
337                 if (!*++cpp) {
338                         (void)fprintf(stderr, "missing host after from\n");
339                         return NULL;
340                 }
341                 ch = 0;
342                 if (**cpp == '!') {
343                         fil.fr_flags |= FR_NOTSRCIP;
344                         (*cpp)++;
345                 }
346                 if (hostmask(&cpp, (u_32_t *)&fil.fr_src,
347                              (u_32_t *)&fil.fr_smsk, &fil.fr_sport, &ch,
348                              &fil.fr_stop)) {
349                         (void)fprintf(stderr, "bad host (%s)\n", *cpp);
350                         return NULL;
351                 }
352                 fil.fr_scmp = ch;
353                 if (!*cpp) {
354                         (void)fprintf(stderr, "missing to fields\n");
355                         return NULL;
356                 }
357
358                 /*
359                  * do the same for the to field (destination host)
360                  */
361                 if (strcasecmp(*cpp, "to")) {
362                         (void)fprintf(stderr,
363                                 "unexpected keyword (%s) - to\n", *cpp);
364                         return NULL;
365                 }
366                 if (!*++cpp) {
367                         (void)fprintf(stderr, "missing host after to\n");
368                         return NULL;
369                 }
370                 ch = 0;
371                 if (**cpp == '!') {
372                         fil.fr_flags |= FR_NOTDSTIP;
373                         (*cpp)++;
374                 }
375                 if (hostmask(&cpp, (u_32_t *)&fil.fr_dst,
376                              (u_32_t *)&fil.fr_dmsk, &fil.fr_dport, &ch,
377                              &fil.fr_dtop)) {
378                         (void)fprintf(stderr, "bad host (%s)\n", *cpp);
379                         return NULL;
380                 }
381                 fil.fr_dcmp = ch;
382         }
383
384         /*
385          * check some sanity, make sure we don't have icmp checks with tcp
386          * or udp or visa versa.
387          */
388         if (fil.fr_proto && (fil.fr_dcmp || fil.fr_scmp) &&
389             fil.fr_proto != IPPROTO_TCP && fil.fr_proto != IPPROTO_UDP) {
390                 (void)fprintf(stderr, "port operation on non tcp/udp\n");
391                 return NULL;
392         }
393         if (fil.fr_icmp && fil.fr_proto != IPPROTO_ICMP) {
394                 (void)fprintf(stderr, "icmp comparisons on wrong protocol\n");
395                 return NULL;
396         }
397
398         if (!*cpp)
399                 return &fil;
400
401         if (*cpp && !strcasecmp(*cpp, "flags")) {
402                 if (!*++cpp) {
403                         (void)fprintf(stderr, "no flags present\n");
404                         return NULL;
405                 }
406                 fil.fr_tcpf = tcp_flags(*cpp, &fil.fr_tcpfm);
407                 cpp++;
408         }
409
410         /*
411          * extras...
412          */
413         if (*cpp && (!strcasecmp(*cpp, "with") || !strcasecmp(*cpp, "and")))
414                 if (extras(&cpp, &fil))
415                         return NULL;
416
417         /*
418          * icmp types for use with the icmp protocol
419          */
420         if (*cpp && !strcasecmp(*cpp, "icmp-type")) {
421                 if (fil.fr_proto != IPPROTO_ICMP) {
422                         (void)fprintf(stderr,
423                                 "icmp with wrong protocol (%d)\n",
424                                 fil.fr_proto);
425                         return NULL;
426                 }
427                 if (addicmp(&cpp, &fil))
428                         return NULL;
429                 fil.fr_icmp = htons(fil.fr_icmp);
430                 fil.fr_icmpm = htons(fil.fr_icmpm);
431         }
432
433         /*
434          * Keep something...
435          */
436         while (*cpp && !strcasecmp(*cpp, "keep"))
437                 if (addkeep(&cpp, &fil))
438                         return NULL;
439
440         /*
441          * head of a new group ?
442          */
443         if (*cpp && !strcasecmp(*cpp, "head")) {
444                 if (!*++cpp) {
445                         (void)fprintf(stderr, "head without group #\n");
446                         return NULL;
447                 }
448                 fil.fr_grhead = atoi(*cpp);
449                 cpp++;
450         }
451
452         /*
453          * head of a new group ?
454          */
455         if (*cpp && !strcasecmp(*cpp, "group")) {
456                 if (!*++cpp) {
457                         (void)fprintf(stderr, "group without group #\n");
458                         return NULL;
459                 }
460                 fil.fr_group = atoi(*cpp);
461                 cpp++;
462         }
463
464         /*
465          * leftovers...yuck
466          */
467         if (*cpp && **cpp) {
468                 fprintf(stderr, "unknown words at end: [");
469                 for (; *cpp; cpp++)
470                         (void)fprintf(stderr, "%s ", *cpp);
471                 (void)fprintf(stderr, "]\n");
472                 return NULL;
473         }
474
475         /*
476          * lazy users...
477          */
478         if ((fil.fr_tcpf || fil.fr_tcpfm) && fil.fr_proto != IPPROTO_TCP) {
479                 (void)fprintf(stderr, "TCP protocol not specified\n");
480                 return NULL;
481         }
482         if (!(fil.fr_ip.fi_fl & FI_TCPUDP) && (fil.fr_proto != IPPROTO_TCP) &&
483             (fil.fr_proto != IPPROTO_UDP) && (fil.fr_dcmp || fil.fr_scmp)) {
484                 if (!fil.fr_proto) {
485                         fil.fr_ip.fi_fl |= FI_TCPUDP;
486                         fil.fr_mip.fi_fl |= FI_TCPUDP;
487                 } else {
488                         (void)fprintf(stderr,
489                                      "port comparisons for non-TCP/UDP\n");
490                         return NULL;
491                 }
492         }
493 /*
494         if ((fil.fr_flags & FR_KEEPFRAG) &&
495             (!(fil.fr_ip.fi_fl & FI_FRAG) || !(fil.fr_ip.fi_fl & FI_FRAG))) {
496                 (void)fprintf(stderr,
497                         "must use 'with frags' with 'keep frags'\n");
498                 return NULL;
499         }
500 */
501         return &fil;
502 }
503
504
505 int to_interface(fdp, to)
506 frdest_t *fdp;
507 char *to;
508 {
509         int     r = 0;
510         char    *s;
511
512         s = index(to, ':');
513         fdp->fd_ifp = NULL;
514         if (s) {
515                 *s++ = '\0';
516                 fdp->fd_ip.s_addr = hostnum(s, &r);
517                 if (r == -1)
518                         return -1;
519         }
520         (void) strncpy(fdp->fd_ifname, to, sizeof(fdp->fd_ifname) - 1);
521         fdp->fd_ifname[sizeof(fdp->fd_ifname) - 1] = '\0';
522         return 0;
523 }
524
525
526 void print_toif(tag, fdp)
527 char *tag;
528 frdest_t *fdp;
529 {
530         (void)printf("%s %s%s", tag, fdp->fd_ifname,
531                      (fdp->fd_ifp || (long)fdp->fd_ifp == -1) ? "" : "(!)");
532         if (fdp->fd_ip.s_addr)
533                 (void)printf(":%s", inet_ntoa(fdp->fd_ip));
534         putchar(' ');
535 }
536
537
538 /*
539  * returns false if neither "hostmask/num" or "hostmask mask addr" are
540  * found in the line segments
541  */
542 int     hostmask(seg, sa, msk, pp, cp, tp)
543 char    ***seg;
544 u_32_t  *sa, *msk;
545 u_short *pp, *tp;
546 u_char  *cp;
547 {
548         char    *s;
549         int     bits = -1, resolved;
550
551         /*
552          * is it possibly hostname/num ?
553          */
554         if ((s = index(**seg, '/')) || (s = index(**seg, ':'))) {
555                 *s++ = '\0';
556                 if (!isdigit(*s))
557                         return -1;
558                 if (index(s, '.'))
559                         *msk = inet_addr(s);
560                 if (!index(s, '.') && !index(s, 'x')) {
561                         /*
562                          * set x most significant bits
563                          */
564                         for (bits = atoi(s); bits; bits--) {
565                                 *msk /= 2;
566                                 *msk |= ntohl(inet_addr("128.0.0.0"));
567                         }
568                         *msk = htonl(*msk);
569                 } else {
570                         if (inet_aton(s, (struct in_addr *)msk) == -1)
571                                 return -1;
572                 }
573                 *sa = hostnum(**seg, &resolved) & *msk;
574                 if (resolved == -1)
575                         return -1;
576                 (*seg)++;
577                 return ports(seg, pp, cp, tp);
578         }
579
580         /*
581          * look for extra segments if "mask" found in right spot
582          */
583         if (*(*seg+1) && *(*seg+2) && !strcasecmp(*(*seg+1), "mask")) {
584                 *sa = hostnum(**seg, &resolved);
585                 if (resolved == -1)
586                         return -1;
587                 (*seg)++;
588                 (*seg)++;
589                 if (inet_aton(**seg, (struct in_addr *)msk) == -1)
590                         return -1;
591                 (*seg)++;
592                 *sa &= *msk;
593                 return ports(seg, pp, cp, tp);
594         }
595
596         if (**seg) {
597                 *sa = hostnum(**seg, &resolved);
598                 if (resolved == -1)
599                         return -1;
600                 (*seg)++;
601                 *msk = (*sa ? inet_addr("255.255.255.255") : 0L);
602                 *sa &= *msk;
603                 return ports(seg, pp, cp, tp);
604         }
605         return -1;
606 }
607
608 /*
609  * returns an ip address as a long var as a result of either a DNS lookup or
610  * straight inet_addr() call
611  */
612 u_32_t  hostnum(host, resolved)
613 char    *host;
614 int     *resolved;
615 {
616         struct  hostent *hp;
617         struct  netent  *np;
618
619         *resolved = 0;
620         if (!strcasecmp("any",host))
621                 return 0L;
622         if (isdigit(*host))
623                 return inet_addr(host);
624         if (!strcasecmp("<thishost>", host))
625                 host = thishost;
626
627         if (!(hp = gethostbyname(host))) {
628                 if (!(np = getnetbyname(host))) {
629                         *resolved = -1;
630                         fprintf(stderr, "can't resolve hostname: %s\n", host);
631                         return 0;
632                 }
633                 return htonl(np->n_net);
634         }
635         return *(u_32_t *)hp->h_addr;
636 }
637
638 /*
639  * check for possible presence of the port fields in the line
640  */
641 int     ports(seg, pp, cp, tp)
642 char    ***seg;
643 u_short *pp, *tp;
644 u_char  *cp;
645 {
646         int     comp = -1;
647
648         if (!*seg || !**seg || !***seg)
649                 return 0;
650         if (!strcasecmp(**seg, "port") && *(*seg + 1) && *(*seg + 2)) {
651                 (*seg)++;
652                 if (isdigit(***seg) && *(*seg + 2)) {
653                         *pp = portnum(**seg);
654                         (*seg)++;
655                         if (!strcmp(**seg, "<>"))
656                                 comp = FR_OUTRANGE;
657                         else if (!strcmp(**seg, "><"))
658                                 comp = FR_INRANGE;
659                         (*seg)++;
660                         *tp = portnum(**seg);
661                 } else if (!strcmp(**seg, "=") || !strcasecmp(**seg, "eq"))
662                         comp = FR_EQUAL;
663                 else if (!strcmp(**seg, "!=") || !strcasecmp(**seg, "ne"))
664                         comp = FR_NEQUAL;
665                 else if (!strcmp(**seg, "<") || !strcasecmp(**seg, "lt"))
666                         comp = FR_LESST;
667                 else if (!strcmp(**seg, ">") || !strcasecmp(**seg, "gt"))
668                         comp = FR_GREATERT;
669                 else if (!strcmp(**seg, "<=") || !strcasecmp(**seg, "le"))
670                         comp = FR_LESSTE;
671                 else if (!strcmp(**seg, ">=") || !strcasecmp(**seg, "ge"))
672                         comp = FR_GREATERTE;
673                 else {
674                         (void)fprintf(stderr,"unknown comparator (%s)\n",
675                                         **seg);
676                         return -1;
677                 }
678                 if (comp != FR_OUTRANGE && comp != FR_INRANGE) {
679                         (*seg)++;
680                         *pp = portnum(**seg);
681                 }
682                 *cp = comp;
683                 (*seg)++;
684         }
685         return 0;
686 }
687
688 /*
689  * find the port number given by the name, either from getservbyname() or
690  * straight atoi()
691  */
692 u_short portnum(name)
693 char    *name;
694 {
695         struct  servent *sp, *sp2;
696         u_short p1 = 0;
697
698         if (isdigit(*name))
699                 return (u_short)atoi(name);
700         if (!proto)
701                 proto = "tcp/udp";
702         if (strcasecmp(proto, "tcp/udp")) {
703                 sp = getservbyname(name, proto);
704                 if (sp)
705                         return ntohs(sp->s_port);
706                 (void) fprintf(stderr, "unknown service \"%s\".\n", name);
707                 return 0;
708         }
709         sp = getservbyname(name, "tcp");
710         if (sp)
711                 p1 = sp->s_port;
712         sp2 = getservbyname(name, "udp");
713         if (!sp || !sp2) {
714                 (void) fprintf(stderr, "unknown tcp/udp service \"%s\".\n",
715                         name);
716                 return 0;
717         }
718         if (p1 != sp2->s_port) {
719                 (void) fprintf(stderr, "%s %d/tcp is a different port to ",
720                         name, p1);
721                 (void) fprintf(stderr, "%s %d/udp\n", name, sp->s_port);
722                 return 0;
723         }
724         return ntohs(p1);
725 }
726
727
728 u_char tcp_flags(flgs, mask)
729 char *flgs;
730 u_char *mask;
731 {
732         u_char tcpf = 0, tcpfm = 0, *fp = &tcpf;
733         char *s, *t;
734
735         for (s = flgs; *s; s++) {
736                 if (*s == '/' && fp == &tcpf) {
737                         fp = &tcpfm;
738                         continue;
739                 }
740                 if (!(t = index(flagset, *s))) {
741                         (void)fprintf(stderr, "unknown flag (%c)\n", *s);
742                         return 0;
743                 }
744                 *fp |= flags[t - flagset];
745         }
746         if (!tcpfm)
747                 tcpfm = 0xff;
748         *mask = tcpfm;
749         return tcpf;
750 }
751
752
753 /*
754  * deal with extra bits on end of the line
755  */
756 int     extras(cp, fr)
757 char    ***cp;
758 struct  frentry *fr;
759 {
760         u_short secmsk;
761         u_long  opts;
762         int     notopt;
763         char    oflags;
764
765         opts = 0;
766         secmsk = 0;
767         notopt = 0;
768         (*cp)++;
769         if (!**cp)
770                 return -1;
771
772         while (**cp && (!strncasecmp(**cp, "ipopt", 5) ||
773                !strncasecmp(**cp, "not", 3) || !strncasecmp(**cp, "opt", 4) ||
774                !strncasecmp(**cp, "frag", 3) || !strncasecmp(**cp, "no", 2) ||
775                !strncasecmp(**cp, "short", 5))) {
776                 if (***cp == 'n' || ***cp == 'N') {
777                         notopt = 1;
778                         (*cp)++;
779                         continue;
780                 } else if (***cp == 'i' || ***cp == 'I') {
781                         if (!notopt)
782                                 fr->fr_ip.fi_fl |= FI_OPTIONS;
783                         fr->fr_mip.fi_fl |= FI_OPTIONS;
784                         goto nextopt;
785                 } else if (***cp == 'f' || ***cp == 'F') {
786                         if (!notopt)
787                                 fr->fr_ip.fi_fl |= FI_FRAG;
788                         fr->fr_mip.fi_fl |= FI_FRAG;
789                         goto nextopt;
790                 } else if (***cp == 'o' || ***cp == 'O') {
791                         if (!*(*cp + 1)) {
792                                 (void)fprintf(stderr,
793                                         "opt missing arguements\n");
794                                 return -1;
795                         }
796                         (*cp)++;
797                         if (!(opts = optname(cp, &secmsk)))
798                                 return -1;
799                         oflags = FI_OPTIONS;
800                 } else if (***cp == 's' || ***cp == 'S') {
801                         if (fr->fr_tcpf) {
802                                 (void) fprintf(stderr,
803                                     "short cannot be used with TCP flags\n");
804                                 return -1;
805                         }
806
807                         if (!notopt)
808                                 fr->fr_ip.fi_fl |= FI_SHORT;
809                         fr->fr_mip.fi_fl |= FI_SHORT;
810                         goto nextopt;
811                 } else
812                         return -1;
813
814                 if (!notopt || !opts)
815                         fr->fr_mip.fi_fl |= oflags;
816                 if (notopt)
817                         if (!secmsk)
818                                 fr->fr_mip.fi_optmsk |= opts;
819                         else
820                                 fr->fr_mip.fi_optmsk |= (opts & ~0x0100);
821                 else
822                                 fr->fr_mip.fi_optmsk |= opts;
823                 fr->fr_mip.fi_secmsk |= secmsk;
824
825                 if (notopt) {
826                         fr->fr_ip.fi_fl &= (~oflags & 0xf);
827                         fr->fr_ip.fi_optmsk &= ~opts;
828                         fr->fr_ip.fi_secmsk &= ~secmsk;
829                 } else {
830                         fr->fr_ip.fi_fl |= oflags;
831                         fr->fr_ip.fi_optmsk |= opts;
832                         fr->fr_ip.fi_secmsk |= secmsk;
833                 }
834 nextopt:
835                 notopt = 0;
836                 opts = 0;
837                 oflags = 0;
838                 secmsk = 0;
839                 (*cp)++;
840         }
841         return 0;
842 }
843
844
845 u_32_t optname(cp, sp)
846 char ***cp;
847 u_short *sp;
848 {
849         struct ipopt_names *io, *so;
850         u_long msk = 0;
851         u_short smsk = 0;
852         char *s;
853         int sec = 0;
854
855         for (s = strtok(**cp, ","); s; s = strtok(NULL, ",")) {
856                 for (io = ionames; io->on_name; io++)
857                         if (!strcasecmp(s, io->on_name)) {
858                                 msk |= io->on_bit;
859                                 break;
860                         }
861                 if (!io->on_name) {
862                         fprintf(stderr, "unknown IP option name %s\n", s);
863                         return 0;
864                 }
865                 if (!strcasecmp(s, "sec-class"))
866                         sec = 1;
867         }
868
869         if (sec && !*(*cp + 1)) {
870                 fprintf(stderr, "missing security level after sec-class\n");
871                 return 0;
872         }
873
874         if (sec) {
875                 (*cp)++;
876                 for (s = strtok(**cp, ","); s; s = strtok(NULL, ",")) {
877                         for (so = secclass; so->on_name; so++)
878                                 if (!strcasecmp(s, so->on_name)) {
879                                         smsk |= so->on_bit;
880                                         break;
881                                 }
882                         if (!so->on_name) {
883                                 fprintf(stderr, "no such security level: %s\n",
884                                         s);
885                                 return 0;
886                         }
887                 }
888                 if (smsk)
889                         *sp = smsk;
890         }
891         return msk;
892 }
893
894
895 #ifdef __STDC__
896 void optprint(u_short secmsk, u_short secbits, u_long optmsk, u_long optbits)
897 #else
898 void optprint(secmsk, secbits, optmsk, optbits)
899 u_short secmsk, secbits;
900 u_long optmsk, optbits;
901 #endif
902 {
903         struct ipopt_names *io, *so;
904         char *s;
905         int secflag = 0;
906
907         s = " opt ";
908         for (io = ionames; io->on_name; io++)
909                 if ((io->on_bit & optmsk) &&
910                     ((io->on_bit & optmsk) == (io->on_bit & optbits))) {
911                         if ((io->on_value != IPOPT_SECURITY) ||
912                             (!secmsk && !secbits)) {
913                                 printf("%s%s", s, io->on_name);
914                                 if (io->on_value == IPOPT_SECURITY)
915                                         io++;
916                                 s = ",";
917                         } else
918                                 secflag = 1;
919                 }
920
921
922         if (secmsk & secbits) {
923                 printf("%ssec-class", s);
924                 s = " ";
925                 for (so = secclass; so->on_name; so++)
926                         if ((secmsk & so->on_bit) &&
927                             ((so->on_bit & secmsk) == (so->on_bit & secbits))) {
928                                 printf("%s%s", s, so->on_name);
929                                 s = ",";
930                         }
931         }
932
933         if ((optmsk && (optmsk != optbits)) ||
934             (secmsk && (secmsk != secbits))) {
935                 s = " ";
936                 printf(" not opt");
937                 if (optmsk != optbits) {
938                         for (io = ionames; io->on_name; io++)
939                                 if ((io->on_bit & optmsk) &&
940                                     ((io->on_bit & optmsk) !=
941                                      (io->on_bit & optbits))) {
942                                         if ((io->on_value != IPOPT_SECURITY) ||
943                                             (!secmsk && !secbits)) {
944                                                 printf("%s%s", s, io->on_name);
945                                                 s = ",";
946                                                 if (io->on_value ==
947                                                     IPOPT_SECURITY)
948                                                         io++;
949                                         } else
950                                                 io++;
951                                 }
952                 }
953
954                 if (secmsk != secbits) {
955                         printf("%ssec-class", s);
956                         s = " ";
957                         for (so = secclass; so->on_name; so++)
958                                 if ((so->on_bit & secmsk) &&
959                                     ((so->on_bit & secmsk) !=
960                                      (so->on_bit & secbits))) {
961                                         printf("%s%s", s, so->on_name);
962                                         s = ",";
963                                 }
964                 }
965         }
966 }
967
968 char    *icmptypes[] = {
969         "echorep", (char *)NULL, (char *)NULL, "unreach", "squench",
970         "redir", (char *)NULL, (char *)NULL, "echo", "routerad",
971         "routersol", "timex", "paramprob", "timest", "timestrep",
972         "inforeq", "inforep", "maskreq", "maskrep", "END"
973 };
974
975 /*
976  * set the icmp field to the correct type if "icmp" word is found
977  */
978 int     addicmp(cp, fp)
979 char    ***cp;
980 struct  frentry *fp;
981 {
982         char    **t;
983         int     i;
984
985         (*cp)++;
986         if (!**cp)
987                 return -1;
988         if (!fp->fr_proto)      /* to catch lusers */
989                 fp->fr_proto = IPPROTO_ICMP;
990         if (isdigit(***cp)) {
991                 i = atoi(**cp);
992         } else {
993                 for (t = icmptypes, i = 0; ; t++, i++) {
994                         if (!*t)
995                                 continue;
996                         if (!strcasecmp("END", *t)) {
997                                 i = -1;
998                                 break;
999                         }
1000                         if (!strcasecmp(*t, **cp))
1001                                 break;
1002                 }
1003                 if (i == -1) {
1004                         (void)fprintf(stderr,
1005                                 "Invalid icmp-type (%s) specified\n", **cp);
1006                         return -1;
1007                 }
1008         }
1009         fp->fr_icmp = (u_short)(i << 8);
1010         fp->fr_icmpm = (u_short)0xff00;
1011         (*cp)++;
1012         if (!**cp)
1013                 return 0;
1014
1015         if (**cp && strcasecmp("code", **cp))
1016                 return 0;
1017         (*cp)++;
1018         if (isdigit(***cp)) {
1019                 i = atoi(**cp);
1020                 fp->fr_icmp |= (u_short)i;
1021                 fp->fr_icmpm = (u_short)0xffff;
1022                 (*cp)++;
1023                 return 0;
1024         }
1025         return -1;
1026 }
1027
1028
1029 #define MAX_ICMPCODE    12
1030
1031 char    *icmpcodes[] = {
1032         "net-unr", "host-unr", "proto-unr", "port-unr", "needfrag", "srcfail",
1033         "net-unk", "host-unk", "isolate", "net-prohib", "host-prohib",
1034         "net-tos", "host-tos", NULL };
1035 /*
1036  * Return the number for the associated ICMP unreachable code.
1037  */
1038 int icmpcode(str)
1039 char *str;
1040 {
1041         char    *s;
1042         int     i, len;
1043
1044         if (!(s = strrchr(str, ')')))
1045                 return -1;
1046         *s = '\0';
1047         if (isdigit(*str))
1048                 return atoi(str);
1049         len = strlen(str);
1050         for (i = 0; icmpcodes[i]; i++)
1051                 if (!strncasecmp(str, icmpcodes[i], MIN(len,
1052                                  strlen(icmpcodes[i])) ))
1053                         return i;
1054         return -1;
1055 }
1056
1057
1058 /*
1059  * set the icmp field to the correct type if "icmp" word is found
1060  */
1061 int     addkeep(cp, fp)
1062 char    ***cp;
1063 struct  frentry *fp;
1064 {
1065         if (fp->fr_proto != IPPROTO_TCP && fp->fr_proto != IPPROTO_UDP &&
1066             fp->fr_proto != IPPROTO_ICMP && !(fp->fr_ip.fi_fl & FI_TCPUDP)) {
1067                 (void)fprintf(stderr, "Can only use keep with UDP/ICMP/TCP\n");
1068                 return -1;
1069         }
1070
1071         (*cp)++;
1072         if (**cp && strcasecmp(**cp, "state") && strcasecmp(**cp, "frags")) {
1073                 (void)fprintf(stderr, "Unrecognised state keyword \"%s\"\n",
1074                         **cp);
1075                 return -1;
1076         }
1077
1078         if (***cp == 's' || ***cp == 'S')
1079                 fp->fr_flags |= FR_KEEPSTATE;
1080         else if (***cp == 'f' || ***cp == 'F')
1081                 fp->fr_flags |= FR_KEEPFRAG;
1082         (*cp)++;
1083         return 0;
1084 }
1085
1086
1087 /*
1088  * count consecutive 1's in bit mask.  If the mask generated by counting
1089  * consecutive 1's is different to that passed, return -1, else return #
1090  * of bits.
1091  */
1092 int     countbits(ip)
1093 u_32_t  ip;
1094 {
1095         u_32_t  ipn;
1096         int     cnt = 0, i, j;
1097
1098         ip = ipn = ntohl(ip);
1099         for (i = 32; i; i--, ipn *= 2)
1100                 if (ipn & 0x80000000)
1101                         cnt++;
1102                 else
1103                         break;
1104         ipn = 0;
1105         for (i = 32, j = cnt; i; i--, j--) {
1106                 ipn *= 2;
1107                 if (j > 0)
1108                         ipn++;
1109         }
1110         if (ipn == ip)
1111                 return cnt;
1112         return -1;
1113 }
1114
1115
1116 char    *portname(pr, port)
1117 int     pr, port;
1118 {
1119         static  char    buf[32];
1120         struct  protoent        *p = NULL;
1121         struct  servent *sv = NULL, *sv1 = NULL;
1122
1123         if (pr == -1) {
1124                 if ((sv = getservbyport(port, "tcp"))) {
1125                         strncpy(buf, sv->s_name, sizeof(buf)-1);
1126                         buf[sizeof(buf)-1] = '\0';
1127                         sv1 = getservbyport(port, "udp");
1128                         sv = strncasecmp(buf, sv->s_name, strlen(buf)) ?
1129                              NULL : sv1;
1130                 }
1131                 if (sv)
1132                         return buf;
1133         } else if (pr && (p = getprotobynumber(pr))) {
1134                 if ((sv = getservbyport(port, p->p_name))) {
1135                         strncpy(buf, sv->s_name, sizeof(buf)-1);
1136                         buf[sizeof(buf)-1] = '\0';
1137                         return buf;
1138                 }
1139         }
1140
1141         (void) sprintf(buf, "%d", port);
1142         return buf;
1143 }
1144
1145
1146 /*
1147  * print the filter structure in a useful way
1148  */
1149 void    printfr(fp)
1150 struct  frentry *fp;
1151 {
1152         static  char    *pcmp1[] = { "*", "=", "!=", "<", ">", "<=", ">=",
1153                                     "<>", "><"};
1154         struct  protoent        *p;
1155         int     ones = 0, pr;
1156         char    *s;
1157         u_char  *t;
1158
1159         if (fp->fr_flags & FR_PASS)
1160                 (void)printf("pass");
1161         else if (fp->fr_flags & FR_BLOCK) {
1162                 (void)printf("block");
1163                 if (fp->fr_flags & FR_RETICMP) {
1164                         (void)printf(" return-icmp");
1165                         if (fp->fr_icode)
1166                                 if (fp->fr_icode <= MAX_ICMPCODE)
1167                                         printf("(%s)",
1168                                                 icmpcodes[(int)fp->fr_icode]);
1169                                 else
1170                                         printf("(%d)", fp->fr_icode);
1171                 }
1172                 if (fp->fr_flags & FR_RETRST)
1173                         (void)printf(" return-rst");
1174         } else if ((fp->fr_flags & FR_LOGMASK) == FR_LOG) {
1175                 (void)printf("log");
1176                 if (fp->fr_flags & FR_LOGBODY)
1177                         (void)printf(" body");
1178                 if (fp->fr_flags & FR_LOGFIRST)
1179                         (void)printf(" first");
1180         } else if (fp->fr_flags & FR_ACCOUNT)
1181                 (void)printf("count");
1182         else if (fp->fr_flags & FR_AUTH)
1183                 (void)printf("auth");
1184         else if (fp->fr_flags & FR_PREAUTH)
1185                 (void)printf("preauth");
1186         else if (fp->fr_skip)
1187                 (void)printf("skip %d", fp->fr_skip);
1188
1189         if (fp->fr_flags & FR_OUTQUE)
1190                 (void)printf(" out ");
1191         else
1192                 (void)printf(" in ");
1193
1194         if (((fp->fr_flags & FR_LOGB) == FR_LOGB) ||
1195             ((fp->fr_flags & FR_LOGP) == FR_LOGP)) {
1196                 (void)printf("log ");
1197                 if (fp->fr_flags & FR_LOGBODY)
1198                         (void)printf("body ");
1199                 if (fp->fr_flags & FR_LOGFIRST)
1200                         (void)printf("first ");
1201                 if (fp->fr_flags & FR_LOGORBLOCK)
1202                         (void)printf("or-block ");
1203         }
1204         if (fp->fr_flags & FR_QUICK)
1205                 (void)printf("quick ");
1206
1207         if (*fp->fr_ifname) {
1208                 (void)printf("on %s%s ", fp->fr_ifname,
1209                         (fp->fr_ifa || (long)fp->fr_ifa == -1) ? "" : "(!)");
1210                 if (*fp->fr_dif.fd_ifname)
1211                         print_toif("dup-to", &fp->fr_dif);
1212                 if (*fp->fr_tif.fd_ifname)
1213                         print_toif("to", &fp->fr_tif);
1214                 if (fp->fr_flags & FR_FASTROUTE)
1215                         (void)printf("fastroute ");
1216
1217         }
1218         if (fp->fr_mip.fi_tos)
1219                 (void)printf("tos %#x ", fp->fr_tos);
1220         if (fp->fr_mip.fi_ttl)
1221                 (void)printf("ttl %d ", fp->fr_ttl);
1222         if (fp->fr_ip.fi_fl & FI_TCPUDP) {
1223                         (void)printf("proto tcp/udp ");
1224                         pr = -1;
1225         } else if ((pr = fp->fr_mip.fi_p)) {
1226                 if ((p = getprotobynumber(fp->fr_proto)))
1227                         (void)printf("proto %s ", p->p_name);
1228                 else
1229                         (void)printf("proto %d ", fp->fr_proto);
1230         }
1231
1232         printf("from %s", fp->fr_flags & FR_NOTSRCIP ? "!" : "");
1233         if (!fp->fr_src.s_addr & !fp->fr_smsk.s_addr)
1234                 (void)printf("any ");
1235         else {
1236                 (void)printf("%s", inet_ntoa(fp->fr_src));
1237                 if ((ones = countbits(fp->fr_smsk.s_addr)) == -1)
1238                         (void)printf("/%s ", inet_ntoa(fp->fr_smsk));
1239                 else
1240                         (void)printf("/%d ", ones);
1241         }
1242         if (fp->fr_scmp)
1243                 if (fp->fr_scmp == FR_INRANGE || fp->fr_scmp == FR_OUTRANGE)
1244                         (void)printf("port %d %s %d ", fp->fr_sport,
1245                                      pcmp1[fp->fr_scmp], fp->fr_stop);
1246                 else
1247                         (void)printf("port %s %s ", pcmp1[fp->fr_scmp],
1248                                      portname(pr, fp->fr_sport));
1249
1250         printf("to %s", fp->fr_flags & FR_NOTDSTIP ? "!" : "");
1251         if (!fp->fr_dst.s_addr & !fp->fr_dmsk.s_addr)
1252                 (void)printf("any");
1253         else {
1254                 (void)printf("%s", inet_ntoa(fp->fr_dst));
1255                 if ((ones = countbits(fp->fr_dmsk.s_addr)) == -1)
1256                         (void)printf("/%s", inet_ntoa(fp->fr_dmsk));
1257                 else
1258                         (void)printf("/%d", ones);
1259         }
1260         if (fp->fr_dcmp) {
1261                 if (fp->fr_dcmp == FR_INRANGE || fp->fr_dcmp == FR_OUTRANGE)
1262                         (void)printf(" port %d %s %d", fp->fr_dport,
1263                                      pcmp1[fp->fr_dcmp], fp->fr_dtop);
1264                 else
1265                         (void)printf(" port %s %s", pcmp1[fp->fr_dcmp],
1266                                      portname(pr, fp->fr_dport));
1267         }
1268         if ((fp->fr_ip.fi_fl & ~FI_TCPUDP) ||
1269             (fp->fr_mip.fi_fl & ~FI_TCPUDP) ||
1270             fp->fr_ip.fi_optmsk || fp->fr_mip.fi_optmsk ||
1271             fp->fr_ip.fi_secmsk || fp->fr_mip.fi_secmsk) {
1272                 (void)printf(" with");
1273                 if (fp->fr_ip.fi_optmsk || fp->fr_mip.fi_optmsk ||
1274                     fp->fr_ip.fi_secmsk || fp->fr_mip.fi_secmsk)
1275                         optprint(fp->fr_mip.fi_secmsk,
1276                                  fp->fr_ip.fi_secmsk,
1277                                  fp->fr_mip.fi_optmsk,
1278                                  fp->fr_ip.fi_optmsk);
1279                 else if (fp->fr_mip.fi_fl & FI_OPTIONS) {
1280                         if (!(fp->fr_ip.fi_fl & FI_OPTIONS))
1281                                 (void)printf(" not");
1282                         (void)printf(" ipopt");
1283                 }
1284                 if (fp->fr_mip.fi_fl & FI_SHORT) {
1285                         if (!(fp->fr_ip.fi_fl & FI_SHORT))
1286                                 (void)printf(" not");
1287                         (void)printf(" short");
1288                 }
1289                 if (fp->fr_mip.fi_fl & FI_FRAG) {
1290                         if (!(fp->fr_ip.fi_fl & FI_FRAG))
1291                                 (void)printf(" not");
1292                         (void)printf(" frag");
1293                 }
1294         }
1295         if (fp->fr_proto == IPPROTO_ICMP && fp->fr_icmpm) {
1296                 int     type = fp->fr_icmp, code;
1297
1298                 type = ntohs(fp->fr_icmp);
1299                 code = type & 0xff;
1300                 type /= 256;
1301                 if (type < (sizeof(icmptypes) / sizeof(char *)) &&
1302                     icmptypes[type])
1303                         (void)printf(" icmp-type %s", icmptypes[type]);
1304                 else
1305                         (void)printf(" icmp-type %d", type);
1306                 if (code)
1307                         (void)printf(" code %d", code);
1308         }
1309         if (fp->fr_proto == IPPROTO_TCP && (fp->fr_tcpf || fp->fr_tcpfm)) {
1310                 (void)printf(" flags ");
1311                 for (s = flagset, t = flags; *s; s++, t++)
1312                         if (fp->fr_tcpf & *t)
1313                                 (void)putchar(*s);
1314                 if (fp->fr_tcpfm) {
1315                         (void)putchar('/');
1316                         for (s = flagset, t = flags; *s; s++, t++)
1317                                 if (fp->fr_tcpfm & *t)
1318                                         (void)putchar(*s);
1319                 }
1320         }
1321
1322         if (fp->fr_flags & FR_KEEPSTATE)
1323                 printf(" keep state");
1324         if (fp->fr_flags & FR_KEEPFRAG)
1325                 printf(" keep frags");
1326         if (fp->fr_grhead)
1327                 printf(" head %d", fp->fr_grhead);
1328         if (fp->fr_group)
1329                 printf(" group %d", fp->fr_group);
1330         (void)putchar('\n');
1331 }
1332
1333 void    binprint(fp)
1334 struct frentry *fp;
1335 {
1336         int i = sizeof(*fp), j = 0;
1337         u_char *s;
1338
1339         for (s = (u_char *)fp; i; i--, s++) {
1340                 j++;
1341                 (void)printf("%02x ",*s);
1342                 if (j == 16) {
1343                         (void)printf("\n");
1344                         j = 0;
1345                 }
1346         }
1347         putchar('\n');
1348         (void)fflush(stdout);
1349 }