| Tenouk C & C++ | MFC Home | IIS & ISAPI Programming 3 | IIS & ISAPI Filter Programming 5 | Download | Site Index |


 

 

 

 

 

Programming the Microsoft Internet Information Server

ISAPI Extension Part 4

 

 

 

 

 

 

Program examples compiled using Visual C++ 6.0 compiler on Windows XP Pro machine with Service Pack 2 and some Figure screen snapshots have been taken on Windows 2000 server. The Internet Information Services version is IIS 4.x/5.x/6.x on Windows 2000 Server SP 4 and Windows XP Pro SP 2. The Internet Explorer is 6.x. Topics and sub topics for this tutorial are listed below. A complete information about IIS installation, configuration and testing a Web site is dumped HERE and how to setup FTP server also included HERE. Both Web and FTP servers were done on Windows 2000 Server SP4. Don’t forget to read Tenouk’s small disclaimer.

 

  1. The Story - The First Step: Getting the Order

  2. Optional Parameters

  3. The Second Step: Processing the Confirmation

  4. Building and Testing myex34a.dll

  5. Debugging the MYEX34A DLL

  6. ISAPI Database Access

  7. Using HTTP Cookies to Link Transactions

  8. How Cookies Work

  9. How an ISAPI Server Extension Processes Cookies

  10. Problems with Cookies

  11. WWW Authentication

  12. Basic Authentication

  13. Windows NT Challenge/Response Authentication

  14. The Secure Sockets Layer

 

 

The Story - The First Step: Getting the Order

 

Junior sales trainees are constantly admonished to "get the order." That's certainly necessary in any form of commerce, including the Internet. When the hungry customer hyperlinks to your site (by clicking on a picture of a pizza, of course), he or she simply downloads an HTML file that looks like this:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

<HTML>

 

<HEAD>

<TITLE>Inside Visual C++ HTML Form 1</TITLE>

</HEAD>

 

<BODY>

 

<H1></H1>

<CENTER><B><FONT color="#0000FF" face="Verdana" size="4">Welcome to CyberPizza</FONT></B></CENTER>

<H1></H1>

<P><FONT color="#FF0000" face="Tahoma">Enter your order.</FONT> </P>

<FORM action="scripts/myex34a.dll?ProcessPizzaForm" method="POST">

  <P>Your Name: <INPUT type="text" name="name" value> </P>

  <P>Your Address: <INPUT type="text" name="address" value> </P>

  <P>Number of Pies: <INPUT type="text" name="quantity" value="1"> </P>

  <P>Pizza Size: </P>

  <MENU>

    <LI><INPUT type="radio" name="size" value="8">8-inch </LI>

    <LI><INPUT type="radio" name="size" value="10">10-inch </LI>

    <LI><INPUT type="radio" name="size" value="12" checked>12-inch </LI>

    <LI><INPUT type="radio" name="size" value="14">14-inch </LI>

  </MENU>

  <P>Toppings: </P>

  <P><INPUT type="checkbox" name="top1" value="Pepperoni" checked> Pepperoni

  <INPUT type="checkbox" name="top2" value="Onions"> Onions

  <INPUT type="checkbox" name="top3" value="Mushrooms"> Mushrooms

  <INPUT type="checkbox" name="top4" value="Sausage"> Sausage </P>

  <P><EM>(you can select multiple toppings)</EM> </P>

  <P><INPUT type="submit" value="Submit Order Now"><INPUT type="reset"> </P>

</FORM>

 

</BODY>

</HTML>

 

Listing 13.

 

Figure 46 shows how the order form appears in the browser.

 

ISAPI, IIS, Winsock, C++ and MFC - Figure 55:  The CyberPizza order form.

 

Figure 46:  The CyberPizza order form.

 

 

So far, no ISAPI DLL is involved. When the customer clicks the Submit Order Now button, the action begins. Here's an example what the server sees:

POST scripts/myex34a.dll?ProcessPizzaForm HTTP/1.0

(request headers)

(blank line)

name=Walter+Sullivan&address=Redmond%2C+WA&quantity=2&size=12&top1=Pepperoni&top3=Mushrooms

Looks like Walter Sullivan has ordered two 12-inch pepperoni and mushroom pizzas. The browser inserts a + sign in place of a space, the %2C is a comma, and the & is the parameter separator. Now let's look at the parse map entries in myex34a.cpp:

ON_PARSE_COMMAND(ProcessPizzaForm, CMyex34aExtension,

    ITS_PSTR ITS_PSTR ITS_I4 ITS_PSTR ITS_PSTR ITS_PSTR ITS_PSTR ITS_PSTR)

ON_PARSE_COMMAND_PARAMS("name address quantity size top1=~ top2=~ top3=~ top4=~")

