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