diff options
-rw-r--r-- | alck.1 | 73 | ||||
-rw-r--r-- | alck.h | 3 | ||||
-rw-r--r-- | gram.y | 44 | ||||
-rw-r--r-- | lex.l | 89 |
4 files changed, 164 insertions, 45 deletions
@@ -13,7 +13,7 @@ .\" .\" You should have received a copy of the GNU General Public License .\" along with Alck. If not, see <http://www.gnu.org/licenses/>. -.TH ALCK 1 "March 5, 2013" "ALCK" +.TH ALCK 1 "March 6, 2013" "ALCK" .SH NAME alck \- check MTA alias files .SH SYNOPSIS @@ -29,10 +29,14 @@ checks one or several .BR sendmail -style alias files for consistency. The following tests are performed: .TP +.B Multiply defined aliases +.TP .B Transitivity test This test discovers eventual circular dependencies. .TP .B Use of prohibited aliases +This test is optional. When enabled, it detects the use of pipes, +file redirects and includes in the input files. .PP The program returns 0 if all tests pass successfully. Otherwise, it diagnoses encountered problems and exits with error code 1. @@ -79,6 +83,10 @@ In any case, if several alias files are supplied, .B alck treats them as parts of a single alias file. .SH OPTIONS +.SS General +These options affect the behavior of the program in general. They can +be specified anywhere in the command line and affect all options that +follow them. .TP \fB\-d\fR \fISPEC\fR Set debug level. The \fISPEC\fR can contain one or more of the @@ -95,6 +103,55 @@ enable lexical analizer debugging. Upper-case variants are also accepted. Prepending a letter with a dash (\fB\-\fR) reverts its sense. .TP +.B \-v +Verbosely report the results. +.SS Local domain names +The distinction between local and remote email addreses allows +.B alck +to detect circular dependencies between aliases. A local email +is any email that has no domain part, or whose domain part contains +a +.BR "local domain" . +For +.B Sendmail +local domains are those that constitute the +.B w +class. +.PP +The following two options supply local domain names to +.BR alck . +They must appear in the command line before any filenames or +.B \-T +options. +.TP +\fB\-w\fR \fIFILE\fR +Read local domain names from the given +.IR FILE . +The file should list each name on a separate line. Surrounding +whitespace is allowed. Empty lines and +comments (lines starting with the +.B # +character) are ignored. +.TP +\fB\-W\fR \fICOMMAND\fR +Run \fICOMMAND\fR and read local domain names from its standard +output. +.B Alck +will consider for inclusion only those lines that begin with +alphanumeric character and don't contain whitespace. Thus, it is +possible to read the local domain names from the +.B Sendmail +configuration with the following option: +.sp +.nf +.in +2 +-W "echo '$=w' | sendmail -bt" +.in +.fi +.SS Input options +Input options can be interspersed with the input file names. They are +processed in the order of their appearance. +.TP \fB\-T\fR \fIFILE\fR Read names of alias files from \fIFILE\fR. .TP @@ -112,19 +169,9 @@ option or the end of command line. This options cancels the effect of the previous .B \-r option. -.TP -.B \-v -Verbosely report the results. -.TP -\fB\-w\fR \fIFILE\fR -Read contents of the -.BR Sendmail 's -.B w -class from the given -.IR FILE . -.PP +.SS Informational The following two options instruct the program to display a certain -kind of information and exit: +kind of information and exit. .TP .B \-h Display a terse help summary. @@ -56,7 +56,8 @@ void slist_append(SLIST **pdst, SLIST * src); char *slist_member(SLIST *plist, char *name); void slist_destroy(SLIST **plist); -void read_include(SLIST **plist, char *name); +void freadlist(SLIST **plist, char *name); +void preadlist(SLIST **plist, char *progname); void regalias(char *name, SLIST *exp); void end_aliases(void); @@ -20,7 +20,8 @@ char *program_name; SLIST *cw_list; /* List of domain names pertaining to Sendmail 'w' class */ -static int restricted; /* prohibit use of `special' aliases (pipes, + +static int restricted; /* prohibit the use of `special' aliases (pipes, file redirections and includes */ int verbose; /* Verbose mode */ int error_count; /* Number of errors detected so far */ @@ -100,7 +101,7 @@ email : string YYERROR; } $$ = NULL; - read_include(&$$, $2); + freadlist(&$$, $2); } ; @@ -111,7 +112,7 @@ string: IDENT %% int -yyerror (char *s) +yyerror(char *s) { parserror(file_name, line_num, "%s", s); error_count++; @@ -174,15 +175,17 @@ usage() printf(" l enable lexical analizer debugging\n"); printf(" Upper-case variants are also accepted. Prepending\n"); printf(" a letter with '-' reverts its sense\n"); - printf(" -f FILE Read names of alias files from FILE\n"); + printf(" -T FILE Read names of alias files from FILE\n"); printf(" -h Display this help list\n"); printf(" -r Restrict alias file syntax to aliases only (i.e.\n"); printf(" prohibit use of pipes and file redirections\n"); printf(" -u Revert the effect of the previous -r option\n"); printf(" -v Verbose mode\n"); printf(" -V print program version and exit\n"); - printf(" -w FILE Read contents of Sendmail `w' class from the given\n"); - printf(" file.\n"); + printf(" -w FILE Read local domain names from the FILE\n"); + printf(" -W PROG Run PROG and read local domain names from" + " its stdout\n"); + printf("\n"); printf("Report bugs to <%s>\n", "gray@gnu.org"); } @@ -210,17 +213,14 @@ main(int argc, char **argv) int c; int file_count = 0; int true = 1; - char *cwfile = "/etc/mail/sendmail.cw"; SLIST *file_list; /* List of files to be read */ struct string_list *s; init_lex(); program_name = argv[0]; - while ((c = getopt(argc, argv, "-d:f:hp:ruVvw:")) != EOF) { + while ((c = getopt(argc, argv, "-d:hp:rT:uVvW:w:")) != EOF) { switch (c) { case 1: - if (!cw_list) - read_include(&cw_list, cwfile); openaliases(optarg); yyparse(); file_count++; @@ -252,11 +252,9 @@ main(int argc, char **argv) } break; - case 'f': - if (!cw_list) - read_include(&cw_list, cwfile); + case 'T': file_list = NULL; - read_include(&file_list, optarg); + freadlist(&file_list, optarg); if (file_list) { for (s = file_list->head; s; s = s->next) { openaliases_prefix(optarg, s->str); @@ -285,24 +283,30 @@ main(int argc, char **argv) case 'V': version(); - exit (0); + exit(0); case 'w': if (file_count) - error("-w must be used before first non-option argument"); - cwfile = optarg; + error("-w must be used before the first " + "non-option argument or -T option"); + freadlist(&cw_list, optarg); + break; + + case 'W': + if (file_count) + error("-W must be used before the first " + "non-option argument or -T option"); + preadlist(&cw_list, optarg); break; default: - exit (1); + exit(1); } } argc -= optind; argv += optind; - if (!cw_list) - read_include(&cw_list, cwfile); while (argc--) { openaliases(*argv++); yyparse(); @@ -196,12 +196,15 @@ lex_debug(int debug) } void -read_include(SLIST **plist, char *name) +freadlist(SLIST **plist, char *name) { char *p; char buffer[256]; - FILE *fp = fopen(name, "r"); - + FILE *fp; + int line = 0; + int skipeol = 0; + + fp = fopen(name, "r"); if (!fp) { parserror(file_name, line_num, "cannot open include file `%s': %s", @@ -211,18 +214,82 @@ read_include(SLIST **plist, char *name) } while (p = fgets(buffer, sizeof buffer, fp)) { - char *q; - + size_t len = strlen(p); + + line++; + + if (len == 0) + continue; + if (p[len-1] != '\n') { + if (!feof(fp)) { + if (!skipeol) + parserror(name, line, + "line too long"); + error_count++; + skipeol = 1; + } + } else if (skipeol) + continue; + else { + p[--len] = 0; + skipeol = 0; + } + while (*p && isspace(*p)) p++; - if (*p == '#') + if (!*p || *p == '#') continue; - for (q = p + strlen(p) - 1; q > p && isspace(*q); q--) - ; - q[1] = 0; - if (*p) - slist_add(plist, p); + + slist_add(plist, p); } fclose(fp); } + +void +preadlist(SLIST **plist, char *progname) +{ + char *p; + char buffer[256]; + FILE *fp; + int line = 0; + int skipeol = 0; + + fp = popen(progname, "r"); + if (!fp) { + parserror(file_name, line_num, + "cannot run `%s': %s", + progname, strerror(errno)); + error_count++; + return; + } + while (p = fgets(buffer, sizeof buffer, fp)) { + size_t len = strlen(p); + char *q; + + line++; + + if (len == 0) + continue; + if (p[len-1] != '\n') { + if (!feof(fp)) { + if (!skipeol) + parserror(progname, line, + "line too long"); + error_count++; + skipeol = 1; + } + } else if (skipeol) + continue; + else { + p[--len] = 0; + skipeol = 0; + } + + if (!*p || !isalnum(*p) || p[strcspn(p, " \t")]) + continue; + + slist_add(plist, p); + } + pclose(fp); +} |