ftpcmd.y revision 1.24
1/* $OpenBSD: ftpcmd.y,v 1.24 2000/11/14 20:27:01 itojun 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.24 2000/11/14 20:27:01 itojun 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 <setjmp.h> 66#include <signal.h> 67#include <tzfile.h> 68#include <stdio.h> 69#include <stdlib.h> 70#include <string.h> 71#include <syslog.h> 72#include <time.h> 73#include <unistd.h> 74#include <netdb.h> 75 76#include "extern.h" 77 78extern union sockunion data_dest; 79extern int logged_in; 80extern struct passwd *pw; 81extern int guest; 82extern int logging; 83extern int type; 84extern int form; 85extern int debug; 86extern int timeout; 87extern int maxtimeout; 88extern int pdata; 89extern char hostname[], remotehost[]; 90extern char proctitle[]; 91extern int usedefault; 92extern int transflag; 93extern char tmpline[]; 94extern int portcheck; 95extern union sockunion his_addr; 96extern int umaskchange; 97 98off_t restart_point; 99 100static int cmd_type; 101static int cmd_form; 102static int cmd_bytesz; 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 CRLF 645 { 646 yyerrok; 647 } 648 ; 649rcmd 650 : RNFR check_login SP pathname CRLF 651 { 652 char *renamefrom(); 653 654 restart_point = (off_t) 0; 655 if ($2 && $4) { 656 fromname = renamefrom($4); 657 if (fromname == NULL && $4) { 658 free($4); 659 } 660 } else { 661 if ($4) 662 free ($4); 663 } 664 } 665 666 | REST check_login SP byte_size CRLF 667 { 668 if ($2) { 669 fromname = NULL; 670 restart_point = $4; /* XXX $4 is only "int" */ 671 reply(350, "Restarting at %qd. %s", restart_point, 672 "Send STORE or RETRIEVE to initiate transfer."); 673 } 674 } 675 ; 676 677username 678 : STRING 679 ; 680 681password 682 : /* empty */ 683 { 684 $$ = (char *)calloc(1, sizeof(char)); 685 } 686 | STRING 687 ; 688 689byte_size 690 : NUMBER 691 ; 692 693host_port 694 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 695 NUMBER COMMA NUMBER 696 { 697 char *a, *p; 698 699 if ($1 < 0 || $1 > 255 || $3 < 0 || $3 > 255 || 700 $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 || 701 $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255) { 702 $$ = 1; 703 } else { 704 data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); 705 data_dest.su_sin.sin_family = AF_INET; 706 p = (char *)&data_dest.su_sin.sin_port; 707 p[0] = $9; p[1] = $11; 708 a = (char *)&data_dest.su_sin.sin_addr; 709 a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 710 $$ = 0; 711 } 712 } 713 ; 714 715host_long_port4 716 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 717 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 718 NUMBER 719 { 720 char *a, *p; 721 722 /* reject invalid LPRT command */ 723 if ($1 != 4 || $3 != 4 724 || $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 725 || $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255 726 || $13 != 2 727 || $15 < 0 || $15 > 255 || $17 < 0 || $17 > 255) { 728 $$ = 1; 729 } else { 730 data_dest.su_sin.sin_len = 731 sizeof(struct sockaddr_in); 732 data_dest.su_family = AF_INET; 733 p = (char *)&data_dest.su_port; 734 p[0] = $15; p[1] = $17; 735 a = (char *)&data_dest.su_sin.sin_addr; 736 a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; 737 $$ = 0; 738 } 739 } 740 ; 741 742host_long_port6 743 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 744 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 745 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 746 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 747 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 748 NUMBER 749 { 750 char *a, *p; 751 752 /* reject invalid LPRT command */ 753 if ($1 != 6 || $3 != 16 754 || $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 755 || $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255 756 || $13 < 0 || $13 > 255 || $15 < 0 || $15 > 255 757 || $17 < 0 || $17 > 255 || $19 < 0 || $19 > 255 758 || $21 < 0 || $21 > 255 || $23 < 0 || $23 > 255 759 || $25 < 0 || $25 > 255 || $27 < 0 || $27 > 255 760 || $29 < 0 || $29 > 255 || $31 < 0 || $31 > 255 761 || $33 < 0 || $33 > 255 || $35 < 0 || $35 > 255 762 || $37 != 2 763 || $39 < 0 || $39 > 255 || $41 < 0 || $41 > 255) { 764 $$ = 1; 765 } else { 766 data_dest.su_sin6.sin6_len = 767 sizeof(struct sockaddr_in6); 768 data_dest.su_family = AF_INET6; 769 p = (char *)&data_dest.su_port; 770 p[0] = $39; p[1] = $41; 771 a = (char *)&data_dest.su_sin6.sin6_addr; 772 a[0] = $5; a[1] = $7; 773 a[2] = $9; a[3] = $11; 774 a[4] = $13; a[5] = $15; 775 a[6] = $17; a[7] = $19; 776 a[8] = $21; a[9] = $23; 777 a[10] = $25; a[11] = $27; 778 a[12] = $29; a[13] = $31; 779 a[14] = $33; a[15] = $35; 780 if (his_addr.su_family == AF_INET6) { 781 /* XXX more sanity checks! */ 782 data_dest.su_sin6.sin6_scope_id = 783 his_addr.su_sin6.sin6_scope_id; 784 } 785 786 $$ = 0; 787 } 788 } 789 ; 790 791form_code 792 : N 793 { 794 $$ = FORM_N; 795 } 796 | T 797 { 798 $$ = FORM_T; 799 } 800 | C 801 { 802 $$ = FORM_C; 803 } 804 ; 805 806type_code 807 : A 808 { 809 cmd_type = TYPE_A; 810 cmd_form = FORM_N; 811 } 812 | A SP form_code 813 { 814 cmd_type = TYPE_A; 815 cmd_form = $3; 816 } 817 | E 818 { 819 cmd_type = TYPE_E; 820 cmd_form = FORM_N; 821 } 822 | E SP form_code 823 { 824 cmd_type = TYPE_E; 825 cmd_form = $3; 826 } 827 | I 828 { 829 cmd_type = TYPE_I; 830 } 831 | L 832 { 833 cmd_type = TYPE_L; 834 cmd_bytesz = NBBY; 835 } 836 | L SP byte_size 837 { 838 cmd_type = TYPE_L; 839 cmd_bytesz = $3; 840 } 841 /* this is for a bug in the BBN ftp */ 842 | L byte_size 843 { 844 cmd_type = TYPE_L; 845 cmd_bytesz = $2; 846 } 847 ; 848 849struct_code 850 : F 851 { 852 $$ = STRU_F; 853 } 854 | R 855 { 856 $$ = STRU_R; 857 } 858 | P 859 { 860 $$ = STRU_P; 861 } 862 ; 863 864mode_code 865 : S 866 { 867 $$ = MODE_S; 868 } 869 | B 870 { 871 $$ = MODE_B; 872 } 873 | C 874 { 875 $$ = MODE_C; 876 } 877 ; 878 879pathname 880 : pathstring 881 { 882 /* 883 * Problem: this production is used for all pathname 884 * processing, but only gives a 550 error reply. 885 * This is a valid reply in some cases but not in others. 886 */ 887 if (logged_in && $1 && strchr($1, '~') != NULL) { 888 glob_t gl; 889 int flags = 890 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; 891 char *pptr = $1; 892 893 /* 894 * glob() will only find a leading ~, but 895 * Netscape kindly puts a slash in front of 896 * it for publish URLs. There needs to be 897 * a flag for glob() that expands tildes 898 * anywhere in the string. 899 */ 900 if ((pptr[0] == '/') && (pptr[1] == '~')) 901 pptr++; 902 903 memset(&gl, 0, sizeof(gl)); 904 if (glob(pptr, flags, NULL, &gl) || 905 gl.gl_pathc == 0) { 906 reply(550, "not found"); 907 $$ = NULL; 908 } else { 909 $$ = strdup(gl.gl_pathv[0]); 910 } 911 globfree(&gl); 912 free($1); 913 } else 914 $$ = $1; 915 } 916 ; 917 918pathstring 919 : STRING 920 ; 921 922octal_number 923 : NUMBER 924 { 925 int ret, dec, multby, digit; 926 927 /* 928 * Convert a number that was read as decimal number 929 * to what it would be if it had been read as octal. 930 */ 931 dec = $1; 932 multby = 1; 933 ret = 0; 934 while (dec) { 935 digit = dec%10; 936 if (digit > 7) { 937 ret = -1; 938 break; 939 } 940 ret += digit * multby; 941 multby *= 8; 942 dec /= 10; 943 } 944 $$ = ret; 945 } 946 ; 947 948 949check_login 950 : /* empty */ 951 { 952 if (logged_in) 953 $$ = 1; 954 else { 955 reply(530, "Please login with USER and PASS."); 956 $$ = 0; 957 } 958 } 959 ; 960 961check_login_epsvall 962 : /* empty */ 963 { 964 if (!logged_in) { 965 reply(530, "Please login with USER and PASS."); 966 $$ = 0; 967 } else if (epsvall) { 968 reply(501, "the command is disallowed " 969 "after EPSV ALL"); 970 usedefault = 1; 971 $$ = 0; 972 } else 973 $$ = 1; 974 } 975 ; 976 977%% 978 979extern jmp_buf errcatch; 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 1168 reply(421, 1169 "Timeout (%d seconds): closing control connection.", timeout); 1170 if (logging) 1171 syslog(LOG_INFO, "User %s timed out after %d seconds", 1172 (pw ? pw -> pw_name : "unknown"), timeout); 1173 dologout(1); 1174} 1175 1176static int 1177yylex() 1178{ 1179 static int cpos, state; 1180 char *cp, *cp2; 1181 struct tab *p; 1182 int n; 1183 char c; 1184 1185 for (;;) { 1186 switch (state) { 1187 1188 case CMD: 1189 (void) signal(SIGALRM, toolong); 1190 (void) alarm((unsigned) timeout); 1191 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 1192 reply(221, "You could at least say goodbye."); 1193 dologout(0); 1194 } 1195 (void) alarm(0); 1196 if ((cp = strchr(cbuf, '\r'))) { 1197 *cp++ = '\n'; 1198 *cp = '\0'; 1199 } 1200#ifdef HASSETPROCTITLE 1201 if (strncasecmp(cbuf, "PASS", 4) != 0) { 1202 if ((cp = strpbrk(cbuf, "\n"))) { 1203 c = *cp; 1204 *cp = '\0'; 1205 setproctitle("%s: %s", proctitle, cbuf); 1206 *cp = c; 1207 } 1208 } 1209#endif /* HASSETPROCTITLE */ 1210 if ((cp = strpbrk(cbuf, " \n"))) 1211 cpos = cp - cbuf; 1212 if (cpos == 0) 1213 cpos = 4; 1214 c = cbuf[cpos]; 1215 cbuf[cpos] = '\0'; 1216 upper(cbuf); 1217 p = lookup(cmdtab, cbuf); 1218 cbuf[cpos] = c; 1219 if (p != 0) { 1220 if (p->implemented == 0) { 1221 nack(p->name); 1222 longjmp(errcatch,0); 1223 /* NOTREACHED */ 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 != 0) { 1245 if (p->implemented == 0) { 1246 state = CMD; 1247 nack(p->name); 1248 longjmp(errcatch,0); 1249 /* NOTREACHED */ 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 yyerror((char *) 0); 1403 state = CMD; 1404 longjmp(errcatch,0); 1405 } 1406} 1407 1408void 1409upper(s) 1410 char *s; 1411{ 1412 while (*s != '\0') { 1413 if (islower(*s)) 1414 *s = toupper(*s); 1415 s++; 1416 } 1417} 1418 1419static void 1420help(ctab, s) 1421 struct tab *ctab; 1422 char *s; 1423{ 1424 struct tab *c; 1425 int width, NCMDS; 1426 char *type; 1427 1428 if (ctab == sitetab) 1429 type = "SITE "; 1430 else 1431 type = ""; 1432 width = 0, NCMDS = 0; 1433 for (c = ctab; c->name != NULL; c++) { 1434 int len = strlen(c->name); 1435 1436 if (len > width) 1437 width = len; 1438 NCMDS++; 1439 } 1440 width = (width + 8) &~ 7; 1441 if (s == 0) { 1442 int i, j, w; 1443 int columns, lines; 1444 1445 lreply(214, "The following %scommands are recognized %s.", 1446 type, "(* =>'s unimplemented)"); 1447 columns = 76 / width; 1448 if (columns == 0) 1449 columns = 1; 1450 lines = (NCMDS + columns - 1) / columns; 1451 for (i = 0; i < lines; i++) { 1452 printf(" "); 1453 for (j = 0; j < columns; j++) { 1454 c = ctab + j * lines + i; 1455 printf("%s%c", c->name, 1456 c->implemented ? ' ' : '*'); 1457 if (c + lines >= &ctab[NCMDS]) 1458 break; 1459 w = strlen(c->name) + 1; 1460 while (w < width) { 1461 putchar(' '); 1462 w++; 1463 } 1464 } 1465 printf("\r\n"); 1466 } 1467 (void) fflush(stdout); 1468 reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1469 return; 1470 } 1471 upper(s); 1472 c = lookup(ctab, s); 1473 if (c == (struct tab *)0) { 1474 reply(502, "Unknown command %s.", s); 1475 return; 1476 } 1477 if (c->implemented) 1478 reply(214, "Syntax: %s%s %s", type, c->name, c->help); 1479 else 1480 reply(214, "%s%-*s\t%s; unimplemented.", type, width, 1481 c->name, c->help); 1482} 1483 1484static void 1485sizecmd(filename) 1486 char *filename; 1487{ 1488 switch (type) { 1489 case TYPE_L: 1490 case TYPE_I: { 1491 struct stat stbuf; 1492 if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) 1493 reply(550, "%s: not a plain file.", filename); 1494 else 1495 reply(213, "%qu", stbuf.st_size); 1496 break; } 1497 case TYPE_A: { 1498 FILE *fin; 1499 int c; 1500 off_t count; 1501 struct stat stbuf; 1502 fin = fopen(filename, "r"); 1503 if (fin == NULL) { 1504 perror_reply(550, filename); 1505 return; 1506 } 1507 if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) { 1508 reply(550, "%s: not a plain file.", filename); 1509 (void) fclose(fin); 1510 return; 1511 } 1512 1513 count = 0; 1514 while((c=getc(fin)) != EOF) { 1515 if (c == '\n') /* will get expanded to \r\n */ 1516 count++; 1517 count++; 1518 } 1519 (void) fclose(fin); 1520 1521 reply(213, "%qd", count); 1522 break; } 1523 default: 1524 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 1525 } 1526} 1527