ftpcmd.y revision 1.41
1/* $OpenBSD: ftpcmd.y,v 1.41 2002/07/02 18:09:53 danh 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 const char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; 49#else 50static const char rcsid[] = 51 "$OpenBSD: ftpcmd.y,v 1.41 2002/07/02 18:09:53 danh Exp $"; 52#endif 53#endif /* not lint */ 54 55#include <sys/param.h> 56#include <sys/socket.h> 57#include <sys/stat.h> 58 59#include <netinet/in.h> 60#include <arpa/ftp.h> 61 62#include <ctype.h> 63#include <errno.h> 64#include <glob.h> 65#include <pwd.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; 103static int state; 104char cbuf[512]; 105char *fromname; 106 107%} 108 109%union { 110 int i; 111 char *s; 112} 113 114%token 115 A B C E F I 116 L N P R S T 117 118 SP CRLF COMMA ALL 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 <i> NUMBER 136 137%type <i> check_login check_login_epsvall octal_number byte_size 138%type <i> struct_code mode_code type_code form_code 139%type <s> pathstring pathname password username 140%type <i> host_port host_long_port4 host_long_port6 141 142%start cmd_list 143 144%% 145 146cmd_list 147 : /* empty */ 148 | cmd_list cmd 149 { 150 if (fromname) { 151 free(fromname); 152 fromname = NULL; 153 } 154 restart_point = (off_t) 0; 155 } 156 | cmd_list rcmd 157 ; 158 159cmd 160 : USER SP username CRLF 161 { 162 user($3); 163 free($3); 164 } 165 | PASS SP password CRLF 166 { 167 pass($3); 168 memset($3, 0, strlen($3)); 169 free($3); 170 } 171 | PORT check_login_epsvall SP host_port CRLF 172 { 173 if ($2) { 174 if ($4) { 175 usedefault = 1; 176 reply(500, 177 "Illegal PORT rejected (range errors)."); 178 } else if (portcheck && 179 ntohs(data_dest.su_sin.sin_port) < IPPORT_RESERVED) { 180 usedefault = 1; 181 reply(500, 182 "Illegal PORT rejected (reserved port)."); 183 } else if (portcheck && 184 memcmp(&data_dest.su_sin.sin_addr, 185 &his_addr.su_sin.sin_addr, 186 sizeof data_dest.su_sin.sin_addr)) { 187 usedefault = 1; 188 reply(500, 189 "Illegal PORT rejected (address wrong)."); 190 } else { 191 usedefault = 0; 192 if (pdata >= 0) { 193 (void) close(pdata); 194 pdata = -1; 195 } 196 reply(200, "PORT command successful."); 197 } 198 } 199 } 200 | LPRT check_login_epsvall SP host_long_port4 CRLF 201 { 202 if ($2) { 203 /* reject invalid host_long_port4 */ 204 if ($4) { 205 reply(500, 206 "Illegal LPRT command rejected"); 207 usedefault = 1; 208 } else { 209 usedefault = 0; 210 if (pdata >= 0) { 211 (void) close(pdata); 212 pdata = -1; 213 } 214 reply(200, "LPRT command successful."); 215 } 216 } 217 } 218 219 | LPRT check_login_epsvall SP host_long_port6 CRLF 220 { 221 if ($2) { 222 /* reject invalid host_long_port6 */ 223 if ($4) { 224 reply(500, 225 "Illegal LPRT command rejected"); 226 usedefault = 1; 227 } else { 228 usedefault = 0; 229 if (pdata >= 0) { 230 (void) close(pdata); 231 pdata = -1; 232 } 233 reply(200, "LPRT command successful."); 234 } 235 } 236 } 237 238 | EPRT check_login_epsvall SP STRING CRLF 239 { 240 if ($2) 241 extended_port($4); 242 free($4); 243 } 244 245 | PASV check_login_epsvall CRLF 246 { 247 if ($2) 248 passive(); 249 } 250 | LPSV check_login_epsvall CRLF 251 { 252 if ($2) 253 long_passive("LPSV", PF_UNSPEC); 254 } 255 | EPSV check_login SP NUMBER CRLF 256 { 257 if ($2) 258 long_passive("EPSV", epsvproto2af($4)); 259 } 260 | EPSV check_login SP ALL CRLF 261 { 262 if ($2) { 263 reply(200, "EPSV ALL command successful."); 264 epsvall++; 265 } 266 } 267 | EPSV check_login CRLF 268 { 269 if ($2) 270 long_passive("EPSV", PF_UNSPEC); 271 } 272 | TYPE check_login SP type_code CRLF 273 { 274 if ($2) { 275 switch (cmd_type) { 276 277 case TYPE_A: 278 if (cmd_form == FORM_N) { 279 reply(200, "Type set to A."); 280 type = cmd_type; 281 form = cmd_form; 282 } else 283 reply(504, "Form must be N."); 284 break; 285 286 case TYPE_E: 287 reply(504, "Type E not implemented."); 288 break; 289 290 case TYPE_I: 291 reply(200, "Type set to I."); 292 type = cmd_type; 293 break; 294 295 case TYPE_L: 296 if (cmd_bytesz == 8) { 297 reply(200, 298 "Type set to L (byte size 8)."); 299 type = cmd_type; 300 } else 301 reply(504, "Byte size must be 8."); 302 303 } 304 } 305 } 306 | STRU check_login SP struct_code CRLF 307 { 308 if ($2) { 309 switch ($4) { 310 311 case STRU_F: 312 reply(200, "STRU F ok."); 313 break; 314 315 default: 316 reply(504, "Unimplemented STRU type."); 317 } 318 } 319 } 320 | MODE check_login SP mode_code CRLF 321 { 322 if ($2) { 323 switch ($4) { 324 325 case MODE_S: 326 reply(200, "MODE S ok."); 327 break; 328 329 default: 330 reply(502, "Unimplemented MODE type."); 331 } 332 } 333 } 334 | ALLO check_login SP NUMBER CRLF 335 { 336 if ($2) { 337 reply(202, "ALLO command ignored."); 338 } 339 } 340 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 341 { 342 if ($2) { 343 reply(202, "ALLO command ignored."); 344 } 345 } 346 | RETR check_login SP pathname CRLF 347 { 348 if ($2 && $4 != NULL) 349 retrieve(NULL, $4); 350 if ($4 != NULL) 351 free($4); 352 } 353 | STOR check_login SP pathname CRLF 354 { 355 if ($2 && $4 != NULL) 356 store($4, "w", 0); 357 if ($4 != NULL) 358 free($4); 359 } 360 | APPE check_login SP pathname CRLF 361 { 362 if ($2 && $4 != NULL) 363 store($4, "a", 0); 364 if ($4 != NULL) 365 free($4); 366 } 367 | NLST check_login CRLF 368 { 369 if ($2) 370 send_file_list("."); 371 } 372 | NLST check_login SP STRING CRLF 373 { 374 if ($2 && $4 != NULL) 375 send_file_list($4); 376 free($4); 377 } 378 | LIST check_login CRLF 379 { 380 if ($2) 381 retrieve("/bin/ls -lgA", ""); 382 } 383 | LIST check_login SP pathname CRLF 384 { 385 if ($2 && $4 != NULL) 386 retrieve("/bin/ls -lgA %s", $4); 387 if ($4 != NULL) 388 free($4); 389 } 390 | STAT check_login SP pathname CRLF 391 { 392 if ($2 && $4 != NULL) 393 statfilecmd($4); 394 if ($4 != NULL) 395 free($4); 396 } 397 | STAT check_login CRLF 398 { 399 if ($2) 400 statcmd(); 401 } 402 | DELE check_login SP pathname CRLF 403 { 404 if ($2 && $4 != NULL) 405 delete($4); 406 if ($4 != NULL) 407 free($4); 408 } 409 | RNTO check_login SP pathname CRLF 410 { 411 if ($2 && $4 != NULL) { 412 if (fromname) { 413 renamecmd(fromname, $4); 414 free(fromname); 415 fromname = NULL; 416 } else { 417 reply(503, 418 "Bad sequence of commands."); 419 } 420 } 421 if ($4 != NULL) 422 free($4); 423 } 424 | ABOR check_login CRLF 425 { 426 if ($2) 427 reply(225, "ABOR command successful."); 428 } 429 | CWD check_login CRLF 430 { 431 if ($2) 432 cwd(pw->pw_dir); 433 } 434 | CWD check_login SP pathname CRLF 435 { 436 if ($2 && $4 != NULL) 437 cwd($4); 438 if ($4 != NULL) 439 free($4); 440 } 441 | HELP CRLF 442 { 443 help(cmdtab, NULL); 444 } 445 | HELP SP STRING CRLF 446 { 447 char *cp = $3; 448 449 if (strncasecmp(cp, "SITE", 4) == 0) { 450 cp = $3 + 4; 451 if (*cp == ' ') 452 cp++; 453 if (*cp) 454 help(sitetab, cp); 455 else 456 help(sitetab, NULL); 457 } else 458 help(cmdtab, $3); 459 free ($3); 460 } 461 | NOOP CRLF 462 { 463 reply(200, "NOOP command successful."); 464 } 465 | MKD check_login SP pathname CRLF 466 { 467 if ($2 && $4 != NULL) 468 makedir($4); 469 if ($4 != NULL) 470 free($4); 471 } 472 | RMD check_login SP pathname CRLF 473 { 474 if ($2 && $4 != NULL) 475 removedir($4); 476 if ($4 != NULL) 477 free($4); 478 } 479 | PWD check_login CRLF 480 { 481 if ($2) 482 pwd(); 483 } 484 | CDUP check_login CRLF 485 { 486 if ($2) 487 cwd(".."); 488 } 489 | SITE SP HELP CRLF 490 { 491 help(sitetab, NULL); 492 } 493 | SITE SP HELP SP STRING CRLF 494 { 495 help(sitetab, $5); 496 free ($5); 497 } 498 | SITE SP UMASK check_login CRLF 499 { 500 int oldmask; 501 502 if ($4) { 503 oldmask = umask(0); 504 (void) umask(oldmask); 505 reply(200, "Current UMASK is %03o", oldmask); 506 } 507 } 508 | SITE SP UMASK check_login SP octal_number CRLF 509 { 510 int oldmask; 511 512 if ($4) { 513 if (($6 == -1) || ($6 > 0777)) { 514 reply(501, "Bad UMASK value"); 515 } else if (!umaskchange) { 516 reply(550, 517 "No permission to change umask."); 518 } else { 519 oldmask = umask($6); 520 reply(200, 521 "UMASK set to %03o (was %03o)", 522 $6, oldmask); 523 } 524 } 525 } 526 | SITE SP CHMOD check_login SP octal_number SP pathname CRLF 527 { 528 if ($4 && ($8 != NULL)) { 529 if (($6 == -1) || ($6 > 0777)) 530 reply(501, 531 "CHMOD: Mode value must be between " 532 "0 and 0777"); 533 else if (!umaskchange) 534 reply(550, 535 "No permission to change mode of %s.", 536 $8); 537 else if (chmod($8, $6) < 0) 538 perror_reply(550, $8); 539 else 540 reply(200, 541 "CHMOD command successful."); 542 } 543 if ($8 != NULL) 544 free($8); 545 } 546 | SITE SP check_login IDLE CRLF 547 { 548 if ($3) 549 reply(200, 550 "Current IDLE time limit is %d seconds; max %d", 551 timeout, maxtimeout); 552 } 553 | SITE SP check_login IDLE SP NUMBER CRLF 554 { 555 if ($3) { 556 if ($6 < 30 || $6 > maxtimeout) { 557 reply(501, 558 "Maximum IDLE time must be between " 559 "30 and %d seconds", 560 maxtimeout); 561 } else { 562 timeout = $6; 563 (void) alarm((unsigned) timeout); 564 reply(200, 565 "Maximum IDLE time set to %d seconds", 566 timeout); 567 } 568 } 569 } 570 | STOU check_login SP pathname CRLF 571 { 572 if ($2 && $4 != NULL) 573 store($4, "w", 1); 574 if ($4 != NULL) 575 free($4); 576 } 577 | SYST check_login CRLF 578 { 579 if ($2) 580#ifdef unix 581#ifdef BSD 582 reply(215, "UNIX Type: L%d Version: BSD-%d", 583 NBBY, BSD); 584#else /* BSD */ 585 reply(215, "UNIX Type: L%d", NBBY); 586#endif /* BSD */ 587#else /* unix */ 588 reply(215, "UNKNOWN Type: L%d", NBBY); 589#endif /* unix */ 590 } 591 592 /* 593 * SIZE is not in RFC959, but Postel has blessed it and 594 * it will be in the updated RFC. 595 * 596 * Return size of file in a format suitable for 597 * using with RESTART (we just count bytes). 598 */ 599 | SIZE check_login SP pathname CRLF 600 { 601 if ($2 && $4 != NULL) 602 sizecmd($4); 603 if ($4 != NULL) 604 free($4); 605 } 606 607 /* 608 * MDTM is not in RFC959, but Postel has blessed it and 609 * it will be in the updated RFC. 610 * 611 * Return modification time of file as an ISO 3307 612 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 613 * where xxx is the fractional second (of any precision, 614 * not necessarily 3 digits) 615 */ 616 | MDTM check_login SP pathname CRLF 617 { 618 if ($2 && $4 != NULL) { 619 struct stat stbuf; 620 if (stat($4, &stbuf) < 0) 621 reply(550, "%s: %s", 622 $4, strerror(errno)); 623 else if (!S_ISREG(stbuf.st_mode)) { 624 reply(550, "%s: not a plain file.", $4); 625 } else { 626 struct tm *t; 627 t = gmtime(&stbuf.st_mtime); 628 reply(213, 629 "%04d%02d%02d%02d%02d%02d", 630 TM_YEAR_BASE + t->tm_year, 631 t->tm_mon+1, t->tm_mday, 632 t->tm_hour, t->tm_min, t->tm_sec); 633 } 634 } 635 if ($4 != NULL) 636 free($4); 637 } 638 | QUIT CRLF 639 { 640 reply(221, "Goodbye."); 641 dologout(0); 642 } 643 | error 644 { 645 yyclearin; /* discard lookahead data */ 646 yyerrok; /* clear error condition */ 647 state = 0; /* reset lexer state */ 648 } 649 ; 650rcmd 651 : RNFR check_login SP pathname CRLF 652 { 653 restart_point = (off_t) 0; 654 if ($2 && $4) { 655 if (fromname) 656 free(fromname); 657 fromname = renamefrom($4); 658 if (fromname == NULL) 659 free($4); 660 } else if ($4) { 661 free ($4); 662 } 663 } 664 665 | REST check_login SP byte_size CRLF 666 { 667 if ($2) { 668 if (fromname) { 669 free(fromname); 670 fromname = NULL; 671 } 672 restart_point = $4; /* XXX $4 is only "int" */ 673 reply(350, "Restarting at %qd. %s", restart_point, 674 "Send STORE or RETRIEVE to initiate transfer."); 675 } 676 } 677 ; 678 679username 680 : STRING 681 ; 682 683password 684 : /* empty */ 685 { 686 $$ = (char *)calloc(1, sizeof(char)); 687 } 688 | STRING 689 ; 690 691byte_size 692 : NUMBER 693 ; 694 695host_port 696 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 697 NUMBER COMMA NUMBER 698 { 699 char *a, *p; 700 701 if ($1 < 0 || $1 > 255 || $3 < 0 || $3 > 255 || 702 $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 || 703 $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255) { 704 $$ = 1; 705 } else { 706 data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); 707 data_dest.su_sin.sin_family = AF_INET; 708 p = (char *)&data_dest.su_sin.sin_port; 709 p[0] = $9; p[1] = $11; 710 a = (char *)&data_dest.su_sin.sin_addr; 711 a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 712 $$ = 0; 713 } 714 } 715 ; 716 717host_long_port4 718 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 719 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 720 NUMBER 721 { 722 char *a, *p; 723 724 /* reject invalid LPRT command */ 725 if ($1 != 4 || $3 != 4 726 || $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 727 || $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255 728 || $13 != 2 729 || $15 < 0 || $15 > 255 || $17 < 0 || $17 > 255) { 730 $$ = 1; 731 } else { 732 data_dest.su_sin.sin_len = 733 sizeof(struct sockaddr_in); 734 data_dest.su_family = AF_INET; 735 p = (char *)&data_dest.su_port; 736 p[0] = $15; p[1] = $17; 737 a = (char *)&data_dest.su_sin.sin_addr; 738 a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; 739 $$ = 0; 740 } 741 } 742 ; 743 744host_long_port6 745 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 746 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 747 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 748 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 749 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 750 NUMBER 751 { 752 char *a, *p; 753 754 /* reject invalid LPRT command */ 755 if ($1 != 6 || $3 != 16 756 || $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 757 || $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255 758 || $13 < 0 || $13 > 255 || $15 < 0 || $15 > 255 759 || $17 < 0 || $17 > 255 || $19 < 0 || $19 > 255 760 || $21 < 0 || $21 > 255 || $23 < 0 || $23 > 255 761 || $25 < 0 || $25 > 255 || $27 < 0 || $27 > 255 762 || $29 < 0 || $29 > 255 || $31 < 0 || $31 > 255 763 || $33 < 0 || $33 > 255 || $35 < 0 || $35 > 255 764 || $37 != 2 765 || $39 < 0 || $39 > 255 || $41 < 0 || $41 > 255) { 766 $$ = 1; 767 } else { 768 data_dest.su_sin6.sin6_len = 769 sizeof(struct sockaddr_in6); 770 data_dest.su_family = AF_INET6; 771 p = (char *)&data_dest.su_port; 772 p[0] = $39; p[1] = $41; 773 a = (char *)&data_dest.su_sin6.sin6_addr; 774 a[0] = $5; a[1] = $7; 775 a[2] = $9; a[3] = $11; 776 a[4] = $13; a[5] = $15; 777 a[6] = $17; a[7] = $19; 778 a[8] = $21; a[9] = $23; 779 a[10] = $25; a[11] = $27; 780 a[12] = $29; a[13] = $31; 781 a[14] = $33; a[15] = $35; 782 if (his_addr.su_family == AF_INET6) { 783 /* XXX more sanity checks! */ 784 data_dest.su_sin6.sin6_scope_id = 785 his_addr.su_sin6.sin6_scope_id; 786 } 787 788 $$ = 0; 789 } 790 } 791 ; 792 793form_code 794 : N 795 { 796 $$ = FORM_N; 797 } 798 | T 799 { 800 $$ = FORM_T; 801 } 802 | C 803 { 804 $$ = FORM_C; 805 } 806 ; 807 808type_code 809 : A 810 { 811 cmd_type = TYPE_A; 812 cmd_form = FORM_N; 813 } 814 | A SP form_code 815 { 816 cmd_type = TYPE_A; 817 cmd_form = $3; 818 } 819 | E 820 { 821 cmd_type = TYPE_E; 822 cmd_form = FORM_N; 823 } 824 | E SP form_code 825 { 826 cmd_type = TYPE_E; 827 cmd_form = $3; 828 } 829 | I 830 { 831 cmd_type = TYPE_I; 832 } 833 | L 834 { 835 cmd_type = TYPE_L; 836 cmd_bytesz = NBBY; 837 } 838 | L SP byte_size 839 { 840 cmd_type = TYPE_L; 841 cmd_bytesz = $3; 842 } 843 /* this is for a bug in the BBN ftp */ 844 | L byte_size 845 { 846 cmd_type = TYPE_L; 847 cmd_bytesz = $2; 848 } 849 ; 850 851struct_code 852 : F 853 { 854 $$ = STRU_F; 855 } 856 | R 857 { 858 $$ = STRU_R; 859 } 860 | P 861 { 862 $$ = STRU_P; 863 } 864 ; 865 866mode_code 867 : S 868 { 869 $$ = MODE_S; 870 } 871 | B 872 { 873 $$ = MODE_B; 874 } 875 | C 876 { 877 $$ = MODE_C; 878 } 879 ; 880 881pathname 882 : pathstring 883 { 884 /* 885 * Problem: this production is used for all pathname 886 * processing, but only gives a 550 error reply. 887 * This is a valid reply in some cases but not in others. 888 */ 889 if (logged_in && $1 && strchr($1, '~') != NULL) { 890 glob_t gl; 891 int flags = 892 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; 893 char *pptr = $1; 894 895 /* 896 * glob() will only find a leading ~, but 897 * Netscape kindly puts a slash in front of 898 * it for publish URLs. There needs to be 899 * a flag for glob() that expands tildes 900 * anywhere in the string. 901 */ 902 if ((pptr[0] == '/') && (pptr[1] == '~')) 903 pptr++; 904 905 memset(&gl, 0, sizeof(gl)); 906 if (glob(pptr, flags, NULL, &gl) || 907 gl.gl_pathc == 0) { 908 reply(550, "not found"); 909 $$ = NULL; 910 } else { 911 $$ = strdup(gl.gl_pathv[0]); 912 } 913 globfree(&gl); 914 free($1); 915 } else 916 $$ = $1; 917 } 918 ; 919 920pathstring 921 : STRING 922 ; 923 924octal_number 925 : NUMBER 926 { 927 int ret, dec, multby, digit; 928 929 /* 930 * Convert a number that was read as decimal number 931 * to what it would be if it had been read as octal. 932 */ 933 dec = $1; 934 multby = 1; 935 ret = 0; 936 while (dec) { 937 digit = dec%10; 938 if (digit > 7) { 939 ret = -1; 940 break; 941 } 942 ret += digit * multby; 943 multby *= 8; 944 dec /= 10; 945 } 946 $$ = ret; 947 } 948 ; 949 950 951check_login 952 : /* empty */ 953 { 954 if (logged_in) 955 $$ = 1; 956 else { 957 reply(530, "Please login with USER and PASS."); 958 $$ = 0; 959 } 960 } 961 ; 962 963check_login_epsvall 964 : /* empty */ 965 { 966 if (!logged_in) { 967 reply(530, "Please login with USER and PASS."); 968 $$ = 0; 969 } else if (epsvall) { 970 reply(501, "the command is disallowed " 971 "after EPSV ALL"); 972 usedefault = 1; 973 $$ = 0; 974 } else 975 $$ = 1; 976 } 977 ; 978 979%% 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(struct tab *, char *); 1063static struct tab * 1064 lookup(struct tab *, char *); 1065static void sizecmd(char *); 1066static int yylex(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 (NULL); 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 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 char *cp; 1148 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 struct syslog_data sdata = SYSLOG_DATA_INIT; 1168 1169 /* XXX signal races */ 1170 reply(421, 1171 "Timeout (%d seconds): closing control connection.", timeout); 1172 if (logging) 1173 syslog_r(LOG_INFO, &sdata, "User %s timed out after %d seconds", 1174 (pw ? pw -> pw_name : "unknown"), timeout); 1175 dologout(1); 1176} 1177 1178static int 1179yylex() 1180{ 1181 static int cpos; 1182 char *cp, *cp2; 1183 struct tab *p; 1184 int n; 1185 char c; 1186 1187 for (;;) { 1188 switch (state) { 1189 1190 case CMD: 1191 (void) alarm((unsigned) timeout); 1192 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 1193 reply(221, "You could at least say goodbye."); 1194 dologout(0); 1195 } 1196 (void) alarm(0); 1197 if ((cp = strchr(cbuf, '\r'))) { 1198 *cp++ = '\n'; 1199 *cp = '\0'; 1200 } 1201#ifdef HASSETPROCTITLE 1202 if (strncasecmp(cbuf, "PASS", 4) != 0) { 1203 if ((cp = strpbrk(cbuf, "\n"))) { 1204 c = *cp; 1205 *cp = '\0'; 1206 setproctitle("%s: %s", proctitle, cbuf); 1207 *cp = c; 1208 } 1209 } 1210#endif /* HASSETPROCTITLE */ 1211 if ((cp = strpbrk(cbuf, " \n"))) 1212 cpos = cp - cbuf; 1213 if (cpos == 0) 1214 cpos = 4; 1215 c = cbuf[cpos]; 1216 cbuf[cpos] = '\0'; 1217 upper(cbuf); 1218 p = lookup(cmdtab, cbuf); 1219 cbuf[cpos] = c; 1220 if (p != NULL) { 1221 if (p->implemented == 0) { 1222 nack(p->name); 1223 return (LEXERR); 1224 } 1225 state = p->state; 1226 yylval.s = p->name; 1227 return (p->token); 1228 } 1229 break; 1230 1231 case SITECMD: 1232 if (cbuf[cpos] == ' ') { 1233 cpos++; 1234 return (SP); 1235 } 1236 cp = &cbuf[cpos]; 1237 if ((cp2 = strpbrk(cp, " \n"))) 1238 cpos = cp2 - cbuf; 1239 c = cbuf[cpos]; 1240 cbuf[cpos] = '\0'; 1241 upper(cp); 1242 p = lookup(sitetab, cp); 1243 cbuf[cpos] = c; 1244 if (p != NULL) { 1245 if (p->implemented == 0) { 1246 state = CMD; 1247 nack(p->name); 1248 return (LEXERR); 1249 } 1250 state = p->state; 1251 yylval.s = p->name; 1252 return (p->token); 1253 } 1254 state = CMD; 1255 break; 1256 1257 case OSTR: 1258 if (cbuf[cpos] == '\n') { 1259 state = CMD; 1260 return (CRLF); 1261 } 1262 /* FALLTHROUGH */ 1263 1264 case STR1: 1265 case ZSTR1: 1266 dostr1: 1267 if (cbuf[cpos] == ' ') { 1268 cpos++; 1269 state = state == OSTR ? STR2 : state+1; 1270 return (SP); 1271 } 1272 break; 1273 1274 case ZSTR2: 1275 if (cbuf[cpos] == '\n') { 1276 state = CMD; 1277 return (CRLF); 1278 } 1279 /* FALLTHROUGH */ 1280 1281 case STR2: 1282 cp = &cbuf[cpos]; 1283 n = strlen(cp); 1284 cpos += n - 1; 1285 /* 1286 * Make sure the string is nonempty and \n terminated. 1287 */ 1288 if (n > 1 && cbuf[cpos] == '\n') { 1289 cbuf[cpos] = '\0'; 1290 yylval.s = strdup(cp); 1291 if (yylval.s == NULL) 1292 fatal("Ran out of memory."); 1293 cbuf[cpos] = '\n'; 1294 state = ARGS; 1295 return (STRING); 1296 } 1297 break; 1298 1299 case NSTR: 1300 if (cbuf[cpos] == ' ') { 1301 cpos++; 1302 return (SP); 1303 } 1304 if (isdigit(cbuf[cpos])) { 1305 cp = &cbuf[cpos]; 1306 while (isdigit(cbuf[++cpos])) 1307 ; 1308 c = cbuf[cpos]; 1309 cbuf[cpos] = '\0'; 1310 yylval.i = atoi(cp); 1311 cbuf[cpos] = c; 1312 state = STR1; 1313 return (NUMBER); 1314 } 1315 state = STR1; 1316 goto dostr1; 1317 1318 case ARGS: 1319 if (isdigit(cbuf[cpos])) { 1320 cp = &cbuf[cpos]; 1321 while (isdigit(cbuf[++cpos])) 1322 ; 1323 c = cbuf[cpos]; 1324 cbuf[cpos] = '\0'; 1325 yylval.i = atoi(cp); 1326 cbuf[cpos] = c; 1327 return (NUMBER); 1328 } 1329 if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 1330 && !isalnum(cbuf[cpos + 3])) { 1331 cpos += 3; 1332 return ALL; 1333 } 1334 switch (cbuf[cpos++]) { 1335 1336 case '\n': 1337 state = CMD; 1338 return (CRLF); 1339 1340 case ' ': 1341 return (SP); 1342 1343 case ',': 1344 return (COMMA); 1345 1346 case 'A': 1347 case 'a': 1348 return (A); 1349 1350 case 'B': 1351 case 'b': 1352 return (B); 1353 1354 case 'C': 1355 case 'c': 1356 return (C); 1357 1358 case 'E': 1359 case 'e': 1360 return (E); 1361 1362 case 'F': 1363 case 'f': 1364 return (F); 1365 1366 case 'I': 1367 case 'i': 1368 return (I); 1369 1370 case 'L': 1371 case 'l': 1372 return (L); 1373 1374 case 'N': 1375 case 'n': 1376 return (N); 1377 1378 case 'P': 1379 case 'p': 1380 return (P); 1381 1382 case 'R': 1383 case 'r': 1384 return (R); 1385 1386 case 'S': 1387 case 's': 1388 return (S); 1389 1390 case 'T': 1391 case 't': 1392 return (T); 1393 1394 } 1395 break; 1396 1397 default: 1398 fatal("Unknown state in scanner."); 1399 } 1400 state = CMD; 1401 return (LEXERR); 1402 } 1403} 1404 1405void 1406upper(s) 1407 char *s; 1408{ 1409 char *p; 1410 1411 for (p = s; *p; p++) { 1412 if (islower(*p)) 1413 *p = toupper(*p); 1414 } 1415} 1416 1417static void 1418help(ctab, s) 1419 struct tab *ctab; 1420 char *s; 1421{ 1422 struct tab *c; 1423 int width, NCMDS; 1424 char *type; 1425 1426 if (ctab == sitetab) 1427 type = "SITE "; 1428 else 1429 type = ""; 1430 width = 0, NCMDS = 0; 1431 for (c = ctab; c->name != NULL; c++) { 1432 int len = strlen(c->name); 1433 1434 if (len > width) 1435 width = len; 1436 NCMDS++; 1437 } 1438 width = (width + 8) &~ 7; 1439 if (s == NULL) { 1440 int i, j, w; 1441 int columns, lines; 1442 1443 lreply(214, "The following %scommands are recognized %s.", 1444 type, "(* =>'s unimplemented)"); 1445 columns = 76 / width; 1446 if (columns == 0) 1447 columns = 1; 1448 lines = (NCMDS + columns - 1) / columns; 1449 for (i = 0; i < lines; i++) { 1450 printf(" "); 1451 for (j = 0; j < columns; j++) { 1452 c = ctab + j * lines + i; 1453 printf("%s%c", c->name, 1454 c->implemented ? ' ' : '*'); 1455 if (c + lines >= &ctab[NCMDS]) 1456 break; 1457 w = strlen(c->name) + 1; 1458 while (w < width) { 1459 putchar(' '); 1460 w++; 1461 } 1462 } 1463 printf("\r\n"); 1464 } 1465 (void) fflush(stdout); 1466 reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1467 return; 1468 } 1469 upper(s); 1470 c = lookup(ctab, s); 1471 if (c == NULL) { 1472 reply(502, "Unknown command %s.", s); 1473 return; 1474 } 1475 if (c->implemented) 1476 reply(214, "Syntax: %s%s %s", type, c->name, c->help); 1477 else 1478 reply(214, "%s%-*s\t%s; unimplemented.", type, width, 1479 c->name, c->help); 1480} 1481 1482static void 1483sizecmd(filename) 1484 char *filename; 1485{ 1486 switch (type) { 1487 case TYPE_L: 1488 case TYPE_I: { 1489 struct stat stbuf; 1490 if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) 1491 reply(550, "%s: not a plain file.", filename); 1492 else 1493 reply(213, "%qu", stbuf.st_size); 1494 break; } 1495 case TYPE_A: { 1496 FILE *fin; 1497 int c; 1498 off_t count; 1499 struct stat stbuf; 1500 fin = fopen(filename, "r"); 1501 if (fin == NULL) { 1502 perror_reply(550, filename); 1503 return; 1504 } 1505 if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) { 1506 reply(550, "%s: not a plain file.", filename); 1507 (void) fclose(fin); 1508 return; 1509 } 1510 if (stbuf.st_size > 10240) { 1511 reply(550, "%s: file too large for SIZE.", filename); 1512 (void) fclose(fin); 1513 return; 1514 } 1515 1516 count = 0; 1517 while((c = getc(fin)) != EOF) { 1518 if (c == '\n') /* will get expanded to \r\n */ 1519 count++; 1520 count++; 1521 } 1522 (void) fclose(fin); 1523 1524 reply(213, "%qd", count); 1525 break; } 1526 default: 1527 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 1528 } 1529} 1530