/* $USAGI: servtab.c,v 1.10 2001/01/29 18:38:19 yoshfuji Exp $ */

/*
 * Copyright (C) 2001 USAGI/WIDE Project.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * Copyright (c) 1983, 1991 The Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <netdb.h>
#include <syslog.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#ifdef MULOG
#include <ctype.h>
#endif

#include "inetd.h"
#include "servtab.h"
#include "builtins.h"
#include "sig.h"
#include "mysleep.h"

#ifdef INET6
#include <netinet/ip6.h>
#endif

char servtab_rcsid[] = 
  "$Id: servtab.c,v 1.6 2000/07/22 20:20:50 dholland Exp $";

static struct biltin biltins[] = {
	/* service	socktype	fork,wait   function */

	/* Echo received data */
	{ "echo",	SOCK_STREAM,	1, 0,	echo_stream, },
	{ "echo",	SOCK_DGRAM,	0, 0,	echo_dg, },

	/* Internet /dev/null */
	{ "discard",	SOCK_STREAM,	1, 0,	discard_stream, },
	{ "discard",	SOCK_DGRAM,	0, 0,	discard_dg, },

	/* Return 32 bit time since 1900 */
	{ "time",	SOCK_STREAM,	0, 0,	machtime_stream, },
	{ "time",	SOCK_DGRAM,	0, 0,	machtime_dg,     },

	/* Return human-readable time */
	{ "daytime",	SOCK_STREAM,	0, 0,	daytime_stream,     },
	{ "daytime",	SOCK_DGRAM,	0, 0,	daytime_dg,     },

	/* Familiar character generator */
	{ "chargen",	SOCK_STREAM,	1, 0,	chargen_stream,     },
	{ "chargen",	SOCK_DGRAM,	0, 0,	chargen_dg,     },

	{ NULL, 	0, 		0, 0, 	NULL }
};

struct servtab *
find_service_by_fd(int fd)
{
	struct servtab *sep;
	for (sep = servtab; sep; sep = sep->se_next) {
		if (sep->se_fd == fd) return sep;
	}
	return NULL;
}

struct servtab *
find_service_by_pid(pid_t pid)
{
	struct servtab *sep;
	for (sep = servtab; sep; sep = sep->se_next) {
		if (sep->se_wait == pid) return sep;
	}
	return NULL;
}

static
struct servtab *
find_same_service(struct servtab *cp)
{
	struct servtab *sep;
	for (sep = servtab; sep; sep = sep->se_next) {
		if (sep->se_family != cp->se_family) continue;
		if (strcmp(sep->se_service, cp->se_service)) continue;
		if (strcmp(sep->se_proto, cp->se_proto)) continue;

		/* 
		 * At this point they're the same up to bind address.
		 * Catch 1: se_address can be null.
		 * Catch 2: two different se_addresses can give the same IP.
		 * Catch 3: contents of se_ctrladdr_in.sin_addr are undefined
		 *          if they're unix sockets. But we do know that this
		 *          isn't the case if se_address isn't null.
		 */

		/* Easy case: both null (cannot point to the same string) */
		if (sep->se_address == cp->se_address) return sep;

		/* Now, if one is null, the other isn't */
		if (sep->se_address==NULL || cp->se_address==NULL) continue;

		/* Don't bother to compare the hostnames, just the IPs */
		switch(sep->se_family) {
#ifdef INET6
		case AF_INET6:
			if (memcmp(&sep->se_ctrladdr_in6.sin6_addr, 
		    	       &cp->se_ctrladdr_in6.sin6_addr, 
			       sizeof(struct in6_addr)) == 0) return sep;
			break;
#endif
		case AF_INET:
			if (sep->se_ctrladdr_in.sin_addr.s_addr ==
		            cp->se_ctrladdr_in.sin_addr.s_addr) return sep;
			break;
		default:
			/*XXX*/
		}
	}
	return NULL;
}

const char *
service_name(struct servtab *sep)
{
	static char rv[256];
	if (sep->se_address) {
		snprintf(rv, sizeof(rv), "%s/%s@%s", sep->se_service, 
			 sep->se_proto, sep->se_address);
	}
	else {
		snprintf(rv, sizeof(rv), "%s/%s", sep->se_service, 
			 sep->se_proto);
	}
	return rv;
}

