352 lines
11 KiB
C++
352 lines
11 KiB
C++
/*--
|
|
Copyright (c) 1995-1998 Microsoft Corporation
|
|
Module Name: REQUEST.H
|
|
Author: Arul Menezes
|
|
Abstract: HTTP request class
|
|
--*/
|
|
|
|
|
|
|
|
// Scalar types used by CHTTPRequest
|
|
typedef enum {
|
|
TOK_GET=1,
|
|
TOK_HEAD,
|
|
TOK_POST,
|
|
TOK_UNKNOWN_VERB,
|
|
TOK_DATE,
|
|
TOK_PRAGMA,
|
|
TOK_COOKIE,
|
|
TOK_ACCEPT,
|
|
TOK_REFERER,
|
|
TOK_UAGENT,
|
|
TOK_AUTH,
|
|
TOK_IFMOD,
|
|
TOK_TYPE,
|
|
TOK_LENGTH,
|
|
TOK_ENCODING,
|
|
TOK_CONNECTION,
|
|
}
|
|
TOKEN;
|
|
|
|
typedef enum
|
|
{
|
|
CONN_NONE = 0,
|
|
CONN_CLOSE = 1,
|
|
CONN_KEEP = 2,
|
|
}
|
|
CONNHEADER;
|
|
|
|
#define CHTTPREQUEST_SIG 0xAB0D
|
|
|
|
|
|
// This object is the top-level object for an incoming HTTP request. One such object
|
|
// is created per request & a thread is created to handle it.
|
|
|
|
class CFilterInfo;
|
|
|
|
class CHttpRequest
|
|
{
|
|
|
|
// socket
|
|
DWORD m_dwSig;
|
|
SOCKET m_socket;
|
|
|
|
// buffers
|
|
CBuffer m_bufRequest;
|
|
CBuffer m_bufRespBody;
|
|
|
|
// method, version, URL etc. Direct results of parse
|
|
PSTR m_pszMethod;
|
|
PSTR m_pszURL;
|
|
PSTR m_pszContentType;
|
|
DWORD m_dwContentLength;
|
|
PSTR m_pszAccept;
|
|
FILETIME m_ftIfModifiedSince;
|
|
DWORD m_dwIfModifiedLength;
|
|
BOOL m_fKeepAlive;
|
|
PSTR m_pszCookie;
|
|
|
|
|
|
// Decoded URL (indirect results of parse)
|
|
PSTR m_pszQueryString;
|
|
PWSTR m_wszPath;
|
|
PWSTR m_wszExt;
|
|
PSTR m_pszPathInfo;
|
|
PSTR m_pszPathTranslated;
|
|
|
|
// VRoot information
|
|
SCRIPT_TYPE m_VRootScriptType;
|
|
DWORD m_dwPermissions;
|
|
AUTHLEVEL m_AuthLevelReqd;
|
|
|
|
// Logging members
|
|
PSTR m_pszLogParam;
|
|
RESPONSESTATUS m_rs;
|
|
|
|
BOOL m_fBufferedResponse; // Are we using m_bufResponse or sending straight to client?
|
|
|
|
// Async support
|
|
HANDLE m_hEvent;
|
|
DWORD m_dwStatus;
|
|
LPEXTENSION_CONTROL_BLOCK m_pECB;
|
|
PVOID m_pvContext;
|
|
PFN_HSE_IO_COMPLETION m_pfnCompletion;
|
|
|
|
// Parsing functions
|
|
void FreeHeaders(void)
|
|
{
|
|
MyFree(m_pszMethod);
|
|
MyFree(m_pszURL);
|
|
MyFree(m_pszContentType);
|
|
MyFree(m_pszAccept);
|
|
MyFree(m_pszQueryString);
|
|
MyFree(m_wszPath);
|
|
MyFree(m_wszExt);
|
|
MyFree(m_pszCookie);
|
|
MyFree(m_pszLogParam);
|
|
MyFree(m_pszPathInfo);
|
|
MyFree(m_pszPathTranslated);
|
|
}
|
|
BOOL ParseHeaders();
|
|
BOOL MyCrackURL(PSTR pszRawURL, int iLen);
|
|
|
|
public:
|
|
BOOL ParseMethod(PCSTR pszMethod, int cbMethod);
|
|
BOOL ParseContentLength(PCSTR pszMethod, TOKEN id);
|
|
BOOL ParseContentType(PCSTR pszMethod, TOKEN id);
|
|
BOOL ParseIfModifiedSince(PCSTR pszMethod, TOKEN id);
|
|
BOOL ParseAuthorization(PCSTR pszMethod, TOKEN id);
|
|
BOOL ParseAccept(PCSTR pszMethod, TOKEN id);
|
|
BOOL ParseConnection(PCSTR pszMethod, TOKEN id);
|
|
BOOL ParseCookie(PCSTR pszMethod, TOKEN id);
|
|
BOOL HandleNTLMAuth(PSTR pszNTLMData);
|
|
PSTR m_pszNTLMOutBuf; // buffer to send client on NTLM response, Base64 encoded
|
|
DWORD m_dwVersion; // LOWORD=minor, HIWORD=major.
|
|
TOKEN m_idMethod;
|
|
CBuffer m_bufRespHeaders;
|
|
CFilterInfo *m_pFInfo; // Filter state information
|
|
|
|
private:
|
|
|
|
// Authentication data members
|
|
AUTHLEVEL m_AuthLevelGranted;
|
|
PSTR m_pszAuthType;
|
|
PSTR m_pszRawRemoteUser; // Holds base64 encoded data, before auth decodes it
|
|
PSTR m_pszRemoteUser;
|
|
PSTR m_pszPassword;
|
|
AUTH_NTLM m_NTLMState; // state info for NTLM process, needs to be saved across requests
|
|
DWORD m_dwAuthFlags;
|
|
WCHAR *m_wszVRootUserList; // Do NOT free this, points to global mem. Contains user/group ACL
|
|
|
|
// Authentication functions
|
|
void FreeAuth(void)
|
|
{
|
|
MyFree(m_pszAuthType);
|
|
MyFree(m_pszRawRemoteUser);
|
|
MyFree(m_pszRemoteUser);
|
|
MyFree(m_pszPassword);
|
|
MyFree(m_pszNTLMOutBuf);
|
|
// Don't free NTLM structs in here
|
|
}
|
|
|
|
BOOL CheckAuth(AUTHLEVEL AuthLevelReqd)
|
|
{
|
|
return ( (AuthLevelReqd <= m_AuthLevelGranted));
|
|
}
|
|
BOOL CheckAuth() { return CheckAuth(m_AuthLevelReqd); }
|
|
|
|
|
|
// File GET/HEAD handling functions
|
|
BOOL IsNotModified(HANDLE hFile, DWORD dwLength);
|
|
static RESPONSESTATUS GLEtoStatus(int iGLE);
|
|
|
|
|
|
// Directory: Default page & browsing functions
|
|
BOOL MapDirToDefaultPage(void);
|
|
BOOL EmitDirListing(void);
|
|
|
|
void Init();
|
|
|
|
|
|
BOOL ReadPostData(DWORD dwMaxSizeToRead, BOOL fInitialPostRead);
|
|
CONNHEADER GetConnHeader() { return m_fKeepAlive ? CONN_KEEP : CONN_CLOSE; }
|
|
|
|
|
|
// ISAPI extension handling functions
|
|
BOOL HandleScript();
|
|
BOOL ExecuteISAPI(void);
|
|
void FillECB(LPEXTENSION_CONTROL_BLOCK pECB);
|
|
BOOL GetServerVariable(PSTR pszVar, PVOID pvOutBuf, PDWORD pdwOutSize, BOOL fFromFilter);
|
|
BOOL WriteClient(PVOID pvBuf, PDWORD pdwSize, BOOL fFromFilter);
|
|
BOOL WriteClientAsync(PVOID pvBuf, PDWORD pdwSize, BOOL fFromFilter);
|
|
BOOL ServerSupportFunction(DWORD dwReq, PVOID pvBuf, PDWORD pdwSize, PDWORD pdwType);
|
|
BOOL ReadClient(PVOID pv, PDWORD pdw);
|
|
|
|
void StartRemoveISAPICacheIfNeeded();
|
|
|
|
// Filter Specific
|
|
BOOL FillFC(PHTTP_FILTER_CONTEXT pfc, DWORD dwNotifyType,
|
|
LPVOID *ppStFilter, LPVOID *ppStFilterOrg,
|
|
PSTR *ppvBuf1, int *pcbBuf, PSTR *ppvBuf2, int *pcbBuf2);
|
|
void CleanupFC(DWORD dwNotifyType, LPVOID* pFilterStruct, LPVOID *pFilterStructOrg,
|
|
PSTR *ppvBuf1, int *pcbBuf, PSTR *ppvBuf2);
|
|
|
|
BOOL AuthenticateFilter();
|
|
BOOL FilterMapURL(PSTR pvBuf, WCHAR *wszPath, DWORD *pdwSize, DWORD dwBufNeeded, PSTR pszURLEx=NULL);
|
|
BOOL MapURLToPath(PSTR pszBuffer, PDWORD pdwSize, LPHSE_URL_MAPEX_INFO pUrlMapEx=NULL);
|
|
|
|
// Filter Callbacks
|
|
BOOL ServerSupportFunction(enum SF_REQ_TYPE sfReq,PVOID pData,ULONG_PTR ul1, ULONG_PTR ul2);
|
|
BOOL GetHeader(LPSTR lpszName, LPVOID lpvBuffer, LPDWORD lpdwSize);
|
|
BOOL SetHeader(LPSTR lpszName, LPSTR lpszValue);
|
|
BOOL AddResponseHeaders(LPSTR lpszHeaders,DWORD dwReserved);
|
|
|
|
|
|
// ASP Setup Fcns
|
|
BOOL ExecuteASP();
|
|
BOOL FillACB(void *p, HINSTANCE hInst);
|
|
|
|
public:
|
|
CHttpRequest(SOCKET sock)
|
|
{
|
|
Init();
|
|
m_socket = sock;
|
|
// Fixes BUG 11771. On a poorly formatted request line, if we didn't
|
|
// read in the http version right we assumed it's value was 0, since 0 < 1.0
|
|
// we'd treat this as a http/0.9 request, no headers would be sent.
|
|
m_dwVersion = MAKELONG(0,1);
|
|
}
|
|
|
|
~CHttpRequest();
|
|
BOOL ReInit();
|
|
|
|
void HandleRequest();
|
|
friend DWORD WINAPI HttpConnectionThread(LPVOID lpv);
|
|
|
|
void GenerateLog(PSTR szBuffer, DWORD_PTR *pdwToWrite);
|
|
// BOOL SetupHeader(PSTR *ppszHeader, int *piHeader, PSTR *ppszExtra, int *piExtra, BOOL fAccessDenied);
|
|
|
|
DWORD GetLogBufferSize();
|
|
|
|
// ISAPI Extension / ASP Specific
|
|
friend BOOL WINAPI GetServerVariable(HCONN hConn, PSTR psz, PVOID pv, PDWORD pdw);
|
|
friend BOOL WINAPI ReadClient(HCONN hConn, PVOID pv, PDWORD pdw);
|
|
friend BOOL WINAPI WriteClient(HCONN hConn, PVOID pv, PDWORD pdw, DWORD dw);
|
|
friend BOOL WINAPI ServerSupportFunction(HCONN hConn, DWORD dwReq, PVOID pvBuf, PDWORD pdwSize, PDWORD pdwType);
|
|
|
|
|
|
// ASP SPECIFIC
|
|
friend BOOL WINAPI Flush(HCONN hConn);
|
|
friend BOOL WINAPI Clear(HCONN hConn);
|
|
friend BOOL WINAPI SetBuffer(HCONN hConn, BOOL fBuffer);
|
|
|
|
friend BOOL WINAPI AddHeader (HCONN hConn, LPSTR lpszName, LPSTR lpszValue);
|
|
|
|
|
|
// FILTER SPECIFIC
|
|
BOOL CallFilter(DWORD dwNotifyType, PSTR *ppvBuf1 = NULL,int *pcbBuf = NULL,
|
|
PSTR *ppvBuf2 = NULL, int *pcbBuf2 = NULL);
|
|
BOOL FilterNoResponse(void);
|
|
|
|
// Filters Friends (exposed to Filter dll)
|
|
friend BOOL WINAPI GetServerVariable(PHTTP_FILTER_CONTEXT pfc, PSTR psz, PVOID pv, PDWORD pdw);
|
|
friend BOOL WINAPI AddResponseHeaders(PHTTP_FILTER_CONTEXT pfc,LPSTR lpszHeaders,DWORD dwReserved);
|
|
friend VOID* WINAPI AllocMem(PHTTP_FILTER_CONTEXT pfc, DWORD cbSize, DWORD dwReserved);
|
|
friend BOOL WINAPI WriteClient(PHTTP_FILTER_CONTEXT pfc, PVOID pv, PDWORD pdw, DWORD dwFlags);
|
|
friend BOOL WINAPI ServerSupportFunction(PHTTP_FILTER_CONTEXT pfc,enum SF_REQ_TYPE sfReq,
|
|
PVOID pData, ULONG_PTR ul1, ULONG_PTR ul2);
|
|
friend BOOL WINAPI SetHeader(PHTTP_FILTER_CONTEXT pfc, LPSTR lpszName, LPSTR lpszValue);
|
|
friend BOOL WINAPI GetHeader(PHTTP_FILTER_CONTEXT pfc, LPSTR lpszName, LPVOID lpvBuffer, LPDWORD lpdwSize);
|
|
};
|
|
|
|
|
|
void SendFile(SOCKET sock, HANDLE hFile, CHttpRequest *pRequest);
|
|
// Response object. This object doesn't own any of the handles or pointers
|
|
// it uses so it doesnt free anything. The caller is responsible in all cases
|
|
// for keeping the handles & memory alive while this object is extant & freeing
|
|
// them as approp at a later time
|
|
class CHttpResponse
|
|
{
|
|
SOCKET m_socket;
|
|
RESPONSESTATUS m_rs;
|
|
CONNHEADER m_connhdr;
|
|
PCSTR m_pszType;
|
|
DWORD m_dwLength;
|
|
PCSTR m_pszRedirect;
|
|
PCSTR m_pszExtraHeaders;
|
|
PCSTR m_pszBody;
|
|
HANDLE m_hFile;
|
|
char m_szMime[MAXMIME];
|
|
CHttpRequest *m_pRequest; // calling request class, for callbacks
|
|
|
|
private:
|
|
void SetTypeFromExtW(PCWSTR wszExt)
|
|
{
|
|
DEBUGCHK(!m_pszType);
|
|
m_szMime[0] = 0;
|
|
if(wszExt)
|
|
{
|
|
CReg reg(HKEY_CLASSES_ROOT, wszExt);
|
|
MyW2A(reg.ValueSZ(L"Content Type"), m_szMime, sizeof(m_szMime));
|
|
}
|
|
if(m_szMime[0])
|
|
m_pszType = m_szMime;
|
|
else
|
|
m_pszType = cszTextHtml;
|
|
}
|
|
public:
|
|
CHttpResponse(SOCKET sock, RESPONSESTATUS status, CONNHEADER connhdr, CHttpRequest *pRequest=NULL)
|
|
{
|
|
ZEROMEM(this);
|
|
m_socket = sock;
|
|
m_rs = status;
|
|
m_connhdr = connhdr;
|
|
m_pRequest = pRequest;
|
|
}
|
|
// for generated bodies (dir listings, redirects etc) & default bodies
|
|
void SetBody(PCSTR pszBody, PCSTR pszType)
|
|
{
|
|
DEBUGCHK(!m_hFile && !m_pszBody && !m_pszType && !m_dwLength);
|
|
m_pszType = pszType;
|
|
m_dwLength = strlen(pszBody);
|
|
m_pszBody = pszBody;
|
|
}
|
|
// for error reponses (NOTE: some have no body, and hence psztatusBdy will be NULL in the table)
|
|
void SetDefaultBody()
|
|
{
|
|
if(rgStatus[m_rs].pszStatusBody)
|
|
SetBody(rgStatus[m_rs].pszStatusBody, cszTextHtml);
|
|
}
|
|
// for real files
|
|
void SetBody(HANDLE hFile, PCWSTR wszExt=NULL, DWORD dwLen=0)
|
|
{
|
|
DEBUGCHK(!m_hFile && !m_pszBody && !m_pszType && !m_dwLength);
|
|
m_hFile = hFile;
|
|
SetTypeFromExtW(wszExt);
|
|
if (dwLen)
|
|
m_dwLength = dwLen;
|
|
else
|
|
{
|
|
m_dwLength = GetFileSize(m_hFile, 0);
|
|
if (m_dwLength == 0)
|
|
m_dwLength = -1; // Use this to signify empty file, needed for keep-alives
|
|
}
|
|
}
|
|
private:
|
|
void SendBody();
|
|
public:
|
|
void SendHeaders(PCSTR pszExtraHeaders, PCSTR pszNewRespStatus);
|
|
void SendRedirect(PCSTR pszRedirect, BOOL fFromFilter=FALSE);
|
|
void SendResponse(PCSTR pszExtraHeaders=NULL, PCSTR pszNewRespStatus=NULL, BOOL fFromFilter=FALSE)
|
|
{
|
|
// see FilterNoResponse for comments on this
|
|
if (!fFromFilter && m_pRequest->FilterNoResponse())
|
|
return;
|
|
|
|
|
|
SendHeaders(pszExtraHeaders,pszNewRespStatus);
|
|
SendBody();
|
|
}
|
|
};
|
|
|