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