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