1 /*- 2 * Copyright (c) 1998 Brian Somers <brian@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$ 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, fd_set *e, int *n) 133 { 134 struct prompt *p = descriptor2prompt(d); 135 int sets; 136 137 sets = 0; 138 139 if (!p->active) 140 return sets; 141 142 if (p->fd_in >= 0) { 143 if (r) { 144 FD_SET(p->fd_in, r); 145 log_Printf(LogTIMER, "prompt %s: fdset(r) %d\n", p->src.from, p->fd_in); 146 sets++; 147 } 148 if (e) { 149 FD_SET(p->fd_in, e); 150 log_Printf(LogTIMER, "prompt %s: fdset(e) %d\n", p->src.from, p->fd_in); 151 sets++; 152 } 153 if (sets && *n < p->fd_in + 1) 154 *n = p->fd_in + 1; 155 } 156 157 prompt_Display(p); 158 159 return sets; 160 } 161 162 static int 163 prompt_IsSet(struct fdescriptor *d, const fd_set *fdset) 164 { 165 struct prompt *p = descriptor2prompt(d); 166 return p->fd_in >= 0 && FD_ISSET(p->fd_in, fdset); 167 } 168 169 170 static void 171 prompt_ShowHelp(struct prompt *p) 172 { 173 prompt_Printf(p, "The following commands are available:\n"); 174 prompt_Printf(p, " ~p\tEnter Packet mode\n"); 175 prompt_Printf(p, " ~t\tShow timers\n"); 176 prompt_Printf(p, " ~m\tShow memory map\n"); 177 prompt_Printf(p, " ~.\tTerminate program\n"); 178 prompt_Printf(p, " ~?\tThis help\n"); 179 } 180 181 static void 182 prompt_Read(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset) 183 { 184 struct prompt *p = descriptor2prompt(d); 185 struct prompt *op; 186 int n; 187 char ch; 188 char linebuff[LINE_LEN]; 189 190 if (p->TermMode == NULL) { 191 n = read(p->fd_in, linebuff, sizeof linebuff - 1); 192 if (n > 0) { 193 if (linebuff[n-1] == '\n') 194 linebuff[--n] = '\0'; 195 else 196 linebuff[n] = '\0'; 197 p->nonewline = 1; /* Maybe command_Decode does a prompt */ 198 prompt_Required(p); 199 if (n) { 200 if ((op = log_PromptContext) == NULL) 201 log_PromptContext = p; 202 if (!command_Decode(bundle, linebuff, n, p, p->src.from)) 203 prompt_Printf(p, "Syntax error\n"); 204 log_PromptContext = op; 205 } 206 } else if (n <= 0) { 207 log_Printf(LogPHASE, "%s: Client connection closed.\n", p->src.from); 208 if (!p->owner) 209 Cleanup(EX_NORMAL); 210 prompt_Destroy(p, 0); 211 } 212 return; 213 } 214 215 switch (p->TermMode->state) { 216 case DATALINK_CLOSED: 217 prompt_Printf(p, "Link lost, terminal mode.\n"); 218 prompt_TtyCommandMode(p); 219 p->nonewline = 0; 220 prompt_Required(p); 221 return; 222 223 case DATALINK_READY: 224 break; 225 226 case DATALINK_OPEN: 227 prompt_Printf(p, "\nPacket mode detected.\n"); 228 prompt_TtyCommandMode(p); 229 p->nonewline = 0; 230 /* We'll get a prompt because of our status change */ 231 /* FALLTHROUGH */ 232 233 default: 234 /* Wait 'till we're in a state we care about */ 235 return; 236 } 237 238 /* 239 * We are in terminal mode, decode special sequences 240 */ 241 n = read(p->fd_in, &ch, 1); 242 log_Printf(LogDEBUG, "Got %d bytes (reading from the terminal)\n", n); 243 244 if (n > 0) { 245 switch (p->readtilde) { 246 case 0: 247 if (ch == '~') 248 p->readtilde = 1; 249 else 250 if (physical_Write(p->TermMode->physical, &ch, n) < 0) { 251 log_Printf(LogWARN, "error writing to modem: %s\n", strerror(errno)); 252 prompt_TtyCommandMode(p); 253 } 254 break; 255 case 1: 256 switch (ch) { 257 case '?': 258 prompt_ShowHelp(p); 259 break; 260 case 'p': 261 datalink_Up(p->TermMode, 0, 1); 262 prompt_Printf(p, "\nPacket mode.\n"); 263 prompt_TtyCommandMode(p); 264 break; 265 case '.': 266 prompt_TtyCommandMode(p); 267 p->nonewline = 0; 268 prompt_Required(p); 269 break; 270 case 't': 271 timer_Show(0, p); 272 break; 273 case 'm': 274 { 275 struct cmdargs arg; 276 277 arg.cmdtab = NULL; 278 arg.cmd = NULL; 279 arg.argc = 0; 280 arg.argn = 0; 281 arg.argv = NULL; 282 arg.bundle = bundle; 283 arg.cx = p->TermMode; 284 arg.prompt = p; 285 286 mbuf_Show(&arg); 287 } 288 break; 289 default: 290 if (physical_Write(p->TermMode->physical, &ch, n) < 0) { 291 log_Printf(LogWARN, "error writing to modem: %s\n", strerror(errno)); 292 prompt_TtyCommandMode(p); 293 } 294 break; 295 } 296 p->readtilde = 0; 297 break; 298 } 299 } 300 } 301 302 static int 303 prompt_Write(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset) 304 { 305 /* We never want to write here ! */ 306 log_Printf(LogALERT, "prompt_Write: Internal error: Bad call !\n"); 307 return 0; 308 } 309 310 struct prompt * 311 prompt_Create(struct server *s, struct bundle *bundle, int fd) 312 { 313 struct prompt *p = (struct prompt *)malloc(sizeof(struct prompt)); 314 315 if (p != NULL) { 316 p->desc.type = PROMPT_DESCRIPTOR; 317 p->desc.UpdateSet = prompt_UpdateSet; 318 p->desc.IsSet = prompt_IsSet; 319 p->desc.Read = prompt_Read; 320 p->desc.Write = prompt_Write; 321 322 if (fd == PROMPT_STD) { 323 char *tty = ttyname(STDIN_FILENO); 324 325 if (!tty) { 326 free(p); 327 return NULL; 328 } 329 p->fd_in = STDIN_FILENO; 330 p->fd_out = STDOUT_FILENO; 331 p->Term = stdout; 332 p->owner = NULL; 333 p->auth = LOCAL_AUTH; 334 p->src.type = "Controller"; 335 strncpy(p->src.from, tty, sizeof p->src.from - 1); 336 p->src.from[sizeof p->src.from - 1] = '\0'; 337 tcgetattr(p->fd_in, &p->oldtio); /* Save original tty mode */ 338 } else { 339 p->fd_in = p->fd_out = fd; 340 p->Term = fdopen(fd, "a+"); 341 p->owner = s; 342 p->auth = *s->cfg.passwd ? LOCAL_NO_AUTH : LOCAL_AUTH; 343 p->src.type = "unknown"; 344 *p->src.from = '\0'; 345 } 346 p->TermMode = NULL; 347 p->nonewline = 1; 348 p->needprompt = 1; 349 p->readtilde = 0; 350 p->bundle = bundle; 351 log_RegisterPrompt(p); 352 } 353 354 return p; 355 } 356 357 void 358 prompt_Destroy(struct prompt *p, int verbose) 359 { 360 if (p) { 361 if (p->Term != stdout) { 362 fclose(p->Term); 363 close(p->fd_in); 364 if (p->fd_out != p->fd_in) 365 close(p->fd_out); 366 if (verbose) 367 log_Printf(LogPHASE, "%s: Client connection dropped.\n", p->src.from); 368 } else 369 prompt_TtyOldMode(p); 370 371 log_UnRegisterPrompt(p); 372 free(p); 373 } 374 } 375 376 void 377 prompt_Printf(struct prompt *p, const char *fmt,...) 378 { 379 if (p && p->active) { 380 va_list ap; 381 382 va_start(ap, fmt); 383 prompt_vPrintf(p, fmt, ap); 384 va_end(ap); 385 } 386 } 387 388 void 389 prompt_vPrintf(struct prompt *p, const char *fmt, va_list ap) 390 { 391 if (p && p->active) { 392 char nfmt[LINE_LEN]; 393 const char *pfmt; 394 395 if (p->TermMode) { 396 /* Stuff '\r' in front of '\n' 'cos we're in raw mode */ 397 int len = strlen(fmt); 398 399 if (len && len < sizeof nfmt - 1 && fmt[len-1] == '\n' && 400 (len == 1 || fmt[len-2] != '\r')) { 401 strcpy(nfmt, fmt); 402 strcpy(nfmt + len - 1, "\r\n"); 403 pfmt = nfmt; 404 } else 405 pfmt = fmt; 406 } else 407 pfmt = fmt; 408 vfprintf(p->Term, pfmt, ap); 409 fflush(p->Term); 410 p->nonewline = 1; 411 } 412 } 413 414 void 415 prompt_TtyInit(struct prompt *p) 416 { 417 int stat, fd = p ? p->fd_in : STDIN_FILENO; 418 struct termios newtio; 419 420 stat = fcntl(fd, F_GETFL, 0); 421 if (stat > 0) { 422 stat |= O_NONBLOCK; 423 fcntl(fd, F_SETFL, stat); 424 } 425 426 if (p) 427 newtio = p->oldtio; 428 else 429 tcgetattr(fd, &newtio); 430 431 newtio.c_lflag &= ~(ECHO | ISIG | ICANON); 432 newtio.c_iflag = 0; 433 newtio.c_oflag &= ~OPOST; 434 if (!p) 435 newtio.c_cc[VINTR] = _POSIX_VDISABLE; 436 newtio.c_cc[VMIN] = 1; 437 newtio.c_cc[VTIME] = 0; 438 newtio.c_cflag |= CS8; 439 tcsetattr(fd, TCSANOW, &newtio); 440 if (p) 441 p->comtio = newtio; 442 } 443 444 /* 445 * Set tty into command mode. We allow canonical input and echo processing. 446 */ 447 void 448 prompt_TtyCommandMode(struct prompt *p) 449 { 450 struct termios newtio; 451 int stat; 452 453 tcgetattr(p->fd_in, &newtio); 454 newtio.c_lflag |= (ECHO | ISIG | ICANON); 455 newtio.c_iflag = p->oldtio.c_iflag; 456 newtio.c_oflag |= OPOST; 457 tcsetattr(p->fd_in, TCSADRAIN, &newtio); 458 459 stat = fcntl(p->fd_in, F_GETFL, 0); 460 if (stat > 0) { 461 stat |= O_NONBLOCK; 462 fcntl(p->fd_in, F_SETFL, stat); 463 } 464 465 p->TermMode = NULL; 466 } 467 468 /* 469 * Set tty into terminal mode which is used while we invoke term command. 470 */ 471 void 472 prompt_TtyTermMode(struct prompt *p, struct datalink *dl) 473 { 474 int stat; 475 476 if (p->Term == stdout) 477 tcsetattr(p->fd_in, TCSADRAIN, &p->comtio); 478 479 stat = fcntl(p->fd_in, F_GETFL, 0); 480 if (stat > 0) { 481 stat &= ~O_NONBLOCK; 482 fcntl(p->fd_in, F_SETFL, stat); 483 } 484 p->TermMode = dl; 485 } 486 487 void 488 prompt_TtyOldMode(struct prompt *p) 489 { 490 int stat; 491 492 stat = fcntl(p->fd_in, F_GETFL, 0); 493 if (stat > 0) { 494 stat &= ~O_NONBLOCK; 495 fcntl(p->fd_in, F_SETFL, stat); 496 } 497 498 if (p->Term == stdout) 499 tcsetattr(p->fd_in, TCSADRAIN, &p->oldtio); 500 } 501 502 pid_t 503 prompt_pgrp(struct prompt *p) 504 { 505 return tcgetpgrp(p->fd_in); 506 } 507 508 int 509 PasswdCommand(struct cmdargs const *arg) 510 { 511 const char *pass; 512 513 if (!arg->prompt) { 514 log_Printf(LogWARN, "passwd: Cannot specify without a prompt\n"); 515 return 0; 516 } 517 518 if (arg->prompt->owner == NULL) { 519 log_Printf(LogWARN, "passwd: Not required\n"); 520 return 0; 521 } 522 523 if (arg->argc == arg->argn) 524 pass = ""; 525 else if (arg->argc > arg->argn+1) 526 return -1; 527 else 528 pass = arg->argv[arg->argn]; 529 530 if (!strcmp(arg->prompt->owner->cfg.passwd, pass)) 531 arg->prompt->auth = LOCAL_AUTH; 532 else 533 arg->prompt->auth = LOCAL_NO_AUTH; 534 535 return 0; 536 } 537 538 static struct pppTimer bgtimer; 539 540 static void 541 prompt_TimedContinue(void *v) 542 { 543 prompt_Continue((struct prompt *)v); 544 } 545 546 void 547 prompt_Continue(struct prompt *p) 548 { 549 timer_Stop(&bgtimer); 550 if (getpgrp() == prompt_pgrp(p)) { 551 prompt_TtyCommandMode(p); 552 p->nonewline = 1; 553 prompt_Required(p); 554 log_ActivatePrompt(p); 555 } else if (!p->owner) { 556 bgtimer.func = prompt_TimedContinue; 557 bgtimer.name = "prompt bg"; 558 bgtimer.load = SECTICKS; 559 bgtimer.arg = p; 560 timer_Start(&bgtimer); 561 } 562 } 563 564 void 565 prompt_Suspend(struct prompt *p) 566 { 567 if (getpgrp() == prompt_pgrp(p)) { 568 prompt_TtyOldMode(p); 569 log_DeactivatePrompt(p); 570 } 571 } 572