VitoPlantamura.com logo - Click here to go to the site home page... Click here to go to the site home page... Click here to go to the Blog page... Click here to go to the archives page... Click here to go to the about page...
"extremely impressive work"
- Mark Russinovich, www.sysinternals.com
(referring to my BugChecker)
my face...
Homepage of Vito Plantamura (@, LinkedIn), Windows Researcher and Developer. [user=Guest] - updated: August 08, 2007
 PuTTY Telnet Client 0.58 ActiveX Control
PUTTY TELNET CLIENT ACTIVEX CONTROL

This is an ActiveX client derived from the PuTTY Telnet client.

INTRODUCTION

For my product GoToTerminal, I wanted to add support for Telnet servers. GoToTerminal is an ActiveX/AJAX based online service, so I needed a Windows Telnet client in the form of an ActiveX control. As in the case of my VNC ActiveX client, I soon discovered that I had to code the control myself, because there was any free and ready to use alternative. So I searched in Google for an open-source Win32 Telnet client to convert to an ActiveX control and, in a matter of minutes, I found and just fell in love with Putty.

However, contrarily to the Ultr@VNC client used as the base for the other control, Putty’s implementation presented several problems that had to be faced and solved from the conversion perspective. For example, the original code is written entirely in C, with heavy use of global and function static variables to maintain the state of the client, whereas the original Ultr@VNC client is written in a strongly object oriented C++ code, with classes representing the client, the connections and so on. Moreover, the original Telnet client supported only one user session at a time (and, more important, per Win32 process), whereas GoToTerminal can host and create multiple ActiveX objects in the same Internet Explorer process representing multiple different connections. So I had to devise new and more complex techniques to convert the original single-connection-per-process code of Putty into an ActiveX object usable in GoToTerminal. The solutions I devised to resolve this problem are described below. The entire conversion took 5 days. This is a screenshot of the GoToTerminal Control Panel, using my Putty ActiveX Client:

A Telnet Server connection in GoToTerminal.

OBJECT MODEL

These are the properties and methods exposed by the control:

[id(1)] HRESULT Connect();
[id(2)] HRESULT Disconnect();
[propget, id(3)] HRESULT Connected([out, retval] BOOL *pVal);
[propget, id(4)] HRESULT Server([out, retval] BSTR *pVal);
[propput, id(4)] HRESULT Server([in] BSTR newVal);
[id(5)] HRESULT ExecuteCommand( [in] BSTR cmdText, [out,retval] BSTR *pRetVal );
[propget, id(6)] HRESULT IsInitialized([out, retval] BOOL *pVal);

There are 2 events supported by the ActiveX control:

[id(1)] HRESULT OnConnected();
[id(2)] HRESULT OnDisconnected(BSTR reason);

CONVERSION DETAILS

As I said above, the conversion presented several problems. The major problem was that Putty is written in C, and that it makes large use of global and function static variables to maintain the state of the client. The solution I devised to resolve this problem was to take these global variables and to remap them to memory allocated and managed by the ActiveX control. This solution was ideal since it would have allowed to resolve the main limitation of the original Putty implementation, i.e. the fact that Putty allows only one user connection at a time, in the same Win32 process. In fact, taking the control over the allocation and storage of all the global (non-CRT) memory in the Putty’s process (i.e. all the memory NOT allocated in the stack or on the heap) would have allowed to eventually create different instances of the same global variable, thus allowing, as a consequence, to let the user connect to different Telnet clients from the same Win32 process.

The idea was to search for all the global and function static variables in the Putty’s source code, and to replace them with macros which would have invoked a function returning a dereferenced pointer. This function, taking the stringized name of the variable and its size as parameters, would have taken care of the allocation and storage of the corresponding chunk of memory.

To more easily and quickly localize all the global variables in the Putty source code, I used the program’s debug information (i.e. the .PDB file generated during the compilation). Since all global and function static variables are all mapped to the same PE memory section, tipically with name “.data” or “.idata”, I tried to obtain the map of these sections (i.e. the list of all the symbols mapped to a given section) from the .PDB file, actually with success. I remembered that such information could be easily obtained opening the .PDB file with DIA, i.e. the Microsoft Debug Interface Access Software Development Kit. Instead of coding a small C++ program calling the DIA SDK, I remembered that the example application accompanying the DIA SDK just produces a verbose text file containing just this kind of information.

Returning to the conversion problem, these are the resulting macros as defined in Putty.h, one for every data type in the original source code:

#define DEFINE_AX_GET_POINTER( name, type )               ( * (type*) AX_GET_POINTER( #name, sizeof( type ) ) )
#define DEFINE_AX_GET_POINTER_ARRAY( name, type, size )   ( (type) AX_GET_POINTER( #name, size ) )
#define DEFINE_AX_GET_POINTER_INT( name, type, init )     ( * (type*) AX_GET_POINTER_INT( #name, sizeof( type ), init ) )
#define DEFINE_AX_GET_POINTER_UINT( name, type, init )    ( * (type*) AX_GET_POINTER_UINT( #name, sizeof( type ), init ) )

