1 /* $NetBSD: ftpcmd.y,v 1.39 1999/10/04 17:36:52 tron Exp $ */ 2 3 /* 4 * Copyright (c) 1985, 1988, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 36 */ 37 38 /* 39 * Grammar for FTP commands. 40 * See RFC 959. 41 */ 42 43 %{ 44 #include <sys/cdefs.h> 45 46 #ifndef lint 47 #if 0 48 static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; 49 #else 50 __RCSID("$NetBSD: ftpcmd.y,v 1.39 1999/10/04 17:36:52 tron 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 #include <arpa/inet.h> 61 62 #include <ctype.h> 63 #include <errno.h> 64 #include <glob.h> 65 #include <pwd.h> 66 #include <setjmp.h> 67 #include <signal.h> 68 #include <stdio.h> 69 #include <stdlib.h> 70 #include <string.h> 71 #include <syslog.h> 72 #include <time.h> 73 #include <tzfile.h> 74 #include <unistd.h> 75 #include <netdb.h> 76 77 #ifdef KERBEROS5 78 #include <krb5/krb5.h> 79 #endif 80 81 #include "extern.h" 82 83 off_t restart_point; 84 85 static int cmd_type; 86 static int cmd_form; 87 static int cmd_bytesz; 88 char cbuf[512]; 89 char *fromname; 90 int hasyyerrored; 91 92 extern jmp_buf errcatch; 93 94 %} 95 96 %union { 97 int i; 98 char *s; 99 } 100 101 %token 102 A B C E F I 103 L N P R S T 104 ALL 105 106 SP CRLF COMMA 107 108 USER PASS ACCT CWD CDUP SMNT 109 QUIT REIN PORT PASV TYPE STRU 110 MODE RETR STOR STOU APPE ALLO 111 REST RNFR RNTO ABOR DELE RMD 112 MKD PWD LIST NLST SITE SYST 113 STAT HELP NOOP 114 115 AUTH ADAT PROT PBSZ CCC MIC 116 CONF ENC 117 118 FEAT OPTS 119 120 SIZE MDTM 121 122 LPRT LPSV EPRT EPSV 123 124 MAIL MLFL MRCP MRSQ MSAM MSND 125 MSOM 126 127 UMASK IDLE CHMOD 128 129 LEXERR 130 131 %token <s> STRING 132 %token <s> ALL 133 %token <i> NUMBER 134 135 %type <i> check_login check_modify octal_number byte_size 136 %type <i> struct_code mode_code type_code form_code decimal_integer 137 %type <s> pathstring pathname password username 138 %type <s> mechanism_name base64data prot_code 139 140 %start cmd_list 141 142 %% 143 144 cmd_list 145 : /* empty */ 146 147 | cmd_list cmd 148 { 149 fromname = NULL; 150 restart_point = (off_t) 0; 151 } 152 153 | cmd_list rcmd 154 155 ; 156 157 cmd 158 /* RFC 959 */ 159 : USER SP username CRLF 160 { 161 user($3); 162 free($3); 163 } 164 165 | PASS SP password CRLF 166 { 167 pass($3); 168 free($3); 169 } 170 171 | CWD check_login CRLF 172 { 173 if ($2) 174 cwd(pw->pw_dir); 175 } 176 177 | CWD check_login SP pathname CRLF 178 { 179 if ($2 && $4 != NULL) 180 cwd($4); 181 if ($4 != NULL) 182 free($4); 183 } 184 185 | CDUP check_login CRLF 186 { 187 if ($2) 188 cwd(".."); 189 } 190 191 | QUIT CRLF 192 { 193 if (logged_in) { 194 lreply(221, ""); 195 lreply(0, 196 "Data traffic for this session was %qd byte%s in %qd file%s.", 197 (qdfmt_t)total_data, PLURAL(total_data), 198 (qdfmt_t)total_files, PLURAL(total_files)); 199 lreply(0, 200 "Total traffic for this session was %qd byte%s in %qd transfer%s.", 201 (qdfmt_t)total_bytes, PLURAL(total_bytes), 202 (qdfmt_t)total_xfers, PLURAL(total_xfers)); 203 } 204 reply(221, 205 "Thank you for using the FTP service on %s.", 206 hostname); 207 if (logged_in) { 208 syslog(LOG_INFO, 209 "Data traffic: %qd byte%s in %qd file%s", 210 (qdfmt_t)total_data, PLURAL(total_data), 211 (qdfmt_t)total_files, PLURAL(total_files)); 212 syslog(LOG_INFO, 213 "Total traffic: %qd byte%s in %qd transfer%s", 214 (qdfmt_t)total_bytes, PLURAL(total_bytes), 215 (qdfmt_t)total_xfers, PLURAL(total_xfers)); 216 } 217 218 dologout(0); 219 } 220 221 | PORT check_login SP host_port CRLF 222 { 223 if ($2) { 224 /* be paranoid, if told so */ 225 if (curclass.checkportcmd && 226 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 227 memcmp(&data_dest.su_sin.sin_addr, 228 &his_addr.su_sin.sin_addr, 229 sizeof(data_dest.su_sin.sin_addr)) != 0)) { 230 reply(500, 231 "Illegal PORT command rejected"); 232 } else if (epsvall) { 233 reply(501, "PORT disallowed after EPSV ALL"); 234 } else { 235 usedefault = 0; 236 if (pdata >= 0) { 237 (void) close(pdata); 238 pdata = -1; 239 } 240 reply(200, "PORT command successful."); 241 } 242 243 } 244 } 245 246 | LPRT check_login SP host_long_port4 CRLF 247 { 248 /* reject invalid host_long_port4 */ 249 if (data_dest.su_family != AF_INET) { 250 reply(500, "Illegal LPRT command rejected"); 251 return (NULL); 252 } 253 /* be paranoid, if told so */ 254 if (curclass.checkportcmd && 255 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 256 memcmp(&data_dest.su_sin.sin_addr, 257 &his_addr.su_sin.sin_addr, 258 sizeof(data_dest.su_sin.sin_addr)) != 0)) { 259 reply(500, "Illegal LPRT command rejected"); 260 return (NULL); 261 } 262 if (epsvall) 263 reply(501, "LPRT disallowed after EPSV ALL"); 264 else { 265 usedefault = 0; 266 if (pdata >= 0) { 267 (void) close(pdata); 268 pdata = -1; 269 } 270 reply(200, "LPRT command successful."); 271 } 272 } 273 274 | LPRT check_login SP host_long_port6 CRLF 275 { 276 /* reject invalid host_long_port6 */ 277 if (data_dest.su_family != AF_INET6) { 278 reply(500, "Illegal LPRT command rejected"); 279 return (NULL); 280 } 281 /* be paranoid, if told so */ 282 if (curclass.checkportcmd && 283 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 284 memcmp(&data_dest.su_sin6.sin6_addr, 285 &his_addr.su_sin6.sin6_addr, 286 sizeof(data_dest.su_sin6.sin6_addr)) != 0)) { 287 reply(500, "Illegal LPRT command rejected"); 288 return (NULL); 289 } 290 if (epsvall) 291 reply(501, "LPRT disallowed after EPSV ALL"); 292 else { 293 usedefault = 0; 294 if (pdata >= 0) { 295 (void) close(pdata); 296 pdata = -1; 297 } 298 reply(200, "LPRT command successful."); 299 } 300 } 301 302 | EPRT check_login SP STRING CRLF 303 { 304 char *tmp = NULL; 305 char *result[3]; 306 char *p, *q; 307 char delim; 308 struct addrinfo hints; 309 struct addrinfo *res; 310 int i; 311 312 if (epsvall) { 313 reply(501, "EPRT disallowed after EPSV ALL"); 314 goto eprt_done; 315 } 316 usedefault = 0; 317 if (pdata >= 0) { 318 (void) close(pdata); 319 pdata = -1; 320 } 321 322 /*XXX checks for login */ 323 324 tmp = strdup($4); 325 if (!tmp) { 326 fatal("not enough core."); 327 /*NOTREACHED*/ 328 } 329 p = tmp; 330 delim = p[0]; 331 p++; 332 memset(result, 0, sizeof(result)); 333 for (i = 0; i < 3; i++) { 334 q = strchr(p, delim); 335 if (!q || *q != delim) { 336 parsefail: 337 reply(500, "Invalid argument, rejected."); 338 if (tmp) 339 free(tmp); 340 usedefault = 1; 341 goto eprt_done; 342 } 343 *q++ = '\0'; 344 result[i] = p; 345 p = q; 346 } 347 348 /* some more sanity check */ 349 p = result[0]; 350 while (*p) { 351 if (!isdigit(*p)) 352 goto parsefail; 353 p++; 354 } 355 p = result[2]; 356 while (*p) { 357 if (!isdigit(*p)) 358 goto parsefail; 359 p++; 360 } 361 362 memset(&hints, 0, sizeof(hints)); 363 if (atoi(result[0]) == 1) 364 hints.ai_family = PF_INET; 365 if (atoi(result[0]) == 2) 366 hints.ai_family = PF_INET6; 367 else 368 hints.ai_family = PF_UNSPEC; /*XXX*/ 369 hints.ai_socktype = SOCK_STREAM; 370 if (getaddrinfo(result[1], result[2], &hints, &res)) 371 goto parsefail; 372 memcpy(&data_dest, res->ai_addr, res->ai_addrlen); 373 if (his_addr.su_family == AF_INET6 374 && data_dest.su_family == AF_INET6) { 375 /* XXX more sanity checks! */ 376 data_dest.su_sin6.sin6_scope_id = 377 his_addr.su_sin6.sin6_scope_id; 378 } 379 /* be paranoid, if told so */ 380 if (curclass.checkportcmd) { 381 int fail; 382 fail = 0; 383 if (ntohs(data_dest.su_port) < IPPORT_RESERVED) 384 fail++; 385 if (data_dest.su_family != his_addr.su_family) 386 fail++; 387 if (data_dest.su_len != his_addr.su_len) 388 fail++; 389 switch (data_dest.su_family) { 390 case AF_INET: 391 fail += memcmp(&data_dest.su_sin.sin_addr, 392 &his_addr.su_sin.sin_addr, 393 sizeof(data_dest.su_sin.sin_addr)); 394 break; 395 case AF_INET6: 396 fail += memcmp(&data_dest.su_sin6.sin6_addr, 397 &his_addr.su_sin6.sin6_addr, 398 sizeof(data_dest.su_sin6.sin6_addr)); 399 break; 400 default: 401 fail++; 402 } 403 if (fail) { 404 reply(500, 405 "Illegal EPRT command rejected"); 406 return (NULL); 407 } 408 } 409 free(tmp); 410 tmp = NULL; 411 if (pdata >= 0) { 412 (void) close(pdata); 413 pdata = -1; 414 } 415 reply(200, "EPRT command successful."); 416 eprt_done:; 417 } 418 419 | PASV check_login CRLF 420 { 421 if (curclass.passive) { 422 passive(); 423 } else { 424 reply(500, "PASV mode not available."); 425 } 426 } 427 428 | LPSV CRLF 429 { 430 if (epsvall) 431 reply(501, "LPSV disallowed after EPSV ALL"); 432 else 433 long_passive("LPSV", PF_UNSPEC); 434 } 435 436 | EPSV SP NUMBER CRLF 437 { 438 int pf; 439 switch ($3) { 440 case 1: 441 pf = PF_INET; 442 break; 443 case 2: 444 pf = PF_INET6; 445 break; 446 default: 447 pf = -1; /*junk*/ 448 break; 449 } 450 long_passive("EPSV", pf); 451 } 452 453 | EPSV SP ALL CRLF 454 { 455 if (!logged_in) { 456 syslog(LOG_NOTICE, "long passive but not logged in"); 457 reply(503, "Login with USER first."); 458 } else { 459 reply(200, "EPSV ALL command successful."); 460 epsvall++; 461 } 462 } 463 464 | EPSV CRLF 465 { 466 long_passive("EPSV", PF_UNSPEC); 467 } 468 469 | TYPE SP type_code CRLF 470 { 471 switch (cmd_type) { 472 473 case TYPE_A: 474 if (cmd_form == FORM_N) { 475 reply(200, "Type set to A."); 476 type = cmd_type; 477 form = cmd_form; 478 } else 479 reply(504, "Form must be N."); 480 break; 481 482 case TYPE_E: 483 reply(504, "Type E not implemented."); 484 break; 485 486 case TYPE_I: 487 reply(200, "Type set to I."); 488 type = cmd_type; 489 break; 490 491 case TYPE_L: 492 #if NBBY == 8 493 if (cmd_bytesz == 8) { 494 reply(200, 495 "Type set to L (byte size 8)."); 496 type = cmd_type; 497 } else 498 reply(504, "Byte size must be 8."); 499 #else /* NBBY == 8 */ 500 UNIMPLEMENTED for NBBY != 8 501 #endif /* NBBY == 8 */ 502 } 503 } 504 505 | STRU SP struct_code CRLF 506 { 507 switch ($3) { 508 509 case STRU_F: 510 reply(200, "STRU F ok."); 511 break; 512 513 default: 514 reply(504, "Unimplemented STRU type."); 515 } 516 } 517 518 | MODE SP mode_code CRLF 519 { 520 switch ($3) { 521 522 case MODE_S: 523 reply(200, "MODE S ok."); 524 break; 525 526 default: 527 reply(502, "Unimplemented MODE type."); 528 } 529 } 530 531 | RETR check_login SP pathname CRLF 532 { 533 if ($2 && $4 != NULL) 534 retrieve(NULL, $4); 535 if ($4 != NULL) 536 free($4); 537 } 538 539 | STOR check_login SP pathname CRLF 540 { 541 if ($2 && $4 != NULL) 542 store($4, "w", 0); 543 if ($4 != NULL) 544 free($4); 545 } 546 547 | STOU check_login SP pathname CRLF 548 { 549 if ($2 && $4 != NULL) 550 store($4, "w", 1); 551 if ($4 != NULL) 552 free($4); 553 } 554 555 | APPE check_login SP pathname CRLF 556 { 557 if ($2 && $4 != NULL) 558 store($4, "a", 0); 559 if ($4 != NULL) 560 free($4); 561 } 562 563 | ALLO SP NUMBER CRLF 564 { 565 reply(202, "ALLO command ignored."); 566 } 567 568 | ALLO SP NUMBER SP R SP NUMBER CRLF 569 { 570 reply(202, "ALLO command ignored."); 571 } 572 573 | RNTO SP pathname CRLF 574 { 575 if (fromname) { 576 renamecmd(fromname, $3); 577 free(fromname); 578 fromname = NULL; 579 } else { 580 reply(503, "Bad sequence of commands."); 581 } 582 free($3); 583 } 584 585 | ABOR CRLF 586 { 587 reply(225, "ABOR command successful."); 588 } 589 590 | DELE check_modify SP pathname CRLF 591 { 592 if ($2 && $4 != NULL) 593 delete($4); 594 if ($4 != NULL) 595 free($4); 596 } 597 598 | RMD check_modify SP pathname CRLF 599 { 600 if ($2 && $4 != NULL) 601 removedir($4); 602 if ($4 != NULL) 603 free($4); 604 } 605 606 | MKD check_modify SP pathname CRLF 607 { 608 if ($2 && $4 != NULL) 609 makedir($4); 610 if ($4 != NULL) 611 free($4); 612 } 613 614 | PWD check_login CRLF 615 { 616 if ($2) 617 pwd(); 618 } 619 620 | LIST check_login CRLF 621 { 622 if ($2) 623 retrieve("/bin/ls -lgA", ""); 624 } 625 626 | LIST check_login SP pathname CRLF 627 { 628 if ($2 && $4 != NULL) 629 retrieve("/bin/ls -lgA %s", $4); 630 if ($4 != NULL) 631 free($4); 632 } 633 634 | NLST check_login CRLF 635 { 636 if ($2) 637 send_file_list("."); 638 } 639 640 | NLST check_login SP STRING CRLF 641 { 642 if ($2 && $4 != NULL) 643 send_file_list($4); 644 if ($4 != NULL) 645 free($4); 646 } 647 648 | SITE SP HELP CRLF 649 { 650 help(sitetab, NULL); 651 } 652 653 | SITE SP HELP SP STRING CRLF 654 { 655 help(sitetab, $5); 656 } 657 658 | SITE SP UMASK check_login CRLF 659 { 660 int oldmask; 661 662 if ($4) { 663 oldmask = umask(0); 664 (void) umask(oldmask); 665 reply(200, "Current UMASK is %03o", oldmask); 666 } 667 } 668 669 | SITE SP UMASK check_modify SP octal_number CRLF 670 { 671 int oldmask; 672 673 if ($4) { 674 if (($6 == -1) || ($6 > 0777)) { 675 reply(501, "Bad UMASK value"); 676 } else { 677 oldmask = umask($6); 678 reply(200, 679 "UMASK set to %03o (was %03o)", 680 $6, oldmask); 681 } 682 } 683 } 684 685 | SITE SP CHMOD check_modify SP octal_number SP pathname CRLF 686 { 687 if ($4 && ($8 != NULL)) { 688 if ($6 > 0777) 689 reply(501, 690 "CHMOD: Mode value must be between 0 and 0777"); 691 else if (chmod($8, $6) < 0) 692 perror_reply(550, $8); 693 else 694 reply(200, "CHMOD command successful."); 695 } 696 if ($8 != NULL) 697 free($8); 698 } 699 700 | SITE SP IDLE CRLF 701 { 702 reply(200, 703 "Current IDLE time limit is %d seconds; max %d", 704 curclass.timeout, curclass.maxtimeout); 705 } 706 707 | SITE SP IDLE SP NUMBER CRLF 708 { 709 if ($5 < 30 || $5 > curclass.maxtimeout) { 710 reply(501, 711 "IDLE time limit must be between 30 and %d seconds", 712 curclass.maxtimeout); 713 } else { 714 curclass.timeout = $5; 715 (void) alarm(curclass.timeout); 716 reply(200, 717 "IDLE time limit set to %d seconds", 718 curclass.timeout); 719 } 720 } 721 722 | SYST CRLF 723 { 724 reply(215, "UNIX Type: L%d %s", NBBY, version); 725 } 726 727 | STAT check_login SP pathname CRLF 728 { 729 if ($2 && $4 != NULL) 730 statfilecmd($4); 731 if ($4 != NULL) 732 free($4); 733 } 734 735 | STAT CRLF 736 { 737 statcmd(); 738 } 739 740 | HELP CRLF 741 { 742 help(cmdtab, NULL); 743 } 744 745 | HELP SP STRING CRLF 746 { 747 char *cp = $3; 748 749 if (strncasecmp(cp, "SITE", 4) == 0) { 750 cp = $3 + 4; 751 if (*cp == ' ') 752 cp++; 753 if (*cp) 754 help(sitetab, cp); 755 else 756 help(sitetab, NULL); 757 } else 758 help(cmdtab, $3); 759 } 760 761 | NOOP CRLF 762 { 763 reply(200, "NOOP command successful."); 764 } 765 766 /* RFC 2228 */ 767 | AUTH SP mechanism_name CRLF 768 { 769 reply(502, "RFC 2228 authentication not implemented."); 770 } 771 772 | ADAT SP base64data CRLF 773 { 774 reply(503, 775 "Please set authentication state with AUTH."); 776 } 777 778 | PROT SP prot_code CRLF 779 { 780 reply(503, 781 "Please set protection buffer size with PBSZ."); 782 } 783 784 | PBSZ SP decimal_integer CRLF 785 { 786 reply(503, 787 "Please set authentication state with AUTH."); 788 } 789 790 | CCC CRLF 791 { 792 reply(533, "No protection enabled."); 793 } 794 795 | MIC SP base64data CRLF 796 { 797 reply(502, "RFC 2228 authentication not implemented."); 798 } 799 800 | CONF SP base64data CRLF 801 { 802 reply(502, "RFC 2228 authentication not implemented."); 803 } 804 805 | ENC SP base64data CRLF 806 { 807 reply(502, "RFC 2228 authentication not implemented."); 808 } 809 810 /* RFC 2389 */ 811 | FEAT CRLF 812 { 813 lreply(211, "Features supported"); 814 lreply(-1, " MDTM"); 815 lreply(-1, " REST STREAM"); 816 lreply(-1, " SIZE"); 817 reply(211, "End"); 818 } 819 820 | OPTS SP STRING CRLF 821 { 822 823 opts($3); 824 } 825 826 827 /* BSD extensions */ 828 829 /* 830 * SIZE is not in RFC 959, but Postel has blessed it and 831 * it will be in the updated RFC. 832 * 833 * Return size of file in a format suitable for 834 * using with RESTART (we just count bytes). 835 */ 836 | SIZE check_login SP pathname CRLF 837 { 838 if ($2 && $4 != NULL) 839 sizecmd($4); 840 if ($4 != NULL) 841 free($4); 842 } 843 844 /* 845 * MDTM is not in RFC 959, but Postel has blessed it and 846 * it will be in the updated RFC. 847 * 848 * Return modification time of file as an ISO 3307 849 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 850 * where xxx is the fractional second (of any precision, 851 * not necessarily 3 digits) 852 */ 853 | MDTM check_login SP pathname CRLF 854 { 855 if ($2 && $4 != NULL) { 856 struct stat stbuf; 857 if (stat($4, &stbuf) < 0) 858 perror_reply(550, $4); 859 else if (!S_ISREG(stbuf.st_mode)) { 860 reply(550, "%s: not a plain file.", $4); 861 } else { 862 struct tm *t; 863 t = gmtime(&stbuf.st_mtime); 864 reply(213, 865 "%04d%02d%02d%02d%02d%02d", 866 TM_YEAR_BASE + t->tm_year, 867 t->tm_mon+1, t->tm_mday, 868 t->tm_hour, t->tm_min, t->tm_sec); 869 } 870 } 871 if ($4 != NULL) 872 free($4); 873 } 874 875 | error CRLF 876 { 877 yyerrok; 878 } 879 ; 880 881 rcmd 882 : REST SP byte_size CRLF 883 { 884 fromname = NULL; 885 restart_point = $3; /* XXX $3 is only "int" */ 886 reply(350, "Restarting at %qd. %s", 887 (qdfmt_t)restart_point, 888 "Send STORE or RETRIEVE to initiate transfer."); 889 } 890 | RNFR check_modify SP pathname CRLF 891 { 892 restart_point = (off_t) 0; 893 if ($2 && $4) { 894 fromname = renamefrom($4); 895 if (fromname == NULL && $4) { 896 free($4); 897 } 898 } 899 } 900 ; 901 902 username 903 : STRING 904 ; 905 906 password 907 : /* empty */ 908 { 909 $$ = (char *)calloc(1, sizeof(char)); 910 } 911 912 | STRING 913 ; 914 915 byte_size 916 : NUMBER 917 ; 918 919 host_port 920 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 921 NUMBER COMMA NUMBER 922 { 923 char *a, *p; 924 925 data_dest.su_len = sizeof(struct sockaddr_in); 926 data_dest.su_family = AF_INET; 927 p = (char *)&data_dest.su_sin.sin_port; 928 p[0] = $9; p[1] = $11; 929 a = (char *)&data_dest.su_sin.sin_addr; 930 a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 931 } 932 ; 933 934 host_long_port4 935 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 936 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 937 NUMBER 938 { 939 char *a, *p; 940 941 data_dest.su_sin.sin_len = 942 sizeof(struct sockaddr_in); 943 data_dest.su_family = AF_INET; 944 p = (char *)&data_dest.su_port; 945 p[0] = $15; p[1] = $17; 946 a = (char *)&data_dest.su_sin.sin_addr; 947 a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; 948 949 /* reject invalid LPRT command */ 950 if ($1 != 4 || $3 != 4 || $13 != 2) 951 memset(&data_dest, 0, sizeof(data_dest)); 952 } 953 ; 954 955 host_long_port6 956 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 957 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 958 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 959 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 960 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 961 NUMBER 962 { 963 char *a, *p; 964 965 data_dest.su_sin6.sin6_len = 966 sizeof(struct sockaddr_in6); 967 data_dest.su_family = AF_INET6; 968 p = (char *)&data_dest.su_port; 969 p[0] = $39; p[1] = $41; 970 a = (char *)&data_dest.su_sin6.sin6_addr; 971 a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; 972 a[4] = $13; a[5] = $15; a[6] = $17; a[7] = $19; 973 a[8] = $21; a[9] = $23; a[10] = $25; a[11] = $27; 974 a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35; 975 if (his_addr.su_family == AF_INET6) { 976 /* XXX more sanity checks! */ 977 data_dest.su_sin6.sin6_scope_id = 978 his_addr.su_sin6.sin6_scope_id; 979 } 980 981 /* reject invalid LPRT command */ 982 if ($1 != 6 || $3 != 16 || $37 != 2) 983 memset(&data_dest, 0, sizeof(data_dest)); 984 } 985 ; 986 987 form_code 988 : N 989 { 990 $$ = FORM_N; 991 } 992 993 | T 994 { 995 $$ = FORM_T; 996 } 997 998 | C 999 { 1000 $$ = FORM_C; 1001 } 1002 ; 1003 1004 type_code 1005 : A 1006 { 1007 cmd_type = TYPE_A; 1008 cmd_form = FORM_N; 1009 } 1010 1011 | A SP form_code 1012 { 1013 cmd_type = TYPE_A; 1014 cmd_form = $3; 1015 } 1016 1017 | E 1018 { 1019 cmd_type = TYPE_E; 1020 cmd_form = FORM_N; 1021 } 1022 1023 | E SP form_code 1024 { 1025 cmd_type = TYPE_E; 1026 cmd_form = $3; 1027 } 1028 1029 | I 1030 { 1031 cmd_type = TYPE_I; 1032 } 1033 1034 | L 1035 { 1036 cmd_type = TYPE_L; 1037 cmd_bytesz = NBBY; 1038 } 1039 1040 | L SP byte_size 1041 { 1042 cmd_type = TYPE_L; 1043 cmd_bytesz = $3; 1044 } 1045 1046 /* this is for a bug in the BBN ftp */ 1047 | L byte_size 1048 { 1049 cmd_type = TYPE_L; 1050 cmd_bytesz = $2; 1051 } 1052 ; 1053 1054 struct_code 1055 : F 1056 { 1057 $$ = STRU_F; 1058 } 1059 1060 | R 1061 { 1062 $$ = STRU_R; 1063 } 1064 1065 | P 1066 { 1067 $$ = STRU_P; 1068 } 1069 ; 1070 1071 mode_code 1072 : S 1073 { 1074 $$ = MODE_S; 1075 } 1076 1077 | B 1078 { 1079 $$ = MODE_B; 1080 } 1081 1082 | C 1083 { 1084 $$ = MODE_C; 1085 } 1086 ; 1087 1088 pathname 1089 : pathstring 1090 { 1091 /* 1092 * Problem: this production is used for all pathname 1093 * processing, but only gives a 550 error reply. 1094 * This is a valid reply in some cases but not in 1095 * others. 1096 */ 1097 if (logged_in && $1 && *$1 == '~') { 1098 glob_t gl; 1099 int flags = 1100 GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE; 1101 1102 if ($1[1] == '\0') 1103 $$ = xstrdup(pw->pw_dir); 1104 else { 1105 memset(&gl, 0, sizeof(gl)); 1106 if (glob($1, flags, NULL, &gl) || 1107 gl.gl_pathc == 0) { 1108 reply(550, "not found"); 1109 $$ = NULL; 1110 } else 1111 $$ = xstrdup(gl.gl_pathv[0]); 1112 globfree(&gl); 1113 } 1114 free($1); 1115 } else 1116 $$ = $1; 1117 } 1118 ; 1119 1120 pathstring 1121 : STRING 1122 ; 1123 1124 octal_number 1125 : NUMBER 1126 { 1127 int ret, dec, multby, digit; 1128 1129 /* 1130 * Convert a number that was read as decimal number 1131 * to what it would be if it had been read as octal. 1132 */ 1133 dec = $1; 1134 multby = 1; 1135 ret = 0; 1136 while (dec) { 1137 digit = dec%10; 1138 if (digit > 7) { 1139 ret = -1; 1140 break; 1141 } 1142 ret += digit * multby; 1143 multby *= 8; 1144 dec /= 10; 1145 } 1146 $$ = ret; 1147 } 1148 ; 1149 1150 mechanism_name 1151 : STRING 1152 ; 1153 1154 base64data 1155 : STRING 1156 ; 1157 1158 prot_code 1159 : STRING 1160 ; 1161 1162 decimal_integer 1163 : NUMBER 1164 ; 1165 1166 check_login 1167 : /* empty */ 1168 { 1169 if (logged_in) 1170 $$ = 1; 1171 else { 1172 reply(530, "Please login with USER and PASS."); 1173 $$ = 0; 1174 hasyyerrored = 1; 1175 } 1176 } 1177 ; 1178 1179 check_modify 1180 : /* empty */ 1181 { 1182 if (logged_in) { 1183 if (curclass.modify) 1184 $$ = 1; 1185 else { 1186 reply(502, 1187 "No permission to use this command."); 1188 $$ = 0; 1189 hasyyerrored = 1; 1190 } 1191 } else { 1192 reply(530, "Please login with USER and PASS."); 1193 $$ = 0; 1194 hasyyerrored = 1; 1195 } 1196 } 1197 1198 %% 1199 1200 #define CMD 0 /* beginning of command */ 1201 #define ARGS 1 /* expect miscellaneous arguments */ 1202 #define STR1 2 /* expect SP followed by STRING */ 1203 #define STR2 3 /* expect STRING */ 1204 #define OSTR 4 /* optional SP then STRING */ 1205 #define ZSTR1 5 /* SP then optional STRING */ 1206 #define ZSTR2 6 /* optional STRING after SP */ 1207 #define SITECMD 7 /* SITE command */ 1208 #define NSTR 8 /* Number followed by a string */ 1209 #define NOARGS 9 /* No arguments allowed */ 1210 1211 struct tab { 1212 char *name; 1213 short token; 1214 short state; 1215 short implemented; /* 1 if command is implemented */ 1216 short hasopts; /* 1 if command takes options */ 1217 char *help; 1218 char *options; 1219 }; 1220 1221 struct tab cmdtab[] = { 1222 /* From RFC 959, in order defined (5.3.1) */ 1223 { "USER", USER, STR1, 1, 0, "<sp> username" }, 1224 { "PASS", PASS, ZSTR1, 1, 0, "<sp> password" }, 1225 { "ACCT", ACCT, STR1, 0, 0, "(specify account)" }, 1226 { "CWD", CWD, OSTR, 1, 0, "[ <sp> directory-name ]" }, 1227 { "CDUP", CDUP, NOARGS, 1, 0, "(change to parent directory)" }, 1228 { "SMNT", SMNT, ARGS, 0, 0, "(structure mount)" }, 1229 { "QUIT", QUIT, NOARGS, 1, 0, "(terminate service)" }, 1230 { "REIN", REIN, NOARGS, 0, 0, "(reinitialize server state)" }, 1231 { "PORT", PORT, ARGS, 1, 0, "<sp> b0, b1, b2, b3, b4" }, 1232 { "LPRT", LPRT, ARGS, 1, 0, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 1233 { "EPRT", EPRT, STR1, 1, 0, "<sp> |af|addr|port|" }, 1234 { "PASV", PASV, NOARGS, 1, 0, "(set server in passive mode)" }, 1235 { "LPSV", LPSV, ARGS, 1, 0, "(set server in passive mode)" }, 1236 { "EPSV", EPSV, ARGS, 1, 0, "[<sp> af|ALL]" }, 1237 { "TYPE", TYPE, ARGS, 1, 0, "<sp> [ A | E | I | L ]" }, 1238 { "STRU", STRU, ARGS, 1, 0, "(specify file structure)" }, 1239 { "MODE", MODE, ARGS, 1, 0, "(specify transfer mode)" }, 1240 { "RETR", RETR, STR1, 1, 0, "<sp> file-name" }, 1241 { "STOR", STOR, STR1, 1, 0, "<sp> file-name" }, 1242 { "STOU", STOU, STR1, 1, 0, "<sp> file-name" }, 1243 { "APPE", APPE, STR1, 1, 0, "<sp> file-name" }, 1244 { "ALLO", ALLO, ARGS, 1, 0, "allocate storage (vacuously)" }, 1245 { "REST", REST, ARGS, 1, 0, "<sp> offset (restart command)" }, 1246 { "RNFR", RNFR, STR1, 1, 0, "<sp> file-name" }, 1247 { "RNTO", RNTO, STR1, 1, 0, "<sp> file-name" }, 1248 { "ABOR", ABOR, NOARGS, 1, 0, "(abort operation)" }, 1249 { "DELE", DELE, STR1, 1, 0, "<sp> file-name" }, 1250 { "RMD", RMD, STR1, 1, 0, "<sp> path-name" }, 1251 { "MKD", MKD, STR1, 1, 0, "<sp> path-name" }, 1252 { "PWD", PWD, NOARGS, 1, 0, "(return current directory)" }, 1253 { "LIST", LIST, OSTR, 1, 0, "[ <sp> path-name ]" }, 1254 { "NLST", NLST, OSTR, 1, 0, "[ <sp> path-name ]" }, 1255 { "SITE", SITE, SITECMD, 1, 0, "site-cmd [ <sp> arguments ]" }, 1256 { "SYST", SYST, NOARGS, 1, 0, "(get type of operating system)" }, 1257 { "STAT", STAT, OSTR, 1, 0, "[ <sp> path-name ]" }, 1258 { "HELP", HELP, OSTR, 1, 0, "[ <sp> <string> ]" }, 1259 { "NOOP", NOOP, NOARGS, 1, 1, "" }, 1260 1261 /* From RFC 2228, in order defined */ 1262 { "AUTH", AUTH, STR1, 1, 0, "<sp> mechanism-name" }, 1263 { "ADAT", ADAT, STR1, 1, 0, "<sp> base-64-data" }, 1264 { "PROT", PROT, STR1, 1, 0, "<sp> prot-code" }, 1265 { "PBSZ", PBSZ, ARGS, 1, 0, "<sp> decimal-integer" }, 1266 { "CCC", CCC, NOARGS, 1, 0, "(Disable data protection)" }, 1267 { "MIC", MIC, STR1, 1, 0, "<sp> base64data" }, 1268 { "CONF", CONF, STR1, 1, 0, "<sp> base64data" }, 1269 { "ENC", ENC, STR1, 1, 0, "<sp> base64data" }, 1270 1271 /* From RFC 2389, in order defined */ 1272 { "FEAT", FEAT, NOARGS, 1, 0, "(display extended features)" }, 1273 { "OPTS", OPTS, STR1, 1, 0, "<sp> command [ <sp> options ]" }, 1274 1275 /* Non standardized extensions */ 1276 { "SIZE", SIZE, OSTR, 1, 0, "<sp> path-name" }, 1277 { "MDTM", MDTM, OSTR, 1, 0, "<sp> path-name" }, 1278 1279 /* obsolete commands */ 1280 { "MAIL", MAIL, OSTR, 0, 0, "(mail to user)" }, 1281 { "MLFL", MLFL, OSTR, 0, 0, "(mail file)" }, 1282 { "MRCP", MRCP, STR1, 0, 0, "(mail recipient)" }, 1283 { "MRSQ", MRSQ, OSTR, 0, 0, "(mail recipient scheme question)" }, 1284 { "MSAM", MSAM, OSTR, 0, 0, "(mail send to terminal and mailbox)" }, 1285 { "MSND", MSND, OSTR, 0, 0, "(mail send to terminal)" }, 1286 { "MSOM", MSOM, OSTR, 0, 0, "(mail send to terminal or mailbox)" }, 1287 { "XCUP", CDUP, NOARGS, 1, 0, "(change to parent directory)" }, 1288 { "XCWD", CWD, OSTR, 1, 0, "[ <sp> directory-name ]" }, 1289 { "XMKD", MKD, STR1, 1, 0, "<sp> path-name" }, 1290 { "XPWD", PWD, NOARGS, 1, 0, "(return current directory)" }, 1291 { "XRMD", RMD, STR1, 1, 0, "<sp> path-name" }, 1292 1293 { NULL, 0, 0, 0, 0, 0 } 1294 }; 1295 1296 struct tab sitetab[] = { 1297 { "UMASK", UMASK, ARGS, 1, 0, "[ <sp> umask ]" }, 1298 { "IDLE", IDLE, ARGS, 1, 0, "[ <sp> maximum-idle-time ]" }, 1299 { "CHMOD", CHMOD, NSTR, 1, 0, "<sp> mode <sp> file-name" }, 1300 { "HELP", HELP, OSTR, 1, 0, "[ <sp> <string> ]" }, 1301 { NULL, 0, 0, 0, 0, 0 } 1302 }; 1303 1304 static void help __P((struct tab *, char *)); 1305 static struct tab *lookup __P((struct tab *, const char *)); 1306 static void opts __P((const char *)); 1307 static void sizecmd __P((char *)); 1308 static void toolong __P((int)); 1309 static int yylex __P((void)); 1310 1311 extern int epsvall; 1312 1313 static struct tab * 1314 lookup(p, cmd) 1315 struct tab *p; 1316 const char *cmd; 1317 { 1318 1319 for (; p->name != NULL; p++) 1320 if (strcasecmp(cmd, p->name) == 0) 1321 return (p); 1322 return (0); 1323 } 1324 1325 #include <arpa/telnet.h> 1326 1327 /* 1328 * getline - a hacked up version of fgets to ignore TELNET escape codes. 1329 */ 1330 char * 1331 getline(s, n, iop) 1332 char *s; 1333 int n; 1334 FILE *iop; 1335 { 1336 off_t b; 1337 int c; 1338 char *cs; 1339 1340 cs = s; 1341 /* tmpline may contain saved command from urgent mode interruption */ 1342 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 1343 *cs++ = tmpline[c]; 1344 if (tmpline[c] == '\n') { 1345 *cs++ = '\0'; 1346 if (debug) 1347 syslog(LOG_DEBUG, "command: %s", s); 1348 tmpline[0] = '\0'; 1349 return(s); 1350 } 1351 if (c == 0) 1352 tmpline[0] = '\0'; 1353 } 1354 while ((c = getc(iop)) != EOF) { 1355 total_bytes++; 1356 total_bytes_in++; 1357 c &= 0377; 1358 if (c == IAC) { 1359 if ((c = getc(iop)) != EOF) { 1360 total_bytes++; 1361 total_bytes_in++; 1362 c &= 0377; 1363 switch (c) { 1364 case WILL: 1365 case WONT: 1366 c = getc(iop); 1367 total_bytes++; 1368 total_bytes_in++; 1369 b = printf("%c%c%c", IAC, DONT, 0377&c); 1370 total_bytes += b; 1371 total_bytes_out += b; 1372 (void) fflush(stdout); 1373 continue; 1374 case DO: 1375 case DONT: 1376 c = getc(iop); 1377 total_bytes++; 1378 total_bytes_in++; 1379 b = printf("%c%c%c", IAC, WONT, 0377&c); 1380 total_bytes += b; 1381 total_bytes_out += b; 1382 (void) fflush(stdout); 1383 continue; 1384 case IAC: 1385 break; 1386 default: 1387 continue; /* ignore command */ 1388 } 1389 } 1390 } 1391 *cs++ = c; 1392 if (--n <= 0 || c == '\n') 1393 break; 1394 } 1395 if (c == EOF && cs == s) 1396 return (NULL); 1397 *cs++ = '\0'; 1398 if (debug) { 1399 if (!guest && strncasecmp("pass ", s, 5) == 0) { 1400 /* Don't syslog passwords */ 1401 syslog(LOG_DEBUG, "command: %.5s ???", s); 1402 } else { 1403 char *cp; 1404 int len; 1405 1406 /* Don't syslog trailing CR-LF */ 1407 len = strlen(s); 1408 cp = s + len - 1; 1409 while (cp >= s && (*cp == '\n' || *cp == '\r')) { 1410 --cp; 1411 --len; 1412 } 1413 syslog(LOG_DEBUG, "command: %.*s", len, s); 1414 } 1415 } 1416 return (s); 1417 } 1418 1419 static void 1420 toolong(signo) 1421 int signo; 1422 { 1423 1424 reply(421, 1425 "Timeout (%d seconds): closing control connection.", 1426 curclass.timeout); 1427 if (logging) 1428 syslog(LOG_INFO, "User %s timed out after %d seconds", 1429 (pw ? pw -> pw_name : "unknown"), curclass.timeout); 1430 dologout(1); 1431 } 1432 1433 static int 1434 yylex() 1435 { 1436 static int cpos, state; 1437 char *cp, *cp2; 1438 struct tab *p; 1439 int n; 1440 char c; 1441 1442 switch (state) { 1443 1444 case CMD: 1445 hasyyerrored = 0; 1446 (void) signal(SIGALRM, toolong); 1447 (void) alarm(curclass.timeout); 1448 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 1449 reply(221, "You could at least say goodbye."); 1450 dologout(0); 1451 } 1452 (void) alarm(0); 1453 #ifdef HASSETPROCTITLE 1454 if (strncasecmp(cbuf, "PASS", 4) != 0) 1455 setproctitle("%s: %s", proctitle, cbuf); 1456 #endif /* HASSETPROCTITLE */ 1457 if ((cp = strchr(cbuf, '\r'))) { 1458 *cp++ = '\n'; 1459 *cp = '\0'; 1460 } 1461 if ((cp = strpbrk(cbuf, " \n"))) 1462 cpos = cp - cbuf; 1463 if (cpos == 0) 1464 cpos = 4; 1465 c = cbuf[cpos]; 1466 cbuf[cpos] = '\0'; 1467 p = lookup(cmdtab, cbuf); 1468 cbuf[cpos] = c; 1469 if (p != NULL) { 1470 if (p->implemented == 0) { 1471 reply(502, "%s command not implemented.", 1472 p->name); 1473 hasyyerrored = 1; 1474 break; 1475 } 1476 state = p->state; 1477 yylval.s = p->name; 1478 return (p->token); 1479 } 1480 break; 1481 1482 case SITECMD: 1483 if (cbuf[cpos] == ' ') { 1484 cpos++; 1485 return (SP); 1486 } 1487 cp = &cbuf[cpos]; 1488 if ((cp2 = strpbrk(cp, " \n"))) 1489 cpos = cp2 - cbuf; 1490 c = cbuf[cpos]; 1491 cbuf[cpos] = '\0'; 1492 p = lookup(sitetab, cp); 1493 cbuf[cpos] = c; 1494 if (p != NULL) { 1495 if (p->implemented == 0) { 1496 reply(502, "SITE %s command not implemented.", 1497 p->name); 1498 hasyyerrored = 1; 1499 break; 1500 } 1501 state = p->state; 1502 yylval.s = p->name; 1503 return (p->token); 1504 } 1505 break; 1506 1507 case OSTR: 1508 if (cbuf[cpos] == '\n') { 1509 state = CMD; 1510 return (CRLF); 1511 } 1512 /* FALLTHROUGH */ 1513 1514 case STR1: 1515 case ZSTR1: 1516 dostr1: 1517 if (cbuf[cpos] == ' ') { 1518 cpos++; 1519 state = state == OSTR ? STR2 : state+1; 1520 return (SP); 1521 } 1522 break; 1523 1524 case ZSTR2: 1525 if (cbuf[cpos] == '\n') { 1526 state = CMD; 1527 return (CRLF); 1528 } 1529 /* FALLTHROUGH */ 1530 1531 case STR2: 1532 cp = &cbuf[cpos]; 1533 n = strlen(cp); 1534 cpos += n - 1; 1535 /* 1536 * Make sure the string is nonempty and \n terminated. 1537 */ 1538 if (n > 1 && cbuf[cpos] == '\n') { 1539 cbuf[cpos] = '\0'; 1540 yylval.s = xstrdup(cp); 1541 cbuf[cpos] = '\n'; 1542 state = ARGS; 1543 return (STRING); 1544 } 1545 break; 1546 1547 case NSTR: 1548 if (cbuf[cpos] == ' ') { 1549 cpos++; 1550 return (SP); 1551 } 1552 if (isdigit(cbuf[cpos])) { 1553 cp = &cbuf[cpos]; 1554 while (isdigit(cbuf[++cpos])) 1555 ; 1556 c = cbuf[cpos]; 1557 cbuf[cpos] = '\0'; 1558 yylval.i = atoi(cp); 1559 cbuf[cpos] = c; 1560 state = STR1; 1561 return (NUMBER); 1562 } 1563 state = STR1; 1564 goto dostr1; 1565 1566 case ARGS: 1567 if (isdigit(cbuf[cpos])) { 1568 cp = &cbuf[cpos]; 1569 while (isdigit(cbuf[++cpos])) 1570 ; 1571 c = cbuf[cpos]; 1572 cbuf[cpos] = '\0'; 1573 yylval.i = atoi(cp); 1574 cbuf[cpos] = c; 1575 return (NUMBER); 1576 } 1577 if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 1578 && !isalnum(cbuf[cpos + 3])) { 1579 yylval.s = xstrdup("ALL"); 1580 cpos += 3; 1581 return ALL; 1582 } 1583 switch (cbuf[cpos++]) { 1584 1585 case '\n': 1586 state = CMD; 1587 return (CRLF); 1588 1589 case ' ': 1590 return (SP); 1591 1592 case ',': 1593 return (COMMA); 1594 1595 case 'A': 1596 case 'a': 1597 return (A); 1598 1599 case 'B': 1600 case 'b': 1601 return (B); 1602 1603 case 'C': 1604 case 'c': 1605 return (C); 1606 1607 case 'E': 1608 case 'e': 1609 return (E); 1610 1611 case 'F': 1612 case 'f': 1613 return (F); 1614 1615 case 'I': 1616 case 'i': 1617 return (I); 1618 1619 case 'L': 1620 case 'l': 1621 return (L); 1622 1623 case 'N': 1624 case 'n': 1625 return (N); 1626 1627 case 'P': 1628 case 'p': 1629 return (P); 1630 1631 case 'R': 1632 case 'r': 1633 return (R); 1634 1635 case 'S': 1636 case 's': 1637 return (S); 1638 1639 case 'T': 1640 case 't': 1641 return (T); 1642 1643 } 1644 break; 1645 1646 case NOARGS: 1647 if (cbuf[cpos] == '\n') { 1648 state = CMD; 1649 return (CRLF); 1650 } 1651 c = cbuf[cpos]; 1652 cbuf[cpos] = '\0'; 1653 reply(501, "'%s' command does not take any arguments.", cbuf); 1654 hasyyerrored = 1; 1655 cbuf[cpos] = c; 1656 break; 1657 1658 default: 1659 fatal("Unknown state in scanner."); 1660 } 1661 yyerror(NULL); 1662 state = CMD; 1663 longjmp(errcatch, 0); 1664 /* NOTREACHED */ 1665 } 1666 1667 /* ARGSUSED */ 1668 void 1669 yyerror(s) 1670 char *s; 1671 { 1672 char *cp; 1673 1674 if (hasyyerrored) 1675 return; 1676 if ((cp = strchr(cbuf,'\n')) != NULL) 1677 *cp = '\0'; 1678 reply(500, "'%s': command not understood.", cbuf); 1679 hasyyerrored = 1; 1680 } 1681 1682 static void 1683 help(ctab, s) 1684 struct tab *ctab; 1685 char *s; 1686 { 1687 struct tab *c; 1688 int width, NCMDS; 1689 off_t b; 1690 char *type; 1691 1692 if (ctab == sitetab) 1693 type = "SITE "; 1694 else 1695 type = ""; 1696 width = 0, NCMDS = 0; 1697 for (c = ctab; c->name != NULL; c++) { 1698 int len = strlen(c->name); 1699 1700 if (len > width) 1701 width = len; 1702 NCMDS++; 1703 } 1704 width = (width + 8) &~ 7; 1705 if (s == 0) { 1706 int i, j, w; 1707 int columns, lines; 1708 1709 lreply(214, ""); 1710 lreply(0, "The following %scommands are recognized.", type); 1711 lreply(0, "(`-' = not implemented, `+' = supports options)"); 1712 columns = 76 / width; 1713 if (columns == 0) 1714 columns = 1; 1715 lines = (NCMDS + columns - 1) / columns; 1716 for (i = 0; i < lines; i++) { 1717 b = printf(" "); 1718 total_bytes += b; 1719 total_bytes_out += b; 1720 for (j = 0; j < columns; j++) { 1721 c = ctab + j * lines + i; 1722 b = printf("%s", c->name); 1723 total_bytes += b; 1724 total_bytes_out += b; 1725 w = strlen(c->name); 1726 if (! c->implemented) { 1727 putchar('-'); 1728 total_bytes++; 1729 total_bytes_out++; 1730 w++; 1731 } 1732 if (c->hasopts) { 1733 putchar('+'); 1734 total_bytes++; 1735 total_bytes_out++; 1736 w++; 1737 } 1738 if (c + lines >= &ctab[NCMDS]) 1739 break; 1740 while (w < width) { 1741 putchar(' '); 1742 total_bytes++; 1743 total_bytes_out++; 1744 w++; 1745 } 1746 } 1747 b = printf("\r\n"); 1748 total_bytes += b; 1749 total_bytes_out += b; 1750 } 1751 (void) fflush(stdout); 1752 reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1753 return; 1754 } 1755 c = lookup(ctab, s); 1756 if (c == (struct tab *)0) { 1757 reply(502, "Unknown command %s.", s); 1758 return; 1759 } 1760 if (c->implemented) 1761 reply(214, "Syntax: %s%s %s", type, c->name, c->help); 1762 else 1763 reply(214, "%s%-*s\t%s; not implemented.", type, width, 1764 c->name, c->help); 1765 } 1766 1767 static void 1768 sizecmd(filename) 1769 char *filename; 1770 { 1771 switch (type) { 1772 case TYPE_L: 1773 case TYPE_I: { 1774 struct stat stbuf; 1775 if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) 1776 reply(550, "%s: not a plain file.", filename); 1777 else 1778 reply(213, "%qu", (qufmt_t)stbuf.st_size); 1779 break; } 1780 case TYPE_A: { 1781 FILE *fin; 1782 int c; 1783 off_t count; 1784 struct stat stbuf; 1785 fin = fopen(filename, "r"); 1786 if (fin == NULL) { 1787 perror_reply(550, filename); 1788 return; 1789 } 1790 if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) { 1791 reply(550, "%s: not a plain file.", filename); 1792 (void) fclose(fin); 1793 return; 1794 } 1795 1796 count = 0; 1797 while((c=getc(fin)) != EOF) { 1798 if (c == '\n') /* will get expanded to \r\n */ 1799 count++; 1800 count++; 1801 } 1802 (void) fclose(fin); 1803 1804 reply(213, "%qd", (qdfmt_t)count); 1805 break; } 1806 default: 1807 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 1808 } 1809 } 1810 1811 static void 1812 opts(command) 1813 const char *command; 1814 { 1815 struct tab *c; 1816 char *ep; 1817 1818 if ((ep = strchr(command, ' ')) != NULL) 1819 *ep++ = '\0'; 1820 c = lookup(cmdtab, command); 1821 if (c == NULL) { 1822 reply(502, "Unknown command %s.", command); 1823 return; 1824 } 1825 if (c->implemented == 0) { 1826 reply(502, "%s command not implemented.", c->name); 1827 return; 1828 } 1829 if (c->hasopts == 0) { 1830 reply(501, "%s command does not support persistent options.", 1831 c->name); 1832 return; 1833 } 1834 1835 if (ep != NULL && *ep != '\0') { 1836 if (c->options != NULL) 1837 free(c->options); 1838 c->options = xstrdup(ep); 1839 } 1840 if (c->options != NULL) 1841 reply(200, "Options for %s are '%s'.", c->name, c->options); 1842 else 1843 reply(200, "No options defined for %s.", c->name); 1844 } 1845