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