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