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