From andrewk@proxy.link-ul.ru  Tue Dec 21 22:55:00 1999
Return-Path: <andrewk@proxy.link-ul.ru>
Received: from proxy.link-ul.ru (proxy.link-ul.ru [195.151.42.2])
	by hub.freebsd.org (Postfix) with ESMTP id 56D75150D2
	for <FreeBSD-gnats-submit@freebsd.org>; Tue, 21 Dec 1999 22:52:47 -0800 (PST)
	(envelope-from andrewk@proxy.link-ul.ru)
Received: (from root@localhost)
	by proxy.link-ul.ru (8.9.3/8.9.2) id JAA05107;
	Wed, 22 Dec 1999 09:41:00 +0300 (MSK)
	(envelope-from andrewk)
Message-Id: <199912220641.JAA05107@proxy.link-ul.ru>
Date: Wed, 22 Dec 1999 09:41:00 +0300 (MSK)
From: Andrew Klyachkin <andrewk@proxy.link-ul.ru>
Reply-To: andrewk@proxy.link-ul.ru
To: FreeBSD-gnats-submit@freebsd.org
Subject: pppd with support for global-one radiusd
X-Send-Pr-Version: 3.2

>Number:         15619
>Category:       bin
>Synopsis:       [patch] standard pppd(8) doesn't authenticate users with global-1 radiusd
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    peter
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Tue Dec 21 23:00:02 PST 1999
>Closed-Date:    Fri Jul 29 10:51:06 UTC 2011
>Last-Modified:  Fri Jul 29 10:51:06 UTC 2011
>Originator:     Andrew Klyachkin
>Release:        FreeBSD 3.4-RC i386
>Organization:
Link Ltd., Ulyanovsk, Russia
>Environment:

	

>Description:

	Standard pppd in 3.x branch cannot authenticate users with global-one
	radiusd. The supplied patch uses derivative code from Merit radiusd
	2.34 to enable radiusd support. This code is still alpha and buggy
	but it works for me. I'm using it to provide Internet access for
	dialup clients. The known limitation is that pppd doesn't handle
	requests from radiusd that user's time is over and it is time to
	disconnect session. And I don't remember, am I implement sending
	information to radiusd about received and sent packets :)
	To enable this feature new option has been added - radius.

>How-To-Repeat:

	

>Fix:
	
	
diff -rPu pppd.old/Makefile pppd/Makefile
--- pppd.old/Makefile	Sun Aug 29 19:46:53 1999
+++ pppd/Makefile	Thu Dec  9 11:17:07 1999
@@ -36,4 +36,8 @@
 DPADD+=	${LIBDES}
 .endif
 
+# RADIUS support
+CFLAGS+=-DRADIUS
+SRCS+=rad_dict.c rad_funcs.c rad_md5.c rad_regexp.c rad_sendserver.c rad_users.c rad_util.c pppradius.c
+
 .include <bsd.prog.mk>
diff -rPu pppd.old/auth.c pppd/auth.c
--- pppd.old/auth.c	Sun Aug 29 19:46:54 1999
+++ pppd/auth.c	Sat Dec 11 13:50:48 1999
@@ -80,6 +80,10 @@
 #endif
 #include "pathnames.h"
 
+#ifdef RADIUS
+#include "radius.h"
+#endif
+
 /* Used for storing a sequence of words.  Usually malloced. */
 struct wordlist {
     struct wordlist	*next;
@@ -179,6 +183,8 @@
     minutes = (etime-stime)/60;
     syslog(LOG_NOTICE, "Connection terminated, connected for %d minutes\n",
 	minutes > 1 ? minutes : 1);
+    if (reqradius)
+	rad_acct_stop(RADIUS_name);
 }
 
 /*
@@ -247,11 +253,14 @@
 
     phase = PHASE_AUTHENTICATE;
     auth = 0;
+    syslog(LOG_DAEMON, "Authentication phase began");
     if (go->neg_chap) {
 	ChapAuthPeer(unit, our_name, go->chap_mdtype);
 	auth |= CHAP_PEER;
     } else if (go->neg_upap) {
+	syslog(LOG_DAEMON, "Authentication with PAP");
 	upap_authpeer(unit);
+	syslog(LOG_DAEMON, "Authntication finished");
 	auth |= PAP_PEER;
     }
     if (ho->neg_chap) {
@@ -263,13 +272,18 @@
 	    if (!get_pap_passwd(passwd))
 		syslog(LOG_ERR, "No secret found for PAP login");
 	}
-	upap_authwithpeer(unit, user, passwd);
 	auth |= PAP_WITHPEER;
     }
     auth_pending[unit] = auth;
 
-    if (!auth)
+	syslog(LOG_DAEMON, "Link established");
+
+    if (!auth) {
+	syslog(LOG_DAEMON, "  network phase");
 	network_phase(unit);
+    } else {
+	syslog(LOG_DAEMON, "Stop network growth!");
+    }
 }
 
 /*
@@ -291,6 +305,7 @@
 	did_authup = 1;
     }
 
+	syslog(LOG_DAEMON, "after path_authup");
 #ifdef CBCP_SUPPORT
     /*
      * If we negotiated callback, do it now.
@@ -670,7 +685,6 @@
     if (f == NULL) {
 	syslog(LOG_ERR, "Can't open PAP password file %s: %m", filename);
 	ret = UPAP_AUTHNAK;
-
     } else {
 	check_access(f, filename);
 	remote = ipwo->accept_remote? 0: ipwo->hisaddr;
@@ -691,6 +705,14 @@
 	}
     }
 
+    /* here will be radius login */
+#ifdef RADIUS
+    if (ret == UPAP_AUTHNAK && reqradius)
+	if (rad_auth(user, passwd) == OK_RC)
+		if (rad_acct_start(user) == OK_RC)
+			ret = UPAP_AUTHACK;
+#endif
+
     if (ret == UPAP_AUTHNAK) {
         if (*msg == (char *) 0)
 	    *msg = "Login incorrect";
@@ -1028,7 +1050,16 @@
     }
 
 #else
-    char *tty;
+	char *tty;
+	syslog(LOG_DAEMON, "plogout");
+
+#ifdef RADIUS
+	if (reqradius) {
+		if (rad_acct_stop(user) != OK_RC) {
+			syslog(LOG_DAEMON, "Can't stop accounting user");
+		}	
+	}
+#endif
 
     tty = devnam;
     if (strncmp(tty, "/dev/", 5) == 0)
diff -rPu pppd.old/conf.h pppd/conf.h
--- pppd.old/conf.h	Thu Jan  1 03:00:00 1970
+++ pppd/conf.h	Wed Aug 21 20:45:46 1996
@@ -0,0 +1,97 @@
+#ifndef CONF_H
+#define CONF_H
+
+/*
+ *
+ *	RADIUS
+ *	Remote Authentication Dial In User Service
+ *
+ *
+ *	Livingston Enterprises, Inc.
+ *	6920 Koll Center Parkway
+ *	Pleasanton, CA   94566
+ *
+ *	Copyright 1992 Livingston Enterprises, Inc.
+ *
+ *	Permission to use, copy, modify, and distribute this software for any
+ *	purpose and without fee is hereby granted, provided that this
+ *	copyright and permission notice appear on all copies and supporting
+ *	documentation, the name of Livingston Enterprises, Inc. not be used
+ *	in advertising or publicity pertaining to distribution of the
+ *	program without specific prior permission, and notice be given
+ *	in supporting documentation that copying and distribution is by
+ *	permission of Livingston Enterprises, Inc.
+ *
+ *	Livingston Enterprises, Inc. makes no representations about
+ *	the suitability of this software for any purpose.  It is
+ *	provided "as is" without express or implied warranty.
+ *
+ */
+
+/*
+ *	@(#)conf.h	0.1 10/11/94
+ */
+
+#define	RADIUS_VERSION		" 2.4.23C " /* MUST be surrounded by spaces */
+
+#if defined(__alpha)
+typedef unsigned int UINT4;
+typedef int INT4;
+#else	/* defined(alpha) */
+typedef unsigned long UINT4;
+typedef long INT4;
+#endif	/* defined(alpha) */
+
+#if defined(unixware) || defined(sys5) || defined(M_UNIX) || defined(sun)
+#include	<string.h>
+#else	/* unixware */
+#include	<strings.h>
+#endif	/* unixware */
+
+#if defined(bsdi)
+#include	<machine/inline.h>
+#include	<machine/endian.h>
+#else	/* bsdi */
+#ifndef	__FreeBSD__
+#include	<malloc.h>
+#endif	/* __FreeBSD__ */
+#endif	/* bsdi */
+
+/* #ifdef SYSV this is needed even on SunOS 4.1.3 */
+#include	<pwd.h>
+/* #endif XXX SYSV */
+
+#if defined(aix)
+#include	<sys/select.h>
+#endif	/* aix */
+
+#if defined(__hpux)
+#include	<unistd.h>
+#endif	/* __hpux */
+
+#ifdef	USE_NDBM
+#include	<ndbm.h>
+#include	<fcntl.h>
+#endif	/* USE_NDBM */
+
+#if defined(USE_DBM)
+#include	<dbm.h>
+#if defined(sun)
+#define NULL 0
+#endif	/* sun */
+#if defined(__sgi)
+#ifdef dbm_error64
+#define store store64
+#define dbminit dbminit64
+#define dbmclose dbmclose64
+#endif	/* dbm_error64 */
+#endif	/* __sgi */
+#endif	/* USE_DBM */
+
+#if defined(linux)
+#include	<unistd.h>
+#define MIN(a, b)      ((a) < (b) ? (a) : (b))
+#define MAX(a, b)      ((a) > (b) ? (a) : (b))
+#endif	/* linux */
+
+#endif /* CONF_H */
diff -rPu pppd.old/md4.c pppd/md4.c
--- pppd.old/md4.c	Thu Jan  1 03:00:00 1970
+++ pppd/md4.c	Tue Dec 16 05:08:47 1997
@@ -0,0 +1,298 @@
+/*
+** ********************************************************************
+** md4.c -- Implementation of MD4 Message Digest Algorithm           **
+** Updated: 2/16/90 by Ronald L. Rivest                              **
+** (C) 1990 RSA Data Security, Inc.                                  **
+** ********************************************************************
+*/
+
+/*
+** To use MD4:
+**   -- Include md4.h in your program
+**   -- Declare an MDstruct MD to hold the state of the digest
+**          computation.
+**   -- Initialize MD using MDbegin(&MD)
+**   -- For each full block (64 bytes) X you wish to process, call
+**          MD4Update(&MD,X,512)
+**      (512 is the number of bits in a full block.)
+**   -- For the last block (less than 64 bytes) you wish to process,
+**          MD4Update(&MD,X,n)
+**      where n is the number of bits in the partial block. A partial
+**      block terminates the computation, so every MD computation
+**      should terminate by processing a partial block, even if it
+**      has n = 0.
+**   -- The message digest is available in MD.buffer[0] ...
+**      MD.buffer[3].  (Least-significant byte of each word
+**      should be output first.)
+**   -- You can print out the digest using MDprint(&MD)
+*/
+
+/* Implementation notes:
+** This implementation assumes that ints are 32-bit quantities.
+*/
+
+#define TRUE  1
+#define FALSE 0
+
+/* Compile-time includes
+*/
+#include <stdio.h>
+#include "md4.h"
+#include "pppd.h"
+
+/* Compile-time declarations of MD4 "magic constants".
+*/
+#define I0  0x67452301       /* Initial values for MD buffer */
+#define I1  0xefcdab89
+#define I2  0x98badcfe
+#define I3  0x10325476
+#define C2  013240474631     /* round 2 constant = sqrt(2) in octal */
+#define C3  015666365641     /* round 3 constant = sqrt(3) in octal */
+/* C2 and C3 are from Knuth, The Art of Programming, Volume 2
+** (Seminumerical Algorithms), Second Edition (1981), Addison-Wesley.
+** Table 2, page 660.
+*/
+
+#define fs1  3               /* round 1 shift amounts */
+#define fs2  7
+#define fs3 11
+#define fs4 19
+#define gs1  3               /* round 2 shift amounts */
+#define gs2  5
+#define gs3  9
+#define gs4 13
+#define hs1  3               /* round 3 shift amounts */
+#define hs2  9
+#define hs3 11
+#define hs4 15
+
+/* Compile-time macro declarations for MD4.
+** Note: The "rot" operator uses the variable "tmp".
+** It assumes tmp is declared as unsigned int, so that the >>
+** operator will shift in zeros rather than extending the sign bit.
+*/
+#define f(X,Y,Z)             ((X&Y) | ((~X)&Z))
+#define g(X,Y,Z)             ((X&Y) | (X&Z) | (Y&Z))
+#define h(X,Y,Z)             (X^Y^Z)
+#define rot(X,S)             (tmp=X,(tmp<<S) | (tmp>>(32-S)))
+#define ff(A,B,C,D,i,s)      A = rot((A + f(B,C,D) + X[i]),s)
+#define gg(A,B,C,D,i,s)      A = rot((A + g(B,C,D) + X[i] + C2),s)
+#define hh(A,B,C,D,i,s)      A = rot((A + h(B,C,D) + X[i] + C3),s)
+
+/* MD4print(MDp)
+** Print message digest buffer MDp as 32 hexadecimal digits.
+** Order is from low-order byte of buffer[0] to high-order byte of
+** buffer[3].
+** Each byte is printed with high-order hexadecimal digit first.
+** This is a user-callable routine.
+*/
+void
+MD4Print(MDp)
+MD4_CTX *MDp;
+{
+  int i,j;
+  for (i=0;i<4;i++)
+    for (j=0;j<32;j=j+8)
+      printf("%02x",(MDp->buffer[i]>>j) & 0xFF);
+}
+
+/* MD4Init(MDp)
+** Initialize message digest buffer MDp.
+** This is a user-callable routine.
+*/
+void
+MD4Init(MDp)
+MD4_CTX *MDp;
+{
+  int i;
+  MDp->buffer[0] = I0;
+  MDp->buffer[1] = I1;
+  MDp->buffer[2] = I2;
+  MDp->buffer[3] = I3;
+  for (i=0;i<8;i++) MDp->count[i] = 0;
+  MDp->done = 0;
+}
+
+/* MDblock(MDp,X)
+** Update message digest buffer MDp->buffer using 16-word data block X.
+** Assumes all 16 words of X are full of data.
+** Does not update MDp->count.
+** This routine is not user-callable.
+*/
+static void
+MDblock(MDp,Xb)
+MD4_CTX *MDp;
+unsigned char *Xb;
+{
+  register unsigned int tmp, A, B, C, D;
+  unsigned int X[16];
+  int i;
+
+  for (i = 0; i < 16; ++i) {
+    X[i] = Xb[0] + (Xb[1] << 8) + (Xb[2] << 16) + (Xb[3] << 24);
+    Xb += 4;
+  }
+
+  A = MDp->buffer[0];
+  B = MDp->buffer[1];
+  C = MDp->buffer[2];
+  D = MDp->buffer[3];
+  /* Update the message digest buffer */
+  ff(A , B , C , D ,  0 , fs1); /* Round 1 */
+  ff(D , A , B , C ,  1 , fs2);
+  ff(C , D , A , B ,  2 , fs3);
+  ff(B , C , D , A ,  3 , fs4);
+  ff(A , B , C , D ,  4 , fs1);
+  ff(D , A , B , C ,  5 , fs2);
+  ff(C , D , A , B ,  6 , fs3);
+  ff(B , C , D , A ,  7 , fs4);
+  ff(A , B , C , D ,  8 , fs1);
+  ff(D , A , B , C ,  9 , fs2);
+  ff(C , D , A , B , 10 , fs3);
+  ff(B , C , D , A , 11 , fs4);
+  ff(A , B , C , D , 12 , fs1);
+  ff(D , A , B , C , 13 , fs2);
+  ff(C , D , A , B , 14 , fs3);
+  ff(B , C , D , A , 15 , fs4);
+  gg(A , B , C , D ,  0 , gs1); /* Round 2 */
+  gg(D , A , B , C ,  4 , gs2);
+  gg(C , D , A , B ,  8 , gs3);
+  gg(B , C , D , A , 12 , gs4);
+  gg(A , B , C , D ,  1 , gs1);
+  gg(D , A , B , C ,  5 , gs2);
+  gg(C , D , A , B ,  9 , gs3);
+  gg(B , C , D , A , 13 , gs4);
+  gg(A , B , C , D ,  2 , gs1);
+  gg(D , A , B , C ,  6 , gs2);
+  gg(C , D , A , B , 10 , gs3);
+  gg(B , C , D , A , 14 , gs4);
+  gg(A , B , C , D ,  3 , gs1);
+  gg(D , A , B , C ,  7 , gs2);
+  gg(C , D , A , B , 11 , gs3);
+  gg(B , C , D , A , 15 , gs4);
+  hh(A , B , C , D ,  0 , hs1); /* Round 3 */
+  hh(D , A , B , C ,  8 , hs2);
+  hh(C , D , A , B ,  4 , hs3);
+  hh(B , C , D , A , 12 , hs4);
+  hh(A , B , C , D ,  2 , hs1);
+  hh(D , A , B , C , 10 , hs2);
+  hh(C , D , A , B ,  6 , hs3);
+  hh(B , C , D , A , 14 , hs4);
+  hh(A , B , C , D ,  1 , hs1);
+  hh(D , A , B , C ,  9 , hs2);
+  hh(C , D , A , B ,  5 , hs3);
+  hh(B , C , D , A , 13 , hs4);
+  hh(A , B , C , D ,  3 , hs1);
+  hh(D , A , B , C , 11 , hs2);
+  hh(C , D , A , B ,  7 , hs3);
+  hh(B , C , D , A , 15 , hs4);
+  MDp->buffer[0] += A;
+  MDp->buffer[1] += B;
+  MDp->buffer[2] += C;
+  MDp->buffer[3] += D;
+}
+
+/* MD4Update(MDp,X,count)
+** Input: X -- a pointer to an array of unsigned characters.
+**        count -- the number of bits of X to use.
+**          (if not a multiple of 8, uses high bits of last byte.)
+** Update MDp using the number of bits of X given by count.
+** This is the basic input routine for an MD4 user.
+** The routine completes the MD computation when count < 512, so
+** every MD computation should end with one call to MD4Update with a
+** count less than 512.  A call with count 0 will be ignored if the
+** MD has already been terminated (done != 0), so an extra call with
+** count 0 can be given as a "courtesy close" to force termination
+** if desired.
+*/
+void
+MD4Update(MDp,X,count)
+MD4_CTX *MDp;
+unsigned char *X;
+unsigned int count;
+{
+  unsigned int i, tmp, bit, byte, mask;
+  unsigned char XX[64];
+  unsigned char *p;
+
+  /* return with no error if this is a courtesy close with count
+  ** zero and MDp->done is true.
+  */
+  if (count == 0 && MDp->done) return;
+  /* check to see if MD is already done and report error */
+  if (MDp->done)
+  { printf("\nError: MD4Update MD already done."); return; }
+
+  /* Add count to MDp->count */
+  tmp = count;
+  p = MDp->count;
+  while (tmp)
+  { tmp += *p;
+  *p++ = tmp;
+  tmp = tmp >> 8;
+  }
+
+  /* Process data */
+  if (count == 512)
+  { /* Full block of data to handle */
+    MDblock(MDp,X);
+  }
+  else if (count > 512) /* Check for count too large */
+  {
+    printf("\nError: MD4Update called with illegal count value %d.",
+	   count);
+    return;
+  }
+  else /* partial block -- must be last block so finish up */
+  {
+    /* Find out how many bytes and residual bits there are */
+    byte = count >> 3;
+    bit =  count & 7;
+    /* Copy X into XX since we need to modify it */
+    for (i=0;i<=byte;i++)   XX[i] = X[i];
+    for (i=byte+1;i<64;i++) XX[i] = 0;
+    /* Add padding '1' bit and low-order zeros in last byte */
+    mask = 1 << (7 - bit);
+    XX[byte] = (XX[byte] | mask) & ~( mask - 1);
+    /* If room for bit count, finish up with this block */
+    if (byte <= 55)
+    {
+      for (i=0;i<8;i++) XX[56+i] = MDp->count[i];
+      MDblock(MDp,XX);
+    }
+    else /* need to do two blocks to finish up */
+    {
+      MDblock(MDp,XX);
+      for (i=0;i<56;i++) XX[i] = 0;
+      for (i=0;i<8;i++)  XX[56+i] = MDp->count[i];
+      MDblock(MDp,XX);
+    }
+    /* Set flag saying we're done with MD computation */
+    MDp->done = 1;
+  }
+}
+
+/*
+** Finish up MD4 computation and return message digest.
+*/
+void
+MD4Final(buf, MD)
+unsigned char *buf;
+MD4_CTX *MD;
+{
+  int i, j;
+  unsigned int w;
+
+  MD4Update(MD, NULL, 0);
+  for (i = 0; i < 4; ++i) {
+    w = MD->buffer[i];
+    for (j = 0; j < 4; ++j) {
+      *buf++ = w;
+      w >>= 8;
+    }
+  }
+}
+
+/*
+** End of md4.c
+****************************(cut)***********************************/
diff -rPu pppd.old/md4.h pppd/md4.h
--- pppd.old/md4.h	Thu Jan  1 03:00:00 1970
+++ pppd/md4.h	Sun Apr  5 09:02:21 1998
@@ -0,0 +1,64 @@
+
+/*
+** ********************************************************************
+** md4.h -- Header file for implementation of                        **
+** MD4 Message Digest Algorithm                                      **
+** Updated: 2/13/90 by Ronald L. Rivest                              **
+** (C) 1990 RSA Data Security, Inc.                                  **
+** ********************************************************************
+*/
+
+#ifndef __P
+# if defined(__STDC__) || defined(__GNUC__)
+#  define __P(x) x
+# else
+#  define __P(x) ()
+# endif
+#endif
+
+
+/* MDstruct is the data structure for a message digest computation.
+*/
+typedef struct {
+	unsigned int buffer[4]; /* Holds 4-word result of MD computation */
+	unsigned char count[8]; /* Number of bits processed so far */
+	unsigned int done;      /* Nonzero means MD computation finished */
+} MD4_CTX;
+
+/* MD4Init(MD4_CTX *)
+** Initialize the MD4_CTX prepatory to doing a message digest
+** computation.
+*/
+extern void MD4Init __P((MD4_CTX *MD));
+
+/* MD4Update(MD,X,count)
+** Input: X -- a pointer to an array of unsigned characters.
+**        count -- the number of bits of X to use (an unsigned int).
+** Updates MD using the first "count" bits of X.
+** The array pointed to by X is not modified.
+** If count is not a multiple of 8, MD4Update uses high bits of
+** last byte.
+** This is the basic input routine for a user.
+** The routine terminates the MD computation when count < 512, so
+** every MD computation should end with one call to MD4Update with a
+** count less than 512.  Zero is OK for a count.
+*/
+extern void MD4Update __P((MD4_CTX *MD, unsigned char *X, unsigned int count));
+
+/* MD4Print(MD)
+** Prints message digest buffer MD as 32 hexadecimal digits.
+** Order is from low-order byte of buffer[0] to high-order byte
+** of buffer[3].
+** Each byte is printed with high-order hexadecimal digit first.
+*/
+extern void MD4Print __P((MD4_CTX *));
+
+/* MD4Final(buf, MD)
+** Returns message digest from MD and terminates the message
+** digest computation.
+*/
+extern void MD4Final __P((unsigned char *, MD4_CTX *));
+
+/*
+** End of md4.h
+****************************(cut)***********************************/
diff -rPu pppd.old/md5.h pppd/md5.h
--- pppd.old/md5.h	Thu Jan  1 03:00:00 1970
+++ pppd/md5.h	Thu Jun  6 21:28:37 1996
@@ -0,0 +1,73 @@
+/* GLOBAL.H - RSAREF types and constants
+ */
+
+/* PROTOTYPES should be set to one if and only if the compiler supports
+  function argument prototyping.
+  The following makes PROTOTYPES default to 0 if it has not already
+  been defined with C compiler flags.
+ */
+
+#ifndef PROTOTYPES
+#define PROTOTYPES 0
+#endif
+
+/* POINTER defines a generic pointer type */
+typedef unsigned char *POINTER;
+
+/* UINT2 defines a two byte word */
+typedef unsigned short int UINT2;
+
+/* UINT4 defines a four byte word */
+#if defined(__alpha)
+typedef unsigned int UINT4;
+#else
+typedef unsigned long int UINT4;
+#endif
+
+/* PROTO_LIST is defined depending on how PROTOTYPES is defined above.
+   If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it
+  returns an empty list.
+ */
+
+#if PROTOTYPES
+#define PROTO_LIST(list) list
+#else
+#define PROTO_LIST(list) ()
+#endif
+
+/* MD5.H - header file for MD5C.C
+ */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+ */
+
+/* MD5 context. */
+typedef struct
+{
+	UINT4           state[4];  /* state (ABCD) */
+	UINT4           count[2];  /* number of bits, modulo 2^64 (lsb first) */
+	unsigned char   buffer[64]; /* input buffer */
+} MD5_CTX;
+
+void MD5Init    PROTO_LIST ((MD5_CTX *));
+void MD5Update  PROTO_LIST ((MD5_CTX *, unsigned char *, unsigned int));
+void MD5Final   PROTO_LIST ((unsigned char[16], MD5_CTX *));
diff -rPu pppd.old/options.c pppd/options.c
--- pppd.old/options.c	Sun Aug 29 19:47:06 1999
+++ pppd/options.c	Wed Dec  8 12:45:48 1999
@@ -58,6 +58,12 @@
 #include "ipxcp.h"
 #endif /* IPX_CHANGE */
 
+#ifdef RADIUS
+#include "radius.h"
+#endif
+
+int reqradius;
+
 #include <net/ppp_comp.h>
 
 #define FALSE	0
@@ -148,6 +154,7 @@
 #ifdef OLD_OPTIONS
 static int setupapfile __P((char **));
 #endif
+static int radiusauth __P((char **));
 static int nochap __P((char **));
 static int reqchap __P((char **));
 static int noaccomp __P((char **));
@@ -294,6 +301,7 @@
     {"+pap", 0, reqpap},	/* Require PAP auth from peer */
     {"refuse-pap", 0, nopap},	/* Don't agree to auth to peer with PAP */
     {"-pap", 0, nopap},		/* Don't allow UPAP authentication with peer */
+    {"radius", 0, radiusauth},  /* Require authentication on RADIUS server */
     {"require-chap", 0, reqchap}, /* Require CHAP authentication from peer */
     {"+chap", 0, reqchap},	/* Require CHAP authentication from peer */
     {"refuse-chap", 0, nochap},	/* Don't agree to auth to peer with CHAP */
@@ -1372,6 +1380,17 @@
     lcp_wantoptions[0].neg_upap = 1;
     setauth(NULL);
     return 1;
+}
+
+/*
+ * radiusauth - Sets reqradius variable to authenticate thru radius server
+ */
+static int
+radiusauth(argv)
+	char **argv;
+{
+	reqradius = 1;
+	return 1;
 }
 
 #if OLD_OPTIONS
Binary files pppd.old/options.o and pppd/options.o differ
diff -rPu pppd.old/ppp.pam pppd/ppp.pam
--- pppd.old/ppp.pam	Thu Jan  1 03:00:00 1970
+++ pppd/ppp.pam	Wed Feb  4 05:50:32 1998
@@ -0,0 +1,6 @@
+#%PAM-1.0
+# Information for the PPPD process with the 'login' option.
+auth	required	/lib/security/pam_pwdb.so shadow nullok
+auth	required	/lib/security/pam_nologin.so
+account	required	/lib/security/pam_permit.so
+session	required	/lib/security/pam_permit.so
diff -rPu pppd.old/pppradius.c pppd/pppradius.c
--- pppd.old/pppradius.c	Thu Jan  1 03:00:00 1970
+++ pppd/pppradius.c	Sat Dec 11 13:42:02 1999
@@ -0,0 +1,394 @@
+#include	<sys/types.h>
+#include	<sys/socket.h>
+#include	<sys/param.h>
+#include	<netinet/in.h>
+#include	<sys/time.h>
+#include	<sys/signal.h>
+#include	<sys/termios.h>
+#ifdef	SVR4
+#include	<sys/systeminfo.h>
+#endif	/* SVR4 */
+
+#include	<netdb.h>
+#include	<pwd.h>
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<time.h>
+#include	<unistd.h>
+
+#include	"radius.h"
+
+#ifndef RESPONSE_TIMEOUT
+#define RESPONSE_TIMEOUT 3
+#endif
+
+#ifndef MAX_RETRIES
+#define MAX_RETRIES	10
+#endif
+
+char            recv_buffer[4096];
+char            send_buffer[4096];
+char            ourhostname[MAXHOSTNAMELEN];
+char           *progname;
+char           *radius_dir;
+int             debug_flag = 0;
+int             dumpcore = 0;
+int             file_logging = 2;   /* 0 => syslog, 1 => logfile, 2 => stderr */
+int             zap_logfile = 0;
+int             authfile_cnt = 0;
+int             clients_cnt = 0;
+int             users_cnt = 0;
+time_t          birthdate;
+AATVPTR		rad_authen_aatv = (AATV *) NULL;
+AATVPTR         rad_ipc_aatv = (AATV *) NULL;
+AATV           *authtype_tv[PW_AUTH_MAX + 1];
+FILE           *ddt = NULL;
+FILE           *msgfd = stderr;
+extern void     dir_init();
+
+static void     radpwtst_usage ();
+
+typedef struct string_list_struct
+{
+	struct string_list_struct *next;
+	char                      *str;
+}string_list;
+
+int	RADIUS_port;
+int	RADIUS_asi;
+int	RADIUS_data;
+time_t	RADIUS_bt;
+time_t	RADIUS_et;
+char	*RADIUS_name;
+
+int rad_auth(char *name, char *password)
+{
+	int		result;
+	int		retries;
+	int		new_old;
+	char		*client_name = (char *)NULL;
+	char		*clear_pw = (char *)NULL;
+	string_list	*vplist = NULL;
+	string_list	**vpnext = &vplist;
+	char		msg[4096];
+	char		passwd[AUTH_PASS_LEN + 1];
+	SEND_DATA	data;
+	int		send_server();
+
+	syslog(LOG_DAEMON, "Trying to authenticate: %s with password: %s\n", name, password);
+
+	data.code = PW_ACCESS_REQUEST;
+	data.svc_port = 1645;
+	data.server = "ns.link-ul.ru";
+	radius_dir = "/var/adm/raddb";
+	data.timeout = RESPONSE_TIMEOUT;
+	data.user_file = (char *)NULL;
+	data.group = (char *)NULL;
+	data.send_pairs = (VALUE_PAIR *)NULL;
+
+	retries = MAX_RETRIES;
+	new_old = 0;
+	data.ustype=PW_FRAMED;
+	data.fptype=PW_PPP;
+	data.port_num = RADIUS_port;
+
+	*vpnext = (string_list *)malloc(sizeof(string_list));
+	(*vpnext)->str = "NAS-Port-Type=0";
+	(*vpnext)->next = (string_list *)NULL;
+	vpnext = &((*vpnext)->next);
+	data.user_name = name;
+	data.password = password;
+
+	dir_init();
+
+	if (dict_init() != 0) {
+		fprintf(stderr, "Missing directory in /var/adm/raddb\n");
+		exit(-1);
+	}
+	for ( ; vplist ; vplist = vplist->next) {
+		if (pair_parse (vplist->str, &data.send_pairs) != 0) {
+			fprintf (stderr,
+				"%s: Invalid attribute-value pair, '%s'\n",
+				 progname, vplist->str);
+			exit(-1);
+		}
+	}
+	data.seq_nbr = RADIUS_data = (u_char) rand();
+	if (gethostname(ourhostname, sizeof(ourhostname)) < 0) {
+		perror("gethostname");
+		exit(-1);
+	}
+	if (client_name == (char *)NULL) {
+		if ((data.client_id = get_ipaddr(ourhostname)) == 0) {
+			printf("Couldn't get own IP address!\n");
+			data.client_id = 0;
+		}
+	} else
+		data.client_id = get_ipaddr(client_name);
+	if ((data.user_file != (char *)NULL)&&(data.group == (char *)NULL))
+		data.group = "DEFAULT";
+	msg[0] = '\0';
+	result = send_server(&data, &retries, msg);
+	if (result == OK_RC) { 
+		printf("\"%s\" authentication OK", data.user_name);
+		RADIUS_name = (char *)malloc(strlen(data.user_name)+1);
+		strcpy(RADIUS_name, data.user_name);
+	} else {
+		printf("\"%s\" authentication failed", data.user_name);
+		if (result != BADRESP_RC)
+			printf("(RC=%i)", result);
+	}
+	if (msg[0])
+		printf(": %s", msg);
+	putchar('\n');
+
+	return (result);
+}
+
+int rad_acct_start(char *name)
+{
+
+	int		result;
+	int		retries;
+	int		new_old;
+	char		*client_name = (char *)NULL;
+	char		*clear_pw = (char *)NULL;
+	string_list	*vplist = NULL;
+	string_list	**vpnext = &vplist;
+	char		msg[4096];
+	char		passwd[AUTH_PASS_LEN + 1];
+	SEND_DATA	data;
+	int		send_server();
+	char		*str;
+
+	data.code = PW_ACCOUNTING_REQUEST;
+	data.svc_port = 1646;
+	data.server = "ns.link-ul.ru";
+	radius_dir = "/var/adm/raddb";
+	data.timeout = RESPONSE_TIMEOUT;
+	data.user_file = (char *)NULL;
+	data.group = (char *)NULL;
+	data.send_pairs = (VALUE_PAIR *)NULL;
+	retries = MAX_RETRIES;
+	new_old = 0;
+	data.ustype = PW_FRAMED;
+	data.fptype = PW_PPP;
+	data.port_num = RADIUS_port;
+	data.user_name = name;
+	data.password = "";
+
+	if (pair_parse("NAS-Port-Type=0", &data.send_pairs) != 0) {
+		fprintf(stderr, "Invalid attribute-value pair\n");
+		exit(-1);
+	}
+	
+	if (pair_parse("Acct-Status-Type=1", &data.send_pairs) != 0) {
+		fprintf(stderr, "Invalid attribute acct-status-type\n");
+		exit(-1);
+	}
+
+	if (pair_parse("Acct-Authentic=3", &data.send_pairs) != 0) {
+		fprintf(stderr, "Invalid attribute acct-authentic\n");
+		exit(-1);
+	}
+
+	if (pair_parse("Acct-Delay-Time=0", &data.send_pairs) != 0) {
+		fprintf(stderr, "Invalid attribute acct-delay-time\n");
+		exit(-1);
+	}
+
+	str = (char *)malloc(128);
+	RADIUS_asi = rand();
+	sprintf(str, "Acct-Session-Id=%x", RADIUS_asi);
+	if (pair_parse(str, &data.send_pairs) != 0) {
+		fprintf(stderr, "Invalid attribute acct-session-id\n");
+		exit(-1);
+	}
+
+	dir_init();
+	if (dict_init() != 0) {
+		fprintf(stderr, "Missing directory in /var/adm/raddb\n");
+		exit(-1);
+	}
+
+	data.seq_nbr = (++RADIUS_data);
+	if (gethostname(ourhostname, sizeof(ourhostname)) < 0) {
+		perror("gethostname");
+		exit(-1);
+	}
+	if (client_name == (char *)NULL) {
+		if ((data.client_id = get_ipaddr(ourhostname)) == 0) {
+			printf("Couldn't get own IP address!\n");
+			data.client_id = 0;
+		}
+	} else
+		data.client_id = get_ipaddr(client_name);
+	if ((data.user_file != (char *)NULL)&&(data.group == (char *)NULL))
+		data.group = "DEFAULT";
+	msg[0] = '\0';
+	result = send_server(&data, &retries, msg);
+
+	if (result == OK_RC) 
+		printf("\"%s\" accounting start OK", data.user_name);
+	else {
+		printf("\"%s\" accounting start failed", data.user_name);
+		if (result != BADRESP_RC)
+			printf("(RC=%i)", result);
+	}
+	if (msg[0])
+		printf(": %s", msg);
+	putchar('\n');
+
+	RADIUS_bt = time(NULL);
+
+	return (result);
+}
+
+int rad_acct_stop(char *name)
+{
+	int		result;
+	int		retries;
+	int		new_old;
+	char		*client_name = (char *)NULL;
+	char		*clear_pw = (char *)NULL;
+	string_list	*vplist = NULL;
+	string_list	**vpnext = &vplist;
+	char		msg[4096];
+	char		passwd[AUTH_PASS_LEN + 1];
+	SEND_DATA	data;
+	int		send_server();
+	char		*str;
+
+	data.code = PW_ACCOUNTING_REQUEST;
+	data.svc_port = 1646;
+	data.server = "ns.link-ul.ru";
+	radius_dir = "/var/adm/raddb";
+	data.timeout = RESPONSE_TIMEOUT;
+	data.user_file = (char *)NULL;
+	data.group = (char *)NULL;
+	data.send_pairs = (VALUE_PAIR *)NULL;
+	retries = MAX_RETRIES;
+	new_old = 0;
+	data.ustype = PW_FRAMED;
+	data.fptype = PW_PPP;
+	data.port_num = RADIUS_port;
+	data.user_name = name;
+	data.password = "";
+
+	if (pair_parse("NAS-Port-Type=0", &data.send_pairs) != 0) {
+		fprintf(stderr, "Invalid attribute-value pair\n");
+		exit(-1);
+	}
+
+	if (pair_parse("Acct-Status-Type=2", &data.send_pairs) != 0) {
+		fprintf(stderr, "Invalid attribute acct-status-type\n");
+		exit(-1);
+	}
+
+	if (pair_parse("Acct-Authentic=3", &data.send_pairs) != 0) {
+		fprintf(stderr, "Invalid attribute acct-authentic\n");
+		exit(-1);
+	}
+
+	if (pair_parse("Acct-Delay-Time=0", &data.send_pairs) != 0) {
+		fprintf(stderr, "Invalid attribute acct-delay-time\n");
+		exit(-1);
+	}
+
+	if (pair_parse("Acct-Output-Octets=0", &data.send_pairs) != 0) {
+		fprintf(stderr, "Invalid attribute acct-output-octets\n");
+		exit(-1);
+	}
+
+	if (pair_parse("Acct-Input-Octets=0", &data.send_pairs) != 0) {
+		fprintf(stderr, "Invalid attribute acct-inpute-octets\n");
+		exit(-1);
+	}
+
+	if (pair_parse("Acct-Input-Packets=0", &data.send_pairs) != 0) {
+		fprintf(stderr, "Invalid attribute at-inpute-packets\n");
+		exit(-1);
+	}
+
+	if (pair_parse("Acct-Output-Packets=0", &data.send_pairs) != 0) {
+		fprintf(stderr, "Invalid attribute acct-output-packets\n");
+		exit(-1);
+	}
+
+	str = (char *)malloc(128);
+	sprintf(str,"Acct-Session-Id=%x", RADIUS_asi);
+	if (pair_parse(str, &data.send_pairs) != 0) {
+		fprintf(stderr, "Invalid attribute acct-session-id\n");
+		exit(-1);
+	}
+
+	RADIUS_et = time(NULL);
+	sprintf(str,"Acct-Session-Time=%ld",RADIUS_et-RADIUS_bt);
+	if (pair_parse(str, &data.send_pairs) != 0) {
+		fprintf(stderr, "Invalid attribute acct-session-time\n");
+		exit(-1);
+	}
+
+	dir_init();
+	if (dict_init() != 0) {
+		fprintf(stderr, "Missing directory in /var/adm/raddb\n");
+		exit(-1);
+	}
+
+	data.seq_nbr = (++RADIUS_data);
+	if (gethostname(ourhostname, sizeof(ourhostname)) < 0) {
+		perror("gethostname");
+		exit(-1);
+	}
+	if (client_name == (char *)NULL) {
+		if ((data.client_id = get_ipaddr(ourhostname)) == 0) {
+			printf("Couldn't get own IP address!\n");
+			data.client_id = 0;
+		}
+	} else
+		data.client_id = get_ipaddr(client_name);
+	if ((data.user_file != (char *)NULL)&&(data.group == (char *)NULL))
+		data.group = "DEFAULT";
+	msg[0] = '\0';
+	result = send_server(&data, &retries, msg);
+	if (result == OK_RC) 
+		printf("\"%s\" accounting stop OK", data.user_name);
+	else {
+		printf("\"%s\" accounting stop failed", data.user_name);
+		if (result != BADRESP_RC)
+			printf("(RC=%i)", result);
+	}
+	if (msg[0])
+		printf(": %s", msg);
+	putchar('\n');
+
+	return (result);
+}
+
+#if 0
+int main (int argc, char *argv[])
+{
+	progname = *argv;
+	srand(time(0));
+	RADIUS_port = rand();
+
+	if (argc != 3)
+		radpwtst_usage();
+	if (authenticate(argv[1], argv[2]) != OK_RC)
+		exit(-1);
+	if (accounting_start(argv[1]) != OK_RC)
+		exit(-1);
+	printf("Press any key to finish\n");
+	getchar();
+	if (accounting_stop(argv[1]) != OK_RC)
+		exit(-1);
+} /* end of main () */
+
+static void
+radpwtst_usage ()
+
+{
+	printf ("Usage: %s username password\n", progname);
+	exit (-1);
+} /* end of radpwtst_usage () */
+#endif
diff -rPu pppd.old/rad_dict.c pppd/rad_dict.c
--- pppd.old/rad_dict.c	Thu Jan  1 03:00:00 1970
+++ pppd/rad_dict.c	Wed May 22 17:54:06 1996
@@ -0,0 +1,365 @@
+/*
+ *
+ *	RADIUS
+ *	Remote Authentication Dial In User Service
+ *
+ *
+ *	Livingston Enterprises, Inc.
+ *	6920 Koll Center Parkway
+ *	Pleasanton, CA   94566
+ *
+ *	Copyright 1992 Livingston Enterprises, Inc.
+ *
+ *	Permission to use, copy, modify, and distribute this software for any
+ *	purpose and without fee is hereby granted, provided that this
+ *	copyright and permission notice appear on all copies and supporting
+ *	documentation, the name of Livingston Enterprises, Inc. not be used
+ *	in advertising or publicity pertaining to distribution of the
+ *	program without specific prior permission, and notice be given
+ *	in supporting documentation that copying and distribution is by
+ *	permission of Livingston Enterprises, Inc.
+ *
+ *	Livingston Enterprises, Inc. makes no representations about
+ *	the suitability of this software for any purpose.  It is
+ *	provided "as is" without express or implied warranty.
+ *
+ * Public entry points in this file:
+ *
+ * dict_attrfind
+ * dict_attrget
+ * dict_init
+ * dict_valfind
+ * dict_valget
+ *
+ */
+
+static char     sccsid[] =
+		"@(#)dict.c 1.2 Copyright 1992 Livingston Enterprises Inc";
+
+static char     rcsid[] = "$Id: dict.c,v 1.9 1996/05/17 14:19:03 web Exp $";
+
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<time.h>
+#include	<sys/types.h>
+#include	<ctype.h>
+#include	<netinet/in.h>
+#include	<syslog.h>
+
+#include	"radius.h"
+
+extern int      debug_flag;
+extern char    *radius_dir;
+
+static DICT_ATTR *dictionary_attributes;
+static DICT_VALUE *dictionary_values;
+
+/*************************************************************************
+ *
+ *	Function: dict_init
+ *
+ *	Purpose: Initialize the dictionary.  Read all ATTRIBUTES into
+ *		 the dictionary_attributes list.  Read all VALUES into
+ *		 the dictionary_values list.
+ *
+ *************************************************************************/
+
+int
+dict_init ()
+
+{
+	FILE           *dictfd;
+	char            dummystr[AUTH_ID_LEN];
+	char            namestr[AUTH_ID_LEN];
+	char            valstr[AUTH_ID_LEN];
+	char            attrstr[AUTH_ID_LEN];
+	char            typestr[AUTH_ID_LEN];
+	int             line_no;
+	DICT_ATTR      *attr;
+	DICT_VALUE     *dval;
+	char            buffer[256];
+	int             value;
+	int             type;
+	static char    *func = "dict_init";
+
+	dprintf(2, (LOG_AUTH, LOG_DEBUG, "%s: entered", func));
+
+	sprintf (buffer, "%s/%s", radius_dir, RADIUS_DICTIONARY);
+	if ((dictfd = fopen (buffer, "r")) == (FILE *) NULL)
+	{
+		fprintf (stderr, "%s: Couldn't open dictionary: %s\n",
+			 func, buffer);
+		return (-1);
+	}
+
+	line_no = 0;
+	while (fgets (buffer, sizeof (buffer), dictfd) != (char *) NULL)
+	{
+		line_no++;
+
+		/* Skip empty space */
+		if (*buffer == '#' || *buffer == '\0' || *buffer == '\n')
+		{
+			continue;
+		}
+
+		if (strncmp (buffer, "ATTRIBUTE", 9) == 0)
+		{
+
+			/* Read the ATTRIBUTE line */
+			if (sscanf (buffer, "%s%s%s%s", dummystr, namestr,
+				    valstr, typestr) != 4)
+			{
+				fprintf (stderr,
+			    "%s: Invalid attribute on line %d of dictionary\n",
+					 func, line_no);
+				return (-1);
+			}
+
+			/*
+			 * Validate all entries
+			 */
+			if (strlen (namestr) > NAME_LENGTH)
+			{
+				fprintf (stderr,
+			   "%s: Invalid name length on line %d of dictionary\n",
+					 func, line_no);
+				return (-1);
+			}
+
+			if (!isdigit (*valstr))
+			{
+				fprintf (stderr,
+				 "%s: Invalid value on line %d of dictionary\n",
+					 func, line_no);
+				return (-1);
+			}
+			value = atoi (valstr);
+
+			if (strcmp (typestr, "string") == 0)
+			{
+				type = PW_TYPE_STRING;
+			}
+			else if (strcmp (typestr, "integer") == 0)
+			{
+				type = PW_TYPE_INTEGER;
+			}
+			else if (strcmp (typestr, "ipaddr") == 0)
+			{
+				type = PW_TYPE_IPADDR;
+			}
+			else if (strcmp (typestr, "date") == 0)
+			{
+				type = PW_TYPE_DATE;
+			}
+			else if (strcmp (typestr, "octets") == 0)
+			{
+				type = PW_TYPE_OCTETS;
+			}
+			else if (strcmp (typestr, "vendor") == 0)
+			{
+				type = PW_TYPE_VENDOR;
+			}
+			else
+			{
+				fprintf (stderr,
+				  "%s: Invalid type on line %d of dictionary\n",
+					 func, line_no);
+				return (-1);
+			}
+
+			/* Create a new attribute for the list */
+			if ((attr =
+				(DICT_ATTR *) malloc (sizeof (DICT_ATTR)))
+							== (DICT_ATTR *) NULL)
+			{
+				fprintf (stderr, "%s: FATAL out of memory\n",
+					func);
+				abort ();
+			}
+			strcpy (attr->name, namestr);
+			attr->value = value;
+			attr->type = type;
+
+			/* Insert it into the list */
+			attr->next = dictionary_attributes;
+			dictionary_attributes = attr;
+		}
+		else if (strncmp (buffer, "VALUE", 5) == 0)
+		{
+			/* Read the VALUE line */
+			if (sscanf (buffer, "%s%s%s%s", dummystr, attrstr,
+				    namestr, valstr) != 4)
+			{
+				fprintf (stderr,
+			   "%s: Invalid value entry on line %d of dictionary\n",
+					 func, line_no);
+				return (-1);
+			}
+
+			/*
+			 * Validate all entries
+			 */
+			if (strlen (attrstr) > NAME_LENGTH)
+			{
+				fprintf (stderr,
+		      "%s: Invalid attribute length on line %d of dictionary\n",
+					 func, line_no);
+				return (-1);
+			}
+
+			if (strlen (namestr) > NAME_LENGTH)
+			{
+				fprintf (stderr,
+			   "%s: Invalid name length on line %d of dictionary\n",
+					 func, line_no);
+				return (-1);
+			}
+
+			if (!isdigit (*valstr))
+			{
+				fprintf (stderr,
+				 "%s: Invalid value on line %d of dictionary\n",
+					 func, line_no);
+				return (-1);
+			}
+			value = atoi (valstr);
+
+			/* Create a new VALUE entry for the list */
+			if ((dval =
+				(DICT_VALUE *) malloc (sizeof (DICT_VALUE)))
+							== (DICT_VALUE *) NULL)
+			{
+				fprintf (stderr, "%s: FATAL out of memory\n",
+					func);
+				abort ();
+			}
+			strcpy (dval->attrname, attrstr);
+			strcpy (dval->name, namestr);
+			dval->value = value;
+
+			/* Insert it into the list */
+			dval->next = dictionary_values;
+			dictionary_values = dval;
+		}
+	}
+	fclose (dictfd);
+	return (0);
+} /* end of dict_init () */
+
+/*************************************************************************
+ *
+ *	Function: dict_attrget
+ *
+ *	Purpose: Return the full attribute structure based on the
+ *		 attribute id number.
+ *
+ *************************************************************************/
+
+DICT_ATTR *
+dict_attrget (attribute)
+
+int             attribute;
+
+{
+	DICT_ATTR      *attr;
+
+	attr = dictionary_attributes;
+	while (attr != (DICT_ATTR *) NULL)
+	{
+		if (attr->value == attribute)
+		{
+			return (attr);
+		}
+		attr = attr->next;
+	}
+	return ((DICT_ATTR *) NULL);
+} /* end of dict_attrget () */
+
+/*************************************************************************
+ *
+ *	Function: dict_attrfind
+ *
+ *	Purpose: Return the full attribute structure based on the
+ *		 attribute name.
+ *
+ *************************************************************************/
+
+DICT_ATTR *
+dict_attrfind (attrname)
+
+char           *attrname;
+{
+	DICT_ATTR      *attr;
+
+	attr = dictionary_attributes;
+	while (attr != (DICT_ATTR *) NULL)
+	{
+		if (strcasecmp (attr->name, attrname) == 0)
+		{
+			return (attr);
+		}
+		attr = attr->next;
+	}
+	return ((DICT_ATTR *) NULL);
+} /* end of dict_attrfind () */
+
+/*************************************************************************
+ *
+ *	Function: dict_valfind
+ *
+ *	Purpose: Return the full value structure based on the
+ *		 value name.
+ *
+ *************************************************************************/
+
+DICT_VALUE *
+dict_valfind (valname)
+
+char           *valname;
+
+{
+	DICT_VALUE     *val;
+
+	val = dictionary_values;
+	while (val != (DICT_VALUE *) NULL)
+	{
+		if (strcasecmp (val->name, valname) == 0)
+		{
+			return (val);
+		}
+		val = val->next;
+	}
+	return ((DICT_VALUE *) NULL);
+} /* end of dict_valfind () */
+
+/*************************************************************************
+ *
+ *	Function: dict_valget
+ *
+ *	Purpose: Return the full value structure based on the
+ *		 actual value and the associated attribute name.
+ *
+ *************************************************************************/
+
+DICT_VALUE *
+dict_valget (value, attrname)
+
+UINT4           value;
+char           *attrname;
+
+{
+	DICT_VALUE     *val;
+
+	val = dictionary_values;
+	while (val != (DICT_VALUE *) NULL)
+	{
+		if (strcmp (val->attrname, attrname) == 0 &&
+				val->value == value)
+		{
+			return (val);
+		}
+		val = val->next;
+	}
+	return ((DICT_VALUE *) NULL);
+} /* end of dict_valget () */
diff -rPu pppd.old/rad_funcs.c pppd/rad_funcs.c
--- pppd.old/rad_funcs.c	Thu Jan  1 03:00:00 1970
+++ pppd/rad_funcs.c	Tue Apr 29 21:20:56 1997
@@ -0,0 +1,2269 @@
+/*
+ * RADIUS -- Remote Authentication Dial In User Service
+ * 
+ * COPYRIGHT  (c)  1992, 1993, 1994, 1995, 1996
+ * THE REGENTS OF THE UNIVERSITY OF MICHIGAN AND MERIT NETWORK, INCORPORATED
+ * ALL RIGHTS RESERVED
+ * 
+ * PERMISSION IS GRANTED TO USE, COPY, CREATE DERIVATIVE WORKS AND REDISTRIBUTE
+ * THIS SOFTWARE AND SUCH DERIVATIVE WORKS IN BINARY FORM ONLY FOR ANY PURPOSE,
+ * SO LONG AS NO FEE IS CHARGED, AND SO LONG AS THE COPYRIGHT NOTICE ABOVE, THIS
+ * GRANT OF PERMISSION, AND THE DISCLAIMER BELOW APPEAR IN ALL COPIES MADE; AND
+ * SO LONG AS THE NAME OF THE UNIVERSITY OF MICHIGAN IS NOT USED IN ANY
+ * ADVERTISING OR PUBLICITY PERTAINING TO THE USE OR DISTRIBUTION OF THIS
+ * SOFTWARE WITHOUT SPECIFIC, WRITTEN PRIOR AUTHORIZATION.
+ * 
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE UNIVERSITY
+ * OF MICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND WITHOUT WARRANTY BY THE
+ * UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE.  THE REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE
+ * LIABLE FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OF THE SOFTWARE, EVEN IF IT HAS BEEN OR IS HEREAFTER
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ * 
+ * For a License to distribute source code or to charge a fee for the program
+ * or a product containing the program, contact MERIT at the University of
+ * Michigan:
+ * 
+ * aaa-license@merit.edu
+ * 
+ * [This version puts NO LIMITS on the use.  It grants the right to create
+ * DERIVATIVE WORKS.  The user may copy and distribute the code in the form
+ * received AND DERIVATIVE WORKS, so long as no fee is charged.  If copies are
+ * made, our copyright notice and the disclaimer must be included on them.  USE
+ * THIS VERSION WITH CARE.  THIS VERSION VERY LIKELY WILL KILL ANY POTENTIAL
+ * FOR LATER COMMERCIALIZATION OF THE SOFTWARE.]
+ *
+ *
+ * Public entry points in this file:
+ * 
+ * add_string
+ * authtype_toa
+ * avpair_add
+ * avpair_assign
+ * avpair_copy
+ * avpair_get
+ * avpair_new
+ * avpair_vtoa
+ * compress_file
+ * debug_list
+ * debug_pair
+ * dumpit
+ * fprint_attr_val
+ * gen_valpairs
+ * get_errmsg
+ * get_passwd
+ * get_vp
+ * get_last_vp
+ * insert_vp
+ * loghead
+ * logit
+ * missing_attribute
+ * parse_realm
+ * prune_pairs
+ * _reply_message
+ * reply_sprintf
+ * setupsock
+ * trunc_logfile
+ * type_string
+ * 
+ */
+
+static char     rcsid[] = "$Id: funcs.c,v 1.99 1996/06/14 16:14:47 web Exp $";
+
+#include	<sys/types.h>
+#include	<sys/param.h>
+
+#if defined(sys5)
+#include	<sys/sysmacros.h>
+#endif	/* sys5 */
+
+#include	<sys/socket.h>
+#include	<sys/time.h>
+
+#if defined(aix) || defined(ultrix)
+#include	<fcntl.h>
+#else	/* aix */
+#include	<sys/fcntl.h>
+#endif	/* aix */
+
+#include	<sys/wait.h>
+#include	<net/if.h>
+#include	<netinet/in.h>
+#include	<arpa/inet.h>
+
+#include	<stdio.h>
+#include	<netdb.h>
+#include	<time.h>
+#include	<ctype.h>
+#include	<errno.h>
+#include	<dirent.h>
+#include	<syslog.h>
+#include	<unistd.h>
+#include	<varargs.h>
+
+#include	"radius.h"
+
+extern int       debug_flag;
+extern int       dumpcore;
+extern int       file_logging;
+extern int       spawn_flag;
+extern int       zap_logfile;
+
+#if !defined(__FreeBSD__) && !defined(_BSDI_VERSION) && !defined(__NetBSD__)
+extern int       sys_nerr;
+extern char     *sys_errlist[];
+#endif	/* __FreeBSD__ */
+
+extern FILE     *ddt;
+extern FILE     *msgfd;
+extern char     *radius_dir;
+extern AATVPTR   rad_authen_aatv;
+extern time_t    birthdate;
+extern char      recv_buffer[4096];
+extern char      send_buffer[4096];
+extern char      ourhostname[MAXHOSTNAMELEN];
+
+/*************************************************************************
+ *
+ *	Function: add_string (string, convert)
+ *
+ *	Purpose: Store string in storage block, returning pointer.  
+ *		 Optimizes string storage allocation and allows only
+ *		 one copy of string to be stored.  A NULL string pointer
+ *		 returns a pointer to a null string.
+ *
+ *		 If convert is non-zero, the string is stored in lower-case.
+ *		 Thus a case insensitive match is performed to attempt to
+ *		 find an already existing copy of the string in the table.
+ *
+ *		 Use this only to store non-dynamic (i.e., configuration)
+ *		 strings as there is no way to delete strings and lookups
+ *		 to see if a string is already is present are quite 
+ *		 inefficient.
+ *
+ *************************************************************************/
+
+char *
+add_string (string, convert)
+
+char          *string;
+int            convert;
+
+{
+	typedef struct char_blk
+	{
+		struct char_blk        *next;
+		char		       *next_ptr;
+		int			rem_len;
+		char			strings[2048];
+	} CHAR_BLK;
+
+	static CHAR_BLK *first_blk = (CHAR_BLK *) NULL;
+	CHAR_BLK        *last_blk;
+	CHAR_BLK        *blk;
+	CHAR_BLK        *space_blk; /* 1st block with sufficient space */
+	register char   *ptr;
+	register char	*fptr;
+	register char	*str;
+	register UINT4	 len;
+	register UINT4	 slen;
+	char		 lcstr[AUTH_ID_LEN]; /* Case converted string */
+	static char     *func = "add_string";
+	
+	if (!string)
+	{
+		string = "";	/* Convert NULL pointer to null string */
+	}
+
+	/* Copy input to all lowercase */
+	if (convert & ASLC)
+	{
+		for (ptr = lcstr, fptr = string ; *fptr ; )
+		{
+			*ptr++ = tolower (*fptr++);
+		}
+		*ptr++ = '\0';
+		len = ptr - lcstr;
+		string = lcstr;
+	}
+	else
+	{
+		len = strlen (string) + 1;
+	}
+
+	space_blk = (CHAR_BLK *) NULL;
+
+	/* See if we're already storing this string */
+	for (blk = last_blk = first_blk ; blk ; last_blk = blk, blk = blk->next)
+	{
+		for (str = blk->strings ; str < blk->next_ptr ; str += slen)
+		{
+			if (len > (slen = (UINT4) *str++))
+			{
+				continue;
+			}
+		
+			fptr = slen - len + str; 
+			ptr = string;
+			while (*ptr++ == *fptr)
+			{
+				if (*fptr++ == '\0') /* Found it! */
+				{	
+					return fptr - len;
+				}
+			}
+		}
+		if (space_blk == (CHAR_BLK *) NULL && blk->rem_len > len)
+		{
+			space_blk = blk;
+		}
+	}
+
+	if (convert & FINDONLY)
+	{
+		return NULL;
+	}
+
+	/* Need to store new string.  See if we found space in previous block */
+	if (space_blk == (CHAR_BLK *) NULL)
+	{
+		if ((blk = (CHAR_BLK *) malloc (sizeof(CHAR_BLK))) 
+							== (CHAR_BLK *) NULL)
+		{
+			logit (LOG_DAEMON, LOG_ALERT,
+				"%s: FATAL couldn't allocate CHAR_BLK storage",
+				func);
+			abort ();
+		}
+		blk->next_ptr = blk->strings;
+		blk->rem_len = sizeof (blk->strings);
+		blk->next = (CHAR_BLK *) NULL;
+		if (last_blk == (CHAR_BLK *) NULL)
+		{
+			first_blk = blk;
+		}
+		else
+		{
+			last_blk->next = blk;
+		}
+		space_blk = blk;
+	}
+	fptr = space_blk->next_ptr;
+	*fptr++ = len;
+	strcpy (fptr, string);
+	space_blk->next_ptr = fptr + len;
+	space_blk->rem_len -= len + 1;
+	return fptr;
+} /* end of add_string () */
+
+/******************************************************************************
+ *
+ *	Function: authtype_toa
+ *
+ *	Purpose: Return a string representation of the authreq code.
+ *
+ *	Returns: pointer to static string.
+ *
+ *****************************************************************************/
+
+char *
+authtype_toa (code)
+
+int             code;		/* code indicating authen, acct, etc. */
+
+{
+	static char *typelist[] =
+	{
+		"invalid(0)",		/* invalid */
+		"access",		/* PW_ACCESS_REQUEST */
+		"accept",		/* PW_ACCESS_ACCEPT */
+		"reject",		/* PW_ACCESS_REJECT */
+		"acct-req",		/* PW_ACCOUNTING_REQUEST */
+		"acct-resp",		/* PW_ACCOUNTING_RESPONSE */
+		"acct-status",		/* PW_ACCOUNTING_STATUS */
+		"pw-request",		/* PW_PASSWORD_REQUEST */
+		"pw-ack",		/* PW_PASSWORD_ACK */
+		"pw-reject",		/* PW_PASSWORD_REJECT */
+		"acct-msg",		/* PW_ACCOUNTING_MESSAGE */
+		"access-challenge",	/* PW_ACCESS_CHALLENGE */
+		"status-server",	/* PW_STATUS_SERVER */
+		"status-client"		/* PW_STATUS_CLIENT */
+	};
+	static char unknown[20];
+
+	if ((code <= 0) || ((sizeof (typelist) / sizeof (typelist[0])) <= code))
+	{
+		if (code == PW_FORWARDING)
+		{
+			return "forwarding";
+		}
+
+		sprintf (unknown, "invalid(%d)", code);
+		return unknown;
+	}
+ 
+	return typelist[code];
+} /* end of authtype_toa () */
+
+/******************************************************************************
+ *
+ *	Function: avpair_add
+ *
+ *	Purpose: Add an attribute-value pair to the given list.
+ *
+ *	Returns: pointer to added a/v pair upon success,
+ *		 NULL pointer upon failure.
+ *
+ *	Remarks: Always appends the new pair to the end of the list.
+ *
+ *****************************************************************************/
+
+VALUE_PAIR *
+avpair_add (list, attrid, pval, len)
+
+VALUE_PAIR    **list;		/* a list of attribute-value pairs. */
+int             attrid;		/* Attribute id number. */
+void           *pval;		/* Pointer to value. */
+int             len;		/* length of value, or 0 */
+
+{
+	VALUE_PAIR     *vp;
+
+	vp = avpair_new (attrid, pval, len);
+
+	if (vp != (VALUE_PAIR *) NULL)
+	{
+		insert_vp (list, (VALUE_PAIR *) NULL, vp);
+	}
+
+	return vp;
+
+} /* end of avpair_add */
+
+/******************************************************************************
+ *
+ *	Function: avpair_assign
+ *
+ *	Purpose: Assign the given raw value to an attribute-value pair.
+ *
+ *	Returns:  0 on success,
+ *		 -1 on failure.
+ *
+ *****************************************************************************/
+
+int
+avpair_assign (vp, pval, len)
+
+VALUE_PAIR     *vp;	/* pointer to the attribute-value pair */
+void           *pval;	/* pointer to value to be assigned */
+int             len;	/* for strings: */
+			/*  len == 0 ==> null-terminated ASCII string */
+			/*  len > 0  ==> len is length of raw binary data */
+			/* for non-strings: */
+			/*  len == 0 ==> just plain data, just gets copied */
+			/*  len > 0  ==> convert data from network ... */
+			/*		... representation before copying */
+
+{
+	int             result = -1;
+	static char    *func = "avpair_assign";
+
+	if (len < 0 || len > AUTH_STRING_LEN)
+	{
+		logit (LOG_DAEMON, LOG_ERR, "%s: bad attribute length %d",
+			func, len);
+	}
+	else
+	{
+		switch (vp->type)
+		{
+		    case PW_TYPE_STRING: /* Use lvalue field to store length. */
+		    case PW_TYPE_OCTETS:
+		    case PW_TYPE_VENDOR:
+			if (len > 0) /* use length to control the raw copy */
+			{
+				memcpy (vp->strvalue, (char *) pval, len);
+				vp->strvalue[len] = 0; /* In case of string! */
+				vp->lvalue = len;
+			}
+			else /* len == 0 means the string is NULL terminated */
+			{
+				strncpy (vp->strvalue, (char *) pval,
+					AUTH_STRING_LEN);
+				vp->lvalue = strlen ((char *) pval);
+			}
+			result = 0;
+			break;
+
+		    case PW_TYPE_DATE:
+		    case PW_TYPE_INTEGER:
+		    case PW_TYPE_IPADDR:
+			if (len > 0) /* need to convert the raw network value */
+			{
+				vp->lvalue = ntohl(* (UINT4 *) pval);
+			}
+			else /* len == 0 indicates just plain data */
+			{
+				memcpy (&vp->lvalue, pval, sizeof (vp->lvalue));
+			}
+			*vp->strvalue = '\0'; /* Insure null for type IPADDR */
+			result = 0;
+			break;
+
+		    default:
+			dprintf(1, (LOG_DAEMON, LOG_DEBUG,
+				"%s: Unknown attribute %d", func, vp->type));
+		}
+	}
+	return result;
+} /* end of avpair_assign () */
+
+/******************************************************************************
+ *
+ *	Function: avpair_copy
+ *
+ *	Purpose: Copy a specified attribute-value pair from one given
+ *		 list to another.
+ *
+ *	Returns: 0 if the attribute-value pair doesn't exist in the source list,
+ *		 1 (non-zero) if the attribute-value pair is copied.
+ *
+ ******************************************************************************
+ */
+
+int
+avpair_copy (pdst, psrc, attr)
+
+VALUE_PAIR    **pdst;
+VALUE_PAIR     *psrc;
+int             attr;
+
+{
+	VALUE_PAIR     *pvp;
+	VALUE_PAIR     *pnew;
+	static char    *func = "avpair_copy";
+
+	if ((pvp = get_vp (psrc, attr)) == (VALUE_PAIR *) NULL)
+	{
+		return 0;
+	}
+
+	if ((pnew = (VALUE_PAIR *) malloc (sizeof (VALUE_PAIR)))
+							== (VALUE_PAIR *) NULL)
+	{
+		logit (LOG_DAEMON, LOG_ALERT, "%s: FATAL out of memory", func);
+		abort ();
+	}
+
+	memcpy ((char *) pnew, (char *) pvp, sizeof (VALUE_PAIR));
+	pnew->next = (VALUE_PAIR *) NULL;
+	insert_vp (pdst, (VALUE_PAIR *) NULL, pnew);
+
+	return 1;
+} /* end of avpair_copy () */
+
+/*******************************************************************************
+ *
+ *	Function: avpair_get
+ *
+ *	Purpose: Find specified a/v pair from a list and return its value.
+ *
+ *	Returns: Type of the attribute on success (look at PW_TYPE_*),
+ *		 or -1 on failure.
+ *
+ ******************************************************************************/
+
+int
+avpair_get (pval, vplist, attrid)
+
+void           *pval;		/* OUT: buffer for returning value */
+VALUE_PAIR     *vplist;		/* pointer to list of attribute-value pairs */
+int             attrid;		/* attribute to find */
+
+{
+	VALUE_PAIR        *vp;
+	static char       *func = "avpair_get";
+
+	vp = get_vp (vplist, attrid);
+	if (vp == (VALUE_PAIR *) NULL)
+	{
+		return (-1);
+	}
+
+	switch (vp->type)
+	{
+	    case PW_TYPE_STRING:
+		strcpy ((char *) pval, vp->strvalue);
+		break;
+
+	    case PW_TYPE_OCTETS:
+	    case PW_TYPE_VENDOR:
+		memcpy ((char *) pval, vp->strvalue, vp->lvalue);
+		break;
+
+	    case PW_TYPE_DATE:
+	    case PW_TYPE_INTEGER:
+	    case PW_TYPE_IPADDR:
+		*((UINT4 *) pval) = vp->lvalue;
+		break;
+
+	    default:
+		dprintf(1, (LOG_DAEMON, LOG_DEBUG,
+			"%s: Unknown attribute %d", func, vp->type));
+	}
+	return vp->type;
+} /* end of avpair_get () */
+
+/******************************************************************************
+ *
+ *	Function: avpair_new
+ *
+ *	Purpose: Make an new attribute-value pair with given parameters.
+ *
+ *	Returns: Pointer to generated a/v pair when successful,
+ *		 NULL when failure.
+ *
+ *****************************************************************************/
+
+VALUE_PAIR *
+avpair_new (attrid, pval, len)
+
+int             attrid;		/* integer id number of the attribute */
+void           *pval;		/* pointer to value */
+int             len;		/* length of value, or 0 */
+
+{
+	VALUE_PAIR     *vp = (VALUE_PAIR *) NULL;
+	DICT_ATTR      *pda;
+	static char    *func = "avpair_new";
+
+	if ((pda = dict_attrget (attrid)) == (DICT_ATTR *) NULL)
+	{
+		dprintf(1, (LOG_DAEMON, LOG_DEBUG,
+				"%s: Unknown attribute %d", func, attrid));
+	}
+	else
+	{
+		if ((vp = (VALUE_PAIR *) malloc (sizeof (VALUE_PAIR)))
+							!= (VALUE_PAIR *) NULL)
+		{
+			strncpy (vp->name, pda->name, sizeof (vp->name));
+			vp->attribute = attrid;
+			vp->next = (VALUE_PAIR *) NULL;
+			vp->type = pda->type;
+			if (avpair_assign (vp, pval, len) == 0)
+			{
+				return vp;
+			}
+			free (vp);
+			vp = (VALUE_PAIR *) NULL;
+		}
+		else
+		{
+			logit (LOG_DAEMON, LOG_ALERT, "%s: FATAL out of memory",
+				func);
+			abort ();
+		}
+	}
+	return vp;
+} /* end of avpair_new () */
+
+#define	MAX_AVPAIR_VTOA 20
+
+/*************************************************************************
+ *
+ *	Function: avpair_vtoa
+ *
+ *	Purpose: Produce a string representation of the value of a pair.
+ *
+ *************************************************************************/
+
+char *
+avpair_vtoa (vp, sws)
+
+VALUE_PAIR	*vp;
+int		 sws;
+
+{
+  int             max;
+  int             pos;
+  UINT4           num;
+  char           *c;
+  char           *p;
+  DICT_VALUE     *dval;
+  struct in_addr  inad;
+  static char     buffers[MAX_AVPAIR_VTOA][1024];
+  static int      ndx = 0;
+  char           *buff = buffers[ndx];
+  static char    *func = "avpair_vtoa";
+
+  if (vp == (VALUE_PAIR *) NULL)
+  {
+	if ((sws & AVPAIR_VTOA_NULL) != 0)
+	{
+		return "";
+	}
+	return NULL;
+  }
+
+  ndx++;
+  if (ndx > MAX_AVPAIR_VTOA)
+  {
+	ndx = 0;
+  }
+
+  switch (vp->type)
+  {
+    case PW_TYPE_STRING:
+	p = (char *) buff;
+	c = vp->strvalue;
+
+	if ((sws & AVPAIR_VTOA_QUOTE) != 0)
+	{
+		*p++ = '\'';
+	}
+
+	max = vp->lvalue;
+	if (max == 0)
+	{
+	  dprintf(2, (LOG_DAEMON, LOG_DEBUG,
+		       "%s: Using strlen() for vp 0x%p {type=%d, attr=%d}",
+		       func, vp, vp->type, vp->attribute));
+	  max = strlen (vp->strvalue);
+	}
+
+	pos = 0;
+	for (pos = 0; pos < max; pos++)
+	{
+		if (c[pos] == '\0')
+		{
+			break;
+		}
+
+		switch (c[pos])
+		{
+		    case '\b':		/* BACKSPACE */
+			*p++ = '\\';
+			*p++ = 'b';
+			break;
+
+		    case '\r':		/* Carriage Return */
+			*p++ = '\\';
+			*p++ = 'r';
+			break;
+
+		    case '\n':		/* Newline / Line feed */
+			*p++ = '\\';
+			*p++ = 'n';
+			break;
+
+		    case '\t':		/* Horizontal tab */
+			*p++ = '\\';
+			*p++ = 't';
+			break;
+
+		    case '\\':		/* Self-escape */
+			*p++ = '\\';
+			*p++ = '\\';
+			break;
+
+		    case '\'':		/* Quote-escape */
+			*p++ = '\\';
+			*p++ = '\'';
+			break;
+
+		    default:
+			if ((' ' <= c[pos]) && (c[pos] < 0x7f))
+			{
+				*p++ = c[pos];
+			}
+			else
+			{
+				char tmp[10];
+
+				if (c[pos] == 0)
+				{
+					strcpy (p, "\\0");
+				}
+				else
+				{
+					sprintf (tmp, "\\x%2.2x",
+						(unsigned char) c[pos]);
+					strcpy (p, tmp);
+				}
+				p += strlen (p);
+			}
+		}
+	}  /* for each character... */
+
+	if ((sws & AVPAIR_VTOA_QUOTE) != 0)
+	{
+		*p++ = '\'';
+	}
+
+	*p = '\0';
+ 
+	break;
+
+    case PW_TYPE_INTEGER:
+	dval = dict_valget (vp->lvalue, vp->name);
+	if (dval != (DICT_VALUE *) NULL)
+	{
+		strcpy (buff, dval->name);
+	}
+	else
+	{
+#if defined(__alpha)
+	sprintf (buff, "%d", vp->lvalue);
+#else	/* defined(alpha) */
+	sprintf (buff, "%ld", vp->lvalue);
+#endif	/* defined(alpha) */
+	}
+	break;
+ 
+    case PW_TYPE_IPADDR:
+	inad.s_addr = htonl(vp->lvalue);
+	strcpy (buff, inet_ntoa (inad));
+	break;
+ 
+    case PW_TYPE_DATE:
+	strftime (buff, sizeof (buff), "%b %e %Y",
+	gmtime ((time_t *) & vp->lvalue));
+	break;
+ 
+
+    case PW_TYPE_OCTETS:
+	strcpy (buff, "0x");
+	p = (char *) buff;
+	p += strlen (buff);
+	c = vp->strvalue;
+
+	for (pos = 0; pos < vp->lvalue; pos++)
+	{
+		char tmp[3];
+ 
+		sprintf (tmp, "%2.2x", c[pos]);
+		strcpy (p, tmp);
+		if (pos % 4 == 3)
+		{
+			strcat (p, " ");
+		}
+		p += strlen (p);
+	}
+	*p = '\0';
+	break;
+
+    case PW_TYPE_VENDOR:
+	c = vp->strvalue;
+	if (*c != 0)
+	{
+		logit (LOG_DAEMON, LOG_ERR,
+			"%s: Invalid vendor-specific code, 0x%x", func, *c);
+		return NULL;
+	}
+	memcpy ((char *) &num, c, 4);
+	pos = ntohl(num);
+
+	sprintf (buff, "v%d-", pos);
+	for (pos = 4; pos < vp->lvalue; pos++)
+	{
+		char tmp[3];
+ 
+		sprintf (tmp, "%2.2x", c[pos]);
+		strcpy (p, tmp);
+		p += strlen (p);
+	}
+	*p = '\0';
+	break;
+
+    default:
+	if (sws & AVPAIR_VTOA_NULL)
+	{
+		return "";
+	}
+	return NULL;
+  } /* switch (vp) */
+
+return buff;
+
+} /* end of avpair_vtoa () */
+
+/*************************************************************************
+ *
+ *	Function: compress_file
+ *
+ *	Purpose: Compress and reopen a file.
+ *
+ *************************************************************************/
+
+void
+compress_file (fd, fname)
+
+FILE          **fd;
+char           *fname;
+
+{
+	int             pid;
+	struct tm      *clock;
+	time_t          now;
+	char            today[80];
+	char            name[128];
+	char            newname[128];
+	static char    *func = "compress_file";
+
+	now = time (0);
+	now -= 86400; /* the archived log is for yesterday! */
+	sprintf (name, "%s/%s", radius_dir, fname);
+	clock = localtime (&now);
+	strftime (today, 15, "%y%m%d", clock);
+	sprintf (newname, "%s/%s.%s", radius_dir, fname, today);
+	if (link (name, newname) < 0) /* get another handle */
+	{
+		return;
+	}
+
+	if (unlink (name) < 0)
+	{
+		return;
+	}
+
+	sync ();		/* update disk metadata */
+
+	fclose (*fd);
+
+	if ((*fd = fopen (name, "w")) == (FILE *) NULL)
+	{
+		return;
+	}
+
+	now += 86400; /* correct the time now */
+	strcpy (today, ctime (&now));
+	fprintf (*fd,
+		"%-24.24s: created by PID %u Version %s up since %-24.24s\n",
+		today, getpid (), RADIUS_VERSION, ctime (&birthdate));
+#ifndef SCO
+	fchmod (fileno (*fd), 0644);
+#endif	/* SCO */
+	
+	if (spawn_flag > 0) /* only compress if we won't block */
+	{
+		pid = (int) fork ();
+		if (pid < 0) /* error */
+		{
+	      		logit (LOG_DAEMON, LOG_ALERT,
+				"%s: fork: %s", func, get_errmsg ());
+			exit (-5);
+		}
+		if (pid > 0) /* parent */
+		{
+			return;
+		}
+		else /* in child */
+		{
+			execl (RADIUS_COMPRESS, RADIUS_COMPRESS,
+				newname, (char *) NULL);
+			exit (0);
+		}
+	}
+
+	return;
+} /* end of compress_file () */
+
+/*************************************************************************
+ *
+ *	Function: debug_list
+ *
+ *	Purpose: Print the value pair list to the desired File.
+ *
+ *************************************************************************/
+
+void
+debug_list (fd, pair)
+
+FILE           *fd;
+VALUE_PAIR     *pair;
+
+{
+	VALUE_PAIR     *vp;
+
+	if (debug_flag)
+	{
+		if (ddt)
+		{
+			fd = ddt;
+		}
+
+		vp = pair;
+
+		while (vp != (VALUE_PAIR *) NULL)
+		{
+			debug_pair (fd, vp);
+			vp = vp->next;
+		}
+	}
+	return;
+} /* end of debug_list () */
+
+/*************************************************************************
+ *
+ *	Function: debug_pair
+ *
+ *	Purpose: Print the Attribute-value pair to the desired File.
+ *
+ *************************************************************************/
+
+void
+debug_pair (fd, pair)
+
+FILE           *fd;
+VALUE_PAIR     *pair;
+
+{
+	if (debug_flag)
+	{
+		if (ddt)
+		{
+			fd = ddt;
+		}
+
+		fputs ("    ", fd);
+		fprint_attr_val (fd, pair);
+		fputs ("\n", fd);
+	}
+	return;
+} /* end of debug_pair () */
+
+/*************************************************************************
+ *
+ *	Function: dumpit
+ *
+ *	Purpose: Dump the memory area using logit()
+ *
+ *	Usage: dumpit (facility, level, ptr, ptrlen, offset, format, args, ...);
+ *
+ *		Where facility and level are found in syslog.h and the
+ *		format is just a printf-style format string using the args.
+ *
+ *************************************************************************/
+
+int
+dumpit (facility, level, ptr, ptrlen, offset, format, va_alist)
+
+int           facility;		/* Logging facility, see dprintf() */
+int           level;		/* Logging level, see dprintf() */
+char         *ptr;		/* Pointer to start of dumping region. */
+unsigned int  ptrlen;		/* Length of the region to dump. */
+unsigned int  offset;		/* Offset from "ptr" to where to start dump. */
+char         *format;		/* A sprintf() like format string. */
+va_dcl				/* Arguments for sprintf() string. */
+
+{
+	va_list         pvar;
+	int             done;
+
+	static char     buffer[MAXPATHLEN]; /* Work area. */
+
+	va_start (pvar);
+	vsprintf (buffer, format, pvar);
+	va_end (pvar);
+
+	if (strlen (buffer) > 0)
+	{
+		logit (facility, level, "%s", buffer);
+	}
+
+	logit (facility, level, "Hex dump at 0x%p/%x for %d bytes", ptr, offset,
+	       ptrlen);
+
+	while (ptrlen > 0)
+	{
+	    done = hex_dump (buffer, ptr, ptrlen, offset);
+	    ptrlen -= done;
+	    offset += done;
+	    ptr += done;
+	    logit (facility, level, "%s", buffer);
+	}
+
+	return 0;
+
+} /* end of dumpit () */
+
+void
+fprint_attr_val (fd, pair)
+
+FILE           *fd;
+VALUE_PAIR     *pair;
+
+{
+	char           *val;
+
+	if (pair->name[0] == '\0')
+	{
+		return;
+	}
+
+	if ((val = avpair_vtoa (pair, 0)) != (char *) NULL)
+	{
+	    fprintf (fd, "%s = %s", pair->name, val);
+	}
+	else
+	{
+	    fprintf (fd, "%s is unknown", pair->name);
+	}
+
+	return;
+
+} /* end of fprint_attr_val () */
+
+/*************************************************************************
+ *
+ *	Function: gen_valpairs
+ *
+ *	Purpose: Takes attribute/value pairs from buffer and builds a
+ *		 value_pair list using allocated memory.
+ *
+ *************************************************************************/
+
+VALUE_PAIR *
+gen_valpairs (auth)
+
+AUTH_HDR       *auth;
+
+{
+	int             attribute;
+	int             attrlen;
+	int             bad_packet = 0; /* Set to one if bad packet rec'v'd. */
+	int             len;
+	UINT4           lvalue;
+	u_char         *ptr;
+	DICT_ATTR      *attr;
+	VALUE_PAIR     *vp;
+	VALUE_PAIR     *pair;
+	static char    *func = "gen_valpairs";
+
+	dprintf(2, (LOG_DAEMON, LOG_DEBUG, "%s: entered", func));
+
+	/*
+	 *	Extract attribute-value pairs
+	 */
+	ptr = auth->data;
+	len = ntohs(auth->length) - AUTH_HDR_LEN; /* length of attributes */
+	vp = (VALUE_PAIR *) NULL;
+
+	while (len > 0)
+	{
+		attribute = *ptr++;
+		attrlen = *ptr++;
+		attrlen -= 2;
+		if (attrlen < 0 || attrlen > AUTH_STRING_LEN || attrlen > len)
+		{
+			dumpit (LOG_DAEMON, LOG_ERR, ptr, attrlen, 0,
+				"Received attribute %d with invalid length %d",
+				attribute, attrlen);
+			bad_packet = 1;
+			break;
+		}
+
+		if ((attr = dict_attrget (attribute)) == (DICT_ATTR *) NULL)
+		{
+			ddumpx(1, (LOG_DAEMON, LOG_DEBUG, ptr, attrlen, 0,
+				"Received unknown attribute %d of length %d",
+				attribute, attrlen));
+			bad_packet = 1;
+		}
+		else
+		{
+			if ((pair =
+				(VALUE_PAIR *) malloc (sizeof (VALUE_PAIR))) ==
+					(VALUE_PAIR *) NULL)
+			{
+				logit (LOG_DAEMON, LOG_ALERT,
+					"%s: FATAL out of memory", func);
+				abort ();
+			}
+			strcpy (pair->name, attr->name);
+			pair->attribute = attr->value;
+			pair->type = attr->type;
+			pair->next = (VALUE_PAIR *) NULL;
+
+			switch (attr->type)
+			{
+
+			    case PW_TYPE_STRING:
+			    case PW_TYPE_OCTETS:
+			    case PW_TYPE_VENDOR:
+				memcpy (pair->strvalue, (char *) ptr, attrlen);
+				pair->strvalue[attrlen] = '\0';
+				pair->lvalue = attrlen;
+				debug_pair (stdout, pair);
+				insert_vp (&vp, (VALUE_PAIR *) NULL, pair);
+				break;
+
+			    case PW_TYPE_INTEGER:
+			    case PW_TYPE_IPADDR:
+				memcpy ((char *) &lvalue, (char *) ptr,
+					sizeof (UINT4));
+				pair->lvalue = ntohl(lvalue);
+				*pair->strvalue = '\0'; /* zap DNS for IPADDR */
+				debug_pair (stdout, pair);
+				insert_vp (&vp, (VALUE_PAIR *) NULL, pair);
+				break;
+
+			    default:
+				dprintf(1, (LOG_DAEMON, LOG_DEBUG,
+					"    %s (Unknown Type)", attr->name));
+				free (pair);
+				break;
+			}
+
+		}
+		ptr += attrlen;
+		len -= attrlen + 2;
+	}
+
+	if (bad_packet)
+	{
+		ddumpx(1, (LOG_DAEMON, LOG_DEBUG, auth->data,
+			ntohs(auth->length) - AUTH_HDR_LEN, 0,
+			"Dump of entire bad packet"));
+	}
+
+	return (vp);
+} /* end of gen_valpairs () */
+
+/*************************************************************************
+ *
+ *	Function: get_errmsg
+ *
+ *	Purpose: Return pointer to static string which gives complete
+ *		 filesystem error message.
+ *
+ *************************************************************************/
+
+char *
+get_errmsg ()
+
+{
+	static char     errmsg[80];
+
+	if (errno < sys_nerr)
+	{
+		return (char *) sys_errlist[errno];
+	}
+	else
+	{
+		sprintf (errmsg, "Error %d", errno);
+		return errmsg;
+	}
+} /* end of get_errmsg () */
+
+/******************************************************************************
+ *
+ *	Function: get_passwd
+ *
+ *	Purpose: Decrypts password contained in the PW_USER_PASSWORD or
+ *		 PW_ENCRYPTED_PASSWORD value-pair in the AUTH_REQ queue
+ *		 entry.  Tries to match one of these passwords against
+ *		 the "user_secret", if the "user_secret" is not a NULL
+ *		 pointer.  Will also perform a CHAP authentication check,
+ *		 if the PW_CHAP_PASSWORD value-pair is found in the request
+ *		 and "user_secret" is provided.
+ *
+ *	Returns: 2 to indicate CHAP failure,
+ *		 1 to indicate PASSWORD match failure,
+ *		 0 to indicate success,
+ *		-1 if no PASSWORD or CHAP value-pair is present.
+ *
+ ******************************************************************************/
+
+int
+get_passwd (authreq, pw, user_secret, salt)
+
+AUTH_REQ       *authreq;	/* Pointer to authentication request.  */
+char           *pw;		/* Receives decrypted password.        */
+				/* (If NULL, only CHAP is attempted.)  */
+char           *user_secret;	/* Secret we share with this user or   */
+				/* NULL.  If NULL, CHAP is not allowed */
+				/* and the password match check is not */
+				/* performed */
+char           *salt;		/* Encryption salt.  If this and the   */
+				/* user_secret are both non-NULL, then */
+				/* crypt() is called to encrypt the    */
+				/* password before attempting to match */
+				/* the user_secret or the check CHAP   */
+				/* reply.*/
+
+{
+	VALUE_PAIR     *item;
+	u_char          buffer[AUTH_PASS_LEN + AUTH_VECTOR_LEN + 1];
+	int             i;
+	int             secretlen;
+	int             result;
+	u_char          digest[CHAP_VALUE_LENGTH];
+	u_char         *ptr;
+	char           *crypt ();
+	static char    *func = "get_passwd";
+
+	dprintf(3, (LOG_DAEMON, LOG_DEBUG, "%s: entered", func));
+
+	if (pw != NULL &&
+		(item = get_vp (authreq->request, PW_USER_PASSWORD))
+							!= (VALUE_PAIR *) NULL)
+	{
+		/* Use the secret to setup the decryption digest */
+		secretlen = strlen ((char *) authreq->secret);
+		strcpy ((char *) buffer, (char *) authreq->secret);
+		memcpy ((char *) buffer + secretlen, (char *) authreq->vector,
+			AUTH_VECTOR_LEN);
+		md5_calc (digest, buffer, secretlen + AUTH_VECTOR_LEN);
+		memcpy (pw, item->strvalue, AUTH_PASS_LEN);
+		for (i = 0; i < AUTH_PASS_LEN; i++)
+		{
+			pw[i] ^= digest[i];
+		}
+		pw[AUTH_PASS_LEN] = '\0'; /* thanks to billw@cisco.com */
+		result = 0;
+		if (user_secret != (char *) NULL)
+		{
+			if (salt != (char *) NULL)
+			{
+				pw = crypt (pw, salt);
+			}
+
+			if ((result = strcmp (pw, user_secret)) != 0)
+			{
+				result = 1;	/* Indicate pw match failure */
+			}
+		}
+	}
+	else if (user_secret != (char *) NULL &&
+			(item = get_vp (authreq->request, PW_CHAP_PASSWORD))
+							!= (VALUE_PAIR *) NULL)
+	{
+		/* Use MD5 to verify CHAP */
+		ptr = buffer;
+		*ptr++ = *item->strvalue;
+		strcpy ((char *) ptr, user_secret);
+
+		/*
+		 * Allow for CHAP using an encrypted password. (The client
+		 * encrypts the password before generating the CHAP challenge
+		 * reply.)  This lets CHAP be used even when passwords are
+		 * stored in an encrypted form.  No clients provide this
+		 * capability yet, but they could start doing so now!
+		 */
+
+		if (salt != (char *) NULL)
+		{
+			ptr = (u_char *) crypt ((char *) ptr, salt);
+		}
+
+		secretlen = strlen ((char *) ptr);
+		ptr += secretlen;
+		memcpy ((char *) ptr, (char *) authreq->vector,
+			AUTH_VECTOR_LEN);
+		md5_calc (digest, buffer,
+			  1 + CHAP_VALUE_LENGTH + secretlen);
+		/* Compare them */
+		if ((result = memcmp ((char *) digest, item->strvalue + 1,
+				      CHAP_VALUE_LENGTH)) != 0)
+		{
+			result = 2;	/* Indicate CHAP failure */
+		}
+
+	}
+	else
+	{
+		if (pw)
+			*pw = '\0';	/* Return null as pw */
+		result = -1;	/* -1 indicates no PW or CHAP vp in request */
+	}
+	return (result);
+} /* end of get_passwd () */
+
+/*************************************************************************
+*
+*	Function: get_last_vp
+*
+*	Purpose: Find the last attribute value-pair (which matches the specified
+*		 attribute) from the specified value-pair list.
+*
+*************************************************************************/
+
+VALUE_PAIR *
+get_last_vp (vp, attr)
+
+VALUE_PAIR     *vp;
+UINT4           attr;
+
+{
+	VALUE_PAIR     *last_vp;
+
+	/*
+	 *	Find the first matching value-pair.
+	 */	
+
+	for (; vp != (VALUE_PAIR *) NULL && vp->attribute != attr; vp = vp->next)
+	{
+		continue;
+	}
+
+	/*
+	 *	If we run off the end of the list, return a NULL.
+	 */
+	if (vp == (VALUE_PAIR *) NULL)
+	{
+		return (VALUE_PAIR *) NULL;
+	}
+
+	/* 
+	 *	Scan the remainder of the list for a match.
+	 *	If we found a match, return it.
+	 */
+	last_vp = get_last_vp (vp->next, attr);
+
+	if (last_vp != (VALUE_PAIR *) NULL)
+	{
+		return (last_vp);
+	}
+
+	/* If didn't find a match, return the one we found above. */
+	return (vp);	
+
+} /* end of get_last_vp () */
+
+/*************************************************************************
+*
+*	Function: get_vp
+*
+*	Purpose: Find the first attribute value-pair (which matches the given
+*		 attribute) from the specified value-pair list.
+*
+*************************************************************************/
+
+VALUE_PAIR *
+get_vp (vp, attr)
+
+VALUE_PAIR     *vp;
+UINT4           attr;
+
+{
+	for (; vp != (VALUE_PAIR *) NULL && vp->attribute != attr; vp = vp->next)
+	{
+		continue;
+	}
+
+	return (vp);
+} /* end of get_vp () */
+
+/*************************************************************************
+ *
+ *	Function: hex_dump
+ *
+ *	Purpose: Format a region of octets into a formatted string
+ *		 suitable for displaying that region of memory.
+ *
+ *************************************************************************/
+
+int
+hex_dump (buffer, data, len, offset)
+
+char         *buffer;
+char         *data;
+int           len;
+int           offset;
+
+{
+    int             i = 0;
+    int             j = 0;
+    int             size = 0;
+    int             wlen = 0;
+    char            hex[3];  /* a hex string placeholder. */
+    unsigned char  *start = (unsigned char *) data;
+
+    if ((!buffer) || (!data) || (len <= 0))
+    {
+	return -1;
+    }
+
+    sprintf (buffer, "0x%p:", data);
+    
+    buffer += strlen (buffer);
+    if (offset >= 0)
+    {
+	sprintf (buffer, " 0x%4.4x|", offset);
+	buffer += strlen (buffer);
+    }
+    
+    if (len > 16)
+    {
+	len = 16;
+    }
+    
+    /* Dump the data in hexadecimal. */
+
+    for (i = 0; i < len; i += 4)
+    {
+	wlen = i + 4;
+	if (wlen > len)
+	{
+		wlen = len;
+	}
+
+	strcat (buffer, " ");
+	buffer++;
+	for ( j = i; j < wlen; j++)
+	{
+	    sprintf (hex, "%2.2X", *start++);
+	    strcat (buffer, hex);
+	    buffer += 2;
+	    size++;
+	}
+	
+	for ( ; j < (i + 4) ; j++)
+	{
+	    strcat (buffer, "..");
+	    buffer += 2;
+	}
+    }
+    
+    for ( ; i < 16 ; i += 4)
+    {
+	strcat (buffer, " ........");
+	buffer += 9;
+    }
+
+    /* Dump the data in ASCII. */
+
+    strcat (buffer, "| |");
+    buffer += strlen (buffer);
+    for (i = 0; i < len; i++)
+    {
+	if ((' ' <= *data) && (*data < 0x7f))
+	{
+	    *buffer++ = *data;
+	}
+	else
+	{
+	    *buffer++ = '.';
+	}
+	data++;
+    }
+
+    for ( ; i < 16 ; i++)
+    {
+	*buffer++ = '|';
+    }
+
+    *buffer++ = '|';
+    *buffer = '\0';  /* Terminate the string. */
+
+    return size;
+ 
+} /* end of hex_dump () */
+
+/*************************************************************************
+ *
+ *	Function: insert_vp
+ *
+ *	Purpose: Given the address of an existing list "a" and a pointer
+ *		 to an entry "p" in that list, add the value pair "b" to
+ *		 the "a" list after the "p" entry.  If "p" is NULL, add
+ *		 the value pair "b" to the end of "a".
+ *
+ *************************************************************************/
+
+void
+insert_vp (a, p, b)
+
+VALUE_PAIR     **a;
+VALUE_PAIR     *p;
+VALUE_PAIR     *b;
+
+{
+	VALUE_PAIR     *this_node;
+	VALUE_PAIR     *vp;
+	static char    *func = "insert_vp";
+
+	if (b->next != (VALUE_PAIR *) NULL)
+	{
+		logit (LOG_DAEMON, LOG_ALERT,
+			"%s: FATAL value pair (0x%p) next ptr. (0x%p) not NULL",
+			func, b, b->next);
+		dumpcore = 1;
+		abort ();
+	}
+
+	if (*a == (VALUE_PAIR *) NULL)
+	{
+		*a = b;
+		return;
+	}
+
+	vp = *a;
+
+	if ( p == (VALUE_PAIR *) NULL) /* run to end of "a" list */
+	{
+		while (vp != (VALUE_PAIR *) NULL)
+		{
+			this_node = vp;
+			vp = vp->next;
+		}
+	}
+	else /* look for the "p" entry in the "a" list */
+	{
+		this_node = *a;
+		while (this_node != (VALUE_PAIR *) NULL)
+		{
+			if (this_node == p)
+			{
+				break;
+			}
+			this_node = this_node->next;
+		}
+	}
+
+	b->next = this_node->next;
+	this_node->next = b;
+
+	return;
+} /* end of insert_vp () */
+
+/*************************************************************************
+ *
+ *	Function: logit
+ *
+ *	Purpose: Log the provided error message to the error logging facility.
+ *
+ *	Usage: logit (facility, level, format, args, ...);
+ *
+ *		Where facility and level are found in syslog.h and the
+ *		format is just a printf-style format string using the args.
+ *
+ *************************************************************************/
+
+int
+logit (va_alist)
+
+va_dcl
+
+{
+	va_list         pvar;
+	int             priority;
+	int             facility;
+	int             level;
+	char           *format;
+	time_t          timeval;
+	char            filename[MAXPATHLEN];
+	static char     buffer[MAXPATHLEN];
+	static char    *func = "logit";
+
+	va_start (pvar);
+	facility = va_arg (pvar, int);
+	level = va_arg (pvar, int);
+	format = va_arg (pvar, char *);
+	vsprintf (buffer, format, pvar);
+	va_end (pvar);
+
+	if (file_logging == 1) /* log it to the logfile */
+	{
+		if (msgfd == stderr)
+		{
+			msgfd = (FILE *) NULL;
+		}
+
+		if (msgfd == (FILE *) NULL)
+		{
+			sprintf (filename, "%s", RADIUS_LOG);
+			msgfd = fopen (filename, "a");
+
+			if (msgfd == (FILE *) NULL)
+			{
+				fprintf (stderr,
+					"%s: Couldn't open %s for logging\n",
+					func, filename);
+				msgfd = stderr;
+			}
+		}
+
+		if (level != LOG_DEBUG) /* don't log debugging messages */
+		{
+			timeval = time (0);
+			fprintf (msgfd, "%-24.24s: %s\n",
+				ctime (&timeval), buffer);
+
+			if (level == LOG_ALERT)
+			{
+				fflush (msgfd);
+			}
+		}
+	}
+	else /* was not logging to a file */
+	{
+		if (file_logging == 2) /* log it to stderr */
+		{
+			fprintf (msgfd, "%s\n", buffer);
+		}
+		else /* log it to syslog(3) */
+		{
+			zap_logfile = 0;
+			priority = facility | level;
+			syslog (priority, "%s", buffer);
+		}
+	}
+
+	if (level == LOG_DEBUG && ddt != (FILE *) NULL)
+	{
+		fprintf (ddt, "%s\n", buffer); /* log it to pre-opened device */
+	}
+
+	return 0;
+} /* end of logit () */
+
+/******************************************************************************
+ *
+ *	Function: loghead
+ *
+ *	Purpose: Write a header in a logfile if it is empty.  This function
+ *		 guarantees the header is only written once at the very
+ *		 beginning of the file.
+ *
+ *	Usage: loghead (file, format, args, ...);
+ *
+ *		Where file is a FILE pointer to an open file and the
+ *		format is just a printf style format string using the args.
+ *
+ *****************************************************************************/
+
+int
+loghead (va_alist)
+
+va_dcl
+
+{
+	va_list          pvar;
+	int              fd;
+	char            *format;
+	FILE            *fp;
+	struct flock     lck;
+
+	va_start (pvar);
+	fp = va_arg (pvar, FILE *);
+	fd = fileno (fp);
+	lck.l_type = F_WRLCK;
+	lck.l_whence = 0;
+	lck.l_start = SEEK_SET;
+	lck.l_len = 0L;
+	lck.l_pid = (pid_t) 0;
+	fcntl (fd, F_SETLKW, lck);
+	if (lseek (fd, 0L, SEEK_CUR) == (off_t) 0L)
+	{
+		format = va_arg (pvar, char *);
+		vfprintf (fp, format, pvar);
+		va_end (pvar);
+	}
+	lck.l_type = F_UNLCK;
+	fcntl (fd, F_SETLK, lck);
+	return 0;
+} /* end of loghead () */
+
+/*************************************************************************
+ *
+ *	Function: missing_attribute
+ *
+ *	Purpose: Generate standard log message for missing attributes.
+ *
+ *************************************************************************/
+
+void
+missing_attribute (authreq, func, attribute, etc)
+
+AUTH_REQ       *authreq;
+char	       *func;		/* Function which called us. */
+int		attribute;	/* Missing attribute. */
+char	       *etc;		/* Optional additional information. */
+
+{
+	VALUE_PAIR    *vp;
+	DICT_ATTR     *attr = dict_attrget (attribute);
+	char          *attr_name;
+	char           unknown_attr[20];
+
+	if (attr != (DICT_ATTR *) NULL)
+	{
+		attr_name = attr->name;  /* Mark name. */
+	}
+	else
+	{
+		sprintf (unknown_attr, "unknown(%d)", attribute);
+		attr_name = unknown_attr;
+	}
+
+	/* Get NAS-Identifier or IP address. */
+	if ((vp = get_vp (authreq->cur_request, PW_NAS_IDENTIFIER))
+							== (VALUE_PAIR *) NULL)
+	{
+		vp = get_vp (authreq->cur_request, PW_NAS_IP_ADDRESS);
+	}
+
+	logit (LOG_DAEMON, LOG_ERR,
+       "%s:* MISSING %s (%d) in %s (type %d) request %d from %s via. %s[%d] %s",
+		func, attr_name, attribute, authtype_toa (authreq->code),
+		authreq->code, authreq->id,
+		(vp == (VALUE_PAIR *) NULL) ? "?" : avpair_vtoa (vp, 0),
+		ip_hostname (authreq->ipaddr), authreq->udp_port,
+		(etc == (char *) NULL)  ? "" : etc);
+	return;
+} /* end of missing_attribute () */
+
+/*************************************************************************
+ *
+ *	Function: parse_realm
+ *
+ *	Purpose: Split user entered string found in PW_USER_NAME into
+ *		 the PW_USER_ID and PW_USER_REALM a/v pairs and hang
+ *		 them both on both the authreq->request and the
+ *		 authreq->cur_request lists for later use.
+ *
+ *	Returns: pointer to the realm a/v pair,
+ *		 NULL otherwise.
+ *
+ *************************************************************************/
+
+VALUE_PAIR *
+parse_realm (authreq)
+
+AUTH_REQ       *authreq;
+
+{
+	int             count;
+	int             type;
+	VALUE_PAIR     *vp;
+	VALUE_PAIR    **vp_prev;
+	DICT_ATTR      *attr;
+	char           *agent;
+	char           *filter;
+	char           *pos;
+	char           *realm;
+	char           *u_realm;
+	char           *userid;
+	char            name[AUTH_ID_LEN + 1];
+	static char    *func = "parse_realm";
+
+	dprintf(2, (LOG_AUTH, LOG_DEBUG, "%s: entered", func));
+
+	if ((vp = get_vp (authreq->cur_request, PW_USER_REALM))
+							!= (VALUE_PAIR *) NULL)
+	{
+		return vp;	/* It's already there, so just return it! */
+	}
+
+	if ((vp = get_vp (authreq->cur_request, PW_USER_ID))
+							== (VALUE_PAIR *) NULL)
+	{
+		return vp;	/* The PW_USER_ID must be there to parse! */
+	}
+
+	/* Make a local copy of the name. */
+	strncpy (name, vp->strvalue, AUTH_ID_LEN);
+	name[AUTH_ID_LEN] = '\0'; /* Guarantee a null terminated string. */
+
+	if ((pos = strchr (name, '@')) != (char *) NULL) /* Is the '@' style. */
+	{
+		if ((userid = strtok (name, "@")) == (char *) NULL)
+		{
+			return (VALUE_PAIR *) NULL; /* Can't use a null name. */
+		}
+		if ((u_realm = strtok (NULL, "")) == (char *) NULL)
+		{
+			u_realm = "";
+		}
+	}
+	else /* There was no '@' in the User-Id. */
+	{
+		if ((pos = strchr (name, '/')) != (char *) NULL) /* '/' style */
+		{
+			if ((u_realm = strtok (name, "/")) == (char *) NULL)
+			{
+				u_realm = "";
+			}
+			if ((userid = strtok (NULL, "")) == (char *) NULL)
+			{
+				return (VALUE_PAIR *) NULL; /* Name was null. */
+			}
+		}
+		else /* There was neither '@' nor '/' in the User-Id. */
+		{
+			userid = name;
+			u_realm = "";
+		}
+	}
+
+	/* Check for null user id. */
+	if ((userid == (char *) NULL) || (*userid == '\0'))
+	{
+		return (VALUE_PAIR *) NULL; /* Name was null. */
+	}
+
+	strcpy (vp->strvalue, userid);	/* Modify the existing value... */
+	vp->lvalue = strlen (vp->strvalue);	/* and set length correctly. */
+
+	/* Next, build the realm a/v pair. */
+	if ((vp = (VALUE_PAIR *) malloc (sizeof (VALUE_PAIR)))
+							== (VALUE_PAIR *) NULL)
+	{
+		logit (LOG_DAEMON, LOG_ALERT,
+			"%s: FATAL out of memory", func);
+		abort ();
+	}
+
+	attr = dict_attrget (PW_USER_REALM);
+	strcpy (vp->name, attr->name);
+	vp->attribute = attr->value;
+	vp->type = attr->type;
+	vp->next = (VALUE_PAIR *) NULL;
+
+	if ((u_realm == (char *) NULL) || (u_realm[0] == '\0'))
+	{
+		vp->strvalue[0] = '\0';
+		vp->lvalue = 0;
+	}
+	else /* There is a realm string. */
+	{
+		if (find_auth_type (u_realm, PW_PROTTYPE_DFLT,
+					(char *) authreq->file_pfx, &type,
+					&agent, &realm, &filter) != 0)
+		{
+			strcpy (vp->strvalue, u_realm);
+		}
+		else /* Use primary (canonical) realm name. */
+		{
+			strcpy (vp->strvalue, realm);
+		}
+		vp->lvalue = strlen (vp->strvalue);	/* Set length */
+	}
+	
+	/*
+	 *	Now stick it at end of original request items on the
+	 *	cur_request list so it can be included in cur_count.
+	 */
+	count = authreq->cur_count;
+	vp_prev = &authreq->cur_request;
+	while (count-- > 0)
+	{
+		if ((vp_prev = &(*vp_prev)->next) == (VALUE_PAIR **) NULL)
+		{
+			logit (LOG_DAEMON, LOG_ERR,
+				"%s: cur_count is bad!", func);
+			abort ();
+		}
+	}
+	vp->next = *vp_prev;
+	*vp_prev = vp;
+
+	authreq->cur_count++; /* indicate and record the additional a/v pair */
+
+	dprintf(2, (LOG_AUTH, LOG_DEBUG, "%s: name = '%s', realm = '%s'",
+		func, userid, vp->strvalue));
+
+	return vp;
+} /* end of parse_realm () */
+
+/*************************************************************************
+ *
+ *	Function: prune_pairs    XXX: turn this into an AATV later
+ *
+ *	Purpose: Remove all extraneous a/v pairs before replying to NAS.
+ *
+ *	Returns: 0 == normal return,
+ *		-1 == some error occurred.
+ *
+ *	Remark: This code may change the order of the a/v pairs returned.
+ *
+ *************************************************************************/
+
+int
+prune_pairs (authreq, vendor, result)
+
+AUTH_REQ       *authreq;	/* modify this request's cur_request list */
+PRUN_LIST      *vendor;		/* for this vendor's NAS */
+int             result;		/* the packet type Access-Accept or -Reject */
+
+{
+	int             i;
+	int             j;
+	int             msg_flag;
+	int             success;
+	VALUE_PAIR    **prev_ptr;
+	VALUE_PAIR     *vp;
+	short           cnt[256];	/* holds the count of each attribute */
+	static char    *func = "prune_pairs";
+
+	switch (result)
+	{
+	    case EV_ACK:
+		msg_flag = PRUN_FLG1;
+		break;
+
+	    case EV_NAK:
+		msg_flag = PRUN_FLG2;
+		break;
+
+	    case EV_ACC_CHAL: /* Not handled according to RADIUS DRAFT RFC */
+		return 0;
+		break;
+
+	    default:
+		return (-1);
+		break;
+	}
+
+	if (result == EV_ACK && authreq->code == PW_ACCOUNTING_REQUEST) /* no */
+	{
+		list_free (authreq->cur_request);
+		authreq->cur_request = (VALUE_PAIR *) NULL;
+		return 0;
+	}
+
+	/* Initialize the array cnt[]. */
+
+	memset (cnt, '\0', sizeof (cnt));
+
+	/* Record the frequency of each reply a/v pair. */
+
+	prev_ptr = &authreq->cur_request;
+	for ( vp = *prev_ptr;
+		vp != (VALUE_PAIR *) NULL ;
+		vp = *prev_ptr)
+	{
+		if (vp->attribute > 255) /* then it must be a check-item */
+		{
+			*prev_ptr = vp->next;
+			free (vp);	/* this one is not allowed */
+		}
+		else /* it is a reply-item, so count it */
+		{
+			++cnt[vp->attribute];
+			prev_ptr = &vp->next;
+		}
+	}
+
+	/* Prune the cur_request list. */
+
+	prev_ptr = &authreq->cur_request;
+	for (vp = *prev_ptr;
+		vp != (VALUE_PAIR *) NULL;
+		vp = *prev_ptr)
+	{
+		j = 0;
+		success = 0;
+		while (vendor->rules[j].value > 0) /* last element is == zero */
+		{
+			if (vendor->rules[j].value == vp->attribute)
+			{
+				success = 1;
+				break;
+			}
+			j++;
+		}
+
+		if (success) /* then j is the index into the rules array */
+		{
+			/* See if this attribute type is allowed in this msg? */
+			if (vendor->rules[j].flags & msg_flag)
+			{
+				i = vendor->rules[j].count;
+				if (i < 0)
+				{
+					i = 9999; /* pick a suitable infinity */
+				}
+
+				if (cnt[vp->attribute] > i) /* reduce counter */
+				{
+					if (cnt[vp->attribute] > 0) /* if pos */
+					{
+						--cnt[vp->attribute];
+					}
+					*prev_ptr = vp->next;
+					free (vp); /* this one is not allowed */
+					vp = (VALUE_PAIR *) NULL;
+				}
+				else /* it's a keeper! */
+				{
+					prev_ptr = &vp->next;
+				}
+			}
+			else /* no, unconditionally remove this a/v pair */
+			{
+				*prev_ptr = vp->next;
+				free (vp);	/* this one is not allowed */
+				vp = (VALUE_PAIR *) NULL;
+			}
+		}
+		else /* this a/v pair is not in the rules, logit and leave it */
+		{
+			logit (LOG_AUTH, LOG_ERR,
+				"%s: odd attribute number %d in reply",
+				func, vp->attribute);
+			prev_ptr = &vp->next;
+		}
+	}
+
+	return 0;
+} /* end of prune_pairs () */
+
+/*************************************************************************
+ *
+ *	Function: _reply_message
+ *
+ *	Purpose: Generate a reply message and stick it into the list
+ *		 of reply items to send back to the client.
+ *
+ *	Returns: 0 == normal return
+ *		-1 == msgno is out of range
+ *
+ *	Remarks: The message is also logged.
+ *
+ *************************************************************************/
+
+int
+_reply_message (authreq, msgno, func, filename, line)
+
+AUTH_REQ       *authreq;	/* this authentication request */
+ERRORCODE       msgno;		/* message number */
+char           *func;		/* calling function name */
+char           *filename;	/* Filename were error occured. */
+int             line;		/* Line number where error occured. */
+
+{
+	static char    *msg[] =
+	{
+		"Internal error",
+		"Configuration error",
+		"Out of memory",
+		"Error creating file",
+		"No token available",
+		"No ports available for guests",
+		"Too many simultaneous sessions",
+		"ABS failure",
+		"Error querying balance",
+		"Your account balance is too low"
+	};
+
+	if ((u_int)(--msgno) < numbof(msg))
+	{
+		logit (LOG_AUTH, LOG_INFO, "%s: %s at %s line %d", func,
+			msg[msgno], filename, line);
+		reply_sprintf (0, authreq, "%s [%s()]", msg[msgno], func);
+		return 0;
+	}
+	reply_sprintf (0, authreq, "Software error [%s()]", func);
+	logit (LOG_AUTH, LOG_ERR, "%s: message %d out of range at %s line %d",
+		func, msgno, filename, line);
+	return (-1);
+} /* end of _reply_message () */
+
+/*************************************************************************
+ *
+ *	Function: reply_sprintf
+ *
+ *	Purpose: Generate a reply message and stick it into the list
+ *		 of reply items to send back to the client.
+ *
+ *	Returns: 0 == normal return
+ *		-1 == authreq is bad somehow.
+ *
+ *	Remarks: The message is also logged if logsw is 1.
+ *               It ALWAYS adds a <CR><LF> ("\r\n") to the message.
+ *
+ *************************************************************************/
+
+int
+reply_sprintf ( va_alist /* int logsw, AUTH_REQ *authreq, char *format, ... */)
+
+va_dcl
+
+{
+	va_list         pvar;
+
+	int             logsw;  /* First argument. */
+	AUTH_REQ       *authreq;
+	VALUE_PAIR     *msg;
+	char           *format;
+
+	char            buf[MAXPATHLEN];
+
+	/* Gather parameters. */
+
+	va_start (pvar);
+	logsw = va_arg (pvar, int);
+	authreq = va_arg (pvar, AUTH_REQ *);
+	format = va_arg (pvar, char *);
+
+	/* Format message, add it to reply, log it if necessary. */
+
+	vsprintf (buf, format, pvar);
+
+	if ( logsw != 0 )
+	{
+	      logit (LOG_AUTH, LOG_INFO, "%s", buf);
+	}
+
+	strcat (buf, "\r\n");     /* Force newline. */
+	if ((msg = get_last_vp (authreq->cur_request, PW_REPLY_MESSAGE))
+							!= (VALUE_PAIR *) NULL)
+	{
+		/* Check to see if there's any space remaining. */
+		if ((strlen (buf) + strlen (msg->strvalue)) > AUTH_STRING_LEN)
+		{
+			msg = (VALUE_PAIR *) NULL;
+		}
+	}
+
+	/* Concatenate the new message onto the last message. */
+	if (msg != (VALUE_PAIR *) NULL)
+	{
+		strcat (msg->strvalue, buf);
+	}
+	else
+	{
+		avpair_add (&authreq->cur_request, PW_REPLY_MESSAGE, buf, 0);
+	}
+
+	return 0;
+} /* end of reply_sprintf () */
+
+/*************************************************************************
+ *
+ *	Function: setupsock
+ *
+ *	Purpose: Gets and binds a socket.
+ *
+ *************************************************************************/
+
+int
+setupsock (sin, portnum)
+
+struct sockaddr_in *sin;
+int             portnum;
+
+{
+	int             sock;
+	int             sinlen;
+	static char    *func = "setupsock";
+
+	/*
+	 * Get a socket.
+	 */
+	if ((sock = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
+	{
+		fprintf (stderr, "%s: socket() UDP socket %d failed, %s\n",
+			func, portnum, sys_errlist[errno]);
+		exit (-11);
+	}
+	sinlen = sizeof (struct sockaddr_in);
+	memset ((char *) sin, '\0', sinlen);
+
+	/*
+	 * Get server's listening port number
+	 */
+	sin->sin_port = htons(portnum);
+
+	/*
+	 * Bind socket to port.  bind finds free port if portnum == 0
+	 */
+	sin->sin_family = AF_INET;
+	sin->sin_addr.s_addr = INADDR_ANY;
+	if (bind (sock, (struct sockaddr *) sin,
+		sizeof (struct sockaddr_in)) < 0)
+	{
+		fprintf (stderr, "%s: bind() UDP socket %d failed, %s\n",
+			func, portnum, sys_errlist[errno]);
+		exit (-11);
+	}
+	/* Retrieve complete socket info */
+	if (getsockname (sock, (struct sockaddr *) sin, &sinlen) < 0)
+	{
+		fprintf (stderr, "%s: getsockname() UDP socket %d failed, %s\n",
+			func, portnum, sys_errlist[errno]);
+		exit (-11);
+	}
+	return sock;
+} /* end of setupsock () */
+
+/*************************************************************************
+ *
+ *	Function: trunc_logfile
+ *
+ *	Purpose: Truncate, compress and rename the logfile near midnight.
+ *
+ *************************************************************************/
+
+void
+trunc_logfile (fd, fname)
+
+FILE          **fd;
+char           *fname;
+
+{
+	static int      archived = 0;
+	struct tm      *clock;
+	time_t          now;
+
+	now = time (0);
+	clock = localtime (&now);
+	if (((clock->tm_wday == TRUNCATION_DAY) || (TRUNCATION_DAY == 7)) &&
+		(clock->tm_hour == 0 && archived == 0))
+	{
+		compress_file (fd, fname);
+
+		archived = 1;
+	}
+	else
+	{
+		if (clock->tm_hour != 0)
+		{
+			archived = 0;
+		}
+	}
+
+	return;
+} /* end of trunc_logfile () */
+
+/*************************************************************************
+ *
+ *	Function: type_string
+ *
+ *	Purpose: Returns protocol type string for logging of authentication
+ *		 requests.
+ *
+ *************************************************************************/
+
+char *
+type_string (authreq, protpair)
+
+AUTH_REQ       *authreq;
+VALUE_PAIR     *protpair;
+
+{
+	char           *ptr;
+	VALUE_PAIR     *user_type;
+	static char     string[10];
+
+	user_type = get_vp (authreq->request, PW_SERVICE_TYPE);
+	if (user_type != (VALUE_PAIR *) NULL &&
+		user_type->lvalue == PW_AUTHENTICATE_ONLY)
+	{
+		ptr = "auth";
+	}
+	else
+	{
+		ptr = (protpair == (VALUE_PAIR *) NULL)
+			? "dumb" : avpair_vtoa (protpair, AVPAIR_VTOA_NULL);
+	}
+	strncpy (string, ptr, sizeof (string));
+	return (string);
+} /* end of type_string () */
diff -rPu pppd.old/rad_md5.c pppd/rad_md5.c
--- pppd.old/rad_md5.c	Thu Jan  1 03:00:00 1970
+++ pppd/rad_md5.c	Tue Jul 18 23:01:32 1995
@@ -0,0 +1,370 @@
+/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
+ */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+ */
+
+#include "md5.h"
+
+/* Constants for MD5Transform routine.
+ */
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+static void MD5Transform PROTO_LIST ((UINT4[4], unsigned char[64]));
+static void Encode PROTO_LIST
+                ((unsigned char *, UINT4 *, unsigned int));
+static void Decode PROTO_LIST
+                ((UINT4 *, unsigned char *, unsigned int));
+static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int));
+static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int));
+
+static unsigned char PADDING[64] = {
+	0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G, H and I are basic MD5 functions.
+ */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+Rotation is separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { \
+ (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define GG(a, b, c, d, x, s, ac) { \
+ (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define HH(a, b, c, d, x, s, ac) { \
+ (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define II(a, b, c, d, x, s, ac) { \
+ (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+
+md5_calc (output, input, inlen)
+unsigned char  *output;
+unsigned char  *input;		/* input block */
+unsigned int    inlen;		/* length of input block */
+{
+	MD5_CTX         context;
+
+	MD5Init (&context);
+	MD5Update (&context, input, inlen);
+	MD5Final (output, &context);
+}
+
+/* MD5 initialization. Begins an MD5 operation, writing a new context.
+ */
+void 
+MD5Init (context)
+MD5_CTX        *context;	/* context */
+{
+	context->count[0] = context->count[1] = 0;
+
+	/*
+	 * Load magic initialization constants.
+	 */
+	context->state[0] = 0x67452301;
+	context->state[1] = 0xefcdab89;
+	context->state[2] = 0x98badcfe;
+	context->state[3] = 0x10325476;
+}
+
+/* MD5 block update operation. Continues an MD5 message-digest
+  operation, processing another message block, and updating the
+  context.
+ */
+void 
+MD5Update (context, input, inputLen)
+MD5_CTX        *context;	/* context */
+unsigned char  *input;		/* input block */
+unsigned int    inputLen;	/* length of input block */
+{
+	unsigned int    i,
+	                index,
+	                partLen;
+
+	/* Compute number of bytes mod 64 */
+	index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
+
+	/* Update number of bits */
+	if ((context->count[0] += ((UINT4) inputLen << 3))
+			< ((UINT4) inputLen << 3))
+		context->count[1]++;
+	context->count[1] += ((UINT4) inputLen >> 29);
+
+	partLen = 64 - index;
+
+	/*
+	 * Transform as many times as possible.
+	 */
+	if (inputLen >= partLen)
+	{
+		MD5_memcpy
+			((POINTER) & context->buffer[index], (POINTER) input, partLen);
+		MD5Transform (context->state, context->buffer);
+
+		for (i = partLen; i + 63 < inputLen; i += 64)
+			MD5Transform (context->state, &input[i]);
+
+		index = 0;
+	}
+	else
+		i = 0;
+
+	/* Buffer remaining input */
+	MD5_memcpy
+		((POINTER) & context->buffer[index], (POINTER) & input[i],
+		 inputLen - i);
+}
+
+/* MD5 finalization. Ends an MD5 message-digest operation, writing the
+  the message digest and zeroizing the context.
+ */
+void 
+MD5Final (digest, context)
+unsigned char   digest[16];	/* message digest */
+MD5_CTX        *context;	/* context */
+{
+	unsigned char   bits[8];
+	unsigned int    index,
+	                padLen;
+
+	/* Save number of bits */
+	Encode (bits, context->count, 8);
+
+	/*
+	 * Pad out to 56 mod 64.
+	 */
+	index = (unsigned int) ((context->count[0] >> 3) & 0x3f);
+	padLen = (index < 56) ? (56 - index) : (120 - index);
+	MD5Update (context, PADDING, padLen);
+
+	/* Append length (before padding) */
+	MD5Update (context, bits, 8);
+
+	/* Store state in digest */
+	Encode (digest, context->state, 16);
+
+	/*
+	 * Zeroize sensitive information.
+	 */
+	MD5_memset ((POINTER) context, 0, sizeof (*context));
+}
+
+/* MD5 basic transformation. Transforms state based on block.
+ */
+static void 
+MD5Transform (state, block)
+UINT4           state[4];
+unsigned char   block[64];
+{
+	UINT4           a = state[0],
+	                b = state[1],
+	                c = state[2],
+	                d = state[3],
+	                x[16];
+
+	Decode (x, block, 64);
+
+	/* Round 1 */
+	FF (a, b, c, d, x[0], S11, 0xd76aa478);	/* 1 */
+	FF (d, a, b, c, x[1], S12, 0xe8c7b756);	/* 2 */
+	FF (c, d, a, b, x[2], S13, 0x242070db);	/* 3 */
+	FF (b, c, d, a, x[3], S14, 0xc1bdceee);	/* 4 */
+	FF (a, b, c, d, x[4], S11, 0xf57c0faf);	/* 5 */
+	FF (d, a, b, c, x[5], S12, 0x4787c62a);	/* 6 */
+	FF (c, d, a, b, x[6], S13, 0xa8304613);	/* 7 */
+	FF (b, c, d, a, x[7], S14, 0xfd469501);	/* 8 */
+	FF (a, b, c, d, x[8], S11, 0x698098d8);	/* 9 */
+	FF (d, a, b, c, x[9], S12, 0x8b44f7af);	/* 10 */
+	FF (c, d, a, b, x[10], S13, 0xffff5bb1);	/* 11 */
+	FF (b, c, d, a, x[11], S14, 0x895cd7be);	/* 12 */
+	FF (a, b, c, d, x[12], S11, 0x6b901122);	/* 13 */
+	FF (d, a, b, c, x[13], S12, 0xfd987193);	/* 14 */
+	FF (c, d, a, b, x[14], S13, 0xa679438e);	/* 15 */
+	FF (b, c, d, a, x[15], S14, 0x49b40821);	/* 16 */
+
+	/* Round 2 */
+	GG (a, b, c, d, x[1], S21, 0xf61e2562);	/* 17 */
+	GG (d, a, b, c, x[6], S22, 0xc040b340);	/* 18 */
+	GG (c, d, a, b, x[11], S23, 0x265e5a51);	/* 19 */
+	GG (b, c, d, a, x[0], S24, 0xe9b6c7aa);	/* 20 */
+	GG (a, b, c, d, x[5], S21, 0xd62f105d);	/* 21 */
+	GG (d, a, b, c, x[10], S22, 0x2441453);	/* 22 */
+	GG (c, d, a, b, x[15], S23, 0xd8a1e681);	/* 23 */
+	GG (b, c, d, a, x[4], S24, 0xe7d3fbc8);	/* 24 */
+	GG (a, b, c, d, x[9], S21, 0x21e1cde6);	/* 25 */
+	GG (d, a, b, c, x[14], S22, 0xc33707d6);	/* 26 */
+	GG (c, d, a, b, x[3], S23, 0xf4d50d87);	/* 27 */
+	GG (b, c, d, a, x[8], S24, 0x455a14ed);	/* 28 */
+	GG (a, b, c, d, x[13], S21, 0xa9e3e905);	/* 29 */
+	GG (d, a, b, c, x[2], S22, 0xfcefa3f8);	/* 30 */
+	GG (c, d, a, b, x[7], S23, 0x676f02d9);	/* 31 */
+	GG (b, c, d, a, x[12], S24, 0x8d2a4c8a);	/* 32 */
+
+	/* Round 3 */
+	HH (a, b, c, d, x[5], S31, 0xfffa3942);	/* 33 */
+	HH (d, a, b, c, x[8], S32, 0x8771f681);	/* 34 */
+	HH (c, d, a, b, x[11], S33, 0x6d9d6122);	/* 35 */
+	HH (b, c, d, a, x[14], S34, 0xfde5380c);	/* 36 */
+	HH (a, b, c, d, x[1], S31, 0xa4beea44);	/* 37 */
+	HH (d, a, b, c, x[4], S32, 0x4bdecfa9);	/* 38 */
+	HH (c, d, a, b, x[7], S33, 0xf6bb4b60);	/* 39 */
+	HH (b, c, d, a, x[10], S34, 0xbebfbc70);	/* 40 */
+	HH (a, b, c, d, x[13], S31, 0x289b7ec6);	/* 41 */
+	HH (d, a, b, c, x[0], S32, 0xeaa127fa);	/* 42 */
+	HH (c, d, a, b, x[3], S33, 0xd4ef3085);	/* 43 */
+	HH (b, c, d, a, x[6], S34, 0x4881d05);	/* 44 */
+	HH (a, b, c, d, x[9], S31, 0xd9d4d039);	/* 45 */
+	HH (d, a, b, c, x[12], S32, 0xe6db99e5);	/* 46 */
+	HH (c, d, a, b, x[15], S33, 0x1fa27cf8);	/* 47 */
+	HH (b, c, d, a, x[2], S34, 0xc4ac5665);	/* 48 */
+
+	/* Round 4 */
+	II (a, b, c, d, x[0], S41, 0xf4292244);	/* 49 */
+	II (d, a, b, c, x[7], S42, 0x432aff97);	/* 50 */
+	II (c, d, a, b, x[14], S43, 0xab9423a7);	/* 51 */
+	II (b, c, d, a, x[5], S44, 0xfc93a039);	/* 52 */
+	II (a, b, c, d, x[12], S41, 0x655b59c3);	/* 53 */
+	II (d, a, b, c, x[3], S42, 0x8f0ccc92);	/* 54 */
+	II (c, d, a, b, x[10], S43, 0xffeff47d);	/* 55 */
+	II (b, c, d, a, x[1], S44, 0x85845dd1);	/* 56 */
+	II (a, b, c, d, x[8], S41, 0x6fa87e4f);	/* 57 */
+	II (d, a, b, c, x[15], S42, 0xfe2ce6e0);	/* 58 */
+	II (c, d, a, b, x[6], S43, 0xa3014314);	/* 59 */
+	II (b, c, d, a, x[13], S44, 0x4e0811a1);	/* 60 */
+	II (a, b, c, d, x[4], S41, 0xf7537e82);	/* 61 */
+	II (d, a, b, c, x[11], S42, 0xbd3af235);	/* 62 */
+	II (c, d, a, b, x[2], S43, 0x2ad7d2bb);	/* 63 */
+	II (b, c, d, a, x[9], S44, 0xeb86d391);	/* 64 */
+
+	state[0] += a;
+	state[1] += b;
+	state[2] += c;
+	state[3] += d;
+
+	/*
+	 * Zeroize sensitive information.
+	 */
+	MD5_memset ((POINTER) x, 0, sizeof (x));
+}
+
+/* Encodes input (UINT4) into output (unsigned char). Assumes len is
+  a multiple of 4.
+ */
+static void 
+Encode (output, input, len)
+unsigned char  *output;
+UINT4          *input;
+unsigned int    len;
+{
+	unsigned int    i,
+	                j;
+
+	for (i = 0, j = 0; j < len; i++, j += 4)
+	{
+		output[j] = (unsigned char) (input[i] & 0xff);
+		output[j + 1] = (unsigned char) ((input[i] >> 8) & 0xff);
+		output[j + 2] = (unsigned char) ((input[i] >> 16) & 0xff);
+		output[j + 3] = (unsigned char) ((input[i] >> 24) & 0xff);
+	}
+}
+
+/* Decodes input (unsigned char) into output (UINT4). Assumes len is
+  a multiple of 4.
+ */
+static void 
+Decode (output, input, len)
+UINT4          *output;
+unsigned char  *input;
+unsigned int    len;
+{
+	unsigned int    i,
+	                j;
+
+	for (i = 0, j = 0; j < len; i++, j += 4)
+		output[i] = ((UINT4) input[j]) | (((UINT4) input[j + 1]) << 8) |
+			(((UINT4) input[j + 2]) << 16) | (((UINT4) input[j + 3]) << 24);
+}
+
+/* Note: Replace "for loop" with standard memcpy if possible.
+ */
+
+static void 
+MD5_memcpy (output, input, len)
+POINTER         output;
+POINTER         input;
+unsigned int    len;
+{
+	unsigned int    i;
+
+	for (i = 0; i < len; i++)
+		output[i] = input[i];
+}
+
+/* Note: Replace "for loop" with standard memset if possible.
+ */
+static void 
+MD5_memset (output, value, len)
+POINTER         output;
+int             value;
+unsigned int    len;
+{
+	unsigned int    i;
+
+	for (i = 0; i < len; i++)
+		((char *) output)[i] = (char) value;
+}
diff -rPu pppd.old/rad_regexp.c pppd/rad_regexp.c
--- pppd.old/rad_regexp.c	Thu Jan  1 03:00:00 1970
+++ pppd/rad_regexp.c	Mon Nov 24 19:51:03 1997
@@ -0,0 +1,24 @@
+#include <string.h>
+#include <sys/types.h>
+#include <regexp.h>
+
+int regimatch(const char *expr, const char *str) {
+  regexp *exp;
+  int res;
+
+  exp = regcomp(expr);
+  res = regexec(exp, str);
+  free(exp);
+  return res;
+}
+
+#if 0
+int main(ac, av)
+char *av[];
+{
+  char *s1, *s2;
+  if (ac != 3) return 3;
+  s1 = strdup(av[1]); s2 = strdup(av[2]);
+  return regimatch(s1, s2);
+}
+#endif
diff -rPu pppd.old/rad_sendserver.c pppd/rad_sendserver.c
--- pppd.old/rad_sendserver.c	Thu Jan  1 03:00:00 1970
+++ pppd/rad_sendserver.c	Wed Dec  8 11:11:08 1999
@@ -0,0 +1,866 @@
+/*
+ *
+ *	RADIUS   Remote Authentication Dial In User Service
+ *
+ *
+ * COPYRIGHT  (c)  1992, 1993, 1994, 1995, 1996
+ * THE REGENTS OF THE UNIVERSITY OF MICHIGAN AND MERIT NETWORK, INCORPORATED
+ * ALL RIGHTS RESERVED
+ * 
+ * PERMISSION IS GRANTED TO USE, COPY, CREATE DERIVATIVE WORKS AND REDISTRIBUTE
+ * THIS SOFTWARE AND SUCH DERIVATIVE WORKS IN BINARY FORM ONLY FOR ANY PURPOSE,
+ * SO LONG AS NO FEE IS CHARGED, AND SO LONG AS THE COPYRIGHT NOTICE ABOVE, THIS
+ * GRANT OF PERMISSION, AND THE DISCLAIMER BELOW APPEAR IN ALL COPIES MADE; AND
+ * SO LONG AS THE NAME OF THE UNIVERSITY OF MICHIGAN IS NOT USED IN ANY
+ * ADVERTISING OR PUBLICITY PERTAINING TO THE USE OR DISTRIBUTION OF THIS
+ * SOFTWARE WITHOUT SPECIFIC, WRITTEN PRIOR AUTHORIZATION.
+ * 
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE UNIVERSITY
+ * OF MICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND WITHOUT WARRANTY BY THE
+ * UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE.  THE REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE
+ * LIABLE FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OF THE SOFTWARE, EVEN IF IT HAS BEEN OR IS HEREAFTER
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ * 
+ * For a License to distribute source code or to charge a fee for the program
+ * or a product containing the program, contact MERIT at the University of
+ * Michigan:
+ * 
+ * aaa-license@merit.edu
+ * 
+ * [This version puts NO LIMITS on the use.  It grants the right to create
+ * DERIVATIVE WORKS.  The user may copy and distribute the code in the form
+ * received AND DERIVATIVE WORKS, so long as no fee is charged.  If copies are
+ * made, our copyright notice and the disclaimer must be included on them.  USE
+ * THIS VERSION WITH CARE.  THIS VERSION VERY LIKELY WILL KILL ANY POTENTIAL
+ * FOR LATER COMMERCIALIZATION OF THE SOFTWARE.]
+ *
+ *
+ * Public entry points in this file:
+ *
+ * check_radius_reply (only public for the MINOS/MNET daemon)
+ * dir_init
+ * send_server
+ * random_vector
+ *
+ */
+
+static char     rcsid[] = "$Id: sendserver.c,v 1.54 1996/05/22 19:58:19 web Exp $";
+
+#include	<sys/types.h>
+#include	<sys/socket.h>
+#include	<netinet/in.h>
+#include	<arpa/inet.h>
+#include	<sys/param.h>
+#include	<sys/time.h>
+
+#if !(defined(FD_SET) || defined(linux))
+#include	<sys/select.h>
+#endif	/* FD_SET */
+
+#include	<errno.h>
+#include	<netdb.h>
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<syslog.h>
+#include	<time.h>
+#include	<unistd.h>
+
+#include	"radius.h"
+
+#if !defined(__FreeBSD__) && !defined(_BSDI_VERSION) && !defined(__NetBSD__)
+extern char    *sys_errlist[];
+#endif	/* __FreeBSD__ */
+
+#ifndef DEFAULT_SERVER
+#define DEFAULT_SERVER	"ns.link-ul.ru"
+#endif
+
+#ifndef DEFAULT_DIR
+#define DEFAULT_DIR	"/var/adm/raddb"
+#endif
+
+#ifndef DEFAULT_DIR2
+#define DEFAULT_DIR2	"/var/adm/raddb"
+#endif
+
+extern char     ourhostname[MAXHOSTNAMELEN];
+extern char    *progname;
+extern int      debug_flag;
+extern char    *radius_dir;
+
+int             radsock = 0;	/* fd for radius socket, if non-blocking mode */
+static int      find_server PROTO ((char *, int, UINT4 *, char *, char *));
+void            random_vector PROTO ((u_char *));
+int             check_radius_reply PROTO ((u_char *, char *, u_char *, u_int, char *));
+
+/*************************************************************************
+*
+*	dir_init - if not set, initializes global var for RADIUS clients
+*
+**************************************************************************/
+
+void
+dir_init ()
+
+{
+	if (radius_dir == NULL || radius_dir[0] == '\0')
+	{
+		radius_dir = DEFAULT_DIR;
+		if (access (radius_dir, X_OK) != 0)
+		{
+			radius_dir = DEFAULT_DIR2;
+		}
+	}
+	return;
+} /* end of dir_init () */
+
+/*************************************************************************
+ *
+ *	Function: pack_list
+ *
+ *	Purpose: Packs an attribute value pair list into a buffer.
+ *
+ *	Returns: Number of octets packed.
+ *
+ *************************************************************************/
+
+static int
+pack_list (vp, buf)
+
+VALUE_PAIR     *vp;
+char           *buf;
+
+{
+	int             length;
+	int             total_length = 0;
+	UINT4           lvalue;
+
+	while (vp != (VALUE_PAIR *) NULL)
+	{
+		debug_pair (stderr, vp);
+		*buf++ = vp->attribute;
+
+		switch (vp->type)
+		{
+		    case PW_TYPE_STRING:
+			/* length = length = (vp->lvalue > 0) ?
+					vp->lvalue : strlen (vp->strvalue); */
+			length = strlen (vp->strvalue);
+			*buf++ = length + 2;
+			memcpy (buf, vp->strvalue, length);
+			buf += length;
+			total_length += length + 2;
+			break;
+
+		    case PW_TYPE_INTEGER:
+		    case PW_TYPE_IPADDR:
+			*buf++ = sizeof (UINT4) + 2;
+			lvalue = htonl (vp->lvalue);
+			memcpy (buf, (char *) &lvalue, sizeof (UINT4));
+			buf += sizeof (UINT4);
+			total_length += sizeof (UINT4) + 2;
+			break;
+
+		    default:
+			break;
+		}
+
+		vp = vp->next;
+	}
+	return total_length;
+} /* end of pack_list () */
+
+/*************************************************************************
+*
+*	send_server - Sends request to specified RADIUS server and waits
+*		      for response.  Request is retransmitted every
+*		      "response_timeout" seconds a maximum of "retry_max"
+*		      times.  Result is 0 if response was received, -1 if
+*		      a problem occurred, or +1 on no-response condition.
+*		      Returns request retransmit count in "retries" if
+*		      server does respond.
+*
+*	Returns:	-1 ERROR_RC   -- on local error,
+*			 0 OK_RC      -- on valid response from server,
+*			 1 TIMEOUT_RC -- after retries * resp_timeout seconds,
+*			-2 BADRESP_RC -- if response from server had errors.
+*
+**************************************************************************/
+
+int 
+send_server (data, retries, msg)
+
+SEND_DATA      *data;		/* Data structure built by clients */
+int            *retries;	/* Maximum num of times to retransmit request */
+				/* Receives number of retries required, also */
+char           *msg;		/* Receives error or advisory message */
+
+{
+	u_char          seq_nbr;    /* Sequence number to use in request  */
+	int             fptype;     /* Framed proto, ustype == PW_FRAMED */
+	int             i;
+	int             length;
+	int             result;
+	int             retry_max;
+	int             salen;
+	int             secretlen;
+	int             sockfd;
+	int             timeout;    /* Number of secs. to wait for response */
+	int             total_length;
+	int             ustype;     /* User service type for this user */
+	UINT4           auth_ipaddr;
+	UINT4           lvalue;
+	UINT4           myipaddr;
+	UINT4           port_num;   /* Port number to use in request  */
+	AUTH_HDR       *auth;
+	VALUE_PAIR     *check;
+	char           *passwd;		/* User password (unencrypted) */
+	u_char         *ptr;
+	VALUE_PAIR     *reply;
+	char           *server_name;	/* Name of server to query */
+	struct sockaddr_in *sin;
+	struct servent *svp;
+	struct timeval  authtime;
+	fd_set          readfds;
+	struct sockaddr salocal;
+	struct sockaddr saremote;
+	u_char          md5buf[256];
+	u_char          passbuf[AUTH_PASS_LEN];
+	u_char          send_buffer[1024];
+	u_char          recv_buffer[1024];
+	u_char          vector[AUTH_VECTOR_LEN];
+	char            file[MAXPATHLEN];
+	char            secret[MAX_SECRET_LENGTH + 1];
+
+#ifdef KCHAP
+	char            kchap_auth;
+	u_char          buffer[AUTH_PASS_LEN + AUTH_VECTOR_LEN + 1];
+	u_char          digest[AUTH_VECTOR_LEN];
+	u_char          user_secret[CHAP_VALUE_LENGTH + 1];
+
+#endif	/* KCHAP */
+
+	/* Set up some defaults */
+	dir_init ();
+
+	server_name = data->server;
+	if (server_name == (char *) NULL || server_name[0] == '\0')
+	{
+		server_name = DEFAULT_SERVER;
+	}
+
+	ustype = data->ustype;
+
+#ifdef	KCHAP
+	if (ustype == 255) /* KCHAP indicator */
+	{
+		kchap_auth = 1;
+		ustype = PW_AUTHENTICATE_ONLY;
+	}
+	else
+	{
+		kchap_auth = 0;
+	}
+#endif	/* KCHAP */
+
+	if (find_server (server_name, ustype, &auth_ipaddr, secret, msg) != 0)
+	{
+		return (ERROR_RC);
+	}
+
+	timeout = data->timeout;
+	if (timeout == 0)
+	{
+		timeout++;
+	}
+
+	if (data->svc_port == 0)
+	{
+		if ((svp = getservbyname ("radius", "udp")) == NULL)
+		{
+			data->svc_port = PW_AUTH_UDP_PORT;
+		}
+		else
+		{
+			data->svc_port = ntohs (svp->s_port);
+		}
+	}
+
+	if (!radsock)
+	{
+		sockfd = socket (AF_INET, SOCK_DGRAM, 0);
+		if (sockfd < 0)
+		{
+			memset (secret, '\0', sizeof (secret));
+			sprintf (msg, "socket: %s\n", sys_errlist[errno]);
+			return (ERROR_RC);
+		}
+
+		length = sizeof (salocal);
+		sin = (struct sockaddr_in *) & salocal;
+		memset ((char *) sin, '\0', length);
+		sin->sin_family = AF_INET;
+		sin->sin_addr.s_addr = INADDR_ANY;
+		sin->sin_port = htons (0);
+		if (bind (sockfd, (struct sockaddr *) sin, length) < 0 ||
+			   getsockname (sockfd, (struct sockaddr *) sin,
+					&length) < 0)
+		{
+			close (sockfd);
+			memset (secret, '\0', sizeof (secret));
+			sprintf (msg, "bind: %s\n", sys_errlist[errno]);
+			return (ERROR_RC);
+		}
+		retry_max = *retries;	/* Max. numbers to try for reply */
+		*retries = 0;	/* Init retry cnt for blocking call */
+	}
+	else
+	{
+		sockfd = radsock;
+		retry_max = 0;	/* No retries if non-blocking */
+	}
+
+	/* Build an authentication request */
+	auth = (AUTH_HDR *) send_buffer;
+	auth->code = data->code;
+	random_vector (vector);
+	seq_nbr = data->seq_nbr;
+	auth->id = seq_nbr;
+	memcpy ((char *) auth->vector, (char *) vector, AUTH_VECTOR_LEN);
+	total_length = AUTH_HDR_LEN;
+	ptr = auth->data;
+
+	/* User Name */
+	*ptr++ = PW_USER_NAME;
+	length = strlen (data->user_name);
+	if (length > AUTH_ID_LEN)
+	{
+		length = AUTH_ID_LEN;
+	}
+	*ptr++ = length + 2;
+	memcpy ((char *) ptr, data->user_name, length);
+	ptr += length;
+	total_length += length + 2;
+
+	passwd = data->password;
+
+	if (auth->code != PW_ACCOUNTING_REQUEST)
+	{
+#ifdef  KCHAP
+		if (kchap_auth)
+		{
+			/* User Password */
+			*ptr++ = PW_CHAP_PASSWORD;
+			*ptr++ = 1 + AUTH_VECTOR_LEN + 2;
+			*ptr++ = seq_nbr;  /* Pass CHAP identifier to RADIUS */
+			*buffer = seq_nbr;/* Put CHAP id in work area for md5 */
+			afs_pwd_to_secret (passwd, user_secret);/* Get secret */
+			user_secret[CHAP_VALUE_LENGTH] = '\0';
+			memcpy ((char *) buffer + 1, (char *) user_secret,
+				CHAP_VALUE_LENGTH);
+			memcpy ((char *) buffer + 1 + CHAP_VALUE_LENGTH,
+				(char *) vector, AUTH_VECTOR_LEN);
+			md5_calc (digest, buffer,
+			  	1 + CHAP_VALUE_LENGTH + AUTH_VECTOR_LEN);
+			memcpy ((char *) ptr, (char *) digest, AUTH_VECTOR_LEN);
+			ptr += AUTH_VECTOR_LEN;
+			total_length += 1 + AUTH_VECTOR_LEN + 2;
+		}
+		else
+		{
+#endif	/* KCHAP */
+
+			/* User Password */
+			*ptr++ = PW_USER_PASSWORD;
+			*ptr++ = AUTH_PASS_LEN + 2;
+
+			/* Encrypt the Password */
+			length = strlen (passwd);
+			if (length > AUTH_PASS_LEN)
+			{
+				length = AUTH_PASS_LEN;
+			}
+			memset ((char *) passbuf, '\0', AUTH_PASS_LEN);
+			memcpy ((char *) passbuf, passwd, length);
+
+			/* Calculate the MD5 Digest */
+			secretlen = strlen (secret);
+			strcpy ((char *) md5buf, secret);
+			memcpy ((char *) md5buf + secretlen,
+				(char *) auth->vector, AUTH_VECTOR_LEN);
+			md5_calc (ptr, md5buf, secretlen + AUTH_VECTOR_LEN);
+
+			/* Xor the password into the MD5 digest */
+			for (i = 0; i < AUTH_PASS_LEN; i++)
+			{
+				*ptr++ ^= passbuf[i];
+			}
+			total_length += AUTH_PASS_LEN + 2;
+
+#ifdef	KCHAP
+		}
+#endif	/* KCHAP */
+	}
+
+	/* Service Type */
+	*ptr++ = PW_SERVICE_TYPE;
+	*ptr++ = 2 + sizeof (UINT4);
+	lvalue = htonl (ustype);
+	memcpy ((char *) ptr, (char *) &lvalue, sizeof (UINT4));
+	ptr = ptr + sizeof (UINT4);
+	total_length += sizeof (UINT4) + 2;
+
+	fptype = data->fptype;
+	if (fptype > 0)			/* if -t [slip | ppp] */
+	{
+		/* Framed Protocol Type */
+		*ptr++ = PW_FRAMED_PROTOCOL;
+		*ptr++ = 2 + sizeof (UINT4);
+		lvalue = htonl (fptype);
+		memcpy ((char *) ptr, (char *) &lvalue, sizeof (UINT4));
+		ptr = ptr + sizeof (UINT4);
+		total_length += sizeof (UINT4) + 2;
+	}
+
+	/* Client IP Address */
+	*ptr++ = PW_NAS_IP_ADDRESS;
+	*ptr++ = 2 + sizeof (UINT4);
+	myipaddr = htonl(data->client_id);
+	memcpy ((char *) ptr, (char *) &myipaddr, sizeof (UINT4));
+	ptr = ptr + sizeof (UINT4);
+	total_length += sizeof (UINT4) + 2;
+
+	/* Client Port Number */
+	*ptr++ = PW_NAS_PORT;
+	*ptr++ = 2 + sizeof (UINT4);
+	port_num = htonl((UINT4) data->port_num);
+	memcpy ((char *) ptr, (char *) &port_num, sizeof (UINT4));
+	ptr = ptr + sizeof (UINT4);
+	total_length += sizeof (UINT4) + 2;
+
+	if (data->user_file != (char *) NULL) /* add a/v pairs from user_file */
+	{
+		sprintf (file, "%s.", data->user_file);
+		check = (VALUE_PAIR *) NULL;
+		if ((user_find (file, data->group, 0, &check, &reply, 1)) == 0)
+		{
+			total_length += (length = pack_list (check, ptr));
+			ptr += length;
+			total_length += (length = pack_list (reply, ptr));
+			ptr += length;
+		}
+	}
+
+	if (data->send_pairs != (VALUE_PAIR *) NULL) /* add more a/v pairs */
+	{
+		total_length += (length = pack_list (data->send_pairs, ptr));
+		ptr += length;
+	}
+
+	auth->length = htons (total_length);
+
+	sin = (struct sockaddr_in *) & saremote;
+	memset ((char *) sin, '\0', sizeof (saremote));
+	sin->sin_family = AF_INET;
+	sin->sin_addr.s_addr = htonl (auth_ipaddr);
+	sin->sin_port = htons (data->svc_port);
+
+	for (;;)
+	{
+		sendto (sockfd, (char *) auth, (int) total_length, (int) 0,
+			(struct sockaddr *) sin, sizeof (struct sockaddr_in));
+
+		if (radsock)
+		{		/* If non-blocking */
+
+			/*
+			 * Return stuff to be saved for evaluation of reply
+			 * when it comes in
+			 */
+			strcpy (msg, secret);
+			memcpy (msg + strlen (msg) + 1, (char *) vector,
+				AUTH_VECTOR_LEN);
+			memset (secret, '\0', sizeof (secret));
+			return 1;	/* Pos. return means no error */
+		}
+		authtime.tv_usec = 0L;
+		authtime.tv_sec = (long) timeout;
+		FD_ZERO (&readfds);
+		FD_SET (sockfd, &readfds);
+		if (select (sockfd + 1, &readfds, NULL, NULL, &authtime) < 0)
+		{
+			if (errno == EINTR)
+				continue;
+			sprintf (msg, "select: %s\n", sys_errlist[errno]);
+			memset (secret, '\0', sizeof (secret));
+			close (sockfd);
+			return (ERROR_RC);
+		}
+		if (FD_ISSET (sockfd, &readfds))
+			break;
+
+		/*
+		 * Timed out waiting for response.  Retry "retry_max" times
+		 * before giving up.  If retry_max = 0, don't retry at all.
+		 */
+		if (++(*retries) >= retry_max)
+		{
+			if (debug_flag > 0)
+			{
+				fprintf (stderr, "\n");
+			}
+			sprintf (msg,
+				"No reply from RADIUS server \"%s(%u)\"\n",
+				 ip_hostname (auth_ipaddr), data->svc_port);
+			close (sockfd);
+			memset (secret, '\0', sizeof (secret));
+			return (TIMEOUT_RC);
+		}
+		else
+		{
+			if (debug_flag > 0)
+			{
+				fprintf (stderr, ".");
+			}
+		}
+	}
+	salen = sizeof (saremote);
+	length = recvfrom (sockfd, (char *) recv_buffer,
+			   (int) sizeof (recv_buffer),
+			   (int) 0, &saremote, &salen);
+
+	if (length <= 0)
+	{
+		sprintf (msg, "recvfrom: %s\n", sys_errlist[errno]);
+		close (sockfd);
+		memset (secret, '\0', sizeof (secret));
+		return (ERROR_RC);
+	}
+	result = check_radius_reply (recv_buffer, secret, vector,
+		(u_int) seq_nbr, msg);
+	close (sockfd);
+	memset (secret, '\0', sizeof (secret));
+	return (result);
+} /* end of send_server () */
+
+/*************************************************************************
+*
+*	check_radius_reply - Verify items in returned packet.
+*
+*	Returns:	OK_RC       -- upon success,
+*			BADRESP_RC  -- if anything looks funny.
+*
+*	Public entry point necessary for MINOS/MNET daemon.
+*
+**************************************************************************/
+
+int 
+check_radius_reply (buffer, secret, vector, seq_nbr, msg)
+
+u_char         *buffer;
+char           *secret;
+u_char          vector[];
+u_int           seq_nbr;
+char           *msg;
+
+{
+	u_char          len;
+	int             result;
+	int             secretlen;
+	int             totallen;
+	AUTH_HDR       *auth;
+	u_char         *next;
+	u_char         *ptr;
+	VALUE_PAIR     *vp;
+	u_char          calc_digest[AUTH_VECTOR_LEN];
+	u_char          reply_digest[AUTH_VECTOR_LEN];
+
+	auth = (AUTH_HDR *) buffer;
+	totallen = ntohs (auth->length);
+
+	/* Verify that id (seq. number) matches what we sent */
+	if (auth->id != (u_char) seq_nbr)
+	{
+		sprintf (msg, "Received non-matching id in server response\n");
+		return (BADRESP_RC);
+	}
+
+	/* Verify the reply digest */
+	memcpy ((char *) reply_digest, (char *) auth->vector, AUTH_VECTOR_LEN);
+	memcpy ((char *) auth->vector, (char *) vector, AUTH_VECTOR_LEN);
+	secretlen = strlen (secret);
+	memcpy ((char *) buffer + totallen, secret, secretlen);
+	md5_calc (calc_digest, (char *) auth, totallen + secretlen);
+
+	if (memcmp ((char *) reply_digest, (char *) calc_digest,
+		    AUTH_VECTOR_LEN) != 0)
+	{
+		sprintf (msg, "Received invalid reply digest from server\n");
+		return (BADRESP_RC);
+	}
+
+	if (debug_flag)
+	{
+		fprintf (stderr, "Received attribute/value pair(s):\n");
+		vp = gen_valpairs (auth); /* just to print out in debug mode */
+	}
+
+	msg[0] = '\0';
+	ptr = (u_char *) auth->data;
+	totallen -= AUTH_HDR_LEN;
+	while (totallen > 0)
+	{
+		len = ptr[1];
+		totallen -= len;
+		next = ptr + len;
+		if (*ptr == '\0')
+		{
+			sprintf (msg, "Received bad attribute type from server\n");
+			return (BADRESP_RC);
+		}
+
+		if (*ptr == PW_REPLY_MESSAGE)
+		{
+			ptr++;
+			ptr++;
+			strncat (msg, (char *) ptr, len - 2);
+			strcat (msg, "\n");
+		}
+		ptr = next;
+	}
+
+	if ((auth->code == PW_ACCESS_ACCEPT) ||
+		(auth->code == PW_PASSWORD_ACK) ||
+		(auth->code == PW_ACCOUNTING_RESPONSE))
+	{
+		result = OK_RC;
+	}
+	else
+	{
+		result = BADRESP_RC;
+	}
+
+	return (result);
+} /* end of check_radius_reply () */
+
+/*************************************************************************
+*
+*	random_vector - Generates a random vector of AUTH_VECTOR_LEN octets.
+*
+*	Returns:	the vector (call by reference)
+*
+**************************************************************************/
+
+void
+random_vector (vector)
+
+u_char         *vector;
+
+{
+	int             randno;
+	int             i;
+
+	srand (time (0));
+	for (i = 0; i < AUTH_VECTOR_LEN;)
+	{
+		randno = rand ();
+		memcpy ((char *) vector, (char *) &randno, sizeof (int));
+		vector += sizeof (int);
+		i += sizeof (int);
+	}
+	return;
+} /* end of random_vector () */
+
+/*************************************************************************
+*
+*	find_match - See if given IP address matches any address of hostname.
+*
+*	Returns:	 0 success
+*			-1 failure
+*
+**************************************************************************/
+
+static int 
+find_match (ip_addr, hostname)
+
+UINT4          *ip_addr;
+char           *hostname;
+
+{
+	UINT4           addr;
+	char          **paddr;
+	struct hostent *hp;
+
+	if (good_ipaddr (hostname) == 0)
+	{
+		if (*ip_addr == ntohl(inet_addr (hostname)))
+		{
+			return (0);
+		}
+	}
+	else
+	{
+		if ((hp = gethostbyname (hostname)) == (struct hostent *) NULL)
+		{
+			return (-1);
+		}
+		if (hp->h_addr_list != (char **) NULL)
+		{
+			for (paddr = hp->h_addr_list; *paddr; paddr++)
+			{
+				addr = ** (UINT4 **) paddr;
+				if (ntohl(addr) == *ip_addr)
+				{
+					return (0);
+				}
+			}
+		}
+	}
+	return (-1);
+} /* end of find_match */
+
+/*************************************************************************
+*
+*	find_server - Look up the given server name in the clients file.
+*
+*	Returns:	 0 success
+*			-1 failure
+*
+**************************************************************************/
+
+static int 
+find_server (server_name, ustype, ip_addr, secret, msg)
+
+char           *server_name;
+int             ustype;
+UINT4          *ip_addr;
+char           *secret;
+char           *msg;
+
+{
+	static UINT4    myipaddr = 0;
+	int             len;
+	int             line_nbr = 0;
+	int             result;
+	FILE           *clientfd;
+	char           *h;
+	char           *s;
+	char           *host2;
+	char            buffer[128];
+	char            fname[MAXPATHLEN];
+	char            hostnm[AUTH_ID_LEN + 1];
+
+	/* Get the IP address of the authentication server */
+	if ((*ip_addr = get_ipaddr (server_name)) == (UINT4) 0)
+	{
+		sprintf (msg, "No such server: \"%s\"\n", server_name);
+		return (-1);
+	}
+	/* Just use dummy secret for management polls */
+	if (ustype == PW_ADMINISTRATIVE_USER) /* was old PW_MANAGEMENT_POLL */
+	{
+		strcpy (secret, MGMT_POLL_SECRET);
+		return 0;
+	}
+	sprintf (fname, "%s/%s", radius_dir, RADIUS_CLIENTS);
+	if ((clientfd = fopen (fname, "r")) == (FILE *) NULL)
+	{
+		sprintf (msg, "Couldn't open file \"%s\"\n", fname);
+		return (-1);
+	}
+	if (!myipaddr)
+	{
+		if ((myipaddr = get_ipaddr (ourhostname)) == 0)
+		{
+			sprintf (msg, "Couldn't get our own ip address\n");
+			fclose (clientfd);
+			return (-1);
+		}
+	}
+
+	result = 0;
+	while (fgets (buffer, sizeof (buffer), clientfd) != (char *) NULL)
+	{
+		line_nbr++;
+
+		if (*buffer == '#')
+		{
+			continue;
+		}
+
+		if ((h = strtok (buffer, " \t\n\r")) == NULL) /* 1st hostname */
+		{
+			continue;
+		}
+
+		memset (hostnm, '\0', AUTH_ID_LEN);
+		len = strlen (h);
+		if (len > AUTH_ID_LEN)
+		{
+			len = AUTH_ID_LEN;
+		}
+		strncpy (hostnm, h, len);
+		hostnm[AUTH_ID_LEN] = '\0';
+
+		if ((s = strtok (NULL, " \t\n\r")) == NULL) /* & secret field */
+		{
+			continue;
+		}
+
+		memset (secret, '\0', MAX_SECRET_LENGTH);
+		len = strlen (s);
+		if (len > MAX_SECRET_LENGTH)
+		{
+			len = MAX_SECRET_LENGTH;
+		}
+		strncpy (secret, s, len);
+		secret[MAX_SECRET_LENGTH] = '\0';
+
+		if (!strchr (hostnm, '/')) /* If single name form */
+		{
+			if (find_match (ip_addr, hostnm) == 0)
+			{
+				result++;
+				break;
+			}
+		}
+		else /* <name1>/<name2> "paired" form */
+		{
+			strtok (hostnm, "/"); /* replaces "/" with NULL char */
+			host2 = strtok (NULL, " ");
+			if (find_match (&myipaddr, hostnm) == 0)
+			{	     /* If we're the 1st name, target is 2nd */
+				if (find_match (ip_addr, host2) == 0)
+				{
+					result++;
+					break;
+				}
+			}
+			else	/* Check to see if we are the second name */
+			{
+				if (find_match (&myipaddr, host2) == 0)
+				{ /* We are the 2nd name, target is 1st name */
+					if (find_match (ip_addr, hostnm) == 0)
+					{
+						result++;
+						break;
+					}
+				}
+			}
+		}
+	}
+	fclose (clientfd);
+	if (result == 0)
+	{
+		memset (buffer, '\0', sizeof (buffer));
+		memset (secret, '\0', sizeof (secret));
+		sprintf (msg, "Couldn't find server in \"%s/%s\": \"%s\"\n",
+			 radius_dir, RADIUS_CLIENTS, server_name);
+		return (-1);
+	}
+	return 0;
+} /* end of find_server () */
diff -rPu pppd.old/rad_users.c pppd/rad_users.c
--- pppd.old/rad_users.c	Thu Jan  1 03:00:00 1970
+++ pppd/rad_users.c	Tue Nov 25 05:24:06 1997
@@ -0,0 +1,3175 @@
+ /*
+  * RADIUS -- Remote Authentication Dial In User Service
+  * 
+  * 
+  * Livingston Enterprises, Inc. 6920 Koll Center Parkway Pleasanton, CA   94566
+  * 
+  * Copyright 1992 Livingston Enterprises, Inc.
+  * 
+  * Permission to use, copy, modify, and distribute this software for any
+  * purpose and without fee is hereby granted, provided that this copyright
+  * and permission notice appear on all copies and supporting documentation,
+  * the name of Livingston Enterprises, Inc. not be used in advertising or
+  * publicity pertaining to distribution of the program without specific
+  * prior permission, and notice be given in supporting documentation that
+  * copying and distribution is by permission of Livingston Enterprises, Inc.
+  * 
+  * Livingston Enterprises, Inc. makes no representations about the suitability
+  * of this software for any purpose.  It is provided "as is" without express
+  * or implied warranty.
+  *
+  * [C] The Regents of the University of Michigan and Merit Network, Inc. 1992,
+  * 1993, 1994, 1995, 1996 All Rights Reserved
+  *
+  * Permission to use, copy, modify, and distribute this software and its
+  * documentation for any purpose and without fee is hereby granted, provided
+  * that the above copyright notice and this permission notice appear in all
+  * copies of the software and derivative works or modified versions thereof,
+  * and that both the copyright notice and this permission and disclaimer
+  * notice appear in supporting documentation.
+  *
+  * THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+  * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF
+  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE REGENTS OF THE
+  * UNIVERSITY OF MICHIGAN AND MERIT NETWORK, INC. DO NOT WARRANT THAT THE
+  * FUNCTIONS CONTAINED IN THE SOFTWARE WILL MEET LICENSEE'S REQUIREMENTS OR
+  * THAT OPERATION WILL BE UNINTERRUPTED OR ERROR FREE.  The Regents of the
+  * University of Michigan and Merit Network, Inc. shall not be liable for
+  * any special, indirect, incidental or consequential damages with respect
+  * to any claim by Licensee or any third party arising from use of the
+  * software.
+  *
+  * Public entry points in this file:
+  * 
+  * add_file_list
+  * config_init
+  * config_files
+  * config_fini
+  * dns_recv
+  * find_auth_type
+  * find_auth_ent
+  * find_client
+  * find_client_by_name
+  * find_host_by_name
+  * free_user_ent
+  * get_our_addr
+  * ip_hostname
+  * list_cat
+  * list_copy
+  * pair_parse
+  * return_file_list
+  * user_find
+  * user_gettime
+  * user_update
+  * 
+  */
+
+static char     sccsid[] =
+		"@(#)users.c 1.3 Copyright 1992 Livingston Enterprises Inc";
+
+static char     rcsid[] = "$Id: users.c,v 1.89 1996/06/11 21:10:39 web Exp $";
+
+#include	<sys/types.h>
+#include	<sys/param.h>
+#include	<sys/socket.h>
+#include	<sys/time.h>
+#include	<netinet/in.h>
+#include	<arpa/inet.h>
+
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<netdb.h>
+#include	<time.h>
+#include	<ctype.h>
+#include	<dirent.h>
+#include	<syslog.h>
+
+#include	"radius.h"
+
+static void          fieldcpy PROTO((char *, char **));
+static FILE_LIST    *find_file_ent PROTO((char *));
+static void          free_clients PROTO((CLIENT_ENTRY *));
+static void          free_file_lists PROTO((void));
+static int           host_is_us PROTO((char *));
+static int           insert_client PROTO((char *, char *, char *));
+static int           read_auth PROTO((FILE_LIST *, int));
+static int           read_users PROTO((FILE_LIST *, int));
+
+#if defined(ultrix) || defined(__hpux) || defined(__bsdi__) || defined(linux) || defined(SCO)
+extern int           h_errno;
+#endif	/* ultrix */
+
+extern char          send_buffer[4096];
+extern char          recv_buffer[4096];
+extern char          ourhostname[MAXHOSTNAMELEN];
+extern AATV         *authtype_tv[]; /* AATVs by authentication types */
+extern int           debug_flag;
+extern int           dumpcore;
+extern char         *radius_dir;
+extern FILE         *ddt;
+extern int           authfile_cnt;
+extern int           clients_cnt;
+extern int           users_cnt;
+extern int           file_logging; /*  0 => syslog, 1 => logfile, 2 => stderr */
+extern AATV         *rad_ipc_aatv;
+extern FILE         *msgfd;
+
+int                  spawn_flag = 1;
+int                  dnspid = 0;   /* PID of current DNS resolver process */
+char                 default_radius_server[128] = DEFAULT_RADIUS_SERVER;
+char                 default_tacacs_server[128] = DEFAULT_TACACS_SERVER;
+int                  rad_ipc_port = 0;
+
+static FILE_LIST    *file_list = (FILE_LIST *) NULL;
+static CLIENT_ENTRY *client_list = (CLIENT_ENTRY *) NULL;
+
+static UINT4         self_ip[11];	/* Used with multi-homed servers */
+static int           is_engine = 0;	/* rlmadmin will not change this */
+static CLIENT_ENTRY *old_clients;
+
+/*************************************************************************
+ *
+ *	Function: add_file_list
+ *
+ *	Purpose: Find an existing FILE_LIST entry on file_list with the
+ *		 specified prefix or add and init a new one if the
+ *		 entry doesn't already exist.
+ *
+ *************************************************************************/
+
+int
+add_file_list (prefix)
+
+char           *prefix;
+
+{
+	FILE_LIST      *file_ent;
+	FILE_LIST     **fl_prev;
+	static char    *func = "add_file_list";
+
+	dprintf(4, (LOG_AUTH, LOG_DEBUG, "%s: entered", func));
+
+	for (fl_prev = &file_list, file_ent = file_list;
+		file_ent;
+		fl_prev = &file_ent->next, file_ent = *fl_prev)
+	{
+		if (strcmp (file_ent->prefix, prefix) == 0)
+		{
+			return 0;
+		}
+	}
+
+	if ((file_ent = (FILE_LIST *) malloc (sizeof (FILE_LIST)))
+							== (FILE_LIST *) NULL)
+	{
+		logit (LOG_DAEMON, LOG_ALERT,
+			"%s: Couldn't allocate FILE_ENTRY storage", func);
+		return (-1);
+	}
+	file_ent->prefix = add_string (prefix, ASIS);
+	file_ent->user_list = (USER_ENTRY *) NULL;
+	file_ent->auth_list = (AUTH_ENTRY *) NULL;
+	file_ent->next = (FILE_LIST *) NULL;
+	*fl_prev = file_ent;
+
+	return 0;
+} /* end of add_file_list () */
+
+/*************************************************************************
+ *
+ *	Function: config_init
+ *
+ *	Purpose: Setup environment for config_files() to run in.
+ *
+ *************************************************************************/
+
+void
+config_init ()
+
+{
+	is_engine = 1; /* flag set when engine calls us */
+
+	/*
+	 *	Set dnspid to defer the call to update_clients()
+	 *	until the end of config_files().
+	 */
+	if (dnspid == 0)
+	{
+		dnspid = -1;
+	}
+
+	/*
+	 *	Save the old clients list so we can pick up
+	 *	the DNS addresses for the new list.
+	 */
+	old_clients = client_list;
+	client_list = (CLIENT_ENTRY *) NULL;
+	return;
+} /* end of config_init () */
+
+/*************************************************************************
+ *
+ *	Function: config_files
+ *
+ *	Purpose: Read database files into memory data structures.  Reads
+ *		 "RADIUS_CLIENTS" and "RADIUS_AUTH" files unconditionally
+ *		 and "RADIUS_USERS" file if users_flag is not zero.  Will
+ * 		 read multiple users and authfiles if the "file_pfx" is
+ *		 specified in a client entry (allowing use of different
+ *		 files for different client NASes).
+ *
+ *		 If clear_flag is greater than zero, remove existing entries.
+ *
+ *		 A new CLIENT_ENTRY is added to the client_list for each
+ *		 client appearing in the "RADIUS_CLIENTS" file.  A new
+ *		 FILE_LIST entry is added for each unique file_pfx found
+ *		 in the "RADIUS_CLIENTS" file.  Each FILE_LIST entry points
+ *		 to a list of USER_ENTRYs containing the information read
+ *		 from the "RADIUS_USERS" file with the corresponding
+ *		 file_pfx.  Also each FILE_LIST entry contains a pointer to
+ *		 the list of AUTH_ENTRYs containing realm information read
+ *		 from the "RADIUS_AUTH" file with the corresponding file_pfx.
+ *		 If either the "RADIUS_USERS" file or the "RADIUS_AUTH" file
+ *		 with the given file_pfx did not exist, the default
+ *		 (non-prefixed) file name entries are used instead.
+ *
+ *************************************************************************/
+
+int
+config_files (users_flag, clear_flag, dolog)
+
+int             users_flag;
+int             clear_flag;
+int             dolog;
+
+{
+	int             i;
+	int             ent_cnt = 0;
+	int             line_nbr = 0;
+	int             result;
+	FILE_LIST      *file_ent;
+	FILE           *clientfd;
+	struct hostent *hp;
+	char           *file_pfx;
+	char           *host;
+	char           *hostnm;
+	char           *secret;
+	char          **paddr;
+	char            buffer[128];
+	char            fname[MAXPATHLEN];
+	static char    *func = "config_files";
+
+	dprintf(2, (LOG_AUTH, LOG_DEBUG, "%s: entered", func));
+
+	if (clear_flag > 0) /* Free current list, if already set up */
+	{
+		free_file_lists ();
+	}
+
+	/*
+	 * Add default file_list entry - the entry for the "users" and
+	 * "authfile" with no optional prefix
+	 */
+	add_file_list ("");
+
+	/*
+	 * Determine the IP address(es) of this machine
+	 */
+	if ((hp = gethostbyname (ourhostname)) == (struct hostent *) NULL)
+	{
+		logit (LOG_DAEMON, LOG_CRIT,
+			"%s: Couldn't get our own IP address(es)", func);
+		return (-1);
+	}
+
+	/*
+	 *	First clear, then save our IP address(es)
+	 *	leaving a zero entry at the end.
+	 */
+	memset ((char *) self_ip, '\0', sizeof (self_ip));
+
+	if (hp->h_addr_list != (char **) NULL)
+	{
+		for (i = 0, paddr = hp->h_addr_list;
+			(*paddr != (char *) NULL) &&
+				(i < sizeof (self_ip) / 4 - 1);
+			i++, paddr++)
+		{
+			memcpy ((char *) &self_ip[i], (char *) *paddr,
+				hp->h_length);
+			self_ip[i] = ntohl(self_ip[i]);
+		}
+	}
+
+	/*
+	 * Now read in all client file entries, adding a new "CLIENT_ENTRY"
+	 * to client_list for each valid file entry found.  If there is more
+	 * than one IP address for a given client DNS name, add an entry for
+	 * each address.
+	 */
+	sprintf (fname, "%s/%s", radius_dir, RADIUS_CLIENTS);
+	if ((clientfd = fopen (fname, "r")) == (FILE *) NULL)
+	{
+		logit (LOG_DAEMON, LOG_ERR, "%s: Couldn't open %s for reading",
+			func, fname);
+		return (-1);
+	}
+
+	clients_cnt = 0;
+	result = -1;    /* Init result code */
+
+	while (fgets (buffer, sizeof (buffer), clientfd) != (char *) NULL)
+	{
+		line_nbr++;
+
+		if (*buffer == COMMENT)
+		{
+			continue;
+		}
+
+		/* first hostname */
+		if ((hostnm = strtok (buffer, " \t\n\r")) == (char *) NULL)
+		{
+			continue;
+		}
+
+		/* and secret field */
+		if ((secret = strtok (NULL, " \t\n\r")) == (char *) NULL)
+		{
+			logit (LOG_DAEMON, LOG_ERR,
+				"%s: missing shared secret: %s line %d",
+				func, fname, line_nbr);
+			continue;
+		}
+
+		file_pfx = strtok (NULL, " \t\n\r"); /* optional prefix */
+
+		/*
+		 * Look for "paired" <name1>/<name2> entries.  This type of
+		 * entry allows core RADIUS servers to share a common "clients"
+		 * file.  If we're name1, name2 is the target name; if we are
+		 * name2, then name1 is the target.  If we're neither name,
+		 * then this entry isn't of interest to us and we ignore it.
+		 */
+		host = hostnm;  /* Assume just one name */
+
+		if (strchr (hostnm, '/'))	/* If <name1>/<name2> form */
+		{
+			strtok (hostnm, "/");
+			host = strtok (NULL, " ");
+			if (host_is_us (hostnm))
+			{
+				/* We are 1st name - client is 2nd */
+			}
+			else
+			{
+				if (host_is_us (host))
+				{
+					/* We are 2nd - client is 1st */
+					host = hostnm;
+				}
+				else /* We are neither - no match */
+				{
+					continue;
+				}
+			}
+		}
+
+		if ((result = insert_client (host, secret, file_pfx)) < 0)
+		{
+			break;
+		}
+		ent_cnt++;
+	} /* end of while () */
+	fclose (clientfd);
+	if (dolog)
+	{
+		logit (LOG_DAEMON, LOG_INFO,
+			"%s: %s (%u entries) read to memory",
+			func, fname, ent_cnt);
+	}
+	clients_cnt = ent_cnt;
+
+	/*
+	 * Finally, go through all the file_list entries just added, reading
+	 * in the "users" and "authfile" for each prefix found.
+	 */
+	for (file_ent = file_list; file_ent; file_ent = file_ent->next)
+	{
+
+#if !(defined(USE_DBM) || defined(USE_NDBM))
+		if (users_flag)
+			if (read_users (file_ent, dolog) != 0)
+			{
+				return (-1);
+			}
+#endif	/* USE_DBM || USE_NDBM */
+
+		if (read_auth (file_ent, dolog) != 0)
+		{
+			return (-1);
+		}
+
+	}
+
+	if (result < 0)
+	{
+		return (-1);
+	}
+
+	return 0;
+} /* end of config_files () */
+
+/*************************************************************************
+ *
+ *	Function: config_fini
+ *
+ *	Purpose: Cleanup environment after config_files() has run.
+ *
+ *************************************************************************/
+
+void
+config_fini ()
+
+{
+	free_clients (old_clients);
+	old_clients = (CLIENT_ENTRY *) NULL;
+
+	if ((file_logging == 1) && (msgfd != (FILE *) NULL))
+	{
+		fflush (msgfd);
+	}
+
+	if (dnspid == -1)
+	{
+		dnspid = 0;
+	}
+
+	update_clients ();       /* Start up the DNS resolver. */
+
+	return;
+} /* end of config_fini () */
+
+/*************************************************************************
+ *
+ *      Function: dns_recv
+ *
+ *      Purpose: Process received DNS updates for clients database.
+ *
+ *************************************************************************/
+
+void
+dns_recv (sin, from_ipaddr, rcvlen)
+
+struct sockaddr_in *sin;
+UINT4               from_ipaddr;
+int                 rcvlen;
+
+{
+	int              cnt;
+	u_char           aliascnt;
+	UINT4            temp;
+	UINT4           *ourad;
+	char            *ptr;
+	IP_ADDRESS      *an_address;
+	DNS_NAME        *a_name;
+	CLIENT_ENTRY    *client_ent;
+	static int       notify_count = 0;
+	static char     *func = "dns_recv";
+
+	notify_count ++;
+	dprintf(4, (LOG_AUTH, LOG_DEBUG, "%s: entered (%d)",
+		func, notify_count));
+	 
+	ptr = recv_buffer + 1;
+
+	for (ourad = self_ip;
+		(*ourad != (UINT4) 0) && (*ourad != from_ipaddr);
+		ourad++)
+	{
+		continue;
+	}
+
+	if (*ourad == (UINT4) 0)
+	{
+		logit (LOG_DAEMON, LOG_INFO, "%s: from %s - Security Breach",
+			func, ip_hostname (from_ipaddr));
+		return;
+	}
+
+	for (client_ent = client_list;
+		client_ent != (CLIENT_ENTRY *) NULL; 
+		client_ent = client_ent->next)
+	{
+		if (strcmp (ptr, client_ent->hostname) == 0)
+		{
+			break;
+		}
+	}
+
+	if (client_ent == (CLIENT_ENTRY *) NULL)
+	{
+		return;
+	}		
+
+	ptr += strlen (ptr) + 1;
+	aliascnt = *ptr++;
+
+	if (*ptr != '\0') /* If alias or IP address present, clear old ones */
+	{
+		/*
+		 * Reset expire_time with some randomness (0 - 60 minutes)
+		 * to avoid a burst of calls to gethostbyname().
+		 */
+		client_ent->expire_time = (time (0) + ADDRESS_AGING +
+						(rand () & 3) * 60 * 20) & ~3;
+		for (an_address = client_ent->addrs;
+			an_address != (IP_ADDRESS *) NULL;
+			an_address = client_ent->addrs)
+		{
+			client_ent->addrs = an_address->next;
+			free (an_address);
+		}
+ 
+		for (a_name = client_ent->names;
+			a_name != (DNS_NAME *) NULL;
+			a_name = client_ent->names)
+		{
+			client_ent->names = a_name->next;
+			free (a_name);
+		}
+	}
+	else /* no alias or IP address present */
+	{
+		memcpy ((char *) &temp, ptr, sizeof (struct in_addr));
+		if (temp == TRY_AGAIN) /* TRY_AGAIN is in netdb.h */
+		{
+			client_ent->expire_time = (time (0) + ADDRESS_AGING) 
+									& ~3;
+			logit (LOG_DAEMON, LOG_ALERT,
+				"%s: DNS timeout on client or host '%s'",
+				func, client_ent->hostname);
+			return;
+		}
+		else
+		{
+			/*
+			 *	Name couldn't be resolved -- log it and retry
+			 *	shortly.  Cleverly (or foolishly) use the low
+			 *	two bits of expire_time to control the logging
+			 *	frequency.
+			 */
+			if ((cnt = client_ent->expire_time & 3) == 0)
+			{
+				/* Log every fourth time (or once per hour) */
+				logit (LOG_DAEMON, LOG_ALERT,
+				     "%s: DNS couldn't resolve name '%s' (%ld)",
+					func, client_ent->hostname, temp);
+			}
+
+			client_ent->expire_time = ((time (0) + ADDRESS_AGING/4)
+						 	& ~3) | (++cnt & 3);
+			if (client_ent->addrs != (IP_ADDRESS *) NULL)
+			{
+				return;
+			}
+
+			/* Add invalid address to client_ent */
+			memset (ptr, 255, sizeof (struct in_addr));
+		}
+	}
+
+	/* Add alias names to client_ent structure */
+	while (aliascnt-- > 0)
+	{
+		/* Note that DNS_NAME structure reserves one extra character. */
+		if ((a_name =
+			(DNS_NAME *) malloc (sizeof (DNS_NAME) + strlen (ptr)))
+							== (DNS_NAME *) NULL)
+		{
+			logit (LOG_DAEMON, LOG_ALERT,
+				"%s: FATAL Couldn't allocate DNS_NAME storage",
+				func);
+			abort ();
+		}
+
+		/* Note that type zero will always be last one. */
+		strcpy (a_name->name, ptr); 
+		ptr += strlen (ptr) + 1;
+		a_name->type =  (u_char) *ptr++;
+		a_name->next = client_ent->names;
+		client_ent->names = a_name;
+	}
+
+	/*
+	 * For each address in the list, add the address to the client_ent.
+	 */
+	while (*ptr != '\0')
+	{
+		if ((an_address = (IP_ADDRESS *) malloc (sizeof (IP_ADDRESS)))
+							== (IP_ADDRESS *) NULL)
+		{
+			logit (LOG_DAEMON, LOG_ALERT,
+				"%s: FATAL Couldn't allocate IP_ADDRESS storage",
+				func);
+			abort ();
+		}
+
+		an_address->next = client_ent->addrs;
+		memcpy ((char *) &temp, ptr, sizeof (struct in_addr));
+		an_address->ipaddr.s_addr = ntohl(temp);
+		client_ent->addrs = an_address;
+		ptr += sizeof (struct in_addr);
+	}
+
+	if (notify_count % DNS_SLEEP == DNS_SLEEP - 1)
+	{
+		logit (LOG_DAEMON, LOG_INFO, "%s: Notified of (%d) DNS changes",
+			func, notify_count);
+	}
+
+	return;
+} /* end of dns_recv () */
+
+/*************************************************************************
+ *
+ *	Function: fieldcpy
+ *
+ *	Purpose: Copy a data field from the buffer.  Advance the buffer
+ *		 past the data field.
+ *
+ *************************************************************************/
+
+static void
+fieldcpy (string, uptr)
+
+char           *string;
+char          **uptr;
+
+{
+	char           *ptr;
+
+	ptr = *uptr;
+	if (*ptr == '"')
+	{
+		ptr++;
+		while (*ptr != '"' && *ptr != '\0' && *ptr != '\n')
+		{
+			*string++ = *ptr++;
+		}
+		*string = '\0';
+		if (*ptr == '"')
+		{
+			ptr++;
+		}
+		*uptr = ptr;
+		return;
+	}
+
+	while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0' && *ptr != '\n' &&
+			*ptr != '=' && *ptr != ',')
+	{
+		*string++ = *ptr++;
+	}
+	*string = '\0';
+	*uptr = ptr;
+	return;
+} /* end of fieldcpy () */
+
+/*************************************************************************
+ *
+ *	Function: find_auth_ent
+ *
+ *	Purpose: Gives access to the private AUTH_ENT for the given realm.
+ *
+ *	Returns: pointer to the AUTH_ENT for the given realm,
+ *		 or, NULL, if error.
+ *
+ *************************************************************************/
+
+AUTH_ENTRY *
+find_auth_ent (u_realm, prot, pfx)
+
+char           *u_realm;
+int             prot;
+char           *pfx;
+
+{
+	int             head;
+	int             pat_len;
+	FILE_LIST      *file_ent;
+	AUTH_ENTRY     *auth_ent;
+	AUTH_ENTRY     *entry;
+	char           *p;
+	char           *realm_name;
+	static char     temp[AUTH_ID_LEN + 1];
+	static char    *func = "find_auth_ent";
+
+	dprintf(2, (LOG_AUTH, LOG_DEBUG, "%s: entered", func));
+
+	if (u_realm[0] == 0)	/* A null realm would match every line. */
+	{
+		dprintf(2, (LOG_AUTH, LOG_DEBUG, "%s: NULL realm", func));
+		return (AUTH_ENTRY *) NULL;
+	}
+
+	if ((file_ent = find_file_ent (pfx)) == (FILE_LIST *) NULL)
+	{
+		dprintf(2, (LOG_AUTH, LOG_DEBUG, "%s: NULL file_ent", func));
+		return (AUTH_ENTRY *) NULL;
+	}
+
+	if ((auth_ent = file_ent->auth_list) == (AUTH_ENTRY *) NULL)
+	{
+		/* If no auth_list for this prefix */
+		file_ent = file_list;
+		/* Default file_ent is first in file_list */
+		if ((auth_ent = file_ent->auth_list) == (AUTH_ENTRY *) NULL)
+		{
+			logit (LOG_DAEMON, LOG_ERR,
+				"%s: no default authfile data structure",
+				func);
+			return (AUTH_ENTRY *) NULL;
+		}
+	}
+
+	/*
+	 *	Match realm name (either exact match or substring match
+	 *	based on *.realm syntax) with user supplied string.
+	 */
+	for ( ; auth_ent ; auth_ent = auth_ent->next )
+	{
+		realm_name = (char *) NULL;
+		if (auth_ent->parent == (AUTH_ENTRY *) NULL) /* parent realm */
+		{
+			entry = auth_ent;
+			/* Look for name match. */
+			if (entry->name[0] == '*') /* this is wildcard realm */
+			{
+				p = &entry->name[1];
+				pat_len = strlen (p);
+				head = strlen (u_realm) - pat_len;
+				if (strncmp ((char *) &u_realm[head],
+					(char *) &entry->name[1], pat_len) == 0)
+				{
+					realm_name = u_realm;
+				}
+				else
+				{
+					realm_name = (char *) NULL;
+				}
+			}
+			else /* not a wildcard realm */
+			{
+				if (strcasecmp (entry->name, u_realm) == 0)
+				{
+					realm_name = entry->name;
+				}
+			}
+		}
+		else /* this entry is an alias name for some real realm */
+		{
+			entry = auth_ent->parent;
+			/* Look for name match. */
+			if (entry->name[0] == '*') /* alias in wildcard realm */
+			{
+				p = &entry->name[1];
+				pat_len = strlen (p);
+				head = strlen (u_realm) - pat_len;
+				if (strncmp ((char *) &u_realm[head],
+					(char *) &entry->name[1], pat_len) == 0)
+				{
+					/* combine real prefix, parent suffix */
+					strcpy (temp, u_realm);
+					if (strtok (temp, ".") != (char *) NULL)
+					{
+						realm_name = strcat (temp,
+							       &entry->name[1]);
+					}
+				}
+				else
+				{
+					realm_name = (char *) NULL;
+				}
+			}
+			else /* regular alias */
+			{
+				if (strcasecmp (auth_ent->name, u_realm) == 0)
+				{
+					realm_name = entry->name;
+				}
+			}
+		}
+
+		if (realm_name != (char *) NULL) /* then we have a name match */
+		{
+			if (!entry->prot || (entry->prot == prot))
+			{
+				break;
+			}
+		}
+	}
+
+	if (auth_ent == (AUTH_ENTRY *) NULL)
+	{
+		dprintf(2, (LOG_AUTH, LOG_DEBUG, "%s: realm not found", func));
+		return (AUTH_ENTRY *) NULL;
+	}
+
+	return entry;
+} /* end of find_auth_ent () */
+
+/*************************************************************************
+ *
+ *	Function: find_auth_type
+ *
+ *	Purpose: Find the proper AUTH_ENTRY to use for the given authentication
+ *		 realm name from the FILE_LIST entry with the given file_pfx.
+ *
+ *	Returns: The authentication type, name of the authentication agent to
+ *		 use, the primary realm name and any optional packet filter
+ *		 to be applied are returned.
+ *
+ *	Returns:  0 = normal return,
+ *		 -1 = error return
+ *
+ *************************************************************************/
+
+int
+find_auth_type (u_realm, prot, pfx, type, agent, realm, filter)
+
+char           *u_realm;
+int             prot;
+char           *pfx;
+int            *type;	/* receives resultant authentication type value */
+char          **agent;	/* receives resultant authentication agent name */
+char          **realm;	/* receives resultant primary realm name */
+char          **filter;	/* receives resultant authentication filter name */
+
+{
+	int             head;
+	int             pat_len;
+	FILE_LIST      *file_ent;
+	AUTH_ENTRY     *auth_ent;
+	AUTH_ENTRY     *entry;
+	char           *p;
+	char           *realm_name;
+	static char     temp[AUTH_ID_LEN + 1];
+	static char    *func = "find_auth_type";
+
+	dprintf(2, (LOG_AUTH, LOG_DEBUG, "%s: entered", func));
+
+	if (u_realm[0] == 0)	/* A null realm would match every line. */
+	{
+		dprintf(2, (LOG_AUTH, LOG_DEBUG, "%s: NULL realm", func));
+		return (-1);
+	}
+
+	if ((file_ent = find_file_ent (pfx)) == (FILE_LIST *) NULL)
+	{
+		dprintf(2, (LOG_AUTH, LOG_DEBUG, "%s: NULL file_ent", func));
+		return (-1);
+	}
+
+	if ((auth_ent = file_ent->auth_list) == (AUTH_ENTRY *) NULL)
+	{
+		/* If no auth_list for this prefix */
+		file_ent = file_list;
+		/* Default file_ent is first in file_list */
+		if ((auth_ent = file_ent->auth_list) == (AUTH_ENTRY *) NULL)
+		{
+			logit (LOG_DAEMON, LOG_ERR,
+				"%s: no default authfile data structure",
+				func);
+			return (-1);
+		}
+	}
+
+	/*
+	 *	Match realm name (either exact match or substring match
+	 *	based on *.realm syntax) with user supplied string.
+	 */
+	for ( ; auth_ent ; auth_ent = auth_ent->next )
+	{
+		realm_name = (char *) NULL;
+		if (auth_ent->parent == (AUTH_ENTRY *) NULL) /* parent realm */
+		{
+			entry = auth_ent;
+			/* Look for name match. */
+			if (entry->name[0] == '*') /* this is wildcard realm */
+			{
+				p = &entry->name[1];
+				pat_len = strlen (p);
+				head = strlen (u_realm) - pat_len;
+				if (strncmp ((char *) &u_realm[head],
+					(char *) &entry->name[1], pat_len) == 0)
+				{
+					realm_name = u_realm;
+				}
+				else
+				{
+					realm_name = (char *) NULL;
+				}
+			}
+			else /* not a wildcard realm */
+			{
+				if (strcasecmp (entry->name, u_realm) == 0)
+				{
+					realm_name = entry->name;
+				}
+			}
+		}
+		else /* this entry is an alias name for some real realm */
+		{
+			entry = auth_ent->parent;
+			/* Look for name match. */
+			if (entry->name[0] == '*') /* alias in wildcard realm */
+			{
+				p = &entry->name[1];
+				pat_len = strlen (p);
+				head = strlen (u_realm) - pat_len;
+				if (strncmp ((char *) &u_realm[head],
+					(char *) &entry->name[1], pat_len) == 0)
+				{
+					/* combine real prefix, parent suffix */
+					strcpy (temp, u_realm);
+					if (strtok (temp, ".") != (char *) NULL)
+					{
+						realm_name = strcat (temp,
+							       &entry->name[1]);
+					}
+				}
+				else
+				{
+					realm_name = (char *) NULL;
+				}
+			}
+			else /* regular alias */
+			{
+				if (strcasecmp (auth_ent->name, u_realm) == 0)
+				{
+					realm_name = entry->name;
+				}
+			}
+		}
+
+		if (realm_name != (char *) NULL) /* then we have a name match */
+		{
+			if (!entry->prot || (entry->prot == prot))
+			{
+				break;
+			}
+		}
+	}
+
+	if (auth_ent == (AUTH_ENTRY *) NULL)
+	{
+		dprintf(2, (LOG_AUTH, LOG_DEBUG, "%s: realm not found", func));
+		return (-1);
+	}
+
+	*type = entry->type;
+	*agent = entry->host;
+	*realm = realm_name;
+	*filter = entry->filter;
+	dprintf(2, (LOG_AUTH, LOG_DEBUG,
+		"%s: type %d, agent '%s', realm '%s' and filter '%s'",
+		func, entry->type, entry->host, realm_name, entry->filter));
+	return 0;
+} /* end of find_auth_type () */
+
+/*************************************************************************
+ *
+ *	Function: find_client
+ *
+ *	Purpose: Find the CLIENT_ENTRY in client_list for the client with
+ *		 the given IP address.  If the entry is found, the secret
+ *		 shared with this client, any configured file_pfx to be
+ *		 used for this client and the client's name are returned.
+ *
+ *	Returns: 0 = found client entry,
+ *		-1 = client not found.
+ *
+ *************************************************************************/
+
+int
+find_client (ipaddr, hostname, secret, pfx)
+
+UINT4           ipaddr;
+char          **hostname;	/* Receives resultant hostname, if found */
+char          **secret;		/* Receives resultant secret, if found */
+char          **pfx;		/* Receives resultant prefix, if found */
+
+{
+	int             ud = 0;
+	CLIENT_ENTRY   *client_ent;
+	IP_ADDRESS     *an_address;
+	time_t          cur_time;
+	static char    *func = "find_client";
+
+	dprintf(4, (LOG_AUTH, LOG_DEBUG, "%s: entered", func));
+
+	cur_time = time (0);
+	for (client_ent = client_list;
+		client_ent;
+		client_ent = client_ent->next)
+	{
+		if (cur_time > client_ent->expire_time)
+		{
+			ud = 1;
+		}
+
+		for (an_address = client_ent->addrs;
+			an_address != (IP_ADDRESS *) NULL;
+			an_address = an_address->next)
+		{
+			if (an_address->ipaddr.s_addr == ipaddr)
+			{
+				break;
+			}
+		}
+
+		if (an_address)
+		{
+			break;
+		}
+	}
+
+	if (ud > 0)
+	{
+		update_clients ();
+	}
+
+	/* Don't match host-only entries (those with a null secret) */
+	if (client_ent == (CLIENT_ENTRY *) NULL || *client_ent->secret == '\0')
+	{
+		return (-1);
+	}
+
+	*hostname = client_ent->hostname;
+	*secret = client_ent->secret;
+	*pfx = client_ent->prefix;
+
+	return (0);
+} /* end of find_client () */
+
+/*************************************************************************
+ *
+ *	Function: find_client_by_name
+ *
+ *	Purpose: Find the CLIENT_ENTRY in client_list for the client with
+ *		 the given hostname.  If the entry is found, the secret
+ *		 shared with this client, any configured file_pfx to be
+ *		 used for this client and the client's name are returned.
+ *
+ *	Returns: 0 = found client entry and resolved IP address,
+ *		 1 = found client entry but no IP address,	
+ *		 2 = found host entry but IP address not obtained 
+ *		     (unresolvable DNS name),	
+ *		-1 = client not found.
+ *
+ *************************************************************************/
+
+int
+find_client_by_name (ipaddr, hostname, secret, pfx)
+
+UINT4          *ipaddr;		/* Receives resultant address, if found */
+char           *hostname;	/* Match this name */
+char          **secret;		/* Receives resultant secret, if found */
+char          **pfx;		/* Receives resultant prefix, if found */
+
+{
+	int             ud = 0;
+	CLIENT_ENTRY   *client_ent;
+	DNS_NAME       *name_ent;
+	char           *name;
+	time_t          cur_time;
+	static char    *func = "find_client_by_name";
+
+	dprintf(4, (LOG_AUTH, LOG_DEBUG, "%s: entered", func));
+
+	if (good_ipaddr (hostname) == 0)
+	{
+		/* name = address same as find_client() call */
+		*ipaddr = ntohl(inet_addr (hostname));
+		return find_client (*ipaddr, &name, secret, pfx);
+	}
+
+	if (strcmp (hostname, RADIUS_LOCALSERVER) == 0)
+	{
+		hostname = ourhostname;
+	}
+
+	cur_time = time (0);
+	for (client_ent = client_list;
+		client_ent != (CLIENT_ENTRY *) NULL;
+		client_ent = client_ent->next)
+	{
+		if (cur_time > client_ent->expire_time)
+		{
+			ud = 1;
+		}
+
+		if (strcmp (client_ent->hostname, hostname) == 0)
+		{
+			break;
+		}
+
+		for (name_ent = client_ent->names;
+			name_ent != (DNS_NAME *) NULL;
+			name_ent = name_ent->next)
+		{
+			if (strcmp (name_ent->name, hostname) == 0)
+			{
+				break;
+			}
+		}
+
+		if (name_ent != (DNS_NAME *) NULL)
+		{
+			break;
+		}
+	}
+
+	if (ud > 0)
+	{
+		update_clients ();
+	}
+
+	/* Don't match host-only entries (those with a null secret) */
+	if (client_ent == (CLIENT_ENTRY *) NULL || *client_ent->secret == '\0')
+	{
+		return (-1);
+	}
+
+	*secret = client_ent->secret;
+	*pfx = client_ent->prefix;
+
+	if (client_ent->addrs == (IP_ADDRESS *) NULL)
+	{
+		*ipaddr = 0;
+		return (1);
+	}
+
+	if ((*ipaddr = client_ent->addrs->ipaddr.s_addr) == -1)
+	{
+		return (2);
+	}
+
+	return (0);
+} /* end of find_client_by_name () */
+
+/*************************************************************************
+ *
+ *	Function: find_host_by_name
+ *
+ *	Purpose: Resolve the host address by looking in the client list.
+ *		 Non-clients (those with a null secret) in this list
+ *		 are matched as well as normal clients.
+ *
+ *	Returns: 0 = found host entry and resolved IP address,
+ *		 1 = found host entry but unresolved IP address,	
+ *		 2 = found host entry but IP address not obtained 
+ *		     (unresolvable DNS name - uses address 255.255.255.255),	
+ *		-1 = host not found.
+ *
+ *************************************************************************/
+
+int
+find_host_by_name (ipaddr, hostname)
+
+UINT4          *ipaddr;		/* receives resultant address if found */
+char           *hostname;	/* Match this name */
+
+{
+	int             ud = 0;
+	char           *p;
+	char           *q;
+	CLIENT_ENTRY   *client_ent;
+	DNS_NAME       *name_ent;
+	time_t          cur_time;
+	static char    *func = "find_host_by_name";
+
+	dprintf(4, (LOG_AUTH, LOG_DEBUG, "%s: entered", func));
+
+	if (good_ipaddr (hostname) == 0)
+	{
+		*ipaddr = ntohl(inet_addr (hostname));
+		return 0;
+	}
+
+	if (strcmp (hostname, RADIUS_LOCALSERVER) == 0)
+	{
+		*ipaddr = self_ip[0];
+		return 0;
+	}
+
+	/* See if it's us.  Match full name or up to "." of our name */
+	for (p = hostname, q = ourhostname; *p == *q; p++, q++)
+	{
+		if (*p == '\0')
+		{
+			break;
+		}
+	}
+
+	if (*p == '\0' && (*q == '\0' || *q == '.'))
+	{
+		*ipaddr = self_ip[0];
+		return 0;
+	}
+
+	cur_time = time (0);
+	for (client_ent = client_list;
+		client_ent != (CLIENT_ENTRY *) NULL;
+		client_ent = client_ent->next)
+	{
+		if (cur_time > client_ent->expire_time)
+		{
+			ud = 1;
+		}
+
+		if (strcmp (client_ent->hostname, hostname) == 0)
+		{
+			break;
+		}
+
+		for (name_ent = client_ent->names;
+			name_ent != (DNS_NAME *) NULL;
+			name_ent = name_ent->next)
+		{
+			if (strcmp (name_ent->name, hostname) == 0)
+			{
+				break;
+			}
+		}
+
+		if (name_ent != (DNS_NAME *) NULL)
+		{
+			break;
+		}
+	}
+
+	if (ud > 0)
+	{
+		update_clients ();
+	}
+
+	if (client_ent == (CLIENT_ENTRY *) NULL)
+	{
+		*ipaddr = 0;
+		return (-1);
+	}
+
+	if (client_ent->addrs == (struct ip_address *) NULL)
+	{
+		*ipaddr = 0;
+		return (1);
+	}
+
+	if ((*ipaddr = client_ent->addrs->ipaddr.s_addr) == -1)
+	{
+		return (2);
+	}
+	return (0);
+} /* end of find_host_by_name () */
+
+/*************************************************************************
+ *
+ *	Function: find_file_ent
+ *
+ *	Purpose: Find a FILE_LIST entry on file_list with the specified
+ *		 file_pfx.  The entry should be found as find_file_ent is
+ *		 only called for file_pfx's that were found in the "clients"
+ *		 file at initialization time.
+ *
+ *************************************************************************/
+
+static FILE_LIST *
+find_file_ent (file_pfx)
+
+char           *file_pfx;
+
+{
+	FILE_LIST      *file_ent;
+	static char    *func = "find_file_ent";
+
+	dprintf(4, (LOG_AUTH, LOG_DEBUG, "%s: entered", func));
+
+	if ((file_ent = file_list) == (FILE_LIST *) NULL)
+	{
+		logit (LOG_DAEMON, LOG_ERR,
+			"%s: No users/authfile data structure", func);
+		return (FILE_LIST *) NULL;
+	}
+	if (file_pfx && file_pfx[0])
+	{
+		while (strcmp (file_ent->prefix, file_pfx) != 0)
+		{
+			if ((file_ent = file_ent->next) == (FILE_LIST *) NULL)
+			{
+				logit (LOG_DAEMON, LOG_ERR,
+					"%s: Couldn't match %s in FILE_LIST",
+					func, file_pfx);
+				return (FILE_LIST *) NULL;
+			}
+		}
+	}
+	return file_ent;
+} /* end of find_file_ent () */
+
+/*************************************************************************
+ *
+ *	Function: free_clients
+ *
+ *	Purpose: Toss client list entries and associated address structure.
+ *
+ *	Remark: Zap storage blocks to avoid leaving any secrets around.
+ *
+ *************************************************************************/
+
+static void
+free_clients (client_list)
+
+CLIENT_ENTRY   *client_list;
+
+{
+	CLIENT_ENTRY      *client_ent;
+	IP_ADDRESS        *an_address;	
+	DNS_NAME          *a_name;
+	static char       *func = "free_clients";
+
+	dprintf(4, (LOG_AUTH, LOG_DEBUG, "%s: entered", func));
+
+	for (client_ent = client_list;
+		client_ent != (CLIENT_ENTRY *) NULL;
+		client_ent = client_list)
+	{
+		client_list = client_ent->next;
+
+		for (an_address = client_ent->addrs;
+			an_address != (IP_ADDRESS *) NULL;
+			an_address = client_ent->addrs)
+		{
+			client_ent->addrs = an_address->next;
+			free (an_address);
+		}
+
+		for (a_name = client_ent->names;
+			a_name != (DNS_NAME *) NULL;
+			a_name = client_ent->names)
+		{
+			client_ent->names = a_name->next;
+			free (a_name);
+		}
+
+		free (client_ent);
+	}
+	return;
+} /* end of free_clients () */
+
+/*************************************************************************
+ *
+ *	Function: free_file_lists
+ *
+ *	Purpose: Free all the storage for the "users" and "authfile"
+ *		 memory resident data structures allocated by calling
+ *		 config_files().
+ *
+ *************************************************************************/
+
+static void
+free_file_lists ()
+
+{
+	FILE_LIST      *file_ent;
+	USER_ENTRY     *user_ent;
+	AUTH_ENTRY     *auth_ent;
+	static char    *func = "free_file_lists";
+
+	dprintf(4, (LOG_AUTH, LOG_DEBUG, "%s: entered", func));
+
+	authfile_cnt = 0;
+	users_cnt = 0;
+
+	for (file_ent = file_list; file_ent; file_ent = file_list)
+	{
+		for (user_ent = file_ent->user_list;
+			user_ent;
+			user_ent = file_ent->user_list)
+		{
+			file_ent->user_list = user_ent->next;
+			free_user_ent (user_ent);
+		}
+		for (auth_ent = file_ent->auth_list;
+			auth_ent;
+			auth_ent = file_ent->auth_list)
+		{
+			file_ent->auth_list = auth_ent->next;
+			free (auth_ent);
+		}
+		file_list = file_ent->next;
+		free (file_ent);
+	}
+
+	return;
+} /* end of free_file_lists () */
+
+/*************************************************************************
+ *
+ *	Function: free_user_ent
+ *
+ *	Purpose: Free all components of a USER_ENTRY structure.  Zap
+ *		 the USER_ENTRY storage.
+ *
+ *************************************************************************/
+
+void
+free_user_ent (user_ent)
+
+USER_ENTRY     *user_ent;
+
+{
+	list_free (user_ent->check);
+	list_free (user_ent->reply);
+	memset ((char *) user_ent, '\0', sizeof (USER_ENTRY));
+	free (user_ent);
+	return;
+} /* end of free_user_ent () */
+
+/*************************************************************************
+ *
+ *	Function: get_our_addr
+ *
+ *	Purpose: A global function to return a local variable (?)
+ *
+ *	Returns: (an) IP address of this machine.
+ *
+ *************************************************************************/
+
+UINT4
+get_our_addr ()
+
+{
+	return self_ip[0];
+} /* end of get_our_addr () */
+
+/*************************************************************************
+ *
+ *	Function: host_is_us
+ *
+ *	Purpose: Determine if we are the given host.
+ *
+ *	Returns: 1 if the given hostname is the name of this host,
+ *		 0 otherwise.
+ *
+ *************************************************************************/
+
+static int
+host_is_us (hostname)
+
+char	       *hostname;
+
+{
+	UINT4	        addr;
+	UINT4	       *adptr;
+
+	if (find_host_by_name (&addr, hostname) == 0)
+	{
+		for (adptr = self_ip; *adptr > 0; adptr++)
+		{
+			if (*adptr == addr)
+			{
+				return 1;
+			}
+		}
+	}
+	return 0;
+} /* end of host_is_us () */
+
+/*************************************************************************
+ *
+ *	Function: insert_client
+ *
+ *	Purpose: Inserts a CLIENT_ENTRY node into client_list for the
+ *		 given hostname.
+ *
+ *	Returns: 0 - inserted ok
+ *		-1 - bad news
+ *
+ *************************************************************************/
+
+static int
+insert_client (hostname, secret, prefix)
+
+char           *hostname;
+char           *secret;
+char           *prefix;
+
+{
+	CLIENT_ENTRY   *client_ent = (CLIENT_ENTRY *) NULL;
+	CLIENT_ENTRY   *oldent;
+	CLIENT_ENTRY  **prev;
+	IP_ADDRESS     *ip_address;
+	struct in_addr  addr;
+	static char    *func = "insert_client";
+
+	dprintf(4, (LOG_AUTH, LOG_DEBUG, "%s: entered", func));
+
+	/* Convert generic name for us to our real name */
+	if (strcmp (hostname, RADIUS_LOCALSERVER) == 0)
+	{
+		hostname = ourhostname;
+	}
+
+	/* Look for entry from previous list (before HUP) */
+
+	if (old_clients != (CLIENT_ENTRY *) NULL)
+	{
+		for (prev = &old_clients;
+			(oldent = *prev) != (CLIENT_ENTRY *) NULL;
+			prev = &oldent->next)
+		{
+			if (strcmp (hostname, oldent->hostname) == 0)
+			{
+				/* Matched - Remove from old list */
+				*prev = oldent->next;
+				client_ent = oldent;
+				break;
+			}
+		}
+	}
+
+	if (client_ent == (CLIENT_ENTRY *) NULL)
+	{
+		if ((client_ent =
+			(CLIENT_ENTRY *) malloc ( sizeof (CLIENT_ENTRY)))
+						== (CLIENT_ENTRY *) NULL)
+		{
+			logit (LOG_DAEMON, LOG_ALERT,
+			     "%s: FATAL Couldn't allocate CLIENT_ENTRY storage",
+				 func);
+			abort ();
+		}
+		client_ent->hostname = add_string (hostname, ASIS);
+		client_ent->names = (DNS_NAME *) NULL;
+		client_ent->addrs = (IP_ADDRESS *) NULL;
+		client_ent->type = CE_DNS;
+
+		/* Set constant addrs now so we don't have to wait for DNS */
+		if (good_ipaddr (hostname) == 0)
+		{
+			client_ent->type = CE_NUMERIC;
+			addr.s_addr = ntohl(inet_addr (hostname));
+		}
+		else
+		{
+			if (strcmp (hostname, ourhostname) == 0)
+			{
+				client_ent->type = CE_OURADDR;
+				addr.s_addr = self_ip[0];
+			}
+		}
+
+		if (client_ent->type != CE_DNS)
+		{
+			if ((ip_address =
+				(IP_ADDRESS *) malloc (sizeof (CLIENT_ENTRY)))
+							== (IP_ADDRESS *) NULL)
+			{
+				logit (LOG_DAEMON, LOG_ALERT,
+			       "%s: FATAL Couldn't allocate IP_ADDRESS storage",
+					func);
+				abort ();
+			}
+
+			ip_address->ipaddr = addr;
+			ip_address->next = (IP_ADDRESS *) NULL;
+			client_ent->addrs = ip_address;
+		}
+	}
+
+	client_ent->secret = add_string (secret, ASIS);
+	client_ent->prefix = add_string (prefix, ASIS);
+	client_ent->expire_time = (time_t) 0;
+	client_ent->next = client_list;
+	client_list = client_ent;
+
+	/*
+	 *	If the entry had an optional file prefix, add a new FILE_ENTRY
+	 *	to the file_list to handle this prefix.  Add_file_list() will
+	 *	not add duplicate entries.
+	 */
+	if (client_ent->prefix[0] != '\0')
+	{
+		add_file_list (client_ent->prefix);
+	}
+	return 0;
+} /* end of insert_client () */
+
+#define	MAX_HOSTNAME_BUFFERS	20
+
+/*************************************************************************
+ *
+ *	Function: ip_hostname
+ *
+ *	Purpose: Return a printable host name (or IP address in dotted quad
+ *		 notation) for the supplied IP address.
+ *
+ *************************************************************************/
+
+char *
+ip_hostname (h_ipaddr)
+
+UINT4           h_ipaddr;
+
+{
+	UINT4          *ourad;
+	CLIENT_ENTRY   *client_ent;
+	IP_ADDRESS     *an_address;
+	DNS_NAME       *a_name;
+	struct hostent *hp;
+	struct in_addr  inad;
+	static char     buffers[MAX_HOSTNAME_BUFFERS][128];
+	static int      ndx = 0;
+	char           *hstname = buffers[ndx];
+
+	for (client_ent = client_list;
+		client_ent != (CLIENT_ENTRY *) NULL;
+		client_ent = client_ent->next)
+	{
+		for (an_address = client_ent->addrs;
+			an_address != (IP_ADDRESS *) NULL;
+			an_address = an_address->next)
+		{
+			if (an_address->ipaddr.s_addr == h_ipaddr)
+			{
+				break;
+			}
+		}
+
+		if (an_address != (IP_ADDRESS *) NULL)
+		{
+			break;
+		}
+	}
+
+	if (client_ent != (CLIENT_ENTRY *) NULL)
+	{
+		/*
+		 *	We found something in our tables.
+		 *	Return a pointer to that instead of
+		 *	copying it to our own static area.
+		 */
+		if ((a_name = client_ent->names) == (DNS_NAME *) NULL ||
+			(a_name->type != 0))
+		{
+			return (client_ent->hostname);
+		}
+		else /* return official name if it's not in the main entry */
+		{
+			return (a_name->name);
+		}
+	}
+
+	/* Didn't find it in our tables.  Keep looking... */
+	for (ourad = self_ip;
+		(*ourad > (UINT4) 0) && (*ourad != h_ipaddr);
+		ourad++)
+	{
+		continue;
+	}
+
+	/* If it was our own address, return our hostname. */
+	if (*ourad > (UINT4) 0)
+	{
+		return (ourhostname);
+	}
+
+	/* It wasn't us, so make something up. */
+	inad.s_addr = htonl(h_ipaddr);
+	strcpy (hstname, inet_ntoa (inad));	/* xxx.yyy.zzz.qqq */
+
+	/*
+	 *	Special check for non-server use.
+	 *	Note: a server always will have at
+	 *	least one client.
+	 */
+	if (client_list == (CLIENT_ENTRY *) NULL)
+	{
+		if ((hp = gethostbyaddr ((char *) &inad.s_addr,
+					sizeof (struct in_addr),
+					AF_INET)) 
+				!= (struct hostent *) NULL)
+		{
+			strcpy (hstname, hp->h_name);
+		}
+	}
+
+	/* Circulate through the buffers... */
+	ndx++;
+	if (ndx >= MAX_HOSTNAME_BUFFERS)
+	{
+		ndx = 0;
+	}
+
+	return (hstname);
+
+} /* end of ip_hostname () */
+
+/*************************************************************************
+ *
+ *	Function: list_cat
+ *
+ *	Purpose: Given two lists, "a" and "b", place "b" at the end of "a"
+ *
+ *************************************************************************/
+
+void
+list_cat (a, b)
+
+VALUE_PAIR    **a;
+VALUE_PAIR     *b;
+
+{
+	VALUE_PAIR    **last;
+	FILE           *debugout = stdout;
+	static char    *func = "list_cat";
+
+	dprintf(4, (LOG_AUTH, LOG_DEBUG, "%s: entered", func));
+
+	if (debug_flag >= 4)
+	{
+		if (ddt)
+		{
+			debugout = ddt;
+		}
+		fprintf (debugout, "First list:\n");
+	}
+
+	for (last = a; *last != (VALUE_PAIR *) NULL; last = &((*last)->next))
+	{
+		if (debug_flag >= 4)
+		{
+			debug_pair (debugout, *last);
+		}
+	}
+
+	*last = b;
+
+	if (debug_flag >= 4)
+	{
+		fprintf (debugout, "and Second list:\n");
+		debug_list (debugout, b);
+	}
+
+	return;
+} /* end of list_cat () */
+
+#define LIST_COPY_LIMIT 256   /* Limit the number of items we will copy. */
+
+/*************************************************************************
+ *
+ *	Function: list_copy
+ *
+ *	Purpose: Make a copy of the entire list of value_pairs pointed to by
+ *		 from_list.  It is necessary to copy the check_items and
+ *		 reply_items from a USER_ENTRY before processing a request
+ *		 because they may be modified or freed.
+ *
+ *************************************************************************/
+
+void
+list_copy (to_list, from_list)
+
+VALUE_PAIR    **to_list;
+VALUE_PAIR     *from_list;
+
+{
+	int             count = 0;      /* Count items we copy. */
+	VALUE_PAIR     *copy_item;
+	VALUE_PAIR     *new_item;
+	VALUE_PAIR    **last;
+	VALUE_PAIR    **old_end;
+	static char    *func = "list_copy";
+
+	dprintf(2, (LOG_AUTH, LOG_DEBUG, "%s: entered", func));
+
+	if (to_list == (VALUE_PAIR **) NULL)
+	{
+		logit (LOG_DAEMON, LOG_ERR, "%s: NULL parameter", func);
+		exit (-13);
+	}
+
+	copy_item = (VALUE_PAIR *) NULL;
+
+	/* run to end of destination list */
+	for (last = to_list ;
+		*last != (VALUE_PAIR *) NULL ;
+		last = &((*last)->next))
+	{
+		if (*last == from_list)
+		{
+			logit (LOG_DAEMON, LOG_ALERT,
+			"%s: FATAL (0x%p->0x%p,0x%p) crosslinked at 0x%p->0x%p",
+			       func, to_list, *to_list, from_list, last, *last);
+			dumpcore = 1;
+			abort ();
+		}
+	}
+	old_end = last; /* Save old end-ptr for sanity checking. */
+
+	new_item = (VALUE_PAIR *) NULL;
+
+	dprintf(5, (LOG_AUTH, LOG_DEBUG,
+			"%s: copy from list 0x%p to end of list at 0x%p->0x%p",
+			func, from_list, to_list, *to_list));
+
+	for (copy_item = from_list ;
+		copy_item != (VALUE_PAIR *) NULL ;
+		copy_item = copy_item->next)
+	{
+		if (count > LIST_COPY_LIMIT)
+		{
+			logit (LOG_DAEMON, LOG_ALERT,
+			 "%s: FATAL (0x%p->0x%p, 0x%p), count=%d, limit exceed",
+				func, to_list, *to_list, from_list, copy_item,
+				count);
+			dumpcore = 1;
+			abort ();
+		}
+
+		if (copy_item == *old_end)
+		{
+			logit (LOG_DAEMON, LOG_ALERT,
+		 "%s: FATAL (0x%X->0x%X, 0x%X) list appended to itself at 0x%X",
+				func, to_list, *to_list, from_list, copy_item);
+			dumpcore = 1;
+			abort ();
+		}
+
+		if ((new_item = (VALUE_PAIR *) malloc (sizeof (VALUE_PAIR)))
+							== (VALUE_PAIR *) NULL)
+		{
+			logit (LOG_DAEMON, LOG_ALERT, "%s: FATAL out of memory",
+				func);
+			abort ();
+		}
+		memcpy ((char *) new_item, (char *) copy_item,
+			sizeof (VALUE_PAIR));
+		new_item->next = (VALUE_PAIR *) NULL;
+
+		*last = new_item; /* always put copy at end of to_list */
+		last = &(new_item->next); /* new end of to_list */
+		count++;
+	}
+	dprintf(2, (LOG_AUTH, LOG_DEBUG, "%s: copied %d items", func, count));
+	return;
+} /* end of list_copy () */
+
+#define PARSE_MODE_NAME		0
+#define PARSE_MODE_EQUAL	1
+#define PARSE_MODE_VALUE	2
+#define PARSE_MODE_INVALID	3
+
+/*************************************************************************
+ *
+ *	Function: pair_parse
+ *
+ *	Purpose: Parses the buffer to extract the attribute-value pairs.
+ *
+ *	Returns: 0 = successful parse of attribute-value pair,
+ *		-1 = syntax (or other) error detected.
+ *
+ *************************************************************************/
+
+int
+pair_parse (buffer, first_pair)
+
+char           *buffer;
+VALUE_PAIR    **first_pair;
+
+{
+	int             mode;
+	int             rc;
+	char            attrstr[AUTH_ID_LEN];
+	char            valstr[AUTH_ID_LEN];
+	DICT_ATTR      *attr;
+	DICT_VALUE     *dval;
+	VALUE_PAIR     *pair;
+	VALUE_PAIR     *link;
+	struct tm      *tm;
+	time_t          timeval;
+	static char    *func = "pair_parse";
+
+	dprintf(4, (LOG_AUTH, LOG_DEBUG, "%s: entered", func));
+
+	mode = PARSE_MODE_NAME;
+	while (*buffer != '\n' && *buffer != '\0')
+	{
+		if (*buffer == ' ' || *buffer == '\t' || *buffer == ',')
+		{
+			buffer++;
+			continue;
+		}
+
+		switch (mode)
+		{
+		    case PARSE_MODE_NAME:		/* Attribute Name */
+			fieldcpy (attrstr, &buffer);
+			if ((attr =
+				dict_attrfind (attrstr)) == (DICT_ATTR *) NULL)
+			{
+				return (-1);
+			}
+			mode = PARSE_MODE_EQUAL;
+			break;
+
+		    case PARSE_MODE_EQUAL:		/* Equal sign */
+			if (*buffer == '=')
+			{
+				mode = PARSE_MODE_VALUE;
+				buffer++;
+			}
+			else
+			{
+				return (-1);
+			}
+			break;
+
+		    case PARSE_MODE_VALUE:		/* Value */
+			fieldcpy (valstr, &buffer);
+
+			if ((pair =
+				(VALUE_PAIR *) malloc (sizeof (VALUE_PAIR)))
+							== (VALUE_PAIR *) NULL)
+			{
+				logit (LOG_DAEMON, LOG_ALERT,
+					"%s: FATAL out of memory", func);
+				abort ();
+			}
+			strcpy (pair->name, attr->name);
+			pair->attribute = attr->value;
+			pair->type = attr->type;
+
+			switch (pair->type)
+			{
+
+			    case PW_TYPE_STRING:
+				strcpy (pair->strvalue, valstr);
+				break;
+
+			    case PW_TYPE_INTEGER:
+				if (isdigit (*valstr))
+				{
+					pair->lvalue = atoi (valstr);
+				}
+				else
+				{
+					if ((dval = dict_valfind (valstr))
+							== (DICT_VALUE *) NULL)
+					{
+						free (pair);
+						return (-1);
+					}
+					else
+					{
+						pair->lvalue = dval->value;
+					}
+				}
+				break;
+
+			    case PW_TYPE_IPADDR:
+				rc = find_host_by_name (&pair->lvalue, valstr);
+				if (rc == -1)   /* Name not in our list yet */
+				{
+					insert_client (valstr, "", "");
+				}
+				else
+				{
+					if (rc == 2) /* Unresolvable DNS name */
+					{
+						free (pair);
+						return (-1);
+					}
+				}
+
+				/* If DNS name given, store it in strvalue */
+				if (good_ipaddr (valstr) == 0)
+				{
+					*pair->strvalue = '\0';
+				}
+				else /* Now we can re-resolve these names */
+				{
+					strcpy (pair->strvalue, valstr);
+				}
+				break;
+
+			    case PW_TYPE_DATE:
+				timeval = time (0);
+				tm = localtime (&timeval);
+				tm->tm_hour = 0;
+				tm->tm_min = 0;
+				tm->tm_sec = 0;
+				user_gettime (valstr, tm);
+#ifdef TIMELOCAL
+				pair->lvalue = (UINT4) timelocal (tm);
+#else	/* TIMELOCAL */
+				pair->lvalue = (UINT4) mktime (tm);
+#endif	/* TIMELOCAL */
+				break;
+
+			    default:
+				free (pair);
+				return (-1);
+			}
+			pair->next = (VALUE_PAIR *) NULL;
+
+			if (*first_pair == (VALUE_PAIR *) NULL)
+			{
+				*first_pair = pair;
+			}
+			else
+			{
+				link = *first_pair;
+				while (link->next != (VALUE_PAIR *) NULL)
+				{
+					link = link->next;
+				}
+				link->next = pair;
+			}
+
+			mode = PARSE_MODE_NAME;
+			break;
+
+		    default:
+			mode = PARSE_MODE_NAME;
+			break;
+		}
+	}
+	return (0);
+} /* end of pair_parse () */
+
+/*************************************************************************
+ *
+ *	Function: read_auth
+ *
+ *	Purpose: Reads in the realm information from the "authfile" with
+ *	         file_pfx corresponding to the given file_ent.  The information
+ *		 read is copied to a data structure that is linked to the
+ *		 given file_ent.
+ *
+ *************************************************************************/
+
+static int
+read_auth (file_ent, dolog)
+
+FILE_LIST      *file_ent;
+int             dolog;
+
+{
+	FILE             *authfd;
+	char             *name;
+	char             *prot;
+	char             *type;
+	char             *host;
+	char             *filter;
+	char             *alias_list;
+	int               line_no = 0;
+	int               ent_cnt = 0;
+	int               type_inx;
+	int               prot_inx;
+	int               error = 0;
+	AUTH_ENTRY       *auth_ent;
+	AUTH_ENTRY      **end_ent;
+	AUTH_ALIAS_ENTRY *alias_ent;
+	DICT_VALUE       *type_val;
+	static char      *auth_prots[] =
+	{
+		PW_PROTTYPES_DFLT,
+		PW_PROTTYPES_CHAP,
+		PW_PROTTYPES_PW,
+		NULL
+	};
+	char              buffer[128];
+	char              fname[MAXPATHLEN];
+	char             *func = "read_auth";
+
+	dprintf(2, (LOG_AUTH, LOG_DEBUG, "%s: entered", func));
+
+	sprintf (fname, "%s/%s%s", radius_dir, file_ent->prefix, RADIUS_AUTH);
+	if ((authfd = fopen (fname, "r")) == (FILE *) NULL)
+	{
+		/*
+		 * It's okay if a non-prefixed RADIUS_AUTH file doesn't exist,
+		 * as long as no CI_AUTHENTICATION_TYPE = REALM entries are
+		 * configured in the RADIUS_USERS file.  If <pfx>RADIUS_AUTH
+		 * doesn't exist, then the non-prefixed RADIUS_AUTH file will
+		 * be used instead.
+		 */
+		return (0);
+	}
+	end_ent = &file_ent->auth_list;
+	while (fgets (buffer, sizeof (buffer), authfd) != (char *) NULL)
+	{
+		line_no++;
+		if (isspace (*buffer) || (*buffer == COMMENT))
+		{
+			continue;
+		}
+		name = strtok (buffer, " ,\t\n\r");
+
+		if (strcmp ("DEFAULT_RADIUS_SERVER", name) == 0)
+		{
+			if ((host = strtok (NULL, " \"\n\r")) != (char *) NULL)
+			{
+				strcpy (default_radius_server, host);
+			}
+			else
+			{
+				default_radius_server[0] = '\0';
+			}		
+			continue;
+		}
+		else
+		{
+			if (strcmp ("DEFAULT_TACACS_SERVER", name) == 0)
+			{
+				if ((host = strtok (NULL, " \"\n\r"))
+							!= (char *) NULL)
+				{
+					strcpy (default_tacacs_server, host);
+				}
+				else
+				{
+					default_tacacs_server[0] = '\0';
+				}		
+				continue;
+			}
+		}
+
+		/* Scan for optional alias list or protocol indicator */
+
+		prot_inx = 0;
+		alias_list = NULL;
+		type = NULL;
+
+		while (type == NULL && !error)
+		{
+			prot = strtok (NULL, " ,\t\n\r");
+			if (prot == NULL)
+			{
+				error++;
+				continue;
+			}
+
+
+			switch (*prot)
+			{
+			    case '(': 	/* "(<aliases>)" */
+				alias_list = prot;
+				if (prot[strlen (prot) - 1] != ')')
+				{
+					if (strtok (NULL, ")") == NULL)
+					{
+						error++;
+					}
+				/* We don't want to break up alias list yet */
+					prot[strlen (prot)] = ' ';
+				}
+				break;
+
+			    case ('-'):	/* "-<protocol>" */
+				if (*++prot == '\0')
+				{
+					error++;
+					break;
+				}
+
+				for (prot_inx = 0;
+				     auth_prots[prot_inx] &&
+				       strcmp (prot, auth_prots[prot_inx]) != 0;
+				     prot_inx++)
+				{
+					;
+				}
+
+				if (!auth_prots[prot_inx])
+				{
+					error++;
+				}
+				break;
+
+			    default:
+				type = prot;
+				break;
+			}
+		}
+
+		if (error)
+		{
+			logit (LOG_DAEMON, LOG_ERR,
+				"%s: Invalid entry in %s at line %d",
+				func, fname, line_no);
+			continue;
+		}
+
+		type_val = dict_valfind (type);
+		if (type_val == (DICT_VALUE *) NULL)
+		{
+			logit (LOG_DAEMON, LOG_ERR,
+				"%s: Invalid TYPE '%s' in %s at line %d",
+				func, type, fname, line_no);
+			continue;
+		}
+		type_inx = type_val->value;
+
+		host = strtok (NULL, " \t\n\r");
+		filter = strtok (NULL, " \t\n\r");
+
+		if (host == NULL && type_inx != AA_UNIX)
+		{
+			logit (LOG_DAEMON, LOG_ERR,
+				"%s: Invalid entry in %s at line %d",
+				func, fname, line_no);
+			continue;
+		}
+
+		if ((auth_ent = (AUTH_ENTRY *) malloc (sizeof (AUTH_ENTRY)))
+				== (AUTH_ENTRY *) NULL)
+		{
+			logit (LOG_DAEMON, LOG_ALERT,
+				"%s: No memory for auth_ent", func);
+			fclose (authfd);
+			return (-1);
+		}
+		auth_ent->parent = (AUTH_ENTRY *) NULL;
+		auth_ent->type = type_inx;
+
+		if (is_engine == 1) /* then we are being called by the engine */
+		{
+			if (authtype_tv[type_inx] == (AATV *) NULL)
+			{
+				logit (LOG_DAEMON, LOG_ERR,
+				  "%s: Missing AATV for entry on line %d of %s",
+					func, line_no, fname);
+				fclose (authfd);
+				return (-1);
+			}
+		}
+
+		auth_ent->prot = prot_inx;
+		auth_ent->name = add_string (name, ASIS);
+		auth_ent->host = add_string (host, ASIS);
+		auth_ent->filter = add_string (filter, ASIS);
+		auth_ent->next = (AUTH_ENTRY *) NULL;
+		*end_ent = auth_ent;
+		end_ent = &auth_ent->next;
+		ent_cnt++;
+
+		/* Add alias entries if aliases given */
+		if (alias_list)
+		{
+			alias_list++;
+			name = strtok (alias_list, " ,)");
+			while (name)
+			{
+				if ((alias_ent = 
+					(AUTH_ALIAS_ENTRY *) malloc 
+						(sizeof(AUTH_ALIAS_ENTRY)) )
+						   == (AUTH_ALIAS_ENTRY *) NULL)
+				{
+					logit (LOG_DAEMON, LOG_ALERT,
+						"%s: No memory for auth_ent",
+						func);
+					fclose (authfd);
+					return (-1);
+				}
+				alias_ent->name = add_string (name, ASIS);
+				alias_ent->parent = auth_ent;
+				alias_ent->next = (AUTH_ENTRY *) NULL;
+				*end_ent = (AUTH_ENTRY *) alias_ent;
+				end_ent = &alias_ent->next;
+				name = strtok (NULL, " ,)");
+			}
+		}
+	}
+	fclose (authfd);
+	if (dolog)
+	{
+		logit (LOG_DAEMON, LOG_INFO,
+			"%s: %s (%u entries) read to memory",
+			func, fname, ent_cnt);
+	}
+	authfile_cnt += ent_cnt;
+
+	return 0;
+} /* end of read_auth () */
+
+#define FIND_MODE_NAME	0
+#define FIND_MODE_REPLY	1
+#define FIND_MODE_SKIP	2
+#define FIND_MODE_FLUSH	3
+
+/*************************************************************************
+ *
+ *	Function: read_users
+ *
+ *	Purpose: For each entry in a "users" file (with the file_pfx
+ *		 corresponding to the given file_ent), read all check_items
+ *		 and reply_items into a data structure.  Each such
+ *		 data structure is linked to the given file_ent.
+ *
+ *	Returns:  0 = normal (successful) return,
+ *		 -1 = error return.
+ *
+ *************************************************************************/
+
+static int
+read_users (file_ent, dolog)
+
+FILE_LIST      *file_ent;
+int             dolog;
+
+{
+	FILE           *userfd;
+	char           *ptr;
+	int             mode;
+	int             line_nbr = 0;
+	int             count = 0;
+	USER_ENTRY     *user_ent = (USER_ENTRY *) NULL;
+	USER_ENTRY    **end_ent;
+	char            buffer[256];
+	char            fname[MAXPATHLEN];
+	static char    *func = "read_users";
+
+	dprintf(2, (LOG_AUTH, LOG_DEBUG, "%s: entered", func));
+
+	/*
+	 * Open the user table
+	 */
+	sprintf (fname, "%s/%s%s", radius_dir, file_ent->prefix, RADIUS_USERS);
+	if ((userfd = fopen (fname, "r")) == (FILE *) NULL)
+	{
+		/*
+		 *  It's ok for prefixed user files to not exist, but
+		 *  non-prefixed user file has to exist.
+		 */
+		if (file_ent->prefix[0] == 0)
+		{
+			logit (LOG_DAEMON, LOG_ERR,
+				"%s: Couldn't open %s for reading",
+			 	func, fname);
+			return (-1);
+		}
+		return (0);
+	}
+
+	end_ent = &file_ent->user_list;
+
+	mode = FIND_MODE_NAME;
+	while (fgets (buffer, sizeof (buffer), userfd) != (char *) NULL)
+	{
+		line_nbr++;
+		if (*buffer == COMMENT)
+		{
+			continue;
+		}
+
+		for (;;)
+		{
+			if (mode == FIND_MODE_NAME)
+			{
+				if (isspace (*buffer))
+				{
+					break;  /* to read another line */
+				}
+				if ((user_ent = (USER_ENTRY *)
+						malloc (sizeof (USER_ENTRY)))
+							== (USER_ENTRY *) NULL)
+				{
+					logit (LOG_DAEMON, LOG_ALERT,
+				     "%s: Couldn't allocate USER_ENTRY storage",
+						func);
+					return (-1);
+				}
+				ptr = strtok (buffer, " \t\n\r");
+				user_ent->name = add_string (ptr, ASIS);
+				user_ent->check = (VALUE_PAIR *) NULL;
+				user_ent->reply = (VALUE_PAIR *) NULL;
+				user_ent->next = (USER_ENTRY *) NULL;
+
+				/*
+				 * Parse the check values
+				 */
+				ptr += strlen (ptr) + 1;
+				if (pair_parse (ptr, &user_ent->check) != 0)
+				{
+					logit (LOG_DAEMON, LOG_ERR,
+				      "%s: %d: Parse error for user %s (check)",
+					 	func, line_nbr, user_ent->name);
+					free_user_ent (user_ent);
+					user_ent = (USER_ENTRY *) NULL;
+				}
+				else
+				{
+					mode = FIND_MODE_REPLY;
+				}
+				break;  /* to read another line */
+			}
+
+			/* Reading reply items */
+			if (isspace (*buffer))
+			{
+				/*
+				 * Parse the reply values
+				 */
+				if (pair_parse (buffer, &user_ent->reply) != 0)
+				{
+					logit (LOG_DAEMON, LOG_ERR,
+				      "%s: %d: Parse error for user %s (reply)",
+						func, line_nbr, user_ent->name);
+					free_user_ent (user_ent);
+					user_ent = (USER_ENTRY *) NULL;
+					mode = FIND_MODE_NAME;
+				}
+				break;
+			}
+
+			/* Start of next entry */
+			*end_ent = user_ent;
+			end_ent = &user_ent->next;
+			user_ent = (USER_ENTRY *) NULL;
+			mode = FIND_MODE_NAME;
+			count++;
+			continue;       /* with same input */
+		}
+	}
+	fclose (userfd);
+	if (user_ent != (USER_ENTRY *) NULL)
+	{
+		*end_ent = user_ent;
+		count++;
+	}
+
+	if (dolog)
+	{
+		logit (LOG_DAEMON, LOG_INFO,
+			"%s: %s (%u entries) read to memory",
+			func, fname, count);
+	}
+	users_cnt += count;
+
+	return (0);
+} /* end of read_users () */
+
+/*************************************************************************
+ *
+ *	Function: return_file_list
+ *
+ *	Purpose: Returns the private file_list pointer for clients in need.
+ *
+ *************************************************************************/
+
+FILE_LIST *
+return_file_list ()
+
+{
+	return (file_list);
+} /* end of return_file_list () */
+
+/*************************************************************************
+ *
+ *	Function: update_clients
+ *
+ *	Purpose: Updates IP address(es) and expire_time in given
+ *		 CLIENT_ENTRY node.
+ *
+ *************************************************************************/
+
+int
+update_clients ()
+
+{
+	int                len;
+	int                notify_count = 0;
+	struct hostent    *hp;
+	struct in_addr   **addrlist;
+	char              *aliascnt;
+	char              *ptr;
+	char             **name;
+	CLIENT_ENTRY      *client_ent;
+	struct sockaddr_in send_sin;
+	time_t             cur_time;
+	static char       *func = "update_clients";
+
+	dprintf(4, (LOG_AUTH, LOG_DEBUG, "%s: entered", func));
+
+	if (dnspid != 0)	/* Already resolving addrs */
+	{
+		return 0;
+	}
+
+	if (spawn_flag > 0)
+	{
+		if ((dnspid = (int) fork ()) < 0)	
+		{
+			dnspid = 0;
+			logit (LOG_DAEMON, LOG_ALERT, "%s: fork: %s",
+				func, get_errmsg ());
+			return (-1);
+		}
+
+		if (dnspid != 0)	/* Parent */
+		{
+			return 0;
+		}
+	}
+	
+	/* ======= Child process code ======= */
+
+	memset ((char *) &send_sin, '\0', sizeof (send_sin));
+	send_sin.sin_family = AF_INET;
+	send_sin.sin_addr.s_addr = htonl(self_ip[0]);
+	send_sin.sin_port = htons(rad_ipc_port);
+
+	cur_time = time (0);
+	for (client_ent = client_list;
+		client_ent != (CLIENT_ENTRY *) NULL;
+		client_ent = client_ent->next)
+	{
+		if (cur_time < client_ent->expire_time)
+		{
+			continue;
+		}
+
+		hp = gethostbyname (client_ent->hostname);
+		ptr = send_buffer;
+		*ptr++ = '\0';		/* Only one code for now */
+		strcpy (ptr, client_ent->hostname);
+		ptr += strlen (ptr) + 1;
+		*ptr = '\0';
+		aliascnt = ptr++;
+
+		if (hp != (struct hostent *) NULL)
+		{
+			if (hp->h_aliases != (char **) NULL)
+			{
+				for (name = hp->h_aliases;
+					*name != (char *) NULL;
+					name++)  
+				{
+					if (strcmp (client_ent->hostname,
+						    *name) != 0)
+					{
+						(*aliascnt)++; 
+						strcpy (ptr, *name);
+						ptr += strlen (ptr) + 1; 
+
+						/* Indicate just an alias */
+						*ptr++ = 1;
+					}
+				}
+			}
+
+			/* Pass official name last */
+			if (strcmp (client_ent->hostname, hp->h_name) != 0)
+			{
+				(*aliascnt)++; 
+				strcpy (ptr, hp->h_name);
+				ptr += strlen (ptr) + 1; 
+				*ptr++ = 0;	/* Indicate official name */
+			}
+
+			if (hp->h_addr_list != (char **) NULL)
+			{
+				addrlist = (struct in_addr **) hp->h_addr_list;
+				while (*addrlist)
+				{
+					memcpy (ptr, *(char **) addrlist,
+						sizeof (struct in_addr));
+					ptr += sizeof (struct in_addr);
+					addrlist++;
+				}
+			}
+			memset (ptr, '\0', sizeof (struct in_addr)); 
+		}
+		else /* Extra check for brain-dead gethostbyname() calls */
+		{
+			if (client_ent->type == CE_NUMERIC)
+			{
+				struct in_addr temp;
+
+				temp.s_addr = ntohl(inet_addr (client_ent->hostname));
+				memcpy (ptr, (char *) &temp,
+					sizeof (struct in_addr));
+				ptr += sizeof (struct in_addr);
+				memset (ptr, '\0', sizeof (struct in_addr));
+			}
+			else
+			{
+				memset (ptr, '\0', sizeof (struct in_addr)); 
+				/* Pass error code in packet */
+				*(ptr + sizeof (struct in_addr) - 1) = h_errno;
+			}
+		}
+
+		len = ptr - send_buffer + sizeof (struct in_addr);
+		notify_count++;
+
+		dprintf (2, (LOG_DAEMON, LOG_DEBUG,
+			"%s: Sendto call number (%d) for client '%s'",
+			func, notify_count, client_ent->hostname));
+
+		if (notify_count % DNS_SLEEP == 0)
+		{
+			sleep (1);
+		}
+
+		/* Send it to main process */
+		sendto (rad_ipc_aatv->sockfd, send_buffer, len, (int) 0,
+			(struct sockaddr *) & send_sin, sizeof (send_sin));
+
+	}
+
+	if (spawn_flag > 0)
+	{
+		_exit (0);
+	}
+
+	return 0;
+} /* end of update_clients () */
+
+/*************************************************************************
+ *
+ *	Function: user_find
+ *
+ *	Purpose: Find the named user in the users database.  Create the
+ *		 set of attribute-value pairs to check and reply with
+ *		 for this user from the database.
+ *
+ *		 Note that the users database can be either in the file
+ *		 "RADIUS_USERS" (-u option), in memory (default), or in
+ *		 a dbm(3) database.
+ *
+ *************************************************************************/
+
+int
+user_find (file_pfx, name, protocol, check_pairs, reply_pairs, not_user_file)
+
+char           *file_pfx;        /* Selects which "users" file to use */
+char           *name;
+int             protocol;
+VALUE_PAIR    **check_pairs;
+VALUE_PAIR    **reply_pairs;
+int             not_user_file;   /* Look up user in a domain specific file */
+
+{
+	int             mode;
+	int             namelen;
+	FILE           *userfd;
+	VALUE_PAIR     *check_first;
+	VALUE_PAIR     *reply_first;
+	VALUE_PAIR     *prot_ent;
+	FILE_LIST      *file_ent;
+	USER_ENTRY     *user_ent;
+	USER_ENTRY     *dflt_ent;
+	char           *ptr;
+	FILE           *debugout = stdout;
+	char            buffer[256];
+	char            fname[MAXPATHLEN];
+	static char    *func = "user_find";
+
+#ifdef USE_NDBM
+	datum           named;
+	datum           contentd;
+	DBM            *db;
+#endif	/* USE_NDBM */
+
+#ifdef USE_DBM
+	datum           named;
+	datum           contentd;
+#endif	/* USE_DBM */
+
+	dprintf(2, (LOG_AUTH, LOG_DEBUG, "%s: entered name='%s'", func, name));
+
+	*check_pairs = check_first = (VALUE_PAIR *) NULL;
+	*reply_pairs = reply_first = (VALUE_PAIR *) NULL;
+
+	if ((namelen = strlen (name)) == 0)
+	{
+		return 1;	/* A null name would match every line. */
+	}
+
+	/*
+	 *	First check what type of lookup to do.
+	 *	See if user file(s) have been cached in memory.
+	 */
+
+	if (not_user_file == 0 &&
+		(dflt_ent = file_list->user_list) != (USER_ENTRY *) NULL)
+	{
+		if ((file_ent = find_file_ent (file_pfx)) == (FILE_LIST *) NULL)
+		{
+			return (-1);
+		}
+		if ((user_ent = file_ent->user_list) == (USER_ENTRY *) NULL)
+		{
+			/* If no user file for this prefix, ...  */
+			/* ... then use first entry in file_list as ... */
+			/* ... default file_ent. */
+			user_ent = dflt_ent;
+		}
+		for (; user_ent; user_ent = user_ent->next)
+		{
+			/* Allow match for entry specifying framed protocol
+			 * type specified in "protocol".  An entry with a 
+			 * matching name but no framed-protocol type check 
+			 * item matches unconditionally.  An entry with a 
+			 * matching name and a framed-protocol type check 
+			 * item must match value in "protocol".
+			 */
+
+		dprintf(2, (LOG_AUTH, LOG_DEBUG, "%s: cache name='%s'", func, user_ent->name));
+
+#ifndef REGNAMES
+			if (strcmp (user_ent->name, name) == 0)
+#else /* REGNAMES */
+			if (regimatch(user_ent->name, name))
+#endif /* REGNAMES */
+			{
+				if ((prot_ent =
+					get_vp (user_ent->check,
+						PW_FRAMED_PROTOCOL))
+							== (VALUE_PAIR *) NULL)
+				{
+					break;
+				}
+				if (prot_ent->lvalue == protocol)
+				{
+					break;
+				}
+			}
+		}
+		if (!user_ent)	/* rc 1 => User not found and no error */
+		{
+			return 1;
+		}
+		list_copy (&check_first, user_ent->check);
+		list_copy (&reply_first, user_ent->reply);
+		if (debug_flag >= 2)
+		{
+			if (ddt)
+			{
+				debugout = ddt;
+			}
+
+			fprintf (debugout, "Check items:\n");
+			debug_list (debugout, user_ent->check);
+			fprintf (debugout, "Reply items:\n");
+			debug_list (debugout, user_ent->reply);
+		}
+		*check_pairs = check_first;
+		*reply_pairs = reply_first;
+		return (0);	/* rc 0 => User found */
+	} /* End of cached users file(s) lookup */
+
+	/*
+	 * Open the user table.
+	 * If prefixed file doesn't exist, use default (non-prefixed) file.
+	 */
+	for (;;)
+	{
+		sprintf (fname, "%s/%s%s", radius_dir,
+			file_pfx ? file_pfx : "", RADIUS_USERS);
+
+#if defined(USE_DBM) || defined(USE_NDBM)
+#ifdef USE_NDBM
+		if ((db = dbm_open (fname, O_RDONLY, 0)) == 0)
+		{
+#endif	/* USE_NDBM */
+#ifdef USE_DBM
+		if (dbminit (fname) != 0)
+		{
+#endif	/* USE_DBM */
+#else	/* USE_DBM || USE_NDBM */
+		if ((userfd = fopen (fname, "r")) == (FILE *) NULL)
+		{
+#endif	/* USE_DBM || USE_NDBM */
+
+			if (not_user_file == 0 &&
+				file_pfx != NULL &&
+				file_pfx[0] != 0)
+			{
+				file_pfx = NULL;
+				continue;
+			}
+
+			logit (LOG_DAEMON, LOG_ERR,
+				"%s: Couldn't open %s for reading",
+			 	func, fname);
+			return (-1);
+		}
+		break;
+	}
+
+#if defined(USE_DBM) || defined(USE_NDBM)
+/*
+ * Note that the DBM feature currently does not support protocol specific user
+ * entry matching.  It could be done by appending a protocol type to the
+ * name when building the database and looking for that modified name here.
+ * This seems like a non-trivial task and we don't use the DBM feature
+ * here so I'll leave this to someone else to implement.
+ */
+	named.dptr = name;
+	named.dsize = namelen;
+
+#ifdef	USE_NDBM
+	contentd = dbm_fetch (db, named);
+#else	/* USE_NDBM */
+	contentd = fetch (named);
+#endif	/* USE_NDBM */
+
+	if (contentd.dsize == 0)
+	{
+		named.dptr = "DEFAULT";
+		named.dsize = strlen ("DEFAULT");
+
+#ifdef	USE_NDBM
+		contentd = dbm_fetch (db, named);
+#else	/* USE_NDBM */
+		contentd = fetch (named);
+#endif	/* USE_NDBM */
+
+		if (contentd.dsize == 0)
+		{
+#ifdef	USE_NDBM
+			dbm_close (db);
+#else	/* USE_NDBM */
+			dbmclose ();
+#endif	/* USE_NDBM */
+			return (-1);
+		}
+	}
+
+	/*
+	 * Parse the check values
+	 */
+	ptr = contentd.dptr;
+	contentd.dptr[contentd.dsize] = '\0';
+
+	if (pair_parse (ptr, &check_first) != 0)
+	{
+		logit (LOG_DAEMON, LOG_ERR,
+			"%s: Parse error for user %s in %s (check)",
+			func, name, fname);
+		list_free (check_first);
+#ifdef	USE_NDBM
+		dbm_close (db);
+#else	/* USE_NDBM */
+		dbmclose ();
+#endif	/* USE_NDBM */
+		return (-1);
+	}
+	while (*ptr != '\n' && *ptr != '\0')
+	{
+		ptr++;
+	}
+	if (*ptr != '\n')
+	{
+		list_free (check_first);
+#ifdef	USE_NDBM
+		dbm_close (db);
+#else	/* USE_NDBM */
+		dbmclose ();
+#endif	/* USE_NDBM */
+		return (-1);
+	}
+	ptr++;
+
+	/*
+	 * Parse the reply values
+	 */
+	if (pair_parse (ptr, &reply_first) != 0)
+	{
+		logit (LOG_DAEMON, LOG_ERR,
+			"%s: Parse error for user %s in %s (reply)",
+			func, name, fname);
+		list_free (check_first);
+		list_free (reply_first);
+#ifdef	USE_NDBM
+		dbm_close (db);
+#else	/* USE_NDBM */
+		dbmclose ();
+#endif	/* USE_NDBM */
+		return (-1);
+	}
+#ifdef	USE_NDBM
+	dbm_close (db);
+#else	/* USE_NDBM */
+	dbmclose ();
+#endif	/* USE_NDBM */
+	*check_pairs = check_first;
+	*reply_pairs = reply_first;
+	return (0);
+
+#else /* USE_DBM || USE_NBDM */
+
+	mode = FIND_MODE_NAME;
+
+	while (fgets (buffer, sizeof (buffer), userfd) != (char *) NULL)
+	{
+		if (*buffer == COMMENT)
+		{
+			continue;
+		}
+
+		if (mode == FIND_MODE_NAME)
+		{
+			/*
+			 * Find the entry starting with the users name.
+			 */
+		dprintf(2, (LOG_AUTH, LOG_DEBUG, "%s: name='%s'", func, user_ent->name));
+
+#ifndef REGNAMES
+			if (strncmp (buffer, name, namelen) == 0 &&
+			    (buffer[namelen] == ' ' || buffer[namelen] == '\t'))
+			{
+				ptr = &buffer[namelen];
+#else /* REGNAMES */
+			ptr = strtok(buffer, " \t\n");
+			fprintf(debugout, "name='%s'", ptr);
+			if (regimatch(ptr, name))
+			{
+				ptr = &buffer[strlen(ptr)+1];
+				fprintf(debugout, "name='%s'", ptr);
+#endif /* REGNAMES */
+				/*
+				 * Parse the check values
+				 */
+				if (pair_parse (ptr, &check_first) != 0)
+				{
+					logit (LOG_DAEMON, LOG_ERR,
+				"%s: Parse error for user %s in %s (DBM check)",
+						 func, name, fname);
+					list_free (check_first);
+					fclose (userfd);
+					return (-1);
+				}
+				/* Allow match for entry specifying framed
+				 * protocol type specified in "protocol".  An
+				 * entry with matching name but no
+				 * framed-protocol type check item matches
+				 * unconditionally.  An entry with matching
+				 * name and a framed-protocol type check item
+				 * must match value in "protocol".
+				 */
+				if ((prot_ent =
+					get_vp (check_first,
+						PW_FRAMED_PROTOCOL))
+							!= (VALUE_PAIR *) NULL)
+				{
+					if (prot_ent->lvalue != protocol)
+					{
+						list_free (check_first);
+						continue;
+					}
+				}
+				mode = FIND_MODE_REPLY;
+			}
+		}
+		else
+		{
+			if (*buffer == ' ' || *buffer == '\t')
+			{
+				/*
+				 * Parse the reply values
+				 */
+				if (pair_parse (buffer, &reply_first) != 0)
+				{
+					logit (LOG_DAEMON, LOG_ERR,
+				"%s: Parse error for user %s in %s (DBM reply)",
+						 func, name, fname);
+					list_free (check_first);
+					list_free (reply_first);
+					fclose (userfd);
+					return (-1);
+				}
+			}
+			else /* We are done */
+			{
+				break;
+			}
+		}
+	}
+	fclose (userfd);
+
+	/* Update the callers pointers */
+	if (mode == FIND_MODE_NAME)
+	{
+		return 1;		/* No error and no match */
+	}
+	*check_pairs = check_first;
+	*reply_pairs = reply_first;
+	return (0);
+#endif	/* USE_DBM || USE_NBDM */
+} /* end of user_find () */
+
+static char * months[] =
+		{
+			"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+			"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+		};
+
+/*************************************************************************
+ *
+ *	Function: user_gettime
+ *
+ *	Purpose: Turns printable string into correct tm struct entries.
+ *
+ *************************************************************************/
+
+void
+user_gettime (valstr, tm)
+
+char           *valstr;
+struct tm      *tm;
+
+{
+	int             i;
+	static char    *func = "user_gettime";
+
+	dprintf(2, (LOG_AUTH, LOG_DEBUG, "%s: entered", func));
+
+	/* Get the month */
+	for (i = 0; i < 12; i++)
+	{
+		if (strncmp (months[i], valstr, 3) == 0)
+		{
+			tm->tm_mon = i;
+			i = 13;
+		}
+	}
+
+	/* Get the Day */
+	tm->tm_mday = atoi (&valstr[4]);
+
+	/* Now the year */
+	tm->tm_year = atoi (&valstr[7]) - 1900;
+} /* end of user_gettime () */
+
+/*************************************************************************
+ *
+ *	Function: user_update
+ *
+ *	Purpose: Updates a user in the database.  Replaces the original
+ *		 entry with the name, the list of check items, and the
+ *		 list of reply items which are supplied.
+ *
+ *************************************************************************/
+
+int
+user_update (name, user_check, user_reply)
+
+char           *name;
+VALUE_PAIR     *user_check;
+VALUE_PAIR     *user_reply;
+
+{
+	FILE           *oldfd;
+	FILE           *userfd;
+	char            buffer[256];
+	char            buffer1[256];
+	int             namelen;
+	int             mode;
+	static char    *func = "user_update";
+
+	dprintf(2, (LOG_AUTH, LOG_DEBUG, "%s: entered", func));
+
+	sprintf (buffer, "%s/%s", radius_dir, RADIUS_USERS);
+	sprintf (buffer1, "%s/%s", radius_dir, RADIUS_HOLD);
+
+	/* Move the user table to a temporary location */
+	if (rename (buffer, buffer1) != 0)
+	{
+		logit (LOG_DAEMON, LOG_ERR, "%s: Couldn't rename %s",
+			func, buffer);
+		return (-1);
+	}
+
+	/* Open the old user file (using the temporary name */
+	if ((oldfd = fopen (buffer1, "r")) == (FILE *) NULL)
+	{
+		logit (LOG_DAEMON, LOG_ERR, "%s: Couldn't open %s for reading",
+			func, buffer1);
+		exit (-9);
+	}
+
+	/* Open the new user file */
+	if ((userfd = fopen (buffer, "w")) == (FILE *) NULL)
+	{
+		logit (LOG_DAEMON, LOG_ERR, "%s: Couldn't open %s for writing",
+			func, buffer);
+		exit (-9);
+	}
+
+	mode = FIND_MODE_NAME;
+	namelen = strlen (name);
+
+	/* Copy the old to the new, only recreating the changed user */
+	while (fgets (buffer, sizeof (buffer), oldfd) != (char *) NULL)
+	{
+		if (mode == FIND_MODE_NAME)
+		{
+			if ((strncmp (buffer, name, namelen) == 0 &&
+			   (buffer[namelen] == ' ' || buffer[namelen] == '\t')))
+			{
+
+				/* Write our new information */
+				fprintf (userfd, "%s\t", name);
+				while (user_check != (VALUE_PAIR *) NULL)
+				{
+					fprint_attr_val (userfd, user_check);
+					if (user_check->next !=
+							(VALUE_PAIR *) NULL)
+					{
+						fprintf (userfd, ", ");
+					}
+					user_check = user_check->next;
+				}
+				fprintf (userfd, "\n\t");
+				while (user_reply != (VALUE_PAIR *) NULL)
+				{
+					fprint_attr_val (userfd, user_reply);
+					if (user_reply->next !=
+							(VALUE_PAIR *) NULL)
+					{
+						fprintf (userfd, ",\n\t");
+					}
+					user_reply = user_reply->next;
+				}
+				fprintf (userfd, "\n");
+				mode = FIND_MODE_SKIP;
+			}
+			else
+			{
+				fputs (buffer, userfd);
+			}
+		}
+		else if (mode == FIND_MODE_SKIP)
+		{
+			if (*buffer != ' ' && *buffer != '\t')
+			{
+				fputs (buffer, userfd);
+				mode = FIND_MODE_FLUSH;
+			}
+		}
+		else
+		{
+			fputs (buffer, userfd);
+		}
+	}
+	fclose (oldfd);
+	fclose (userfd);
+	return (0);
+} /* end of user_update () */
diff -rPu pppd.old/rad_util.c pppd/rad_util.c
--- pppd.old/rad_util.c	Thu Jan  1 03:00:00 1970
+++ pppd/rad_util.c	Wed May 22 17:54:30 1996
@@ -0,0 +1,178 @@
+/*
+ *
+ *	RADIUS   Remote Authentication Dial In User Service
+ *
+ *
+ *	Livingston Enterprises, Inc.
+ *	6920 Koll Center Parkway
+ *	Pleasanton, CA   94566
+ *
+ *	Copyright 1992 Livingston Enterprises, Inc.
+ *
+ *	Permission to use, copy, modify, and distribute this software for any
+ *	purpose and without fee is hereby granted, provided that this
+ *	copyright and permission notice appear on all copies and supporting
+ *	documentation, the name of Livingston Enterprises, Inc. not be used
+ *	in advertising or publicity pertaining to distribution of the
+ *	program without specific prior permission, and notice be given
+ *	in supporting documentation that copying and distribution is by
+ *	permission of Livingston Enterprises, Inc.
+ *
+ *	Livingston Enterprises, Inc. makes no representations about
+ *	the suitability of this software for any purpose.  It is
+ *	provided "as is" without express or implied warranty.
+ *
+ * [C] The Regents of the University of Michigan and Merit Network, Inc. 1992,
+ * 1993, 1994, 1995, 1996 All Rights Reserved
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies of the software and derivative works or modified versions thereof,
+ * and that both the copyright notice and this permission and disclaimer
+ * notice appear in supporting documentation.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE REGENTS OF THE
+ * UNIVERSITY OF MICHIGAN AND MERIT NETWORK, INC. DO NOT WARRANT THAT THE
+ * FUNCTIONS CONTAINED IN THE SOFTWARE WILL MEET LICENSEE'S REQUIREMENTS OR
+ * THAT OPERATION WILL BE UNINTERRUPTED OR ERROR FREE.  The Regents of the
+ * University of Michigan and Merit Network, Inc. shall not be liable for any
+ * special, indirect, incidental or consequential damages with respect to any
+ * claim by Licensee or any third party arising from use of the software.
+ *
+ *	Public entry points in this file:
+ *
+ *	get_ipaddr
+ *	good_ipaddr
+ *	list_free
+ *
+ */
+
+static char     sccsid[] =
+		"@(#)util.c 1.1 Copyright 1992 Livingston Enterprises Inc";
+
+static char     rcsid[] = "$Id: util.c,v 1.9 1996/05/21 15:16:01 web Exp $";
+
+#include	<sys/types.h>
+#include	<sys/socket.h>
+#include	<sys/time.h>
+#include	<netinet/in.h>
+#include	<arpa/inet.h>
+
+#include	<stdio.h>
+#include	<netdb.h>
+#include	<ctype.h>
+#include	<time.h>
+
+#include	"radius.h"
+
+/*************************************************************************
+ *
+ *	Function: get_ipaddr
+ *
+ *	Purpose: Return an IP address in host long notation from a host
+ *		 name or address in dot notation.
+ *
+ *************************************************************************/
+
+UINT4
+get_ipaddr (host)
+
+char           *host;
+
+{
+	struct hostent *hp;
+
+	if (good_ipaddr (host) == 0)
+	{
+		return ntohl(inet_addr (host));
+	}
+	else if ((hp = gethostbyname (host)) == (struct hostent *) NULL)
+	{
+		return ((UINT4) 0);
+	}
+	return ntohl((*(UINT4 *) hp->h_addr));
+} /* end of get_ipaddr () */
+
+/*************************************************************************
+ *
+ *	Function: good_ipaddr
+ *
+ *	Purpose: Check for valid IP address in standard dot notation.
+ *
+ *************************************************************************/
+
+int
+good_ipaddr (addr)
+
+char           *addr;
+
+{
+	int             dot_count;
+	int             digit_count;
+
+	if (addr == (char *) NULL)
+	{
+		return (-1);
+	}
+
+	dot_count = 0;
+	digit_count = 0;
+
+	while (*addr != '\0' && *addr != ' ')
+	{
+		if (*addr == '.')
+		{
+			dot_count++;
+			digit_count = 0;
+		}
+		else if (!isdigit (*addr))
+		{
+			dot_count = 5;
+		}
+		else
+		{
+			digit_count++;
+			if (digit_count > 3)
+			{
+				dot_count = 5;
+			}
+		}
+		addr++;
+	}
+	if (dot_count != 3)
+	{
+		return (-1);
+	}
+	else
+	{
+		return (0);
+	}
+} /* end of good_ipaddr () */
+
+/*************************************************************************
+ *
+ *	Function: list_free
+ *
+ *	Purpose: Release the memory used by a list of a/v pairs.
+ *
+ *************************************************************************/
+
+void
+list_free (pair)
+
+VALUE_PAIR     *pair;
+
+{
+	VALUE_PAIR     *next;
+
+	while (pair != (VALUE_PAIR *) NULL)
+	{
+		next = pair->next;
+		free (pair);
+		pair = next;
+	}
+	return;
+} /* end of list_free () */
diff -rPu pppd.old/radius.h pppd/radius.h
--- pppd.old/radius.h	Thu Jan  1 03:00:00 1970
+++ pppd/radius.h	Sat Dec 11 13:40:57 1999
@@ -0,0 +1,1137 @@
+#ifndef RADIUS_H
+#define RADIUS_H
+
+/*
+ *	RADIUS   Remote Authentication Dial In User Service
+ *
+ *	Livingston Enterprises, Inc.
+ *	6920 Koll Center Parkway
+ *	Pleasanton, CA   94566
+ *
+ *	Copyright 1992 Livingston Enterprises, Inc.
+ *
+ *	Permission to use, copy, modify, and distribute this software for any
+ *	purpose and without fee is hereby granted, provided that this
+ *	copyright and permission notice appear on all copies and supporting
+ *	documentation, the name of Livingston Enterprises, Inc. not be used
+ *	in advertising or publicity pertaining to distribution of the
+ *	program without specific prior permission, and notice be given
+ *	in supporting documentation that copying and distribution is by
+ *	permission of Livingston Enterprises, Inc.
+ *
+ *	Livingston Enterprises, Inc. makes no representations about
+ *	the suitability of this software for any purpose.  It is
+ *	provided "as is" without express or implied warranty.
+ *
+ * [C] The Regents of the University of Michigan and Merit Network, Inc. 1992,
+ * 1993, 1994, 1995, 1996 All Rights Reserved
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies of the software and derivative works or modified versions thereof,
+ * and that both the copyright notice and this permission and disclaimer
+ * notice appear in supporting documentation.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE REGENTS OF THE
+ * UNIVERSITY OF MICHIGAN AND MERIT NETWORK, INC. DO NOT WARRANT THAT THE
+ * FUNCTIONS CONTAINED IN THE SOFTWARE WILL MEET LICENSEE'S REQUIREMENTS OR
+ * THAT OPERATION WILL BE UNINTERRUPTED OR ERROR FREE.  The Regents of the
+ * University of Michigan and Merit Network, Inc. shall not be liable for any
+ * special, indirect, incidental or consequential damages with respect to any
+ * claim by Licensee or any third party arising from use of the software.
+ *
+ *	@(#)radius.h	1.3 1/20/93
+ *
+ *	$Id: radius.h,v 2.64 1996/06/19 18:16:23 web Exp $
+ */
+
+#include "conf.h"
+
+#define	COMMENT			'#'	/* comment char for config files */
+
+#define AUTH_VECTOR_LEN		16
+#define AUTH_PASS_LEN		64
+#define AUTH_ID_LEN		64
+#define AUTH_STRING_LEN		128	/* maximum of 253 */
+
+#define FILTER_LEN		16
+#define NAME_LENGTH		32
+#define	MAX_FSMID_LEN		20	/* Maximum length of %FSMID string */
+
+typedef struct pw_auth_hdr
+{
+	u_char          code;
+	u_char          id;
+	u_short         length;
+	u_char          vector[AUTH_VECTOR_LEN];
+	u_char          data[2];
+} AUTH_HDR;
+
+#define AUTH_HDR_LEN			20
+#define MAX_SECRET_LENGTH		16
+#define CHAP_VALUE_LENGTH		16
+
+#if !defined(PW_AUTH_UDP_PORT)
+#define PW_AUTH_UDP_PORT		1645
+#endif
+
+#if !defined(PW_ACCT_UDP_PORT)
+#define PW_ACCT_UDP_PORT		1646
+#endif
+
+#define PW_TYPE_STRING			0
+#define PW_TYPE_INTEGER			1
+#define PW_TYPE_IPADDR			2
+#define PW_TYPE_DATE			3
+#define PW_TYPE_OCTETS			4
+#define PW_TYPE_VENDOR			5
+
+/* standard RADIUS codes */
+
+#define	PW_ACCESS_REQUEST		1
+#define	PW_ACCESS_ACCEPT		2
+#define	PW_ACCESS_REJECT		3
+#define	PW_ACCOUNTING_REQUEST		4
+#define	PW_ACCOUNTING_RESPONSE		5
+#define	PW_ACCOUNTING_STATUS		6
+#define	PW_PASSWORD_REQUEST		7
+#define	PW_PASSWORD_ACK			8
+#define	PW_PASSWORD_REJECT		9
+#define	PW_ACCOUNTING_MESSAGE		10
+#define	PW_ACCESS_CHALLENGE		11
+#define	PW_STATUS_SERVER		12
+#define	PW_STATUS_CLIENT		13
+#define	PW_FORWARDING			216
+
+
+/* standard RADIUS attribute-value pairs */
+
+#define	PW_USER_NAME			1	/* string */
+#define	PW_USER_PASSWORD		2	/* string */
+#define	PW_CHAP_PASSWORD		3	/* string */
+#define	PW_NAS_IP_ADDRESS		4	/* ipaddr */
+#define	PW_NAS_PORT			5	/* integer */
+#define	PW_SERVICE_TYPE			6	/* integer */
+#define	PW_FRAMED_PROTOCOL		7	/* integer */
+#define	PW_FRAMED_IP_ADDRESS		8	/* ipaddr */
+#define	PW_FRAMED_IP_NETMASK		9	/* ipaddr */
+#define	PW_FRAMED_ROUTING		10	/* integer */
+#define	PW_FILTER_ID		        11	/* string */
+#define	PW_FRAMED_MTU			12	/* integer */
+#define	PW_FRAMED_COMPRESSION		13	/* integer */
+#define	PW_LOGIN_IP_HOST		14	/* ipaddr */
+#define	PW_LOGIN_SERVICE		15	/* integer */
+#define	PW_LOGIN_PORT			16	/* integer */
+#define	PW_OLD_PASSWORD			17	/* string */ /* deprecated */
+#define	PW_REPLY_MESSAGE		18	/* string */
+#define	PW_LOGIN_CALLBACK_NUMBER	19	/* string */
+#define	PW_FRAMED_CALLBACK_ID		20	/* string */
+#define	PW_EXPIRATION			21	/* date */ /* deprecated */
+#define	PW_FRAMED_ROUTE			22	/* string */
+#define	PW_FRAMED_IPX_NETWORK		23	/* integer */
+#define	PW_STATE			24	/* string */
+#define	PW_CLASS			25	/* string */
+#define	PW_VENDOR_SPECIFIC		26	/* string */
+#define	PW_SESSION_TIMEOUT		27	/* integer */
+#define	PW_IDLE_TIMEOUT			28	/* integer */
+#define	PW_TERMINATION_ACTION		29	/* integer */
+#define	PW_CALLED_STATION_ID		30	/* string */
+#define	PW_CALLING_STATION_ID		31	/* string */
+#define	PW_NAS_IDENTIFIER		32	/* string */
+#define	PW_PROXY_STATE			33	/* string */
+#define	PW_LOGIN_LAT_SERVICE		34	/* string */
+#define	PW_LOGIN_LAT_NODE		35	/* string */
+#define	PW_LOGIN_LAT_GROUP		36	/* string */
+#define	PW_FRAMED_APPLETALK_LINK	37	/* integer */
+#define	PW_FRAMED_APPLETALK_NETWORK	38	/* integer */
+#define	PW_FRAMED_APPLETALK_ZONE	39	/* string */
+#define	PW_CHAP_CHALLENGE		60	/* string */
+#define	PW_NAS_PORT_TYPE		61	/* integer */
+#define	PW_PORT_LIMIT			62	/* integer */
+#define	PW_LOGIN_LAT_PORT		63	/* string */
+
+/*	Accounting */
+
+#define	PW_ACCT_STATUS_TYPE		40	/* integer */
+#define	PW_ACCT_DELAY_TIME		41	/* integer */
+#define	PW_ACCT_INPUT_OCTETS		42	/* integer */
+#define	PW_ACCT_OUTPUT_OCTETS		43	/* integer */
+#define	PW_ACCT_SESSION_ID		44	/* string */
+#define	PW_ACCT_AUTHENTIC		45	/* integer */
+#define	PW_ACCT_SESSION_TIME		46	/* integer */
+#define	PW_ACCT_INPUT_PACKETS		47	/* integer */
+#define	PW_ACCT_OUTPUT_PACKETS		48	/* integer */
+#define	PW_ACCT_TERMINATE_CAUSE		49	/* integer */
+#define	PW_ACCT_MULTI_SESSION_ID	50	/* string */
+
+/*	Merit Experimental Extensions */
+
+/*	Temporary assignment for LOG AATV session logging */
+
+#define PW_LAS_START_TIME		145	/* integer */
+#define PW_LAS_CODE			146	/* integer */
+#define PW_LAS_DURATION			147	/* integer */
+#define PW_LOCAL_DURATION		148	/* integer */
+
+#define	PW_SERVICE_CLASS		149	/* string */
+#define	PW_PORT_ENTRY			150	/* string */
+#define	PW_PROXY_ACTION			211	/* string */
+#define	PW_TOKEN			213	/* string */
+#define	PW_HUNTGROUP_NAME		221	/* string */
+#define	PW_USER_ID			222	/* string */
+#define	PW_USER_REALM			223	/* string */
+
+/*	Configuration Only Attributes (for check-items) */
+
+#define	CI_COMMENT			1024	/* string */
+#define	CI_XVALUE			1025	/* integer */
+#define	CI_XSTRING			1026	/* string */
+#define	CI_AUTHENTICATION_TYPE		1027	/* integer */
+#define	CI_PROHIBIT			1028	/* integer */
+#define	CI_USER_CATEGORY		1029	/* string */
+#define	CI_GROUP_NAME			1030	/* string */
+#define	CI_ENCRYPTED_PASSWORD		1031	/* string */
+#define	CI_EXPIRATION			1032	/* date */
+#define	CI_USER_PASSWORD		1033	/* string */
+#define	CI_SIMULTANEOUS_USE		1034	/* integer */
+#define	CI_SERVER_NAME			1035	/* string */
+
+/*	Integer Translations */
+
+/*	SERVICE TYPES	*/
+
+#define	PW_LOGIN			1
+#define	PW_FRAMED			2
+#define	PW_CALLBACK_LOGIN		3
+#define	PW_CALLBACK_FRAMED		4
+#define	PW_OUTBOUND_USER		5
+#define	PW_ADMINISTRATIVE_USER		6
+#define	PW_SHELL_USER			7
+#define PW_AUTHENTICATE_ONLY		8
+#define PW_CALLBACK_ADMIN_USER		9
+
+/*	FRAMED PROTOCOLS	*/
+
+#define	PW_PPP				1
+#define	PW_SLIP				2
+#define	PW_ARA				3
+#define	PW_GANDALF			4
+
+/*	FRAMED ROUTING VALUES	*/
+
+#define	PW_NONE				0
+#define	PW_BROADCAST			1
+#define	PW_LISTEN			2
+#define	PW_BROADCAST_LISTEN		3
+
+/*	FRAMED COMPRESSION TYPES	*/
+
+#define	PW_VAN_JACOBSON_TCP_IP		1
+#define	PW_IPX_HEADER_COMPRESSION	2
+
+/*	LOGIN SERVICES	*/
+
+#define	PW_TELNET			0
+#define	PW_RLOGIN			1
+#define	PW_TCP_CLEAR			2
+#define	PW_PORTMASTER			3
+#define	PW_LAT				4
+
+/*	TERMINATION ACTIONS	*/
+
+#define	PW_DEFAULT			0
+#define	PW_RADIUS_REQUEST		1
+
+/*	AUTHENTICATION TYPES */
+
+#define AA_NONE		0	/* This is not a valid user id entry */
+#define AA_UNIX		1	/* Use local Unix password file */
+#define AA_AKRB		2	/* AFS Kerberos type authentication */
+#define AA_MKRB		3	/* MIT Kerberos type authentication */
+#define AA_RAD		4	/* Pass to remote RADIUS server */
+#define AA_MNET		5	/* Do Merit specific authentication */
+#define AA_KCHAP	6	/* Kerberos CHAP authentication */
+#define AA_TACACS	7	/* Encrypted TACACS authentication */
+#define AA_REALM	8	/* Find given realm in authfile */
+#define AA_LOCAL	9
+#define AA_FILE		10	/* ID/PW list in a file */
+#define	AA_SCRIPT	11	/* External script */
+
+#define PW_AUTH_MAX	11	/* Highest authentication type */
+
+/*	PROHIBIT PROTOCOL  */
+
+#define PW_DUMB		0	/* 1 and 2 are defined in FRAMED PROTOCOLS */
+#define PW_AUTH_ONLY	3
+#define PW_ALL		255
+
+/*	ACCOUNTING STATUS TYPES    */
+
+#define PW_STATUS_START		1
+#define PW_STATUS_STOP		2
+#define PW_STATUS_ALIVE		3
+#define PW_STATUS_MODEM_START	4
+#define PW_STATUS_MODEM_STOP	5
+#define PW_STATUS_CANCEL	6
+#define PW_ACCOUNTING_ON	7
+#define PW_ACCOUNTING_OFF	8
+
+/*	ACCOUNTING TERMINATION CAUSES    */
+
+#define PW_USER_REQUEST		1
+#define PW_LOST_CARRIER		2
+#define PW_LOST_SERVICE		3
+#define PW_ACCT_IDLE_TIMEOUT	4
+#define PW_ACCT_SESSION_TIMEOUT	5
+#define PW_ADMIN_RESET		6
+#define PW_ADMIN_REBOOT		7
+#define PW_PORT_ERROR		8
+#define PW_NAS_ERROR		9
+#define PW_NAS_REQUEST		10
+#define PW_NAS_REBOOT		11
+#define PW_PORT_UNNEEDED	12
+#define PW_PORT_PREEMPTED	13
+#define PW_PORT_SUSPENDED	14
+#define PW_SERVICE_UNAVAILABLE	15
+#define PW_CALLBACK		16
+#define PW_USER_ERROR		17
+#define PW_HOST_REQUEST		18
+
+/*	NAS PORT TYPES    */
+
+#define PW_ASYNC		0
+#define PW_SYNC			1
+#define PW_ISDN_SYNC		2
+#define PW_ISDN_SYNC_V120	3
+#define PW_ISDN_SYNC_V110	4
+
+/* Default Database File Names */
+
+#ifndef RADIUS_DIR
+#define RADIUS_DIR		"/var/adm/raddb"
+#endif
+
+#ifndef RADACCT_DIR
+#define RADACCT_DIR		"/var/adm/radacct"
+#endif
+
+#ifndef RADIUS_SCRIPT
+#define RADIUS_SCRIPT		"/usr/local/adm/radauth"
+#endif
+
+#ifndef RADIUS_SCRIPT_ACCT
+#define RADIUS_SCRIPT_ACCT	"/usr/local/adm/radacct"
+#endif
+
+/*
+ *	Note:	To change where these files go, do not change the #defines
+ *		below, instead change the RADIUS_DIR #define above.
+ */
+
+#define RADIUS_DICTIONARY	"dictionary"
+#define RADIUS_CLIENTS		"clients"
+#define RADIUS_USERS		"users"
+#define RADIUS_HOLD		"holdusers"
+#define RADIUS_LOG		"/var/adm/radius.log"
+#define RADIUS_AUTH		"authfile"
+#define RADIUS_PID		"radiusd.pid"
+#define RADIUS_FSM		"radius.fsm"
+#define RADIUS_DEBUG		"/tmp/radius.debug"
+
+#ifndef RADIUS_COMPRESS
+#define RADIUS_COMPRESS		"/usr/bin/gzip"  /* might be gzip, etc. */
+#endif
+
+#ifndef RADIUS_LOCALSERVER
+#define RADIUS_LOCALSERVER	"localserver"
+#endif
+
+#ifndef DEFAULT_REALM
+#define DEFAULT_REALM		"DEFAULT"
+#endif
+
+#ifndef NULL_REALM
+#define NULL_REALM		"NULL"
+#endif
+
+/* Server data structures */
+
+typedef struct dict_attr
+{
+	char              name[NAME_LENGTH + 1];	/* attribute name */
+	int               value;			/* attribute index */
+	int               type;				/* string, int, etc. */
+	struct dict_attr *next;
+} DICT_ATTR;
+
+typedef struct dict_value
+{
+	char               attrname[NAME_LENGTH +1];
+	char               name[NAME_LENGTH + 1];
+	int                value;
+	struct dict_value *next;
+} DICT_VALUE;
+
+typedef struct value_pair
+{
+	char               name[NAME_LENGTH + 1];
+	int                attribute;
+	int                type;
+	UINT4              lvalue;
+	char               strvalue[AUTH_STRING_LEN + 1];
+	struct value_pair *next;
+} VALUE_PAIR;
+
+typedef struct auth_req
+{
+	UINT4             ipaddr;           /* IP address of requestor */
+	u_short           udp_port;         /* UDP reply socket of requestor */
+	u_char            id;               /* Original request seq. number */
+	u_char            code;             /* Type of RADIUS packet */
+	u_char            vector[AUTH_VECTOR_LEN];
+	char             *secret;
+	char             *file_pfx;
+	char             *realm_filter;
+	u_char            ttl;              /* Global queue time-to-live secs */
+	u_char            timer;            /* General utility timer */
+	u_char            reply_id;         /* RADIUS-to-RADIUS seq. number */
+	u_char            retry_cnt;        /* Counter for duplicate requests */
+	u_char            state;            /* State of current request */
+	u_char            sws;              /* Switches, flags, etc. */
+	int               result;           /* Result of previous action */
+	int               cur_count;        /* Original number request pairs */
+	struct aatv      *fsm_aatv;         /* Pointer to current FSM action */
+	struct aatv      *direct_aatv;      /* Pointer to actual action */
+	struct event_ent *event_q;          /* Pointer to active event queue */
+	struct auth_req  *next;             /* Global request queue link */
+	VALUE_PAIR       *request;          /* Original client a/v pairs */
+	VALUE_PAIR       *cur_request;      /* Represents current a/v pairs */
+	VALUE_PAIR       *user_check;       /* List of users file check items */
+} AUTH_REQ;
+
+typedef struct event_ent
+{
+	struct event_ent *next;
+	AUTH_REQ         *auth_head; /* pointer back to the authreq structure */
+	struct aatv      *fsm_aatv;  /* record action from FSM table */
+	struct aatv      *sub_aatv;  /* record action when request was issued */
+	u_char           *packet;    /* copy of request packet which was sent */
+	int               len;       /* length of packet */
+	pid_t             pid;       /* fork type: pid, socket type: == zero */
+	struct sockaddr_in sin;      /* socket info for packet re-sending */
+	int               evalue;    /* AATV act_func integer argument */
+	u_char            state;     /* state in which the request was issued */
+	char              action[NAME_LENGTH+1]; /* "cmd" arg to radius_send */
+	char              estring[AUTH_ID_LEN]; /* AATV act_func string arg */
+} EVENT_ENT;
+
+typedef struct user_ent
+{
+	struct user_ent *next;
+	char            *name;
+	VALUE_PAIR      *check;
+	VALUE_PAIR      *reply;
+} USER_ENTRY;
+
+#ifdef  MERIT_LAS
+typedef struct lasrealm_ent *LAS_REALM;
+#endif	/* MERIT_LAS */
+
+typedef struct auth_ent
+{
+	struct auth_ent *next;
+	char            *name;
+	struct auth_ent *parent;
+	int              prot;
+	int              type;
+	char            *host;
+	char            *filter;
+#ifdef  MERIT_LAS
+	LAS_REALM        las_realm;
+#endif	/* MERIT_LAS */
+} AUTH_ENTRY;
+
+/* The following must match the beginning of the auth_ent structure */
+typedef struct auth_aent
+{
+	struct auth_ent *next;
+	char            *name;
+	struct auth_ent *parent;
+} AUTH_ALIAS_ENTRY;
+
+typedef struct linklist_entry
+{
+	struct linklist_entry *next;	/* pointer to next entry in list */
+} LINKLIST_ENT;
+
+#define	numbof(X)	(sizeof(X)/sizeof(X[0]))
+
+typedef struct name_list
+{
+	struct name_list  *next;
+	char              *name;
+	u_char             flag;
+	u_short            num;
+} NAME_LIST;
+
+/*	Binary port entry structure used in Port-Entry attribute */
+
+#define	PORT_ENTRY_VERSION	0	/* increase if change structure here */
+
+typedef struct bin_port_ent
+{
+	u_char             version;	/* be sure to use PORT_ENTRY_VERSION */
+	u_char             port_source; /* zero => was HGAS, one => otherwise */
+	time_t             start_time;	/* start time of session on this port */
+	UINT4              port_nbr;	/* port number of this session */
+	UINT4              duration;	/* session length (seconds) */
+} BIN_PORT_ENT;
+
+/*
+ * Use the following to specify default "realm" names to use for
+ * authentication-type entries of RADIUS or TACACS that may be
+ * configured in the "users" file.  May be configured globally
+ * in the Makefile or changed in the authfile on a running server.
+ */
+
+#ifndef DEFAULT_RADIUS_SERVER
+#define DEFAULT_RADIUS_SERVER ""
+#endif
+
+#ifndef DEFAULT_TACACS_SERVER
+#define DEFAULT_TACACS_SERVER ""
+#endif
+
+/******************************************************************
+ *
+ *      PW_PROTTYPE & PW_PROTTYPES - define authentication protocol allowed
+ *                                   for particular realm entry in authfile.
+ *
+ *      The PW_PROTTYPE value is stored in the auth_ent.prot field.
+ *      The PW_PROTTYPE value corresponds to the order of PW_PROTTYPES.
+ *
+ *****************************************************************/
+
+#define PW_PROTTYPE_DFLT	0	/* Use this entry for any protocol */
+#define PW_PROTTYPE_CHAP	1	/* Entry is for CHAP style authent. */
+#define PW_PROTTYPE_PW		2	/* Entry is for id/pw style authent. */
+
+#define PW_PROTTYPES_DFLT	"DEFAULT"
+#define PW_PROTTYPES_CHAP	"CHAP"
+#define PW_PROTTYPES_PW		"PW"
+
+typedef struct file_list
+{
+	struct file_list       *next;
+	char                   *prefix;
+	USER_ENTRY             *user_list;
+	AUTH_ENTRY             *auth_list;
+} FILE_LIST;
+
+typedef struct ip_address
+{
+	struct ip_address *next;
+	struct in_addr     ipaddr;
+} IP_ADDRESS;
+
+typedef struct dns_name
+{
+	struct dns_name   *next;
+	u_char             type;	/* 0 = official name, 1 = alias */
+	char               name[1];
+} DNS_NAME;
+
+typedef struct client_ent
+{
+	struct client_ent *next;
+	IP_ADDRESS        *addrs;
+	char              *secret;
+	char              *prefix;
+	char              *hostname;
+	DNS_NAME          *names;
+	time_t             expire_time;
+	enum {CE_DNS, CE_NUMERIC, CE_OURADDR} type;
+} CLIENT_ENTRY;
+
+#define dprintf(lev, args)	{ if (debug_flag >= lev) logit args; }
+#define	ddumpx(lev, args)	{ if (debug_flag > lev) dumpit args; }
+
+/* 	Define return codes from "SendServer" utility */
+
+#define BADRESP_RC	-2
+#define ERROR_RC	-1
+#define OK_RC		0
+#define TIMEOUT_RC	1
+
+typedef struct send_data /* Used to pass information to sendserver() function */
+{
+	u_char          code;		/* RADIUS packet code */
+	u_char          seq_nbr;	/* Packet sequence number */
+	char           *user_name;
+	char           *password;	/* Cleartext user password */
+	u_char          ustype;		/* Service-Type attribute */
+	u_char          fptype;		/* Framed-Protocol attribute */
+	char           *server;		/* Name/addrress of RADIUS server */
+	int             svc_port;	/* RADIUS protocol destination port */
+	int             timeout;	/* Session timeout in seconds */
+	UINT4           client_id;	/* IP address of client */
+	int             port_num;	/* Port number on client */
+	char           *user_file;	/* Users style file of a/v pairs */
+	char           *group;
+	VALUE_PAIR     *send_pairs;     /* More a/v pairs to send */
+	VALUE_PAIR    **receive_pairs;  /* Where to place received a/v pairs */
+} SEND_DATA;
+
+/*
+ *	Handle older syslog versions, too!
+ */
+
+#ifndef	LOG_CONS
+#define	LOG_DAEMON		0
+#define	LOG_AUTH		0
+#endif
+
+#define	MGMT_POLL_SECRET	"Hardlyasecret"
+#define	MAX_REQUESTS		128
+#define	MAX_REQUEST_TIME	30	/* Lifetime of a request */
+#define	CLEANUP_DELAY		5	/* Hold onto old requests this long */
+#define	DEFAULT_INETD_TIMEOUT	15	/* Fifteen minutes by default */
+#define	DEFAULT_TIMER_VALUE	3	/* Three seconds by default */
+#define	ADDRESS_AGING		60*60	/* One hour by default */
+#define	DFLT_TACACS_UDP_PORT	49	/* Default TACACS server port */
+#define	SESS_ID_LEN		8	/* session id length */
+#define SECONDS_PER_DAY		86400
+#define TRUNCATION_DAY		7   /* Sunday is zero (0), daily is seven (7) */
+#define	DNS_SLEEP		100	/* Time which DNS sub-process sleeps. */
+
+typedef enum				/* error code */
+{
+  EC_OK,				/* no error */
+  EC_INTERNAL,				/* internal error */
+  EC_CONFIG,				/* configuration error */
+  EC_NO_MEMORY,				/* out of memory */
+  EC_CREATE_FILE,			/* error creating file */
+  EC_NO_TOKEN,				/* no token available */
+  EC_NO_PORTS,				/* no ports available for guests */
+  EC_TOO_MANY_SESSIONS,			/* user has too many sessions */
+  EC_ABS_FAILURE,                       /* ABS failed (with message) */
+  EC_NO_BALANCE,			/* error querying for balance */
+  EC_BAD_BALANCE			/* balance too low */
+} ERRORCODE;
+
+typedef enum				/* accounting code */
+{
+	AC_ERROR	= -1,		/* no accounting code */
+	AC_NORMAL,			/* normal disconnect */
+	AC_REJECT,			/* rejected by this server */
+	AC_CANCEL,			/* access rejected by someone */
+	AC_NOCONFIRM,			/* no confirmation */
+	AC_OVERTIME,			/* session over maximum time allowed */
+	AC_UNKNOWN,			/* session ended for unknown reason */
+	AC_NOTOKEN,			/* rejected because no token */
+	AC_NOTLOCAL,			/* session not local */
+	AC_SUSPEND,			/* session suspended */
+	AC_FAILED,			/* authentication failed */
+	AC_AUTHORIZED,			/* session authorized (for stats) */
+	AC_NASREBOOT,			/* released due to NAS reboot */
+	AC_REMOTE,			/* remote session, failed to forward */
+	AC_NUMBOFCODE			/* number of accounting code */
+} ACCTCODE;
+
+#ifndef PROTO
+#ifdef __STDC__
+#define PROTO(x) x
+#else
+#define PROTO(x) ()
+#define const
+#endif /* !__STDC__ */
+#endif /* !PROTO */
+
+union action_u
+{
+	struct aatv    *aatv;	/* points to the id field of an AATV */
+	char           *proxy;	/* pointer to a Proxy-Action string */
+} UACTION;
+
+/*	Define event structure (for events generated by AATV recv functions */
+
+typedef struct ev
+{
+	u_char          state;
+	union action_u  a;
+	int             isproxy;	/* set to one if action "a" is proxy */
+	int             value;
+	char            xstring[AUTH_ID_LEN];
+} EV;
+
+/*	Define aatvfunc_type codes */ 
+
+#define	AA_DIRECT	0	/* Function gives direct reply */
+#define	AA_SOCKET	1	/* Deferred reply returned on socket */
+#define	AA_FORK		2	/* Spawn a process to wait for reply */
+#define	AA_FREPLY	3	/* Fork & get reply on server socket */
+
+typedef struct aatv
+{
+	u_char       id[NAME_LENGTH + 1];
+	char         authen_type; /* a -1 value indicates built-in AATV types */
+	u_char       aatvfunc_type;
+	void       (*init) PROTO((struct aatv *));
+	int        (*timer) PROTO((void));
+	int        (*act_func) PROTO((AUTH_REQ *, int, char *));
+	AUTH_REQ * (*recv) PROTO((struct sockaddr_in *, UINT4, u_int, EV *));
+	void       (*cleanup) PROTO((void));
+	UINT4        sockfd;
+} AATV, *AATVPTR;
+
+extern AATV    *authtype_tv[];
+
+#ifdef  MERIT_LAS
+extern AATVPTR  rad_log_aatv;		/* For logging (selector) */
+extern AATVPTR  rad_log_all_aatv;	/* For logging (debugging) */
+extern AATVPTR  rad_log_brief_aatv;	/* For logging (logging) */
+extern AATVPTR  rad_log_old_aatv;	/* For logging (logging) */
+extern AATVPTR  rad_log_v1_0_aatv;	/* For logging (logging) */
+extern AATVPTR  rad_log_v1_1_aatv;	/* For logging (logging) */
+extern AATVPTR  rad_log_v2_0_aatv;	/* For logging (logging) */
+extern AATVPTR  rad_log_v2_1_aatv;	/* For logging (logging) */
+#endif	/* MERIT_LAS */
+
+/*	Specify all authentication/authorization transfer vectors here. */
+
+extern AATVPTR	rad_realm_aatv;		/* Needed for authtype = realm */
+extern AATVPTR	rad_2rad_aatv;		/* Authtype = Radius */
+extern AATVPTR	rad_tacs_aatv;		/* Authtype = TACACS */
+extern AATVPTR	rad_unix_aatv;		/* Authtype = Unix-pw */
+extern AATVPTR	rad_kchp_aatv;		/* Authtype = KCHAP */
+extern AATVPTR	rad_mnet_aatv;		/* Authtype = mnet */
+extern AATVPTR	rad_akrb_aatv;		/* Authtype = akerb */
+extern AATVPTR	rad_mkrb_aatv;		/* Authtype = mkerb */
+extern AATVPTR  rad_script_aatv;        /* Authtype = SCRIPT */
+extern AATVPTR	rad_file_aatv;		/* Authtype = File */
+extern AATVPTR	rad_authen_aatv;	/* Authentication begins here */
+extern AATVPTR	rad_passwd_aatv;	/* Used for changing passwords */
+
+#ifdef  MERIT_HUNTGROUP
+#include	"huntgroup.h"
+#define EN_HGAS1		"HGAS1"
+#define EN_HGAS2		"HGAS2"
+#define EN_HGAS3		"HGAS3"
+#define EN_HGAS4		"HGAS4"
+#define EN_BACCT		"BACCT"
+extern AATVPTR	rad_hgas1_aatv;		/* Hg Authorization begins here */
+extern AATVPTR	rad_hgas2_aatv;		/* Hg Authorization continues here */
+extern AATVPTR	rad_hgas3_aatv;		/* Hg Accounting begins here */
+extern AATVPTR	rad_hgas4_aatv; 	/* Hg Accounting continues here */
+extern AATVPTR	rad_hgasrmt_aatv;	/* Hg forwarding to remote server */
+extern AATVPTR	rad_hgacctrmt_aatv;	/* Hg accounting origination */
+extern AATVPTR	rad_hgaslog_aatv;	/* Hg logging action (for HGAS1) */
+
+#ifdef  MERIT_HUNTGROUP_DAC
+extern AATVPTR	rad_hgdac1_aatv;	/* Hg DAC policy begins here */
+extern AATVPTR	rad_hgdac2_aatv;	/* Hg DAC policy continues here */
+extern AATVPTR	rad_hgdac3_aatv;	/* Hg DAC accounting begins here */
+#define DACAATVS ,&rad_hgdac1_aatv,&rad_hgdac2_aatv,&rad_hgdac3_aatv
+#else	/* MERIT_HUNTGROUP_DAC */
+#define DACAATVS
+#endif	/* MERIT_HUNTGROUP_DAC */
+
+#ifdef  MERIT_HUNTGROUP_SHP
+extern AATVPTR	rad_hgshp1_aatv;	/* Hg SHP policy begins here */
+extern AATVPTR	rad_hgshp2_aatv;	/* Hg SHP policy continues here */
+extern AATVPTR	rad_hgshp3_aatv;	/* Hg SHP accounting begins here */
+#define SHPAATVS ,&rad_hgshp1_aatv,&rad_hgshp2_aatv,&rad_hgshp3_aatv
+#else	/* MERIT_HUNTGROUP_SHP */
+#define SHPAATVS
+#endif	/* MERIT_HUNTGROUP_SHP */
+
+#define HGAATVS	,&rad_hgas1_aatv,&rad_hgas2_aatv,&rad_hgas3_aatv,&rad_hgas4_aatv,&rad_hgasrmt_aatv,&rad_hgaslog_aatv,&rad_hgacctrmt_aatv DACAATVS SHPAATVS
+#else	/* MERIT_HUNTGROUP */
+#define HGAATVS
+#define EN_HGAS1		""
+#define EN_HGAS2		""
+#define EN_HGAS3		""
+#define EN_HGAS4		""
+#define EN_BACCT		""
+#endif	/* MERIT_HUNTGROUP */
+
+#ifdef  MERIT_ORGANIZATION
+#include	"oas.h"
+#define EN_OAS			"OAS"
+#define EN_OAS_ACCT		"OAS_ACCT"
+extern AATVPTR	rad_oas_aatv;		/* Org Authorization begins here */
+extern AATVPTR	rad_oasrem_aatv;	/* Org Authorization remote stuff */
+extern AATVPTR	rad_oasloc_aatv;	/* Org Authorization local stuff */
+extern AATVPTR	oas_acct_aatv;		/* Org Accounting begins here */
+#define OASAATVS ,&rad_oas_aatv,&rad_oasrem_aatv,&rad_oasloc_aatv,&oas_acct_aatv
+#else	/* MERIT_ORGANIZATION */
+#define OASAATVS
+#define EN_OAS			""
+#define EN_OAS_ACCT		""
+#endif	/* MERIT_ORGANIZATION */
+
+#ifdef  MERIT_LAS
+#include	"las.h"
+#define EN_LAS			"AUTHENTICATE"
+#define EN_LAS_ACCT		"LAS_ACCT"
+extern AATVPTR	rad_las_aatv;		/* Local authorization */
+extern AATVPTR	las_auth_subaatv;	/* Generic LAS authorization */
+extern AATVPTR	las_acct_subaatv;	/* Generic LAS accounting */
+extern AATVPTR	las_acct_aatv;		/* LAS accounting */
+
+#ifdef	LAS_NO_HGAS
+#define	LASCPAATV
+#else	/* LAS_NO_HGAS */
+extern AATVPTR	lascp_aatv;		/* LAS synchronizing */
+#define	LASCPAATV	,&lascp_aatv
+#endif	/* LAS_NO_HGAS */
+
+#ifdef UOFM_LAS
+#include	"umlas.h"
+extern AATVPTR  las_um_aatv;		/* U of M LAS */
+#define LASAATVS  ,&las_auth_subaatv,&las_acct_subaatv,&las_um_aatv, \
+		&rad_las_aatv,&las_acct_aatv LASCPAATV
+#else	/* UOFM_LAS */
+#define LASAATVS  ,&las_auth_subaatv,&las_acct_subaatv, \
+		&rad_las_aatv,&las_acct_aatv LASCPAATV
+#endif	/* UOFM_LAS */
+#else	/* MERIT_LAS */
+#define LASAATVS
+#define EN_LAS			""
+#define EN_LAS_ACCT		""
+#endif	/* MERIT_LAS */
+
+#define AUTHENAATVS	&rad_realm_aatv,   &rad_unix_aatv,   &rad_2rad_aatv, \
+			&rad_tacs_aatv,    &rad_kchp_aatv,   &rad_mnet_aatv, \
+			&rad_akrb_aatv,    &rad_mkrb_aatv,   &rad_file_aatv, \
+			&rad_authen_aatv,  &rad_passwd_aatv, &rad_script_aatv
+
+
+#define AATVS	AUTHENAATVS HGAATVS OASAATVS LASAATVS
+
+/*
+ *	Event names (EN_*) in RADIUS   ###   see the NOTE in enum_event()
+ */
+
+#define EN_NAK			"NAK"
+#define EN_ACK			"ACK"
+#define EN_ERROR		"ERROR"
+#define EN_WAIT			"WAIT"
+#define EN_FATAL		"FATAL"
+#define EN_DUP_REQ		"DUP"
+#define EN_TIMER		"TIMER"
+#define EN_TIMEOUT		"TIMEOUT"
+#define EN_ABORT		"ABORT"
+#define EN_NEW_AUTHEN		"AUTHEN"
+#define EN_NEW_ACCT		"ACCT"
+#define EN_NEW_PASSWD		"PASSWD"
+#define EN_RE_ACCESS		"REACCESS"
+#define EN_ACC_CHAL		"ACC_CHAL"
+#define EN_MGT_POLL		"MGT_POLL"
+#define EN_AUTH_ONLY		"AUTH_ONLY"
+#define EN_ACCT_START		"ACCT_START"
+#define EN_ACCT_STOP		"ACCT_STOP"
+#define EN_ACCT_ALIVE		"ACCT_ALIVE"
+#define EN_ACCT_MODEM_START	"ACCT_MSTART"
+#define EN_ACCT_MODEM_STOP	"ACCT_MSTOP"
+#define EN_ACCT_CANCEL		"ACCT_CANCEL"
+#define EN_RC1			"RC1"
+#define EN_RC2			"RC2"
+#define EN_RC3			"RC3"
+#define EN_RC4			"RC4"
+#define EN_RC5			"RC5"
+#define EN_RC6			"RC6"
+#define EN_RC7			"RC7"
+#define EN_RC8			"RC8"
+#define EN_RC9			"RC9"
+#define EN_RC10			"RC10"
+#define EN_RC11			"RC11"
+#define EN_RC12			"RC12"
+#define EN_RC13			"RC13"
+#define EN_RC14			"RC14"
+#define EN_RC15			"RC15"
+#define EN_RC16			"RC16"
+#define EN_RC17			"RC17"
+#define EN_RC18			"RC18"
+#define EN_RC19			"RC19"
+#define EN_RC20			"RC20"
+#define EN_RC21			"RC21"
+
+/*
+ *	Event numbers in RADIUS   ###   see the NOTE in enum_event()
+ */
+typedef enum
+{
+	EV_NAK			= -1,
+	EV_ACK			= 0,
+	EV_ERROR		= 1,
+	EV_WAIT			= 2,
+	EV_FATAL		= 3,
+	EV_DUP_REQ		= 4,
+	EV_TIMER		= 5,
+	EV_TIMEOUT		= 6,
+	EV_ABORT		= 7,
+
+	/* arbitrary return codes from AATV action functions */
+
+	EV_RC1			= 8,
+	EV_RC2			= 9,
+	EV_RC3			= 10,
+	EV_RC4			= 11,
+	EV_RC5			= 12,
+	EV_RC6			= 13,
+	EV_RC7			= 14,
+	EV_RC8			= 15,
+	EV_RC9			= 16,
+	EV_RC10			= 17,
+	EV_RC11			= 18,
+	EV_RC12			= 19,
+	EV_RC13			= 20,
+	EV_RC14			= 21,
+	EV_RC15			= 22,
+	EV_RC16			= 23,
+	EV_RC17			= 24,
+	EV_RC18			= 25,
+	EV_RC19			= 26,
+	EV_RC20			= 27,
+	EV_RC21			= 28
+} EVENT;
+
+/* Request type events */
+
+#define	EV_NEW_AUTHEN		EV_RC1
+#define	EV_NEW_ACCT		EV_RC2
+#define	EV_NEW_PASSWD		EV_RC3
+#define	EV_RE_ACCESS		EV_RC4
+#define	EV_ACC_CHAL		EV_RC5
+#define	EV_MGT_POLL		EV_RC6
+#define	EV_AUTH_ONLY		EV_RC7
+#ifdef  MERIT_HUNTGROUP
+#define	EV_HGAS1		EV_RC8
+#define	EV_HGAS2		EV_RC9
+#define	EV_HGAS3		EV_RC10
+#define	EV_BACCT		EV_RC11
+#else	/* MERIT_HUNTGROUP */
+#define	EV_HGAS1		EV_ACK
+#define	EV_HGAS2		EV_ACK
+#define	EV_HGAS3		EV_ACK
+#define	EV_BACCT		EV_ACK
+#endif	/* MERIT_HUNTGROUP */
+#define EV_ACCT_START		EV_RC12
+#define EV_ACCT_STOP		EV_RC13
+#define EV_ACCT_ALIVE		EV_RC14
+#define EV_ACCT_MODEM_START	EV_RC15
+#define EV_ACCT_MODEM_STOP	EV_RC16
+#define EV_ACCT_CANCEL		EV_RC17
+#ifdef  MERIT_ORGANIZATION
+#define	EV_OAS			EV_RC18
+#define	EV_OAS_ACCT		EV_RC19
+#else	/* MERIT_ORGANIZATION */
+#define	EV_OAS			EV_ACK
+#define	EV_OAS_ACCT		EV_ACK
+#endif	/* MERIT_ORGANIZATION */
+#ifdef  MERIT_LAS
+#define	EV_LAS			EV_RC20
+#define	EV_LAS_ACCT		EV_RC21
+#else	/* MERIT_LAS */
+#define	EV_LAS			EV_ACK
+#define	EV_LAS_ACCT		EV_ACK
+#endif	/* MERIT_LAS */
+
+typedef enum		/* Typedef for second add_string() argument */
+{
+	ASIS		= 0x0000,	/* No conversion on string */
+	ASLC		= 0x0001,	/* Store as lower case sting */
+	FINDONLY	= 0x0002	/* Find string only */
+} AS_CONVERT;
+
+/*
+ *	The finite state machine (FSM) table is laid out as follows:
+ *
+ *	state0:
+ *		event01         aatv01          nextstate01
+ *		event02         aatv02          nextstate02
+ *		...
+ *	state1:
+ *		event11         aatv11          nextstate11
+ *		...
+ */
+
+#define NUMSTATES	32	/* initial maximum number of states */
+
+#define ST_INIT		0	/* initial state */
+
+#define ST_RESERVED	240	/* beginning of reserved state range */
+#define ST_SEEN		241	/* flag for state seen before being defined */
+#define ST_DEFINED	242	/* flag for state definition */
+
+#define ST_RECV		251	/* to indicate state which receives requests */
+#define ST_HOLD		252	/* to indicate dead requests */
+#define ST_SAME		253	/* for default action table */
+#define ST_ANY		254	/* for default action table */
+#define ST_END		255	/* end of FSM table */
+
+typedef struct statelist        /* list of all state names */
+{
+	int        maxst;	/* capacity of this list */
+	int        nst;		/* number of states already there */
+	NAME_LIST *states;	/* list of states found in the config file */
+} STATELIST;
+
+typedef struct fsm_entry	/* The Finite State Machine an array of these */
+{
+	struct fsm_entry *next;		/* list of entries for this state */
+	EV                event;	/* (state.action.event) 3-tuple */
+	AATV             *action;	/* what AATV (action) to invoke */
+	int               xvalue;	/* miscellaneous integer from FSM */
+	char             *xstring;	/* miscellaneous string from FSM */
+	u_char            next_state;	/* the next state to visit */
+} FSM_ENT;
+
+typedef struct prun_rule /* Pruning data structure (from RADIUS DRAFT RFC) */
+{
+	int               value;	/* this is the attribute value */
+	int               flags;	/* inclusive OR of PRUN_FLG values */
+	int               count;	/* how many the RFC says to allow */
+} PRUN_RULE;
+
+typedef struct prun_list
+{
+	char              vendor[AUTH_ID_LEN + 1];
+	PRUN_RULE        *rules;
+	struct prun_list *next;
+} PRUN_LIST;
+
+#define	PRUN_FLG1	1	/* this attribute allowable in Access_Accept */
+#define	PRUN_FLG2	2	/* this attribute allowable in Access_Reject */
+
+#define AR_NO_LOG	0x01		    /* sws: Suppress logging flag */
+#define AR_FROM_PROXY	0x04		    /* sws: authreq came from NAS */
+
+#define SAR_NO_LOG(authreq) (authreq->sws |= AR_NO_LOG)	      /* set flag */
+#define CAR_NO_LOG(authreq) (authreq->sws &= ~AR_NO_LOG)      /* clear flag */
+#define TAR_NO_LOG(authreq) ((authreq->sws & AR_NO_LOG) != 0) /* test flag */
+
+#define SAR_FROM_PROXY(authreq) (authreq->sws |= AR_FROM_PROXY)   /* set flag */
+#define CAR_FROM_PROXY(authreq) (authreq->sws &= ~AR_FROM_PROXY)  /* clr flag */
+#define TAR_FROM_PROXY(authreq) ((authreq->sws & AR_FROM_PROXY) != 0) /* test */
+
+#define AVPAIR_VTOA_QUOTE 0x0001 /* Quote strings with "'" */
+#define AVPAIR_VTOA_NULL  0x0002 /* Print "" instead of NULL for missing item */
+#define AVPAIR_VTOA_MASK  0x00ff /* Reserve fourteen more bits. */
+
+#define LOG_VP_QUOTE	0x0001	/* Quote strings (same as AVPAIR_VTOA_QUOTE) */
+#define LOG_VP_NULL	0x0002  /* Use "" (incompatible with LOG_VP_NA) */
+#define LOG_VP_TAB	0x0100	/* Put tab after printing. */
+#define LOG_VP_NA	0x0200  /* fprintf ("NA") if no attr exists in list. */
+#define LOG_VP_LAST	0x0400	/* Log last value pair found. */
+#define LOG_VP_ALL	0x0800	/* Log all attributes found. */
+#define LOG_VP_MASK	0xFFFF	/* Switches available. */
+
+/* dict.c */
+int dict_init PROTO((void));
+DICT_ATTR * dict_attrget PROTO((int));
+DICT_ATTR * dict_attrfind PROTO((char *));
+DICT_VALUE * dict_valfind PROTO((char *));
+DICT_VALUE * dict_valget PROTO((UINT4, char *));
+
+/* fsm.c */
+/*AATV * find_aatv PROTO((char *));
+int init_fsm PROTO((int, AATVPTR **, int, char *, FSM_ENT ***, FSM_ENT ***));
+*/
+/* funcs.c */
+char * add_string PROTO((char *, int));
+char * authtype_toa PROTO((int));
+VALUE_PAIR * avpair_add PROTO((VALUE_PAIR **, int, void *, int));
+int avpair_assign PROTO((VALUE_PAIR *, void *, int));
+int avpair_copy PROTO((VALUE_PAIR **, VALUE_PAIR *, int));
+int avpair_get PROTO((void *, VALUE_PAIR *, int));
+VALUE_PAIR * avpair_new PROTO((int, void *, int));
+char * avpair_vtoa PROTO((VALUE_PAIR *, int));
+void compress_file PROTO((FILE **, char *));
+void debug_list PROTO((FILE *, VALUE_PAIR *));
+void debug_pair PROTO((FILE *, VALUE_PAIR *));
+int dumpit PROTO((/* int, int, void *, int, int, char *, ...*/));
+void fprint_attr_val PROTO((FILE *, VALUE_PAIR *));
+VALUE_PAIR * gen_valpairs PROTO((AUTH_HDR *));
+char * get_errmsg PROTO((void));
+int get_passwd PROTO((AUTH_REQ *, char *, char *, char *));
+VALUE_PAIR * get_vp PROTO((VALUE_PAIR *, UINT4));
+VALUE_PAIR * get_last_vp PROTO((VALUE_PAIR *, UINT4));
+int hex_dump PROTO((char *, char *, int, int));
+void insert_vp PROTO((VALUE_PAIR **, VALUE_PAIR *, VALUE_PAIR *));
+int loghead PROTO(( /* va_alist */ ));
+int logit PROTO(( /* int facility, int level, char *format, ... */ ));
+void missing_attribute PROTO((AUTH_REQ *, char *, int, char *));
+VALUE_PAIR * parse_realm PROTO((AUTH_REQ *));
+int prune_pairs PROTO((AUTH_REQ *, PRUN_LIST *, int));
+#define reply_message(authreq, msgno, msg) _reply_message(authreq, msgno, msg,__FILE__, __LINE__)
+int _reply_message PROTO((AUTH_REQ *, ERRORCODE, char *, char *, int));
+int reply_sprintf PROTO(( /* int logsw, AUTHREQ *, char *format, ... */ ));
+int setupsock PROTO((struct sockaddr_in *, int));
+void trunc_logfile PROTO((FILE **, char *));
+char * type_string PROTO((AUTH_REQ *, VALUE_PAIR *));
+
+/* passchange.c */
+/*int pw_expired PROTO((UINT4));*/
+
+/* radiusd.c */
+/*AUTH_REQ * build_acct_req PROTO((AUTH_REQ *, int, char *, int, VALUE_PAIR *));
+int call_action PROTO((AATV *, AUTH_REQ *, int, char *));
+AUTH_REQ * rad_2rad_recv PROTO((struct sockaddr_in *, UINT4, u_int, EV *));
+AUTH_REQ * rad_recv PROTO((struct sockaddr_in *, UINT4, u_int, EV *));
+int radius_send PROTO((char *, u_int, char *, AUTH_REQ *, int));
+void start_fsm PROTO((AUTH_REQ *, int, char *, char *));
+*/
+/* sesslog.c */
+/*VALUE_PAIR *log_vp PROTO((FILE *, VALUE_PAIR *, int, int));
+int logfmt_brief PROTO((FILE *, VALUE_PAIR *));
+int logfmt_old PROTO((FILE *, VALUE_PAIR *, int));
+int logfmt_v1_0 PROTO((FILE *, VALUE_PAIR *));
+int logfmt_v1_1 PROTO((FILE *, VALUE_PAIR *));
+int logfmt_v2_0 PROTO((FILE *, VALUE_PAIR *, int, u_short *));
+int logfmt_v2_1 PROTO((FILE *, VALUE_PAIR *, int));
+*/
+/* users.c */
+int add_file_list PROTO((char *));
+void config_init PROTO((void));
+int config_files PROTO((int, int, int));
+void config_fini PROTO((void));
+void dns_recv PROTO((struct sockaddr_in *, UINT4, int));
+AUTH_ENTRY * find_auth_ent PROTO((char *, int, char*));
+int find_auth_type PROTO((char *, int, char *, int *, char **, char **, char **));
+int find_client PROTO((UINT4, char **, char **, char **));
+int find_client_by_name PROTO((UINT4 *, char *, char **, char **));
+int find_host_by_name PROTO((UINT4 *, char *));
+void free_user_ent PROTO((USER_ENTRY *));
+UINT4 get_our_addr PROTO((void));
+char * ip_hostname PROTO((UINT4));
+void list_cat PROTO((VALUE_PAIR **, VALUE_PAIR *));
+void list_copy PROTO((VALUE_PAIR **, VALUE_PAIR *));
+int pair_parse PROTO((char *, VALUE_PAIR **));
+FILE_LIST * return_file_list PROTO((void));
+int update_clients PROTO((void));
+int user_find PROTO((char *, char *, int, VALUE_PAIR **, VALUE_PAIR **, int));
+void user_gettime PROTO((char *, struct tm *));
+int user_update PROTO((char *, VALUE_PAIR *, VALUE_PAIR*));
+
+/* util.c */
+UINT4 get_ipaddr PROTO((char *));
+int good_ipaddr PROTO((char *));
+void list_free PROTO((VALUE_PAIR *));
+
+/* version.c */
+/*char * version PROTO((void));
+*/
+/* pppradius.c */
+int rad_auth PROTO((char *, char *));
+int rad_acct_start PROTO((char *));
+int rad_acct_stop PROTO((char *));
+
+int reqradius;
+char *RADIUS_name;
+
+#endif /* RADIUS_H */
diff -rPu pppd.old/sys-NeXT.c pppd/sys-NeXT.c
--- pppd.old/sys-NeXT.c	Thu Jan  1 03:00:00 1970
+++ pppd/sys-NeXT.c	Wed Mar 25 05:17:22 1998
@@ -0,0 +1,1703 @@
+/*
+ * sys-next.c - System-dependent procedures for setting up
+ * PPP interfaces on NeXT 3.2/3.3  systems
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * Copyright (c) 1994 Philippe-Andre Prindeville.
+ * 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 Carnegie Mellon 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: sys-NeXT.c,v 1.9 1998/03/25 02:17:23 paulus Exp $";
+#endif
+
+#include <stdio.h>
+#include <syslog.h>
+#include <termios.h>
+#include <utmp.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <libc.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+
+#include <net/if.h>
+#include <net/ppp_defs.h>
+#include <net/if_ppp.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#if !(NS_TARGET >= 40)
+/* XXX get an error "duplicate member ip_v under 4.1 GAMMA */
+#include <netinet/ip.h>
+#endif /* NS_TARGET */
+#include <netinet/if_ether.h>
+#include <net/route.h>
+#include <netinet/in.h>
+
+#include <netinfo/ni.h>
+
+#include "pppd.h"
+
+static int initdisc = -1;	/* Initial TTY discipline */
+static int initfdflags = -1;	/* Initial file descriptor flags for fd */
+static int ppp_fd = -1;		/* fd which is set to PPP discipline */
+static int loop_slave = -1;
+static int loop_master;
+static char loop_name[20];
+
+extern int errno;
+
+static int	restore_term;	/* 1 => we've munged the terminal */
+static struct termios inittermios; /* Initial TTY termios */
+
+static char *lock_file;
+
+static int sockfd;		/* socket for doing interface ioctls */
+static int pppdev;  /* +++ */
+
+#if defined(i386) && defined(HAS_BROKEN_IOCTL)
+#define	ioctl	myioctl
+#endif
+
+static int if_is_up;		/* the interface is currently up */
+static u_int32_t default_route_gateway;	/* gateway addr for default route */
+static u_int32_t proxy_arp_addr;	/* remote addr for proxy arp */
+
+/* Prototypes for procedures local to this file. */
+static int translate_speed __P((int));
+static int baud_rate_of __P((int));
+static int dodefaultroute __P((u_int32_t, int));
+static int get_ether_addr __P((u_int32_t, struct sockaddr *));
+static int ether_by_host __P((char *, struct ether_addr *));
+
+
+/*
+ * sys_init - System-dependent initialization.
+ */
+void
+sys_init()
+{
+    openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP);
+    setlogmask(LOG_UPTO(LOG_INFO));
+
+    /* Get an internet socket for doing socket ioctl's on. */
+    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+	syslog(LOG_ERR, "Couldn't create IP socket: %m");
+	die(1);
+    }
+
+    if((pppdev = open("/dev/ppp0", O_RDWR, O_NONBLOCK)) == NULL)
+      {
+	syslog(LOG_ERR, "Couldn't open /dev/ppp0: %m");
+	die(1);
+      }
+      
+}
+
+/*
+ * sys_cleanup - restore any system state we modified before exiting:
+ * mark the interface down, delete default route and/or proxy arp entry.
+ * This should call die() because it's called from die().
+ */
+void
+sys_cleanup()
+{
+    struct ifreq ifr;
+
+    if (if_is_up) {
+	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+	if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) >= 0
+	    && ((ifr.ifr_flags & IFF_UP) != 0)) {
+	    ifr.ifr_flags &= ~IFF_UP;
+	    ioctl(sockfd, SIOCSIFFLAGS, &ifr);
+	}
+    }
+
+    if (default_route_gateway)
+	cifdefaultroute(0, 0, default_route_gateway);
+    if (proxy_arp_addr)
+	cifproxyarp(0, proxy_arp_addr);
+
+    close(pppdev);
+}
+
+/*
+ * note_debug_level - note a change in the debug level.
+ */
+void
+note_debug_level()
+{
+    if (debug) {
+	syslog(LOG_INFO, "Debug turned ON, Level %d", debug);
+	setlogmask(LOG_UPTO(LOG_DEBUG));
+    } else {
+	setlogmask(LOG_UPTO(LOG_WARNING));
+    }
+}
+
+/*
+ * ppp_available - check whether the system has any ppp interfaces
+ * (in fact we check whether we can do an ioctl on ppp0).
+ */
+int
+ppp_available()
+{
+    int s, ok;
+    struct ifreq ifr;
+    extern char *no_ppp_msg;
+
+    if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+	return 1;		/* can't tell - maybe we're not root */
+
+    strncpy(ifr.ifr_name, "ppp0", sizeof (ifr.ifr_name));
+    ok = ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0;
+    close(s);
+
+    no_ppp_msg = "\
+This system lacks kernel support for PPP.  To include PPP support\n\
+in the kernel, please follow the steps detailed in the README.NeXT\n\
+file in the ppp-2.2 distribution.\n";
+
+    return ok;
+}
+
+/*
+ * establish_ppp - Turn the serial port into a ppp interface.
+ */
+void
+establish_ppp(fd)
+    int fd;
+{
+    int pppdisc = PPPDISC;
+    int x;
+
+    if (ioctl(fd, TIOCGETD, &initdisc) < 0) {
+	syslog(LOG_ERR, "ioctl(TIOCGETD): %m");
+	die(1);
+    }
+    if (ioctl(fd, TIOCSETD, &pppdisc) < 0) {
+	syslog(LOG_ERR, "ioctl(establish TIOCSETD): %m");
+	die(1);
+    }
+
+    /*
+     * Find out which interface we were given.
+     */
+    if (ioctl(fd, PPPIOCGUNIT, &ifunit) < 0) {	
+	syslog(LOG_ERR, "ioctl(PPPIOCGUNIT): %m");
+	die(1);
+    }
+
+    /*
+     * Enable debug in the driver if requested.
+     */
+    if (kdebugflag) {
+	if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
+	    syslog(LOG_WARNING, "ioctl(PPPIOCGFLAGS): %m");
+	} else {
+	    x |= (kdebugflag & 0xFF) * SC_DEBUG;
+	    if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0)
+		syslog(LOG_WARNING, "ioctl(PPPIOCSFLAGS): %m");
+	}
+    }
+
+    /*
+     * Set device for non-blocking reads so PPPD can poll for
+     * input from the kernel.
+     */
+    if ((initfdflags = fcntl(fd, F_GETFL)) == -1
+	|| fcntl(fd, F_SETFL, initfdflags | O_NONBLOCK) == -1) {
+	syslog(LOG_WARNING, "Couldn't set device to non-blocking mode: %m");
+    }
+
+}
+
+
+/*
+ * disestablish_ppp - Restore the serial port to normal operation.
+ * This shouldn't call die() because it's called from die().
+ */
+void
+disestablish_ppp(fd)
+    int fd;
+{
+    /* Reset non-blocking mode on fd. */
+    if (initfdflags != -1 && fcntl(fd, F_SETFL, initfdflags) < 0)
+	syslog(LOG_WARNING, "Couldn't restore device fd flags: %m");
+    initfdflags = -1;
+
+    /* Restore old line discipline. */
+    if (initdisc >= 0 && ioctl(fd, TIOCSETD, &initdisc) < 0)
+	syslog(LOG_ERR, "ioctl(TIOCSETD): %m");
+    initdisc = -1;
+}
+
+/*
+ * Check whether the link seems not to be 8-bit clean.
+ */
+void
+clean_check()
+{
+    int x;
+    char *s;
+
+    if (ioctl(ttyfd, PPPIOCGFLAGS, (caddr_t) &x) == 0) {
+	s = NULL;
+	switch (~x & (SC_RCV_B7_0|SC_RCV_B7_1|SC_RCV_EVNP|SC_RCV_ODDP)) {
+	case SC_RCV_B7_0:
+	    s = "bit 7 set to 1";
+	    break;
+	case SC_RCV_B7_1:
+	    s = "bit 7 set to 0";
+	    break;
+	case SC_RCV_EVNP:
+	    s = "odd parity";
+	    break;
+	case SC_RCV_ODDP:
+	    s = "even parity";
+	    break;
+	}
+	if (s != NULL) {
+	    syslog(LOG_WARNING, "Serial link is not 8-bit clean:");
+	    syslog(LOG_WARNING, "All received characters had %s", s);
+	}
+    }
+}
+
+/*
+ * List of valid speeds.
+ */
+struct speed {
+    int speed_int, speed_val;
+} speeds[] = {
+#ifdef B50
+    { 50, B50 },
+#endif
+#ifdef B75
+    { 75, B75 },
+#endif
+#ifdef B110
+    { 110, B110 },
+#endif
+#ifdef B134
+    { 134, B134 },
+#endif
+#ifdef B150
+    { 150, B150 },
+#endif
+#ifdef B200
+    { 200, B200 },
+#endif
+#ifdef B300
+    { 300, B300 },
+#endif
+#ifdef B600
+    { 600, B600 },
+#endif
+#ifdef B1200
+    { 1200, B1200 },
+#endif
+#ifdef B1800
+    { 1800, B1800 },
+#endif
+#ifdef B2000
+    { 2000, B2000 },
+#endif
+#ifdef B2400
+    { 2400, B2400 },
+#endif
+#ifdef B3600
+    { 3600, B3600 },
+#endif
+#ifdef B4800
+    { 4800, B4800 },
+#endif
+#ifdef B7200
+    { 7200, B7200 },
+#endif
+#ifdef B9600
+    { 9600, B9600 },
+#endif
+#ifdef B19200
+    { 19200, B19200 },
+#endif
+#ifdef B38400
+    { 38400, B38400 },
+#endif
+#ifdef EXTA
+    { 19200, EXTA },
+#endif
+#ifdef EXTB
+    { 38400, EXTB },
+#endif
+#ifdef B14400
+    { 14400, B14400 },
+#endif
+#ifdef B28800
+    { 28800, B28800 },
+#endif
+#ifdef B43200
+    { 43200, B43200 },
+#endif
+#ifdef B57600
+    { 57600, B57600 },
+#endif
+/*
+#ifndef B115200
+#warning Defining B115200
+#define B115200 20
+#endif
+*/
+#ifdef B115200
+    { 115200, B115200 },
+#endif
+    { 0, 0 }
+};
+
+/*
+ * Translate from bits/second to a speed_t.
+ */
+int
+translate_speed(bps)
+    int bps;
+{
+    struct speed *speedp;
+
+    if (bps == 0)
+	return 0;
+    for (speedp = speeds; speedp->speed_int; speedp++)
+	if (bps == speedp->speed_int)
+	    return speedp->speed_val;
+    syslog(LOG_WARNING, "speed %d not supported", bps);
+    return 0;
+}
+
+/*
+ * Translate from a speed_t to bits/second.
+ */
+static int
+baud_rate_of(speed)
+    int speed;
+{
+    struct speed *speedp;
+
+    if (speed == 0)
+	return 0;
+    for (speedp = speeds; speedp->speed_int; speedp++)
+	if (speed == speedp->speed_val)
+	    return speedp->speed_int;
+    return 0;
+}
+
+
+/*
+ * set_up_tty: Set up the serial port on `fd' for 8 bits, no parity,
+ * at the requested speed, etc.  If `local' is true, set CLOCAL
+ * regardless of whether the modem option was specified.
+ */
+void
+set_up_tty(fd, local)
+    int fd, local;
+{
+    int speed, x, modembits;
+    struct termios tios;
+
+    if (tcgetattr(fd, &tios) < 0) {
+	syslog(LOG_ERR, "tcgetattr: %m");
+	die(1);
+    }
+
+    if (!restore_term)
+	inittermios = tios;
+
+    tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CLOCAL);
+
+    tios.c_cflag |= CS8 | CREAD | HUPCL;
+    if (local || !modem)
+	tios.c_cflag |= CLOCAL;
+
+    tios.c_iflag = IGNBRK | IGNPAR;
+    tios.c_oflag = 0;
+    tios.c_lflag = 0;
+    tios.c_cc[VMIN] = 1;
+    tios.c_cc[VTIME] = 0;
+
+    if (crtscts == -2) {
+	tios.c_iflag |= IXON | IXOFF;
+	tios.c_cc[VSTOP] = 0x13;	/* DC3 = XOFF = ^S */
+	tios.c_cc[VSTART] = 0x11;	/* DC1 = XON  = ^Q */
+    }
+
+    speed = translate_speed(inspeed);
+    if (speed) {
+	cfsetospeed(&tios, speed);
+	cfsetispeed(&tios, speed);
+    } else {
+	speed = cfgetospeed(&tios);
+	/*
+	 * We can't proceed if the serial port speed is B0,
+	 * since that implies that the serial port is disabled.
+	 */
+	if (speed == B0) {
+	    syslog(LOG_ERR, "Baud rate for %s is 0; need explicit baud rate",
+		   devnam);
+	    die(1);
+	}
+    }
+
+    if (modem) {
+      modembits = TIOCM_RTS | TIOCM_CTS;
+      if (ioctl(fd, (crtscts ? TIOCMBIS : TIOCMBIC), &modembits) < 0)
+	syslog(LOG_ERR, "ioctl: TIOCMBIS/BIC: %m");
+    }
+
+    if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) {
+	syslog(LOG_ERR, "tcsetattr: %m");
+	die(1);
+    }
+
+   baud_rate = inspeed = baud_rate_of(speed);
+   restore_term = 1;
+}
+
+/*
+ * restore_tty - restore the terminal to the saved settings.
+ */
+void
+restore_tty(fd)
+    int fd;
+{
+    if (restore_term) {
+	if (tcsetattr(fd, TCSAFLUSH, &inittermios) < 0)
+	    if (errno != ENXIO)
+		syslog(LOG_WARNING, "tcsetattr: %m");
+	restore_term = 0;
+    }
+}
+
+/*
+ * setdtr - control the DTR line on the serial port.
+ * This is called from die(), so it shouldn't call die().
+ *
+ * The write hack is to get NXFax to recognize that there is
+ * activity on the port.  Not using the write nukes
+ * NXFax's capability to determine port usage.
+ *
+ */
+void
+setdtr(fd, on)
+int fd, on;
+{
+    int modembits = TIOCM_DTR;
+
+    if (!on)
+      {
+	write(fd, " ", 1);
+	sleep(1);
+      }
+
+/*    ioctl(fd, (on? TIOCMBIS: TIOCMBIC), &modembits); */
+    ioctl(fd, (on? TIOCSDTR: TIOCCDTR), 0);
+}
+
+
+/*
+ * output - Output PPP packet.
+ */
+void
+output(unit, p, len)
+    int unit;
+    u_char *p;
+    int len;
+{
+    if (debug)
+	log_packet(p, len, "sent ", LOG_DEBUG);
+
+    if (write(ttyfd, p, len) < 0) {
+	if (errno == EWOULDBLOCK || errno == ENOBUFS
+	    || errno == ENXIO || errno == EIO) {
+	    syslog(LOG_WARNING, "write: warning: %m");
+	} else {
+	    syslog(LOG_ERR, "write: %m");
+	    die(1);
+	}
+    }
+}
+
+
+/*
+ * wait_input - wait until there is data available on ttyfd,
+ * for the length of time specified by *timo (indefinite
+ * if timo is NULL).
+ */
+void
+wait_input(timo)
+    struct timeval *timo;
+{
+    fd_set ready;
+    int n;
+
+    FD_ZERO(&ready);
+    FD_SET(ttyfd, &ready);
+    n = select(ttyfd+1, &ready, NULL, &ready, timo);
+    if (n < 0 && errno != EINTR) {
+	syslog(LOG_ERR, "select: %m");
+	die(1);
+    }
+}
+
+
+/*
+ * read_packet - get a PPP packet from the serial device.
+ */
+int
+read_packet(buf)
+    u_char *buf;
+{
+    int len;
+
+    if ((len = read(ttyfd, buf, PPP_MTU + PPP_HDRLEN)) < 0) {
+	if (errno == EWOULDBLOCK || errno == EINTR) {
+	    MAINDEBUG((LOG_DEBUG, "read: %m"));
+	    return -1;
+	}
+	syslog(LOG_ERR, "read: %m");
+	die(1);
+    }
+    return len;
+}
+
+
+/*
+ * ppp_send_config - configure the transmit characteristics of
+ * the ppp interface.
+ */
+void
+ppp_send_config(unit, mtu, asyncmap, pcomp, accomp)
+    int unit, mtu;
+    u_int32_t asyncmap;
+    int pcomp, accomp;
+{
+    u_int x;
+    struct ifreq ifr;
+
+    strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+    ifr.ifr_mtu = mtu;
+    if (ioctl(sockfd, SIOCSIFMTU, (caddr_t) &ifr) < 0) {
+	syslog(LOG_ERR, "ioctl(SIOCSIFMTU): %m");
+	quit();
+    }
+
+    if (ioctl(ttyfd, PPPIOCSASYNCMAP, (caddr_t) &asyncmap) < 0) {
+	syslog(LOG_ERR, "ioctl(PPPIOCSASYNCMAP): %m");
+	quit();
+    }
+
+    if (ioctl(ttyfd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
+	syslog(LOG_ERR, "ioctl(PPPIOCGFLAGS): %m");
+	quit();
+    }
+    x = pcomp? x | SC_COMP_PROT: x &~ SC_COMP_PROT;
+    x = accomp? x | SC_COMP_AC: x &~ SC_COMP_AC;
+    if (ioctl(ttyfd, PPPIOCSFLAGS, (caddr_t) &x) < 0) {
+	syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
+	quit();
+    }
+}
+
+
+/*
+ * ppp_set_xaccm - set the extended transmit ACCM for the interface.
+ */
+void
+ppp_set_xaccm(unit, accm)
+    int unit;
+    ext_accm accm;
+{
+    if (ioctl(ttyfd, PPPIOCSXASYNCMAP, accm) < 0 && errno != ENOTTY)
+	syslog(LOG_WARNING, "ioctl(PPPIOCSXASYNCMAP): %m");
+}
+
+
+/*
+ * ppp_recv_config - configure the receive-side characteristics of
+ * the ppp interface.
+ */
+void
+ppp_recv_config(unit, mru, asyncmap, pcomp, accomp)
+    int unit, mru;
+    u_int32_t asyncmap;
+    int pcomp, accomp;
+{
+    int x;
+
+    if (ioctl(ttyfd, PPPIOCSMRU, (caddr_t) &mru) < 0) {
+	syslog(LOG_ERR, "ioctl(PPPIOCSMRU): %m");
+	quit();
+    }
+    if (ioctl(ttyfd, PPPIOCSRASYNCMAP, (caddr_t) &asyncmap) < 0) {
+	syslog(LOG_ERR, "ioctl(PPPIOCSRASYNCMAP): %m");
+	quit();
+    }
+    if (ioctl(ttyfd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
+	syslog(LOG_ERR, "ioctl(PPPIOCGFLAGS): %m");
+	quit();
+    }
+    x = !accomp? x | SC_REJ_COMP_AC: x &~ SC_REJ_COMP_AC;
+    if (ioctl(ttyfd, PPPIOCSFLAGS, (caddr_t) &x) < 0) {
+	syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
+	quit();
+    }
+}
+
+/*
+ * ccp_test - ask kernel whether a given compression method
+ * is acceptable for use.
+ */
+int
+ccp_test(unit, opt_ptr, opt_len, for_transmit)
+    int unit, opt_len, for_transmit;
+    u_char *opt_ptr;
+{
+    struct ppp_option_data data;
+
+    data.ptr = opt_ptr;
+    data.length = opt_len;
+    data.transmit = for_transmit;
+    if (ioctl(ttyfd, PPPIOCSCOMPRESS, (caddr_t) &data) >= 0)
+	return 1;
+    return (errno == ENOBUFS)? 0: -1;
+}
+
+/*
+ * ccp_flags_set - inform kernel about the current state of CCP.
+ */
+void
+ccp_flags_set(unit, isopen, isup)
+    int unit, isopen, isup;
+{
+    int x;
+
+    if (ioctl(ttyfd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
+	syslog(LOG_ERR, "ioctl(PPPIOCGFLAGS): %m");
+	return;
+    }
+    x = isopen? x | SC_CCP_OPEN: x &~ SC_CCP_OPEN;
+    x = isup? x | SC_CCP_UP: x &~ SC_CCP_UP;
+    if (ioctl(ttyfd, PPPIOCSFLAGS, (caddr_t) &x) < 0)
+	syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
+}
+
+/*
+ * ccp_fatal_error - returns 1 if decompression was disabled as a
+ * result of an error detected after decompression of a packet,
+ * 0 otherwise.  This is necessary because of patent nonsense.
+ */
+int
+ccp_fatal_error(unit)
+    int unit;
+{
+    int x;
+
+    if (ioctl(ttyfd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
+	syslog(LOG_ERR, "ioctl(PPPIOCGFLAGS): %m");
+	return 0;
+    }
+    return x & SC_DC_FERROR;
+}
+
+/*
+ * sifvjcomp - config tcp header compression
+ */
+int
+sifvjcomp(u, vjcomp, cidcomp, maxcid)
+    int u, vjcomp, cidcomp, maxcid;
+{
+    u_int x;
+
+    if (ioctl(ttyfd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
+	syslog(LOG_ERR, "ioctl(PPIOCGFLAGS): %m");
+	return 0;
+    }
+    x = vjcomp ? x | SC_COMP_TCP: x &~ SC_COMP_TCP;
+    x = cidcomp? x & ~SC_NO_TCP_CCID: x | SC_NO_TCP_CCID;
+    if (ioctl(ttyfd, PPPIOCSFLAGS, (caddr_t) &x) < 0) {
+	syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
+	return 0;
+    }
+    if (ioctl(ttyfd, PPPIOCSMAXCID, (caddr_t) &maxcid) < 0) {
+	syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
+	return 0;
+    }
+    return 1;
+}
+
+/*
+ * sifup - Config the interface up and enable IP packets to pass.
+ */
+#ifndef SC_ENABLE_IP
+#define SC_ENABLE_IP	0x100	/* compat for old versions of kernel code */
+#endif
+
+int
+sifup(u)
+    int u;
+{
+    struct ifreq ifr;
+    u_int x;
+    struct npioctl npi;
+
+    strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+    if (ioctl(sockfd, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+	syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m");
+	return 0;
+    }
+    ifr.ifr_flags |= IFF_UP;
+    if (ioctl(sockfd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
+	syslog(LOG_ERR, "ioctl(SIOCSIFFLAGS): %m");
+	return 0;
+    }
+    if_is_up = 1;
+    npi.protocol = PPP_IP;
+    npi.mode = NPMODE_PASS;
+    if (ioctl(ttyfd, PPPIOCSNPMODE, &npi) < 0) {
+	if (errno != ENOTTY) {
+	    syslog(LOG_ERR, "ioctl(PPPIOCSNPMODE): %m");
+	    return 0;
+	}
+	/* for backwards compatibility */
+	if (ioctl(ttyfd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
+	    syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m");
+	    return 0;
+	}
+	x |= SC_ENABLE_IP;
+	if (ioctl(ttyfd, PPPIOCSFLAGS, (caddr_t) &x) < 0) {
+	    syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
+	    return 0;
+	}
+    }
+    return 1;
+}
+
+/*
+ * sifdown - Config the interface down and disable IP.
+ */
+int
+sifdown(u)
+    int u;
+{
+    struct ifreq ifr;
+    u_int x;
+    int rv;
+    struct npioctl npi;
+
+    rv = 1;
+    npi.protocol = PPP_IP;
+    npi.mode = NPMODE_ERROR;
+    ioctl(ttyfd, PPPIOCSNPMODE, (caddr_t) &npi);
+    /* ignore errors, because ttyfd might have been closed by now. */
+
+
+    strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+    if (ioctl(sockfd, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+	syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m");
+	rv = 0;
+    } else {
+	ifr.ifr_flags &= ~IFF_UP;
+	if (ioctl(sockfd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
+	    syslog(LOG_ERR, "ioctl(SIOCSIFFLAGS): %m");
+	    rv = 0;
+	} else
+	    if_is_up = 0;
+    }
+    return rv;
+}
+
+/*
+ * SET_SA_FAMILY - set the sa_family field of a struct sockaddr,
+ * if it exists.
+ */
+#define SET_SA_FAMILY(addr, family)		\
+    BZERO((char *) &(addr), sizeof(addr));	\
+    addr.sa_family = (family); 
+
+/*
+ * sifaddr - Config the interface IP addresses and netmask.
+ */
+int
+sifaddr(u, o, h, m)
+    int u;
+    u_int32_t o, h, m;
+{
+    int ret;
+    struct ifreq ifr;
+
+    ret = 1;
+    strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+    SET_SA_FAMILY(ifr.ifr_addr, AF_INET);
+    ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr = o;
+    if (ioctl(sockfd, SIOCSIFADDR, (caddr_t) &ifr) < 0) {
+	syslog(LOG_ERR, "ioctl(SIOCAIFADDR): %m");
+	ret = 0;
+    }
+    ((struct sockaddr_in *) &ifr.ifr_dstaddr)->sin_addr.s_addr = h;
+    if (ioctl(sockfd, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) {
+	syslog(LOG_ERR, "ioctl(SIOCSIFDSTADDR): %m");
+	ret = 0;
+    }
+    if (m != 0) {
+	((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr = m;
+	syslog(LOG_INFO, "Setting interface mask to %s\n", ip_ntoa(m));
+	if (ioctl(sockfd, SIOCSIFNETMASK, (caddr_t) &ifr) < 0) {
+	    syslog(LOG_ERR, "ioctl(SIOCSIFNETMASK): %m");
+	    ret = 0;
+	}
+    }
+    return ret;
+}
+
+/*
+ * cifaddr - Clear the interface IP addresses, and delete routes
+ * through the interface if possible.
+ *
+ * N.B.: under NextStep, you can't *delete* an address on an interface,
+ * so we change it to 0.0.0.0...  A real hack.  But it simplifies
+ * reconnection on the server side.
+ */
+int
+cifaddr(u, o, h)
+    int u;
+    u_int32_t o, h;
+{
+    struct rtentry rt;
+
+#if 1
+    h = o = 0L;
+    (void) sifaddr(u, o, h, 0L);
+#endif
+    SET_SA_FAMILY(rt.rt_dst, AF_INET);
+    ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr = h;
+    SET_SA_FAMILY(rt.rt_gateway, AF_INET);
+    ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = o;
+    rt.rt_flags = RTF_HOST;
+    if (ioctl(sockfd, SIOCDELRT, (caddr_t) &rt) < 0) {
+	syslog(LOG_ERR, "ioctl(SIOCDELRT): %m");
+	return 0;
+    }
+    return 1;
+}
+
+/*
+ * sifdefaultroute - assign a default route through the address given.
+ */
+int
+sifdefaultroute(u, l, g)
+    int u;
+    u_int32_t l, g;
+{
+    return dodefaultroute(g, 's');
+}
+
+/*
+ * cifdefaultroute - delete a default route through the address given.
+ */
+int
+cifdefaultroute(u, l, g)
+    int u;
+    u_int32_t l, g;
+{
+    return dodefaultroute(g, 'c');
+}
+
+/*
+ * dodefaultroute - talk to a routing socket to add/delete a default route.
+ */
+int
+dodefaultroute(g, cmd)
+    u_int32_t g;
+    int cmd;
+{
+    struct rtentry rt;
+
+    SET_SA_FAMILY(rt.rt_dst, AF_INET);
+    ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr = 0L;
+    SET_SA_FAMILY(rt.rt_gateway, AF_INET);
+    ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = g;
+    rt.rt_flags = RTF_GATEWAY;
+    if (ioctl(sockfd, (cmd == 's') ? SIOCADDRT : SIOCDELRT, &rt) < 0) {
+	syslog(LOG_ERR, "%cifdefaultroute: ioctl(%s): %m", cmd,
+	       (cmd == 's') ? "SIOCADDRT" : "SIOCDELRT");
+	return 0;
+    }
+    default_route_gateway = (cmd == 's')? g: 0;
+    return 1;
+}
+
+/*
+ * sifproxyarp - Make a proxy ARP entry for the peer.
+ */
+int
+sifproxyarp(unit, hisaddr)
+    int unit;
+    u_int32_t hisaddr;
+{
+    struct arpreq arpreq;
+
+    BZERO(&arpreq, sizeof(arpreq));
+
+    /*
+     * Get the hardware address of an interface on the same subnet
+     * as our local address.
+     */
+    if (!get_ether_addr(hisaddr, &arpreq.arp_ha)) {
+	syslog(LOG_ERR, "Cannot determine ethernet address for proxy ARP");
+	return 0;
+    }
+
+    SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
+    ((struct sockaddr_in *) &arpreq.arp_pa)->sin_addr.s_addr = hisaddr;
+    arpreq.arp_flags = ATF_PERM | ATF_PUBL;
+    if (ioctl(sockfd, SIOCSARP, (caddr_t)&arpreq) < 0) {
+	syslog(LOG_ERR, "ioctl(SIOCSARP): %m");
+	return 0;
+    }
+
+    proxy_arp_addr = hisaddr;
+    return 1;
+}
+
+/*
+ * cifproxyarp - Delete the proxy ARP entry for the peer.
+ */
+int
+cifproxyarp(unit, hisaddr)
+    int unit;
+    u_int32_t hisaddr;
+{
+    struct arpreq arpreq;
+
+    BZERO(&arpreq, sizeof(arpreq));
+    SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
+    ((struct sockaddr_in *) &arpreq.arp_pa)->sin_addr.s_addr = hisaddr;
+    if (ioctl(sockfd, SIOCDARP, (caddr_t)&arpreq) < 0) {
+	syslog(LOG_WARNING, "ioctl(SIOCDARP): %m");
+	return 0;
+    }
+    proxy_arp_addr = 0;
+    return 1;
+}
+
+/*
+ * get_ether_addr - get the hardware address of an interface on the
+ * the same subnet as ipaddr.
+ */
+#define MAX_IFS		32
+
+int
+get_ether_addr(ipaddr, hwaddr)
+    u_int32_t ipaddr;
+    struct sockaddr *hwaddr;
+{
+    struct ifreq *ifr, *ifend, *ifp;
+    u_int32_t ina, mask;
+    struct ether_addr dla;
+    struct ifreq ifreq;
+    struct ifconf ifc;
+    struct ifreq ifs[MAX_IFS];
+    struct hostent *hostent;
+
+    ifc.ifc_len = sizeof(ifs);
+    ifc.ifc_req = ifs;
+    if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
+	syslog(LOG_ERR, "ioctl(SIOCGIFCONF): %m");
+	return 0;
+    }
+
+    /*
+     * Scan through looking for an interface with an Internet
+     * address on the same subnet as `ipaddr'.
+     */
+    ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+    for (ifr = ifc.ifc_req; ifr < ifend; ifr = (struct ifreq *)
+		((char *)&ifr->ifr_addr + sizeof(struct sockaddr))) {
+	if (ifr->ifr_addr.sa_family == AF_INET) {
+	    ina = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr;
+	    strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+	    /*
+	     * Check that the interface is up, and not point-to-point
+	     * or loopback.
+	     */
+	    if (ioctl(sockfd, SIOCGIFFLAGS, &ifreq) < 0)
+		continue;
+	    if ((ifreq.ifr_flags &
+		 (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP))
+		 != (IFF_UP|IFF_BROADCAST))
+		continue;
+	    /*
+	     * Get its netmask and check that it's on the right subnet.
+	     */
+	    if (ioctl(sockfd, SIOCGIFNETMASK, &ifreq) < 0)
+		continue;
+	    mask = ((struct sockaddr_in*)&ifreq.ifr_addr)->sin_addr.s_addr;
+	    if ((ipaddr & mask) != (ina & mask))
+		continue;
+
+	    break;
+	}
+    }
+
+    if (ifr >= ifend)
+	return 0;
+    syslog(LOG_INFO, "found interface %s for proxy arp", ifr->ifr_name);
+
+    /*
+     * Get the hostname and look for an entry using the ethers database.
+     * Under NeXTStep this is the best we can do for now.
+     */
+    if ((hostent = gethostbyaddr((char*)&ina, sizeof(ina), AF_INET)) == NULL)
+	return 0;
+
+    if (ether_by_host(hostent->h_name, &dla)) {
+	syslog(LOG_INFO, "Add entry for %s in /etc/ethers", hostent->h_name);
+	return 0;	/* it's not there */
+    }
+    hwaddr->sa_family = AF_UNSPEC;
+    BCOPY(&dla, hwaddr->sa_data, sizeof(dla));
+    return 1;
+}
+
+static int
+ether_by_host(hostname, etherptr)
+    char *hostname;
+    struct ether_addr *etherptr;
+{
+    struct ether_addr *thisptr;
+    void *conn;
+    ni_id root;
+    ni_namelist val;
+    char path[256];
+
+    if (!ether_hostton(hostname, etherptr))
+	return 0;
+    /*
+     * We shall now try and
+     * find the address in the
+     * top domain of netinfo.
+     */
+    strcat(strcpy(path, "/machines/"), hostname);
+
+    if (ni_open((void *)0, "/", &conn)
+     || ni_root(conn, &root)
+     || ni_pathsearch(conn, &root, path)
+     || ni_lookupprop(conn, &root, "en_address", &val))
+	return 1;
+
+    /*
+     * Now we can convert the returned string into an ethernet address.
+     */
+    strcpy(path, val.ni_namelist_val[0]);
+    ni_free(conn);
+    if ((thisptr = (struct ether_addr*)ether_aton(path)) == NULL)
+	return 1;
+    BCOPY(thisptr, etherptr, sizeof(struct ether_addr));
+    return 0;
+}
+
+
+
+/*
+ * Return user specified netmask, modified by any mask we might determine
+ * for address `addr' (in network byte order).
+ * Here we scan through the system's list of interfaces, looking for
+ * any non-point-to-point interfaces which might appear to be on the same
+ * network as `addr'.  If we find any, we OR in their netmask to the
+ * user-specified netmask.
+ */
+u_int32_t
+GetMask(addr)
+    u_int32_t addr;
+{
+    u_int32_t mask, nmask, ina;
+    struct ifreq *ifr, *ifend, ifreq;
+    struct ifconf ifc;
+    struct ifreq ifs[MAX_IFS];
+
+    addr = ntohl(addr);
+    if (IN_CLASSA(addr))	/* determine network mask for address class */
+	nmask = IN_CLASSA_NET;
+    else if (IN_CLASSB(addr))
+	nmask = IN_CLASSB_NET;
+    else
+	nmask = IN_CLASSC_NET;
+    /* class D nets are disallowed by bad_ip_adrs */
+    mask = netmask | htonl(nmask);
+
+    /*
+     * Scan through the system's network interfaces.
+     */
+    ifc.ifc_len = sizeof(ifs);
+    ifc.ifc_req = ifs;
+    if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
+	syslog(LOG_WARNING, "ioctl(SIOCGIFCONF): %m");
+	return mask;
+    }
+    ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+    for (ifr = ifc.ifc_req; ifr < ifend; ifr = (struct ifreq *)
+	 	((char *)&ifr->ifr_addr + sizeof(struct sockaddr))) {
+	/*
+	 * Check the interface's internet address.
+	 */
+	if (ifr->ifr_addr.sa_family != AF_INET)
+	    continue;
+	ina = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr;
+	if ((ntohl(ina) & nmask) != (addr & nmask))
+	    continue;
+	/*
+	 * Check that the interface is up, and not point-to-point or loopback.
+	 */
+	strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+	if (ioctl(sockfd, SIOCGIFFLAGS, &ifreq) < 0)
+	    continue;
+	if ((ifreq.ifr_flags & (IFF_UP|IFF_POINTOPOINT|IFF_LOOPBACK))
+	    != IFF_UP)
+	    continue;
+	/*
+	 * Get its netmask and OR it into our mask.
+	 */
+	if (ioctl(sockfd, SIOCGIFNETMASK, &ifreq) < 0)
+	    continue;
+	mask |= ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr.s_addr;
+    }
+
+    return mask;
+}
+
+
+
+/*
+ * daemon - Detach us from the terminal session.
+ */
+int
+daemon(nochdir, noclose)
+    int nochdir, noclose;
+{
+    int pid;
+
+    if ((pid = fork()) < 0)
+	return -1;
+    if (pid != 0)
+	exit(0);		/* parent dies */
+    (void)setsid();
+    if (!nochdir)
+	chdir("/");
+    if (!noclose) {
+	fclose(stdin);		/* don't need stdin, stdout, stderr */
+	fclose(stdout);
+	fclose(stderr);
+    }
+    return 0;
+}
+
+
+char *
+strdup(s)
+    const char *s;
+{
+    char *d = malloc(strlen(s) + 1);
+
+    if (d) strcpy(d, s);
+    return d;
+}
+
+/*
+ * This logwtmp() implementation is subject to the following copyright:
+ *
+ * Copyright (c) 1988 The Regents of the University of California.
+ * 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 the University of California, Berkeley.  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.
+ */
+
+#define WTMPFILE        "/usr/adm/wtmp"
+
+void
+logwtmp(line, name, host)
+    const char *line, *name, *host;
+{
+    int fd;
+    struct stat buf;
+    struct utmp ut;
+
+    if ((fd = open(WTMPFILE, O_WRONLY|O_APPEND, 0)) < 0)
+	return;
+    if (!fstat(fd, &buf)) {
+	(void)strncpy(ut.ut_line, line, sizeof(ut.ut_line));
+	(void)strncpy(ut.ut_name, name, sizeof(ut.ut_name));
+	(void)strncpy(ut.ut_host, host, sizeof(ut.ut_host));
+	(void)time(&ut.ut_time);
+	if (write(fd, (char *)&ut, sizeof(struct utmp)) != sizeof(struct utmp))
+	    (void)ftruncate(fd, buf.st_size);
+    }
+    close(fd);
+}
+
+/*
+ * Routines for locking and unlocking the serial device, moved here
+ * from chat.c.
+ */
+
+#define LOCK_PREFIX	"/usr/spool/uucp/LCK/LCK.."
+
+/*
+ * lock - create a lock file for the named device
+ */
+int
+lock(dev)
+    char *dev;
+{
+    int fd, pid, n;
+    char *p;
+
+    if ((p = strrchr(dev, '/')) != NULL)
+	dev = p + 1;
+    lock_file = malloc(strlen(LOCK_PREFIX) + strlen(dev) + 1);
+    if (lock_file == NULL)
+	novm("lock file name");
+    strcat(strcpy(lock_file, LOCK_PREFIX), dev);
+
+    while ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0) {
+	if (errno == EEXIST
+	    && (fd = open(lock_file, O_RDONLY, 0)) >= 0) {
+	    /* Read the lock file to find out who has the device locked */
+	    n = read(fd, &pid, sizeof(pid));
+	    if (n <= 0) {
+		syslog(LOG_ERR, "Can't read pid from lock file %s", lock_file);
+		close(fd);
+	    } else {
+		if (kill(pid, 0) == -1 && errno == ESRCH) {
+		    /* pid no longer exists - remove the lock file */
+		    if (unlink(lock_file) == 0) {
+			close(fd);
+			syslog(LOG_NOTICE, "Removed stale lock on %s (pid %d)",
+			       dev, pid);
+			continue;
+		    } else
+			syslog(LOG_WARNING, "Couldn't remove stale lock on %s",
+			       dev);
+		} else
+		    syslog(LOG_NOTICE, "Device %s is locked by pid %d",
+			   dev, pid);
+	    }
+	    close(fd);
+	} else
+	    syslog(LOG_ERR, "Can't create lock file %s: %m", lock_file);
+	free(lock_file);
+	lock_file = NULL;
+	return -1;
+    }
+
+    pid = getpid();
+    write(fd, &pid, sizeof pid);
+
+    close(fd);
+    return 0;
+}
+
+/*
+ * unlock - remove our lockfile
+ */
+void
+unlock()
+{
+    if (lock_file) {
+	unlink(lock_file);
+	free(lock_file);
+	lock_file = NULL;
+    }
+}
+
+#if defined(i386) && defined(HAS_BROKEN_IOCTL)
+int
+ioctl(fd, cmd, c)
+    int fd, cmd;
+    caddr_t c;
+{
+#undef	ioctl
+    int ret;
+
+#ifdef DEBUGIOCTL
+    int serrno;
+    u_char let, code, size;
+
+    size = (cmd >> 16) & IOCPARM_MASK;
+    let = (cmd >> 8);
+    code = cmd;
+
+    if (let == 't' && (75 <= code && code <= 90))
+    syslog(LOG_INFO, "ioctl(%d, 0x%x ('%c', %d, %d), 0x%x)\n", fd, cmd,
+	   let, code, size, c);
+#endif
+
+    ret = ioctl(fd, cmd, c);
+
+#ifdef DEBUGIOCTL
+    serrno = errno;
+    if (ret == -1)
+	syslog(LOG_INFO, "ioctl('%c', %d, %d) errno = %d (%m)\n",
+		let, code, size, errno);
+    if (let == 't' && (75 <= code && code <= 90) && (cmd & IOC_OUT)) {
+	int i, len = ((cmd >> 16) & IOCPARM_MASK);
+	for (i = 0; i < len / 4; ++i)
+		syslog(LOG_INFO, "word[%d] @ 0x%06x = 0x%x\n",
+		       i, &((int *) c)[i],((int *)c)[i]);
+    }
+    errno = serrno;
+#endif
+
+    if (ret == -1 && errno == EPERM)
+	errno = ret = 0;
+    return ret;
+}
+#endif	/* HAS_BROKEN_IOCTL */
+
+
+#if defined(FIXSIGS) && (defined (hppa) || defined(sparc))
+
+/*
+ * These redefinitions of Posix functions are necessary
+ * because HPPA systems have an OS bug that causes 
+ * sigaction to core dump:
+ *
+ * AlainF 9-Nov-1994	HACK FOR HP-PA/NEXTSTEP
+ *			sigaction(3) seems broken in the HP-PA NeXTSTEP 3.2
+ *			Posix lib. This causes pppd to SIGBUS at the expiration
+ *			of the first timeout (_sigtramp seems to invoke
+ *			the SIGALRM handler at an unreasonably low address).
+ *			All calls so sigaction(3) have been changed to calls
+ *			to sigvec(2) and sigprocmask(SIG_BLOCK,...) to
+ *			sigblock(2).
+ *			This is kind of a hack, especially since there are
+ *			other routines of the Posix lib still used, but
+ *			it worked for me.
+ *
+ * Dave Hess <David-Hess@net.tamu.edu> noted that 3.3 Sparc seems to
+ * have the same bug.  Thus this fix has been enabled for SPARC also.
+ *
+ *
+ */
+
+int sigemptyset(sigset_t *mask)
+{
+  *mask = 0;
+}
+
+sigaddset(sigset_t *mask, int which_sig)
+{
+  *mask |= sigmask(which_sig);
+}
+
+
+int sigaction(int sig, const struct sigaction *act, struct sigaction *oact)
+{
+   struct sigvec sv;
+   static int in = 0;
+
+   sv.sv_handler = act->sa_handler;
+   sv.sv_mask = act->sa_mask;
+   sv.sv_flags = 0;
+
+   if (!in)
+     {
+       in = 1;
+       syslog(LOG_WARNING, "PPPD: Inside modified HP and SPARC sigaction\n");
+     }
+
+   return sigvec(sig, &sv, NULL);
+}
+
+#endif
+
+
+/*
+ * Code following is added for 2.3 compatibility
+ */
+
+/*
+ * get_idle_time - return how long the link has been idle.
+ */
+int
+get_idle_time(u, ip)
+    int u;
+    struct ppp_idle *ip;
+{
+  return (ioctl(ttyfd, PPPIOCGIDLE, ip) >= 0); 
+}
+
+
+/*
+ * get_loop_output - read characters from the loopback, form them
+ * into frames, and detect when we want to bring the real link up.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ */
+int
+get_loop_output()
+{
+
+#if 0
+    int rv = 0;
+    int n;
+
+    while ((n = read(loop_master, inbuf, sizeof(inbuf))) >= 0) {
+	if (loop_chars(inbuf, n))
+	    rv = 1;
+    }
+
+    if (n == 0) {
+	syslog(LOG_ERR, "eof on loopback");
+	die(1);
+    } else if (errno != EWOULDBLOCK){
+	syslog(LOG_ERR, "read from loopback: %m");
+	die(1);
+    }
+
+    return rv;
+#endif
+
+    return 0;
+}
+
+/*
+ * sifnpmode - Set the mode for handling packets for a given NP.
+ */
+int
+sifnpmode(u, proto, mode)
+    int u;
+    int proto;
+    enum NPmode mode;
+{
+    struct npioctl npi;
+
+    npi.protocol = proto;
+    npi.mode = mode;
+    if (ioctl(ttyfd, PPPIOCSNPMODE, &npi) < 0) {
+	syslog(LOG_ERR, "ioctl(set NP %d mode to %d): %m", proto, mode);
+	return 0;
+    }
+    return 1;
+}
+
+
+/*
+ * open_ppp_loopback - open the device we use for getting
+ * packets in demand mode, and connect it to a ppp interface.
+ * Here we use a pty.
+ */
+void
+open_ppp_loopback()
+{
+
+#if 0
+    int flags;
+    struct termios tios;
+    int pppdisc = PPPDISC;
+
+    syslog(LOG_ERR, "open_ppp_loopback called!");
+    die(1);
+
+    if (openpty(&loop_master, &loop_slave, loop_name, NULL, NULL) < 0) {
+	syslog(LOG_ERR, "No free pty for loopback");
+	die(1);
+    }
+    SYSDEBUG((LOG_DEBUG, "using %s for loopback", loop_name));
+
+    if (tcgetattr(loop_slave, &tios) == 0) {
+	tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB);
+	tios.c_cflag |= CS8 | CREAD;
+	tios.c_iflag = IGNPAR;
+	tios.c_oflag = 0;
+	tios.c_lflag = 0;
+	if (tcsetattr(loop_slave, TCSAFLUSH, &tios) < 0)
+	    syslog(LOG_WARNING, "couldn't set attributes on loopback: %m");
+    }
+
+    if ((flags = fcntl(loop_master, F_GETFL)) != -1) 
+	if (fcntl(loop_master, F_SETFL, flags | O_NONBLOCK) == -1)
+	    syslog(LOG_WARNING, "couldn't set loopback to nonblock: %m");
+
+    ttyfd = loop_slave;
+    if (ioctl(ttyfd, TIOCSETD, &pppdisc) < 0) {
+	syslog(LOG_ERR, "ioctl(TIOCSETD): %m");
+	die(1);
+    }
+
+    /*
+     * Find out which interface we were given.
+     */
+    if (ioctl(ttyfd, PPPIOCGUNIT, &ifunit) < 0) {	
+	syslog(LOG_ERR, "ioctl(PPPIOCGUNIT): %m");
+	die(1);
+    }
+
+    /*
+     * Enable debug in the driver if requested.
+     */
+    if (kdebugflag) {
+	if (ioctl(ttyfd, PPPIOCGFLAGS, (caddr_t) &flags) < 0) {
+	    syslog(LOG_WARNING, "ioctl (PPPIOCGFLAGS): %m");
+	} else {
+	    flags |= (kdebugflag & 0xFF) * SC_DEBUG;
+	    if (ioctl(ttyfd, PPPIOCSFLAGS, (caddr_t) &flags) < 0)
+		syslog(LOG_WARNING, "ioctl(PPPIOCSFLAGS): %m");
+	}
+    }
+
+#endif
+
+}
+
+/*
+ * restore_loop - reattach the ppp unit to the loopback.
+ */
+void
+restore_loop()
+{
+    int x;
+
+    /*
+     * Transfer the ppp interface back to the loopback.
+     */
+    if (ioctl(ttyfd, PPPIOCXFERUNIT, 0) < 0) {
+	syslog(LOG_ERR, "ioctl(transfer ppp unit): %m");
+	die(1);
+    }
+    x = PPPDISC;
+    if (ioctl(loop_slave, TIOCSETD, &x) < 0) {
+	syslog(LOG_ERR, "ioctl(TIOCSETD): %m");
+	die(1);
+    }
+
+    /*
+     * Check that we got the same unit again.
+     */
+    if (ioctl(loop_slave, PPPIOCGUNIT, &x) < 0) {	
+	syslog(LOG_ERR, "ioctl(PPPIOCGUNIT): %m");
+	die(1);
+    }
+    if (x != ifunit) {
+	syslog(LOG_ERR, "transfer_ppp failed: wanted unit %d, got %d",
+	       ifunit, x);
+	die(1);
+    }
+    ttyfd = loop_slave;
+}
+
+
+/*
+ * Use the hostid as part of the random number seed.
+ */
+int
+get_host_seed()
+{
+    return gethostid();
+}
+
+
+/*
+ * sys_check_options - check the options that the user specified
+ */
+void
+sys_check_options()
+{
+  /*
+   * We don't support demand dialing yet.
+   */
+  if(demand)
+    {
+      syslog(LOG_WARNING, "PPP-2.3 for NeXTSTEP does not yet support demand dialing\n");
+      demand = 0;
+    }
+}
+
+
+/*
+ * sys_close - Clean up in a child process before execing.
+ */
+void
+sys_close()
+{
+    close(sockfd);
+    if (loop_slave >= 0) {
+	close(loop_slave);
+	close(loop_master);
+    }
+    closelog();
+}
+
+
+/*
+ * wait_loop_output - wait until there is data available on the
+ * loopback, for the length of time specified by *timo (indefinite
+ * if timo is NULL).
+ */
+void wait_loop_output(timo)
+    struct timeval *timo;
+{
+    fd_set ready;
+    int n;
+
+    FD_ZERO(&ready);
+    FD_SET(loop_master, &ready);
+    n = select(loop_master + 1, &ready, NULL, &ready, timo);
+    if (n < 0 && errno != EINTR) {
+	syslog(LOG_ERR, "select: %m");
+	die(1);
+    }
+}
+
+
+/*
+ * wait_time - wait for a given length of time or until a
+ * signal is received.
+ */
+void wait_time(timo)
+    struct timeval *timo;
+{
+    int n;
+
+    n = select(0, NULL, NULL, NULL, timo);
+    if (n < 0 && errno != EINTR) {
+	syslog(LOG_ERR, "select: %m");
+	die(1);
+    }
+}
diff -rPu pppd.old/sys-osf.c pppd/sys-osf.c
--- pppd.old/sys-osf.c	Thu Jan  1 03:00:00 1970
+++ pppd/sys-osf.c	Wed Mar 25 05:19:26 1998
@@ -0,0 +1,1661 @@
+/*
+ * System-dependent procedures for pppd under Digital UNIX (OSF/1).
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies.  This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: sys-osf.c,v 1.13 1998/03/25 02:19:27 paulus Exp $";
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <termios.h>
+#include <signal.h>
+#include <malloc.h>
+#include <utmp.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#include <sys/syslog.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/route.h>
+#include <net/ppp_defs.h>
+#include <net/pppio.h>
+#include <netinet/in.h>
+
+#include "pppd.h"
+
+static int	pppfd;
+static int	fdmuxid = -1;
+static int	iffd;
+static int	sockfd;
+
+static int	restore_term;
+static struct termios inittermios;
+static struct winsize wsinfo;	/* Initial window size info */
+static pid_t	tty_sid;	/* PID of our session leader */
+
+extern u_char	inpacket_buf[];	/* borrowed from main.c */
+
+static int	link_mtu, link_mru;
+
+#define NMODULES	32
+static int	tty_nmodules;
+static char	tty_modules[NMODULES][FMNAMESZ+1];
+
+static int closed_stdio;
+static int initfdflags = -1;
+static int orig_ttyfd = -1;
+
+static int	if_is_up;	/* Interface has been marked up */
+static u_int32_t ifaddrs[2];	/* local and remote addresses */
+static u_int32_t default_route_gateway;	/* Gateway for default route added */
+static u_int32_t proxy_arp_addr;	/* Addr for proxy arp entry added */
+
+/* Prototypes for procedures local to this file. */
+static int translate_speed __P((int));
+static int baud_rate_of __P((int));
+static int get_ether_addr __P((u_int32_t, struct sockaddr *));
+static int strioctl __P((int, int, void *, int, int));
+
+
+/*
+ * sys_init - System-dependent initialization.
+ */
+void
+sys_init()
+{
+    int x;
+
+    openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP);
+    setlogmask(LOG_UPTO(LOG_INFO));
+    if (debug)
+	setlogmask(LOG_UPTO(LOG_DEBUG));
+
+    /* Get an internet socket for doing socket ioctl's on. */
+    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+	syslog(LOG_ERR, "Couldn't create IP socket: %m");
+	die(1);
+    }
+
+    if (default_device)
+	tty_sid = getsid((pid_t)0);
+
+    /*
+     * Open the ppp device.
+     */
+    pppfd = open("/dev/streams/ppp", O_RDWR | O_NONBLOCK, 0);
+    if (pppfd < 0) {
+	syslog(LOG_ERR, "Can't open /dev/streams/ppp: %m");
+	die(1);
+    }
+    if (kdebugflag) {
+	x = PPPDBG_LOG + PPPDBG_DRIVER;
+	strioctl(pppfd, PPPIO_DEBUG, &x, sizeof(int), 0);
+    }
+
+    /* Assign a new PPA and get its unit number. */
+    if (strioctl(pppfd, PPPIO_NEWPPA, &ifunit, 0, sizeof(int)) < 0) {
+	syslog(LOG_ERR, "Can't create new PPP interface: %m");
+	die(1);
+    }
+
+    /*
+     * Open the ppp device again and push the if_ppp module on it.
+     */
+    iffd = open("/dev/streams/ppp", O_RDWR, 0);
+    if (iffd < 0) {
+	syslog(LOG_ERR, "Can't open /dev/streams/ppp (2): %m");
+	die(1);
+    }
+    if (kdebugflag) {
+	x = PPPDBG_LOG + PPPDBG_DRIVER;
+	strioctl(iffd, PPPIO_DEBUG, &x, sizeof(int), 0);
+    }
+    if (strioctl(iffd, PPPIO_ATTACH, &ifunit, sizeof(int), 0) < 0) {
+	syslog(LOG_ERR, "Couldn't attach ppp interface to device: %m");
+	die(1);
+    }
+    if (ioctl(iffd, I_PUSH, "if_ppp") < 0) {
+	syslog(LOG_ERR, "Can't push ppp interface module: %m");
+	die(1);
+    }
+    if (kdebugflag) {
+	x = PPPDBG_LOG + PPPDBG_IF;
+	strioctl(iffd, PPPIO_DEBUG, &x, sizeof(int), 0);
+    }
+    if (strioctl(iffd, PPPIO_NEWPPA, &ifunit, sizeof(int), 0) < 0) {
+	syslog(LOG_ERR, "Couldn't create ppp interface unit: %m");
+	die(1);
+    }
+    x = PPP_IP;
+    if (strioctl(iffd, PPPIO_BIND, &x, sizeof(int), 0) < 0) {
+	syslog(LOG_ERR, "Couldn't bind ppp interface to IP SAP: %m");
+	die(1);
+    }
+}
+
+/*
+ * sys_cleanup - restore any system state we modified before exiting:
+ * mark the interface down, delete default route and/or proxy arp entry.
+ * This shouldn't call die() because it's called from die().
+ */
+void
+sys_cleanup()
+{
+    if (if_is_up)
+	sifdown(0);
+    if (ifaddrs[0])
+	cifaddr(0, ifaddrs[0], ifaddrs[1]);
+    if (default_route_gateway)
+	cifdefaultroute(0, 0, default_route_gateway);
+    if (proxy_arp_addr)
+	cifproxyarp(0, proxy_arp_addr);
+}
+
+/*
+ * sys_close - Clean up in a child process before execing.
+ */
+void
+sys_close()
+{
+    close(iffd);
+    close(pppfd);
+    close(sockfd);
+    closelog();
+}
+
+/*
+ * sys_check_options - check the options that the user specified
+ */
+void
+sys_check_options()
+{
+}
+
+
+/*
+ * daemon - Detach us from controlling terminal session.
+ */
+int
+daemon(nochdir, noclose)
+    int nochdir, noclose;
+{
+    int pid;
+
+    if ((pid = fork()) < 0)
+	return -1;
+    if (pid != 0)
+	exit(0);		/* parent dies */
+    setsid();
+    if (!nochdir)
+	chdir("/");
+    if (!noclose) {
+	fclose(stdin);		/* don't need stdin, stdout, stderr */
+	fclose(stdout);
+	fclose(stderr);
+    }
+    return 0;
+}
+
+/*
+ * note_debug_level - note a change in the debug level.
+ */
+void
+note_debug_level()
+{
+    if (debug) {
+	setlogmask(LOG_UPTO(LOG_DEBUG));
+    } else {
+	setlogmask(LOG_UPTO(LOG_WARNING));
+    }
+}
+
+/*
+ * ppp_available - check whether the system has any ppp interfaces
+ */
+int
+ppp_available()
+{
+    struct stat buf;
+
+    return stat("/dev/streams/ppp", &buf) >= 0;
+}
+
+char pipename[] = "/dev/streams/pipe";
+
+/*
+ *  streampipe -- Opens a STREAMS based pipe.  Used by streamify().
+ */
+
+int 
+streampipe(int fd[2])
+{
+    if ((fd[0]=open(pipename, O_RDWR)) == -1)
+	return(-1);
+    else if ((fd[1]=open(pipename, O_RDWR)) == -1) {
+	close(fd[0]);
+	return(-1);
+    } else if (ioctl(fd[0], I_PIPE, fd[1]) != 0) {
+	close(fd[0]);
+	close(fd[1]);
+	return(-1);
+    } else {
+	return(ioctl(fd[0], I_PUSH, "pipemod"));
+    }
+}
+
+/*
+ *  streamify -- Needed for Digital UNIX, since some tty devices are not STREAMS
+ *               modules (but ptys are, and pipes can be).
+ */
+
+#define BUFFSIZE 1000     /*  Size of buffer for streamify()  */
+
+int 
+streamify(int fd)
+{
+    int fdes[2];
+    fd_set readfds;
+    int ret, fret, rret, maxfd;
+    static char buffer[BUFFSIZE];
+    struct sigaction sa;
+
+    if (streampipe(fdes) != 0)
+	syslog(LOG_ERR, "streampipe(): %m\n");
+    else if (isastream(fdes[0]) == 1) {
+	if ((fret=fork()) < 0) {
+	    syslog(LOG_ERR, "fork(): %m\n");
+	} else if (fret == 0) {
+	    /*  Process to forward things from pipe to tty  */
+            sigemptyset(&(sa.sa_mask));
+	    sa.sa_handler = SIG_DFL;
+	    sa.sa_flags = 0;
+	    sigaction(SIGHUP, &sa, NULL);   /*  Go back to default actions */
+	    sigaction(SIGINT, &sa, NULL);   /*  for changed signals.  */
+	    sigaction(SIGTERM, &sa, NULL);
+	    sigaction(SIGCHLD, &sa, NULL);
+	    sigaction(SIGUSR1, &sa, NULL);
+	    sigaction(SIGUSR2, &sa, NULL);
+	    close(fdes[0]);
+
+	    maxfd = (fdes[1]>fd)?fdes[1]:fd;
+	    while (1) {
+		FD_ZERO(&readfds);
+		FD_SET(fdes[1], &readfds);
+		FD_SET(fd, &readfds);
+		ret = select(maxfd+1, &readfds, NULL, NULL, NULL);
+		if (FD_ISSET(fd, &readfds)) {
+		    rret = read(fd, buffer, BUFFSIZE);
+		    if (rret == 0) {
+			MAINDEBUG((LOG_DEBUG, "slave died:  EOF on tty."));
+			exit(0);
+		    } else {
+			write(fdes[1], buffer, rret);
+		    }
+		}
+		if (FD_ISSET(fdes[1], &readfds)) {
+		    rret = read(fdes[1], buffer, BUFFSIZE);
+		    if (rret == 0) {
+			MAINDEBUG((LOG_DEBUG, "slave died:  EOF on pipe."));
+			exit(0);
+		    } else {
+			write(fd, buffer, rret);
+		    }
+		}
+	    }
+	} else {
+	    close(fdes[1]);
+	    orig_ttyfd = fd;
+	    return(fdes[0]);
+        }
+    }
+
+    return(-1);
+}
+
+/*
+ * establish_ppp - Turn the serial port into a ppp interface.
+ */
+void
+establish_ppp(fd)
+    int fd;
+{
+    int i;
+
+    if (isastream(fd) != 1) {
+	if ((ttyfd = fd = streamify(fd)) < 0) {
+	    syslog(LOG_ERR, "Couldn't get a STREAMS module!\n");
+	    die(1);
+	}
+    }
+
+    /* Pop any existing modules off the tty stream. */
+    for (i = 0;; ++i) {
+	if (ioctl(fd, I_LOOK, tty_modules[i]) < 0
+	    || ioctl(fd, I_POP, 0) < 0)
+	    break;
+        syslog(LOG_ERR, "popping module %s\n", tty_modules[i]);
+    }
+
+    tty_nmodules = i;
+
+    /* Push the async hdlc module and the compressor module. */
+    if (ioctl(fd, I_PUSH, "ppp_ahdl") < 0) {
+	syslog(LOG_ERR, "Couldn't push PPP Async HDLC module: %m");
+	die(1);
+    }
+    if (ioctl(fd, I_PUSH, "ppp_comp") < 0) {
+	syslog(LOG_ERR, "Couldn't push PPP compression module: %m");
+/*	die(1); */
+    }
+
+    /* read mode, message non-discard mode */
+    if (ioctl(fd, I_SRDOPT, RMSGN|RPROTNORM) < 0) {
+        syslog(LOG_ERR, "ioctl(I_SRDOPT, RMSGN): %m");
+        die(1);
+    }
+
+    /* Link the serial port under the PPP multiplexor. */
+    if ((fdmuxid = ioctl(pppfd, I_LINK, fd)) < 0) {
+	syslog(LOG_ERR, "Can't link tty to PPP mux: %m");
+	die(1);
+    }
+
+    /* close stdin, stdout, stderr if they might refer to the device */
+    if (default_device && !closed_stdio) {
+        int i;
+
+        for (i = 0; i <= 2; ++i)
+            if (i != fd && i != sockfd)
+                close(i);
+        closed_stdio = 1;
+    }
+
+    /*
+     * Set device for non-blocking reads.
+     */
+    if ((initfdflags = fcntl(fd, F_GETFL)) == -1
+        || fcntl(fd, F_SETFL, initfdflags | O_NONBLOCK) == -1) {
+        syslog(LOG_WARNING, "Couldn't set device to non-blocking mode: %m");
+    }
+}
+
+/*
+ * restore_loop - reattach the ppp unit to the loopback.
+ * This doesn't need to do anything because disestablish_ppp does it.
+ */
+void
+restore_loop()
+{
+}
+
+/*
+ * disestablish_ppp - Restore the serial port to normal operation.
+ * It attempts to reconstruct the stream with the previously popped
+ * modules.  This shouldn't call die() because it's called from die().
+ */
+void
+disestablish_ppp(fd)
+    int fd;
+{
+    int i;
+
+    if (fdmuxid >= 0) {
+	if (ioctl(pppfd, I_UNLINK, fdmuxid) < 0) {
+	    if (!hungup)
+		syslog(LOG_ERR, "Can't unlink tty from PPP mux: %m");
+	}
+	fdmuxid = -1;
+
+        /* Reset non-blocking mode on the file descriptor. */
+        if (initfdflags != -1 && fcntl(fd, F_SETFL, initfdflags) < 0)
+            syslog(LOG_WARNING, "Couldn't restore device fd flags: %m");
+        initfdflags = -1;
+
+	if (!hungup) {
+	    while (ioctl(fd, I_POP, 0) >= 0)
+		;
+	    for (i = tty_nmodules - 1; i >= 0; --i)
+		if (ioctl(fd, I_PUSH, tty_modules[i]) < 0)
+		    syslog(LOG_ERR, "Couldn't restore tty module %s: %m",
+			   tty_modules[i]);
+	}
+
+	if (hungup && default_device && tty_sid > 0) {
+	    /*
+	     * If we have received a hangup, we need to send a SIGHUP
+	     * to the terminal's controlling process.  The reason is
+	     * that the original stream head for the terminal hasn't
+	     * seen the M_HANGUP message (it went up through the ppp
+	     * driver to the stream head for our fd to /dev/ppp).
+	     */
+	    syslog(LOG_DEBUG, "sending hangup to %d", tty_sid);
+	    if (kill(tty_sid, SIGHUP) < 0)
+		syslog(LOG_ERR, "couldn't kill pgrp: %m");
+	}
+	if (orig_ttyfd >= 0) {
+	    close(fd);
+	    (void)wait((void *)0);
+	    ttyfd = orig_ttyfd;
+	    orig_ttyfd = -1;
+	}
+    }
+}
+
+/*
+ * Check whether the link seems not to be 8-bit clean.
+ */
+void
+clean_check()
+{
+    int x;
+    char *s;
+
+    if (strioctl(pppfd, PPPIO_GCLEAN, &x, 0, sizeof(x)) < 0)
+	return;
+    s = NULL;
+    switch (~x) {
+    case RCV_B7_0:
+	s = "bit 7 set to 1";
+	break;
+    case RCV_B7_1:
+	s = "bit 7 set to 0";
+	break;
+    case RCV_EVNP:
+	s = "odd parity";
+	break;
+    case RCV_ODDP:
+	s = "even parity";
+	break;
+    }
+    if (s != NULL) {
+	syslog(LOG_WARNING, "Serial link is not 8-bit clean:");
+	syslog(LOG_WARNING, "All received characters had %s", s);
+    }
+}
+
+/*
+ * List of valid speeds.
+ */
+struct speed {
+    int speed_int, speed_val;
+} speeds[] = {
+#ifdef B50
+    { 50, B50 },
+#endif
+#ifdef B75
+    { 75, B75 },
+#endif
+#ifdef B110
+    { 110, B110 },
+#endif
+#ifdef B134
+    { 134, B134 },
+#endif
+#ifdef B150
+    { 150, B150 },
+#endif
+#ifdef B200
+    { 200, B200 },
+#endif
+#ifdef B300
+    { 300, B300 },
+#endif
+#ifdef B600
+    { 600, B600 },
+#endif
+#ifdef B1200
+    { 1200, B1200 },
+#endif
+#ifdef B1800
+    { 1800, B1800 },
+#endif
+#ifdef B2000
+    { 2000, B2000 },
+#endif
+#ifdef B2400
+    { 2400, B2400 },
+#endif
+#ifdef B3600
+    { 3600, B3600 },
+#endif
+#ifdef B4800
+    { 4800, B4800 },
+#endif
+#ifdef B7200
+    { 7200, B7200 },
+#endif
+#ifdef B9600
+    { 9600, B9600 },
+#endif
+#ifdef B19200
+    { 19200, B19200 },
+#endif
+#ifdef B38400
+    { 38400, B38400 },
+#endif
+#ifdef EXTA
+    { 19200, EXTA },
+#endif
+#ifdef EXTB
+    { 38400, EXTB },
+#endif
+#ifdef B57600
+    { 57600, B57600 },
+#endif
+#ifdef B115200
+    { 115200, B115200 },
+#endif
+    { 0, 0 }
+};
+
+/*
+ * Translate from bits/second to a speed_t.
+ */
+static int
+translate_speed(bps)
+    int bps;
+{
+    struct speed *speedp;
+
+    if (bps == 0)
+	return 0;
+    for (speedp = speeds; speedp->speed_int; speedp++)
+	if (bps == speedp->speed_int)
+	    return speedp->speed_val;
+    syslog(LOG_WARNING, "speed %d not supported", bps);
+    return 0;
+}
+
+/*
+ * Translate from a speed_t to bits/second.
+ */
+static int
+baud_rate_of(speed)
+    int speed;
+{
+    struct speed *speedp;
+
+    if (speed == 0)
+	return 0;
+    for (speedp = speeds; speedp->speed_int; speedp++)
+	if (speed == speedp->speed_val)
+	    return speedp->speed_int;
+    return 0;
+}
+
+/*
+ * set_up_tty: Set up the serial port on `fd' for 8 bits, no parity,
+ * at the requested speed, etc.  If `local' is true, set CLOCAL
+ * regardless of whether the modem option was specified.
+ */
+void
+set_up_tty(fd, local)
+    int fd, local;
+{
+    int speed;
+    struct termios tios;
+
+    if (tcgetattr(fd, &tios) < 0) {
+	syslog(LOG_ERR, "tcgetattr: %m");
+	die(1);
+    }
+
+    if (!restore_term) {
+	inittermios = tios;
+	ioctl(fd, TIOCGWINSZ, &wsinfo);
+    }
+
+    tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CLOCAL);
+    if (crtscts > 0)
+	tios.c_cflag |= CRTSCTS;
+    else if (crtscts < 0)
+	tios.c_cflag &= ~CRTSCTS;
+
+    tios.c_cflag |= CS8 | CREAD | HUPCL;
+    if (local || !modem)
+	tios.c_cflag |= CLOCAL;
+    tios.c_iflag = IGNBRK | IGNPAR;
+    tios.c_oflag = 0;
+    tios.c_lflag = 0;
+    tios.c_cc[VMIN] = 1;
+    tios.c_cc[VTIME] = 0;
+
+    if (crtscts == -2) {
+	tios.c_iflag |= IXON | IXOFF;
+	tios.c_cc[VSTOP] = 0x13;	/* DC3 = XOFF = ^S */
+	tios.c_cc[VSTART] = 0x11;	/* DC1 = XON  = ^Q */
+    }
+
+    speed = translate_speed(inspeed);
+    if (speed) {
+	cfsetospeed(&tios, speed);
+	cfsetispeed(&tios, speed);
+    } else {
+	speed = cfgetospeed(&tios);
+	/*
+	 * We can't proceed if the serial port speed is 0,
+	 * since that implies that the serial port is disabled.
+	 */
+	if (speed == B0) {
+	    syslog(LOG_ERR, "Baud rate for %s is 0; need explicit baud rate",
+		   devnam);
+	    die(1);
+	}
+    }
+
+    if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) {
+	syslog(LOG_ERR, "tcsetattr: %m");
+	die(1);
+    }
+
+    baud_rate = inspeed = baud_rate_of(speed);
+    restore_term = 1;
+}
+
+/*
+ * restore_tty - restore the terminal to the saved settings.
+ */
+void
+restore_tty(fd)
+    int fd;
+{
+    if (restore_term) {
+	if (!default_device) {
+	    /*
+	     * Turn off echoing, because otherwise we can get into
+	     * a loop with the tty and the modem echoing to each other.
+	     * We presume we are the sole user of this tty device, so
+	     * when we close it, it will revert to its defaults anyway.
+	     */
+	    inittermios.c_lflag &= ~(ECHO | ECHONL);
+	}
+	if (tcsetattr(fd, TCSAFLUSH, &inittermios) < 0)
+	    if (!hungup && errno != ENXIO)
+		syslog(LOG_WARNING, "tcsetattr: %m");
+	ioctl(fd, TIOCSWINSZ, &wsinfo);
+	restore_term = 0;
+    }
+}
+
+/*
+ * setdtr - control the DTR line on the serial port.
+ * This is called from die(), so it shouldn't call die().
+ */
+void
+setdtr(fd, on)
+int fd, on;
+{
+    int modembits = TIOCM_DTR;
+
+    ioctl(fd, (on? TIOCMBIS: TIOCMBIC), &modembits);
+}
+
+/*
+ * open_loopback - open the device we use for getting packets
+ * in demand mode.  Under Solaris 2, we use our existing fd
+ * to the ppp driver.
+ */
+void
+open_ppp_loopback()
+{
+}
+
+/*
+ * output - Output PPP packet.
+ */
+void
+output(unit, p, len)
+    int unit;
+    u_char *p;
+    int len;
+{
+    struct strbuf data;
+    int retries;
+    struct pollfd pfd;
+
+    if (debug)
+	log_packet(p, len, "sent ", LOG_DEBUG);
+
+    data.len = len;
+    data.buf = (caddr_t) p;
+    retries = 4;
+    while (putmsg(pppfd, NULL, &data, 0) < 0) {
+	if (--retries < 0 || (errno != EWOULDBLOCK && errno != EAGAIN)) {
+	    if (errno != ENXIO)
+		syslog(LOG_ERR, "Couldn't send packet: %m");
+	    break;
+	}
+	pfd.fd = pppfd;
+	pfd.events = POLLOUT;
+	poll(&pfd, 1, 250);	/* wait for up to 0.25 seconds */
+    }
+}
+
+
+/*
+ * wait_input - wait until there is data available on fd,
+ * for the length of time specified by *timo (indefinite
+ * if timo is NULL).
+ */
+void
+wait_input(timo)
+    struct timeval *timo;
+{
+    int t;
+    struct pollfd pfd;
+
+    t = timo == NULL? -1: timo->tv_sec * 1000 + timo->tv_usec / 1000;
+    pfd.fd = pppfd;
+    pfd.events = POLLIN | POLLPRI | POLLHUP;
+    if (poll(&pfd, 1, t) < 0 && errno != EINTR) {
+	syslog(LOG_ERR, "poll: %m");
+	die(1);
+    }
+}
+
+/*
+ * wait_loop_output - wait until there is data available on the
+ * loopback, for the length of time specified by *timo (indefinite
+ * if timo is NULL).
+ */
+void
+wait_loop_output(timo)
+    struct timeval *timo;
+{
+    wait_input(timo);
+}
+
+/*
+ * wait_time - wait for a given length of time or until a
+ * signal is received.
+ */
+void
+wait_time(timo)
+    struct timeval *timo;
+{
+    int n;
+
+    n = select(0, NULL, NULL, NULL, timo);
+    if (n < 0 && errno != EINTR) {
+	syslog(LOG_ERR, "select: %m");
+	die(1);
+    }
+}
+
+
+/*
+ * read_packet - get a PPP packet from the serial device.
+ */
+int
+read_packet(buf)
+    u_char *buf;
+{
+    struct strbuf ctrl, data;
+    int flags, len;
+    unsigned char ctrlbuf[64];
+
+    for (;;) {
+	data.maxlen = PPP_MRU + PPP_HDRLEN;
+	data.buf = (caddr_t) buf;
+	ctrl.maxlen = sizeof(ctrlbuf);
+	ctrl.buf = (caddr_t) ctrlbuf;
+	flags = 0;
+	len = getmsg(pppfd, &ctrl, &data, &flags);
+	if (len < 0) {
+	    if (errno = EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
+		return -1;
+	    syslog(LOG_ERR, "Error reading packet: %m");
+	    die(1);
+	}
+
+	if (ctrl.len <= 0)
+	    return data.len;
+
+	/*
+	 * Got a M_PROTO or M_PCPROTO message.  Huh?
+	 */
+	if (debug)
+	    syslog(LOG_DEBUG, "got ctrl msg len=%d", ctrl.len);
+
+    }
+}
+
+/*
+ * get_loop_output - get outgoing packets from the ppp device,
+ * and detect when we want to bring the real link up.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ */
+int
+get_loop_output()
+{
+    int len;
+    int rv = 0;
+
+    while ((len = read_packet(inpacket_buf)) > 0) {
+	if (loop_frame(inpacket_buf, len))
+	    rv = 1;
+    }
+    return rv;
+}
+
+/*
+ * ppp_send_config - configure the transmit characteristics of
+ * the ppp interface.
+ */
+void
+ppp_send_config(unit, mtu, asyncmap, pcomp, accomp)
+    int unit, mtu;
+    u_int32_t asyncmap;
+    int pcomp, accomp;
+{
+    int cf[2];
+
+    link_mtu = mtu;
+    if (strioctl(pppfd, PPPIO_MTU, &mtu, sizeof(mtu), 0) < 0) {
+	if (hungup && errno == ENXIO)
+	    return;
+	syslog(LOG_ERR, "Couldn't set MTU: %m");
+    }
+    if (strioctl(pppfd, PPPIO_XACCM, &asyncmap, sizeof(asyncmap), 0) < 0) {
+	syslog(LOG_ERR, "Couldn't set transmit ACCM: %m");
+    }
+    cf[0] = (pcomp? COMP_PROT: 0) + (accomp? COMP_AC: 0);
+    cf[1] = COMP_PROT | COMP_AC;
+    if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+	syslog(LOG_ERR, "Couldn't set prot/AC compression: %m");
+    }
+}
+
+/*
+ * ppp_set_xaccm - set the extended transmit ACCM for the interface.
+ */
+void
+ppp_set_xaccm(unit, accm)
+    int unit;
+    ext_accm accm;
+{
+    if (strioctl(pppfd, PPPIO_XACCM, accm, sizeof(ext_accm), 0) < 0) {
+	if (!hungup || errno != ENXIO)
+	    syslog(LOG_WARNING, "Couldn't set extended ACCM: %m");
+    }
+}
+
+/*
+ * ppp_recv_config - configure the receive-side characteristics of
+ * the ppp interface.
+ */
+void
+ppp_recv_config(unit, mru, asyncmap, pcomp, accomp)
+    int unit, mru;
+    u_int32_t asyncmap;
+    int pcomp, accomp;
+{
+    int cf[2];
+
+    link_mru = mru;
+    if (strioctl(pppfd, PPPIO_MRU, &mru, sizeof(mru), 0) < 0) {
+	if (hungup && errno == ENXIO)
+	    return;
+	syslog(LOG_ERR, "Couldn't set MRU: %m");
+    }
+    if (strioctl(pppfd, PPPIO_RACCM, &asyncmap, sizeof(asyncmap), 0) < 0) {
+	syslog(LOG_ERR, "Couldn't set receive ACCM: %m");
+    }
+    cf[0] = (pcomp? DECOMP_PROT: 0) + (accomp? DECOMP_AC: 0);
+    cf[1] = DECOMP_PROT | DECOMP_AC;
+    if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+	syslog(LOG_ERR, "Couldn't set prot/AC decompression: %m");
+    }
+}
+
+/*
+ * ccp_test - ask kernel whether a given compression method
+ * is acceptable for use.
+ *
+ * In Digital UNIX the memory buckets for chunks >16K are not
+ * primed when the system comes up.  That means we're not
+ * likely to get the memory needed for the compressor on
+ * the first try.  The way we work around this is to have
+ * the driver spin off a thread to go get the memory for us
+ * (we can't block at that point in a streams context.)
+ *
+ * This code synchronizes with the thread when it has returned
+ * with the memory we need.  The driver will continue to return
+ * with EAGAIN until the thread comes back.  We give up here
+ * if after 10 attempts in one second we still don't have memory.
+ * It's up to the driver to not lose track of that memory if
+ * thread takes too long to return.
+ */
+int
+ccp_test(unit, opt_ptr, opt_len, for_transmit)
+    int unit, opt_len, for_transmit;
+    u_char *opt_ptr;
+{
+    struct timeval tval;
+    int i;
+
+    tval.tv_sec = 0;
+    tval.tv_usec = 100000;
+    for (i = 0; i < 10; ++i) {
+        if (strioctl(pppfd, (for_transmit? PPPIO_XCOMP: PPPIO_RCOMP),
+	    opt_ptr, opt_len, 0) >= 0) {
+	    return 1;
+	}
+	if (errno != EAGAIN)
+	    break;
+        wait_time(&tval);
+    }
+    if (errno != 0)
+	syslog(LOG_ERR, "hard failure trying to get memory for a compressor: %m");
+    return (errno == ENOSR)? 0: -1;
+}
+
+/*
+ * ccp_flags_set - inform kernel about the current state of CCP.
+ */
+void
+ccp_flags_set(unit, isopen, isup)
+    int unit, isopen, isup;
+{
+    int cf[2];
+
+    cf[0] = (isopen? CCP_ISOPEN: 0) + (isup? CCP_ISUP: 0);
+    cf[1] = CCP_ISOPEN | CCP_ISUP | CCP_ERROR | CCP_FATALERROR;
+    if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+	if (!hungup || errno != ENXIO)
+	    syslog(LOG_ERR, "Couldn't set kernel CCP state: %m");
+    }
+}
+
+/*
+ * get_idle_time - return how long the link has been idle.
+ */
+int
+get_idle_time(u, ip)
+    int u;
+    struct ppp_idle *ip;
+{
+    return strioctl(pppfd, PPPIO_GIDLE, ip, 0, sizeof(struct ppp_idle)) >= 0;
+}
+
+
+/*
+ * ccp_fatal_error - returns 1 if decompression was disabled as a
+ * result of an error detected after decompression of a packet,
+ * 0 otherwise.  This is necessary because of patent nonsense.
+ */
+int
+ccp_fatal_error(unit)
+    int unit;
+{
+    int cf[2];
+
+    cf[0] = cf[1] = 0;
+    if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+	if (errno != ENXIO && errno != EINVAL)
+	    syslog(LOG_ERR, "Couldn't get compression flags: %m");
+	return 0;
+    }
+    return cf[0] & CCP_FATALERROR;
+}
+
+/*
+ * sifvjcomp - config tcp header compression
+ */
+int
+sifvjcomp(u, vjcomp, xcidcomp, xmaxcid)
+    int u, vjcomp, xcidcomp, xmaxcid;
+{
+    int cf[2];
+    char maxcid[2];
+
+    if (vjcomp) {
+	maxcid[0] = xcidcomp;
+	maxcid[1] = 15;		/* XXX should be rmaxcid */
+	if (strioctl(pppfd, PPPIO_VJINIT, maxcid, sizeof(maxcid), 0) < 0) {
+	    syslog(LOG_ERR, "Couldn't initialize VJ compression: %m");
+	}
+    }
+
+    cf[0] = (vjcomp? COMP_VJC + DECOMP_VJC: 0)	/* XXX this is wrong */
+	+ (xcidcomp? COMP_VJCCID + DECOMP_VJCCID: 0);
+    cf[1] = COMP_VJC + DECOMP_VJC + COMP_VJCCID + DECOMP_VJCCID;
+    if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+	if (vjcomp)
+	    syslog(LOG_ERR, "Couldn't enable VJ compression: %m");
+    }
+
+    return 1;
+}
+
+/*
+ * sifup - Config the interface up and enable IP packets to pass.
+ */
+int
+sifup(u)
+    int u;
+{
+    struct ifreq ifr;
+
+    strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+    if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
+	syslog(LOG_ERR, "Couldn't mark interface up (get): %m");
+	return 0;
+    }
+    ifr.ifr_flags |= IFF_UP;
+    if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
+	syslog(LOG_ERR, "Couldn't mark interface up (set): %m");
+	return 0;
+    }
+    if_is_up = 1;
+    return 1;
+}
+
+/*
+ * sifdown - Config the interface down and disable IP.
+ */
+int
+sifdown(u)
+    int u;
+{
+    struct ifreq ifr;
+
+    strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+    if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
+	syslog(LOG_ERR, "Couldn't mark interface down (get): %m");
+	return 0;
+    }
+    if ((ifr.ifr_flags & IFF_UP) != 0) {
+	ifr.ifr_flags &= ~IFF_UP;
+	if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
+	    syslog(LOG_ERR, "Couldn't mark interface down (set): %m");
+	    return 0;
+	}
+    }
+    if_is_up = 0;
+    return 1;
+}
+
+/*
+ * sifnpmode - Set the mode for handling packets for a given NP.
+ */
+int
+sifnpmode(u, proto, mode)
+    int u;
+    int proto;
+    enum NPmode mode;
+{
+    int npi[2];
+
+    npi[0] = proto;
+    npi[1] = (int) mode;
+    if (strioctl(pppfd, PPPIO_NPMODE, npi, 2 * sizeof(int), 0) < 0) {
+	syslog(LOG_ERR, "ioctl(set NP %d mode to %d): %m", proto, mode);
+	return 0;
+    }
+    return 1;
+}
+
+#define INET_ADDR(x)	(((struct sockaddr_in *) &(x))->sin_addr.s_addr)
+
+/*
+ * SET_SA_FAMILY - initialize a struct sockaddr, setting the sa_family field.
+ */
+#define SET_SA_FAMILY(addr, family)             \
+    BZERO((char *) &(addr), sizeof(addr));      \
+    addr.sa_family = (family);                  \
+    addr.sa_len = sizeof ((addr))
+
+/*
+ * sifaddr - Config the interface IP addresses and netmask.
+ */
+int
+sifaddr(u, o, h, m)
+    int u;
+    u_int32_t o, h, m;
+{
+    struct ifreq ifr;
+    struct ifaliasreq addreq;
+    int ret;
+
+    ret = 1;
+
+    /* flush old address, if any
+     */
+    bzero(&ifr, sizeof (ifr));
+    strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+    SET_SA_FAMILY(ifr.ifr_addr, AF_INET);
+    ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr = o;
+    if ((ioctl(sockfd, (int)SIOCDIFADDR, (caddr_t) &ifr) < 0)
+        && errno != EADDRNOTAVAIL) {
+        syslog(LOG_ERR, "ioctl(SIOCDIFADDR): %m");
+        ret = 0;
+    }
+
+    bzero(&addreq, sizeof (addreq));
+    strncpy(addreq.ifra_name, ifname, sizeof (addreq.ifra_name));
+    SET_SA_FAMILY(addreq.ifra_addr, AF_INET);
+    SET_SA_FAMILY(addreq.ifra_broadaddr, AF_INET);
+    ((struct sockaddr_in *)&addreq.ifra_addr)->sin_addr.s_addr = o;
+    ((struct sockaddr_in *)&addreq.ifra_broadaddr)->sin_addr.s_addr = h;
+
+    if (m != 0) {
+        ((struct sockaddr_in *)&addreq.ifra_mask)->sin_addr.s_addr = m;
+        addreq.ifra_mask.sa_len = sizeof (struct sockaddr);
+        syslog(LOG_INFO, "Setting interface mask to %s\n", ip_ntoa(m));
+    }
+
+    /* install new src/dst and (possibly) netmask
+     */
+    if (ioctl(sockfd, SIOCPIFADDR, &addreq) < 0) {
+        syslog(LOG_ERR, "ioctl(SIOCPIFADDR): %m");
+        ret = 0;
+    }
+
+    ifr.ifr_metric = link_mtu;
+    if (ioctl(sockfd, SIOCSIPMTU, &ifr) < 0) {
+	syslog(LOG_ERR, "Couldn't set IP MTU: %m");
+        ret = 0;
+    }
+
+    ifaddrs[0] = o;
+    ifaddrs[1] = h;
+    return (ret);
+}
+
+
+/*
+ * cifaddr - Clear the interface IP addresses, and delete routes
+ * through the interface if possible.
+ */
+int
+cifaddr(u, o, h)
+    int u;
+    u_int32_t o, h;
+{
+    struct ifreq ifr;
+
+    ifaddrs[0] = 0;
+    bzero(&ifr, sizeof (ifr));
+    strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+    SET_SA_FAMILY(ifr.ifr_addr, AF_INET);
+    ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr = o;
+    if (ioctl(sockfd, (int)SIOCDIFADDR, (caddr_t) &ifr) < 0) {
+        syslog(LOG_ERR, "ioctl(SIOCDIFADDR): %m");
+        return 0;
+    }
+    return 1;
+}
+
+
+/*
+ * sifdefaultroute - assign a default route through the address given.
+ */
+int
+sifdefaultroute(u, l, g)
+    int u;
+    u_int32_t l, g;
+{
+    struct ortentry rt;
+
+    BZERO(&rt, sizeof(rt));
+    SET_SA_FAMILY(rt.rt_dst, AF_INET);
+    SET_SA_FAMILY(rt.rt_gateway, AF_INET);
+    ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = g;
+    rt.rt_flags = RTF_GATEWAY;
+    if (ioctl(sockfd, (int)SIOCADDRT, &rt) < 0) {
+        syslog(LOG_ERR, "default route ioctl(SIOCADDRT): %m");
+        return 0;
+    }
+    default_route_gateway = g;
+    return 1;
+}
+
+
+/*
+ * cifdefaultroute - delete a default route through the address given.
+ */
+int
+cifdefaultroute(u, l, g)
+    int u;
+    u_int32_t l, g;
+{
+    struct ortentry rt;
+
+    BZERO(&rt, sizeof(rt));
+    SET_SA_FAMILY(rt.rt_dst, AF_INET);
+    SET_SA_FAMILY(rt.rt_gateway, AF_INET);
+    ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = g;
+    rt.rt_flags = RTF_GATEWAY;
+    if (ioctl(sockfd, (int)SIOCDELRT, &rt) < 0) {
+        syslog(LOG_ERR, "default route ioctl(SIOCDELRT): %m");
+        return 0;
+    }
+    default_route_gateway = 0;
+    return 1;
+}
+
+/*
+ * sifproxyarp - Make a proxy ARP entry for the peer.
+ */
+int
+sifproxyarp(unit, hisaddr)
+    int unit;
+    u_int32_t hisaddr;
+{
+    struct arpreq arpreq;
+
+    BZERO(&arpreq, sizeof(arpreq));
+
+    /*
+     * Get the hardware address of an interface on the same subnet
+     * as our local address.
+     */
+    if (!get_ether_addr(hisaddr, &arpreq.arp_ha)) {
+        syslog(LOG_WARNING, "Cannot determine ethernet address for proxy ARP");
+        return 0;
+    }
+
+    SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
+    ((struct sockaddr_in *) &arpreq.arp_pa)->sin_addr.s_addr = hisaddr;
+    arpreq.arp_flags = ATF_PERM | ATF_PUBL;
+    if (ioctl(sockfd, (int)SIOCSARP, (caddr_t)&arpreq) < 0) {
+        syslog(LOG_ERR, "ioctl(SIOCSARP): %m");
+        return 0;
+    }
+
+    proxy_arp_addr = hisaddr;
+    return 1;
+}
+
+
+/*
+ * cifproxyarp - Delete the proxy ARP entry for the peer.
+ */
+int
+cifproxyarp(unit, hisaddr)
+    int unit;
+    u_int32_t hisaddr;
+{
+    struct arpreq arpreq;
+
+    BZERO(&arpreq, sizeof(arpreq));
+    SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
+    ((struct sockaddr_in *) &arpreq.arp_pa)->sin_addr.s_addr = hisaddr;
+    if (ioctl(sockfd, (int)SIOCDARP, (caddr_t)&arpreq) < 0) {
+        syslog(LOG_ERR, "ioctl(SIOCDARP): %m");
+        return 0;
+    }
+    proxy_arp_addr = 0;
+    return 1;
+}
+
+/*
+ * get_ether_addr - get the hardware address of an interface on the
+ * the same subnet as ipaddr.
+ */
+#define MAX_IFS		32
+
+static int
+get_ether_addr(ipaddr, hwaddr)
+    u_int32_t ipaddr;
+    struct sockaddr *hwaddr;
+{
+    struct ifreq *ifr, *ifend;
+    u_int32_t ina, mask;
+    struct ifreq ifreq;
+    struct ifconf ifc;
+    struct ifreq ifs[MAX_IFS];
+    struct ifdevea ifdevreq;
+
+    ifc.ifc_len = sizeof(ifs);
+    ifc.ifc_req = ifs;
+    if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
+	syslog(LOG_ERR, "ioctl(SIOCGIFCONF): %m");
+	return 0;
+    }
+
+    /*
+     * Scan through looking for an interface with an Internet
+     * address on the same subnet as `ipaddr'.
+     */
+    ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+    for (ifr = ifc.ifc_req; ifr < ifend; ifr++) {
+        if (ifr->ifr_addr.sa_family == AF_INET) {
+
+            /*
+             * Check that the interface is up, and not point-to-point
+             * or loopback.
+             */
+            strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+            if (ioctl(sockfd, SIOCGIFFLAGS, &ifreq) < 0)
+                continue;
+            if ((ifreq.ifr_flags &
+                 (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP))
+                 != (IFF_UP|IFF_BROADCAST))
+                continue;
+
+            /*
+             * Get its netmask and check that it's on the right subnet.
+             */
+            if (ioctl(sockfd, SIOCGIFNETMASK, &ifreq) < 0)
+                continue;
+            ina = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr;
+            mask = ((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr.s_addr;
+            if ((ipaddr & mask) != (ina & mask))
+                continue;
+
+            break;
+        } else {
+	    if (ifr->ifr_addr.sa_len > sizeof (ifr->ifr_addr))
+		ifr = (struct ifreq *)((caddr_t)ifr + (ifr->ifr_addr.sa_len - sizeof (ifr->ifr_addr)));
+	}
+    }
+
+    if (ifr >= ifend)
+	return 0;
+    syslog(LOG_INFO, "found interface %s for proxy arp", ifr->ifr_name);
+
+    strncpy(ifdevreq.ifr_name, ifr->ifr_name, sizeof(ifdevreq.ifr_name));
+
+    if (ioctl(sockfd, (int)SIOCRPHYSADDR, &ifdevreq) < 0) {
+        perror("ioctl(SIOCRPHYSADDR)");
+        return(0);
+    }
+
+    hwaddr->sa_family = AF_UNSPEC;
+    memcpy(hwaddr->sa_data, ifdevreq.current_pa, sizeof(ifdevreq.current_pa));
+    return 1;
+}
+
+#define	WTMPFILE	"/usr/adm/wtmp"
+
+void
+logwtmp(line, name, host)
+    const char *line, *name, *host;
+{
+    int fd;
+    struct stat buf;
+    struct utmp ut;
+
+    if ((fd = open(WTMPFILE, O_WRONLY|O_APPEND, 0)) < 0)
+	return;
+    if (!fstat(fd, &buf)) {
+	(void)strncpy(ut.ut_line, line, sizeof(ut.ut_line));
+	(void)strncpy(ut.ut_name, name, sizeof(ut.ut_name));
+	(void)strncpy(ut.ut_host, host, sizeof(ut.ut_host));
+	(void)time(&ut.ut_time);
+	if (write(fd, (char *)&ut, sizeof(struct utmp)) != sizeof(struct utmp))
+	    (void)ftruncate(fd, buf.st_size);
+    }
+    close(fd);
+}
+
+/*
+ * Return user specified netmask, modified by any mask we might determine
+ * for address `addr' (in network byte order).
+ * Here we scan through the system's list of interfaces, looking for
+ * any non-point-to-point interfaces which might appear to be on the same
+ * network as `addr'.  If we find any, we OR in their netmask to the
+ * user-specified netmask.
+ */
+u_int32_t
+GetMask(addr)
+    u_int32_t addr;
+{
+    u_int32_t mask, nmask, ina;
+    struct ifreq *ifr, *ifend, ifreq;
+    struct ifconf ifc;
+
+    addr = ntohl(addr);
+    if (IN_CLASSA(addr))	/* determine network mask for address class */
+	nmask = IN_CLASSA_NET;
+    else if (IN_CLASSB(addr))
+	nmask = IN_CLASSB_NET;
+    else
+	nmask = IN_CLASSC_NET;
+    /* class D nets are disallowed by bad_ip_adrs */
+    mask = netmask | htonl(nmask);
+
+    /*
+     * Scan through the system's network interfaces.
+     */
+    ifc.ifc_len = MAX_IFS * sizeof(struct ifreq);
+    ifc.ifc_req = (struct ifreq *)alloca(ifc.ifc_len);
+    if (ifc.ifc_req == 0)
+	return mask;
+    if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
+	syslog(LOG_WARNING, "Couldn't get system interface list: %m");
+	return mask;
+    }
+    ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+    for (ifr = ifc.ifc_req; ifr < ifend; ifr++) {
+	/*
+	 * Check the interface's internet address.
+	 */
+	if (ifr->ifr_addr.sa_family == AF_INET) {
+	    ina = INET_ADDR(ifr->ifr_addr);
+	    if ((ntohl(ina) & nmask) != (addr & nmask))
+	        continue;
+	    /*
+	     * Check that the interface is up, and not point-to-point or loopback.
+	     */
+	    strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+	    if (ioctl(sockfd, SIOCGIFFLAGS, &ifreq) < 0)
+	        continue;
+	    if ((ifreq.ifr_flags & (IFF_UP|IFF_POINTOPOINT|IFF_LOOPBACK))
+	        != IFF_UP)
+	        continue;
+	    /*
+	     * Get its netmask and OR it into our mask.
+	     */
+	    if (ioctl(sockfd, SIOCGIFNETMASK, &ifreq) < 0)
+	        continue;
+	    mask |= INET_ADDR(ifreq.ifr_addr);
+	    break;
+	} else {
+	    if (ifr->ifr_addr.sa_len > sizeof (ifr->ifr_addr))
+		ifr = (struct ifreq *)((caddr_t)ifr + (ifr->ifr_addr.sa_len - sizeof (ifr->ifr_addr)));
+	}
+    }
+
+    return mask;
+}
+
+static int
+strioctl(fd, cmd, ptr, ilen, olen)
+    int fd, cmd, ilen, olen;
+    void *ptr;
+{
+    struct strioctl str;
+
+    str.ic_cmd = cmd;
+    str.ic_timout = 0;
+    str.ic_len = ilen;
+    str.ic_dp = ptr;
+    if (ioctl(fd, I_STR, &str) == -1)
+	return -1;
+    if (str.ic_len != olen)
+	syslog(LOG_DEBUG, "strioctl: expected %d bytes, got %d for cmd %x\n",
+	       olen, str.ic_len, cmd);
+    return 0;
+}
+
+/*
+ * Use the hostid as part of the random number seed.
+ */
+int
+get_host_seed()
+{
+    return gethostid();
+}
+
+/*
+ * Code for locking/unlocking the serial device.
+ * This code is derived from chat.c.
+ */
+
+#if !defined(HDB) && !defined(SUNOS3)
+#define	HDB	1		/* ascii lock files are the default */
+#endif
+
+#ifndef LOCK_DIR
+# if HDB
+#  define	PIDSTRING
+#  define	LOCK_PREFIX	"/usr/spool/locks/LCK.."
+# else /* HDB */
+#  define	LOCK_PREFIX	"/usr/spool/uucp/LCK.."
+# endif /* HDB */
+#endif /* LOCK_DIR */
+
+static char *lock_file;		/* name of lock file created */
+
+/*
+ * lock - create a lock file for the named device.
+ */
+int
+lock(dev)
+    char *dev;
+{
+    char hdb_lock_buffer[12];
+    int fd, pid, n;
+    char *p;
+
+    if ((p = strrchr(dev, '/')) != NULL)
+	dev = p + 1;
+    lock_file = malloc(strlen(LOCK_PREFIX) + strlen(dev) + 1);
+    if (lock_file == NULL)
+	novm("lock file name");
+    strcat(strcpy(lock_file, LOCK_PREFIX), dev);
+
+    while ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0) {
+	if (errno == EEXIST
+	    && (fd = open(lock_file, O_RDONLY, 0)) >= 0) {
+	    /* Read the lock file to find out who has the device locked */
+#ifdef PIDSTRING
+	    n = read(fd, hdb_lock_buffer, 11);
+	    if (n > 0) {
+		hdb_lock_buffer[n] = 0;
+		pid = atoi(hdb_lock_buffer);
+	    }
+#else
+	    n = read(fd, &pid, sizeof(pid));
+#endif
+	    if (n <= 0) {
+		syslog(LOG_ERR, "Can't read pid from lock file %s", lock_file);
+		close(fd);
+	    } else {
+		if (kill(pid, 0) == -1 && errno == ESRCH) {
+		    /* pid no longer exists - remove the lock file */
+		    if (unlink(lock_file) == 0) {
+			close(fd);
+			syslog(LOG_NOTICE, "Removed stale lock on %s (pid %d)",
+			       dev, pid);
+			continue;
+		    } else
+			syslog(LOG_WARNING, "Couldn't remove stale lock on %s",
+			       dev);
+		} else
+		    syslog(LOG_NOTICE, "Device %s is locked by pid %d",
+			   dev, pid);
+	    }
+	    close(fd);
+	} else
+	    syslog(LOG_ERR, "Can't create lock file %s: %m", lock_file);
+	free(lock_file);
+	lock_file = NULL;
+	return -1;
+    }
+
+#ifdef PIDSTRING
+    sprintf(hdb_lock_buffer, "%10d\n", getpid());
+    write(fd, hdb_lock_buffer, 11);
+#else
+    pid = getpid();
+    write(fd, &pid, sizeof pid);
+#endif
+
+    close(fd);
+    return 0;
+}
+
+/*
+ * unlock - remove our lockfile
+ */
+void
+unlock()
+{
+    if (lock_file) {
+	unlink(lock_file);
+	free(lock_file);
+	lock_file = NULL;
+    }
+}
+
+int
+set_filters(pass, active)
+    struct bpf_program *pass, *active;
+{
+    return 1;
+}
+
+int
+bpf_compile(program, buf, optimize)
+    struct bpf_program *program;
+    char *buf;
+    int optimize;
+{
+    return 0;
+}
+
+char *
+bpf_geterr()
+{
+    return 0;
+}
+
+u_int
+bpf_filter(pc, p, wirelen, buflen)
+    struct bpf_insn *pc;
+    u_char *p;
+    u_int wirelen;
+    u_int buflen;
+{
+    return 0;
+}
diff -rPu pppd.old/sys-sunos4.c pppd/sys-sunos4.c
--- pppd.old/sys-sunos4.c	Thu Jan  1 03:00:00 1970
+++ pppd/sys-sunos4.c	Wed Mar 25 05:19:29 1998
@@ -0,0 +1,1467 @@
+/*
+ * System-dependent procedures for pppd under SunOS 4.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies.  This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: sys-sunos4.c,v 1.9 1998/03/25 02:19:29 paulus Exp $";
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <termios.h>
+#include <signal.h>
+#include <malloc.h>
+#include <utmp.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#include <sys/syslog.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/nit_if.h>
+#include <net/route.h>
+#include <net/ppp_defs.h>
+#include <net/pppio.h>
+#include <netinet/in.h>
+
+#include "pppd.h"
+
+#if defined(sun) && defined(sparc)
+#include <alloca.h>
+#ifndef __GNUC__
+extern void *alloca();
+#endif
+#endif /*sparc*/
+
+static int	pppfd;
+static int	fdmuxid = -1;
+static int	iffd;
+static int	sockfd;
+
+static int	restore_term;
+static struct termios inittermios;
+static struct winsize wsinfo;	/* Initial window size info */
+static pid_t	parent_pid;	/* PID of our parent */
+
+extern u_char	inpacket_buf[];	/* borrowed from main.c */
+
+static int	link_mtu, link_mru;
+
+#define NMODULES	32
+static int	tty_nmodules;
+static char	tty_modules[NMODULES][FMNAMESZ+1];
+
+static int	if_is_up;	/* Interface has been marked up */
+static u_int32_t ifaddrs[2];	/* local and remote addresses */
+static u_int32_t default_route_gateway;	/* Gateway for default route added */
+static u_int32_t proxy_arp_addr;	/* Addr for proxy arp entry added */
+
+/* Prototypes for procedures local to this file. */
+static int translate_speed __P((int));
+static int baud_rate_of __P((int));
+static int get_ether_addr __P((u_int32_t, struct sockaddr *));
+static int strioctl __P((int, int, void *, int, int));
+
+
+/*
+ * sys_init - System-dependent initialization.
+ */
+void
+sys_init()
+{
+    int x;
+
+    /* Get an internet socket for doing socket ioctl's on. */
+    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+	syslog(LOG_ERR, "Couldn't create IP socket: %m");
+	die(1);
+    }
+
+    /*
+     * We may want to send a SIGHUP to the session leader associated
+     * with our controlling terminal later.  Because SunOS doesn't
+     * have getsid(), we make do with sending the signal to our
+     * parent process.
+     */
+    parent_pid = getppid();
+
+    /*
+     * Open the ppp device.
+     */
+    pppfd = open("/dev/ppp", O_RDWR | O_NONBLOCK, 0);
+    if (pppfd < 0) {
+	syslog(LOG_ERR, "Can't open /dev/ppp: %m");
+	die(1);
+    }
+    if (kdebugflag) {
+	x = PPPDBG_LOG + PPPDBG_DRIVER;
+	strioctl(pppfd, PPPIO_DEBUG, &x, sizeof(int), 0);
+    }
+
+    /* Assign a new PPA and get its unit number. */
+    if (strioctl(pppfd, PPPIO_NEWPPA, &ifunit, 0, sizeof(int)) < 0) {
+	syslog(LOG_ERR, "Can't create new PPP interface: %m");
+	die(1);
+    }
+
+    /*
+     * Open the ppp device again and push the if_ppp module on it.
+     */
+    iffd = open("/dev/ppp", O_RDWR, 0);
+    if (iffd < 0) {
+	syslog(LOG_ERR, "Can't open /dev/ppp (2): %m");
+	die(1);
+    }
+    if (kdebugflag) {
+	x = PPPDBG_LOG + PPPDBG_DRIVER;
+	strioctl(iffd, PPPIO_DEBUG, &x, sizeof(int), 0);
+    }
+    if (strioctl(iffd, PPPIO_ATTACH, &ifunit, sizeof(int), 0) < 0) {
+	syslog(LOG_ERR, "Couldn't attach ppp interface to device: %m");
+	die(1);
+    }
+    if (ioctl(iffd, I_PUSH, "if_ppp") < 0) {
+	syslog(LOG_ERR, "Can't push ppp interface module: %m");
+	die(1);
+    }
+    if (kdebugflag) {
+	x = PPPDBG_LOG + PPPDBG_IF;
+	strioctl(iffd, PPPIO_DEBUG, &x, sizeof(int), 0);
+    }
+    if (strioctl(iffd, PPPIO_NEWPPA, &ifunit, sizeof(int), 0) < 0) {
+	syslog(LOG_ERR, "Couldn't create ppp interface unit: %m");
+	die(1);
+    }
+    x = PPP_IP;
+    if (strioctl(iffd, PPPIO_BIND, &x, sizeof(int), 0) < 0) {
+	syslog(LOG_ERR, "Couldn't bind ppp interface to IP SAP: %m");
+	die(1);
+    }
+}
+
+/*
+ * sys_cleanup - restore any system state we modified before exiting:
+ * mark the interface down, delete default route and/or proxy arp entry.
+ * This shouldn't call die() because it's called from die().
+ */
+void
+sys_cleanup()
+{
+    if (if_is_up)
+	sifdown(0);
+    if (ifaddrs[0])
+	cifaddr(0, ifaddrs[0], ifaddrs[1]);
+    if (default_route_gateway)
+	cifdefaultroute(0, 0, default_route_gateway);
+    if (proxy_arp_addr)
+	cifproxyarp(0, proxy_arp_addr);
+}
+
+/*
+ * sys_close - Clean up in a child process before execing.
+ */
+void
+sys_close()
+{
+    close(iffd);
+    close(pppfd);
+    close(sockfd);
+}
+
+/*
+ * sys_check_options - check the options that the user specified
+ */
+void
+sys_check_options()
+{
+}
+
+
+/*
+ * daemon - Detach us from controlling terminal session.
+ */
+int
+daemon(nochdir, noclose)
+    int nochdir, noclose;
+{
+    int pid;
+
+    if ((pid = fork()) < 0)
+	return -1;
+    if (pid != 0)
+	exit(0);		/* parent dies */
+    setsid();
+    if (!nochdir)
+	chdir("/");
+    if (!noclose) {
+	fclose(stdin);		/* don't need stdin, stdout, stderr */
+	fclose(stdout);
+	fclose(stderr);
+    }
+    return 0;
+}
+
+/*
+ * ppp_available - check whether the system has any ppp interfaces
+ */
+int
+ppp_available()
+{
+    struct stat buf;
+
+    return stat("/dev/ppp", &buf) >= 0;
+}
+
+/*
+ * establish_ppp - Turn the serial port into a ppp interface.
+ */
+void
+establish_ppp(fd)
+    int fd;
+{
+    int i;
+
+    /* Pop any existing modules off the tty stream. */
+    for (i = 0;; ++i)
+	if (ioctl(fd, I_LOOK, tty_modules[i]) < 0
+	    || ioctl(fd, I_POP, 0) < 0)
+	    break;
+    tty_nmodules = i;
+
+    /* Push the async hdlc module and the compressor module. */
+    if (ioctl(fd, I_PUSH, "ppp_ahdl") < 0) {
+	syslog(LOG_ERR, "Couldn't push PPP Async HDLC module: %m");
+	die(1);
+    }
+    if (ioctl(fd, I_PUSH, "ppp_comp") < 0) {
+	syslog(LOG_ERR, "Couldn't push PPP compression module: %m");
+/*	die(1); */
+    }
+
+    /* Link the serial port under the PPP multiplexor. */
+    if ((fdmuxid = ioctl(pppfd, I_LINK, fd)) < 0) {
+	syslog(LOG_ERR, "Can't link tty to PPP mux: %m");
+	die(1);
+    }
+}
+
+/*
+ * restore_loop - reattach the ppp unit to the loopback.
+ * This doesn't need to do anything because disestablish_ppp does it.
+ */
+void
+restore_loop()
+{
+}
+
+/*
+ * disestablish_ppp - Restore the serial port to normal operation.
+ * It attempts to reconstruct the stream with the previously popped
+ * modules.  This shouldn't call die() because it's called from die().
+ */
+void
+disestablish_ppp(fd)
+    int fd;
+{
+    int i;
+
+    if (fdmuxid >= 0) {
+	if (ioctl(pppfd, I_UNLINK, fdmuxid) < 0) {
+	    if (!hungup)
+		syslog(LOG_ERR, "Can't unlink tty from PPP mux: %m");
+	}
+	fdmuxid = -1;
+
+	if (!hungup) {
+	    while (ioctl(fd, I_POP, 0) >= 0)
+		;
+	    for (i = tty_nmodules - 1; i >= 0; --i)
+		if (ioctl(fd, I_PUSH, tty_modules[i]) < 0)
+		    syslog(LOG_ERR, "Couldn't restore tty module %s: %m",
+			   tty_modules[i]);
+	}
+	if (hungup && default_device && parent_pid > 0) {
+	    /*
+	     * If we have received a hangup, we need to send a SIGHUP
+	     * to the terminal's controlling process.  The reason is
+	     * that the original stream head for the terminal hasn't
+	     * seen the M_HANGUP message (it went up through the ppp
+	     * driver to the stream head for our fd to /dev/ppp).
+	     * Actually we send the signal to the process that invoked
+	     * pppd, since SunOS doesn't have getsid().
+	     */
+	    kill(parent_pid, SIGHUP);
+	}
+    }
+}
+
+/*
+ * Check whether the link seems not to be 8-bit clean.
+ */
+void
+clean_check()
+{
+    int x;
+    char *s;
+
+    if (strioctl(pppfd, PPPIO_GCLEAN, &x, 0, sizeof(x)) < 0)
+	return;
+    s = NULL;
+    switch (~x) {
+    case RCV_B7_0:
+	s = "bit 7 set to 1";
+	break;
+    case RCV_B7_1:
+	s = "bit 7 set to 0";
+	break;
+    case RCV_EVNP:
+	s = "odd parity";
+	break;
+    case RCV_ODDP:
+	s = "even parity";
+	break;
+    }
+    if (s != NULL) {
+	syslog(LOG_WARNING, "Serial link is not 8-bit clean:");
+	syslog(LOG_WARNING, "All received characters had %s", s);
+    }
+}
+
+/*
+ * List of valid speeds.
+ */
+struct speed {
+    int speed_int, speed_val;
+} speeds[] = {
+#ifdef B50
+    { 50, B50 },
+#endif
+#ifdef B75
+    { 75, B75 },
+#endif
+#ifdef B110
+    { 110, B110 },
+#endif
+#ifdef B134
+    { 134, B134 },
+#endif
+#ifdef B150
+    { 150, B150 },
+#endif
+#ifdef B200
+    { 200, B200 },
+#endif
+#ifdef B300
+    { 300, B300 },
+#endif
+#ifdef B600
+    { 600, B600 },
+#endif
+#ifdef B1200
+    { 1200, B1200 },
+#endif
+#ifdef B1800
+    { 1800, B1800 },
+#endif
+#ifdef B2000
+    { 2000, B2000 },
+#endif
+#ifdef B2400
+    { 2400, B2400 },
+#endif
+#ifdef B3600
+    { 3600, B3600 },
+#endif
+#ifdef B4800
+    { 4800, B4800 },
+#endif
+#ifdef B7200
+    { 7200, B7200 },
+#endif
+#ifdef B9600
+    { 9600, B9600 },
+#endif
+#ifdef B19200
+    { 19200, B19200 },
+#endif
+#ifdef B38400
+    { 38400, B38400 },
+#endif
+#ifdef EXTA
+    { 19200, EXTA },
+#endif
+#ifdef EXTB
+    { 38400, EXTB },
+#endif
+#ifdef B57600
+    { 57600, B57600 },
+#endif
+#ifdef B115200
+    { 115200, B115200 },
+#endif
+    { 0, 0 }
+};
+
+/*
+ * Translate from bits/second to a speed_t.
+ */
+static int
+translate_speed(bps)
+    int bps;
+{
+    struct speed *speedp;
+
+    if (bps == 0)
+	return 0;
+    for (speedp = speeds; speedp->speed_int; speedp++)
+	if (bps == speedp->speed_int)
+	    return speedp->speed_val;
+    syslog(LOG_WARNING, "speed %d not supported", bps);
+    return 0;
+}
+
+/*
+ * Translate from a speed_t to bits/second.
+ */
+static int
+baud_rate_of(speed)
+    int speed;
+{
+    struct speed *speedp;
+
+    if (speed == 0)
+	return 0;
+    for (speedp = speeds; speedp->speed_int; speedp++)
+	if (speed == speedp->speed_val)
+	    return speedp->speed_int;
+    return 0;
+}
+
+/*
+ * set_up_tty: Set up the serial port on `fd' for 8 bits, no parity,
+ * at the requested speed, etc.  If `local' is true, set CLOCAL
+ * regardless of whether the modem option was specified.
+ */
+void
+set_up_tty(fd, local)
+    int fd, local;
+{
+    int speed;
+    struct termios tios;
+
+    if (tcgetattr(fd, &tios) < 0) {
+	syslog(LOG_ERR, "tcgetattr: %m");
+	die(1);
+    }
+
+    if (!restore_term) {
+	inittermios = tios;
+	ioctl(fd, TIOCGWINSZ, &wsinfo);
+    }
+
+    tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CLOCAL);
+    if (crtscts > 0)
+	tios.c_cflag |= CRTSCTS;
+    else if (crtscts < 0)
+	tios.c_cflag &= ~CRTSCTS;
+
+    tios.c_cflag |= CS8 | CREAD | HUPCL;
+    if (local || !modem)
+	tios.c_cflag |= CLOCAL;
+    tios.c_iflag = IGNBRK | IGNPAR;
+    tios.c_oflag = 0;
+    tios.c_lflag = 0;
+    tios.c_cc[VMIN] = 1;
+    tios.c_cc[VTIME] = 0;
+
+    if (crtscts == -2) {
+	tios.c_iflag |= IXON | IXOFF;
+	tios.c_cc[VSTOP] = 0x13;	/* DC3 = XOFF = ^S */
+	tios.c_cc[VSTART] = 0x11;	/* DC1 = XON  = ^Q */
+    }
+
+    speed = translate_speed(inspeed);
+    if (speed) {
+	cfsetospeed(&tios, speed);
+	cfsetispeed(&tios, speed);
+    } else {
+	speed = cfgetospeed(&tios);
+	/*
+	 * We can't proceed if the serial port speed is 0,
+	 * since that implies that the serial port is disabled.
+	 */
+	if (speed == B0) {
+	    syslog(LOG_ERR, "Baud rate for %s is 0; need explicit baud rate",
+		   devnam);
+	    die(1);
+	}
+    }
+
+    if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) {
+	syslog(LOG_ERR, "tcsetattr: %m");
+	die(1);
+    }
+
+    baud_rate = inspeed = baud_rate_of(speed);
+    restore_term = 1;
+}
+
+/*
+ * restore_tty - restore the terminal to the saved settings.
+ */
+void
+restore_tty(fd)
+    int fd;
+{
+    if (restore_term) {
+	if (!default_device) {
+	    /*
+	     * Turn off echoing, because otherwise we can get into
+	     * a loop with the tty and the modem echoing to each other.
+	     * We presume we are the sole user of this tty device, so
+	     * when we close it, it will revert to its defaults anyway.
+	     */
+	    inittermios.c_lflag &= ~(ECHO | ECHONL);
+	}
+	if (tcsetattr(fd, TCSAFLUSH, &inittermios) < 0)
+	    if (!hungup && errno != ENXIO)
+		syslog(LOG_WARNING, "tcsetattr: %m");
+	ioctl(fd, TIOCSWINSZ, &wsinfo);
+	restore_term = 0;
+    }
+}
+
+/*
+ * setdtr - control the DTR line on the serial port.
+ * This is called from die(), so it shouldn't call die().
+ */
+void
+setdtr(fd, on)
+int fd, on;
+{
+    int modembits = TIOCM_DTR;
+
+    ioctl(fd, (on? TIOCMBIS: TIOCMBIC), &modembits);
+}
+
+/*
+ * open_loopback - open the device we use for getting packets
+ * in demand mode.  Under Solaris 2, we use our existing fd
+ * to the ppp driver.
+ */
+void
+open_ppp_loopback()
+{
+}
+
+/*
+ * output - Output PPP packet.
+ */
+void
+output(unit, p, len)
+    int unit;
+    u_char *p;
+    int len;
+{
+    struct strbuf data;
+    int retries;
+    struct pollfd pfd;
+
+    if (debug)
+	log_packet(p, len, "sent ", LOG_DEBUG);
+
+    data.len = len;
+    data.buf = (caddr_t) p;
+    retries = 4;
+    while (putmsg(pppfd, NULL, &data, 0) < 0) {
+	if (--retries < 0 || (errno != EWOULDBLOCK && errno != EAGAIN)) {
+	    if (errno != ENXIO)
+		syslog(LOG_ERR, "Couldn't send packet: %m");
+	    break;
+	}
+	pfd.fd = pppfd;
+	pfd.events = POLLOUT;
+	poll(&pfd, 1, 250);	/* wait for up to 0.25 seconds */
+    }
+}
+
+
+/*
+ * wait_input - wait until there is data available on fd,
+ * for the length of time specified by *timo (indefinite
+ * if timo is NULL).
+ */
+void
+wait_input(timo)
+    struct timeval *timo;
+{
+    int t;
+    struct pollfd pfd;
+
+    t = timo == NULL? -1: timo->tv_sec * 1000 + timo->tv_usec / 1000;
+    pfd.fd = pppfd;
+    pfd.events = POLLIN | POLLPRI | POLLHUP;
+    if (poll(&pfd, 1, t) < 0 && errno != EINTR) {
+	syslog(LOG_ERR, "poll: %m");
+	die(1);
+    }
+}
+
+/*
+ * wait_loop_output - wait until there is data available on the
+ * loopback, for the length of time specified by *timo (indefinite
+ * if timo is NULL).
+ */
+void
+wait_loop_output(timo)
+    struct timeval *timo;
+{
+    wait_input(timo);
+}
+
+/*
+ * wait_time - wait for a given length of time or until a
+ * signal is received.
+ */
+void
+wait_time(timo)
+    struct timeval *timo;
+{
+    int n;
+
+    n = select(0, NULL, NULL, NULL, timo);
+    if (n < 0 && errno != EINTR) {
+	syslog(LOG_ERR, "select: %m");
+	die(1);
+    }
+}
+
+
+/*
+ * read_packet - get a PPP packet from the serial device.
+ */
+int
+read_packet(buf)
+    u_char *buf;
+{
+    struct strbuf ctrl, data;
+    int flags, len;
+    unsigned char ctrlbuf[64];
+
+    for (;;) {
+	data.maxlen = PPP_MRU + PPP_HDRLEN;
+	data.buf = (caddr_t) buf;
+	ctrl.maxlen = sizeof(ctrlbuf);
+	ctrl.buf = (caddr_t) ctrlbuf;
+	flags = 0;
+	len = getmsg(pppfd, &ctrl, &data, &flags);
+	if (len < 0) {
+	    if (errno = EAGAIN || errno == EINTR)
+		return -1;
+	    syslog(LOG_ERR, "Error reading packet: %m");
+	    die(1);
+	}
+
+	if (ctrl.len <= 0)
+	    return data.len;
+
+	/*
+	 * Got a M_PROTO or M_PCPROTO message.  Huh?
+	 */
+	if (debug)
+	    syslog(LOG_DEBUG, "got ctrl msg len=%d", ctrl.len);
+
+    }
+}
+
+/*
+ * get_loop_output - get outgoing packets from the ppp device,
+ * and detect when we want to bring the real link up.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ */
+int
+get_loop_output()
+{
+    int len;
+    int rv = 0;
+
+    while ((len = read_packet(inpacket_buf)) > 0) {
+	if (loop_frame(inpacket_buf, len))
+	    rv = 1;
+    }
+    return rv;
+}
+
+/*
+ * ppp_send_config - configure the transmit characteristics of
+ * the ppp interface.
+ */
+void
+ppp_send_config(unit, mtu, asyncmap, pcomp, accomp)
+    int unit, mtu;
+    u_int32_t asyncmap;
+    int pcomp, accomp;
+{
+    int cf[2];
+    struct ifreq ifr;
+
+    link_mtu = mtu;
+    if (strioctl(pppfd, PPPIO_MTU, &mtu, sizeof(mtu), 0) < 0) {
+	if (hungup && errno == ENXIO)
+	    return;
+	syslog(LOG_ERR, "Couldn't set MTU: %m");
+    }
+    if (strioctl(pppfd, PPPIO_XACCM, &asyncmap, sizeof(asyncmap), 0) < 0) {
+	syslog(LOG_ERR, "Couldn't set transmit ACCM: %m");
+    }
+    cf[0] = (pcomp? COMP_PROT: 0) + (accomp? COMP_AC: 0);
+    cf[1] = COMP_PROT | COMP_AC;
+    if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+	syslog(LOG_ERR, "Couldn't set prot/AC compression: %m");
+    }
+
+    /* set mtu for ip as well */
+    memset(&ifr, 0, sizeof(ifr));
+    strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+    ifr.ifr_metric = link_mtu;
+    if (ioctl(sockfd, SIOCSIFMTU, &ifr) < 0) {
+	syslog(LOG_ERR, "Couldn't set IP MTU: %m");
+    }
+}
+
+/*
+ * ppp_set_xaccm - set the extended transmit ACCM for the interface.
+ */
+void
+ppp_set_xaccm(unit, accm)
+    int unit;
+    ext_accm accm;
+{
+    if (strioctl(pppfd, PPPIO_XACCM, accm, sizeof(ext_accm), 0) < 0) {
+	if (!hungup || errno != ENXIO)
+	    syslog(LOG_WARNING, "Couldn't set extended ACCM: %m");
+    }
+}
+
+/*
+ * ppp_recv_config - configure the receive-side characteristics of
+ * the ppp interface.
+ */
+void
+ppp_recv_config(unit, mru, asyncmap, pcomp, accomp)
+    int unit, mru;
+    u_int32_t asyncmap;
+    int pcomp, accomp;
+{
+    int cf[2];
+
+    link_mru = mru;
+    if (strioctl(pppfd, PPPIO_MRU, &mru, sizeof(mru), 0) < 0) {
+	if (hungup && errno == ENXIO)
+	    return;
+	syslog(LOG_ERR, "Couldn't set MRU: %m");
+    }
+    if (strioctl(pppfd, PPPIO_RACCM, &asyncmap, sizeof(asyncmap), 0) < 0) {
+	syslog(LOG_ERR, "Couldn't set receive ACCM: %m");
+    }
+    cf[0] = (pcomp? DECOMP_PROT: 0) + (accomp? DECOMP_AC: 0);
+    cf[1] = DECOMP_PROT | DECOMP_AC;
+    if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+	syslog(LOG_ERR, "Couldn't set prot/AC decompression: %m");
+    }
+}
+
+/*
+ * ccp_test - ask kernel whether a given compression method
+ * is acceptable for use.
+ */
+int
+ccp_test(unit, opt_ptr, opt_len, for_transmit)
+    int unit, opt_len, for_transmit;
+    u_char *opt_ptr;
+{
+    if (strioctl(pppfd, (for_transmit? PPPIO_XCOMP: PPPIO_RCOMP),
+		 opt_ptr, opt_len, 0) >= 0)
+	return 1;
+    return (errno == ENOSR)? 0: -1;
+}
+
+/*
+ * ccp_flags_set - inform kernel about the current state of CCP.
+ */
+void
+ccp_flags_set(unit, isopen, isup)
+    int unit, isopen, isup;
+{
+    int cf[2];
+
+    cf[0] = (isopen? CCP_ISOPEN: 0) + (isup? CCP_ISUP: 0);
+    cf[1] = CCP_ISOPEN | CCP_ISUP | CCP_ERROR | CCP_FATALERROR;
+    if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+	if (!hungup || errno != ENXIO)
+	    syslog(LOG_ERR, "Couldn't set kernel CCP state: %m");
+    }
+}
+
+/*
+ * get_idle_time - return how long the link has been idle.
+ */
+int
+get_idle_time(u, ip)
+    int u;
+    struct ppp_idle *ip;
+{
+    return strioctl(pppfd, PPPIO_GIDLE, ip, 0, sizeof(struct ppp_idle)) >= 0;
+}
+
+
+/*
+ * ccp_fatal_error - returns 1 if decompression was disabled as a
+ * result of an error detected after decompression of a packet,
+ * 0 otherwise.  This is necessary because of patent nonsense.
+ */
+int
+ccp_fatal_error(unit)
+    int unit;
+{
+    int cf[2];
+
+    cf[0] = cf[1] = 0;
+    if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+	if (errno != ENXIO && errno != EINVAL)
+	    syslog(LOG_ERR, "Couldn't get compression flags: %m");
+	return 0;
+    }
+    return cf[0] & CCP_FATALERROR;
+}
+
+/*
+ * sifvjcomp - config tcp header compression
+ */
+int
+sifvjcomp(u, vjcomp, xcidcomp, xmaxcid)
+    int u, vjcomp, xcidcomp, xmaxcid;
+{
+    int cf[2];
+    char maxcid[2];
+
+    if (vjcomp) {
+	maxcid[0] = xcidcomp;
+	maxcid[1] = 15;		/* XXX should be rmaxcid */
+	if (strioctl(pppfd, PPPIO_VJINIT, maxcid, sizeof(maxcid), 0) < 0) {
+	    syslog(LOG_ERR, "Couldn't initialize VJ compression: %m");
+	}
+    }
+
+    cf[0] = (vjcomp? COMP_VJC + DECOMP_VJC: 0)	/* XXX this is wrong */
+	+ (xcidcomp? COMP_VJCCID + DECOMP_VJCCID: 0);
+    cf[1] = COMP_VJC + DECOMP_VJC + COMP_VJCCID + DECOMP_VJCCID;
+    if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+	if (vjcomp)
+	    syslog(LOG_ERR, "Couldn't enable VJ compression: %m");
+    }
+
+    return 1;
+}
+
+/*
+ * sifup - Config the interface up and enable IP packets to pass.
+ */
+int
+sifup(u)
+    int u;
+{
+    struct ifreq ifr;
+
+    strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+    if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
+	syslog(LOG_ERR, "Couldn't mark interface up (get): %m");
+	return 0;
+    }
+    ifr.ifr_flags |= IFF_UP;
+    if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
+	syslog(LOG_ERR, "Couldn't mark interface up (set): %m");
+	return 0;
+    }
+    if_is_up = 1;
+    return 1;
+}
+
+/*
+ * sifdown - Config the interface down and disable IP.
+ */
+int
+sifdown(u)
+    int u;
+{
+    struct ifreq ifr;
+
+    strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+    if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
+	syslog(LOG_ERR, "Couldn't mark interface down (get): %m");
+	return 0;
+    }
+    if ((ifr.ifr_flags & IFF_UP) != 0) {
+	ifr.ifr_flags &= ~IFF_UP;
+	if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
+	    syslog(LOG_ERR, "Couldn't mark interface down (set): %m");
+	    return 0;
+	}
+    }
+    if_is_up = 0;
+    return 1;
+}
+
+/*
+ * sifnpmode - Set the mode for handling packets for a given NP.
+ */
+int
+sifnpmode(u, proto, mode)
+    int u;
+    int proto;
+    enum NPmode mode;
+{
+    int npi[2];
+
+    npi[0] = proto;
+    npi[1] = (int) mode;
+    if (strioctl(pppfd, PPPIO_NPMODE, npi, 2 * sizeof(int), 0) < 0) {
+	syslog(LOG_ERR, "ioctl(set NP %d mode to %d): %m", proto, mode);
+	return 0;
+    }
+    return 1;
+}
+
+#define INET_ADDR(x)	(((struct sockaddr_in *) &(x))->sin_addr.s_addr)
+
+/*
+ * sifaddr - Config the interface IP addresses and netmask.
+ */
+int
+sifaddr(u, o, h, m)
+    int u;
+    u_int32_t o, h, m;
+{
+    struct ifreq ifr;
+
+    memset(&ifr, 0, sizeof(ifr));
+    strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+    ifr.ifr_addr.sa_family = AF_INET;
+    INET_ADDR(ifr.ifr_addr) = m;
+    if (ioctl(sockfd, SIOCSIFNETMASK, &ifr) < 0) {
+	syslog(LOG_ERR, "Couldn't set IP netmask: %m");
+    }
+    ifr.ifr_addr.sa_family = AF_INET;
+    INET_ADDR(ifr.ifr_addr) = o;
+    if (ioctl(sockfd, SIOCSIFADDR, &ifr) < 0) {
+	syslog(LOG_ERR, "Couldn't set local IP address: %m");
+    }
+    ifr.ifr_dstaddr.sa_family = AF_INET;
+    INET_ADDR(ifr.ifr_dstaddr) = h;
+    if (ioctl(sockfd, SIOCSIFDSTADDR, &ifr) < 0) {
+	syslog(LOG_ERR, "Couldn't set remote IP address: %m");
+    }
+#if 0	/* now done in ppp_send_config */
+    ifr.ifr_metric = link_mtu;
+    if (ioctl(sockfd, SIOCSIFMTU, &ifr) < 0) {
+	syslog(LOG_ERR, "Couldn't set IP MTU: %m");
+    }
+#endif
+    ifaddrs[0] = o;
+    ifaddrs[1] = h;
+
+    return 1;
+}
+
+/*
+ * cifaddr - Clear the interface IP addresses, and delete routes
+ * through the interface if possible.
+ */
+int
+cifaddr(u, o, h)
+    int u;
+    u_int32_t o, h;
+{
+    struct rtentry rt;
+
+    bzero(&rt, sizeof(rt));
+    rt.rt_dst.sa_family = AF_INET;
+    INET_ADDR(rt.rt_dst) = h;
+    rt.rt_gateway.sa_family = AF_INET;
+    INET_ADDR(rt.rt_gateway) = o;
+    rt.rt_flags = RTF_HOST;
+    if (ioctl(sockfd, SIOCDELRT, &rt) < 0)
+	syslog(LOG_ERR, "Couldn't delete route through interface: %m");
+    ifaddrs[0] = 0;
+    return 1;
+}
+
+/*
+ * sifdefaultroute - assign a default route through the address given.
+ */
+int
+sifdefaultroute(u, l, g)
+    int u;
+    u_int32_t l, g;
+{
+    struct rtentry rt;
+
+    bzero(&rt, sizeof(rt));
+    rt.rt_dst.sa_family = AF_INET;
+    INET_ADDR(rt.rt_dst) = 0;
+    rt.rt_gateway.sa_family = AF_INET;
+    INET_ADDR(rt.rt_gateway) = g;
+    rt.rt_flags = RTF_GATEWAY;
+
+    if (ioctl(sockfd, SIOCADDRT, &rt) < 0) {
+	syslog(LOG_ERR, "Can't add default route: %m");
+	return 0;
+    }
+
+    default_route_gateway = g;
+    return 1;
+}
+
+/*
+ * cifdefaultroute - delete a default route through the address given.
+ */
+int
+cifdefaultroute(u, l, g)
+    int u;
+    u_int32_t l, g;
+{
+    struct rtentry rt;
+
+    bzero(&rt, sizeof(rt));
+    rt.rt_dst.sa_family = AF_INET;
+    INET_ADDR(rt.rt_dst) = 0;
+    rt.rt_gateway.sa_family = AF_INET;
+    INET_ADDR(rt.rt_gateway) = g;
+    rt.rt_flags = RTF_GATEWAY;
+
+    if (ioctl(sockfd, SIOCDELRT, &rt) < 0) {
+	syslog(LOG_ERR, "Can't delete default route: %m");
+	return 0;
+    }
+
+    default_route_gateway = 0;
+    return 1;
+}
+
+/*
+ * sifproxyarp - Make a proxy ARP entry for the peer.
+ */
+int
+sifproxyarp(unit, hisaddr)
+    int unit;
+    u_int32_t hisaddr;
+{
+    struct arpreq arpreq;
+
+    bzero(&arpreq, sizeof(arpreq));
+    if (!get_ether_addr(hisaddr, &arpreq.arp_ha))
+	return 0;
+
+    arpreq.arp_pa.sa_family = AF_INET;
+    INET_ADDR(arpreq.arp_pa) = hisaddr;
+    arpreq.arp_flags = ATF_PERM | ATF_PUBL;
+    if (ioctl(sockfd, SIOCSARP, (caddr_t) &arpreq) < 0) {
+	syslog(LOG_ERR, "Couldn't set proxy ARP entry: %m");
+	return 0;
+    }
+
+    proxy_arp_addr = hisaddr;
+    return 1;
+}
+
+/*
+ * cifproxyarp - Delete the proxy ARP entry for the peer.
+ */
+int
+cifproxyarp(unit, hisaddr)
+    int unit;
+    u_int32_t hisaddr;
+{
+    struct arpreq arpreq;
+
+    bzero(&arpreq, sizeof(arpreq));
+    arpreq.arp_pa.sa_family = AF_INET;
+    INET_ADDR(arpreq.arp_pa) = hisaddr;
+    if (ioctl(sockfd, SIOCDARP, (caddr_t)&arpreq) < 0) {
+	syslog(LOG_ERR, "Couldn't delete proxy ARP entry: %m");
+	return 0;
+    }
+
+    proxy_arp_addr = 0;
+    return 1;
+}
+
+/*
+ * get_ether_addr - get the hardware address of an interface on the
+ * the same subnet as ipaddr.
+ */
+#define MAX_IFS		32
+
+static int
+get_ether_addr(ipaddr, hwaddr)
+    u_int32_t ipaddr;
+    struct sockaddr *hwaddr;
+{
+    struct ifreq *ifr, *ifend;
+    u_int32_t ina, mask;
+    struct ifreq ifreq;
+    struct ifconf ifc;
+    struct ifreq ifs[MAX_IFS];
+    int nit_fd;
+
+    ifc.ifc_len = sizeof(ifs);
+    ifc.ifc_req = ifs;
+    if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
+	syslog(LOG_ERR, "ioctl(SIOCGIFCONF): %m");
+	return 0;
+    }
+
+    /*
+     * Scan through looking for an interface with an Internet
+     * address on the same subnet as `ipaddr'.
+     */
+    ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+    for (ifr = ifc.ifc_req; ifr < ifend; ifr = (struct ifreq *)
+	    ((char *)&ifr->ifr_addr + sizeof(struct sockaddr))) {
+        if (ifr->ifr_addr.sa_family == AF_INET) {
+
+            /*
+             * Check that the interface is up, and not point-to-point
+             * or loopback.
+             */
+            strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+            if (ioctl(sockfd, SIOCGIFFLAGS, &ifreq) < 0)
+                continue;
+            if ((ifreq.ifr_flags &
+                 (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP))
+                 != (IFF_UP|IFF_BROADCAST))
+                continue;
+
+            /*
+             * Get its netmask and check that it's on the right subnet.
+             */
+            if (ioctl(sockfd, SIOCGIFNETMASK, &ifreq) < 0)
+                continue;
+            ina = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr;
+            mask = ((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr.s_addr;
+            if ((ipaddr & mask) != (ina & mask))
+                continue;
+
+            break;
+        }
+    }
+
+    if (ifr >= ifend)
+	return 0;
+    syslog(LOG_INFO, "found interface %s for proxy arp", ifr->ifr_name);
+
+    /*
+     * Grab the physical address for this interface.
+     */
+    if ((nit_fd = open("/dev/nit", O_RDONLY)) < 0) {
+	syslog(LOG_ERR, "Couldn't open /dev/nit: %m");
+	return 0;
+    }
+    strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+    if (ioctl(nit_fd, NIOCBIND, &ifreq) < 0
+	|| ioctl(nit_fd, SIOCGIFADDR, &ifreq) < 0) {
+	syslog(LOG_ERR, "Couldn't get hardware address for %s: %m",
+	       ifreq.ifr_name);
+	close(nit_fd);
+	return 0;
+    }
+
+    hwaddr->sa_family = AF_UNSPEC;
+    memcpy(hwaddr->sa_data, ifreq.ifr_addr.sa_data, 6);
+    close(nit_fd);
+    return 1;
+}
+
+#define	WTMPFILE	"/usr/adm/wtmp"
+
+void
+logwtmp(line, name, host)
+    const char *line, *name, *host;
+{
+    int fd;
+    struct stat buf;
+    struct utmp ut;
+
+    if ((fd = open(WTMPFILE, O_WRONLY|O_APPEND, 0)) < 0)
+	return;
+    if (!fstat(fd, &buf)) {
+	(void)strncpy(ut.ut_line, line, sizeof(ut.ut_line));
+	(void)strncpy(ut.ut_name, name, sizeof(ut.ut_name));
+	(void)strncpy(ut.ut_host, host, sizeof(ut.ut_host));
+	(void)time(&ut.ut_time);
+	if (write(fd, (char *)&ut, sizeof(struct utmp)) != sizeof(struct utmp))
+	    (void)ftruncate(fd, buf.st_size);
+    }
+    close(fd);
+}
+
+/*
+ * Return user specified netmask, modified by any mask we might determine
+ * for address `addr' (in network byte order).
+ * Here we scan through the system's list of interfaces, looking for
+ * any non-point-to-point interfaces which might appear to be on the same
+ * network as `addr'.  If we find any, we OR in their netmask to the
+ * user-specified netmask.
+ */
+u_int32_t
+GetMask(addr)
+    u_int32_t addr;
+{
+    u_int32_t mask, nmask, ina;
+    struct ifreq *ifr, *ifend, ifreq;
+    struct ifconf ifc;
+
+    addr = ntohl(addr);
+    if (IN_CLASSA(addr))	/* determine network mask for address class */
+	nmask = IN_CLASSA_NET;
+    else if (IN_CLASSB(addr))
+	nmask = IN_CLASSB_NET;
+    else
+	nmask = IN_CLASSC_NET;
+    /* class D nets are disallowed by bad_ip_adrs */
+    mask = netmask | htonl(nmask);
+
+    /*
+     * Scan through the system's network interfaces.
+     */
+    ifc.ifc_len = MAX_IFS * sizeof(struct ifreq);
+    ifc.ifc_req = alloca(ifc.ifc_len);
+    if (ifc.ifc_req == 0)
+	return mask;
+    if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
+	syslog(LOG_WARNING, "Couldn't get system interface list: %m");
+	return mask;
+    }
+    ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+    for (ifr = ifc.ifc_req; ifr < ifend; ++ifr) {
+	/*
+	 * Check the interface's internet address.
+	 */
+	if (ifr->ifr_addr.sa_family != AF_INET)
+	    continue;
+	ina = INET_ADDR(ifr->ifr_addr);
+	if ((ntohl(ina) & nmask) != (addr & nmask))
+	    continue;
+	/*
+	 * Check that the interface is up, and not point-to-point or loopback.
+	 */
+	strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+	if (ioctl(sockfd, SIOCGIFFLAGS, &ifreq) < 0)
+	    continue;
+	if ((ifreq.ifr_flags & (IFF_UP|IFF_POINTOPOINT|IFF_LOOPBACK))
+	    != IFF_UP)
+	    continue;
+	/*
+	 * Get its netmask and OR it into our mask.
+	 */
+	if (ioctl(sockfd, SIOCGIFNETMASK, &ifreq) < 0)
+	    continue;
+	mask |= INET_ADDR(ifreq.ifr_addr);
+    }
+
+    return mask;
+}
+
+static int
+strioctl(fd, cmd, ptr, ilen, olen)
+    int fd, cmd, ilen, olen;
+    void *ptr;
+{
+    struct strioctl str;
+
+    str.ic_cmd = cmd;
+    str.ic_timout = 0;
+    str.ic_len = ilen;
+    str.ic_dp = ptr;
+    if (ioctl(fd, I_STR, &str) == -1)
+	return -1;
+    if (str.ic_len != olen)
+	syslog(LOG_DEBUG, "strioctl: expected %d bytes, got %d for cmd %x\n",
+	       olen, str.ic_len, cmd);
+    return 0;
+}
+
+/*
+ * Use the hostid as part of the random number seed.
+ */
+int
+get_host_seed()
+{
+    return gethostid();
+}
+
+/*
+ * Code for locking/unlocking the serial device.
+ * This code is derived from chat.c.
+ */
+
+#if !defined(HDB) && !defined(SUNOS3)
+#define	HDB	1		/* ascii lock files are the default */
+#endif
+
+#ifndef LOCK_DIR
+# if HDB
+#  define	PIDSTRING
+#  define	LOCK_PREFIX	"/usr/spool/locks/LCK.."
+# else /* HDB */
+#  define	LOCK_PREFIX	"/usr/spool/uucp/LCK.."
+# endif /* HDB */
+#endif /* LOCK_DIR */
+
+static char *lock_file;		/* name of lock file created */
+
+/*
+ * lock - create a lock file for the named device.
+ */
+int
+lock(dev)
+    char *dev;
+{
+    char hdb_lock_buffer[12];
+    int fd, pid, n;
+    char *p;
+
+    if ((p = strrchr(dev, '/')) != NULL)
+	dev = p + 1;
+    lock_file = malloc(strlen(LOCK_PREFIX) + strlen(dev) + 1);
+    if (lock_file == NULL)
+	novm("lock file name");
+    strcat(strcpy(lock_file, LOCK_PREFIX), dev);
+
+    while ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0) {
+	if (errno == EEXIST
+	    && (fd = open(lock_file, O_RDONLY, 0)) >= 0) {
+	    /* Read the lock file to find out who has the device locked */
+#ifdef PIDSTRING
+	    n = read(fd, hdb_lock_buffer, 11);
+	    if (n > 0) {
+		hdb_lock_buffer[n] = 0;
+		pid = atoi(hdb_lock_buffer);
+	    }
+#else
+	    n = read(fd, &pid, sizeof(pid));
+#endif
+	    if (n <= 0) {
+		syslog(LOG_ERR, "Can't read pid from lock file %s", lock_file);
+		close(fd);
+	    } else {
+		if (kill(pid, 0) == -1 && errno == ESRCH) {
+		    /* pid no longer exists - remove the lock file */
+		    if (unlink(lock_file) == 0) {
+			close(fd);
+			syslog(LOG_NOTICE, "Removed stale lock on %s (pid %d)",
+			       dev, pid);
+			continue;
+		    } else
+			syslog(LOG_WARNING, "Couldn't remove stale lock on %s",
+			       dev);
+		} else
+		    syslog(LOG_NOTICE, "Device %s is locked by pid %d",
+			   dev, pid);
+	    }
+	    close(fd);
+	} else
+	    syslog(LOG_ERR, "Can't create lock file %s: %m", lock_file);
+	free(lock_file);
+	lock_file = NULL;
+	return -1;
+    }
+
+#ifdef PIDSTRING
+    sprintf(hdb_lock_buffer, "%10d\n", getpid());
+    write(fd, hdb_lock_buffer, 11);
+#else
+    pid = getpid();
+    write(fd, &pid, sizeof pid);
+#endif
+
+    close(fd);
+    return 0;
+}
+
+/*
+ * unlock - remove our lockfile
+ */
+void
+unlock()
+{
+    if (lock_file) {
+	unlink(lock_file);
+	free(lock_file);
+	lock_file = NULL;
+    }
+}
+
+/*
+ * SunOS doesn't have strtoul :-(
+ */
+unsigned long
+strtoul(str, ptr, base)
+    char *str, **ptr;
+    int base;
+{
+    return (unsigned long) strtol(str, ptr, base);
+}
+
+/*
+ * Or strerror :-(
+ */
+extern char *sys_errlist[];
+extern int sys_nerr;
+
+char *
+strerror(n)
+    int n;
+{
+    static char unknown[32];
+
+    if (n > 0 && n < sys_nerr)
+	return sys_errlist[n];
+    sprintf(unknown, "Error %d", n);
+    return unknown;
+}
diff -rPu pppd.old/sys-svr4.c pppd/sys-svr4.c
--- pppd.old/sys-svr4.c	Thu Jan  1 03:00:00 1970
+++ pppd/sys-svr4.c	Wed Mar 25 05:19:31 1998
@@ -0,0 +1,1690 @@
+/*
+ * System-dependent procedures for pppd under Solaris 2.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies.  This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: sys-svr4.c,v 1.17 1998/03/25 02:19:31 paulus Exp $";
+#endif
+
+#include <limits.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <termios.h>
+#ifndef CRTSCTS
+#include <sys/termiox.h>
+#endif
+#include <signal.h>
+#include <utmpx.h>
+#include <sys/types.h>
+#include <sys/ioccom.h>
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/syslog.h>
+#include <sys/sysmacros.h>
+#include <sys/systeminfo.h>
+#include <sys/dlpi.h>
+#include <sys/stat.h>
+#include <sys/mkdev.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/route.h>
+#include <net/ppp_defs.h>
+#include <net/pppio.h>
+#include <netinet/in.h>
+
+#include "pppd.h"
+
+static int	pppfd;
+static int	fdmuxid = -1;
+static int	ipfd;
+static int	ipmuxid = -1;
+
+static int	restore_term;
+static struct termios inittermios;
+#ifndef CRTSCTS
+static struct termiox inittermiox;
+static int	termiox_ok;
+#endif
+static struct winsize wsinfo;	/* Initial window size info */
+static pid_t	tty_sid;	/* original session ID for terminal */
+
+extern u_char	inpacket_buf[];	/* borrowed from main.c */
+
+static int	link_mtu, link_mru;
+
+#define NMODULES	32
+static int	tty_nmodules;
+static char	tty_modules[NMODULES][FMNAMESZ+1];
+
+static int	if_is_up;	/* Interface has been marked up */
+static u_int32_t default_route_gateway;	/* Gateway for default route added */
+static u_int32_t proxy_arp_addr;	/* Addr for proxy arp entry added */
+
+/* Prototypes for procedures local to this file. */
+static int translate_speed __P((int));
+static int baud_rate_of __P((int));
+static int get_ether_addr __P((u_int32_t, struct sockaddr *));
+static int get_hw_addr __P((char *, struct sockaddr *));
+static int dlpi_attach __P((int, int));
+static int dlpi_info_req __P((int));
+static int dlpi_get_reply __P((int, union DL_primitives *, int, int));
+static int strioctl __P((int, int, void *, int, int));
+
+
+/*
+ * sys_init - System-dependent initialization.
+ */
+void
+sys_init()
+{
+    int ifd, x;
+#ifndef sun
+    struct ifreq ifr;
+    struct {
+	union DL_primitives prim;
+	char space[64];
+    } reply;
+#endif
+
+    ipfd = open("/dev/ip", O_RDWR, 0);
+    if (ipfd < 0) {
+	syslog(LOG_ERR, "Couldn't open IP device: %m");
+	die(1);
+    }
+
+    if (default_device)
+	tty_sid = getsid((pid_t)0);
+
+    pppfd = open("/dev/ppp", O_RDWR | O_NONBLOCK, 0);
+    if (pppfd < 0) {
+	syslog(LOG_ERR, "Can't open /dev/ppp: %m");
+	die(1);
+    }
+    if (kdebugflag & 1) {
+	x = PPPDBG_LOG + PPPDBG_DRIVER;
+	strioctl(pppfd, PPPIO_DEBUG, &x, sizeof(int), 0);
+    }
+
+    /* Assign a new PPA and get its unit number. */
+    if (strioctl(pppfd, PPPIO_NEWPPA, &ifunit, 0, sizeof(int)) < 0) {
+	syslog(LOG_ERR, "Can't create new PPP interface: %m");
+	die(1);
+    }
+
+    /*
+     * Open the ppp device again and link it under the ip multiplexor.
+     * IP will assign a unit number which hopefully is the same as ifunit.
+     * I don't know any way to be certain they will be the same. :-(
+     */
+    ifd = open("/dev/ppp", O_RDWR, 0);
+    if (ifd < 0) {
+	syslog(LOG_ERR, "Can't open /dev/ppp (2): %m");
+	die(1);
+    }
+    if (kdebugflag & 1) {
+	x = PPPDBG_LOG + PPPDBG_DRIVER;
+	strioctl(ifd, PPPIO_DEBUG, &x, sizeof(int), 0);
+    }
+#ifdef sun
+    if (ioctl(ifd, I_PUSH, "ip") < 0) {
+	syslog(LOG_ERR, "Can't push IP module: %m");
+	close(ifd);
+	die(1);
+    }
+#else
+    if (dlpi_attach(ifd, ifunit) < 0 ||
+	dlpi_get_reply(ifd, &reply.prim, DL_OK_ACK, sizeof(reply)) < 0) {
+	syslog(LOG_ERR, "Can't attach to ppp%d: %m", ifunit);
+	close(ifd);
+	die(1);
+    }
+#endif
+    ipmuxid = ioctl(ipfd, I_LINK, ifd);
+    close(ifd);
+    if (ipmuxid < 0) {
+	syslog(LOG_ERR, "Can't link PPP device to IP: %m");
+	die(1);
+    }
+
+#ifndef sun
+    /* Set the interface name for the link. */
+    (void) sprintf (ifr.ifr_name, "ppp%d", ifunit);
+    ifr.ifr_metric = ipmuxid;
+    if (strioctl(ipfd, SIOCSIFNAME, (char *)&ifr, sizeof ifr, 0) < 0) {
+	syslog(LOG_ERR, "Can't set interface name %s: %m", ifr.ifr_name);
+	die(1);
+    }
+#endif
+}
+
+/*
+ * sys_cleanup - restore any system state we modified before exiting:
+ * mark the interface down, delete default route and/or proxy arp entry.
+ * This should call die() because it's called from die().
+ */
+void
+sys_cleanup()
+{
+    struct ifreq ifr;
+
+    if (if_is_up)
+	sifdown(0);
+    if (default_route_gateway)
+	cifdefaultroute(0, default_route_gateway, default_route_gateway);
+    if (proxy_arp_addr)
+	cifproxyarp(0, proxy_arp_addr);
+}
+
+/*
+ * sys_close - Clean up in a child process before execing.
+ */
+void
+sys_close()
+{
+    close(ipfd);
+    if (pppfd >= 0)
+	close(pppfd);
+}
+
+/*
+ * sys_check_options - check the options that the user specified
+ */
+void
+sys_check_options()
+{
+}
+
+
+/*
+ * daemon - Detach us from controlling terminal session.
+ */
+int
+daemon(nochdir, noclose)
+    int nochdir, noclose;
+{
+    int pid;
+
+    if ((pid = fork()) < 0)
+	return -1;
+    if (pid != 0)
+	exit(0);		/* parent dies */
+    setsid();
+    if (!nochdir)
+	chdir("/");
+    if (!noclose) {
+	fclose(stdin);		/* don't need stdin, stdout, stderr */
+	fclose(stdout);
+	fclose(stderr);
+    }
+    return 0;
+}
+
+/*
+ * ppp_available - check whether the system has any ppp interfaces
+ */
+int
+ppp_available()
+{
+    struct stat buf;
+
+    return stat("/dev/ppp", &buf) >= 0;
+}
+
+/*
+ * establish_ppp - Turn the serial port into a ppp interface.
+ */
+void
+establish_ppp(fd)
+    int fd;
+{
+    int i;
+
+    /* Pop any existing modules off the tty stream. */
+    for (i = 0;; ++i)
+	if (ioctl(fd, I_LOOK, tty_modules[i]) < 0
+	    || ioctl(fd, I_POP, 0) < 0)
+	    break;
+    tty_nmodules = i;
+
+    /* Push the async hdlc module and the compressor module. */
+    if (ioctl(fd, I_PUSH, "ppp_ahdl") < 0) {
+	syslog(LOG_ERR, "Couldn't push PPP Async HDLC module: %m");
+	die(1);
+    }
+    if (kdebugflag & 4) {
+	i = PPPDBG_LOG + PPPDBG_AHDLC;
+	strioctl(pppfd, PPPIO_DEBUG, &i, sizeof(int), 0);
+    }
+    if (ioctl(fd, I_PUSH, "ppp_comp") < 0) {
+	syslog(LOG_ERR, "Couldn't push PPP compression module: %m");
+/*	die(1); */
+    }
+    if (kdebugflag & 2) {
+	i = PPPDBG_LOG + PPPDBG_COMP;
+	strioctl(pppfd, PPPIO_DEBUG, &i, sizeof(int), 0);
+    }
+
+    /* Link the serial port under the PPP multiplexor. */
+    if ((fdmuxid = ioctl(pppfd, I_LINK, fd)) < 0) {
+	syslog(LOG_ERR, "Can't link tty to PPP mux: %m");
+	die(1);
+    }
+}
+
+/*
+ * restore_loop - reattach the ppp unit to the loopback.
+ * This doesn't need to do anything because disestablish_ppp does it.
+ */
+void
+restore_loop()
+{
+}
+
+/*
+ * disestablish_ppp - Restore the serial port to normal operation.
+ * It attempts to reconstruct the stream with the previously popped
+ * modules.  This shouldn't call die() because it's called from die().
+ */
+void
+disestablish_ppp(fd)
+    int fd;
+{
+    int i;
+
+    if (fdmuxid >= 0) {
+	if (ioctl(pppfd, I_UNLINK, fdmuxid) < 0) {
+	    if (!hungup)
+		syslog(LOG_ERR, "Can't unlink tty from PPP mux: %m");
+	}
+	fdmuxid = -1;
+
+	if (!hungup) {
+	    while (ioctl(fd, I_POP, 0) >= 0)
+		;
+	    for (i = tty_nmodules - 1; i >= 0; --i)
+		if (ioctl(fd, I_PUSH, tty_modules[i]) < 0)
+		    syslog(LOG_ERR, "Couldn't restore tty module %s: %m",
+			   tty_modules[i]);
+	}
+	if (hungup && default_device && tty_sid > 0) {
+	    /*
+	     * If we have received a hangup, we need to send a SIGHUP
+	     * to the terminal's controlling process.  The reason is
+	     * that the original stream head for the terminal hasn't
+	     * seen the M_HANGUP message (it went up through the ppp
+	     * driver to the stream head for our fd to /dev/ppp).
+	     */
+	    kill(tty_sid, SIGHUP);
+	}
+    }
+}
+
+/*
+ * Check whether the link seems not to be 8-bit clean.
+ */
+void
+clean_check()
+{
+    int x;
+    char *s;
+
+    if (strioctl(pppfd, PPPIO_GCLEAN, &x, 0, sizeof(x)) < 0)
+	return;
+    s = NULL;
+    switch (~x) {
+    case RCV_B7_0:
+	s = "bit 7 set to 1";
+	break;
+    case RCV_B7_1:
+	s = "bit 7 set to 0";
+	break;
+    case RCV_EVNP:
+	s = "odd parity";
+	break;
+    case RCV_ODDP:
+	s = "even parity";
+	break;
+    }
+    if (s != NULL) {
+	syslog(LOG_WARNING, "Serial link is not 8-bit clean:");
+	syslog(LOG_WARNING, "All received characters had %s", s);
+    }
+}
+
+/*
+ * List of valid speeds.
+ */
+struct speed {
+    int speed_int, speed_val;
+} speeds[] = {
+#ifdef B50
+    { 50, B50 },
+#endif
+#ifdef B75
+    { 75, B75 },
+#endif
+#ifdef B110
+    { 110, B110 },
+#endif
+#ifdef B134
+    { 134, B134 },
+#endif
+#ifdef B150
+    { 150, B150 },
+#endif
+#ifdef B200
+    { 200, B200 },
+#endif
+#ifdef B300
+    { 300, B300 },
+#endif
+#ifdef B600
+    { 600, B600 },
+#endif
+#ifdef B1200
+    { 1200, B1200 },
+#endif
+#ifdef B1800
+    { 1800, B1800 },
+#endif
+#ifdef B2000
+    { 2000, B2000 },
+#endif
+#ifdef B2400
+    { 2400, B2400 },
+#endif
+#ifdef B3600
+    { 3600, B3600 },
+#endif
+#ifdef B4800
+    { 4800, B4800 },
+#endif
+#ifdef B7200
+    { 7200, B7200 },
+#endif
+#ifdef B9600
+    { 9600, B9600 },
+#endif
+#ifdef B19200
+    { 19200, B19200 },
+#endif
+#ifdef B38400
+    { 38400, B38400 },
+#endif
+#ifdef EXTA
+    { 19200, EXTA },
+#endif
+#ifdef EXTB
+    { 38400, EXTB },
+#endif
+#ifdef B57600
+    { 57600, B57600 },
+#endif
+#ifdef B115200
+    { 115200, B115200 },
+#endif
+    { 0, 0 }
+};
+
+/*
+ * Translate from bits/second to a speed_t.
+ */
+static int
+translate_speed(bps)
+    int bps;
+{
+    struct speed *speedp;
+
+    if (bps == 0)
+	return 0;
+    for (speedp = speeds; speedp->speed_int; speedp++)
+	if (bps == speedp->speed_int)
+	    return speedp->speed_val;
+    syslog(LOG_WARNING, "speed %d not supported", bps);
+    return 0;
+}
+
+/*
+ * Translate from a speed_t to bits/second.
+ */
+static int
+baud_rate_of(speed)
+    int speed;
+{
+    struct speed *speedp;
+
+    if (speed == 0)
+	return 0;
+    for (speedp = speeds; speedp->speed_int; speedp++)
+	if (speed == speedp->speed_val)
+	    return speedp->speed_int;
+    return 0;
+}
+
+/*
+ * set_up_tty: Set up the serial port on `fd' for 8 bits, no parity,
+ * at the requested speed, etc.  If `local' is true, set CLOCAL
+ * regardless of whether the modem option was specified.
+ */
+void
+set_up_tty(fd, local)
+    int fd, local;
+{
+    int speed;
+    struct termios tios;
+#if !defined (CRTSCTS)
+    struct termiox tiox;
+#endif
+
+    if (tcgetattr(fd, &tios) < 0) {
+	syslog(LOG_ERR, "tcgetattr: %m");
+	die(1);
+    }
+
+#ifndef CRTSCTS
+    termiox_ok = 1;
+    if (ioctl (fd, TCGETX, &tiox) < 0) {
+	termiox_ok = 0;
+	if (errno != ENOTTY)
+	    syslog (LOG_ERR, "TCGETX: %m");
+    }
+#endif
+
+    if (!restore_term) {
+	inittermios = tios;
+#ifndef CRTSCTS
+	inittermiox = tiox;
+#endif
+	ioctl(fd, TIOCGWINSZ, &wsinfo);
+    }
+
+    tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CLOCAL);
+#ifdef CRTSCTS
+    if (crtscts > 0)
+	tios.c_cflag |= CRTSCTS;
+    else if (crtscts < 0)
+	tios.c_cflag &= ~CRTSCTS;
+#else
+    if (crtscts != 0 && !termiox_ok) {
+	syslog(LOG_ERR, "Can't set RTS/CTS flow control");
+    } else if (crtscts > 0) {
+	tiox.x_hflag |= RTSXOFF|CTSXON;
+    } else if (crtscts < 0) {
+	tiox.x_hflag &= ~(RTSXOFF|CTSXON);
+    }
+#endif
+
+    tios.c_cflag |= CS8 | CREAD | HUPCL;
+    if (local || !modem)
+	tios.c_cflag |= CLOCAL;
+    tios.c_iflag = IGNBRK | IGNPAR;
+    tios.c_oflag = 0;
+    tios.c_lflag = 0;
+    tios.c_cc[VMIN] = 1;
+    tios.c_cc[VTIME] = 0;
+
+    if (crtscts == -2) {
+	tios.c_iflag |= IXON | IXOFF;
+	tios.c_cc[VSTOP] = 0x13;	/* DC3 = XOFF = ^S */
+	tios.c_cc[VSTART] = 0x11;	/* DC1 = XON  = ^Q */
+    }
+
+    speed = translate_speed(inspeed);
+    if (speed) {
+	cfsetospeed(&tios, speed);
+	cfsetispeed(&tios, speed);
+    } else {
+	speed = cfgetospeed(&tios);
+	/*
+	 * We can't proceed if the serial port speed is 0,
+	 * since that implies that the serial port is disabled.
+	 */
+	if (speed == B0) {
+	    syslog(LOG_ERR, "Baud rate for %s is 0; need explicit baud rate",
+		   devnam);
+	    die(1);
+	}
+    }
+
+    if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) {
+	syslog(LOG_ERR, "tcsetattr: %m");
+	die(1);
+    }
+
+#ifndef CRTSCTS
+    if (termiox_ok && ioctl (fd, TCSETXF, &tiox) < 0){
+	syslog (LOG_ERR, "TCSETXF: %m");
+    }
+#endif
+
+    baud_rate = inspeed = baud_rate_of(speed);
+    restore_term = 1;
+}
+
+/*
+ * restore_tty - restore the terminal to the saved settings.
+ */
+void
+restore_tty(fd)
+    int fd;
+{
+    if (restore_term) {
+	if (!default_device) {
+	    /*
+	     * Turn off echoing, because otherwise we can get into
+	     * a loop with the tty and the modem echoing to each other.
+	     * We presume we are the sole user of this tty device, so
+	     * when we close it, it will revert to its defaults anyway.
+	     */
+	    inittermios.c_lflag &= ~(ECHO | ECHONL);
+	}
+	if (tcsetattr(fd, TCSAFLUSH, &inittermios) < 0)
+	    if (!hungup && errno != ENXIO)
+		syslog(LOG_WARNING, "tcsetattr: %m");
+#ifndef CRTSCTS
+	if (ioctl (fd, TCSETXF, &inittermiox) < 0){
+	    if (!hungup && errno != ENXIO)
+		syslog (LOG_ERR, "TCSETXF: %m");
+	}
+#endif
+	ioctl(fd, TIOCSWINSZ, &wsinfo);
+	restore_term = 0;
+    }
+}
+
+/*
+ * setdtr - control the DTR line on the serial port.
+ * This is called from die(), so it shouldn't call die().
+ */
+void
+setdtr(fd, on)
+int fd, on;
+{
+    int modembits = TIOCM_DTR;
+
+    ioctl(fd, (on? TIOCMBIS: TIOCMBIC), &modembits);
+}
+
+/*
+ * open_loopback - open the device we use for getting packets
+ * in demand mode.  Under Solaris 2, we use our existing fd
+ * to the ppp driver.
+ */
+void
+open_ppp_loopback()
+{
+}
+
+/*
+ * output - Output PPP packet.
+ */
+void
+output(unit, p, len)
+    int unit;
+    u_char *p;
+    int len;
+{
+    struct strbuf data;
+    int retries;
+    struct pollfd pfd;
+
+    if (debug)
+	log_packet(p, len, "sent ", LOG_DEBUG);
+
+    data.len = len;
+    data.buf = (caddr_t) p;
+    retries = 4;
+    while (putmsg(pppfd, NULL, &data, 0) < 0) {
+	if (--retries < 0 || (errno != EWOULDBLOCK && errno != EAGAIN)) {
+	    if (errno != ENXIO)
+		syslog(LOG_ERR, "Couldn't send packet: %m");
+	    break;
+	}
+	pfd.fd = pppfd;
+	pfd.events = POLLOUT;
+	poll(&pfd, 1, 250);	/* wait for up to 0.25 seconds */
+    }
+}
+
+
+/*
+ * wait_input - wait until there is data available on fd,
+ * for the length of time specified by *timo (indefinite
+ * if timo is NULL).
+ */
+void
+wait_input(timo)
+    struct timeval *timo;
+{
+    int t;
+    struct pollfd pfd;
+
+    t = timo == NULL? -1: timo->tv_sec * 1000 + timo->tv_usec / 1000;
+    pfd.fd = pppfd;
+    pfd.events = POLLIN | POLLPRI | POLLHUP;
+    if (poll(&pfd, 1, t) < 0 && errno != EINTR) {
+	syslog(LOG_ERR, "poll: %m");
+	die(1);
+    }
+}
+
+/*
+ * wait_loop_output - wait until there is data available on the
+ * loopback, for the length of time specified by *timo (indefinite
+ * if timo is NULL).
+ */
+void
+wait_loop_output(timo)
+    struct timeval *timo;
+{
+    wait_input(timo);
+}
+
+/*
+ * wait_time - wait for a given length of time or until a
+ * signal is received.
+ */
+void
+wait_time(timo)
+    struct timeval *timo;
+{
+    int n;
+
+    n = select(0, NULL, NULL, NULL, timo);
+    if (n < 0 && errno != EINTR) {
+	syslog(LOG_ERR, "select: %m");
+	die(1);
+    }
+}
+
+
+/*
+ * read_packet - get a PPP packet from the serial device.
+ */
+int
+read_packet(buf)
+    u_char *buf;
+{
+    struct strbuf ctrl, data;
+    int flags, len;
+    unsigned char ctrlbuf[sizeof(union DL_primitives) + 64];
+
+    for (;;) {
+	data.maxlen = PPP_MRU + PPP_HDRLEN;
+	data.buf = (caddr_t) buf;
+	ctrl.maxlen = sizeof(ctrlbuf);
+	ctrl.buf = (caddr_t) ctrlbuf;
+	flags = 0;
+	len = getmsg(pppfd, &ctrl, &data, &flags);
+	if (len < 0) {
+	    if (errno = EAGAIN || errno == EINTR)
+		return -1;
+	    syslog(LOG_ERR, "Error reading packet: %m");
+	    die(1);
+	}
+
+	if (ctrl.len <= 0)
+	    return data.len;
+
+	/*
+	 * Got a M_PROTO or M_PCPROTO message.  Interpret it
+	 * as a DLPI primitive??
+	 */
+	if (debug)
+	    syslog(LOG_DEBUG, "got dlpi prim 0x%x, len=%d",
+		   ((union DL_primitives *)ctrlbuf)->dl_primitive, ctrl.len);
+
+    }
+}
+
+/*
+ * get_loop_output - get outgoing packets from the ppp device,
+ * and detect when we want to bring the real link up.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ */
+int
+get_loop_output()
+{
+    int len;
+    int rv = 0;
+
+    while ((len = read_packet(inpacket_buf)) > 0) {
+	if (loop_frame(inpacket_buf, len))
+	    rv = 1;
+    }
+    return rv;
+}
+
+/*
+ * ppp_send_config - configure the transmit characteristics of
+ * the ppp interface.
+ */
+void
+ppp_send_config(unit, mtu, asyncmap, pcomp, accomp)
+    int unit, mtu;
+    u_int32_t asyncmap;
+    int pcomp, accomp;
+{
+    int cf[2];
+    struct ifreq ifr;
+
+    link_mtu = mtu;
+    if (strioctl(pppfd, PPPIO_MTU, &mtu, sizeof(mtu), 0) < 0) {
+	if (hungup && errno == ENXIO)
+	    return;
+	syslog(LOG_ERR, "Couldn't set MTU: %m");
+    }
+    if (fdmuxid >= 0) {
+	/* can't set these if we don't have a stream attached below /dev/ppp */
+	if (strioctl(pppfd, PPPIO_XACCM, &asyncmap, sizeof(asyncmap), 0) < 0) {
+	    syslog(LOG_ERR, "Couldn't set transmit ACCM: %m");
+	}
+	cf[0] = (pcomp? COMP_PROT: 0) + (accomp? COMP_AC: 0);
+	cf[1] = COMP_PROT | COMP_AC;
+	if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+	    syslog(LOG_ERR, "Couldn't set prot/AC compression: %m");
+	}
+    }
+
+    /* set the MTU for IP as well */
+    memset(&ifr, 0, sizeof(ifr));
+    strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+    ifr.ifr_metric = link_mtu;
+    if (ioctl(ipfd, SIOCSIFMTU, &ifr) < 0) {
+	syslog(LOG_ERR, "Couldn't set IP MTU: %m");
+    }
+}
+
+/*
+ * ppp_set_xaccm - set the extended transmit ACCM for the interface.
+ */
+void
+ppp_set_xaccm(unit, accm)
+    int unit;
+    ext_accm accm;
+{
+    if (fdmuxid >= 0
+	&& strioctl(pppfd, PPPIO_XACCM, accm, sizeof(ext_accm), 0) < 0) {
+	if (!hungup || errno != ENXIO)
+	    syslog(LOG_WARNING, "Couldn't set extended ACCM: %m");
+    }
+}
+
+/*
+ * ppp_recv_config - configure the receive-side characteristics of
+ * the ppp interface.
+ */
+void
+ppp_recv_config(unit, mru, asyncmap, pcomp, accomp)
+    int unit, mru;
+    u_int32_t asyncmap;
+    int pcomp, accomp;
+{
+    int cf[2];
+
+    link_mru = mru;
+    if (strioctl(pppfd, PPPIO_MRU, &mru, sizeof(mru), 0) < 0) {
+	if (hungup && errno == ENXIO)
+	    return;
+	syslog(LOG_ERR, "Couldn't set MRU: %m");
+    }
+    if (fdmuxid >= 0) {
+	/* can't set these if we don't have a stream attached below /dev/ppp */
+	if (strioctl(pppfd, PPPIO_RACCM, &asyncmap, sizeof(asyncmap), 0) < 0) {
+	    syslog(LOG_ERR, "Couldn't set receive ACCM: %m");
+	}
+	cf[0] = (pcomp? DECOMP_PROT: 0) + (accomp? DECOMP_AC: 0);
+	cf[1] = DECOMP_PROT | DECOMP_AC;
+	if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+	    syslog(LOG_ERR, "Couldn't set prot/AC decompression: %m");
+	}
+    }
+}
+
+/*
+ * ccp_test - ask kernel whether a given compression method
+ * is acceptable for use.
+ */
+int
+ccp_test(unit, opt_ptr, opt_len, for_transmit)
+    int unit, opt_len, for_transmit;
+    u_char *opt_ptr;
+{
+    if (strioctl(pppfd, (for_transmit? PPPIO_XCOMP: PPPIO_RCOMP),
+		 opt_ptr, opt_len, 0) >= 0)
+	return 1;
+    return (errno == ENOSR)? 0: -1;
+}
+
+/*
+ * ccp_flags_set - inform kernel about the current state of CCP.
+ */
+void
+ccp_flags_set(unit, isopen, isup)
+    int unit, isopen, isup;
+{
+    int cf[2];
+
+    cf[0] = (isopen? CCP_ISOPEN: 0) + (isup? CCP_ISUP: 0);
+    cf[1] = CCP_ISOPEN | CCP_ISUP | CCP_ERROR | CCP_FATALERROR;
+    if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+	if (!hungup || errno != ENXIO)
+	    syslog(LOG_ERR, "Couldn't set kernel CCP state: %m");
+    }
+}
+
+/*
+ * get_idle_time - return how long the link has been idle.
+ */
+int
+get_idle_time(u, ip)
+    int u;
+    struct ppp_idle *ip;
+{
+    return strioctl(pppfd, PPPIO_GIDLE, ip, 0, sizeof(struct ppp_idle)) >= 0;
+}
+
+#if 0
+/*
+ * set_filters - transfer the pass and active filters to the kernel.
+ */
+int
+set_filters(pass, active)
+    struct bpf_program *pass, *active;
+{
+    int ret = 1;
+
+    if (pass->bf_len > 0) {
+	if (strioctl(pppfd, PPPIO_PASSFILT, pass,
+		     sizeof(struct bpf_program), 0) < 0) {
+	    syslog(LOG_ERR, "Couldn't set pass-filter in kernel: %m");
+	    ret = 0;
+	}
+    }
+    if (active->bf_len > 0) {
+	if (strioctl(pppfd, PPPIO_ACTIVEFILT, active,
+		     sizeof(struct bpf_program), 0) < 0) {
+	    syslog(LOG_ERR, "Couldn't set active-filter in kernel: %m");
+	    ret = 0;
+	}
+    }
+    return ret;
+}
+#endif
+
+/*
+ * ccp_fatal_error - returns 1 if decompression was disabled as a
+ * result of an error detected after decompression of a packet,
+ * 0 otherwise.  This is necessary because of patent nonsense.
+ */
+int
+ccp_fatal_error(unit)
+    int unit;
+{
+    int cf[2];
+
+    cf[0] = cf[1] = 0;
+    if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+	if (errno != ENXIO && errno != EINVAL)
+	    syslog(LOG_ERR, "Couldn't get compression flags: %m");
+	return 0;
+    }
+    return cf[0] & CCP_FATALERROR;
+}
+
+/*
+ * sifvjcomp - config tcp header compression
+ */
+int
+sifvjcomp(u, vjcomp, xcidcomp, xmaxcid)
+    int u, vjcomp, xcidcomp, xmaxcid;
+{
+    int cf[2];
+    char maxcid[2];
+
+    if (vjcomp) {
+	maxcid[0] = xcidcomp;
+	maxcid[1] = 15;		/* XXX should be rmaxcid */
+	if (strioctl(pppfd, PPPIO_VJINIT, maxcid, sizeof(maxcid), 0) < 0) {
+	    syslog(LOG_ERR, "Couldn't initialize VJ compression: %m");
+	}
+    }
+
+    cf[0] = (vjcomp? COMP_VJC + DECOMP_VJC: 0)	/* XXX this is wrong */
+	+ (xcidcomp? COMP_VJCCID + DECOMP_VJCCID: 0);
+    cf[1] = COMP_VJC + DECOMP_VJC + COMP_VJCCID + DECOMP_VJCCID;
+    if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+	if (vjcomp)
+	    syslog(LOG_ERR, "Couldn't enable VJ compression: %m");
+    }
+
+    return 1;
+}
+
+/*
+ * sifup - Config the interface up and enable IP packets to pass.
+ */
+int
+sifup(u)
+    int u;
+{
+    struct ifreq ifr;
+
+    strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+    if (ioctl(ipfd, SIOCGIFFLAGS, &ifr) < 0) {
+	syslog(LOG_ERR, "Couldn't mark interface up (get): %m");
+	return 0;
+    }
+    ifr.ifr_flags |= IFF_UP;
+    if (ioctl(ipfd, SIOCSIFFLAGS, &ifr) < 0) {
+	syslog(LOG_ERR, "Couldn't mark interface up (set): %m");
+	return 0;
+    }
+    if_is_up = 1;
+    return 1;
+}
+
+/*
+ * sifdown - Config the interface down and disable IP.
+ */
+int
+sifdown(u)
+    int u;
+{
+    struct ifreq ifr;
+
+    if (ipmuxid < 0)
+	return 1;
+    strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+    if (ioctl(ipfd, SIOCGIFFLAGS, &ifr) < 0) {
+	syslog(LOG_ERR, "Couldn't mark interface down (get): %m");
+	return 0;
+    }
+    ifr.ifr_flags &= ~IFF_UP;
+    if (ioctl(ipfd, SIOCSIFFLAGS, &ifr) < 0) {
+	syslog(LOG_ERR, "Couldn't mark interface down (set): %m");
+	return 0;
+    }
+    if_is_up = 0;
+    return 1;
+}
+
+/*
+ * sifnpmode - Set the mode for handling packets for a given NP.
+ */
+int
+sifnpmode(u, proto, mode)
+    int u;
+    int proto;
+    enum NPmode mode;
+{
+    int npi[2];
+
+    npi[0] = proto;
+    npi[1] = (int) mode;
+    if (strioctl(pppfd, PPPIO_NPMODE, &npi, 2 * sizeof(int), 0) < 0) {
+	syslog(LOG_ERR, "ioctl(set NP %d mode to %d): %m", proto, mode);
+	return 0;
+    }
+    return 1;
+}
+
+#define INET_ADDR(x)	(((struct sockaddr_in *) &(x))->sin_addr.s_addr)
+
+/*
+ * sifaddr - Config the interface IP addresses and netmask.
+ */
+int
+sifaddr(u, o, h, m)
+    int u;
+    u_int32_t o, h, m;
+{
+    struct ifreq ifr;
+    int ret = 1;
+
+    memset(&ifr, 0, sizeof(ifr));
+    strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+    ifr.ifr_addr.sa_family = AF_INET;
+    INET_ADDR(ifr.ifr_addr) = m;
+    if (ioctl(ipfd, SIOCSIFNETMASK, &ifr) < 0) {
+	syslog(LOG_ERR, "Couldn't set IP netmask: %m");
+	ret = 0;
+    }
+    ifr.ifr_addr.sa_family = AF_INET;
+    INET_ADDR(ifr.ifr_addr) = o;
+    if (ioctl(ipfd, SIOCSIFADDR, &ifr) < 0) {
+	syslog(LOG_ERR, "Couldn't set local IP address: %m");
+	ret = 0;
+    }
+
+    /*
+     * On some systems, we have to explicitly set the point-to-point
+     * flag bit before we can set a destination address.
+     */
+    if (ioctl(ipfd, SIOCGIFFLAGS, &ifr) >= 0
+	&& (ifr.ifr_flags & IFF_POINTOPOINT) == 0) {
+	ifr.ifr_flags |= IFF_POINTOPOINT;
+	if (ioctl(ipfd, SIOCSIFFLAGS, &ifr) < 0) {
+	    syslog(LOG_ERR, "Couldn't mark interface pt-to-pt: %m");
+	    ret = 0;
+	}
+    }
+    ifr.ifr_dstaddr.sa_family = AF_INET;
+    INET_ADDR(ifr.ifr_dstaddr) = h;
+    if (ioctl(ipfd, SIOCSIFDSTADDR, &ifr) < 0) {
+	syslog(LOG_ERR, "Couldn't set remote IP address: %m");
+	ret = 0;
+    }
+#if 0	/* now done in ppp_send_config */
+    ifr.ifr_metric = link_mtu;
+    if (ioctl(ipfd, SIOCSIFMTU, &ifr) < 0) {
+	syslog(LOG_ERR, "Couldn't set IP MTU: %m");
+    }
+#endif
+
+    return ret;
+}
+
+/*
+ * cifaddr - Clear the interface IP addresses, and delete routes
+ * through the interface if possible.
+ */
+int
+cifaddr(u, o, h)
+    int u;
+    u_int32_t o, h;
+{
+#if defined(__USLC__)		/* was: #if 0 */
+    cifroute(unit, ouraddr, hisaddr);
+    if (ipmuxid >= 0) {
+	syslog(LOG_NOTICE, "Removing ppp interface unit");
+	if (ioctl(ipfd, I_UNLINK, ipmuxid) < 0) {
+	    syslog(LOG_ERR, "Can't remove ppp interface unit: %m");
+	    return 0;
+	}
+	ipmuxid = -1;
+    }
+#endif
+    return 1;
+}
+
+/*
+ * sifdefaultroute - assign a default route through the address given.
+ */
+int
+sifdefaultroute(u, l, g)
+    int u;
+    u_int32_t l, g;
+{
+    struct rtentry rt;
+
+#if defined(__USLC__)
+    g = l;			/* use the local address as gateway */
+#endif
+    memset(&rt, 0, sizeof(rt));
+    rt.rt_dst.sa_family = AF_INET;
+    INET_ADDR(rt.rt_dst) = 0;
+    rt.rt_gateway.sa_family = AF_INET;
+    INET_ADDR(rt.rt_gateway) = g;
+    rt.rt_flags = RTF_GATEWAY;
+
+    if (ioctl(ipfd, SIOCADDRT, &rt) < 0) {
+	syslog(LOG_ERR, "Can't add default route: %m");
+	return 0;
+    }
+
+    default_route_gateway = g;
+    return 1;
+}
+
+/*
+ * cifdefaultroute - delete a default route through the address given.
+ */
+int
+cifdefaultroute(u, l, g)
+    int u;
+    u_int32_t l, g;
+{
+    struct rtentry rt;
+
+#if defined(__USLC__)
+    g = l;			/* use the local address as gateway */
+#endif
+    memset(&rt, 0, sizeof(rt));
+    rt.rt_dst.sa_family = AF_INET;
+    INET_ADDR(rt.rt_dst) = 0;
+    rt.rt_gateway.sa_family = AF_INET;
+    INET_ADDR(rt.rt_gateway) = g;
+    rt.rt_flags = RTF_GATEWAY;
+
+    if (ioctl(ipfd, SIOCDELRT, &rt) < 0) {
+	syslog(LOG_ERR, "Can't delete default route: %m");
+	return 0;
+    }
+
+    default_route_gateway = 0;
+    return 1;
+}
+
+/*
+ * sifproxyarp - Make a proxy ARP entry for the peer.
+ */
+int
+sifproxyarp(unit, hisaddr)
+    int unit;
+    u_int32_t hisaddr;
+{
+    struct arpreq arpreq;
+
+    memset(&arpreq, 0, sizeof(arpreq));
+    if (!get_ether_addr(hisaddr, &arpreq.arp_ha))
+	return 0;
+
+    arpreq.arp_pa.sa_family = AF_INET;
+    INET_ADDR(arpreq.arp_pa) = hisaddr;
+    arpreq.arp_flags = ATF_PERM | ATF_PUBL;
+    if (ioctl(ipfd, SIOCSARP, (caddr_t) &arpreq) < 0) {
+	syslog(LOG_ERR, "Couldn't set proxy ARP entry: %m");
+	return 0;
+    }
+
+    proxy_arp_addr = hisaddr;
+    return 1;
+}
+
+/*
+ * cifproxyarp - Delete the proxy ARP entry for the peer.
+ */
+int
+cifproxyarp(unit, hisaddr)
+    int unit;
+    u_int32_t hisaddr;
+{
+    struct arpreq arpreq;
+
+    memset(&arpreq, 0, sizeof(arpreq));
+    arpreq.arp_pa.sa_family = AF_INET;
+    INET_ADDR(arpreq.arp_pa) = hisaddr;
+    if (ioctl(ipfd, SIOCDARP, (caddr_t)&arpreq) < 0) {
+	syslog(LOG_ERR, "Couldn't delete proxy ARP entry: %m");
+	return 0;
+    }
+
+    proxy_arp_addr = 0;
+    return 1;
+}
+
+/*
+ * get_ether_addr - get the hardware address of an interface on the
+ * the same subnet as ipaddr.
+ */
+#define MAX_IFS		32
+
+static int
+get_ether_addr(ipaddr, hwaddr)
+    u_int32_t ipaddr;
+    struct sockaddr *hwaddr;
+{
+    struct ifreq *ifr, *ifend, ifreq;
+    int nif;
+    struct ifconf ifc;
+    u_int32_t ina, mask;
+
+    /*
+     * Scan through the system's network interfaces.
+     */
+#ifdef SIOCGIFNUM
+    if (ioctl(ipfd, SIOCGIFNUM, &nif) < 0)
+#endif
+	nif = MAX_IFS;
+    ifc.ifc_len = nif * sizeof(struct ifreq);
+    ifc.ifc_buf = (caddr_t) malloc(ifc.ifc_len);
+    if (ifc.ifc_buf == 0)
+	return 0;
+    if (ioctl(ipfd, SIOCGIFCONF, &ifc) < 0) {
+	syslog(LOG_WARNING, "Couldn't get system interface list: %m");
+	free(ifc.ifc_buf);
+	return 0;
+    }
+    ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+    for (ifr = ifc.ifc_req; ifr < ifend; ++ifr) {
+	if (ifr->ifr_addr.sa_family != AF_INET)
+	    continue;
+	/*
+	 * Check that the interface is up, and not point-to-point or loopback.
+	 */
+	strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+	if (ioctl(ipfd, SIOCGIFFLAGS, &ifreq) < 0)
+	    continue;
+	if ((ifreq.ifr_flags &
+	     (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP))
+	    != (IFF_UP|IFF_BROADCAST))
+	    continue;
+	/*
+	 * Get its netmask and check that it's on the right subnet.
+	 */
+	if (ioctl(ipfd, SIOCGIFNETMASK, &ifreq) < 0)
+	    continue;
+	ina = INET_ADDR(ifr->ifr_addr);
+	mask = INET_ADDR(ifreq.ifr_addr);
+	if ((ipaddr & mask) == (ina & mask))
+	    break;
+    }
+
+    if (ifr >= ifend) {
+	syslog(LOG_WARNING, "No suitable interface found for proxy ARP");
+	free(ifc.ifc_buf);
+	return 0;
+    }
+
+    syslog(LOG_INFO, "found interface %s for proxy ARP", ifr->ifr_name);
+    if (!get_hw_addr(ifr->ifr_name, hwaddr)) {
+	syslog(LOG_ERR, "Couldn't get hardware address for %s", ifr->ifr_name);
+	free(ifc.ifc_buf);
+	return 0;
+    }
+
+    free(ifc.ifc_buf);
+    return 1;
+}
+
+/*
+ * get_hw_addr - obtain the hardware address for a named interface.
+ */
+static int
+get_hw_addr(name, hwaddr)
+    char *name;
+    struct sockaddr *hwaddr;
+{
+    char *p, *q;
+    int unit, iffd, adrlen;
+    unsigned char *adrp;
+    char ifdev[24];
+    struct {
+	union DL_primitives prim;
+	char space[64];
+    } reply;
+
+    /*
+     * We have to open the device and ask it for its hardware address.
+     * First split apart the device name and unit.
+     */
+    strcpy(ifdev, "/dev/");
+    q = ifdev + 5;		/* strlen("/dev/") */
+    while (*name != 0 && !isdigit(*name))
+	*q++ = *name++;
+    *q = 0;
+    unit = atoi(name);
+
+    /*
+     * Open the device and do a DLPI attach and phys_addr_req.
+     */
+    iffd = open(ifdev, O_RDWR);
+    if (iffd < 0) {
+	syslog(LOG_ERR, "Can't open %s: %m", ifdev);
+	return 0;
+    }
+    if (dlpi_attach(iffd, unit) < 0
+	|| dlpi_get_reply(iffd, &reply.prim, DL_OK_ACK, sizeof(reply)) < 0
+	|| dlpi_info_req(iffd) < 0
+	|| dlpi_get_reply(iffd, &reply.prim, DL_INFO_ACK, sizeof(reply)) < 0) {
+	close(iffd);
+	return 0;
+    }
+
+    adrlen = reply.prim.info_ack.dl_addr_length;
+    adrp = (unsigned char *)&reply + reply.prim.info_ack.dl_addr_offset;
+#if DL_CURRENT_VERSION >= 2
+    if (reply.prim.info_ack.dl_sap_length < 0)
+	adrlen += reply.prim.info_ack.dl_sap_length;
+    else
+	adrp += reply.prim.info_ack.dl_sap_length;
+#endif
+    hwaddr->sa_family = AF_UNSPEC;
+    memcpy(hwaddr->sa_data, adrp, adrlen);
+
+    return 1;
+}
+
+static int
+dlpi_attach(fd, ppa)
+    int fd, ppa;
+{
+    dl_attach_req_t req;
+    struct strbuf buf;
+
+    req.dl_primitive = DL_ATTACH_REQ;
+    req.dl_ppa = ppa;
+    buf.len = sizeof(req);
+    buf.buf = (void *) &req;
+    return putmsg(fd, &buf, NULL, RS_HIPRI);
+}
+
+static int
+dlpi_info_req(fd)
+    int fd;
+{
+    dl_info_req_t req;
+    struct strbuf buf;
+
+    req.dl_primitive = DL_INFO_REQ;
+    buf.len = sizeof(req);
+    buf.buf = (void *) &req;
+    return putmsg(fd, &buf, NULL, RS_HIPRI);
+}
+
+static int
+dlpi_get_reply(fd, reply, expected_prim, maxlen)
+    union DL_primitives *reply;
+    int fd, expected_prim, maxlen;
+{
+    struct strbuf buf;
+    int flags, n;
+    struct pollfd pfd;
+
+    /*
+     * Use poll to wait for a message with a timeout.
+     */
+    pfd.fd = fd;
+    pfd.events = POLLIN | POLLPRI;
+    do {
+	n = poll(&pfd, 1, 1000);
+    } while (n == -1 && errno == EINTR);
+    if (n <= 0)
+	return -1;
+
+    /*
+     * Get the reply.
+     */
+    buf.maxlen = maxlen;
+    buf.buf = (void *) reply;
+    flags = 0;
+    if (getmsg(fd, &buf, NULL, &flags) < 0)
+	return -1;
+
+    if (buf.len < sizeof(ulong)) {
+	if (debug)
+	    syslog(LOG_DEBUG, "dlpi response short (len=%d)\n", buf.len);
+	return -1;
+    }
+
+    if (reply->dl_primitive == expected_prim)
+	return 0;
+
+    if (debug) {
+	if (reply->dl_primitive == DL_ERROR_ACK) {
+	    syslog(LOG_DEBUG, "dlpi error %d (unix errno %d) for prim %x\n",
+		   reply->error_ack.dl_errno, reply->error_ack.dl_unix_errno,
+		   reply->error_ack.dl_error_primitive);
+	} else {
+	    syslog(LOG_DEBUG, "dlpi unexpected response prim %x\n",
+		   reply->dl_primitive);
+	}
+    }
+
+    return -1;
+}
+
+/*
+ * Return user specified netmask, modified by any mask we might determine
+ * for address `addr' (in network byte order).
+ * Here we scan through the system's list of interfaces, looking for
+ * any non-point-to-point interfaces which might appear to be on the same
+ * network as `addr'.  If we find any, we OR in their netmask to the
+ * user-specified netmask.
+ */
+u_int32_t
+GetMask(addr)
+    u_int32_t addr;
+{
+    u_int32_t mask, nmask, ina;
+    struct ifreq *ifr, *ifend, ifreq;
+    int nif;
+    struct ifconf ifc;
+
+    addr = ntohl(addr);
+    if (IN_CLASSA(addr))	/* determine network mask for address class */
+	nmask = IN_CLASSA_NET;
+    else if (IN_CLASSB(addr))
+	nmask = IN_CLASSB_NET;
+    else
+	nmask = IN_CLASSC_NET;
+    /* class D nets are disallowed by bad_ip_adrs */
+    mask = netmask | htonl(nmask);
+
+    /*
+     * Scan through the system's network interfaces.
+     */
+#ifdef SIOCGIFNUM
+    if (ioctl(ipfd, SIOCGIFNUM, &nif) < 0)
+#endif
+	nif = MAX_IFS;
+    ifc.ifc_len = nif * sizeof(struct ifreq);
+    ifc.ifc_buf = (caddr_t) malloc(ifc.ifc_len);
+    if (ifc.ifc_buf == 0)
+	return mask;
+    if (ioctl(ipfd, SIOCGIFCONF, &ifc) < 0) {
+	syslog(LOG_WARNING, "Couldn't get system interface list: %m");
+	free(ifc.ifc_buf);
+	return mask;
+    }
+    ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+    for (ifr = ifc.ifc_req; ifr < ifend; ++ifr) {
+	/*
+	 * Check the interface's internet address.
+	 */
+	if (ifr->ifr_addr.sa_family != AF_INET)
+	    continue;
+	ina = INET_ADDR(ifr->ifr_addr);
+	if ((ntohl(ina) & nmask) != (addr & nmask))
+	    continue;
+	/*
+	 * Check that the interface is up, and not point-to-point or loopback.
+	 */
+	strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+	if (ioctl(ipfd, SIOCGIFFLAGS, &ifreq) < 0)
+	    continue;
+	if ((ifreq.ifr_flags & (IFF_UP|IFF_POINTOPOINT|IFF_LOOPBACK))
+	    != IFF_UP)
+	    continue;
+	/*
+	 * Get its netmask and OR it into our mask.
+	 */
+	if (ioctl(ipfd, SIOCGIFNETMASK, &ifreq) < 0)
+	    continue;
+	mask |= INET_ADDR(ifreq.ifr_addr);
+    }
+
+    free(ifc.ifc_buf);
+    return mask;
+}
+
+/*
+ * logwtmp - write an accounting record to the /var/adm/wtmp file.
+ */
+void
+logwtmp(line, name, host)
+    const char *line, *name, *host;
+{
+    static struct utmpx utmpx;
+
+    if (name[0] != 0) {
+	/* logging in */
+	strncpy(utmpx.ut_user, name, sizeof(utmpx.ut_user));
+	strncpy(utmpx.ut_id, ifname, sizeof(utmpx.ut_id));
+	strncpy(utmpx.ut_line, line, sizeof(utmpx.ut_line));
+	utmpx.ut_pid = getpid();
+	utmpx.ut_type = USER_PROCESS;
+    } else {
+	utmpx.ut_type = DEAD_PROCESS;
+    }
+    gettimeofday(&utmpx.ut_tv, NULL);
+    updwtmpx("/var/adm/wtmpx", &utmpx);
+}
+
+/*
+ * get_host_seed - return the serial number of this machine.
+ */
+int
+get_host_seed()
+{
+    char buf[32];
+
+    if (sysinfo(SI_HW_SERIAL, buf, sizeof(buf)) < 0) {
+	syslog(LOG_ERR, "sysinfo: %m");
+	return 0;
+    }
+    return (int) strtoul(buf, NULL, 16);
+}
+
+static int
+strioctl(fd, cmd, ptr, ilen, olen)
+    int fd, cmd, ilen, olen;
+    void *ptr;
+{
+    struct strioctl str;
+
+    str.ic_cmd = cmd;
+    str.ic_timout = 0;
+    str.ic_len = ilen;
+    str.ic_dp = ptr;
+    if (ioctl(fd, I_STR, &str) == -1)
+	return -1;
+    if (str.ic_len != olen)
+	syslog(LOG_DEBUG, "strioctl: expected %d bytes, got %d for cmd %x\n",
+	       olen, str.ic_len, cmd);
+    return 0;
+}
+
+/*
+ * lock - create a lock file for the named lock device
+ */
+
+#define LOCK_PREFIX	"/var/spool/locks/LK."
+static char lock_file[40];	/* name of lock file created */
+
+int
+lock(dev)
+    char *dev;
+{
+    int n, fd, pid;
+    struct stat sbuf;
+    char ascii_pid[12];
+
+    if (stat(dev, &sbuf) < 0) {
+	syslog(LOG_ERR, "Can't get device number for %s: %m", dev);
+	return -1;
+    }
+    if ((sbuf.st_mode & S_IFMT) != S_IFCHR) {
+	syslog(LOG_ERR, "Can't lock %s: not a character device", dev);
+	return -1;
+    }
+    sprintf(lock_file, "%s%03d.%03d.%03d", LOCK_PREFIX, major(sbuf.st_dev),
+	    major(sbuf.st_rdev), minor(sbuf.st_rdev));
+
+    while ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0) {
+	if (errno == EEXIST
+	    && (fd = open(lock_file, O_RDONLY, 0)) >= 0) {
+	    /* Read the lock file to find out who has the device locked */
+	    n = read(fd, ascii_pid, 11);
+	    if (n <= 0) {
+		syslog(LOG_ERR, "Can't read pid from lock file %s", lock_file);
+		close(fd);
+	    } else {
+		ascii_pid[n] = 0;
+		pid = atoi(ascii_pid);
+		if (pid > 0 && kill(pid, 0) == -1 && errno == ESRCH) {
+		    /* pid no longer exists - remove the lock file */
+		    if (unlink(lock_file) == 0) {
+			close(fd);
+			syslog(LOG_NOTICE, "Removed stale lock on %s (pid %d)",
+			       dev, pid);
+			continue;
+		    } else
+			syslog(LOG_WARNING, "Couldn't remove stale lock on %s",
+			       dev);
+		} else
+		    syslog(LOG_NOTICE, "Device %s is locked by pid %d",
+			   dev, pid);
+	    }
+	    close(fd);
+	} else
+	    syslog(LOG_ERR, "Can't create lock file %s: %m", lock_file);
+	lock_file[0] = 0;
+	return -1;
+    }
+
+    sprintf(ascii_pid, "%10d\n", getpid());
+    write(fd, ascii_pid, 11);
+
+    close(fd);
+    return 1;
+}
+
+/*
+ * unlock - remove our lockfile
+ */
+void
+unlock()
+{
+    if (lock_file[0]) {
+	unlink(lock_file);
+	lock_file[0] = 0;
+    }
+}
+
+
+/*
+ * cifroute - delete a route through the addresses given.
+ */
+int
+cifroute(u, our, his)
+    int u;
+    u_int32_t our, his;
+{
+    struct rtentry rt;
+
+    memset(&rt, 0, sizeof(rt));
+    rt.rt_dst.sa_family = AF_INET;
+    INET_ADDR(rt.rt_dst) = his;
+    rt.rt_gateway.sa_family = AF_INET;
+    INET_ADDR(rt.rt_gateway) = our;
+    rt.rt_flags = RTF_HOST;
+
+    if (ioctl(ipfd, SIOCDELRT, &rt) < 0) {
+	syslog(LOG_ERR, "Can't delete route: %m");
+	return 0;
+    }
+
+    return 1;
+}

>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-bugs->peter 
Responsible-Changed-By: johan 
Responsible-Changed-When: Fri May 10 18:46:27 PDT 2002 
Responsible-Changed-Why:  
Peter, can you have a look at this proposed addition to 
pppd. 


http://www.freebsd.org/cgi/query-pr.cgi?pr=15619 
State-Changed-From-To: open->closed 
State-Changed-By: maxim 
State-Changed-When: Fri Jul 29 10:50:43 UTC 2011 
State-Changed-Why:  
We don't have pppd(8) in the base anymore. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=15619 
>Unformatted:
