ftpcmd.y revision 17435
1/* 2 * Copyright (c) 1985, 1988, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 34 */ 35 36/* 37 * Grammar for FTP commands. 38 * See RFC 959. 39 */ 40 41%{ 42 43#ifndef lint 44static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; 45#endif /* not lint */ 46 47#include <sys/param.h> 48#include <sys/socket.h> 49#include <sys/stat.h> 50 51#include <netinet/in.h> 52#include <arpa/ftp.h> 53 54#include <ctype.h> 55#include <errno.h> 56#include <glob.h> 57#include <pwd.h> 58#include <setjmp.h> 59#include <signal.h> 60#include <stdio.h> 61#include <stdlib.h> 62#include <string.h> 63#include <syslog.h> 64#include <time.h> 65#include <unistd.h> 66#include <libutil.h> 67 68#include "extern.h" 69 70extern struct sockaddr_in data_dest, his_addr; 71extern int logged_in; 72extern struct passwd *pw; 73extern int guest; 74extern int paranoid; 75extern int logging; 76extern int type; 77extern int form; 78extern int debug; 79extern int timeout; 80extern int maxtimeout; 81extern int pdata; 82extern char hostname[], remotehost[]; 83extern char proctitle[]; 84extern int usedefault; 85extern int transflag; 86extern char tmpline[]; 87 88off_t restart_point; 89 90static int cmd_type; 91static int cmd_form; 92static int cmd_bytesz; 93char cbuf[512]; 94char *fromname; 95 96%} 97 98%union { 99 int i; 100 char *s; 101} 102 103%token 104 A B C E F I 105 L N P R S T 106 107 SP CRLF COMMA 108 109 USER PASS ACCT REIN QUIT PORT 110 PASV TYPE STRU MODE RETR STOR 111 APPE MLFL MAIL MSND MSOM MSAM 112 MRSQ MRCP ALLO REST RNFR RNTO 113 ABOR DELE CWD LIST NLST SITE 114 STAT HELP NOOP MKD RMD PWD 115 CDUP STOU SMNT SYST SIZE MDTM 116 117 UMASK IDLE CHMOD 118 119 LEXERR 120 121%token <s> STRING 122%token <i> NUMBER 123 124%type <i> check_login octal_number byte_size 125%type <i> struct_code mode_code type_code form_code 126%type <s> pathstring pathname password username 127 128%start cmd_list 129 130%% 131 132cmd_list 133 : /* empty */ 134 | cmd_list cmd 135 { 136 fromname = (char *) 0; 137 restart_point = (off_t) 0; 138 } 139 | cmd_list rcmd 140 ; 141 142cmd 143 : USER SP username CRLF 144 { 145 user($3); 146 free($3); 147 } 148 | PASS SP password CRLF 149 { 150 pass($3); 151 free($3); 152 } 153 | PORT check_login SP host_port CRLF 154 { 155 if ($2) { 156 if (paranoid && 157 ((ntohs(data_dest.sin_port) < 158 IPPORT_RESERVED) || 159 memcmp(&data_dest.sin_addr, 160 &his_addr.sin_addr, 161 sizeof(data_dest.sin_addr)))) { 162 usedefault = 1; 163 reply(500, 164 "Illegal PORT range rejected."); 165 } else { 166 usedefault = 0; 167 if (pdata >= 0) { 168 (void) close(pdata); 169 pdata = -1; 170 } 171 reply(200, "PORT command successful."); 172 } 173 } 174 } 175 | PASV check_login CRLF 176 { 177 if ($2) 178 passive(); 179 } 180 | TYPE SP type_code CRLF 181 { 182 switch (cmd_type) { 183 184 case TYPE_A: 185 if (cmd_form == FORM_N) { 186 reply(200, "Type set to A."); 187 type = cmd_type; 188 form = cmd_form; 189 } else 190 reply(504, "Form must be N."); 191 break; 192 193 case TYPE_E: 194 reply(504, "Type E not implemented."); 195 break; 196 197 case TYPE_I: 198 reply(200, "Type set to I."); 199 type = cmd_type; 200 break; 201 202 case TYPE_L: 203#if NBBY == 8 204 if (cmd_bytesz == 8) { 205 reply(200, 206 "Type set to L (byte size 8)."); 207 type = cmd_type; 208 } else 209 reply(504, "Byte size must be 8."); 210#else /* NBBY == 8 */ 211 UNIMPLEMENTED for NBBY != 8 212#endif /* NBBY == 8 */ 213 } 214 } 215 | STRU SP struct_code CRLF 216 { 217 switch ($3) { 218 219 case STRU_F: 220 reply(200, "STRU F ok."); 221 break; 222 223 default: 224 reply(504, "Unimplemented STRU type."); 225 } 226 } 227 | MODE SP mode_code CRLF 228 { 229 switch ($3) { 230 231 case MODE_S: 232 reply(200, "MODE S ok."); 233 break; 234 235 default: 236 reply(502, "Unimplemented MODE type."); 237 } 238 } 239 | ALLO SP NUMBER CRLF 240 { 241 reply(202, "ALLO command ignored."); 242 } 243 | ALLO SP NUMBER SP R SP NUMBER CRLF 244 { 245 reply(202, "ALLO command ignored."); 246 } 247 | RETR check_login SP pathname CRLF 248 { 249 if ($2 && $4 != NULL) 250 retrieve((char *) 0, $4); 251 if ($4 != NULL) 252 free($4); 253 } 254 | STOR check_login SP pathname CRLF 255 { 256 if ($2 && $4 != NULL) 257 store($4, "w", 0); 258 if ($4 != NULL) 259 free($4); 260 } 261 | APPE check_login SP pathname CRLF 262 { 263 if ($2 && $4 != NULL) 264 store($4, "a", 0); 265 if ($4 != NULL) 266 free($4); 267 } 268 | NLST check_login CRLF 269 { 270 if ($2) 271 send_file_list("."); 272 } 273 | NLST check_login SP STRING CRLF 274 { 275 if ($2 && $4 != NULL) 276 send_file_list($4); 277 if ($4 != NULL) 278 free($4); 279 } 280 | LIST check_login CRLF 281 { 282 if ($2) 283 retrieve("/bin/ls -lgA", ""); 284 } 285 | LIST check_login SP pathname CRLF 286 { 287 if ($2 && $4 != NULL) 288 retrieve("/bin/ls -lgA %s", $4); 289 if ($4 != NULL) 290 free($4); 291 } 292 | STAT check_login SP pathname CRLF 293 { 294 if ($2 && $4 != NULL) 295 statfilecmd($4); 296 if ($4 != NULL) 297 free($4); 298 } 299 | STAT CRLF 300 { 301 statcmd(); 302 } 303 | DELE check_login SP pathname CRLF 304 { 305 if ($2 && $4 != NULL) 306 delete($4); 307 if ($4 != NULL) 308 free($4); 309 } 310 | RNTO check_login SP pathname CRLF 311 { 312 if ($2) { 313 if (fromname) { 314 renamecmd(fromname, $4); 315 free(fromname); 316 fromname = (char *) 0; 317 } else { 318 reply(503, "Bad sequence of commands."); 319 } 320 } 321 free($4); 322 } 323 | ABOR CRLF 324 { 325 reply(225, "ABOR command successful."); 326 } 327 | CWD check_login CRLF 328 { 329 if ($2) 330 cwd(pw->pw_dir); 331 } 332 | CWD check_login SP pathname CRLF 333 { 334 if ($2 && $4 != NULL) 335 cwd($4); 336 if ($4 != NULL) 337 free($4); 338 } 339 | HELP CRLF 340 { 341 help(cmdtab, (char *) 0); 342 } 343 | HELP SP STRING CRLF 344 { 345 char *cp = $3; 346 347 if (strncasecmp(cp, "SITE", 4) == 0) { 348 cp = $3 + 4; 349 if (*cp == ' ') 350 cp++; 351 if (*cp) 352 help(sitetab, cp); 353 else 354 help(sitetab, (char *) 0); 355 } else 356 help(cmdtab, $3); 357 } 358 | NOOP CRLF 359 { 360 reply(200, "NOOP command successful."); 361 } 362 | MKD check_login SP pathname CRLF 363 { 364 if ($2 && $4 != NULL) 365 makedir($4); 366 if ($4 != NULL) 367 free($4); 368 } 369 | RMD check_login SP pathname CRLF 370 { 371 if ($2 && $4 != NULL) 372 removedir($4); 373 if ($4 != NULL) 374 free($4); 375 } 376 | PWD check_login CRLF 377 { 378 if ($2) 379 pwd(); 380 } 381 | CDUP check_login CRLF 382 { 383 if ($2) 384 cwd(".."); 385 } 386 | SITE SP HELP CRLF 387 { 388 help(sitetab, (char *) 0); 389 } 390 | SITE SP HELP SP STRING CRLF 391 { 392 help(sitetab, $5); 393 } 394 | SITE SP UMASK check_login CRLF 395 { 396 int oldmask; 397 398 if ($4) { 399 oldmask = umask(0); 400 (void) umask(oldmask); 401 reply(200, "Current UMASK is %03o", oldmask); 402 } 403 } 404 | SITE SP UMASK check_login SP octal_number CRLF 405 { 406 int oldmask; 407 408 if ($4) { 409 if (($6 == -1) || ($6 > 0777)) { 410 reply(501, "Bad UMASK value"); 411 } else { 412 oldmask = umask($6); 413 reply(200, 414 "UMASK set to %03o (was %03o)", 415 $6, oldmask); 416 } 417 } 418 } 419 | SITE SP CHMOD check_login SP octal_number SP pathname CRLF 420 { 421 if ($4 && ($8 != NULL)) { 422 if ($6 > 0777) 423 reply(501, 424 "CHMOD: Mode value must be between 0 and 0777"); 425 else if (chmod($8, $6) < 0) 426 perror_reply(550, $8); 427 else 428 reply(200, "CHMOD command successful."); 429 } 430 if ($8 != NULL) 431 free($8); 432 } 433 | SITE SP IDLE CRLF 434 { 435 reply(200, 436 "Current IDLE time limit is %d seconds; max %d", 437 timeout, maxtimeout); 438 } 439 | SITE SP IDLE SP NUMBER CRLF 440 { 441 if ($5 < 30 || $5 > maxtimeout) { 442 reply(501, 443 "Maximum IDLE time must be between 30 and %d seconds", 444 maxtimeout); 445 } else { 446 timeout = $5; 447 (void) alarm((unsigned) timeout); 448 reply(200, 449 "Maximum IDLE time set to %d seconds", 450 timeout); 451 } 452 } 453 | STOU check_login SP pathname CRLF 454 { 455 if ($2 && $4 != NULL) 456 store($4, "w", 1); 457 if ($4 != NULL) 458 free($4); 459 } 460 | SYST CRLF 461 { 462#ifdef unix 463#ifdef BSD 464 reply(215, "UNIX Type: L%d Version: BSD-%d", 465 NBBY, BSD); 466#else /* BSD */ 467 reply(215, "UNIX Type: L%d", NBBY); 468#endif /* BSD */ 469#else /* unix */ 470 reply(215, "UNKNOWN Type: L%d", NBBY); 471#endif /* unix */ 472 } 473 474 /* 475 * SIZE is not in RFC959, but Postel has blessed it and 476 * it will be in the updated RFC. 477 * 478 * Return size of file in a format suitable for 479 * using with RESTART (we just count bytes). 480 */ 481 | SIZE check_login SP pathname CRLF 482 { 483 if ($2 && $4 != NULL) 484 sizecmd($4); 485 if ($4 != NULL) 486 free($4); 487 } 488 489 /* 490 * MDTM is not in RFC959, but Postel has blessed it and 491 * it will be in the updated RFC. 492 * 493 * Return modification time of file as an ISO 3307 494 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 495 * where xxx is the fractional second (of any precision, 496 * not necessarily 3 digits) 497 */ 498 | MDTM check_login SP pathname CRLF 499 { 500 if ($2 && $4 != NULL) { 501 struct stat stbuf; 502 if (stat($4, &stbuf) < 0) 503 reply(550, "%s: %s", 504 $4, strerror(errno)); 505 else if (!S_ISREG(stbuf.st_mode)) { 506 reply(550, "%s: not a plain file.", $4); 507 } else { 508 struct tm *t; 509 t = gmtime(&stbuf.st_mtime); 510 reply(213, 511 "%04d%02d%02d%02d%02d%02d", 512 1900 + t->tm_year, 513 t->tm_mon+1, t->tm_mday, 514 t->tm_hour, t->tm_min, t->tm_sec); 515 } 516 } 517 if ($4 != NULL) 518 free($4); 519 } 520 | QUIT CRLF 521 { 522 reply(221, "Goodbye."); 523 dologout(0); 524 } 525 | error CRLF 526 { 527 yyerrok; 528 } 529 ; 530rcmd 531 : RNFR check_login SP pathname CRLF 532 { 533 char *renamefrom(); 534 535 restart_point = (off_t) 0; 536 if ($2 && $4) { 537 fromname = renamefrom($4); 538 if (fromname == (char *) 0 && $4) { 539 free($4); 540 } 541 } 542 } 543 | REST SP byte_size CRLF 544 { 545 fromname = (char *) 0; 546 restart_point = $3; /* XXX $3 is only "int" */ 547 reply(350, "Restarting at %qd. %s", restart_point, 548 "Send STORE or RETRIEVE to initiate transfer."); 549 } 550 ; 551 552username 553 : STRING 554 ; 555 556password 557 : /* empty */ 558 { 559 $$ = (char *)calloc(1, sizeof(char)); 560 } 561 | STRING 562 ; 563 564byte_size 565 : NUMBER 566 ; 567 568host_port 569 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 570 NUMBER COMMA NUMBER 571 { 572 char *a, *p; 573 574 data_dest.sin_len = sizeof(struct sockaddr_in); 575 data_dest.sin_family = AF_INET; 576 p = (char *)&data_dest.sin_port; 577 p[0] = $9; p[1] = $11; 578 a = (char *)&data_dest.sin_addr; 579 a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 580 } 581 ; 582 583form_code 584 : N 585 { 586 $$ = FORM_N; 587 } 588 | T 589 { 590 $$ = FORM_T; 591 } 592 | C 593 { 594 $$ = FORM_C; 595 } 596 ; 597 598type_code 599 : A 600 { 601 cmd_type = TYPE_A; 602 cmd_form = FORM_N; 603 } 604 | A SP form_code 605 { 606 cmd_type = TYPE_A; 607 cmd_form = $3; 608 } 609 | E 610 { 611 cmd_type = TYPE_E; 612 cmd_form = FORM_N; 613 } 614 | E SP form_code 615 { 616 cmd_type = TYPE_E; 617 cmd_form = $3; 618 } 619 | I 620 { 621 cmd_type = TYPE_I; 622 } 623 | L 624 { 625 cmd_type = TYPE_L; 626 cmd_bytesz = NBBY; 627 } 628 | L SP byte_size 629 { 630 cmd_type = TYPE_L; 631 cmd_bytesz = $3; 632 } 633 /* this is for a bug in the BBN ftp */ 634 | L byte_size 635 { 636 cmd_type = TYPE_L; 637 cmd_bytesz = $2; 638 } 639 ; 640 641struct_code 642 : F 643 { 644 $$ = STRU_F; 645 } 646 | R 647 { 648 $$ = STRU_R; 649 } 650 | P 651 { 652 $$ = STRU_P; 653 } 654 ; 655 656mode_code 657 : S 658 { 659 $$ = MODE_S; 660 } 661 | B 662 { 663 $$ = MODE_B; 664 } 665 | C 666 { 667 $$ = MODE_C; 668 } 669 ; 670 671pathname 672 : pathstring 673 { 674 /* 675 * Problem: this production is used for all pathname 676 * processing, but only gives a 550 error reply. 677 * This is a valid reply in some cases but not in others. 678 */ 679 if (logged_in && $1 && *$1 == '~') { 680 glob_t gl; 681 int flags = 682 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; 683 684 memset(&gl, 0, sizeof(gl)); 685 if (glob($1, flags, NULL, &gl) || 686 gl.gl_pathc == 0) { 687 reply(550, "not found"); 688 $$ = NULL; 689 } else { 690 $$ = strdup(gl.gl_pathv[0]); 691 } 692 globfree(&gl); 693 free($1); 694 } else 695 $$ = $1; 696 } 697 ; 698 699pathstring 700 : STRING 701 ; 702 703octal_number 704 : NUMBER 705 { 706 int ret, dec, multby, digit; 707 708 /* 709 * Convert a number that was read as decimal number 710 * to what it would be if it had been read as octal. 711 */ 712 dec = $1; 713 multby = 1; 714 ret = 0; 715 while (dec) { 716 digit = dec%10; 717 if (digit > 7) { 718 ret = -1; 719 break; 720 } 721 ret += digit * multby; 722 multby *= 8; 723 dec /= 10; 724 } 725 $$ = ret; 726 } 727 ; 728 729 730check_login 731 : /* empty */ 732 { 733 if (logged_in) 734 $$ = 1; 735 else { 736 reply(530, "Please login with USER and PASS."); 737 $$ = 0; 738 } 739 } 740 ; 741 742%% 743 744extern jmp_buf errcatch; 745 746#define CMD 0 /* beginning of command */ 747#define ARGS 1 /* expect miscellaneous arguments */ 748#define STR1 2 /* expect SP followed by STRING */ 749#define STR2 3 /* expect STRING */ 750#define OSTR 4 /* optional SP then STRING */ 751#define ZSTR1 5 /* SP then optional STRING */ 752#define ZSTR2 6 /* optional STRING after SP */ 753#define SITECMD 7 /* SITE command */ 754#define NSTR 8 /* Number followed by a string */ 755 756struct tab { 757 char *name; 758 short token; 759 short state; 760 short implemented; /* 1 if command is implemented */ 761 char *help; 762}; 763 764struct tab cmdtab[] = { /* In order defined in RFC 765 */ 765 { "USER", USER, STR1, 1, "<sp> username" }, 766 { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 767 { "ACCT", ACCT, STR1, 0, "(specify account)" }, 768 { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 769 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 770 { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 771 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 772 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 773 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 774 { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 775 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 776 { "RETR", RETR, STR1, 1, "<sp> file-name" }, 777 { "STOR", STOR, STR1, 1, "<sp> file-name" }, 778 { "APPE", APPE, STR1, 1, "<sp> file-name" }, 779 { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 780 { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 781 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 782 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 783 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 784 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 785 { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 786 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 787 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 788 { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 789 { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 790 { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 791 { "DELE", DELE, STR1, 1, "<sp> file-name" }, 792 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 793 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 794 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 795 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 796 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 797 { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 798 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 799 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 800 { "NOOP", NOOP, ARGS, 1, "" }, 801 { "MKD", MKD, STR1, 1, "<sp> path-name" }, 802 { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 803 { "RMD", RMD, STR1, 1, "<sp> path-name" }, 804 { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 805 { "PWD", PWD, ARGS, 1, "(return current directory)" }, 806 { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 807 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 808 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 809 { "STOU", STOU, STR1, 1, "<sp> file-name" }, 810 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 811 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 812 { NULL, 0, 0, 0, 0 } 813}; 814 815struct tab sitetab[] = { 816 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 817 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 818 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 819 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 820 { NULL, 0, 0, 0, 0 } 821}; 822 823static char *copy __P((char *)); 824static void help __P((struct tab *, char *)); 825static struct tab * 826 lookup __P((struct tab *, char *)); 827static void sizecmd __P((char *)); 828static void toolong __P((int)); 829static int yylex __P((void)); 830 831static struct tab * 832lookup(p, cmd) 833 struct tab *p; 834 char *cmd; 835{ 836 837 for (; p->name != NULL; p++) 838 if (strcmp(cmd, p->name) == 0) 839 return (p); 840 return (0); 841} 842 843#include <arpa/telnet.h> 844 845/* 846 * getline - a hacked up version of fgets to ignore TELNET escape codes. 847 */ 848char * 849getline(s, n, iop) 850 char *s; 851 int n; 852 FILE *iop; 853{ 854 int c; 855 register char *cs; 856 857 cs = s; 858/* tmpline may contain saved command from urgent mode interruption */ 859 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 860 *cs++ = tmpline[c]; 861 if (tmpline[c] == '\n') { 862 *cs++ = '\0'; 863 if (debug) 864 syslog(LOG_DEBUG, "command: %s", s); 865 tmpline[0] = '\0'; 866 return(s); 867 } 868 if (c == 0) 869 tmpline[0] = '\0'; 870 } 871 while ((c = getc(iop)) != EOF) { 872 c &= 0377; 873 if (c == IAC) { 874 if ((c = getc(iop)) != EOF) { 875 c &= 0377; 876 switch (c) { 877 case WILL: 878 case WONT: 879 c = getc(iop); 880 printf("%c%c%c", IAC, DONT, 0377&c); 881 (void) fflush(stdout); 882 continue; 883 case DO: 884 case DONT: 885 c = getc(iop); 886 printf("%c%c%c", IAC, WONT, 0377&c); 887 (void) fflush(stdout); 888 continue; 889 case IAC: 890 break; 891 default: 892 continue; /* ignore command */ 893 } 894 } 895 } 896 *cs++ = c; 897 if (--n <= 0 || c == '\n') 898 break; 899 } 900 if (c == EOF && cs == s) 901 return (NULL); 902 *cs++ = '\0'; 903 if (debug) { 904 if (!guest && strncasecmp("pass ", s, 5) == 0) { 905 /* Don't syslog passwords */ 906 syslog(LOG_DEBUG, "command: %.5s ???", s); 907 } else { 908 register char *cp; 909 register int len; 910 911 /* Don't syslog trailing CR-LF */ 912 len = strlen(s); 913 cp = s + len - 1; 914 while (cp >= s && (*cp == '\n' || *cp == '\r')) { 915 --cp; 916 --len; 917 } 918 syslog(LOG_DEBUG, "command: %.*s", len, s); 919 } 920 } 921 return (s); 922} 923 924static void 925toolong(signo) 926 int signo; 927{ 928 929 reply(421, 930 "Timeout (%d seconds): closing control connection.", timeout); 931 if (logging) 932 syslog(LOG_INFO, "User %s timed out after %d seconds", 933 (pw ? pw -> pw_name : "unknown"), timeout); 934 dologout(1); 935} 936 937static int 938yylex() 939{ 940 static int cpos, state; 941 char *cp, *cp2; 942 struct tab *p; 943 int n; 944 char c; 945 946 for (;;) { 947 switch (state) { 948 949 case CMD: 950 (void) signal(SIGALRM, toolong); 951 (void) alarm((unsigned) timeout); 952 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 953 reply(221, "You could at least say goodbye."); 954 dologout(0); 955 } 956 (void) alarm(0); 957#ifdef SETPROCTITLE 958 if (strncasecmp(cbuf, "PASS", 4) != NULL) 959 setproctitle("%s: %s", proctitle, cbuf); 960#endif /* SETPROCTITLE */ 961 if ((cp = strchr(cbuf, '\r'))) { 962 *cp++ = '\n'; 963 *cp = '\0'; 964 } 965 if ((cp = strpbrk(cbuf, " \n"))) 966 cpos = cp - cbuf; 967 if (cpos == 0) 968 cpos = 4; 969 c = cbuf[cpos]; 970 cbuf[cpos] = '\0'; 971 upper(cbuf); 972 p = lookup(cmdtab, cbuf); 973 cbuf[cpos] = c; 974 if (p != 0) { 975 if (p->implemented == 0) { 976 nack(p->name); 977 longjmp(errcatch,0); 978 /* NOTREACHED */ 979 } 980 state = p->state; 981 yylval.s = p->name; 982 return (p->token); 983 } 984 break; 985 986 case SITECMD: 987 if (cbuf[cpos] == ' ') { 988 cpos++; 989 return (SP); 990 } 991 cp = &cbuf[cpos]; 992 if ((cp2 = strpbrk(cp, " \n"))) 993 cpos = cp2 - cbuf; 994 c = cbuf[cpos]; 995 cbuf[cpos] = '\0'; 996 upper(cp); 997 p = lookup(sitetab, cp); 998 cbuf[cpos] = c; 999 if (guest == 0 && p != 0) { 1000 if (p->implemented == 0) { 1001 state = CMD; 1002 nack(p->name); 1003 longjmp(errcatch,0); 1004 /* NOTREACHED */ 1005 } 1006 state = p->state; 1007 yylval.s = p->name; 1008 return (p->token); 1009 } 1010 state = CMD; 1011 break; 1012 1013 case OSTR: 1014 if (cbuf[cpos] == '\n') { 1015 state = CMD; 1016 return (CRLF); 1017 } 1018 /* FALLTHROUGH */ 1019 1020 case STR1: 1021 case ZSTR1: 1022 dostr1: 1023 if (cbuf[cpos] == ' ') { 1024 cpos++; 1025 state = state == OSTR ? STR2 : ++state; 1026 return (SP); 1027 } 1028 break; 1029 1030 case ZSTR2: 1031 if (cbuf[cpos] == '\n') { 1032 state = CMD; 1033 return (CRLF); 1034 } 1035 /* FALLTHROUGH */ 1036 1037 case STR2: 1038 cp = &cbuf[cpos]; 1039 n = strlen(cp); 1040 cpos += n - 1; 1041 /* 1042 * Make sure the string is nonempty and \n terminated. 1043 */ 1044 if (n > 1 && cbuf[cpos] == '\n') { 1045 cbuf[cpos] = '\0'; 1046 yylval.s = copy(cp); 1047 cbuf[cpos] = '\n'; 1048 state = ARGS; 1049 return (STRING); 1050 } 1051 break; 1052 1053 case NSTR: 1054 if (cbuf[cpos] == ' ') { 1055 cpos++; 1056 return (SP); 1057 } 1058 if (isdigit(cbuf[cpos])) { 1059 cp = &cbuf[cpos]; 1060 while (isdigit(cbuf[++cpos])) 1061 ; 1062 c = cbuf[cpos]; 1063 cbuf[cpos] = '\0'; 1064 yylval.i = atoi(cp); 1065 cbuf[cpos] = c; 1066 state = STR1; 1067 return (NUMBER); 1068 } 1069 state = STR1; 1070 goto dostr1; 1071 1072 case ARGS: 1073 if (isdigit(cbuf[cpos])) { 1074 cp = &cbuf[cpos]; 1075 while (isdigit(cbuf[++cpos])) 1076 ; 1077 c = cbuf[cpos]; 1078 cbuf[cpos] = '\0'; 1079 yylval.i = atoi(cp); 1080 cbuf[cpos] = c; 1081 return (NUMBER); 1082 } 1083 switch (cbuf[cpos++]) { 1084 1085 case '\n': 1086 state = CMD; 1087 return (CRLF); 1088 1089 case ' ': 1090 return (SP); 1091 1092 case ',': 1093 return (COMMA); 1094 1095 case 'A': 1096 case 'a': 1097 return (A); 1098 1099 case 'B': 1100 case 'b': 1101 return (B); 1102 1103 case 'C': 1104 case 'c': 1105 return (C); 1106 1107 case 'E': 1108 case 'e': 1109 return (E); 1110 1111 case 'F': 1112 case 'f': 1113 return (F); 1114 1115 case 'I': 1116 case 'i': 1117 return (I); 1118 1119 case 'L': 1120 case 'l': 1121 return (L); 1122 1123 case 'N': 1124 case 'n': 1125 return (N); 1126 1127 case 'P': 1128 case 'p': 1129 return (P); 1130 1131 case 'R': 1132 case 'r': 1133 return (R); 1134 1135 case 'S': 1136 case 's': 1137 return (S); 1138 1139 case 'T': 1140 case 't': 1141 return (T); 1142 1143 } 1144 break; 1145 1146 default: 1147 fatal("Unknown state in scanner."); 1148 } 1149 yyerror((char *) 0); 1150 state = CMD; 1151 longjmp(errcatch,0); 1152 } 1153} 1154 1155void 1156upper(s) 1157 char *s; 1158{ 1159 while (*s != '\0') { 1160 if (islower(*s)) 1161 *s = toupper(*s); 1162 s++; 1163 } 1164} 1165 1166static char * 1167copy(s) 1168 char *s; 1169{ 1170 char *p; 1171 1172 p = malloc((unsigned) strlen(s) + 1); 1173 if (p == NULL) 1174 fatal("Ran out of memory."); 1175 (void) strcpy(p, s); 1176 return (p); 1177} 1178 1179static void 1180help(ctab, s) 1181 struct tab *ctab; 1182 char *s; 1183{ 1184 struct tab *c; 1185 int width, NCMDS; 1186 char *type; 1187 1188 if (ctab == sitetab) 1189 type = "SITE "; 1190 else 1191 type = ""; 1192 width = 0, NCMDS = 0; 1193 for (c = ctab; c->name != NULL; c++) { 1194 int len = strlen(c->name); 1195 1196 if (len > width) 1197 width = len; 1198 NCMDS++; 1199 } 1200 width = (width + 8) &~ 7; 1201 if (s == 0) { 1202 int i, j, w; 1203 int columns, lines; 1204 1205 lreply(214, "The following %scommands are recognized %s.", 1206 type, "(* =>'s unimplemented)"); 1207 columns = 76 / width; 1208 if (columns == 0) 1209 columns = 1; 1210 lines = (NCMDS + columns - 1) / columns; 1211 for (i = 0; i < lines; i++) { 1212 printf(" "); 1213 for (j = 0; j < columns; j++) { 1214 c = ctab + j * lines + i; 1215 printf("%s%c", c->name, 1216 c->implemented ? ' ' : '*'); 1217 if (c + lines >= &ctab[NCMDS]) 1218 break; 1219 w = strlen(c->name) + 1; 1220 while (w < width) { 1221 putchar(' '); 1222 w++; 1223 } 1224 } 1225 printf("\r\n"); 1226 } 1227 (void) fflush(stdout); 1228 reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1229 return; 1230 } 1231 upper(s); 1232 c = lookup(ctab, s); 1233 if (c == (struct tab *)0) { 1234 reply(502, "Unknown command %s.", s); 1235 return; 1236 } 1237 if (c->implemented) 1238 reply(214, "Syntax: %s%s %s", type, c->name, c->help); 1239 else 1240 reply(214, "%s%-*s\t%s; unimplemented.", type, width, 1241 c->name, c->help); 1242} 1243 1244static void 1245sizecmd(filename) 1246 char *filename; 1247{ 1248 switch (type) { 1249 case TYPE_L: 1250 case TYPE_I: { 1251 struct stat stbuf; 1252 if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) 1253 reply(550, "%s: not a plain file.", filename); 1254 else 1255 reply(213, "%qu", stbuf.st_size); 1256 break; } 1257 case TYPE_A: { 1258 FILE *fin; 1259 int c; 1260 off_t count; 1261 struct stat stbuf; 1262 fin = fopen(filename, "r"); 1263 if (fin == NULL) { 1264 perror_reply(550, filename); 1265 return; 1266 } 1267 if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) { 1268 reply(550, "%s: not a plain file.", filename); 1269 (void) fclose(fin); 1270 return; 1271 } 1272 1273 count = 0; 1274 while((c=getc(fin)) != EOF) { 1275 if (c == '\n') /* will get expanded to \r\n */ 1276 count++; 1277 count++; 1278 } 1279 (void) fclose(fin); 1280 1281 reply(213, "%qd", count); 1282 break; } 1283 default: 1284 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 1285 } 1286} 1287