main.c revision 31823
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.107 1997/12/13 02:37:27 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  int nfds;
366
367  nfds = getdtablesize();
368  if (nfds >= FD_SETSIZE)
369    /*
370     * If we've got loads of file descriptors, make sure they're all
371     * closed.  If they aren't, we may end up with a seg fault when our
372     * `fd_set's get too big when select()ing !
373     */
374    while (--nfds > 2)
375      close(nfds);
376
377  VarTerm = 0;
378  name = strrchr(argv[0], '/');
379  LogOpen(name ? name + 1 : argv[0]);
380
381  argc--;
382  argv++;
383  label = ProcessArgs(argc, argv);
384  if (!(mode & MODE_DIRECT))
385    VarTerm = stdout;
386
387  ID0init();
388  if (ID0realuid() != 0) {
389    char conf[200], *ptr;
390
391    snprintf(conf, sizeof conf, "%s/%s", _PATH_PPP, CONFFILE);
392    do {
393      if (!access(conf, W_OK)) {
394        LogPrintf(LogALERT, "ppp: Access violation: Please protect %s\n", conf);
395        return -1;
396      }
397      ptr = conf + strlen(conf)-2;
398      while (ptr > conf && *ptr != '/')
399        *ptr-- = '\0';
400    } while (ptr >= conf);
401  }
402
403  if (!ValidSystem(label)) {
404    fprintf(stderr, "You may not use ppp in this mode with this label\n");
405    if (mode & MODE_DIRECT) {
406      const char *l;
407      l = label ? label : "default";
408      VarTerm = 0;
409      LogPrintf(LogWARN, "Label %s rejected -direct connection\n", l);
410    }
411    LogClose();
412    return 1;
413  }
414
415  if (!GetShortHost())
416    return 1;
417  Greetings();
418  IpcpDefAddress();
419
420  if (mode & MODE_INTER)
421    VarLocalAuth = LOCAL_AUTH;
422
423  if (SelectSystem("default", CONFFILE) < 0 && VarTerm)
424    fprintf(VarTerm, "Warning: No default entry is given in config file.\n");
425
426  if (OpenTunnel(&tunno) < 0) {
427    LogPrintf(LogWARN, "OpenTunnel: %s\n", strerror(errno));
428    return EX_START;
429  }
430  if (mode & MODE_INTER) {
431    fprintf(VarTerm, "Interactive mode\n");
432    netfd = STDOUT_FILENO;
433  } else if ((mode & MODE_OUTGOING_DAEMON) && !(mode & MODE_DEDICATED))
434    if (label == NULL) {
435      if (VarTerm)
436	fprintf(VarTerm, "Destination system must be specified in"
437		" auto, background or ddial mode.\n");
438      return EX_START;
439    }
440
441  tcgetattr(0, &oldtio);	/* Save original tty mode */
442
443  pending_signal(SIGHUP, CloseSession);
444  pending_signal(SIGTERM, CloseSession);
445  pending_signal(SIGINT, CloseConnection);
446  pending_signal(SIGQUIT, CloseSession);
447#ifdef SIGPIPE
448  signal(SIGPIPE, SIG_IGN);
449#endif
450#ifdef SIGALRM
451  pending_signal(SIGALRM, SIG_IGN);
452#endif
453  if (mode & MODE_INTER) {
454#ifdef SIGTSTP
455    pending_signal(SIGTSTP, TerminalStop);
456#endif
457#ifdef SIGTTIN
458    pending_signal(SIGTTIN, TerminalStop);
459#endif
460#ifdef SIGTTOU
461    pending_signal(SIGTTOU, SIG_IGN);
462#endif
463  }
464  if (!(mode & MODE_INTER)) {
465#ifdef SIGUSR1
466    pending_signal(SIGUSR1, SetUpServer);
467#endif
468#ifdef SIGUSR2
469    pending_signal(SIGUSR2, BringDownServer);
470#endif
471  }
472
473  if (label) {
474    if (SelectSystem(label, CONFFILE) < 0) {
475      LogPrintf(LogWARN, "Destination system %s not found in conf file.\n",
476                GetLabel());
477      Cleanup(EX_START);
478    }
479    /*
480     * We don't SetLabel() 'till now in case SelectSystem() has an
481     * embeded load "otherlabel" command.
482     */
483    SetLabel(label);
484    if (mode & MODE_OUTGOING_DAEMON &&
485	DefHisAddress.ipaddr.s_addr == INADDR_ANY) {
486      LogPrintf(LogWARN, "You must \"set ifaddr\" in label %s for"
487		" auto, background or ddial mode.\n", label);
488      Cleanup(EX_START);
489    }
490  }
491
492  if (mode & MODE_DAEMON) {
493    if (mode & MODE_BACKGROUND) {
494      if (pipe(BGFiledes)) {
495	LogPrintf(LogERROR, "pipe: %s\n", strerror(errno));
496	Cleanup(EX_SOCK);
497      }
498    }
499
500    if (!(mode & MODE_DIRECT)) {
501      pid_t bgpid;
502
503      bgpid = fork();
504      if (bgpid == -1) {
505	LogPrintf(LogERROR, "fork: %s\n", strerror(errno));
506	Cleanup(EX_SOCK);
507      }
508      if (bgpid) {
509	char c = EX_NORMAL;
510
511	if (mode & MODE_BACKGROUND) {
512	  /* Wait for our child to close its pipe before we exit. */
513	  BGPid = bgpid;
514	  close(BGFiledes[1]);
515	  if (read(BGFiledes[0], &c, 1) != 1) {
516	    fprintf(VarTerm, "Child exit, no status.\n");
517	    LogPrintf(LogPHASE, "Parent: Child exit, no status.\n");
518	  } else if (c == EX_NORMAL) {
519	    fprintf(VarTerm, "PPP enabled.\n");
520	    LogPrintf(LogPHASE, "Parent: PPP enabled.\n");
521	  } else {
522	    fprintf(VarTerm, "Child failed (%s).\n", ex_desc((int) c));
523	    LogPrintf(LogPHASE, "Parent: Child failed (%s).\n",
524		      ex_desc((int) c));
525	  }
526	  close(BGFiledes[0]);
527	}
528	return c;
529      } else if (mode & MODE_BACKGROUND)
530	close(BGFiledes[0]);
531    }
532
533    VarTerm = 0;		/* We know it's currently stdout */
534    close(1);
535    close(2);
536
537    if (mode & MODE_DIRECT)
538      TtyInit(1);
539    else if (mode & MODE_DAEMON) {
540      setsid();
541      close(0);
542    }
543  } else {
544    TtyInit(0);
545    TtyCommandMode(1);
546  }
547
548  snprintf(pid_filename, sizeof(pid_filename), "%stun%d.pid",
549           _PATH_VARRUN, tunno);
550  lockfile = ID0fopen(pid_filename, "w");
551  if (lockfile != NULL) {
552    fprintf(lockfile, "%d\n", (int) getpid());
553    fclose(lockfile);
554  } else
555    LogPrintf(LogALERT, "Warning: Can't create %s: %s\n",
556              pid_filename, strerror(errno));
557
558  LogPrintf(LogPHASE, "PPP Started.\n");
559
560
561  do
562    DoLoop();
563  while (mode & MODE_DEDICATED);
564
565  Cleanup(EX_DONE);
566  return 0;
567}
568
569/*
570 *  Turn into packet mode, where we speak PPP.
571 */
572void
573PacketMode()
574{
575  if (RawModem() < 0) {
576    LogPrintf(LogWARN, "PacketMode: Not connected.\n");
577    return;
578  }
579  AsyncInit();
580  VjInit(15);
581  LcpInit();
582  IpcpInit();
583  CcpInit();
584  LcpUp();
585
586  LcpOpen(VarOpenMode);
587  if (mode & MODE_INTER)
588    TtyCommandMode(1);
589  if (VarTerm) {
590    fprintf(VarTerm, "Packet mode.\n");
591    aft_cmd = 1;
592  }
593}
594
595static void
596ShowHelp(void)
597{
598  fprintf(stderr, "The following commands are available:\r\n");
599  fprintf(stderr, " ~p\tEnter Packet mode\r\n");
600  fprintf(stderr, " ~-\tDecrease log level\r\n");
601  fprintf(stderr, " ~+\tIncrease log level\r\n");
602  fprintf(stderr, " ~t\tShow timers (only in \"log debug\" mode)\r\n");
603  fprintf(stderr, " ~m\tShow memory map (only in \"log debug\" mode)\r\n");
604  fprintf(stderr, " ~.\tTerminate program\r\n");
605  fprintf(stderr, " ~?\tThis help\r\n");
606}
607
608static void
609ReadTty(void)
610{
611  int n;
612  char ch;
613  static int ttystate;
614  char linebuff[LINE_LEN];
615
616  LogPrintf(LogDEBUG, "termode = %d, netfd = %d, mode = %d\n",
617	    TermMode, netfd, mode);
618  if (!TermMode) {
619    n = read(netfd, linebuff, sizeof(linebuff) - 1);
620    if (n > 0) {
621      aft_cmd = 1;
622      if (linebuff[n-1] == '\n')
623        linebuff[--n] = '\0';
624      if (n)
625        DecodeCommand(linebuff, n, IsInteractive(0) ? NULL : "Client");
626      Prompt();
627    } else if (n <= 0)
628      DropClient();
629    return;
630  }
631
632  /*
633   * We are in terminal mode, decode special sequences
634   */
635  n = read(fileno(VarTerm), &ch, 1);
636  LogPrintf(LogDEBUG, "Got %d bytes (reading from the terminal)\n", n);
637
638  if (n > 0) {
639    switch (ttystate) {
640    case 0:
641      if (ch == '~')
642	ttystate++;
643      else
644	write(modem, &ch, n);
645      break;
646    case 1:
647      switch (ch) {
648      case '?':
649	ShowHelp();
650	break;
651      case 'p':
652
653	/*
654	 * XXX: Should check carrier.
655	 */
656	if (LcpFsm.state <= ST_CLOSED) {
657	  VarOpenMode = OPEN_ACTIVE;
658	  PacketMode();
659	}
660	break;
661      case '.':
662	TermMode = 1;
663	aft_cmd = 1;
664	TtyCommandMode(1);
665	break;
666      case 't':
667	if (LogIsKept(LogDEBUG)) {
668	  ShowTimers();
669	  break;
670	}
671      case 'm':
672	if (LogIsKept(LogDEBUG)) {
673	  ShowMemMap(NULL);
674	  break;
675	}
676      default:
677	if (write(modem, &ch, n) < 0)
678	  LogPrintf(LogERROR, "error writing to modem.\n");
679	break;
680      }
681      ttystate = 0;
682      break;
683    }
684  }
685}
686
687
688/*
689 *  Here, we'll try to detect HDLC frame
690 */
691
692static const char *FrameHeaders[] = {
693  "\176\377\003\300\041",
694  "\176\377\175\043\300\041",
695  "\176\177\175\043\100\041",
696  "\176\175\337\175\043\300\041",
697  "\176\175\137\175\043\100\041",
698  NULL,
699};
700
701static const u_char *
702HdlcDetect(u_char * cp, int n)
703{
704  const char *ptr, *fp, **hp;
705
706  cp[n] = '\0';			/* be sure to null terminated */
707  ptr = NULL;
708  for (hp = FrameHeaders; *hp; hp++) {
709    fp = *hp;
710    if (DEV_IS_SYNC)
711      fp++;
712    ptr = strstr((char *) cp, fp);
713    if (ptr)
714      break;
715  }
716  return ((const u_char *) ptr);
717}
718
719static struct pppTimer RedialTimer;
720
721static void
722RedialTimeout(void *v)
723{
724  StopTimer(&RedialTimer);
725  LogPrintf(LogPHASE, "Redialing timer expired.\n");
726}
727
728static void
729StartRedialTimer(int Timeout)
730{
731  StopTimer(&RedialTimer);
732
733  if (Timeout) {
734    RedialTimer.state = TIMER_STOPPED;
735
736    if (Timeout > 0)
737      RedialTimer.load = Timeout * SECTICKS;
738    else
739      RedialTimer.load = (random() % REDIAL_PERIOD) * SECTICKS;
740
741    LogPrintf(LogPHASE, "Enter pause (%d) for redialing.\n",
742	      RedialTimer.load / SECTICKS);
743
744    RedialTimer.func = RedialTimeout;
745    StartTimer(&RedialTimer);
746  }
747}
748
749
750static void
751DoLoop(void)
752{
753  fd_set rfds, wfds, efds;
754  int pri, i, n, wfd, nfds;
755  struct sockaddr_in hisaddr;
756  struct timeval timeout, *tp;
757  int ssize = sizeof(hisaddr);
758  const u_char *cp;
759  int tries;
760  int qlen;
761  int res;
762  struct tun_data tun;
763#define rbuff tun.data
764
765  if (mode & MODE_DIRECT) {
766    LogPrintf(LogDEBUG, "Opening modem\n");
767    if (OpenModem() < 0)
768      return;
769    LogPrintf(LogPHASE, "Packet mode enabled\n");
770    PacketMode();
771  } else if (mode & MODE_DEDICATED) {
772    if (modem < 0)
773      while (OpenModem() < 0)
774	nointr_sleep(VarReconnectTimer);
775  }
776  fflush(VarTerm);
777
778  timeout.tv_sec = 0;
779  timeout.tv_usec = 0;
780  reconnectState = RECON_UNKNOWN;
781
782  if (mode & MODE_BACKGROUND)
783    dial_up = 1;		/* Bring the line up */
784  else
785    dial_up = 0;		/* XXXX */
786  tries = 0;
787  for (;;) {
788    nfds = 0;
789    FD_ZERO(&rfds);
790    FD_ZERO(&wfds);
791    FD_ZERO(&efds);
792
793    /*
794     * If the link is down and we're in DDIAL mode, bring it back up.
795     */
796    if (mode & MODE_DDIAL && LcpFsm.state <= ST_CLOSED)
797      dial_up = 1;
798
799    /*
800     * If we lost carrier and want to re-establish the connection due to the
801     * "set reconnect" value, we'd better bring the line back up.
802     */
803    if (LcpFsm.state <= ST_CLOSED) {
804      if (!dial_up && reconnectState == RECON_TRUE) {
805	if (++reconnectCount <= VarReconnectTries) {
806	  LogPrintf(LogPHASE, "Connection lost, re-establish (%d/%d)\n",
807		    reconnectCount, VarReconnectTries);
808	  StartRedialTimer(VarReconnectTimer);
809	  dial_up = 1;
810	} else {
811	  if (VarReconnectTries)
812	    LogPrintf(LogPHASE, "Connection lost, maximum (%d) times\n",
813		      VarReconnectTries);
814	  reconnectCount = 0;
815	  if (mode & MODE_BACKGROUND)
816	    Cleanup(EX_DEAD);
817	}
818	reconnectState = RECON_ENVOKED;
819      } else if (mode & MODE_DEDICATED)
820        if (VarOpenMode == OPEN_ACTIVE)
821          PacketMode();
822    }
823
824    /*
825     * If Ip packet for output is enqueued and require dial up, Just do it!
826     */
827    if (dial_up && RedialTimer.state != TIMER_RUNNING) {
828      LogPrintf(LogDEBUG, "going to dial: modem = %d\n", modem);
829      if (OpenModem() < 0) {
830	tries++;
831	if (!(mode & MODE_DDIAL) && VarDialTries)
832	  LogPrintf(LogCHAT, "Failed to open modem (attempt %u of %d)\n",
833		    tries, VarDialTries);
834	else
835	  LogPrintf(LogCHAT, "Failed to open modem (attempt %u)\n", tries);
836
837	if (!(mode & MODE_DDIAL) && VarDialTries && tries >= VarDialTries) {
838	  if (mode & MODE_BACKGROUND)
839	    Cleanup(EX_DIAL);	/* Can't get the modem */
840	  dial_up = 0;
841	  reconnectState = RECON_UNKNOWN;
842	  reconnectCount = 0;
843	  tries = 0;
844	} else
845	  StartRedialTimer(VarRedialTimeout);
846      } else {
847	tries++;		/* Tries are per number, not per list of
848				 * numbers. */
849	if (!(mode & MODE_DDIAL) && VarDialTries)
850	  LogPrintf(LogCHAT, "Dial attempt %u of %d\n", tries, VarDialTries);
851	else
852	  LogPrintf(LogCHAT, "Dial attempt %u\n", tries);
853
854	if ((res = DialModem()) == EX_DONE) {
855	  nointr_sleep(1);		/* little pause to allow peer starts */
856	  ModemTimeout(NULL);
857	  PacketMode();
858	  dial_up = 0;
859	  reconnectState = RECON_UNKNOWN;
860	  tries = 0;
861	} else {
862	  if (mode & MODE_BACKGROUND) {
863	    if (VarNextPhone == NULL || res == EX_SIG)
864	      Cleanup(EX_DIAL);	/* Tried all numbers - no luck */
865	    else
866	      /* Try all numbers in background mode */
867	      StartRedialTimer(VarRedialNextTimeout);
868	  } else if (!(mode & MODE_DDIAL) &&
869		     ((VarDialTries && tries >= VarDialTries) ||
870		      res == EX_SIG)) {
871	    /* I give up !  Can't get through :( */
872	    StartRedialTimer(VarRedialTimeout);
873	    dial_up = 0;
874	    reconnectState = RECON_UNKNOWN;
875	    reconnectCount = 0;
876	    tries = 0;
877	  } else if (VarNextPhone == NULL)
878	    /* Dial failed. Keep quite during redial wait period. */
879	    StartRedialTimer(VarRedialTimeout);
880	  else
881	    StartRedialTimer(VarRedialNextTimeout);
882	}
883      }
884    }
885    qlen = ModemQlen();
886
887    if (qlen == 0) {
888      IpStartOutput();
889      qlen = ModemQlen();
890    }
891    if (modem >= 0) {
892      if (modem + 1 > nfds)
893	nfds = modem + 1;
894      FD_SET(modem, &rfds);
895      FD_SET(modem, &efds);
896      if (qlen > 0) {
897	FD_SET(modem, &wfds);
898      }
899    }
900    if (server >= 0) {
901      if (server + 1 > nfds)
902	nfds = server + 1;
903      FD_SET(server, &rfds);
904    }
905
906    /*
907     * *** IMPORTANT ***
908     *
909     * CPU is serviced every TICKUNIT micro seconds. This value must be chosen
910     * with great care. If this values is too big, it results loss of
911     * characters from modem and poor responce. If this values is too small,
912     * ppp process eats many CPU time.
913     */
914#ifndef SIGALRM
915    nointr_usleep(TICKUNIT);
916    TimerService();
917#else
918    handle_signals();
919#endif
920
921    /* If there are aren't many packets queued, look for some more. */
922    if (qlen < 20 && tun_in >= 0) {
923      if (tun_in + 1 > nfds)
924	nfds = tun_in + 1;
925      FD_SET(tun_in, &rfds);
926    }
927    if (netfd >= 0) {
928      if (netfd + 1 > nfds)
929	nfds = netfd + 1;
930      FD_SET(netfd, &rfds);
931      FD_SET(netfd, &efds);
932    }
933#ifndef SIGALRM
934
935    /*
936     * Normally, select() will not block because modem is writable. In AUTO
937     * mode, select will block until we find packet from tun
938     */
939    tp = (RedialTimer.state == TIMER_RUNNING) ? &timeout : NULL;
940    i = select(nfds, &rfds, &wfds, &efds, tp);
941#else
942
943    /*
944     * When SIGALRM timer is running, a select function will be return -1 and
945     * EINTR after a Time Service signal hundler is done.  If the redial
946     * timer is not running and we are trying to dial, poll with a 0 value
947     * timer.
948     */
949    tp = (dial_up && RedialTimer.state != TIMER_RUNNING) ? &timeout : NULL;
950    i = select(nfds, &rfds, &wfds, &efds, tp);
951#endif
952
953    if (i == 0) {
954      continue;
955    }
956    if (i < 0) {
957      if (errno == EINTR) {
958	handle_signals();
959	continue;
960      }
961      LogPrintf(LogERROR, "DoLoop: select(): %s\n", strerror(errno));
962      break;
963    }
964    if ((netfd >= 0 && FD_ISSET(netfd, &efds)) || (modem >= 0 && FD_ISSET(modem, &efds))) {
965      LogPrintf(LogALERT, "Exception detected.\n");
966      break;
967    }
968    if (server >= 0 && FD_ISSET(server, &rfds)) {
969      LogPrintf(LogPHASE, "connected to client.\n");
970      wfd = accept(server, (struct sockaddr *) & hisaddr, &ssize);
971      if (wfd < 0) {
972	LogPrintf(LogERROR, "DoLoop: accept(): %s\n", strerror(errno));
973	continue;
974      }
975      if (netfd >= 0) {
976	write(wfd, "already in use.\n", 16);
977	close(wfd);
978	continue;
979      } else
980	netfd = wfd;
981      VarTerm = fdopen(netfd, "a+");
982      LocalAuthInit();
983      Greetings();
984      IsInteractive(1);
985      Prompt();
986    }
987    if (netfd >= 0 && FD_ISSET(netfd, &rfds))
988      /* something to read from tty */
989      ReadTty();
990    if (modem >= 0 && FD_ISSET(modem, &wfds)) {
991      /* ready to write into modem */
992      ModemStartOutput(modem);
993      if (modem < 0)
994        dial_up = 1;
995    }
996    if (modem >= 0 && FD_ISSET(modem, &rfds)) {
997      /* something to read from modem */
998      if (LcpFsm.state <= ST_CLOSED)
999	nointr_usleep(10000);
1000      n = read(modem, rbuff, sizeof(rbuff));
1001      if ((mode & MODE_DIRECT) && n <= 0) {
1002	DownConnection();
1003      } else
1004	LogDumpBuff(LogASYNC, "ReadFromModem", rbuff, n);
1005
1006      if (LcpFsm.state <= ST_CLOSED) {
1007	/*
1008	 * In dedicated mode, we just discard input until LCP is started.
1009	 */
1010	if (!(mode & MODE_DEDICATED)) {
1011	  cp = HdlcDetect(rbuff, n);
1012	  if (cp) {
1013	    /*
1014	     * LCP packet is detected. Turn ourselves into packet mode.
1015	     */
1016	    if (cp != rbuff) {
1017	      write(modem, rbuff, cp - rbuff);
1018	      write(modem, "\r\n", 2);
1019	    }
1020	    PacketMode();
1021	  } else
1022	    write(fileno(VarTerm), rbuff, n);
1023	}
1024      } else {
1025	if (n > 0)
1026	  AsyncInput(rbuff, n);
1027      }
1028    }
1029    if (tun_in >= 0 && FD_ISSET(tun_in, &rfds)) {	/* something to read
1030							 * from tun */
1031      n = read(tun_in, &tun, sizeof(tun));
1032      if (n < 0) {
1033	LogPrintf(LogERROR, "read from tun: %s\n", strerror(errno));
1034	continue;
1035      }
1036      n -= sizeof(tun)-sizeof(tun.data);
1037      if (n <= 0) {
1038	LogPrintf(LogERROR, "read from tun: Only %d bytes read\n", n);
1039	continue;
1040      }
1041      if (!tun_check_header(tun, AF_INET))
1042          continue;
1043      if (((struct ip *) rbuff)->ip_dst.s_addr == IpcpInfo.want_ipaddr.s_addr) {
1044	/* we've been asked to send something addressed *to* us :( */
1045	if (VarLoopback) {
1046	  pri = PacketCheck(rbuff, n, FL_IN);
1047	  if (pri >= 0) {
1048	    struct mbuf *bp;
1049
1050#ifndef NOALIAS
1051	    if (mode & MODE_ALIAS) {
1052	      VarPacketAliasIn(rbuff, sizeof rbuff);
1053	      n = ntohs(((struct ip *) rbuff)->ip_len);
1054	    }
1055#endif
1056	    bp = mballoc(n, MB_IPIN);
1057	    memcpy(MBUF_CTOP(bp), rbuff, n);
1058	    IpInput(bp);
1059	    LogPrintf(LogDEBUG, "Looped back packet addressed to myself\n");
1060	  }
1061	  continue;
1062	} else
1063	  LogPrintf(LogDEBUG, "Oops - forwarding packet addressed to myself\n");
1064      }
1065
1066      /*
1067       * Process on-demand dialup. Output packets are queued within tunnel
1068       * device until IPCP is opened.
1069       */
1070      if (LcpFsm.state <= ST_CLOSED && (mode & MODE_AUTO)) {
1071	pri = PacketCheck(rbuff, n, FL_DIAL);
1072	if (pri >= 0) {
1073#ifndef NOALIAS
1074	  if (mode & MODE_ALIAS) {
1075	    VarPacketAliasOut(rbuff, sizeof rbuff);
1076	    n = ntohs(((struct ip *) rbuff)->ip_len);
1077	  }
1078#endif
1079	  IpEnqueue(pri, rbuff, n);
1080	  dial_up = 1;	/* XXX */
1081	}
1082	continue;
1083      }
1084      pri = PacketCheck(rbuff, n, FL_OUT);
1085      if (pri >= 0) {
1086#ifndef NOALIAS
1087	if (mode & MODE_ALIAS) {
1088	  VarPacketAliasOut(rbuff, sizeof rbuff);
1089	  n = ntohs(((struct ip *) rbuff)->ip_len);
1090	}
1091#endif
1092	IpEnqueue(pri, rbuff, n);
1093      }
1094    }
1095  }
1096  LogPrintf(LogDEBUG, "Job (DoLoop) done.\n");
1097}
1098