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