ftpcmd.y revision 1.22
1/* $OpenBSD: ftpcmd.y,v 1.22 2000/11/13 15:39:08 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.22 2000/11/13 15:39:08 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_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 check_epsvall SP host_port CRLF 170 { 171 if ($2 && $3) { 172 if ($5) { 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 check_epsvall SP host_long_port4 CRLF 199 { 200 if ($2 && $3) { 201 /* reject invalid host_long_port4 */ 202 if ($5) { 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 check_epsvall SP host_long_port6 CRLF 218 { 219 if ($2 && $3) { 220 /* reject invalid host_long_port6 */ 221 if ($5) { 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 check_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 && $3) { /* 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($5); 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 check_epsvall CRLF 328 { 329 if ($2 && $3) 330 passive(); 331 } 332 | LPSV check_login check_epsvall CRLF 333 { 334 if ($2 && $3) 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_epsvall 1060 : /* empty */ 1061 { 1062 if (epsvall) { 1063 reply(501, "the command is disallowed " 1064 "after EPSV ALL"); 1065 usedefault = 1; 1066 $$ = 0; 1067 } else 1068 $$ = 1; 1069 } 1070 ; 1071 1072%% 1073 1074extern jmp_buf errcatch; 1075 1076#define CMD 0 /* beginning of command */ 1077#define ARGS 1 /* expect miscellaneous arguments */ 1078#define STR1 2 /* expect SP followed by STRING */ 1079#define STR2 3 /* expect STRING */ 1080#define OSTR 4 /* optional SP then STRING */ 1081#define ZSTR1 5 /* SP then optional STRING */ 1082#define ZSTR2 6 /* optional STRING after SP */ 1083#define SITECMD 7 /* SITE command */ 1084#define NSTR 8 /* Number followed by a string */ 1085 1086struct tab { 1087 char *name; 1088 short token; 1089 short state; 1090 short implemented; /* 1 if command is implemented */ 1091 char *help; 1092}; 1093 1094struct tab cmdtab[] = { /* In order defined in RFC 765 */ 1095 { "USER", USER, STR1, 1, "<sp> username" }, 1096 { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 1097 { "ACCT", ACCT, STR1, 0, "(specify account)" }, 1098 { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 1099 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 1100 { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 1101 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 1102 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 1103 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 1104 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 1105 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 1106 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 1107 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 1108 { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 1109 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 1110 { "RETR", RETR, STR1, 1, "<sp> file-name" }, 1111 { "STOR", STOR, STR1, 1, "<sp> file-name" }, 1112 { "APPE", APPE, STR1, 1, "<sp> file-name" }, 1113 { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 1114 { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 1115 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 1116 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 1117 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 1118 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 1119 { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 1120 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 1121 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 1122 { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 1123 { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 1124 { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 1125 { "DELE", DELE, STR1, 1, "<sp> file-name" }, 1126 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 1127 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 1128 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 1129 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 1130 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 1131 { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 1132 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 1133 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 1134 { "NOOP", NOOP, ARGS, 1, "" }, 1135 { "MKD", MKD, STR1, 1, "<sp> path-name" }, 1136 { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 1137 { "RMD", RMD, STR1, 1, "<sp> path-name" }, 1138 { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 1139 { "PWD", PWD, ARGS, 1, "(return current directory)" }, 1140 { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 1141 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 1142 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 1143 { "STOU", STOU, STR1, 1, "<sp> file-name" }, 1144 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 1145 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 1146 { NULL, 0, 0, 0, 0 } 1147}; 1148 1149struct tab sitetab[] = { 1150 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 1151 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 1152 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 1153 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 1154 { NULL, 0, 0, 0, 0 } 1155}; 1156 1157static void help __P((struct tab *, char *)); 1158static struct tab * 1159 lookup __P((struct tab *, char *)); 1160static void sizecmd __P((char *)); 1161static int yylex __P((void)); 1162 1163extern int epsvall; 1164 1165static struct tab * 1166lookup(p, cmd) 1167 struct tab *p; 1168 char *cmd; 1169{ 1170 1171 for (; p->name != NULL; p++) 1172 if (strcmp(cmd, p->name) == 0) 1173 return (p); 1174 return (0); 1175} 1176 1177#include <arpa/telnet.h> 1178 1179/* 1180 * getline - a hacked up version of fgets to ignore TELNET escape codes. 1181 */ 1182char * 1183getline(s, n, iop) 1184 char *s; 1185 int n; 1186 FILE *iop; 1187{ 1188 int c; 1189 register char *cs; 1190 1191 cs = s; 1192/* tmpline may contain saved command from urgent mode interruption */ 1193 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 1194 *cs++ = tmpline[c]; 1195 if (tmpline[c] == '\n') { 1196 *cs++ = '\0'; 1197 if (debug) 1198 syslog(LOG_DEBUG, "command: %s", s); 1199 tmpline[0] = '\0'; 1200 return(s); 1201 } 1202 if (c == 0) 1203 tmpline[0] = '\0'; 1204 } 1205 while ((c = getc(iop)) != EOF) { 1206 c &= 0377; 1207 if (c == IAC) { 1208 if ((c = getc(iop)) != EOF) { 1209 c &= 0377; 1210 switch (c) { 1211 case WILL: 1212 case WONT: 1213 c = getc(iop); 1214 printf("%c%c%c", IAC, DONT, 0377&c); 1215 (void) fflush(stdout); 1216 continue; 1217 case DO: 1218 case DONT: 1219 c = getc(iop); 1220 printf("%c%c%c", IAC, WONT, 0377&c); 1221 (void) fflush(stdout); 1222 continue; 1223 case IAC: 1224 break; 1225 default: 1226 continue; /* ignore command */ 1227 } 1228 } 1229 } 1230 *cs++ = c; 1231 if (--n <= 0 || c == '\n') 1232 break; 1233 } 1234 if (c == EOF && cs == s) 1235 return (NULL); 1236 *cs++ = '\0'; 1237 if (debug) { 1238 if (!guest && strncasecmp("pass ", s, 5) == 0) { 1239 /* Don't syslog passwords */ 1240 syslog(LOG_DEBUG, "command: %.5s ???", s); 1241 } else { 1242 register char *cp; 1243 register int len; 1244 1245 /* Don't syslog trailing CR-LF */ 1246 len = strlen(s); 1247 cp = s + len - 1; 1248 while (cp >= s && (*cp == '\n' || *cp == '\r')) { 1249 --cp; 1250 --len; 1251 } 1252 syslog(LOG_DEBUG, "command: %.*s", len, s); 1253 } 1254 } 1255 return (s); 1256} 1257 1258void 1259toolong(signo) 1260 int signo; 1261{ 1262 1263 reply(421, 1264 "Timeout (%d seconds): closing control connection.", timeout); 1265 if (logging) 1266 syslog(LOG_INFO, "User %s timed out after %d seconds", 1267 (pw ? pw -> pw_name : "unknown"), timeout); 1268 dologout(1); 1269} 1270 1271static int 1272yylex() 1273{ 1274 static int cpos, state; 1275 char *cp, *cp2; 1276 struct tab *p; 1277 int n; 1278 char c; 1279 1280 for (;;) { 1281 switch (state) { 1282 1283 case CMD: 1284 (void) signal(SIGALRM, toolong); 1285 (void) alarm((unsigned) timeout); 1286 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 1287 reply(221, "You could at least say goodbye."); 1288 dologout(0); 1289 } 1290 (void) alarm(0); 1291 if ((cp = strchr(cbuf, '\r'))) { 1292 *cp++ = '\n'; 1293 *cp = '\0'; 1294 } 1295#ifdef HASSETPROCTITLE 1296 if (strncasecmp(cbuf, "PASS", 4) != 0) { 1297 if ((cp = strpbrk(cbuf, "\n"))) { 1298 c = *cp; 1299 *cp = '\0'; 1300 setproctitle("%s: %s", proctitle, cbuf); 1301 *cp = c; 1302 } 1303 } 1304#endif /* HASSETPROCTITLE */ 1305 if ((cp = strpbrk(cbuf, " \n"))) 1306 cpos = cp - cbuf; 1307 if (cpos == 0) 1308 cpos = 4; 1309 c = cbuf[cpos]; 1310 cbuf[cpos] = '\0'; 1311 upper(cbuf); 1312 p = lookup(cmdtab, cbuf); 1313 cbuf[cpos] = c; 1314 if (p != 0) { 1315 if (p->implemented == 0) { 1316 nack(p->name); 1317 longjmp(errcatch,0); 1318 /* NOTREACHED */ 1319 } 1320 state = p->state; 1321 yylval.s = p->name; 1322 return (p->token); 1323 } 1324 break; 1325 1326 case SITECMD: 1327 if (cbuf[cpos] == ' ') { 1328 cpos++; 1329 return (SP); 1330 } 1331 cp = &cbuf[cpos]; 1332 if ((cp2 = strpbrk(cp, " \n"))) 1333 cpos = cp2 - cbuf; 1334 c = cbuf[cpos]; 1335 cbuf[cpos] = '\0'; 1336 upper(cp); 1337 p = lookup(sitetab, cp); 1338 cbuf[cpos] = c; 1339 if (p != 0) { 1340 if (p->implemented == 0) { 1341 state = CMD; 1342 nack(p->name); 1343 longjmp(errcatch,0); 1344 /* NOTREACHED */ 1345 } 1346 state = p->state; 1347 yylval.s = p->name; 1348 return (p->token); 1349 } 1350 state = CMD; 1351 break; 1352 1353 case OSTR: 1354 if (cbuf[cpos] == '\n') { 1355 state = CMD; 1356 return (CRLF); 1357 } 1358 /* FALLTHROUGH */ 1359 1360 case STR1: 1361 case ZSTR1: 1362 dostr1: 1363 if (cbuf[cpos] == ' ') { 1364 cpos++; 1365 state = state == OSTR ? STR2 : state+1; 1366 return (SP); 1367 } 1368 break; 1369 1370 case ZSTR2: 1371 if (cbuf[cpos] == '\n') { 1372 state = CMD; 1373 return (CRLF); 1374 } 1375 /* FALLTHROUGH */ 1376 1377 case STR2: 1378 cp = &cbuf[cpos]; 1379 n = strlen(cp); 1380 cpos += n - 1; 1381 /* 1382 * Make sure the string is nonempty and \n terminated. 1383 */ 1384 if (n > 1 && cbuf[cpos] == '\n') { 1385 cbuf[cpos] = '\0'; 1386 yylval.s = strdup(cp); 1387 if (yylval.s == NULL) 1388 fatal("Ran out of memory."); 1389 cbuf[cpos] = '\n'; 1390 state = ARGS; 1391 return (STRING); 1392 } 1393 break; 1394 1395 case NSTR: 1396 if (cbuf[cpos] == ' ') { 1397 cpos++; 1398 return (SP); 1399 } 1400 if (isdigit(cbuf[cpos])) { 1401 cp = &cbuf[cpos]; 1402 while (isdigit(cbuf[++cpos])) 1403 ; 1404 c = cbuf[cpos]; 1405 cbuf[cpos] = '\0'; 1406 yylval.i = atoi(cp); 1407 cbuf[cpos] = c; 1408 state = STR1; 1409 return (NUMBER); 1410 } 1411 state = STR1; 1412 goto dostr1; 1413 1414 case ARGS: 1415 if (isdigit(cbuf[cpos])) { 1416 cp = &cbuf[cpos]; 1417 while (isdigit(cbuf[++cpos])) 1418 ; 1419 c = cbuf[cpos]; 1420 cbuf[cpos] = '\0'; 1421 yylval.i = atoi(cp); 1422 cbuf[cpos] = c; 1423 return (NUMBER); 1424 } 1425 if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 1426 && !isalnum(cbuf[cpos + 3])) { 1427 yylval.s = strdup("ALL"); 1428 cpos += 3; 1429 return ALL; 1430 } 1431 switch (cbuf[cpos++]) { 1432 1433 case '\n': 1434 state = CMD; 1435 return (CRLF); 1436 1437 case ' ': 1438 return (SP); 1439 1440 case ',': 1441 return (COMMA); 1442 1443 case 'A': 1444 case 'a': 1445 return (A); 1446 1447 case 'B': 1448 case 'b': 1449 return (B); 1450 1451 case 'C': 1452 case 'c': 1453 return (C); 1454 1455 case 'E': 1456 case 'e': 1457 return (E); 1458 1459 case 'F': 1460 case 'f': 1461 return (F); 1462 1463 case 'I': 1464 case 'i': 1465 return (I); 1466 1467 case 'L': 1468 case 'l': 1469 return (L); 1470 1471 case 'N': 1472 case 'n': 1473 return (N); 1474 1475 case 'P': 1476 case 'p': 1477 return (P); 1478 1479 case 'R': 1480 case 'r': 1481 return (R); 1482 1483 case 'S': 1484 case 's': 1485 return (S); 1486 1487 case 'T': 1488 case 't': 1489 return (T); 1490 1491 } 1492 break; 1493 1494 default: 1495 fatal("Unknown state in scanner."); 1496 } 1497 yyerror((char *) 0); 1498 state = CMD; 1499 longjmp(errcatch,0); 1500 } 1501} 1502 1503void 1504upper(s) 1505 char *s; 1506{ 1507 while (*s != '\0') { 1508 if (islower(*s)) 1509 *s = toupper(*s); 1510 s++; 1511 } 1512} 1513 1514static void 1515help(ctab, s) 1516 struct tab *ctab; 1517 char *s; 1518{ 1519 struct tab *c; 1520 int width, NCMDS; 1521 char *type; 1522 1523 if (ctab == sitetab) 1524 type = "SITE "; 1525 else 1526 type = ""; 1527 width = 0, NCMDS = 0; 1528 for (c = ctab; c->name != NULL; c++) { 1529 int len = strlen(c->name); 1530 1531 if (len > width) 1532 width = len; 1533 NCMDS++; 1534 } 1535 width = (width + 8) &~ 7; 1536 if (s == 0) { 1537 int i, j, w; 1538 int columns, lines; 1539 1540 lreply(214, "The following %scommands are recognized %s.", 1541 type, "(* =>'s unimplemented)"); 1542 columns = 76 / width; 1543 if (columns == 0) 1544 columns = 1; 1545 lines = (NCMDS + columns - 1) / columns; 1546 for (i = 0; i < lines; i++) { 1547 printf(" "); 1548 for (j = 0; j < columns; j++) { 1549 c = ctab + j * lines + i; 1550 printf("%s%c", c->name, 1551 c->implemented ? ' ' : '*'); 1552 if (c + lines >= &ctab[NCMDS]) 1553 break; 1554 w = strlen(c->name) + 1; 1555 while (w < width) { 1556 putchar(' '); 1557 w++; 1558 } 1559 } 1560 printf("\r\n"); 1561 } 1562 (void) fflush(stdout); 1563 reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1564 return; 1565 } 1566 upper(s); 1567 c = lookup(ctab, s); 1568 if (c == (struct tab *)0) { 1569 reply(502, "Unknown command %s.", s); 1570 return; 1571 } 1572 if (c->implemented) 1573 reply(214, "Syntax: %s%s %s", type, c->name, c->help); 1574 else 1575 reply(214, "%s%-*s\t%s; unimplemented.", type, width, 1576 c->name, c->help); 1577} 1578 1579static void 1580sizecmd(filename) 1581 char *filename; 1582{ 1583 switch (type) { 1584 case TYPE_L: 1585 case TYPE_I: { 1586 struct stat stbuf; 1587 if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) 1588 reply(550, "%s: not a plain file.", filename); 1589 else 1590 reply(213, "%qu", stbuf.st_size); 1591 break; } 1592 case TYPE_A: { 1593 FILE *fin; 1594 int c; 1595 off_t count; 1596 struct stat stbuf; 1597 fin = fopen(filename, "r"); 1598 if (fin == NULL) { 1599 perror_reply(550, filename); 1600 return; 1601 } 1602 if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) { 1603 reply(550, "%s: not a plain file.", filename); 1604 (void) fclose(fin); 1605 return; 1606 } 1607 1608 count = 0; 1609 while((c=getc(fin)) != EOF) { 1610 if (c == '\n') /* will get expanded to \r\n */ 1611 count++; 1612 count++; 1613 } 1614 (void) fclose(fin); 1615 1616 reply(213, "%qd", count); 1617 break; } 1618 default: 1619 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 1620 } 1621} 1622