Skip to content

Instantly share code, notes, and snippets.

@daald
Created February 15, 2014 15:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save daald/5f37de8352e1c8ca62db to your computer and use it in GitHub Desktop.
Save daald/5f37de8352e1c8ca62db to your computer and use it in GitHub Desktop.
FastCGI-ISAPI bridge for running Windows ISAPI Server Extension DLLs under Wine/Linux - WORK IN PROGRESS
/**
Copyright (c) 2013-2014 "Daniel Alder"
FastCGI-ISAPI bridge for running Windows ISAPI Server Extension DLLs under Wine/Linux
[http://github.com/daald]
This file is part of fastcgi-to-isapi.
fastcgi-to-isapi is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**/
// see http://www.fastcgi.com/devkit/doc/fastcgi-whitepaper/fastcgi.htm
#include <stdlib.h>
#include <fcgi_stdio.h>
//#include <httpext.h>
#include "../includes_win_other/httpext.h"
const DWORD MAX_PREFETCH_ISAPI_INPUT = 0xC000; // 48K; http://www.hummel1.de/prog/isapi/isapiinput.htm
void verbose(const char *fmt, ...);
#define CHECK_CONN_ID(hConn) \
if (hConn != curCon->connId) { \
verbose("Wrong ConnID called!"); \
SetLastError(ERROR_INVALID_PARAMETER); \
return FALSE; \
}
char* getenvdef(const char* name, char* def) {
char* result = getenv(name);
if (result != NULL)
return result;
else
return def;
}
typedef struct _WRAPPER_CONNECTION_DATA {
HCONN connId;
EXTENSION_CONTROL_BLOCK ecb;
} WRAPPER_CONNECTION_DATA, *LPWRAPPER_CONNECTION_DATA;
LPWRAPPER_CONNECTION_DATA curCon;
BOOL (PASCAL GetServerVariableImpl)(HCONN hConn, LPSTR lpszVariableName, LPVOID lpvBuffer, LPDWORD lpdwSize) {
verbose(" GetServerVariableImpl called %s.", lpszVariableName);
CHECK_CONN_ID(hConn);
// see http://msdn.microsoft.com/en-us/library/aa927099.aspx
// The lpszVariableName parameter can be used to retrieve a specific request (client) header by using the HTTP_headername value. For example, supplying the value HTTP_ACCEPT returns the Accept header, and HTTP_VERSION returns the Version header.
char* result = getenv(lpszVariableName);
verbose(" %s: %s", lpszVariableName, result);
if (result == NULL) {
SetLastError(ERROR_NO_DATA);
return FALSE;
}
size_t len = strlen(result);
strncpy(lpvBuffer, result, *lpdwSize-1);
((LPSTR)lpvBuffer)[*lpdwSize-1] = 0;
if (len > *lpdwSize-1) {
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
//pECB->GetServerVariable( pECB->ConnID, "User-Agent", varBuffer, &bufLen);
//TODO support only the keys given in the MS list / ERROR_INVALID_INDEX
*lpdwSize = len + 1;
return TRUE;
}
BOOL (PASCAL WriteClientImpl)(HCONN hConn, LPVOID lpvBuffer, LPDWORD lpdwSize, DWORD dwReserved) {
verbose(" WriteClientImpl called.");
CHECK_CONN_ID(hConn);
size_t res = fwrite(lpvBuffer, 1, *lpdwSize, stdout);
*lpdwSize = res;
return (res > 0)?TRUE:FALSE;
}
BOOL (PASCAL ReadClientImpl)(HCONN hConn, LPVOID lpvBuffer, LPDWORD lpdwSize) {
verbose(" ReadClientImpl called.");
CHECK_CONN_ID(hConn);
size_t res = fread(lpvBuffer, 1, *lpdwSize, stdin);
*lpdwSize = res;
return (res > 0)?TRUE:FALSE;
}
BOOL (PASCAL ServerSupportFunctionImpl)(HCONN hConn, DWORD dwHSERequest, LPVOID lpvBuffer, LPDWORD lpdwSize, LPDWORD lpdwDataType) {
CHECK_CONN_ID(hConn);
// see http://msdn.microsoft.com/en-us/library/aa926202.aspx
switch (dwHSERequest) {
case HSE_REQ_SEND_URL_REDIRECT_RESP:
verbose(" ServerSupportFunction %s not implemented", "HSE_REQ_SEND_URL_REDIRECT_RESP");
return FALSE;//TODO implement
case HSE_REQ_SEND_URL:
verbose(" ServerSupportFunction %s not implemented", "HSE_REQ_SEND_URL");
return FALSE;//TODO implement
case HSE_REQ_SEND_RESPONSE_HEADER:
verbose(" ServerSupportFunction(%s) called", "HSE_REQ_SEND_RESPONSE_HEADER");
if (dwHSERequest != 0)
printf("Status: %s\r\n", (char*)lpvBuffer);
if (lpdwDataType != NULL) {
fwrite((void*)lpdwDataType, 1, *lpdwSize, stdout);
} else
printf("\r\n"); // skip to output of content
return TRUE;
case HSE_REQ_DONE_WITH_SESSION:
verbose(" ServerSupportFunction %s not implemented", "HSE_REQ_DONE_WITH_SESSION");
return FALSE;//TODO implement
case HSE_REQ_SEND_RESPONSE_HEADER_EX:
verbose(" ServerSupportFunction(%s) called", "HSE_REQ_SEND_RESPONSE_HEADER_EX");
LPHSE_SEND_HEADER_EX_INFO hi = (LPHSE_SEND_HEADER_EX_INFO)lpvBuffer;
if (hi->pszStatus != NULL)
printf("Status: %s\r\n", hi->pszStatus);
if (hi->pszHeader != NULL) {
// assert strlen(pszHeader) == cchHeader
// assert cchHeader >= 2
// assert pszHeader[cchHeader-2] == '\r'
// assert pszHeader[cchHeader-1] == '\n'
fwrite(hi->pszHeader, 1, hi->cchHeader, stdout);
} else
printf("\r\n"); // skip to output of content
if (hi->fKeepConn == FALSE)
verbose(" HSE_SEND_HEADER_EX_INFO.fKeepConn==FALSE not implemented");
return TRUE;
case HSE_REQ_END_RESERVED:
verbose(" ServerSupportFunction %s not implemented", "HSE_REQ_END_RESERVED");
return FALSE;//TODO implement
default:
verbose(" ServerSupportFunction with unknown dequest %lu called", dwHSERequest);
return FALSE;//TODO implement
}
}
int main( int argc, const char* argv[] )
{
int count = 0;
#ifndef COMPILED_IN_ISAPI
verbose("Initializing dll");
HMODULE isapidll;
if (argc < 2) {
verbose("Need argument (dll name)");
exit(78);
}
isapidll = LoadLibrary(argv[1]);
if (!isapidll) {
verbose("DLL not found");
exit(127);
}
PFN_HTTPEXTENSIONPROC isapidllExtProc;
isapidllExtProc = GetProcAddress(isapidll, "HttpExtensionProc");
#endif //!COMPILED_IN_ISAPI
verbose("Starting loop");
while(FCGI_Accept() >= 0) {
verbose("FCGI run %d", count++);
curCon = malloc(sizeof(WRAPPER_CONNECTION_DATA));
curCon->connId = (HCONN)1; // there is only one connection without threading
curCon->ecb.cbSize = sizeof(EXTENSION_CONTROL_BLOCK);
curCon->ecb.dwVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR); //winapi windef.h
curCon->ecb.ConnID = curCon->connId;
curCon->ecb.dwHttpStatusCode = 200;
strncpy(curCon->ecb.lpszLogData, "FastCGI to ISAPI wrapper", HSE_LOG_BUFFER_LEN-1);
curCon->ecb.lpszMethod = getenvdef("REQUEST_METHOD", "GET");
verbose("curCon->ecb.lpszMethod '%s'", curCon->ecb.lpszMethod);
curCon->ecb.lpszQueryString = getenv("QUERY_STRING");
verbose("curCon->ecb.lpszQueryString '%s'", curCon->ecb.lpszQueryString);
curCon->ecb.lpszPathInfo = getenv("PATH_INFO");
verbose("curCon->ecb.lpszPathInfo '%s'", curCon->ecb.lpszPathInfo);
curCon->ecb.lpszPathTranslated = getenv("PATH_TRANSLATED");
verbose("curCon->ecb.lpszPathTranslated '%s'", curCon->ecb.lpszPathTranslated);
curCon->ecb.lpszContentType = getenv("CONTENT_TYPE");
verbose("curCon->ecb.lpszContentType '%s'", curCon->ecb.lpszContentType);
curCon->ecb.GetServerVariable = &GetServerVariableImpl;
curCon->ecb.WriteClient = &WriteClientImpl;
curCon->ecb.ReadClient = &ReadClientImpl;
curCon->ecb.ServerSupportFunction = &ServerSupportFunctionImpl;
char *contentLength = getenv("CONTENT_LENGTH");
int len = 0;
if (contentLength != NULL)
len = strtoul(contentLength, NULL, 10);
curCon->ecb.cbTotalBytes = len;
curCon->ecb.cbAvailable = len;
if (curCon->ecb.cbAvailable > MAX_PREFETCH_ISAPI_INPUT)
curCon->ecb.cbAvailable = MAX_PREFETCH_ISAPI_INPUT;
char* fetchedData = NULL;
if (curCon->ecb.cbAvailable > 0) {
// pre-read beginning of data into lpbData
fetchedData = malloc(curCon->ecb.cbAvailable + 1);//don't know if null-terminating is mandatory, but we do
fread(fetchedData, 1, curCon->ecb.cbAvailable, stdin);
//TODO necessary? rewind(stdin); // calls to curCon->ecb.ReadClient() should? read from beginning of data
}
curCon->ecb.lpbData = fetchedData;
verbose(" Calling ISAPI");
DWORD extRet;
#ifdef COMPILED_IN_ISAPI
extRet = HttpExtensionProc(&(curCon->ecb));
#else //!COMPILED_IN_ISAPI
extRet = isapidllExtProc(&(curCon->ecb));
#endif //COMPILED_IN_ISAPI
switch (extRet) {
case HSE_STATUS_SUCCESS:
/* The extension has finished processing and the server should disconnect the client and free up allocated resources. */
verbose(" ISAPI finished with %s", "HSE_STATUS_SUCCESS");
break;
case HSE_STATUS_SUCCESS_AND_KEEP_CONN:
/* The extension has finished processing and the server should wait for the next HTTP request if the client supports Keep-Alive connections. The extension can return this only if it was able to send the correct Content-Length header to the client. */
verbose(" ISAPI finished with %s", "HSE_STATUS_SUCCESS_AND_KEEP_CONN");
break;
case HSE_STATUS_PENDING:
/* The extension has queued the request for processing and will notify the server when it has finished. See HSE_REQ_DONE_WITH_SESSION under ServerSupportFunction.
If the ISAPI extension is using HSE_STATUS_PENDING, then responses can be sent from work done in another thread after returning HSE_STATUS_PENDING. */
verbose(" ISAPI finished with %s", "HSE_STATUS_PENDING");
break;
case HSE_STATUS_ERROR:
/* The extension has encountered an error while processing the request, so the server can disconnect the client and free up allocated resources. An HTTP status code of 500 is written to the IIS log for the request. */
verbose(" ISAPI finished with %s", "HSE_STATUS_ERROR");
verbose(" returning http error 500");
printf("Status: 500 Internal Server Error\r\n"
"\r\n"
"<h1>500 Internal Server Error</h1>\r\n"
"The server encountered an unexpected condition which prevented it from fulfilling the request.\r\n");
break;
default:
verbose(" ISAPI finished with an unknown exit code %d", extRet);
verbose(" returning http error 500");
printf("Status: 500 Internal Server Error\r\n"
"\r\n"
"<h1>500 Internal Server Error</h1>\r\n"
"The server encountered an unexpected condition which prevented it from fulfilling the request.\r\n");
}
if (fetchedData != NULL)
free(fetchedData);
verbose("Request done");
}
verbose("Program end");
exit(0);
}
#undef fprintf
#undef stderr
#undef vfprintf
void verbose(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
fprintf(stderr, ">> ");
vfprintf(stderr, fmt, args);
fprintf(stderr, "\n");
va_end(args);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment