+++ /dev/null
-/* ftp.c
- *
- * Copyright (c) 1996-2001 Mike Gleason, NCEMRSoft.
- * All rights reserved.
- *
- */
-
-#define _libncftp_ftp_c_
-#include "syshdrs.h"
-
-char gLibNcFTPVersion[64] = kLibraryVersion;
-
-#ifdef NO_SIGNALS
-char gNoSignalsMarker[] = "@(#) LibNcFTP - NO_SIGNALS";
-#else
-
-static int gGotSig = 0;
-#ifdef HAVE_SIGSETJMP
-static sigjmp_buf gCancelConnectJmp;
-#else
-static jmp_buf gCancelConnectJmp;
-#endif
-
-#endif
-
-
-#ifndef lint
-static char gCopyright[] = "@(#) LibNcFTP Copyright 1995-2000, by Mike Gleason. All rights reserved.";
-#endif
-
-#ifdef HAVE_LIBSOCKS5
-# define SOCKS 5
-# include <socks.h>
-#else
-# ifdef HAVE_LIBSOCKS
-# define accept Raccept
-# define connect Rconnect
-# define getsockname Rgetsockname
-# define listen Rlisten
-# endif
-#endif
-
-
-
-
-/* On entry, you should have 'host' be set to a symbolic name (like
- * cse.unl.edu), or set to a numeric address (like 129.93.3.1).
- * If the function fails, it will return NULL, but if the host was
- * a numeric style address, you'll have the ip_address to fall back on.
- */
-
-static struct hostent *
-GetHostEntry(char *host, struct in_addr *ip_address)
-{
- struct in_addr ip;
- struct hostent *hp;
-
- /* See if the host was given in the dotted IP format, like "36.44.0.2."
- * If it was, inet_addr will convert that to a 32-bit binary value;
- * it not, inet_addr will return (-1L).
- */
- ip.s_addr = inet_addr(host);
- if (ip.s_addr != INADDR_NONE) {
- hp = NULL;
- } else {
- /* No IP address, so it must be a hostname, like ftp.wustl.edu. */
- hp = gethostbyname(host);
- if (hp != NULL)
- (void) memcpy(&ip.s_addr, hp->h_addr_list[0], (size_t) hp->h_length);
- }
- if (ip_address != NULL)
- *ip_address = ip;
- return (hp);
-} /* GetHostEntry */
-
-
-
-
-/* Makes every effort to return a fully qualified domain name. */
-int
-GetOurHostName(char *host, size_t siz)
-{
-#ifdef HOSTNAME
- /* You can hardcode in the name if this routine doesn't work
- * the way you want it to.
- */
- Strncpy(host, HOSTNAME, siz);
- return (1); /* Success */
-#else
- struct hostent *hp;
- int result;
- char **curAlias;
- char domain[64];
- char *cp;
- int rc;
-
- host[0] = '\0';
- result = gethostname(host, (int) siz);
- if ((result < 0) || (host[0] == '\0')) {
- return (-1);
- }
-
- if (strchr(host, '.') != NULL) {
- /* gethostname returned full name (like "cse.unl.edu"), instead
- * of just the node name (like "cse").
- */
- return (2); /* Success */
- }
-
- hp = gethostbyname(host);
- if (hp != NULL) {
- /* Maybe the host entry has the full name. */
- cp = strchr((char *) hp->h_name, '.');
- if ((cp != NULL) && (cp[1] != '\0')) {
- /* The 'name' field for the host entry had full name. */
- (void) Strncpy(host, (char *) hp->h_name, siz);
- return (3); /* Success */
- }
-
- /* Now try the list of aliases, to see if any of those look real. */
- for (curAlias = hp->h_aliases; *curAlias != NULL; curAlias++) {
- cp = strchr(*curAlias, '.');
- if ((cp != NULL) && (cp[1] != '\0')) {
- (void) Strncpy(host, *curAlias, siz);
- return (4); /* Success */
- }
- }
- }
-
- /* Otherwise, we just have the node name. See if we can get the
- * domain name ourselves.
- */
-#ifdef DOMAINNAME
- (void) STRNCPY(domain, DOMAINNAME);
- rc = 5;
-#else
- rc = -1;
- domain[0] = '\0';
-# if defined(HAVE_RES_INIT) && defined(HAVE__RES_DEFDNAME)
- if (domain[0] == '\0') {
- (void) res_init();
- if ((_res.defdname != NULL) && (_res.defdname[0] != '\0')) {
- (void) STRNCPY(domain, _res.defdname);
- rc = 6;
- }
- }
-# endif /* HAVE_RES_INIT && HAVE__RES_DEFDNAME */
-
- if (domain[0] == '\0') {
- FILE *fp;
- char line[256];
- char *tok;
-
- fp = fopen("/etc/resolv.conf", "r");
- if (fp != NULL) {
- (void) memset(line, 0, sizeof(line));
- while (fgets(line, sizeof(line) - 1, fp) != NULL) {
- if (!isalpha((int) line[0]))
- continue; /* Skip comment lines. */
- tok = strtok(line, " \t\n\r");
- if (tok == NULL)
- continue; /* Impossible */
- if (strcmp(tok, "domain") == 0) {
- tok = strtok(NULL, " \t\n\r");
- if (tok == NULL)
- continue; /* syntax error */
- (void) STRNCPY(domain, tok);
- rc = 7;
- break; /* Done. */
- }
- }
- (void) fclose(fp);
- }
- }
-#endif /* DOMAINNAME */
-
- if (domain[0] != '\0') {
- /* Supposedly, it's legal for a domain name with
- * a period at the end.
- */
- cp = domain + strlen(domain) - 1;
- if (*cp == '.')
- *cp = '\0';
- if (domain[0] != '.')
- (void) Strncat(host, ".", siz);
- (void) Strncat(host, domain, siz);
- }
- if (rc < 0)
- host[0] = '\0';
- return(rc); /* Success */
-#endif /* !HOSTNAME */
-} /* GetOurHostName */
-
-
-
-void
-CloseControlConnection(const FTPCIPtr cip)
-{
- /* This will close each file, if it was open. */
-#ifdef NO_SIGNALS
- SClose(cip->ctrlSocketR, 3);
- cip->ctrlSocketR = kClosedFileDescriptor;
- cip->ctrlSocketW = kClosedFileDescriptor;
- DisposeSReadlineInfo(&cip->ctrlSrl);
-#else /* NO_SIGNALS */
- if (cip->ctrlTimeout > 0)
- (void) alarm(cip->ctrlTimeout);
- CloseFile(&cip->cin);
- CloseFile(&cip->cout);
- cip->ctrlSocketR = kClosedFileDescriptor;
- cip->ctrlSocketW = kClosedFileDescriptor;
- if (cip->ctrlTimeout > 0)
- (void) alarm(0);
-#endif /* NO_SIGNALS */
- cip->connected = 0;
- cip->loggedIn = 0;
-} /* CloseControlConnection */
-
-
-
-static int
-GetSocketAddress(const FTPCIPtr cip, int sockfd, struct sockaddr_in *saddr)
-{
- int len = (int) sizeof (struct sockaddr_in);
- int result = 0;
-
- if (getsockname(sockfd, (struct sockaddr *)saddr, &len) < 0) {
- Error(cip, kDoPerror, "Could not get socket name.\n");
- cip->errNo = kErrGetSockName;
- result = kErrGetSockName;
- }
- return (result);
-} /* GetSocketAddress */
-
-
-
-
-int
-SetKeepAlive(const FTPCIPtr cip, int sockfd)
-{
-#ifndef SO_KEEPALIVE
- cip->errNo = kErrSetKeepAlive;
- return (kErrSetKeepAlive);
-#else
- int opt;
-
- opt = 1;
-
- if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, (int) sizeof(opt)) < 0) {
- /* Error(cip, kDoPerror, "Could not set keep-alive mode.\n"); */
- cip->errNo = kErrSetKeepAlive;
- return (kErrSetKeepAlive);
- }
- return (kNoErr);
-#endif /* SO_KEEPALIVE */
-} /* SetKeepAlive */
-
-
-
-
-int
-SetLinger(const FTPCIPtr cip, int sockfd, int onoff)
-{
-#ifndef SO_LINGER
- cip->errNo = kErrSetLinger;
- return (kErrSetLinger);
-#else
- struct linger li;
-
- if (onoff != 0) {
- li.l_onoff = 1;
- li.l_linger = 120; /* 2 minutes, but system ignores field. */
- } else {
- li.l_onoff = 0;
- li.l_linger = 0;
- }
- /* Have the system make an effort to deliver any unsent data,
- * even after we close the connection.
- */
- if (setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char *) &li, (int) sizeof(li)) < 0) {
- /* Error(cip, kDoPerror, "Could not set linger mode.\n"); */
- cip->errNo = kErrSetLinger;
- return (kErrSetLinger);
- }
- return (kNoErr);
-#endif /* SO_LINGER */
-} /* SetLinger */
-
-
-
-
-#ifdef IP_TOS
-int
-SetTypeOfService(const FTPCIPtr cip, int sockfd, int tosType)
-{
- /* Specify to the router what type of connection this is, so it
- * can prioritize packets.
- */
- if (setsockopt(sockfd, IPPROTO_IP, IP_TOS, (char *) &tosType, (int) sizeof(tosType)) < 0) {
- /* Error(cip, kDoPerror, "Could not set type of service.\n"); */
- cip->errNo = kErrSetTypeOfService;
- return (kErrSetTypeOfService);
- }
- return (kNoErr);
-} /* SetTypeOfService */
-#endif /* IP_TOS */
-
-
-
-
-#ifdef SO_OOBINLINE
-int
-SetInlineOutOfBandData(const FTPCIPtr cip, int sockfd)
-{
- int on = 1;
-
- if (setsockopt(sockfd, SOL_SOCKET, SO_OOBINLINE, (char *) &on, (int) sizeof(on)) < 0) {
- Error(cip, kDoPerror, "Could not set out of band inline mode.\n");
- cip->errNo = kErrSetOutOfBandInline;
- return (kErrSetOutOfBandInline);
- }
- return (kNoErr);
-} /* SetInlineOutOfBandData */
-#endif /* SO_OOBINLINE */
-
-
-
-
-#ifndef NO_SIGNALS
-
-static void
-CancelConnect(int signum)
-{
- gGotSig = signum;
-#ifdef HAVE_SIGSETJMP
- siglongjmp(gCancelConnectJmp, 1);
-#else
- longjmp(gCancelConnectJmp, 1);
-#endif /* HAVE_SIGSETJMP */
-} /* CancelConnect */
-
-#endif /* NO_SIGNALS */
-
-
-
-int
-OpenControlConnection(const FTPCIPtr cip, char *host, unsigned int port)
-{
- struct in_addr ip_address;
- int err = 0;
- int result;
- int oerrno;
- volatile int sockfd = -1;
- volatile int sock2fd = -1;
- ResponsePtr rp;
- char **volatile curaddr;
- struct hostent *hp;
- char *volatile fhost;
- unsigned int fport;
-#ifndef NO_SIGNALS
- volatile FTPSigProc osigint;
- volatile FTPSigProc osigalrm;
- volatile FTPCIPtr vcip;
- int sj;
-#endif /* NO_SIGNALS */
- const char *firstLine, *secondLine, *srvr;
-
- LIBNCFTP_USE_VAR(gLibNcFTPVersion);
- LIBNCFTP_USE_VAR(gCopyright);
-#ifdef NO_SIGNALS
- LIBNCFTP_USE_VAR(gNoSignalsMarker);
-#endif /* NO_SIGNALS */
-
- if (cip->firewallType == kFirewallNotInUse) {
- fhost = host;
- fport = port;
- } else {
- fhost = cip->firewallHost;
- fport = cip->firewallPort;
- }
- if (fport == 0)
- fport = cip->lip->defaultPort;
-
- /* Since we're the client, we just have to get a socket() and
- * connect() it.
- */
- (void) ZERO(cip->servCtlAddr);
- cip->cin = NULL;
- cip->cout = NULL;
-
- /* Make sure we use network byte-order. */
- fport = (unsigned int) htons((unsigned short) fport);
-
- cip->servCtlAddr.sin_port = (unsigned short) fport;
-
- hp = GetHostEntry(fhost, &ip_address);
-
- if (hp == NULL) {
- /* Okay, no Host entry, but maybe we have a numeric address
- * in ip_address we can try.
- */
- if (ip_address.s_addr == INADDR_NONE) {
- Error(cip, kDontPerror, "%s: unknown host.\n", fhost);
- cip->errNo = kErrHostUnknown;
- return (kErrHostUnknown);
- }
- cip->servCtlAddr.sin_family = AF_INET;
- cip->servCtlAddr.sin_addr.s_addr = ip_address.s_addr;
- } else {
- cip->servCtlAddr.sin_family = hp->h_addrtype;
- /* We'll fill in the rest of the structure below. */
- }
-
- /* After obtaining a socket, try to connect it to a remote
- * address. If we didn't get a host entry, we will only have
- * one thing to try (ip_address); if we do have one, we can try
- * every address in the list from the host entry.
- */
-
- if (hp == NULL) {
- /* Since we're given a single raw address, and not a host entry,
- * we can only try this one address and not any other addresses
- * that could be present for a site with a host entry.
- */
-
- if ((sockfd = socket(cip->servCtlAddr.sin_family, SOCK_STREAM, 0)) < 0) {
- Error(cip, kDoPerror, "Could not get a socket.\n");
- cip->errNo = kErrNewStreamSocket;
- return (kErrNewStreamSocket);
- }
-
- /* This doesn't do anything if you left these
- * at their defaults (zero). Otherwise it
- * tries to set the buffer size to the
- * size specified.
- */
- (void) SetSockBufSize(sockfd, cip->ctrlSocketRBufSize, cip->ctrlSocketSBufSize);
-
-#ifdef NO_SIGNALS
- err = SConnect(sockfd, &cip->servCtlAddr, (int) cip->connTimeout);
-
- if (err < 0) {
- oerrno = errno;
- (void) SClose(sockfd, 3);
- errno = oerrno;
- sockfd = -1;
- }
-#else /* NO_SIGNALS */
- osigint = (volatile FTPSigProc) signal(SIGINT, CancelConnect);
- if (cip->connTimeout > 0) {
- osigalrm = (volatile FTPSigProc) signal(SIGALRM, CancelConnect);
- (void) alarm(cip->connTimeout);
- }
-
- vcip = cip;
-
-#ifdef HAVE_SIGSETJMP
- sj = sigsetjmp(gCancelConnectJmp, 1);
-#else
- sj = setjmp(gCancelConnectJmp);
-#endif /* HAVE_SIGSETJMP */
-
- if (sj != 0) {
- /* Interrupted by a signal. */
- (void) closesocket(sockfd);
- (void) signal(SIGINT, (FTPSigProc) osigint);
- if (vcip->connTimeout > 0) {
- (void) alarm(0);
- (void) signal(SIGALRM, (FTPSigProc) osigalrm);
- }
- if (gGotSig == SIGINT) {
- result = vcip->errNo = kErrConnectMiscErr;
- Error(vcip, kDontPerror, "Connection attempt canceled.\n");
- (void) kill(getpid(), SIGINT);
- } else if (gGotSig == SIGALRM) {
- result = vcip->errNo = kErrConnectRetryableErr;
- Error(vcip, kDontPerror, "Connection attempt timed-out.\n");
- (void) kill(getpid(), SIGALRM);
- } else {
- result = vcip->errNo = kErrConnectMiscErr;
- Error(vcip, kDontPerror, "Connection attempt failed due to an unexpected signal (%d).\n", gGotSig);
- }
- return (result);
- } else {
- err = connect(sockfd, (struct sockaddr *) &cip->servCtlAddr,
- (int) sizeof (cip->servCtlAddr));
- if (cip->connTimeout > 0) {
- (void) alarm(0);
- (void) signal(SIGALRM, (FTPSigProc) osigalrm);
- }
- (void) signal(SIGINT, (FTPSigProc) osigint);
- }
-
- if (err < 0) {
- oerrno = errno;
- (void) closesocket(sockfd);
- errno = oerrno;
- sockfd = -1;
- }
-#endif /* NO_SIGNALS */
- } else {
- /* We can try each address in the list. We'll quit when we
- * run out of addresses to try or get a successful connection.
- */
- for (curaddr = hp->h_addr_list; *curaddr != NULL; curaddr++) {
- if ((sockfd = socket(cip->servCtlAddr.sin_family, SOCK_STREAM, 0)) < 0) {
- Error(cip, kDoPerror, "Could not get a socket.\n");
- cip->errNo = kErrNewStreamSocket;
- return (kErrNewStreamSocket);
- }
- /* This could overwrite the address field in the structure,
- * but this is okay because the structure has a junk field
- * just for this purpose.
- */
- (void) memcpy(&cip->servCtlAddr.sin_addr, *curaddr, (size_t) hp->h_length);
-
- /* This doesn't do anything if you left these
- * at their defaults (zero). Otherwise it
- * tries to set the buffer size to the
- * size specified.
- */
- (void) SetSockBufSize(sockfd, cip->ctrlSocketRBufSize, cip->ctrlSocketSBufSize);
-
-#ifdef NO_SIGNALS
- err = SConnect(sockfd, &cip->servCtlAddr, (int) cip->connTimeout);
-
- if (err == 0)
- break;
- oerrno = errno;
- (void) SClose(sockfd, 3);
- errno = oerrno;
- sockfd = -1;
-#else /* NO_SIGNALS */
-
- osigint = (volatile FTPSigProc) signal(SIGINT, CancelConnect);
- if (cip->connTimeout > 0) {
- osigalrm = (volatile FTPSigProc) signal(SIGALRM, CancelConnect);
- (void) alarm(cip->connTimeout);
- }
-
- vcip = cip;
-#ifdef HAVE_SIGSETJMP
- sj = sigsetjmp(gCancelConnectJmp, 1);
-#else
- sj = setjmp(gCancelConnectJmp);
-#endif /* HAVE_SIGSETJMP */
-
- if (sj != 0) {
- /* Interrupted by a signal. */
- (void) closesocket(sockfd);
- (void) signal(SIGINT, (FTPSigProc) osigint);
- if (vcip->connTimeout > 0) {
- (void) alarm(0);
- (void) signal(SIGALRM, (FTPSigProc) osigalrm);
- }
- if (gGotSig == SIGINT) {
- result = vcip->errNo = kErrConnectMiscErr;
- Error(vcip, kDontPerror, "Connection attempt canceled.\n");
- (void) kill(getpid(), SIGINT);
- } else if (gGotSig == SIGALRM) {
- result = vcip->errNo = kErrConnectRetryableErr;
- Error(vcip, kDontPerror, "Connection attempt timed-out.\n");
- (void) kill(getpid(), SIGALRM);
- } else {
- result = vcip->errNo = kErrConnectMiscErr;
- Error(vcip, kDontPerror, "Connection attempt failed due to an unexpected signal (%d).\n", gGotSig);
- }
- return (result);
- } else {
- err = connect(sockfd, (struct sockaddr *) &cip->servCtlAddr,
- (int) sizeof (cip->servCtlAddr));
- if (cip->connTimeout > 0) {
- (void) alarm(0);
- (void) signal(SIGALRM, (FTPSigProc) osigalrm);
- }
- (void) signal(SIGINT, (FTPSigProc) osigint);
- }
-
- if (err == 0)
- break;
- oerrno = errno;
- (void) closesocket(sockfd);
- errno = oerrno;
- sockfd = -1;
-#endif /* NO_SIGNALS */
- }
- }
-
- if (err < 0) {
- /* Could not connect. Close up shop and go home. */
-
- /* If possible, tell the caller if they should bother
- * calling back later.
- */
- switch (errno) {
-#ifdef ENETDOWN
- case ENETDOWN:
-#elif defined(WSAENETDOWN)
- case WSAENETDOWN:
-#endif
-#ifdef ENETUNREACH
- case ENETUNREACH:
-#elif defined(WSAENETUNREACH)
- case WSAENETUNREACH:
-#endif
-#ifdef ECONNABORTED
- case ECONNABORTED:
-#elif defined(WSAECONNABORTED)
- case WSAECONNABORTED:
-#endif
-#ifdef ETIMEDOUT
- case ETIMEDOUT:
-#elif defined(WSAETIMEDOUT)
- case WSAETIMEDOUT:
-#endif
-#ifdef EHOSTDOWN
- case EHOSTDOWN:
-#elif defined(WSAEHOSTDOWN)
- case WSAEHOSTDOWN:
-#endif
-#ifdef ECONNRESET
- case ECONNRESET:
-#elif defined(WSAECONNRESET)
- case WSAECONNRESET:
-#endif
- Error(cip, kDoPerror, "Could not connect to %s -- try again later.\n", fhost);
- result = cip->errNo = kErrConnectRetryableErr;
- break;
-#ifdef ECONNREFUSED
- case ECONNREFUSED:
-#elif defined(WSAECONNREFUSED)
- case WSAECONNREFUSED:
-#endif
- Error(cip, kDoPerror, "Could not connect to %s.\n", fhost);
- result = cip->errNo = kErrConnectRefused;
- break;
- default:
- Error(cip, kDoPerror, "Could not connect to %s.\n", fhost);
- result = cip->errNo = kErrConnectMiscErr;
- }
- goto fatal;
- }
-
- /* Get our end of the socket address for later use. */
- if ((result = GetSocketAddress(cip, sockfd, &cip->ourCtlAddr)) < 0)
- goto fatal;
-
-#ifdef SO_OOBINLINE
- /* We want Out-of-band data to appear in the regular stream,
- * since we can handle TELNET.
- */
- (void) SetInlineOutOfBandData(cip, sockfd);
-#endif
- (void) SetKeepAlive(cip, sockfd);
- (void) SetLinger(cip, sockfd, 0); /* Don't need it for ctrl. */
-
-#if defined(IP_TOS) && defined(IPTOS_LOWDELAY)
- /* Control connection is somewhat interactive, so quick response
- * is desired.
- */
- (void) SetTypeOfService(cip, sockfd, IPTOS_LOWDELAY);
-#endif
-
-#ifdef NO_SIGNALS
- cip->ctrlSocketR = sockfd;
- cip->ctrlSocketW = sockfd;
- cip->cout = NULL;
- cip->cin = NULL;
- sock2fd = kClosedFileDescriptor;
-
- if (InitSReadlineInfo(&cip->ctrlSrl, sockfd, cip->srlBuf, sizeof(cip->srlBuf), (int) cip->ctrlTimeout, 1) < 0) {
- result = kErrFdopenW;
- cip->errNo = kErrFdopenW;
- Error(cip, kDoPerror, "Could not fdopen.\n");
- goto fatal;
- }
-#else /* NO_SIGNALS */
- if ((sock2fd = dup(sockfd)) < 0) {
- result = kErrDupSocket;
- cip->errNo = kErrDupSocket;
- Error(cip, kDoPerror, "Could not duplicate a file descriptor.\n");
- goto fatal;
- }
-
- /* Now setup the FILE pointers for use with the Std I/O library
- * routines.
- */
- if ((cip->cin = fdopen(sockfd, "r")) == NULL) {
- result = kErrFdopenR;
- cip->errNo = kErrFdopenR;
- Error(cip, kDoPerror, "Could not fdopen.\n");
- goto fatal;
- }
-
- if ((cip->cout = fdopen(sock2fd, "w")) == NULL) {
- result = kErrFdopenW;
- cip->errNo = kErrFdopenW;
- Error(cip, kDoPerror, "Could not fdopen.\n");
- CloseFile(&cip->cin);
- sockfd = kClosedFileDescriptor;
- goto fatal;
- }
-
- cip->ctrlSocketR = sockfd;
- cip->ctrlSocketW = sockfd;
-
- /* We'll be reading and writing lines, so use line buffering. This
- * is necessary since the stdio library will use full buffering
- * for all streams not associated with the tty.
- */
-#ifdef HAVE_SETLINEBUF
- setlinebuf(cip->cin);
- setlinebuf(cip->cout);
-#else
- (void) SETVBUF(cip->cin, NULL, _IOLBF, (size_t) BUFSIZ);
- (void) SETVBUF(cip->cout, NULL, _IOLBF, (size_t) BUFSIZ);
-#endif
-#endif /* NO_SIGNALS */
-
-#ifdef HAVE_INET_NTOP /* Mostly to workaround bug in IRIX 6.5's inet_ntoa */
- (void) memset(cip->ip, 0, sizeof(cip->ip));
- (void) inet_ntop(AF_INET, &cip->servCtlAddr.sin_addr, cip->ip, sizeof(cip->ip) - 1);
-#else
- (void) STRNCPY(cip->ip, inet_ntoa(cip->servCtlAddr.sin_addr));
-#endif
- if ((hp == NULL) || (hp->h_name == NULL))
- (void) STRNCPY(cip->actualHost, fhost);
- else
- (void) STRNCPY(cip->actualHost, (char *) hp->h_name);
-
- /* Read the startup message from the server. */
- rp = InitResponse();
- if (rp == NULL) {
- Error(cip, kDontPerror, "Malloc failed.\n");
- cip->errNo = kErrMallocFailed;
- result = cip->errNo;
- goto fatal;
- }
-
- result = GetResponse(cip, rp);
- if ((result < 0) && (rp->msg.first == NULL)) {
- goto fatal;
- }
- if (rp->msg.first != NULL) {
- cip->serverType = kServerTypeUnknown;
- srvr = NULL;
- firstLine = rp->msg.first->line;
- secondLine = NULL;
- if (rp->msg.first->next != NULL)
- secondLine = rp->msg.first->next->line;
-
- if (strstr(firstLine, "Version wu-") != NULL) {
- cip->serverType = kServerTypeWuFTPd;
- srvr = "wu-ftpd";
- } else if (strstr(firstLine, "NcFTPd") != NULL) {
- cip->serverType = kServerTypeNcFTPd;
- srvr = "NcFTPd Server";
- } else if (STRNEQ("ProFTPD", firstLine, 7)) {
- cip->serverType = kServerTypeProFTPD;
- srvr = "ProFTPD";
- } else if (strstr(firstLine, "Microsoft FTP Service") != NULL) {
- cip->serverType = kServerTypeMicrosoftFTP;
- srvr = "Microsoft FTP Service";
- } else if (strstr(firstLine, "(NetWare ") != NULL) {
- cip->serverType = kServerTypeNetWareFTP;
- srvr = "NetWare FTP Service";
- } else if (STRNEQ("WFTPD", firstLine, 5)) {
- cip->serverType = kServerTypeWFTPD;
- srvr = "WFTPD";
- } else if (STRNEQ("Serv-U FTP", firstLine, 10)) {
- cip->serverType = kServerTypeServ_U;
- srvr = "Serv-U FTP-Server";
- } else if (strstr(firstLine, "VFTPD") != NULL) {
- cip->serverType = kServerTypeVFTPD;
- srvr = "VFTPD";
- } else if (STRNEQ("FTP-Max", firstLine, 7)) {
- cip->serverType = kServerTypeFTP_Max;
- srvr = "FTP-Max";
- } else if (strstr(firstLine, "Roxen") != NULL) {
- cip->serverType = kServerTypeRoxen;
- srvr = "Roxen";
- } else if (strstr(firstLine, "WS_FTP") != NULL) {
- cip->serverType = kServerTypeWS_FTP;
- srvr = "WS_FTP Server";
- } else if ((secondLine != NULL) && (strstr(secondLine, "WarFTP") != NULL)) {
- cip->serverType = kServerTypeWarFTPd;
- srvr = "WarFTPd";
- }
-
- if (srvr != NULL)
- PrintF(cip, "Remote server is running %s.\n", srvr);
-
- /* Do the application's connect message callback, if present. */
- if ((cip->onConnectMsgProc != 0) && (rp->codeType < 4))
- (*cip->onConnectMsgProc)(cip, rp);
- }
-
- if (rp->codeType >= 4) {
- /* They probably hung up on us right away. That's too bad,
- * but we can tell the caller that they can call back later
- * and try again.
- */
- DoneWithResponse(cip, rp);
- result = kErrConnectRetryableErr;
- Error(cip, kDontPerror, "Server hungup immediately after connect.\n");
- cip->errNo = kErrConnectRetryableErr;
- goto fatal;
- }
- if (result < 0) /* Some other error occurred during connect message */
- goto fatal;
- cip->connected = 1;
- DoneWithResponse(cip, rp);
- return (kNoErr);
-
-fatal:
- if (sockfd > 0)
- (void) closesocket(sockfd);
- if (sock2fd > 0)
- (void) closesocket(sock2fd);
- CloseFile(&cip->cin);
- CloseFile(&cip->cout);
- cip->ctrlSocketR = kClosedFileDescriptor;
- cip->ctrlSocketW = kClosedFileDescriptor;
- return (result);
-} /* OpenControlConnection */
-
-
-
-
-void
-CloseDataConnection(const FTPCIPtr cip)
-{
- if (cip->dataSocket != kClosedFileDescriptor) {
-#ifdef NO_SIGNALS
- SClose(cip->dataSocket, 3);
-#else /* NO_SIGNALS */
- if (cip->xferTimeout > 0)
- (void) alarm(cip->xferTimeout);
- (void) closesocket(cip->dataSocket);
- if (cip->xferTimeout > 0)
- (void) alarm(0);
-#endif /* NO_SIGNALS */
- cip->dataSocket = kClosedFileDescriptor;
- }
- memset(&cip->ourDataAddr, 0, sizeof(cip->ourDataAddr));
- memset(&cip->servDataAddr, 0, sizeof(cip->servDataAddr));
-} /* CloseDataConnection */
-
-
-
-
-int
-SetStartOffset(const FTPCIPtr cip, longest_int restartPt)
-{
- ResponsePtr rp;
- int result;
-
- if (restartPt != (longest_int) 0) {
- rp = InitResponse();
- if (rp == NULL) {
- Error(cip, kDontPerror, "Malloc failed.\n");
- cip->errNo = kErrMallocFailed;
- return (cip->errNo);
- }
-
- /* Force reset to offset zero. */
- if (restartPt == (longest_int) -1)
- restartPt = (longest_int) 0;
-#ifdef PRINTF_LONG_LONG
- result = RCmd(cip, rp,
- "REST " PRINTF_LONG_LONG,
- restartPt);
-#else
- result = RCmd(cip, rp, "REST %ld", (long) restartPt);
-#endif
-
- if (result < 0) {
- return (result);
- } else if (result == 3) {
- cip->hasREST = kCommandAvailable;
- DoneWithResponse(cip, rp);
- } else if (UNIMPLEMENTED_CMD(rp->code)) {
- cip->hasREST = kCommandNotAvailable;
- DoneWithResponse(cip, rp);
- cip->errNo = kErrSetStartPoint;
- return (kErrSetStartPoint);
- } else {
- DoneWithResponse(cip, rp);
- cip->errNo = kErrSetStartPoint;
- return (kErrSetStartPoint);
- }
- }
- return (0);
-} /* SetStartOffset */
-
-
-
-static int
-SendPort(const FTPCIPtr cip, struct sockaddr_in *saddr)
-{
- char *a, *p;
- int result;
- ResponsePtr rp;
-
- rp = InitResponse();
- if (rp == NULL) {
- Error(cip, kDontPerror, "Malloc failed.\n");
- cip->errNo = kErrMallocFailed;
- return (cip->errNo);
- }
-
- /* These will point to data in network byte order. */
- a = (char *) &saddr->sin_addr;
- p = (char *) &saddr->sin_port;
-#define UC(x) (int) (((int) x) & 0xff)
-
- /* Need to tell the other side which host (the address) and
- * which process (port) on that host to send data to.
- */
- result = RCmd(cip, rp, "PORT %d,%d,%d,%d,%d,%d",
- UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
-
- if (result < 0) {
- return (result);
- } else if (result != 2) {
- /* A 500'ish response code means the PORT command failed. */
- DoneWithResponse(cip, rp);
- cip->errNo = kErrPORTFailed;
- return (cip->errNo);
- }
- DoneWithResponse(cip, rp);
- return (kNoErr);
-} /* SendPort */
-
-
-
-
-static int
-Passive(const FTPCIPtr cip, struct sockaddr_in *saddr, int *weird)
-{
- ResponsePtr rp;
- int i[6], j;
- unsigned char n[6];
- char *cp;
- int result;
-
- rp = InitResponse();
- if (rp == NULL) {
- Error(cip, kDontPerror, "Malloc failed.\n");
- cip->errNo = kErrMallocFailed;
- return (cip->errNo);
- }
-
- result = RCmd(cip, rp, "PASV");
- if (result < 0)
- goto done;
-
- if (rp->codeType != 2) {
- /* Didn't understand or didn't want passive port selection. */
- cip->errNo = result = kErrPASVFailed;
- goto done;
- }
-
- /* The other side returns a specification in the form of
- * an internet address as the first four integers (each
- * integer stands for 8-bits of the real 32-bit address),
- * and two more integers for the port (16-bit port).
- *
- * It should give us something like:
- * "Entering Passive Mode (129,93,33,1,10,187)", so look for
- * digits with sscanf() starting 24 characters down the string.
- */
- for (cp = rp->msg.first->line; ; cp++) {
- if (*cp == '\0') {
- Error(cip, kDontPerror, "Cannot parse PASV response: %s\n", rp->msg.first->line);
- goto done;
- }
- if (isdigit((int) *cp))
- break;
- }
-
- if (sscanf(cp, "%d,%d,%d,%d,%d,%d",
- &i[0], &i[1], &i[2], &i[3], &i[4], &i[5]) != 6) {
- Error(cip, kDontPerror, "Cannot parse PASV response: %s\n", rp->msg.first->line);
- goto done;
- }
-
- for (j=0, *weird = 0; j<6; j++) {
- /* Some ftp servers return bogus port octets, such as
- * boombox.micro.umn.edu. Let the caller know if we got a
- * weird looking octet.
- */
- if ((i[j] < 0) || (i[j] > 255))
- *weird = *weird + 1;
- n[j] = (unsigned char) (i[j] & 0xff);
- }
-
- (void) memcpy(&saddr->sin_addr, &n[0], (size_t) 4);
- (void) memcpy(&saddr->sin_port, &n[4], (size_t) 2);
-
- result = kNoErr;
-done:
- DoneWithResponse(cip, rp);
- return (result);
-} /* Passive */
-
-
-
-
-static int
-BindToEphemeralPortNumber(int sockfd, struct sockaddr_in *addrp, int ephemLo, int ephemHi)
-{
- int i;
- int result;
- int rangesize;
- unsigned short port;
-
- addrp->sin_family = AF_INET;
- if (((int) ephemLo == 0) || ((int) ephemLo >= (int) ephemHi)) {
- /* Do it the normal way. System will
- * pick one, typically in the range
- * of 1024-4999.
- */
- addrp->sin_port = 0; /* Let system pick one. */
-
- result = bind(sockfd, (struct sockaddr *) addrp, sizeof(struct sockaddr_in));
- } else {
- rangesize = (int) ((int) ephemHi - (int) ephemLo);
- result = 0;
- for (i=0; i<10; i++) {
- port = (unsigned short) (((int) rand() % rangesize) + (int) ephemLo);
- addrp->sin_port = port;
-
- result = bind(sockfd, (struct sockaddr *) addrp, sizeof(struct sockaddr_in));
- if (result == 0)
- break;
- if ((errno != 999)
- /* This next line is just fodder to
- * shut the compiler up about variable
- * not being used.
- */
- && (gCopyright[0] != '\0'))
- break;
- }
- }
- return (result);
-} /* BindToEphemeralPortNumber */
-
-
-
-
-int
-OpenDataConnection(const FTPCIPtr cip, int mode)
-{
- int dataSocket;
- int weirdPort;
- int result;
-
- /* Before we can transfer any data, and before we even ask the
- * remote server to start transferring via RETR/NLST/etc, we have
- * to setup the connection.
- */
-
-tryPort2:
- weirdPort = 0;
- result = 0;
- CloseDataConnection(cip); /* In case we didn't before... */
-
- dataSocket = socket(AF_INET, SOCK_STREAM, 0);
- if (dataSocket < 0) {
- Error(cip, kDoPerror, "Could not get a data socket.\n");
- result = kErrNewStreamSocket;
- cip->errNo = kErrNewStreamSocket;
- return result;
- }
-
- /* This doesn't do anything if you left these
- * at their defaults (zero). Otherwise it
- * tries to set the buffer size to the
- * size specified.
- */
- (void) SetSockBufSize(dataSocket, cip->dataSocketRBufSize, cip->dataSocketSBufSize);
-
- if ((cip->hasPASV == kCommandNotAvailable) || (mode == kSendPortMode)) {
-tryPort:
- cip->ourDataAddr = cip->ourCtlAddr;
- cip->ourDataAddr.sin_family = AF_INET;
-
-#ifdef HAVE_LIBSOCKS
- cip->ourDataAddr.sin_port = 0;
- if (Rbind(dataSocket, (struct sockaddr *) &cip->ourDataAddr,
- (int) sizeof (cip->ourDataAddr),
- cip->servCtlAddr.sin_addr.s_addr) < 0)
-#else
- if (BindToEphemeralPortNumber(dataSocket, &cip->ourDataAddr, (int) cip->ephemLo, (int) cip->ephemHi) < 0)
-#endif
- {
- Error(cip, kDoPerror, "Could not bind the data socket");
- result = kErrBindDataSocket;
- cip->errNo = kErrBindDataSocket;
- goto bad;
- }
-
- /* Need to do this so we can figure out which port the system
- * gave to us.
- */
- if ((result = GetSocketAddress(cip, dataSocket, &cip->ourDataAddr)) < 0)
- goto bad;
-
- if (listen(dataSocket, 1) < 0) {
- Error(cip, kDoPerror, "listen failed");
- result = kErrListenDataSocket;
- cip->errNo = kErrListenDataSocket;
- goto bad;
- }
-
- if ((result = SendPort(cip, &cip->ourDataAddr)) < 0)
- goto bad;
-
- cip->dataPortMode = kSendPortMode;
- } else {
- /* Passive mode. Let the other side decide where to send. */
-
- cip->servDataAddr = cip->servCtlAddr;
- cip->servDataAddr.sin_family = AF_INET;
- cip->ourDataAddr = cip->ourCtlAddr;
- cip->ourDataAddr.sin_family = AF_INET;
-
- if (Passive(cip, &cip->servDataAddr, &weirdPort) < 0) {
- Error(cip, kDontPerror, "Passive mode refused.\n");
- cip->hasPASV = kCommandNotAvailable;
-
- /* We can try using regular PORT commands, which are required
- * by all FTP protocol compliant programs, if you said so.
- *
- * We don't do this automatically, because if your host
- * is running a firewall you (probably) do not want SendPort
- * FTP for security reasons.
- */
- if (mode == kFallBackToSendPortMode)
- goto tryPort;
- result = kErrPassiveModeFailed;
- cip->errNo = kErrPassiveModeFailed;
- goto bad;
- }
-
-#ifdef HAVE_LIBSOCKS
- cip->ourDataAddr.sin_port = 0;
- if (Rbind(dataSocket, (struct sockaddr *) &cip->ourDataAddr,
- (int) sizeof (cip->ourDataAddr),
- cip->servCtlAddr.sin_addr.s_addr) < 0)
-#else
- if (BindToEphemeralPortNumber(dataSocket, &cip->ourDataAddr, (int) cip->ephemLo, (int) cip->ephemHi) < 0)
-#endif
- {
- Error(cip, kDoPerror, "Could not bind the data socket");
- result = kErrBindDataSocket;
- cip->errNo = kErrBindDataSocket;
- goto bad;
- }
-
-#ifdef NO_SIGNALS
- result = SConnect(dataSocket, &cip->servDataAddr, (int) cip->connTimeout);
-#else /* NO_SIGNALS */
- if (cip->connTimeout > 0)
- (void) alarm(cip->connTimeout);
-
- result = connect(dataSocket, (struct sockaddr *) &cip->servDataAddr, (int) sizeof(cip->servDataAddr));
- if (cip->connTimeout > 0)
- (void) alarm(0);
-#endif /* NO_SIGNALS */
-
-#ifdef NO_SIGNALS
- if (result == kTimeoutErr) {
- if (mode == kFallBackToSendPortMode) {
- Error(cip, kDontPerror, "Data connection timed out.\n");
- Error(cip, kDontPerror, "Falling back to PORT instead of PASV mode.\n");
- (void) closesocket(dataSocket);
- cip->hasPASV = kCommandNotAvailable;
- goto tryPort2;
- }
- Error(cip, kDontPerror, "Data connection timed out.\n");
- result = kErrConnectDataSocket;
- cip->errNo = kErrConnectDataSocket;
- } else
-#endif /* NO_SIGNALS */
-
- if (result < 0) {
-#ifdef ECONNREFUSED
- if ((weirdPort > 0) && (errno == ECONNREFUSED)) {
-#elif defined(WSAECONNREFUSED)
- if ((weirdPort > 0) && (errno == WSAECONNREFUSED)) {
-#endif
- Error(cip, kDontPerror, "Server sent back a bogus port number.\nI will fall back to PORT instead of PASV mode.\n");
- if (mode == kFallBackToSendPortMode) {
- (void) closesocket(dataSocket);
- cip->hasPASV = kCommandNotAvailable;
- goto tryPort2;
- }
- result = kErrServerSentBogusPortNumber;
- cip->errNo = kErrServerSentBogusPortNumber;
- goto bad;
- }
- if (mode == kFallBackToSendPortMode) {
- Error(cip, kDoPerror, "connect failed.\n");
- Error(cip, kDontPerror, "Falling back to PORT instead of PASV mode.\n");
- (void) closesocket(dataSocket);
- cip->hasPASV = kCommandNotAvailable;
- goto tryPort2;
- }
- Error(cip, kDoPerror, "connect failed.\n");
- result = kErrConnectDataSocket;
- cip->errNo = kErrConnectDataSocket;
- goto bad;
- }
-
- /* Need to do this so we can figure out which port the system
- * gave to us.
- */
- if ((result = GetSocketAddress(cip, dataSocket, &cip->ourDataAddr)) < 0)
- goto bad;
-
- cip->dataPortMode = kPassiveMode;
- cip->hasPASV = kCommandAvailable;
- }
-
- (void) SetLinger(cip, dataSocket, 1);
- (void) SetKeepAlive(cip, dataSocket);
-
-#if defined(IP_TOS) && defined(IPTOS_THROUGHPUT)
- /* Data connection is a non-interactive data stream, so
- * high throughput is desired, at the expense of low
- * response time.
- */
- (void) SetTypeOfService(cip, dataSocket, IPTOS_THROUGHPUT);
-#endif
-
- cip->dataSocket = dataSocket;
- return (0);
-bad:
- (void) closesocket(dataSocket);
- return (result);
-} /* OpenDataConnection */
-
-
-
-
-int
-AcceptDataConnection(const FTPCIPtr cip)
-{
- int newSocket;
-#ifndef NO_SIGNALS
- int len;
-#endif
- unsigned short remoteDataPort;
- unsigned short remoteCtrlPort;
-
- /* If we did a PORT, we have some things to finish up.
- * If we did a PASV, we're ready to go.
- */
- if (cip->dataPortMode == kSendPortMode) {
- /* Accept will give us back the server's data address; at the
- * moment we don't do anything with it though.
- */
- memset(&cip->servDataAddr, 0, sizeof(cip->servDataAddr));
-
-#ifdef NO_SIGNALS
- newSocket = SAccept(cip->dataSocket, &cip->servDataAddr, (int) cip->connTimeout);
-#else /* NO_SIGNALS */
- len = (int) sizeof(cip->servDataAddr);
- if (cip->connTimeout > 0)
- (void) alarm(cip->connTimeout);
- newSocket = accept(cip->dataSocket, (struct sockaddr *) &cip->servDataAddr, &len);
- if (cip->connTimeout > 0)
- (void) alarm(0);
-#endif /* NO_SIGNALS */
-
- (void) closesocket(cip->dataSocket);
- if (newSocket < 0) {
- Error(cip, kDoPerror, "Could not accept a data connection.\n");
- cip->dataSocket = kClosedFileDescriptor;
- cip->errNo = kErrAcceptDataSocket;
- return (kErrAcceptDataSocket);
- }
-
- if (cip->require20 != 0) {
- remoteDataPort = ntohs(cip->servDataAddr.sin_port);
- remoteCtrlPort = ntohs(cip->servCtlAddr.sin_port);
- if ((int) remoteDataPort != ((int) remoteCtrlPort - 1)) {
- Error(cip, kDontPerror, "Data connection did not originate on correct port!\n");
- (void) closesocket(newSocket);
- cip->dataSocket = kClosedFileDescriptor;
- cip->errNo = kErrAcceptDataSocket;
- return (kErrAcceptDataSocket);
- } else if (memcmp(&cip->servDataAddr.sin_addr.s_addr, &cip->servCtlAddr.sin_addr.s_addr, sizeof(cip->servDataAddr.sin_addr.s_addr)) != 0) {
- Error(cip, kDontPerror, "Data connection did not originate from remote server!\n");
- (void) closesocket(newSocket);
- cip->dataSocket = kClosedFileDescriptor;
- cip->errNo = kErrAcceptDataSocket;
- return (kErrAcceptDataSocket);
- }
- }
-
- cip->dataSocket = newSocket;
- }
-
- return (0);
-} /* AcceptDataConnection */
-
-
-
-
-void
-HangupOnServer(const FTPCIPtr cip)
-{
- /* Since we want to close both sides of the connection for each
- * socket, we can just have them closed with close() instead of
- * using shutdown().
- */
- CloseControlConnection(cip);
- CloseDataConnection(cip);
-} /* HangupOnServer */
-
-
-
-
-void
-SendTelnetInterrupt(const FTPCIPtr cip)
-{
- char msg[4];
-
- /* 1. User system inserts the Telnet "Interrupt Process" (IP) signal
- * in the Telnet stream.
- */
-
- if (cip->cout != NULL)
- (void) fflush(cip->cout);
-
- msg[0] = (char) (unsigned char) IAC;
- msg[1] = (char) (unsigned char) IP;
- (void) send(cip->ctrlSocketW, msg, 2, 0);
-
- /* 2. User system sends the Telnet "Sync" signal. */
-#if 1
- msg[0] = (char) (unsigned char) IAC;
- msg[1] = (char) (unsigned char) DM;
- if (send(cip->ctrlSocketW, msg, 2, MSG_OOB) != 2)
- Error(cip, kDoPerror, "Could not send an urgent message.\n");
-#else
- /* "Send IAC in urgent mode instead of DM because UNIX places oob mark
- * after urgent byte rather than before as now is protocol," says
- * the BSD ftp code.
- */
- msg[0] = (char) (unsigned char) IAC;
- if (send(cip->ctrlSocketW, msg, 1, MSG_OOB) != 1)
- Error(cip, kDoPerror, "Could not send an urgent message.\n");
- (void) fprintf(cip->cout, "%c", DM);
- (void) fflush(cip->cout);
-#endif
-} /* SendTelnetInterrupt */
-
-/* eof FTP.c */