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