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