Optional Parameters

 

When you write your parse map statements, you must understand the browser's rules for sending parameter values from a form. In the MYEX34A pizza form, the browser always sends parameters for text fields, even if the user enters no data. If the user left the Name field blank, for example, the browser would send name=&. For check box fields, however, it's a different story. The browser sends the check box parameter value only if the user checks the box. The parameters associated with check boxes are thus defined as optional parameters. If your parse macro for parameters looked like this:

ON_PARSE_COMMAND_PARAMS("name address quantity size top1 top2 top3 top4")

there would be trouble if the customer didn't check all the toppings. The HTTP request would simply fail, and the customer would have to search for another pizza site. The =~ symbols in the myex34a.cpp code designate the last four parameters as optional, with default values ~. If the Toppings option is checked, the form transmits the value; otherwise, it transmits a ~ character, which the DLL can test for. Optional parameters must be listed last. The DLL's ProcessPizzaForm() function reads the parameter values and produces an HTML confirmation form, which it sends to the customer. Here is part of the function's code:

 

                    *pCtxt << "<form action=\"myex34a.dll?ConfirmOrder\" method=POST>";

        *pCtxt << "<p><input type=\"hidden\" name=\"name\" value=\"";

        *pCtxt << pstrName << "\">"; // xref to original order

        *pCtxt << "<p><input type=\"submit\" value=\"Confirm and charge my credit card\">";

        *pCtxt << "</form>";

        // Store this order in a disk file or database, referenced by name

    }

    else {

        *pCtxt << "You forgot to enter name or address. Back up and try again. ";

    }

    EndContent(pCtxt);

 

The resulting browser screen is shown in Figure 47.

 

ISAPI, IIS, Winsock, C++ and MFC - Figure 56: The pizza confirmation browser screen.

 

Figure 47: The pizza confirmation browser screen.

 

As you can see, we took a shortcut computing the price. To accept, the customer clicks the submit button named Confirm And Charge My Credit Card.

 

The Second Step: Processing the Confirmation

 

When the user clicks the Confirm And Charge My Credit Card button, the browser sends a second POST request to the server, specifying that the CMyex34aExtension::ConfirmOrder function be called. But now you have to solve a big problem. Each HTTP connection (request/response) is independent of all others. How are you going to link the confirmation request with the original order? Although there are different ways to do this, the most common approach is to send some text back with the confirmation in a hidden input tag. When the confirmation parameter values come back, the server uses the hidden text to match the confirmation to the original order, which it has stored somewhere on its hard disk. In the MYEX34A example, the customer's name is used in the hidden field, although it might be safer to use some combination of the name, date, and time. Here's the HTML code that CMyex34aExtension::ProcessPizzaForm sends to the customer as part of the confirmation form:

 

<input type="hidden" name="name" value="Walter Sullivan">

 

Here's the code for the CMyex34aExtension::ConfirmOrder function:

void CMyex34aExtension::ConfirmOrder(CHttpServerContext* pCtxt, LPCTSTR pstrName)

{

    StartContent(pCtxt);

    WriteTitle(pCtxt);

    *pCtxt << "<p>Our courteous delivery person will arrive within 30 minutes. ";

    *pCtxt << "<p>Thank you, " << pstrName << ", for using CyberPizza. ";

    // Now retrieve the order from disk by name, and then make the pizza.

    //  Be prepared to delete the order after a while if the customer doesn't confirm.

    m_cs.Lock(); // gotta be threadsafe

    long int nTotal = ++m_nTotalPizzaOrders;

    m_cs.Unlock();

    *pCtxt << "<p>Total pizza orders = " << nTotal;

    EndContent(pCtxt);

}

The customer's name comes back in the pstrName parameter, and that's what you use to retrieve the original order from disk. The function also keeps track of the total number of orders, using a critical section (m_cs) to ensure thread synchronization.

 

Building and Testing myex34a.dll

 

Building the project adds a DLL to the Debug subdirectory. You must copy this DLL to a directory that the server can find and copy PizzaForm.html also. You can use the scripts and wwwroot subdirectory that already under \Inetpub, or you can set up new virtual directories.

If you make changes to the MYEX34A DLL in the Visual C++ project, be sure to use Internet Service Manager to turn off the WWW service (because the old DLL stays loaded), copy the new DLL to the scripts directory, and then turn the WWW service on again. The revised DLL will be loaded as soon as the first client requests it. If everything has been installed correctly, you should be able to load PizzaForm.html from the browser and then order some pizza. Enjoy!

 

Debugging the MYEX34A DLL

 

The fact that IIS is a Windows NT service complicates debugging ISAPI DLLs. Services normally run as part of the operating system, controlled by the service manager database. They have their own window station, and they run on their own invisible desktop. This involves some of the murkier parts of Windows NT, and not much published information is available. However, you can use these steps to debug your MYEX34A DLL (or any ISAPI DLL):

 

  1. Use the Internet Service Manager to stop all IIS services.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

----------------------------------------------------------------------------------------------------------------------------------------------------------

ISAPI, IIS, Winsock, C++ and MFC - Figure 57: Stopping the IIS Admin and other dependent (World Wide Web publishing) services.

 

Figure 48: Stopping the IIS Admin and other dependent (World Wide Web publishing) services.

 

  1. Choose Settings from the MYEX34A Project menu, and in the Project Settings dialog, type in the data as shown.

 

C:\WINDOWS\system32\inetsrv\inetinfo.exe or C:\WINNT\system32\inetsrv\inetinfo.exe depends on your Windows OS version.

 

ISAPI, IIS, Winsock, C++ and MFC - Figure 58: Entering the program that will be invoked during the debug process.

 

Figure 49: Entering the program that will be invoked during the debug process.

 

  1. Start Active Directory Users and Computers (Administrative Tools menu). Choose User Rights from the Policies menu, check Show Advanced User Rights, select the right Act As Part Of The Operating System, and add your user group as shown on the facing page. For Windows 2000 and above you can use Local Security Policy (Administrative Tools), select User Rights Assignment under Local Policies.

 

ISAPI, IIS, Winsock, C++ and MFC - Figure 59: Changing the User Right Assignment setting through Local Security Settings.

 

Figure 50: Changing the User Right Assignment setting through Local Security Settings.

 

ISAPI, IIS, Winsock, C++ and MFC - Figure 60: Adding a user to the Act as part of the operating system policy.

 

Figure 51: Adding a user to the Act as part of the operating system policy.

 

  1. Repeat step 3 to set the right for Generate Security Audits.

 

ISAPI, IIS, Winsock, C++ and MFC - Figure 61: Generate Security Audits policy.

 

Figure 52: Generate Security Audits policy.

 

  1. Log off and on to Windows NT to activate the new permission. (Don't forget this step.)
  2. Make sure that the current MYEX34A DLL file has been copied into the scripts directory.
  3. Start debugging. You can set breakpoints, step through code, and see the output of TRACE messages.

 

ISAPI Database Access

 

Your ISAPI server extension could use ODBC to access an SQL database. Before you write pages of ODBC code, however, check out the Internet Database Connector (IDC) described in the IIS documentation. The Internet Database Connector is a ready-to-run DLL, Httpodbc.dll, that collects SQL query parameters and formats the output. You control the process by writing an IDC file that describes the data source and an HTX file that is a template for the resulting HTML file. No C++ programming is necessary. The Internet Database Connector is for queries only. If you want to update a database, you must write your own ISAPI server extension with ODBC calls. Make sure your ODBC driver is multithreaded, as is the latest SQL server driver.

 

Using HTTP Cookies to Link Transactions

 

Now that you've wolfed down the pizza, it's time for some dessert. However, the cookies that we'll be digesting in this section are not made with chocolate chips. Cookies are used to store information on our customers' hard disks. In the MYEX34A example, the server stores the customer name in a hidden field of the confirmation form. That works fine for linking the confirmation to the order, but it doesn't help you track how many pizzas Walter ordered this year. If you notice that Walter consistently orders pepperoni pizzas, you might want to send him some e-mail when you have a surplus of pepperoni.

 

How Cookies Work

 

With cookies, you assign Walter a customer ID number with his first order and make him keep track of that number on his computer. The server assigns the number by sending a response header such as this one:

 

Set-Cookie: customer_id=12345; path=/; expires=Monday, 02-Sep-05 00:00:00 GMT

 

The string customer_id is the arbitrary cookie name you have assigned, the / value for path means that the browser sends the cookie value for any request to your site (named CyberPizza.com), and the expiration date is necessary for the browser to store the cookie value. When the browser sees the Set-Cookie response header, it creates (or replaces) an entry in its cookies.txt file as follows:

customer_id

12345

cyberpizza.com/

0

2096697344

0

2093550622

35

*

Every user profile will have cookies.txt. The following Figure shows the example.

 

ISAPI, IIS, Winsock, C++ and MFC - Figure 62: Cookies.txt for user profiles in Windows XP Pro.

 

Figure 53 Cookies.txt for user profiles in Windows XP Pro.

 

ISAPI, IIS, Winsock, C++ and MFC - Figure 63: Cookies.txt’s content example of the user profile for Firefox browser.

 

Figure 54: Cookies.txt’s content example of the user profile for Firefox browser.

 

 

Thereafter, when the browser requests anything from CyberPizza.com, the browser sends a request header like this:

Cookie: customer_id=12345

How an ISAPI Server Extension Processes Cookies

 

Your ISAPI server extension function makes a call like this one to store the cookie at the browser:

AddHeader(pCtxt, "Set-Cookie: session_id=12345; path=/;"
     " expires=Monday, " 02-Sep-05 00:00:00 GMT\r\n");

To retrieve the cookie, another function uses code like this:

char strCookies[200];

DWORD dwLength = 200;

pCtxt->GetServerVariable("HTTP_COOKIE", strCookies, &dwLength);

The strCookies variable should now contain the text customer_id=12345.

 

Problems with Cookies

 

There was uproar some time ago when Internet users first discovered that companies were storing data on the users' PCs. New browser versions now ask permission before storing a cookie from a Web site. Customers could thus refuse to accept your cookie, for example, they could erase their cookies.txt file, or this file could become full. If you decide to use cookies at your Web site, you'll just have to deal with those possibilities.

 

WWW Authentication

 

Up to now, your IIS has been set to allow anonymous logons, which means that anyone in the world can access your server without supplying a user name or password. All users are logged on as IUSR_MYMACHINENAME and can access any files for which that user name has permissions. As stated in Module 32, you should be using NTFS on your server for maximum security.

 

Basic Authentication

 

The simplest way to limit server access is to enable basic authentication. Then, if a client makes an anonymous request, the server sends back the response:

HTTP/1.0 401 Unauthorized

together with a response header like this:

WWW-Authenticate: Basic realm="xxxx"

The client prompts the user for a user name and password, and then it resends the request with a request header something like this:

Authorization: Basic 2rc234ldfd8kdr

The string that follows Basic is a pseudoencrypted version of the user name and password, which the server decodes and uses to impersonate the client. The trouble with basic authentication is that intruders can pick up the user name and password and use it to gain access to your server. IIS and most browsers support basic authentication, but it's not very effective.

 

Windows NT Challenge/Response Authentication

 

Windows NT challenge/response authentication is often used for intranets running on Microsoft networks, but you can use it on the Internet as well. IIS supports it, but not all browsers do. If the server has challenge/response activated, a client making an ordinary request gets this response header:

WWW-Authenticate: NTLM

Authorization: NTLM T1RMTVNTUAABAAAAA5IAA ...

The string after NTLM is the well-encoded user name, the password is never sent over the network. The server issues a challenge, with a response header like this:

WWW-Authenticate: NTLM RPTUFJTgAAAAAA ...

The client, which knows the password, does some math on the challenge code and the password and then sends back a response in a request header like this:

Authorization: NTLM AgACAAgAAAAAAAAAA ...

The server, which has looked up the client's password from the user name, runs the same math on the password and challenge code. It then compares the client's response code against its own result. If the client's and the server's results match, the server honors the client's request by impersonating the client's user name and sending the requested data.

When the client resends the request, the challenge/response dialog is performed over a single-socket connection with keep-alive capability as specified in the Connection request header.

WinInet fully supports Windows NT challenge/response authentication. Thus, Internet Explorer 4.0/above and the MYEX34A WinInet clients support it. If the client computer is logged on to a Windows NT domain, the user name and password are passed through. If the client is on the Internet, WinInet prompts for the user name and password. If you're writing WinInet code, you must use the INTERNET_FLAG_KEEP_CONNECTION flag in all CHttpConnection::OpenRequest and CInternetSession::OpenURL calls as EX34A illustrates.

 

The Secure Sockets Layer

 

Windows NT challenge/response authentication controls only who logs on to a server. Anyone snooping on the Net can read the contents of the TCP/IP segments. The secure sockets layer (SSL) (the open source version – OpenSSL) goes one step further and encodes the actual requests and responses (with a performance hit, of course). Both IIS and WinInet support SSL. The secure sockets layer is described in the IIS documentation. The newest one is Transport Layer Security (TLS).

 

 

Continue on next Module...

 

 

 

 

 

 

 

 

 

 

 

 

 

Further reading and digging:

  1. Apache web server for Windows ISAPI support.

  2. IIS 6.0 and CGI.

  3. DCOM at MSDN.

  4. COM+ at MSDN.

  5. COM at MSDN.

  6. Win32 process, thread and synchronization story can be found starting from Module R.

  7. MSDN MFC 7.0 class library online documentation.

  8. MSDN MFC 9.0 class library online documentation - latest version.

  9. MSDN Library

  10. Windows data type.

  11. Win32 programming Tutorial.

  12. The best of C/C++, MFC, Windows and other related books.

  13. Unicode and Multibyte character set: Story and program examples.

 

 


 

| Tenouk C & C++ | MFC Home | IIS & ISAPI Programming 3 | IIS & ISAPI Filter Programming 5 | Download | Site Index |