Driving PuTTY
This login program is now complete save for one detail: I need a way to send my responses back to AutoPutty.
The first part of this is pretty obvious I just need to read the data from the dialog box and post it to AutoPutty with my useful WM_COPYDATA command. This happens in my WM_APP+1 handler:
afx_msg LRESULT CPuttyDriverDlg::OnWmAppPlusOne(WPARAM wParam, LPARAM lParam)
{
UpdateData(TRUE);
CString msg;
switch ( wParam ) {
case 0 :
msg = this->m_UserId + '\r'; break;
case 1:
msg = this->m_Password + '\r'; break;
}
if ( this->m_PuttyWindow ) {
COPYDATASTRUCT cd;
cd.dwData = (ULONG_PTR) 0xF00DFACE;
cd.cbData = msg.GetLength();
cd.lpData = (PVOID) (const char *) msg;
::SendMessage( this->m_PuttyWindow,
WM_COPYDATA,
(WPARAM) this->m_hWnd,
(LPARAM) &cd );
}
return 0;
}
Sending this data to AutoPutty is fine, but right now the program doesn't do anything with that message. The final piece of work is to add a WM_COPYDATA handler to window.c.
Simply grabbing the data is easy enough the data structure that accompanies the message contains a pointer to the data and a value indicating its length. However, I have two problems I have to solve before the data is actually sent out to the to whatever device AutoPutty is connected to.
First, I have to take into account the fact that PuTTY was written to use wide characters. My driver program was built using MultiByte characters, so we have a mismatch. This means I have to do a conversion of the data from one domain to the other. This is a two step process - I call MultiByteToWideChar() once to determine how much space I need, then I allocate a buffer and call it again.
The second thing I need to do is determine what to do with the data once I've converted it. PuTTY takes all terminal input and eventually passes through a function called luni_send(). Calling this function directly from the Windows procedure seems to work just fine.
The WM_COPYDATA handler I created looks like this:
case WM_COPYDATA :
{
COPYDATASTRUCT *cd = (COPYDATASTRUCT *) lParam;
int wsize = MultiByteToWideChar( CP_ACP,
MB_PRECOMPOSED,
(LPCSTR) cd->lpData,
cd->dwData,
NULL,
0 );
wchar_t *buf = (wchar_t *) calloc( wsize+1, sizeof(wchar_t) );
MultiByteToWideChar( CP_ACP,
MB_PRECOMPOSED,
(LPCSTR) cd->lpData,
cd->dwData,
buf,
wsize + 1 );
if (term->ldisc)
luni_send(term->ldisc, buf, wsize, 0);
free( buf );
}
At this point, I have a working program - it connects to my designated host, and sends the username and password of my choice to the host, connecting me to the system.
I should add a note of caution here: Automating logins is a tempting time saver, but in general, this is a really bad idea. Any time you hard code credentials into a program, you open the door to all sorts of new attacks on your system.
In my video demo program, the user has to enter a name and password, so nothing is hardcoded, but even this adds security holes to a system. I encourage you to think of this as a demonstration only.
Source Code
I've included the complete source code for PuttyDriver, the MFC project that controls AutoPutty. It was built with Visual Studio 2010, so you may have a little work to do if you backport it to earlier versions. My use of language features and classes should be compatible with much earlier versions this is all very simple code.
Because PuTTY is always changing, I am not redistributing a snapshot of the version I used. Instead, I'm including before and after copies of the two source files I modified: window.c and terminal.c. If you build with Putty 0.61, you should be able to drop these two files right on top of the files included with the distribution and be on your way. With later versions of PuTTY you will have to perform an intelligent merge of the changes, which I hope will be a fairly effortless process.
Downloads
- PuttyDriver.zip: The PuttyDriver source and project. You will need to add the PuTTY project to this solution as described in the article.
- putty.zip: This contains the two PuTTY source files modified for this project. Both the original 0.61 source and my modified source are supplied. Executables are supplied as well, which may or may not work on your system.


