1 /* This file is part of Mailfromd.
2    Copyright (C) 2005-2021 Sergey Poznyakoff
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16 
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <stdarg.h>
26 #include <syslog.h>
27 #include <signal.h>
28 #include <pwd.h>
29 #include <grp.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <netdb.h>
34 
35 #include <mailutils/mailutils.h>
36 #include <mailutils/server.h>
37 #include <mailutils/syslog.h>
38 #include <mailutils/cli.h>
39 #include <mailutils/dbm.h>
40 
41 #include "mailfromd.h"
42 #include "callout.h"
43 #include "srvman.h"
44 #include "srvcfg.h"
45 #include "filenames.h"
46 #include "builtin.h"
47 #include "prog.h"
48 
49 
50 /* Configurable options */
51 
52 int mode = MAILFROMD_DAEMON; /* Default operation mode */
53 enum smtp_state test_state = smtp_state_envfrom; /* State for --test mode */
54 int need_script = 1;   /* Set if the current mode requires reading the
55 			  script file */
56 char *script_file = DEFAULT_SCRIPT_FILE;
57 int script_check;      /* Check config file syntax and exit */
58 extern int yy_flex_debug;   /* Enable tracing the lexical analyzer */
59 int script_ydebug;     /* Enable tracing the parser */
60 int script_dump_tree;  /* Dump created config tree to stdout */
61 int script_dump_code;  /* Dump disassembled code to stdout */
62 int script_dump_macros; /* Dump used Sendmail macros */
63 int script_dump_xref;  /* Dump cross-reference */
64 int preprocess_option; /* Only preprocess the sources */
65 int location_column_option; /* Print column numbers in error locations */
66 
67 char *ext_pp = DEF_EXT_PP; /* External preprocessor to use */
68 char *ext_pp_options;
69 int ext_pp_options_given;
70 
71 int do_trace;          /* Enable tracing configuration */
72 unsigned optimization_level = 1; /* Optimization level */
73 
74 int stack_trace_option;          /* Print stack traces on runtime errors */
75 
76 char *main_function_name = "main";
77 char *callout_server_url;
78 
79 mu_stream_t mf_strecho; /* Output stream for 'echo' statements */
80 
81 #define ARG_UNSET (-1)
82 
83 static int trace_option = ARG_UNSET;
84 static mu_list_t trace_modules;
85 
86 static char *resolv_conf_file;
87 
88 /* Preprocessor helper function */
89 static void
add_pp_option(const char * opt,const char * arg)90 add_pp_option(const char *opt, const char *arg)
91 {
92 	size_t len;
93 
94 	len = 1 + strlen(opt) + (arg ? strlen(arg) : 0);
95 	if (ext_pp_options) {
96 		len += strlen(ext_pp_options);
97 		ext_pp_options = mu_realloc(ext_pp_options, len + 1);
98 	} else {
99 		ext_pp_options = mu_alloc(len + 1);
100 		ext_pp_options[0] = 0;
101 	}
102 	strcat(ext_pp_options, " ");
103 	strcat(ext_pp_options, opt);
104 	strcat(ext_pp_options, arg);
105 }
106 
107 void
pp_define(const char * symbol)108 pp_define(const char *symbol)
109 {
110 	add_pp_option("-D", symbol);
111 }
112 
113 
114 /* Logging & debugging */
115 
116 static mu_stream_t mf_trace_stream;
117 
118 static void
open_trace_stream()119 open_trace_stream()
120 {
121 	int rc = mu_filter_create(&mf_trace_stream,
122 				  mf_strecho,
123 				  "C-escape",
124 				  MU_FILTER_ENCODE,
125 				  MU_STREAM_WRITE);
126 	if (rc) {
127 		mu_error(_("cannot create trace stream, "
128 			   "using standard log: %s"),
129 			 mu_strerror(rc));
130 		mf_trace_stream = mf_strecho;
131 	}
132 }
133 
134 void
trace(const char * fmt,...)135 trace(const char *fmt, ...)
136 {
137 	if (do_trace) {
138 		int bval = 0;
139 		va_list ap;
140 
141 		if (!mf_trace_stream)
142 			open_trace_stream();
143 
144 		va_start(ap, fmt);
145 		mu_stream_vprintf(mf_trace_stream, fmt, ap);
146 		bval = 1;
147 		mu_stream_ioctl(mf_trace_stream, MU_IOCTL_FILTER,
148 				MU_IOCTL_FILTER_SET_DISABLED,
149 				&bval);
150 		mu_stream_write(mf_trace_stream, "\n", 1, NULL);
151 		bval = 0;
152 		mu_stream_ioctl(mf_trace_stream, MU_IOCTL_FILTER,
153 				MU_IOCTL_FILTER_SET_DISABLED,
154 				&bval);
155 		va_end(ap);
156 	}
157 }
158 
159 void
log_status(sfsistat status,SMFICTX * ctx)160 log_status(sfsistat status, SMFICTX *ctx)
161 {
162 	mu_debug_level_t lev;
163 
164 	mu_debug_category_level(NULL, 0, &lev);
165 	if ((lev & ~MU_DEBUG_LEVEL_MASK(MU_DEBUG_ERROR)) &&
166 	    status != SMFIS_CONTINUE) {
167 		const char *str = sfsistat_str(status);
168 		if (str)
169 			logmsg(MU_LOG_INFO,
170 			       "%s%s", mailfromd_msgid(ctx), str);
171 		else
172 			logmsg(MU_LOG_INFO,
173 			       "%sstatus %d",
174 			       mailfromd_msgid(ctx), status);
175 	}
176 }
177 
178 
179 /* Sendmail class file support */
180 
181 static mu_list_t domain_list;
182 
183 /* Read domains from sendmail-style domain file NAME and store them in
184    DOMAIN_LIST */
185 int
read_domain_file(const char * name)186 read_domain_file(const char *name)
187 {
188 	FILE *fp;
189 	char buf[256];
190 	char *p;
191 
192 	fp = fopen(name, "r");
193 	if (!fp) {
194 		mu_error(_("cannot open file `%s': %s"),
195 			 name, mu_strerror(errno));
196 		return 1;
197 	}
198 
199 	if (!domain_list) {
200 		mu_list_create(&domain_list);
201 		mu_list_set_comparator(domain_list, mf_list_compare_string);
202 	}
203 
204 	while (p = fgets(buf, sizeof buf, fp)) {
205 		char *q;
206 
207 		while (*p && mu_isspace(*p))
208 			p++;
209 		if (*p == '#')
210 			continue;
211 
212 		for (q = p; *q && !mu_isspace(*q); q++)
213 			;
214 		*q = 0;
215 
216 		if (*p == 0)
217 			continue;
218 
219 		mu_list_append(domain_list, strdup(p));
220 	}
221 
222 	fclose(fp);
223 	return 0;
224 }
225 
226 /* Return true if we relay domain NAME */
227 int
relayed_domain_p(char * name)228 relayed_domain_p(char *name)
229 {
230 	char *p = name;
231 
232 	while (p) {
233 		if (mu_list_locate(domain_list, p, NULL) == 0) {
234 			mu_debug(MF_SOURCE_MAIN, MU_DEBUG_TRACE5,
235 				 ("%s is in relayed domain %s", name, p));
236 			return 1;
237 		}
238 		p = strchr(p, '.');
239 		if (p)
240 			p++;
241 	}
242 	return 0;
243 }
244 
245 /* Return true if CLIENT represents a host we relay. CLIENT is a dotted-quad
246    IP address. */
247 int
host_in_relayed_domain_p(char * client)248 host_in_relayed_domain_p(char *client)
249 {
250 	int rc;
251 	char *hbuf;
252 
253 	if (mu_list_is_empty(domain_list))
254 		return 0;
255 
256 	if (resolve_ipstr(client, &hbuf))
257 		return 0;
258 	rc = relayed_domain_p(hbuf);
259 	free(hbuf);
260 	return rc;
261 }
262 
263 static mu_list_t relayed_domain_files;
264 
265 static int
load_relay_file(void * item,void * data)266 load_relay_file(void *item, void *data)
267 {
268 	read_domain_file(item);
269 	return 0;
270 }
271 
272 static void
init_relayed_domains(void)273 init_relayed_domains(void)
274 {
275 	mu_list_foreach(relayed_domain_files, load_relay_file, NULL);
276 	mu_list_destroy(&relayed_domain_files);
277 }
278 
279 /* Command line parsing */
280 
281 const char *program_version = "mailfromd (" PACKAGE_STRING ")";
282 static char prog_doc[] = N_("mailfromd -- a general purpose milter daemon");
283 static char args_doc[] = "[var=value...][SCRIPT]";
284 
285 static void
opt_testmode(struct mu_parseopt * po,struct mu_option * op,char const * arg)286 opt_testmode(struct mu_parseopt *po, struct mu_option *op, char const *arg)
287 {
288 	mode = MAILFROMD_TEST;
289 	if (arg) {
290 		test_state =  string_to_state(arg);
291 		if (test_state == smtp_state_none) {
292 			mu_parseopt_error(po,
293 					  _("unknown smtp state tag: %s"),
294 					  arg);
295 			exit(po->po_exit_error);
296 		}
297 		log_stream = "stderr";
298 		need_script = 1;
299 	}
300 }
301 
302 static void
opt_runmode(struct mu_parseopt * po,struct mu_option * op,char const * arg)303 opt_runmode(struct mu_parseopt *po, struct mu_option *op, char const *arg)
304 {
305 	mode = MAILFROMD_RUN;
306 	if (arg)
307 		main_function_name = mu_strdup (arg);
308 	log_stream = "stderr";
309 	need_script = 1;
310 }
311 
312 static void
opt_lint(struct mu_parseopt * po,struct mu_option * op,char const * arg)313 opt_lint(struct mu_parseopt *po, struct mu_option *op, char const *arg)
314 {
315 	log_stream = "stderr";
316 	script_check = 1;
317 	need_script = 1;
318 }
319 
320 static void
opt_show_defaults(struct mu_parseopt * po,struct mu_option * op,char const * arg)321 opt_show_defaults(struct mu_parseopt *po, struct mu_option *op,
322 		  char const *arg)
323 {
324 	mode = MAILFROMD_SHOW_DEFAULTS;
325 	need_script = 0;
326 }
327 
328 static void
opt_daemon(struct mu_parseopt * po,struct mu_option * op,char const * arg)329 opt_daemon(struct mu_parseopt *po, struct mu_option *op,
330 	   char const *arg)
331 {
332 	mode = MAILFROMD_DAEMON;
333 	need_script = 1;
334 }
335 
336 static void
opt_include_dir(struct mu_parseopt * po,struct mu_option * op,char const * arg)337 opt_include_dir(struct mu_parseopt *po, struct mu_option *op,
338 		char const *arg)
339 {
340 	add_include_dir(arg);
341 }
342 
343 static void
opt_milter_socket(struct mu_parseopt * po,struct mu_option * op,char const * arg)344 opt_milter_socket(struct mu_parseopt *po, struct mu_option *op,
345 		  char const *arg)
346 {
347 	mf_srvcfg_add("default", arg);
348 }
349 
350 static void
opt_callout_socket(struct mu_parseopt * po,struct mu_option * op,char const * arg)351 opt_callout_socket(struct mu_parseopt *po, struct mu_option *op,
352 		   char const *arg)
353 {
354 	mf_srvcfg_add("callout", arg);
355 	free(callout_server_url);
356 	callout_server_url = mu_strdup(arg);
357 }
358 
359 static void
opt_mtasim(struct mu_parseopt * po,struct mu_option * op,char const * arg)360 opt_mtasim(struct mu_parseopt *po, struct mu_option *op,
361 	   char const *arg)
362 {
363 	mtasim_option = 1;
364 	server_flags |= MF_SERVER_FOREGROUND;
365 }
366 
367 static void
opt_optimize(struct mu_parseopt * po,struct mu_option * op,char const * arg)368 opt_optimize(struct mu_parseopt *po, struct mu_option *op,
369 	     char const *arg)
370 {
371 	if (!arg)
372 		optimization_level = 1;
373 	else {
374 		char *p;
375 		optimization_level = strtoul(arg, &p, 0);
376 		if (*p)
377 			mu_parseopt_error(po,
378 					  _("-O level must be a non-negative integer"));
379 	}
380 }
381 
382 static void
opt_variable(struct mu_parseopt * po,struct mu_option * op,char const * arg)383 opt_variable(struct mu_parseopt *po, struct mu_option *op,
384 	     char const *arg)
385 {
386 	char *p;
387 	struct mu_locus_range locus = {{ "<command line>", 1, 0 }};
388 
389 	p = strchr(arg, '=');
390 	if (!p) {
391 		mu_parseopt_error(po,
392 				  _("expected assignment, but found `%s'"),
393 				  arg);
394 		exit(po->po_exit_error);
395 	}
396 	*p++ = 0;
397 	defer_initialize_variable(arg, p, &locus);
398 }
399 
400 static void
opt_relayed_domain_file(struct mu_parseopt * po,struct mu_option * op,char const * arg)401 opt_relayed_domain_file(struct mu_parseopt *po, struct mu_option *op,
402 			char const *arg)
403 {
404 	if (!relayed_domain_files)
405 		mu_list_create(&relayed_domain_files);
406 	mu_list_append(relayed_domain_files, mu_strdup(arg));
407 }
408 
409 static void
opt_clear_ext_pp(struct mu_parseopt * po,struct mu_option * op,char const * arg)410 opt_clear_ext_pp(struct mu_parseopt *po, struct mu_option *op, char const *arg)
411 {
412 	ext_pp = NULL;
413 }
414 
415 static void
opt_define(struct mu_parseopt * po,struct mu_option * op,char const * arg)416 opt_define(struct mu_parseopt *po, struct mu_option *op, char const *arg)
417 {
418 	ext_pp_options_given = 1;
419 	add_pp_option("-D", arg);
420 }
421 
422 static void
opt_undefine(struct mu_parseopt * po,struct mu_option * op,char const * arg)423 opt_undefine(struct mu_parseopt *po, struct mu_option *op, char const *arg)
424 {
425 	ext_pp_options_given = 1;
426 	add_pp_option("-U", arg);
427 }
428 
429 static void
opt_set_milter_timeout(struct mu_parseopt * po,struct mu_option * op,char const * arg)430 opt_set_milter_timeout(struct mu_parseopt *po, struct mu_option *op,
431 		       char const *arg)
432 {
433 	time_t v;
434 
435 	if (mu_str_to_c(arg, mu_c_time, &v, NULL)) {
436 		mu_parseopt_error(po, _("%s: not a valid interval"), arg);
437 		exit(po->po_exit_error);
438 	}
439 
440 	if (smfi_settimeout(v) == MI_FAILURE) {
441 		mu_parseopt_error(po, _("invalid milter timeout: %s"), arg);
442 		exit(po->po_exit_error);
443 	}
444 }
445 
446 static void
destroy_trace_item(void * ptr)447 destroy_trace_item(void *ptr)
448 {
449 	free(ptr);
450 }
451 
452 static void
opt_trace_program(struct mu_parseopt * po,struct mu_option * op,char const * arg)453 opt_trace_program(struct mu_parseopt *po, struct mu_option *op,
454 		  char const *arg)
455 {
456 	if (!trace_modules) {
457 		mu_list_create(&trace_modules);
458 		mu_list_set_destroy_item(trace_modules, destroy_trace_item);
459 	}
460 	mu_list_append(trace_modules, mu_strdup(arg ? arg : "all"));
461 }
462 
463 static void
opt_dump_code(struct mu_parseopt * po,struct mu_option * op,char const * arg)464 opt_dump_code(struct mu_parseopt *po, struct mu_option *op,
465 	      char const *arg)
466 {
467 	script_dump_code = 1;
468 	log_stream = "stderr";
469 }
470 
471 static void
opt_dump_grammar_trace(struct mu_parseopt * po,struct mu_option * op,char const * arg)472 opt_dump_grammar_trace(struct mu_parseopt *po, struct mu_option *op,
473 		       char const *arg)
474 {
475 	script_ydebug = 1;
476 	log_stream = "stderr";
477 }
478 
479 static void
opt_dump_lex_trace(struct mu_parseopt * po,struct mu_option * op,char const * arg)480 opt_dump_lex_trace(struct mu_parseopt *po, struct mu_option *op,
481 		       char const *arg)
482 {
483 	yy_flex_debug = 1;
484 	log_stream = "stderr";
485 }
486 
487 static void
opt_dump_macros(struct mu_parseopt * po,struct mu_option * op,char const * arg)488 opt_dump_macros(struct mu_parseopt *po, struct mu_option *op,
489 		char const *arg)
490 {
491 	script_dump_macros = 1;
492 	log_stream = "stderr";
493 }
494 
495 static void
opt_dump_tree(struct mu_parseopt * po,struct mu_option * op,char const * arg)496 opt_dump_tree(struct mu_parseopt *po, struct mu_option *op,
497 	      char const *arg)
498 {
499 	script_dump_tree = 1;
500 	log_stream = "stderr";
501 }
502 
503 static void
opt_gacopyz_log(struct mu_parseopt * po,struct mu_option * op,char const * arg)504 opt_gacopyz_log(struct mu_parseopt *po, struct mu_option *op,
505 		char const *arg)
506 {
507 	int lev = gacopyz_string_to_log_level(arg);
508 	if (lev == -1) {
509 		mu_parseopt_error(po, _("%s: invalid log level"), arg);
510 		exit(po->po_exit_error);
511 	}
512 	milter_setlogmask(SMI_LOG_FROM(lev));
513 }
514 
515 static void
opt_dump_xref(struct mu_parseopt * po,struct mu_option * op,char const * arg)516 opt_dump_xref(struct mu_parseopt *po, struct mu_option *op,
517 	      char const *arg)
518 {
519 	script_dump_xref = 1;
520 	log_stream = "stderr";
521 }
522 
523 static struct mu_option mailfromd_options[] = {
524 	MU_OPTION_GROUP(N_("Operation modifiers")),
525 	{ "test", 't', N_("HANDLER"), MU_OPTION_ARG_OPTIONAL,
526 	  N_("run in test mode"),
527 	  mu_c_string, NULL, opt_testmode },
528 	{ "run",    0, N_("FUNC"), MU_OPTION_ARG_OPTIONAL,
529 	  N_("run script and execute function FUNC (\"main\" if not given)"),
530 	  mu_c_string, NULL, opt_runmode },
531 	{ "lint",   0, NULL, MU_OPTION_DEFAULT,
532 	  N_("check syntax and exit"),
533 	  mu_c_string, NULL, opt_lint },
534 	{ "syntax-check", 0, NULL, MU_OPTION_ALIAS, },
535 	{ "show-defaults", 0, NULL, MU_OPTION_DEFAULT,
536 	  N_("show compilation defaults"),
537 	  mu_c_string, NULL, opt_show_defaults },
538 
539 	{ "daemon", 0, NULL, MU_OPTION_DEFAULT,
540 	  N_("run in daemon mode (default)"),
541 	  mu_c_string, NULL, opt_daemon },
542 
543 	{ NULL, 'E', NULL, MU_OPTION_DEFAULT,
544 	  N_("preprocess source files and exit"),
545 	  mu_c_bool, &preprocess_option },
546 	/* Reserved for future use: */
547 	{ "compile", 'c', NULL, MU_OPTION_HIDDEN,
548 	  N_("compile files"),
549 	  mu_c_void },
550 	{ "load", 'l', N_("FILE"), MU_OPTION_HIDDEN,
551 	  N_("load library"),
552 	  mu_c_void },
553 	{ "load-dir", 'L', N_("DIR"), MU_OPTION_HIDDEN,
554 	  N_("add DIR to the load path"),
555 	  mu_c_void },
556 
557 	MU_OPTION_GROUP(N_("General options")),
558 	{ "include", 'I', N_("DIR"), MU_OPTION_DEFAULT,
559 	  N_("add the directory DIR to the list of directories to be "
560 	     "searched for header files"),
561 	  mu_c_string, NULL, opt_include_dir },
562 	{ "port", 'p', N_("STRING"), MU_OPTION_DEFAULT,
563 	  N_("set communication socket"),
564 	  mu_c_string, NULL, opt_milter_socket },
565 	{ "milter-socket", 0, NULL, MU_OPTION_ALIAS },
566 	{ "callout-socket", 0, N_("STRING"), MU_OPTION_DEFAULT,
567 	  N_("set callout socket"),
568 	  mu_c_string, NULL, opt_callout_socket },
569 	{ "mtasim", 0, NULL, MU_OPTION_IMMEDIATE,
570 	  N_("run in mtasim compatibility mode"),
571 	  mu_c_string, NULL, opt_mtasim },
572 	{ "optimize", 'O', N_("LEVEL"), MU_OPTION_ARG_OPTIONAL,
573 	  N_("set code optimization level"),
574 	  mu_c_string, NULL, opt_optimize },
575 	{ "variable", 'v', N_("VAR=VALUE"), MU_OPTION_DEFAULT,
576 	  N_("assign VALUE to VAR"),
577 	  mu_c_string, NULL, opt_variable },
578 	{ "relayed-domain-file", 0, N_("FILE"), MU_OPTION_DEFAULT,
579 	  N_("read relayed domains from FILE"),
580 	  mu_c_string, NULL, opt_relayed_domain_file },
581 	{ "resolv-conf-file", 0, N_("FILE"), MU_OPTION_DEFAULT,
582 	  N_("read resolver configuration from FILE"),
583 	  mu_c_string, &resolv_conf_file },
584 
585 	MU_OPTION_GROUP(N_("Preprocessor options")),
586 	{ "preprocessor", 0, N_("COMMAND"), MU_OPTION_DEFAULT,
587 	  N_("use command as external preprocessor"),
588 	  mu_c_string, &ext_pp },
589 	{ "no-preprocessor", 0, NULL, MU_OPTION_DEFAULT,
590 	  N_("disable the use of external preprocessor"),
591 	  mu_c_string, NULL, opt_clear_ext_pp },
592 	{ "define", 'D', N_("NAME[=VALUE]"), MU_OPTION_DEFAULT,
593 	  N_("define a preprocessor symbol NAME as having VALUE, or empty"),
594 	  mu_c_string, NULL, opt_define },
595 	{ "undefine", 'U', N_("NAME"), MU_OPTION_DEFAULT,
596 	  N_("undefine a preprocessor symbol NAME"),
597 	  mu_c_string, NULL, opt_undefine },
598 
599 	MU_OPTION_GROUP(N_("Timeout control")),
600 	{ "milter-timeout", 0, N_("TIME"), MU_OPTION_DEFAULT,
601 	  N_("set MTA connection timeout"),
602 	  mu_c_string, NULL, opt_set_milter_timeout },
603 
604 	MU_OPTION_GROUP (N_("Informational and debugging options")),
605 	{ "location-column", 0, NULL, MU_OPTION_DEFAULT,
606 	  N_("print location column numbers in compiler diagnostics messages"),
607 	  mu_c_bool, &location_column_option },
608 	{ "trace", 0, NULL, MU_OPTION_DEFAULT,
609 	  N_("trace executed actions"),
610 	  mu_c_bool, &trace_option },
611 	{ "trace-program", 0, N_("MODULES"), MU_OPTION_ARG_OPTIONAL,
612 	  N_("enable filter program tracing"),
613 	  mu_c_string, NULL, opt_trace_program },
614 
615 	{ "dump-code", 0, NULL, MU_OPTION_DEFAULT,
616 	  N_("dump disassembled code"),
617 	  mu_c_string, NULL, opt_dump_code },
618 
619 	{ "dump-grammar-trace", 0, NULL, MU_OPTION_DEFAULT,
620 	  N_("dump grammar traces"),
621 	  mu_c_string, NULL, opt_dump_grammar_trace },
622 	{ "dump-lex-trace", 0, NULL, MU_OPTION_DEFAULT,
623 	  N_("dump lexical analyzer traces"),
624 	  mu_c_string, NULL, opt_dump_lex_trace },
625 	{ "dump-tree", 0, NULL, MU_OPTION_DEFAULT,
626 	  N_("dump parser tree"),
627 	  mu_c_string, NULL, opt_dump_tree },
628 	{ "dump-macros", 0, NULL, MU_OPTION_DEFAULT,
629 	  N_("show used Sendmail macros"),
630 	  mu_c_string, NULL, opt_dump_macros },
631 	{ "xref", 0, NULL, MU_OPTION_DEFAULT,
632 	  N_("produce a cross-reference listing"),
633 	  mu_c_string, NULL, opt_dump_xref },
634 	{ "dump-xref", 0, NULL, MU_OPTION_ALIAS },
635 	{ "gacopyz-log", 0, N_("LEVEL"), MU_OPTION_DEFAULT,
636 	  N_("set Gacopyz log level"),
637 	  mu_c_string, NULL, opt_gacopyz_log },
638 	{ "stack-trace", 0, NULL, MU_OPTION_DEFAULT,
639 	  N_("enable stack traces on runtime errors"),
640 	  mu_c_bool, &stack_trace_option },
641 
642 	MU_OPTION_END
643 }, *options[] = { mailfromd_options, NULL };
644 
645 static int
validate_options()646 validate_options()
647 {
648 	/* FIXME */
649 	return 0;
650 }
651 
652 static int
flush_trace_module(void * item,void * data)653 flush_trace_module(void *item, void *data)
654 {
655 	enable_prog_trace((const char *) item);
656 	return 0;
657 }
658 
659 static char *capa[] = {
660 	"auth",
661 	"debug",
662 	"logging",
663 	"locking",
664 	"mailer",
665 	".mfd:server",
666 	NULL
667 };
668 
669 
670 /* Mailutils configuration */
671 
672 static int
cb_milter_timeout(void * data,mu_config_value_t * arg)673 cb_milter_timeout(void *data, mu_config_value_t *arg)
674 {
675 	struct timeval tv;
676 	int rc = config_cb_timeout (&tv, arg);
677 
678 	if (rc)
679 		return 1;
680 	if (smfi_settimeout(tv.tv_sec) == MI_FAILURE) {
681 		mu_error(_("Invalid milter timeout: %lu"),
682 			 (unsigned long) tv.tv_sec);
683 		exit(EX_USAGE);
684 	}
685 
686 	return 0;
687 }
688 
689 static int
cb_set_variable(void * data,mu_config_value_t * arg)690 cb_set_variable(void *data, mu_config_value_t *arg)
691 {
692 	const char *value;
693 	char *alloc_str = NULL;
694 	struct mu_locus_range locus = MU_LOCUS_RANGE_INITIALIZER;
695 
696 	if (mu_cfg_assert_value_type(arg, MU_CFG_ARRAY))
697 		return 1;
698 	if (arg->v.arg.c < 2) {
699 		mu_error(_("not enough arguments"));
700 		return 1;
701 	} else if (arg->v.arg.c > 2) {
702 		mu_error(_("too many arguments"));
703 		return 1;
704 	}
705 
706 	if (mu_cfg_assert_value_type(&arg->v.arg.v[0], MU_CFG_STRING))
707 		return 1;
708 
709 	switch (arg->v.arg.v[1].type) {
710 	case MU_CFG_STRING:
711 		value = arg->v.arg.v[1].v.string;
712 		break;
713 
714 	case MU_CFG_ARRAY:
715 		alloc_str = config_array_to_string(&arg->v.arg.v[1]);
716 		value = alloc_str;
717 		break;
718 
719 	case MU_CFG_LIST:
720 		mu_error(_("unexpected list"));
721 		return 1;
722 
723 	default:
724 		mu_error (_("INTERNAL ERROR at %s:%d: please report"),
725 			  __FILE__, __LINE__);
726 		abort();
727 	}
728 
729 	mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,
730 			 MU_IOCTL_LOGSTREAM_GET_LOCUS_RANGE, &locus);
731 	defer_initialize_variable(arg->v.arg.v[0].v.string, value, &locus);
732 	mu_locus_range_deinit(&locus);
733 	free(alloc_str);
734 	return 0;
735 }
736 
737 static int
cb_include_path(void * data,mu_config_value_t * val)738 cb_include_path(void *data, mu_config_value_t *val)
739 {
740 	int i, rc = 0;
741 	struct mu_wordsplit ws;
742 	mu_iterator_t itr;
743 
744 	switch (val->type) {
745 	case MU_CFG_STRING:
746 		ws.ws_delim = ":";
747 		if (mu_wordsplit(val->v.string, &ws,
748 				 MU_WRDSF_NOVAR |
749 				 MU_WRDSF_NOCMD | MU_WRDSF_DELIM)) {
750 			mu_error("mu_wordsplit: %s",
751 				 mu_wordsplit_strerror(&ws));
752 			return 1;
753 		}
754 		for (i = 0; i < ws.ws_wordc; i++)
755 			add_include_dir(ws.ws_wordv[i]);
756 		mu_wordsplit_free(&ws);
757 		break;
758 
759 	case MU_CFG_ARRAY:
760 		for (i = 0; i < val->v.arg.c; i++) {
761 			if (mu_cfg_assert_value_type(&val->v.arg.v[i],
762 						     MU_CFG_STRING))
763 				return 1;
764 			add_include_dir(val->v.arg.v[i].v.string);
765 		}
766 		break;
767 
768 	case MU_CFG_LIST:
769 		mu_list_get_iterator(val->v.list, &itr);
770 		for (mu_iterator_first(itr);
771 		     !mu_iterator_is_done(itr); mu_iterator_next(itr)) {
772 			mu_config_value_t *pval;
773 			mu_iterator_current(itr, (void*) &pval);
774 			rc = mu_cfg_assert_value_type(pval, MU_CFG_STRING);
775 			if (rc)
776 				break;
777 			add_include_dir(pval->v.string);
778 		}
779 		mu_iterator_destroy(&itr);
780 	}
781 	return rc;
782 }
783 
784 static int
cb_trace_program(void * data,mu_config_value_t * val)785 cb_trace_program(void *data, mu_config_value_t *val)
786 {
787 	int i, rc = 0;
788 
789 	switch (val->type) {
790 	case MU_CFG_STRING:
791 		enable_prog_trace(val->v.string);
792 		break;
793 
794 	case MU_CFG_ARRAY:
795 		for (i = 0; i < val->v.arg.c; i++) {
796 			if (mu_cfg_assert_value_type(&val->v.arg.v[i],
797 						     MU_CFG_STRING))
798 				return 1;
799 			enable_prog_trace(val->v.arg.v[i].v.string);
800 		}
801 		break;
802 
803 	case MU_CFG_LIST:
804 		mu_list_foreach(val->v.list, flush_trace_module, NULL);
805 		break;
806 	}
807 	return rc;
808 }
809 
810 static int
cb_relayed_domain_file(void * data,mu_config_value_t * val)811 cb_relayed_domain_file(void *data, mu_config_value_t *val)
812 {
813 	switch (val->type) {
814 	case MU_CFG_STRING:
815 		read_domain_file(val->v.string);
816 		break;
817 
818 	case MU_CFG_LIST:
819 		mu_list_foreach(val->v.list, load_relay_file, NULL);
820 		break;
821 
822 	default:
823 		mu_error (_("expected string or list of strings"));
824 	}
825 	return 0;
826 }
827 
828 
829 struct mu_cfg_param mf_cfg_param[] = {
830  	{ ".mfd:server",  mu_cfg_section, NULL, 0, NULL, NULL },
831 	{ "stack-trace", mu_c_bool, &stack_trace_option, 0, NULL,
832 	  N_("Dump stack traces on runtime errors.") },
833 
834 	{ "milter-timeout", mu_cfg_callback, NULL, 0, cb_milter_timeout,
835 	  N_("Set milter timeout."),
836 	  N_("time: interval") },
837         { "callout-url", mu_c_string, &callout_server_url, 0, NULL,
838 	  N_("Sets the URL of the default callout server."),
839 	  N_("url") },
840 	{ "include-path", mu_cfg_callback, NULL, 0, cb_include_path,
841 	  N_("Add directories to the list of directories to be searched for "
842 	     "header files.  Argument is a list of directory names "
843 	     "separated by colons."),
844 	  N_("path: string") },
845 	{ "setvar", mu_cfg_callback, NULL, 0, cb_set_variable,
846 	  N_("Initialize a mailfromd variable <var> to <value>."),
847 	  N_("var: string> <value: string-or-number") },
848 	{ "script-file", mu_c_string, &script_file, 0, NULL,
849 	  N_("Read filter script from <file>."),
850 	  N_("file") },
851 	{ "trace-actions", mu_c_bool, &do_trace, 0, NULL,
852 	  N_("Trace executed actions.") },
853 	{ "trace-program", mu_cfg_callback, NULL, 0, cb_trace_program,
854 	  N_("Enable filter program tracing."),
855 	  N_("modules: list") },
856 
857 	{ "relayed-domain-file", mu_cfg_callback, NULL, 0,
858 	  cb_relayed_domain_file,
859 	  N_("Read relayed domain names from the file"),
860 	  N_("file: string") },
861 
862         { "lock-retry-count", mu_cfg_callback, NULL, 0,
863 	  config_cb_lock_retry_count,
864 	  N_("Retry acquiring DBM file lock this number of times.") },
865 	{ "lock-retry-timeout", mu_cfg_callback, NULL, 0,
866 	  config_cb_lock_retry_timeout,
867 	  N_("Set the time span between the two DBM locking attempts."),
868 	  N_("time: interval") },
869 
870 	{ "runtime", mu_cfg_section, NULL },
871 
872 	{ NULL }
873 };
874 
875 
876 static struct mu_cfg_param *runtime_param;
877 static size_t runtime_param_cnt;
878 static size_t runtime_param_max;
879 
880 static void
_add_runtime_param_entry(struct mu_cfg_param * p)881 _add_runtime_param_entry(struct mu_cfg_param *p)
882 {
883 	if (runtime_param_cnt == runtime_param_max) {
884 		if (runtime_param_max == 0)
885 			runtime_param_max = 16;
886 		runtime_param = mu_2nrealloc(runtime_param,
887 					   &runtime_param_max,
888 					   sizeof(runtime_param[0]));
889 	}
890 	runtime_param[runtime_param_cnt++] = *p;
891 }
892 
893 void
mf_add_runtime_params(struct mu_cfg_param * p)894 mf_add_runtime_params(struct mu_cfg_param *p)
895 {
896 	for (; p->ident; p++)
897 		_add_runtime_param_entry(p);
898 }
899 
900 void
mf_runtime_param_finish()901 mf_runtime_param_finish()
902 {
903 	if (runtime_param_cnt) {
904 		static struct mu_cfg_param term = { NULL };
905 		struct mu_cfg_section *section;
906 
907 		_add_runtime_param_entry(&term);
908 
909 		if (mu_create_canned_section ("runtime", &section) == 0) {
910 			section->parser = NULL;
911 			section->docstring = N_("Configure MFL runtime values.");
912 			section->label = NULL;
913 			mu_cfg_section_add_params(section, runtime_param);
914 		}
915 	} else {
916 		struct mu_cfg_param *p;
917 		for (p = mf_cfg_param; p->ident; p++) {
918 			if (strcmp (p->ident, "runtime") == 0) {
919 				memset (p, 0, sizeof (*p));
920 				break;
921 			}
922 		}
923 	}
924 }
925 
926 
927 
928 /* Auxiliary functions */
929 unsigned keyword_column = 0;
930 unsigned header_column = 2;
931 unsigned value_column = 32;
932 unsigned right_margin = 79;
933 
934 static void
set_column(mu_stream_t str,unsigned margin)935 set_column(mu_stream_t str, unsigned margin)
936 {
937 	mu_stream_ioctl(str, MU_IOCTL_WORDWRAPSTREAM,
938 			MU_IOCTL_WORDWRAP_SET_MARGIN,
939 			&margin);
940 }
941 
942 static int
db_format_enumerator(struct db_format * fmt,void * data)943 db_format_enumerator(struct db_format *fmt, void *data)
944 {
945 	mu_stream_t str = data;
946 
947 	set_column(str, keyword_column);
948         mu_stream_printf(str, "%s database:", fmt->name);
949 	set_column(str, value_column);
950 	mu_stream_printf(str, "%s\n", fmt->dbname);
951 
952 	set_column(str, keyword_column);
953 	if (strcmp(fmt->name, "cache") == 0) {
954 		mu_stream_printf(str, "%s positive expiration:", fmt->name);
955 		set_column(str, value_column);
956 		mu_stream_printf(str, "%lu\n", fmt->expire_interval);
957 
958 		set_column(str, keyword_column);
959 		mu_stream_printf(str, "%s negative expiration:", fmt->name);
960 		set_column(str, value_column);
961 		mu_stream_printf(str, "%lu\n", negative_expire_interval);
962 	} else {
963 		mu_stream_printf(str, "%s expiration:", fmt->name);
964 		set_column(str, value_column);
965 		mu_stream_printf(str, "%lu\n", fmt->expire_interval);
966 	}
967 	return 0;
968 }
969 
970 static void
list_db_formats(mu_stream_t str,const char * pfx)971 list_db_formats(mu_stream_t str, const char *pfx)
972 {
973 	mu_iterator_t itr;
974 	int rc;
975 	const char *defdb = DEFAULT_DB_TYPE;
976 
977 	set_column(str, keyword_column);
978 	mu_stream_printf(str, "%s:", pfx);
979 
980 	set_column(str, value_column);
981 
982 	rc = mu_dbm_impl_iterator(&itr);
983 	if (rc) {
984 		mu_stream_printf(str, "%s\n", _("unknown"));
985 		mu_error("%s", mu_strerror(rc));
986 	} else {
987 		int i;
988 		for (mu_iterator_first(itr), i = 0; !mu_iterator_is_done(itr);
989 		     mu_iterator_next(itr), i++) {
990 			struct mu_dbm_impl *impl;
991 
992 			mu_iterator_current(itr, (void**)&impl);
993 			if (i)
994 				mu_stream_printf(str, ", ");
995 			else if (!defdb)
996 				defdb = impl->_dbm_name;
997 			mu_stream_printf(str, "%s", impl->_dbm_name);
998 		}
999 		mu_stream_write(str, "\n", 1, NULL);
1000 		mu_iterator_destroy(&itr);
1001 	}
1002 
1003 	set_column(str, keyword_column);
1004 	mu_stream_printf(str, "%s:", "default database type");
1005 	set_column(str, value_column);
1006 	mu_stream_printf(str, "%s\n", defdb);
1007 }
1008 
1009 struct string_value {
1010 	char const *kw;
1011 	int type;
1012 	union {
1013 		char *s_const;
1014 		char **s_var;
1015 		char *(*s_func) (void);
1016 	} data;
1017 };
1018 
1019 static char *
string_preprocessor(void)1020 string_preprocessor (void)
1021 {
1022 	return ext_pp ? ext_pp : "none";
1023 }
1024 
1025 #ifdef USE_SYSLOG_ASYNC
1026 # if DEFAULT_SYSLOG_ASYNC == 1
1027 #  define DEFAULT_SYSLOG "non-blocking"
1028 # else
1029 #  define DEFAULT_SYSLOG "blocking"
1030 # endif
1031 #else
1032 #  define DEFAULT_SYSLOG "blocking"
1033 #endif
1034 
1035 enum {
1036 	STRING_CONSTANT,
1037 	STRING_VARIABLE,
1038 	STRING_FUNCTION
1039 };
1040 
1041 static struct string_value string_values[] = {
1042 	{ "version",        STRING_CONSTANT, { .s_const = VERSION } },
1043 	{ "script file",    STRING_VARIABLE, { .s_var   = &script_file } },
1044 	{ "preprocessor",   STRING_FUNCTION, { .s_func  = string_preprocessor } },
1045 	{ "user",           STRING_VARIABLE, { .s_var   = &mf_server_user } },
1046 	{ "statedir",       STRING_VARIABLE, { .s_var   = &mailfromd_state_dir } },
1047 	{ "socket",         STRING_CONSTANT, { .s_const = DEFAULT_SOCKET } },
1048 	{ "pidfile",        STRING_VARIABLE, { .s_var   = &pidfile } },
1049 	{ "default syslog", STRING_CONSTANT, { .s_const = DEFAULT_SYSLOG } },
1050 	{ NULL }
1051 };
1052 
1053 static void
print_string_values(mu_stream_t str)1054 print_string_values(mu_stream_t str)
1055 {
1056 	struct string_value *p;
1057 	char const *val;
1058 
1059 	for (p = string_values; p->kw; p++) {
1060 		set_column(str, keyword_column);
1061 		mu_stream_printf(str, "%s:", p->kw);
1062 
1063 		switch (p->type) {
1064 		case STRING_CONSTANT:
1065 			val = p->data.s_const;
1066 			break;
1067 
1068 		case STRING_VARIABLE:
1069 			val = *p->data.s_var;
1070 			break;
1071 
1072 		case STRING_FUNCTION:
1073 			val = p->data.s_func ();
1074 		}
1075 
1076 		set_column(str, value_column);
1077 		mu_stream_printf(str, "%s\n", val);
1078 	}
1079 }
1080 
1081 void
mailfromd_show_defaults(void)1082 mailfromd_show_defaults(void)
1083 {
1084 	int rc;
1085 	mu_stream_t str;
1086 
1087 	rc = mu_wordwrap_stream_create (&str, mu_strout, 0, right_margin);
1088 	if (rc) {
1089 		str = mu_strout;
1090 		mu_stream_ref(str);
1091 	}
1092 
1093 	print_string_values(str);
1094 
1095 	list_db_formats(str, "supported databases");
1096 
1097 	set_column(str, keyword_column);
1098 	mu_stream_printf(str, "%s:", "optional features");
1099 	set_column(str, value_column);
1100 #if defined WITH_DKIM
1101 	mu_stream_printf(str, " %s", "DKIM");
1102 #endif
1103 #if defined WITH_GEOIP
1104 	mu_stream_printf(str, " %s", "GeoIP");
1105 #endif
1106 #if defined WITH_GEOIP2
1107 	mu_stream_printf(str, " %s", "GeoIP2");
1108 #endif
1109 #if defined WITH_DSPAM
1110 	mu_stream_printf(str, " %s", "DSPAM");
1111 #endif
1112 	mu_stream_write(str, "\n", 1, NULL);
1113 
1114 	db_format_enumerate(db_format_enumerator, str);
1115 
1116 	mu_stream_destroy (&str);
1117 }
1118 
1119 static int
args_in_order(int argc,char ** argv)1120 args_in_order(int argc, char **argv)
1121 {
1122 	int i;
1123 	for (i = 0; i < argc; i++) {
1124 		size_t len = strcspn(argv[i], "=");
1125 		if (len > 3
1126 		    && (memcmp(argv[i], "--ru", 4) == 0
1127 			|| memcmp(argv[i], "--run", 5) == 0)) {
1128 			return MF_GETOPT_IN_ORDER;
1129 		}
1130 	}
1131 	return MF_GETOPT_DEFAULT;
1132 }
1133 
1134 
1135 static void
provide_default_milter_server(void)1136 provide_default_milter_server(void)
1137 {
1138 	if (mfd_srvman_count_servers() == 0) {
1139 		mu_diag_output(MU_DIAG_WARNING,
1140 			       _("no servers defined; will listen on %s"),
1141 			       DEFAULT_SOCKET);
1142 		mf_srvcfg_add("milter", DEFAULT_SOCKET);
1143 	}
1144 }
1145 
1146 static void
provide_default_callout_server(void)1147 provide_default_callout_server(void)
1148 {
1149 	struct variable *var;
1150 
1151 	if (provide_callout &&
1152 	    !callout_server_url &&
1153 	    (!(var = variable_lookup("callout_server_url")) ||
1154 	     (var->sym.flags & SYM_REFERENCED) &&
1155 	     !(var->sym.flags & SYM_INITIALIZED))) {
1156 		mf_srvcfg_add("callout", DEFAULT_CALLOUT_SOCKET);
1157 	}
1158 }
1159 
1160 
1161 static char *modnames[] = {
1162 #define __DBGMOD_C_ARRAY
1163 # include "mfd-dbgmod.h"
1164 #undef __DBGMOD_C_ARRAY
1165 	NULL
1166 };
1167 
1168 mu_debug_handle_t mfd_debug_handle;
1169 
1170 static void
debug_init(void)1171 debug_init(void)
1172 {
1173 	int i;
1174 
1175 	mfd_debug_handle = mu_debug_register_category (modnames[0]);
1176 	for (i = 1; modnames[i]; i++)
1177 		mu_debug_register_category (modnames[i]);
1178 }
1179 
1180 
1181 int
mf_server_function(const char * key,struct mf_srvcfg * cfg)1182 mf_server_function(const char *key, struct mf_srvcfg *cfg)
1183 {
1184 	if (!key || strcmp(key, "default") == 0 || strcmp(key, "milter") == 0)
1185 		cfg->server = milter_session_server;
1186 	else if (strcmp(key, "callout") == 0) {
1187 		cfg->server = mfd_callout_session_server;
1188 		if (cfg->defopt ||
1189 		    mu_list_locate(cfg->options, "default", NULL) == 0)
1190 			callout_server_url =
1191 				mu_strdup(mu_url_to_string(cfg->url));
1192 	} else
1193 		return 1;
1194 	return 0;
1195 }
1196 
1197 static void
open_strecho(int daemon_mode)1198 open_strecho (int daemon_mode)
1199 {
1200 	int rc;
1201 	if (daemon_mode) {
1202 		rc = mu_stream_ioctl(mu_strerr, MU_IOCTL_LOGSTREAM,
1203 				     MU_IOCTL_LOGSTREAM_CLONE,
1204 				     &mf_strecho);
1205 		if (rc == 0) {
1206 			int s = MU_LOG_INFO;
1207 			mu_stream_ioctl(mf_strecho, MU_IOCTL_LOGSTREAM,
1208 					MU_IOCTL_LOGSTREAM_SET_SEVERITY,
1209 					&s);
1210 		}
1211 	} else {
1212 #if 0
1213 		mf_strecho = mu_strout;
1214 		mu_stream_ref(mf_strecho);
1215 		rc = 0;
1216 #else
1217 		rc = mu_stdio_stream_create(&mf_strecho, MU_STDERR_FD, 0);
1218 #endif
1219 	}
1220 	if (rc) {
1221 		mu_diag_output(MU_LOG_CRIT,
1222 			       _("cannot create echo output stream: %s"),
1223 			       mu_strerror(rc));
1224 		exit(EX_UNAVAILABLE);
1225 	}
1226 }
1227 
1228 void
mailfromd_alloc_die()1229 mailfromd_alloc_die()
1230 {
1231 	parse_error("not enough memory");
1232         abort();
1233 }
1234 
1235 extern char **environ;
1236 
1237 struct mu_cli_setup cli = {
1238 	.optv = options,
1239 	.cfg = mf_cfg_param,
1240 	.prog_doc = prog_doc,
1241 	.prog_args = args_doc
1242 };
1243 
1244 int
main(int argc,char ** argv)1245 main(int argc, char **argv)
1246 {
1247 	prog_counter_t entry_point;
1248 	int stderr_is_closed = stderr_closed_p();
1249 
1250 	mf_init_nls();
1251 	mf_proctitle_init(argc, argv, environ);
1252 	mu_alloc_die_hook = mailfromd_alloc_die;
1253 
1254 	MU_AUTH_REGISTER_ALL_MODULES();
1255 	mu_register_all_formats();
1256 	mu_register_all_mailer_formats();
1257 	yy_flex_debug = 0;
1258 
1259 	/* Initialize milter */
1260 	milter_setlogmask(SMI_LOG_FROM(SMI_LOG_WARN));
1261 	milter_settimeout(7200);
1262 
1263 	/* Set default logging */
1264 	mu_set_program_name(argv[0]);
1265 	mu_log_tag = mu_strdup(mu_program_name);
1266 	mu_log_facility = DEFAULT_LOG_FACILITY;
1267 	mu_stdstream_setup(MU_STDSTREAM_RESET_NONE);
1268 	mf_srvcfg_log_setup(stderr_is_closed ? "syslog" : "stderr");
1269 
1270 	debug_init();
1271 	libcallout_init();
1272 	init_string_space();
1273 	init_symbols();
1274 	builtin_setup();
1275 	mf_runtime_param_finish();
1276 	db_format_setup();
1277 	include_path_setup();
1278 	pragma_setup();
1279 
1280 	mf_server_save_cmdline(argc, argv);
1281 
1282 	dnsbase_init();
1283 	mu_acl_cfg_init();
1284 	database_cfg_init();
1285 	srvman_init();
1286 	mf_srvcfg_init(argv[0], "(milter | callout)");
1287 
1288 	mf_getopt(&cli, &argc, &argv, capa, args_in_order(argc, argv));
1289 
1290 	if (validate_options())
1291 		exit(EX_USAGE);
1292 
1293 	init_relayed_domains();
1294 	if (resolv_conf_file)
1295 		dnsbase_file_init(resolv_conf_file);
1296 
1297 	if (trace_option != ARG_UNSET)
1298 		do_trace = trace_option;
1299 	if (trace_modules) {
1300 		mu_list_foreach(trace_modules, flush_trace_module, NULL);
1301 		mu_list_destroy(&trace_modules);
1302 	}
1303 
1304 	mf_srvcfg_flush();
1305 
1306 	alloc_ext_pp();
1307 
1308 	if (need_script) {
1309 		char *new_script = NULL;
1310 		if (argc) {
1311 			int i, n = -1;
1312 			for (i = 0; i < argc; i++) {
1313 				if (strchr(argv[i], '=') == 0) {
1314 					if (n == -1) {
1315 						n = i;
1316 						if (mode == MAILFROMD_RUN)
1317 							break;
1318 					} else {
1319 						mu_error(_("script file "
1320 							   "specified twice "
1321 							   "(%s and %s)"),
1322 							 argv[n], argv[i]);
1323 						exit(EX_USAGE);
1324 					}
1325 				}
1326 			}
1327 			if (n >= 0) {
1328 				new_script = argv[n];
1329 				memmove(argv + n, argv + n + 1,
1330 					(argc - n + 1) * sizeof argv[0]);
1331 				argc--;
1332 			}
1333 		}
1334 
1335 		if (new_script)
1336 			script_file = new_script;
1337 
1338 		if (script_file[0] != '/')
1339 			/* Clear saved command line */
1340 			mf_server_save_cmdline(0, NULL);
1341 		if (preprocess_option)
1342 			exit(preprocess_input());
1343 		if (parse_program(script_file, script_ydebug))
1344 			exit(EX_CONFIG);
1345 	}
1346 
1347 	if (script_dump_tree)
1348 		print_syntax_tree();
1349 	if (script_dump_code)
1350 		print_code();
1351 
1352 	if (script_dump_xref)
1353 		print_xref();
1354 
1355 	if (script_dump_macros)
1356 		print_used_macros();
1357 
1358 	fixup_code();
1359 
1360 	if (script_check || script_dump_macros
1361 	    || script_dump_code || script_dump_tree || script_dump_xref
1362 	    || yy_flex_debug || script_ydebug)
1363 		exit(EX_OK);
1364 
1365 	switch (mode) {
1366 	case MAILFROMD_DAEMON:
1367 		mf_server_log_setup();
1368 		provide_default_milter_server();
1369 		provide_default_callout_server();
1370 		break;
1371 
1372 	case MAILFROMD_RUN: {
1373 		struct function *fun = function_lookup(main_function_name);
1374 		if (!fun) {
1375 			mu_error(_("function %s is not defined"),
1376 				 main_function_name);
1377 			exit(EX_CONFIG);
1378 		}
1379 
1380 		if (fun->parmcount || !fun->varargs) {
1381 			mu_error(_("function %s must take variable number of "
1382 				   "arguments"),
1383 				 main_function_name);
1384 			exit(EX_CONFIG);
1385 		}
1386 		if (fun->rettype != dtype_number) {
1387 			mu_error(_("function %s must return number"),
1388 				 main_function_name);
1389 			exit(EX_CONFIG);
1390 		}
1391 		entry_point = fun->entry;
1392 	}
1393 	}
1394 
1395 	free_exceptions();
1396 	free_symbols();
1397 	free_string_space();
1398 	free_parser_data();
1399 
1400 	mf_namefixup_run(mailfromd_state_dir);
1401 	mf_namefixup_free();
1402 
1403 	switch (mode) {
1404 	case MAILFROMD_DAEMON:
1405 		if (argc > 0) {
1406 			mu_error(_("too many arguments"));
1407 			exit(EX_USAGE);
1408 		}
1409 		if (script_file[0] != '/') {
1410 			mu_diag_output(MU_DIAG_WARNING,
1411 				       _("script file is given "
1412 					 "without full file name"));
1413 			server_flags |= MF_SERVER_NORESTART;
1414 		}
1415 		open_strecho(1);
1416 		mf_server_lint_option = "--lint";
1417 		mf_server_start("mailfromd", mailfromd_state_dir, pidfile,
1418 				server_flags);
1419 		break;
1420 
1421 	case MAILFROMD_TEST:
1422 		open_strecho(0);
1423 		mailfromd_test(argc, argv);
1424 		break;
1425 
1426 	case MAILFROMD_SHOW_DEFAULTS:
1427 		mailfromd_show_defaults();
1428 		break;
1429 
1430 	case MAILFROMD_RUN:
1431 		open_strecho(0);
1432 		mailfromd_run(entry_point, argc, argv);
1433 	}
1434 
1435 	exit(EX_OK);
1436 }
1437 
1438