Home | History | Annotate | Download | only in src
      1 /*-
      2  * Copyright (c) 1998 Brian Somers <brian (at) Awfulhak.org>
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     24  * SUCH DAMAGE.
     25  *
     26  * $FreeBSD: src/usr.sbin/ppp/prompt.c,v 1.31.26.1 2010/12/21 17:10:29 kensmith Exp $
     27  */
     28 
     29 #include <sys/param.h>
     30 #include <netinet/in.h>
     31 #include <netinet/in_systm.h>
     32 #include <netinet/ip.h>
     33 #include <sys/socket.h>
     34 #include <sys/un.h>
     35 
     36 #include <errno.h>
     37 #include <stdarg.h>
     38 #include <stdio.h>
     39 #include <stdlib.h>
     40 #include <string.h>
     41 #include <sys/fcntl.h>
     42 #include <termios.h>
     43 #include <unistd.h>
     44 
     45 #include "layer.h"
     46 #include "defs.h"
     47 #include "timer.h"
     48 #include "command.h"
     49 #include "log.h"
     50 #include "descriptor.h"
     51 #include "prompt.h"
     52 #include "fsm.h"
     53 #include "auth.h"
     54 #include "iplist.h"
     55 #include "throughput.h"
     56 #include "slcompress.h"
     57 #include "mbuf.h"
     58 #include "lqr.h"
     59 #include "hdlc.h"
     60 #include "lcp.h"
     61 #include "ncpaddr.h"
     62 #include "ipcp.h"
     63 #include "filter.h"
     64 #include "async.h"
     65 #include "ccp.h"
     66 #include "link.h"
     67 #include "physical.h"
     68 #include "mp.h"
     69 #ifndef NORADIUS
     70 #include "radius.h"
     71 #endif
     72 #include "ipv6cp.h"
     73 #include "ncp.h"
     74 #include "bundle.h"
     75 #include "chat.h"
     76 #include "chap.h"
     77 #include "cbcp.h"
     78 #include "datalink.h"
     79 #include "server.h"
     80 #include "main.h"
     81 
     82 static void
     83 prompt_Display(struct prompt *p)
     84 {
     85   /* XXX: See Index2Nam() - should we only figure this out once ? */
     86   static char shostname[MAXHOSTNAMELEN];
     87   const char *pconnect, *pauth;
     88 
     89   if (p->TermMode || !p->needprompt)
     90     return;
     91 
     92   p->needprompt = 0;
     93 
     94   if (p->nonewline)
     95     p->nonewline = 0;
     96   else
     97     fprintf(p->Term, "\n");
     98 
     99   if (p->auth == LOCAL_AUTH)
    100     pauth = " ON ";
    101   else
    102     pauth = " on ";
    103 
    104   if (p->bundle->ncp.ipcp.fsm.state == ST_OPENED)
    105     pconnect = "PPP";
    106 #ifndef NOINET6
    107   else if (!Enabled(p->bundle, OPT_IPCP) &&
    108 	   p->bundle->ncp.ipv6cp.fsm.state == ST_OPENED)
    109     pconnect = "PPP";
    110 #endif
    111   else if (bundle_Phase(p->bundle) == PHASE_NETWORK)
    112     pconnect = "PPp";
    113   else if (bundle_Phase(p->bundle) == PHASE_AUTHENTICATE)
    114     pconnect = "Ppp";
    115   else
    116     pconnect = "ppp";
    117 
    118   if (*shostname == '\0') {
    119     char *dot;
    120 
    121     if (gethostname(shostname, sizeof shostname) || *shostname == '\0')
    122       strcpy(shostname, "localhost");
    123     else if ((dot = strchr(shostname, '.')))
    124       *dot = '\0';
    125   }
    126 
    127   fprintf(p->Term, "%s%s%s> ", pconnect, pauth, shostname);
    128   fflush(p->Term);
    129 }
    130 
    131 static int
    132 prompt_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w __unused,
    133 		 fd_set *e, int *n)
    134 {
    135   struct prompt *p = descriptor2prompt(d);
    136   int sets;
    137 
    138   sets = 0;
    139 
    140   if (!p->active)
    141     return sets;
    142 
    143   if (p->fd_in >= 0) {
    144     if (r) {
    145       FD_SET(p->fd_in, r);
    146       log_Printf(LogTIMER, "prompt %s: fdset(r) %d\n", p->src.from, p->fd_in);
    147       sets++;
    148     }
    149     if (e) {
    150       FD_SET(p->fd_in, e);
    151       log_Printf(LogTIMER, "prompt %s: fdset(e) %d\n", p->src.from, p->fd_in);
    152       sets++;
    153     }
    154     if (sets && *n < p->fd_in + 1)
    155       *n = p->fd_in + 1;
    156   }
    157 
    158   prompt_Display(p);
    159 
    160   return sets;
    161 }
    162 
    163 static int
    164 prompt_IsSet(struct fdescriptor *d, const fd_set *fdset)
    165 {
    166   struct prompt *p = descriptor2prompt(d);
    167   return p->fd_in >= 0 && FD_ISSET(p->fd_in, fdset);
    168 }
    169 
    170 
    171 static void
    172 prompt_ShowHelp(struct prompt *p)
    173 {
    174   prompt_Printf(p, "The following commands are available:\n");
    175   prompt_Printf(p, " ~p\tEnter Packet mode\n");
    176   prompt_Printf(p, " ~t\tShow timers\n");
    177   prompt_Printf(p, " ~m\tShow memory map\n");
    178   prompt_Printf(p, " ~.\tTerminate program\n");
    179   prompt_Printf(p, " ~?\tThis help\n");
    180 }
    181 
    182 static void
    183 prompt_Read(struct fdescriptor *d, struct bundle *bundle,
    184 	    const fd_set *fdset __unused)
    185 {
    186   struct prompt *p = descriptor2prompt(d);
    187   struct prompt *op;
    188   int n;
    189   char ch;
    190   char linebuff[LINE_LEN];
    191 
    192   if (p->TermMode == NULL) {
    193     n = read(p->fd_in, linebuff, sizeof linebuff - 1);
    194     if (n > 0) {
    195       if (linebuff[n-1] == '\n')
    196         linebuff[--n] = '\0';
    197       else
    198         linebuff[n] = '\0';
    199       p->nonewline = 1;		/* Maybe command_Decode does a prompt */
    200       prompt_Required(p);
    201       if (n) {
    202         if ((op = log_PromptContext) == NULL)
    203           log_PromptContext = p;
    204         if (!command_Decode(bundle, linebuff, n, p, p->src.from))
    205           prompt_Printf(p, "Syntax error\n");
    206         log_PromptContext = op;
    207       }
    208     } else if (n <= 0) {
    209       log_Printf(LogPHASE, "%s: Client connection closed.\n", p->src.from);
    210       if (!p->owner)
    211         Cleanup();
    212       prompt_Destroy(p, 0);
    213     }
    214     return;
    215   }
    216 
    217   switch (p->TermMode->state) {
    218     case DATALINK_CLOSED:
    219       prompt_Printf(p, "Link lost, terminal mode.\n");
    220       prompt_TtyCommandMode(p);
    221       p->nonewline = 0;
    222       prompt_Required(p);
    223       return;
    224 
    225     case DATALINK_READY:
    226       break;
    227 
    228     case DATALINK_OPEN:
    229       prompt_Printf(p, "\nPacket mode detected.\n");
    230       prompt_TtyCommandMode(p);
    231       p->nonewline = 0;
    232       /* We'll get a prompt because of our status change */
    233       /* FALLTHROUGH */
    234 
    235     default:
    236       /* Wait 'till we're in a state we care about */
    237       return;
    238   }
    239 
    240   /*
    241    * We are in terminal mode, decode special sequences
    242    */
    243   n = read(p->fd_in, &ch, 1);
    244   log_Printf(LogDEBUG, "Got %d bytes (reading from the terminal)\n", n);
    245 
    246   if (n > 0) {
    247     switch (p->readtilde) {
    248     case 0:
    249       if (ch == '~')
    250         p->readtilde = 1;
    251       else
    252 	if (physical_Write(p->TermMode->physical, &ch, n) < 0) {
    253 	  log_Printf(LogWARN, "error writing to modem: %s\n", strerror(errno));
    254           prompt_TtyCommandMode(p);
    255         }
    256       break;
    257     case 1:
    258       switch (ch) {
    259       case '?':
    260 	prompt_ShowHelp(p);
    261 	break;
    262       case 'p':
    263         datalink_Up(p->TermMode, 0, 1);
    264         prompt_Printf(p, "\nPacket mode.\n");
    265 	prompt_TtyCommandMode(p);
    266         break;
    267       case '.':
    268 	prompt_TtyCommandMode(p);
    269         p->nonewline = 0;
    270         prompt_Required(p);
    271 	break;
    272       case 't':
    273 	timer_Show(0, p);
    274 	break;
    275       case 'm':
    276         {
    277           struct cmdargs arg;
    278 
    279           arg.cmdtab = NULL;
    280           arg.cmd = NULL;
    281           arg.argc = 0;
    282           arg.argn = 0;
    283           arg.argv = NULL;
    284           arg.bundle = bundle;
    285           arg.cx = p->TermMode;
    286           arg.prompt = p;
    287 
    288 	  mbuf_Show(&arg);
    289         }
    290 	break;
    291       default:
    292 	if (physical_Write(p->TermMode->physical, &ch, n) < 0) {
    293 	  log_Printf(LogWARN, "error writing to modem: %s\n", strerror(errno));
    294           prompt_TtyCommandMode(p);
    295         }
    296 	break;
    297       }
    298       p->readtilde = 0;
    299       break;
    300     }
    301   }
    302 }
    303 
    304 static int
    305 prompt_Write(struct fdescriptor *d __unused, struct bundle *bundle __unused,
    306 	     const fd_set *fdset __unused)
    307 {
    308   /* We never want to write here ! */
    309   log_Printf(LogALERT, "prompt_Write: Internal error: Bad call !\n");
    310   return 0;
    311 }
    312 
    313 struct prompt *
    314 prompt_Create(struct server *s, struct bundle *bundle, int fd)
    315 {
    316   struct prompt *p = (struct prompt *)malloc(sizeof(struct prompt));
    317 
    318   if (p != NULL) {
    319     p->desc.type = PROMPT_DESCRIPTOR;
    320     p->desc.UpdateSet = prompt_UpdateSet;
    321     p->desc.IsSet = prompt_IsSet;
    322     p->desc.Read = prompt_Read;
    323     p->desc.Write = prompt_Write;
    324 
    325     if (fd == PROMPT_STD) {
    326       char *tty = ttyname(STDIN_FILENO);
    327 
    328       if (!tty) {
    329         free(p);
    330         return NULL;
    331       }
    332       p->fd_in = STDIN_FILENO;
    333       p->fd_out = STDOUT_FILENO;
    334       p->Term = stdout;
    335       p->owner = NULL;
    336       p->auth = LOCAL_AUTH;
    337       p->src.type = "Controller";
    338       strncpy(p->src.from, tty, sizeof p->src.from - 1);
    339       p->src.from[sizeof p->src.from - 1] = '\0';
    340       tcgetattr(p->fd_in, &p->oldtio);	/* Save original tty mode */
    341     } else {
    342       p->fd_in = p->fd_out = fd;
    343       p->Term = fdopen(fd, "a+");
    344       p->owner = s;
    345       p->auth = *s->cfg.passwd ? LOCAL_NO_AUTH : LOCAL_AUTH;
    346       p->src.type = "unknown";
    347       *p->src.from = '\0';
    348     }
    349     p->TermMode = NULL;
    350     p->nonewline = 1;
    351     p->needprompt = 1;
    352     p->readtilde = 0;
    353     p->bundle = bundle;
    354     log_RegisterPrompt(p);
    355   }
    356 
    357   return p;
    358 }
    359 
    360 void
    361 prompt_Destroy(struct prompt *p, int verbose)
    362 {
    363   if (p) {
    364     if (p->Term != stdout) {
    365       fclose(p->Term);
    366       close(p->fd_in);
    367       if (p->fd_out != p->fd_in)
    368         close(p->fd_out);
    369       if (verbose)
    370         log_Printf(LogPHASE, "%s: Client connection dropped.\n", p->src.from);
    371     } else
    372       prompt_TtyOldMode(p);
    373 
    374     log_UnRegisterPrompt(p);
    375     free(p);
    376   }
    377 }
    378 
    379 void
    380 prompt_Printf(struct prompt *p, const char *fmt,...)
    381 {
    382   if (p && p->active) {
    383     va_list ap;
    384 
    385     va_start(ap, fmt);
    386     prompt_vPrintf(p, fmt, ap);
    387     va_end(ap);
    388   }
    389 }
    390 
    391 void
    392 prompt_vPrintf(struct prompt *p, const char *fmt, va_list ap)
    393 {
    394   if (p && p->active) {
    395     char nfmt[LINE_LEN];
    396     const char *pfmt;
    397 
    398     if (p->TermMode) {
    399       /* Stuff '\r' in front of '\n' 'cos we're in raw mode */
    400       size_t len = strlen(fmt);
    401 
    402       if (len && len < sizeof nfmt - 1 && fmt[len-1] == '\n' &&
    403           (len == 1 || fmt[len-2] != '\r')) {
    404         strcpy(nfmt, fmt);
    405         strcpy(nfmt + len - 1, "\r\n");
    406         pfmt = nfmt;
    407       } else
    408         pfmt = fmt;
    409     } else
    410       pfmt = fmt;
    411     vfprintf(p->Term, pfmt, ap);
    412     fflush(p->Term);
    413     p->nonewline = 1;
    414   }
    415 }
    416 
    417 void
    418 prompt_TtyInit(struct prompt *p)
    419 {
    420   int stat, fd = p ? p->fd_in : STDIN_FILENO;
    421   struct termios newtio;
    422 
    423   stat = fcntl(fd, F_GETFL, 0);
    424   if (stat > 0) {
    425     stat |= O_NONBLOCK;
    426     fcntl(fd, F_SETFL, stat);
    427   }
    428 
    429   if (p)
    430     newtio = p->oldtio;
    431   else
    432     tcgetattr(fd, &newtio);
    433 
    434   newtio.c_lflag &= ~(ECHO | ISIG | ICANON);
    435   newtio.c_iflag = 0;
    436   newtio.c_oflag &= ~OPOST;
    437   if (!p)
    438     newtio.c_cc[VINTR] = _POSIX_VDISABLE;
    439   newtio.c_cc[VMIN] = 1;
    440   newtio.c_cc[VTIME] = 0;
    441   newtio.c_cflag |= CS8;
    442   tcsetattr(fd, TCSANOW, &newtio);
    443   if (p)
    444     p->comtio = newtio;
    445 }
    446 
    447 /*
    448  *  Set tty into command mode. We allow canonical input and echo processing.
    449  */
    450 void
    451 prompt_TtyCommandMode(struct prompt *p)
    452 {
    453   struct termios newtio;
    454   int stat;
    455 
    456   tcgetattr(p->fd_in, &newtio);
    457   newtio.c_lflag |= (ECHO | ISIG | ICANON);
    458   newtio.c_iflag = p->oldtio.c_iflag;
    459   newtio.c_oflag |= OPOST;
    460   tcsetattr(p->fd_in, TCSADRAIN, &newtio);
    461 
    462   stat = fcntl(p->fd_in, F_GETFL, 0);
    463   if (stat > 0) {
    464     stat |= O_NONBLOCK;
    465     fcntl(p->fd_in, F_SETFL, stat);
    466   }
    467 
    468   p->TermMode = NULL;
    469 }
    470 
    471 /*
    472  * Set tty into terminal mode which is used while we invoke term command.
    473  */
    474 void
    475 prompt_TtyTermMode(struct prompt *p, struct datalink *dl)
    476 {
    477   int stat;
    478 
    479   if (p->Term == stdout)
    480     tcsetattr(p->fd_in, TCSADRAIN, &p->comtio);
    481 
    482   stat = fcntl(p->fd_in, F_GETFL, 0);
    483   if (stat > 0) {
    484     stat &= ~O_NONBLOCK;
    485     fcntl(p->fd_in, F_SETFL, stat);
    486   }
    487   p->TermMode = dl;
    488 }
    489 
    490 void
    491 prompt_TtyOldMode(struct prompt *p)
    492 {
    493   int stat;
    494 
    495   stat = fcntl(p->fd_in, F_GETFL, 0);
    496   if (stat > 0) {
    497     stat &= ~O_NONBLOCK;
    498     fcntl(p->fd_in, F_SETFL, stat);
    499   }
    500 
    501   if (p->Term == stdout)
    502     tcsetattr(p->fd_in, TCSADRAIN, &p->oldtio);
    503 }
    504 
    505 pid_t
    506 prompt_pgrp(struct prompt *p)
    507 {
    508   return tcgetpgrp(p->fd_in);
    509 }
    510 
    511 int
    512 PasswdCommand(struct cmdargs const *arg)
    513 {
    514   const char *pass;
    515 
    516   if (!arg->prompt) {
    517     log_Printf(LogWARN, "passwd: Cannot specify without a prompt\n");
    518     return 0;
    519   }
    520 
    521   if (arg->prompt->owner == NULL) {
    522     log_Printf(LogWARN, "passwd: Not required\n");
    523     return 0;
    524   }
    525 
    526   if (arg->argc == arg->argn)
    527     pass = "";
    528   else if (arg->argc > arg->argn+1)
    529     return -1;
    530   else
    531     pass = arg->argv[arg->argn];
    532 
    533   if (!strcmp(arg->prompt->owner->cfg.passwd, pass))
    534     arg->prompt->auth = LOCAL_AUTH;
    535   else
    536     arg->prompt->auth = LOCAL_NO_AUTH;
    537 
    538   return 0;
    539 }
    540 
    541 static struct pppTimer bgtimer;
    542 
    543 static void
    544 prompt_TimedContinue(void *v)
    545 {
    546   prompt_Continue((struct prompt *)v);
    547 }
    548 
    549 void
    550 prompt_Continue(struct prompt *p)
    551 {
    552   timer_Stop(&bgtimer);
    553   if (getpgrp() == prompt_pgrp(p)) {
    554     prompt_TtyCommandMode(p);
    555     p->nonewline = 1;
    556     prompt_Required(p);
    557     log_ActivatePrompt(p);
    558   } else if (!p->owner) {
    559     bgtimer.func = prompt_TimedContinue;
    560     bgtimer.name = "prompt bg";
    561     bgtimer.load = SECTICKS;
    562     bgtimer.arg = p;
    563     timer_Start(&bgtimer);
    564   }
    565 }
    566 
    567 void
    568 prompt_Suspend(struct prompt *p)
    569 {
    570   if (getpgrp() == prompt_pgrp(p)) {
    571     prompt_TtyOldMode(p);
    572     log_DeactivatePrompt(p);
    573   }
    574 }
    575