814 lines
18 KiB
C++
814 lines
18 KiB
C++
/*++
|
||
|
||
Copyright (c) 1994 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
session.cxx
|
||
|
||
Abstract:
|
||
|
||
Contains functions to create and delete FTP_SESSION_INFOs
|
||
|
||
Contents:
|
||
FtpSessionInitialize
|
||
FtpSessionTerminate
|
||
AcquireFtpSessionList
|
||
ReleaseFtpSessionList
|
||
AcquireFtpSessionLock
|
||
ReleaseFtpSessionLock
|
||
CleanupFtpSessions
|
||
TerminateFtpSession
|
||
DereferenceFtpSession
|
||
CreateFtpSession
|
||
(DestroyFtpSession)
|
||
FindFtpSession
|
||
|
||
Author:
|
||
|
||
Richard L Firth (rfirth) 09-Jun-95
|
||
|
||
Environment:
|
||
|
||
Win32 user-level DLL
|
||
|
||
Revision History:
|
||
|
||
09-Jun-95 rfirth
|
||
Created
|
||
|
||
--*/
|
||
|
||
#include <wininetp.h>
|
||
#include "ftpapih.h"
|
||
#include "..\urlcache\debug.h"
|
||
//
|
||
// private prototypes
|
||
//
|
||
|
||
PRIVATE
|
||
VOID
|
||
DestroyFtpSession(
|
||
IN LPFTP_SESSION_INFO SessionInfo
|
||
);
|
||
|
||
//
|
||
// public data
|
||
//
|
||
|
||
//
|
||
// FtpSessionList - a doubly-linked list of all the FTP_SESSION_INFOs owned by
|
||
// this process
|
||
//
|
||
|
||
PUBLIC SERIALIZED_LIST FtpSessionList;
|
||
|
||
//
|
||
// external data
|
||
//
|
||
|
||
extern BOOL InDllCleanup;
|
||
|
||
//
|
||
// functions
|
||
//
|
||
|
||
#if INET_DEBUG
|
||
|
||
|
||
VOID
|
||
FtpSessionInitialize(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initializes any global data items for this module.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_PRINT(FTP, INFO,
|
||
("FTPSESSIONLIST: Initializing ftp session list\r\n"));
|
||
InitializeSerializedList(&FtpSessionList);
|
||
}
|
||
|
||
|
||
VOID
|
||
FtpSessionTerminate(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Terminates any items initialized by FtpSessionInitialize()
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_PRINT(FTP, INFO,
|
||
("FTPSESSIONLIST: Terminating ftp session list\r\n"));
|
||
TerminateSerializedList(&FtpSessionList);
|
||
}
|
||
|
||
|
||
VOID
|
||
AcquireFtpSessionList(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Acquires the FtpSessionList lock
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_PRINT(FTP, INFO,
|
||
("FTPSESSIONLIST: Trying to acquiring ftp session list\r\n"));
|
||
LockSerializedList(&FtpSessionList);
|
||
DEBUG_PRINT(FTP, INFO,
|
||
("FTPSESSIONLIST: Acquired ftp session list\r\n"));
|
||
}
|
||
|
||
|
||
VOID
|
||
ReleaseFtpSessionList(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Releases the FtpSessionList lock
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_PRINT(FTP, INFO,
|
||
("FTPSESSIONLIST: Trying to release ftp session list\r\n"));
|
||
UnlockSerializedList(&FtpSessionList);
|
||
DEBUG_PRINT(FTP, INFO,
|
||
("FTPSESSIONLIST: Release ftp session list\r\n"));
|
||
}
|
||
|
||
|
||
VOID
|
||
AcquireFtpSessionLock(
|
||
IN LPFTP_SESSION_INFO SessionInfo
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Acquires an individual session info lock
|
||
|
||
Arguments:
|
||
|
||
SessionInfo - pointer to FTP_SESSION_INFO to lock
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_PRINT(FTP, INFO,
|
||
("FTPSESSION: Trying to acquire %d\r\n", &SessionInfo->CriticalSection));
|
||
EnterCriticalSection(&SessionInfo->CriticalSection);
|
||
DEBUG_PRINT(FTP, INFO,
|
||
("FTPSESSION: Acquired ftp session %d\r\n", &SessionInfo->CriticalSection));
|
||
}
|
||
|
||
|
||
VOID
|
||
ReleaseFtpSessionLock(
|
||
IN LPFTP_SESSION_INFO SessionInfo
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Releases an individual session info lock
|
||
|
||
Arguments:
|
||
|
||
SessionInfo - pointer to FTP_SESSION_INFO to unlock
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_PRINT(FTP, INFO,
|
||
("FTPSESSION: Trying to release %d\r\n", &SessionInfo->CriticalSection));
|
||
LeaveCriticalSection(&SessionInfo->CriticalSection);
|
||
DEBUG_PRINT(FTP, INFO,
|
||
("FTPSESSION: Released ftp session %d\r\n", &SessionInfo->CriticalSection));
|
||
}
|
||
|
||
#endif // INET_DEBUG
|
||
|
||
|
||
VOID
|
||
CleanupFtpSessions(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Terminates all active FTP sessions owned by this process
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
LPFTP_SESSION_INFO info;
|
||
|
||
DEBUG_ENTER((DBG_FTP,
|
||
None,
|
||
"CleanupFtpSessions",
|
||
NULL
|
||
));
|
||
|
||
//
|
||
// walk the session list. For each FTP_SESSION_INFO, close the data and
|
||
// control sockets, if open, then destroy the session
|
||
//
|
||
|
||
do {
|
||
|
||
DWORD error;
|
||
|
||
AcquireFtpSessionList();
|
||
|
||
info = (LPFTP_SESSION_INFO)HeadOfSerializedList(&FtpSessionList);
|
||
if (info == (LPFTP_SESSION_INFO)&FtpSessionList.List.Flink) {
|
||
ReleaseFtpSessionList();
|
||
break;
|
||
}
|
||
|
||
//
|
||
// kill the data and control sockets. Remorselessly. If the sockets are
|
||
// set to INVALID_SOCKET, then Close() will do the right thing
|
||
//
|
||
|
||
error = info->socketData->Close();
|
||
if (error != ERROR_SUCCESS) {
|
||
|
||
DEBUG_PRINT(SESSION,
|
||
ERROR,
|
||
("Data: Close(%x) returns %d\n",
|
||
info->socketData,
|
||
error
|
||
));
|
||
|
||
}
|
||
|
||
error = info->socketControl->Close();
|
||
if (error != ERROR_SUCCESS) {
|
||
|
||
DEBUG_PRINT(SESSION,
|
||
ERROR,
|
||
("Control: Close(%x) returns %d\n",
|
||
info->socketControl,
|
||
error
|
||
));
|
||
|
||
}
|
||
|
||
ReleaseFtpSessionList();
|
||
DestroyFtpSession(info);
|
||
} while (1);
|
||
|
||
DEBUG_LEAVE(0);
|
||
}
|
||
|
||
|
||
VOID
|
||
TerminateFtpSession(
|
||
IN LPFTP_SESSION_INFO SessionInfo
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initiates termination of the FTP_SESSION_INFO object
|
||
|
||
Arguments:
|
||
|
||
SessionInfo - pointer to FTP_SESSION_INFO to terminate
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOL destroy;
|
||
int i;
|
||
|
||
INET_ASSERT(SessionInfo != NULL);
|
||
INET_ASSERT(!(SessionInfo->Flags & FFTP_IN_DESTRUCTOR));
|
||
INET_ASSERT(SessionInfo->ReferenceCount >= 2);
|
||
|
||
DEBUG_ENTER((DBG_FTP,
|
||
None,
|
||
"TerminateFtpSession",
|
||
"%x",
|
||
SessionInfo
|
||
));
|
||
|
||
//
|
||
// first off, set the in-destructor flag. This will cause any other threads
|
||
// trying to find & reference this object to fail
|
||
//
|
||
|
||
SessionInfo->Flags |= FFTP_IN_DESTRUCTOR;
|
||
|
||
//
|
||
// now we need to decrement the reference count by 2, which should cause
|
||
// the FTP_SESSION_INFO to be destroyed
|
||
//
|
||
|
||
AcquireFtpSessionLock(SessionInfo);
|
||
INET_ASSERT(SessionInfo->ReferenceCount>=2);
|
||
if (InterlockedDecrement(&SessionInfo->ReferenceCount))
|
||
{
|
||
InterlockedDecrement(&SessionInfo->ReferenceCount);
|
||
}
|
||
|
||
if (SessionInfo->ReferenceCount == 0) {
|
||
DestroyFtpSession(SessionInfo);
|
||
} else {
|
||
ReleaseFtpSessionLock(SessionInfo);
|
||
DEBUG_PRINT(REFCOUNT,
|
||
WARNING,
|
||
("unexpected: SessionInfo(%x)->ReferenceCount = %d\n",
|
||
SessionInfo,
|
||
SessionInfo->ReferenceCount
|
||
));
|
||
}
|
||
DEBUG_LEAVE(0);
|
||
}
|
||
|
||
|
||
VOID
|
||
DereferenceFtpSession(
|
||
IN LPFTP_SESSION_INFO SessionInfo
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Reduces the reference count on an FTP_SESSION_INFO object by 1. If the
|
||
reference count goes to 0, the FTP_SESSION_INFO is destroyed
|
||
|
||
Arguments:
|
||
|
||
SessionInfo - pointer to FTP_SESSION_INFO to dereference
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
INET_ASSERT(SessionInfo->ReferenceCount >= 1);
|
||
|
||
DEBUG_ENTER((DBG_FTP,
|
||
None,
|
||
"DereferenceFtpSession",
|
||
"%x",
|
||
SessionInfo
|
||
));
|
||
|
||
AcquireFtpSessionLock(SessionInfo);
|
||
INET_ASSERT(SessionInfo->ReferenceCount>1);
|
||
if (InterlockedDecrement(&SessionInfo->ReferenceCount) == 0) {
|
||
if (SessionInfo->socketControl->IsValid()
|
||
|| SessionInfo->socketData->IsValid())
|
||
{
|
||
// All sockets should have been closed by now. However
|
||
// there's a case where two wFtpDisconnect closes the connection
|
||
// but another thread revives those sockets.
|
||
// In this case, shutting down the session would be inappropriate.
|
||
// We should reset the ref count so that it may continue
|
||
INET_ASSERT((SessionInfo->Flags & FFTP_IN_DESTRUCTOR));
|
||
InterlockedIncrement(&SessionInfo->ReferenceCount);
|
||
ReleaseFtpSessionLock(SessionInfo);
|
||
}
|
||
else
|
||
{
|
||
DestroyFtpSession(SessionInfo);
|
||
}
|
||
} else {
|
||
ReleaseFtpSessionLock(SessionInfo);
|
||
DEBUG_PRINT(REFCOUNT,
|
||
INFO,
|
||
("SessionInfo(%x)->ReferenceCount = %d\n",
|
||
SessionInfo,
|
||
SessionInfo->ReferenceCount
|
||
));
|
||
}
|
||
|
||
DEBUG_LEAVE(0);
|
||
}
|
||
|
||
|
||
DWORD
|
||
CreateFtpSession(
|
||
IN LPSTR lpszHost,
|
||
IN INTERNET_PORT Port,
|
||
IN DWORD dwFlags,
|
||
OUT LPFTP_SESSION_INFO* lpSessionInfo
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Creates a new FTP_SESSION_INFO object. If successful, adds the new object
|
||
to the FtpSessionList; the reference count of the new object will be 1 (i.e.
|
||
it is unowned)
|
||
|
||
Arguments:
|
||
|
||
lpszHost - pointer to name of the host we are connecting to. Mainly
|
||
for diagnostic purposes (can remove it later)
|
||
|
||
Port - Port at which the FTP server listens. Only in anomalous
|
||
circumstances will this not be 21
|
||
|
||
dwFlags - flags controlling session creation. Can be:
|
||
- FFTP_PASSIVE_MODE
|
||
|
||
lpSessionInfo - pointer to returned session info
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Success - ERROR_SUCCESS
|
||
|
||
Failure - ERROR_NOT_ENOUGH_MEMORY
|
||
Couldn't allocate memory
|
||
|
||
ERROR_INTERNET_OUT_OF_HANDLES
|
||
Couldn't allocate a handle
|
||
|
||
ERROR_INTERNET_SHUTDOWN
|
||
The DLL is being unloaded
|
||
|
||
--*/
|
||
|
||
{
|
||
LPFTP_SESSION_INFO sessionInfo;
|
||
DWORD error;
|
||
|
||
DEBUG_ENTER((DBG_FTP,
|
||
Dword,
|
||
"CreateFtpSession",
|
||
"%q, %d, %x",
|
||
lpszHost,
|
||
Port,
|
||
lpSessionInfo
|
||
));
|
||
|
||
//
|
||
// if the DLL is being unloaded then return the shutdown error
|
||
//
|
||
|
||
if (InDllCleanup) {
|
||
error = ERROR_INTERNET_SHUTDOWN;
|
||
goto quit;
|
||
}
|
||
|
||
//
|
||
// allocate the FTP_SESSION_INFO 'object' and its various sub-assemblies
|
||
//
|
||
|
||
sessionInfo = NEW(FTP_SESSION_INFO);
|
||
if (sessionInfo != NULL) {
|
||
sessionInfo->Host = NEW_STRING(lpszHost);
|
||
if (sessionInfo->Host != NULL) {
|
||
error = AllocateHandle((LPVOID)sessionInfo, &sessionInfo->Handle);
|
||
if (error == ERROR_SUCCESS) {
|
||
|
||
INET_ASSERT(sessionInfo->ServerType == FTP_SERVER_TYPE_UNKNOWN);
|
||
|
||
//
|
||
// initialize any non-zero fields
|
||
//
|
||
|
||
InitializeListHead(&sessionInfo->List);
|
||
sessionInfo->Port = Port;
|
||
sessionInfo->Flags = dwFlags;
|
||
sessionInfo->ReferenceCount = 1;
|
||
InitializeListHead(&sessionInfo->FindFileList);
|
||
InitializeCriticalSection(&sessionInfo->CriticalSection);
|
||
SetSessionSignature(sessionInfo);
|
||
|
||
//
|
||
// Allocate Our Socket Objects.
|
||
//
|
||
|
||
//
|
||
// Isn't this kinda of messy? I've copied the format of
|
||
// surrounding block, since there is no clean quit case in this
|
||
// function... arhh.
|
||
//
|
||
|
||
sessionInfo->socketControl = new ICSocket();
|
||
if ( sessionInfo->socketControl )
|
||
{
|
||
sessionInfo->socketData = new ICSocket();
|
||
if ( sessionInfo->socketData )
|
||
{
|
||
sessionInfo->socketListener = new ICSocket();
|
||
if ( sessionInfo->socketListener == NULL )
|
||
{
|
||
sessionInfo->socketControl->Dereference();
|
||
sessionInfo->socketData->Dereference();
|
||
error = ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
sessionInfo->socketControl->Dereference();
|
||
error = ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
|
||
}
|
||
else
|
||
{
|
||
error = ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
}
|
||
} else {
|
||
error = ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
} else {
|
||
error = ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
if (error == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// add this FTP_SESSION_INFO to the object list
|
||
//
|
||
|
||
InsertAtHeadOfSerializedList(&FtpSessionList, &sessionInfo->List);
|
||
*lpSessionInfo = sessionInfo;
|
||
|
||
DEBUG_PRINT(SESSION,
|
||
INFO,
|
||
("handle = %x, SessionInfo = %x\n",
|
||
sessionInfo->Handle,
|
||
sessionInfo
|
||
));
|
||
|
||
} else if (sessionInfo != NULL) {
|
||
if (sessionInfo->Host != NULL) {
|
||
DEL_STRING(sessionInfo->Host);
|
||
}
|
||
DEL(sessionInfo);
|
||
}
|
||
|
||
quit:
|
||
|
||
DEBUG_LEAVE(error);
|
||
|
||
return error;
|
||
}
|
||
|
||
|
||
PRIVATE
|
||
VOID
|
||
DestroyFtpSession(
|
||
IN LPFTP_SESSION_INFO SessionInfo
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Removes an FTP_SESSION_INFO object from the session list and destroys it.
|
||
|
||
Note: SessionInfo MUST be owned by the current thread (critical section held
|
||
but reference count == 0)
|
||
|
||
Arguments:
|
||
|
||
SessionInfo - pointer to FTP_SESSION_INFO to delete
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_FTP,
|
||
None,
|
||
"DestroyFtpSession",
|
||
"%x",
|
||
SessionInfo
|
||
));
|
||
|
||
INET_DEBUG_ASSERT(SessionInfo->Signature == FTP_SESSION_SIGNATURE);
|
||
INET_ASSERT(!SessionInfo->socketControl->IsValid());
|
||
INET_ASSERT(!SessionInfo->socketData->IsValid());
|
||
INET_ASSERT(SessionInfo->ReferenceCount == 0);
|
||
|
||
RemoveFromSerializedList(&FtpSessionList, (PLIST_ENTRY)&SessionInfo->List);
|
||
|
||
INET_DEBUG_ASSERT(SessionInfo->List.Flink == NULL);
|
||
INET_DEBUG_ASSERT(SessionInfo->List.Blink == NULL);
|
||
|
||
ReleaseFtpSessionLock(SessionInfo);
|
||
|
||
if (SessionInfo->Handle) {
|
||
FreeHandle(SessionInfo->Handle);
|
||
}
|
||
|
||
if (SessionInfo->Host != NULL) {
|
||
DEL_STRING(SessionInfo->Host);
|
||
}
|
||
|
||
if ( SessionInfo->socketControl )
|
||
SessionInfo->socketControl->Dereference();
|
||
|
||
if ( SessionInfo->socketData )
|
||
SessionInfo->socketData->Dereference();
|
||
|
||
if ( SessionInfo->socketListener )
|
||
SessionInfo->socketListener->Dereference();
|
||
|
||
ClearFindList(&SessionInfo->FindFileList);
|
||
|
||
DeleteCriticalSection(&SessionInfo->CriticalSection);
|
||
|
||
DEL(SessionInfo);
|
||
|
||
DEBUG_LEAVE(0);
|
||
}
|
||
|
||
|
||
BOOL
|
||
FindFtpSession(
|
||
IN HANDLE Handle,
|
||
OUT LPFTP_SESSION_INFO *lpSessionInfo
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Searches the session list for an FTP_SESSION_INFO object. If found, the
|
||
FTP_SESSION_INFO's reference count is incremented by 1
|
||
|
||
Arguments:
|
||
|
||
Handle - handle to search for
|
||
|
||
lpSessionInfo - pointer to returned FTP_SESSION_INFO if found
|
||
|
||
Return Value:
|
||
|
||
BOOL
|
||
Success - TRUE, *lppSessionInfo is pointer to FTP_SESSION_INFO
|
||
|
||
Failure - FALSE
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOL found;
|
||
LPFTP_SESSION_INFO sessionInfo;
|
||
|
||
DEBUG_ENTER((DBG_FTP,
|
||
Bool,
|
||
"FindFtpSession",
|
||
"%x",
|
||
Handle
|
||
));
|
||
|
||
//
|
||
// lock the list, to prevent the element from moving under us
|
||
//
|
||
|
||
AcquireFtpSessionList();
|
||
|
||
found = FALSE;
|
||
for (sessionInfo = (LPFTP_SESSION_INFO)FtpSessionList.List.Flink;
|
||
sessionInfo != (LPFTP_SESSION_INFO)&FtpSessionList.List;
|
||
sessionInfo = (LPFTP_SESSION_INFO)(sessionInfo->List.Flink)) {
|
||
|
||
if (sessionInfo->Handle == Handle) {
|
||
AcquireFtpSessionLock(sessionInfo);
|
||
|
||
//
|
||
// if the destructor flag is set then another thread is already
|
||
// destroying this session - we cannot return it. Treat as not
|
||
// found
|
||
//
|
||
|
||
if (!(sessionInfo->Flags & FFTP_IN_DESTRUCTOR)) {
|
||
|
||
//
|
||
// although we're holding the session info lock (a critical
|
||
// section), we still need to perform interlocked increment
|
||
// on the reference count
|
||
//
|
||
|
||
InterlockedIncrement(&sessionInfo->ReferenceCount);
|
||
found = TRUE;
|
||
}
|
||
ReleaseFtpSessionLock(sessionInfo);
|
||
break;
|
||
}
|
||
}
|
||
|
||
ReleaseFtpSessionList();
|
||
|
||
if (found) {
|
||
*lpSessionInfo = sessionInfo;
|
||
|
||
DEBUG_PRINT(SESSION,
|
||
INFO,
|
||
("handle = %x, FTP_SESSION_INFO = %x\n",
|
||
Handle,
|
||
sessionInfo
|
||
));
|
||
|
||
} else {
|
||
|
||
DEBUG_PRINT(SESSION,
|
||
ERROR,
|
||
("handle %x: not found\n",
|
||
Handle
|
||
));
|
||
|
||
}
|
||
|
||
DEBUG_LEAVE(found);
|
||
|
||
return found;
|
||
}
|