/***************************************

    This is part of frox: A simple transparent FTP proxy
    Copyright (C) 2000 James Hollingshead

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

  config.c: configuration file parsing

*****   This file was autogenerated by ./configen.pl from
*****   configs.tpl and configs.c.in
*****   CHANGES TO THIS FILE WILL BE LOST. Please
*****   edit configs.tpl or configs.c.in instead
  
  ***************************************/

#include <sys/stat.h>
#include <fcntl.h>
#include <netdb.h>
#include <pwd.h>
#include <grp.h>
#include <syslog.h>

#include "common.h"

void usage(int option);
void set_defaults(void);

struct options config;

enum vartype { BOOL, FILENAME, DIRECTRY, STRING, ADDRESS, ADDRPRT, INT, PRTRNGE,
	ACL, SUBSECT
};

struct option_array {
	const char *name;
	enum vartype type;
	void *var;
	char cmdline;
	int reloadable;
	int essential;
};

struct option_array opts[] = {
  {"",                FILENAME,&config.config_file, 'f',  FALSE, FALSE},
  {"Listen",          ADDRESS, &config.listen,      '\0', FALSE, FALSE},
  {"Port",            INT,     &config.lport,       'p',  FALSE,  TRUE },
  {"ResolvLoadHack",  STRING,  &config.resolvhack,  '\0', FALSE, FALSE},
  {"BindToDevice",    STRING,  &config.device,      '\0', FALSE, FALSE},
  {"FromInetd",       BOOL,    &config.inetd,       'i',  FALSE, FALSE},
  {"NoDetach",        BOOL,    &config.nodetach,    'N',  FALSE, FALSE},
  {"FTPProxy",        ADDRPRT, &config.ftpproxy,    '\0', TRUE, FALSE},
  {"FTPProxyNoPort",  BOOL,    &config.ftpproxynp,  '\0', TRUE, FALSE},
  {"ControlPorts",    PRTRNGE, &config.contports,   '\0', TRUE, FALSE},
  {"PassivePorts",    PRTRNGE, &config.pasvports,   '\0', TRUE, FALSE},
  {"ActivePorts",     PRTRNGE, &config.actvports,   '\0', TRUE, FALSE},
  {"Timeout",         INT,     &config.timeout,     '\0', TRUE, FALSE},
  {"MaxForks",        INT,     &config.maxforks,    'm',  TRUE, FALSE},
  {"MaxForksPerHost", INT,     &config.maxforksph,  '\0', TRUE, FALSE},
  {"MaxTransferRate", INT,     &config.maxdlrate,   '\0', TRUE, FALSE},
  {"CacheDlRate",     INT,     &config.cachedlrate, '\0', TRUE, FALSE},
  {"MaxUploadRate",   INT,     &config.maxulrate,   '\0', TRUE, FALSE},
  {"User",            STRING,  &config.user,        '\0', FALSE,  TRUE },
  {"Group",           STRING,  &config.group,       '\0', FALSE,  TRUE },
  {"WorkingDir",      STRING,  &config.chroot,      '\0', FALSE,  TRUE },
  {"DontChroot",      BOOL,    &config.dontchroot,  '\0', FALSE, FALSE},
  {"AllowNonAscii",   BOOL,    &config.nonasciiok,  '\0', TRUE, FALSE},
  {"BounceDefend",    BOOL,    &config.bdefend,     '\0', TRUE, FALSE},
  {"SameAddress",     BOOL,    &config.sameaddress, '\0', TRUE, FALSE},
  {"LogFile",         FILENAME,&config.logfile,     '\0', FALSE, FALSE},
  {"LogLevel",        INT,     &config.loglevel,    'l',  TRUE, FALSE},
  {"XferLogging",     BOOL,    &config.xferlogging, '\0', TRUE, FALSE},
  {"PidFile",         FILENAME,&config.pidfile,     '\0', FALSE, FALSE},
  {"APConv",          BOOL,    &config.apconv,      '\0', TRUE, FALSE},
  {"PAConv",          BOOL,    &config.paconv,      '\0', TRUE, FALSE},
  {"DoNTP",           BOOL,    &config.ntp,         '\0', TRUE, FALSE},
  {"NTPAddress",      ADDRPRT, &config.ntpdest,     '\0', TRUE, FALSE},
  {"TcpOutgoingAddr", ADDRESS, &config.tcpoutaddr,  '\0', TRUE, FALSE},
  {"PASVAddress",     ADDRESS, &config.pasvaddress, '\0', TRUE, FALSE},
  {"ACL",             ACL,     &config.acls,        '\0', TRUE, FALSE},
  {"SubSection",      SUBSECT, &config.subsecs,     '\0', TRUE, FALSE},
#ifdef TRANS_DATA
  {"TransparentData", BOOL,    &config.transdata,   '\0', FALSE, FALSE},
#endif
#ifdef USE_CACHE
  {"CacheModule",     STRING,  &config.cachemod,    '\0', FALSE, FALSE},
  {"CacheSize",       INT,     &config.cachesize,   '\0', FALSE, FALSE},
  {"HTTPProxy",       ADDRPRT, &config.httpproxy,   '\0', TRUE, FALSE},
  {"ForceHTTP",       BOOL,    &config.forcehttp,   '\0', TRUE, FALSE},
  {"MinCacheSize",    INT,     &config.mincachesize,'\0', TRUE, FALSE},
  {"StrictCaching",   BOOL,    &config.strictcache, '\0', TRUE, FALSE},
  {"CacheOnFQDN",     BOOL,    &config.usefqdn,     '\0', TRUE, FALSE},
  {"CacheAll",        BOOL,    &config.cacheall,    '\0', TRUE, FALSE},
#endif
#ifdef USE_CCP
  {"CCProgram",       FILENAME,&config.ccpcmd,      '\0', TRUE, FALSE},
  {"UseOldCCP",       BOOL,    &config.oldccp,      '\0', TRUE, FALSE},
#endif
#ifdef DO_VSCAN
  {"VirusScanner",    STRING,  &config.vscanner,    '\0', TRUE, FALSE},
  {"VSOK",            INT,     &config.vscanok,     '\0', TRUE, FALSE},
  {"VSProgressMsgs",  INT,     &config.vscanpm,     '\0', TRUE, FALSE},
#endif
#ifdef DO_SSL
  {"UseSSL",          BOOL,    &config.usessl,      '\0', TRUE, FALSE},
  {"DataSSL",         BOOL,    &config.datassl,     '\0', TRUE, FALSE},
  {"AnonSSL",         BOOL,    &config.anonssl,     '\0', TRUE, FALSE},
#endif
  {0, 0, 0, 0, 0, 0}
};

void print_config(void);
int name2addr(sstr * str, struct in_addr *addr);
int store_acl(sstr * str, struct acl_list *acls);
int store_subsect(sstr * str, struct subsect_list *list);
int set_opt(struct option_array *opt, sstr * str);
u_int16_t *get_portlist(sstr * str);
int process_opts(void);
int parse_line(sstr * buf);
int match_acl(struct acl_item *acl, struct sockaddr_in *src,
	      struct sockaddr_in *dst, const char *user);

static char cmdline_set[100] = { 0 };
static int rereading = FALSE;
static int sublevel = 0;
static int line_no;

/* ------------------------------------------------------------- **
** Functions to read / parse the config file.
** ------------------------------------------------------------- */
int process_cmdline(int argc, char *argv[])
{
	int i;
	struct option_array *opt;
	sstr *arg;
	char *optp, *argp;

	set_defaults();
	if (argc <= 1)
		return 0;

	arg = sstr_init(0);

	i = 1;
	optp = argv[i] + 1;

	while (i < argc) {
		if (argv[i][0] != '-')
			usage(-1);
		opt = opts;
		if (*optp == '-')
			usage(-1);	/*?TODO longopts */
		else {
			while (opt->name != NULL && opt->cmdline != *optp)
				opt++;
			if (opt->name == NULL)
				usage(*optp);
			if (strlen(cmdline_set) < 99)
				cmdline_set[strlen(cmdline_set)] = *optp;
			if (opt->type == BOOL) {
				sstr_cpy2(arg, "yes");
				set_opt(opt, arg);
				if (*(++optp) == 0) {
					i++;
					optp = argv[i] + 1;
				}
			} else {
				argp = optp + 1;
				if (*argp == 0) {
					if (i >= argc - 1 ||
					    *(argp = argv[++i]) == '-')
						usage(*optp);

				}
				sstr_cpy2(arg, argp);
				set_opt(opt, arg);
				i++;
				optp = argv[i] + 1;
			}
		}
	}
	sstr_free(arg);
	return (0);
}

