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