420 lines
9.6 KiB
C
420 lines
9.6 KiB
C
/*++
|
|
|
|
File: cgiwrap.c
|
|
|
|
Demonstrates an executable which can be used to load an ISAPI DLL like
|
|
a CGI script.
|
|
|
|
--*/
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
#include <httpext.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
//
|
|
// These are things that go out in the Response Header
|
|
//
|
|
|
|
#define HTTP_VER "HTTP/1.0"
|
|
#define SERVER_VERSION "Http-Srv-Beta2/1.0"
|
|
|
|
//
|
|
// Simple wrappers for the heap APIS
|
|
//
|
|
|
|
#define xmalloc(s) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (s))
|
|
#define xfree(s) HeapFree(GetProcessHeap(), 0, (s))
|
|
|
|
//
|
|
// The mandatory exports from the ISAPI DLL
|
|
//
|
|
|
|
typedef BOOL(WINAPI * VersionProc) (HSE_VERSION_INFO *);
|
|
typedef DWORD(*HttpExtProc) (EXTENSION_CONTROL_BLOCK *);
|
|
|
|
|
|
//
|
|
// Prototypes of the functions this sample implements
|
|
//
|
|
|
|
BOOL WINAPI FillExtensionControlBlock(EXTENSION_CONTROL_BLOCK *);
|
|
BOOL WINAPI GetServerVariable(HCONN, LPSTR, LPVOID, LPDWORD);
|
|
BOOL WINAPI ReadClient(HCONN, LPVOID, LPDWORD);
|
|
BOOL WINAPI WriteClient(HCONN, LPVOID, LPDWORD, DWORD);
|
|
BOOL WINAPI ServerSupportFunction(HCONN, DWORD, LPVOID, LPDWORD, LPDWORD);
|
|
char *MakeDateStr(VOID);
|
|
char *GetEnv(char *);
|
|
|
|
|
|
//
|
|
// In the startup of this program, we look at our executable name and
|
|
// replace the ".EXE" with ".DLL" to find the ISAPI DLL we need to load.
|
|
// This means that the executable need only be given the same "name" as
|
|
// the DLL to load. There is no recompilation required.
|
|
//
|
|
|
|
int __cdecl
|
|
main(int argc, char **argv)
|
|
{
|
|
|
|
HINSTANCE hDll;
|
|
VersionProc GetExtensionVersion;
|
|
HttpExtProc HttpExtensionProc;
|
|
HSE_VERSION_INFO version_info;
|
|
EXTENSION_CONTROL_BLOCK ECB;
|
|
DWORD rc;
|
|
char szModuleFileName[256], *c;
|
|
|
|
|
|
if (!GetModuleFileName(NULL, szModuleFileName, 256)) {
|
|
fprintf(stderr, "cannot get ModuleFileName %d\n", GetLastError());
|
|
return -1;
|
|
}
|
|
|
|
rc = strlen(szModuleFileName);
|
|
c = szModuleFileName + rc - 4; // Go back to the last "."
|
|
|
|
c[1] = 'D';
|
|
c[2] = 'L';
|
|
c[3] = 'L';
|
|
|
|
hDll = LoadLibrary(szModuleFileName); // Load our DLL
|
|
|
|
if (!hDll) {
|
|
fprintf(stderr, "Error: Failure to load %s.dll (%d)\n",
|
|
argv[0], GetLastError());
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// Find the exported functions
|
|
//
|
|
|
|
GetExtensionVersion = (VersionProc) GetProcAddress(hDll,
|
|
"GetExtensionVersion");
|
|
if (!GetExtensionVersion) {
|
|
fprintf(stderr, "Can't Get Extension Version %d\n", GetLastError());
|
|
return -1;
|
|
}
|
|
HttpExtensionProc = (HttpExtProc) GetProcAddress(hDll,
|
|
"HttpExtensionProc");
|
|
if (!HttpExtensionProc) {
|
|
fprintf(stderr, "Can't Get Extension proc %d\n", GetLastError());
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// This should really check if the version information matches what
|
|
// we expect.
|
|
//
|
|
|
|
__try {
|
|
if (!GetExtensionVersion(&version_info)) {
|
|
fprintf(stderr, "Fatal: GetExtensionVersion failed\n");
|
|
return -1;
|
|
}
|
|
}
|
|
__except(1) {
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// Fill the ECB with the necessary information
|
|
//
|
|
|
|
if (!FillExtensionControlBlock(&ECB)) {
|
|
fprintf(stderr, "Fill Ext Block Failed\n");
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// Call the DLL
|
|
//
|
|
|
|
__try {
|
|
rc = HttpExtensionProc(&ECB);
|
|
}
|
|
__except(1) {
|
|
return -1;
|
|
}
|
|
|
|
|
|
//
|
|
// We should really free memory (e.g., from GetEnv), but we'll be dead
|
|
// soon enough
|
|
//
|
|
|
|
if (rc == HSE_STATUS_PENDING)
|
|
|
|
//
|
|
// We will exit in ServerSupportFunction
|
|
//
|
|
|
|
Sleep(INFINITE);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// GetServerVariable() is how the DLL calls the main program to figure out
|
|
// the environment variables it needs. This is a required function.
|
|
//
|
|
|
|
BOOL WINAPI
|
|
GetServerVariable(HCONN hConn, LPSTR lpszVariableName,
|
|
LPVOID lpBuffer, LPDWORD lpdwSize)
|
|
{
|
|
|
|
DWORD rc;
|
|
|
|
//
|
|
// We don't really have an HCONN, so we assume a value of 0 (which is
|
|
// passed
|
|
// to the DLL in the ECB by HttpExtensionProc().
|
|
// Hence the check for a non-zero HCONN
|
|
|
|
if (hConn) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
rc = GetEnvironmentVariable(lpszVariableName, lpBuffer, *lpdwSize);
|
|
|
|
if (!rc) {
|
|
|
|
//
|
|
// return of 0 indicates the variable was not found
|
|
//
|
|
SetLastError(ERROR_NO_DATA);
|
|
return FALSE;
|
|
}
|
|
|
|
if (rc > *lpdwSize) {
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// GetEnvironmentVariable does not count the NUL character
|
|
//
|
|
*lpdwSize = rc + 1;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Again, we don't have an HCONN, so we simply wrap ReadClient() to
|
|
// ReadFile on stdin. The semantics of the two functions are the same
|
|
//
|
|
|
|
BOOL WINAPI
|
|
ReadClient(HCONN hConn, LPVOID lpBuffer, LPDWORD lpdwSize)
|
|
{
|
|
|
|
return ReadFile(GetStdHandle(STD_INPUT_HANDLE), lpBuffer, (*lpdwSize),
|
|
lpdwSize, NULL);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// ditto for WriteClient()
|
|
//
|
|
|
|
BOOL WINAPI
|
|
WriteClient(HCONN hConn, LPVOID lpBuffer, LPDWORD lpdwSize,
|
|
DWORD dwReserved)
|
|
{
|
|
|
|
return WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), lpBuffer, *lpdwSize,
|
|
lpdwSize, NULL);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// This is a special callback function used by the DLL for certain extra
|
|
// functionality. Look at the API help for details.
|
|
//
|
|
|
|
BOOL WINAPI
|
|
ServerSupportFunction(HCONN hConn, DWORD dwHSERequest,
|
|
LPVOID lpvBuffer, LPDWORD lpdwSize, LPDWORD lpdwDataType)
|
|
{
|
|
|
|
char *lpszRespBuf;
|
|
char *temp;
|
|
DWORD dwBytes;
|
|
BOOL bRet;
|
|
|
|
switch (dwHSERequest) {
|
|
|
|
case (HSE_REQ_SEND_RESPONSE_HEADER):
|
|
lpszRespBuf = xmalloc(*lpdwSize + 80); // accomodate our header
|
|
|
|
if (!lpszRespBuf)
|
|
return FALSE;
|
|
wsprintf(lpszRespBuf, "%s %s %s %s\r\n%s",
|
|
HTTP_VER,
|
|
lpvBuffer ? lpvBuffer : "200 Ok", // Default response is 200 Ok
|
|
temp = MakeDateStr(), // Create a time string
|
|
SERVER_VERSION,
|
|
|
|
//
|
|
// If this exists, it is a pointer to a data buffer to be sent.
|
|
//
|
|
lpdwDataType ? (char *) lpdwDataType : NULL);
|
|
|
|
xfree(temp);
|
|
|
|
dwBytes = strlen(lpszRespBuf);
|
|
bRet = WriteClient(0, lpszRespBuf, &dwBytes, 0);
|
|
xfree(lpszRespBuf);
|
|
|
|
break;
|
|
|
|
|
|
case (HSE_REQ_DONE_WITH_SESSION):
|
|
|
|
//
|
|
// A real server would do cleanup here
|
|
//
|
|
|
|
ExitProcess(0);
|
|
break;
|
|
|
|
case (HSE_REQ_SEND_URL_REDIRECT_RESP):
|
|
|
|
//
|
|
// This sends a redirect (temporary) to the client.
|
|
// The header construction is similar to RESPONSE_HEADER above.
|
|
//
|
|
|
|
lpszRespBuf = xmalloc(*lpdwSize + 80);
|
|
if (!lpszRespBuf)
|
|
return FALSE;
|
|
wsprintf(lpszRespBuf, "%s %s %s\r\n",
|
|
HTTP_VER,
|
|
"302 Moved Temporarily",
|
|
(lpdwSize > 0) ? lpvBuffer : 0);
|
|
dwBytes = strlen(lpszRespBuf);
|
|
bRet = WriteClient(0, lpszRespBuf, &dwBytes, 0);
|
|
xfree(lpszRespBuf);
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
break;
|
|
}
|
|
return bRet;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Makes a string of the date and time from GetSystemTime().
|
|
// This is in UTC, as required by the HTTP spec.`
|
|
//
|
|
|
|
char *
|
|
MakeDateStr(void)
|
|
{
|
|
SYSTEMTIME systime;
|
|
char *szDate = xmalloc(64);
|
|
|
|
char *DaysofWeek[] =
|
|
{"Sun", "Mon", "Tue", "Wed", "Thurs", "Fri", "Sat"};
|
|
char *Months[] =
|
|
{"NULL", "Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
|
|
|
GetSystemTime(&systime);
|
|
|
|
wsprintf(szDate, "%s, %d %s %d %d:%d.%d",
|
|
DaysofWeek[systime.wDayOfWeek],
|
|
systime.wDay,
|
|
Months[systime.wMonth],
|
|
systime.wYear,
|
|
systime.wHour, systime.wMinute,
|
|
systime.wSecond);
|
|
|
|
return szDate;
|
|
}
|
|
|
|
|
|
//
|
|
// Fill the ECB up
|
|
//
|
|
|
|
BOOL WINAPI
|
|
FillExtensionControlBlock(EXTENSION_CONTROL_BLOCK * ECB)
|
|
{
|
|
|
|
char *temp;
|
|
|
|
ECB->cbSize = sizeof (EXTENSION_CONTROL_BLOCK);
|
|
ECB->dwVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR);
|
|
ECB->ConnID = 0;
|
|
|
|
//
|
|
// Pointers to the functions the DLL will call.
|
|
//
|
|
|
|
ECB->GetServerVariable = GetServerVariable;
|
|
ECB->ReadClient = ReadClient;
|
|
ECB->WriteClient = WriteClient;
|
|
ECB->ServerSupportFunction = ServerSupportFunction;
|
|
|
|
//
|
|
// Fill in the standard CGI environment variables
|
|
//
|
|
|
|
ECB->lpszMethod = GetEnv("REQUEST_METHOD");
|
|
ECB->lpszQueryString = GetEnv("QUERY_STRING");
|
|
ECB->lpszPathInfo = GetEnv("PATH_INFO");
|
|
ECB->lpszPathTranslated = GetEnv("PATH_TRANSLATED");
|
|
ECB->cbTotalBytes = ((temp = GetEnv("CONTENT_LENGTH")) ?
|
|
(atoi(temp)) : 0);
|
|
ECB->cbAvailable = 0;
|
|
ECB->lpbData = "";
|
|
ECB->lpszContentType = GetEnv("CONTENT_TYPE");
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Works like _getenv(), but uses win32 functions instead.
|
|
//
|
|
|
|
char *
|
|
GetEnv(LPSTR lpszEnvVar)
|
|
{
|
|
|
|
char *var, dummy;
|
|
DWORD dwLen;
|
|
|
|
if (!lpszEnvVar)
|
|
return "";
|
|
|
|
dwLen = GetEnvironmentVariable(lpszEnvVar, &dummy, 1);
|
|
|
|
if (dwLen == 0)
|
|
return "";
|
|
|
|
var = xmalloc(dwLen);
|
|
if (!var)
|
|
return "";
|
|
|
|
(void) GetEnvironmentVariable(lpszEnvVar, var, dwLen);
|
|
|
|
return var;
|
|
}
|