/*This is out here as a global so that if config is copied to a backup
  location read_config can be called recursively to read a subsection.*/
FILE *fp = NULL;

int read_config()
{
	struct option_array *opt;
	sstr *buf;

	if (!fp) {
		if (config.config_file == NULL) fp = fopen(CONFIG_FILE, "r");
		else fp = fopen(config.config_file, "r");
		if (fp == NULL) return (-1);
		line_no=0;
	}

	buf = sstr_init(0);
	while (sstr_fgets(buf, fp) != NULL) {
		line_no++;
		switch (parse_line(buf)) {
		case -1:
			sstr_free(buf);
			if (sublevel == 0) fclose(fp);
			return (-1);
		case 0:
			break;

		case 1:	/* end subsection */
			sstr_free(buf);
			return (0);
		}
	};
	sstr_free(buf);
	fclose(fp);
	fp = NULL;

	opt = opts;
	while (opt->name != NULL && !opt->essential)
		opt++;
	if (opt->name != NULL) {
		fprintf(stderr, "Essential option \"%s\" not specified.\n",
			opt->name);
		return (-1);
	}
	process_opts();
#ifdef DEBUG
	if(!config.inetd) print_config();
#endif
	return (0);
}

int parse_line(sstr * buf)
{
	struct option_array *opt;
	sstr *tok;
	tok = sstr_init(0);

	if (sstr_getchar(buf, 0) == '#')
		return (0);
	if (sstr_token(buf, tok, " =\t\r\n", 0) == -1)
		return (0);

	if (!sstr_casecmp2(tok, "EndSection"))
		return (sublevel > 0 ? 1 : -1);

	opt = opts;
	while (opt->name != NULL && sstr_casecmp2(tok, opt->name))
		opt++;
	if (opt->name == NULL) {
		fprintf(stderr,"Unrecognised option \"%s\" at line %d of %s\n",
			sstr_buf(tok), line_no, config.config_file);
		return (-1);
	}
	if (opt->cmdline && strchr(cmdline_set, opt->cmdline)) {
		fprintf(stderr,
			"\"%s\" specified on the command line: ignored\n",
			opt->name);
		return (0);
	}
	if (set_opt(opt, buf) == -1) {
		fprintf(stderr, "Invalid argument to %s at line %d of %s\n",
			opt->name, line_no, config.config_file);
		return (-1);
	}
	return (0);
}

void usage(int option)
{
	if (option != 'h' && option != -1)
		fprintf(stderr, "Unknown option %c\n", option);
	fprintf(stderr, "Usage frox [-f config_file] [-h]\n");
	exit(option == 'h' ? 0 : -1);
}

void set_defaults(void)
{
	sstr *tmp;
	tmp = sstr_init(0);

	config.resolvhack = NULL;
	config.device = NULL;
	sstr_cpy2(tmp, "ControlPorts 40000-50000");
	parse_line(tmp);
	sstr_cpy2(tmp, "PassivePorts 40000-50000");
	parse_line(tmp);
	sstr_cpy2(tmp, "ActivePorts 40000-50000");
	parse_line(tmp);
	config.timeout = 300;
	config.user = NULL;
	config.group = NULL;
	config.nonasciiok = FALSE;
	config.bdefend = TRUE;
	config.loglevel = 15;
	config.xferlogging = TRUE;
#ifdef TRANS_DATA
#endif
#ifdef USE_CACHE
	config.cachemod = NULL;
#endif
#ifdef USE_CCP
	config.ccpcmd = NULL;
	config.oldccp = TRUE;
#endif
#ifdef DO_VSCAN
	config.vscanner = NULL;
	config.vscanok = 0;
	config.vscanpm = 0;
#endif
#ifdef DO_SSL
	config.datassl = TRUE;
	config.anonssl = TRUE;
#endif

	sstr_free(tmp);
}

