/* * User Process PPP * * Written by Toshiharu OHNO (tony-o@iij.ad.jp) * * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the Internet Initiative Japan, Inc. The name of the * IIJ may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * $Id: main.c,v 1.121.2.12 1998/02/07 22:22:43 brian Exp $ * * TODO: * o Add commands for traffic summary, version display, etc. * o Add signal handler for misc controls. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "command.h" #include "mbuf.h" #include "log.h" #include "defs.h" #include "id.h" #include "timer.h" #include "fsm.h" #include "modem.h" #include "bundle.h" #include "hdlc.h" #include "lcp.h" #include "ccp.h" #include "iplist.h" #include "throughput.h" #include "ipcp.h" #include "loadalias.h" #include "vars.h" #include "auth.h" #include "filter.h" #include "systems.h" #include "ip.h" #include "sig.h" #include "server.h" #include "main.h" #include "vjcomp.h" #include "async.h" #include "pathnames.h" #include "tun.h" #include "route.h" #include "link.h" #include "physical.h" #ifndef O_NONBLOCK #ifdef O_NDELAY #define O_NONBLOCK O_NDELAY #endif #endif int TermMode = 0; static struct termios oldtio; /* Original tty mode */ static struct termios comtio; /* Command level tty mode */ static pid_t BGPid = 0; static char pid_filename[MAXPATHLEN]; static int dial_up; static void DoLoop(struct bundle *); static void TerminalStop(int); static const char *ex_desc(int); static void TtyInit(int DontWantInt) { struct termios newtio; int stat; stat = fcntl(netfd, F_GETFL, 0); if (stat > 0) { stat |= O_NONBLOCK; (void) fcntl(netfd, F_SETFL, stat); } newtio = oldtio; newtio.c_lflag &= ~(ECHO | ISIG | ICANON); newtio.c_iflag = 0; newtio.c_oflag &= ~OPOST; newtio.c_cc[VEOF] = _POSIX_VDISABLE; if (DontWantInt) newtio.c_cc[VINTR] = _POSIX_VDISABLE; newtio.c_cc[VMIN] = 1; newtio.c_cc[VTIME] = 0; newtio.c_cflag |= CS8; tcsetattr(netfd, TCSANOW, &newtio); comtio = newtio; } /* * Set tty into command mode. We allow canonical input and echo processing. */ void TtyCommandMode(struct bundle *bundle, int prompt) { struct termios newtio; int stat; if (!(mode & MODE_INTER)) return; tcgetattr(netfd, &newtio); newtio.c_lflag |= (ECHO | ISIG | ICANON); newtio.c_iflag = oldtio.c_iflag; newtio.c_oflag |= OPOST; tcsetattr(netfd, TCSADRAIN, &newtio); stat = fcntl(netfd, F_GETFL, 0); if (stat > 0) { stat |= O_NONBLOCK; (void) fcntl(netfd, F_SETFL, stat); } TermMode = 0; if (prompt) Prompt(bundle); } /* * Set tty into terminal mode which is used while we invoke term command. */ void TtyTermMode() { int stat; tcsetattr(netfd, TCSADRAIN, &comtio); stat = fcntl(netfd, F_GETFL, 0); if (stat > 0) { stat &= ~O_NONBLOCK; (void) fcntl(netfd, F_SETFL, stat); } TermMode = 1; } void TtyOldMode() { int stat; stat = fcntl(netfd, F_GETFL, 0); if (stat > 0) { stat &= ~O_NONBLOCK; (void) fcntl(netfd, F_SETFL, stat); } tcsetattr(netfd, TCSADRAIN, &oldtio); } static struct bundle *SignalBundle; int CleaningUp; void Cleanup(int excode) { CleaningUp = 1; reconnect(RECON_FALSE); if (bundle_Phase(SignalBundle) != PHASE_DEAD) { bundle_Close(SignalBundle, NULL); return; } DropClient(1); ServerClose(); ID0unlink(pid_filename); if (mode & MODE_BACKGROUND && BGFiledes[1] != -1) { char c = EX_ERRDEAD; if (write(BGFiledes[1], &c, 1) == 1) LogPrintf(LogPHASE, "Parent notified of failure.\n"); else LogPrintf(LogPHASE, "Failed to notify parent of failure.\n"); close(BGFiledes[1]); } LogPrintf(LogPHASE, "PPP Terminated (%s).\n", ex_desc(excode)); TtyOldMode(); link_Destroy(physical2link(SignalBundle->physical)); LogClose(); exit(excode); } static void CloseConnection(int signo) { /* NOTE, these are manual, we've done a setsid() */ pending_signal(SIGINT, SIG_IGN); LogPrintf(LogPHASE, "Caught signal %d, abort connection\n", signo); reconnectState = RECON_FALSE; reconnectCount = 0; link_Close(&SignalBundle->physical->link, SignalBundle, 0); dial_up = 0; pending_signal(SIGINT, CloseConnection); } static void CloseSession(int signo) { if (BGPid) { kill(BGPid, SIGINT); exit(EX_TERM); } LogPrintf(LogPHASE, "Signal %d, terminate.\n", signo); Cleanup(EX_TERM); } static void TerminalCont(int signo) { pending_signal(SIGCONT, SIG_DFL); pending_signal(SIGTSTP, TerminalStop); TtyCommandMode(SignalBundle, getpgrp() == tcgetpgrp(netfd)); } static void TerminalStop(int signo) { pending_signal(SIGCONT, TerminalCont); TtyOldMode(); pending_signal(SIGTSTP, SIG_DFL); kill(getpid(), signo); } static void SetUpServer(int signo) { int res; VarHaveLocalAuthKey = 0; LocalAuthInit(); if ((res = ServerTcpOpen(SERVER_PORT + SignalBundle->unit)) != 0) LogPrintf(LogERROR, "SIGUSR1: Failed %d to open port %d\n", res, SERVER_PORT + SignalBundle->unit); } static void BringDownServer(int signo) { VarHaveLocalAuthKey = 0; LocalAuthInit(); ServerClose(); } static const char * ex_desc(int ex) { static char num[12]; static const char *desc[] = { "normal", "start", "sock", "modem", "dial", "dead", "done", "reboot", "errdead", "hangup", "term", "nodial", "nologin" }; if (ex >= 0 && ex < sizeof desc / sizeof *desc) return desc[ex]; snprintf(num, sizeof num, "%d", ex); return num; } static void Usage(void) { fprintf(stderr, "Usage: ppp [-auto | -background | -direct | -dedicated | -ddial ]" #ifndef NOALIAS " [ -alias ]" #endif " [system]\n"); exit(EX_START); } static char * ProcessArgs(int argc, char **argv) { int optc; char *cp; optc = 0; mode = MODE_INTER; while (argc > 0 && **argv == '-') { cp = *argv + 1; if (strcmp(cp, "auto") == 0) { mode |= MODE_AUTO; mode &= ~MODE_INTER; } else if (strcmp(cp, "background") == 0) { mode |= MODE_BACKGROUND; mode &= ~MODE_INTER; } else if (strcmp(cp, "direct") == 0) { mode |= MODE_DIRECT; mode &= ~MODE_INTER; } else if (strcmp(cp, "dedicated") == 0) { mode |= MODE_DEDICATED; mode &= ~MODE_INTER; } else if (strcmp(cp, "ddial") == 0) { mode |= MODE_DDIAL; mode &= ~MODE_INTER; #ifndef NOALIAS } else if (strcmp(cp, "alias") == 0) { if (loadAliasHandlers(&VarAliasHandlers) == 0) mode |= MODE_ALIAS; else LogPrintf(LogWARN, "Cannot load alias library\n"); optc--; /* this option isn't exclusive */ #endif } else Usage(); optc++; argv++; argc--; } if (argc > 1) { fprintf(stderr, "specify only one system label.\n"); exit(EX_START); } if (optc > 1) { fprintf(stderr, "specify only one mode.\n"); exit(EX_START); } return argc == 1 ? *argv : NULL; /* Don't SetLabel yet ! */ } int main(int argc, char **argv) { FILE *lockfile; char *name, *label; int nfds; struct bundle *bundle; nfds = getdtablesize(); if (nfds >= FD_SETSIZE) /* * If we've got loads of file descriptors, make sure they're all * closed. If they aren't, we may end up with a seg fault when our * `fd_set's get too big when select()ing ! */ while (--nfds > 2) close(nfds); VarTerm = 0; name = strrchr(argv[0], '/'); LogOpen(name ? name + 1 : argv[0]); tcgetattr(STDIN_FILENO, &oldtio); /* Save original tty mode */ argc--; argv++; label = ProcessArgs(argc, argv); if (!(mode & MODE_DIRECT)) VarTerm = stdout; ID0init(); if (ID0realuid() != 0) { char conf[200], *ptr; snprintf(conf, sizeof conf, "%s/%s", _PATH_PPP, CONFFILE); do { if (!access(conf, W_OK)) { LogPrintf(LogALERT, "ppp: Access violation: Please protect %s\n", conf); return -1; } ptr = conf + strlen(conf)-2; while (ptr > conf && *ptr != '/') *ptr-- = '\0'; } while (ptr >= conf); } if (!ValidSystem(label)) { fprintf(stderr, "You may not use ppp in this mode with this label\n"); if (mode & MODE_DIRECT) { const char *l; l = label ? label : "default"; LogPrintf(LogWARN, "Label %s rejected -direct connection\n", l); } LogClose(); return 1; } if (mode & MODE_INTER) VarLocalAuth = LOCAL_AUTH; if ((bundle = bundle_Create("/dev/tun")) == NULL) { LogPrintf(LogWARN, "bundle_Create: %s\n", strerror(errno)); return EX_START; } if (!GetShortHost()) return 1; IsInteractive(1); IpcpDefAddress(); SignalBundle = bundle; if (SelectSystem(bundle, "default", CONFFILE) < 0 && VarTerm) fprintf(VarTerm, "Warning: No default entry is given in config file.\n"); if ((mode & MODE_OUTGOING_DAEMON) && !(mode & MODE_DEDICATED)) if (label == NULL) { if (VarTerm) fprintf(VarTerm, "Destination system must be specified in" " auto, background or ddial mode.\n"); return EX_START; } pending_signal(SIGHUP, CloseSession); pending_signal(SIGTERM, CloseSession); pending_signal(SIGINT, CloseConnection); pending_signal(SIGQUIT, CloseSession); #ifdef SIGPIPE signal(SIGPIPE, SIG_IGN); #endif #ifdef SIGALRM pending_signal(SIGALRM, SIG_IGN); #endif if (mode & MODE_INTER) { #ifdef SIGTSTP pending_signal(SIGTSTP, TerminalStop); #endif #ifdef SIGTTIN pending_signal(SIGTTIN, TerminalStop); #endif #ifdef SIGTTOU pending_signal(SIGTTOU, SIG_IGN); #endif } if (!(mode & MODE_INTER)) { #ifdef SIGUSR1 pending_signal(SIGUSR1, SetUpServer); #endif #ifdef SIGUSR2 pending_signal(SIGUSR2, BringDownServer); #endif } if (label) { if (SelectSystem(bundle, label, CONFFILE) < 0) { LogPrintf(LogWARN, "Destination system %s not found in conf file.\n", GetLabel()); Cleanup(EX_START); } /* * We don't SetLabel() 'till now in case SelectSystem() has an * embeded load "otherlabel" command. */ SetLabel(label); if (mode & MODE_AUTO && IpcpInfo.DefHisAddress.ipaddr.s_addr == INADDR_ANY) { LogPrintf(LogWARN, "You must \"set ifaddr\" in label %s for auto mode.\n", label); Cleanup(EX_START); } } if (mode & MODE_DAEMON) { if (mode & MODE_BACKGROUND) { if (pipe(BGFiledes)) { LogPrintf(LogERROR, "pipe: %s\n", strerror(errno)); Cleanup(EX_SOCK); } } if (!(mode & MODE_DIRECT)) { pid_t bgpid; bgpid = fork(); if (bgpid == -1) { LogPrintf(LogERROR, "fork: %s\n", strerror(errno)); Cleanup(EX_SOCK); } if (bgpid) { char c = EX_NORMAL; if (mode & MODE_BACKGROUND) { /* Wait for our child to close its pipe before we exit. */ BGPid = bgpid; close(BGFiledes[1]); if (read(BGFiledes[0], &c, 1) != 1) { fprintf(VarTerm, "Child exit, no status.\n"); LogPrintf(LogPHASE, "Parent: Child exit, no status.\n"); } else if (c == EX_NORMAL) { fprintf(VarTerm, "PPP enabled.\n"); LogPrintf(LogPHASE, "Parent: PPP enabled.\n"); } else { fprintf(VarTerm, "Child failed (%s).\n", ex_desc((int) c)); LogPrintf(LogPHASE, "Parent: Child failed (%s).\n", ex_desc((int) c)); } close(BGFiledes[0]); } return c; } else if (mode & MODE_BACKGROUND) close(BGFiledes[0]); } VarTerm = 0; /* We know it's currently stdout */ close(STDOUT_FILENO); close(STDERR_FILENO); if (mode & MODE_DIRECT) /* STDIN_FILENO gets used by modem_Open in DIRECT mode */ TtyInit(1); else if (mode & MODE_DAEMON) { setsid(); close(STDIN_FILENO); } } else { close(STDIN_FILENO); if ((netfd = open(_PATH_TTY, O_RDONLY)) < 0) { fprintf(stderr, "Cannot open %s for intput !\n", _PATH_TTY); return 2; } close(STDERR_FILENO); TtyInit(0); TtyCommandMode(bundle, 1); } snprintf(pid_filename, sizeof pid_filename, "%stun%d.pid", _PATH_VARRUN, bundle->unit); lockfile = ID0fopen(pid_filename, "w"); if (lockfile != NULL) { fprintf(lockfile, "%d\n", (int) getpid()); fclose(lockfile); } #ifndef RELEASE_CRUNCH else LogPrintf(LogALERT, "Warning: Can't create %s: %s\n", pid_filename, strerror(errno)); #endif LogPrintf(LogPHASE, "PPP Started.\n"); do DoLoop(bundle); while (mode & MODE_DEDICATED); Cleanup(EX_DONE); return 0; } /* * Turn into packet mode, where we speak PPP. */ void PacketMode(struct bundle *bundle, int delay) { if (modem_Raw(bundle->physical) < 0) { LogPrintf(LogWARN, "PacketMode: Not connected.\n"); return; } LcpInit(bundle, bundle->physical); IpcpInit(bundle, physical2link(bundle->physical)); CcpInit(bundle, physical2link(bundle->physical)); LcpUp(); LcpOpen(delay); if (mode & MODE_INTER) TtyCommandMode(bundle, 0); if (VarTerm) { fprintf(VarTerm, "Packet mode.\n"); /* aft_cmd = 1; */ } } static void ShowHelp(void) { fprintf(stderr, "The following commands are available:\r\n"); fprintf(stderr, " ~p\tEnter Packet mode\r\n"); fprintf(stderr, " ~-\tDecrease log level\r\n"); fprintf(stderr, " ~+\tIncrease log level\r\n"); fprintf(stderr, " ~t\tShow timers (only in \"log debug\" mode)\r\n"); fprintf(stderr, " ~m\tShow memory map (only in \"log debug\" mode)\r\n"); fprintf(stderr, " ~.\tTerminate program\r\n"); fprintf(stderr, " ~?\tThis help\r\n"); } static void ReadTty(struct bundle *bundle) { int n; char ch; static int ttystate; char linebuff[LINE_LEN]; LogPrintf(LogDEBUG, "termode = %d, netfd = %d, mode = %d\n", TermMode, netfd, mode); if (!TermMode) { n = read(netfd, linebuff, sizeof linebuff - 1); if (n > 0) { aft_cmd = 1; if (linebuff[n-1] == '\n') linebuff[--n] = '\0'; else linebuff[n] = '\0'; if (n) DecodeCommand(bundle, linebuff, n, IsInteractive(0) ? NULL : "Client"); Prompt(bundle); } else if (n <= 0) { LogPrintf(LogPHASE, "Client connection closed.\n"); DropClient(0); } return; } /* * We are in terminal mode, decode special sequences */ n = read(netfd, &ch, 1); LogPrintf(LogDEBUG, "Got %d bytes (reading from the terminal)\n", n); if (n > 0) { switch (ttystate) { case 0: if (ch == '~') ttystate++; else /* XXX missing return value check */ Physical_Write(bundle->physical, &ch, n); break; case 1: switch (ch) { case '?': ShowHelp(); break; case 'p': /* * XXX: Should check carrier. */ if (LcpInfo.fsm.state <= ST_CLOSED) PacketMode(bundle, 0); break; case '.': TermMode = 1; aft_cmd = 1; TtyCommandMode(bundle, 1); break; case 't': if (LogIsKept(LogDEBUG)) { ShowTimers(); break; } case 'm': if (LogIsKept(LogDEBUG)) { ShowMemMap(NULL); break; } default: if (Physical_Write(bundle->physical, &ch, n) < 0) LogPrintf(LogERROR, "error writing to modem.\n"); break; } ttystate = 0; break; } } } /* * Here, we'll try to detect HDLC frame */ static const char *FrameHeaders[] = { "\176\377\003\300\041", "\176\377\175\043\300\041", "\176\177\175\043\100\041", "\176\175\337\175\043\300\041", "\176\175\137\175\043\100\041", NULL, }; static const u_char * HdlcDetect(struct physical *physical, u_char * cp, int n) { const char *ptr, *fp, **hp; cp[n] = '\0'; /* be sure to null terminated */ ptr = NULL; for (hp = FrameHeaders; *hp; hp++) { fp = *hp; if (Physical_IsSync(physical)) fp++; ptr = strstr((char *) cp, fp); if (ptr) break; } return ((const u_char *) ptr); } static struct pppTimer RedialTimer; static void RedialTimeout(void *v) { StopTimer(&RedialTimer); LogPrintf(LogPHASE, "Redialing timer expired.\n"); } static void StartRedialTimer(int Timeout) { StopTimer(&RedialTimer); if (Timeout) { RedialTimer.state = TIMER_STOPPED; if (Timeout > 0) RedialTimer.load = Timeout * SECTICKS; else RedialTimer.load = (random() % REDIAL_PERIOD) * SECTICKS; LogPrintf(LogPHASE, "Enter pause (%d) for redialing.\n", RedialTimer.load / SECTICKS); RedialTimer.func = RedialTimeout; StartTimer(&RedialTimer); } } #define IN_SIZE sizeof(struct sockaddr_in) #define UN_SIZE sizeof(struct sockaddr_in) #define ADDRSZ (IN_SIZE > UN_SIZE ? IN_SIZE : UN_SIZE) static void DoLoop(struct bundle *bundle) { fd_set rfds, wfds, efds; int pri, i, n, wfd, nfds; char hisaddr[ADDRSZ]; struct sockaddr *sa = (struct sockaddr *)hisaddr; struct sockaddr_in *sin = (struct sockaddr_in *)hisaddr; struct timeval timeout, *tp; int ssize = ADDRSZ; const u_char *cp; int tries; int qlen; int res; struct tun_data tun; #define rbuff tun.data if (mode & MODE_DIRECT) { LogPrintf(LogDEBUG, "Opening modem\n"); if (modem_Open(bundle->physical, bundle) < 0) return; LogPrintf(LogPHASE, "Packet mode enabled\n"); PacketMode(bundle, VarOpenMode); } else if (mode & MODE_DEDICATED) { if (!link_IsActive(physical2link(bundle->physical))) while (modem_Open(bundle->physical, bundle) < 0) nointr_sleep(VarReconnectTimer); } fflush(VarTerm); timeout.tv_sec = 0; timeout.tv_usec = 0; reconnectState = RECON_UNKNOWN; if (mode & MODE_BACKGROUND) dial_up = 1; /* Bring the line up */ else dial_up = 0; /* XXXX */ tries = 0; for (;;) { nfds = 0; FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); /* * If the link is down and we're in DDIAL mode, bring it back up. */ if (mode & MODE_DDIAL && LcpInfo.fsm.state <= ST_CLOSED) dial_up = 1; /* * If we lost carrier and want to re-establish the connection due to the * "set reconnect" value, we'd better bring the line back up. */ if (LcpInfo.fsm.state <= ST_CLOSED) { if (!dial_up && reconnectState == RECON_TRUE) { if (++reconnectCount <= VarReconnectTries) { LogPrintf(LogPHASE, "Connection lost, re-establish (%d/%d)\n", reconnectCount, VarReconnectTries); StartRedialTimer(VarReconnectTimer); dial_up = 1; } else { if (VarReconnectTries) LogPrintf(LogPHASE, "Connection lost, maximum (%d) times\n", VarReconnectTries); reconnectCount = 0; if (mode & MODE_BACKGROUND) Cleanup(EX_DEAD); } reconnectState = RECON_ENVOKED; } else if (mode & MODE_DEDICATED) PacketMode(bundle, VarOpenMode); } /* * If Ip packet for output is enqueued and require dial up, Just do it! */ if (dial_up && RedialTimer.state != TIMER_RUNNING) { LogPrintf(LogDEBUG, "going to dial: modem = %d\n", Physical_GetFD(bundle->physical)); if (modem_Open(bundle->physical, bundle) < 0) { tries++; if (!(mode & MODE_DDIAL) && VarDialTries) LogPrintf(LogCHAT, "Failed to open modem (attempt %u of %d)\n", tries, VarDialTries); else LogPrintf(LogCHAT, "Failed to open modem (attempt %u)\n", tries); if (!(mode & MODE_DDIAL) && VarDialTries && tries >= VarDialTries) { if (mode & MODE_BACKGROUND) Cleanup(EX_DIAL); /* Can't get the modem */ dial_up = 0; reconnectState = RECON_UNKNOWN; reconnectCount = 0; tries = 0; } else StartRedialTimer(VarRedialTimeout); } else { tries++; /* Tries are per number, not per list of * numbers. */ if (!(mode & MODE_DDIAL) && VarDialTries) LogPrintf(LogCHAT, "Dial attempt %u of %d\n", tries, VarDialTries); else LogPrintf(LogCHAT, "Dial attempt %u\n", tries); if ((res = modem_Dial(bundle->physical, bundle)) == EX_DONE) { PacketMode(bundle, VarOpenMode); dial_up = 0; reconnectState = RECON_UNKNOWN; tries = 0; } else { if (mode & MODE_BACKGROUND) { if (VarNextPhone == NULL || res == EX_SIG) Cleanup(EX_DIAL); /* Tried all numbers - no luck */ else /* Try all numbers in background mode */ StartRedialTimer(VarRedialNextTimeout); } else if (!(mode & MODE_DDIAL) && ((VarDialTries && tries >= VarDialTries) || res == EX_SIG)) { /* I give up ! Can't get through :( */ StartRedialTimer(VarRedialTimeout); dial_up = 0; reconnectState = RECON_UNKNOWN; reconnectCount = 0; tries = 0; } else if (VarNextPhone == NULL) /* Dial failed. Keep quite during redial wait period. */ StartRedialTimer(VarRedialTimeout); else StartRedialTimer(VarRedialNextTimeout); } } } qlen = link_QueueLen(physical2link(bundle->physical)); if (qlen == 0) { IpStartOutput(physical2link(bundle->physical)); qlen = link_QueueLen(physical2link(bundle->physical)); } if (link_IsActive(physical2link(bundle->physical))) { /* XXX-ML this should probably be abstracted */ if (Physical_GetFD(bundle->physical) + 1 > nfds) nfds = Physical_GetFD(bundle->physical) + 1; Physical_FD_SET(bundle->physical, &rfds); Physical_FD_SET(bundle->physical, &efds); if (qlen > 0) { Physical_FD_SET(bundle->physical, &wfds); } } if (server >= 0) { if (server + 1 > nfds) nfds = server + 1; FD_SET(server, &rfds); } #ifndef SIGALRM /* * *** IMPORTANT *** * CPU is serviced every TICKUNIT micro seconds. This value must be chosen * with great care. If this values is too big, it results in loss of * characters from the modem and poor response. If this value is too * small, ppp eats too much CPU time. */ usleep(TICKUNIT); TimerService(); #else handle_signals(); #endif /* If there are aren't many packets queued, look for some more. */ if (qlen < 20 && bundle->tun_fd >= 0) { if (bundle->tun_fd + 1 > nfds) nfds = bundle->tun_fd + 1; FD_SET(bundle->tun_fd, &rfds); } if (netfd >= 0) { if (netfd + 1 > nfds) nfds = netfd + 1; FD_SET(netfd, &rfds); FD_SET(netfd, &efds); } #ifndef SIGALRM /* * Normally, select() will not block because modem is writable. In AUTO * mode, select will block until we find packet from tun */ tp = (RedialTimer.state == TIMER_RUNNING) ? &timeout : NULL; i = select(nfds, &rfds, &wfds, &efds, tp); #else /* * When SIGALRM timer is running, a select function will be return -1 and * EINTR after a Time Service signal hundler is done. If the redial * timer is not running and we are trying to dial, poll with a 0 value * timer. */ tp = (dial_up && RedialTimer.state != TIMER_RUNNING) ? &timeout : NULL; i = select(nfds, &rfds, &wfds, &efds, tp); #endif if (i == 0) { continue; } if (i < 0) { if (errno == EINTR) { handle_signals(); continue; } LogPrintf(LogERROR, "DoLoop: select(): %s\n", strerror(errno)); break; } if ((netfd >= 0 && FD_ISSET(netfd, &efds)) || (Physical_FD_ISSET(bundle->physical, &efds))) { LogPrintf(LogALERT, "Exception detected.\n"); break; } if (server >= 0 && FD_ISSET(server, &rfds)) { wfd = accept(server, sa, &ssize); if (wfd < 0) { LogPrintf(LogERROR, "DoLoop: accept(): %s\n", strerror(errno)); continue; } switch (sa->sa_family) { case AF_LOCAL: LogPrintf(LogPHASE, "Connected to local client.\n"); break; case AF_INET: if (ntohs(sin->sin_port) < 1024) { LogPrintf(LogALERT, "Rejected client connection from %s:%u" "(invalid port number) !\n", inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); close(wfd); continue; } LogPrintf(LogPHASE, "Connected to client from %s:%u\n", inet_ntoa(sin->sin_addr), sin->sin_port); break; default: write(wfd, "Unrecognised access !\n", 22); close(wfd); continue; } if (netfd >= 0) { write(wfd, "Connection already in use.\n", 27); close(wfd); continue; } netfd = wfd; VarTerm = fdopen(netfd, "a+"); LocalAuthInit(); IsInteractive(1); Prompt(bundle); } if (netfd >= 0 && FD_ISSET(netfd, &rfds)) /* something to read from tty */ ReadTty(bundle); if (Physical_FD_ISSET(bundle->physical, &wfds)) { /* ready to write into modem */ link_StartOutput(physical2link(bundle->physical)); if (!link_IsActive(physical2link(bundle->physical))) dial_up = 1; } if (Physical_FD_ISSET(bundle->physical, &rfds)) { /* something to read from modem */ if (LcpInfo.fsm.state <= ST_CLOSED) nointr_usleep(10000); n = Physical_Read(bundle->physical, rbuff, sizeof rbuff); if ((mode & MODE_DIRECT) && n <= 0) { reconnect(RECON_TRUE); link_Close(&bundle->physical->link, bundle, 0); } else LogDumpBuff(LogASYNC, "ReadFromModem", rbuff, n); if (LcpInfo.fsm.state <= ST_CLOSED) { /* * In dedicated mode, we just discard input until LCP is started. */ if (!(mode & MODE_DEDICATED)) { cp = HdlcDetect(bundle->physical, rbuff, n); if (cp) { /* * LCP packet is detected. Turn ourselves into packet mode. */ if (cp != rbuff) { /* XXX missing return value checks */ Physical_Write(bundle->physical, rbuff, cp - rbuff); Physical_Write(bundle->physical, "\r\n", 2); } PacketMode(bundle, 0); } else if (VarTerm) write(fileno(VarTerm), rbuff, n); } } else { if (n > 0) async_Input(bundle, rbuff, n, bundle->physical); } } if (bundle->tun_fd >= 0 && FD_ISSET(bundle->tun_fd, &rfds)) { /* something to read from tun */ n = read(bundle->tun_fd, &tun, sizeof tun); if (n < 0) { LogPrintf(LogERROR, "read from tun: %s\n", strerror(errno)); continue; } n -= sizeof tun - sizeof tun.data; if (n <= 0) { LogPrintf(LogERROR, "read from tun: Only %d bytes read\n", n); continue; } if (!tun_check_header(tun, AF_INET)) continue; if (((struct ip *) rbuff)->ip_dst.s_addr == IpcpInfo.want_ipaddr.s_addr) { /* we've been asked to send something addressed *to* us :( */ if (VarLoopback) { pri = PacketCheck(rbuff, n, FL_IN); if (pri >= 0) { struct mbuf *bp; #ifndef NOALIAS if (mode & MODE_ALIAS) { VarPacketAliasIn(rbuff, sizeof rbuff); n = ntohs(((struct ip *) rbuff)->ip_len); } #endif bp = mballoc(n, MB_IPIN); memcpy(MBUF_CTOP(bp), rbuff, n); IpInput(bundle, bp); LogPrintf(LogDEBUG, "Looped back packet addressed to myself\n"); } continue; } else LogPrintf(LogDEBUG, "Oops - forwarding packet addressed to myself\n"); } /* * Process on-demand dialup. Output packets are queued within tunnel * device until IPCP is opened. */ if (LcpInfo.fsm.state <= ST_CLOSED && (mode & MODE_AUTO) && (pri = PacketCheck(rbuff, n, FL_DIAL)) >= 0) dial_up = 1; pri = PacketCheck(rbuff, n, FL_OUT); if (pri >= 0) { #ifndef NOALIAS if (mode & MODE_ALIAS) { VarPacketAliasOut(rbuff, sizeof rbuff); n = ntohs(((struct ip *) rbuff)->ip_len); } #endif IpEnqueue(pri, rbuff, n); } } } LogPrintf(LogDEBUG, "Job (DoLoop) done.\n"); }