What An isapi extension is? By mehdi mousavi
Discusses isapi extensions in detail and shows how to import an extension to validate a given credit card #.
Introduction
Unless you have been a caveman, you have seen a number of web sites that whenever is browsed, the navigation ends up to a DLL file residing in a scripting directory of that dominium Something like the following pseudo URL!:
http://www.mydomain.com/script/example.dll?id=p05874&tx=870250azt6
What Does this DLL SUPPOSE Do And What Does it has to do with today's paper?
These DLLs are created using the Internet Server API, or ISAPI for short. ISAPI is developed to provide a benefit or two, over the shortcomings of Common Gateway Interface, CGI. Although we surprisingly experience new web sites developed exclusively by CGI scripts nowadays, however ISAPI DLLS HAVE GOT SOMETHING TO OFFER THAT CGI COULD NEVER BRING US THIS WAY OR THAT WAY.
I am going to start off this paper by describing the underlying details that any ISAPI programmer has to know, to be able to develop a better ISAPI extension. From then on, I will go through a development of a useful ISAPI extension step by step. THE EXTENSITION IS Supposed to Validate A Given Credit Card. Yeah! This is also my answer to Those People Who asked me the algorithm involved validating a credit card over and over agiding a credit card over and over agid. Gotta GO!
What is isapi?
Internet Server Application Programming Interface (ISAPI), is an API developed to provide the application developers with a powerful way to extend the functionality of Internet Information Server (IIS). Although ISAPI extensions by no means are limited to IIS, they are extensively used in CONJUNCTION with MS-IIS.
CGI VS. ISAPI
Developing a CGI program involves creating an EXE with C, C , and / or Perl programming languages. This EXE file will be executed and terminated for every request received, causing an excessive memory usage, whenever users hit the same page over and over again! This excessive memory usage that could bring the server completely down, has been solved under ISAPI extensions. An ISAPI extension is a regular DLL file that exposes 3 special functions that is called by the calling process (ie, IIS) and therefore, will be loaded to memory once, no matter how many clients are going to use it at the same time. (It would be a good idea if you could take a look at a reference, to see how memory management is done under Windows 2000. The Visual C 6.0 Bible, Chapter 18, The Memory Management, Describes It Well!)
Isapi Fundamentals
Since the ISAPI extension and the calling process (IIS) live at the same address space, they could contact each other, directly. This means a great potential to bring the whole IIS down, and in some cases, the entire web server! Take a Look at The Following Figure:
You see whatever problem your extension encounters, it could affect the entire web server process, if it's not handled properly. As illustrated above, communicating between the extension and IIS is done via a pointer to a structure of type ECB, or Extension Control Block that Is Declared As Follows:
Typedef struct _extension_control_block
{
DWORD CBSIZE; / / SIZE OF THIS STRUCT.
DWORD DWVERSION; // Version Info of this Spec
HCONN Connid; // context number not to be modified!
DWORD DWHTTPSTATUSCODE; // http status code
Char lpszlogdata [hse_log_buffer_len]; // Null Terminated log infolpstr lpszmethod; // request_method
LPSTR LPSZQUERYSTRING; // Query_String
LPSTR LPSZPATHINFO; // Path_info
LPSTR LPSZPATHTRANSLATED; // PATH_TRANSLATED
DWORD CBTOTALBYTES; // Total Bytes IND from Clom Clom Clom Clom Clom Clom
DWORD CBAVAILABLE; / / AVAILABLE NUMBER OF BYTES
LPBYTE LPBDATA; / / POINTER TO CBAVAILABLE BYTES
LPSTR LPSZCONTENTTYPE; // Content Type of Client Data
BOOL (WinApi * GetServerVariable) (HCONN HCONN,
LPSTR LPSZVARIABLENAME,
LPVOID LPVBUFFER,
LPDWORD LPDWSIZE);
BOOL (WinApi * WriteClient) (HCONN Connid,
LPVOID BUFFER,
LPDWORD LPDWBYTES,
DWORD DWRESERVED);
Bool (WinApi * ReadClient) (HCONN Connid,
LPVOID LPVBUFFER,
LPDWORD LPDWSIZE);
Bool (WinApi * ServersupportFunction) (HCONN HCONN,
DWORD DWHSEREQUEST,
LPVOID LPVBUFFER,
LPDWORD LPDWSIZE,
LPDWORD LPDWDATYPE);
} EXTENSION_CONTROL_BLOCK, * LPEXTENSION_CONTROL_BLOCK;
Whatever information either the calling process or the extension wants to pass to the other, is done through this control block. We will shortly have a look at this ECB structure. For now, let's see how IIS works in conjunction with your extension, to serve The Visitor of Your Web Site.
WHENEVER An Extension IS Accessed (EG, http://www.mydomain.com/script/example.dll?id=p05874 & tx = 870250azt6), IIS Checks to See WHETHER THE EXAMPLE.DLL IS Loaded Into Memory. If IT IS not, then it initiates the loading process. Once the DLL is loaded into memory, a worker thread starts running to manage our extension, and thereafter the entry point (DLLMain function) is called. When the DLLMain finishes, the server makes a call to GetExtensionVersion Function to Perform Two Tasks: Exchange Version Information
Get A Short Text Description of the Extension
The server then calls the HttpExtensionProc function passing a copy of the ECB's pointer to start the actual ISAPI extension. This is the function that makes writing data back to the client, possible! We will examine this, shortly.
The third and the last entry point in an ISAPI extension DLL is the TerminateExtension function that is called whenever the extension is going to be unloaded from the memory. All the cleanup code can be done in this function.
In brief, an isapi extension is a regular dll this exposes 3 functions to interact with the server:
GetExtensionVersion
HTTPEXTENSIONPROC
TerminateExtension (Optional)
Having this information in hand, let's start with the dllmain, The entry point of any dll!
Dllmain, The entry point
As indicated by Microsoft, "the DllMain function is an optional entry point into a dynamic-link library (DLL). If the function is used, it is called by the system when processes and threads are initialized and terminated, or upon calls to the Loadlibrary and freetable functions, That Is Prototyped As Follows:
Bool Apientry Dllmain (Handle Hmodule, DWORD DWCALLREASON,
LPVOID LPRESERVED);
If you provide your extension with this function, it will be called upon the initialization and the termination process of your extension The state is indicated by the dwCallReason parameter that could be one the following predefined values:. DLL_PROCESS_ATTACHED
DLL_THREAD_ATTACH
DLL_THREAD_DETACH
DLL_PROCESS_DETACH
Describing Each of these Parameters in detail is beyond The Scope of this Paper, SO i Simply Refer You To Microsoft's Developer Network To Read More About this function.
Anyhow, we could save the hModule parameter for later use in our extension (if this suites us) and simply return TRUE from this function. We usually do not have anything to do in this function, while developing an extension!
GetExtensionVersion, The Actual Entry Point
THIS function is actially the first entry point That Is Called by Iis to determine the information about the extension. To understand this better, let's have a look at it's prototype:
Bool WinApi getExtensionVersion (HSE_VERSION_INFO * PVER);
Upon the Activation of this function, we are supposed to fill out the extension information for the pver parameter passed to the function. This Pointer is of type hse_version_info what is declared as Follows:
Typedef struct _hse_version_info
{
DWORD DWEXTensionVersion;
Char lpszextensionDesc [hse_max_ext_dll_name_len];
} HSE_VERSION_INFO, * LPHSE_VERSION_INFO;
where dwExtensionVersion is the extension version and lpszExtensionDescription is the description of the extension. If we return TRUE from this function, we notify IIS that our extension is ready to be used. Otherwise, IIS will not use the extension.
HTTPEXTENSIONPROC, THE Main Entry Point
The amazing part of any ISAPI extension starts when the extension procedure (HttpExtensionProc) is called. As far as you could remember, this is the procedure that makes writing data back to the client possible! To see how this happens, lets start by having a Look at the prototype of httpextensionproc: DWORD WINAPI HTTPEXTENSIONPROC (Extension_Control_block * PECB);
where pECB is a pointer to an extension control block, that makes the intercommunication between the server and the extension possible. You decide what the web page should contain and how to present it to the user in this entry point. But how?
Do you recall the MEMBERS of the ECB's STRUCTURE? ECB Contains a Method, Prototyped As Follows:
Bool WriteClient (HCONN Connid, LPVOID BUFFER, LPDWORD LPDWBYTES,
DWORD DWSYNC);
Using this member function, you can send the data present in the given Buffer to the client that is identified by its ConnID, the one that made the request. For example, to send A BIG RED ROSE to a client, you could simply make the FOLLOWING CALLS:
Char szbigredros [] =
"A big red rose";
DWORD DWSIZE = Strlen (szbigredros);
PECB-> WriteClient (PECB-> Connid, SzbigredRose, dwsize, 0);
NEAT, Huh? I think you already has got the minimum underlying knowledge to develop your first isapi extension. So, let's start ...
Project Requirements
A little bit patience!
MS-VC 6.0 Compiler
MS-Windows 2000 Advanced Server with MS-IIS Installed
A Web Browser
Goals
We are going to develop a non-MFC ISAPI extension, using plain Win32 API calls that is supposed to validate a given Master Card. We name the extension validate.dll and it simply writes the client a string telling the client, whether the given credit Card Number Is Valid. of Course, We Should Examine What Happensiff IF A User Wants To Browse The Page THROUGH THE FOLLOWING URL: http: //mydomain/script/validate.dll? Some% 20String
oral
http://mydomain/script/validate.dll?
Or Some Sort of Such Invalid Urls, of Course, from The Purpose Our Extension's View (The Urls Are Valid for your Browser, Though!).
In Such Circumstances, We Simply Echo The Following Text To The Client: What You have entered is an invalid master card #.
AND That's all what for extension does!
Why Not MFC?
Although MFC amazingly simplifies the parsing process of query strings, it will dramatically increase the file size of our extension! On the other hand, if you know the subtle nuance of a non-MFC extension, you are a step further towards creating a better MFC Extension. so, I decided to Avoid Using Mfc for Our First ISAPI Extension.
Luhn Algorithm
Now, the question is how can we check whether a given credit card number is valid. The algorithm involved in this checking is called Luhn algorithm (AKA Sum10 algorithm, AFAIK). Consider a number, say, 5168254236021548. To understand if this is a Valid CC #, You Should Follow 4 Easy Guidelines:
Starting from the Leftmost Digit, We Simply Multiply Alternating Numbers by Two. I., We Multiply Shown Bold Numbers by TWO:
2. 5168254236021548
3. 5 * 2 = 10
4. 6 * 2 = 12
5. 2 * 2 = 4
6. 4 * 2 = 8
7. 3 * 2 = 6
8. 0 * 2 = 09. 1 * 2 = 2
4 * 2 = 8
Add The Separate Digits of All The Products, As Follows:
1 0 1 2 4 8 6 0 2 8 = 32
Add the unaffected Digits to the product, i.e.,
1 8 5 2 6 2 5 8 = 37
Add The Result of the Step 2 and 3 and Divide IT by 10
13. 32 37 = 69
69% 10 = 9
IF the answer is 0, this is a valid cc #, OtherWise, IT is not!
SO 5168254236021548 IS An Invalid CC # (Since 69% 10 IS 9 NOT 0).
Further checking
Having completed the luhn checking, we Could Go A Step Forward by Checking The Type of The cc, According to the Following Table:
Credit Card
Prefix
Length (DIGITS)
Master Card
51-55
16
Visa
4
13, 16
American Express
34, 37
15
However, for our example, we just assume that the given cc # is a Master card, so we check the card against the conditions for Master Cards, ie, we could simply check if the given number has got 16 digits. If it's not, WE SIMPLY Echo That The Give Number IS Not a Valid Master Card Number. You Could Extend The FunctionAlity, if you want.
How to import luhn checking IN C?
Here, I'VE Tried to Program A Procedure So That IT Simply Returns 0 if The Given String IS A Valid Master Card #. OtherWise, IT Will Return A Non-Zero Number Indicating The Error Code:
#define err_wrong_number_of_digits 1
#define err_not_a_mastercard 2
#define err_INValid_cc 3
#define err_INValid_input 4
BYTE CHECKCC (Const Char * Psznumber)
{
IF (Strlen (psznumber)! = 16)
Return Err_Wrong_Number_Of_Digits;
For (int i = 0; i <16; i )
IF (! isdigit (psznumber [i]))
Return err_invalid_input; if (psznumber [0]! = '5' || psznumber [1] <'1' || psznumber [1]> '5')
RETURN ERR_NOT_A_MASTERCARD;
Int nsum;
For (i = 0, nsum = 0; i <16; i = 2)
{
INT NDIGIT = (PSZNumber [I] - 48) * 2;
NSUM = (NDIGIT <10? NDIGIT: NDIGIT / 10 NDIGIT% 10)
(psznumber [i 1] - 48);
}
IF (nsum% 10)
Return Err_INVALID_CC;
Return 0;
}
And Therefore, Checking A Master Card Is Simply Calling The FOLLOWING FUNCTION:
BYTE BYRET = CHECKCC ("1269875230210254);
IF (! Byret)
{
// this is a valid master card #
}
Else
{
// an invalid master card #, byret shows the error code!
}
Starting Our Extension
With this information in hand, it's now time to develop our ISAPI extension. Launch your VC compiler. From the File menu, select the New command. Having the Projects tab selected, left-click on Win32 Dynamic-Link Library. Within the Project Name space, type validate and click ok to continue. Within the Win32 Dynamic-Link Library box, select the second option, A simple DLL project and press the Finish button. At this point, we've got a DLL project ready to be implemented.
Since We are not intended Either in ul_reason_for_call or hModule Parameters, WE SKIP THEMPLY AND WE JUST RETURN TRUE IN THIS Entry Point. So we have got the dllmain function implemented as flollows:
Bool apientry dllmain (Handle Hmodule,
DWORD UL_REASON_FOR_CALL, LPVOID LPRESERVED)
{
Return True;
}
. Now, let's gear from DllMain to the first actual entry point, GetExtensionVersion As far as you could remember, we have to implement this function in order to serve two things described before So let's add it to our project:. BOOL WINAPI GetExtensionVersion (HSE_VERSION_INFO * PVER)
{
PVER-> DWEXTensionVersion = HSE_VERSION;
STRNCPY (PVER-> LPSZEXTENSIONDESC,
"Validate Isapi Extension", HSE_MAX_EXT_DLL_NAME_LEN;
Return True;
}
WHERE HSE_VERSION IS Defined in The Httpext.h Header File As Follows:
#define hse_version makelong (hse_version_minor, hse_version_major)
SO pese! We return true to indicate That! We return True To Indicate That IIS Is Permitted to Run Our Extension!
What's next, then It's now time to find a way to get the query string back from IIS But how You probably remember that IIS can communicate with our DLL through the ECB's pointer, passed as the only parameter to the HttpExtensionProc function?.?:
DWORD WINAPI HTTPEXTENSIONPROC (Extension_Control_block * PECB);
where pECB contains a data member, say, lpszQueryString, that points to the query string. In other words, if the user tries to our validate.dll via access the http://mydomain.com/validate.dll?12345 URL, pECB -> LPSZQUERYSTRING IS Equal to 12345, The String Followed by The? mark.
Another thing we should overcome before going on, is to learn how we could use pECB to echo a string (including HTML, JavaScript and etc.) to the client. Via This is done the WriteClient method of the ECB block, which is prototyped as Follows:
Bool (WinApi * WriteClient) (HCONN Connid, LPVOID BUFFER,
LPDWORD LPDWBYTES, DWORD DWRESERVED);
where ConnID is the connection identifier of the client to which the response data should be sent, Buffer points to the data to be sent, lpdwBytes is the length of the mentioned Buffer, and dwReserved as it sounds, is reserved.With this information in hand , Let's Implement Our HTTPEXTensionProc:
DWORD WINAPI HTTPEXTENSIONPROC (Extension_Control_Block * PECB)
{
STARTCONText (PECB);
Byte Byret = Checkcc (PECB-> LPSZQUERYSTRING);
IF (! Byret)
{
// this is a Valid Master Card, Echo a Suitable String to The Client
WriteContext (PECB,
"
Color = '# 008000'> CONGRATULATIONS! ");
WriteContext (PECB,
"
");
WriteContext (PECB,
"% s is a valid master card # / r / n"
,
PECB-> LPSZQUERYSTRING);
}
Else
{
// this is an invalid master card, echo a property string to the client!
WriteContext (PECB,
"
Color = '# 800000'> Sorry! "
);
WriteContext (PECB,
"
What you have entered is an ");
WriteContext (PECB,
"Invalid Master Card # / R / N"
);
}
ENDCONTEXT (PECB);
Return HSE_STATUS_SUCCESS;
}
And what about startContext, EndContext, and WRITECONTEXT FUNCONTEXT FUNCONTEXT IS A FUNCTION TO SIMPLIFY The Process of Echoing a String To The Client, IMPLEMENTED AS FOLLOWS:
Void WriteContext (Extension_Control_Block * PECB, Char * pszformat, ...)
{
Char szbuffer [1024];
VA_LIST ARG_PTR;
VA_Start (arg_ptr, pszformat);
vsprintf (szbuffer, pszformat, arg_ptr);
VA_END (Arg_PTR);
DWORD DWSIZE = Strlen (SZBuffer);
PECB-> WriteClient (PECB-> Connid, Szbuffer, & DWSize, 0);
}
THIS WAY, WE DON 'THED TO PASS The Length of The String or Other Non-Useful Information over and over aga , for example: WriteContext (PECB, "5 6 =% D", 5 6);
That Echoes The 5 6 = 11 string to the client.
On The Other Hand Startcontext Is Provided to Echo Necessary Heading Or Startup HTML Code To The Client. It's Counterpart, EndContext, Also Echoes The Footer To The Client:
Void StartContext (extension_control_block * pecb)
{
WriteContext (PECB, "/ R / N / R / N");
}
Void endContext (extension_control_block * pecb)
{
WriteContext (PECB, "/ R / N");
}
NOW Set The Active Project Configuration To Release Mode and Compile The Program. It is now time to see how to install and use our extension.
Assuming you are running Windows 2000, select the Internet Services Manager from the Administrative Tools of the Programs menu to bring the Internet Information Services snap-in to life. From the left panel, navigate your way through the available leaves of the web sites to the proper directory, where you plan to use the already created extension This is usually done in scripts directory of that dominium For this purpose, I've created a scripts directory under the articles folder..:
Right Click on The Scripts Directory and SELECT Properties To Open Up The Following Box:
Make the necessary changes to the box, so that it looks like the above picture and then apply the changes. Now, copy the validate.dll file from the release directory to the scripts directory you already configured. Launch your favorite web browser, and try TO Access The DLL, Passing a Number As ITS Query String: http://myth/Articles/scripts/validate.dll? 1234567890125436
For your Dominium, please pay..............
Doing So, You'll See A DAMN FAMILIAR Web Page:
Oooooops! What went wrong, causing this damn page to appear ?! If you think a little bit, you would probably remember that we have to expose those 3 (in our case 2) entry points we have used in our program, GetExtensionVersion and HttpExtensionProc . So let's go back to the project to fix.
While In VC Environment, Create a Blank file named validate.def and import it this Way:
Validate.def: Declares The Module Parameters for the DLL.
Library "validate"
Description 'Validate Isapi Extension'
Exports
Explicit Exports Can Go Here
HTTPEXTENSIONPROC @ 1
GetExtensionVersion @ 2
Then, from the project menu, select add to project, and then select the files item. Add the already created def file to the project. Rebuild the DLL. Let's copy the file from the release directory to the scripts directory again and browse the page Than Time, You Will Get The Following Page:
Now try to access the page using a valid master card number, and you'll get the congratulations page. Then try to delete the validate.dll located in the scripts directory! What happens? Yep! You'll get an error indicating that the DLL IS Already in Use and cannot Be deleted. So WE Have to do, IF we want to update, or ...?? Number! Not at all! All you have to Do is to stop IIS. this Is Done Through The Internet Information Services Snap-in! Launch IT, And Right Click on The Server's Name. from The Context Menu, SELECT Restart Iis. in The "What Do You Want IIS To Do?" Combo Box, STOP Internet Services ON Server and Press The Ok Button. Wait for Seconds, And It's Done! Now You Can Delete and / or Modify Your Extension! Neat, Huh?
THE Final Word
This is what ISAPI extensions are supposed to do They extend the functionality of IIS Through this article, I repeated the word extension over and over again It's now time to say that ISAPI programs are divided into two categories:... ISAPI extensions and ISAPI filters .
ISAPI filters, unlike the ISAPI extensions would be called for any hit made to the web server! In other words, they magnificently slow down the process, since they are called over and over again. However, they could be absolutely useful when creating a logging Service, or doing some specific jobs. Since describing isapi filters in detail deserves another paper, let me Leave it here to you to understand the subtle nuances of how they work.
Anyhow, this was the simplest ISAPI extension that we developed today, just to show you what the heck an extension is and how it works. It was a synchronous, single-threaded DLL that is the most easiest DLL to develop! Real-world applications are not this easy to implement though, since you have to face the multi-threaded issues, as well as connection pools and other advanced topics. It's all up to you to learn how to play them magnificently, though, and this paper is just a Starting Point! That's All, Folks. Aloha! How to Contact the Author
I, As Ever Before, Would Love To Hear Your Comments, Questions and / OR Suggestions. So, please do not hesitate to send the me, mehdi_mousavi@hotmail.com.
References
Professional Visual C Isapi Programming, by Michael Tracy
Microsoft Developer NetWork, MSDN