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