main.c revision 26551
1/*
2 *			User Process PPP
3 *
4 *	    Written by Toshiharu OHNO (tony-o@iij.ad.jp)
5 *
6 *   Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
7 *
8 * Redistribution and use in source and binary forms are permitted
9 * provided that the above copyright notice and this paragraph are
10 * duplicated in all such forms and that any documentation,
11 * advertising materials, and other materials related to such
12 * distribution and use acknowledge that the software was developed
13 * by the Internet Initiative Japan, Inc.  The name of the
14 * IIJ may not be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 *
20 * $Id: main.c,v 1.60 1997/06/09 03:27:28 brian Exp $
21 *
22 *	TODO:
23 *		o Add commands for traffic summary, version display, etc.
24 *		o Add signal handler for misc controls.
25 */
26#include "fsm.h"
27#include <fcntl.h>
28#include <paths.h>
29#include <sys/time.h>
30#include <termios.h>
31#include <signal.h>
32#include <sys/wait.h>
33#include <errno.h>
34#include <netdb.h>
35#include <unistd.h>
36#include <sys/socket.h>
37#include <arpa/inet.h>
38#include <netinet/in_systm.h>
39#include <netinet/ip.h>
40#include "modem.h"
41#include "os.h"
42#include "hdlc.h"
43#include "ccp.h"
44#include "lcp.h"
45#include "ipcp.h"
46#include "loadalias.h"
47#include "vars.h"
48#include "auth.h"
49#include "filter.h"
50#include "systems.h"
51#include "ip.h"
52#include "sig.h"
53
54#define LAUTH_M1 "Warning: No password entry for this host in ppp.secret\n"
55#define LAUTH_M2 "Warning: Manipulation is allowed by anyone\n"
56
57#ifndef O_NONBLOCK
58#ifdef O_NDELAY
59#define	O_NONBLOCK O_NDELAY
60#endif
61#endif
62
63extern void VjInit(), AsyncInit();
64extern void AsyncInput();
65extern int  SelectSystem();
66
67extern void DecodeCommand(), Prompt();
68extern int aft_cmd;
69extern int IsInteractive();
70static void DoLoop(void);
71static void TerminalStop();
72static char *ex_desc();
73
74static struct termios oldtio;		/* Original tty mode */
75static struct termios comtio;		/* Command level tty mode */
76int TermMode;
77static int server;
78static pid_t BGPid = 0;
79struct sockaddr_in ifsin;
80static char pid_filename[MAXPATHLEN];
81static char if_filename[MAXPATHLEN];
82int tunno;
83
84static void
85TtyInit()
86{
87  struct termios newtio;
88  int stat;
89
90  stat = fcntl(0, F_GETFL, 0);
91  if (stat > 0) {
92	 stat |= O_NONBLOCK;
93	 (void)fcntl(0, F_SETFL, stat);
94  }
95  newtio = oldtio;
96  newtio.c_lflag &= ~(ECHO|ISIG|ICANON);
97  newtio.c_iflag = 0;
98  newtio.c_oflag &= ~OPOST;
99  newtio.c_cc[VEOF] = _POSIX_VDISABLE;
100  newtio.c_cc[VINTR] = _POSIX_VDISABLE;
101  newtio.c_cc[VMIN] = 1;
102  newtio.c_cc[VTIME] = 0;
103  newtio.c_cflag |= CS8;
104  tcsetattr(0, TCSADRAIN, &newtio);
105  comtio = newtio;
106}
107
108/*
109 *  Set tty into command mode. We allow canonical input and echo processing.
110 */
111void
112TtyCommandMode(prompt)
113int prompt;
114{
115  struct termios newtio;
116  int stat;
117
118  if (!(mode & MODE_INTER))
119    return;
120  tcgetattr(0, &newtio);
121  newtio.c_lflag |= (ECHO|ISIG|ICANON);
122  newtio.c_iflag = oldtio.c_iflag;
123  newtio.c_oflag |= OPOST;
124  tcsetattr(0, TCSADRAIN, &newtio);
125  stat = fcntl(0, F_GETFL, 0);
126  if (stat > 0) {
127	 stat |= O_NONBLOCK;
128	 (void)fcntl(0, F_SETFL, stat);
129  }
130  TermMode = 0;
131  if(prompt) Prompt();
132}
133
134/*
135 * Set tty into terminal mode which is used while we invoke term command.
136 */
137void
138TtyTermMode()
139{
140  int stat;
141
142  tcsetattr(0, TCSADRAIN, &comtio);
143  stat = fcntl(0, F_GETFL, 0);
144  if (stat > 0) {
145	 stat &= ~O_NONBLOCK;
146	 (void)fcntl(0, F_SETFL, stat);
147  }
148  TermMode = 1;
149}
150
151void
152TtyOldMode()
153{
154  int stat;
155
156  stat = fcntl(0, F_GETFL, 0);
157  if (stat > 0) {
158	  stat &= ~O_NONBLOCK;
159	  (void)fcntl(0, F_SETFL, stat);
160  }
161  tcsetattr(0, TCSANOW, &oldtio);
162}
163
164void
165Cleanup(excode)
166int excode;
167{
168
169  OsLinkdown();
170  OsCloseLink(1);
171  sleep(1);
172  if (mode & MODE_AUTO)
173    DeleteIfRoutes(1);
174  (void)unlink(pid_filename);
175  (void)unlink(if_filename);
176  OsInterfaceDown(1);
177  if (mode & MODE_BACKGROUND && BGFiledes[1] != -1) {
178    char c = EX_ERRDEAD;
179    if (write(BGFiledes[1],&c,1) == 1)
180      LogPrintf(LogPHASE,"Parent notified of failure.\n");
181    else
182      LogPrintf(LogPHASE,"Failed to notify parent of failure.\n");
183    close(BGFiledes[1]);
184  }
185  LogPrintf(LogPHASE, "PPP Terminated (%s).\n",ex_desc(excode));
186  LogClose();
187  if (server >= 0) {
188    close(server);
189    server = -1;
190  }
191
192  TtyOldMode();
193
194  exit(excode);
195}
196
197static void
198Hangup(signo)
199int signo;
200{
201#ifdef TRAPSEGV
202  if (signo == SIGSEGV) {
203	LogPrintf(LogPHASE, "Signal %d, core dump.\n", signo);
204	LogClose();
205	abort();
206  }
207#endif
208  if (BGPid) {
209      kill (BGPid, SIGTERM);
210      exit (EX_HANGUP);
211  }
212  else {
213      LogPrintf(LogPHASE, "Signal %d, hangup.\n", signo);
214      Cleanup(EX_HANGUP);
215  }
216}
217
218static void
219CloseSession(signo)
220int signo;
221{
222   if (BGPid) {
223     kill (BGPid, SIGINT);
224     exit (EX_TERM);
225   }
226   LogPrintf(LogPHASE, "Signal %d, terminate.\n", signo);
227   reconnect(RECON_FALSE);
228   LcpClose();
229   Cleanup(EX_TERM);
230}
231
232static void
233TerminalCont()
234{
235  pending_signal(SIGCONT, SIG_DFL);
236  pending_signal(SIGTSTP, TerminalStop);
237  TtyCommandMode(getpgrp() == tcgetpgrp(0));
238}
239
240static void
241TerminalStop(signo)
242int signo;
243{
244  pending_signal(SIGCONT, TerminalCont);
245  TtyOldMode();
246  pending_signal(SIGTSTP, SIG_DFL);
247  kill(getpid(), signo);
248}
249
250static char *
251ex_desc(int ex)
252{
253  static char num[12];
254  static char *desc[] = { "normal", "start", "sock",
255    "modem", "dial", "dead", "done", "reboot", "errdead",
256    "hangup", "term", "nodial", "nologin" };
257
258  if (ex >= 0 && ex < sizeof(desc)/sizeof(*desc))
259    return desc[ex];
260  snprintf(num, sizeof num, "%d", ex);
261  return num;
262}
263
264void
265Usage()
266{
267  fprintf(stderr,
268          "Usage: ppp [-auto | -background | -direct | -dedicated | -ddial ] [ -alias ] [system]\n");
269  exit(EX_START);
270}
271
272void
273ProcessArgs(int argc, char **argv)
274{
275  int optc;
276  char *cp;
277
278  optc = 0;
279  while (argc > 0 && **argv == '-') {
280    cp = *argv + 1;
281    if (strcmp(cp, "auto") == 0)
282      mode |= MODE_AUTO;
283    else if (strcmp(cp, "background") == 0)
284      mode |= MODE_BACKGROUND|MODE_AUTO;
285    else if (strcmp(cp, "direct") == 0)
286      mode |= MODE_DIRECT;
287    else if (strcmp(cp, "dedicated") == 0)
288      mode |= MODE_DEDICATED;
289    else if (strcmp(cp, "ddial") == 0)
290      mode |= MODE_DDIAL|MODE_AUTO;
291    else if (strcmp(cp, "alias") == 0) {
292      if (loadAliasHandlers(&VarAliasHandlers) == 0)
293        mode |= MODE_ALIAS;
294      else
295        LogPrintf(LogWARN, "Cannot load alias library\n");
296      optc--;             /* this option isn't exclusive */
297    }
298    else
299      Usage();
300    optc++;
301    argv++; argc--;
302  }
303  if (argc > 1) {
304    fprintf(stderr, "specify only one system label.\n");
305    exit(EX_START);
306  }
307  if (argc == 1) dstsystem = *argv;
308
309  if (optc > 1) {
310    fprintf(stderr, "specify only one mode.\n");
311    exit(EX_START);
312  }
313}
314
315static void
316Greetings()
317{
318  if (VarTerm) {
319    fprintf(VarTerm, "User Process PPP. Written by Toshiharu OHNO.\n");
320    fflush(VarTerm);
321  }
322}
323
324void
325main(argc, argv)
326int argc;
327char **argv;
328{
329  FILE *lockfile;
330  char *name;
331
332  VarTerm = 0;
333  name = rindex(argv[0], '/');
334  LogOpen(name ? name+1 : argv[0]);
335
336  argc--; argv++;
337  mode = MODE_INTER;		/* default operation is interactive mode */
338  netfd = server = modem = tun_in = -1;
339  ProcessArgs(argc, argv);
340  if (!(mode & MODE_DIRECT))
341    VarTerm = stdout;
342  Greetings();
343  GetUid();
344  IpcpDefAddress();
345
346  if (SelectSystem("default", CONFFILE) < 0 && VarTerm)
347    fprintf(VarTerm, "Warning: No default entry is given in config file.\n");
348
349  switch ( LocalAuthInit() ) {
350    case NOT_FOUND:
351        if (VarTerm) {
352    	  fprintf(VarTerm,LAUTH_M1);
353    	  fprintf(VarTerm,LAUTH_M2);
354          fflush(VarTerm);
355        }
356	/* Fall down */
357    case VALID:
358	VarLocalAuth = LOCAL_AUTH;
359	break;
360    default:
361	break;
362  }
363
364  if (OpenTunnel(&tunno) < 0) {
365    LogPrintf(LogWARN, "open_tun: %s", strerror(errno));
366    exit(EX_START);
367  }
368
369  if (mode & (MODE_AUTO|MODE_DIRECT|MODE_DEDICATED))
370    mode &= ~MODE_INTER;
371  if (mode & MODE_INTER) {
372    fprintf(VarTerm, "Interactive mode\n");
373    netfd = STDIN_FILENO;
374  } else if (mode & MODE_AUTO) {
375    fprintf(VarTerm, "Automatic Dialer mode\n");
376    if (dstsystem == NULL) {
377      if (VarTerm)
378        fprintf(VarTerm, "Destination system must be specified in"
379              " auto, background or ddial mode.\n");
380      exit(EX_START);
381    }
382  }
383
384  tcgetattr(0, &oldtio);		/* Save original tty mode */
385
386  pending_signal(SIGHUP, Hangup);
387  pending_signal(SIGTERM, CloseSession);
388  pending_signal(SIGINT, CloseSession);
389  pending_signal(SIGQUIT, CloseSession);
390#ifdef TRAPSEGV
391  signal(SIGSEGV, Hangup);
392#endif
393#ifdef SIGPIPE
394  signal(SIGPIPE, SIG_IGN);
395#endif
396#ifdef SIGALRM
397  pending_signal(SIGALRM, SIG_IGN);
398#endif
399  if(mode & MODE_INTER)
400    {
401#ifdef SIGTSTP
402      pending_signal(SIGTSTP, TerminalStop);
403#endif
404#ifdef SIGTTIN
405      pending_signal(SIGTTIN, TerminalStop);
406#endif
407#ifdef SIGTTOU
408      pending_signal(SIGTTOU, SIG_IGN);
409#endif
410    }
411
412  if (dstsystem) {
413    if (SelectSystem(dstsystem, CONFFILE) < 0) {
414      LogPrintf(LogWARN, "Destination system not found in conf file.\n");
415      Cleanup(EX_START);
416    }
417    if ((mode & MODE_AUTO) && DefHisAddress.ipaddr.s_addr == INADDR_ANY) {
418      LogPrintf(LogWARN, "Must specify dstaddr with"
419              " auto, background or ddial mode.\n");
420      Cleanup(EX_START);
421    }
422  }
423
424  if (!(mode & MODE_INTER)) {
425    int port = SERVER_PORT + tunno;
426
427    if (mode & MODE_BACKGROUND) {
428      if (pipe (BGFiledes)) {
429        LogPrintf(LogERROR, "pipe: %s", strerror(errno));
430	Cleanup(EX_SOCK);
431      }
432    }
433
434    /* Create server socket and listen at there. */
435    server = socket(PF_INET, SOCK_STREAM, 0);
436    if (server < 0) {
437      LogPrintf(LogERROR, "socket: %s", strerror(errno));
438      Cleanup(EX_SOCK);
439    }
440    ifsin.sin_family = AF_INET;
441    ifsin.sin_addr.s_addr = INADDR_ANY;
442    ifsin.sin_port = htons(port);
443    setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &server, sizeof server);
444    if (bind(server, (struct sockaddr *) &ifsin, sizeof(ifsin)) < 0) {
445      LogPrintf(LogERROR, "bind: %s", strerror(errno));
446      if (errno == EADDRINUSE && VarTerm)
447        fprintf(VarTerm, "Wait for a while, then try again.\n");
448      Cleanup(EX_SOCK);
449    }
450    if (listen(server, 5) != 0) {
451      LogPrintf(LogERROR, "Unable to listen to socket - OS overload?\n");
452      Cleanup(EX_SOCK);
453    }
454
455    if (!(mode & MODE_DIRECT)) {
456      pid_t bgpid;
457
458      bgpid = fork ();
459      if (bgpid == -1) {
460        LogPrintf(LogERROR, "fork: %s", strerror(errno));
461	Cleanup (EX_SOCK);
462      }
463      if (bgpid) {
464	char c = EX_NORMAL;
465
466	if (mode & MODE_BACKGROUND) {
467	  /* Wait for our child to close its pipe before we exit. */
468	  BGPid = bgpid;
469          close(BGFiledes[1]);
470	  if (read(BGFiledes[0], &c, 1) != 1) {
471	    fprintf(VarTerm, "Child exit, no status.\n");
472	    LogPrintf (LogPHASE, "Parent: Child exit, no status.\n");
473	  } else if (c == EX_NORMAL) {
474	    fprintf(VarTerm, "PPP enabled.\n");
475	    LogPrintf (LogPHASE, "Parent: PPP enabled.\n");
476	  } else {
477	    fprintf(VarTerm, "Child failed (%s).\n",ex_desc((int)c));
478	    LogPrintf(LogPHASE, "Parent: Child failed (%s).\n",
479                      ex_desc((int)c));
480          }
481          close(BGFiledes[0]);
482	}
483        exit(c);
484      } else if (mode & MODE_BACKGROUND)
485          close(BGFiledes[0]);
486    }
487
488    snprintf(pid_filename, sizeof (pid_filename), "%stun%d.pid",
489             _PATH_VARRUN, tunno);
490    (void)unlink(pid_filename);
491
492    if ((lockfile = fopen(pid_filename, "w")) != NULL) {
493      fprintf(lockfile, "%d\n", (int)getpid());
494      fclose(lockfile);
495    } else
496      LogPrintf(LogALERT, "Warning: Can't create %s: %s\n",
497                pid_filename, strerror(errno));
498
499    snprintf(if_filename, sizeof if_filename, "%s%s.if",
500             _PATH_VARRUN, VarBaseDevice);
501    (void)unlink(if_filename);
502
503    if ((lockfile = fopen(if_filename, "w")) != NULL) {
504      fprintf(lockfile, "tun%d\n", tunno);
505      fclose(lockfile);
506    } else
507      LogPrintf(LogALERT, "Warning: Can't create %s: %s\n",
508                if_filename, strerror(errno));
509
510    if (server >= 0)
511	LogPrintf(LogPHASE, "Listening at %d.\n", port);
512
513    VarTerm = 0;   /* We know it's currently stdin */
514
515#ifdef DOTTYINIT
516    if (mode & (MODE_DIRECT|MODE_DEDICATED)) { /* } */
517#else
518    if (mode & MODE_DIRECT) {
519#endif
520      chdir("/");  /* Be consistent with daemon() */
521      TtyInit();
522    } else
523      daemon(0,0);
524  } else {
525    TtyInit();
526    TtyCommandMode(1);
527  }
528  LogPrintf(LogPHASE, "PPP Started.\n");
529
530
531  do
532   DoLoop();
533  while (mode & MODE_DEDICATED);
534
535  Cleanup(EX_DONE);
536}
537
538/*
539 *  Turn into packet mode, where we speak PPP.
540 */
541void
542PacketMode()
543{
544  if (RawModem(modem) < 0) {
545    LogPrintf(LogWARN, "PacketMode: Not connected.\n");
546    return;
547  }
548
549  AsyncInit();
550  VjInit();
551  LcpInit();
552  IpcpInit();
553  CcpInit();
554  LcpUp();
555
556  LcpOpen(VarOpenMode);
557  if ((mode & (MODE_INTER|MODE_AUTO)) == MODE_INTER) {
558    TtyCommandMode(1);
559    if (VarTerm) {
560      fprintf(VarTerm, "Packet mode.\n");
561      aft_cmd = 1;
562    }
563  }
564}
565
566static void
567ShowHelp()
568{
569  fprintf(stderr, "The following commands are available:\n");
570  fprintf(stderr, " ~p\tEnter Packet mode\n");
571  fprintf(stderr, " ~-\tDecrease log level\n");
572  fprintf(stderr, " ~+\tIncrease log level\n");
573  fprintf(stderr, " ~t\tShow timers (only in \"log debug\" mode)\n");
574  fprintf(stderr, " ~m\tShow memory map (only in \"log debug\" mode)\n");
575  fprintf(stderr, " ~.\tTerminate program\n");
576  fprintf(stderr, " ~?\tThis help\n");
577}
578
579static void
580ReadTty()
581{
582  int n;
583  char ch;
584  static int ttystate;
585  FILE *oVarTerm;
586#define MAXLINESIZE 200
587  char linebuff[MAXLINESIZE];
588
589  LogPrintf(LogDEBUG, "termode = %d, netfd = %d, mode = %d\n",
590            TermMode, netfd, mode);
591  if (!TermMode) {
592    n = read(netfd, linebuff, sizeof(linebuff)-1);
593    if (n > 0) {
594      aft_cmd = 1;
595      DecodeCommand(linebuff, n, 1);
596    } else {
597      LogPrintf(LogPHASE, "client connection closed.\n");
598      VarLocalAuth = LOCAL_NO_AUTH;
599      mode &= ~MODE_INTER;
600      oVarTerm = VarTerm;
601      VarTerm = 0;
602      if (oVarTerm && oVarTerm != stdout)
603        fclose(oVarTerm);
604      close(netfd);
605      netfd = -1;
606    }
607    return;
608  }
609
610  /*
611   *  We are in terminal mode, decode special sequences
612   */
613  n = read(fileno(VarTerm), &ch, 1);
614  LogPrintf(LogDEBUG, "Got %d bytes (reading from the terminal)", n);
615
616  if (n > 0) {
617    switch (ttystate) {
618    case 0:
619      if (ch == '~')
620	ttystate++;
621      else
622	write(modem, &ch, n);
623      break;
624    case 1:
625      switch (ch) {
626      case '?':
627	ShowHelp();
628	break;
629      case 'p':
630	/*
631	 * XXX: Should check carrier.
632	 */
633	if (LcpFsm.state <= ST_CLOSED) {
634	  VarOpenMode = OPEN_ACTIVE;
635	  PacketMode();
636	}
637	break;
638      case '.':
639	TermMode = 1;
640	aft_cmd = 1;
641	TtyCommandMode(1);
642	break;
643      case 't':
644	if (LogIsKept(LogDEBUG)) {
645	  ShowTimers();
646	  break;
647	}
648      case 'm':
649	if (LogIsKept(LogDEBUG)) {
650	  ShowMemMap();
651	  break;
652	}
653      default:
654	if (write(modem, &ch, n) < 0)
655	  LogPrintf(LogERROR, "error writing to modem.\n");
656	break;
657      }
658      ttystate = 0;
659      break;
660    }
661  }
662}
663
664
665/*
666 *  Here, we'll try to detect HDLC frame
667 */
668
669static char *FrameHeaders[] = {
670  "\176\377\003\300\041",
671  "\176\377\175\043\300\041",
672  "\176\177\175\043\100\041",
673  "\176\175\337\175\043\300\041",
674  "\176\175\137\175\043\100\041",
675  NULL,
676};
677
678u_char *
679HdlcDetect(cp, n)
680u_char *cp;
681int n;
682{
683  char *ptr, *fp, **hp;
684
685  cp[n] = '\0';	/* be sure to null terminated */
686  ptr = NULL;
687  for (hp = FrameHeaders; *hp; hp++) {
688    fp = *hp;
689    if (DEV_IS_SYNC)
690      fp++;
691    ptr = strstr((char *)cp, fp);
692    if (ptr)
693      break;
694  }
695  return((u_char *)ptr);
696}
697
698static struct pppTimer RedialTimer;
699
700static void
701RedialTimeout()
702{
703  StopTimer(&RedialTimer);
704  LogPrintf(LogPHASE, "Redialing timer expired.\n");
705}
706
707static void
708StartRedialTimer(Timeout)
709	int Timeout;
710{
711  StopTimer(&RedialTimer);
712
713  if (Timeout) {
714    RedialTimer.state = TIMER_STOPPED;
715
716    if (Timeout > 0)
717	RedialTimer.load = Timeout * SECTICKS;
718    else
719	RedialTimer.load = (random() % REDIAL_PERIOD) * SECTICKS;
720
721    LogPrintf(LogPHASE, "Enter pause (%d) for redialing.\n",
722	      RedialTimer.load / SECTICKS);
723
724    RedialTimer.func = RedialTimeout;
725    StartTimer(&RedialTimer);
726  }
727}
728
729
730static void
731DoLoop()
732{
733  fd_set rfds, wfds, efds;
734  int pri, i, n, wfd, nfds;
735  struct sockaddr_in hisaddr;
736  struct timeval timeout, *tp;
737  int ssize = sizeof(hisaddr);
738  u_char *cp;
739  u_char rbuff[MAX_MRU];
740  int dial_up;
741  int tries;
742  int qlen;
743  pid_t pgroup;
744
745  pgroup = getpgrp();
746
747  if (mode & MODE_DIRECT) {
748    LogPrintf(LogDEBUG, "Opening modem\n");
749    modem = OpenModem(mode);
750    LogPrintf(LogPHASE, "Packet mode enabled\n");
751    PacketMode();
752  } else if (mode & MODE_DEDICATED) {
753    if (modem < 0)
754      modem = OpenModem(mode);
755  }
756
757  fflush(VarTerm);
758
759  timeout.tv_sec = 0;
760  timeout.tv_usec = 0;
761  reconnectState = RECON_UNKNOWN;
762
763  if (mode & MODE_BACKGROUND)
764    dial_up = TRUE;			/* Bring the line up */
765  else
766    dial_up = FALSE;			/* XXXX */
767  tries = 0;
768  for (;;) {
769    nfds = 0;
770    FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds);
771
772    /*
773     * If the link is down and we're in DDIAL mode, bring it back
774     * up.
775     */
776    if (mode & MODE_DDIAL && LcpFsm.state <= ST_CLOSED)
777        dial_up = TRUE;
778
779    /*
780     * If we lost carrier and want to re-establish the connection
781     * due to the "set reconnect" value, we'd better bring the line
782     * back up.
783     */
784    if (LcpFsm.state <= ST_CLOSED) {
785      if (dial_up != TRUE && reconnectState == RECON_TRUE) {
786        if (++reconnectCount <= VarReconnectTries) {
787          LogPrintf(LogPHASE, "Connection lost, re-establish (%d/%d)\n",
788                    reconnectCount, VarReconnectTries);
789	  StartRedialTimer(VarReconnectTimer);
790          dial_up = TRUE;
791        } else {
792          if (VarReconnectTries)
793            LogPrintf(LogPHASE, "Connection lost, maximum (%d) times\n",
794                      VarReconnectTries);
795          reconnectCount = 0;
796          if (mode & MODE_BACKGROUND)
797            Cleanup(EX_DEAD);
798        }
799        reconnectState = RECON_ENVOKED;
800      }
801    }
802
803   /*
804    * If Ip packet for output is enqueued and require dial up,
805    * Just do it!
806    */
807    if ( dial_up && RedialTimer.state != TIMER_RUNNING ) {
808      LogPrintf(LogDEBUG, "going to dial: modem = %d\n", modem);
809      modem = OpenModem(mode);
810      if (modem < 0) {
811        tries++;
812        if (VarDialTries)
813          LogPrintf(LogCHAT, "Failed to open modem (attempt %u of %d)\n",
814	            tries, VarDialTries);
815        else
816	  LogPrintf(LogCHAT, "Failed to open modem (attempt %u)\n", tries);
817
818	if (VarDialTries && tries >= VarDialTries) {
819	  if (mode & MODE_BACKGROUND)
820	    Cleanup(EX_DIAL);  /* Can't get the modem */
821	  dial_up = FALSE;
822          reconnectState = RECON_UNKNOWN;
823          reconnectCount = 0;
824	  tries = 0;
825        } else
826	  StartRedialTimer(VarRedialTimeout);
827      } else {
828	tries++;    /* Tries are per number, not per list of numbers. */
829        if (VarDialTries)
830	  LogPrintf(LogCHAT, "Dial attempt %u of %d\n", tries,
831		    VarDialTries);
832        else
833	  LogPrintf(LogCHAT, "Dial attempt %u\n", tries);
834	if (DialModem() == EX_DONE) {
835	  sleep(1);	       /* little pause to allow peer starts */
836	  ModemTimeout();
837	  PacketMode();
838	  dial_up = FALSE;
839          reconnectState = RECON_UNKNOWN;
840	  tries = 0;
841	} else {
842	  CloseModem();
843	  if (mode & MODE_BACKGROUND) {
844	    if (VarNextPhone == NULL)
845	      Cleanup(EX_DIAL);  /* Tried all numbers - no luck */
846	    else
847	      /* Try all numbers in background mode */
848	      StartRedialTimer(VarRedialNextTimeout);
849	  } else if (VarDialTries && tries >= VarDialTries) {
850	    /* I give up !  Can't get through :( */
851	    StartRedialTimer(VarRedialTimeout);
852	    dial_up = FALSE;
853            reconnectState = RECON_UNKNOWN;
854            reconnectCount = 0;
855	    tries = 0;
856	  } else if (VarNextPhone == NULL)
857	    /* Dial failed. Keep quite during redial wait period. */
858	    StartRedialTimer(VarRedialTimeout);
859	  else
860	    StartRedialTimer(VarRedialNextTimeout);
861	}
862      }
863    }
864    qlen = ModemQlen();
865
866    if (qlen == 0) {
867      IpStartOutput();
868      qlen = ModemQlen();
869    }
870
871    if (modem >= 0) {
872      if (modem + 1 > nfds)
873	nfds = modem + 1;
874      FD_SET(modem, &rfds);
875      FD_SET(modem, &efds);
876      if (qlen > 0) {
877	FD_SET(modem, &wfds);
878      }
879    }
880    if (server >= 0) {
881      if (server + 1 > nfds)
882	nfds = server + 1;
883      FD_SET(server, &rfds);
884    }
885
886    /*  *** IMPORTANT ***
887     *
888     *  CPU is serviced every TICKUNIT micro seconds.
889     *	This value must be chosen with great care. If this values is
890     *  too big, it results loss of characters from modem and poor responce.
891     *  If this values is too small, ppp process eats many CPU time.
892     */
893#ifndef SIGALRM
894    usleep(TICKUNIT);
895    TimerService();
896#else
897    handle_signals();
898#endif
899
900    /* If there are aren't many packets queued, look for some more. */
901    if (qlen < 20 && tun_in >= 0) {
902      if (tun_in + 1 > nfds)
903	nfds = tun_in + 1;
904      FD_SET(tun_in, &rfds);
905    }
906
907    if (netfd >= 0) {
908      if (netfd + 1 > nfds)
909	nfds = netfd + 1;
910      FD_SET(netfd, &rfds);
911      FD_SET(netfd, &efds);
912    }
913
914#ifndef SIGALRM
915    /*
916     *  Normally, select() will not block because modem is writable.
917     *  In AUTO mode, select will block until we find packet from tun
918     */
919    tp = (RedialTimer.state == TIMER_RUNNING)? &timeout : NULL;
920    i = select(nfds, &rfds, &wfds, &efds, tp);
921#else
922    /*
923     * When SIGALRM timer is running, a select function will be
924     * return -1 and EINTR after a Time Service signal hundler
925     * is done.  If the redial timer is not running and we are
926     * trying to dial, poll with a 0 value timer.
927     */
928    tp = (dial_up && RedialTimer.state != TIMER_RUNNING) ? &timeout : NULL;
929    i = select(nfds, &rfds, &wfds, &efds, tp);
930#endif
931
932    if ( i == 0 ) {
933        continue;
934    }
935
936    if ( i < 0 ) {
937       if ( errno == EINTR ) {
938          handle_signals();
939          continue;
940       }
941       LogPrintf(LogERROR, "select: %s", strerror(errno));
942       break;
943    }
944
945    if ((netfd >= 0 && FD_ISSET(netfd, &efds)) || (modem >= 0 && FD_ISSET(modem, &efds))) {
946      LogPrintf(LogALERT, "Exception detected.\n");
947      break;
948    }
949
950    if (server >= 0 && FD_ISSET(server, &rfds)) {
951      LogPrintf(LogPHASE, "connected to client.\n");
952      wfd = accept(server, (struct sockaddr *)&hisaddr, &ssize);
953      if (wfd < 0) {
954        LogPrintf(LogERROR, "accept: %s", strerror(errno));
955	continue;
956      }
957      if (netfd >= 0) {
958	write(wfd, "already in use.\n", 16);
959	close(wfd);
960	continue;
961      } else
962	netfd = wfd;
963      VarTerm = fdopen(netfd, "a+");
964      mode |= MODE_INTER;
965      Greetings();
966      switch ( LocalAuthInit() ) {
967         case NOT_FOUND:
968	    if (VarTerm) {
969    	      fprintf(VarTerm,LAUTH_M1);
970    	      fprintf(VarTerm,LAUTH_M2);
971              fflush(VarTerm);
972            }
973	    /* Fall down */
974         case VALID:
975	    VarLocalAuth = LOCAL_AUTH;
976	    break;
977         default:
978	    break;
979      }
980      (void) IsInteractive();
981      Prompt();
982    }
983
984    if ((mode & MODE_INTER) && (netfd >= 0 && FD_ISSET(netfd, &rfds)) &&
985	((mode & MODE_AUTO) || pgroup == tcgetpgrp(0))) {
986      /* something to read from tty */
987      ReadTty();
988    }
989    if (modem >= 0) {
990      if (FD_ISSET(modem, &wfds)) {	/* ready to write into modem */
991	 ModemStartOutput(modem);
992      }
993      if (FD_ISSET(modem, &rfds)) {	/* something to read from modem */
994	if (LcpFsm.state <= ST_CLOSED)
995	  usleep(10000);
996	n = read(modem, rbuff, sizeof(rbuff));
997	if ((mode & MODE_DIRECT) && n <= 0) {
998	  DownConnection();
999	} else
1000          LogDumpBuff(LogASYNC, "ReadFromModem", rbuff, n);
1001
1002	if (LcpFsm.state <= ST_CLOSED) {
1003	  /*
1004	   *  In dedicated mode, we just discard input until LCP is started.
1005	   */
1006	  if (!(mode & MODE_DEDICATED)) {
1007	    cp = HdlcDetect(rbuff, n);
1008	    if (cp) {
1009	      /*
1010	       * LCP packet is detected. Turn ourselves into packet mode.
1011	       */
1012	      if (cp != rbuff) {
1013	        write(modem, rbuff, cp - rbuff);
1014	        write(modem, "\r\n", 2);
1015	      }
1016	      PacketMode();
1017	    } else
1018	      write(fileno(VarTerm), rbuff, n);
1019	  }
1020	} else {
1021	  if (n > 0)
1022	    AsyncInput(rbuff, n);
1023	}
1024      }
1025    }
1026
1027    if (tun_in >= 0 && FD_ISSET(tun_in, &rfds)) {       /* something to read from tun */
1028      n = read(tun_in, rbuff, sizeof(rbuff));
1029      if (n < 0) {
1030        LogPrintf(LogERROR, "read from tun: %s", strerror(errno));
1031	continue;
1032      }
1033      /*
1034       *  Process on-demand dialup. Output packets are queued within tunnel
1035       *  device until IPCP is opened.
1036       */
1037      if (LcpFsm.state <= ST_CLOSED && (mode & MODE_AUTO)) {
1038	pri = PacketCheck(rbuff, n, FL_DIAL);
1039	if (pri >= 0) {
1040	  if (mode & MODE_ALIAS) {
1041	    VarPacketAliasOut(rbuff, sizeof rbuff);
1042	    n = ntohs(((struct ip *)rbuff)->ip_len);
1043	  }
1044	  IpEnqueue(pri, rbuff, n);
1045	  dial_up = TRUE;		/* XXX */
1046	}
1047	continue;
1048      }
1049      pri = PacketCheck(rbuff, n, FL_OUT);
1050      if (pri >= 0) {
1051        if (mode & MODE_ALIAS) {
1052          VarPacketAliasOut(rbuff, sizeof rbuff);
1053          n = ntohs(((struct ip *)rbuff)->ip_len);
1054        }
1055	IpEnqueue(pri, rbuff, n);
1056      }
1057    }
1058  }
1059  LogPrintf(LogDEBUG, "Job (DoLoop) done.\n");
1060}
1061