/********* config parser ********************************/

static void loadconfigent(struct servtab *cp);

static FILE *fconfig = NULL;


struct wordmap {
	const char *word;
	int val;
};

static struct wordmap socket_types[] = {
	{ "stream",    SOCK_STREAM    },
	{ "dgram",     SOCK_DGRAM     },
	{ "rdm",       SOCK_RDM       },
	{ "seqpacket", SOCK_SEQPACKET },
	{ "raw",       SOCK_RAW       },
	{ NULL,        -1 }
};

static
int
map_word(struct wordmap *wm, const char *word)
{
	int i;
	for (i=0; wm[i].word!=NULL; i++) {
		if (!strcmp(wm[i].word, word)) return wm[i].val;
	}
	return -1;
}

static
struct biltin *
find_builtin(int socktype, const char *service)
{
	int i;
	for (i=0; biltins[i].bi_service; i++) {
		if (biltins[i].bi_socktype != socktype) continue;
		if (strcmp(biltins[i].bi_service, service)) continue;
		return &biltins[i];
	}
	
	return NULL;
}

static
const char *
assemble_entry(struct servtab *sep, int nfields, char **fields)
{
	char *proto;
	int use_family = PF_UNSPEC;
#ifdef INET6
	int use_v6only = 0;
	struct sockaddr_storage service_home;
	struct addrinfo hints, *res0, *res;
	char *serv = "discard";	/* discard(dummy) */
	int err;
#else
	struct in_addr service_home;
	struct hostent *hp;
#endif
	char *s, *t;
	int i;

	if (nfields < 6) {
		return "Incomplete config entry";
	}

	memset(sep, 0, sizeof(*sep));
	memset(&service_home, 0, sizeof(service_home));

	proto = fields[2];
	if (!strcmp(fields[2], "unix")) {
		use_family = PF_UNIX;
	} else {
		char *p = strchr(proto, '/');
		if (p)
			proto = p + 1;

		if (strncmp(proto, "udp", 3) == 0 || 
		    strncmp(proto, "tcp", 3) == 0) {
			int len = strlen(proto);

			switch(len) {
			case 3:
				use_family = PF_INET;
				break;
			case 4:
				if (*(proto + 3) == '4')
					use_family = PF_INET;
#ifdef INET6
				else if (*(proto + 3) == '6') {
					use_family = PF_INET6;
					use_v6only = 1;
				}
#endif
				else
					return "Invalid / unsupported protocol";
				break;
			case 5:
#ifdef INET6
				if (strcmp(proto + 3, "46") == 0)
					use_family = PF_INET6;
				else
#endif
					return "Invalid / unsupported protocol";
				break;				
			default:
				return "Invalid / unsupported protocol";
			}

			/* now we shall set the protocol name back to the 
			   standard value. */
			*(proto + 3) = '\0';
		}
	}
	
	s = strchr(fields[0], '@');
	if (s) {
		*s++ = 0;
		sep->se_address = s;

		if (use_family == PF_UNIX) {
			return "@host given for unix socket";
		}
	}

#ifdef INET6
	memset(&hints, 0, sizeof(hints));
	hints.ai_family = use_family;
	hints.ai_flags = AI_PASSIVE;

	err = getaddrinfo(s, s ? NULL : serv, &hints, &res0);
	if (err)
		return gai_strerror(err);

	for (res=res0; res; res=res->ai_next){
		switch(res->ai_family){
		case PF_INET:
			((struct sockaddr_in*)res->ai_addr)->sin_port = 0;
			break;
		case PF_INET6:
			((struct sockaddr_in6*)res->ai_addr)->sin6_port = 0;
			break;
		case PF_UNIX:
			((struct sockaddr_un*)res->ai_addr)->sun_path[0] = 0;
			break;
		default:
			/*XXX*/
			break;
		}
	}

	if (res0==NULL)
		return "Service hostname/address not found";

	if ((size_t)res0->ai_addrlen > sizeof(sep->se_ctrladdr_storage)) {
		freeaddrinfo(res0);
		return "sockaddr got from getaddrinfo() was too large";
	}

	memcpy(&service_home, res0->ai_addr, res0->ai_addrlen);
	sep->se_ctrladdr_storage = service_home;
	sep->se_ctrladdr_size = res0->ai_addrlen;
	if (use_v6only)
		sep->se_sockopt_ipv6_only = 1;

	freeaddrinfo(res0);
#else
	switch (use_family) {
	case PF_INET:
		if (s) {
			hp = gethostbyname(s);
			if (hp==NULL)
				return "Service hostname/address not found";
			memcpy(&service_home, hp->h_addr, sizeof(service_home));
		}
		else
			service_home.s_addr = INADDR_ANY;
		sep->se_ctrladdr_size = sizeof(struct sockaddr_in);
		sep->se_ctrladdr_in.sin_family = PF_INET;
		sep->se_ctrladdr_in.sin_addr = service_home;
		break;
	case PF_UNIX:
		sep->se_ctrladdr_size = sizeof(struct sockaddr_un);
		sep->se_ctrladdr_un.sun_family = PF_UNIX;
		strncpy(sep->se_ctrladdr_un.sun_path, sep->se_service, 
			sizeof(sep->se_ctrladdr_un.sun_path));
		sep->se_ctrladdr_un.sun_path[sizeof(sep->se_ctrladdr_un.sun_path)-1] = '\0';
		break;
	default:
		/*XXX*/
	}
#endif

	sep->se_family = use_family;
	sep->se_socktype = map_word(socket_types, fields[1]);
	sep->se_proto = proto;
	sep->se_service = fields[0];

	if (strcmp(fields[2], "unix")) {
		if (!strncmp(sep->se_proto, "rpc/", 4)) {
#ifdef RPC
			s = strchr(sep->se_service, '/');
			if (!s) return "No RPC version";
			*s++ = 0;
			sep->se_rpcversl = strtol(s, &t, 0);
			if (t==s) return "Invalid RPC version";
			if (*t=='-') {
				s = t+1;
				sep->se_rpcversh = strtol(s, &t, 0);
				if (t==s) return "Invalid RPC high version";
			}
			else {
				sep->se_rpcversh = sep->se_rpcversl;
			}
#else
			return "No support for RPC services compiled in";
#endif
		}
	}

	s = strchr(fields[3], '.');
	if (s) {
		*s++ = 0;
		sep->se_max = atoi(s);
	}
	else {
		sep->se_max = TOOMANY;
	}
	sep->se_wait = !strcmp(fields[3], "wait");

	s = strchr(fields[4], '.');
	if (s) {
		*s++ = 0;
		sep->se_group = s;
	}
	else {
		sep->se_group = NULL;
	}
	sep->se_user = fields[4];

	sep->se_server = fields[5];
	if (!strcmp(fields[5], "internal")) {
		sep->se_bi = find_builtin(sep->se_socktype, sep->se_service);
		if (sep->se_bi==NULL) {
			return "Invalid internal service";
		}
		sep->se_wait = sep->se_bi->bi_wait;
	}
	else {
		sep->se_bi = NULL;
	}

	/* The rest are argv[]. */ 
	for (i=6; i<nfields; i++) {
#ifdef MULOG
		char *arg = fields[i], *colon;
		if (i == 6 && (colon = rindex(arg, ':'))) {
			while (arg < colon) {
				int	x;
				char	*ccp;

				switch (*arg++) {
				case 'l':
					x = 1;
					if (isdigit(*arg)) {
						x = strtol(arg, &ccp, 0);
						if (ccp == arg)
							break;
						arg = ccp;
					}
					sep->se_log &= ~MULOG_RFC931;
					sep->se_log |= x;
					break;
				case 'a':
					sep->se_log |= MULOG_RFC931;
					break;
				default:
					break;
				}
			}
			memmove(fields[i], colon+1, strlen(colon+1)+1);
		}
#endif
		sep->se_argv[i-6] = fields[i];
	}

	/* Most programs core if argv[0] is null. */
	if (!sep->se_argv[0]) {
		sep->se_argv[0] = dostrdup(sep->se_server);
	}

	free(fields[1]);
	free(fields[3]);
	return NULL;
}

static
const char *
nexttoken(void)
{
	static char linebuf[256];
	char *s;

	/*
	 * Hack: linebuf[0-1] is not read into; it is always "Q ". This permits
	 * us to initialize strtok so that it will return the first word on
	 * the line on the _next_ call if we so choose.
	 */
	linebuf[0] = 'Q';
	linebuf[1] = ' ';

	s = strtok(NULL, " \t\n");
	if (s!=NULL) return s;

	do {
		if (fgets(linebuf+2, sizeof(linebuf)-2, fconfig)==NULL) {
			return NULL;
		}
	} while (linebuf[2]=='#');

	if (!strchr(" \t", linebuf[2])) {
		/* Not a continuation line - send back EOL */
		strtok(linebuf, " \t\n");  /* returns Q */
		return "";
	}

	s = strtok(linebuf+2, " \t\n");
	if (!s) return ""; /* empty line -> send EOL */
	return s;
}

static
void
loadconfigfile(void (*sendents)(struct servtab *))
{
	/* 6 fields plus argv, but the last entry of argv[] must remain null */
	char *fields[MAXARGV+6-1];
	int nfields=0;
	int warned=0;
	const char *s;
	int eof=0;
	struct servtab assm; /* assemble entries into here */
	char junk[4];

	/*
	 * Insure strtok() will start at EOL.
	 */
	junk[0] = 'Q' ;
	junk[1] = 0;	
	strtok(junk, " \t\n");
	
	while (!eof) {
		s = nexttoken();
		if (!s&& nfields<1) {
			eof = 1;
		}
		else if (!s || (!*s && nfields>0)) {
			const char *errmsg;
			errmsg = assemble_entry(&assm, nfields, fields);
			if (errmsg) {
				syslog(LOG_WARNING, "Bad config for %s: %s"
				       " (skipped)", fields[0], errmsg);
				/* fix memory leak */
				while (nfields) 
					free(fields[--nfields]);
			}
			else {
				sendents(&assm);
			}
                        if (!s) {
	                	eof = 1;
	                        }
	                else {
				nfields = 0;
			       	warned = 0;
			}
		}
		else if (!*s) {
			/* blank line */
		}
		else if (nfields < MAXARGV+6) {
			fields[nfields++] = dostrdup(s);
		}
		else if (!warned) {
			syslog(LOG_WARNING, "%s: too many arguments, max %d",
			       fields[0], MAXARGV);
			warned = 1;
		}
	}
}

/********* config loading ****************************************/

static int
setconfig(void)
{

	if (fconfig != NULL) {
		fseek(fconfig, 0L, SEEK_SET);
		return (1);
	}
	fconfig = fopen(configfile, "r");
	return (fconfig != NULL);
}

static void
endconfig(void)
{
	if (fconfig) {
		(void) fclose(fconfig);
		fconfig = NULL;
	}
}

static void
freeconfig(struct servtab *cp)
{
	int i;

	if (cp->se_service)
		free(cp->se_service);
	cp->se_service = NULL;

	cp->se_address = NULL;  /* points into se_service */

	if (cp->se_proto)
		free(cp->se_proto);
	cp->se_proto = NULL;

	if (cp->se_user)
		free(cp->se_user);
	cp->se_user = NULL;

	cp->se_group = NULL;  /* points into se_user */

	if (cp->se_server)
		free(cp->se_server);
	cp->se_server = NULL;

	for (i = 0; i < MAXARGV; i++) {
		if (cp->se_argv[i])
			free(cp->se_argv[i]);
		cp->se_argv[i] = NULL;
	}
}

/*
 * print_service:
 *	Dump relevant information to stderr
 */
static void
print_service(const char *action, struct servtab *sep)
{
	if (isrpcservice(sep))
		fprintf(stderr,
		    "%s: %s rpcprog=%d, rpcvers = %d/%d, proto=%s, wait.max=%d.%d, user.group=%s.%s builtin=%lx server=%s\n",
		    action, sep->se_service,
		    sep->se_rpcprog, sep->se_rpcversh, sep->se_rpcversl, sep->se_proto,
		    sep->se_wait, sep->se_max, sep->se_user, sep->se_group,
		    (unsigned long)sep->se_bi, sep->se_server);
	else
		fprintf(stderr,
		    "%s: %s proto=%s, wait.max=%d.%d, user.group=%s.%s builtin=%lx server=%s\n",
		    action, sep->se_service, sep->se_proto,
		    sep->se_wait, sep->se_max, sep->se_user, sep->se_group,
		    (unsigned long)sep->se_bi, sep->se_server);
}

static
void
loadconfigent(struct servtab *cp)
{
	struct servtab *sep;
	unsigned n;

	/*
	 * dholland 7/14/1997: always use the canonical service 
	 * name to protect against silly configs that list a 
	 * service twice under different aliases. This has been
	 * observed in the wild thanks to Slackware... 
	 * Note: this is a patch, not a fix. The real fix is
	 * to key the table by port and protocol, not service name
	 * and protocol.
	 */
#ifdef INET6
	if ((cp->se_family==AF_INET || cp->se_family==AF_INET6) && 
		!isrpcservice(cp)) {
#else
	if (cp->se_family==AF_INET && !isrpcservice(cp)) {
#endif
		u_short port = atoi(cp->se_service);
		
		if (!port) {
			struct servent *sp;
			sp = getservbyname(cp->se_service,
					   cp->se_proto);
			if (sp != NULL) { /* bogus services are caught later */
				if (strcmp(cp->se_service, sp->s_name)) {
					/*
					 * Ugh. Since se_address points into
					 * se_service, we need to copy both
					 * together. Ew.
					 */
					char *tmp, *tmp2 = NULL;
					const char *addr = cp->se_address;
					size_t len = strlen(sp->s_name)+1;
					if (addr==NULL) addr = "";
					len += strlen(addr)+1;
					tmp = domalloc(len);
					strcpy(tmp, cp->se_service);
					if (cp->se_address) {
						tmp2 = tmp+strlen(tmp)+1;
						strcpy(tmp2, cp->se_address);
					}
					free(cp->se_service);
					cp->se_service = tmp;
					cp->se_address = tmp2;
				}
			}
		}
	}
	/* End silly patch */

	sep = find_same_service(cp);

	if (sep != NULL) {
		int i;
		
		if (sep->se_checked) {
			syslog(LOG_WARNING, 
			       "extra conf for service %s (skipped)\n",
			       service_name(sep));
			return;
		}
		
#define SWAP(type, a, b) {type c=(type)a; (type)a=(type)b; (type)b=(type)c;}

		/*
		 * sep->se_wait may be holding the pid of a daemon
		 * that we're waiting for.  If so, don't overwrite
		 * it unless the config file explicitly says don't 
		 * wait.
		 */
		if (cp->se_bi == 0 && (sep->se_wait == 1 || cp->se_wait == 0))
			sep->se_wait = cp->se_wait;
		if (cp->se_max != sep->se_max)
			SWAP(int, cp->se_max, sep->se_max);
		if (cp->se_user)
			SWAP(char *, sep->se_user, cp->se_user);
		if (cp->se_group)
			SWAP(char *, sep->se_group, cp->se_group);
		if (cp->se_server)
			SWAP(char *, sep->se_server, cp->se_server);
		if (cp->se_address) {
			/* must swap se_service; se_address points into it */
			SWAP(char *, sep->se_service, cp->se_service);
			SWAP(char *, sep->se_address, cp->se_address);
		}
		for (i = 0; i < MAXARGV; i++)
			SWAP(char *, sep->se_argv[i], cp->se_argv[i]);
#undef SWAP
		if (isrpcservice(sep))
			unregister_rpc(sep);
		sep->se_rpcversl = cp->se_rpcversl;
		sep->se_rpcversh = cp->se_rpcversh;
		freeconfig(cp);
		if (debug) {
			print_service("REDO", sep);
		}
	}
	else {
		sep = enter(cp);
		if (debug)
			print_service("ADD ", sep);
	}
	sep->se_checked = 1;
	
	switch (sep->se_family) {
	case AF_UNIX:
		if (sep->se_fd != -1)
			break;
		(void)unlink(sep->se_service);
		n = strlen(sep->se_service);
		if (n > sizeof(sep->se_ctrladdr_un.sun_path) - 1) 
			n = sizeof(sep->se_ctrladdr_un.sun_path) - 1;
		strncpy(sep->se_ctrladdr_un.sun_path, 
			sep->se_service, n);
		sep->se_ctrladdr_un.sun_path[n] = 0;
		sep->se_ctrladdr_un.sun_family = AF_UNIX;
		sep->se_ctrladdr_size = n +
			sizeof sep->se_ctrladdr_un.sun_family;
		setup(sep);
		break;
	case AF_INET:
#ifdef INET6
	case AF_INET6:
#endif
		sep->se_family = sep->se_family;
#ifdef INET6
		sep->se_ctrladdr_size = sep->se_family == AF_INET ?
					sizeof(struct sockaddr_in) :
					sizeof(struct sockaddr_in6);
#else
		sep->se_ctrladdr_size = sizeof(struct sockaddr_in);
#endif
		if (isrpcservice(sep)) {
			struct rpcent *rp;
			
			sep->se_rpcprog = atoi(sep->se_service);
			if (sep->se_rpcprog == 0) {
				rp = getrpcbyname(sep->se_service);
				if (rp == 0) {
					syslog(LOG_ERR,
					       "%s: unknown service",
					       sep->se_service);
					return;
				}
				sep->se_rpcprog = rp->r_number;
			}
			if (sep->se_fd == -1)
				setup(sep);
			if (sep->se_fd != -1)
				register_rpc(sep);
		} 
		else {
			u_short port = atoi(sep->se_service);
			
			if (!port) {
				struct servent *sp;
				sp = getservbyname(sep->se_service,
						   sep->se_proto);
				if (sp == 0) {
					syslog(LOG_ERR,
					       "%s: unknown service",
					       service_name(sep));
					return;
				}
				/* the s_port field of the struct servent 
				 * returned by getservbyname is already in 
				 * network byte order, so there is no need
			 	 * to call htons on sp->s_port. */
				port = ntohs(sp->s_port);
			}
			if (port != sockaddr_port(&(sep->se_ctrladdr),
					          sep->se_ctrladdr_size)) {
#ifdef INET6
				if (sep->se_family == AF_INET6)
					sep->se_ctrladdr_in6.sin6_port = htons(port);
				else
#endif
					sep->se_ctrladdr_in.sin_port = htons(port);
				if (sep->se_fd != -1) {
					closeit(sep);
				}
			}
			if (sep->se_fd == -1)
				setup(sep);
		}
	}
}

void
config(int signum)
{
	register struct servtab *sep, **sepp;

	(void)signum;

	if (!setconfig()) {
		syslog(LOG_ERR, "%s: %m", configfile);
		return;
	}
	for (sep = servtab; sep; sep = sep->se_next)
		sep->se_checked = 0;

	loadconfigfile(loadconfigent);

	endconfig();
	/*
	 * Purge anything not looked at above.
	 */
	sepp = &servtab;
	while ((sep = *sepp) != NULL) {
		if (sep->se_checked) {
			sepp = &sep->se_next;
			continue;
		}
		*sepp = sep->se_next;
		if (sep->se_fd != -1) {
			closeit(sep);
		}
		if (isrpcservice(sep))
			unregister_rpc(sep);
		if (sep->se_family == AF_UNIX)
			(void)unlink(sep->se_service);
		if (debug)
			print_service("FREE", sep);
		freeconfig(sep);
		free((char *)sep);
	}
}

/********* SIGALRM handler ******************************/

void
restart_services(void)
{
	struct servtab *sep;
	for (sep = servtab; sep; sep = sep->se_next) {
		if (sep->se_fd == -1) {
			switch (sep->se_family) {
			case AF_UNIX:
#ifdef INET6
			case AF_INET6:
#endif
			case AF_INET:
				setup(sep);
				if (sep->se_fd != -1 && isrpcservice(sep))
					register_rpc(sep);
				break;
			}
		}
	}
}



