1 /* ftp.c 2 * 3 * Copyright (c) 1996-2001 Mike Gleason, NCEMRSoft. 4 * All rights reserved. 5 * 6 */ 7 8 #define _libncftp_ftp_c_ 9 #include "syshdrs.h" 10 11 char gLibNcFTPVersion[64] = kLibraryVersion; 12 13 #ifdef NO_SIGNALS 14 char gNoSignalsMarker[] = "@(#) LibNcFTP - NO_SIGNALS"; 15 #else 16 17 static int gGotSig = 0; 18 #ifdef HAVE_SIGSETJMP 19 static sigjmp_buf gCancelConnectJmp; 20 #else 21 static jmp_buf gCancelConnectJmp; 22 #endif 23 24 #endif 25 26 27 #ifndef lint 28 static char gCopyright[] = "@(#) LibNcFTP Copyright 1995-2000, by Mike Gleason. All rights reserved."; 29 #endif 30 31 #ifdef HAVE_LIBSOCKS5 32 # define SOCKS 5 33 # include <socks.h> 34 #else 35 # ifdef HAVE_LIBSOCKS 36 # define accept Raccept 37 # define connect Rconnect 38 # define getsockname Rgetsockname 39 # define listen Rlisten 40 # endif 41 #endif 42 43 44 45 46 /* On entry, you should have 'host' be set to a symbolic name (like 47 * cse.unl.edu), or set to a numeric address (like 129.93.3.1). 48 * If the function fails, it will return NULL, but if the host was 49 * a numeric style address, you'll have the ip_address to fall back on. 50 */ 51 52 static struct hostent * 53 GetHostEntry(char *host, struct in_addr *ip_address) 54 { 55 struct in_addr ip; 56 struct hostent *hp; 57 58 /* See if the host was given in the dotted IP format, like "36.44.0.2." 59 * If it was, inet_addr will convert that to a 32-bit binary value; 60 * it not, inet_addr will return (-1L). 61 */ 62 ip.s_addr = inet_addr(host); 63 if (ip.s_addr != INADDR_NONE) { 64 hp = NULL; 65 } else { 66 /* No IP address, so it must be a hostname, like ftp.wustl.edu. */ 67 hp = gethostbyname(host); 68 if (hp != NULL) 69 (void) memcpy(&ip.s_addr, hp->h_addr_list[0], (size_t) hp->h_length); 70 } 71 if (ip_address != NULL) 72 *ip_address = ip; 73 return (hp); 74 } /* GetHostEntry */ 75 76 77 78 79 /* Makes every effort to return a fully qualified domain name. */ 80 int 81 GetOurHostName(char *host, size_t siz) 82 { 83 #ifdef HOSTNAME 84 /* You can hardcode in the name if this routine doesn't work 85 * the way you want it to. 86 */ 87 Strncpy(host, HOSTNAME, siz); 88 return (1); /* Success */ 89 #else 90 struct hostent *hp; 91 int result; 92 char **curAlias; 93 char domain[64]; 94 char *cp; 95 int rc; 96 97 host[0] = '\0'; 98 result = gethostname(host, (int) siz); 99 if ((result < 0) || (host[0] == '\0')) { 100 return (-1); 101 } 102 103 if (strchr(host, '.') != NULL) { 104 /* gethostname returned full name (like "cse.unl.edu"), instead 105 * of just the node name (like "cse"). 106 */ 107 return (2); /* Success */ 108 } 109 110 hp = gethostbyname(host); 111 if (hp != NULL) { 112 /* Maybe the host entry has the full name. */ 113 cp = strchr((char *) hp->h_name, '.'); 114 if ((cp != NULL) && (cp[1] != '\0')) { 115 /* The 'name' field for the host entry had full name. */ 116 (void) Strncpy(host, (char *) hp->h_name, siz); 117 return (3); /* Success */ 118 } 119 120 /* Now try the list of aliases, to see if any of those look real. */ 121 for (curAlias = hp->h_aliases; *curAlias != NULL; curAlias++) { 122 cp = strchr(*curAlias, '.'); 123 if ((cp != NULL) && (cp[1] != '\0')) { 124 (void) Strncpy(host, *curAlias, siz); 125 return (4); /* Success */ 126 } 127 } 128 } 129 130 /* Otherwise, we just have the node name. See if we can get the 131 * domain name ourselves. 132 */ 133 #ifdef DOMAINNAME 134 (void) STRNCPY(domain, DOMAINNAME); 135 rc = 5; 136 #else 137 rc = -1; 138 domain[0] = '\0'; 139 # if defined(HAVE_RES_INIT) && defined(HAVE__RES_DEFDNAME) 140 if (domain[0] == '\0') { 141 (void) res_init(); 142 if ((_res.defdname != NULL) && (_res.defdname[0] != '\0')) { 143 (void) STRNCPY(domain, _res.defdname); 144 rc = 6; 145 } 146 } 147 # endif /* HAVE_RES_INIT && HAVE__RES_DEFDNAME */ 148 149 if (domain[0] == '\0') { 150 FILE *fp; 151 char line[256]; 152 char *tok; 153 154 fp = fopen("/etc/resolv.conf", "r"); 155 if (fp != NULL) { 156 (void) memset(line, 0, sizeof(line)); 157 while (fgets(line, sizeof(line) - 1, fp) != NULL) { 158 if (!isalpha((int) line[0])) 159 continue; /* Skip comment lines. */ 160 tok = strtok(line, " \t\n\r"); 161 if (tok == NULL) 162 continue; /* Impossible */ 163 if (strcmp(tok, "domain") == 0) { 164 tok = strtok(NULL, " \t\n\r"); 165 if (tok == NULL) 166 continue; /* syntax error */ 167 (void) STRNCPY(domain, tok); 168 rc = 7; 169 break; /* Done. */ 170 } 171 } 172 (void) fclose(fp); 173 } 174 } 175 #endif /* DOMAINNAME */ 176 177 if (domain[0] != '\0') { 178 /* Supposedly, it's legal for a domain name with 179 * a period at the end. 180 */ 181 cp = domain + strlen(domain) - 1; 182 if (*cp == '.') 183 *cp = '\0'; 184 if (domain[0] != '.') 185 (void) Strncat(host, ".", siz); 186 (void) Strncat(host, domain, siz); 187 } 188 if (rc < 0) 189 host[0] = '\0'; 190 return(rc); /* Success */ 191 #endif /* !HOSTNAME */ 192 } /* GetOurHostName */ 193 194 195 196 void 197 CloseControlConnection(const FTPCIPtr cip) 198 { 199 /* This will close each file, if it was open. */ 200 #ifdef NO_SIGNALS 201 SClose(cip->ctrlSocketR, 3); 202 cip->ctrlSocketR = kClosedFileDescriptor; 203 cip->ctrlSocketW = kClosedFileDescriptor; 204 DisposeSReadlineInfo(&cip->ctrlSrl); 205 #else /* NO_SIGNALS */ 206 if (cip->ctrlTimeout > 0) 207 (void) alarm(cip->ctrlTimeout); 208 CloseFile(&cip->cin); 209 CloseFile(&cip->cout); 210 cip->ctrlSocketR = kClosedFileDescriptor; 211 cip->ctrlSocketW = kClosedFileDescriptor; 212 if (cip->ctrlTimeout > 0) 213 (void) alarm(0); 214 #endif /* NO_SIGNALS */ 215 cip->connected = 0; 216 cip->loggedIn = 0; 217 } /* CloseControlConnection */ 218 219 220 221 static int 222 GetSocketAddress(const FTPCIPtr cip, int sockfd, struct sockaddr_in *saddr) 223 { 224 int len = (int) sizeof (struct sockaddr_in); 225 int result = 0; 226 227 if (getsockname(sockfd, (struct sockaddr *)saddr, &len) < 0) { 228 Error(cip, kDoPerror, "Could not get socket name.\n"); 229 cip->errNo = kErrGetSockName; 230 result = kErrGetSockName; 231 } 232 return (result); 233 } /* GetSocketAddress */ 234 235 236 237 238 int 239 SetKeepAlive(const FTPCIPtr cip, int sockfd) 240 { 241 #ifndef SO_KEEPALIVE 242 cip->errNo = kErrSetKeepAlive; 243 return (kErrSetKeepAlive); 244 #else 245 int opt; 246 247 opt = 1; 248 249 if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, (int) sizeof(opt)) < 0) { 250 /* Error(cip, kDoPerror, "Could not set keep-alive mode.\n"); */ 251 cip->errNo = kErrSetKeepAlive; 252 return (kErrSetKeepAlive); 253 } 254 return (kNoErr); 255 #endif /* SO_KEEPALIVE */ 256 } /* SetKeepAlive */ 257 258 259 260 261 int 262 SetLinger(const FTPCIPtr cip, int sockfd, int onoff) 263 { 264 #ifndef SO_LINGER 265 cip->errNo = kErrSetLinger; 266 return (kErrSetLinger); 267 #else 268 struct linger li; 269 270 if (onoff != 0) { 271 li.l_onoff = 1; 272 li.l_linger = 120; /* 2 minutes, but system ignores field. */ 273 } else { 274 li.l_onoff = 0; 275 li.l_linger = 0; 276 } 277 /* Have the system make an effort to deliver any unsent data, 278 * even after we close the connection. 279 */ 280 if (setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char *) &li, (int) sizeof(li)) < 0) { 281 /* Error(cip, kDoPerror, "Could not set linger mode.\n"); */ 282 cip->errNo = kErrSetLinger; 283 return (kErrSetLinger); 284 } 285 return (kNoErr); 286 #endif /* SO_LINGER */ 287 } /* SetLinger */ 288 289 290 291 292 #ifdef IP_TOS 293 int 294 SetTypeOfService(const FTPCIPtr cip, int sockfd, int tosType) 295 { 296 /* Specify to the router what type of connection this is, so it 297 * can prioritize packets. 298 */ 299 if (setsockopt(sockfd, IPPROTO_IP, IP_TOS, (char *) &tosType, (int) sizeof(tosType)) < 0) { 300 /* Error(cip, kDoPerror, "Could not set type of service.\n"); */ 301 cip->errNo = kErrSetTypeOfService; 302 return (kErrSetTypeOfService); 303 } 304 return (kNoErr); 305 } /* SetTypeOfService */ 306 #endif /* IP_TOS */ 307 308 309 310 311 #ifdef SO_OOBINLINE 312 int 313 SetInlineOutOfBandData(const FTPCIPtr cip, int sockfd) 314 { 315 int on = 1; 316 317 if (setsockopt(sockfd, SOL_SOCKET, SO_OOBINLINE, (char *) &on, (int) sizeof(on)) < 0) { 318 Error(cip, kDoPerror, "Could not set out of band inline mode.\n"); 319 cip->errNo = kErrSetOutOfBandInline; 320 return (kErrSetOutOfBandInline); 321 } 322 return (kNoErr); 323 } /* SetInlineOutOfBandData */ 324 #endif /* SO_OOBINLINE */ 325 326 327 328 329 #ifndef NO_SIGNALS 330 331 static void 332 CancelConnect(int signum) 333 { 334 gGotSig = signum; 335 #ifdef HAVE_SIGSETJMP 336 siglongjmp(gCancelConnectJmp, 1); 337 #else 338 longjmp(gCancelConnectJmp, 1); 339 #endif /* HAVE_SIGSETJMP */ 340 } /* CancelConnect */ 341 342 #endif /* NO_SIGNALS */ 343 344 345 346 int 347 OpenControlConnection(const FTPCIPtr cip, char *host, unsigned int port) 348 { 349 struct in_addr ip_address; 350 int err = 0; 351 int result; 352 int oerrno; 353 volatile int sockfd = -1; 354 volatile int sock2fd = -1; 355 ResponsePtr rp; 356 char **volatile curaddr; 357 struct hostent *hp; 358 char *volatile fhost; 359 unsigned int fport; 360 #ifndef NO_SIGNALS 361 volatile FTPSigProc osigint; 362 volatile FTPSigProc osigalrm; 363 volatile FTPCIPtr vcip; 364 int sj; 365 #endif /* NO_SIGNALS */ 366 const char *firstLine, *secondLine, *srvr; 367 368 LIBNCFTP_USE_VAR(gLibNcFTPVersion); 369 LIBNCFTP_USE_VAR(gCopyright); 370 #ifdef NO_SIGNALS 371 LIBNCFTP_USE_VAR(gNoSignalsMarker); 372 #endif /* NO_SIGNALS */ 373 374 if (cip->firewallType == kFirewallNotInUse) { 375 fhost = host; 376 fport = port; 377 } else { 378 fhost = cip->firewallHost; 379 fport = cip->firewallPort; 380 } 381 if (fport == 0) 382 fport = cip->lip->defaultPort; 383 384 /* Since we're the client, we just have to get a socket() and 385 * connect() it. 386 */ 387 (void) ZERO(cip->servCtlAddr); 388 cip->cin = NULL; 389 cip->cout = NULL; 390 391 /* Make sure we use network byte-order. */ 392 fport = (unsigned int) htons((unsigned short) fport); 393 394 cip->servCtlAddr.sin_port = (unsigned short) fport; 395 396 hp = GetHostEntry(fhost, &ip_address); 397 398 if (hp == NULL) { 399 /* Okay, no Host entry, but maybe we have a numeric address 400 * in ip_address we can try. 401 */ 402 if (ip_address.s_addr == INADDR_NONE) { 403 Error(cip, kDontPerror, "%s: unknown host.\n", fhost); 404 cip->errNo = kErrHostUnknown; 405 return (kErrHostUnknown); 406 } 407 cip->servCtlAddr.sin_family = AF_INET; 408 cip->servCtlAddr.sin_addr.s_addr = ip_address.s_addr; 409 } else { 410 cip->servCtlAddr.sin_family = hp->h_addrtype; 411 /* We'll fill in the rest of the structure below. */ 412 } 413 414 /* After obtaining a socket, try to connect it to a remote 415 * address. If we didn't get a host entry, we will only have 416 * one thing to try (ip_address); if we do have one, we can try 417 * every address in the list from the host entry. 418 */ 419 420 if (hp == NULL) { 421 /* Since we're given a single raw address, and not a host entry, 422 * we can only try this one address and not any other addresses 423 * that could be present for a site with a host entry. 424 */ 425 426 if ((sockfd = socket(cip->servCtlAddr.sin_family, SOCK_STREAM, 0)) < 0) { 427 Error(cip, kDoPerror, "Could not get a socket.\n"); 428 cip->errNo = kErrNewStreamSocket; 429 return (kErrNewStreamSocket); 430 } 431 432 /* This doesn't do anything if you left these 433 * at their defaults (zero). Otherwise it 434 * tries to set the buffer size to the 435 * size specified. 436 */ 437 (void) SetSockBufSize(sockfd, cip->ctrlSocketRBufSize, cip->ctrlSocketSBufSize); 438 439 #ifdef NO_SIGNALS 440 err = SConnect(sockfd, &cip->servCtlAddr, (int) cip->connTimeout); 441 442 if (err < 0) { 443 oerrno = errno; 444 (void) SClose(sockfd, 3); 445 errno = oerrno; 446 sockfd = -1; 447 } 448 #else /* NO_SIGNALS */ 449 osigint = (volatile FTPSigProc) signal(SIGINT, CancelConnect); 450 if (cip->connTimeout > 0) { 451 osigalrm = (volatile FTPSigProc) signal(SIGALRM, CancelConnect); 452 (void) alarm(cip->connTimeout); 453 } 454 455 vcip = cip; 456 457 #ifdef HAVE_SIGSETJMP 458 sj = sigsetjmp(gCancelConnectJmp, 1); 459 #else 460 sj = setjmp(gCancelConnectJmp); 461 #endif /* HAVE_SIGSETJMP */ 462 463 if (sj != 0) { 464 /* Interrupted by a signal. */ 465 (void) closesocket(sockfd); 466 (void) signal(SIGINT, (FTPSigProc) osigint); 467 if (vcip->connTimeout > 0) { 468 (void) alarm(0); 469 (void) signal(SIGALRM, (FTPSigProc) osigalrm); 470 } 471 if (gGotSig == SIGINT) { 472 result = vcip->errNo = kErrConnectMiscErr; 473 Error(vcip, kDontPerror, "Connection attempt canceled.\n"); 474 (void) kill(getpid(), SIGINT); 475 } else if (gGotSig == SIGALRM) { 476 result = vcip->errNo = kErrConnectRetryableErr; 477 Error(vcip, kDontPerror, "Connection attempt timed-out.\n"); 478 (void) kill(getpid(), SIGALRM); 479 } else { 480 result = vcip->errNo = kErrConnectMiscErr; 481 Error(vcip, kDontPerror, "Connection attempt failed due to an unexpected signal (%d).\n", gGotSig); 482 } 483 return (result); 484 } else { 485 err = connect(sockfd, (struct sockaddr *) &cip->servCtlAddr, 486 (int) sizeof (cip->servCtlAddr)); 487 if (cip->connTimeout > 0) { 488 (void) alarm(0); 489 (void) signal(SIGALRM, (FTPSigProc) osigalrm); 490 } 491 (void) signal(SIGINT, (FTPSigProc) osigint); 492 } 493 494 if (err < 0) { 495 oerrno = errno; 496 (void) closesocket(sockfd); 497 errno = oerrno; 498 sockfd = -1; 499 } 500 #endif /* NO_SIGNALS */ 501 } else { 502 /* We can try each address in the list. We'll quit when we 503 * run out of addresses to try or get a successful connection. 504 */ 505 for (curaddr = hp->h_addr_list; *curaddr != NULL; curaddr++) { 506 if ((sockfd = socket(cip->servCtlAddr.sin_family, SOCK_STREAM, 0)) < 0) { 507 Error(cip, kDoPerror, "Could not get a socket.\n"); 508 cip->errNo = kErrNewStreamSocket; 509 return (kErrNewStreamSocket); 510 } 511 /* This could overwrite the address field in the structure, 512 * but this is okay because the structure has a junk field 513 * just for this purpose. 514 */ 515 (void) memcpy(&cip->servCtlAddr.sin_addr, *curaddr, (size_t) hp->h_length); 516 517 /* This doesn't do anything if you left these 518 * at their defaults (zero). Otherwise it 519 * tries to set the buffer size to the 520 * size specified. 521 */ 522 (void) SetSockBufSize(sockfd, cip->ctrlSocketRBufSize, cip->ctrlSocketSBufSize); 523 524 #ifdef NO_SIGNALS 525 err = SConnect(sockfd, &cip->servCtlAddr, (int) cip->connTimeout); 526 527 if (err == 0) 528 break; 529 oerrno = errno; 530 (void) SClose(sockfd, 3); 531 errno = oerrno; 532 sockfd = -1; 533 #else /* NO_SIGNALS */ 534 535 osigint = (volatile FTPSigProc) signal(SIGINT, CancelConnect); 536 if (cip->connTimeout > 0) { 537 osigalrm = (volatile FTPSigProc) signal(SIGALRM, CancelConnect); 538 (void) alarm(cip->connTimeout); 539 } 540 541 vcip = cip; 542 #ifdef HAVE_SIGSETJMP 543 sj = sigsetjmp(gCancelConnectJmp, 1); 544 #else 545 sj = setjmp(gCancelConnectJmp); 546 #endif /* HAVE_SIGSETJMP */ 547 548 if (sj != 0) { 549 /* Interrupted by a signal. */ 550 (void) closesocket(sockfd); 551 (void) signal(SIGINT, (FTPSigProc) osigint); 552 if (vcip->connTimeout > 0) { 553 (void) alarm(0); 554 (void) signal(SIGALRM, (FTPSigProc) osigalrm); 555 } 556 if (gGotSig == SIGINT) { 557 result = vcip->errNo = kErrConnectMiscErr; 558 Error(vcip, kDontPerror, "Connection attempt canceled.\n"); 559 (void) kill(getpid(), SIGINT); 560 } else if (gGotSig == SIGALRM) { 561 result = vcip->errNo = kErrConnectRetryableErr; 562 Error(vcip, kDontPerror, "Connection attempt timed-out.\n"); 563 (void) kill(getpid(), SIGALRM); 564 } else { 565 result = vcip->errNo = kErrConnectMiscErr; 566 Error(vcip, kDontPerror, "Connection attempt failed due to an unexpected signal (%d).\n", gGotSig); 567 } 568 return (result); 569 } else { 570 err = connect(sockfd, (struct sockaddr *) &cip->servCtlAddr, 571 (int) sizeof (cip->servCtlAddr)); 572 if (cip->connTimeout > 0) { 573 (void) alarm(0); 574 (void) signal(SIGALRM, (FTPSigProc) osigalrm); 575 } 576 (void) signal(SIGINT, (FTPSigProc) osigint); 577 } 578 579 if (err == 0) 580 break; 581 oerrno = errno; 582 (void) closesocket(sockfd); 583 errno = oerrno; 584 sockfd = -1; 585 #endif /* NO_SIGNALS */ 586 } 587 } 588 589 if (err < 0) { 590 /* Could not connect. Close up shop and go home. */ 591 592 /* If possible, tell the caller if they should bother 593 * calling back later. 594 */ 595 switch (errno) { 596 #ifdef ENETDOWN 597 case ENETDOWN: 598 #elif defined(WSAENETDOWN) 599 case WSAENETDOWN: 600 #endif 601 #ifdef ENETUNREACH 602 case ENETUNREACH: 603 #elif defined(WSAENETUNREACH) 604 case WSAENETUNREACH: 605 #endif 606 #ifdef ECONNABORTED 607 case ECONNABORTED: 608 #elif defined(WSAECONNABORTED) 609 case WSAECONNABORTED: 610 #endif 611 #ifdef ETIMEDOUT 612 case ETIMEDOUT: 613 #elif defined(WSAETIMEDOUT) 614 case WSAETIMEDOUT: 615 #endif 616 #ifdef EHOSTDOWN 617 case EHOSTDOWN: 618 #elif defined(WSAEHOSTDOWN) 619 case WSAEHOSTDOWN: 620 #endif 621 #ifdef ECONNRESET 622 case ECONNRESET: 623 #elif defined(WSAECONNRESET) 624 case WSAECONNRESET: 625 #endif 626 Error(cip, kDoPerror, "Could not connect to %s -- try again later.\n", fhost); 627 result = cip->errNo = kErrConnectRetryableErr; 628 break; 629 #ifdef ECONNREFUSED 630 case ECONNREFUSED: 631 #elif defined(WSAECONNREFUSED) 632 case WSAECONNREFUSED: 633 #endif 634 Error(cip, kDoPerror, "Could not connect to %s.\n", fhost); 635 result = cip->errNo = kErrConnectRefused; 636 break; 637 default: 638 Error(cip, kDoPerror, "Could not connect to %s.\n", fhost); 639 result = cip->errNo = kErrConnectMiscErr; 640 } 641 goto fatal; 642 } 643 644 /* Get our end of the socket address for later use. */ 645 if ((result = GetSocketAddress(cip, sockfd, &cip->ourCtlAddr)) < 0) 646 goto fatal; 647 648 #ifdef SO_OOBINLINE 649 /* We want Out-of-band data to appear in the regular stream, 650 * since we can handle TELNET. 651 */ 652 (void) SetInlineOutOfBandData(cip, sockfd); 653 #endif 654 (void) SetKeepAlive(cip, sockfd); 655 (void) SetLinger(cip, sockfd, 0); /* Don't need it for ctrl. */ 656 657 #if defined(IP_TOS) && defined(IPTOS_LOWDELAY) 658 /* Control connection is somewhat interactive, so quick response 659 * is desired. 660 */ 661 (void) SetTypeOfService(cip, sockfd, IPTOS_LOWDELAY); 662 #endif 663 664 #ifdef NO_SIGNALS 665 cip->ctrlSocketR = sockfd; 666 cip->ctrlSocketW = sockfd; 667 cip->cout = NULL; 668 cip->cin = NULL; 669 sock2fd = kClosedFileDescriptor; 670 671 if (InitSReadlineInfo(&cip->ctrlSrl, sockfd, cip->srlBuf, sizeof(cip->srlBuf), (int) cip->ctrlTimeout, 1) < 0) { 672 result = kErrFdopenW; 673 cip->errNo = kErrFdopenW; 674 Error(cip, kDoPerror, "Could not fdopen.\n"); 675 goto fatal; 676 } 677 #else /* NO_SIGNALS */ 678 if ((sock2fd = dup(sockfd)) < 0) { 679 result = kErrDupSocket; 680 cip->errNo = kErrDupSocket; 681 Error(cip, kDoPerror, "Could not duplicate a file descriptor.\n"); 682 goto fatal; 683 } 684 685 /* Now setup the FILE pointers for use with the Std I/O library 686 * routines. 687 */ 688 if ((cip->cin = fdopen(sockfd, "r")) == NULL) { 689 result = kErrFdopenR; 690 cip->errNo = kErrFdopenR; 691 Error(cip, kDoPerror, "Could not fdopen.\n"); 692 goto fatal; 693 } 694 695 if ((cip->cout = fdopen(sock2fd, "w")) == NULL) { 696 result = kErrFdopenW; 697 cip->errNo = kErrFdopenW; 698 Error(cip, kDoPerror, "Could not fdopen.\n"); 699 CloseFile(&cip->cin); 700 sockfd = kClosedFileDescriptor; 701 goto fatal; 702 } 703 704 cip->ctrlSocketR = sockfd; 705 cip->ctrlSocketW = sockfd; 706 707 /* We'll be reading and writing lines, so use line buffering. This 708 * is necessary since the stdio library will use full buffering 709 * for all streams not associated with the tty. 710 */ 711 #ifdef HAVE_SETLINEBUF 712 setlinebuf(cip->cin); 713 setlinebuf(cip->cout); 714 #else 715 (void) SETVBUF(cip->cin, NULL, _IOLBF, (size_t) BUFSIZ); 716 (void) SETVBUF(cip->cout, NULL, _IOLBF, (size_t) BUFSIZ); 717 #endif 718 #endif /* NO_SIGNALS */ 719 720 #ifdef HAVE_INET_NTOP /* Mostly to workaround bug in IRIX 6.5's inet_ntoa */ 721 (void) memset(cip->ip, 0, sizeof(cip->ip)); 722 (void) inet_ntop(AF_INET, &cip->servCtlAddr.sin_addr, cip->ip, sizeof(cip->ip) - 1); 723 #else 724 (void) STRNCPY(cip->ip, inet_ntoa(cip->servCtlAddr.sin_addr)); 725 #endif 726 if ((hp == NULL) || (hp->h_name == NULL)) 727 (void) STRNCPY(cip->actualHost, fhost); 728 else 729 (void) STRNCPY(cip->actualHost, (char *) hp->h_name); 730 731 /* Read the startup message from the server. */ 732 rp = InitResponse(); 733 if (rp == NULL) { 734 Error(cip, kDontPerror, "Malloc failed.\n"); 735 cip->errNo = kErrMallocFailed; 736 result = cip->errNo; 737 goto fatal; 738 } 739 740 result = GetResponse(cip, rp); 741 if ((result < 0) && (rp->msg.first == NULL)) { 742 goto fatal; 743 } 744 if (rp->msg.first != NULL) { 745 cip->serverType = kServerTypeUnknown; 746 srvr = NULL; 747 firstLine = rp->msg.first->line; 748 secondLine = NULL; 749 if (rp->msg.first->next != NULL) 750 secondLine = rp->msg.first->next->line; 751 752 if (strstr(firstLine, "Version wu-") != NULL) { 753 cip->serverType = kServerTypeWuFTPd; 754 srvr = "wu-ftpd"; 755 } else if (strstr(firstLine, "NcFTPd") != NULL) { 756 cip->serverType = kServerTypeNcFTPd; 757 srvr = "NcFTPd Server"; 758 } else if (STRNEQ("ProFTPD", firstLine, 7)) { 759 cip->serverType = kServerTypeProFTPD; 760 srvr = "ProFTPD"; 761 } else if (strstr(firstLine, "Microsoft FTP Service") != NULL) { 762 cip->serverType = kServerTypeMicrosoftFTP; 763 srvr = "Microsoft FTP Service"; 764 } else if (strstr(firstLine, "(NetWare ") != NULL) { 765 cip->serverType = kServerTypeNetWareFTP; 766 srvr = "NetWare FTP Service"; 767 } else if (STRNEQ("WFTPD", firstLine, 5)) { 768 cip->serverType = kServerTypeWFTPD; 769 srvr = "WFTPD"; 770 } else if (STRNEQ("Serv-U FTP", firstLine, 10)) { 771 cip->serverType = kServerTypeServ_U; 772 srvr = "Serv-U FTP-Server"; 773 } else if (strstr(firstLine, "VFTPD") != NULL) { 774 cip->serverType = kServerTypeVFTPD; 775 srvr = "VFTPD"; 776 } else if (STRNEQ("FTP-Max", firstLine, 7)) { 777 cip->serverType = kServerTypeFTP_Max; 778 srvr = "FTP-Max"; 779 } else if (strstr(firstLine, "Roxen") != NULL) { 780 cip->serverType = kServerTypeRoxen; 781 srvr = "Roxen"; 782 } else if (strstr(firstLine, "WS_FTP") != NULL) { 783 cip->serverType = kServerTypeWS_FTP; 784 srvr = "WS_FTP Server"; 785 } else if ((secondLine != NULL) && (strstr(secondLine, "WarFTP") != NULL)) { 786 cip->serverType = kServerTypeWarFTPd; 787 srvr = "WarFTPd"; 788 } 789 790 if (srvr != NULL) 791 PrintF(cip, "Remote server is running %s.\n", srvr); 792 793 /* Do the application's connect message callback, if present. */ 794 if ((cip->onConnectMsgProc != 0) && (rp->codeType < 4)) 795 (*cip->onConnectMsgProc)(cip, rp); 796 } 797 798 if (rp->codeType >= 4) { 799 /* They probably hung up on us right away. That's too bad, 800 * but we can tell the caller that they can call back later 801 * and try again. 802 */ 803 DoneWithResponse(cip, rp); 804 result = kErrConnectRetryableErr; 805 Error(cip, kDontPerror, "Server hungup immediately after connect.\n"); 806 cip->errNo = kErrConnectRetryableErr; 807 goto fatal; 808 } 809 if (result < 0) /* Some other error occurred during connect message */ 810 goto fatal; 811 cip->connected = 1; 812 DoneWithResponse(cip, rp); 813 return (kNoErr); 814 815 fatal: 816 if (sockfd > 0) 817 (void) closesocket(sockfd); 818 if (sock2fd > 0) 819 (void) closesocket(sock2fd); 820 CloseFile(&cip->cin); 821 CloseFile(&cip->cout); 822 cip->ctrlSocketR = kClosedFileDescriptor; 823 cip->ctrlSocketW = kClosedFileDescriptor; 824 return (result); 825 } /* OpenControlConnection */ 826 827 828 829 830 void 831 CloseDataConnection(const FTPCIPtr cip) 832 { 833 if (cip->dataSocket != kClosedFileDescriptor) { 834 #ifdef NO_SIGNALS 835 SClose(cip->dataSocket, 3); 836 #else /* NO_SIGNALS */ 837 if (cip->xferTimeout > 0) 838 (void) alarm(cip->xferTimeout); 839 (void) closesocket(cip->dataSocket); 840 if (cip->xferTimeout > 0) 841 (void) alarm(0); 842 #endif /* NO_SIGNALS */ 843 cip->dataSocket = kClosedFileDescriptor; 844 } 845 memset(&cip->ourDataAddr, 0, sizeof(cip->ourDataAddr)); 846 memset(&cip->servDataAddr, 0, sizeof(cip->servDataAddr)); 847 } /* CloseDataConnection */ 848 849 850 851 852 int 853 SetStartOffset(const FTPCIPtr cip, longest_int restartPt) 854 { 855 ResponsePtr rp; 856 int result; 857 858 if (restartPt != (longest_int) 0) { 859 rp = InitResponse(); 860 if (rp == NULL) { 861 Error(cip, kDontPerror, "Malloc failed.\n"); 862 cip->errNo = kErrMallocFailed; 863 return (cip->errNo); 864 } 865 866 /* Force reset to offset zero. */ 867 if (restartPt == (longest_int) -1) 868 restartPt = (longest_int) 0; 869 #ifdef PRINTF_LONG_LONG 870 result = RCmd(cip, rp, 871 "REST " PRINTF_LONG_LONG, 872 restartPt); 873 #else 874 result = RCmd(cip, rp, "REST %ld", (long) restartPt); 875 #endif 876 877 if (result < 0) { 878 return (result); 879 } else if (result == 3) { 880 cip->hasREST = kCommandAvailable; 881 DoneWithResponse(cip, rp); 882 } else if (UNIMPLEMENTED_CMD(rp->code)) { 883 cip->hasREST = kCommandNotAvailable; 884 DoneWithResponse(cip, rp); 885 cip->errNo = kErrSetStartPoint; 886 return (kErrSetStartPoint); 887 } else { 888 DoneWithResponse(cip, rp); 889 cip->errNo = kErrSetStartPoint; 890 return (kErrSetStartPoint); 891 } 892 } 893 return (0); 894 } /* SetStartOffset */ 895 896 897 898 static int 899 SendPort(const FTPCIPtr cip, struct sockaddr_in *saddr) 900 { 901 char *a, *p; 902 int result; 903 ResponsePtr rp; 904 905 rp = InitResponse(); 906 if (rp == NULL) { 907 Error(cip, kDontPerror, "Malloc failed.\n"); 908 cip->errNo = kErrMallocFailed; 909 return (cip->errNo); 910 } 911 912 /* These will point to data in network byte order. */ 913 a = (char *) &saddr->sin_addr; 914 p = (char *) &saddr->sin_port; 915 #define UC(x) (int) (((int) x) & 0xff) 916 917 /* Need to tell the other side which host (the address) and 918 * which process (port) on that host to send data to. 919 */ 920 result = RCmd(cip, rp, "PORT %d,%d,%d,%d,%d,%d", 921 UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 922 923 if (result < 0) { 924 return (result); 925 } else if (result != 2) { 926 /* A 500'ish response code means the PORT command failed. */ 927 DoneWithResponse(cip, rp); 928 cip->errNo = kErrPORTFailed; 929 return (cip->errNo); 930 } 931 DoneWithResponse(cip, rp); 932 return (kNoErr); 933 } /* SendPort */ 934 935 936 937 938 static int 939 Passive(const FTPCIPtr cip, struct sockaddr_in *saddr, int *weird) 940 { 941 ResponsePtr rp; 942 int i[6], j; 943 unsigned char n[6]; 944 char *cp; 945 int result; 946 947 rp = InitResponse(); 948 if (rp == NULL) { 949 Error(cip, kDontPerror, "Malloc failed.\n"); 950 cip->errNo = kErrMallocFailed; 951 return (cip->errNo); 952 } 953 954 result = RCmd(cip, rp, "PASV"); 955 if (result < 0) 956 goto done; 957 958 if (rp->codeType != 2) { 959 /* Didn't understand or didn't want passive port selection. */ 960 cip->errNo = result = kErrPASVFailed; 961 goto done; 962 } 963 964 /* The other side returns a specification in the form of 965 * an internet address as the first four integers (each 966 * integer stands for 8-bits of the real 32-bit address), 967 * and two more integers for the port (16-bit port). 968 * 969 * It should give us something like: 970 * "Entering Passive Mode (129,93,33,1,10,187)", so look for 971 * digits with sscanf() starting 24 characters down the string. 972 */ 973 for (cp = rp->msg.first->line; ; cp++) { 974 if (*cp == '\0') { 975 Error(cip, kDontPerror, "Cannot parse PASV response: %s\n", rp->msg.first->line); 976 goto done; 977 } 978 if (isdigit((int) *cp)) 979 break; 980 } 981 982 if (sscanf(cp, "%d,%d,%d,%d,%d,%d", 983 &i[0], &i[1], &i[2], &i[3], &i[4], &i[5]) != 6) { 984 Error(cip, kDontPerror, "Cannot parse PASV response: %s\n", rp->msg.first->line); 985 goto done; 986 } 987 988 for (j=0, *weird = 0; j<6; j++) { 989 /* Some ftp servers return bogus port octets, such as 990 * boombox.micro.umn.edu. Let the caller know if we got a 991 * weird looking octet. 992 */ 993 if ((i[j] < 0) || (i[j] > 255)) 994 *weird = *weird + 1; 995 n[j] = (unsigned char) (i[j] & 0xff); 996 } 997 998 (void) memcpy(&saddr->sin_addr, &n[0], (size_t) 4); 999 (void) memcpy(&saddr->sin_port, &n[4], (size_t) 2); 1000 1001 result = kNoErr; 1002 done: 1003 DoneWithResponse(cip, rp); 1004 return (result); 1005 } /* Passive */ 1006 1007 1008 1009 1010 static int 1011 BindToEphemeralPortNumber(int sockfd, struct sockaddr_in *addrp, int ephemLo, int ephemHi) 1012 { 1013 int i; 1014 int result; 1015 int rangesize; 1016 unsigned short port; 1017 1018 addrp->sin_family = AF_INET; 1019 if (((int) ephemLo == 0) || ((int) ephemLo >= (int) ephemHi)) { 1020 /* Do it the normal way. System will 1021 * pick one, typically in the range 1022 * of 1024-4999. 1023 */ 1024 addrp->sin_port = 0; /* Let system pick one. */ 1025 1026 result = bind(sockfd, (struct sockaddr *) addrp, sizeof(struct sockaddr_in)); 1027 } else { 1028 rangesize = (int) ((int) ephemHi - (int) ephemLo); 1029 result = 0; 1030 for (i=0; i<10; i++) { 1031 port = (unsigned short) (((int) rand() % rangesize) + (int) ephemLo); 1032 addrp->sin_port = port; 1033 1034 result = bind(sockfd, (struct sockaddr *) addrp, sizeof(struct sockaddr_in)); 1035 if (result == 0) 1036 break; 1037 if ((errno != 999) 1038 /* This next line is just fodder to 1039 * shut the compiler up about variable 1040 * not being used. 1041 */ 1042 && (gCopyright[0] != '\0')) 1043 break; 1044 } 1045 } 1046 return (result); 1047 } /* BindToEphemeralPortNumber */ 1048 1049 1050 1051 1052 int 1053 OpenDataConnection(const FTPCIPtr cip, int mode) 1054 { 1055 int dataSocket; 1056 int weirdPort; 1057 int result; 1058 1059 /* Before we can transfer any data, and before we even ask the 1060 * remote server to start transferring via RETR/NLST/etc, we have 1061 * to setup the connection. 1062 */ 1063 1064 tryPort2: 1065 weirdPort = 0; 1066 result = 0; 1067 CloseDataConnection(cip); /* In case we didn't before... */ 1068 1069 dataSocket = socket(AF_INET, SOCK_STREAM, 0); 1070 if (dataSocket < 0) { 1071 Error(cip, kDoPerror, "Could not get a data socket.\n"); 1072 result = kErrNewStreamSocket; 1073 cip->errNo = kErrNewStreamSocket; 1074 return result; 1075 } 1076 1077 /* This doesn't do anything if you left these 1078 * at their defaults (zero). Otherwise it 1079 * tries to set the buffer size to the 1080 * size specified. 1081 */ 1082 (void) SetSockBufSize(dataSocket, cip->dataSocketRBufSize, cip->dataSocketSBufSize); 1083 1084 if ((cip->hasPASV == kCommandNotAvailable) || (mode == kSendPortMode)) { 1085 tryPort: 1086 cip->ourDataAddr = cip->ourCtlAddr; 1087 cip->ourDataAddr.sin_family = AF_INET; 1088 1089 #ifdef HAVE_LIBSOCKS 1090 cip->ourDataAddr.sin_port = 0; 1091 if (Rbind(dataSocket, (struct sockaddr *) &cip->ourDataAddr, 1092 (int) sizeof (cip->ourDataAddr), 1093 cip->servCtlAddr.sin_addr.s_addr) < 0) 1094 #else 1095 if (BindToEphemeralPortNumber(dataSocket, &cip->ourDataAddr, (int) cip->ephemLo, (int) cip->ephemHi) < 0) 1096 #endif 1097 { 1098 Error(cip, kDoPerror, "Could not bind the data socket"); 1099 result = kErrBindDataSocket; 1100 cip->errNo = kErrBindDataSocket; 1101 goto bad; 1102 } 1103 1104 /* Need to do this so we can figure out which port the system 1105 * gave to us. 1106 */ 1107 if ((result = GetSocketAddress(cip, dataSocket, &cip->ourDataAddr)) < 0) 1108 goto bad; 1109 1110 if (listen(dataSocket, 1) < 0) { 1111 Error(cip, kDoPerror, "listen failed"); 1112 result = kErrListenDataSocket; 1113 cip->errNo = kErrListenDataSocket; 1114 goto bad; 1115 } 1116 1117 if ((result = SendPort(cip, &cip->ourDataAddr)) < 0) 1118 goto bad; 1119 1120 cip->dataPortMode = kSendPortMode; 1121 } else { 1122 /* Passive mode. Let the other side decide where to send. */ 1123 1124 cip->servDataAddr = cip->servCtlAddr; 1125 cip->servDataAddr.sin_family = AF_INET; 1126 cip->ourDataAddr = cip->ourCtlAddr; 1127 cip->ourDataAddr.sin_family = AF_INET; 1128 1129 if (Passive(cip, &cip->servDataAddr, &weirdPort) < 0) { 1130 Error(cip, kDontPerror, "Passive mode refused.\n"); 1131 cip->hasPASV = kCommandNotAvailable; 1132 1133 /* We can try using regular PORT commands, which are required 1134 * by all FTP protocol compliant programs, if you said so. 1135 * 1136 * We don't do this automatically, because if your host 1137 * is running a firewall you (probably) do not want SendPort 1138 * FTP for security reasons. 1139 */ 1140 if (mode == kFallBackToSendPortMode) 1141 goto tryPort; 1142 result = kErrPassiveModeFailed; 1143 cip->errNo = kErrPassiveModeFailed; 1144 goto bad; 1145 } 1146 1147 #ifdef HAVE_LIBSOCKS 1148 cip->ourDataAddr.sin_port = 0; 1149 if (Rbind(dataSocket, (struct sockaddr *) &cip->ourDataAddr, 1150 (int) sizeof (cip->ourDataAddr), 1151 cip->servCtlAddr.sin_addr.s_addr) < 0) 1152 #else 1153 if (BindToEphemeralPortNumber(dataSocket, &cip->ourDataAddr, (int) cip->ephemLo, (int) cip->ephemHi) < 0) 1154 #endif 1155 { 1156 Error(cip, kDoPerror, "Could not bind the data socket"); 1157 result = kErrBindDataSocket; 1158 cip->errNo = kErrBindDataSocket; 1159 goto bad; 1160 } 1161 1162 #ifdef NO_SIGNALS 1163 result = SConnect(dataSocket, &cip->servDataAddr, (int) cip->connTimeout); 1164 #else /* NO_SIGNALS */ 1165 if (cip->connTimeout > 0) 1166 (void) alarm(cip->connTimeout); 1167 1168 result = connect(dataSocket, (struct sockaddr *) &cip->servDataAddr, (int) sizeof(cip->servDataAddr)); 1169 if (cip->connTimeout > 0) 1170 (void) alarm(0); 1171 #endif /* NO_SIGNALS */ 1172 1173 #ifdef NO_SIGNALS 1174 if (result == kTimeoutErr) { 1175 if (mode == kFallBackToSendPortMode) { 1176 Error(cip, kDontPerror, "Data connection timed out.\n"); 1177 Error(cip, kDontPerror, "Falling back to PORT instead of PASV mode.\n"); 1178 (void) closesocket(dataSocket); 1179 cip->hasPASV = kCommandNotAvailable; 1180 goto tryPort2; 1181 } 1182 Error(cip, kDontPerror, "Data connection timed out.\n"); 1183 result = kErrConnectDataSocket; 1184 cip->errNo = kErrConnectDataSocket; 1185 } else 1186 #endif /* NO_SIGNALS */ 1187 1188 if (result < 0) { 1189 #ifdef ECONNREFUSED 1190 if ((weirdPort > 0) && (errno == ECONNREFUSED)) { 1191 #elif defined(WSAECONNREFUSED) 1192 if ((weirdPort > 0) && (errno == WSAECONNREFUSED)) { 1193 #endif 1194 Error(cip, kDontPerror, "Server sent back a bogus port number.\nI will fall back to PORT instead of PASV mode.\n"); 1195 if (mode == kFallBackToSendPortMode) { 1196 (void) closesocket(dataSocket); 1197 cip->hasPASV = kCommandNotAvailable; 1198 goto tryPort2; 1199 } 1200 result = kErrServerSentBogusPortNumber; 1201 cip->errNo = kErrServerSentBogusPortNumber; 1202 goto bad; 1203 } 1204 if (mode == kFallBackToSendPortMode) { 1205 Error(cip, kDoPerror, "connect failed.\n"); 1206 Error(cip, kDontPerror, "Falling back to PORT instead of PASV mode.\n"); 1207 (void) closesocket(dataSocket); 1208 cip->hasPASV = kCommandNotAvailable; 1209 goto tryPort2; 1210 } 1211 Error(cip, kDoPerror, "connect failed.\n"); 1212 result = kErrConnectDataSocket; 1213 cip->errNo = kErrConnectDataSocket; 1214 goto bad; 1215 } 1216 1217 /* Need to do this so we can figure out which port the system 1218 * gave to us. 1219 */ 1220 if ((result = GetSocketAddress(cip, dataSocket, &cip->ourDataAddr)) < 0) 1221 goto bad; 1222 1223 cip->dataPortMode = kPassiveMode; 1224 cip->hasPASV = kCommandAvailable; 1225 } 1226 1227 (void) SetLinger(cip, dataSocket, 1); 1228 (void) SetKeepAlive(cip, dataSocket); 1229 1230 #if defined(IP_TOS) && defined(IPTOS_THROUGHPUT) 1231 /* Data connection is a non-interactive data stream, so 1232 * high throughput is desired, at the expense of low 1233 * response time. 1234 */ 1235 (void) SetTypeOfService(cip, dataSocket, IPTOS_THROUGHPUT); 1236 #endif 1237 1238 cip->dataSocket = dataSocket; 1239 return (0); 1240 bad: 1241 (void) closesocket(dataSocket); 1242 return (result); 1243 } /* OpenDataConnection */ 1244 1245 1246 1247 1248 int 1249 AcceptDataConnection(const FTPCIPtr cip) 1250 { 1251 int newSocket; 1252 #ifndef NO_SIGNALS 1253 int len; 1254 #endif 1255 unsigned short remoteDataPort; 1256 unsigned short remoteCtrlPort; 1257 1258 /* If we did a PORT, we have some things to finish up. 1259 * If we did a PASV, we're ready to go. 1260 */ 1261 if (cip->dataPortMode == kSendPortMode) { 1262 /* Accept will give us back the server's data address; at the 1263 * moment we don't do anything with it though. 1264 */ 1265 memset(&cip->servDataAddr, 0, sizeof(cip->servDataAddr)); 1266 1267 #ifdef NO_SIGNALS 1268 newSocket = SAccept(cip->dataSocket, &cip->servDataAddr, (int) cip->connTimeout); 1269 #else /* NO_SIGNALS */ 1270 len = (int) sizeof(cip->servDataAddr); 1271 if (cip->connTimeout > 0) 1272 (void) alarm(cip->connTimeout); 1273 newSocket = accept(cip->dataSocket, (struct sockaddr *) &cip->servDataAddr, &len); 1274 if (cip->connTimeout > 0) 1275 (void) alarm(0); 1276 #endif /* NO_SIGNALS */ 1277 1278 (void) closesocket(cip->dataSocket); 1279 if (newSocket < 0) { 1280 Error(cip, kDoPerror, "Could not accept a data connection.\n"); 1281 cip->dataSocket = kClosedFileDescriptor; 1282 cip->errNo = kErrAcceptDataSocket; 1283 return (kErrAcceptDataSocket); 1284 } 1285 1286 if (cip->require20 != 0) { 1287 remoteDataPort = ntohs(cip->servDataAddr.sin_port); 1288 remoteCtrlPort = ntohs(cip->servCtlAddr.sin_port); 1289 if ((int) remoteDataPort != ((int) remoteCtrlPort - 1)) { 1290 Error(cip, kDontPerror, "Data connection did not originate on correct port!\n"); 1291 (void) closesocket(newSocket); 1292 cip->dataSocket = kClosedFileDescriptor; 1293 cip->errNo = kErrAcceptDataSocket; 1294 return (kErrAcceptDataSocket); 1295 } else if (memcmp(&cip->servDataAddr.sin_addr.s_addr, &cip->servCtlAddr.sin_addr.s_addr, sizeof(cip->servDataAddr.sin_addr.s_addr)) != 0) { 1296 Error(cip, kDontPerror, "Data connection did not originate from remote server!\n"); 1297 (void) closesocket(newSocket); 1298 cip->dataSocket = kClosedFileDescriptor; 1299 cip->errNo = kErrAcceptDataSocket; 1300 return (kErrAcceptDataSocket); 1301 } 1302 } 1303 1304 cip->dataSocket = newSocket; 1305 } 1306 1307 return (0); 1308 } /* AcceptDataConnection */ 1309 1310 1311 1312 1313 void 1314 HangupOnServer(const FTPCIPtr cip) 1315 { 1316 /* Since we want to close both sides of the connection for each 1317 * socket, we can just have them closed with close() instead of 1318 * using shutdown(). 1319 */ 1320 CloseControlConnection(cip); 1321 CloseDataConnection(cip); 1322 } /* HangupOnServer */ 1323 1324 1325 1326 1327 void 1328 SendTelnetInterrupt(const FTPCIPtr cip) 1329 { 1330 char msg[4]; 1331 1332 /* 1. User system inserts the Telnet "Interrupt Process" (IP) signal 1333 * in the Telnet stream. 1334 */ 1335 1336 if (cip->cout != NULL) 1337 (void) fflush(cip->cout); 1338 1339 msg[0] = (char) (unsigned char) IAC; 1340 msg[1] = (char) (unsigned char) IP; 1341 (void) send(cip->ctrlSocketW, msg, 2, 0); 1342 1343 /* 2. User system sends the Telnet "Sync" signal. */ 1344 #if 1 1345 msg[0] = (char) (unsigned char) IAC; 1346 msg[1] = (char) (unsigned char) DM; 1347 if (send(cip->ctrlSocketW, msg, 2, MSG_OOB) != 2) 1348 Error(cip, kDoPerror, "Could not send an urgent message.\n"); 1349 #else 1350 /* "Send IAC in urgent mode instead of DM because UNIX places oob mark 1351 * after urgent byte rather than before as now is protocol," says 1352 * the BSD ftp code. 1353 */ 1354 msg[0] = (char) (unsigned char) IAC; 1355 if (send(cip->ctrlSocketW, msg, 1, MSG_OOB) != 1) 1356 Error(cip, kDoPerror, "Could not send an urgent message.\n"); 1357 (void) fprintf(cip->cout, "%c", DM); 1358 (void) fflush(cip->cout); 1359 #endif 1360 } /* SendTelnetInterrupt */ 1361 1362 /* eof FTP.c */ 1363