ftpcmd.y revision 92282
1/* $NetBSD: ftpcmd.y,v 1.66 2001/12/01 10:25:30 lukem Exp $ */ 2 3/*- 4 * Copyright (c) 1997-2001 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Luke Mewburn. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39/* 40 * Copyright (c) 1985, 1988, 1993, 1994 41 * The Regents of the University of California. All rights reserved. 42 * 43 * Redistribution and use in source and binary forms, with or without 44 * modification, are permitted provided that the following conditions 45 * are met: 46 * 1. Redistributions of source code must retain the above copyright 47 * notice, this list of conditions and the following disclaimer. 48 * 2. Redistributions in binary form must reproduce the above copyright 49 * notice, this list of conditions and the following disclaimer in the 50 * documentation and/or other materials provided with the distribution. 51 * 3. All advertising materials mentioning features or use of this software 52 * must display the following acknowledgement: 53 * This product includes software developed by the University of 54 * California, Berkeley and its contributors. 55 * 4. Neither the name of the University nor the names of its contributors 56 * may be used to endorse or promote products derived from this software 57 * without specific prior written permission. 58 * 59 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 60 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 61 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 62 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 63 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 64 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 65 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 66 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 67 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 68 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 69 * SUCH DAMAGE. 70 * 71 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 72 */ 73 74/* 75 * Grammar for FTP commands. 76 * See RFC 959. 77 */ 78 79%{ 80#include "lukemftpd.h" 81 82#include "extern.h" 83#include "version.h" 84 85static int cmd_type; 86static int cmd_form; 87static int cmd_bytesz; 88 89char cbuf[FTP_BUFLEN]; 90char *cmdp; 91char *fromname; 92 93%} 94 95%union { 96 int i; 97 char *s; 98} 99 100%token 101 A B C E F I 102 L N P R S T 103 104 SP CRLF COMMA 105 106 USER PASS ACCT CWD CDUP SMNT 107 QUIT REIN PORT PASV TYPE STRU 108 MODE RETR STOR STOU APPE ALLO 109 REST RNFR RNTO ABOR DELE RMD 110 MKD PWD LIST NLST SITE SYST 111 STAT HELP NOOP 112 113 AUTH ADAT PROT PBSZ CCC MIC 114 CONF ENC 115 116 FEAT OPTS 117 118 SIZE MDTM MLST MLSD 119 120 LPRT LPSV EPRT EPSV 121 122 MAIL MLFL MRCP MRSQ MSAM MSND 123 MSOM 124 125 CHMOD IDLE RATEGET RATEPUT UMASK 126 127 LEXERR 128 129%token <s> STRING 130%token <s> ALL 131%token <i> NUMBER 132 133%type <i> check_login octal_number byte_size 134%type <i> struct_code mode_code type_code form_code decimal_integer 135%type <s> pathstring pathname password username 136%type <s> mechanism_name base64data prot_code 137 138%start cmd_sel 139 140%% 141 142cmd_sel 143 : cmd 144 { 145 fromname = NULL; 146 restart_point = (off_t) 0; 147 } 148 149 | rcmd 150 151 ; 152 153cmd 154 /* RFC 959 */ 155 : USER SP username CRLF 156 { 157 user($3); 158 free($3); 159 } 160 161 | PASS SP password CRLF 162 { 163 pass($3); 164 memset($3, 0, strlen($3)); 165 free($3); 166 } 167 168 | CWD check_login CRLF 169 { 170 if ($2) 171 cwd(homedir); 172 } 173 174 | CWD check_login SP pathname CRLF 175 { 176 if ($2 && $4 != NULL) 177 cwd($4); 178 if ($4 != NULL) 179 free($4); 180 } 181 182 | CDUP check_login CRLF 183 { 184 if ($2) 185 cwd(".."); 186 } 187 188 | QUIT CRLF 189 { 190 if (logged_in) { 191 reply(-221, "%s", ""); 192 reply(0, 193 "Data traffic for this session was " LLF " byte%s in " LLF " file%s.", 194 (LLT)total_data, PLURAL(total_data), 195 (LLT)total_files, PLURAL(total_files)); 196 reply(0, 197 "Total traffic for this session was " LLF " byte%s in " LLF " transfer%s.", 198 (LLT)total_bytes, PLURAL(total_bytes), 199 (LLT)total_xfers, PLURAL(total_xfers)); 200 } 201 reply(221, 202 "Thank you for using the FTP service on %s.", 203 hostname); 204 if (logged_in && logging) { 205 syslog(LOG_INFO, 206 "Data traffic: " LLF " byte%s in " LLF " file%s", 207 (LLT)total_data, PLURAL(total_data), 208 (LLT)total_files, PLURAL(total_files)); 209 syslog(LOG_INFO, 210 "Total traffic: " LLF " byte%s in " LLF " transfer%s", 211 (LLT)total_bytes, PLURAL(total_bytes), 212 (LLT)total_xfers, PLURAL(total_xfers)); 213 } 214 215 dologout(0); 216 } 217 218 | PORT check_login SP host_port CRLF 219 { 220 if ($2) 221 port_check("PORT", AF_INET); 222 } 223 224 | LPRT check_login SP host_long_port4 CRLF 225 { 226 if ($2) 227 port_check("LPRT", AF_INET); 228 } 229 230 | LPRT check_login SP host_long_port6 CRLF 231 { 232#ifdef INET6 233 if ($2) 234 port_check("LPRT", AF_INET6); 235#else 236 reply(500, "IPv6 support not available."); 237#endif 238 } 239 240 | EPRT check_login SP STRING CRLF 241 { 242 if ($2) { 243 if (extended_port($4) == 0) 244 port_check("EPRT", -1); 245 } 246 free($4); 247 } 248 249 | PASV check_login CRLF 250 { 251 if ($2) { 252 if (CURCLASS_FLAGS_ISSET(passive)) 253 passive(); 254 else 255 reply(500, "PASV mode not available."); 256 } 257 } 258 259 | LPSV check_login CRLF 260 { 261 if ($2) { 262 if (epsvall) 263 reply(501, 264 "LPSV disallowed after EPSV ALL"); 265 else 266 long_passive("LPSV", PF_UNSPEC); 267 } 268 } 269 270 | EPSV check_login SP NUMBER CRLF 271 { 272 if ($2) 273 long_passive("EPSV", epsvproto2af($4)); 274 } 275 276 | EPSV check_login SP ALL CRLF 277 { 278 if ($2) { 279 reply(200, "EPSV ALL command successful."); 280 epsvall++; 281 } 282 } 283 284 | EPSV check_login CRLF 285 { 286 if ($2) 287 long_passive("EPSV", PF_UNSPEC); 288 } 289 290 | TYPE check_login SP type_code CRLF 291 { 292 if ($2) { 293 294 switch (cmd_type) { 295 296 case TYPE_A: 297 if (cmd_form == FORM_N) { 298 reply(200, "Type set to A."); 299 type = cmd_type; 300 form = cmd_form; 301 } else 302 reply(504, "Form must be N."); 303 break; 304 305 case TYPE_E: 306 reply(504, "Type E not implemented."); 307 break; 308 309 case TYPE_I: 310 reply(200, "Type set to I."); 311 type = cmd_type; 312 break; 313 314 case TYPE_L: 315#if NBBY == 8 316 if (cmd_bytesz == 8) { 317 reply(200, 318 "Type set to L (byte size 8)."); 319 type = cmd_type; 320 } else 321 reply(504, "Byte size must be 8."); 322#else /* NBBY == 8 */ 323 UNIMPLEMENTED for NBBY != 8 324#endif /* NBBY == 8 */ 325 } 326 327 } 328 } 329 330 | STRU check_login SP struct_code CRLF 331 { 332 if ($2) { 333 switch ($4) { 334 335 case STRU_F: 336 reply(200, "STRU F ok."); 337 break; 338 339 default: 340 reply(504, "Unimplemented STRU type."); 341 } 342 } 343 } 344 345 | MODE check_login SP mode_code CRLF 346 { 347 if ($2) { 348 switch ($4) { 349 350 case MODE_S: 351 reply(200, "MODE S ok."); 352 break; 353 354 default: 355 reply(502, "Unimplemented MODE type."); 356 } 357 } 358 } 359 360 | RETR check_login SP pathname CRLF 361 { 362 if ($2 && $4 != NULL) 363 retrieve(NULL, $4); 364 if ($4 != NULL) 365 free($4); 366 } 367 368 | STOR SP pathname CRLF 369 { 370 if (check_write($3, 1)) 371 store($3, "w", 0); 372 if ($3 != NULL) 373 free($3); 374 } 375 376 | STOU SP pathname CRLF 377 { 378 if (check_write($3, 1)) 379 store($3, "w", 1); 380 if ($3 != NULL) 381 free($3); 382 } 383 384 | APPE SP pathname CRLF 385 { 386 if (check_write($3, 1)) 387 store($3, "a", 0); 388 if ($3 != NULL) 389 free($3); 390 } 391 392 | ALLO check_login SP NUMBER CRLF 393 { 394 if ($2) 395 reply(202, "ALLO command ignored."); 396 } 397 398 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 399 { 400 if ($2) 401 reply(202, "ALLO command ignored."); 402 } 403 404 | RNTO SP pathname CRLF 405 { 406 if (check_write($3, 0)) { 407 if (fromname) { 408 renamecmd(fromname, $3); 409 free(fromname); 410 fromname = NULL; 411 } else { 412 reply(503, "Bad sequence of commands."); 413 } 414 } 415 if ($3 != NULL) 416 free($3); 417 } 418 419 | ABOR check_login CRLF 420 { 421 if (is_oob) 422 abor(); 423 else if ($2) 424 reply(225, "ABOR command successful."); 425 } 426 427 | DELE SP pathname CRLF 428 { 429 if (check_write($3, 0)) 430 delete($3); 431 if ($3 != NULL) 432 free($3); 433 } 434 435 | RMD SP pathname CRLF 436 { 437 if (check_write($3, 0)) 438 removedir($3); 439 if ($3 != NULL) 440 free($3); 441 } 442 443 | MKD SP pathname CRLF 444 { 445 if (check_write($3, 0)) 446 makedir($3); 447 if ($3 != NULL) 448 free($3); 449 } 450 451 | PWD check_login CRLF 452 { 453 if ($2) 454 pwd(); 455 } 456 457 | LIST check_login CRLF 458 { 459 char *argv[] = { INTERNAL_LS, "-lgA", NULL }; 460 461 if ($2) 462 retrieve(argv, ""); 463 } 464 465 | LIST check_login SP pathname CRLF 466 { 467 char *argv[] = { INTERNAL_LS, "-lgA", NULL, NULL }; 468 469 if ($2 && $4 != NULL) { 470 argv[2] = $4; 471 retrieve(argv, $4); 472 } 473 if ($4 != NULL) 474 free($4); 475 } 476 477 | NLST check_login CRLF 478 { 479 if ($2) 480 send_file_list("."); 481 } 482 483 | NLST check_login SP pathname CRLF 484 { 485 if ($2) 486 send_file_list($4); 487 free($4); 488 } 489 490 | SITE SP HELP CRLF 491 { 492 help(sitetab, NULL); 493 } 494 495 | SITE SP CHMOD SP octal_number SP pathname CRLF 496 { 497 if (check_write($7, 0)) { 498 if ($5 > 0777) 499 reply(501, 500 "CHMOD: Mode value must be between 0 and 0777"); 501 else if (chmod($7, $5) < 0) 502 perror_reply(550, $7); 503 else 504 reply(200, "CHMOD command successful."); 505 } 506 if ($7 != NULL) 507 free($7); 508 } 509 510 | SITE SP HELP SP STRING CRLF 511 { 512 help(sitetab, $5); 513 free($5); 514 } 515 516 | SITE SP IDLE check_login CRLF 517 { 518 if ($4) { 519 reply(200, 520 "Current IDLE time limit is %d seconds; max %d", 521 curclass.timeout, curclass.maxtimeout); 522 } 523 } 524 525 | SITE SP IDLE check_login SP NUMBER CRLF 526 { 527 if ($4) { 528 if ($6 < 30 || $6 > curclass.maxtimeout) { 529 reply(501, 530 "IDLE time limit must be between 30 and %d seconds", 531 curclass.maxtimeout); 532 } else { 533 curclass.timeout = $6; 534 (void) alarm(curclass.timeout); 535 reply(200, 536 "IDLE time limit set to %d seconds", 537 curclass.timeout); 538 } 539 } 540 } 541 542 | SITE SP RATEGET check_login CRLF 543 { 544 if ($4) { 545 reply(200, 546 "Current RATEGET is " LLF " bytes/sec", 547 (LLT)curclass.rateget); 548 } 549 } 550 551 | SITE SP RATEGET check_login SP STRING CRLF 552 { 553 char *p = $6; 554 LLT rate; 555 556 if ($4) { 557 rate = strsuftoll(p); 558 if (rate == -1) 559 reply(501, "Invalid RATEGET %s", p); 560 else if (curclass.maxrateget && 561 rate > curclass.maxrateget) 562 reply(501, 563 "RATEGET " LLF " is larger than maximum RATEGET " LLF, 564 (LLT)rate, 565 (LLT)curclass.maxrateget); 566 else { 567 curclass.rateget = rate; 568 reply(200, 569 "RATEGET set to " LLF " bytes/sec", 570 (LLT)curclass.rateget); 571 } 572 } 573 free($6); 574 } 575 576 | SITE SP RATEPUT check_login CRLF 577 { 578 if ($4) { 579 reply(200, 580 "Current RATEPUT is " LLF " bytes/sec", 581 (LLT)curclass.rateput); 582 } 583 } 584 585 | SITE SP RATEPUT check_login SP STRING CRLF 586 { 587 char *p = $6; 588 LLT rate; 589 590 if ($4) { 591 rate = strsuftoll(p); 592 if (rate == -1) 593 reply(501, "Invalid RATEPUT %s", p); 594 else if (curclass.maxrateput && 595 rate > curclass.maxrateput) 596 reply(501, 597 "RATEPUT " LLF " is larger than maximum RATEPUT " LLF, 598 (LLT)rate, 599 (LLT)curclass.maxrateput); 600 else { 601 curclass.rateput = rate; 602 reply(200, 603 "RATEPUT set to " LLF " bytes/sec", 604 (LLT)curclass.rateput); 605 } 606 } 607 free($6); 608 } 609 610 | SITE SP UMASK check_login CRLF 611 { 612 int oldmask; 613 614 if ($4) { 615 oldmask = umask(0); 616 (void) umask(oldmask); 617 reply(200, "Current UMASK is %03o", oldmask); 618 } 619 } 620 621 | SITE SP UMASK check_login SP octal_number CRLF 622 { 623 int oldmask; 624 625 if ($4 && CURCLASS_FLAGS_ISSET(modify)) { 626 if (($6 == -1) || ($6 > 0777)) { 627 reply(501, "Bad UMASK value"); 628 } else { 629 oldmask = umask($6); 630 reply(200, 631 "UMASK set to %03o (was %03o)", 632 $6, oldmask); 633 } 634 } 635 } 636 637 | SYST CRLF 638 { 639 if (EMPTYSTR(version)) 640 reply(215, "UNIX Type: L%d", NBBY); 641 else 642 reply(215, "UNIX Type: L%d Version: %s", NBBY, 643 version); 644 } 645 646 | STAT check_login SP pathname CRLF 647 { 648 if ($2 && $4 != NULL) 649 statfilecmd($4); 650 if ($4 != NULL) 651 free($4); 652 } 653 654 | STAT CRLF 655 { 656 if (is_oob) 657 statxfer(); 658 else 659 statcmd(); 660 } 661 662 | HELP CRLF 663 { 664 help(cmdtab, NULL); 665 } 666 667 | HELP SP STRING CRLF 668 { 669 char *cp = $3; 670 671 if (strncasecmp(cp, "SITE", 4) == 0) { 672 cp = $3 + 4; 673 if (*cp == ' ') 674 cp++; 675 if (*cp) 676 help(sitetab, cp); 677 else 678 help(sitetab, NULL); 679 } else 680 help(cmdtab, $3); 681 free($3); 682 } 683 684 | NOOP CRLF 685 { 686 reply(200, "NOOP command successful."); 687 } 688 689 /* RFC 2228 */ 690 | AUTH SP mechanism_name CRLF 691 { 692 reply(502, "RFC 2228 authentication not implemented."); 693 free($3); 694 } 695 696 | ADAT SP base64data CRLF 697 { 698 reply(503, 699 "Please set authentication state with AUTH."); 700 free($3); 701 } 702 703 | PROT SP prot_code CRLF 704 { 705 reply(503, 706 "Please set protection buffer size with PBSZ."); 707 free($3); 708 } 709 710 | PBSZ SP decimal_integer CRLF 711 { 712 reply(503, 713 "Please set authentication state with AUTH."); 714 } 715 716 | CCC CRLF 717 { 718 reply(533, "No protection enabled."); 719 } 720 721 | MIC SP base64data CRLF 722 { 723 reply(502, "RFC 2228 authentication not implemented."); 724 free($3); 725 } 726 727 | CONF SP base64data CRLF 728 { 729 reply(502, "RFC 2228 authentication not implemented."); 730 free($3); 731 } 732 733 | ENC SP base64data CRLF 734 { 735 reply(502, "RFC 2228 authentication not implemented."); 736 free($3); 737 } 738 739 /* RFC 2389 */ 740 | FEAT CRLF 741 { 742 743 feat(); 744 } 745 746 | OPTS SP STRING CRLF 747 { 748 749 opts($3); 750 free($3); 751 } 752 753 754 /* extensions from draft-ietf-ftpext-mlst-11 */ 755 756 /* 757 * Return size of file in a format suitable for 758 * using with RESTART (we just count bytes). 759 */ 760 | SIZE check_login SP pathname CRLF 761 { 762 if ($2 && $4 != NULL) 763 sizecmd($4); 764 if ($4 != NULL) 765 free($4); 766 } 767 768 /* 769 * Return modification time of file as an ISO 3307 770 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 771 * where xxx is the fractional second (of any precision, 772 * not necessarily 3 digits) 773 */ 774 | MDTM check_login SP pathname CRLF 775 { 776 if ($2 && $4 != NULL) { 777 struct stat stbuf; 778 if (stat($4, &stbuf) < 0) 779 perror_reply(550, $4); 780 else if (!S_ISREG(stbuf.st_mode)) { 781 reply(550, "%s: not a plain file.", $4); 782 } else { 783 struct tm *t; 784 785 t = gmtime(&stbuf.st_mtime); 786 reply(213, 787 "%04d%02d%02d%02d%02d%02d", 788 TM_YEAR_BASE + t->tm_year, 789 t->tm_mon+1, t->tm_mday, 790 t->tm_hour, t->tm_min, t->tm_sec); 791 } 792 } 793 if ($4 != NULL) 794 free($4); 795 } 796 797 | MLST check_login SP pathname CRLF 798 { 799 if ($2 && $4 != NULL) 800 mlst($4); 801 if ($4 != NULL) 802 free($4); 803 } 804 805 | MLST check_login CRLF 806 { 807 mlst(NULL); 808 } 809 810 | MLSD check_login SP pathname CRLF 811 { 812 if ($2 && $4 != NULL) 813 mlsd($4); 814 if ($4 != NULL) 815 free($4); 816 } 817 818 | MLSD check_login CRLF 819 { 820 mlsd(NULL); 821 } 822 823 | error CRLF 824 { 825 yyerrok; 826 } 827 ; 828 829rcmd 830 : REST check_login SP byte_size CRLF 831 { 832 if ($2) { 833 fromname = NULL; 834 restart_point = $4; /* XXX: $4 is only "int" */ 835 reply(350, 836 "Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.", 837 (LLT)restart_point); 838 } 839 } 840 841 | RNFR SP pathname CRLF 842 { 843 restart_point = (off_t) 0; 844 if (check_write($3, 0)) 845 fromname = renamefrom($3); 846 if ($3 != NULL) 847 free($3); 848 } 849 ; 850 851username 852 : STRING 853 ; 854 855password 856 : /* empty */ 857 { 858 $$ = (char *)calloc(1, sizeof(char)); 859 } 860 861 | STRING 862 ; 863 864byte_size 865 : NUMBER 866 ; 867 868host_port 869 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 870 NUMBER COMMA NUMBER 871 { 872 char *a, *p; 873 874 memset(&data_dest, 0, sizeof(data_dest)); 875 data_dest.su_len = sizeof(struct sockaddr_in); 876 data_dest.su_family = AF_INET; 877 p = (char *)&data_dest.su_port; 878 p[0] = $9; p[1] = $11; 879 a = (char *)&data_dest.su_addr; 880 a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 881 } 882 ; 883 884host_long_port4 885 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 886 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 887 NUMBER 888 { 889 char *a, *p; 890 891 memset(&data_dest, 0, sizeof(data_dest)); 892 data_dest.su_len = sizeof(struct sockaddr_in); 893 data_dest.su_family = AF_INET; 894 p = (char *)&data_dest.su_port; 895 p[0] = $15; p[1] = $17; 896 a = (char *)&data_dest.su_addr; 897 a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; 898 899 /* reject invalid LPRT command */ 900 if ($1 != 4 || $3 != 4 || $13 != 2) 901 memset(&data_dest, 0, sizeof(data_dest)); 902 } 903 ; 904 905host_long_port6 906 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 907 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 908 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 909 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 910 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 911 NUMBER 912 { 913#ifdef INET6 914 char *a, *p; 915 916 memset(&data_dest, 0, sizeof(data_dest)); 917 data_dest.su_len = sizeof(struct sockaddr_in6); 918 data_dest.su_family = AF_INET6; 919 p = (char *)&data_dest.su_port; 920 p[0] = $39; p[1] = $41; 921 a = (char *)&data_dest.si_su.su_sin6.sin6_addr; 922 a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; 923 a[4] = $13; a[5] = $15; a[6] = $17; a[7] = $19; 924 a[8] = $21; a[9] = $23; a[10] = $25; a[11] = $27; 925 a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35; 926 if (his_addr.su_family == AF_INET6) { 927 /* XXX: more sanity checks! */ 928 data_dest.su_scope_id = his_addr.su_scope_id; 929 } 930#else 931 memset(&data_dest, 0, sizeof(data_dest)); 932#endif /* INET6 */ 933 /* reject invalid LPRT command */ 934 if ($1 != 6 || $3 != 16 || $37 != 2) 935 memset(&data_dest, 0, sizeof(data_dest)); 936 } 937 ; 938 939form_code 940 : N 941 { 942 $$ = FORM_N; 943 } 944 945 | T 946 { 947 $$ = FORM_T; 948 } 949 950 | C 951 { 952 $$ = FORM_C; 953 } 954 ; 955 956type_code 957 : A 958 { 959 cmd_type = TYPE_A; 960 cmd_form = FORM_N; 961 } 962 963 | A SP form_code 964 { 965 cmd_type = TYPE_A; 966 cmd_form = $3; 967 } 968 969 | E 970 { 971 cmd_type = TYPE_E; 972 cmd_form = FORM_N; 973 } 974 975 | E SP form_code 976 { 977 cmd_type = TYPE_E; 978 cmd_form = $3; 979 } 980 981 | I 982 { 983 cmd_type = TYPE_I; 984 } 985 986 | L 987 { 988 cmd_type = TYPE_L; 989 cmd_bytesz = NBBY; 990 } 991 992 | L SP byte_size 993 { 994 cmd_type = TYPE_L; 995 cmd_bytesz = $3; 996 } 997 998 /* this is for a bug in the BBN ftp */ 999 | L byte_size 1000 { 1001 cmd_type = TYPE_L; 1002 cmd_bytesz = $2; 1003 } 1004 ; 1005 1006struct_code 1007 : F 1008 { 1009 $$ = STRU_F; 1010 } 1011 1012 | R 1013 { 1014 $$ = STRU_R; 1015 } 1016 1017 | P 1018 { 1019 $$ = STRU_P; 1020 } 1021 ; 1022 1023mode_code 1024 : S 1025 { 1026 $$ = MODE_S; 1027 } 1028 1029 | B 1030 { 1031 $$ = MODE_B; 1032 } 1033 1034 | C 1035 { 1036 $$ = MODE_C; 1037 } 1038 ; 1039 1040pathname 1041 : pathstring 1042 { 1043 /* 1044 * Problem: this production is used for all pathname 1045 * processing, but only gives a 550 error reply. 1046 * This is a valid reply in some cases but not in 1047 * others. 1048 */ 1049 if (logged_in && $1 && *$1 == '~') { 1050 char *path, *home, *result; 1051 size_t len; 1052 1053 path = strchr($1 + 1, '/'); 1054 if (path != NULL) 1055 *path++ = '\0'; 1056 if ($1[1] == '\0') 1057 home = homedir; 1058 else { 1059 struct passwd *hpw; 1060 1061 if ((hpw = getpwnam($1 + 1)) != NULL) 1062 home = hpw->pw_dir; 1063 else 1064 home = $1; 1065 } 1066 len = strlen(home) + 1; 1067 if (path != NULL) 1068 len += strlen(path) + 1; 1069 if ((result = malloc(len)) == NULL) 1070 fatal("Local resource failure: malloc"); 1071 strlcpy(result, home, len); 1072 if (path != NULL) { 1073 strlcat(result, "/", len); 1074 strlcat(result, path, len); 1075 } 1076 $$ = result; 1077 free($1); 1078 } else 1079 $$ = $1; 1080 } 1081 ; 1082 1083pathstring 1084 : STRING 1085 ; 1086 1087octal_number 1088 : NUMBER 1089 { 1090 int ret, dec, multby, digit; 1091 1092 /* 1093 * Convert a number that was read as decimal number 1094 * to what it would be if it had been read as octal. 1095 */ 1096 dec = $1; 1097 multby = 1; 1098 ret = 0; 1099 while (dec) { 1100 digit = dec%10; 1101 if (digit > 7) { 1102 ret = -1; 1103 break; 1104 } 1105 ret += digit * multby; 1106 multby *= 8; 1107 dec /= 10; 1108 } 1109 $$ = ret; 1110 } 1111 ; 1112 1113mechanism_name 1114 : STRING 1115 ; 1116 1117base64data 1118 : STRING 1119 ; 1120 1121prot_code 1122 : STRING 1123 ; 1124 1125decimal_integer 1126 : NUMBER 1127 ; 1128 1129check_login 1130 : /* empty */ 1131 { 1132 if (logged_in) 1133 $$ = 1; 1134 else { 1135 reply(530, "Please login with USER and PASS."); 1136 $$ = 0; 1137 hasyyerrored = 1; 1138 } 1139 } 1140 ; 1141 1142%% 1143 1144#define CMD 0 /* beginning of command */ 1145#define ARGS 1 /* expect miscellaneous arguments */ 1146#define STR1 2 /* expect SP followed by STRING */ 1147#define STR2 3 /* expect STRING */ 1148#define OSTR 4 /* optional SP then STRING */ 1149#define ZSTR1 5 /* SP then optional STRING */ 1150#define ZSTR2 6 /* optional STRING after SP */ 1151#define SITECMD 7 /* SITE command */ 1152#define NSTR 8 /* Number followed by a string */ 1153#define NOARGS 9 /* No arguments allowed */ 1154#define EOLN 10 /* End of line */ 1155 1156struct tab cmdtab[] = { 1157 /* From RFC 959, in order defined (5.3.1) */ 1158 { "USER", USER, STR1, 1, "<sp> username" }, 1159 { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 1160 { "ACCT", ACCT, STR1, 0, "(specify account)" }, 1161 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 1162 { "CDUP", CDUP, NOARGS, 1, "(change to parent directory)" }, 1163 { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 1164 { "QUIT", QUIT, NOARGS, 1, "(terminate service)" }, 1165 { "REIN", REIN, NOARGS, 0, "(reinitialize server state)" }, 1166 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 1167 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 1168 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 1169 { "PASV", PASV, NOARGS, 1, "(set server in passive mode)" }, 1170 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 1171 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 1172 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 1173 { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 1174 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 1175 { "RETR", RETR, STR1, 1, "<sp> file-name" }, 1176 { "STOR", STOR, STR1, 1, "<sp> file-name" }, 1177 { "STOU", STOU, STR1, 1, "<sp> file-name" }, 1178 { "APPE", APPE, STR1, 1, "<sp> file-name" }, 1179 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 1180 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 1181 { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 1182 { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 1183 { "ABOR", ABOR, NOARGS, 4, "(abort operation)" }, 1184 { "DELE", DELE, STR1, 1, "<sp> file-name" }, 1185 { "RMD", RMD, STR1, 1, "<sp> path-name" }, 1186 { "MKD", MKD, STR1, 1, "<sp> path-name" }, 1187 { "PWD", PWD, NOARGS, 1, "(return current directory)" }, 1188 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 1189 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 1190 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 1191 { "SYST", SYST, NOARGS, 1, "(get type of operating system)" }, 1192 { "STAT", STAT, OSTR, 4, "[ <sp> path-name ]" }, 1193 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 1194 { "NOOP", NOOP, NOARGS, 2, "" }, 1195 1196 /* From RFC 2228, in order defined */ 1197 { "AUTH", AUTH, STR1, 1, "<sp> mechanism-name" }, 1198 { "ADAT", ADAT, STR1, 1, "<sp> base-64-data" }, 1199 { "PROT", PROT, STR1, 1, "<sp> prot-code" }, 1200 { "PBSZ", PBSZ, ARGS, 1, "<sp> decimal-integer" }, 1201 { "CCC", CCC, NOARGS, 1, "(Disable data protection)" }, 1202 { "MIC", MIC, STR1, 4, "<sp> base64data" }, 1203 { "CONF", CONF, STR1, 4, "<sp> base64data" }, 1204 { "ENC", ENC, STR1, 4, "<sp> base64data" }, 1205 1206 /* From RFC 2389, in order defined */ 1207 { "FEAT", FEAT, NOARGS, 1, "(display extended features)" }, 1208 { "OPTS", OPTS, STR1, 1, "<sp> command [ <sp> options ]" }, 1209 1210 /* from draft-ietf-ftpext-mlst-11 */ 1211 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 1212 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 1213 { "MLST", MLST, OSTR, 2, "[ <sp> path-name ]" }, 1214 { "MLSD", MLSD, OSTR, 1, "[ <sp> directory-name ]" }, 1215 1216 /* obsolete commands */ 1217 { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 1218 { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 1219 { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 1220 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 1221 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 1222 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 1223 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 1224 { "XCUP", CDUP, NOARGS, 1, "(change to parent directory)" }, 1225 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 1226 { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 1227 { "XPWD", PWD, NOARGS, 1, "(return current directory)" }, 1228 { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 1229 1230 { NULL, 0, 0, 0, 0 } 1231}; 1232 1233struct tab sitetab[] = { 1234 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 1235 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 1236 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 1237 { "RATEGET", RATEGET,OSTR, 1, "[ <sp> get-throttle-rate ]" }, 1238 { "RATEPUT", RATEPUT,OSTR, 1, "[ <sp> put-throttle-rate ]" }, 1239 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 1240 { NULL, 0, 0, 0, NULL } 1241}; 1242 1243static int check_write(const char *, int); 1244static void help(struct tab *, const char *); 1245static void port_check(const char *, int); 1246static void toolong(int); 1247static int yylex(void); 1248 1249extern int epsvall; 1250 1251/* 1252 * Check if a filename is allowed to be modified (isupload == 0) or 1253 * uploaded (isupload == 1), and if necessary, check the filename is `sane'. 1254 */ 1255static int 1256check_write(const char *file, int isupload) 1257{ 1258 if (file == NULL) 1259 return (0); 1260 if (! logged_in) { 1261 reply(530, "Please login with USER and PASS."); 1262 return (0); 1263 } 1264 /* checking modify */ 1265 if (! isupload && ! CURCLASS_FLAGS_ISSET(modify)) { 1266 reply(502, "No permission to use this command."); 1267 return (0); 1268 } 1269 /* checking upload */ 1270 if (isupload && ! CURCLASS_FLAGS_ISSET(upload)) { 1271 reply(502, "No permission to use this command."); 1272 return (0); 1273 } 1274 /* checking sanenames */ 1275 if (CURCLASS_FLAGS_ISSET(sanenames)) { 1276 const char *p; 1277 1278 if (file[0] == '.') 1279 goto insane_name; 1280 for (p = file; *p; p++) { 1281 if (isalnum(*p) || *p == '-' || *p == '+' || 1282 *p == ',' || *p == '.' || *p == '_') 1283 continue; 1284 insane_name: 1285 reply(553, "File name `%s' not allowed.", file); 1286 return (0); 1287 } 1288 } 1289 return (1); 1290} 1291 1292struct tab * 1293lookup(struct tab *p, const char *cmd) 1294{ 1295 1296 for (; p->name != NULL; p++) 1297 if (strcasecmp(cmd, p->name) == 0) 1298 return (p); 1299 return (0); 1300} 1301 1302#include <arpa/telnet.h> 1303 1304/* 1305 * getline - a hacked up version of fgets to ignore TELNET escape codes. 1306 */ 1307char * 1308getline(char *s, int n, FILE *iop) 1309{ 1310 int c; 1311 char *cs; 1312 1313 cs = s; 1314/* tmpline may contain saved command from urgent mode interruption */ 1315 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 1316 *cs++ = tmpline[c]; 1317 if (tmpline[c] == '\n') { 1318 *cs++ = '\0'; 1319 if (debug) 1320 syslog(LOG_DEBUG, "command: %s", s); 1321 tmpline[0] = '\0'; 1322 return(s); 1323 } 1324 if (c == 0) 1325 tmpline[0] = '\0'; 1326 } 1327 while ((c = getc(iop)) != EOF) { 1328 total_bytes++; 1329 total_bytes_in++; 1330 c &= 0377; 1331 if (c == IAC) { 1332 if ((c = getc(iop)) != EOF) { 1333 total_bytes++; 1334 total_bytes_in++; 1335 c &= 0377; 1336 switch (c) { 1337 case WILL: 1338 case WONT: 1339 c = getc(iop); 1340 total_bytes++; 1341 total_bytes_in++; 1342 cprintf(stdout, "%c%c%c", IAC, DONT, 0377&c); 1343 (void) fflush(stdout); 1344 continue; 1345 case DO: 1346 case DONT: 1347 c = getc(iop); 1348 total_bytes++; 1349 total_bytes_in++; 1350 cprintf(stdout, "%c%c%c", IAC, WONT, 0377&c); 1351 (void) fflush(stdout); 1352 continue; 1353 case IAC: 1354 break; 1355 default: 1356 continue; /* ignore command */ 1357 } 1358 } 1359 } 1360 *cs++ = c; 1361 if (--n <= 0 || c == '\n') 1362 break; 1363 } 1364 if (c == EOF && cs == s) 1365 return (NULL); 1366 *cs++ = '\0'; 1367 if (debug) { 1368 if ((curclass.type != CLASS_GUEST && 1369 strncasecmp(s, "PASS ", 5) == 0) || 1370 strncasecmp(s, "ACCT ", 5) == 0) { 1371 /* Don't syslog passwords */ 1372 syslog(LOG_DEBUG, "command: %.4s ???", s); 1373 } else { 1374 char *cp; 1375 int len; 1376 1377 /* Don't syslog trailing CR-LF */ 1378 len = strlen(s); 1379 cp = s + len - 1; 1380 while (cp >= s && (*cp == '\n' || *cp == '\r')) { 1381 --cp; 1382 --len; 1383 } 1384 syslog(LOG_DEBUG, "command: %.*s", len, s); 1385 } 1386 } 1387 return (s); 1388} 1389 1390static void 1391toolong(int signo) 1392{ 1393 1394 reply(421, 1395 "Timeout (%d seconds): closing control connection.", 1396 curclass.timeout); 1397 if (logging) 1398 syslog(LOG_INFO, "User %s timed out after %d seconds", 1399 (pw ? pw->pw_name : "unknown"), curclass.timeout); 1400 dologout(1); 1401} 1402 1403void 1404ftp_handle_line(char *cp) 1405{ 1406 1407 cmdp = cp; 1408 yyparse(); 1409} 1410 1411void 1412ftp_loop(void) 1413{ 1414 1415 while (1) { 1416 (void) signal(SIGALRM, toolong); 1417 (void) alarm(curclass.timeout); 1418 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 1419 reply(221, "You could at least say goodbye."); 1420 dologout(0); 1421 } 1422 (void) alarm(0); 1423 ftp_handle_line(cbuf); 1424 } 1425 /*NOTREACHED*/ 1426} 1427 1428static int 1429yylex(void) 1430{ 1431 static int cpos, state; 1432 char *cp, *cp2; 1433 struct tab *p; 1434 int n; 1435 char c; 1436 1437 switch (state) { 1438 1439 case CMD: 1440 hasyyerrored = 0; 1441 if ((cp = strchr(cmdp, '\r'))) { 1442 *cp = '\0'; 1443#if HAVE_SETPROCTITLE 1444 if (strncasecmp(cmdp, "PASS", 4) != 0 && 1445 strncasecmp(cmdp, "ACCT", 4) != 0) 1446 setproctitle("%s: %s", proctitle, cmdp); 1447#endif /* HAVE_SETPROCTITLE */ 1448 *cp++ = '\n'; 1449 *cp = '\0'; 1450 } 1451 if ((cp = strpbrk(cmdp, " \n"))) 1452 cpos = cp - cmdp; 1453 if (cpos == 0) 1454 cpos = 4; 1455 c = cmdp[cpos]; 1456 cmdp[cpos] = '\0'; 1457 p = lookup(cmdtab, cmdp); 1458 cmdp[cpos] = c; 1459 if (p != NULL) { 1460 if (is_oob && ! CMD_OOB(p)) { 1461 /* command will be handled in-band */ 1462 return (0); 1463 } else if (! CMD_IMPLEMENTED(p)) { 1464 reply(502, "%s command not implemented.", 1465 p->name); 1466 hasyyerrored = 1; 1467 break; 1468 } 1469 state = p->state; 1470 yylval.s = p->name; 1471 return (p->token); 1472 } 1473 break; 1474 1475 case SITECMD: 1476 if (cmdp[cpos] == ' ') { 1477 cpos++; 1478 return (SP); 1479 } 1480 cp = &cmdp[cpos]; 1481 if ((cp2 = strpbrk(cp, " \n"))) 1482 cpos = cp2 - cmdp; 1483 c = cmdp[cpos]; 1484 cmdp[cpos] = '\0'; 1485 p = lookup(sitetab, cp); 1486 cmdp[cpos] = c; 1487 if (p != NULL) { 1488 if (!CMD_IMPLEMENTED(p)) { 1489 reply(502, "SITE %s command not implemented.", 1490 p->name); 1491 hasyyerrored = 1; 1492 break; 1493 } 1494 state = p->state; 1495 yylval.s = p->name; 1496 return (p->token); 1497 } 1498 break; 1499 1500 case OSTR: 1501 if (cmdp[cpos] == '\n') { 1502 state = EOLN; 1503 return (CRLF); 1504 } 1505 /* FALLTHROUGH */ 1506 1507 case STR1: 1508 case ZSTR1: 1509 dostr1: 1510 if (cmdp[cpos] == ' ') { 1511 cpos++; 1512 state = state == OSTR ? STR2 : state+1; 1513 return (SP); 1514 } 1515 break; 1516 1517 case ZSTR2: 1518 if (cmdp[cpos] == '\n') { 1519 state = EOLN; 1520 return (CRLF); 1521 } 1522 /* FALLTHROUGH */ 1523 1524 case STR2: 1525 cp = &cmdp[cpos]; 1526 n = strlen(cp); 1527 cpos += n - 1; 1528 /* 1529 * Make sure the string is nonempty and \n terminated. 1530 */ 1531 if (n > 1 && cmdp[cpos] == '\n') { 1532 cmdp[cpos] = '\0'; 1533 yylval.s = xstrdup(cp); 1534 cmdp[cpos] = '\n'; 1535 state = ARGS; 1536 return (STRING); 1537 } 1538 break; 1539 1540 case NSTR: 1541 if (cmdp[cpos] == ' ') { 1542 cpos++; 1543 return (SP); 1544 } 1545 if (isdigit(cmdp[cpos])) { 1546 cp = &cmdp[cpos]; 1547 while (isdigit(cmdp[++cpos])) 1548 ; 1549 c = cmdp[cpos]; 1550 cmdp[cpos] = '\0'; 1551 yylval.i = atoi(cp); 1552 cmdp[cpos] = c; 1553 state = STR1; 1554 return (NUMBER); 1555 } 1556 state = STR1; 1557 goto dostr1; 1558 1559 case ARGS: 1560 if (isdigit(cmdp[cpos])) { 1561 cp = &cmdp[cpos]; 1562 while (isdigit(cmdp[++cpos])) 1563 ; 1564 c = cmdp[cpos]; 1565 cmdp[cpos] = '\0'; 1566 yylval.i = atoi(cp); 1567 cmdp[cpos] = c; 1568 return (NUMBER); 1569 } 1570 if (strncasecmp(&cmdp[cpos], "ALL", 3) == 0 1571 && !isalnum(cmdp[cpos + 3])) { 1572 yylval.s = xstrdup("ALL"); 1573 cpos += 3; 1574 return ALL; 1575 } 1576 switch (cmdp[cpos++]) { 1577 1578 case '\n': 1579 state = EOLN; 1580 return (CRLF); 1581 1582 case ' ': 1583 return (SP); 1584 1585 case ',': 1586 return (COMMA); 1587 1588 case 'A': 1589 case 'a': 1590 return (A); 1591 1592 case 'B': 1593 case 'b': 1594 return (B); 1595 1596 case 'C': 1597 case 'c': 1598 return (C); 1599 1600 case 'E': 1601 case 'e': 1602 return (E); 1603 1604 case 'F': 1605 case 'f': 1606 return (F); 1607 1608 case 'I': 1609 case 'i': 1610 return (I); 1611 1612 case 'L': 1613 case 'l': 1614 return (L); 1615 1616 case 'N': 1617 case 'n': 1618 return (N); 1619 1620 case 'P': 1621 case 'p': 1622 return (P); 1623 1624 case 'R': 1625 case 'r': 1626 return (R); 1627 1628 case 'S': 1629 case 's': 1630 return (S); 1631 1632 case 'T': 1633 case 't': 1634 return (T); 1635 1636 } 1637 break; 1638 1639 case NOARGS: 1640 if (cmdp[cpos] == '\n') { 1641 state = EOLN; 1642 return (CRLF); 1643 } 1644 c = cmdp[cpos]; 1645 cmdp[cpos] = '\0'; 1646 reply(501, "'%s' command does not take any arguments.", cmdp); 1647 hasyyerrored = 1; 1648 cmdp[cpos] = c; 1649 break; 1650 1651 case EOLN: 1652 state = CMD; 1653 return (0); 1654 1655 default: 1656 fatal("Unknown state in scanner."); 1657 } 1658 yyerror(NULL); 1659 state = CMD; 1660 is_oob = 0; 1661 longjmp(errcatch, 0); 1662 /* NOTREACHED */ 1663} 1664 1665/* ARGSUSED */ 1666void 1667yyerror(char *s) 1668{ 1669 char *cp; 1670 1671 if (hasyyerrored || is_oob) 1672 return; 1673 if ((cp = strchr(cmdp,'\n')) != NULL) 1674 *cp = '\0'; 1675 reply(500, "'%s': command not understood.", cmdp); 1676 hasyyerrored = 1; 1677} 1678 1679static void 1680help(struct tab *ctab, const char *s) 1681{ 1682 struct tab *c; 1683 int width, NCMDS; 1684 char *htype; 1685 1686 if (ctab == sitetab) 1687 htype = "SITE "; 1688 else 1689 htype = ""; 1690 width = 0, NCMDS = 0; 1691 for (c = ctab; c->name != NULL; c++) { 1692 int len = strlen(c->name); 1693 1694 if (len > width) 1695 width = len; 1696 NCMDS++; 1697 } 1698 width = (width + 8) &~ 7; 1699 if (s == 0) { 1700 int i, j, w; 1701 int columns, lines; 1702 1703 reply(-214, "%s", ""); 1704 reply(0, "The following %scommands are recognized.", htype); 1705 reply(0, "(`-' = not implemented, `+' = supports options)"); 1706 columns = 76 / width; 1707 if (columns == 0) 1708 columns = 1; 1709 lines = (NCMDS + columns - 1) / columns; 1710 for (i = 0; i < lines; i++) { 1711 cprintf(stdout, " "); 1712 for (j = 0; j < columns; j++) { 1713 c = ctab + j * lines + i; 1714 cprintf(stdout, "%s", c->name); 1715 w = strlen(c->name); 1716 if (! CMD_IMPLEMENTED(c)) { 1717 CPUTC('-', stdout); 1718 w++; 1719 } 1720 if (CMD_HAS_OPTIONS(c)) { 1721 CPUTC('+', stdout); 1722 w++; 1723 } 1724 if (c + lines >= &ctab[NCMDS]) 1725 break; 1726 while (w < width) { 1727 CPUTC(' ', stdout); 1728 w++; 1729 } 1730 } 1731 cprintf(stdout, "\r\n"); 1732 } 1733 (void) fflush(stdout); 1734 reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1735 return; 1736 } 1737 c = lookup(ctab, s); 1738 if (c == (struct tab *)0) { 1739 reply(502, "Unknown command %s.", s); 1740 return; 1741 } 1742 if (CMD_IMPLEMENTED(c)) 1743 reply(214, "Syntax: %s%s %s", htype, c->name, c->help); 1744 else 1745 reply(214, "%s%-*s\t%s; not implemented.", htype, width, 1746 c->name, c->help); 1747} 1748 1749/* 1750 * Check that the structures used for a PORT, LPRT or EPRT command are 1751 * valid (data_dest, his_addr), and if necessary, detect ftp bounce attacks. 1752 * If family != -1 check that his_addr.su_family == family. 1753 */ 1754static void 1755port_check(const char *cmd, int family) 1756{ 1757 char h1[NI_MAXHOST], h2[NI_MAXHOST]; 1758 char s1[NI_MAXHOST], s2[NI_MAXHOST]; 1759#ifdef NI_WITHSCOPEID 1760 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID; 1761#else 1762 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; 1763#endif 1764 1765 if (epsvall) { 1766 reply(501, "%s disallowed after EPSV ALL", cmd); 1767 return; 1768 } 1769 1770 if (family != -1 && his_addr.su_family != family) { 1771 port_check_fail: 1772 reply(500, "Illegal %s command rejected", cmd); 1773 return; 1774 } 1775 1776 if (data_dest.su_family != his_addr.su_family) 1777 goto port_check_fail; 1778 1779 /* be paranoid, if told so */ 1780 if (CURCLASS_FLAGS_ISSET(checkportcmd)) { 1781#ifdef INET6 1782 /* 1783 * be paranoid, there are getnameinfo implementation that does 1784 * not present scopeid portion 1785 */ 1786 if (data_dest.su_family == AF_INET6 && 1787 data_dest.su_scope_id != his_addr.su_scope_id) 1788 goto port_check_fail; 1789#endif 1790 1791 if (getnameinfo((struct sockaddr *)&data_dest, data_dest.su_len, 1792 h1, sizeof(h1), s1, sizeof(s1), niflags)) 1793 goto port_check_fail; 1794 if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, 1795 h2, sizeof(h2), s2, sizeof(s2), niflags)) 1796 goto port_check_fail; 1797 1798 if (atoi(s1) < IPPORT_RESERVED || strcmp(h1, h2) != 0) 1799 goto port_check_fail; 1800 } 1801 1802 usedefault = 0; 1803 if (pdata >= 0) { 1804 (void) close(pdata); 1805 pdata = -1; 1806 } 1807 reply(200, "%s command successful.", cmd); 1808} 1809