/*Any final processing that needs to be done*/
int process_opts(void)
{
	if (config.chroot) {	/*So that filename stripping works properly */
		if (config.chroot[strlen(config.chroot) - 1] == '/')
			config.chroot[strlen(config.chroot) - 1] = 0;
	}

	/* Get uid/gid before we chroot */

	if (config.group) {
		struct group *grp;
		if ((grp = getgrnam(config.group)) == NULL) {
			fprintf(stderr, "Unable to find group %s\n",
				config.group);
			return (-1);
		}
		config.gid = grp->gr_gid;
	}

	if (config.user) {
		struct passwd *pw;
		if ((pw = getpwnam(config.user)) == NULL) {
			fprintf(stderr, "Unable to find user %s\n",
				config.user);
			return (-1);
		}
		config.uid = pw->pw_uid;
	}

	config.listen_address.sin_addr = config.listen;
	config.listen_address.sin_port = htons(config.lport);
	config.listen_address.sin_family = AF_INET;
	return (0);
}

int reread_config(void)
{
	int i;
	struct acl_list acls;

	acls = config.acls;
	config.acls.num = 0;
	config.acls.list = NULL;

	rereading = TRUE;
	sublevel = 0;
	i = read_config();

	if (i == -1)
		config.acls = acls;
	else
		free(acls.list);

	return (i);
}

int set_opt(struct option_array *opt, sstr * str)
{
	sstr *tok;
	int sep;

	if (rereading && !opt->reloadable)
		return (0);

	tok = sstr_init(0);

	sstr_ncat2(str, "\n", 1);	/*For sstr_token */
	opt->essential = FALSE;

	switch (opt->type) {
	case BOOL:
		sstr_token(str, tok, " \t\r\n", 0);
		if (!sstr_casecmp2(tok, "yes"))
			*(int *) opt->var = TRUE;
		else if (!sstr_casecmp2(tok, "no"))
			*(int *) opt->var = FALSE;
		else
			return (-1);
		break;
	case FILENAME:
	case STRING:
		sstr_token(str, tok, " \t\r\n", SSTR_QTOK);
		*(char **) opt->var = strdup(sstr_buf(tok));
		break;
	case DIRECTRY:
		sstr_token(str, tok, " \t\r\n", SSTR_QTOK);
		if (sstr_getchar(tok, sstr_len(tok) - 1) != '/')
			sstr_ncat2(tok, "/", 1);
		*(char **) opt->var = strdup(sstr_buf(tok));
		break;
	case ADDRESS:
		sstr_token(str, tok, " \t\r\n", 0);
		return name2addr(tok, (struct in_addr *) opt->var);
		break;
	case ADDRPRT:
		sep = sstr_token(str, tok, " :\t\r\n", 0);
		if( name2addr(tok, &((struct sockaddr_in *) opt->var)->sin_addr)==-1)
			return(-1);
		if (sep == ':')
			((struct sockaddr_in *) opt->var)->sin_port
			    = ntohs(sstr_atoi(str));
		((struct sockaddr_in *) opt->var)->sin_family = AF_INET;
		break;
	case INT:
		sstr_token(str, tok, " \t\r\n", 0);
		*(int *) opt->var = sstr_atoi(tok);
		break;
	case PRTRNGE:
		sstr_token(str, tok, " \t-,\r\n", 0);
		((int *) opt->var)[0] = sstr_atoi(tok);
		((int *) opt->var)[1] = sstr_atoi(str);
		if(!valid_uint16(((int *) opt->var)[0]) ||
		   !valid_uint16(((int *) opt->var)[1])) {
			fprintf(stderr, "Port out of range\n");
			return -1;
		}
		if(((int *) opt->var)[0] >= ((int *) opt->var)[1]) {
			fprintf(stderr, "Port range is inverted\n");
			return -1;
		}
		break;
	case ACL:
		return store_acl(str, (struct acl_list *) opt->var);
		break;
	case SUBSECT:
		return store_subsect(str, (struct subsect_list *) opt->var);
	}
	sstr_free(tok);
	return (0);
}

int name2addr(sstr * str, struct in_addr *addr)
{
	int c;
	c = sstr_getchar(str, 0);
	if (sstr_len(str) == 1 && c == '*') {
		addr->s_addr = INADDR_ANY;
		return (0);
	}
	if (c < '0' || c > '9') {
		struct hostent *hostinfo;

		hostinfo = gethostbyname(sstr_buf(str));
		if (hostinfo == NULL) {
			fprintf(stderr, "Unable to resolve host %s\n",
				sstr_buf(str));
			return (-1);
		}
		*addr = *((struct in_addr *) *hostinfo->h_addr_list);
	} else {
		if (inet_aton(sstr_buf(str), addr) == 0)
			return (-1);
	}
	return (0);
}

