ftpcmd.y revision 1.47
1/* $OpenBSD: ftpcmd.y,v 1.47 2004/12/06 23:04:14 deraadt 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.47 2004/12/06 23:04:14 deraadt 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 "extern.h" 73#include "monitor.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(PREAUTH_SLAVE_DIED); 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 int 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 int 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 1167void 1168toolong(signo) 1169 int signo; 1170{ 1171 struct syslog_data sdata = SYSLOG_DATA_INIT; 1172 1173 reply_r(421, 1174 "Timeout (%d seconds): closing control connection.", timeout); 1175 if (logging) 1176 syslog_r(LOG_INFO, &sdata, "User %s timed out after %d seconds", 1177 (pw ? pw -> pw_name : "unknown"), timeout); 1178 dologout(1); 1179} 1180 1181static int 1182yylex() 1183{ 1184 static int cpos; 1185 char *cp, *cp2; 1186 struct tab *p; 1187 int n; 1188 char c; 1189 1190 for (;;) { 1191 switch (state) { 1192 1193 case CMD: 1194 (void) alarm((unsigned) timeout); 1195 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 1196 reply(221, "You could at least say goodbye."); 1197 dologout(0); 1198 } 1199 (void) alarm(0); 1200 if ((cp = strchr(cbuf, '\r'))) { 1201 *cp++ = '\n'; 1202 *cp = '\0'; 1203 } 1204#ifdef HASSETPROCTITLE 1205 if (strncasecmp(cbuf, "PASS", 4) != 0) { 1206 if ((cp = strpbrk(cbuf, "\n"))) { 1207 c = *cp; 1208 *cp = '\0'; 1209 setproctitle("%s: %s", proctitle, cbuf); 1210 *cp = c; 1211 } 1212 } 1213#endif /* HASSETPROCTITLE */ 1214 if ((cp = strpbrk(cbuf, " \n"))) 1215 cpos = cp - cbuf; 1216 if (cpos == 0) 1217 cpos = 4; 1218 c = cbuf[cpos]; 1219 cbuf[cpos] = '\0'; 1220 upper(cbuf); 1221 p = lookup(cmdtab, cbuf); 1222 cbuf[cpos] = c; 1223 if (p != NULL) { 1224 if (p->implemented == 0) { 1225 nack(p->name); 1226 return (LEXERR); 1227 } 1228 state = p->state; 1229 yylval.s = p->name; 1230 return (p->token); 1231 } 1232 break; 1233 1234 case SITECMD: 1235 if (cbuf[cpos] == ' ') { 1236 cpos++; 1237 return (SP); 1238 } 1239 cp = &cbuf[cpos]; 1240 if ((cp2 = strpbrk(cp, " \n"))) 1241 cpos = cp2 - cbuf; 1242 c = cbuf[cpos]; 1243 cbuf[cpos] = '\0'; 1244 upper(cp); 1245 p = lookup(sitetab, cp); 1246 cbuf[cpos] = c; 1247 if (p != NULL) { 1248 if (p->implemented == 0) { 1249 state = CMD; 1250 nack(p->name); 1251 return (LEXERR); 1252 } 1253 state = p->state; 1254 yylval.s = p->name; 1255 return (p->token); 1256 } 1257 state = CMD; 1258 break; 1259 1260 case OSTR: 1261 if (cbuf[cpos] == '\n') { 1262 state = CMD; 1263 return (CRLF); 1264 } 1265 /* FALLTHROUGH */ 1266 1267 case STR1: 1268 case ZSTR1: 1269 dostr1: 1270 if (cbuf[cpos] == ' ') { 1271 cpos++; 1272 state = state == OSTR ? STR2 : state+1; 1273 return (SP); 1274 } 1275 break; 1276 1277 case ZSTR2: 1278 if (cbuf[cpos] == '\n') { 1279 state = CMD; 1280 return (CRLF); 1281 } 1282 /* FALLTHROUGH */ 1283 1284 case STR2: 1285 cp = &cbuf[cpos]; 1286 n = strlen(cp); 1287 cpos += n - 1; 1288 /* 1289 * Make sure the string is nonempty and \n terminated. 1290 */ 1291 if (n > 1 && cbuf[cpos] == '\n') { 1292 cbuf[cpos] = '\0'; 1293 yylval.s = strdup(cp); 1294 if (yylval.s == NULL) 1295 fatal("Ran out of memory."); 1296 cbuf[cpos] = '\n'; 1297 state = ARGS; 1298 return (STRING); 1299 } 1300 break; 1301 1302 case NSTR: 1303 if (cbuf[cpos] == ' ') { 1304 cpos++; 1305 return (SP); 1306 } 1307 if (isdigit(cbuf[cpos])) { 1308 cp = &cbuf[cpos]; 1309 while (isdigit(cbuf[++cpos])) 1310 ; 1311 c = cbuf[cpos]; 1312 cbuf[cpos] = '\0'; 1313 yylval.i = atoi(cp); 1314 cbuf[cpos] = c; 1315 state = STR1; 1316 return (NUMBER); 1317 } 1318 state = STR1; 1319 goto dostr1; 1320 1321 case ARGS: 1322 if (isdigit(cbuf[cpos])) { 1323 cp = &cbuf[cpos]; 1324 while (isdigit(cbuf[++cpos])) 1325 ; 1326 c = cbuf[cpos]; 1327 cbuf[cpos] = '\0'; 1328 yylval.i = atoi(cp); 1329 cbuf[cpos] = c; 1330 return (NUMBER); 1331 } 1332 if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 1333 && !isalnum(cbuf[cpos + 3])) { 1334 cpos += 3; 1335 return ALL; 1336 } 1337 switch (cbuf[cpos++]) { 1338 1339 case '\n': 1340 state = CMD; 1341 return (CRLF); 1342 1343 case ' ': 1344 return (SP); 1345 1346 case ',': 1347 return (COMMA); 1348 1349 case 'A': 1350 case 'a': 1351 return (A); 1352 1353 case 'B': 1354 case 'b': 1355 return (B); 1356 1357 case 'C': 1358 case 'c': 1359 return (C); 1360 1361 case 'E': 1362 case 'e': 1363 return (E); 1364 1365 case 'F': 1366 case 'f': 1367 return (F); 1368 1369 case 'I': 1370 case 'i': 1371 return (I); 1372 1373 case 'L': 1374 case 'l': 1375 return (L); 1376 1377 case 'N': 1378 case 'n': 1379 return (N); 1380 1381 case 'P': 1382 case 'p': 1383 return (P); 1384 1385 case 'R': 1386 case 'r': 1387 return (R); 1388 1389 case 'S': 1390 case 's': 1391 return (S); 1392 1393 case 'T': 1394 case 't': 1395 return (T); 1396 1397 } 1398 break; 1399 1400 default: 1401 fatal("Unknown state in scanner."); 1402 } 1403 state = CMD; 1404 return (LEXERR); 1405 } 1406} 1407 1408void 1409upper(s) 1410 char *s; 1411{ 1412 char *p; 1413 1414 for (p = s; *p; p++) { 1415 if (islower(*p)) 1416 *p = toupper(*p); 1417 } 1418} 1419 1420static void 1421help(ctab, s) 1422 struct tab *ctab; 1423 char *s; 1424{ 1425 struct tab *c; 1426 int width, NCMDS; 1427 char *type; 1428 1429 if (ctab == sitetab) 1430 type = "SITE "; 1431 else 1432 type = ""; 1433 width = 0, NCMDS = 0; 1434 for (c = ctab; c->name != NULL; c++) { 1435 int len = strlen(c->name); 1436 1437 if (len > width) 1438 width = len; 1439 NCMDS++; 1440 } 1441 width = (width + 8) &~ 7; 1442 if (s == NULL) { 1443 int i, j, w; 1444 int columns, lines; 1445 1446 lreply(214, "The following %scommands are recognized %s.", 1447 type, "(* =>'s unimplemented)"); 1448 columns = 76 / width; 1449 if (columns == 0) 1450 columns = 1; 1451 lines = (NCMDS + columns - 1) / columns; 1452 for (i = 0; i < lines; i++) { 1453 printf(" "); 1454 for (j = 0; j < columns; j++) { 1455 c = ctab + j * lines + i; 1456 printf("%s%c", c->name, 1457 c->implemented ? ' ' : '*'); 1458 if (c + lines >= &ctab[NCMDS]) 1459 break; 1460 w = strlen(c->name) + 1; 1461 while (w < width) { 1462 putchar(' '); 1463 w++; 1464 } 1465 } 1466 printf("\r\n"); 1467 } 1468 (void) fflush(stdout); 1469 reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1470 return; 1471 } 1472 upper(s); 1473 c = lookup(ctab, s); 1474 if (c == NULL) { 1475 reply(502, "Unknown command %s.", s); 1476 return; 1477 } 1478 if (c->implemented) 1479 reply(214, "Syntax: %s%s %s", type, c->name, c->help); 1480 else 1481 reply(214, "%s%-*s\t%s; unimplemented.", type, width, 1482 c->name, c->help); 1483} 1484 1485static void 1486sizecmd(filename) 1487 char *filename; 1488{ 1489 switch (type) { 1490 case TYPE_L: 1491 case TYPE_I: { 1492 struct stat stbuf; 1493 if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) 1494 reply(550, "%s: not a plain file.", filename); 1495 else 1496 reply(213, "%qu", stbuf.st_size); 1497 break; } 1498 case TYPE_A: { 1499 FILE *fin; 1500 int c; 1501 off_t count; 1502 struct stat stbuf; 1503 fin = fopen(filename, "r"); 1504 if (fin == NULL) { 1505 perror_reply(550, filename); 1506 return; 1507 } 1508 if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) { 1509 reply(550, "%s: not a plain file.", filename); 1510 (void) fclose(fin); 1511 return; 1512 } 1513 if (stbuf.st_size > 10240) { 1514 reply(550, "%s: file too large for SIZE.", filename); 1515 (void) fclose(fin); 1516 return; 1517 } 1518 1519 count = 0; 1520 while((c = getc(fin)) != EOF) { 1521 if (c == '\n') /* will get expanded to \r\n */ 1522 count++; 1523 count++; 1524 } 1525 (void) fclose(fin); 1526 1527 reply(213, "%qd", count); 1528 break; } 1529 default: 1530 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 1531 } 1532} 1533