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