Click here to Skip to main content
Click here to Skip to main content

WinINet Test Application

By , 12 Apr 2008
 

Introduction

This is a WTL program that is able to test connections with web servers using just about every possible combination of requirements that are commonly encountered. The program is able to connect:

  • POST or GET
  • SSL
  • supply personal certificate, if required by the web server
  • vary receive time out
  • supply username/password for web server
  • ignore bad CA certificates

The program uses asynchronous WinINet calls to handle all communication and reporting. The program also demonstrates how to allow the user to select a personal certificate from the My store.

Background

I put this program together to help test some issues I encountered with an unusual firewall configuration and web server security requirements. The output from the WinINet call back routine is very helpful when debugging connection issues. I have placed a number of messages in the ATLTRACE macro output as well as the main dialog of the program. While example code for most of the operations can be found in a variety of locations, I needed to tie everything together into a single application, implemented with consistent error handling.

The program started as an amalgamation of several samples from Microsoft. After a few hours of coding and a quick port to WTL, the application was pretty well gutted and rewritten. I need to give credit where credit is due for this application: the Microsoft examples saved me a lot of time (except when they were wrong or incomplete and cost me a lot of time).

Using the Code

The code is very simple to use. Simply download the demonstration and compile it. I am using WTL 8.0 with VS 2005. You may need to download WTL.

This is a WTL modal dialog application. I used some wait functions developed by Andy Smith to get around blocking issues when a modal dialog is utilized. You'll really want to examine the main WinINet loop, located in SendRequest. Here, you will learn how to handle ERROR_INTERNET_INVALID_CA, ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED, and HTTP_STATUS_DENIED. The WinInetCallback method is utilized to supply information like personal certificates or user name and password for resubmission to the web server and pass communication errors to the WinINet loop in SendRequest via the context variable.

Obtaining a Pointer to a Personal Certificate

There are times when you may need to allow a user to select a personal certificate. For instance, you are writing an application that uses WinINet. Here is some simple code that allows a user to select from a list of personal certificates loaded in their certificate store:

#include <prsht.h>
#include <cryptuiapi.h>

...

HCERTSTORE        m_hMyStore;
PCCERT_CONTEXT    m_pHSCertContext;

...

// Open the 'MY' certificate store to be presented to the user
if(!(m_hMyStore = CertOpenStore(
    CERT_STORE_PROV_SYSTEM,
    0,
    NULL,
    CERT_SYSTEM_STORE_CURRENT_USER,
    L"MY")))
{
    // Let the user know we could not open the store.
    CString tMsg, tTitle;
    GetErrorMessage(GetLastError(), &tMsg);
    MessageBox((LPCTSTR) tMsg, 
               (LPCTSTR) tTitle.LoadString(IDS_TITLEFAILED), 
               MB_ICONEXCLAMATION);
    return FALSE;
}
else
{
    // If we get to this stage, the certificate store
    // was opened successfully.
    // Display a selection of certificates to choose from.
    m_pHSCertContext = CryptUIDlgSelectCertificateFromStore(
        m_hMyStore,
        NULL,
        NULL,
        NULL,
        CRYPTUI_SELECT_EXPIRATION_COLUMN | 
        CRYPTUI_SELECT_LOCATION_COLUMN |
        CRYPTUI_SELECT_FRIENDLYNAME_COLUMN |
        CRYPTUI_SELECT_INTENDEDUSE_COLUMN ,
        0,
        NULL);
}

//Show an error if a certificate is not seleted.
if (!m_pHSCertContext)
{
    CString tMsg, tTitle;
    MessageBox((LPCTSTR) tMsg.LoadString(IDS_ERR1), 
               (LPCTSTR) tTitle.LoadString(IDS_ERRT1), 
               MB_ICONEXCLAMATION);
    return FALSE;
}

Supplying a Personal Certificate on a WinINet Handle

TCP/IP communications are a little tricky, in that you cannot supply the certificate when you make the request. You need to call OpenRequest, and then wait for the INTERNET_STATUS_REQUEST_COMPLETE signal in your Callback routine. You can then check the returned error, looking for ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED in the INTERNET_ASYNC_RESULT struct. When you get the error, you need to use InternetSetOption on your OpenRequest handle to supply the certificate and then repeat the OpenRequest. It's really pretty simple. It's just that the docs and samples leave out a lot of the error checking and recovery work that you need. It helps to have a server setup with a valid SSL certificate and require personal certificates in order to connect. Here is a code fragment for handling ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED:

case INTERNET_STATUS_REQUEST_COMPLETE:
    {
        //check for errors
        if(ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED == pRes->dwError)
        {
            // The server requires a certificate

            // Save dwError so we can act on it.
            cpMainDlg->m_dwError = pRes->dwError;
            sMsg.Format("%s: CLIENT_AUTH_CERT_NEEDED (%d)", 
                        cpMainDlg->m_szMemo, pRes->dwError);

            // Check that we have a certificate pointer,
            // otherwise, it's time to fall over dead.
            if(cpMainDlg->m_pHSCertContext == NULL)
            {
                //write the callback information to the buffer
                sMsg += _T(" No cert provided by user");
            }
            else
            {
                // We have a pointer (there's no guarantee it's valid)
                // We have to be careful and check all returns for errors
                // as the pointer could point to la-la land if someone in
                // a different thread has closed the certificate store.

                // Attempt to set the option
                if (!::InternetSetOption( cpMainDlg->hRequest,
                    INTERNET_OPTION_CLIENT_CERT_CONTEXT ,
                    (LPVOID) cpMainDlg->m_pHSCertContext,
                    sizeof(CERT_CONTEXT) ))
                {
                    // Display the error.
                    CString sErrMsg;
                    cpMainDlg->GetErrorMessage(GetLastError(), &sErrMsg);
                    sMsg += _T(" ");
                    sMsg += sErrMsg;
                }
            }
        } // end if (ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED == dwError)
...
    }

Future Plans

I will add authentication handling for proxy servers in a future revision. Currently, only one set of credentials may be supplied, but the program needs to allow for two sets of credentials, one for the proxy server and one for the web server. I also want to add a file attachment dialog for testing the POST method with binary data or pseudo form data attached.

History

  • Version 1.0 - First release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Gene OK
Business Analyst Southwest Research Institute
United States United States
Member
I am a C++ coder. I am also proficient with Oracle PL/SQL. A lot of folks dislike Oracle, but I find Oracle is a significant revenue enhancer. Customers pay for first rate Oracle programming skills.
 
I have extensive experience with COM+, COM, ATL, WTL and installation package development. I've developed several packages in C#, but I prefer managed/native C++. I've been coding for nearly thirty plus years, getting my start with atomic and molecular orbital calculations in FORTRAN. I've been working with C or C++ since the days of QuickC, Desmet C, Datalight C and MSC 5.1.
 
One of my pet peeves in life is a programmer's lack of attention to the details of error handling. Most example code I see on the internet lacks depth. No use of Window's Event Logging and a lack of understanding as to how to handle exceptions. If folks actually think about how to properly debug and test, there would be fewer "slop" articles and a lot higher quality.
 
Including instrumentation in your software to allow proper diagnosis of failures is far more important to a user than the latest Gee-Whiz-Bang visual effects. Graphical gotta-haves fade like the lettuce in a refrigerator, but solid programs just keep on running, no matter what environment they are placed in.
 
My Web Site, Blog & Wiki

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberYiannis Spyridakis5 Nov '11 - 3:54 
Thanks, good stuff indeed!
GeneralMy vote of 5grouplyricC8 Oct '10 - 15:34 
Great!I just need it。
GeneralThanks!memberVolodymyr Metlyakov24 Feb '09 - 3:43 
Just Thank You for this code, it's very good for learning!
GeneralERROR_IO_PENDINGmemberkellykwai15 Sep '08 - 0:15 
InternetSetOption( hRequest, INTERNET_OPTION_CLIENT_CERT_CONTEXT ,
(LPVOID) m_pHSCertContext,
sizeof(CERT_CONTEXT) );
HttpSendRequest(hRequest, NULL, 0, NULL,0 );
 

 
It cannot be send and ERROR_IO_PENDING message is returned...
 
What's wrong????Confused | :confused:
GeneralRe: ERROR_IO_PENDINGmemberCodeWizard195115 Sep '08 - 4:32 
The web server has to be setup to accept personal certificates.
 
You have to wait for the server to request the personal certificate. You cannot send the certificate with the initial Send Request.
 
Both the client and server must be setup to recognize the certificate authority used to generate the certificate.
 
The certificate you are using must be created for personal use. You cannot use a test certificate generated for use with a web server as a personal certificate.
 
I am not sure of the source of your error, but my guess is there is something wrong with your certificate, or your machine does not recognize the certificate authority.
 
CodeWiz51
 
-- Life is not a spectator sport. I came to play.
My Web Site, Blog & Wiki

GeneralRe: ERROR_IO_PENDINGmemberkellykwai15 Sep '08 - 5:09 
Thank you very much!Rose | [Rose] Rose | [Rose]
 
The certificate is generated by the counterparty (HTTPS server).
 
If I didn't send the certificate before HTTPSendRequest, the return message is "CLIENT_AUTH_CERT_NEEDED"
GeneralRe: ERROR_IO_PENDINGmemberCodeWizard195115 Sep '08 - 5:21 
You have to wait for the CLIENT_AUTH_CERT_NEEDED message before you set the personal certificate using InteretSetOption. It is then safe to send the response with the certificate set.
 
I want to repeat, you should not send the certificate until the server requests it. Wait for CLIENT_AUTH_CERT_NEEDED before calling InternetSetOption.
 
CodeWiz51
 
-- Life is not a spectator sport. I came to play.
My Web Site, Blog & Wiki

GeneralRe: ERROR_IO_PENDINGmemberkellykwai15 Sep '08 - 5:55 
You are so nice...
Thank you very much!
 
I will have a try soon
GeneralRe: ERROR_IO_PENDINGmemberkellykwai17 Sep '08 - 1:03 
You are right. It's problem of the certificate.
 
May I ask if there are any references for HTTPS Server programming? D'Oh! | :doh:
 
Thanks!
GeneralRe: ERROR_IO_PENDINGmemberCodeWizard195117 Sep '08 - 1:51 
The encryption on the server side is generally handled by IIS. You shouldn't need to do any server side programming. Even a .Net web service SSL communication is handled by IIS.
 
CodeWiz51
 
-- Life is not a spectator sport. I came to play.
My Web Site, Blog & Wiki

GeneralRe: ERROR_IO_PENDINGmemberkellykwai17 Sep '08 - 2:20 
Thanks for your kindly reply.
 
For my situation, I can start my IIS service with port 15000 and a valid certificate.
Someone may send message to me (eg. https://www.xx.xx:15000) using program like WinInet Test Application. My point is how to get the message and reply it?????Confused | :confused: Confused | :confused:
 

Thanks!
 
Anyway, would you mind tell me where are you from? I'm at Hong Kong.
GeneralRe: ERROR_IO_PENDINGmemberCodeWizard195117 Sep '08 - 3:17 
The simplest way is to specify a page:
 
https://www.yourwebserver.xxx/mypage.aspx:15000
 
You would be much better off using 443 for the port so you do not have to specify a port.
 
CodeWiz51
 
-- Life is not a spectator sport. I came to play.
My Web Site, Blog & Wiki

GeneralRe: ERROR_IO_PENDINGmemberkellykwai17 Sep '08 - 5:55 
would you give me any idea about "mypage.aspx"?
 
I didn't have any experiences of internet progamming.
 
Thx!
GeneralRe: ERROR_IO_PENDINGmemberCodeWizard195117 Sep '08 - 7:33 
You'll have to learn aspx on your own. There's plenty of references here on code project.
 
CodeWiz51
 
-- Life is not a spectator sport. I came to play.
My Web Site, Blog & Wiki

GeneralRe: ERROR_IO_PENDINGmemberkellykwai17 Sep '08 - 16:45 
thank you very much!
GeneralRe: ERROR_IO_PENDINGmemberkellykwai21 Sep '08 - 22:35 
do you know about HTTP Server API of C++?
do you think it is a right tool for HTTPs Server?
QuestionHow to get response message?memberkellykwai9 Sep '08 - 20:35 
The program is wonderful!
I'm a beginner. So would I ask how to get the received message (such that RESPONSE_RECEIVED (>0))?
 
Thanks!
GeneralNo certificates are showedmemberkellykwai9 Sep '08 - 0:42 
When I click "Supply Personal Certificate"
 
there are no certificates appeared. Does it mean that I have not installed any certificate yet?
GeneralRe: No certificates are showedmemberCodeWizard19519 Sep '08 - 1:02 
Yes. If there are no certificates in your Personal Store, then you'll need to install one.
 
CodeWiz51
 
-- Life is not a spectator sport. I came to play.
My Web Site, Blog & Wiki

GeneralCompile problemmemberAnders Karlsson18 Jun '08 - 5:11 
Hi, this looks good,
but I cannot compile because 'atlapp.h' is missing (unknown file for me).
 
fatal error C1083: Cannot open include file: 'atlapp.h': No such file or directory
 
Any suggestions where from /how I should include this? Thanks
AnswerRe: Compile problemmemberCodeWizard195118 Jun '08 - 5:50 
atlapp.h is part of WTL, per the 'Using the Code' section. You may need to download WTL. If you have downloaded and installed WTL, then you need to wire it into your VS 2005 environment.
 
You will need to add the WTL include directory to your c++ include directory options under Tools->Options. In the treeview you will open the Projects and Solutions branch, then select VC++ Directories. In the pull down labeled 'Show directories for:', select Include files and then add the WTL include directory. I place the WTL include directory at the beginning of the include list, as WTL headers supersede some ATL functionality.
 
If you are not experienced with installing WTL and wiring it into VS 2005, I suggest you read Michael Dunn's excellent tutorials starting with Part I. Any WTL version will work, but I used WTL 8.0 for this article.
 
CodeWiz51
 
-- Life is not a spectator sport. I came to play.
Code's Musings | Code's Articles

QuestionProxy Authentication w/ Wininet ...Can it be Done????memberV3RO12 May '08 - 8:31 
First off thanks for the code. I noticed though that you mentioned providing some samples for proxy authentication, which happens to be something Im wrestling w/ now. I can find tons of .NET ways to provide proxy credentials using .NET System.Net.CredentialCache.DefaultCredentials to the proxy handle, BUT for Wininet I cant find diddley. I need to anticipate programmatically if Basic or NTLM after the first "407" exception is caught, without prompts to the user. Basically it should retrieve whatever credentials IE is using. Can ya throw me a bone here?
 
Thanks V3RO
AnswerRe: Proxy Authentication w/ Wininet ...Can it be Done????memberCodeWizard195112 May '08 - 9:09 
You use InternetSetOption with the function set to INTERNET_OPTION_PROXY_PASSWORD or INTERNET_OPTION_PROXY_USERNAME when you catch the 407 status. If you are using NTLM to authenticate to the proxy through wininet it will supply the parameters automatically. WinInet is in a different session from IE. I am not sure how you would retrieve the basic authentication parameters that you supplied to IE.
 
You can query the scheme after you get the 407 status. Just loop, calling
HttpQueryInfo(hRequest, dwFlags, szScheme, &cbScheme, &dwIndex); until you get an error. You can test on the string returned in szScheme to get the scheme type before you supply any parameters.
 
Take a look at this sample, supplied in the Platform SDK. Your root directory will probably be different for installation of the SDK:
C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Samples\Web\networking\httpauth
 
CodeWiz51
 
-- Life is not a spectator sport. I came to play.
Code's Musings | Code's Articles

GeneralRe: Proxy Authentication w/ Wininet ...Can it be Done????memberV3RO12 May '08 - 9:20 
Thanks, WOW! a straight forward non-cryptic answer..Smile | :) with a sample code reference. you rock!
Thanks again this is exactly what I was looking for.
 
