main.c revision 29551
1230557Sjimharris/*
2230557Sjimharris *			User Process PPP
3230557Sjimharris *
4230557Sjimharris *	    Written by Toshiharu OHNO (tony-o@iij.ad.jp)
5230557Sjimharris *
6230557Sjimharris *   Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
7230557Sjimharris *
8230557Sjimharris * Redistribution and use in source and binary forms are permitted
9230557Sjimharris * provided that the above copyright notice and this paragraph are
10230557Sjimharris * duplicated in all such forms and that any documentation,
11230557Sjimharris * advertising materials, and other materials related to such
12230557Sjimharris * distribution and use acknowledge that the software was developed
13230557Sjimharris * by the Internet Initiative Japan, Inc.  The name of the
14230557Sjimharris * IIJ may not be used to endorse or promote products derived
15230557Sjimharris * from this software without specific prior written permission.
16230557Sjimharris * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17230557Sjimharris * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18230557Sjimharris * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19230557Sjimharris *
20230557Sjimharris * $Id: main.c,v 1.78 1997/09/16 23:15:13 brian Exp $
21230557Sjimharris *
22230557Sjimharris *	TODO:
23230557Sjimharris *		o Add commands for traffic summary, version display, etc.
24230557Sjimharris *		o Add signal handler for misc controls.
25230557Sjimharris */
26230557Sjimharris#include "fsm.h"
27230557Sjimharris#include <fcntl.h>
28230557Sjimharris#include <paths.h>
29230557Sjimharris#include <sys/time.h>
30230557Sjimharris#include <termios.h>
31230557Sjimharris#include <signal.h>
32230557Sjimharris#include <sys/wait.h>
33230557Sjimharris#include <errno.h>
34230557Sjimharris#include <netdb.h>
35230557Sjimharris#include <unistd.h>
36230557Sjimharris#include <sys/socket.h>
37230557Sjimharris#include <arpa/inet.h>
38230557Sjimharris#include <netinet/in_systm.h>
39230557Sjimharris#include <netinet/ip.h>
40230557Sjimharris#include <sysexits.h>
41230557Sjimharris#include "modem.h"
42230557Sjimharris#include "os.h"
43230557Sjimharris#include "hdlc.h"
44230557Sjimharris#include "ccp.h"
45230557Sjimharris#include "lcp.h"
46230557Sjimharris#include "ipcp.h"
47230557Sjimharris#include "loadalias.h"
48230557Sjimharris#include "vars.h"
49230557Sjimharris#include "auth.h"
50230557Sjimharris#include "filter.h"
51230557Sjimharris#include "systems.h"
52230557Sjimharris#include "ip.h"
53230557Sjimharris#include "sig.h"
54230557Sjimharris#include "server.h"
55230557Sjimharris#include "lcpproto.h"
56230557Sjimharris
57230557Sjimharris#ifndef O_NONBLOCK
58230557Sjimharris#ifdef O_NDELAY
59230557Sjimharris#define	O_NONBLOCK O_NDELAY
60230557Sjimharris#endif
61230557Sjimharris#endif
62230557Sjimharris
63230557Sjimharrisextern void VjInit(), AsyncInit();
64230557Sjimharrisextern void AsyncInput();
65230557Sjimharrisextern int SelectSystem();
66230557Sjimharris
67230557Sjimharrisextern void DecodeCommand(), Prompt();
68230557Sjimharrisextern int aft_cmd;
69230557Sjimharrisextern int IsInteractive();
70230557Sjimharrisstatic void DoLoop(void);
71230557Sjimharrisstatic void TerminalStop();
72230557Sjimharrisstatic char *ex_desc();
73230557Sjimharris
74230557Sjimharrisstatic struct termios oldtio;	/* Original tty mode */
75230557Sjimharrisstatic struct termios comtio;	/* Command level tty mode */
76230557Sjimharrisint TermMode;
77230557Sjimharrisstatic pid_t BGPid = 0;
78230557Sjimharrisstatic char pid_filename[MAXPATHLEN];
79230557Sjimharrisstatic char if_filename[MAXPATHLEN];
80230557Sjimharrisint tunno;
81230557Sjimharrisstatic int dial_up;
82230557Sjimharris
83230557Sjimharrisstatic void
84230557SjimharrisTtyInit(int DontWantInt)
85230557Sjimharris{
86230557Sjimharris  struct termios newtio;
87230557Sjimharris  int stat;
88230557Sjimharris
89230557Sjimharris  stat = fcntl(0, F_GETFL, 0);
90230557Sjimharris  if (stat > 0) {
91230557Sjimharris    stat |= O_NONBLOCK;
92230557Sjimharris    (void) fcntl(0, F_SETFL, stat);
93230557Sjimharris  }
94230557Sjimharris  newtio = oldtio;
95230557Sjimharris  newtio.c_lflag &= ~(ECHO | ISIG | ICANON);
96230557Sjimharris  newtio.c_iflag = 0;
97230557Sjimharris  newtio.c_oflag &= ~OPOST;
98230557Sjimharris  newtio.c_cc[VEOF] = _POSIX_VDISABLE;
99230557Sjimharris  if (DontWantInt)
100230557Sjimharris    newtio.c_cc[VINTR] = _POSIX_VDISABLE;
101230557Sjimharris  newtio.c_cc[VMIN] = 1;
102230557Sjimharris  newtio.c_cc[VTIME] = 0;
103230557Sjimharris  newtio.c_cflag |= CS8;
104230557Sjimharris  tcsetattr(0, TCSADRAIN, &newtio);
105230557Sjimharris  comtio = newtio;
106230557Sjimharris}
107230557Sjimharris
108230557Sjimharris/*
109230557Sjimharris *  Set tty into command mode. We allow canonical input and echo processing.
110230557Sjimharris */
111230557Sjimharrisvoid
112230557SjimharrisTtyCommandMode(int prompt)
113230557Sjimharris{
114230557Sjimharris  struct termios newtio;
115230557Sjimharris  int stat;
116230557Sjimharris
117230557Sjimharris  if (!(mode & MODE_INTER))
118230557Sjimharris    return;
119230557Sjimharris  tcgetattr(0, &newtio);
120230557Sjimharris  newtio.c_lflag |= (ECHO | ISIG | ICANON);
121230557Sjimharris  newtio.c_iflag = oldtio.c_iflag;
122230557Sjimharris  newtio.c_oflag |= OPOST;
123230557Sjimharris  tcsetattr(0, TCSADRAIN, &newtio);
124230557Sjimharris  stat = fcntl(0, F_GETFL, 0);
125230557Sjimharris  if (stat > 0) {
126230557Sjimharris    stat |= O_NONBLOCK;
127230557Sjimharris    (void) fcntl(0, F_SETFL, stat);
128230557Sjimharris  }
129230557Sjimharris  TermMode = 0;
130230557Sjimharris  if (prompt)
131230557Sjimharris    Prompt();
132230557Sjimharris}
133230557Sjimharris
134230557Sjimharris/*
135230557Sjimharris * Set tty into terminal mode which is used while we invoke term command.
136230557Sjimharris */
137230557Sjimharrisvoid
138230557SjimharrisTtyTermMode()
139230557Sjimharris{
140230557Sjimharris  int stat;
141230557Sjimharris
142230557Sjimharris  tcsetattr(0, TCSADRAIN, &comtio);
143230557Sjimharris  stat = fcntl(0, F_GETFL, 0);
144230557Sjimharris  if (stat > 0) {
145230557Sjimharris    stat &= ~O_NONBLOCK;
146230557Sjimharris    (void) fcntl(0, F_SETFL, stat);
147230557Sjimharris  }
148230557Sjimharris  TermMode = 1;
149230557Sjimharris}
150230557Sjimharris
151230557Sjimharrisvoid
152230557SjimharrisTtyOldMode()
153230557Sjimharris{
154230557Sjimharris  int stat;
155230557Sjimharris
156230557Sjimharris  stat = fcntl(0, F_GETFL, 0);
157230557Sjimharris  if (stat > 0) {
158230557Sjimharris    stat &= ~O_NONBLOCK;
159230557Sjimharris    (void) fcntl(0, F_SETFL, stat);
160230557Sjimharris  }
161230557Sjimharris  tcsetattr(0, TCSANOW, &oldtio);
162230557Sjimharris}
163230557Sjimharris
164230557Sjimharrisvoid
165230557SjimharrisCleanup(int excode)
166230557Sjimharris{
167230557Sjimharris  OsLinkdown();
168230557Sjimharris  OsCloseLink(1);
169230557Sjimharris  sleep(1);
170230557Sjimharris  if (mode & MODE_AUTO)
171230557Sjimharris    DeleteIfRoutes(1);
172230557Sjimharris  (void) unlink(pid_filename);
173230557Sjimharris  (void) unlink(if_filename);
174230557Sjimharris  OsInterfaceDown(1);
175230557Sjimharris  if (mode & MODE_BACKGROUND && BGFiledes[1] != -1) {
176230557Sjimharris    char c = EX_ERRDEAD;
177230557Sjimharris
178230557Sjimharris    if (write(BGFiledes[1], &c, 1) == 1)
179230557Sjimharris      LogPrintf(LogPHASE, "Parent notified of failure.\n");
180230557Sjimharris    else
181230557Sjimharris      LogPrintf(LogPHASE, "Failed to notify parent of failure.\n");
182230557Sjimharris    close(BGFiledes[1]);
183230557Sjimharris  }
184230557Sjimharris  LogPrintf(LogPHASE, "PPP Terminated (%s).\n", ex_desc(excode));
185230557Sjimharris  LogClose();
186230557Sjimharris  ServerClose();
187230557Sjimharris  TtyOldMode();
188230557Sjimharris
189230557Sjimharris  exit(excode);
190230557Sjimharris}
191230557Sjimharris
192230557Sjimharrisstatic void
193230557SjimharrisCloseConnection(int signo)
194230557Sjimharris{
195230557Sjimharris  /* NOTE, these are manual, we've done a setsid() */
196230557Sjimharris  LogPrintf(LogPHASE, "Caught signal %d, abort connection\n", signo);
197230557Sjimharris  reconnectState = RECON_FALSE;
198230557Sjimharris  reconnectCount = 0;
199230557Sjimharris  DownConnection();
200230557Sjimharris  dial_up = FALSE;
201230557Sjimharris}
202230557Sjimharris
203230557Sjimharrisstatic void
204230557SjimharrisCloseSession(int signo)
205230557Sjimharris{
206230557Sjimharris  if (BGPid) {
207230557Sjimharris    kill(BGPid, SIGINT);
208230557Sjimharris    exit(EX_TERM);
209230557Sjimharris  }
210230557Sjimharris  LogPrintf(LogPHASE, "Signal %d, terminate.\n", signo);
211230557Sjimharris  reconnect(RECON_FALSE);
212230557Sjimharris  LcpClose();
213230557Sjimharris  Cleanup(EX_TERM);
214230557Sjimharris}
215230557Sjimharris
216230557Sjimharrisstatic void
217230557SjimharrisTerminalCont()
218230557Sjimharris{
219230557Sjimharris  pending_signal(SIGCONT, SIG_DFL);
220230557Sjimharris  pending_signal(SIGTSTP, TerminalStop);
221230557Sjimharris  TtyCommandMode(getpgrp() == tcgetpgrp(0));
222230557Sjimharris}
223230557Sjimharris
224230557Sjimharrisstatic void
225230557SjimharrisTerminalStop(int signo)
226230557Sjimharris{
227230557Sjimharris  pending_signal(SIGCONT, TerminalCont);
228230557Sjimharris  TtyOldMode();
229230557Sjimharris  pending_signal(SIGTSTP, SIG_DFL);
230230557Sjimharris  kill(getpid(), signo);
231230557Sjimharris}
232230557Sjimharris
233230557Sjimharrisstatic void
234230557SjimharrisSetUpServer(int signo)
235230557Sjimharris{
236230557Sjimharris  int res;
237230557Sjimharris
238230557Sjimharris  if ((res = ServerTcpOpen(SERVER_PORT + tunno)) != 0)
239230557Sjimharris    LogPrintf(LogERROR, "SIGUSR1: Failed %d to open port %d\n",
240230557Sjimharris	      res, SERVER_PORT + tunno);
241230557Sjimharris}
242230557Sjimharris
243230557Sjimharrisstatic char *
244230557Sjimharrisex_desc(int ex)
245230557Sjimharris{
246230557Sjimharris  static char num[12];
247230557Sjimharris  static char *desc[] = {"normal", "start", "sock",
248230557Sjimharris    "modem", "dial", "dead", "done", "reboot", "errdead",
249230557Sjimharris  "hangup", "term", "nodial", "nologin"};
250230557Sjimharris
251230557Sjimharris  if (ex >= 0 && ex < sizeof(desc) / sizeof(*desc))
252230557Sjimharris    return desc[ex];
253230557Sjimharris  snprintf(num, sizeof num, "%d", ex);
254230557Sjimharris  return num;
255230557Sjimharris}
256230557Sjimharris
257230557Sjimharrisvoid
258230557SjimharrisUsage()
259230557Sjimharris{
260230557Sjimharris  fprintf(stderr,
261230557Sjimharris	  "Usage: ppp [-auto | -background | -direct | -dedicated | -ddial ] [ -alias ] [system]\n");
262230557Sjimharris  exit(EX_START);
263230557Sjimharris}
264230557Sjimharris
265230557Sjimharrisvoid
266230557SjimharrisProcessArgs(int argc, char **argv)
267230557Sjimharris{
268230557Sjimharris  int optc;
269230557Sjimharris  char *cp;
270230557Sjimharris
271230557Sjimharris  optc = 0;
272230557Sjimharris  while (argc > 0 && **argv == '-') {
273230557Sjimharris    cp = *argv + 1;
274230557Sjimharris    if (strcmp(cp, "auto") == 0)
275230557Sjimharris      mode |= MODE_AUTO;
276230557Sjimharris    else if (strcmp(cp, "background") == 0)
277230557Sjimharris      mode |= MODE_BACKGROUND | MODE_AUTO;
278230557Sjimharris    else if (strcmp(cp, "direct") == 0)
279230557Sjimharris      mode |= MODE_DIRECT;
280230557Sjimharris    else if (strcmp(cp, "dedicated") == 0)
281230557Sjimharris      mode |= MODE_DEDICATED;
282230557Sjimharris    else if (strcmp(cp, "ddial") == 0)
283230557Sjimharris      mode |= MODE_DDIAL | MODE_AUTO;
284230557Sjimharris    else if (strcmp(cp, "alias") == 0) {
285230557Sjimharris      if (loadAliasHandlers(&VarAliasHandlers) == 0)
286230557Sjimharris	mode |= MODE_ALIAS;
287230557Sjimharris      else
288230557Sjimharris	LogPrintf(LogWARN, "Cannot load alias library\n");
289230557Sjimharris      optc--;			/* this option isn't exclusive */
290230557Sjimharris    } else
291230557Sjimharris      Usage();
292230557Sjimharris    optc++;
293230557Sjimharris    argv++;
294230557Sjimharris    argc--;
295230557Sjimharris  }
296230557Sjimharris  if (argc > 1) {
297230557Sjimharris    fprintf(stderr, "specify only one system label.\n");
298230557Sjimharris    exit(EX_START);
299230557Sjimharris  }
300230557Sjimharris  if (argc == 1)
301230557Sjimharris    dstsystem = *argv;
302230557Sjimharris
303230557Sjimharris  if (optc > 1) {
304230557Sjimharris    fprintf(stderr, "specify only one mode.\n");
305230557Sjimharris    exit(EX_START);
306230557Sjimharris  }
307230557Sjimharris}
308230557Sjimharris
309230557Sjimharrisstatic void
310230557SjimharrisGreetings()
311230557Sjimharris{
312230557Sjimharris  if (VarTerm) {
313230557Sjimharris    fprintf(VarTerm, "User Process PPP. Written by Toshiharu OHNO.\n");
314230557Sjimharris    fflush(VarTerm);
315230557Sjimharris  }
316230557Sjimharris}
317230557Sjimharris
318230557Sjimharrisint
319230557Sjimharrismain(int argc, char **argv)
320230557Sjimharris{
321230557Sjimharris  FILE *lockfile;
322230557Sjimharris  char *name;
323230557Sjimharris
324230557Sjimharris  VarTerm = 0;
325230557Sjimharris  name = rindex(argv[0], '/');
326230557Sjimharris  LogOpen(name ? name + 1 : argv[0]);
327230557Sjimharris
328230557Sjimharris  argc--;
329230557Sjimharris  argv++;
330230557Sjimharris  mode = MODE_INTER;		/* default operation is interactive mode */
331230557Sjimharris  netfd = modem = tun_in = -1;
332230557Sjimharris  server = -2;
333230557Sjimharris  ProcessArgs(argc, argv);
334230557Sjimharris  if (!(mode & MODE_DIRECT)) {
335230557Sjimharris    if (getuid() != 0) {
336230557Sjimharris      fprintf(stderr, "You may only run ppp in client mode as user id 0\n");
337230557Sjimharris      LogClose();
338230557Sjimharris      return EX_NOPERM;
339230557Sjimharris    }
340230557Sjimharris    VarTerm = stdout;
341230557Sjimharris  }
342230557Sjimharris  Greetings();
343230557Sjimharris  GetUid();
344230557Sjimharris  IpcpDefAddress();
345230557Sjimharris  LocalAuthInit();
346230557Sjimharris
347230557Sjimharris  if (SelectSystem("default", CONFFILE) < 0 && VarTerm)
348230557Sjimharris    fprintf(VarTerm, "Warning: No default entry is given in config file.\n");
349230557Sjimharris
350230557Sjimharris  if (OpenTunnel(&tunno) < 0) {
351230557Sjimharris    LogPrintf(LogWARN, "open_tun: %s\n", strerror(errno));
352230557Sjimharris    return EX_START;
353230557Sjimharris  }
354230557Sjimharris  if (mode & (MODE_AUTO | MODE_DIRECT | MODE_DEDICATED))
355230557Sjimharris    mode &= ~MODE_INTER;
356230557Sjimharris  if (mode & MODE_INTER) {
357230557Sjimharris    fprintf(VarTerm, "Interactive mode\n");
358230557Sjimharris    netfd = STDOUT_FILENO;
359230557Sjimharris  } else if (mode & MODE_AUTO) {
360230557Sjimharris    fprintf(VarTerm, "Automatic Dialer mode\n");
361230557Sjimharris    if (dstsystem == NULL) {
362230557Sjimharris      if (VarTerm)
363230557Sjimharris	fprintf(VarTerm, "Destination system must be specified in"
364230557Sjimharris		" auto, background or ddial mode.\n");
365230557Sjimharris      return EX_START;
366230557Sjimharris    }
367230557Sjimharris  }
368230557Sjimharris  tcgetattr(0, &oldtio);	/* Save original tty mode */
369230557Sjimharris
370230557Sjimharris  pending_signal(SIGHUP, CloseSession);
371230557Sjimharris  pending_signal(SIGTERM, CloseSession);
372230557Sjimharris  pending_signal(SIGINT, CloseConnection);
373230557Sjimharris  pending_signal(SIGQUIT, CloseSession);
374230557Sjimharris#ifdef SIGPIPE
375230557Sjimharris  signal(SIGPIPE, SIG_IGN);
376230557Sjimharris#endif
377230557Sjimharris#ifdef SIGALRM
378230557Sjimharris  pending_signal(SIGALRM, SIG_IGN);
379230557Sjimharris#endif
380230557Sjimharris  if (mode & MODE_INTER) {
381230557Sjimharris#ifdef SIGTSTP
382230557Sjimharris    pending_signal(SIGTSTP, TerminalStop);
383230557Sjimharris#endif
384230557Sjimharris#ifdef SIGTTIN
385230557Sjimharris    pending_signal(SIGTTIN, TerminalStop);
386230557Sjimharris#endif
387230557Sjimharris#ifdef SIGTTOU
388230557Sjimharris    pending_signal(SIGTTOU, SIG_IGN);
389230557Sjimharris#endif
390230557Sjimharris  }
391230557Sjimharris#ifdef SIGUSR1
392230557Sjimharris  if (mode != MODE_INTER)
393230557Sjimharris    pending_signal(SIGUSR1, SetUpServer);
394230557Sjimharris#endif
395230557Sjimharris
396230557Sjimharris  if (dstsystem) {
397230557Sjimharris    if (SelectSystem(dstsystem, CONFFILE) < 0) {
398230557Sjimharris      LogPrintf(LogWARN, "Destination system not found in conf file.\n");
399230557Sjimharris      Cleanup(EX_START);
400230557Sjimharris    }
401230557Sjimharris    if ((mode & MODE_AUTO) && DefHisAddress.ipaddr.s_addr == INADDR_ANY) {
402230557Sjimharris      LogPrintf(LogWARN, "Must specify dstaddr with"
403230557Sjimharris		" auto, background or ddial mode.\n");
404230557Sjimharris      Cleanup(EX_START);
405230557Sjimharris    }
406230557Sjimharris  }
407230557Sjimharris
408230557Sjimharris  if (!(mode & MODE_INTER)) {
409230557Sjimharris    if (mode & MODE_BACKGROUND) {
410230557Sjimharris      if (pipe(BGFiledes)) {
411230557Sjimharris	LogPrintf(LogERROR, "pipe: %s\n", strerror(errno));
412230557Sjimharris	Cleanup(EX_SOCK);
413230557Sjimharris      }
414230557Sjimharris    }
415230557Sjimharris    /* Create server socket and listen. */
416230557Sjimharris    if (server == -2)
417230557Sjimharris      ServerTcpOpen(SERVER_PORT + tunno);
418230557Sjimharris
419    if (!(mode & MODE_DIRECT)) {
420      pid_t bgpid;
421
422      bgpid = fork();
423      if (bgpid == -1) {
424	LogPrintf(LogERROR, "fork: %s\n", strerror(errno));
425	Cleanup(EX_SOCK);
426      }
427      if (bgpid) {
428	char c = EX_NORMAL;
429
430	if (mode & MODE_BACKGROUND) {
431	  /* Wait for our child to close its pipe before we exit. */
432	  BGPid = bgpid;
433	  close(BGFiledes[1]);
434	  if (read(BGFiledes[0], &c, 1) != 1) {
435	    fprintf(VarTerm, "Child exit, no status.\n");
436	    LogPrintf(LogPHASE, "Parent: Child exit, no status.\n");
437	  } else if (c == EX_NORMAL) {
438	    fprintf(VarTerm, "PPP enabled.\n");
439	    LogPrintf(LogPHASE, "Parent: PPP enabled.\n");
440	  } else {
441	    fprintf(VarTerm, "Child failed (%s).\n", ex_desc((int) c));
442	    LogPrintf(LogPHASE, "Parent: Child failed (%s).\n",
443		      ex_desc((int) c));
444	  }
445	  close(BGFiledes[0]);
446	}
447	return c;
448      } else if (mode & MODE_BACKGROUND)
449	close(BGFiledes[0]);
450    }
451    snprintf(pid_filename, sizeof(pid_filename), "%stun%d.pid",
452	     _PATH_VARRUN, tunno);
453    (void) unlink(pid_filename);
454
455    if ((lockfile = fopen(pid_filename, "w")) != NULL) {
456      fprintf(lockfile, "%d\n", (int) getpid());
457      fclose(lockfile);
458    } else
459      LogPrintf(LogALERT, "Warning: Can't create %s: %s\n",
460		pid_filename, strerror(errno));
461
462    snprintf(if_filename, sizeof if_filename, "%s%s.if",
463	     _PATH_VARRUN, VarBaseDevice);
464    (void) unlink(if_filename);
465
466    if ((lockfile = fopen(if_filename, "w")) != NULL) {
467      fprintf(lockfile, "tun%d\n", tunno);
468      fclose(lockfile);
469    } else
470      LogPrintf(LogALERT, "Warning: Can't create %s: %s\n",
471		if_filename, strerror(errno));
472
473    VarTerm = 0;		/* We know it's currently stdout */
474    close(1);
475    close(2);
476
477#ifdef DOTTYINIT
478    if (mode & (MODE_DIRECT | MODE_DEDICATED))
479#else
480    if (mode & MODE_DIRECT)
481#endif
482      TtyInit(1);
483    else {
484      setsid();
485      close(0);
486    }
487  } else {
488    TtyInit(0);
489    TtyCommandMode(1);
490  }
491  LogPrintf(LogPHASE, "PPP Started.\n");
492
493
494  do
495    DoLoop();
496  while (mode & MODE_DEDICATED);
497
498  Cleanup(EX_DONE);
499  return 0;
500}
501
502/*
503 *  Turn into packet mode, where we speak PPP.
504 */
505void
506PacketMode()
507{
508  if (RawModem(modem) < 0) {
509    LogPrintf(LogWARN, "PacketMode: Not connected.\n");
510    return;
511  }
512  AsyncInit();
513  VjInit();
514  LcpInit();
515  IpcpInit();
516  CcpInit();
517  LcpUp();
518
519  LcpOpen(VarOpenMode);
520  if ((mode & (MODE_INTER | MODE_AUTO)) == MODE_INTER) {
521    TtyCommandMode(1);
522    if (VarTerm) {
523      fprintf(VarTerm, "Packet mode.\n");
524      aft_cmd = 1;
525    }
526  }
527}
528
529static void
530ShowHelp()
531{
532  fprintf(stderr, "The following commands are available:\r\n");
533  fprintf(stderr, " ~p\tEnter Packet mode\r\n");
534  fprintf(stderr, " ~-\tDecrease log level\r\n");
535  fprintf(stderr, " ~+\tIncrease log level\r\n");
536  fprintf(stderr, " ~t\tShow timers (only in \"log debug\" mode)\r\n");
537  fprintf(stderr, " ~m\tShow memory map (only in \"log debug\" mode)\r\n");
538  fprintf(stderr, " ~.\tTerminate program\r\n");
539  fprintf(stderr, " ~?\tThis help\r\n");
540}
541
542static void
543ReadTty()
544{
545  int n;
546  char ch;
547  static int ttystate;
548  FILE *oVarTerm;
549
550#define MAXLINESIZE 200
551  char linebuff[MAXLINESIZE];
552
553  LogPrintf(LogDEBUG, "termode = %d, netfd = %d, mode = %d\n",
554	    TermMode, netfd, mode);
555  if (!TermMode) {
556    n = read(netfd, linebuff, sizeof(linebuff) - 1);
557    if (n > 0) {
558      aft_cmd = 1;
559      DecodeCommand(linebuff, n, 1);
560    } else {
561      LogPrintf(LogPHASE, "client connection closed.\n");
562      VarLocalAuth = LOCAL_NO_AUTH;
563      mode &= ~MODE_INTER;
564      oVarTerm = VarTerm;
565      VarTerm = 0;
566      if (oVarTerm && oVarTerm != stdout)
567	fclose(oVarTerm);
568      close(netfd);
569      netfd = -1;
570    }
571    return;
572  }
573
574  /*
575   * We are in terminal mode, decode special sequences
576   */
577  n = read(fileno(VarTerm), &ch, 1);
578  LogPrintf(LogDEBUG, "Got %d bytes (reading from the terminal)\n", n);
579
580  if (n > 0) {
581    switch (ttystate) {
582    case 0:
583      if (ch == '~')
584	ttystate++;
585      else
586	write(modem, &ch, n);
587      break;
588    case 1:
589      switch (ch) {
590      case '?':
591	ShowHelp();
592	break;
593      case 'p':
594
595	/*
596	 * XXX: Should check carrier.
597	 */
598	if (LcpFsm.state <= ST_CLOSED) {
599	  VarOpenMode = OPEN_ACTIVE;
600	  PacketMode();
601	}
602	break;
603      case '.':
604	TermMode = 1;
605	aft_cmd = 1;
606	TtyCommandMode(1);
607	break;
608      case 't':
609	if (LogIsKept(LogDEBUG)) {
610	  ShowTimers();
611	  break;
612	}
613      case 'm':
614	if (LogIsKept(LogDEBUG)) {
615	  ShowMemMap();
616	  break;
617	}
618      default:
619	if (write(modem, &ch, n) < 0)
620	  LogPrintf(LogERROR, "error writing to modem.\n");
621	break;
622      }
623      ttystate = 0;
624      break;
625    }
626  }
627}
628
629
630/*
631 *  Here, we'll try to detect HDLC frame
632 */
633
634static char *FrameHeaders[] = {
635  "\176\377\003\300\041",
636  "\176\377\175\043\300\041",
637  "\176\177\175\043\100\041",
638  "\176\175\337\175\043\300\041",
639  "\176\175\137\175\043\100\041",
640  NULL,
641};
642
643u_char *
644HdlcDetect(u_char * cp, int n)
645{
646  char *ptr, *fp, **hp;
647
648  cp[n] = '\0';			/* be sure to null terminated */
649  ptr = NULL;
650  for (hp = FrameHeaders; *hp; hp++) {
651    fp = *hp;
652    if (DEV_IS_SYNC)
653      fp++;
654    ptr = strstr((char *) cp, fp);
655    if (ptr)
656      break;
657  }
658  return ((u_char *) ptr);
659}
660
661static struct pppTimer RedialTimer;
662
663static void
664RedialTimeout()
665{
666  StopTimer(&RedialTimer);
667  LogPrintf(LogPHASE, "Redialing timer expired.\n");
668}
669
670static void
671StartRedialTimer(int Timeout)
672{
673  StopTimer(&RedialTimer);
674
675  if (Timeout) {
676    RedialTimer.state = TIMER_STOPPED;
677
678    if (Timeout > 0)
679      RedialTimer.load = Timeout * SECTICKS;
680    else
681      RedialTimer.load = (random() % REDIAL_PERIOD) * SECTICKS;
682
683    LogPrintf(LogPHASE, "Enter pause (%d) for redialing.\n",
684	      RedialTimer.load / SECTICKS);
685
686    RedialTimer.func = RedialTimeout;
687    StartTimer(&RedialTimer);
688  }
689}
690
691
692static void
693DoLoop()
694{
695  fd_set rfds, wfds, efds;
696  int pri, i, n, wfd, nfds;
697  struct sockaddr_in hisaddr;
698  struct timeval timeout, *tp;
699  int ssize = sizeof(hisaddr);
700  u_char *cp;
701  u_char rbuff[MAX_MRU];
702  int tries;
703  int qlen;
704  int res;
705  pid_t pgroup;
706
707  pgroup = getpgrp();
708
709  if (mode & MODE_DIRECT) {
710    LogPrintf(LogDEBUG, "Opening modem\n");
711    if (OpenModem(mode) < 0)
712      return;
713    LogPrintf(LogPHASE, "Packet mode enabled\n");
714    close(0);
715    PacketMode();
716  } else if (mode & MODE_DEDICATED) {
717    if (modem < 0)
718      while (OpenModem(mode) < 0)
719	sleep(VarReconnectTimer);
720  }
721  fflush(VarTerm);
722
723  timeout.tv_sec = 0;
724  timeout.tv_usec = 0;
725  reconnectState = RECON_UNKNOWN;
726
727  if (mode & MODE_BACKGROUND)
728    dial_up = TRUE;		/* Bring the line up */
729  else
730    dial_up = FALSE;		/* XXXX */
731  tries = 0;
732  for (;;) {
733    nfds = 0;
734    FD_ZERO(&rfds);
735    FD_ZERO(&wfds);
736    FD_ZERO(&efds);
737
738    /*
739     * If the link is down and we're in DDIAL mode, bring it back up.
740     */
741    if (mode & MODE_DDIAL && LcpFsm.state <= ST_CLOSED)
742      dial_up = TRUE;
743
744    /*
745     * If we lost carrier and want to re-establish the connection due to the
746     * "set reconnect" value, we'd better bring the line back up.
747     */
748    if (LcpFsm.state <= ST_CLOSED) {
749      if (dial_up != TRUE && reconnectState == RECON_TRUE) {
750	if (++reconnectCount <= VarReconnectTries) {
751	  LogPrintf(LogPHASE, "Connection lost, re-establish (%d/%d)\n",
752		    reconnectCount, VarReconnectTries);
753	  StartRedialTimer(VarReconnectTimer);
754	  dial_up = TRUE;
755	} else {
756	  if (VarReconnectTries)
757	    LogPrintf(LogPHASE, "Connection lost, maximum (%d) times\n",
758		      VarReconnectTries);
759	  reconnectCount = 0;
760	  if (mode & MODE_BACKGROUND)
761	    Cleanup(EX_DEAD);
762	}
763	reconnectState = RECON_ENVOKED;
764      }
765    }
766
767    /*
768     * If Ip packet for output is enqueued and require dial up, Just do it!
769     */
770    if (dial_up && RedialTimer.state != TIMER_RUNNING) {
771      LogPrintf(LogDEBUG, "going to dial: modem = %d\n", modem);
772      if (OpenModem(mode) < 0) {
773	tries++;
774	if (!(mode & MODE_DDIAL) && VarDialTries)
775	  LogPrintf(LogCHAT, "Failed to open modem (attempt %u of %d)\n",
776		    tries, VarDialTries);
777	else
778	  LogPrintf(LogCHAT, "Failed to open modem (attempt %u)\n", tries);
779
780	if (!(mode & MODE_DDIAL) && VarDialTries && tries >= VarDialTries) {
781	  if (mode & MODE_BACKGROUND)
782	    Cleanup(EX_DIAL);	/* Can't get the modem */
783	  dial_up = FALSE;
784	  reconnectState = RECON_UNKNOWN;
785	  reconnectCount = 0;
786	  tries = 0;
787	} else
788	  StartRedialTimer(VarRedialTimeout);
789      } else {
790	tries++;		/* Tries are per number, not per list of
791				 * numbers. */
792	if (!(mode & MODE_DDIAL) && VarDialTries)
793	  LogPrintf(LogCHAT, "Dial attempt %u of %d\n", tries, VarDialTries);
794	else
795	  LogPrintf(LogCHAT, "Dial attempt %u\n", tries);
796
797	if ((res = DialModem()) == EX_DONE) {
798	  sleep(1);		/* little pause to allow peer starts */
799	  ModemTimeout();
800	  PacketMode();
801	  dial_up = FALSE;
802	  reconnectState = RECON_UNKNOWN;
803	  tries = 0;
804	} else {
805	  CloseModem();
806	  if (mode & MODE_BACKGROUND) {
807	    if (VarNextPhone == NULL || res == EX_SIG)
808	      Cleanup(EX_DIAL);	/* Tried all numbers - no luck */
809	    else
810	      /* Try all numbers in background mode */
811	      StartRedialTimer(VarRedialNextTimeout);
812	  } else if (!(mode & MODE_DDIAL) &&
813		     ((VarDialTries && tries >= VarDialTries) ||
814		      res == EX_SIG)) {
815	    /* I give up !  Can't get through :( */
816	    StartRedialTimer(VarRedialTimeout);
817	    dial_up = FALSE;
818	    reconnectState = RECON_UNKNOWN;
819	    reconnectCount = 0;
820	    tries = 0;
821	  } else if (VarNextPhone == NULL)
822	    /* Dial failed. Keep quite during redial wait period. */
823	    StartRedialTimer(VarRedialTimeout);
824	  else
825	    StartRedialTimer(VarRedialNextTimeout);
826	}
827      }
828    }
829    qlen = ModemQlen();
830
831    if (qlen == 0) {
832      IpStartOutput();
833      qlen = ModemQlen();
834    }
835    if (modem >= 0) {
836      if (modem + 1 > nfds)
837	nfds = modem + 1;
838      FD_SET(modem, &rfds);
839      FD_SET(modem, &efds);
840      if (qlen > 0) {
841	FD_SET(modem, &wfds);
842      }
843    }
844    if (server >= 0) {
845      if (server + 1 > nfds)
846	nfds = server + 1;
847      FD_SET(server, &rfds);
848    }
849
850    /*
851     * *** IMPORTANT ***
852     *
853     * CPU is serviced every TICKUNIT micro seconds. This value must be chosen
854     * with great care. If this values is too big, it results loss of
855     * characters from modem and poor responce. If this values is too small,
856     * ppp process eats many CPU time.
857     */
858#ifndef SIGALRM
859    usleep(TICKUNIT);
860    TimerService();
861#else
862    handle_signals();
863#endif
864
865    /* If there are aren't many packets queued, look for some more. */
866    if (qlen < 20 && tun_in >= 0) {
867      if (tun_in + 1 > nfds)
868	nfds = tun_in + 1;
869      FD_SET(tun_in, &rfds);
870    }
871    if (netfd >= 0) {
872      if (netfd + 1 > nfds)
873	nfds = netfd + 1;
874      FD_SET(netfd, &rfds);
875      FD_SET(netfd, &efds);
876    }
877#ifndef SIGALRM
878
879    /*
880     * Normally, select() will not block because modem is writable. In AUTO
881     * mode, select will block until we find packet from tun
882     */
883    tp = (RedialTimer.state == TIMER_RUNNING) ? &timeout : NULL;
884    i = select(nfds, &rfds, &wfds, &efds, tp);
885#else
886
887    /*
888     * When SIGALRM timer is running, a select function will be return -1 and
889     * EINTR after a Time Service signal hundler is done.  If the redial
890     * timer is not running and we are trying to dial, poll with a 0 value
891     * timer.
892     */
893    tp = (dial_up && RedialTimer.state != TIMER_RUNNING) ? &timeout : NULL;
894    i = select(nfds, &rfds, &wfds, &efds, tp);
895#endif
896
897    if (i == 0) {
898      continue;
899    }
900    if (i < 0) {
901      if (errno == EINTR) {
902	handle_signals();
903	continue;
904      }
905      LogPrintf(LogERROR, "DoLoop: select(): %s\n", strerror(errno));
906      break;
907    }
908    if ((netfd >= 0 && FD_ISSET(netfd, &efds)) || (modem >= 0 && FD_ISSET(modem, &efds))) {
909      LogPrintf(LogALERT, "Exception detected.\n");
910      break;
911    }
912    if (server >= 0 && FD_ISSET(server, &rfds)) {
913      LogPrintf(LogPHASE, "connected to client.\n");
914      wfd = accept(server, (struct sockaddr *) & hisaddr, &ssize);
915      if (wfd < 0) {
916	LogPrintf(LogERROR, "DoLoop: accept(): %s\n", strerror(errno));
917	continue;
918      }
919      if (netfd >= 0) {
920	write(wfd, "already in use.\n", 16);
921	close(wfd);
922	continue;
923      } else
924	netfd = wfd;
925      VarTerm = fdopen(netfd, "a+");
926      mode |= MODE_INTER;
927      Greetings();
928      (void) IsInteractive();
929      Prompt();
930    }
931    if ((mode & MODE_INTER) && (netfd >= 0 && FD_ISSET(netfd, &rfds)) &&
932	((mode & MODE_AUTO) || pgroup == tcgetpgrp(0))) {
933      /* something to read from tty */
934      ReadTty();
935    }
936    if (modem >= 0) {
937      if (FD_ISSET(modem, &wfds)) {	/* ready to write into modem */
938	ModemStartOutput(modem);
939      }
940      if (FD_ISSET(modem, &rfds)) {	/* something to read from modem */
941	if (LcpFsm.state <= ST_CLOSED)
942	  usleep(10000);
943	n = read(modem, rbuff, sizeof(rbuff));
944	if ((mode & MODE_DIRECT) && n <= 0) {
945	  DownConnection();
946	} else
947	  LogDumpBuff(LogASYNC, "ReadFromModem", rbuff, n);
948
949	if (LcpFsm.state <= ST_CLOSED) {
950
951	  /*
952	   * In dedicated mode, we just discard input until LCP is started.
953	   */
954	  if (!(mode & MODE_DEDICATED)) {
955	    cp = HdlcDetect(rbuff, n);
956	    if (cp) {
957
958	      /*
959	       * LCP packet is detected. Turn ourselves into packet mode.
960	       */
961	      if (cp != rbuff) {
962		write(modem, rbuff, cp - rbuff);
963		write(modem, "\r\n", 2);
964	      }
965	      PacketMode();
966	    } else
967	      write(fileno(VarTerm), rbuff, n);
968	  }
969	} else {
970	  if (n > 0)
971	    AsyncInput(rbuff, n);
972	}
973      }
974    }
975    if (tun_in >= 0 && FD_ISSET(tun_in, &rfds)) {	/* something to read
976							 * from tun */
977      n = read(tun_in, rbuff, sizeof(rbuff));
978      if (n < 0) {
979	LogPrintf(LogERROR, "read from tun: %s\n", strerror(errno));
980	continue;
981      }
982      if (((struct ip *) rbuff)->ip_dst.s_addr == IpcpInfo.want_ipaddr.s_addr) {
983	/* we've been asked to send something addressed *to* us :( */
984	if (VarLoopback) {
985	  pri = PacketCheck(rbuff, n, FL_IN);
986	  if (pri >= 0) {
987	    struct mbuf *bp;
988
989	    if (mode & MODE_ALIAS) {
990	      VarPacketAliasIn(rbuff, sizeof rbuff);
991	      n = ntohs(((struct ip *) rbuff)->ip_len);
992	    }
993	    bp = mballoc(n, MB_IPIN);
994	    bcopy(rbuff, MBUF_CTOP(bp), n);
995	    IpInput(bp);
996	    LogPrintf(LogDEBUG, "Looped back packet addressed to myself\n");
997	  }
998	  continue;
999	} else
1000	  LogPrintf(LogDEBUG, "Oops - forwarding packet addressed to myself\n");
1001      }
1002
1003      /*
1004       * Process on-demand dialup. Output packets are queued within tunnel
1005       * device until IPCP is opened.
1006       */
1007      if (LcpFsm.state <= ST_CLOSED && (mode & MODE_AUTO)) {
1008	pri = PacketCheck(rbuff, n, FL_DIAL);
1009	if (pri >= 0) {
1010	  if (mode & MODE_ALIAS) {
1011	    VarPacketAliasOut(rbuff, sizeof rbuff);
1012	    n = ntohs(((struct ip *) rbuff)->ip_len);
1013	  }
1014	  IpEnqueue(pri, rbuff, n);
1015	  dial_up = TRUE;	/* XXX */
1016	}
1017	continue;
1018      }
1019      pri = PacketCheck(rbuff, n, FL_OUT);
1020      if (pri >= 0) {
1021	if (mode & MODE_ALIAS) {
1022	  VarPacketAliasOut(rbuff, sizeof rbuff);
1023	  n = ntohs(((struct ip *) rbuff)->ip_len);
1024	}
1025	IpEnqueue(pri, rbuff, n);
1026      }
1027    }
1028  }
1029  LogPrintf(LogDEBUG, "Job (DoLoop) done.\n");
1030}
1031