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