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