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