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