📄 ftpd.c
字号:
if (size < 0)
{
if (sysGetErrno() == EWOULDBLOCK)
{
/* temporary disable write to the socket */
FD_CLR(tran->sock, &globalSocketSendSet);
tran->wrDisabled = 1;
/* move transfer structure to the head of the list */
if (tran != FIRST_LIST_ELEM(&transfListRoot_g, transfer_t*))
{
ft_listDel((lelem_t*)(void*)tran);
ft_listAddHead(&transfListRoot_g, (lelem_t*)(void*)tran);
}
blockedTrans_g++;
}
}
else
if (size > 0)
{
if ((u16_t)size > tran->dlbufremain)
size = (int) tran->dlbufremain;
tran->dlbufpos += (u16_t) size;
tran->dlbufremain -= (u16_t) size;
tran->owner->lastTrans = COUNTERVAR;
}
moreToSend = 1;
}
return moreToSend;
}
/* This function processes all open data connections
* and does the file up- or download for the clients that
* are ready to send or receive data. This function handles
* also newly set up data connections.
*/
static sint_t ft_processDataConnections(fd_set* clientset, int fdcount)
{
transfer_t *tran, *next;
struct sockaddr addr;
unsigned long one = 1;
sint_t checked;
int s, alen;
next = FIRST_LIST_ELEM(&transfListRoot_g, transfer_t*);
checked = 0;
while (!END_OF_LIST(&transfListRoot_g, next) && (checked < fdcount))
{
tran = next;
next = NEXT_LIST_ELEM(tran, transfer_t*);
if ((tran->state <= TSTATE_GOTPASV) ||
(tran->state == TSTATE_GOTPORT) ||
!FD_ISSET(tran->sock, clientset))
{
continue;
}
checked++;
FD_CLR(tran->sock, clientset);
if (tran->state == TSTATE_WAITONPASV)
{
/* incoming PASV connection */
alen = sizeof(addr);
s = accept(tran->sock, (struct sockaddr*)&addr, &alen);
ft_delSocket(tran->sock);
if (s < 0)
{
ft_destroyTransfer(tran);
continue;
}
tran->sock = s;
ioctlsocket(tran->sock, FIONBIO, (void*)&one);
ft_initTransfer(tran);
if (tran->upload)
{
continue;
}
}
if (tran->state < TSTATE_TRANSFER)
{
ft_initTransfer(tran);
if (tran->upload)
{
continue;
}
}
if (tran->upload)
{
if (ft_handleUpload(tran) != 0)
{
continue;
}
}
else
{
if (ft_handleDownload(tran) != 0)
{
continue;
}
}
/* The file transfer is done when one of the functions
* ft_handleUpload or ft_handleDownload returned with zero.
*/
ft_reply(tran->owner, 226, "Transfer complete.");
tran->owner->lastTrans = COUNTERVAR;
ft_destroyTransfer(tran);
}
return checked;
}
/*-------------------------------------------------------------------------*/
/* Accept and open a new connection to the next client.
*/
static void ft_acceptClient(void)
{
struct sockaddr_in addr;
int s, alen;
static sint_t errors = 0;
alen = sizeof(addr);
sysMemSet(&addr, 0, sizeof(addr));
s = accept(serverSocket_g, (struct sockaddr *)&addr, &alen);
if (s < 0)
{
if ((sysGetErrno() == EBADF
#ifdef EPIPE
|| sysGetErrno() == EPIPE
#endif
) && (++errors >= 3))
{
/* try to recover server socket on too much errors */
ft_delSocket(serverSocket_g);
serverSocket_g = ft_newServerSocket();
errors = 0;
}
}
else
{
errors = 0;
if (ft_newConnection(s) != 0)
{
closesocket(s);
}
}
}
/*-------------------------------------------------------------------------*/
/* Check for timed out connections,
* and close them.
*/
static void ft_timeout(void)
{
connection_t *conn, *next;
COUNTERTYPE now = COUNTERVAR;
COUNTERSTYPE remain;
/* run through the list of connections */
next = FIRST_LIST_ELEM(&connListRoot_g, connection_t*);
while (!END_OF_LIST(&connListRoot_g, next))
{
conn = next;
next = NEXT_LIST_ELEM(conn, connection_t*);
remain = (COUNTERSTYPE)
(conn->lastTrans + (FTP_TIMEOUT * COUNTERTPS)) -
(COUNTERSTYPE) now;
if (remain <= 0)
{
ft_reply(conn, 421, "Timeout, closing connection.");
ft_destroyConnection(conn);
}
}
}
/*-------------------------------------------------------------------------*/
/* This function removes some bytes from the
* control connection input buffer. It is called
* when the command line parser has finnished
* executing a command.
*/
static void ft_removeRxbufBytes(connection_t *conn, u16_t count)
{
sint_t i;
if (conn->buflen <= count)
{
conn->buflen = 0;
}
else
{
conn->buflen -= count;
for (i = 0; i < conn->buflen; i++)
conn->rxbuffer[i] = conn->rxbuffer[count + i];
}
}
/*-------------------------------------------------------------------------*/
/* create a new server socket that listens to incomming FTP requests.
*/
static int ft_newServerSocket(void)
{
unsigned long one = 1;
struct sockaddr_in addr;
int s, err;
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s < 0)
return -1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*) &one, sizeof(one));
ioctlsocket(s, FIONBIO, (void*) &one); /* set nonblocking mode */
sysMemSet(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(FTP_PORT);
err = bind(s, (struct sockaddr *)&addr, sizeof(struct sockaddr));
if (err < 0)
{
if ((sysGetErrno() == ENOMEM) || (sysGetErrno() == EADDRINUSE))
{
/* wait some time and try again...*/
sysSleep(500);
err = bind(s, (struct sockaddr *)&addr, sizeof(struct sockaddr));
}
if (err < 0)
{
closesocket(s);
sysPrintErr("Failed to bind server socket!\n");
return -1;
}
}
listen(s, 20);
ft_addSocket(s);
return s;
}
/*-------------------------------------------------------------------------*/
/* Initialize a data connection for sending.
* No files and sockets are opened here, we just
* set up the socket and initialize some values.
* After initialization, we send the 150 reply.
*/
static void ft_initTransfer(transfer_t *tran)
{
#ifdef SO_LINGER
struct linger ling;
#endif
#ifdef SOL_TCP
#ifdef IPTOS_THROUGHPUT
const int mode = IPTOS_THROUGHPUT;
#endif
unsigned long zero = 0;
#ifdef TCP_CORK
unsigned long one = 1;
#endif
#endif
#ifdef SOL_TCP
/* set options for maximum throughput */
#ifdef IPTOS_THROUGHPUT
setsockopt(tran->sock, SOL_IP, IP_TOS, (void *)&mode, sizeof(mode));
#endif
setsockopt(tran->sock, SOL_TCP, TCP_NODELAY, (void *)&zero, sizeof(zero));
#ifdef TCP_CORK
setsockopt(tran->sock, SOL_TCP, TCP_CORK, (void *)&one, sizeof(one));
#endif
#endif
tran->state = TSTATE_TRANSFER;
if (tran->upload)
{
FD_CLR(tran->sock, &globalSocketSendSet);
ft_addSocket(tran->sock);
}
else
{
ft_addSendSocket(tran->sock);
}
#ifdef SO_LINGER
ling.l_onoff = 0;
ling.l_linger = 0;
setsockopt(tran->sock, SOL_SOCKET, SO_LINGER, (void*)&ling, sizeof(ling));
#endif
if (tran->dirlisting != 0)
{
ft_reply(tran->owner, 150,
"Opening ASCII mode data connection for directory listing.");
}
else
{
ft_reply(tran->owner, 150, "Opening BINARY mode data connection");
}
if (tran->filehandle >= 0)
{
fsys_seek(tran->filehandle, (sint_t) tran->owner->resumePos, FSSEEK_SET);
}
tran->owner->lastTrans = COUNTERVAR;
}
/*-------------------------------------------------------------------------*/
/* This function tries to remove all faulty
* file handles from the file sets.
*/
static sint_t ft_fdSanity(void)
{
struct timeval tv = {0,0};
connection_t *conn, *nextc;
transfer_t *tran, *nextt;
fd_set fds;
char buf;
/* Test if the server socket has failed.
If so, destroy the socket and create a new one. */
FD_ZERO(&fds);
FD_SET(serverSocket_g, &fds);
if (select(serverSocket_g, &fds, NULL, NULL, &tv) < 0)
{
FD_CLR(serverSocket_g, &globalSocketSet);
closesocket(serverSocket_g);
serverSocket_g = ft_newServerSocket();
if (serverSocket_g < 0)
return -1;
}
/* close all faulty connections */
nextc = FIRST_LIST_ELEM(&connListRoot_g, connection_t*);
while (!END_OF_LIST(&connListRoot_g, nextc))
{
conn = nextc;
nextc = NEXT_LIST_ELEM(conn, connection_t*);
if ((recv(conn->sock, &buf, 0, 0) < 0) &&
(sysGetErrno() == EBADF))
{
ft_destroyConnection(conn);
}
}
/* close all faulty filetransfers */
nextt = FIRST_LIST_ELEM(&transfListRoot_g, transfer_t*);
while (!END_OF_LIST(&transfListRoot_g, nextt))
{
tran = nextt;
nextt = NEXT_LIST_ELEM(tran, transfer_t*);
if ((recv(tran->sock, &buf, 0, 0) < 0) &&
(sysGetErrno() == EBADF))
{
ft_destroyTransfer(tran);
}
}
return 0;
}
/* Returns nonzero when the ftpd is started.
*/
sint_t ftpd_running(void)
{
return ftpd_running_g;
}
/* This function stoppes the FTP demon.
*/
sint_t ftpd_stop(void)
{
sint_t i;
if (ftpd_running_g == 0)
return 0;
ftpd_terminate_request_g = 1;
for (i = 0; i < 22; i++)
{
sysSleep(100);
if (ftpd_running_g == 0)
{
return 0;
}
}
return -1; /* failed to stop FTPD */
}
/* main function. does all initialization.
*/
sint_t ftpd_start(char *username, char *password)
{
connection_t *conn, *nextc;
transfer_t *tran, *nextt;
struct timeval timeout;
COUNTERTYPE wctr, bctr, bt;
fd_set fds, fds_send;
sint_t i;
if (ftpd_running_g != 0)
return 0; /* we are just up and running */
ftpd_running_g = 1;
if ((username == NULL) || (password == NULL))
return -1;
sysStrncpy(username_g, username, MAX_NAMESIZE);
sysStrncpy(password_g, password, MAX_NAMESIZE);
username_g[MAX_NAMESIZE] = 0;
password_g[MAX_NAMESIZE] = 0;
ftpd_terminate_request_g = 0;
blockedTrans_g = 0;
FD_ZERO(&globalSocketSet);
FD_ZERO(&globalSocketSendSet);
open_connections_g = 0;
serverSocket_g = ft_newServerSocket();
if (serverSocket_g < 0)
{
ftpd_running_g = 0;
return -1;
}
/* init lists */
INIT_LIST_HEAD(&connListRoot_g);
INIT_LIST_HEAD(&transfListRoot_g);
bt = COUNTERTPS / 10;
if (bt == 0) bt = 1;
wctr = COUNTERVAR + 30*(COUNTERTPS);
bctr = COUNTERVAR + bt;
while (serverSocket_g >= 0)
{
/* load fds */
fds = globalSocketSet;
fds_send = globalSocketSendSet;
/* wait and timeout after 1.1 second when there is no activity
*/
timeout.tv_sec = (blockedTrans_g == 0) ? 1 : 0;
timeout.tv_usec = 100000;
i = select(FD_SETSIZE, &fds, &fds_send, NULL, &timeout);
if (ftpd_terminate_request_g != 0)
break;
if (i < 0)
{
if (sysGetErrno() == EBADF)
{
/* test for insane file descriptors */
ft_fdSanity();
}
else
if (sysGetErrno() != EINTR)
{
sysPrintErr("select() failed\n");
}
continue;
}
/* re-enable write for all temp. disabled connections */
if (((COUNTERSTYPE)bctr - (COUNTERSTYPE)COUNTERVAR) <= 0)
{
bctr += bt;
tran = FIRST_LIST_ELEM(&transfListRoot_g, transfer_t*);
while (blockedTrans_g > 0)
{
if (tran->wrDisabled != 0)
{
ft_addSendSocket(tran->sock);
tran->wrDisabled = 0;
}
tran = NEXT_LIST_ELEM(tran, transfer_t*);
blockedTrans_g--;
}
}
/* remove any timed out sockets */
if (((COUNTERSTYPE)wctr - (COUNTERSTYPE)COUNTERVAR) <= 0)
{
wctr += 30*(COUNTERTPS);
ft_timeout();
}
if (i > 0)
{
/* first handle all sends... */
i -= ft_processDataConnections(&fds_send, i);
/* ... then the PASV connections and uploads ... */
i -= ft_processDataConnections(&fds, i);
/* ... and then the rest. */
ft_processCtrlConnections(&fds, i);
/* check for incomming connections */
if (FD_ISSET(serverSocket_g, &fds))
{
ft_acceptClient();
if (serverSocket_g < 0)
break;
i--;
}
}
}
/* cleanup everything and quit then */
/* close server socket */
if (serverSocket_g >= 0)
closesocket(serverSocket_g);
/* close all connections */
nextc = FIRST_LIST_ELEM(&connListRoot_g, connection_t*);
while (!END_OF_LIST(&connListRoot_g, nextc))
{
conn = nextc;
nextc = NEXT_LIST_ELEM(conn, connection_t*);
if (conn->transfer != NULL)
{
ft_abortTransfer(conn);
}
ft_reply(conn, 421, "FTPD shut down.");
ft_destroyConnection(conn);
}
/* close all filetransfers */
nextt = FIRST_LIST_ELEM(&transfListRoot_g, transfer_t*);
while (!END_OF_LIST(&transfListRoot_g, nextt))
{
tran = nextt;
nextt = NEXT_LIST_ELEM(tran, transfer_t*);
ft_destroyTransfer(tran);
}
ftpd_running_g = 0;
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -