A simple port scanner using ACE proactor
Today I wrote a simple port scanner using ACE proactor. Even though the code itself is useless, the process helped me to know ACE proctor's implementation details much better.
Proactor is a very good model for high performance IO, but you can not implement an efficient port scanner with it.
First, since most platforms do not have native capability for asynchronously connecting sockets, ACE may choos to emulate asynchronous connections with an instance of ACE_Select_reactor managed privately by the framework (for example, on windows). Btw, the description on C++NP v2 page 283, sidebar 57 is inaccurate: even though Windows XP/2003 has that capability(via ConnectEX() API), ACE is not using it now (to support older platform like windows 2000). You can check out the source code of ACE_WIN32_Asynch_Connect::connect() to see yourself. So in this case you are using a Reactor in deed, and since the framework need to spawn thread to do the emulation, it is less efficient than using Reactor directly.
Second, since each ACE_Handler can only be bundled with one handle/socket, you need to create one ACE_Handler for each connection, which definitely has a few more extra overhead than just creating a plain old socket handle.
Here is the source:
Proactor is a very good model for high performance IO, but you can not implement an efficient port scanner with it.
First, since most platforms do not have native capability for asynchronously connecting sockets, ACE may choos to emulate asynchronous connections with an instance of ACE_Select_reactor managed privately by the framework (for example, on windows). Btw, the description on C++NP v2 page 283, sidebar 57 is inaccurate: even though Windows XP/2003 has that capability(via ConnectEX() API), ACE is not using it now (to support older platform like windows 2000). You can check out the source code of ACE_WIN32_Asynch_Connect::connect() to see yourself. So in this case you are using a Reactor in deed, and since the framework need to spawn thread to do the emulation, it is less efficient than using Reactor directly.
Second, since each ACE_Handler can only be bundled with one handle/socket, you need to create one ACE_Handler for each connection, which definitely has a few more extra overhead than just creating a plain old socket handle.
Here is the source:
#include <ace/Log_Msg.h>
#include <ace/INET_Addr.h>
#include <ace/OS.h>
#include <ace/Proactor.h>
#include <ace/WIN32_Proactor.h>
class PortScanner : public ACE_Handler
{
public:
PortScanner()
{
}
int open(ACE_Proactor& proactor, const ACE_INET_Addr& remote_addr)
{
this->proactor(&proactor);
this->remote_addr_.set(remote_addr);
if (connect_.open(*this, ACE_INVALID_HANDLE, 0, this->proactor()) < 0)
{
return -1;
}
return connect_.connect(ACE_INVALID_HANDLE, remote_addr_, ACE_Addr::sap_any, 1);
}
void handle_connect(const ACE_Asynch_Connect::Result &result)
{
//This handler will be called if connection is established or RST is received,
//You may no get any response in a timely manner even if there is no firewall.
ACE_ASSERT(result.connect_handle () != ACE_INVALID_HANDLE);
if (result.success())
{
//Connection is established
ACE_DEBUG((LM_NOTICE, "%s:%d is open\n",
remote_addr_.get_host_addr(),
remote_addr_.get_port_number()));
}
else
{
//Got a RST
}
}
private:
ACE_INET_Addr remote_addr_;
ACE_Asynch_Connect connect_;
};
int ACE_TMAIN(int /*argc*/, ACE_TCHAR** /*argv*/)
{
ACE_Proactor proactor;
ACE_INET_Addr scan_target("127.0.0.1:0");
//Asynchronous connection are simulated on Both Windows and Unix
//(C++NP v2 page 283, sidebar 57 is inaccurate).
//Since ACE_Select_reactor is used internally, the handles limit
//applies. You can either increase the limit(1024 by default), or
//simply do not register too much handles simultaneously.
unsigned short start_port = 1;
unsigned short end_port = 1024;
ACE_ASSERT(end_port > start_port);
const size_t size = end_port - start_port + 1;
//'size' is not const expression so we can not create the array on stack
PortScanner* portscanners = new PortScanner[size];
for (unsigned short i = start_port; i < end_port; ++i)
{
scan_target.set_port_number(i);
portscanners[i].open(proactor, scan_target);
}
//Run event loop for 10 seconds
ACE_DEBUG((LM_NOTICE, ACE_TEXT("Portscan started\n")));
ACE_Time_Value timeout(30);
proactor.proactor_run_event_loop(timeout);
delete[] portscanners;
return 0;
}
3 Comments:
Hi,
I strongly think that you're wrong since you're comparing Win32 with the mighty UNIX :P. I am joking.
What I am serious about is that you've stated that on both UNIX and Win32 platforms the connect is sync and not async.
I've checked the source and for POSIX platforms (that is UNIX/Linux) the connect is async. (check in ace/POSIX_Asynch_IO.cpp, method ACE_POSIX_Asynch_Connect::connect_i where the actual implementation resides).
Also I was unable to find the emulation layer you're talking about - that emulates the Proactor via a Reactor. Some source code hints would be advisable.
Thank you Bogdan. Yes, issue 1 only applies to WIN32. I should make it more clear.
And the code hints of where this emulation of the Proactor takes place with a Reactor?
Post a Comment
<< Home