ftp.nice.ch/pub/next/unix/communication/Alby.2.PPP.0.3.N.bs.tar.gz#/source/ppp.c

This is ppp.c in view mode; [Download] [Up]

/*
 * ppp.c - Point-to-Point Protocol.
 *
 * Copyright (c) 1989 Carnegie Mellon University.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by 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.
 */

/*
 * TODO:
 * Don't prompt for passwd on /dev/tty (go into server mode).
 * Might want to choose IP addresses from config file based on user id
 * if the server, or based on remote host if tip'd client.
 * Way to set asyncmap.
 * Wait for tty queue to empty before exiting in quit.
 */

/*
 * There are three scenarios:
 * 1.  ppp used as daemon started from /etc/rc or perhaps /etc/ttys.
 *	a.  server
 *	b.  authentication necessary
 *	c.  want to use constant local ip addr
 *	d.  want to use constant remote ip addr, constant ip addr based on
 *	    authenticated user, or request ip addr
 * 2.  ppp used on /dev/tty after remote login.
 *	a.  server
 *	b.  no authentication necessary or allowed
 *	c.  want to use constant local ip addr
 *	d.  want to use constant remote ip addr, constant ip addr based on
 *	    authenticated user, or request ip addr
 * 3.  ppp used on line after tip'ing out.
 *	a.  client
 *	b.  remote end may request authentication
 *	c.  want to use constant local ip addr or request ip addr
 *	d.  want to use constant remote ip addr based on tip'd host, or
 *	    request remote ip addr
 */

#include <string.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#ifndef	STREAMS
#include <sgtty.h>
#endif
#include <pwd.h>
#include <syslog.h>
#include <netdb.h>
#include <utmp.h>

#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/callout.h>

#include <net/if.h>

#ifdef	STREAMS
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/termios.h>
#include <sys/conf.h>
#endif

# define	DEVNAME_SIZE	128	/* Buffer size for /dev/* filenames */

#ifdef sun
/*
 *	Get definition of SUNOS
 */
#include <pixrect/pr_impl_util.h>
#endif

#include "magic.h"
#include "ppp.h"
#include "fsm.h"
#include "lcp.h"
#include "ipcp.h"
#include "upap.h"
#include "patchlevel.h"


#ifdef __NeXT__
struct callout {
	int	c_time;		/* incremental time */
	void	*c_arg;		/* argument to routine */
	func	c_func;		/* routine */
	struct	callout *c_next;
};
#endif

#define PPPHOSTS	"/etc/hosts.ppp"

int dial = 0;
char dial_cmd[256];
int debug = 0;			/* Debug flag */
int fsm_debug = 0;		/* FSM debug flag */
int lcp_debug = 0;		/* LCP debug flag */
int ipcp_debug = 0;		/* IPCP debug flag */
int upap_debug = 0;		/* UPAP debug flag */

char *progname;			/* Name of this program */
char devname[DEVNAME_SIZE] = "/dev/tty";	/* Device name */
char ifname[IFNAMSIZ];		/* Interface name */
int ifunit;			/* Interface unit number */
char inspeed = 0, outspeed = 0;	/* Input/Output speed */
int fd;				/* Device file descriptor */
int s;				/* Socket file descriptor */
int initdisc;			/* Initial TTY discipline */
#ifndef	STREAMS
struct sgttyb initsgttyb;	/* Initial TTY sgttyb */
#endif
int initfdflags;		/* Initial file descriptor flags */
#ifdef __NeXT__
int	pid;			/* Our pid */
#else
pid_t	pid;			/* Our pid */
#endif
#ifdef sun
pid_t	pgrpid;			/* Process Group ID */
#endif
char user[80];			/* User name */


void hup(), intr(), term(), alrm(), io(), incdebug(), nodebug();
int setdebug(), setpassive(), noopt(), novj(), noupap(), requpap();
int setspeed(), noaccomp(), noasyncmap(), noipaddr(), nomagicnumber();
int setasyncmap(), setold(), setdial();
int setmru(), nomru(), nopcomp();
void getuserpasswd();

#ifdef	STREAMS
extern	char	*ttyname();
#define	MAXMODULES	10		/* max number of module names that we can save */
struct	modlist {
	char	modname[FMNAMESZ+1];
} str_modules[MAXMODULES];
int	str_module_count = 0;
#endif
/*
 * Valid arguments.
 */
struct cmd {
    char *cmd_name;
    int (*cmd_func)();
} cmds[] = {
    "-all", noopt,			/* Don't request/allow any options */
    "-ac", noaccomp,			/* Disable Address/Control compress */
    "-am", noasyncmap,			/* Disable asyncmap negotiation */
    "-as", setasyncmap,			/* set the desired async map */
    "-d", setdebug,			/* Increase debugging level */
    "debug", setdebug,			/* Increase debugging level */
    "-ip", noipaddr,			/* Disable IP address negotiation */
    "-mn", nomagicnumber,		/* Disable magic number negotiation */
    "-mru", nomru,			/* Disable mru negotiation */
    "mru", setmru,			/* Set MRU value for negotiation */
    "-p", setpassive,			/* Set passive mode */
    "-pc", nopcomp,			/* Disable protocol field compress */
    "passive", setpassive,		/* Set passive mode */
    "+ua", requpap,			/* Require UPAP authentication */
    "-ua", noupap,			/* Don't allow UPAP authentication */
    "-vj", novj,			/* Disable VJ compression */
    "dial", setdial,			/* Use dial to dialup */
    "old", setold,			/* Backward compatibility mode */
#if 0
    "local", setlocalipaddr,
    "remote", setremoteipaddr,
    "test", enabletestline,
#endif
    NULL
};


/*
 * PPP Data Link Layer "protocol" table.
 * One entry per supported protocol.
 */
struct protent {
    u_short protocol;
    void (*init)();
    void (*input)();
    void (*protrej)();
} prottbl[] = {
    { LCP, lcp_init, lcp_input, lcp_protrej },
    { IPCP, ipcp_init, ipcp_input, ipcp_protrej },
    { UPAP, upap_init, upap_input, upap_protrej },
};


char *usage = "PPP patch level %d\n\
Usage: %s [ arguments ], where arguments are:\n\
	-all		Don't request/allow any options\n\
	-ac		Disable Address/Control compression\n\
	-am		Disable asyncmap negotiation\n\
	-as <n>		Set the desired async map to hex <n>\n\
	-d		Increase debugging level\n\
	debug		Increase debugging level\n\
	-ip		Disable IP address negotiation\n\
	-mn		Disable magic number negotiation\n\
	-mru		Disable mru negotiation\n\
	mru <n>		Set MRU value to <n> for negotiation\n\
	-p		Set passive mode\n\
	-pc		Disable protocol field compression\n\
	passive		Set passive mode\n\
	+ua		Require UPAP authentication\n\
	-ua		Don't allow UPAP authentication\n\
	-vj		Disable VJ compression\n\
	old		Backward compatibility mode for use with old PPP versions\n\
	dial <cmd>	Dial using cmd (stdin/out is the device)\n\
	<device>	Communicate over the named device\n\
	<speed>		Set the baud rate to <speed>\n\
	<loc>:<rem>	Set the local and/or remote interface IP\n\
			addresses.  Either one may be omitted.\n";

#ifdef	STREAMS
str_restore(ecode)
	int	ecode;		/* called by exit */
/* 
			this module will attempt to reconstruct the stream with the previously popped
			modules
*/

{
	while(ioctl(fd, I_POP, 0) == 0);	/* pop any we pushed */

	for(; str_module_count > 0; str_module_count--) {
		if(ioctl(fd, I_PUSH, str_modules[str_module_count-1].modname)) {
			if(debug)
				fprintf(stderr,
					"str_restore: couldn't push module %s\n",
					str_modules[str_module_count-1].modname);
		}
		else {
			if(debug)
				fprintf(stderr,
					"str_restore: pushed module %s\n",
					str_modules[str_module_count-1].modname);
		}
	}


}
#endif

main(argc, argv)
    int argc;
    char *argv[];
{
    int mask, i;
#ifdef	STREAMS
    struct termios	tios;
#else
    int pppdisc = PPPDISC;
    struct sgttyb sgttyb;
#endif
    struct sigvec sv;
    struct cmd *cmdp;

    pid = getpid();
    /*
     * Initialize syslog system and magic number package.
     */
#if BSD >= 43
    openlog("ppp", LOG_PID | LOG_NDELAY, LOG_DAEMON);
    setlogmask(LOG_UPTO(LOG_WARNING));
#else
    openlog("ppp", LOG_PID);
#define LOG_UPTO(x) (x)
#define setlogmask(x) (x)
#endif
#ifdef        STREAMS
    if (ttyname(fileno(stdin)))
	strcpy(devname, ttyname(fileno(stdin)));
#endif

    magic_init();
    /*
     * Initialize to the standard option set and then parse the command
     * line arguments.
     */
    for (i = 0; i < sizeof (prottbl) / sizeof (struct protent); i++)
	(*prottbl[i].init)(0);

    progname = *argv;
    for (argc--, argv++; argc; ) {
	/*
	 * First see if it's a command.
	 */
	for (cmdp = cmds; cmdp->cmd_name; cmdp++)
	    if (!strcmp(*argv, cmdp->cmd_name) &&
		(*cmdp->cmd_func)(&argc, &argv))
		break;

	/*
	 * Maybe a tty name, speed or IP address?
	 */
	if (cmdp->cmd_name == NULL &&
	    !setdevname(&argc, &argv) &&
	    !setspeed(&argc, &argv) &&
	    !setipaddr(&argc, &argv)) {
	    fprintf(stderr, usage, PATCHLEVEL, progname);
	    exit(1);
	}
    }

    /*
     * Initialize state.
     */
#if defined(sun) && defined(SUNOS) && SUNOS >= 41
    if ((pgrpid = setsid()) < 0)
	pgrpid = getpgrp(0);
#endif
#ifdef __NeXT__
    if (strcmp(devname, "/dev/tty"))
    {
	int f;
	if ((f = open ("/dev/tty", O_RDWR)) != -1) {
	    ioctl (f, TIOCNOTTY, (char *)0);
	    close (f);
	}
    }
#endif

    if ((fd = open(devname, O_RDWR /*| O_NDELAY*/)) < 0) {
	perror(devname);
	exit(1);
    }
    
#ifdef __NeXT__
    if (ioctl (fd, TIOCSPGRP, &pid) == -1)
    {
	perror("TIOCSPGRP");
	_exit(1);
    }
    dup2(fd, 0);
    dup2(fd, 1);
    setpgrp (0, pid);

#endif
    if (dial)
    {
	char cmd[1024];
	sprintf(cmd, "%s %d", dial_cmd, getpid());
	if (system(cmd))
	{
	    fprintf(stderr, "dial command '%s' failed\n", cmd);
	    exit(1);
	}
    }

#if defined(sun) && defined(SUNOS) && SUNOS >= 41
    if (getpid() != pgrpid && tcsetpgrp(fd, pgrpid) < 0) {
  	perror("ppp: tcsetpgrp()");
  	exit(1);
    }
#endif
    if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
	perror("ppp: socket");
	exit(1);
    }
#ifdef	STREAMS
	while(1)	{ /* go through and save the name of all the modules, then pop em */
		if(!ioctl(fd, I_LOOK, str_modules[str_module_count].modname))
			if(debug)
				fprintf(stderr,
					"popped stream module : %s\n",
					str_modules[str_module_count].modname);
		if(!ioctl(fd, I_POP, 0))
			str_module_count++;
		else
			break;
	}

	on_exit(str_restore,NULL);		/* if we exit, then try and restore the stream */

	if(ioctl(fd, TCGETS, (caddr_t) &tios) < 0) {
		perror("ppp: ioctl(TCGETS)");
		exit(1);
	}
	tios.c_cflag  = CRTSCTS | (inspeed  ? inspeed : (tios.c_cflag & CBAUD));
	tios.c_cflag |= CS8|CREAD|HUPCL;
	tios.c_iflag = IGNBRK;
	if(ioctl(fd, TCSETS, (caddr_t) &tios) < 0) {
		perror("ppp: ioctl(TCSETS)");
		exit(1);
	}
	/* now push the async/fcs module */
	if(ioctl(fd, I_PUSH, "pppasync") < 0) {
		perror("ppp: ioctl(I_PUSH, ppp_async)");
		exit(1);
	}
	/* finally, push the ppp_if module that actually handles the network interface */
	if(ioctl(fd, I_PUSH, "pppif") < 0) {
		perror("ppp: ioctl(I_PUSH, ppp_if)");
		exit(1);
	}
	if(ioctl(fd, I_SETSIG, S_INPUT) < 0) {
		perror("ppp: ioctl(I_SETSIG, S_INPUT)");
		exit(1);
	}
	/* read mode, message non-discard mode */
	if(ioctl(fd, I_SRDOPT, RMSGN) < 0) {
		perror("ppp: ioctl(I_SRDOPT, RMSGN)");
		exit(1);
	}
	/* Flush any waiting messages, or we'll never get SIGPOLL */
	if(ioctl(fd, I_FLUSH, FLUSHRW) < 0) {
		perror("ppp: ioctl(I_FLUSH, FLUSHRW)");
		exit(1);
	}
#else
    /*
     * Put the tty in raw mode and set the discipline to PPP.
     */
    if (ioctl(fd, TIOCGETP, &initsgttyb) < 0) {
	perror("ppp: ioctl(TIOCGETP)");
	exit(1);
    }
    sgttyb = initsgttyb;
    sgttyb.sg_flags = RAW | ANYP;
    if (inspeed)
	sgttyb.sg_ispeed = inspeed;
    if (outspeed)
	sgttyb.sg_ospeed = outspeed;
    if (ioctl(fd, TIOCSETP, &sgttyb) < 0) {
	perror("ppp: ioctl(TIOCSETP)");
	exit(1);
    }

    if (ioctl(fd, TIOCGETD, &initdisc) < 0) {
	perror("ppp: ioctl(TIOCGETD)");
	exit(1);
    }
    if (ioctl(fd, TIOCSETD, &pppdisc) < 0) {
	perror("ppp: ioctl(TIOCSETD)");
	exit(1);
    }
#endif
    /*
     * Find out which interface we were given.
     */
#ifdef	STREAMS
    if (ioctl(fd, SIOCGETU, &ifunit) < 0) {	/* in streams, ppp_if handles this ioctl */
	perror("ppp: ioctl(SIOCGETU)");
	exit(1);
    }
#else
    if (ioctl(fd, TIOCGETD, &ifunit) < 0) {	
	perror("ppp: ioctl(TIOCGETD)");
	exit(1);
    }
#endif
    sprintf(ifname, "ppp%d", ifunit);
    if (debug)
	fprintf(stderr, "Interface %s.\n", ifname);

    if (debug)
	fprintf(stderr, "Pid %d.\n", pid);

# if !defined(sun) || !defined(SUNOS) || SUNOS < 41
    if (ioctl(fd, TIOCSPGRP, &pid) < 0) {
	perror("ppp: ioctl(TIOCSPGRP)");
	exit(1);
    }
# endif

    /*
     * Compute mask of all interesting signals and install signal handlers
     * for each.  Only one signal handler may be active at a time.  Therefore,
     * all other signals should be masked when any handler is executing.
     */
    mask = sigmask(SIGHUP) | sigmask(SIGINT) | sigmask(SIGALRM) |
	sigmask(SIGIO);
#ifdef	STREAMS
    mask |= sigmask(SIGPOLL);
#endif
    sv.sv_handler = hup;		/* Hangup */
    sv.sv_mask = mask;
    sv.sv_flags = 0;
    if (sigvec(SIGHUP, &sv, NULL)) {
	perror("ppp: sigvec(SIGHUP)");
	exit(1);
    }
    sv.sv_handler = intr;		/* Interrupt */
    sv.sv_mask = mask;
    sv.sv_flags = 0;
    if (sigvec(SIGINT, &sv, NULL)) {
	perror("ppp: sigvec(SIGINT)");
	exit(1);
    }
    sv.sv_handler = term;		/* Terminate */
    sv.sv_mask = mask;
    sv.sv_flags = 0;
    if (sigvec(SIGTERM, &sv, NULL)) {
	perror("ppp: sigvec(SIGTERM)");
	exit(1);
    }
    sv.sv_handler = alrm;		/* Timeout */
    sv.sv_mask = mask;
    sv.sv_flags = 0;
    if (sigvec(SIGALRM, &sv, NULL)) {
	perror("ppp: sigvec(SIGALRM)");
	exit(1);
    }
    sv.sv_handler = io;			/* Input available */
    sv.sv_mask = mask;
    sv.sv_flags = 0;
    if (sigvec(SIGIO, &sv, NULL)) {
	perror("ppp: sigvec(SIGIO)");
	exit(1);
    }
#ifdef	STREAMS
    sv.sv_handler = io;			/* Input available */
    sv.sv_mask = mask;
    sv.sv_flags = 0;
    if (sigvec(SIGPOLL, &sv, NULL)) {
	perror("ppp: sigvec(SIGPOLL)");
	exit(1);
    }
#endif
    (void) signal(SIGUSR1, incdebug);	/* Increment debug flag */
    (void) signal(SIGUSR2, nodebug);	/* Reset debug flag */

    /*
     * Record initial device flags, then set device to cause SIGIO
     * signals to be generated.
     */
    if ((initfdflags = fcntl(fd, F_GETFL)) == -1) {
	perror("ppp: fcnt(F_GETFL)");
	exit(1);
    }
    if (fcntl(fd, F_SETFL, FNDELAY | FASYNC) == -1) {
	perror("ppp: fcnt(F_SETFL, FNDELAY | FASYNC)");
	exit(1);
    }

    /*
     * Block all signals, start opening the connection, and  wait for
     * incoming signals (reply, timeout, etc.).
     */
    syslog (LOG_DEBUG, "connect: %s %s", ifname, devname);
    sigblock(mask);			/* Block signals now */
    lcp_lowerup(0);			/* XXX Well, sort of... */
    if (lcp_wantoptions[0].passive)
	lcp_passiveopen(0);		/* Start protocol in passive mode */
    else
	lcp_activeopen(0);		/* Start protocol in active mode */
    for (;;) {
	sigpause(0);			/* Wait for next signal */

	/* Need to read user/passwd? */
	if (upap[0].us_flags & UPAPF_UPPENDING) {
	    sigsetmask(0);		/* Allow other signals to occur */
	    getuserpasswd();		/* Get user and passwd */
	    upap[0].us_flags &= ~UPAPF_UPPENDING;
	    upap[0].us_flags |= UPAPF_UPVALID;
	    sigsetmask(mask);		/* Diallow signals */
	    upap_authwithpeer(0);
	}
    }
}


/*
 * quit - Clean up state and exit.
 */
void quit()
{
    if (fcntl(fd, F_SETFL, initfdflags) == -1) {
	perror("ppp: fcnt(F_SETFL, fdflags)");
	exit(1);
    }
#ifndef	STREAMS
    if (ioctl(fd, TIOCSETP, &initsgttyb) < 0) {
	perror("ppp: ioctl(TIOCSETP)");
	exit(1);
    }
    if (ioctl(fd, TIOCSETD, &initdisc) < 0) {
	perror("ppp: ioctl(TIOCSETD)");
	exit(1);
    }
#else
    if(ioctl(fd, I_FLUSH, FLUSHRW) < 0) {
	perror("ppp: quit ioctl(I_FLUSH,FLUSHRW)");
    }
    str_restore(-1);
#endif
    close(fd);
    if (dial)
    {
	char lockfile[256];
	strcpy(lockfile, LCK_DIR);
	strcat(lockfile, "/LCK..");
	if (rindex(devname, '/'))
		strcat(lockfile, rindex(devname , '/') + 1);
	else
		strcat(lockfile, devname);
	fprintf(stderr,"Unlinking %s\n", lockfile);
	unlink(lockfile);
    }

    exit(0);
}


struct callout *callout = NULL;		/* Callout list */
struct timeval schedtime;		/* Time last timeout was set */


/*
 * timeout - Schedule a timeout.
 *
 * Note that this timeout takes the number of seconds, NOT hz (as in
 * the kernel).
 */
void timeout(func1, arg, time)
    int (*func1)();
    caddr_t arg;
    int time;
{
    struct itimerval itv;
    struct callout *newp, **oldpp;
    
    if (debug > 2)
	fprintf(stderr, "Timeout %x:%x in %d seconds.\n",
		(int) func1, (int) arg, time);

    /*
     * Allocate timeout.
     */
    if ((newp = (struct callout *) malloc(sizeof(struct callout))) == NULL) {
	fprintf(stderr, "ppp: Out of memory!\n");
	exit(1);
    }
    newp->c_arg = arg;
    newp->c_func = func1;

    /*
     * Find correct place to link it in and decrement its time by the
     * amount of time used by preceding timeouts.
     */
    for (oldpp = &callout;
	 *oldpp && (*oldpp)->c_time <= time;
	 oldpp = &(*oldpp)->c_next)
	time -= (*oldpp)->c_time;
    newp->c_time = time;
    newp->c_next = *oldpp;
    if (*oldpp)
	(*oldpp)->c_time -= time;
    *oldpp = newp;

    /*
     * If this is now the first callout then we have to set a new
     * itimer.
     */
    if (callout == newp) {
	itv.it_interval.tv_sec = itv.it_interval.tv_usec =
	    itv.it_value.tv_usec = 0;
	itv.it_value.tv_sec = callout->c_time;
	if (debug > 2)
	    fprintf(stderr, "Setting itimer for %d seconds.\n",
		    itv.it_value.tv_sec);
	if (setitimer(ITIMER_REAL, &itv, NULL)) {
	    perror("ppp: setitimer(ITIMER_REAL)");
	    exit(1);
	}
	if (gettimeofday(&schedtime, NULL)) {
	    perror("ppp: gettimeofday");
	    exit(1);
	}
    }
}


/*
 * untimeout - Unschedule a timeout.
 */
void untimeout(func1, arg)
    int (*func1)();
    caddr_t arg;
{

    struct itimerval itv;
    struct callout **copp, *freep;
    int reschedule = 0;

    if (debug > 2)
	fprintf(stderr, "Untimeout %x:%x.\n", (int) func1, (int) arg);

    /*
     * If the first callout is unscheduled then we have to set a new
     * itimer.
     */
    if (callout &&
	callout->c_func == func1 &&
	callout->c_arg == arg)
	reschedule = 1;

    /*
     * Find first matching timeout.  Add its time to the next timeouts
     * time.
     */
    for (copp = &callout; *copp; copp = &(*copp)->c_next)
	if ((*copp)->c_func == func1 &&
	    (*copp)->c_arg == arg) {
	    freep = *copp;
	    *copp = freep->c_next;
	    if (*copp)
		(*copp)->c_time += freep->c_time;
	    free(freep);
	    break;
	}

    if (reschedule) {
	itv.it_interval.tv_sec = itv.it_interval.tv_usec =
	    itv.it_value.tv_usec = 0;
	itv.it_value.tv_sec = callout ? callout->c_time : 0;
	if (debug > 2)
	    fprintf(stderr, "Setting itimer for %d seconds.\n",
		    itv.it_value.tv_sec);
	if (setitimer(ITIMER_REAL, &itv, NULL)) {
	    perror("ppp: setitimer(ITIMER_REAL)");
	    exit(1);
	}
	if (gettimeofday(&schedtime, NULL)) {
	    perror("ppp: gettimeofday");
	    exit(1);
	}
    }
}


/*
 * adjtimeout - Decrement the first timeout by the amount of time since
 * it was scheduled.
 */
void adjtimeout()
{
    struct timeval tv;
    int timediff;

    if (callout == NULL)
	return;
    /*
     * Make sure that the clock hasn't been warped dramatically.
     * Account for recently expired, but blocked timer by adding
     * small fudge factor.
     */
    if (gettimeofday(&tv, NULL)) {
	perror("ppp: gettimeofday");
	exit(1);
    }
    timediff = tv.tv_sec - schedtime.tv_sec;
    if (timediff < 0 ||
	timediff > callout->c_time + 1)
	return;

    callout->c_time -= timediff;	/* OK, Adjust time */
}


/*
 * output - Output PPP packet.
 */
void output(unit, p, len)
    int unit;
    u_char *p;
    int len;
{
#ifdef	STREAMS
	struct strbuf	str;
#endif
    if (unit != 0) {
	fprintf(stderr, "ppp: output: unit != 0!\n");
	abort();
    }
#ifdef	STREAMS
    str.len = len;
    str.buf = (caddr_t) p;
    if(putmsg(fd, NULL, &str, 0) < 0) {
	perror("ppp: putmsg");
	exit(1);
     }
#else
    if (write(fd, p, len) < 0) {
	perror("ppp: write");
	exit(1);
    }
#endif
    free(p);
}


/*
 * hup - Catch SIGHUP signal.
 *
 * Indicates that the physical layer has been disconnected.
 */
void hup()
{
    if (debug > 1)
	fprintf(stderr, "Hangup\n");
    adjtimeout();		/* Adjust timeouts */
    lcp_lowerdown(0);		/* Reset connection */
}


/*
 * term - Catch SIGTERM signal.
 *
 * Indicates that we should initiate a graceful disconnect and exit.
 */
void term()
{
    if (debug > 1)
	fprintf(stderr, "Terminate\n");
    adjtimeout();		/* Adjust timeouts */
    lcp_close(0);		/* Close connection */
}


/*
 * intr - Catch SIGINT signal (DEL/^C).
 *
 * Indicates that we should initiate a graceful disconnect and exit.
 */
void intr()
{
    if (debug > 1)
	fprintf(stderr, "Interrupt\n");
    adjtimeout();		/* Adjust timeouts */
    lcp_close(0);		/* Close connection */
}


/*
 * alrm - Catch SIGALRM signal.
 *
 * Indicates a timeout.
 */
void alrm()
{
    struct itimerval itv;
    struct callout *freep;

    if (debug > 1)
	fprintf(stderr, "Alarm\n");

    /*
     * Call and free first scheduled timeout and any that were scheduled
     * for the same time.
     */
    while (callout) {
	freep = callout;	/* Remove entry before calling */
	callout = freep->c_next;
	(*freep->c_func)(freep->c_arg);
	free(freep);
	if (callout->c_time)
	    break;
    }

    /*
     * Set a new itimer if there are more timeouts scheduled.
     */
    if (callout) {
	itv.it_interval.tv_sec = itv.it_interval.tv_usec =
	    itv.it_value.tv_usec = 0;
	itv.it_value.tv_sec = callout->c_time;
	if (debug > 2)
	    fprintf(stderr, "Setting itimer for %d seconds.\n",
		    itv.it_value.tv_sec);
	if (setitimer(ITIMER_REAL, &itv, NULL)) {
	    perror("ppp: setitimer(ITIMER_REAL)");
	    exit(1);
	}
	if (gettimeofday(&schedtime, NULL)) {
	    perror("ppp: gettimeofday");
	    exit(1);
	}
    }
}


/*
 * io - Catch SIGIO signal.
 *
 * Indicates that incoming data is available.
 */
void io()
{
    int len, i;
    u_char *p;
    u_short protocol;
#ifdef	STREAMS
    struct strbuf str;
#endif
    if (debug > 1)
	fprintf(stderr, "IO\n");
    adjtimeout();		/* Adjust timeouts */

    for (;;) {			/* Read all available packets */
	p = (u_char *) malloc(MTU + DLLHEADERLEN);
#ifdef	STREAMS
	str.maxlen = MTU+DLLHEADERLEN;
	str.buf = (caddr_t) p;
	i = 0;
	len = getmsg(fd, NULL, &str, &i);
	if(len < 0) {
		if(errno == EAGAIN || errno == EWOULDBLOCK) {
			if(debug > 2)
				perror("ppp: getmsg(fd)");
			return;
		}
		perror("ppp: getmsg(fd)");
		exit(1);
	}
	else if(len && debug > 2) 
			fprintf(stderr, "ppp: getmsg returns 0x%x\n",len);

	if(str.len < 0) {
		if(debug > 2)
			fprintf(stderr, "ppp: getmsg short return length %d\n",
				str.len);
		return;
	}
		
	len = str.len;
#else
	if ((len = read(fd, p, MTU + DLLHEADERLEN)) < 0) {
	    if (errno == EWOULDBLOCK) {
		if (debug > 2)
		    perror("ppp: read(fd)");
		return;
	    }
	    else {
		perror("ppp: read(fd)");
		exit(1);
	    }
	}
	else 
#endif
	if (len == 0) {
	    fprintf(stderr, "ppp: End of file on fd!\n");
	    exit(1);
	}
	
	if (len < DLLHEADERLEN) {
	    if (debug)
		fprintf(stderr, "ppp: input: Received short packet.\n");
	    return;
	}

	p += 2;				/* Skip address and control */
	GETSHORT(protocol, p);
	len -= DLLHEADERLEN;

	/*
	 * Toss all non-LCP packets unless LCP is OPEN.
	 */
	if (protocol != LCP &&
	    lcp_fsm[0].state != OPEN) {
	    free(p - DLLHEADERLEN);
	    return;
	}

	/*
	 * Upcall the proper protocol input routine.
	 */
	for (i = 0; i < sizeof (prottbl) / sizeof (struct protent); i++)
	    if (prottbl[i].protocol == protocol) {
		(*prottbl[i].input)(0, p, len);
		break;
	    }

	if (i == sizeof (prottbl) / sizeof (struct protent)) {
	    if (debug)
		fprintf(stderr,
			"ppp: input: Unknown protocol (%x) received!\n",
			protocol);
	    p -= DLLHEADERLEN;
	    len += DLLHEADERLEN;
	    lcp_sprotrej(0, p, len);
	}
    }
}


/*
 * demuxprotrej - Demultiplex a Protocol-Reject.
 */
void demuxprotrej(unit, protocol)
    int unit;
    u_short protocol;
{
    int i;

    /*
     * Upcall the proper Protocol-Reject routine.
     */
    for (i = 0; i < sizeof (prottbl) / sizeof (struct protent); i++)
	if (prottbl[i].protocol == protocol) {
	    (*prottbl[i].protrej)(unit);
	    return;
	}
    if (debug)
	fprintf(stderr,
		"ppp: demuxprotrej: Unrecognized Protocol-Reject!\n");
}


/*
 * incdebug - Catch SIGUSR1 signal.
 *
 * Increment debug flag.
 */
void incdebug()
{
    debug++;
}


/*
 * nodebug - Catch SIGUSR2 signal.
 *
 * Turn off debugging.
 */
void nodebug()
{
    debug = 0;
}


/*
 * setdebug - Set debug (command line argument).
 */
int setdebug(argcp, argvp)
    int *argcp;
    char ***argvp;
{
    debug++;
    fsm_debug++;
    lcp_debug++;
    ipcp_debug++;
    upap_debug++;
    --*argcp, ++*argvp;
    return (1);
}

/*
 * setdial - Set dial (command line argument).
 */
int setdial(argcp, argvp)
    int *argcp;
    char ***argvp;
{
    dial++;
    --*argcp, ++*argvp;
    strncpy(dial_cmd, **argvp, 256);
    --*argcp, ++*argvp;
    return (1);
}


/*
 * noopt - Disable all options.
 */
int noopt(argcp, argvp)
    int *argcp;
    char ***argvp;
{
    bzero(&lcp_wantoptions[0], sizeof (struct lcp_options));
    bzero(&lcp_allowoptions[0], sizeof (struct lcp_options));
    bzero(&ipcp_wantoptions[0], sizeof (struct ipcp_options));
    bzero(&ipcp_allowoptions[0], sizeof (struct ipcp_options));
    --*argcp, ++*argvp;
    return (1);
}


/*
 * noaccomp - Disable Address/Control field compression negotiation.
 */
int noaccomp(argcp, argvp)
    int *argcp;
    char ***argvp;
{
    lcp_wantoptions[0].neg_accompression = 0;
    lcp_allowoptions[0].neg_accompression = 0;
    --*argcp, ++*argvp;
    return (1);
}


/*
 * noasyncmap - Disable async map negotiation.
 */
int noasyncmap(argcp, argvp)
    int *argcp;
    char ***argvp;
{
    lcp_wantoptions[0].neg_asyncmap = 0;
    lcp_allowoptions[0].neg_asyncmap = 0;
    --*argcp, ++*argvp;
    return (1);
}


/*
 * noipaddr - Disable IP address negotiation.
 */
int noipaddr(argcp, argvp)
    int *argcp;
    char ***argvp;
{
    ipcp_wantoptions[0].neg_addrs = 0;
    ipcp_allowoptions[0].neg_addrs = 0;
    --*argcp, ++*argvp;
    return (1);
}


/*
 * nomagicnumber - Disable magic number negotiation.
 */
int nomagicnumber(argcp, argvp)
    int *argcp;
    char ***argvp;
{
    lcp_wantoptions[0].neg_magicnumber = 0;
    lcp_allowoptions[0].neg_magicnumber = 0;
    --*argcp, ++*argvp;
    return (1);
}


/*
 * nomru - Disable mru negotiation.
 */
int nomru(argcp, argvp)
    int *argcp;
    char ***argvp;
{
    lcp_wantoptions[0].neg_mru = 0;
    lcp_allowoptions[0].neg_mru = 0;
    --*argcp, ++*argvp;
    return (1);
}


/*
 * setmru - Set MRU for negotiation.
 */
int setmru(argcp, argvp)
    int *argcp;
    char ***argvp;
{
    --*argcp, ++*argvp;
    lcp_wantoptions[0].mru = atoi(**argvp);
    --*argcp, ++*argvp;
    return (1);
}


/*
 * nopcomp - Disable Protocol field compression negotiation.
 */
int nopcomp(argcp, argvp)
    int *argcp;
    char ***argvp;
{
    lcp_wantoptions[0].neg_pcompression = 0;
    lcp_allowoptions[0].neg_pcompression = 0;
    --*argcp, ++*argvp;
    return (1);
}


/*
 * setpassive - Set passive mode.
 */
int setpassive(argcp, argvp)
    int *argcp;
    char ***argvp;
{
    lcp_wantoptions[0].passive = 1;
    --*argcp, ++*argvp;
    return (1);
}


/*
 * noupap - Disable UPAP authentication.
 */
int noupap(argcp, argvp)
    int *argcp;
    char ***argvp;
{
    lcp_allowoptions[0].neg_upap = 0;
    --*argcp, ++*argvp;
    return (1);
}


/*
 * requpap - Require UPAP authentication.
 */
int requpap(argcp, argvp)
    int *argcp;
    char ***argvp;
{
    lcp_wantoptions[0].neg_upap = 1;
    lcp_allowoptions[0].neg_upap = 0;
    --*argcp, ++*argvp;
    return (1);
}


/*
 * novj - Disable Van Jacobson style IP header compression.
 */
int novj(argcp, argvp)
    int *argcp;
    char ***argvp;
{
    ipcp_wantoptions[0].neg_vj = 0;
    ipcp_allowoptions[0].neg_vj = 0;
    --*argcp, ++*argvp;
    return (1);
}


/*
 *	setold - Set backward compatibility mode
 */
int setold(argcp, argvp)
int *argcp;
char ***argvp;
    {
    extern short ip_vj_comp;

    ip_vj_comp = 0x0037;
    --*argcp, ++*argvp;
    return (1);
    }

/*
 * 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 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 EXTA
    { 19200, EXTA },
#endif
#ifdef EXTB
    { 38400, EXTB },
#endif
    { 0, 0 }
};

int
setasyncmap(argcp, argvp) 
	int	*argcp;
	char	***argvp;
{
	unsigned long asyncmap;

	asyncmap = 0xffffffff;
	++*argvp;
	sscanf(**argvp,"%lx",&asyncmap);
	++*argvp;
	lcp_wantoptions[0].asyncmap = asyncmap;
	*argcp -= 2;
	return(1);
}

/*
 * setspeed - Set the speed.
 */
int setspeed(argcp, argvp)
    int *argcp;
    char ***argvp;
{
    int speed;
    struct speed *speedp;

    speed = atoi(**argvp);
    for (speedp = speeds; speedp->speed_int; speedp++)
	if (speed == speedp->speed_int) {
	    inspeed = speedp->speed_val;
	    --*argcp, ++*argvp;
	    return (1);
	}
    return (0);
}


/*
 * setdevname - Set the device name.
 */
int setdevname(argcp, argvp)
    int *argcp;
    char ***argvp;
{
    char dev[DEVNAME_SIZE];
    char *cp = **argvp;
    struct stat statbuf;
    char *tty, *ttyname();

    if (strncmp("/dev/", cp, sizeof ("/dev/") - 1)) {
	(void) sprintf(dev, "/dev/%s", cp);
	cp = dev;
    }

    /*
     * Check if there is a device by this name.
     */
    if (stat(cp, &statbuf) < 0) {
	if (errno == ENOENT)
	    return (0);
	perror(cp);
	exit(1);
    }

    strcpy(devname, cp);
    --*argcp, ++*argvp;

    /*
     * If we haven't already decided to require authentication,
     * or we are running ppp on the control terminal, then we can
     * allow authentication to be requested.
     */
    if ((tty = ttyname(fileno(stdin))) == NULL)
      tty = ""; /* running from init means no stdin.  Null kills strcmp -KWK */
    if (lcp_wantoptions[0].neg_upap == 0 &&
	strcmp(devname, "/dev/tty") &&
	strcmp(devname, tty)) {
	lcp_wantoptions[0].neg_upap = 0;
	lcp_allowoptions[0].neg_upap = 1;
    }
    return (1);
}


/*
 * setipaddr - Set the IP address
 */
int setipaddr(argcp, argvp)
    int *argcp;
    char ***argvp;
{
    u_long local, remote;
    struct hostent *hp;
    char *colon, *index();

    /*
     * IP address pair separated by ":".
     */
    if ((colon = index(**argvp, ':')) == NULL)
	return (0);
    
    /*
     * If colon first character, then no local addr.
     */
    if (colon == **argvp) {
	local = 0l;
	++colon;
    }
    else {
	*colon++ = '\0';
	if ((local = inet_addr(**argvp)) == -1) {
	    if ((hp = gethostbyname(**argvp)) == NULL) {
		fprintf(stderr, "unknown host: %s\n", **argvp);
		goto ret;
	    }
	    bcopy(hp->h_addr, (char *) &local, hp->h_length);
	}
    }

    /*
     * If colon last character, then no remote addr.
     */
    if (*colon == '\0')
	remote = 0l;
    else {
	if ((remote = inet_addr(colon)) == -1) {
	    if ((hp = gethostbyname(colon)) == NULL) {
		fprintf(stderr, "unknown host: %s\n", colon);
		goto ret;
	    }
	    bcopy(hp->h_addr, (char *) &remote, hp->h_length);
	}
    }

    ipcp_wantoptions[0].neg_addrs = 1;
    ipcp_wantoptions[0].ouraddr = local;
    ipcp_wantoptions[0].hisaddr = remote;

ret:
    --*argcp, ++*argvp;
    return (1);
}


/*
 * getuserpasswd - Get the user name and passwd.
 */
void getuserpasswd()
{
    struct passwd *pw, *getpwuid();
    char *getlogin(), *getpass();

    /*
     * Prompt for user name with a default value.  Default is
     * the last value if any, or the login name or the user name.
     */
    if (upap[0].us_user == NULL &&
	(upap[0].us_user = getlogin()) == NULL &&
	(pw = getpwuid(getuid())) != NULL)
	upap[0].us_user = pw->pw_name;

    printf("Login (%s): ", upap[0].us_user);
    fgets(user, sizeof (user) - 1, stdin);
    user[strlen(user) - 1] = '\0';
    if (user[0] != '\0')		/* Choosing the default? */
	upap[0].us_user = user;
    upap[0].us_userlen = strlen(upap[0].us_user);

    upap[0].us_passwd = getpass("Password: ");
    upap[0].us_passwdlen = strlen(upap[0].us_passwd);
}


/*
 * login - Check the user name and passwd and login the user.
 *
 * returns:
 *	UPAP_AUTHNAK: Login failed.
 *	UPAP_AUTHACK: Login succeeded.
 * In either case, msg points to an appropriate message.
 */
u_char login(user, userlen, passwd, passwdlen, msg, msglen)
    char *user;
    int userlen;
    char *passwd;
    int passwdlen;
    char **msg;
    int *msglen;
{
    struct passwd *pw;
    char *epasswd, *crypt();
    static int attempts = 0;
    char *tty, *rindex();

    *(user + userlen) = '\0';
    *(passwd + passwdlen) = '\0';

    if ((pw = getpwnam(user)) == NULL) {
	*msg = "Login incorrect";
	*msglen = strlen(*msg);
        if(debug > 2)
		fprintf(stderr,"ppp: upap login userid '%s' incorrect\n",user);
	return (UPAP_AUTHNAK);
    }

    /*
     * XXX If no passwd, let them login without one.
     */
    if (pw->pw_passwd == '\0') {
	*msg = "Login ok";
	*msglen = strlen(*msg);
	return (UPAP_AUTHACK);
    }

    epasswd = crypt(passwd, pw->pw_passwd);
    if (strcmp(epasswd, pw->pw_passwd)) {
	*msg = "Login incorrect";
	*msglen = strlen(*msg);
	if(debug > 2)
		fprintf(stderr, "ppp: upap login password '%s' incorrect\n",
			passwd);
	/*
	 * Frustrate passwd stealer programs.
	 * Allow 10 tries, but start backing off after 3 (stolen from login).
	 * On 10'th, drop the connection.
	 */
	if (attempts++ >= 10) {
	    syslog(LOG_NOTICE, "%d LOGIN FAILURES ON %s, %s",
		   attempts, devname, user);
	    lcp_close(0);		/* Drop DTR? */
	}
	if (attempts > 3)
	    sleep((u_int) (attempts - 3) * 5);
	return (UPAP_AUTHNAK);
    }

    attempts = 0;			/* Reset count */
    *msg = "Login ok";
    *msglen = strlen(*msg);
    if(debug > 2)
	fprintf(stderr,"ppp: upap login and password OK\n");
    tty = rindex(devname, '/');
    if (tty == NULL)
	tty = devname;
    else
	tty++;
    logwtmp(tty, user, "");		/* Add wtmp login entry */

    return (UPAP_AUTHACK);
}


/*
 * logout - Logout the user.
 */
void logout()
{
    char *tty;

    tty = rindex(devname, '/');
    if (tty == NULL)
	tty = devname;
    else
	tty++;
    logwtmp(tty, "", "");		/* Add wtmp logout entry */
}


/*
 * getuseropt - Get the options from /etc/hosts.ppp for this user.
 */
int getuseropt(user)
    char *user;
{
    char buf[1024], *s;
    FILE *fp;
    int rc = 0;

    if ((fp = fopen(PPPHOSTS, "r")) == NULL)
	return (0);;

    /*
     * Loop till we find an entry for this user.
     */
    for (;;) {
	if (fgets(buf, sizeof (buf), fp) == NULL) {
	    if (feof(fp))
		break;
	    else {
		perror("fgets");
		exit(1);
	    }
	}
	if ((s = index(buf, ' ')) == NULL)
	    continue;
	*s++ = '\0';
	if (!strcmp(user, buf)) {
	    rc = 1;
	    break;
	}
    }
    fclose(fp);
    return (rc);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.