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