/*++ 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 #include #include #include // // 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; }