📄 ftp.c
字号:
/* 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 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -