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