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