1 /* 2 * Copyright (c) 1985, 1988, 1993, 1994 3 * The Regents of the University of California. 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 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 34 */ 35 36 /* 37 * Grammar for FTP commands. 38 * See RFC 959. 39 */ 40 41 %{ 42 43 #ifndef lint 44 static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; 45 #endif /* not lint */ 46 47 #include <sys/param.h> 48 #include <sys/socket.h> 49 #include <sys/stat.h> 50 51 #include <netinet/in.h> 52 #include <arpa/ftp.h> 53 54 #include <ctype.h> 55 #include <errno.h> 56 #include <glob.h> 57 #include <pwd.h> 58 #include <setjmp.h> 59 #include <signal.h> 60 #include <stdio.h> 61 #include <stdlib.h> 62 #include <string.h> 63 #include <syslog.h> 64 #include <time.h> 65 #include <unistd.h> 66 #include <libutil.h> 67 68 #include "extern.h" 69 70 extern struct sockaddr_in data_dest; 71 extern int logged_in; 72 extern struct passwd *pw; 73 extern int guest; 74 extern int logging; 75 extern int type; 76 extern int form; 77 extern int debug; 78 extern int timeout; 79 extern int maxtimeout; 80 extern int pdata; 81 extern char hostname[], remotehost[]; 82 extern char proctitle[]; 83 extern int usedefault; 84 extern int transflag; 85 extern char tmpline[]; 86 87 off_t restart_point; 88 89 static int cmd_type; 90 static int cmd_form; 91 static int cmd_bytesz; 92 char cbuf[512]; 93 char *fromname; 94 95 %} 96 97 %union { 98 int i; 99 char *s; 100 } 101 102 %token 103 A B C E F I 104 L N P R S T 105 106 SP CRLF COMMA 107 108 USER PASS ACCT REIN QUIT PORT 109 PASV TYPE STRU MODE RETR STOR 110 APPE MLFL MAIL MSND MSOM MSAM 111 MRSQ MRCP ALLO REST RNFR RNTO 112 ABOR DELE CWD LIST NLST SITE 113 STAT HELP NOOP MKD RMD PWD 114 CDUP STOU SMNT SYST SIZE MDTM 115 116 UMASK IDLE CHMOD 117 118 LEXERR 119 120 %token <s> STRING 121 %token <i> NUMBER 122 123 %type <i> check_login octal_number byte_size 124 %type <i> struct_code mode_code type_code form_code 125 %type <s> pathstring pathname password username 126 127 %start cmd_list 128 129 %% 130 131 cmd_list 132 : /* empty */ 133 | cmd_list cmd 134 { 135 fromname = (char *) 0; 136 restart_point = (off_t) 0; 137 } 138 | cmd_list rcmd 139 ; 140 141 cmd 142 : USER SP username CRLF 143 { 144 user($3); 145 free($3); 146 } 147 | PASS SP password CRLF 148 { 149 pass($3); 150 free($3); 151 } 152 | PORT SP host_port CRLF 153 { 154 usedefault = 0; 155 if (pdata >= 0) { 156 (void) close(pdata); 157 pdata = -1; 158 } 159 reply(200, "PORT command successful."); 160 } 161 | PASV CRLF 162 { 163 passive(); 164 } 165 | TYPE SP type_code CRLF 166 { 167 switch (cmd_type) { 168 169 case TYPE_A: 170 if (cmd_form == FORM_N) { 171 reply(200, "Type set to A."); 172 type = cmd_type; 173 form = cmd_form; 174 } else 175 reply(504, "Form must be N."); 176 break; 177 178 case TYPE_E: 179 reply(504, "Type E not implemented."); 180 break; 181 182 case TYPE_I: 183 reply(200, "Type set to I."); 184 type = cmd_type; 185 break; 186 187 case TYPE_L: 188 #if NBBY == 8 189 if (cmd_bytesz == 8) { 190 reply(200, 191 "Type set to L (byte size 8)."); 192 type = cmd_type; 193 } else 194 reply(504, "Byte size must be 8."); 195 #else /* NBBY == 8 */ 196 UNIMPLEMENTED for NBBY != 8 197 #endif /* NBBY == 8 */ 198 } 199 } 200 | STRU SP struct_code CRLF 201 { 202 switch ($3) { 203 204 case STRU_F: 205 reply(200, "STRU F ok."); 206 break; 207 208 default: 209 reply(504, "Unimplemented STRU type."); 210 } 211 } 212 | MODE SP mode_code CRLF 213 { 214 switch ($3) { 215 216 case MODE_S: 217 reply(200, "MODE S ok."); 218 break; 219 220 default: 221 reply(502, "Unimplemented MODE type."); 222 } 223 } 224 | ALLO SP NUMBER CRLF 225 { 226 reply(202, "ALLO command ignored."); 227 } 228 | ALLO SP NUMBER SP R SP NUMBER CRLF 229 { 230 reply(202, "ALLO command ignored."); 231 } 232 | RETR check_login SP pathname CRLF 233 { 234 if ($2 && $4 != NULL) 235 retrieve((char *) 0, $4); 236 if ($4 != NULL) 237 free($4); 238 } 239 | STOR check_login SP pathname CRLF 240 { 241 if ($2 && $4 != NULL) 242 store($4, "w", 0); 243 if ($4 != NULL) 244 free($4); 245 } 246 | APPE check_login SP pathname CRLF 247 { 248 if ($2 && $4 != NULL) 249 store($4, "a", 0); 250 if ($4 != NULL) 251 free($4); 252 } 253 | NLST check_login CRLF 254 { 255 if ($2) 256 send_file_list("."); 257 } 258 | NLST check_login SP STRING CRLF 259 { 260 if ($2 && $4 != NULL) 261 send_file_list($4); 262 if ($4 != NULL) 263 free($4); 264 } 265 | LIST check_login CRLF 266 { 267 if ($2) 268 retrieve("/bin/ls -lgA", ""); 269 } 270 | LIST check_login SP pathname CRLF 271 { 272 if ($2 && $4 != NULL) 273 retrieve("/bin/ls -lgA %s", $4); 274 if ($4 != NULL) 275 free($4); 276 } 277 | STAT check_login SP pathname CRLF 278 { 279 if ($2 && $4 != NULL) 280 statfilecmd($4); 281 if ($4 != NULL) 282 free($4); 283 } 284 | STAT CRLF 285 { 286 statcmd(); 287 } 288 | DELE check_login SP pathname CRLF 289 { 290 if ($2 && $4 != NULL) 291 delete($4); 292 if ($4 != NULL) 293 free($4); 294 } 295 | RNTO SP pathname CRLF 296 { 297 if (fromname) { 298 renamecmd(fromname, $3); 299 free(fromname); 300 fromname = (char *) 0; 301 } else { 302 reply(503, "Bad sequence of commands."); 303 } 304 free($3); 305 } 306 | ABOR CRLF 307 { 308 reply(225, "ABOR command successful."); 309 } 310 | CWD check_login CRLF 311 { 312 if ($2) 313 cwd(pw->pw_dir); 314 } 315 | CWD check_login SP pathname CRLF 316 { 317 if ($2 && $4 != NULL) 318 cwd($4); 319 if ($4 != NULL) 320 free($4); 321 } 322 | HELP CRLF 323 { 324 help(cmdtab, (char *) 0); 325 } 326 | HELP SP STRING CRLF 327 { 328 char *cp = $3; 329 330 if (strncasecmp(cp, "SITE", 4) == 0) { 331 cp = $3 + 4; 332 if (*cp == ' ') 333 cp++; 334 if (*cp) 335 help(sitetab, cp); 336 else 337 help(sitetab, (char *) 0); 338 } else 339 help(cmdtab, $3); 340 } 341 | NOOP CRLF 342 { 343 reply(200, "NOOP command successful."); 344 } 345 | MKD check_login SP pathname CRLF 346 { 347 if ($2 && $4 != NULL) 348 makedir($4); 349 if ($4 != NULL) 350 free($4); 351 } 352 | RMD check_login SP pathname CRLF 353 { 354 if ($2 && $4 != NULL) 355 removedir($4); 356 if ($4 != NULL) 357 free($4); 358 } 359 | PWD check_login CRLF 360 { 361 if ($2) 362 pwd(); 363 } 364 | CDUP check_login CRLF 365 { 366 if ($2) 367 cwd(".."); 368 } 369 | SITE SP HELP CRLF 370 { 371 help(sitetab, (char *) 0); 372 } 373 | SITE SP HELP SP STRING CRLF 374 { 375 help(sitetab, $5); 376 } 377 | SITE SP UMASK check_login CRLF 378 { 379 int oldmask; 380 381 if ($4) { 382 oldmask = umask(0); 383 (void) umask(oldmask); 384 reply(200, "Current UMASK is %03o", oldmask); 385 } 386 } 387 | SITE SP UMASK check_login SP octal_number CRLF 388 { 389 int oldmask; 390 391 if ($4) { 392 if (($6 == -1) || ($6 > 0777)) { 393 reply(501, "Bad UMASK value"); 394 } else { 395 oldmask = umask($6); 396 reply(200, 397 "UMASK set to %03o (was %03o)", 398 $6, oldmask); 399 } 400 } 401 } 402 | SITE SP CHMOD check_login SP octal_number SP pathname CRLF 403 { 404 if ($4 && ($8 != NULL)) { 405 if ($6 > 0777) 406 reply(501, 407 "CHMOD: Mode value must be between 0 and 0777"); 408 else if (chmod($8, $6) < 0) 409 perror_reply(550, $8); 410 else 411 reply(200, "CHMOD command successful."); 412 } 413 if ($8 != NULL) 414 free($8); 415 } 416 | SITE SP IDLE CRLF 417 { 418 reply(200, 419 "Current IDLE time limit is %d seconds; max %d", 420 timeout, maxtimeout); 421 } 422 | SITE SP IDLE SP NUMBER CRLF 423 { 424 if ($5 < 30 || $5 > maxtimeout) { 425 reply(501, 426 "Maximum IDLE time must be between 30 and %d seconds", 427 maxtimeout); 428 } else { 429 timeout = $5; 430 (void) alarm((unsigned) timeout); 431 reply(200, 432 "Maximum IDLE time set to %d seconds", 433 timeout); 434 } 435 } 436 | STOU check_login SP pathname CRLF 437 { 438 if ($2 && $4 != NULL) 439 store($4, "w", 1); 440 if ($4 != NULL) 441 free($4); 442 } 443 | SYST CRLF 444 { 445 #ifdef unix 446 #ifdef BSD 447 reply(215, "UNIX Type: L%d Version: BSD-%d", 448 NBBY, BSD); 449 #else /* BSD */ 450 reply(215, "UNIX Type: L%d", NBBY); 451 #endif /* BSD */ 452 #else /* unix */ 453 reply(215, "UNKNOWN Type: L%d", NBBY); 454 #endif /* unix */ 455 } 456 457 /* 458 * SIZE is not in RFC959, but Postel has blessed it and 459 * it will be in the updated RFC. 460 * 461 * Return size of file in a format suitable for 462 * using with RESTART (we just count bytes). 463 */ 464 | SIZE check_login SP pathname CRLF 465 { 466 if ($2 && $4 != NULL) 467 sizecmd($4); 468 if ($4 != NULL) 469 free($4); 470 } 471 472 /* 473 * MDTM is not in RFC959, but Postel has blessed it and 474 * it will be in the updated RFC. 475 * 476 * Return modification time of file as an ISO 3307 477 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 478 * where xxx is the fractional second (of any precision, 479 * not necessarily 3 digits) 480 */ 481 | MDTM check_login SP pathname CRLF 482 { 483 if ($2 && $4 != NULL) { 484 struct stat stbuf; 485 if (stat($4, &stbuf) < 0) 486 reply(550, "%s: %s", 487 $4, strerror(errno)); 488 else if (!S_ISREG(stbuf.st_mode)) { 489 reply(550, "%s: not a plain file.", $4); 490 } else { 491 struct tm *t; 492 t = gmtime(&stbuf.st_mtime); 493 reply(213, 494 "19%02d%02d%02d%02d%02d%02d", 495 t->tm_year, t->tm_mon+1, t->tm_mday, 496 t->tm_hour, t->tm_min, t->tm_sec); 497 } 498 } 499 if ($4 != NULL) 500 free($4); 501 } 502 | QUIT CRLF 503 { 504 reply(221, "Goodbye."); 505 dologout(0); 506 } 507 | error CRLF 508 { 509 yyerrok; 510 } 511 ; 512 rcmd 513 : RNFR check_login SP pathname CRLF 514 { 515 char *renamefrom(); 516 517 restart_point = (off_t) 0; 518 if ($2 && $4) { 519 fromname = renamefrom($4); 520 if (fromname == (char *) 0 && $4) { 521 free($4); 522 } 523 } 524 } 525 | REST SP byte_size CRLF 526 { 527 fromname = (char *) 0; 528 restart_point = $3; /* XXX $3 is only "int" */ 529 reply(350, "Restarting at %qd. %s", restart_point, 530 "Send STORE or RETRIEVE to initiate transfer."); 531 } 532 ; 533 534 username 535 : STRING 536 ; 537 538 password 539 : /* empty */ 540 { 541 $$ = (char *)calloc(1, sizeof(char)); 542 } 543 | STRING 544 ; 545 546 byte_size 547 : NUMBER 548 ; 549 550 host_port 551 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 552 NUMBER COMMA NUMBER 553 { 554 char *a, *p; 555 556 a = (char *)&data_dest.sin_addr; 557 a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 558 p = (char *)&data_dest.sin_port; 559 p[0] = $9; p[1] = $11; 560 data_dest.sin_family = AF_INET; 561 } 562 ; 563 564 form_code 565 : N 566 { 567 $$ = FORM_N; 568 } 569 | T 570 { 571 $$ = FORM_T; 572 } 573 | C 574 { 575 $$ = FORM_C; 576 } 577 ; 578 579 type_code 580 : A 581 { 582 cmd_type = TYPE_A; 583 cmd_form = FORM_N; 584 } 585 | A SP form_code 586 { 587 cmd_type = TYPE_A; 588 cmd_form = $3; 589 } 590 | E 591 { 592 cmd_type = TYPE_E; 593 cmd_form = FORM_N; 594 } 595 | E SP form_code 596 { 597 cmd_type = TYPE_E; 598 cmd_form = $3; 599 } 600 | I 601 { 602 cmd_type = TYPE_I; 603 } 604 | L 605 { 606 cmd_type = TYPE_L; 607 cmd_bytesz = NBBY; 608 } 609 | L SP byte_size 610 { 611 cmd_type = TYPE_L; 612 cmd_bytesz = $3; 613 } 614 /* this is for a bug in the BBN ftp */ 615 | L byte_size 616 { 617 cmd_type = TYPE_L; 618 cmd_bytesz = $2; 619 } 620 ; 621 622 struct_code 623 : F 624 { 625 $$ = STRU_F; 626 } 627 | R 628 { 629 $$ = STRU_R; 630 } 631 | P 632 { 633 $$ = STRU_P; 634 } 635 ; 636 637 mode_code 638 : S 639 { 640 $$ = MODE_S; 641 } 642 | B 643 { 644 $$ = MODE_B; 645 } 646 | C 647 { 648 $$ = MODE_C; 649 } 650 ; 651 652 pathname 653 : pathstring 654 { 655 /* 656 * Problem: this production is used for all pathname 657 * processing, but only gives a 550 error reply. 658 * This is a valid reply in some cases but not in others. 659 */ 660 if (logged_in && $1 && *$1 == '~') { 661 glob_t gl; 662 int flags = 663 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; 664 665 memset(&gl, 0, sizeof(gl)); 666 if (glob($1, flags, NULL, &gl) || 667 gl.gl_pathc == 0) { 668 reply(550, "not found"); 669 $$ = NULL; 670 } else { 671 $$ = strdup(gl.gl_pathv[0]); 672 } 673 globfree(&gl); 674 free($1); 675 } else 676 $$ = $1; 677 } 678 ; 679 680 pathstring 681 : STRING 682 ; 683 684 octal_number 685 : NUMBER 686 { 687 int ret, dec, multby, digit; 688 689 /* 690 * Convert a number that was read as decimal number 691 * to what it would be if it had been read as octal. 692 */ 693 dec = $1; 694 multby = 1; 695 ret = 0; 696 while (dec) { 697 digit = dec%10; 698 if (digit > 7) { 699 ret = -1; 700 break; 701 } 702 ret += digit * multby; 703 multby *= 8; 704 dec /= 10; 705 } 706 $$ = ret; 707 } 708 ; 709 710 711 check_login 712 : /* empty */ 713 { 714 if (logged_in) 715 $$ = 1; 716 else { 717 reply(530, "Please login with USER and PASS."); 718 $$ = 0; 719 } 720 } 721 ; 722 723 %% 724 725 extern jmp_buf errcatch; 726 727 #define CMD 0 /* beginning of command */ 728 #define ARGS 1 /* expect miscellaneous arguments */ 729 #define STR1 2 /* expect SP followed by STRING */ 730 #define STR2 3 /* expect STRING */ 731 #define OSTR 4 /* optional SP then STRING */ 732 #define ZSTR1 5 /* SP then optional STRING */ 733 #define ZSTR2 6 /* optional STRING after SP */ 734 #define SITECMD 7 /* SITE command */ 735 #define NSTR 8 /* Number followed by a string */ 736 737 struct tab { 738 char *name; 739 short token; 740 short state; 741 short implemented; /* 1 if command is implemented */ 742 char *help; 743 }; 744 745 struct tab cmdtab[] = { /* In order defined in RFC 765 */ 746 { "USER", USER, STR1, 1, "<sp> username" }, 747 { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 748 { "ACCT", ACCT, STR1, 0, "(specify account)" }, 749 { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 750 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 751 { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 752 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 753 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 754 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 755 { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 756 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 757 { "RETR", RETR, STR1, 1, "<sp> file-name" }, 758 { "STOR", STOR, STR1, 1, "<sp> file-name" }, 759 { "APPE", APPE, STR1, 1, "<sp> file-name" }, 760 { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 761 { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 762 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 763 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 764 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 765 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 766 { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 767 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 768 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 769 { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 770 { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 771 { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 772 { "DELE", DELE, STR1, 1, "<sp> file-name" }, 773 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 774 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 775 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 776 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 777 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 778 { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 779 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 780 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 781 { "NOOP", NOOP, ARGS, 1, "" }, 782 { "MKD", MKD, STR1, 1, "<sp> path-name" }, 783 { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 784 { "RMD", RMD, STR1, 1, "<sp> path-name" }, 785 { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 786 { "PWD", PWD, ARGS, 1, "(return current directory)" }, 787 { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 788 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 789 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 790 { "STOU", STOU, STR1, 1, "<sp> file-name" }, 791 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 792 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 793 { NULL, 0, 0, 0, 0 } 794 }; 795 796 struct tab sitetab[] = { 797 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 798 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 799 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 800 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 801 { NULL, 0, 0, 0, 0 } 802 }; 803 804 static char *copy __P((char *)); 805 static void help __P((struct tab *, char *)); 806 static struct tab * 807 lookup __P((struct tab *, char *)); 808 static void sizecmd __P((char *)); 809 static void toolong __P((int)); 810 static int yylex __P((void)); 811 812 static struct tab * 813 lookup(p, cmd) 814 struct tab *p; 815 char *cmd; 816 { 817 818 for (; p->name != NULL; p++) 819 if (strcmp(cmd, p->name) == 0) 820 return (p); 821 return (0); 822 } 823 824 #include <arpa/telnet.h> 825 826 /* 827 * getline - a hacked up version of fgets to ignore TELNET escape codes. 828 */ 829 char * 830 getline(s, n, iop) 831 char *s; 832 int n; 833 FILE *iop; 834 { 835 int c; 836 register char *cs; 837 838 cs = s; 839 /* tmpline may contain saved command from urgent mode interruption */ 840 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 841 *cs++ = tmpline[c]; 842 if (tmpline[c] == '\n') { 843 *cs++ = '\0'; 844 if (debug) 845 syslog(LOG_DEBUG, "command: %s", s); 846 tmpline[0] = '\0'; 847 return(s); 848 } 849 if (c == 0) 850 tmpline[0] = '\0'; 851 } 852 while ((c = getc(iop)) != EOF) { 853 c &= 0377; 854 if (c == IAC) { 855 if ((c = getc(iop)) != EOF) { 856 c &= 0377; 857 switch (c) { 858 case WILL: 859 case WONT: 860 c = getc(iop); 861 printf("%c%c%c", IAC, DONT, 0377&c); 862 (void) fflush(stdout); 863 continue; 864 case DO: 865 case DONT: 866 c = getc(iop); 867 printf("%c%c%c", IAC, WONT, 0377&c); 868 (void) fflush(stdout); 869 continue; 870 case IAC: 871 break; 872 default: 873 continue; /* ignore command */ 874 } 875 } 876 } 877 *cs++ = c; 878 if (--n <= 0 || c == '\n') 879 break; 880 } 881 if (c == EOF && cs == s) 882 return (NULL); 883 *cs++ = '\0'; 884 if (debug) { 885 if (!guest && strncasecmp("pass ", s, 5) == 0) { 886 /* Don't syslog passwords */ 887 syslog(LOG_DEBUG, "command: %.5s ???", s); 888 } else { 889 register char *cp; 890 register int len; 891 892 /* Don't syslog trailing CR-LF */ 893 len = strlen(s); 894 cp = s + len - 1; 895 while (cp >= s && (*cp == '\n' || *cp == '\r')) { 896 --cp; 897 --len; 898 } 899 syslog(LOG_DEBUG, "command: %.*s", len, s); 900 } 901 } 902 return (s); 903 } 904 905 static void 906 toolong(signo) 907 int signo; 908 { 909 910 reply(421, 911 "Timeout (%d seconds): closing control connection.", timeout); 912 if (logging) 913 syslog(LOG_INFO, "User %s timed out after %d seconds", 914 (pw ? pw -> pw_name : "unknown"), timeout); 915 dologout(1); 916 } 917 918 static int 919 yylex() 920 { 921 static int cpos, state; 922 char *cp, *cp2; 923 struct tab *p; 924 int n; 925 char c; 926 927 for (;;) { 928 switch (state) { 929 930 case CMD: 931 (void) signal(SIGALRM, toolong); 932 (void) alarm((unsigned) timeout); 933 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 934 reply(221, "You could at least say goodbye."); 935 dologout(0); 936 } 937 (void) alarm(0); 938 #ifdef SETPROCTITLE 939 if (strncasecmp(cbuf, "PASS", 4) != NULL) 940 setproctitle("%s: %s", proctitle, cbuf); 941 #endif /* SETPROCTITLE */ 942 if ((cp = strchr(cbuf, '\r'))) { 943 *cp++ = '\n'; 944 *cp = '\0'; 945 } 946 if ((cp = strpbrk(cbuf, " \n"))) 947 cpos = cp - cbuf; 948 if (cpos == 0) 949 cpos = 4; 950 c = cbuf[cpos]; 951 cbuf[cpos] = '\0'; 952 upper(cbuf); 953 p = lookup(cmdtab, cbuf); 954 cbuf[cpos] = c; 955 if (p != 0) { 956 if (p->implemented == 0) { 957 nack(p->name); 958 longjmp(errcatch,0); 959 /* NOTREACHED */ 960 } 961 state = p->state; 962 yylval.s = p->name; 963 return (p->token); 964 } 965 break; 966 967 case SITECMD: 968 if (cbuf[cpos] == ' ') { 969 cpos++; 970 return (SP); 971 } 972 cp = &cbuf[cpos]; 973 if ((cp2 = strpbrk(cp, " \n"))) 974 cpos = cp2 - cbuf; 975 c = cbuf[cpos]; 976 cbuf[cpos] = '\0'; 977 upper(cp); 978 p = lookup(sitetab, cp); 979 cbuf[cpos] = c; 980 if (guest == 0 && p != 0) { 981 if (p->implemented == 0) { 982 state = CMD; 983 nack(p->name); 984 longjmp(errcatch,0); 985 /* NOTREACHED */ 986 } 987 state = p->state; 988 yylval.s = p->name; 989 return (p->token); 990 } 991 state = CMD; 992 break; 993 994 case OSTR: 995 if (cbuf[cpos] == '\n') { 996 state = CMD; 997 return (CRLF); 998 } 999 /* FALLTHROUGH */ 1000 1001 case STR1: 1002 case ZSTR1: 1003 dostr1: 1004 if (cbuf[cpos] == ' ') { 1005 cpos++; 1006 state = state == OSTR ? STR2 : ++state; 1007 return (SP); 1008 } 1009 break; 1010 1011 case ZSTR2: 1012 if (cbuf[cpos] == '\n') { 1013 state = CMD; 1014 return (CRLF); 1015 } 1016 /* FALLTHROUGH */ 1017 1018 case STR2: 1019 cp = &cbuf[cpos]; 1020 n = strlen(cp); 1021 cpos += n - 1; 1022 /* 1023 * Make sure the string is nonempty and \n terminated. 1024 */ 1025 if (n > 1 && cbuf[cpos] == '\n') { 1026 cbuf[cpos] = '\0'; 1027 yylval.s = copy(cp); 1028 cbuf[cpos] = '\n'; 1029 state = ARGS; 1030 return (STRING); 1031 } 1032 break; 1033 1034 case NSTR: 1035 if (cbuf[cpos] == ' ') { 1036 cpos++; 1037 return (SP); 1038 } 1039 if (isdigit(cbuf[cpos])) { 1040 cp = &cbuf[cpos]; 1041 while (isdigit(cbuf[++cpos])) 1042 ; 1043 c = cbuf[cpos]; 1044 cbuf[cpos] = '\0'; 1045 yylval.i = atoi(cp); 1046 cbuf[cpos] = c; 1047 state = STR1; 1048 return (NUMBER); 1049 } 1050 state = STR1; 1051 goto dostr1; 1052 1053 case ARGS: 1054 if (isdigit(cbuf[cpos])) { 1055 cp = &cbuf[cpos]; 1056 while (isdigit(cbuf[++cpos])) 1057 ; 1058 c = cbuf[cpos]; 1059 cbuf[cpos] = '\0'; 1060 yylval.i = atoi(cp); 1061 cbuf[cpos] = c; 1062 return (NUMBER); 1063 } 1064 switch (cbuf[cpos++]) { 1065 1066 case '\n': 1067 state = CMD; 1068 return (CRLF); 1069 1070 case ' ': 1071 return (SP); 1072 1073 case ',': 1074 return (COMMA); 1075 1076 case 'A': 1077 case 'a': 1078 return (A); 1079 1080 case 'B': 1081 case 'b': 1082 return (B); 1083 1084 case 'C': 1085 case 'c': 1086 return (C); 1087 1088 case 'E': 1089 case 'e': 1090 return (E); 1091 1092 case 'F': 1093 case 'f': 1094 return (F); 1095 1096 case 'I': 1097 case 'i': 1098 return (I); 1099 1100 case 'L': 1101 case 'l': 1102 return (L); 1103 1104 case 'N': 1105 case 'n': 1106 return (N); 1107 1108 case 'P': 1109 case 'p': 1110 return (P); 1111 1112 case 'R': 1113 case 'r': 1114 return (R); 1115 1116 case 'S': 1117 case 's': 1118 return (S); 1119 1120 case 'T': 1121 case 't': 1122 return (T); 1123 1124 } 1125 break; 1126 1127 default: 1128 fatal("Unknown state in scanner."); 1129 } 1130 yyerror((char *) 0); 1131 state = CMD; 1132 longjmp(errcatch,0); 1133 } 1134 } 1135 1136 void 1137 upper(s) 1138 char *s; 1139 { 1140 while (*s != '\0') { 1141 if (islower(*s)) 1142 *s = toupper(*s); 1143 s++; 1144 } 1145 } 1146 1147 static char * 1148 copy(s) 1149 char *s; 1150 { 1151 char *p; 1152 1153 p = malloc((unsigned) strlen(s) + 1); 1154 if (p == NULL) 1155 fatal("Ran out of memory."); 1156 (void) strcpy(p, s); 1157 return (p); 1158 } 1159 1160 static void 1161 help(ctab, s) 1162 struct tab *ctab; 1163 char *s; 1164 { 1165 struct tab *c; 1166 int width, NCMDS; 1167 char *type; 1168 1169 if (ctab == sitetab) 1170 type = "SITE "; 1171 else 1172 type = ""; 1173 width = 0, NCMDS = 0; 1174 for (c = ctab; c->name != NULL; c++) { 1175 int len = strlen(c->name); 1176 1177 if (len > width) 1178 width = len; 1179 NCMDS++; 1180 } 1181 width = (width + 8) &~ 7; 1182 if (s == 0) { 1183 int i, j, w; 1184 int columns, lines; 1185 1186 lreply(214, "The following %scommands are recognized %s.", 1187 type, "(* =>'s unimplemented)"); 1188 columns = 76 / width; 1189 if (columns == 0) 1190 columns = 1; 1191 lines = (NCMDS + columns - 1) / columns; 1192 for (i = 0; i < lines; i++) { 1193 printf(" "); 1194 for (j = 0; j < columns; j++) { 1195 c = ctab + j * lines + i; 1196 printf("%s%c", c->name, 1197 c->implemented ? ' ' : '*'); 1198 if (c + lines >= &ctab[NCMDS]) 1199 break; 1200 w = strlen(c->name) + 1; 1201 while (w < width) { 1202 putchar(' '); 1203 w++; 1204 } 1205 } 1206 printf("\r\n"); 1207 } 1208 (void) fflush(stdout); 1209 reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1210 return; 1211 } 1212 upper(s); 1213 c = lookup(ctab, s); 1214 if (c == (struct tab *)0) { 1215 reply(502, "Unknown command %s.", s); 1216 return; 1217 } 1218 if (c->implemented) 1219 reply(214, "Syntax: %s%s %s", type, c->name, c->help); 1220 else 1221 reply(214, "%s%-*s\t%s; unimplemented.", type, width, 1222 c->name, c->help); 1223 } 1224 1225 static void 1226 sizecmd(filename) 1227 char *filename; 1228 { 1229 switch (type) { 1230 case TYPE_L: 1231 case TYPE_I: { 1232 struct stat stbuf; 1233 if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) 1234 reply(550, "%s: not a plain file.", filename); 1235 else 1236 reply(213, "%qu", stbuf.st_size); 1237 break; } 1238 case TYPE_A: { 1239 FILE *fin; 1240 int c; 1241 off_t count; 1242 struct stat stbuf; 1243 fin = fopen(filename, "r"); 1244 if (fin == NULL) { 1245 perror_reply(550, filename); 1246 return; 1247 } 1248 if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) { 1249 reply(550, "%s: not a plain file.", filename); 1250 (void) fclose(fin); 1251 return; 1252 } 1253 1254 count = 0; 1255 while((c=getc(fin)) != EOF) { 1256 if (c == '\n') /* will get expanded to \r\n */ 1257 count++; 1258 count++; 1259 } 1260 (void) fclose(fin); 1261 1262 reply(213, "%qd", count); 1263 break; } 1264 default: 1265 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 1266 } 1267 } 1268