main.c revision 31061
16059Samurai/* 26059Samurai * User Process PPP 36059Samurai * 46059Samurai * Written by Toshiharu OHNO (tony-o@iij.ad.jp) 56059Samurai * 66059Samurai * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd. 76059Samurai * 86059Samurai * Redistribution and use in source and binary forms are permitted 96059Samurai * provided that the above copyright notice and this paragraph are 106059Samurai * duplicated in all such forms and that any documentation, 116059Samurai * advertising materials, and other materials related to such 126059Samurai * distribution and use acknowledge that the software was developed 136059Samurai * by the Internet Initiative Japan, Inc. The name of the 146059Samurai * IIJ may not be used to endorse or promote products derived 156059Samurai * from this software without specific prior written permission. 166059Samurai * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 176059Samurai * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 186059Samurai * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 196059Samurai * 2031061Sbrian * $Id: main.c,v 1.88 1997/11/08 00:28:09 brian Exp $ 218857Srgrimes * 226059Samurai * TODO: 236059Samurai * o Add commands for traffic summary, version display, etc. 246059Samurai * o Add signal handler for misc controls. 256059Samurai */ 2630715Sbrian#include <sys/param.h> 2730715Sbrian#include <sys/socket.h> 2830715Sbrian#include <netinet/in.h> 2930715Sbrian#include <netinet/in_systm.h> 3030715Sbrian#include <netinet/ip.h> 3130715Sbrian#include <arpa/inet.h> 3230715Sbrian#include <netdb.h> 3330715Sbrian 3430715Sbrian#include <errno.h> 356059Samurai#include <fcntl.h> 3611336Samurai#include <paths.h> 3730715Sbrian#include <signal.h> 3830715Sbrian#include <stdio.h> 3930715Sbrian#include <stdlib.h> 4030715Sbrian#include <string.h> 416059Samurai#include <sys/time.h> 4230715Sbrian#include <sys/wait.h> 4330715Sbrian#include <sysexits.h> 446059Samurai#include <termios.h> 4518786Sjkh#include <unistd.h> 4630715Sbrian 4730715Sbrian#include "mbuf.h" 4830715Sbrian#include "log.h" 4930715Sbrian#include "defs.h" 5031061Sbrian#include "id.h" 5130715Sbrian#include "timer.h" 5230715Sbrian#include "fsm.h" 536059Samurai#include "modem.h" 546059Samurai#include "os.h" 556059Samurai#include "hdlc.h" 5613389Sphk#include "ccp.h" 576059Samurai#include "lcp.h" 586059Samurai#include "ipcp.h" 5926142Sbrian#include "loadalias.h" 6030715Sbrian#include "command.h" 616059Samurai#include "vars.h" 626735Samurai#include "auth.h" 637001Samurai#include "filter.h" 6413389Sphk#include "systems.h" 6513389Sphk#include "ip.h" 6623840Sbrian#include "sig.h" 6726940Sbrian#include "server.h" 6828536Sbrian#include "lcpproto.h" 6930715Sbrian#include "main.h" 7030715Sbrian#include "vjcomp.h" 7130715Sbrian#include "async.h" 726059Samurai 736735Samurai#ifndef O_NONBLOCK 746735Samurai#ifdef O_NDELAY 756735Samurai#define O_NONBLOCK O_NDELAY 766735Samurai#endif 776735Samurai#endif 786735Samurai 7930715Sbrianint TermMode = 0; 8030715Sbrianint tunno = 0; 816059Samurai 8228679Sbrianstatic struct termios oldtio; /* Original tty mode */ 8328679Sbrianstatic struct termios comtio; /* Command level tty mode */ 8420813Sjkhstatic pid_t BGPid = 0; 8525634Sbrianstatic char pid_filename[MAXPATHLEN]; 8627061Sbrianstatic int dial_up; 876059Samurai 8830715Sbrianstatic void DoLoop(void); 8930715Sbrianstatic void TerminalStop(int); 9030715Sbrianstatic char *ex_desc(int); 9130715Sbrian 926059Samuraistatic void 9326858SbrianTtyInit(int DontWantInt) 946059Samurai{ 956059Samurai struct termios newtio; 966059Samurai int stat; 976059Samurai 986059Samurai stat = fcntl(0, F_GETFL, 0); 9925630Sbrian if (stat > 0) { 10028679Sbrian stat |= O_NONBLOCK; 10128679Sbrian (void) fcntl(0, F_SETFL, stat); 10225630Sbrian } 1036059Samurai newtio = oldtio; 10428679Sbrian newtio.c_lflag &= ~(ECHO | ISIG | ICANON); 1056059Samurai newtio.c_iflag = 0; 1066059Samurai newtio.c_oflag &= ~OPOST; 1076059Samurai newtio.c_cc[VEOF] = _POSIX_VDISABLE; 10826858Sbrian if (DontWantInt) 10926858Sbrian newtio.c_cc[VINTR] = _POSIX_VDISABLE; 1106059Samurai newtio.c_cc[VMIN] = 1; 1116059Samurai newtio.c_cc[VTIME] = 0; 1126059Samurai newtio.c_cflag |= CS8; 1136735Samurai tcsetattr(0, TCSADRAIN, &newtio); 1146059Samurai comtio = newtio; 1156059Samurai} 1166059Samurai 1176059Samurai/* 1186059Samurai * Set tty into command mode. We allow canonical input and echo processing. 1196059Samurai */ 12010528Samuraivoid 12128679SbrianTtyCommandMode(int prompt) 1226059Samurai{ 1236059Samurai struct termios newtio; 1246059Samurai int stat; 1256059Samurai 1266059Samurai if (!(mode & MODE_INTER)) 1276059Samurai return; 1286735Samurai tcgetattr(0, &newtio); 12928679Sbrian newtio.c_lflag |= (ECHO | ISIG | ICANON); 1306059Samurai newtio.c_iflag = oldtio.c_iflag; 1316059Samurai newtio.c_oflag |= OPOST; 1326735Samurai tcsetattr(0, TCSADRAIN, &newtio); 1336059Samurai stat = fcntl(0, F_GETFL, 0); 13425630Sbrian if (stat > 0) { 13528679Sbrian stat |= O_NONBLOCK; 13628679Sbrian (void) fcntl(0, F_SETFL, stat); 13725630Sbrian } 1386059Samurai TermMode = 0; 13928679Sbrian if (prompt) 14028679Sbrian Prompt(); 1416059Samurai} 1426059Samurai 1436059Samurai/* 1446059Samurai * Set tty into terminal mode which is used while we invoke term command. 1456059Samurai */ 1466059Samuraivoid 1476059SamuraiTtyTermMode() 1486059Samurai{ 1496059Samurai int stat; 1506059Samurai 1516735Samurai tcsetattr(0, TCSADRAIN, &comtio); 1526059Samurai stat = fcntl(0, F_GETFL, 0); 15325630Sbrian if (stat > 0) { 15428679Sbrian stat &= ~O_NONBLOCK; 15528679Sbrian (void) fcntl(0, F_SETFL, stat); 15625630Sbrian } 1576059Samurai TermMode = 1; 1586059Samurai} 1596059Samurai 1606059Samuraivoid 16110528SamuraiTtyOldMode() 1626059Samurai{ 1636059Samurai int stat; 1646059Samurai 1656059Samurai stat = fcntl(0, F_GETFL, 0); 16625630Sbrian if (stat > 0) { 16728679Sbrian stat &= ~O_NONBLOCK; 16828679Sbrian (void) fcntl(0, F_SETFL, stat); 16925630Sbrian } 1706735Samurai tcsetattr(0, TCSANOW, &oldtio); 17110528Samurai} 17210528Samurai 17310528Samuraivoid 17428679SbrianCleanup(int excode) 17510528Samurai{ 17630825Sbrian OsInterfaceDown(1); 17730825Sbrian HangupModem(1); 17830697Sbrian nointr_sleep(1); 17925908Sbrian if (mode & MODE_AUTO) 1806059Samurai DeleteIfRoutes(1); 18131061Sbrian ID0unlink(pid_filename); 18223863Sbrian if (mode & MODE_BACKGROUND && BGFiledes[1] != -1) { 18323863Sbrian char c = EX_ERRDEAD; 18428679Sbrian 18528679Sbrian if (write(BGFiledes[1], &c, 1) == 1) 18628679Sbrian LogPrintf(LogPHASE, "Parent notified of failure.\n"); 18723863Sbrian else 18828679Sbrian LogPrintf(LogPHASE, "Failed to notify parent of failure.\n"); 18923863Sbrian close(BGFiledes[1]); 19023863Sbrian } 19128679Sbrian LogPrintf(LogPHASE, "PPP Terminated (%s).\n", ex_desc(excode)); 19226940Sbrian ServerClose(); 19310528Samurai TtyOldMode(); 19431061Sbrian LogClose(); 1956059Samurai 1966059Samurai exit(excode); 1976059Samurai} 1986059Samurai 1996059Samuraistatic void 20028679SbrianCloseConnection(int signo) 2016059Samurai{ 20226858Sbrian /* NOTE, these are manual, we've done a setsid() */ 20327157Sbrian LogPrintf(LogPHASE, "Caught signal %d, abort connection\n", signo); 20428679Sbrian reconnectState = RECON_FALSE; 20528684Sbrian reconnectCount = 0; 20628684Sbrian DownConnection(); 20730715Sbrian dial_up = 0; 2086059Samurai} 2096059Samurai 2106059Samuraistatic void 21128679SbrianCloseSession(int signo) 2126059Samurai{ 21328679Sbrian if (BGPid) { 21428679Sbrian kill(BGPid, SIGINT); 21528679Sbrian exit(EX_TERM); 21628679Sbrian } 21728679Sbrian LogPrintf(LogPHASE, "Signal %d, terminate.\n", signo); 21828679Sbrian reconnect(RECON_FALSE); 21928679Sbrian LcpClose(); 22028679Sbrian Cleanup(EX_TERM); 2216059Samurai} 2226059Samurai 22310528Samuraistatic void 22410528SamuraiTerminalCont() 22510528Samurai{ 22623840Sbrian pending_signal(SIGCONT, SIG_DFL); 22723840Sbrian pending_signal(SIGTSTP, TerminalStop); 22810528Samurai TtyCommandMode(getpgrp() == tcgetpgrp(0)); 22910528Samurai} 23010528Samurai 23110528Samuraistatic void 23228679SbrianTerminalStop(int signo) 23310528Samurai{ 23423840Sbrian pending_signal(SIGCONT, TerminalCont); 23510528Samurai TtyOldMode(); 23623840Sbrian pending_signal(SIGTSTP, SIG_DFL); 23710528Samurai kill(getpid(), signo); 23810528Samurai} 23910528Samurai 24026940Sbrianstatic void 24128679SbrianSetUpServer(int signo) 24226940Sbrian{ 24326940Sbrian int res; 24428679Sbrian 24528679Sbrian if ((res = ServerTcpOpen(SERVER_PORT + tunno)) != 0) 24629083Sbrian LogPrintf(LogERROR, "SIGUSR1: Failed %d to open port %d\n", 24729083Sbrian res, SERVER_PORT + tunno); 24826940Sbrian} 24926940Sbrian 25025908Sbrianstatic char * 25125908Sbrianex_desc(int ex) 25225908Sbrian{ 25325908Sbrian static char num[12]; 25428679Sbrian static char *desc[] = {"normal", "start", "sock", 25525908Sbrian "modem", "dial", "dead", "done", "reboot", "errdead", 25628679Sbrian "hangup", "term", "nodial", "nologin"}; 25710528Samurai 25828679Sbrian if (ex >= 0 && ex < sizeof(desc) / sizeof(*desc)) 25925908Sbrian return desc[ex]; 26025908Sbrian snprintf(num, sizeof num, "%d", ex); 26125908Sbrian return num; 26225908Sbrian} 26325908Sbrian 26430715Sbrianstatic void 2656059SamuraiUsage() 2666059Samurai{ 26720120Snate fprintf(stderr, 26828679Sbrian "Usage: ppp [-auto | -background | -direct | -dedicated | -ddial ] [ -alias ] [system]\n"); 2696059Samurai exit(EX_START); 2706059Samurai} 2716059Samurai 27230715Sbrianstatic void 2736059SamuraiProcessArgs(int argc, char **argv) 2746059Samurai{ 2756059Samurai int optc; 2766059Samurai char *cp; 2776059Samurai 2786059Samurai optc = 0; 2796059Samurai while (argc > 0 && **argv == '-') { 2806059Samurai cp = *argv + 1; 2816059Samurai if (strcmp(cp, "auto") == 0) 2826059Samurai mode |= MODE_AUTO; 28320813Sjkh else if (strcmp(cp, "background") == 0) 28428679Sbrian mode |= MODE_BACKGROUND | MODE_AUTO; 2856059Samurai else if (strcmp(cp, "direct") == 0) 2866059Samurai mode |= MODE_DIRECT; 2876059Samurai else if (strcmp(cp, "dedicated") == 0) 2886059Samurai mode |= MODE_DEDICATED; 28920120Snate else if (strcmp(cp, "ddial") == 0) 29028679Sbrian mode |= MODE_DDIAL | MODE_AUTO; 29120365Sjkh else if (strcmp(cp, "alias") == 0) { 29226142Sbrian if (loadAliasHandlers(&VarAliasHandlers) == 0) 29328679Sbrian mode |= MODE_ALIAS; 29426142Sbrian else 29528679Sbrian LogPrintf(LogWARN, "Cannot load alias library\n"); 29628679Sbrian optc--; /* this option isn't exclusive */ 29728679Sbrian } else 2986059Samurai Usage(); 2996059Samurai optc++; 30028679Sbrian argv++; 30128679Sbrian argc--; 3026059Samurai } 3036059Samurai if (argc > 1) { 3046059Samurai fprintf(stderr, "specify only one system label.\n"); 3056059Samurai exit(EX_START); 3066059Samurai } 30728679Sbrian if (argc == 1) 30828679Sbrian dstsystem = *argv; 3096059Samurai 3106059Samurai if (optc > 1) { 3116059Samurai fprintf(stderr, "specify only one mode.\n"); 3126059Samurai exit(EX_START); 3136059Samurai } 3146059Samurai} 3156059Samurai 3166059Samuraistatic void 3176059SamuraiGreetings() 3186059Samurai{ 31926516Sbrian if (VarTerm) { 32026516Sbrian fprintf(VarTerm, "User Process PPP. Written by Toshiharu OHNO.\n"); 32126516Sbrian fflush(VarTerm); 32226516Sbrian } 3236059Samurai} 3246059Samurai 32526940Sbrianint 32628679Sbrianmain(int argc, char **argv) 3276059Samurai{ 32825707Sbrian FILE *lockfile; 32926516Sbrian char *name; 33026516Sbrian 33126551Sbrian VarTerm = 0; 33230715Sbrian name = strrchr(argv[0], '/'); 33328679Sbrian LogOpen(name ? name + 1 : argv[0]); 33426516Sbrian 33528679Sbrian argc--; 33628679Sbrian argv++; 3376059Samurai ProcessArgs(argc, argv); 33829083Sbrian if (!(mode & MODE_DIRECT)) { 33929083Sbrian if (getuid() != 0) { 34029083Sbrian fprintf(stderr, "You may only run ppp in client mode as user id 0\n"); 34129083Sbrian LogClose(); 34229083Sbrian return EX_NOPERM; 34329083Sbrian } 34426551Sbrian VarTerm = stdout; 34529083Sbrian } 34631061Sbrian ID0init(); 34726551Sbrian Greetings(); 3486059Samurai IpcpDefAddress(); 34929083Sbrian LocalAuthInit(); 3506059Samurai 35126516Sbrian if (SelectSystem("default", CONFFILE) < 0 && VarTerm) 35226516Sbrian fprintf(VarTerm, "Warning: No default entry is given in config file.\n"); 3536059Samurai 3546059Samurai if (OpenTunnel(&tunno) < 0) { 35526940Sbrian LogPrintf(LogWARN, "open_tun: %s\n", strerror(errno)); 35626940Sbrian return EX_START; 3576059Samurai } 35828679Sbrian if (mode & (MODE_AUTO | MODE_DIRECT | MODE_DEDICATED)) 3596059Samurai mode &= ~MODE_INTER; 3606059Samurai if (mode & MODE_INTER) { 36126516Sbrian fprintf(VarTerm, "Interactive mode\n"); 36226690Sbrian netfd = STDOUT_FILENO; 3636059Samurai } else if (mode & MODE_AUTO) { 36426516Sbrian fprintf(VarTerm, "Automatic Dialer mode\n"); 3656059Samurai if (dstsystem == NULL) { 36626516Sbrian if (VarTerm) 36728679Sbrian fprintf(VarTerm, "Destination system must be specified in" 36828679Sbrian " auto, background or ddial mode.\n"); 36926940Sbrian return EX_START; 3706059Samurai } 3716059Samurai } 37228679Sbrian tcgetattr(0, &oldtio); /* Save original tty mode */ 3736059Samurai 37427157Sbrian pending_signal(SIGHUP, CloseSession); 37523840Sbrian pending_signal(SIGTERM, CloseSession); 37627157Sbrian pending_signal(SIGINT, CloseConnection); 37723840Sbrian pending_signal(SIGQUIT, CloseSession); 3786735Samurai#ifdef SIGPIPE 37924753Sache signal(SIGPIPE, SIG_IGN); 3806735Samurai#endif 3816735Samurai#ifdef SIGALRM 38223840Sbrian pending_signal(SIGALRM, SIG_IGN); 3836735Samurai#endif 38428679Sbrian if (mode & MODE_INTER) { 38510528Samurai#ifdef SIGTSTP 38626940Sbrian pending_signal(SIGTSTP, TerminalStop); 38710528Samurai#endif 38810528Samurai#ifdef SIGTTIN 38926940Sbrian pending_signal(SIGTTIN, TerminalStop); 39010528Samurai#endif 39110528Samurai#ifdef SIGTTOU 39226940Sbrian pending_signal(SIGTTOU, SIG_IGN); 39310528Samurai#endif 39426940Sbrian } 39526940Sbrian#ifdef SIGUSR1 39626940Sbrian if (mode != MODE_INTER) 39726940Sbrian pending_signal(SIGUSR1, SetUpServer); 39826940Sbrian#endif 3996059Samurai 4006059Samurai if (dstsystem) { 4016059Samurai if (SelectSystem(dstsystem, CONFFILE) < 0) { 40226551Sbrian LogPrintf(LogWARN, "Destination system not found in conf file.\n"); 4036059Samurai Cleanup(EX_START); 4046059Samurai } 4056059Samurai if ((mode & MODE_AUTO) && DefHisAddress.ipaddr.s_addr == INADDR_ANY) { 40626551Sbrian LogPrintf(LogWARN, "Must specify dstaddr with" 40728679Sbrian " auto, background or ddial mode.\n"); 4086059Samurai Cleanup(EX_START); 4096059Samurai } 4106059Samurai } 41126940Sbrian 4126059Samurai if (!(mode & MODE_INTER)) { 41320813Sjkh if (mode & MODE_BACKGROUND) { 41428679Sbrian if (pipe(BGFiledes)) { 41528974Sbrian LogPrintf(LogERROR, "pipe: %s\n", strerror(errno)); 41620813Sjkh Cleanup(EX_SOCK); 41720813Sjkh } 4186059Samurai } 41930715Sbrian /* Create server socket and listen (initial value is -2) */ 42029083Sbrian if (server == -2) 42129083Sbrian ServerTcpOpen(SERVER_PORT + tunno); 4226059Samurai 4236059Samurai if (!(mode & MODE_DIRECT)) { 42420813Sjkh pid_t bgpid; 42511336Samurai 42628679Sbrian bgpid = fork(); 42720813Sjkh if (bgpid == -1) { 42828974Sbrian LogPrintf(LogERROR, "fork: %s\n", strerror(errno)); 42928679Sbrian Cleanup(EX_SOCK); 43020813Sjkh } 43120813Sjkh if (bgpid) { 43220813Sjkh char c = EX_NORMAL; 43311336Samurai 43420813Sjkh if (mode & MODE_BACKGROUND) { 43520813Sjkh /* Wait for our child to close its pipe before we exit. */ 43620813Sjkh BGPid = bgpid; 43728679Sbrian close(BGFiledes[1]); 43825908Sbrian if (read(BGFiledes[0], &c, 1) != 1) { 43926516Sbrian fprintf(VarTerm, "Child exit, no status.\n"); 44028679Sbrian LogPrintf(LogPHASE, "Parent: Child exit, no status.\n"); 44125908Sbrian } else if (c == EX_NORMAL) { 44226516Sbrian fprintf(VarTerm, "PPP enabled.\n"); 44328679Sbrian LogPrintf(LogPHASE, "Parent: PPP enabled.\n"); 44425908Sbrian } else { 44528679Sbrian fprintf(VarTerm, "Child failed (%s).\n", ex_desc((int) c)); 44626516Sbrian LogPrintf(LogPHASE, "Parent: Child failed (%s).\n", 44728679Sbrian ex_desc((int) c)); 44828679Sbrian } 44928679Sbrian close(BGFiledes[0]); 45020813Sjkh } 45128679Sbrian return c; 45223863Sbrian } else if (mode & MODE_BACKGROUND) 45328679Sbrian close(BGFiledes[0]); 45425707Sbrian } 45520813Sjkh 45628679Sbrian VarTerm = 0; /* We know it's currently stdout */ 45729551Sbrian close(1); 45826686Sbrian close(2); 45926551Sbrian 4606059Samurai#ifdef DOTTYINIT 46128679Sbrian if (mode & (MODE_DIRECT | MODE_DEDICATED)) 4626059Samurai#else 46326686Sbrian if (mode & MODE_DIRECT) 4646059Samurai#endif 46526858Sbrian TtyInit(1); 46626686Sbrian else { 46726686Sbrian setsid(); 46829551Sbrian close(0); 46926686Sbrian } 4706059Samurai } else { 47126858Sbrian TtyInit(0); 47210528Samurai TtyCommandMode(1); 4736059Samurai } 47429696Sbrian 47529696Sbrian snprintf(pid_filename, sizeof(pid_filename), "%stun%d.pid", 47629696Sbrian _PATH_VARRUN, tunno); 47731061Sbrian lockfile = ID0fopen(pid_filename, "w"); 47831061Sbrian if (lockfile != NULL) { 47929696Sbrian fprintf(lockfile, "%d\n", (int) getpid()); 48029696Sbrian fclose(lockfile); 48129696Sbrian } else 48229696Sbrian LogPrintf(LogALERT, "Warning: Can't create %s: %s\n", 48329696Sbrian pid_filename, strerror(errno)); 48429696Sbrian 48526516Sbrian LogPrintf(LogPHASE, "PPP Started.\n"); 4866059Samurai 4876059Samurai 4886059Samurai do 48928679Sbrian DoLoop(); 4906059Samurai while (mode & MODE_DEDICATED); 4916059Samurai 4926059Samurai Cleanup(EX_DONE); 49326940Sbrian return 0; 4946059Samurai} 4956059Samurai 4966059Samurai/* 49720813Sjkh * Turn into packet mode, where we speak PPP. 4986059Samurai */ 4996059Samuraivoid 5006059SamuraiPacketMode() 5016059Samurai{ 50231034Sbrian if (RawModem() < 0) { 50326516Sbrian LogPrintf(LogWARN, "PacketMode: Not connected.\n"); 5046059Samurai return; 5056059Samurai } 5066059Samurai AsyncInit(); 50730187Sbrian VjInit(15); 5086059Samurai LcpInit(); 5096059Samurai IpcpInit(); 5106059Samurai CcpInit(); 5116059Samurai LcpUp(); 5126059Samurai 51325872Sbrian LcpOpen(VarOpenMode); 51428679Sbrian if ((mode & (MODE_INTER | MODE_AUTO)) == MODE_INTER) { 51510528Samurai TtyCommandMode(1); 51626516Sbrian if (VarTerm) { 51726516Sbrian fprintf(VarTerm, "Packet mode.\n"); 51826516Sbrian aft_cmd = 1; 51926516Sbrian } 5206059Samurai } 5216059Samurai} 5226059Samurai 5236059Samuraistatic void 5246059SamuraiShowHelp() 5256059Samurai{ 52626901Sbrian fprintf(stderr, "The following commands are available:\r\n"); 52726901Sbrian fprintf(stderr, " ~p\tEnter Packet mode\r\n"); 52826901Sbrian fprintf(stderr, " ~-\tDecrease log level\r\n"); 52926901Sbrian fprintf(stderr, " ~+\tIncrease log level\r\n"); 53026901Sbrian fprintf(stderr, " ~t\tShow timers (only in \"log debug\" mode)\r\n"); 53126901Sbrian fprintf(stderr, " ~m\tShow memory map (only in \"log debug\" mode)\r\n"); 53226901Sbrian fprintf(stderr, " ~.\tTerminate program\r\n"); 53326901Sbrian fprintf(stderr, " ~?\tThis help\r\n"); 5346059Samurai} 5356059Samurai 5366059Samuraistatic void 5376059SamuraiReadTty() 5386059Samurai{ 5396059Samurai int n; 5406059Samurai char ch; 5416059Samurai static int ttystate; 54226516Sbrian FILE *oVarTerm; 54328679Sbrian 5446059Samurai#define MAXLINESIZE 200 5456059Samurai char linebuff[MAXLINESIZE]; 5466059Samurai 54726516Sbrian LogPrintf(LogDEBUG, "termode = %d, netfd = %d, mode = %d\n", 54828679Sbrian TermMode, netfd, mode); 5496059Samurai if (!TermMode) { 55028679Sbrian n = read(netfd, linebuff, sizeof(linebuff) - 1); 5516735Samurai if (n > 0) { 55226516Sbrian aft_cmd = 1; 55330913Sbrian if (linebuff[n-1] == '\n') 55430913Sbrian linebuff[--n] = '\0'; 55530913Sbrian if (n) { 55630913Sbrian if (IsInteractive(0)) 55730913Sbrian LogPrintf(LogCOMMAND, "%s\n", linebuff); 55830913Sbrian else 55930913Sbrian LogPrintf(LogCOMMAND, "Client: %s\n", linebuff); 56030913Sbrian DecodeCommand(linebuff, n, 1); 56130913Sbrian } else 56230913Sbrian Prompt(); 5636735Samurai } else { 56426516Sbrian LogPrintf(LogPHASE, "client connection closed.\n"); 56524753Sache VarLocalAuth = LOCAL_NO_AUTH; 56626516Sbrian mode &= ~MODE_INTER; 56726516Sbrian oVarTerm = VarTerm; 56826516Sbrian VarTerm = 0; 56926516Sbrian if (oVarTerm && oVarTerm != stdout) 57028679Sbrian fclose(oVarTerm); 5716059Samurai close(netfd); 5726059Samurai netfd = -1; 5736059Samurai } 5746059Samurai return; 5756059Samurai } 5766059Samurai 5776059Samurai /* 57828679Sbrian * We are in terminal mode, decode special sequences 5796059Samurai */ 58026516Sbrian n = read(fileno(VarTerm), &ch, 1); 58128974Sbrian LogPrintf(LogDEBUG, "Got %d bytes (reading from the terminal)\n", n); 5826059Samurai 5836059Samurai if (n > 0) { 5846059Samurai switch (ttystate) { 5856059Samurai case 0: 5866059Samurai if (ch == '~') 5876059Samurai ttystate++; 5886059Samurai else 5896059Samurai write(modem, &ch, n); 5906059Samurai break; 5916059Samurai case 1: 5926059Samurai switch (ch) { 5936059Samurai case '?': 5946059Samurai ShowHelp(); 5956059Samurai break; 5966059Samurai case 'p': 59728679Sbrian 5986059Samurai /* 5996059Samurai * XXX: Should check carrier. 6006059Samurai */ 6016059Samurai if (LcpFsm.state <= ST_CLOSED) { 6026059Samurai VarOpenMode = OPEN_ACTIVE; 6036059Samurai PacketMode(); 6046059Samurai } 6056059Samurai break; 6066059Samurai case '.': 6076059Samurai TermMode = 1; 60826516Sbrian aft_cmd = 1; 60910528Samurai TtyCommandMode(1); 6106059Samurai break; 61126516Sbrian case 't': 61226516Sbrian if (LogIsKept(LogDEBUG)) { 61326516Sbrian ShowTimers(); 61426516Sbrian break; 61526516Sbrian } 61626516Sbrian case 'm': 61726516Sbrian if (LogIsKept(LogDEBUG)) { 61826516Sbrian ShowMemMap(); 61926516Sbrian break; 62026516Sbrian } 6216059Samurai default: 6226059Samurai if (write(modem, &ch, n) < 0) 62326516Sbrian LogPrintf(LogERROR, "error writing to modem.\n"); 6246059Samurai break; 6256059Samurai } 6266059Samurai ttystate = 0; 6276059Samurai break; 6286059Samurai } 6296059Samurai } 6306059Samurai} 6316059Samurai 6326059Samurai 6336059Samurai/* 6346059Samurai * Here, we'll try to detect HDLC frame 6356059Samurai */ 6366059Samurai 6376059Samuraistatic char *FrameHeaders[] = { 6386735Samurai "\176\377\003\300\041", 6396735Samurai "\176\377\175\043\300\041", 6406735Samurai "\176\177\175\043\100\041", 6416735Samurai "\176\175\337\175\043\300\041", 6426735Samurai "\176\175\137\175\043\100\041", 6436059Samurai NULL, 6446059Samurai}; 6456059Samurai 64630715Sbrianstatic u_char * 64728679SbrianHdlcDetect(u_char * cp, int n) 6486059Samurai{ 6496735Samurai char *ptr, *fp, **hp; 6506059Samurai 65128679Sbrian cp[n] = '\0'; /* be sure to null terminated */ 6526059Samurai ptr = NULL; 6536059Samurai for (hp = FrameHeaders; *hp; hp++) { 6546735Samurai fp = *hp; 6556735Samurai if (DEV_IS_SYNC) 6566735Samurai fp++; 65728679Sbrian ptr = strstr((char *) cp, fp); 65813389Sphk if (ptr) 6596059Samurai break; 6606059Samurai } 66128679Sbrian return ((u_char *) ptr); 6626059Samurai} 6636059Samurai 6646059Samuraistatic struct pppTimer RedialTimer; 6656059Samurai 6666059Samuraistatic void 6676059SamuraiRedialTimeout() 6686059Samurai{ 6696059Samurai StopTimer(&RedialTimer); 67026516Sbrian LogPrintf(LogPHASE, "Redialing timer expired.\n"); 6716059Samurai} 6726059Samurai 6736059Samuraistatic void 67428679SbrianStartRedialTimer(int Timeout) 6756059Samurai{ 6766059Samurai StopTimer(&RedialTimer); 67711336Samurai 67824939Sbrian if (Timeout) { 67911336Samurai RedialTimer.state = TIMER_STOPPED; 68011336Samurai 68124939Sbrian if (Timeout > 0) 68228679Sbrian RedialTimer.load = Timeout * SECTICKS; 68311336Samurai else 68428679Sbrian RedialTimer.load = (random() % REDIAL_PERIOD) * SECTICKS; 68511336Samurai 68626516Sbrian LogPrintf(LogPHASE, "Enter pause (%d) for redialing.\n", 68724939Sbrian RedialTimer.load / SECTICKS); 68824939Sbrian 68911336Samurai RedialTimer.func = RedialTimeout; 69011336Samurai StartTimer(&RedialTimer); 69111336Samurai } 6926059Samurai} 6936059Samurai 6946059Samurai 6956059Samuraistatic void 6966059SamuraiDoLoop() 6976059Samurai{ 6986059Samurai fd_set rfds, wfds, efds; 69923598Sache int pri, i, n, wfd, nfds; 7006059Samurai struct sockaddr_in hisaddr; 7016059Samurai struct timeval timeout, *tp; 7026059Samurai int ssize = sizeof(hisaddr); 7036059Samurai u_char *cp; 7046059Samurai u_char rbuff[MAX_MRU]; 70511336Samurai int tries; 7069448Samurai int qlen; 70726858Sbrian int res; 70810528Samurai pid_t pgroup; 7096059Samurai 71010528Samurai pgroup = getpgrp(); 71110528Samurai 71225908Sbrian if (mode & MODE_DIRECT) { 71326551Sbrian LogPrintf(LogDEBUG, "Opening modem\n"); 71431034Sbrian if (OpenModem() < 0) 71529521Sbrian return; 71626516Sbrian LogPrintf(LogPHASE, "Packet mode enabled\n"); 7176059Samurai PacketMode(); 7186059Samurai } else if (mode & MODE_DEDICATED) { 71923598Sache if (modem < 0) 72031034Sbrian while (OpenModem() < 0) 72130697Sbrian nointr_sleep(VarReconnectTimer); 7226059Samurai } 72326516Sbrian fflush(VarTerm); 7246059Samurai 7257001Samurai timeout.tv_sec = 0; 7266059Samurai timeout.tv_usec = 0; 72726098Sbrian reconnectState = RECON_UNKNOWN; 7286059Samurai 72923863Sbrian if (mode & MODE_BACKGROUND) 73030715Sbrian dial_up = 1; /* Bring the line up */ 73123863Sbrian else 73230715Sbrian dial_up = 0; /* XXXX */ 73311336Samurai tries = 0; 7346059Samurai for (;;) { 73523598Sache nfds = 0; 73628679Sbrian FD_ZERO(&rfds); 73728679Sbrian FD_ZERO(&wfds); 73828679Sbrian FD_ZERO(&efds); 7397001Samurai 74028679Sbrian /* 74128679Sbrian * If the link is down and we're in DDIAL mode, bring it back up. 74220120Snate */ 74320120Snate if (mode & MODE_DDIAL && LcpFsm.state <= ST_CLOSED) 74430715Sbrian dial_up = 1; 74520120Snate 74625067Sbrian /* 74728679Sbrian * If we lost carrier and want to re-establish the connection due to the 74828679Sbrian * "set reconnect" value, we'd better bring the line back up. 74925067Sbrian */ 75025908Sbrian if (LcpFsm.state <= ST_CLOSED) { 75130715Sbrian if (!dial_up && reconnectState == RECON_TRUE) { 75228679Sbrian if (++reconnectCount <= VarReconnectTries) { 75328679Sbrian LogPrintf(LogPHASE, "Connection lost, re-establish (%d/%d)\n", 75428679Sbrian reconnectCount, VarReconnectTries); 75525908Sbrian StartRedialTimer(VarReconnectTimer); 75630715Sbrian dial_up = 1; 75728679Sbrian } else { 75828679Sbrian if (VarReconnectTries) 75928679Sbrian LogPrintf(LogPHASE, "Connection lost, maximum (%d) times\n", 76028679Sbrian VarReconnectTries); 76128679Sbrian reconnectCount = 0; 76228679Sbrian if (mode & MODE_BACKGROUND) 76328679Sbrian Cleanup(EX_DEAD); 76428679Sbrian } 76528679Sbrian reconnectState = RECON_ENVOKED; 76625801Sbrian } 76725908Sbrian } 76825067Sbrian 76928679Sbrian /* 77028679Sbrian * If Ip packet for output is enqueued and require dial up, Just do it! 77128679Sbrian */ 77228679Sbrian if (dial_up && RedialTimer.state != TIMER_RUNNING) { 77326516Sbrian LogPrintf(LogDEBUG, "going to dial: modem = %d\n", modem); 77431034Sbrian if (OpenModem() < 0) { 77528679Sbrian tries++; 77628679Sbrian if (!(mode & MODE_DDIAL) && VarDialTries) 77728679Sbrian LogPrintf(LogCHAT, "Failed to open modem (attempt %u of %d)\n", 77828679Sbrian tries, VarDialTries); 77928679Sbrian else 78028679Sbrian LogPrintf(LogCHAT, "Failed to open modem (attempt %u)\n", tries); 78126551Sbrian 78226696Sbrian if (!(mode & MODE_DDIAL) && VarDialTries && tries >= VarDialTries) { 78326551Sbrian if (mode & MODE_BACKGROUND) 78428679Sbrian Cleanup(EX_DIAL); /* Can't get the modem */ 78530715Sbrian dial_up = 0; 78628679Sbrian reconnectState = RECON_UNKNOWN; 78728679Sbrian reconnectCount = 0; 78826551Sbrian tries = 0; 78928679Sbrian } else 79026551Sbrian StartRedialTimer(VarRedialTimeout); 79111336Samurai } else { 79228679Sbrian tries++; /* Tries are per number, not per list of 79328679Sbrian * numbers. */ 79428679Sbrian if (!(mode & MODE_DDIAL) && VarDialTries) 79526696Sbrian LogPrintf(LogCHAT, "Dial attempt %u of %d\n", tries, VarDialTries); 79628679Sbrian else 79728679Sbrian LogPrintf(LogCHAT, "Dial attempt %u\n", tries); 79826696Sbrian 79926858Sbrian if ((res = DialModem()) == EX_DONE) { 80030697Sbrian nointr_sleep(1); /* little pause to allow peer starts */ 80111336Samurai ModemTimeout(); 80211336Samurai PacketMode(); 80330715Sbrian dial_up = 0; 80428679Sbrian reconnectState = RECON_UNKNOWN; 80511336Samurai tries = 0; 80611336Samurai } else { 80724844Sbrian if (mode & MODE_BACKGROUND) { 80826858Sbrian if (VarNextPhone == NULL || res == EX_SIG) 80928679Sbrian Cleanup(EX_DIAL); /* Tried all numbers - no luck */ 81024844Sbrian else 81124939Sbrian /* Try all numbers in background mode */ 81224939Sbrian StartRedialTimer(VarRedialNextTimeout); 81326858Sbrian } else if (!(mode & MODE_DDIAL) && 81428679Sbrian ((VarDialTries && tries >= VarDialTries) || 81528679Sbrian res == EX_SIG)) { 81624843Sbrian /* I give up ! Can't get through :( */ 81724939Sbrian StartRedialTimer(VarRedialTimeout); 81830715Sbrian dial_up = 0; 81928679Sbrian reconnectState = RECON_UNKNOWN; 82028679Sbrian reconnectCount = 0; 82124843Sbrian tries = 0; 82224843Sbrian } else if (VarNextPhone == NULL) 82324843Sbrian /* Dial failed. Keep quite during redial wait period. */ 82424939Sbrian StartRedialTimer(VarRedialTimeout); 82524843Sbrian else 82624939Sbrian StartRedialTimer(VarRedialNextTimeout); 82711336Samurai } 82811336Samurai } 8297001Samurai } 8309448Samurai qlen = ModemQlen(); 83113733Sdfr 83213733Sdfr if (qlen == 0) { 83313733Sdfr IpStartOutput(); 83413733Sdfr qlen = ModemQlen(); 83513733Sdfr } 83623598Sache if (modem >= 0) { 83723598Sache if (modem + 1 > nfds) 83823598Sache nfds = modem + 1; 8397001Samurai FD_SET(modem, &rfds); 8407001Samurai FD_SET(modem, &efds); 8419448Samurai if (qlen > 0) { 8427001Samurai FD_SET(modem, &wfds); 8437001Samurai } 8447001Samurai } 84523598Sache if (server >= 0) { 84623598Sache if (server + 1 > nfds) 84723598Sache nfds = server + 1; 84823598Sache FD_SET(server, &rfds); 84923598Sache } 8506059Samurai 85128679Sbrian /* 85228679Sbrian * *** IMPORTANT *** 85328679Sbrian * 85428679Sbrian * CPU is serviced every TICKUNIT micro seconds. This value must be chosen 85528679Sbrian * with great care. If this values is too big, it results loss of 85628679Sbrian * characters from modem and poor responce. If this values is too small, 85728679Sbrian * ppp process eats many CPU time. 8586059Samurai */ 8596735Samurai#ifndef SIGALRM 86030697Sbrian nointr_usleep(TICKUNIT); 8616059Samurai TimerService(); 86223840Sbrian#else 86323840Sbrian handle_signals(); 8646735Samurai#endif 86510877Sbde 86610877Sbde /* If there are aren't many packets queued, look for some more. */ 86723598Sache if (qlen < 20 && tun_in >= 0) { 86823598Sache if (tun_in + 1 > nfds) 86923598Sache nfds = tun_in + 1; 87010877Sbde FD_SET(tun_in, &rfds); 87123598Sache } 87223598Sache if (netfd >= 0) { 87323598Sache if (netfd + 1 > nfds) 87423598Sache nfds = netfd + 1; 8756059Samurai FD_SET(netfd, &rfds); 8766059Samurai FD_SET(netfd, &efds); 8776059Samurai } 87828679Sbrian#ifndef SIGALRM 8797001Samurai 8806059Samurai /* 88128679Sbrian * Normally, select() will not block because modem is writable. In AUTO 88228679Sbrian * mode, select will block until we find packet from tun 8836059Samurai */ 88428679Sbrian tp = (RedialTimer.state == TIMER_RUNNING) ? &timeout : NULL; 88523598Sache i = select(nfds, &rfds, &wfds, &efds, tp); 8866735Samurai#else 88728679Sbrian 8888857Srgrimes /* 88928679Sbrian * When SIGALRM timer is running, a select function will be return -1 and 89028679Sbrian * EINTR after a Time Service signal hundler is done. If the redial 89128679Sbrian * timer is not running and we are trying to dial, poll with a 0 value 89228679Sbrian * timer. 8937001Samurai */ 89411336Samurai tp = (dial_up && RedialTimer.state != TIMER_RUNNING) ? &timeout : NULL; 89523598Sache i = select(nfds, &rfds, &wfds, &efds, tp); 8966735Samurai#endif 89722074Sbrian 89828679Sbrian if (i == 0) { 89928679Sbrian continue; 9006059Samurai } 90128679Sbrian if (i < 0) { 90228679Sbrian if (errno == EINTR) { 90328679Sbrian handle_signals(); 90428679Sbrian continue; 90528679Sbrian } 90628974Sbrian LogPrintf(LogERROR, "DoLoop: select(): %s\n", strerror(errno)); 90728679Sbrian break; 9088857Srgrimes } 90923598Sache if ((netfd >= 0 && FD_ISSET(netfd, &efds)) || (modem >= 0 && FD_ISSET(modem, &efds))) { 91026516Sbrian LogPrintf(LogALERT, "Exception detected.\n"); 9116059Samurai break; 9126059Samurai } 91323598Sache if (server >= 0 && FD_ISSET(server, &rfds)) { 91426516Sbrian LogPrintf(LogPHASE, "connected to client.\n"); 91528679Sbrian wfd = accept(server, (struct sockaddr *) & hisaddr, &ssize); 91624753Sache if (wfd < 0) { 91728974Sbrian LogPrintf(LogERROR, "DoLoop: accept(): %s\n", strerror(errno)); 91824753Sache continue; 91924753Sache } 92023598Sache if (netfd >= 0) { 9216059Samurai write(wfd, "already in use.\n", 16); 9226059Samurai close(wfd); 9236059Samurai continue; 9246059Samurai } else 9256059Samurai netfd = wfd; 92626516Sbrian VarTerm = fdopen(netfd, "a+"); 9276059Samurai mode |= MODE_INTER; 9286059Samurai Greetings(); 92930913Sbrian IsInteractive(1); 93025630Sbrian Prompt(); 9316059Samurai } 93223598Sache if ((mode & MODE_INTER) && (netfd >= 0 && FD_ISSET(netfd, &rfds)) && 93310858Samurai ((mode & MODE_AUTO) || pgroup == tcgetpgrp(0))) { 9346059Samurai /* something to read from tty */ 9356059Samurai ReadTty(); 9366059Samurai } 93723598Sache if (modem >= 0) { 9386059Samurai if (FD_ISSET(modem, &wfds)) { /* ready to write into modem */ 93928679Sbrian ModemStartOutput(modem); 9406059Samurai } 9416059Samurai if (FD_ISSET(modem, &rfds)) { /* something to read from modem */ 9426735Samurai if (LcpFsm.state <= ST_CLOSED) 94330697Sbrian nointr_usleep(10000); 9446059Samurai n = read(modem, rbuff, sizeof(rbuff)); 9456059Samurai if ((mode & MODE_DIRECT) && n <= 0) { 9466059Samurai DownConnection(); 9476059Samurai } else 94828679Sbrian LogDumpBuff(LogASYNC, "ReadFromModem", rbuff, n); 9496059Samurai 9506059Samurai if (LcpFsm.state <= ST_CLOSED) { 95128679Sbrian 9526059Samurai /* 95328679Sbrian * In dedicated mode, we just discard input until LCP is started. 9546059Samurai */ 9556059Samurai if (!(mode & MODE_DEDICATED)) { 9566059Samurai cp = HdlcDetect(rbuff, n); 9576059Samurai if (cp) { 95828679Sbrian 9596059Samurai /* 9606059Samurai * LCP packet is detected. Turn ourselves into packet mode. 9616059Samurai */ 9626059Samurai if (cp != rbuff) { 96328679Sbrian write(modem, rbuff, cp - rbuff); 96428679Sbrian write(modem, "\r\n", 2); 9656059Samurai } 9666059Samurai PacketMode(); 9676059Samurai } else 96826516Sbrian write(fileno(VarTerm), rbuff, n); 9696059Samurai } 9706059Samurai } else { 9716059Samurai if (n > 0) 9726059Samurai AsyncInput(rbuff, n); 9736059Samurai } 9746059Samurai } 9756059Samurai } 97628679Sbrian if (tun_in >= 0 && FD_ISSET(tun_in, &rfds)) { /* something to read 97728679Sbrian * from tun */ 9786059Samurai n = read(tun_in, rbuff, sizeof(rbuff)); 9796059Samurai if (n < 0) { 98028974Sbrian LogPrintf(LogERROR, "read from tun: %s\n", strerror(errno)); 9816059Samurai continue; 9826059Samurai } 98328679Sbrian if (((struct ip *) rbuff)->ip_dst.s_addr == IpcpInfo.want_ipaddr.s_addr) { 98428536Sbrian /* we've been asked to send something addressed *to* us :( */ 98528536Sbrian if (VarLoopback) { 98628536Sbrian pri = PacketCheck(rbuff, n, FL_IN); 98728536Sbrian if (pri >= 0) { 98828536Sbrian struct mbuf *bp; 98928679Sbrian 99028536Sbrian if (mode & MODE_ALIAS) { 99128536Sbrian VarPacketAliasIn(rbuff, sizeof rbuff); 99228679Sbrian n = ntohs(((struct ip *) rbuff)->ip_len); 99328536Sbrian } 99428536Sbrian bp = mballoc(n, MB_IPIN); 99530715Sbrian memcpy(MBUF_CTOP(bp), rbuff, n); 99628536Sbrian IpInput(bp); 99728536Sbrian LogPrintf(LogDEBUG, "Looped back packet addressed to myself\n"); 99828536Sbrian } 99928536Sbrian continue; 100028679Sbrian } else 100128536Sbrian LogPrintf(LogDEBUG, "Oops - forwarding packet addressed to myself\n"); 100228536Sbrian } 100328536Sbrian 10046059Samurai /* 100528679Sbrian * Process on-demand dialup. Output packets are queued within tunnel 100628679Sbrian * device until IPCP is opened. 10076059Samurai */ 10086059Samurai if (LcpFsm.state <= ST_CLOSED && (mode & MODE_AUTO)) { 10097001Samurai pri = PacketCheck(rbuff, n, FL_DIAL); 10106059Samurai if (pri >= 0) { 101120365Sjkh if (mode & MODE_ALIAS) { 101226142Sbrian VarPacketAliasOut(rbuff, sizeof rbuff); 101328679Sbrian n = ntohs(((struct ip *) rbuff)->ip_len); 101420365Sjkh } 10156059Samurai IpEnqueue(pri, rbuff, n); 101630715Sbrian dial_up = 1; /* XXX */ 10176059Samurai } 10186059Samurai continue; 10196059Samurai } 10207001Samurai pri = PacketCheck(rbuff, n, FL_OUT); 102120365Sjkh if (pri >= 0) { 102228679Sbrian if (mode & MODE_ALIAS) { 102328679Sbrian VarPacketAliasOut(rbuff, sizeof rbuff); 102428679Sbrian n = ntohs(((struct ip *) rbuff)->ip_len); 102528679Sbrian } 10266059Samurai IpEnqueue(pri, rbuff, n); 102720365Sjkh } 10286059Samurai } 10296059Samurai } 103026516Sbrian LogPrintf(LogDEBUG, "Job (DoLoop) done.\n"); 10316059Samurai} 1032