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