ftpcmd.y revision 1.28
1/* $OpenBSD: ftpcmd.y,v 1.28 2001/12/28 17:38:17 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.28 2001/12/28 17:38:17 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 if ($4 != NULL) 241 free($4); 242 } 243 244 | PASV check_login_epsvall CRLF 245 { 246 if ($2) 247 passive(); 248 } 249 | LPSV check_login_epsvall CRLF 250 { 251 if ($2) 252 long_passive("LPSV", PF_UNSPEC); 253 } 254 | EPSV check_login SP NUMBER CRLF 255 { 256 if ($2) 257 long_passive("EPSV", epsvproto2af($4)); 258 } 259 | EPSV check_login SP ALL CRLF 260 { 261 if ($2) { 262 reply(200, "EPSV ALL command successful."); 263 epsvall++; 264 } 265 } 266 | EPSV check_login CRLF 267 { 268 if ($2) 269 long_passive("EPSV", PF_UNSPEC); 270 } 271 | TYPE check_login SP type_code CRLF 272 { 273 if ($2) { 274 switch (cmd_type) { 275 276 case TYPE_A: 277 if (cmd_form == FORM_N) { 278 reply(200, "Type set to A."); 279 type = cmd_type; 280 form = cmd_form; 281 } else 282 reply(504, "Form must be N."); 283 break; 284 285 case TYPE_E: 286 reply(504, "Type E not implemented."); 287 break; 288 289 case TYPE_I: 290 reply(200, "Type set to I."); 291 type = cmd_type; 292 break; 293 294 case TYPE_L: 295 if (cmd_bytesz == 8) { 296 reply(200, 297 "Type set to L (byte size 8)."); 298 type = cmd_type; 299 } else 300 reply(504, "Byte size must be 8."); 301 302 } 303 } 304 } 305 | STRU check_login SP struct_code CRLF 306 { 307 if ($2) { 308 switch ($4) { 309 310 case STRU_F: 311 reply(200, "STRU F ok."); 312 break; 313 314 default: 315 reply(504, "Unimplemented STRU type."); 316 } 317 } 318 } 319 | MODE check_login SP mode_code CRLF 320 { 321 if ($2) { 322 switch ($4) { 323 324 case MODE_S: 325 reply(200, "MODE S ok."); 326 break; 327 328 default: 329 reply(502, "Unimplemented MODE type."); 330 } 331 } 332 } 333 | ALLO check_login SP NUMBER CRLF 334 { 335 if ($2) { 336 reply(202, "ALLO command ignored."); 337 } 338 } 339 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 340 { 341 if ($2) { 342 reply(202, "ALLO command ignored."); 343 } 344 } 345 | RETR check_login SP pathname CRLF 346 { 347 if ($2 && $4 != NULL) 348 retrieve(NULL, $4); 349 if ($4 != NULL) 350 free($4); 351 } 352 | STOR check_login SP pathname CRLF 353 { 354 if ($2 && $4 != NULL) 355 store($4, "w", 0); 356 if ($4 != NULL) 357 free($4); 358 } 359 | APPE check_login SP pathname CRLF 360 { 361 if ($2 && $4 != NULL) 362 store($4, "a", 0); 363 if ($4 != NULL) 364 free($4); 365 } 366 | NLST check_login CRLF 367 { 368 if ($2) 369 send_file_list("."); 370 } 371 | NLST check_login SP STRING CRLF 372 { 373 if ($2 && $4 != NULL) 374 send_file_list($4); 375 if ($4 != NULL) 376 free($4); 377 } 378 | LIST check_login CRLF 379 { 380 if ($2) 381 retrieve("/bin/ls -lgA", ""); 382 } 383 | LIST check_login SP pathname CRLF 384 { 385 if ($2 && $4 != NULL) 386 retrieve("/bin/ls -lgA %s", $4); 387 if ($4 != NULL) 388 free($4); 389 } 390 | STAT check_login SP pathname CRLF 391 { 392 if ($2 && $4 != NULL) 393 statfilecmd($4); 394 if ($4 != NULL) 395 free($4); 396 } 397 | STAT check_login CRLF 398 { 399 if ($2) 400 statcmd(); 401 } 402 | DELE check_login SP pathname CRLF 403 { 404 if ($2 && $4 != NULL) 405 delete($4); 406 if ($4 != NULL) 407 free($4); 408 } 409 | RNTO check_login SP pathname CRLF 410 { 411 if ($2) { 412 if (fromname) { 413 renamecmd(fromname, $4); 414 free(fromname); 415 fromname = NULL; 416 } else { 417 reply(503, 418 "Bad sequence of commands."); 419 } 420 } 421 free($4); 422 } 423 | ABOR check_login CRLF 424 { 425 if ($2) 426 reply(225, "ABOR command successful."); 427 } 428 | CWD check_login CRLF 429 { 430 if ($2) 431 cwd(pw->pw_dir); 432 } 433 | CWD check_login SP pathname CRLF 434 { 435 if ($2 && $4 != NULL) 436 cwd($4); 437 if ($4 != NULL) 438 free($4); 439 } 440 | HELP CRLF 441 { 442 help(cmdtab, NULL); 443 } 444 | HELP SP STRING CRLF 445 { 446 char *cp = $3; 447 448 if (strncasecmp(cp, "SITE", 4) == 0) { 449 cp = $3 + 4; 450 if (*cp == ' ') 451 cp++; 452 if (*cp) 453 help(sitetab, cp); 454 else 455 help(sitetab, NULL); 456 } else 457 help(cmdtab, $3); 458 459 if ($3 != NULL) 460 free ($3); 461 } 462 | NOOP CRLF 463 { 464 reply(200, "NOOP command successful."); 465 } 466 | MKD check_login SP pathname CRLF 467 { 468 if ($2 && $4 != NULL) 469 makedir($4); 470 if ($4 != NULL) 471 free($4); 472 } 473 | RMD check_login SP pathname CRLF 474 { 475 if ($2 && $4 != NULL) 476 removedir($4); 477 if ($4 != NULL) 478 free($4); 479 } 480 | PWD check_login CRLF 481 { 482 if ($2) 483 pwd(); 484 } 485 | CDUP check_login CRLF 486 { 487 if ($2) 488 cwd(".."); 489 } 490 | SITE SP HELP CRLF 491 { 492 help(sitetab, NULL); 493 } 494 | SITE SP HELP SP STRING CRLF 495 { 496 help(sitetab, $5); 497 498 if ($5 != NULL) 499 free ($5); 500 } 501 | SITE SP UMASK check_login CRLF 502 { 503 int oldmask; 504 505 if ($4) { 506 oldmask = umask(0); 507 (void) umask(oldmask); 508 reply(200, "Current UMASK is %03o", oldmask); 509 } 510 } 511 | SITE SP UMASK check_login SP octal_number CRLF 512 { 513 int oldmask; 514 515 if ($4) { 516 if (($6 == -1) || ($6 > 0777)) { 517 reply(501, "Bad UMASK value"); 518 } else if (!umaskchange) { 519 reply(550, 520 "No permission to change umask."); 521 } else { 522 oldmask = umask($6); 523 reply(200, 524 "UMASK set to %03o (was %03o)", 525 $6, oldmask); 526 } 527 } 528 } 529 | SITE SP CHMOD check_login SP octal_number SP pathname CRLF 530 { 531 if ($4 && ($8 != NULL)) { 532 if ($6 > 0777) 533 reply(501, 534 "CHMOD: Mode value must be between " 535 "0 and 0777"); 536 else if (!umaskchange) 537 reply(550, 538 "No permission to change mode of %s.", 539 $8); 540 else if (chmod($8, $6) < 0) 541 perror_reply(550, $8); 542 else 543 reply(200, 544 "CHMOD command successful."); 545 } 546 if ($8 != NULL) 547 free($8); 548 } 549 | SITE SP check_login IDLE CRLF 550 { 551 if ($3) 552 reply(200, 553 "Current IDLE time limit is %d 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 char *renamefrom(); 657 658 restart_point = (off_t) 0; 659 if ($2 && $4) { 660 fromname = renamefrom($4); 661 if (fromname == NULL && $4) { 662 free($4); 663 } 664 } else { 665 if ($4) 666 free ($4); 667 } 668 } 669 670 | REST check_login SP byte_size CRLF 671 { 672 if ($2) { 673 fromname = NULL; 674 restart_point = $4; /* XXX $4 is only "int" */ 675 reply(350, "Restarting at %qd. %s", restart_point, 676 "Send STORE or RETRIEVE to initiate transfer."); 677 } 678 } 679 ; 680 681username 682 : STRING 683 ; 684 685password 686 : /* empty */ 687 { 688 $$ = (char *)calloc(1, sizeof(char)); 689 } 690 | STRING 691 ; 692 693byte_size 694 : NUMBER 695 ; 696 697host_port 698 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 699 NUMBER COMMA NUMBER 700 { 701 char *a, *p; 702 703 if ($1 < 0 || $1 > 255 || $3 < 0 || $3 > 255 || 704 $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 || 705 $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255) { 706 $$ = 1; 707 } else { 708 data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); 709 data_dest.su_sin.sin_family = AF_INET; 710 p = (char *)&data_dest.su_sin.sin_port; 711 p[0] = $9; p[1] = $11; 712 a = (char *)&data_dest.su_sin.sin_addr; 713 a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 714 $$ = 0; 715 } 716 } 717 ; 718 719host_long_port4 720 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 721 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 722 NUMBER 723 { 724 char *a, *p; 725 726 /* reject invalid LPRT command */ 727 if ($1 != 4 || $3 != 4 728 || $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 729 || $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255 730 || $13 != 2 731 || $15 < 0 || $15 > 255 || $17 < 0 || $17 > 255) { 732 $$ = 1; 733 } else { 734 data_dest.su_sin.sin_len = 735 sizeof(struct sockaddr_in); 736 data_dest.su_family = AF_INET; 737 p = (char *)&data_dest.su_port; 738 p[0] = $15; p[1] = $17; 739 a = (char *)&data_dest.su_sin.sin_addr; 740 a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; 741 $$ = 0; 742 } 743 } 744 ; 745 746host_long_port6 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 COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 751 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 752 NUMBER 753 { 754 char *a, *p; 755 756 /* reject invalid LPRT command */ 757 if ($1 != 6 || $3 != 16 758 || $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 759 || $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255 760 || $13 < 0 || $13 > 255 || $15 < 0 || $15 > 255 761 || $17 < 0 || $17 > 255 || $19 < 0 || $19 > 255 762 || $21 < 0 || $21 > 255 || $23 < 0 || $23 > 255 763 || $25 < 0 || $25 > 255 || $27 < 0 || $27 > 255 764 || $29 < 0 || $29 > 255 || $31 < 0 || $31 > 255 765 || $33 < 0 || $33 > 255 || $35 < 0 || $35 > 255 766 || $37 != 2 767 || $39 < 0 || $39 > 255 || $41 < 0 || $41 > 255) { 768 $$ = 1; 769 } else { 770 data_dest.su_sin6.sin6_len = 771 sizeof(struct sockaddr_in6); 772 data_dest.su_family = AF_INET6; 773 p = (char *)&data_dest.su_port; 774 p[0] = $39; p[1] = $41; 775 a = (char *)&data_dest.su_sin6.sin6_addr; 776 a[0] = $5; a[1] = $7; 777 a[2] = $9; a[3] = $11; 778 a[4] = $13; a[5] = $15; 779 a[6] = $17; a[7] = $19; 780 a[8] = $21; a[9] = $23; 781 a[10] = $25; a[11] = $27; 782 a[12] = $29; a[13] = $31; 783 a[14] = $33; a[15] = $35; 784 if (his_addr.su_family == AF_INET6) { 785 /* XXX more sanity checks! */ 786 data_dest.su_sin6.sin6_scope_id = 787 his_addr.su_sin6.sin6_scope_id; 788 } 789 790 $$ = 0; 791 } 792 } 793 ; 794 795form_code 796 : N 797 { 798 $$ = FORM_N; 799 } 800 | T 801 { 802 $$ = FORM_T; 803 } 804 | C 805 { 806 $$ = FORM_C; 807 } 808 ; 809 810type_code 811 : A 812 { 813 cmd_type = TYPE_A; 814 cmd_form = FORM_N; 815 } 816 | A SP form_code 817 { 818 cmd_type = TYPE_A; 819 cmd_form = $3; 820 } 821 | E 822 { 823 cmd_type = TYPE_E; 824 cmd_form = FORM_N; 825 } 826 | E SP form_code 827 { 828 cmd_type = TYPE_E; 829 cmd_form = $3; 830 } 831 | I 832 { 833 cmd_type = TYPE_I; 834 } 835 | L 836 { 837 cmd_type = TYPE_L; 838 cmd_bytesz = NBBY; 839 } 840 | L SP byte_size 841 { 842 cmd_type = TYPE_L; 843 cmd_bytesz = $3; 844 } 845 /* this is for a bug in the BBN ftp */ 846 | L byte_size 847 { 848 cmd_type = TYPE_L; 849 cmd_bytesz = $2; 850 } 851 ; 852 853struct_code 854 : F 855 { 856 $$ = STRU_F; 857 } 858 | R 859 { 860 $$ = STRU_R; 861 } 862 | P 863 { 864 $$ = STRU_P; 865 } 866 ; 867 868mode_code 869 : S 870 { 871 $$ = MODE_S; 872 } 873 | B 874 { 875 $$ = MODE_B; 876 } 877 | C 878 { 879 $$ = MODE_C; 880 } 881 ; 882 883pathname 884 : pathstring 885 { 886 /* 887 * Problem: this production is used for all pathname 888 * processing, but only gives a 550 error reply. 889 * This is a valid reply in some cases but not in others. 890 */ 891 if (logged_in && $1 && strchr($1, '~') != NULL) { 892 glob_t gl; 893 int flags = 894 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; 895 char *pptr = $1; 896 897 /* 898 * glob() will only find a leading ~, but 899 * Netscape kindly puts a slash in front of 900 * it for publish URLs. There needs to be 901 * a flag for glob() that expands tildes 902 * anywhere in the string. 903 */ 904 if ((pptr[0] == '/') && (pptr[1] == '~')) 905 pptr++; 906 907 memset(&gl, 0, sizeof(gl)); 908 if (glob(pptr, flags, NULL, &gl) || 909 gl.gl_pathc == 0) { 910 reply(550, "not found"); 911 $$ = NULL; 912 } else { 913 $$ = strdup(gl.gl_pathv[0]); 914 } 915 globfree(&gl); 916 free($1); 917 } else 918 $$ = $1; 919 } 920 ; 921 922pathstring 923 : STRING 924 ; 925 926octal_number 927 : NUMBER 928 { 929 int ret, dec, multby, digit; 930 931 /* 932 * Convert a number that was read as decimal number 933 * to what it would be if it had been read as octal. 934 */ 935 dec = $1; 936 multby = 1; 937 ret = 0; 938 while (dec) { 939 digit = dec%10; 940 if (digit > 7) { 941 ret = -1; 942 break; 943 } 944 ret += digit * multby; 945 multby *= 8; 946 dec /= 10; 947 } 948 $$ = ret; 949 } 950 ; 951 952 953check_login 954 : /* empty */ 955 { 956 if (logged_in) 957 $$ = 1; 958 else { 959 reply(530, "Please login with USER and PASS."); 960 $$ = 0; 961 } 962 } 963 ; 964 965check_login_epsvall 966 : /* empty */ 967 { 968 if (!logged_in) { 969 reply(530, "Please login with USER and PASS."); 970 $$ = 0; 971 } else if (epsvall) { 972 reply(501, "the command is disallowed " 973 "after EPSV ALL"); 974 usedefault = 1; 975 $$ = 0; 976 } else 977 $$ = 1; 978 } 979 ; 980 981%% 982 983#define CMD 0 /* beginning of command */ 984#define ARGS 1 /* expect miscellaneous arguments */ 985#define STR1 2 /* expect SP followed by STRING */ 986#define STR2 3 /* expect STRING */ 987#define OSTR 4 /* optional SP then STRING */ 988#define ZSTR1 5 /* SP then optional STRING */ 989#define ZSTR2 6 /* optional STRING after SP */ 990#define SITECMD 7 /* SITE command */ 991#define NSTR 8 /* Number followed by a string */ 992 993struct tab { 994 char *name; 995 short token; 996 short state; 997 short implemented; /* 1 if command is implemented */ 998 char *help; 999}; 1000 1001struct tab cmdtab[] = { /* In order defined in RFC 765 */ 1002 { "USER", USER, STR1, 1, "<sp> username" }, 1003 { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 1004 { "ACCT", ACCT, STR1, 0, "(specify account)" }, 1005 { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 1006 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 1007 { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 1008 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 1009 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 1010 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 1011 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 1012 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 1013 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 1014 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 1015 { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 1016 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 1017 { "RETR", RETR, STR1, 1, "<sp> file-name" }, 1018 { "STOR", STOR, STR1, 1, "<sp> file-name" }, 1019 { "APPE", APPE, STR1, 1, "<sp> file-name" }, 1020 { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 1021 { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 1022 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 1023 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 1024 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 1025 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 1026 { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 1027 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 1028 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 1029 { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 1030 { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 1031 { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 1032 { "DELE", DELE, STR1, 1, "<sp> file-name" }, 1033 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 1034 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 1035 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 1036 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 1037 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 1038 { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 1039 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 1040 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 1041 { "NOOP", NOOP, ARGS, 1, "" }, 1042 { "MKD", MKD, STR1, 1, "<sp> path-name" }, 1043 { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 1044 { "RMD", RMD, STR1, 1, "<sp> path-name" }, 1045 { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 1046 { "PWD", PWD, ARGS, 1, "(return current directory)" }, 1047 { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 1048 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 1049 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 1050 { "STOU", STOU, STR1, 1, "<sp> file-name" }, 1051 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 1052 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 1053 { NULL, 0, 0, 0, 0 } 1054}; 1055 1056struct tab sitetab[] = { 1057 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 1058 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 1059 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 1060 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 1061 { NULL, 0, 0, 0, 0 } 1062}; 1063 1064static void help __P((struct tab *, char *)); 1065static struct tab * 1066 lookup __P((struct tab *, char *)); 1067static void sizecmd __P((char *)); 1068static int yylex __P((void)); 1069 1070extern int epsvall; 1071 1072static struct tab * 1073lookup(p, cmd) 1074 struct tab *p; 1075 char *cmd; 1076{ 1077 1078 for (; p->name != NULL; p++) 1079 if (strcmp(cmd, p->name) == 0) 1080 return (p); 1081 return (0); 1082} 1083 1084#include <arpa/telnet.h> 1085 1086/* 1087 * getline - a hacked up version of fgets to ignore TELNET escape codes. 1088 */ 1089char * 1090getline(s, n, iop) 1091 char *s; 1092 int n; 1093 FILE *iop; 1094{ 1095 int c; 1096 char *cs; 1097 1098 cs = s; 1099/* tmpline may contain saved command from urgent mode interruption */ 1100 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 1101 *cs++ = tmpline[c]; 1102 if (tmpline[c] == '\n') { 1103 *cs++ = '\0'; 1104 if (debug) 1105 syslog(LOG_DEBUG, "command: %s", s); 1106 tmpline[0] = '\0'; 1107 return(s); 1108 } 1109 if (c == 0) 1110 tmpline[0] = '\0'; 1111 } 1112 while ((c = getc(iop)) != EOF) { 1113 c &= 0377; 1114 if (c == IAC) { 1115 if ((c = getc(iop)) != EOF) { 1116 c &= 0377; 1117 switch (c) { 1118 case WILL: 1119 case WONT: 1120 c = getc(iop); 1121 printf("%c%c%c", IAC, DONT, 0377&c); 1122 (void) fflush(stdout); 1123 continue; 1124 case DO: 1125 case DONT: 1126 c = getc(iop); 1127 printf("%c%c%c", IAC, WONT, 0377&c); 1128 (void) fflush(stdout); 1129 continue; 1130 case IAC: 1131 break; 1132 default: 1133 continue; /* ignore command */ 1134 } 1135 } 1136 } 1137 *cs++ = c; 1138 if (--n <= 0 || c == '\n') 1139 break; 1140 } 1141 if (c == EOF && cs == s) 1142 return (NULL); 1143 *cs++ = '\0'; 1144 if (debug) { 1145 if (!guest && strncasecmp("pass ", s, 5) == 0) { 1146 /* Don't syslog passwords */ 1147 syslog(LOG_DEBUG, "command: %.5s ???", s); 1148 } else { 1149 char *cp; 1150 int len; 1151 1152 /* Don't syslog trailing CR-LF */ 1153 len = strlen(s); 1154 cp = s + len - 1; 1155 while (cp >= s && (*cp == '\n' || *cp == '\r')) { 1156 --cp; 1157 --len; 1158 } 1159 syslog(LOG_DEBUG, "command: %.*s", len, s); 1160 } 1161 } 1162 return (s); 1163} 1164 1165void 1166toolong(signo) 1167 int signo; 1168{ 1169 struct syslog_data sdata = SYSLOG_DATA_INIT; 1170 1171 /* XXX signal races */ 1172 reply(421, 1173 "Timeout (%d seconds): closing control connection.", timeout); 1174 if (logging) 1175 syslog_r(LOG_INFO, &sdata, "User %s timed out after %d seconds", 1176 (pw ? pw -> pw_name : "unknown"), timeout); 1177 dologout(1); 1178} 1179 1180static int 1181yylex() 1182{ 1183 static int cpos; 1184 char *cp, *cp2; 1185 struct tab *p; 1186 int n; 1187 char c; 1188 1189 for (;;) { 1190 switch (state) { 1191 1192 case CMD: 1193 (void) signal(SIGALRM, toolong); 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 != 0) { 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 != 0) { 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 yylval.s = strdup("ALL"); 1335 cpos += 3; 1336 return ALL; 1337 } 1338 switch (cbuf[cpos++]) { 1339 1340 case '\n': 1341 state = CMD; 1342 return (CRLF); 1343 1344 case ' ': 1345 return (SP); 1346 1347 case ',': 1348 return (COMMA); 1349 1350 case 'A': 1351 case 'a': 1352 return (A); 1353 1354 case 'B': 1355 case 'b': 1356 return (B); 1357 1358 case 'C': 1359 case 'c': 1360 return (C); 1361 1362 case 'E': 1363 case 'e': 1364 return (E); 1365 1366 case 'F': 1367 case 'f': 1368 return (F); 1369 1370 case 'I': 1371 case 'i': 1372 return (I); 1373 1374 case 'L': 1375 case 'l': 1376 return (L); 1377 1378 case 'N': 1379 case 'n': 1380 return (N); 1381 1382 case 'P': 1383 case 'p': 1384 return (P); 1385 1386 case 'R': 1387 case 'r': 1388 return (R); 1389 1390 case 'S': 1391 case 's': 1392 return (S); 1393 1394 case 'T': 1395 case 't': 1396 return (T); 1397 1398 } 1399 break; 1400 1401 default: 1402 fatal("Unknown state in scanner."); 1403 } 1404 state = CMD; 1405 return (LEXERR); 1406 } 1407} 1408 1409void 1410upper(s) 1411 char *s; 1412{ 1413 while (*s != '\0') { 1414 if (islower(*s)) 1415 *s = toupper(*s); 1416 s++; 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 == 0) { 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 == (struct tab *)0) { 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 1514 count = 0; 1515 while((c=getc(fin)) != EOF) { 1516 if (c == '\n') /* will get expanded to \r\n */ 1517 count++; 1518 count++; 1519 } 1520 (void) fclose(fin); 1521 1522 reply(213, "%qd", count); 1523 break; } 1524 default: 1525 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 1526 } 1527} 1528