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