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