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