ftpcmd.y revision 101378
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 45static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; 46#endif 47static const char rcsid[] = 48 "$FreeBSD: head/libexec/ftpd/ftpcmd.y 101378 2002-08-05 14:10:57Z yar $"; 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 76extern union sockunion data_dest, his_addr; 77extern int logged_in; 78extern struct passwd *pw; 79extern int guest; 80extern int paranoid; 81extern int logging; 82extern int type; 83extern int form; 84extern int ftpdebug; 85extern int timeout; 86extern int maxtimeout; 87extern int pdata; 88extern char *hostname; 89extern char remotehost[]; 90extern char proctitle[]; 91extern int usedefault; 92extern int transflag; 93extern char tmpline[]; 94extern int readonly; 95extern int noepsv; 96extern int noretr; 97extern int noguestretr; 98extern char *typenames[]; /* defined in <arpa/ftp.h> included from ftpd.c */ 99 100off_t restart_point; 101 102static int cmd_type; 103static int cmd_form; 104static int cmd_bytesz; 105static int state; 106char cbuf[512]; 107char *fromname = (char *) 0; 108 109extern int epsvall; 110 111%} 112 113%union { 114 struct { 115 off_t o; 116 int i; 117 } u; 118 char *s; 119} 120 121%token 122 A B C E F I 123 L N P R S T 124 ALL 125 126 SP CRLF COMMA 127 128 USER PASS ACCT REIN QUIT PORT 129 PASV TYPE STRU MODE RETR STOR 130 APPE MLFL MAIL MSND MSOM MSAM 131 MRSQ MRCP ALLO REST RNFR RNTO 132 ABOR DELE CWD LIST NLST SITE 133 STAT HELP NOOP MKD RMD PWD 134 CDUP STOU SMNT SYST SIZE MDTM 135 LPRT LPSV EPRT EPSV 136 137 UMASK IDLE CHMOD MDFIVE 138 139 LEXERR 140 141%token <s> STRING 142%token <u> NUMBER 143 144%type <u.i> check_login octal_number byte_size 145%type <u.i> check_login_ro check_login_epsv 146%type <u.i> struct_code mode_code type_code form_code 147%type <s> pathstring pathname password username 148%type <s> ALL 149 150%start cmd_list 151 152%% 153 154cmd_list 155 : /* empty */ 156 | cmd_list cmd 157 { 158 if (fromname) 159 free(fromname); 160 fromname = (char *) 0; 161 restart_point = (off_t) 0; 162 } 163 | cmd_list rcmd 164 ; 165 166cmd 167 : USER SP username CRLF 168 { 169 user($3); 170 free($3); 171 } 172 | PASS SP password CRLF 173 { 174 pass($3); 175 free($3); 176 } 177 | PASS CRLF 178 { 179 pass(""); 180 } 181 | PORT check_login SP host_port CRLF 182 { 183 if (epsvall) { 184 reply(501, "no PORT allowed after EPSV ALL"); 185 goto port_done; 186 } 187 if (!$2) 188 goto port_done; 189 if (port_check("PORT") == 1) 190 goto port_done; 191#ifdef INET6 192 if ((his_addr.su_family != AF_INET6 || 193 !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) { 194 /* shoud never happen */ 195 usedefault = 1; 196 reply(500, "Invalid address rejected."); 197 goto port_done; 198 } 199 port_check_v6("pcmd"); 200#endif 201 port_done: 202 } 203 | LPRT check_login SP host_long_port CRLF 204 { 205 if (epsvall) { 206 reply(501, "no LPRT allowed after EPSV ALL"); 207 goto lprt_done; 208 } 209 if (!$2) 210 goto lprt_done; 211 if (port_check("LPRT") == 1) 212 goto lprt_done; 213#ifdef INET6 214 if (his_addr.su_family != AF_INET6) { 215 usedefault = 1; 216 reply(500, "Invalid address rejected."); 217 goto lprt_done; 218 } 219 if (port_check_v6("LPRT") == 1) 220 goto lprt_done; 221#endif 222 lprt_done: 223 } 224 | EPRT check_login SP STRING CRLF 225 { 226 char delim; 227 char *tmp = NULL; 228 char *p, *q; 229 char *result[3]; 230 struct addrinfo hints; 231 struct addrinfo *res; 232 int i; 233 234 if (epsvall) { 235 reply(501, "no EPRT allowed after EPSV ALL"); 236 goto eprt_done; 237 } 238 if (!$2) 239 goto eprt_done; 240 241 memset(&data_dest, 0, sizeof(data_dest)); 242 tmp = strdup($4); 243 if (ftpdebug) 244 syslog(LOG_DEBUG, "%s", tmp); 245 if (!tmp) { 246 fatalerror("not enough core"); 247 /*NOTREACHED*/ 248 } 249 p = tmp; 250 delim = p[0]; 251 p++; 252 memset(result, 0, sizeof(result)); 253 for (i = 0; i < 3; i++) { 254 q = strchr(p, delim); 255 if (!q || *q != delim) { 256 parsefail: 257 reply(500, 258 "Invalid argument, rejected."); 259 if (tmp) 260 free(tmp); 261 usedefault = 1; 262 goto eprt_done; 263 } 264 *q++ = '\0'; 265 result[i] = p; 266 if (ftpdebug) 267 syslog(LOG_DEBUG, "%d: %s", i, p); 268 p = q; 269 } 270 271 /* some more sanity check */ 272 p = result[0]; 273 while (*p) { 274 if (!isdigit(*p)) 275 goto parsefail; 276 p++; 277 } 278 p = result[2]; 279 while (*p) { 280 if (!isdigit(*p)) 281 goto parsefail; 282 p++; 283 } 284 285 /* grab address */ 286 memset(&hints, 0, sizeof(hints)); 287 if (atoi(result[0]) == 1) 288 hints.ai_family = PF_INET; 289#ifdef INET6 290 else if (atoi(result[0]) == 2) 291 hints.ai_family = PF_INET6; 292#endif 293 else 294 hints.ai_family = PF_UNSPEC; /*XXX*/ 295 hints.ai_socktype = SOCK_STREAM; 296 i = getaddrinfo(result[1], result[2], &hints, &res); 297 if (i) 298 goto parsefail; 299 memcpy(&data_dest, res->ai_addr, res->ai_addrlen); 300#ifdef INET6 301 if (his_addr.su_family == AF_INET6 302 && data_dest.su_family == AF_INET6) { 303 /* XXX more sanity checks! */ 304 data_dest.su_sin6.sin6_scope_id = 305 his_addr.su_sin6.sin6_scope_id; 306 } 307#endif 308 free(tmp); 309 tmp = NULL; 310 311 if (port_check("EPRT") == 1) 312 goto eprt_done; 313#ifdef INET6 314 if (his_addr.su_family != AF_INET6) { 315 usedefault = 1; 316 reply(500, "Invalid address rejected."); 317 goto eprt_done; 318 } 319 if (port_check_v6("EPRT") == 1) 320 goto eprt_done; 321#endif 322 eprt_done: 323 free($4); 324 } 325 | PASV check_login CRLF 326 { 327 if (epsvall) 328 reply(501, "no PASV allowed after EPSV ALL"); 329 else if ($2) 330 passive(); 331 } 332 | LPSV check_login CRLF 333 { 334 if (epsvall) 335 reply(501, "no LPSV allowed after EPSV ALL"); 336 else if ($2) 337 long_passive("LPSV", PF_UNSPEC); 338 } 339 | EPSV check_login_epsv SP NUMBER CRLF 340 { 341 if ($2) { 342 int pf; 343 switch ($4.i) { 344 case 1: 345 pf = PF_INET; 346 break; 347#ifdef INET6 348 case 2: 349 pf = PF_INET6; 350 break; 351#endif 352 default: 353 pf = -1; /*junk value*/ 354 break; 355 } 356 long_passive("EPSV", pf); 357 } 358 } 359 | EPSV check_login_epsv SP ALL CRLF 360 { 361 if ($2) { 362 reply(200, 363 "EPSV ALL command successful."); 364 epsvall++; 365 } 366 } 367 | EPSV check_login_epsv CRLF 368 { 369 if ($2) 370 long_passive("EPSV", PF_UNSPEC); 371 } 372 | TYPE check_login SP type_code CRLF 373 { 374 if ($2) { 375 switch (cmd_type) { 376 377 case TYPE_A: 378 if (cmd_form == FORM_N) { 379 reply(200, "Type set to A."); 380 type = cmd_type; 381 form = cmd_form; 382 } else 383 reply(504, "Form must be N."); 384 break; 385 386 case TYPE_E: 387 reply(504, "Type E not implemented."); 388 break; 389 390 case TYPE_I: 391 reply(200, "Type set to I."); 392 type = cmd_type; 393 break; 394 395 case TYPE_L: 396#if NBBY == 8 397 if (cmd_bytesz == 8) { 398 reply(200, 399 "Type set to L (byte size 8)."); 400 type = cmd_type; 401 } else 402 reply(504, "Byte size must be 8."); 403#else /* NBBY == 8 */ 404 UNIMPLEMENTED for NBBY != 8 405#endif /* NBBY == 8 */ 406 } 407 } 408 } 409 | STRU check_login SP struct_code CRLF 410 { 411 if ($2) { 412 switch ($4) { 413 414 case STRU_F: 415 reply(200, "STRU F ok."); 416 break; 417 418 default: 419 reply(504, "Unimplemented STRU type."); 420 } 421 } 422 } 423 | MODE check_login SP mode_code CRLF 424 { 425 if ($2) { 426 switch ($4) { 427 428 case MODE_S: 429 reply(200, "MODE S ok."); 430 break; 431 432 default: 433 reply(502, "Unimplemented MODE type."); 434 } 435 } 436 } 437 | ALLO check_login SP NUMBER CRLF 438 { 439 if ($2) { 440 reply(202, "ALLO command ignored."); 441 } 442 } 443 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 444 { 445 if ($2) { 446 reply(202, "ALLO command ignored."); 447 } 448 } 449 | RETR check_login SP pathname CRLF 450 { 451 if (noretr || (guest && noguestretr)) 452 reply(500, "RETR command is disabled"); 453 else if ($2 && $4 != NULL) 454 retrieve((char *) 0, $4); 455 456 if ($4 != NULL) 457 free($4); 458 } 459 | STOR check_login_ro SP pathname CRLF 460 { 461 if ($2 && $4 != NULL) 462 store($4, "w", 0); 463 if ($4 != NULL) 464 free($4); 465 } 466 | APPE check_login_ro SP pathname CRLF 467 { 468 if ($2 && $4 != NULL) 469 store($4, "a", 0); 470 if ($4 != NULL) 471 free($4); 472 } 473 | NLST check_login CRLF 474 { 475 if ($2) 476 send_file_list("."); 477 } 478 | NLST check_login SP STRING CRLF 479 { 480 if ($2 && $4 != NULL) 481 send_file_list($4); 482 if ($4 != NULL) 483 free($4); 484 } 485 | LIST check_login CRLF 486 { 487 if ($2) 488 retrieve("/bin/ls -lgA", ""); 489 } 490 | LIST check_login SP pathstring CRLF 491 { 492 if ($2 && $4 != NULL) 493 retrieve("/bin/ls -lgA %s", $4); 494 if ($4 != NULL) 495 free($4); 496 } 497 | STAT check_login SP pathname CRLF 498 { 499 if ($2 && $4 != NULL) 500 statfilecmd($4); 501 if ($4 != NULL) 502 free($4); 503 } 504 | STAT check_login CRLF 505 { 506 if ($2) { 507 statcmd(); 508 } 509 } 510 | DELE check_login_ro SP pathname CRLF 511 { 512 if ($2 && $4 != NULL) 513 delete($4); 514 if ($4 != NULL) 515 free($4); 516 } 517 | RNTO check_login_ro SP pathname CRLF 518 { 519 if ($2) { 520 if (fromname) { 521 renamecmd(fromname, $4); 522 free(fromname); 523 fromname = (char *) 0; 524 } else { 525 reply(503, "Bad sequence of commands."); 526 } 527 } 528 free($4); 529 } 530 | ABOR check_login CRLF 531 { 532 if ($2) 533 reply(225, "ABOR command successful."); 534 } 535 | CWD check_login CRLF 536 { 537 if ($2) { 538 if (guest) 539 cwd("/"); 540 else 541 cwd(pw->pw_dir); 542 } 543 } 544 | CWD check_login SP pathname CRLF 545 { 546 if ($2 && $4 != NULL) 547 cwd($4); 548 if ($4 != NULL) 549 free($4); 550 } 551 | HELP CRLF 552 { 553 help(cmdtab, (char *) 0); 554 } 555 | HELP SP STRING CRLF 556 { 557 char *cp = $3; 558 559 if (strncasecmp(cp, "SITE", 4) == 0) { 560 cp = $3 + 4; 561 if (*cp == ' ') 562 cp++; 563 if (*cp) 564 help(sitetab, cp); 565 else 566 help(sitetab, (char *) 0); 567 } else 568 help(cmdtab, $3); 569 free($3); 570 } 571 | NOOP CRLF 572 { 573 reply(200, "NOOP command successful."); 574 } 575 | MKD check_login_ro SP pathname CRLF 576 { 577 if ($2 && $4 != NULL) 578 makedir($4); 579 if ($4 != NULL) 580 free($4); 581 } 582 | RMD check_login_ro SP pathname CRLF 583 { 584 if ($2 && $4 != NULL) 585 removedir($4); 586 if ($4 != NULL) 587 free($4); 588 } 589 | PWD check_login CRLF 590 { 591 if ($2) 592 pwd(); 593 } 594 | CDUP check_login CRLF 595 { 596 if ($2) 597 cwd(".."); 598 } 599 | SITE SP HELP CRLF 600 { 601 help(sitetab, (char *) 0); 602 } 603 | SITE SP HELP SP STRING CRLF 604 { 605 help(sitetab, $5); 606 free($5); 607 } 608 | SITE SP MDFIVE check_login SP pathname CRLF 609 { 610 char p[64], *q; 611 612 if ($4) { 613 q = MD5File($6, p); 614 if (q != NULL) 615 reply(200, "MD5(%s) = %s", $6, p); 616 else 617 perror_reply(550, $6); 618 } 619 if ($6) 620 free($6); 621 } 622 | SITE SP UMASK check_login CRLF 623 { 624 int oldmask; 625 626 if ($4) { 627 oldmask = umask(0); 628 (void) umask(oldmask); 629 reply(200, "Current UMASK is %03o", oldmask); 630 } 631 } 632 | SITE SP UMASK check_login SP octal_number CRLF 633 { 634 int oldmask; 635 636 if ($4) { 637 if (($6 == -1) || ($6 > 0777)) { 638 reply(501, "Bad UMASK value"); 639 } else { 640 oldmask = umask($6); 641 reply(200, 642 "UMASK set to %03o (was %03o)", 643 $6, oldmask); 644 } 645 } 646 } 647 | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF 648 { 649 if ($4 && ($8 != NULL)) { 650 if (($6 == -1 ) || ($6 > 0777)) 651 reply(501, "Bad mode value"); 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 ; 763rcmd 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 793username 794 : STRING 795 ; 796 797password 798 : /* empty */ 799 { 800 $$ = (char *)calloc(1, sizeof(char)); 801 } 802 | STRING 803 ; 804 805byte_size 806 : NUMBER 807 { 808 $$ = $1.i; 809 } 810 ; 811 812host_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 827host_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 873form_code 874 : N 875 { 876 $$ = FORM_N; 877 } 878 | T 879 { 880 $$ = FORM_T; 881 } 882 | C 883 { 884 $$ = FORM_C; 885 } 886 ; 887 888type_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 931struct_code 932 : F 933 { 934 $$ = STRU_F; 935 } 936 | R 937 { 938 $$ = STRU_R; 939 } 940 | P 941 { 942 $$ = STRU_P; 943 } 944 ; 945 946mode_code 947 : S 948 { 949 $$ = MODE_S; 950 } 951 | B 952 { 953 $$ = MODE_B; 954 } 955 | C 956 { 957 $$ = MODE_C; 958 } 959 ; 960 961pathname 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_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 994pathstring 995 : STRING 996 ; 997 998octal_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 1025check_login 1026 : /* empty */ 1027 { 1028 $$ = check_login1(); 1029 } 1030 ; 1031 1032check_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 1044check_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#define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */ 1071 1072struct tab { 1073 char *name; 1074 short token; 1075 short state; 1076 short implemented; /* 1 if command is implemented */ 1077 char *help; 1078}; 1079 1080struct tab cmdtab[] = { /* In order defined in RFC 765 */ 1081 { "USER", USER, STR1, 1, "<sp> username" }, 1082 { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" }, 1083 { "ACCT", ACCT, STR1, 0, "(specify account)" }, 1084 { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 1085 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 1086 { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 1087 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 1088 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 1089 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 1090 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 1091 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 1092 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 1093 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 1094 { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 1095 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 1096 { "RETR", RETR, STR1, 1, "<sp> file-name" }, 1097 { "STOR", STOR, STR1, 1, "<sp> file-name" }, 1098 { "APPE", APPE, STR1, 1, "<sp> file-name" }, 1099 { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 1100 { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 1101 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 1102 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 1103 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 1104 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 1105 { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 1106 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 1107 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 1108 { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 1109 { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 1110 { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 1111 { "DELE", DELE, STR1, 1, "<sp> file-name" }, 1112 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 1113 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 1114 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 1115 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 1116 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 1117 { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 1118 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 1119 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 1120 { "NOOP", NOOP, ARGS, 1, "" }, 1121 { "MKD", MKD, STR1, 1, "<sp> path-name" }, 1122 { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 1123 { "RMD", RMD, STR1, 1, "<sp> path-name" }, 1124 { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 1125 { "PWD", PWD, ARGS, 1, "(return current directory)" }, 1126 { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 1127 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 1128 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 1129 { "STOU", STOU, STR1, 1, "<sp> file-name" }, 1130 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 1131 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 1132 { NULL, 0, 0, 0, 0 } 1133}; 1134 1135struct tab sitetab[] = { 1136 { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" }, 1137 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 1138 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 1139 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 1140 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 1141 { NULL, 0, 0, 0, 0 } 1142}; 1143 1144static char *copy(char *); 1145static void help(struct tab *, char *); 1146static struct tab * 1147 lookup(struct tab *, char *); 1148static int port_check(const char *); 1149static int port_check_v6(const char *); 1150static void sizecmd(char *); 1151static void toolong(int); 1152static void v4map_data_dest(void); 1153static int yylex(void); 1154 1155static struct tab * 1156lookup(struct tab *p, char *cmd) 1157{ 1158 1159 for (; p->name != NULL; p++) 1160 if (strcmp(cmd, p->name) == 0) 1161 return (p); 1162 return (0); 1163} 1164 1165#include <arpa/telnet.h> 1166 1167/* 1168 * getline - a hacked up version of fgets to ignore TELNET escape codes. 1169 */ 1170char * 1171getline(char *s, int n, FILE *iop) 1172{ 1173 int c; 1174 register char *cs; 1175 1176 cs = s; 1177/* tmpline may contain saved command from urgent mode interruption */ 1178 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 1179 *cs++ = tmpline[c]; 1180 if (tmpline[c] == '\n') { 1181 *cs++ = '\0'; 1182 if (ftpdebug) 1183 syslog(LOG_DEBUG, "command: %s", s); 1184 tmpline[0] = '\0'; 1185 return(s); 1186 } 1187 if (c == 0) 1188 tmpline[0] = '\0'; 1189 } 1190 while ((c = getc(iop)) != EOF) { 1191 c &= 0377; 1192 if (c == IAC) { 1193 if ((c = getc(iop)) != EOF) { 1194 c &= 0377; 1195 switch (c) { 1196 case WILL: 1197 case WONT: 1198 c = getc(iop); 1199 printf("%c%c%c", IAC, DONT, 0377&c); 1200 (void) fflush(stdout); 1201 continue; 1202 case DO: 1203 case DONT: 1204 c = getc(iop); 1205 printf("%c%c%c", IAC, WONT, 0377&c); 1206 (void) fflush(stdout); 1207 continue; 1208 case IAC: 1209 break; 1210 default: 1211 continue; /* ignore command */ 1212 } 1213 } 1214 } 1215 *cs++ = c; 1216 if (--n <= 0 || c == '\n') 1217 break; 1218 } 1219 if (c == EOF && cs == s) 1220 return (NULL); 1221 *cs++ = '\0'; 1222 if (ftpdebug) { 1223 if (!guest && strncasecmp("pass ", s, 5) == 0) { 1224 /* Don't syslog passwords */ 1225 syslog(LOG_DEBUG, "command: %.5s ???", s); 1226 } else { 1227 register char *cp; 1228 register int len; 1229 1230 /* Don't syslog trailing CR-LF */ 1231 len = strlen(s); 1232 cp = s + len - 1; 1233 while (cp >= s && (*cp == '\n' || *cp == '\r')) { 1234 --cp; 1235 --len; 1236 } 1237 syslog(LOG_DEBUG, "command: %.*s", len, s); 1238 } 1239 } 1240 return (s); 1241} 1242 1243static void 1244toolong(int signo) 1245{ 1246 1247 reply(421, 1248 "Timeout (%d seconds): closing control connection.", timeout); 1249 if (logging) 1250 syslog(LOG_INFO, "User %s timed out after %d seconds", 1251 (pw ? pw -> pw_name : "unknown"), timeout); 1252 dologout(1); 1253} 1254 1255static int 1256yylex(void) 1257{ 1258 static int cpos; 1259 char *cp, *cp2; 1260 struct tab *p; 1261 int n; 1262 char c; 1263 1264 for (;;) { 1265 switch (state) { 1266 1267 case CMD: 1268 (void) signal(SIGALRM, toolong); 1269 (void) alarm((unsigned) timeout); 1270 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 1271 reply(221, "You could at least say goodbye."); 1272 dologout(0); 1273 } 1274 (void) alarm(0); 1275#ifdef SETPROCTITLE 1276 if (strncasecmp(cbuf, "PASS", 4) != 0) 1277 setproctitle("%s: %s", proctitle, cbuf); 1278#endif /* SETPROCTITLE */ 1279 if ((cp = strchr(cbuf, '\r'))) { 1280 *cp++ = '\n'; 1281 *cp = '\0'; 1282 } 1283 if ((cp = strpbrk(cbuf, " \n"))) 1284 cpos = cp - cbuf; 1285 if (cpos == 0) 1286 cpos = 4; 1287 c = cbuf[cpos]; 1288 cbuf[cpos] = '\0'; 1289 upper(cbuf); 1290 p = lookup(cmdtab, cbuf); 1291 cbuf[cpos] = c; 1292 if (p != 0) { 1293 if (p->implemented == 0) { 1294 nack(p->name); 1295 return (LEXERR); 1296 } 1297 state = p->state; 1298 yylval.s = p->name; 1299 return (p->token); 1300 } 1301 break; 1302 1303 case SITECMD: 1304 if (cbuf[cpos] == ' ') { 1305 cpos++; 1306 return (SP); 1307 } 1308 cp = &cbuf[cpos]; 1309 if ((cp2 = strpbrk(cp, " \n"))) 1310 cpos = cp2 - cbuf; 1311 c = cbuf[cpos]; 1312 cbuf[cpos] = '\0'; 1313 upper(cp); 1314 p = lookup(sitetab, cp); 1315 cbuf[cpos] = c; 1316 if (guest == 0 && p != 0) { 1317 if (p->implemented == 0) { 1318 state = CMD; 1319 nack(p->name); 1320 return (LEXERR); 1321 } 1322 state = p->state; 1323 yylval.s = p->name; 1324 return (p->token); 1325 } 1326 state = CMD; 1327 break; 1328 1329 case ZSTR1: 1330 case OSTR: 1331 if (cbuf[cpos] == '\n') { 1332 state = CMD; 1333 return (CRLF); 1334 } 1335 /* FALLTHROUGH */ 1336 1337 case STR1: 1338 dostr1: 1339 if (cbuf[cpos] == ' ') { 1340 cpos++; 1341 state = state == OSTR ? STR2 : state+1; 1342 return (SP); 1343 } 1344 break; 1345 1346 case ZSTR2: 1347 if (cbuf[cpos] == '\n') { 1348 state = CMD; 1349 return (CRLF); 1350 } 1351 /* FALLTHROUGH */ 1352 1353 case STR2: 1354 cp = &cbuf[cpos]; 1355 n = strlen(cp); 1356 cpos += n - 1; 1357 /* 1358 * Make sure the string is nonempty and \n terminated. 1359 */ 1360 if (n > 1 && cbuf[cpos] == '\n') { 1361 cbuf[cpos] = '\0'; 1362 yylval.s = copy(cp); 1363 cbuf[cpos] = '\n'; 1364 state = ARGS; 1365 return (STRING); 1366 } 1367 break; 1368 1369 case NSTR: 1370 if (cbuf[cpos] == ' ') { 1371 cpos++; 1372 return (SP); 1373 } 1374 if (isdigit(cbuf[cpos])) { 1375 cp = &cbuf[cpos]; 1376 while (isdigit(cbuf[++cpos])) 1377 ; 1378 c = cbuf[cpos]; 1379 cbuf[cpos] = '\0'; 1380 yylval.u.i = atoi(cp); 1381 cbuf[cpos] = c; 1382 state = STR1; 1383 return (NUMBER); 1384 } 1385 state = STR1; 1386 goto dostr1; 1387 1388 case ARGS: 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 yylval.u.o = strtoull(cp, (char **)NULL, 10); 1397 cbuf[cpos] = c; 1398 return (NUMBER); 1399 } 1400 if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 1401 && !isalnum(cbuf[cpos + 3])) { 1402 cpos += 3; 1403 return ALL; 1404 } 1405 switch (cbuf[cpos++]) { 1406 1407 case '\n': 1408 state = CMD; 1409 return (CRLF); 1410 1411 case ' ': 1412 return (SP); 1413 1414 case ',': 1415 return (COMMA); 1416 1417 case 'A': 1418 case 'a': 1419 return (A); 1420 1421 case 'B': 1422 case 'b': 1423 return (B); 1424 1425 case 'C': 1426 case 'c': 1427 return (C); 1428 1429 case 'E': 1430 case 'e': 1431 return (E); 1432 1433 case 'F': 1434 case 'f': 1435 return (F); 1436 1437 case 'I': 1438 case 'i': 1439 return (I); 1440 1441 case 'L': 1442 case 'l': 1443 return (L); 1444 1445 case 'N': 1446 case 'n': 1447 return (N); 1448 1449 case 'P': 1450 case 'p': 1451 return (P); 1452 1453 case 'R': 1454 case 'r': 1455 return (R); 1456 1457 case 'S': 1458 case 's': 1459 return (S); 1460 1461 case 'T': 1462 case 't': 1463 return (T); 1464 1465 } 1466 break; 1467 1468 default: 1469 fatalerror("Unknown state in scanner."); 1470 } 1471 state = CMD; 1472 return (LEXERR); 1473 } 1474} 1475 1476void 1477upper(char *s) 1478{ 1479 while (*s != '\0') { 1480 if (islower(*s)) 1481 *s = toupper(*s); 1482 s++; 1483 } 1484} 1485 1486static char * 1487copy(char *s) 1488{ 1489 char *p; 1490 1491 p = malloc((unsigned) strlen(s) + 1); 1492 if (p == NULL) 1493 fatalerror("Ran out of memory."); 1494 (void) strcpy(p, s); 1495 return (p); 1496} 1497 1498static void 1499help(struct tab *ctab, char *s) 1500{ 1501 struct tab *c; 1502 int width, NCMDS; 1503 char *type; 1504 1505 if (ctab == sitetab) 1506 type = "SITE "; 1507 else 1508 type = ""; 1509 width = 0, NCMDS = 0; 1510 for (c = ctab; c->name != NULL; c++) { 1511 int len = strlen(c->name); 1512 1513 if (len > width) 1514 width = len; 1515 NCMDS++; 1516 } 1517 width = (width + 8) &~ 7; 1518 if (s == 0) { 1519 int i, j, w; 1520 int columns, lines; 1521 1522 lreply(214, "The following %scommands are recognized %s.", 1523 type, "(* =>'s unimplemented)"); 1524 columns = 76 / width; 1525 if (columns == 0) 1526 columns = 1; 1527 lines = (NCMDS + columns - 1) / columns; 1528 for (i = 0; i < lines; i++) { 1529 printf(" "); 1530 for (j = 0; j < columns; j++) { 1531 c = ctab + j * lines + i; 1532 printf("%s%c", c->name, 1533 c->implemented ? ' ' : '*'); 1534 if (c + lines >= &ctab[NCMDS]) 1535 break; 1536 w = strlen(c->name) + 1; 1537 while (w < width) { 1538 putchar(' '); 1539 w++; 1540 } 1541 } 1542 printf("\r\n"); 1543 } 1544 (void) fflush(stdout); 1545 reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1546 return; 1547 } 1548 upper(s); 1549 c = lookup(ctab, s); 1550 if (c == (struct tab *)0) { 1551 reply(502, "Unknown command %s.", s); 1552 return; 1553 } 1554 if (c->implemented) 1555 reply(214, "Syntax: %s%s %s", type, c->name, c->help); 1556 else 1557 reply(214, "%s%-*s\t%s; unimplemented.", type, width, 1558 c->name, c->help); 1559} 1560 1561static void 1562sizecmd(char *filename) 1563{ 1564 switch (type) { 1565 case TYPE_L: 1566 case TYPE_I: { 1567 struct stat stbuf; 1568 if (stat(filename, &stbuf) < 0) 1569 perror_reply(550, filename); 1570 else if (!S_ISREG(stbuf.st_mode)) 1571 reply(550, "%s: not a plain file.", filename); 1572 else 1573 reply(213, "%qu", stbuf.st_size); 1574 break; } 1575 case TYPE_A: { 1576 FILE *fin; 1577 int c; 1578 off_t count; 1579 struct stat stbuf; 1580 fin = fopen(filename, "r"); 1581 if (fin == NULL) { 1582 perror_reply(550, filename); 1583 return; 1584 } 1585 if (fstat(fileno(fin), &stbuf) < 0) { 1586 perror_reply(550, filename); 1587 (void) fclose(fin); 1588 return; 1589 } else if (!S_ISREG(stbuf.st_mode)) { 1590 reply(550, "%s: not a plain file.", filename); 1591 (void) fclose(fin); 1592 return; 1593 } else if (stbuf.st_size > MAXASIZE) { 1594 reply(550, "%s: too large for type A SIZE.", filename); 1595 (void) fclose(fin); 1596 return; 1597 } 1598 1599 count = 0; 1600 while((c=getc(fin)) != EOF) { 1601 if (c == '\n') /* will get expanded to \r\n */ 1602 count++; 1603 count++; 1604 } 1605 (void) fclose(fin); 1606 1607 reply(213, "%qd", count); 1608 break; } 1609 default: 1610 reply(504, "SIZE not implemented for type %s.", 1611 typenames[type]); 1612 } 1613} 1614 1615/* Return 1, if port check is done. Return 0, if not yet. */ 1616static int 1617port_check(const char *pcmd) 1618{ 1619 if (his_addr.su_family == AF_INET) { 1620 if (data_dest.su_family != AF_INET) { 1621 usedefault = 1; 1622 reply(500, "Invalid address rejected."); 1623 return 1; 1624 } 1625 if (paranoid && 1626 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 1627 memcmp(&data_dest.su_sin.sin_addr, 1628 &his_addr.su_sin.sin_addr, 1629 sizeof(data_dest.su_sin.sin_addr)))) { 1630 usedefault = 1; 1631 reply(500, "Illegal PORT range rejected."); 1632 } else { 1633 usedefault = 0; 1634 if (pdata >= 0) { 1635 (void) close(pdata); 1636 pdata = -1; 1637 } 1638 reply(200, "%s command successful.", pcmd); 1639 } 1640 return 1; 1641 } 1642 return 0; 1643} 1644 1645static int 1646check_login1(void) 1647{ 1648 if (logged_in) 1649 return 1; 1650 else { 1651 reply(530, "Please login with USER and PASS."); 1652 return 0; 1653 } 1654} 1655 1656#ifdef INET6 1657/* Return 1, if port check is done. Return 0, if not yet. */ 1658static int 1659port_check_v6(const char *pcmd) 1660{ 1661 if (his_addr.su_family == AF_INET6) { 1662 if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) 1663 /* Convert data_dest into v4 mapped sockaddr.*/ 1664 v4map_data_dest(); 1665 if (data_dest.su_family != AF_INET6) { 1666 usedefault = 1; 1667 reply(500, "Invalid address rejected."); 1668 return 1; 1669 } 1670 if (paranoid && 1671 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 1672 memcmp(&data_dest.su_sin6.sin6_addr, 1673 &his_addr.su_sin6.sin6_addr, 1674 sizeof(data_dest.su_sin6.sin6_addr)))) { 1675 usedefault = 1; 1676 reply(500, "Illegal PORT range rejected."); 1677 } else { 1678 usedefault = 0; 1679 if (pdata >= 0) { 1680 (void) close(pdata); 1681 pdata = -1; 1682 } 1683 reply(200, "%s command successful.", pcmd); 1684 } 1685 return 1; 1686 } 1687 return 0; 1688} 1689 1690static void 1691v4map_data_dest(void) 1692{ 1693 struct in_addr savedaddr; 1694 int savedport; 1695 1696 if (data_dest.su_family != AF_INET) { 1697 usedefault = 1; 1698 reply(500, "Invalid address rejected."); 1699 return; 1700 } 1701 1702 savedaddr = data_dest.su_sin.sin_addr; 1703 savedport = data_dest.su_port; 1704 1705 memset(&data_dest, 0, sizeof(data_dest)); 1706 data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); 1707 data_dest.su_sin6.sin6_family = AF_INET6; 1708 data_dest.su_sin6.sin6_port = savedport; 1709 memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); 1710 memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], 1711 (caddr_t)&savedaddr, sizeof(savedaddr)); 1712} 1713#endif 1714