📄 ftpdlib.c
字号:
addrLen = sizeof (struct sockaddr); ftpdDebugMsg ("waiting for a new client connection...\n",0,0,0,0); newSock = accept (ftpdServerSock, (struct sockaddr *) &addr, &addrLen); if (newSock < 0) { ftpdDebugMsg ("cannot accept a new connection\n",0,0,0,0); break; } /* * Register a new FTP client session. This process is a critical * section with the server shutdown routine. If an error occurs, * the reply must be sent over the control connection to the peer * before the semaphore is released. Otherwise, this task could * be deleted and no response would be sent, possibly causing * the new client to hang indefinitely. */ semTake (ftpsMutexSem, WAIT_FOREVER); setsockopt (newSock, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof (on)); inet_ntoa_b (addr.sin_addr, a_ip_addr); ftpdDebugMsg ("accepted a new client connection from %s\n", (int) a_ip_addr, 0, 0, 0); /* Create a new session entry for this connection, if possible. */ pSlot = ftpdSessionAdd (); if (pSlot == NULL) /* Maximum number of connections reached. */ { /* Send transient failure report to client. */ ftpdCmdSend (pSlot, newSock, 421, "Session limit reached, closing control connection", 0, 0, 0, 0, 0, 0); ftpdDebugMsg ("cannot get a new connection slot\n",0,0,0,0); close (newSock); semGive (ftpsMutexSem); continue; } pSlot->cmdSock = newSock; /* Save the control address and assign the default data address. */ bcopy ( (char *)&addr, (char *)&pSlot->peerAddr, sizeof (struct sockaddr_in)); bcopy ( (char *)&addr, (char *)&pSlot->dataAddr, sizeof (struct sockaddr_in)); pSlot->dataAddr.sin_port = htons (FTP_DATA_PORT); /* Create a task name. */ sprintf (ftpdWorkTaskName, "tFtpdServ%d", ftpdNumTasks); /* Spawn a secondary task to process FTP requests for this session. */ ftpdDebugMsg ("creating a new server task %s...\n", (int) ftpdWorkTaskName, 0, 0, 0); if (taskSpawn (ftpdWorkTaskName, ftpdWorkTaskPriority, ftpdWorkTaskOptions, ftpdWorkTaskStackSize, ftpdWorkTask, (int) pSlot, 0, 0, 0, 0, 0, 0, 0, 0, 0) == ERROR) { /* Send transient failure report to client. */ ftpdCmdSend (pSlot, newSock, 421, "Service not available, closing control connection", 0, 0, 0, 0, 0, 0); ftpdSessionDelete (pSlot); ftpdDebugMsg ("cannot create a new work task\n",0,0,0,0); semGive (ftpsMutexSem); continue; } ftpdDebugMsg ("done.\n",0,0,0,0); /* Session added - end of critical section with shutdown routine. */ semGive (ftpsMutexSem); } /* Fatal error - update state of server. */ ftpsActive = FALSE; return; }/********************************************************************************* ftpdInit - initialize the FTP server task** This routine installs the password verification routine indicated by* <pLoginRtn> and establishes a control connection for the primary FTP* server task, which it then creates. It is called automatically during* system startup if INCLUDE_FTP_SERVER is defined. The primary server task * supports simultaneous client sessions, up to the limit specified by the * global variable 'ftpsMaxClients'. The default value allows a maximum of * four simultaneous connections. The <stackSize> argument specifies the stack * size for the primary server task. It is set to the value specified in the * 'ftpdWorkTaskStackSize' global variable by default.** RETURNS:* OK if server started, or ERROR otherwise.** ERRNO: N/A*/STATUS ftpdInit ( FUNCPTR pLoginRtn, /* user verification routine, or NULL */ int stackSize /* task stack size, or 0 for default */ ) { int on = 1; struct sockaddr_in ctrlAddr; if (ftpsActive) return (OK); loginVerifyRtn = pLoginRtn; ftpsShutdownFlag = FALSE; ftpsCurrentClients = 0; /* Create the FTP server control socket. */ ftpdServerSock = socket (AF_INET, SOCK_STREAM, 0); if (ftpdServerSock < 0) return (ERROR); /* Create data structures for managing client connections. */ lstInit (&ftpsSessionList); ftpsMutexSem = semMCreate (SEM_Q_FIFO | SEM_DELETE_SAFE); if (ftpsMutexSem == NULL) { close (ftpdServerSock); return (ERROR); } ftpsSignalSem = semBCreate (SEM_Q_FIFO, SEM_EMPTY); if (ftpsSignalSem == NULL) { close (ftpdServerSock); semDelete (ftpsMutexSem); return (ERROR); } /* Setup control connection for client requests. */ ctrlAddr.sin_family = AF_INET; ctrlAddr.sin_addr.s_addr = INADDR_ANY; ctrlAddr.sin_port = htons (FTP_DAEMON_PORT); if (setsockopt (ftpdServerSock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0) { close (ftpdServerSock); semDelete (ftpsMutexSem); semDelete (ftpsSignalSem); return (ERROR); } if (bind (ftpdServerSock, (struct sockaddr *) &ctrlAddr, sizeof (ctrlAddr)) < 0) { close (ftpdServerSock); semDelete (ftpsMutexSem); semDelete (ftpsSignalSem); return (ERROR); } if (listen (ftpdServerSock, 5) < 0) { close (ftpdServerSock); semDelete (ftpsMutexSem); semDelete (ftpsSignalSem); return (ERROR); } /* Create a FTP server task to receive client requests. */ ftpdTaskId = taskSpawn ("tFtpdTask", ftpdTaskPriority - 1, ftpdTaskOptions, stackSize == 0 ? ftpdWorkTaskStackSize : stackSize, (FUNCPTR) ftpdTask, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); if (ftpdTaskId == ERROR) { ftpdDebugMsg ("ERROR: ftpdTask cannot be created\n",0,0,0,0); close (ftpdServerSock); semDelete (ftpsMutexSem); semDelete (ftpsSignalSem); return (ERROR); } ftpsActive = TRUE; ftpdDebugMsg ("ftpdTask created\n",0,0,0,0); return (OK); }/********************************************************************************* ftpdDelete - terminate the FTP server task** This routine halts the FTP server and closes the control connection. All* client sessions are removed after completing any commands in progress.* When this routine executes, no further client connections will be accepted* until the server is restarted. This routine is not reentrant and must not* be called from interrupt level.** NOTE: If any file transfer operations are in progress when this routine is* executed, the transfers will be aborted, possibly leaving incomplete files* on the destination host.** RETURNS: OK if shutdown completed, or ERROR otherwise.** ERRNO: N/A** INTERNAL* This routine is synchronized with the deletion routine for a client session* so that the exit of the client tasks can be detected. It also shares a* critical section with the creation of client sessions to prevent orphaned* tasks, which would occur if a session were added after this routine had* shut down all known clients.*/STATUS ftpdDelete (void) { BOOL serverActive = FALSE; FTPD_SESSION_DATA * pData; if (! ftpsActive) /* Automatic success if server is not running. */ return (OK); /* * Remove the FTP server task to prevent additional sessions from starting. * The exclusion semaphore guarantees a stable list of active clients. */ semTake (ftpsMutexSem, WAIT_FOREVER); taskDelete (ftpdTaskId); ftpdTaskId = -1; if (ftpsCurrentClients != 0) serverActive = TRUE; /* * Set the shutdown flag so that any secondary server tasks will exit * as soon as possible. Once the FTP server has started, this routine is * the only writer of the flag and the secondary tasks are the only * readers. To avoid unnecessary blocking, the secondary tasks do not * guard access to this flag when checking for a pending shutdown during * normal processing. Those tasks do protect access to this flag during * their cleanup routine to prevent a race condition which would result * in incorrect use of the signalling semaphore. */ ftpsShutdownFlag = TRUE; /* * Close the command sockets of any active sessions to prevent further * activity. If the session is waiting for a command from a socket, * the close will trigger the session exit. */ pData = (FTPD_SESSION_DATA *)lstFirst (&ftpsSessionList); while (pData != NULL) { ftpdSockFree (&pData->cmdSock); pData = (FTPD_SESSION_DATA *)lstNext (&pData->node); } semGive (ftpsMutexSem); /* Wait for all secondary tasks to exit. */ if (serverActive) { /* * When a shutdown is in progress, the cleanup routine of the last * client task to exit gives the signalling semaphore. */ semTake (ftpsSignalSem, WAIT_FOREVER); } /* * Remove the original socket - this occurs after all secondary tasks * have exited to avoid prematurely closing their control sockets. */ ftpdSockFree (&ftpdServerSock); /* Remove the protection and signalling semaphores and list of clients. */ lstFree (&ftpsSessionList); /* Sanity check - should already be empty. */ semDelete (ftpsMutexSem); semDelete (ftpsSignalSem); ftpsActive = FALSE; return (OK); }/********************************************************************************* ftpdSessionAdd - add a new entry to the ftpd session slot list** Each of the incoming FTP sessions is associated with an entry in the* FTP server's session list which records session-specific context for each* control connection established by the FTP clients. This routine creates and* initializes a new entry in the session list, unless the needed memory is not* available or the upper limit for simultaneous connections is reached.** RETURNS: A pointer to the session list entry, or NULL of none available.** ERRNO: N/A** NOMANUAL** INTERNAL* This routine executes within a critical section of the primary FTP server* task, so mutual exclusion is already present when adding entries to the* client list and updating the shared variables indicating the current number* of connected clients.*/LOCAL FTPD_SESSION_DATA *ftpdSessionAdd (void) { FAST FTPD_SESSION_DATA *pSlot; if (ftpsCurrentClients == ftpsMaxClients) return (NULL); /* get memory for the new session entry */ pSlot = (FTPD_SESSION_DATA *) calloc (sizeof (FTPD_SESSION_DATA), 1); if (pSlot == NULL) { return (NULL); } /* initialize key fields in the newly acquired slot */ pSlot->dataSock = FTPD_SOCK_FREE; pSlot->cmdSock = FTPD_SOCK_FREE; pSlot->cmdSockError = OK; pSlot->status = FTPD_STREAM_MODE | FTPD_ASCII_TYPE | FTPD_NO_RECORD_STRU; pSlot->byteCount = 0; /* assign the default directory for this guy */ ioDefPathGet (pSlot->curDirName); /* Add new entry to the list of active sessions. */ lstAdd (&ftpsSessionList, &pSlot->node); ftpdNumTasks++; ftpsCurrentClients++; return (pSlot); }/********************************************************************************* ftpdSessionDelete - remove an entry from the FTP session list** This routine removes the session-specific context from the session list* after the client exits or a fatal error occurs.** RETURNS: N/A** ERRNO: N/A** NOMANUAL** INTERNAL* Unless an error occurs, this routine is only called in response to a* pending server shutdown, indicated by the shutdown flag. Even if the* shutdown flag is not detected and this routine is called because of an* error, the appropriate signal will still be sent to any pending shutdown* routine. The shutdown routine will only return after any active client* sessions have exited.*/LOCAL void ftpdSessionDelete ( FAST FTPD_SESSION_DATA *pSlot /* pointer to the slot to be deleted */ ) { if (pSlot == NULL) /* null slot? don't do anything */ return; /* * The deletion of a session entry must be an atomic operation to support * an upper limit on the number of simultaneous connections allowed. * This mutual exclusion also prevents a race condition with the server * shutdown routine. The last client session will always send an exit * signal to the shutdown routine, whether or not the shutdown flag was * detected during normal processing. */ semTake (ftpsMutexSem, WAIT_FOREVER); --ftpdNumTasks; --ftpsCurrentClients; lstDelete (&ftpsSessionList, &pSlot->node); ftpdSockFree (&pSlot->cmdSock); /* release data and command sockets */ ftpdSockFree (&pSlot->dataSock); free (pSlot); /* Send required signal if all sessions are closed. */ if (ftpsShutdownFlag) { if (ftpsCurrentClients == 0) semGive (ftpsSignalSem); } semGive (ftpsMutexSem); return; }/********************************************************************************* ftpdWorkTask - main protocol processor for the FTP service** This function handles all the FTP protocol requests by parsing* the request string and performing appropriate actions and returning* the result strings. The main body of this function is a large* FOREVER loop which reads in FTP request commands from the client* located on the other side of the connection. If the result of
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -