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