/*
 * Copyright (c) 2002-2006 Sendmail, Inc. and its suppliers.
 *	All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 */

#include "sm/generic.h"
SM_RCSID("@(#)$Id: smar_rdcf.c,v 1.80 2007/11/10 04:03:58 ca Exp $")

#include "sm/common.h"
#include "sm/error.h"
#include "sm/assert.h"
#include "sm/ctype.h"
#include "sm/io.h"
#include "sm/net.h"
#include "sm/mapc.h"
#include "sm/bdb.h"
#include "sm/version.h"
#include "smar.h"
#include "log.h"
#include "sm/sysexits.h"
#include "sm/sm-conf.h"
#include "sm/sm-conf-prt.h"
#include "sm/smarcnfdef.h"

/*
**  SMAR_SHOWCOPT -- show compile time options
**
**	Parameters:
**		smar_ctx -- SMAR context
**
**	Returns:
**		none
*/

static void
smar_showcopt(smar_ctx_P smar_ctx)
{
	sm_io_fprintf(smioout, "compile time options:\n"
#if RCBCOMM_DEBUG
		"RCBCOMM_DEBUG\n"
#endif
#if SMAR_DEBUG
		"SMAR_DEBUG\n"
#endif
#if SMAR_RVRS_ALL
		"SMAR_RVRS_ALL\n"
#endif
#if SMAR_TEST
		"SMAR_TEST\n"
#endif
#if SMAR_USE_DNS
		"SMAR_USE_DNS\n"
#endif
#if MTA_LIBDNS_TEST
		"MTA_LIBDNS_TEST\n"
#endif
		);
	sm_io_flush(smioout);
}

/*
**  SMAR_SHOWVERSION -- show version information
**
**	Parameters:
**		smar_ctx -- SMAR context
**		progname -- program name
**		cnf -- configuration file name
**		level -- how much detail?
**
**	Returns:
**		none
*/

static sm_ret_T
smar_showversion(smar_ctx_P smar_ctx, const char *progname, const char *cnf, uint level)
{
	char *prefix;

	prefix = "";
	if (level >= SM_SV_RD_CONF)
		prefix = "# ";
	sm_io_fprintf(smioout, "%s%s: %s\n", prefix, progname, MTA_VERSION_STR);
	if (SM_SHOW_VERSION(level))
		sm_io_fprintf(smioout, "%sversion=0x%08x\n",
			prefix, MTA_VERSION);
	if (SM_SHOW_LIBS(level))
		(void) sm_bdbversionprt(smioout);
	if (SM_SHOW_OPTIONS(level))
		smar_showcopt(smar_ctx);
	if (SM_SHOW_RD_CONF(level) && cnf != NULL && *cnf != '\0') {
		sm_io_fprintf(smioout,
			"# configuration-file=%s\n\n", cnf);
		(void) sm_conf_prt_conf(smar_global_defs,
				&smar_ctx->smar_cnf, smioout);
	}
	else if (SM_SHOW_DFLT_CONF(level)) {
		sm_io_fprintf(smioout, "# default configuration:\n\n");
		(void) sm_conf_prt_dflt(smar_global_defs, 0, smioout);
	}
	else if (SM_SHOW_ALL_CONF(level)) {
		sm_io_fprintf(smioout,
			"# configuration including flags.\n"
			"# note: this data cannot be used as configuration file\n"
			"# but it shows the available flags.\n"
			);
		(void) sm_conf_prt_dflt(smar_global_defs, SMC_FLD_FLAGS,
			smioout);
	}
	sm_io_flush(smioout);
	return sm_error_perm(SM_EM_AR, SM_E_DONTSTART);
}

/*
**  SMAR_USAGE -- usage message
**
**	Parameters:
**		smar_ctx -- SMAR context
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
smar_usage(smar_ctx_P smar_ctx)
{
	sm_io_fprintf(smioerr, "usage: smar [options]\n"
		"%s address resolver\n"
		"-A flags   Set aliases flags (localpart, localdomain, all)\n"
		"-D         Print compile time options\n"
#if SMAR_DEBUG
		"-d n       Debug level\n"
#endif
		"-f name    Name of configuration file [none]\n"
#if SM_HEAP_CHECK
		"-H n       Heap check level\n"
#endif
		"-i IPv4    Use IPv4 address for name server [127.0.0.1]\n"
		"           (up to %u can be specified)\n"
		"-O n       DNS query timeout [%ds]\n"
#if SMAR_TEST
		"-R n       Cause a resolver error if random() < n\n"
#endif
		"-r         Do not read /etc/resolv.conf\n"
		"-S a=name  Socket for client communication (QMGR/SMTPS) [%s]\n"
		"-T         Use TCP for DNS queries instead of UDP\n"
		"-U         Use connect(2) for UDP\n"
		"-v n       Loglevel\n"
		"-V         Show version information (can be specified multiple times)\n"
		, MTA_VERSION_STR
		, SM_DNS_MAX_TSKS	/* -i */
		, DNS_TIMEOUT		/* -O */
		, smar_ctx->smar_cnf.smar_cnf_sock
		);
	return sm_error_perm(SM_EM_AR, SM_E_USAGE);
}

/*
**  SMAR_RDCF -- read configuration
**
**	Parameters:
**		smar_ctx -- SMAR context
**		argc -- number of arguments
**		argv -- vector of arguments
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
smar_rd_resolv_conf(smar_ctx_P smar_ctx)
{
	uint ui;
	char buf[64], *cp;
	FILE *fp;

#define	MATCH(line, name) \
	(!strncmp(line, name, sizeof(name) - 1) && \
	(line[sizeof(name) - 1] == ' ' || \
	 line[sizeof(name) - 1] == '\t'))

	if ((fp = fopen("/etc/resolv.conf", "r")) != NULL) {
		while (fgets(buf, sizeof(buf), fp) != NULL) {
			if ((ui = smar_ctx->smar_dns_ntsks) < SM_DNS_MAX_TSKS &&
			    MATCH(buf, "nameserver"))
			{
				cp = buf + sizeof("nameserver") - 1;
				while (*cp == ' ' || *cp == '\t')
					cp++;

				/* NO error checks! */
				smar_ctx->smar_nameserveripv4s[ui] = (ipv4_T) inet_addr(cp);
				++smar_ctx->smar_dns_ntsks;
			}
		}
		(void) fclose(fp);
	}
	return SM_SUCCESS;
}

/*
**  SMAR_RDCF -- read configuration
**
**	Parameters:
**		smar_ctx -- SMAR context
**		argc -- number of arguments
**		argv -- vector of arguments
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
smar_rdcf(smar_ctx_P smar_ctx, int argc, char *argv[])
{
	int c, list_mapc;
	uint ui, showversion;
	sm_ret_T ret;
	char *s;

	SM_IS_SMAR_CTX(smar_ctx);

	showversion = 0;
	list_mapc = -1;
	ret = SM_SUCCESS;
	while ((c = getopt(argc, argv, "A:Dd:f:H:hi:l:O:R:r:S:TUv:V?")) != -1
		&& ret == SM_SUCCESS)
	{
		switch (c) {
		  case 'A':
			smar_ctx->smar_cnf.smar_cnf_alias_fl = strtoul(optarg, NULL, 0);
			break;
		  case 'D':
			smar_showcopt(smar_ctx);
			ret = sm_error_info(SM_EM_AR, SM_E_DONTSTART);
			break;
		  case 'd':
			ui = (uint) atoi(optarg);
#if SMAR_DEBUG
			smar_debug = ui;
#endif
#if RCBCOMM_DEBUG
			rcbcomm_debug = ui;
#endif
			break;
		  case 'f':
			s = strdup(optarg);
			if (NULL == s) {
				sm_io_fprintf(smioerr,
					"sev=ERROR, func=smar_rdcf, argument=\"%@s\", strdup=%m\n"
					, optarg, sm_err_temp(errno));
				ret = sm_error_perm(SM_EM_Q_START, EX_OSERR);
				break;
			}
			smar_ctx->smar_cnf.smar_cnf_conffile = s;
			ret = smar_read_cnf(smar_ctx, s, &smar_ctx->smar_cnf.smar_cnf_smc);
			if (sm_is_err(ret)) {
				sm_io_fprintf(smioerr,
					"sev=ERROR, func=smar_rdcf, status=invalid_configuration_file, error=%m\n"
					, ret);
			}
			break;

		  case 'H':
#if SM_HEAP_CHECK
			SmHeapCheck = atoi(optarg);
#endif
			break;
		  case 'i':
#if SMAR_USE_DNS
			if ((ui = smar_ctx->smar_dns_ntsks) < SM_DNS_MAX_TSKS) {
				smar_ctx->smar_nameserveripv4s[ui] = (ipv4_T) inet_addr(optarg);
				++smar_ctx->smar_dns_ntsks;
			}
			else {
				sm_io_fprintf(smioerr,
					"sev=ERROR, func=smar_rdcf, status=too many nameservers, max=%u\n"
					, SM_DNS_MAX_TSKS);
				ret = smar_usage(smar_ctx);
			}
#endif /* SMAR_USE_DNS */
			break;
		  case 'l':
			list_mapc = (int) strtol(optarg, NULL, 0);
			break;
		  case 'O':
			ui = (uint) atoi(optarg);
			ret = dns_mgr_set_timeout(smar_ctx->smar_dns_mgr_ctx,
					ui);
			break;

		  case 'R':