V3R0
GeneralRe: Proxy Authentication w/ Wininet ...Can it be Done????memberCodeWizard195112 May '08 - 9:49 
Yeah, this stuff is not as magical as the "brains" on this site would have you believe. All it takes is some exposure to working code and access to the SDK documentation. Good luck and let me know what you come up with. Big Grin | :-D
 
CodeWiz51
 
-- Life is not a spectator sport. I came to play.
Code's Musings | Code's Articles

GeneralLooks wonderfulmemberJerry Evans16 Apr '08 - 1:05 
Now if only I could download it - CP keeps throwing error pages at me.
 
Interestingly your app shows up when I was contemplating a profiling tool for web site quality. Ever wonder which upstream server is responsible for the delay in getting a page rendered or why a video is slow to play? I suspect it might be quite easy to bolt this functionality on to your splendid looking debugger.
 
I'll post a rating when I've had a chance to play.
 
Thanks + best wishes
 
Jerry
GeneralRe: Looks wonderfulmemberCodeWizard195116 Apr '08 - 3:16 
Thanks for your comment.
 
CP Editors just moved the application from unedited to edited. The source code may not have been property moved or the link may not have been updated when you tried to download. I just tested it and the download seems to be working properly.
 
Gene
Generalsimply greatmemberSudhir Mangla13 Apr '08 - 0:40 
Simply great piece of code
 
Sudhir Mangla

http://DevelopersVoice.com
(VC++ FAQ, MFC FAQ, C++ FAQ)
http://Programmerworld.net
(Free books and source code)
http://Faq.Programmerworld.net
(FAQ and Tips for programmers)
GeneralRe: simply greatmemberCodeWizard195113 Apr '08 - 2:26 
Thanks Sudhir. It was a lot of fun putting coding the application.
 
Gene

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 12 Apr 2008
Article Copyright 2008 by Gene OK
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid