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