int addrange_match(struct in_addr addr, char *range)
{
	char *s;
	if(*range=='*') return TRUE;
	else if((s=strchr(range, '/'))) {
		struct in_addr a, m;
		s = strchr(range, '/');
		*s++='\0';
		if(inet_aton(range, &a) == 0) {
			write_log(ERROR, "Unable to parse ACL. Ignoring.");
			return FALSE;
		}
		if(strchr(s, '.')) {
			if (inet_aton(s, &m) == 0) {
				write_log(ERROR,
					  "Unable to parse ACL. Ignoring.");
				return FALSE;
			}
		} else {
			int i = atoi(s);
			m.s_addr = htonl(0xFFFFFFFF << (32-i));
		}
		if((addr.s_addr & m.s_addr) == (a.s_addr & m.s_addr))
			return TRUE;
		else return FALSE;
	} else if(*range >= '0' && *range<='9') {
		struct in_addr a;
		if(inet_aton(range, &a) == 0) {
			write_log(ERROR, "Unable to parse ACL. Ignoring.");
			return FALSE;
		}
		if(a.s_addr == addr.s_addr) return TRUE;
		else return FALSE;
	} else {
		struct hostent *hostinfo;
		struct in_addr *ap;
		int i;

		hostinfo = gethostbyname(range);
		if (hostinfo == NULL) {
			write_log(ERROR, "Unable to resolve host %s in ACL.",
				  range);
			return FALSE;
		}
		for(i=0;hostinfo->h_addr_list[i];i++){
			ap = (struct in_addr *) hostinfo->h_addr_list[i];
			if(ap->s_addr == addr.s_addr) return TRUE;
		}
		return FALSE;
	}
}

int store_addressrange(sstr * str, struct in_addr *addr, struct in_addr *msk)
{
	sstr *tok;
	int sep, i;
	tok = sstr_init(0);

	sep = sstr_token(str, tok, " /->\r\n", 0);
	if(name2addr(tok, addr)==-1) return(-1);

	if (sep == '/') {
		sep = sstr_token(str, tok, " /->\r\n", 0);
		if (sstr_chr(tok, '.')==-1) {	/* x.x.x.x/yy */
			i = sstr_atoi(tok);
			msk->s_addr = htonl(0xFFFFFFFF << (32 - i));
		} else
			if(name2addr(tok, msk)==-1) return(-1);
	} else {		/*No mask given */
		if (addr->s_addr == INADDR_ANY)
			msk->s_addr = INADDR_ANY;
		else
			msk->s_addr = INADDR_BROADCAST;
	}
	sstr_free(tok);
	return (0);
}

u_int16_t *get_portlist(sstr * str)
{
	sstr *tok;
	int i = 0, sep;
	u_int16_t *ports = NULL;
	tok = sstr_init(0);

	do {
		ports = realloc(ports, (i + 2) * sizeof(u_int16_t));
		if(!ports) die(ERROR, "Out of memory reading config file",
			       0, 0, -1);
		sep = sstr_token(str, tok, " \t,-\r\n", 0);
		if (sep == -1)
			break;

		if (!sstr_cmp2(tok, "*")) {
			ports[i] = 1;
			ports[i + 1] = 0xFFFF;
			i += 2;
			break;
		}
		ports[i] = ports[i + 1] = sstr_atoi(tok);

		if (sep == '-') {
			sep = sstr_token(str, tok, " \t,\n", 0);
			if (sep == -1)
				ports[i + 1] = 0xFFFF;
			else
				ports[i + 1] = sstr_atoi(tok);
		}
		i += 2;
	} while (sep != -1);

	ports[i] = ports[i + 1] = 0;
	sstr_free(tok);
	return (ports);
}

void parse_match(sstr *str, struct acl_item *item)
{
	sstr *tok;
	tok = sstr_init(0);

	item->user = NULL;
	sstr_token(str, tok, " \t\n", 0);
	item->src = strdup(sstr_buf(tok));
	sstr_token(str, NULL, " \t\n", 0); /*Removes the " - " from the ACL*/
	if(sstr_chr(str, '@')!=-1) {
		sstr_token(str, tok, "@", 0);
		item->user = strdup(sstr_buf(tok));
		config.fakentp = TRUE;
	}
	sstr_token(str, tok, " \t\n", 0);
	item->dst = strdup(sstr_buf(tok));
	item->ntp_ports = get_portlist(str);

	sstr_free(tok);
}