#if SMAR_TEST
			smar_ctx->smar_rand_err = atol(optarg);
#endif
			break;

		  case 'r':
			smar_ctx->smar_cnf.smar_cnf_dns_flags &= ~DNS_USE_RSLVCNF;
			break;

		  case 'S':
			if (optarg != NULL && ISALPHA(optarg[0])
			    && optarg[1] == '=' && optarg[2] != '\0')
				ui = 2;
			else {
				ret = smar_usage(smar_ctx);
				break;
			}
			s = NULL;
			switch (optarg[0]) {
			  case 'a':
				s = strdup(optarg + ui);
				if (NULL == s) {
					sm_io_fprintf(smioerr,
						"sev=ERROR, func=smar_rdcf, argument=\"%@s\", strdup=%m\n"
						, optarg + ui
						, sm_err_temp(errno));
					ret = sm_error_perm(SM_EM_Q_START,
							EX_OSERR);
				}
				break;
			  default:
				ret = smar_usage(smar_ctx);
				break;
			}
			switch (optarg[0]) {
			  case 'a':
				smar_ctx->smar_cnf.smar_cnf_sock = s;
				break;
			  default:
				ret = smar_usage(smar_ctx);
				break;
			}
			s = NULL;
			break;

		  case 'T':
			smar_ctx->smar_cnf.smar_cnf_dns_flags |= DNS_TSK_FL_USETCP;
			break;
		  case 'U':
			smar_ctx->smar_cnf.smar_cnf_dns_flags |= DNS_TSK_FL_CONNECTUDP;
			break;
		  case 'v':
			smar_ctx->smar_cnf.smar_cnf_loglevel =
				(uint) atoi(optarg);
			break;

		  case 'V':
			++showversion;
			break;

		  case 'h':
		  case '?':
		  default:
			ret = smar_usage(smar_ctx);
			break;
		}
	}
	if (sm_is_err(ret))
		goto error;
	if (showversion > 0) {
		ret = smar_showversion(smar_ctx, argv[0],
			smar_ctx->smar_cnf.smar_cnf_conffile, showversion);
		goto error;
	}
	if (list_mapc >= 0) {
		sm_maps_P maps;

		maps = NULL;
		ret = sm_maps_init(&maps);
		if (sm_is_err(ret))
			goto error;
		if (NULL == maps) {
			ret = sm_error_perm(SM_EM_AR, SM_E_DONTSTART);
			goto error;
		}
		ret = sm_maps_create(maps);
		if (sm_is_err(ret))
			goto error;
		c = sm_mapc_list(smioout, maps, list_mapc, 0);
		ret = sm_error_perm(SM_EM_AR, SM_E_DONTSTART);
		goto error;
	}
	if (0 == smar_ctx->smar_dns_ntsks) {
		if (SM_IS_FLAG(smar_ctx->smar_cnf.smar_cnf_dns_flags, DNS_USE_RSLVCNF))
			(void) smar_rd_resolv_conf(smar_ctx);
		else {
			smar_ctx->smar_nameserveripv4s[0] = (ipv4_T) htonl(LOCALHOST_IP);
			smar_ctx->smar_dns_ntsks = 1;
		}
	}

	smar_ctx->smar_status = SMAR_ST_CONF;
	return SM_SUCCESS;

  error:
	return ret;
}


syntax highlighted by Code2HTML, v. 0.9.1