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