int store_acl(sstr * str, struct acl_list *acls)
{
	sstr *tok;
	struct acl_item item;

	tok = sstr_init(0);

	sstr_token(str, tok, " \t\n", 0);

	if (!sstr_casecmp2(tok, "Allow"))
		item.action = ALLOW;
	else if (!sstr_casecmp2(tok, "Deny"))
		item.action = DENY;
	else
		return (-1);
	sstr_free(tok);

	parse_match(str, &item);

	acls->list = realloc(acls->list,
			     (acls->num + 1) * sizeof(struct acl_item));
	if(!acls->list) return(-1);
	memcpy(acls->list + acls->num, &item, sizeof(struct acl_item));
	acls->num++;
	return (0);
}

/* This is quite horrible, but it does seem to work... We make a
   shallow copy of config, and then recall read_config. config will
   now be changed with any additional options in the subsection - we
   copy it where we want it, and restore config. The copy is only a
   shallow one, but we get away with it as other funcs overwrite
   pointers not their contents. */
int store_subsect(sstr * str, struct subsect_list *subsecs)
{
	struct options tmp;
	struct subsect item;

	parse_match(str, &item.match);

	memcpy(&tmp, &config, sizeof(struct options));
	sublevel++;
	config.acls.list = NULL;
	config.subsecs.list = NULL;
	config.acls.num = config.subsecs.num = 0;

	if (read_config() == -1)
		return (-1);

	sublevel--;
	if(process_opts()==-1) return(-1);

	memcpy(&item.config, &config, sizeof(struct options));
	memcpy(&config, &tmp, sizeof(struct options));

	subsecs->list = realloc(subsecs->list,
				(subsecs->num + 1) * sizeof(struct subsect));
	memcpy(subsecs->list + subsecs->num, &item, sizeof(struct subsect));
	subsecs->num++;
	return (0);
}

/* ------------------------------------------------------------- **
** Alter absolute pathnames to still work after chroot.
** ------------------------------------------------------------- */
void strip_filenames(void)
{
	struct option_array *opt = opts;
	char *filename;

	if (config.chroot == NULL)
		return;

	do {
		if (opt->type != DIRECTRY && opt->type != FILENAME)
			continue;
		filename = *(char **) opt->var;
		if (filename == NULL)
			continue;
		debug2("stripping %s: ", opt->name);
		debug2("%s-->", filename);
		if (strncmp(config.chroot, filename, strlen(config.chroot)))
			*filename = 0;
		else {
			memmove(filename, filename + strlen(config.chroot),
				strlen(filename) - strlen(config.chroot) + 1);
			if(opt->type == DIRECTRY && 
			   filename[strlen(filename)-1]!='/')
				strcat(filename, "/");
		}
		debug2("%s\n", filename);
	} while ((++opt)->name != NULL);

	free(config.chroot);
	config.chroot="/";
}

/* ------------------------------------------------------------- **
** Functions to return config file options.
** ------------------------------------------------------------- */
int match_acl(struct acl_item *acl, struct sockaddr_in *src,
	      struct sockaddr_in *dst, const char *user)
{
	u_int16_t port;
	int i;
	char ftpstr[4];
	const char *userp;
	if(!strcasecmp(user, "anonymous")) {
		strcpy(ftpstr, "ftp");
		userp=ftpstr;
	} else userp = user;

	port = ntohs(dst->sin_port);
	if (addrange_match(src->sin_addr, acl->src) && 
	    addrange_match(dst->sin_addr, acl->dst) &&
	    (!acl->user || !strcasecmp(userp, acl->user))) {
		for (i = 0; acl->ntp_ports[i] != 0; i += 2)
			if (port >= acl->ntp_ports[i] &&
			    port <= acl->ntp_ports[i + 1]) {
				return (TRUE);
			}
		if (i == 0)
			return (TRUE);
	}
	return (FALSE);
}

int config_connectionok(struct sockaddr_in *src, struct sockaddr_in *dst,
		        const char *user)
{
	int i;

	for (i = 0; i < config.acls.num; i++)
		if (match_acl(config.acls.list + i, src, dst, user))
			break;
	if (i != config.acls.num) {
		if (config.acls.list[i].action == ALLOW) {
			config_change(src, dst, user);
			return (TRUE);
		} else
			return (FALSE);
	}
	return (FALSE);
}

void config_change(struct sockaddr_in *src, struct sockaddr_in *dst,
		   const char *user)
{
	int i;

	for (i = 0; i < config.subsecs.num; i++)
		if (match_acl(&config.subsecs.list[i].match, src, dst, user))
			break;

	if (i != config.subsecs.num) {
		write_log(VERBOSE, "Changing to config file subsection %d", i+1);
		memcpy(&config, &config.subsecs.list[i].config, sizeof(config));
	}
}

int config_pasvok(struct sockaddr_in *addr)
{
	if (addr->sin_addr.s_addr == 0)
		return (FALSE);

	if (config.sameaddress) {
		if (addr->sin_addr.s_addr !=
		    info->server_control.address.sin_addr.s_addr) {
			return (FALSE);
		}
	}
	return (TRUE);
}

int config_portok(struct sockaddr_in *addr)
{
	if (addr->sin_addr.s_addr == 0)
		return (FALSE);

	if (config.sameaddress)
		if (addr->sin_addr.s_addr !=
		    info->client_control.address.sin_addr.s_addr)
			return (FALSE);

	if (config.bdefend)
		if (ntohs(addr->sin_port) < 1024)
			return (FALSE);

	return (TRUE);
}

#ifdef DEBUG
void print_config(void)
{
	struct option_array *opt;
	struct subsect *p;
	int i, j;

	opt = opts;
	while (opt->name != NULL) {
		switch (opt->type) {
		case BOOL:
			fprintf(stderr, "%s = %s\n", opt->name,
				*(int *) opt->var == TRUE ? "TRUE" : "FALSE");
			break;
		case FILENAME:
		case DIRECTRY:
		case STRING:
			fprintf(stderr, "%s = %s\n", opt->name,
				*(char **) opt->var);
			break;
		case ADDRESS:
			fprintf(stderr, "%s = %s\n", opt->name,
				inet_ntoa(*(struct in_addr *) opt->var));
			break;
		case ADDRPRT:
			fprintf(stderr, "%s = %s:%d\n", opt->name,
				inet_ntoa(((struct sockaddr_in *) opt->
					   var)->sin_addr),
				htons(((struct sockaddr_in *) opt->var)->
				      sin_port));
			break;
		case INT:
			fprintf(stderr, "%s = %d\n", opt->name,
				*(int *) opt->var);
			break;
		case PRTRNGE:
			fprintf(stderr, "%s = %d-%d\n", opt->name,
				((int *) opt->var)[0], ((int *) opt->var)[1]);
			break;
		case ACL:
		case SUBSECT:
			break;
		}
		opt++;
	}
	for (i = 0; i < config.acls.num; i++) {
		fprintf(stderr, "ACL: %s", config.acls.list[i].src);
		fprintf(stderr, " --> %s @ %s", 
				config.acls.list[i].user ?
				     config.acls.list[i].user : "*",
				config.acls.list[i].dst);
		fprintf(stderr, ": %s ",
			config.acls.list[i].action == DENY ? "DENY" : "ALLOW");
		for (j = 0; config.acls.list[i].ntp_ports[j] != 0; j += 2) {
			fprintf(stderr, "%d-%d,",
				config.acls.list[i].ntp_ports[j],
				config.acls.list[i].ntp_ports[j + 1]);
		}
		fprintf(stderr, "\n");

	}

	for (i = 0; i < config.subsecs.num; i++) {
		struct options tmp;
		p = config.subsecs.list + i;

		fprintf(stderr, "Subsection: %s", p->match.src);
		fprintf(stderr, " --> %s @ %s", 
				p->match.user ? p->match.user : "*",
				p->match.dst);
		for (j = 0; p->match.ntp_ports[j] != 0; j += 2) {
			fprintf(stderr, "%d-%d,", p->match.ntp_ports[j],
				p->match.ntp_ports[j + 1]);
		}
		fprintf(stderr, "  {\n");
		memcpy(&tmp, &config, sizeof(struct options));
		memcpy(&config, &p->config, sizeof(struct options));
		print_config();
		memcpy(&config, &tmp, sizeof(struct options));
		fprintf(stderr, "}\n");
	}
}
#endif


syntax highlighted by Code2HTML, v. 0.9.1