command.c revision 26911
1/* 2 * PPP User command processing module 3 * 4 * Written by Toshiharu OHNO (tony-o@iij.ad.jp) 5 * 6 * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd. 7 * 8 * Redistribution and use in source and binary forms are permitted 9 * provided that the above copyright notice and this paragraph are 10 * duplicated in all such forms and that any documentation, 11 * advertising materials, and other materials related to such 12 * distribution and use acknowledge that the software was developed 13 * by the Internet Initiative Japan, Inc. The name of the 14 * IIJ may not be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 18 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 19 * 20 * $Id: command.c,v 1.61 1997/06/23 23:10:07 brian Exp $ 21 * 22 */ 23#include <sys/types.h> 24#include <ctype.h> 25#include <termios.h> 26#include <sys/wait.h> 27#include <time.h> 28#include <netdb.h> 29#include <sys/socket.h> 30#include <netinet/in.h> 31#include <arpa/inet.h> 32#include <net/route.h> 33#include <paths.h> 34#include <alias.h> 35#include <fcntl.h> 36#include <errno.h> 37#include "fsm.h" 38#include "phase.h" 39#include "lcp.h" 40#include "ipcp.h" 41#include "modem.h" 42#include "filter.h" 43#include "command.h" 44#include "alias_cmd.h" 45#include "hdlc.h" 46#include "loadalias.h" 47#include "vars.h" 48#include "systems.h" 49#include "chat.h" 50#include "os.h" 51#include "timeout.h" 52 53extern void Cleanup(), TtyTermMode(), PacketMode(); 54extern int EnableCommand(), DisableCommand(), DisplayCommand(); 55extern int AcceptCommand(), DenyCommand(); 56static int AliasCommand(); 57extern int LocalAuthCommand(); 58extern int LoadCommand(), SaveCommand(); 59extern int ChangeParity(char *); 60extern int SelectSystem(); 61extern int ShowRoute(); 62extern void TtyOldMode(), TtyCommandMode(); 63extern struct pppvars pppVars; 64extern struct cmdtab const SetCommands[]; 65 66extern char *IfDevName; 67 68struct in_addr ifnetmask; 69int randinit; 70 71static int ShowCommand(), TerminalCommand(), QuitCommand(); 72static int CloseCommand(), DialCommand(), DownCommand(); 73static int SetCommand(), AddCommand(), DeleteCommand(); 74static int ShellCommand(); 75 76static int 77HelpCommand(list, argc, argv, plist) 78struct cmdtab *list; 79int argc; 80char **argv; 81struct cmdtab *plist; 82{ 83 struct cmdtab *cmd; 84 int n; 85 86 if (!VarTerm) 87 return 0; 88 89 if (argc > 0) { 90 for (cmd = plist; cmd->name; cmd++) 91 if (strcasecmp(cmd->name, *argv) == 0 && (cmd->lauth & VarLocalAuth)) { 92 fprintf(VarTerm, "%s\n", cmd->syntax); 93 return 0; 94 } 95 96 return -1; 97 } 98 99 n = 0; 100 for (cmd = plist; cmd->func; cmd++) 101 if (cmd->name && (cmd->lauth & VarLocalAuth)) { 102 fprintf(VarTerm, " %-9s: %-20s\n", cmd->name, cmd->helpmes); 103 n++; 104 } 105 106 if (n & 1) 107 fprintf(VarTerm, "\n"); 108 109 return 0; 110} 111 112int 113IsInteractive() 114{ 115 char *mes = NULL; 116 117 if (mode & MODE_DDIAL) 118 mes = "Working in dedicated dial mode."; 119 else if (mode & MODE_BACKGROUND) 120 mes = "Working in background mode."; 121 else if (mode & MODE_AUTO) 122 mes = "Working in auto mode."; 123 else if (mode & MODE_DIRECT) 124 mes = "Working in direct mode."; 125 else if (mode & MODE_DEDICATED) 126 mes = "Working in dedicated mode."; 127 if (mes) { 128 if (VarTerm) 129 fprintf(VarTerm, "%s\n", mes); 130 return 0; 131 } 132 return 1; 133} 134 135static int 136DialCommand(cmdlist, argc, argv) 137struct cmdtab *cmdlist; 138int argc; 139char **argv; 140{ 141 int tries; 142 int res; 143 144 if (LcpFsm.state > ST_CLOSED) { 145 if (VarTerm) 146 fprintf(VarTerm, "LCP state is [%s]\n", StateNames[LcpFsm.state]); 147 return 0; 148 } 149 150 if (!IsInteractive()) 151 return(1); 152 153 if (argc > 0) { 154 if (SelectSystem(*argv, CONFFILE) < 0) { 155 if (VarTerm) 156 fprintf(VarTerm, "%s: not found.\n", *argv); 157 return -1; 158 } 159 } 160 161 tries = 0; 162 do { 163 if (VarTerm) 164 fprintf(VarTerm, "Dial attempt %u of %d\n", ++tries, VarDialTries); 165 modem = OpenModem(mode); 166 if (modem < 0) { 167 if (VarTerm) 168 fprintf(VarTerm, "Failed to open modem.\n"); 169 break; 170 } 171 if ((res = DialModem()) == EX_DONE) { 172 sleep(1); 173 ModemTimeout(); 174 PacketMode(); 175 break; 176 } else if (res == EX_SIG) 177 return 1; 178 } while (VarDialTries == 0 || tries < VarDialTries); 179 180 return 0; 181} 182 183static int 184ShellCommand(cmdlist, argc, argv) 185struct cmdtab *cmdlist; 186int argc; 187char **argv; 188{ 189 const char *shell; 190 pid_t shpid; 191 FILE *oVarTerm; 192 193#ifdef SHELL_ONLY_INTERACTIVELY 194 /* we're only allowed to shell when we run ppp interactively */ 195 if (mode != MODE_INTER) { 196 LogPrintf(LogWARN, "Can only start a shell in interactive mode\n"); 197 return 1; 198 } 199#endif 200#ifdef NO_SHELL_IN_AUTO_INTERACTIVE 201 /* 202 * we want to stop shell commands when we've got a telnet connection 203 * to an auto mode ppp 204 */ 205 if ((mode & (MODE_AUTO|MODE_INTER)) == (MODE_AUTO|MODE_INTER)) { 206 LogPrintf(LogWARN, "Shell is not allowed interactively in auto mode\n"); 207 return 1; 208 } 209#endif 210 211 if(argc == 0 && !(mode & MODE_INTER)) { 212 LogPrintf(LogWARN, "Can only start an interactive shell in" 213 " interactive mode\n"); 214 return 1; 215 } 216 217 if((shell = getenv("SHELL")) == 0) 218 shell = _PATH_BSHELL; 219 220 if((shpid = fork()) == 0) { 221 int dtablesize, i, fd; 222 223 if (VarTerm) 224 fd = fileno(VarTerm); 225 else if ((fd = open("/dev/null", O_RDWR)) == -1) { 226 LogPrintf(LogALERT, "Failed to open /dev/null: %s\n", strerror(errno)); 227 exit(1); 228 } 229 230 for (i = 0; i < 3; i++) 231 dup2(fd, i); 232 233 if (fd > 2) 234 if (VarTerm) { 235 oVarTerm = VarTerm; 236 VarTerm = 0; 237 if (oVarTerm && oVarTerm != stdout) 238 fclose(oVarTerm); 239 } else 240 close(fd); 241 242 for (dtablesize = getdtablesize(), i = 3; i < dtablesize; i++) 243 (void)close(i); 244 245 /* 246 * We are running setuid, we should change to 247 * real user for avoiding security problems. 248 */ 249 if (setgid(getgid()) < 0) { 250 LogPrintf(LogERROR, "setgid: %s\n", strerror(errno)); 251 exit(1); 252 } 253 if (setuid(getuid()) < 0) { 254 LogPrintf(LogERROR, "setuid: %s\n", strerror(errno)); 255 exit(1); 256 } 257 TtyOldMode(); 258 if(argc > 0) { 259 /* substitute pseudo args */ 260 for (i=1; i<argc; i++) { 261 if (strcasecmp(argv[i], "HISADDR") == 0) { 262 argv[i] = strdup(inet_ntoa(IpcpInfo.his_ipaddr)); 263 } 264 if (strcasecmp(argv[i], "INTERFACE") == 0) { 265 argv[i] = strdup(IfDevName); 266 } 267 if (strcasecmp(argv[i], "MYADDR") == 0) { 268 argv[i] = strdup(inet_ntoa(IpcpInfo.want_ipaddr)); 269 } 270 } 271 (void)execvp(argv[0], argv); 272 } 273 else 274 (void)execl(shell, shell, NULL); 275 276 LogPrintf(LogWARN, "exec() of %s failed\n", argc > 0 ? argv[0] : shell); 277 exit(255); 278 } 279 280 if( shpid == (pid_t)-1 ) { 281 LogPrintf(LogERROR, "Fork failed: %s\n", strerror(errno)); 282 } else { 283 int status; 284 (void)waitpid(shpid, &status, 0); 285 } 286 287 TtyCommandMode(1); 288 289 return(0); 290} 291 292struct cmdtab const Commands[] = { 293 { "accept", NULL, AcceptCommand, LOCAL_AUTH, 294 "accept option request", "accept option .."}, 295 { "add", NULL, AddCommand, LOCAL_AUTH, 296 "add route", "add dest mask gateway"}, 297 { "close", NULL, CloseCommand, LOCAL_AUTH, 298 "Close connection", "close"}, 299 { "delete", NULL, DeleteCommand, LOCAL_AUTH, 300 "delete route", "delete ALL | dest [gateway [mask]]"}, 301 { "deny", NULL, DenyCommand, LOCAL_AUTH, 302 "Deny option request", "deny option .."}, 303 { "dial", "call", DialCommand, LOCAL_AUTH, 304 "Dial and login", "dial|call [remote]"}, 305 { "disable", NULL, DisableCommand, LOCAL_AUTH, 306 "Disable option", "disable option .."}, 307 { "display", NULL, DisplayCommand, LOCAL_AUTH, 308 "Display option configs", "display"}, 309 { "enable", NULL, EnableCommand, LOCAL_AUTH, 310 "Enable option", "enable option .."}, 311 { "passwd", NULL, LocalAuthCommand,LOCAL_NO_AUTH, 312 "Password for manipulation", "passwd option .."}, 313 { "load", NULL, LoadCommand, LOCAL_AUTH, 314 "Load settings", "load [remote]"}, 315 { "save", NULL, SaveCommand, LOCAL_AUTH, 316 "Save settings", "save"}, 317 { "set", "setup", SetCommand, LOCAL_AUTH, 318 "Set parameters", "set[up] var value"}, 319 { "shell", "!", ShellCommand, LOCAL_AUTH, 320 "Run a subshell", "shell|! [sh command]"}, 321 { "show", NULL, ShowCommand, LOCAL_AUTH, 322 "Show status and statictics", "show var"}, 323 { "term", NULL, TerminalCommand,LOCAL_AUTH, 324 "Enter to terminal mode", "term"}, 325 { "alias", NULL, AliasCommand, LOCAL_AUTH, 326 "alias control", "alias option [yes|no]"}, 327 { "quit", "bye", QuitCommand, LOCAL_AUTH | LOCAL_NO_AUTH, 328 "Quit PPP program", "quit|bye [all]"}, 329 { "help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH, 330 "Display this message", "help|? [command]", (void *)Commands }, 331 { NULL, "down", DownCommand, LOCAL_AUTH, 332 "Generate down event", "down"}, 333 { NULL, NULL, NULL }, 334}; 335 336extern int ReportCcpStatus(); 337extern int ReportLcpStatus(); 338extern int ReportIpcpStatus(); 339extern int ReportProtStatus(); 340extern int ReportCompress(); 341extern int ShowModemStatus(); 342extern int ReportHdlcStatus(); 343extern int ShowMemMap(); 344 345static int ShowLogLevel() 346{ 347 int i; 348 349 if (!VarTerm) 350 return 0; 351 fprintf(VarTerm, "Log:"); 352 for (i = LogMIN; i < LogMAXCONF; i++) { 353 if (LogIsKept(i)) 354 fprintf(VarTerm, " %s", LogName(i)); 355 } 356 fprintf(VarTerm, "\n"); 357 358 return 0; 359} 360 361static int ShowEscape() 362{ 363 int code, bit; 364 365 if (!VarTerm) 366 return 0; 367 if (EscMap[32]) { 368 for (code = 0; code < 32; code++) 369 if (EscMap[code]) 370 for (bit = 0; bit < 8; bit++) 371 if (EscMap[code] & (1<<bit)) 372 fprintf(VarTerm, " 0x%02x", (code << 3) + bit); 373 fprintf(VarTerm, "\n"); 374 } 375 return 1; 376} 377 378static int ShowTimeout() 379{ 380 if (!VarTerm) 381 return 0; 382 fprintf(VarTerm, " Idle Timer: %d secs LQR Timer: %d secs" 383 " Retry Timer: %d secs\n", VarIdleTimeout, VarLqrTimeout, 384 VarRetryTimeout); 385 return 1; 386} 387 388static int ShowAuthKey() 389{ 390 if (!VarTerm) 391 return 0; 392 fprintf(VarTerm, "AuthName = %s\n", VarAuthName); 393 fprintf(VarTerm, "AuthKey = %s\n", VarAuthKey); 394 return 1; 395} 396 397static int ShowVersion() 398{ 399 extern char VarVersion[]; 400 extern char VarLocalVersion[]; 401 402 if (!VarTerm) 403 return 0; 404 fprintf(VarTerm, "%s - %s \n", VarVersion, VarLocalVersion); 405 return 1; 406} 407 408static int ShowInitialMRU() 409{ 410 if (!VarTerm) 411 return 0; 412 fprintf(VarTerm, " Initial MRU: %ld\n", VarMRU); 413 return 1; 414} 415 416static int ShowPreferredMTU() 417{ 418 if (!VarTerm) 419 return 0; 420 if (VarPrefMTU) 421 fprintf(VarTerm, " Preferred MTU: %ld\n", VarPrefMTU); 422 else 423 fprintf(VarTerm, " Preferred MTU: unspecified\n"); 424 return 1; 425} 426 427static int ShowReconnect() 428{ 429 if (!VarTerm) 430 return 0; 431 fprintf(VarTerm, " Reconnect Timer: %d, %d tries\n", 432 VarReconnectTimer, VarReconnectTries); 433 return 1; 434} 435 436static int ShowRedial() 437{ 438 if (!VarTerm) 439 return 0; 440 fprintf(VarTerm, " Redial Timer: "); 441 442 if (VarRedialTimeout >= 0) { 443 fprintf(VarTerm, " %d seconds, ", VarRedialTimeout); 444 } 445 else { 446 fprintf(VarTerm, " Random 0 - %d seconds, ", REDIAL_PERIOD); 447 } 448 449 fprintf(VarTerm, " Redial Next Timer: "); 450 451 if (VarRedialNextTimeout >= 0) { 452 fprintf(VarTerm, " %d seconds, ", VarRedialNextTimeout); 453 } 454 else { 455 fprintf(VarTerm, " Random 0 - %d seconds, ", REDIAL_PERIOD); 456 } 457 458 if (VarDialTries) 459 fprintf(VarTerm, "%d dial tries", VarDialTries); 460 461 fprintf(VarTerm, "\n"); 462 463 return 1; 464} 465 466#ifndef NOMSEXT 467static int ShowMSExt() 468{ 469 if (!VarTerm) 470 return 0; 471 fprintf(VarTerm, " MS PPP extention values \n" ); 472 fprintf(VarTerm, " Primary NS : %s\n", inet_ntoa( ns_entries[0] )); 473 fprintf(VarTerm, " Secondary NS : %s\n", inet_ntoa( ns_entries[1] )); 474 fprintf(VarTerm, " Primary NBNS : %s\n", inet_ntoa( nbns_entries[0] )); 475 fprintf(VarTerm, " Secondary NBNS : %s\n", inet_ntoa( nbns_entries[1] )); 476 return 1; 477} 478#endif 479 480extern int ShowIfilter(), ShowOfilter(), ShowDfilter(), ShowAfilter(); 481 482struct cmdtab const ShowCommands[] = { 483 { "afilter", NULL, ShowAfilter, LOCAL_AUTH, 484 "Show keep Alive filters", "show afilter option .."}, 485 { "auth", NULL, ShowAuthKey, LOCAL_AUTH, 486 "Show auth name/key", "show auth"}, 487 { "ccp", NULL, ReportCcpStatus, LOCAL_AUTH, 488 "Show CCP status", "show cpp"}, 489 { "compress", NULL, ReportCompress, LOCAL_AUTH, 490 "Show compression statictics", "show compress"}, 491 { "dfilter", NULL, ShowDfilter, LOCAL_AUTH, 492 "Show Demand filters", "show dfilteroption .."}, 493 { "escape", NULL, ShowEscape, LOCAL_AUTH, 494 "Show escape characters", "show escape"}, 495 { "hdlc", NULL, ReportHdlcStatus, LOCAL_AUTH, 496 "Show HDLC error summary", "show hdlc"}, 497 { "ifilter", NULL, ShowIfilter, LOCAL_AUTH, 498 "Show Input filters", "show ifilter option .."}, 499 { "ipcp", NULL, ReportIpcpStatus, LOCAL_AUTH, 500 "Show IPCP status", "show ipcp"}, 501 { "lcp", NULL, ReportLcpStatus, LOCAL_AUTH, 502 "Show LCP status", "show lcp"}, 503 { "log", NULL, ShowLogLevel, LOCAL_AUTH, 504 "Show current log level", "show log"}, 505 { "mem", NULL, ShowMemMap, LOCAL_AUTH, 506 "Show memory map", "show mem"}, 507 { "modem", NULL, ShowModemStatus, LOCAL_AUTH, 508 "Show modem setups", "show modem"}, 509 { "mru", NULL, ShowInitialMRU, LOCAL_AUTH, 510 "Show Initial MRU", "show mru"}, 511 { "mtu", NULL, ShowPreferredMTU, LOCAL_AUTH, 512 "Show Preferred MTU", "show mtu"}, 513 { "ofilter", NULL, ShowOfilter, LOCAL_AUTH, 514 "Show Output filters", "show ofilter option .."}, 515 { "proto", NULL, ReportProtStatus, LOCAL_AUTH, 516 "Show protocol summary", "show proto"}, 517 { "reconnect",NULL, ShowReconnect, LOCAL_AUTH, 518 "Show Reconnect timer,tries", "show reconnect"}, 519 { "redial", NULL, ShowRedial, LOCAL_AUTH, 520 "Show Redial timeout value", "show redial"}, 521 { "route", NULL, ShowRoute, LOCAL_AUTH, 522 "Show routing table", "show route"}, 523 { "timeout", NULL, ShowTimeout, LOCAL_AUTH, 524 "Show Idle timeout value", "show timeout"}, 525#ifndef NOMSEXT 526 { "msext", NULL, ShowMSExt, LOCAL_AUTH, 527 "Show MS PPP extentions", "show msext"}, 528#endif 529 { "version", NULL, ShowVersion, LOCAL_NO_AUTH | LOCAL_AUTH, 530 "Show version string", "show version"}, 531 { "help", "?", HelpCommand, LOCAL_NO_AUTH | LOCAL_AUTH, 532 "Display this message", "show help|? [command]", (void *)ShowCommands}, 533 { NULL, NULL, NULL }, 534}; 535 536struct cmdtab * 537FindCommand(cmds, str, pmatch) 538struct cmdtab *cmds; 539char *str; 540int *pmatch; 541{ 542 int nmatch; 543 int len; 544 struct cmdtab *found; 545 546 found = NULL; 547 len = strlen(str); 548 nmatch = 0; 549 while (cmds->func) { 550 if (cmds->name && strncasecmp(str, cmds->name, len) == 0) { 551 if (cmds->name[len] == '\0') { 552 *pmatch = 1; 553 return cmds; 554 } 555 nmatch++; 556 found = cmds; 557 } else if(cmds->alias && strncasecmp(str, cmds->alias, len) == 0) { 558 if (cmds->alias[len] == '\0') { 559 *pmatch = 1; 560 return cmds; 561 } 562 nmatch++; 563 found = cmds; 564 } 565 cmds++; 566 } 567 *pmatch = nmatch; 568 return found; 569} 570 571int 572FindExec(cmdlist, argc, argv) 573struct cmdtab *cmdlist; 574int argc; 575char **argv; 576{ 577 struct cmdtab *cmd; 578 int val = 1; 579 int nmatch; 580 581 cmd = FindCommand(cmdlist, *argv, &nmatch); 582 if (nmatch > 1) 583 LogPrintf(LogWARN, "%s: Ambiguous command\n", *argv); 584 else if (cmd && ( cmd->lauth & VarLocalAuth ) ) 585 val = (cmd->func)(cmd, --argc, ++argv, cmd->args); 586 else 587 LogPrintf(LogWARN, "%s: Invalid command\n", *argv); 588 589 if (val == -1) 590 LogPrintf(LogWARN, "Usage: %s\n", cmd->syntax); 591 else if(val) 592 LogPrintf(LogCOMMAND, "%s: Failed %d\n", *argv, val); 593 594 return val; 595} 596 597int aft_cmd = 1; 598extern int TermMode; 599 600void 601Prompt() 602{ 603 char *pconnect, *pauth; 604 605 if (!(mode & MODE_INTER) || !VarTerm || TermMode) 606 return; 607 608 if (!aft_cmd) 609 fprintf(VarTerm, "\n"); 610 else 611 aft_cmd = 0; 612 613 if ( VarLocalAuth == LOCAL_AUTH ) 614 pauth = " ON "; 615 else 616 pauth = " on "; 617 if (IpcpFsm.state == ST_OPENED && phase == PHASE_NETWORK) 618 pconnect = "PPP"; 619 else 620 pconnect = "ppp"; 621 fprintf(VarTerm, "%s%s%s> ", pconnect, pauth, VarShortHost); 622 fflush(VarTerm); 623} 624 625void 626DecodeCommand(buff, nb, prompt) 627char *buff; 628int nb; 629int prompt; 630{ 631 char *vector[20]; 632 char **argv; 633 int argc; 634 char *cp; 635 636 if (nb > 0) { 637 cp = buff + strcspn(buff, "\r\n"); 638 if (cp) 639 *cp = '\0'; 640 argc = MakeArgs(buff, vector, VECSIZE(vector)); 641 argv = vector; 642 643 if (argc > 0) 644 FindExec(Commands, argc, argv); 645 } 646 if (prompt) 647 Prompt(); 648} 649 650static int 651ShowCommand(list, argc, argv) 652struct cmdtab *list; 653int argc; 654char **argv; 655{ 656 if (argc > 0) 657 FindExec(ShowCommands, argc, argv); 658 else if (VarTerm) 659 fprintf(VarTerm, "Use ``show ?'' to get a list.\n"); 660 else 661 LogPrintf(LogWARN, "show command must have arguments\n"); 662 663 return 0; 664} 665 666static int 667TerminalCommand() 668{ 669 if (LcpFsm.state > ST_CLOSED) { 670 if (VarTerm) 671 fprintf(VarTerm, "LCP state is [%s]\n", StateNames[LcpFsm.state]); 672 return 1; 673 } 674 if (!IsInteractive()) 675 return(1); 676 modem = OpenModem(mode); 677 if (modem < 0) { 678 if (VarTerm) 679 fprintf(VarTerm, "Failed to open modem.\n"); 680 return(1); 681 } 682 if (VarTerm) { 683 fprintf(VarTerm, "Enter to terminal mode.\n"); 684 fprintf(VarTerm, "Type `~?' for help.\n"); 685 } 686 TtyTermMode(); 687 return(0); 688} 689 690static int 691QuitCommand(list, argc, argv) 692struct cmdtab *list; 693int argc; 694char **argv; 695{ 696 FILE *oVarTerm; 697 698 if (mode & (MODE_DIRECT|MODE_DEDICATED|MODE_AUTO)) { 699 if (argc > 0 && (VarLocalAuth & LOCAL_AUTH)) { 700 Cleanup(EX_NORMAL); 701 mode &= ~MODE_INTER; 702 oVarTerm = VarTerm; 703 VarTerm = 0; 704 if (oVarTerm && oVarTerm != stdout) 705 fclose(oVarTerm); 706 } else { 707 LogPrintf(LogPHASE, "Client connection closed.\n"); 708 VarLocalAuth = LOCAL_NO_AUTH; 709 mode &= ~MODE_INTER; 710 oVarTerm = VarTerm; 711 VarTerm = 0; 712 if (oVarTerm && oVarTerm != stdout) 713 fclose(oVarTerm); 714 close(netfd); 715 netfd = -1; 716 } 717 } else 718 Cleanup(EX_NORMAL); 719 720 return 0; 721} 722 723static int 724CloseCommand() 725{ 726 reconnect(RECON_FALSE); 727 LcpClose(); 728 if (mode & MODE_BACKGROUND) 729 Cleanup(EX_NORMAL); 730 return 0; 731} 732 733static int 734DownCommand() 735{ 736 LcpDown(); 737 return 0; 738} 739 740static int 741SetModemSpeed(list, argc, argv) 742struct cmdtab *list; 743int argc; 744char **argv; 745{ 746 int speed; 747 748 if (argc > 0) { 749 if (strcmp(*argv, "sync") == 0) { 750 VarSpeed = 0; 751 return 0; 752 } 753 speed = atoi(*argv); 754 if (IntToSpeed(speed) != B0) { 755 VarSpeed = speed; 756 return 0; 757 } 758 LogPrintf(LogWARN, "%s: Invalid speed\n", *argv); 759 } 760 return -1; 761} 762 763static int 764SetReconnect(list, argc, argv) 765struct cmdtab *list; 766int argc; 767char **argv; 768{ 769 if (argc == 2) { 770 VarReconnectTimer = atoi(argv[0]); 771 VarReconnectTries = atoi(argv[1]); 772 return 0; 773 } 774 775 return -1; 776} 777 778static int 779SetRedialTimeout(list, argc, argv) 780struct cmdtab *list; 781int argc; 782char **argv; 783{ 784 int timeout; 785 int tries; 786 char *dot; 787 788 if (argc == 1 || argc == 2 ) { 789 if (strncasecmp(argv[0], "random", 6) == 0 && 790 (argv[0][6] == '\0' || argv[0][6] == '.')) { 791 VarRedialTimeout = -1; 792 if (!randinit) { 793 randinit = 1; 794 srandomdev(); 795 } 796 } else { 797 timeout = atoi(argv[0]); 798 799 if (timeout >= 0) 800 VarRedialTimeout = timeout; 801 else { 802 LogPrintf(LogWARN, "Invalid redial timeout\n"); 803 return -1; 804 } 805 } 806 807 dot = index(argv[0],'.'); 808 if (dot) { 809 if (strcasecmp(++dot, "random") == 0) { 810 VarRedialNextTimeout = -1; 811 if (!randinit) { 812 randinit = 1; 813 srandomdev(); 814 } 815 } 816 else { 817 timeout = atoi(dot); 818 if (timeout >= 0) 819 VarRedialNextTimeout = timeout; 820 else { 821 LogPrintf(LogWARN, "Invalid next redial timeout\n"); 822 return -1; 823 } 824 } 825 } 826 else 827 VarRedialNextTimeout = NEXT_REDIAL_PERIOD; /* Default next timeout */ 828 829 if (argc == 2) { 830 tries = atoi(argv[1]); 831 832 if (tries >= 0) { 833 VarDialTries = tries; 834 } else { 835 LogPrintf(LogWARN, "Invalid retry value\n"); 836 return 1; 837 } 838 } 839 return 0; 840 } 841 842 return -1; 843} 844 845static int 846SetModemParity(list, argc, argv) 847struct cmdtab *list; 848int argc; 849char **argv; 850{ 851 return argc > 0 ? ChangeParity(*argv) : -1; 852} 853 854static int 855SetLogLevel(list, argc, argv) 856struct cmdtab *list; 857int argc; 858char **argv; 859{ 860 int i; 861 int res; 862 char *arg; 863 864 res = 0; 865 if (argc == 0 || (argv[0][0] != '+' && argv[0][0] != '-')) 866 LogDiscardAll(); 867 while (argc--) { 868 arg = **argv == '+' || **argv == '-' ? *argv + 1 : *argv; 869 for (i = LogMIN; i <= LogMAX; i++) 870 if (strcasecmp(arg, LogName(i)) == 0) { 871 if (**argv == '-') 872 LogDiscard(i); 873 else 874 LogKeep(i); 875 break; 876 } 877 if (i > LogMAX) { 878 LogPrintf(LogWARN, "%s: Invalid log value\n", arg); 879 res = -1; 880 } 881 argv++; 882 } 883 return res; 884} 885 886static int 887SetEscape(list, argc, argv) 888struct cmdtab *list; 889int argc; 890char **argv; 891{ 892 int code; 893 894 for (code = 0; code < 33; code++) 895 EscMap[code] = 0; 896 while (argc-- > 0) { 897 sscanf(*argv++, "%x", &code); 898 code &= 0xff; 899 EscMap[code >> 3] |= (1 << (code&7)); 900 EscMap[32] = 1; 901 } 902 return 0; 903} 904 905static int 906SetInitialMRU(list, argc, argv) 907struct cmdtab *list; 908int argc; 909char **argv; 910{ 911 long mru; 912 char *err; 913 914 if (argc > 0) { 915 mru = atol(*argv); 916 if (mru < MIN_MRU) 917 err = "Given MRU value (%ld) is too small.\n"; 918 else if (mru > MAX_MRU) 919 err = "Given MRU value (%ld) is too big.\n"; 920 else { 921 VarMRU = mru; 922 return 0; 923 } 924 LogPrintf(LogWARN, err, mru); 925 } 926 927 return -1; 928} 929 930static int 931SetPreferredMTU(list, argc, argv) 932struct cmdtab *list; 933int argc; 934char **argv; 935{ 936 long mtu; 937 char *err; 938 939 if (argc > 0) { 940 mtu = atol(*argv); 941 if (mtu == 0) { 942 VarPrefMTU = 0; 943 return 0; 944 } else if (mtu < MIN_MTU) 945 err = "Given MTU value (%ld) is too small.\n"; 946 else if (mtu > MAX_MTU) 947 err = "Given MTU value (%ld) is too big.\n"; 948 else { 949 VarPrefMTU = mtu; 950 return 0; 951 } 952 LogPrintf(LogWARN, err, mtu); 953 } 954 955 return -1; 956} 957 958static int 959SetIdleTimeout(list, argc, argv) 960struct cmdtab *list; 961int argc; 962char **argv; 963{ 964 if (argc-- > 0) { 965 VarIdleTimeout = atoi(*argv++); 966 UpdateIdleTimer(); /* If we're connected, restart the idle timer */ 967 if (argc-- > 0) { 968 VarLqrTimeout = atoi(*argv++); 969 if (VarLqrTimeout < 1) 970 VarLqrTimeout = 30; 971 if (argc > 0) { 972 VarRetryTimeout = atoi(*argv); 973 if (VarRetryTimeout < 1 || VarRetryTimeout > 10) 974 VarRetryTimeout = 3; 975 } 976 } 977 return 0; 978 } 979 980 return -1; 981} 982 983struct in_addr 984GetIpAddr(cp) 985char *cp; 986{ 987 struct hostent *hp; 988 struct in_addr ipaddr; 989 990 hp = gethostbyname(cp); 991 if (hp && hp->h_addrtype == AF_INET) 992 bcopy(hp->h_addr, &ipaddr, hp->h_length); 993 else if (inet_aton(cp, &ipaddr) == 0) 994 ipaddr.s_addr = 0; 995 return(ipaddr); 996} 997 998static int 999SetInterfaceAddr(list, argc, argv) 1000struct cmdtab *list; 1001int argc; 1002char **argv; 1003{ 1004 1005 DefMyAddress.ipaddr.s_addr = DefHisAddress.ipaddr.s_addr = 0L; 1006 if (argc > 4) 1007 return -1; 1008 1009 if (argc > 0) { 1010 if (ParseAddr(argc, argv++, 1011 &DefMyAddress.ipaddr, 1012 &DefMyAddress.mask, 1013 &DefMyAddress.width) == 0) 1014 return 1; 1015 if (--argc > 0) { 1016 if (ParseAddr(argc, argv++, 1017 &DefHisAddress.ipaddr, 1018 &DefHisAddress.mask, 1019 &DefHisAddress.width) == 0) 1020 return 2; 1021 if (--argc > 0) { 1022 ifnetmask = GetIpAddr(*argv); 1023 if (--argc > 0) { 1024 if (ParseAddr(argc, argv++, 1025 &DefTriggerAddress.ipaddr, 1026 &DefTriggerAddress.mask, 1027 &DefTriggerAddress.width) == 0) 1028 return 3; 1029 } 1030 } 1031 } 1032 } 1033 /* 1034 * For backwards compatibility, 0.0.0.0 means any address. 1035 */ 1036 if (DefMyAddress.ipaddr.s_addr == 0) { 1037 DefMyAddress.mask.s_addr = 0; 1038 DefMyAddress.width = 0; 1039 } 1040 if (DefHisAddress.ipaddr.s_addr == 0) { 1041 DefHisAddress.mask.s_addr = 0; 1042 DefHisAddress.width = 0; 1043 } 1044 1045 if ((mode & MODE_AUTO) || 1046 ((mode & MODE_DEDICATED) && dstsystem)) { 1047 if (OsSetIpaddress(DefMyAddress.ipaddr, DefHisAddress.ipaddr, ifnetmask) < 0) 1048 return 4; 1049 } 1050 return 0; 1051} 1052 1053#ifndef NOMSEXT 1054 1055void 1056SetMSEXT(pri_addr, sec_addr, argc, argv) 1057struct in_addr *pri_addr; 1058struct in_addr *sec_addr; 1059int argc; 1060char **argv; 1061{ 1062 int dummyint; 1063 struct in_addr dummyaddr; 1064 1065 pri_addr->s_addr = sec_addr->s_addr = 0L; 1066 1067 if( argc > 0 ) { 1068 ParseAddr(argc, argv++, pri_addr, &dummyaddr, &dummyint); 1069 if( --argc > 0 ) 1070 ParseAddr(argc, argv++, sec_addr, &dummyaddr, &dummyint); 1071 else 1072 sec_addr->s_addr = pri_addr->s_addr; 1073 } 1074 1075 /* 1076 * if the primary/secondary ns entries are 0.0.0.0 we should 1077 * set them to either the localhost's ip, or the values in 1078 * /etc/resolv.conf ?? 1079 * 1080 * up to you if you want to implement this... 1081 */ 1082 1083} 1084 1085static int 1086SetNS(list, argc, argv) 1087struct cmdtab *list; 1088int argc; 1089char **argv; 1090{ 1091 SetMSEXT(&ns_entries[0], &ns_entries[1], argc, argv); 1092 return 0; 1093} 1094 1095static int 1096SetNBNS(list, argc, argv) 1097struct cmdtab *list; 1098int argc; 1099char **argv; 1100{ 1101 SetMSEXT(&nbns_entries[0], &nbns_entries[1], argc, argv); 1102 return 0; 1103} 1104 1105#endif /* MS_EXT */ 1106 1107#define VAR_AUTHKEY 0 1108#define VAR_DIAL 1 1109#define VAR_LOGIN 2 1110#define VAR_AUTHNAME 3 1111#define VAR_DEVICE 4 1112#define VAR_ACCMAP 5 1113#define VAR_PHONE 6 1114 1115static int 1116SetVariable(list, argc, argv, param) 1117struct cmdtab *list; 1118int argc; 1119char **argv; 1120int param; 1121{ 1122 u_long map; 1123 char *arg; 1124 1125 if (argc > 0) 1126 arg = *argv; 1127 else 1128 arg = ""; 1129 1130 switch (param) { 1131 case VAR_AUTHKEY: 1132 strncpy(VarAuthKey, arg, sizeof(VarAuthKey)-1); 1133 VarAuthKey[sizeof(VarAuthKey)-1] = '\0'; 1134 break; 1135 case VAR_AUTHNAME: 1136 strncpy(VarAuthName, arg, sizeof(VarAuthName)-1); 1137 VarAuthName[sizeof(VarAuthName)-1] = '\0'; 1138 break; 1139 case VAR_DIAL: 1140 strncpy(VarDialScript, arg, sizeof(VarDialScript)-1); 1141 VarDialScript[sizeof(VarDialScript)-1] = '\0'; 1142 break; 1143 case VAR_LOGIN: 1144 strncpy(VarLoginScript, arg, sizeof(VarLoginScript)-1); 1145 VarLoginScript[sizeof(VarLoginScript)-1] = '\0'; 1146 break; 1147 case VAR_DEVICE: 1148 CloseModem(); 1149 strncpy(VarDevice, arg, sizeof(VarDevice)-1); 1150 VarDevice[sizeof(VarDevice)-1] = '\0'; 1151 VarBaseDevice = rindex(VarDevice, '/'); 1152 VarBaseDevice = VarBaseDevice ? VarBaseDevice + 1 : ""; 1153 break; 1154 case VAR_ACCMAP: 1155 sscanf(arg, "%lx", &map); 1156 VarAccmap = map; 1157 break; 1158 case VAR_PHONE: 1159 strncpy(VarPhoneList, arg, sizeof(VarPhoneList)-1); 1160 VarPhoneList[sizeof(VarPhoneList)-1] = '\0'; 1161 strcpy(VarPhoneCopy, VarPhoneList); 1162 VarNextPhone = VarPhoneCopy; 1163 break; 1164 } 1165 return 0; 1166} 1167 1168static int SetCtsRts(list, argc, argv) 1169struct cmdtab *list; 1170int argc; 1171char **argv; 1172{ 1173 if (argc > 0) { 1174 if (strcmp(*argv, "on") == 0) 1175 VarCtsRts = TRUE; 1176 else if (strcmp(*argv, "off") == 0) 1177 VarCtsRts = FALSE; 1178 else 1179 return -1; 1180 return 0; 1181 } 1182 return -1; 1183} 1184 1185 1186static int SetOpenMode(list, argc, argv) 1187struct cmdtab *list; 1188int argc; 1189char **argv; 1190{ 1191 if (argc > 0) { 1192 if (strcmp(*argv, "active") == 0) 1193 VarOpenMode = OPEN_ACTIVE; 1194 else if (strcmp(*argv, "passive") == 0) 1195 VarOpenMode = OPEN_PASSIVE; 1196 else 1197 return -1; 1198 return 0; 1199 } 1200 return -1; 1201} 1202 1203extern int SetIfilter(), SetOfilter(), SetDfilter(), SetAfilter(); 1204 1205struct cmdtab const SetCommands[] = { 1206 { "accmap", NULL, SetVariable, LOCAL_AUTH, 1207 "Set accmap value", "set accmap hex-value", (void *)VAR_ACCMAP}, 1208 { "afilter", NULL, SetAfilter, LOCAL_AUTH, 1209 "Set keep Alive filter", "set afilter ..."}, 1210 { "authkey", "key", SetVariable, LOCAL_AUTH, 1211 "Set authentication key", "set authkey|key key", (void *)VAR_AUTHKEY}, 1212 { "authname", NULL, SetVariable, LOCAL_AUTH, 1213 "Set authentication name", "set authname name", (void *)VAR_AUTHNAME}, 1214 { "ctsrts", NULL, SetCtsRts, LOCAL_AUTH, 1215 "Use CTS/RTS modem signalling", "set ctsrts [on|off]"}, 1216 { "device", "line", SetVariable, LOCAL_AUTH, 1217 "Set modem device name", "set device|line device-name", (void *)VAR_DEVICE}, 1218 { "dfilter", NULL, SetDfilter, LOCAL_AUTH, 1219 "Set demand filter", "set dfilter ..."}, 1220 { "dial", NULL, SetVariable, LOCAL_AUTH, 1221 "Set dialing script", "set dial chat-script", (void *)VAR_DIAL}, 1222 { "escape", NULL, SetEscape, LOCAL_AUTH, 1223 "Set escape characters", "set escape hex-digit ..."}, 1224 { "ifaddr", NULL, SetInterfaceAddr, LOCAL_AUTH, 1225 "Set destination address", "set ifaddr [src-addr [dst-addr [netmask [trg-addr]]]]"}, 1226 { "ifilter", NULL, SetIfilter, LOCAL_AUTH, 1227 "Set input filter", "set ifilter ..."}, 1228 { "log", NULL, SetLogLevel, LOCAL_AUTH, 1229 "Set log level", "set log [+|-]value..."}, 1230 { "login", NULL, SetVariable, LOCAL_AUTH, 1231 "Set login script", "set login chat-script", (void *)VAR_LOGIN }, 1232 { "mru", NULL, SetInitialMRU, LOCAL_AUTH, 1233 "Set Initial MRU value", "set mru value" }, 1234 { "mtu", NULL, SetPreferredMTU, LOCAL_AUTH, 1235 "Set Preferred MTU value", "set mtu value" }, 1236 { "ofilter", NULL, SetOfilter, LOCAL_AUTH, 1237 "Set output filter", "set ofilter ..." }, 1238 { "openmode", NULL, SetOpenMode, LOCAL_AUTH, 1239 "Set open mode", "set openmode [active|passive]"}, 1240 { "parity", NULL, SetModemParity, LOCAL_AUTH, 1241 "Set modem parity", "set parity [odd|even|none]"}, 1242 { "phone", NULL, SetVariable, LOCAL_AUTH, 1243 "Set telephone number(s)", "set phone phone1[:phone2[...]]", (void *)VAR_PHONE }, 1244 { "reconnect",NULL, SetReconnect, LOCAL_AUTH, 1245 "Set Reconnect timeout", "set reconnect value ntries"}, 1246 { "redial", NULL, SetRedialTimeout, LOCAL_AUTH, 1247 "Set Redial timeout", "set redial value|random[.value|random] [dial_attempts]"}, 1248 { "speed", NULL, SetModemSpeed, LOCAL_AUTH, 1249 "Set modem speed", "set speed value"}, 1250 { "timeout", NULL, SetIdleTimeout, LOCAL_AUTH, 1251 "Set Idle timeout", "set timeout value"}, 1252#ifndef NOMSEXT 1253 { "ns", NULL, SetNS, LOCAL_AUTH, 1254 "Set NameServer", "set ns pri-addr [sec-addr]"}, 1255 { "nbns", NULL, SetNBNS, LOCAL_AUTH, 1256 "Set NetBIOS NameServer", "set nbns pri-addr [sec-addr]"}, 1257#endif 1258 { "help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH, 1259 "Display this message", "set help|? [command]", (void *)SetCommands}, 1260 { NULL, NULL, NULL }, 1261}; 1262 1263static int 1264SetCommand(list, argc, argv) 1265struct cmdtab *list; 1266int argc; 1267char **argv; 1268{ 1269 if (argc > 0) 1270 FindExec(SetCommands, argc, argv); 1271 else if (VarTerm) 1272 fprintf(VarTerm, "Use `set ?' to get a list or `set ? <var>' for" 1273 " syntax help.\n"); 1274 else 1275 LogPrintf(LogWARN, "set command must have arguments\n"); 1276 1277 return 0; 1278} 1279 1280 1281static int 1282AddCommand(list, argc, argv) 1283struct cmdtab *list; 1284int argc; 1285char **argv; 1286{ 1287 struct in_addr dest, gateway, netmask; 1288 1289 if (argc == 3) { 1290 dest = GetIpAddr(argv[0]); 1291 netmask = GetIpAddr(argv[1]); 1292 if (strcasecmp(argv[2], "HISADDR") == 0) 1293 gateway = IpcpInfo.his_ipaddr; 1294 else 1295 gateway = GetIpAddr(argv[2]); 1296 OsSetRoute(RTM_ADD, dest, gateway, netmask); 1297 return 0; 1298 } 1299 1300 return -1; 1301} 1302 1303static int 1304DeleteCommand(list, argc, argv) 1305struct cmdtab *list; 1306int argc; 1307char **argv; 1308{ 1309 struct in_addr dest, gateway, netmask; 1310 1311 if (argc == 1 && strcasecmp(argv[0], "all") == 0) 1312 DeleteIfRoutes(0); 1313 else if (argc > 0 && argc < 4) { 1314 dest = GetIpAddr(argv[0]); 1315 netmask.s_addr = INADDR_ANY; 1316 if (argc > 1) { 1317 if (strcasecmp(argv[1], "HISADDR") == 0) 1318 gateway = IpcpInfo.his_ipaddr; 1319 else 1320 gateway = GetIpAddr(argv[1]); 1321 if (argc == 3) { 1322 if (inet_aton(argv[2], &netmask) == 0) { 1323 LogPrintf(LogWARN, "Bad netmask value.\n"); 1324 return -1; 1325 } 1326 } 1327 } else 1328 gateway.s_addr = INADDR_ANY; 1329 OsSetRoute(RTM_DELETE, dest, gateway, netmask); 1330 } else 1331 return -1; 1332 1333 return 0; 1334} 1335 1336static int AliasEnable(); 1337static int AliasOption(); 1338 1339static struct cmdtab const AliasCommands[] = 1340{ 1341 { "enable", NULL, AliasEnable, LOCAL_AUTH, 1342 "enable IP aliasing", "alias enable [yes|no]"}, 1343 { "port", NULL, AliasRedirectPort, LOCAL_AUTH, 1344 "port redirection", "alias port [proto addr_local:port_local port_alias]"}, 1345 { "addr", NULL, AliasRedirectAddr, LOCAL_AUTH, 1346 "static address translation", "alias addr [addr_local addr_alias]"}, 1347 { "deny_incoming", NULL, AliasOption, LOCAL_AUTH, 1348 "stop incoming connections", "alias deny_incoming [yes|no]", 1349 (void*)PKT_ALIAS_DENY_INCOMING}, 1350 { "log", NULL, AliasOption, LOCAL_AUTH, 1351 "log aliasing link creation", "alias log [yes|no]", 1352 (void*)PKT_ALIAS_LOG}, 1353 { "same_ports", NULL, AliasOption, LOCAL_AUTH, 1354 "try to leave port numbers unchanged", "alias same_ports [yes|no]", 1355 (void*)PKT_ALIAS_SAME_PORTS}, 1356 { "use_sockets", NULL, AliasOption, LOCAL_AUTH, 1357 "allocate host sockets", "alias use_sockets [yes|no]", 1358 (void*)PKT_ALIAS_USE_SOCKETS }, 1359 { "unregistered_only", NULL, AliasOption, LOCAL_AUTH, 1360 "alias unregistered (private) IP address space only", 1361 "alias unregistered_only [yes|no]", 1362 (void*)PKT_ALIAS_UNREGISTERED_ONLY}, 1363 { "help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH, 1364 "Display this message", "alias help|? [command]", 1365 (void *)AliasCommands}, 1366 { NULL, NULL, NULL }, 1367}; 1368 1369 1370static int 1371AliasCommand(list, argc, argv) 1372struct cmdtab *list; 1373int argc; 1374char **argv; 1375{ 1376 if (argc > 0) 1377 FindExec(AliasCommands, argc, argv); 1378 else if (VarTerm) 1379 fprintf(VarTerm, "Use `alias help' to get a list or `alias help <option>'" 1380 " for syntax help.\n"); 1381 else 1382 LogPrintf(LogWARN, "alias command must have arguments\n"); 1383 1384 return 0; 1385} 1386 1387static int 1388AliasEnable(list, argc, argv) 1389struct cmdtab *list; 1390int argc; 1391char **argv; 1392{ 1393 if (argc == 1) 1394 if (strcasecmp(argv[0], "yes") == 0) { 1395 if (!(mode & MODE_ALIAS)) { 1396 if (loadAliasHandlers(&VarAliasHandlers) == 0) { 1397 mode |= MODE_ALIAS; 1398 return 0; 1399 } 1400 LogPrintf(LogWARN, "Cannot load alias library\n"); 1401 return 1; 1402 } 1403 return 0; 1404 } else if (strcasecmp(argv[0], "no") == 0) { 1405 if (mode & MODE_ALIAS) { 1406 unloadAliasHandlers(); 1407 mode &= ~MODE_ALIAS; 1408 } 1409 return 0; 1410 } 1411 1412 return -1; 1413} 1414 1415 1416static int 1417AliasOption(list, argc, argv, param) 1418struct cmdtab *list; 1419int argc; 1420char **argv; 1421void* param; 1422{ 1423 if (argc == 1) 1424 if (strcasecmp(argv[0], "yes") == 0) { 1425 if (mode & MODE_ALIAS) { 1426 VarSetPacketAliasMode((unsigned)param, (unsigned)param); 1427 return 0; 1428 } 1429 LogPrintf(LogWARN, "alias not enabled\n"); 1430 } else if (strcmp(argv[0], "no") == 0) { 1431 if (mode & MODE_ALIAS) { 1432 VarSetPacketAliasMode(0, (unsigned)param); 1433 return 0; 1434 } 1435 LogPrintf(LogWARN, "alias not enabled\n"); 1436 } 1437 1438 return -1; 1439} 1440