So, according to this system, a global variable definition like this:

int flags;

has to be replaced in the Putty’s sources with this code:

#define flags               DEFINE_AX_GET_POINTER( flags, int )

And this is the definition of the AX_GET_POINTER function, whose name is the result of the expansion of the DEFINE_AX_GET_POINTER macro above:

extern "C" VOID* AX_GET_POINTER( CHAR* name, INT size )
{
    CCriticalSection::scope        __access__( ( (CPuttyAxObj*) ::TlsGetValue( g_Tls.tls ) )->m_csGetPointerAccess );
 
    // already allocated ?
 
    VOID*     pvExist = ::ReturnIfAllocated_NOLOCK( name, size );
    if ( pvExist )
        return pvExist;
 
    // allocate.
 
    return ::ForceAllocate_NOLOCK( name, size );
}

and the two functions called above, ReturnIfAllocated_NOLOCK and ForceAllocate_NOLOCK:

static VOID* ReturnIfAllocated_NOLOCK( CHAR* name, INT size )
{
    // already allocated ?
 
    std::vector< SMemRegionInfo >*     pvec = & ( (CPuttyAxObj*) ::TlsGetValue( g_Tls.tls ) )->m_vsMemRegionInfo;
 
    for( int i=0; i<pvec->size (); i ++ )
    {
        SMemRegionInfo*??     p = & (*pvec)[ i ];
        if ( ::strcmp( p->pszName, name ) == 0 )
        {
           if ( p->iMemSize != size )
               throw;
           else
               return p->pvMem;
        }
    }
 
    return NULL;
}
 
static VOID* ForceAllocate_NOLOCK( CHAR* name, INT size )
{
    // allocate.
 
    std::vector< SMemRegionInfo >*     pvec = & ( (CPuttyAxObj*) ::TlsGetValue( g_Tls.tls ) )->m_vsMemRegionInfo;
 
    VOID*         mem = ::malloc( size );
    if ( mem == NULL )
        return NULL;
 
    ::memset( mem, 0, size );
 
    pvec->push_back( SMemRegionInfo( name, mem, size ) );
    return mem;
}

As you can easily see from the code above, m_vsMemRegionInfo is the repository (of type vector< SMemRegionInfo >) containing the allocation information about all the memory symbols associated with a specific instance of the ActiveX control (CPuttyAxObj). The pointer to the corresponding CPuttyAxObj object is obtained from a Thread Local Storage slot, whose value is set and restored each time the execution reaches the Window Procedure of the Telnet client, thus allowing the reentrance in the Window Procedure itself. This is the definition of the Window Procedure associated with the Telnet client window:

extern "C" LRESULT CALLBACK WndProc_Ax(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    CPuttyAxObj*        pAx = (CPuttyAxObj*) ::GetWindowLong( hwnd, GWL_USERDATA );
 
    PVOID            pvTlsOld = NULL;
 
    if ( pAx )
    {
        pvTlsOld = ::TlsGetValue( g_Tls.tls );
        ::TlsSetValue( g_Tls.tls, pAx );
    }
 
        LRESULT lr = ::WndProc_Putty(hwnd, msg, wparam, lparam);
 
    if ( pAx )
    {
        ::TlsSetValue( g_Tls.tls, pvTlsOld );
    }
 
    return lr;
}

LICENSE

My ActiveX Putty client is licensed under the terms of the GNU General Public License (GPL).

DOWNLOAD

You can download the source code of the Putty ActiveX client from here.

Also take a look at my Ultr@VNC ActiveX Control.

 Quotes
"Among the Windows experts I know personally, no one can beat Vito Plantamura."
- Francesco Balena, Code Architects SRL

"Your NDIS Monitor application, is amongst the most impressive networking code I have seen on the .Net framework."
- Ben Hakim.
 Photos
Various images from italian conferences and events (keep the mouse on a thumbnail for a short description):
Me at the Microsoft/HP/Intel organized Route64 event in Milan in May 2005, explaining how COM+ behaves on 64-bit Microsoft operating systems. I was there with the friends of Code Architects.
Me at the Microsoft Security Roadshow event in Bari in April 2006, explaining how the logon process works in Windows NT. There were 250 attendees.
Microsoft Security Roadshow 2006 in Treviso. This is an image of the huge 700-seats conference room.
Me at the Microsoft Security Roadshow 2006 in Treviso. This is a moment of the 3-hours session.
 Site login
NOTE: Actually the login feature is used only for administrative and content management purposes.
Username

Password

Everything here (code, binaries, text, graphics, design, html) is © 2010 Vito Plantamura and VPC Technologies SRL (VATID: IT06203700965).
If you download something (compilable or not) from the site, you should read the license policy file.
If you want to contact me via email, write at this address.