/*
**  Copyright (c) 1991 Bolt Beranek and Newman, Inc.
**  All rights reserved.
**
**  Redistribution and use in source and binary forms are permitted
**  provided that: (1) source distributions retain this entire copyright
**  notice and comment, and (2) distributions including binaries display
**  the following acknowledgement:  ``This product includes software
**  developed by Bolt Beranek and Newman, Inc. and CREN/CSNET'' in the
**  documentation or other materials provided with the distribution and in
**  all advertising materials mentioning features or use of this software.
**  Neither the name of Bolt Beranek and Newman nor CREN/CSNET may be used
**  to endorse or promote products derived from this software without
**  specific prior written permission.
**
**  THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
**  WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
**  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
/*
 * Copyright (c) 1992 Purdue University
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by Purdue University.  The name of the University may not be used
 * to endorse or promote products derived * from this software without
 * specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * Note: this copyright applies to portions of this software developed
 * at Purdue beyond the software covered by the original copyright.
 */
#include <stdio.h>
#include <ctype.h>
#include <netdb.h>
#include <memory.h>
#include <malloc.h>
#include <string.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/stream.h>
#include <sys/ioctl.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <sys/dlpi.h>

#include "ppp.h"
#include "dp.h"
#include "dpio.h"
#include "dpd.h"
#include "dpconf.h"


/*
**  Some systems don't have these in their <netinet/in.h>
*/
#ifndef	IPPROTO_EGP
#define IPPROTO_EGP	8
#endif	/* IPPROTO_EGP */
#ifndef	IPPROTO_RDP
#define IPPROTO_RDP	27
#endif	/* IPPROTO_EGP */


#define EQ(a, b)	(strcmp((a), (b)) == 0)
#define nexttok()	strtok((char *)NULL, SEPARATORS)


extern int		linenum;

static int		errorcount;
static char		SEPARATORS[] = " \t";
static REMOTE		*RemoteTable;
static MODEM		*ModemTable;
int			RemoteCount;
static int		ModemCount;
static char		WHERE[] = "readconfig";
extern void		d_vlog();

extern long		strtol();

/*
**  Find this interface in our list.
*/
REMOTE *
findconfig(device)
    register char	*device;
{
    register REMOTE	*rp;
    register int	i;

    for (rp = RemoteTable, i = RemoteCount; --i >= 0; rp++)
	if (strcmp(device, rp->Device) == 0)
	    return rp;
    return NULL;
}

/*
**  Find this interface in our list by login name.
*/
REMOTE *
findlogin(login)
    register char	*login;
{
    register REMOTE	*rp;
    register int	i;

    for (rp = RemoteTable, i = RemoteCount; --i >= 0; rp++)
	if (strcmp(login, rp->Login) == 0)
	    return rp;
    return NULL;
}

/*
**  Find this modem or modem rotary in our list.
*/
MODEM *
findmodem(modem)
    register char	*modem;
{
    register MODEM	*mp;
    register int	i;

    for (mp = ModemTable, i = ModemCount; --i >= 0; mp++)
	if (strcmp(modem, mp->Name) == 0)
	    return mp;
    return NULL;
}


/*
 * Functions to de-allocate memory that is allocated for each configuration
 */
free_remotes()
{
    REMOTE *rp;
    int i;
    if (RemoteTable) {
	for (rp = RemoteTable, i = RemoteCount; --i >= 0; rp++)
	    free_remote(rp);
	free((char *)RemoteTable);
    }
    RemoteCount = 0;
}

free_remote(rp)
REMOTE *rp;
{
    if (rp->Sitename)
	(void)free(rp->Sitename);
    if (rp->Login)
	(void)free(rp->Login);
    if (rp->Phone)
	(void)free(rp->Phone);
    if (rp->Script)
	(void)free(rp->Script);
    if (rp->ScriptArgs) {
	if (rp->ScriptArgs[0])
	    (void)free(rp->ScriptArgs[0]);
	(void)free((char *)rp->ScriptArgs);
    }
    if (rp->Transcript)
	(void)free(rp->Transcript);
    if (rp->PppArgs) {
	if (rp->PppArgs[0])
	    (void)free(rp->PppArgs[0]);
	(void)free((char *)rp->PppArgs);
    }
    if (rp->Modems)
	(void)free((char *)rp->Modems);
    if (rp->AuxProgram)
	(void)free(rp->AuxProgram);
    if (rp->AuxArgs) {
	if (rp->AuxArgs[0])
	    (void)free(rp->AuxArgs[0]);
	(void)free((char *)rp->AuxArgs);
    }
}

free_modems()
{
    MODEM *mp;
    int i;
    if (ModemTable) {
	for (mp = ModemTable, i = ModemCount; --i >= 0; mp++)
	    free_modem(mp);
	free((char *)ModemTable);
    }
    ModemCount = 0;
}

free_modem(mp)
MODEM *mp;
{
    if (mp->Name)
	(void)free(mp->Name);
    if (mp->Line)
	(void)free(mp->Line);
    if (mp->DialScript)
	(void)free(mp->DialScript);
    if (mp->DialCharMap)
	(void)free(mp->DialCharMap);
    if (mp->Modems)
	(void)free((char *)mp->Modems);
}

/*
**  Is the host (or its network) on the list of hosts and networks?
*/
int
hostinlist(list, addr)
    struct in_addr	*list;
    struct in_addr	addr;
{
    register int	i;
    register int	j;

    j = i = addr.s_addr % MAXHOSTS;

    do {
	if (list[j].s_addr == 0)
	    return 0;
	if (list[j].s_addr == addr.s_addr)
	    return 1;
	if (inet_lnaof(list[j]) == 0
	 && inet_netof(list[j]) == inet_netof(addr))
	    return 1;

	if (++j == MAXHOSTS)
	    j = 0;
    } while (j != i);

    return 0;
}


#include <varargs.h>

void
yyerror(va_alist)
va_dcl
{
    va_list ap;
    char *path,
	 *fmt,
	 buff[256];

    va_start(ap);
    path = va_arg(ap, char *);
    fmt = va_arg(ap, char *);
    (void)sprintf(buff, "Error near line %d of\n\t\"%s\":  %s",
	linenum, path, fmt);
    d_vlog(DLOG_GENERAL, WHERE, buff, ap);
    va_end(ap);
    errorcount++;
}


static void
parseprotocols(path, lp, set)
    char	*path;
    u_long	*lp;
    int		set;
{
    char	*p;
    int		i;
    struct protoent *pe;

    setprotoent(1);
    while (p = nexttok()) {
	if (pe = getprotobyname(p))
	    i = pe->p_proto;
	else {
	    yyerror(path, "Bad protocol \"%s\"", p);
	    continue;
	}
	if (set == P_SET)
	    lp[P_WORD(i)] |=  P_BIT(i);
	else
	    lp[P_WORD(i)] &= ~P_BIT(i);
    }
    endprotoent();
}


static int
positivenumber(p)
    char	*p;
{
    if (*p == '\0')
	return 0;
    for ( ; *p; p++)
	if (!isdigit(*p))
	    return 0;
    return 1;
}


static long
parsetime(path)
    char	*path;
{
    long	l;
    int		i;
    char	*p;

    for (l = 0; p = nexttok(); )
	if (!positivenumber(p) || (i = atoi(p)) < 0 || i > 23)
	    yyerror(path, "Bad hour \"%s\"", p);
	else
	    l |= (1L << i);
    return l;
}


static int
addhosttolist(list, addr)
    struct in_addr	*list;
    struct in_addr	addr;
{
    register int	i;
    register int	j;

    j = i = addr.s_addr % MAXHOSTS;
    do {
	if (list[j].s_addr == 0) {
	    list[j] = addr;
	    return 1;
	}
	if (++j == MAXHOSTS)
	    j = 0;
    } while (j != i);

    return 0;
}



static int
parseaddresslist(path, list)
    char		*path;
    struct in_addr	*list;
{
    char		*p;
    struct in_addr	new;
    int			i;
    struct hostent	*hp;
    struct netent	*np;
    struct in_addr	in;

    for (i = 0; p = nexttok(); i++) {
	if ((new.s_addr = inet_addr(p)) != -1)
	    ;
	else if (np = getnetbyname(p)) {
	    in.s_addr = np->n_net;
	    new = inet_makeaddr(in.s_addr, 0);
	}
	else if (hp = gethostbyname(p))
	    (void)memcpy((caddr_t)&new, hp->h_addr, sizeof new);
	else {
	    yyerror(path, "Bad IP address \"%s\"", p);
	    continue;
	}
	if (!hostinlist(list, new) && !addhosttolist(list, new))
	    yyerror(path, "Can't add \"%s\" to address list", p);
    }
    return i;
}

int
readconfig(path)
char *path;
{
    var_val *v;
    int nr = 0, nmr = 0, nm = 0;
    char *cfn;
    FILE *cf;
#if	0
    path = "dp-conf";
#endif
    cfn = expand_dir_file("$DPCONF_DIR", path);
    if (!(cf = fopen(cfn, "r"))) {
	perror(cfn);
	(void)free(cfn);
	return 0;
    }
    /*
     * Scan the file so we can allocate memory for our configuration.
     */
    while (v = readconf(cfn, cf))
	if (strcmp(v->var, "IF") == 0)
	    nr++;
	else if (strcmp(v->var, "ROTARY") == 0)
	    nmr++;
	else if (strcmp(v->var, "MODEM") == 0) {
	    nmr++;
	    nm++;
	}
	else
	    yyerror(cfn, "unknown configuration option: \"%s\"\n", v->var);

    if (nr == 0)
	yyerror(cfn, "No Interfaces defined\n");
    if (nm == 0)
	yyerror(cfn, "No Modems defined\n");

    if (errorcount)
	return 0;

    free_remotes();
    RemoteTable = (REMOTE *)malloc((unsigned int)nr * sizeof(*RemoteTable));
    if (RemoteTable == NULL) {
	yyerror(cfn, "Can't allocate Remote table, %m");
	(void)free(cfn);
	return 0;
    }
    (void)memset((char *)RemoteTable, 0, nr * sizeof(*RemoteTable));
    free_modems();
    ModemTable = (MODEM *)malloc((unsigned int)nmr * sizeof(*ModemTable));
    if (ModemTable == NULL) {
	yyerror(cfn, "Can't allocate Modem table, %m");
	(void)free(cfn);
	return 0;
    }
    (void)memset((char *)ModemTable, 0, nmr * sizeof (*ModemTable));
    /*
     * Scan the file once for each type of configuration info.
     */
    rewind(cf);
    while (v = readconf(cfn, cf))
	if (strcmp(v->var, "MODEM") == 0)
	    def_modem(cfn, v);
    rewind(cf);
    while (v = readconf(cfn, cf))
	if (strcmp(v->var, "ROTARY") == 0)
	    def_rotary(cfn, v);
    rewind(cf);
    while (v = readconf(cfn, cf))
	if (strcmp(v->var, "IF") == 0)
	    def_if(cfn, v);

    (void)fclose(cf);
    (void)free(cfn);

    return errorcount == 0;
}

char *
savestr(s)
char *s;
{
    char *m = malloc((unsigned)strlen(s)+1);
    (void)strcpy(m, s);
    return m;
}

char **
saveargs(path, s)
char *path;
char *s;
{
    char *m, *nm;
    char **a;
    int n;

    if (!*s)
	return (char **)0;
    s = savestr(s);
    for (n = 0, m = s ; m ; m = ((m = strchr(m, ',')) ? m+1 : (char *)0))
	n++;
    a = (char **)malloc((unsigned)(n+1) * sizeof(char **));
    if (!a) {
	yyerror(path, "Can't allocate Argument list");
	return (char **)0;
    }
    n = 0;
    for (m = s; m ; m = nm) {
	if (nm = strchr(m, ','))
	    *nm++ = '\0';
	a[n++] = m;
    }
    a[n] = (char *)0;
    return a;
}

char *
find_val(v, var)
var_val *v;
char *var;
{
    for ( ; v->var ; v++)
	if (strcmp(v->var, var) == 0) {
	    v->used++;
	    return v->val;
	}
    return (char *)0;
}


unused_vals(path, type, name, v)
char *path,
     *type,
     *name;
var_val *v;
{
    for ( ; v->var ; v++)
	if (!v->used) {
	    yyerror(path, "Unrecognized definition in %s %s: %s=%s",
		    name, type, v->var, v->val);
	}
}

MODEM **
modem_list(path, list, nmp, type, name)
char *path, *list;
int *nmp;
char *type, *name;
{
    MODEM **mpp, *mmp;
    char *m, *nm;
    int n;
    for (n = 0, m = list ; m ; m = ((m = strchr(m, ',')) ? m+1 : (char *)0))
	n++;
    mpp = (MODEM **)malloc((unsigned)n * sizeof(MODEM *));
    if (!mpp)
	yyerror(path, "Can't allocate Modem list");
    else {
	n = 0;
	for (m = list; m ; m = nm) {
	    if (nm = strchr(m, ','))
		*nm++ = '\0';
	    if (mmp = findmodem(m)) {
		mpp[n++] = mmp;
	    }
	    else
		yyerror(path, "Unknown MODEM \"%s\" in %s \"%s\"",
			m, type, name);
	}
    }
    *nmp = n;
    return mpp;
}

def_modem(path, v)
char *path;
var_val *v;
{
    MODEM *mp;
    char *val;

    if (!(val = find_val(v, "MODEM")))
	yyerror(path, "MODEM definition must have MODEM defined");
    else {
	if (mp = findmodem(val)) {
	    yyerror(path, "MODEM \"%s\" defined more than once");
	    free_modem(mp);
	}
	else
	    mp = &ModemTable[ModemCount++];
	mp->Name = savestr(val);
    }
    mp->NModems = 0;	/* No rotary modems defined */
    if (!(val = find_val(v, "DEV")))
	yyerror(path, "MODEM definition must have DEV defined");
    else
	mp->Line = savestr(val);
    
    /*
     * Interpret common definitions for modems and rotaries.
     */
    def_modem_rotary(path, mp, v);
    /*
     * Make sure we used everything that was given.
     */
    unused_vals(path, "MODEM", mp->Name, v);
}

def_rotary(path, v)
char *path;
var_val *v;
{
    MODEM *mp, **mpp;
    char *val;

    if (!(val = find_val(v, "ROTARY")))
	yyerror(path, "ROTARY definition must have ROTARY defined");
    else {
	if (mp = findmodem(val)) {
	    yyerror(path, "ROTARY \"%s\" defined more than once", mp->Name);
	    free_modem(mp);
	}
	else
	    mp = &ModemTable[ModemCount++];
	mp->Name = savestr(val);
    }

    /*
     * Interpret common definitions for modems and rotaries.
     */
    def_modem_rotary(path, mp, v);
    /*
     * Extract and verify list of modems.
     */
    if (!(val = find_val(v, "MODEMS")))
	yyerror(path, "ROTARY definition must have MODEMS defined");
    else {
	int n;
	mp->Modems = modem_list(path, val, &mp->NModems, "ROTARY", mp->Name);
	if (!mp->DialScript)
	    for (n = mp->NModems, mpp = mp->Modems; n-- ; mpp++)
		if (!(*mpp)->DialScript)
		    yyerror(path, "ROTARY/MODEM %s/%s has no dialing script",
			    mp->Name, (*mpp)->Name);
	if (!mp->Speed)
	    for (n = mp->NModems, mpp = mp->Modems; n-- ; mpp++)
		if (!(*mpp)->Speed)
		    yyerror(path, "ROTARY/MODEM %s/%s has no baud rate set",
			    mp->Name, (*mpp)->Name);
    }
    
    /*
     * Make sure we used everything that was given.
     */
    unused_vals(path, "ROTARY", mp->Name, v);
}


def_modem_rotary(path, mp, v)
char *path;
MODEM *mp;
var_val *v;
{
    char *val;

    if (val = find_val(v, "BAUD"))
	switch (mp->Speed = atoi(val)) {
	 case 1200:
	 case 2400:
	 case 4800:
	 case 9600:
	 case 19200:
	 case 38400:
#ifdef	STS
	 case 57600:
#endif	STS
#ifdef	FASTZS
	 case 76800:
#endif	FASTZS
	    break;
	 default:
	    yyerror(path, "Bad MODEM speed \"%s\"\n", val);
	    break;
	}

    if (val = find_val(v, "DIAL_SCRIPT"))
	mp->DialScript = savestr(val);

    if (val = find_val(v, "DIAL_CHARMAP"))
	mp->DialCharMap = savestr(val);

    if (val = find_val(v, "ASYNC_MAP"))
	mp->AsyncMap = strtol(val, (char **)0, 16);
}

def_if(path, v)
char *path;
var_val *v;
{
    REMOTE *rp;
    char *val;

    if (!(val = find_val(v, "IF")))
	yyerror(path, "IF definition must have IF defined");
    else {
	if (rp = findconfig(val)) {
	    yyerror(path, "IF \"%s\" defined more than once", rp->Sitename);
	    free_remote(rp);
	}
	else
	    rp = &RemoteTable[RemoteCount++];
	if ((int)strlen(val) > MAXDEVLEN)
	    yyerror(path, "IF \"%s\" name too long (> %d)",
		    rp->Sitename, MAXDEVLEN);
	else {
	    (void)strcpy(rp->Device, val);
	    while (*val && !isdigit(*val))
		val++;
	    if ((rp->Unit = atoi(val)) < 0) {
		yyerror(path, "IF \"%s\" bad unit number (%d)",
			rp->Sitename, rp->Unit);
	    }
	}
    }

    if (!if_addrs(rp))
	return;

    if (val = find_val(v, "SYS"))
	rp->Sitename = savestr(val);
    else {
	struct hostent *hp = gethostbyaddr((char *)&rp->DstAddress,
					   sizeof(rp->DstAddress), AF_INET);
	rp->Sitename = savestr(hp ? hp->h_name : inet_ntoa(rp->DstAddress));
    }

    if (!(val = find_val(v, "MODEMS")))
	yyerror(path, "IF definition must have MODEMS defined");
    else
	rp->Modems = modem_list(path, val, &rp->NModems, "IF", rp->Sitename);

    if (val = find_val(v, "LOGIN_SCRIPT"))
	rp->Script = savestr(val);
    if (val = find_val(v, "LOGIN_ARGS"))
	rp->ScriptArgs = saveargs(path, val);

    if (val = find_val(v, "LOGIN"))
	rp->Login = savestr(val);

    if (val = find_val(v, "PHONE"))
	rp->Phone = savestr(val);

    if (val = find_val(v, "ASYNC_MAP"))
	rp->AsyncMap = strtol(val, (char **)0, 16);

    if (val = find_val(v, "LOG_LEVEL"))
	rp->LogLevel = atoi(val);

    if (val = find_val(v, "TRACE")) {
	char *p;
	if ((p = strchr(val, '@')) && isdigit(p[1]) && p[2] == '\0') {
	    rp->Transtyle = p[1] == '0' ? TS_LOW : TS_HIGH;
	    *p = '\0';
	}
	else
	    rp->Transtyle = TS_LOW;
	rp->Transcript = expand_dir_file("$DPTRACE_DIR", val);
    }

    if (val = find_val(v, "PPP_ARGS"))
	rp->PppArgs = saveargs(path, val);

    if (val = find_val(v, "AUX"))
	rp->AuxProgram = savestr(val);
    if (val = find_val(v, "AUX_ARGS"))
	rp->AuxArgs = saveargs(path, val);

    if (val = find_val(v, "ACCESS"))
	(void)readaccess(path, val, rp);

    /*
     * Make sure we used everything that was given.
     */
    unused_vals(path, "IF", rp->Sitename, v);
}

dp_settimeos(dt, to)
dp_timeo_t *dt;
int to;
{
    dt->dt_active =
    dt->dt_active_c =
    dt->dt_active_u =
    dt->dt_waiting =
    dt->dt_failcall = to;
}


readaccess(cpath, path, rp)
char *cpath,
     *path;
REMOTE *rp;
{
    int i;
    FILE *F;
    char *p, *word;
    char buff[BUFSIZ];
    dp_timeo_t *timeouts;

    /* Set access defaults. */
    for (i = 0; i < 7; i++)
	rp->Times[i] = ~0;
    for (i = 0; i < 8; i++)
	rp->Protocols[i] = ~0;
    for (rp->AllowCount = 0, i = 0; i < MAXHOSTS; i++)
	rp->AllowTo[i].s_addr = 0;
    for (rp->DisallowCount = 0, i = 0; i < MAXHOSTS; i++)
	rp->DisallowFrom[i].s_addr = 0;

    dp_settimeos(&rp->DTimeouts, DPT_NO_TIMEOUT);
    dp_settimeos(&rp->ATimeouts, DPT_NO_TIMEOUT);

    /* Field five, the access file. */
    if (*path == '\0')
	return errorcount == 0;

    path = expand_dirs_file("$DPCONF_DIR:$DPACCESS_DIR", path);
    if ((F = fopen(path, "r")) == NULL) {
	yyerror(cpath, "Can't open access file \"%s\"", path);
	(void)free(path);
	return 0;
    }

    /* Read lines. */
    for (linenum = 1; fgets(buff, sizeof buff, F); linenum++) {
	if ((p = strchr(buff, '\n')) == NULL) {
	    yyerror(buff, "Line too long", (char *)NULL);
	    (void)fclose(F);
	    (void)free(path);
	    return 0;
	}
	*p = '\0';
	if ((word = strtok(buff, SEPARATORS)) == NULL)
	    continue;
	if (*word == '#')
	    continue;
	/*
	 * Allow the setting of Dial out or Answer call timeouts seperately.
	 * This should also be done for allowable call times, etc., but that
	 * has not yet been implemented.
	 */
	timeouts = &rp->DTimeouts;
	if (strncmp(word, "answer:", sizeof("answer:")-1) == 0) {
	    word += sizeof("answer:")-1;
	    timeouts = &rp->ATimeouts;
	}
	else if (strncmp(word, "dial:", sizeof("dial:")-1) == 0)
	    word += sizeof("dial:")-1;

	/*
	 * Dispatch on the word to fill in the fields.
	 */
	if (EQ(word, "allowto") || EQ(word, "gooddstaddresses"))
	    rp->AllowCount += parseaddresslist(path, rp->AllowTo);
	else if (EQ(word, "disallowfrom") || EQ(word, "badsrcaddresses"))
	    rp->DisallowCount += parseaddresslist(path, rp->DisallowFrom);
	else if (EQ(word, "protocols") || EQ(word, "goodprotocols")) {
	    /*
	     * specific protocols to allow
	     * disallow all protocols first, then allow the specific ones
	     * on the `protocols' line
	     */
	    for (i = 0 ;
		 i < sizeof(rp->Protocols)/sizeof(rp->Protocols[0]) ;
		 i++)
		rp->Protocols[i] = 0;
	    parseprotocols(path, rp->Protocols, P_SET);
	}
	else if (EQ(word, "badprotocols")) {
	    /*
	     * specific protocols to disallow
	     * start either with all enabled by default or with the ones
	     * enabled by protocols or goodprotocols line
	     */
	    parseprotocols(path, rp->Protocols, P_RESET);
	}
	else if (EQ(word, "inactivity")) {
	    if (p = nexttok())
		if (!positivenumber(p))
		    yyerror(path, "Bad number \"%s\"", p);
		else
		    timeouts->dt_active = atoi(p);
	}
	else if (EQ(word, "last_close")) {
	    if (p = nexttok())
		if (!positivenumber(p))
		    yyerror(path, "Bad number \"%s\"", p);
		else
		    timeouts->dt_active_c = atoi(p);
	}
	else if (EQ(word, "non_tcp")) {
	    if (p = nexttok())
		if (!positivenumber(p))
		    yyerror(path, "Bad number \"%s\"", p);
		else
		    timeouts->dt_active_u = atoi(p);
	}
	else if (EQ(word, "failedcall")) {
	    if (p = nexttok())
		if (!positivenumber(p))
		    yyerror(path, "Bad number \"%s\"", p);
		else
		    timeouts->dt_failcall = atoi(p);
	}
	else if (EQ(word, "callwait")) {
	    if (p = nexttok())
		if (!positivenumber(p))
		    yyerror(path, "Bad number \"%s\"", p);
		else
		    timeouts->dt_waiting = atoi(p);
	}
	else if (EQ(word, "weekdays"))
	    rp->Times[1] = rp->Times[2] = rp->Times[3] =
	    rp->Times[4] = rp->Times[5] = parsetime(path);
	else if (EQ(word, "weekends"))
	    rp->Times[0] = rp->Times[6] = parsetime(path);
	else if (EQ(word, "sun") || EQ(word, "sunday"))
	    rp->Times[0] = parsetime(path);
	else if (EQ(word, "mon") || EQ(word, "monday"))
	    rp->Times[1] = parsetime(path);
	else if (EQ(word, "tue") || EQ(word, "tuesday"))
	    rp->Times[2] = parsetime(path);
	else if (EQ(word, "wed") || EQ(word, "wednesday"))
	    rp->Times[3] = parsetime(path);
	else if (EQ(word, "thu") || EQ(word, "thursday"))
	    rp->Times[4] = parsetime(path);
	else if (EQ(word, "fri") || EQ(word, "friday"))
	    rp->Times[5] = parsetime(path);
	else if (EQ(word, "sat") || EQ(word, "saturday"))
	    rp->Times[6] = parsetime(path);
	else
	    yyerror(path, "Bad parameter \"%s\"", word);
    }

    (void)free(path);
    (void)fclose(F);
    return errorcount == 0;
}


set_if_timeouts()
{
    REMOTE	 *rp;
    int i;

    for (rp = RemoteTable, i = RemoteCount; --i >= 0; rp++)
	if (!if_dtimeouts(rp))
	    return 0;
    return 1;
}

/*
 * Get the internet addresses from the kernel for the given interface.
 */
if_addrs(rp)
REMOTE		*rp;
{
    static char		WHERE[] = "if_addrs";
    int			s;
    struct ifreq	ifr;

    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
	d_log(DLOG_GENERAL, WHERE,
	      "Can't create socket to read interface addresses for \"%s\", %m",
	      rp->Device);
	return 0;
    }
    (void)strcpy(ifr.ifr_name, rp->Device);
    if (ioctl(s, SIOCGIFADDR, (caddr_t)&ifr) < 0) {
	d_log(DLOG_GENERAL, WHERE,
	      "Can't get interface address for \"%s\", %m", rp->Device);
	(void)close(s);
	return 0;
    }
    rp->Address = ((struct sockaddr_in *)(&ifr.ifr_addr))->sin_addr;
    if (ioctl(s, SIOCGIFDSTADDR, (caddr_t)&ifr) < 0) {
	d_log(DLOG_GENERAL, WHERE,
	      "Can't get interface destination address for \"%s\", %m",
	      rp->Device);
	(void)close(s);
	return 0;
    }
    rp->DstAddress = ((struct sockaddr_in *)(&ifr.ifr_dstaddr))->sin_addr;
    (void)close(s);
    if (rp->Address.s_addr == INADDR_ANY) {
	d_log(DLOG_GENERAL, WHERE,
	      "Interface address for \"%s\" not configured, %m",
	      rp->Device);
	return 0;
    }
    if (rp->DstAddress.s_addr == INADDR_ANY) {
	d_log(DLOG_GENERAL, WHERE,
	      "Destination interface address for \"%s\" not configured, %m",
	      rp->Device);
	return 0;
    }
    return 1;
}

if_dtimeouts(rp)
REMOTE	 *rp;
{
    static char		WHERE[] = "if_dtimeouts";


    int			s;
    struct ifreq	ifr;
#define	to		((u_long *)(ifr.ifr_data))
    

    if (strioctl(rp->DlpiFd, DPIOCSTIMEOS, -1,
		 sizeof(rp->DTimeouts), (char *)&rp->DTimeouts) < 0) {
	d_log(DLOG_GENERAL, WHERE,
	      "Can't set dial timeouts for \"%s\", %m", rp->Device);
	return 0;
    }
    d_log(DLOG_ALL, WHERE, "Set Dial Timeouts for \"%s\"", rp->Device);
    return 1;
}

if_atimeouts(rp)
REMOTE	 *rp;
{
    static char		WHERE[] = "if_atimeouts";

    if (strioctl(rp->DlpiFd, DPIOCSTIMEOS, -1,
		 sizeof(rp->ATimeouts), (char *)&rp->ATimeouts) < 0) {
	d_log(DLOG_GENERAL, WHERE,
	      "Can't set answer timeouts for \"%s\", %m", rp->Device);
	return 0;
    }
    d_log(DLOG_ALL, WHERE, "Set Answer Timeouts for \"%s\"", rp->Device);
    return 1;
}


static REMOTE		**Fd2Remote;
static int		MaxFd;
static struct pollfd	*DPPollFds;

/*
 *  Find this interface in our list.
 */
REMOTE *
findfdconfig(fd)
int fd;
{
    return (fd >= 0 && fd < MaxFd) ? Fd2Remote[fd] : (REMOTE *)0;
}

/*
 * Open each DP PPA and set up a polling structure for each one.
 */
int
opendpdevs(pfdp)
struct pollfd **pfdp;
{
    REMOTE	   *rp;
    struct pollfd *pf;
    int i;
    int fd;


    DPPollFds = (struct pollfd *)malloc(RemoteCount * sizeof(*DPPollFds));
    memset((char *)DPPollFds, 0, RemoteCount * sizeof(*DPPollFds));

    for (rp = RemoteTable, pf = DPPollFds, i = RemoteCount;
	 --i >= 0;
	 rp++, pf++) {
	if (opendp(rp, DP_DIAL_SAP(PPP_IP)))
	    return 0;
	pf->fd = rp->DlpiFd;
	pf->events = POLLIN|POLLPRI;
    }

    
    for (MaxFd = -1, rp = RemoteTable, i = RemoteCount; --i >= 0; rp++)
	if (rp->DlpiFd > MaxFd)
	    MaxFd = rp->DlpiFd;
    MaxFd++;

    if (MaxFd > 0) {
	Fd2Remote = (REMOTE **)malloc(MaxFd * sizeof(*Fd2Remote));
	memset((char *)Fd2Remote, 0, MaxFd * sizeof(*Fd2Remote));
    }
    for (rp = RemoteTable, i = RemoteCount ; --i >= 0 ; rp++)
	Fd2Remote[rp->DlpiFd] = rp;

    *pfdp = DPPollFds;
    return RemoteCount;
}

opendp(rp, sap)
REMOTE *rp;
ulong sap;
{
    int fd;
    int fl;
    char buf[DP_MAXDLBUF];

    if ((fd = open(DEVDP, O_RDWR)) < 0) {
	d_log(DLOG_GENERAL, WHERE,
	      "Can't open \"%s\", %m", DEVDP);
	return -1;
    }
    rp->DlpiFd = fd;

    fl = 1;
    if (fcntl(fd, F_SETFL, fl) < 0) {
	d_log(DLOG_GENERAL, WHERE,
	      "Can't set close-on-exec flag for \"%s\", %m", rp->Device);
	close(fd);
	return -1;
    }

    /*
     * Attach to the specified PPA (unit), and bind to the packets
     * of interest, namely Dial requests for IP.
     */
    dlattachreq(fd, rp->Unit);
    if (dlokack(fd, buf)) {
	close(fd);
	return -1;
    }

    if (sap) {
	dlbindreq(fd, sap, 0, DL_CLDLS, 0, 0);
	if (dlbindack(fd, buf)) {
	    close(fd);
	    return -1;
	}
    }

    return 0;
}

closedpdevs()
{
    REMOTE	 *rp;
    int i;

    for (rp = RemoteTable, i = RemoteCount; --i >= 0; rp++)
	(void)close(rp->DlpiFd);

    (void)free((char *)DPPollFds);
    DPPollFds = (struct pollfd *)0;
    (void)free((char *)Fd2Remote);
    Fd2Remote = (REMOTE **)0;
    MaxFd = -1;
}
