Getting a copy of any received ICMP packet with boost asio - c++

My goal is to write a little program in c++ with boost asio that notifies me whenever an ICMP packet is received on any network interface.
The program doesn't print any errors and there are no exceptions.
But it also doesn't receive any ICMP packets sent by any program but one (the ping example of boost asio, which can be found here).
What's even stranger is the fact that the ICMP echo request packet from the boost example (after adjusting the payload and the ICMP identifier accordingly) and the default windows ICMP echo request (when using "ping" in the windows commmand line) look almost exactly the same in wireshark. The only difference beeing the identification field in the IPv4 header and thus also the ICMP checksum.
The same behaviour can be observed no matter where the echo request comes from, be it a virtual machine or another real computer in the network.
I can ping all those machines without any issues.
Disabling the windows firewall yields the same result.
OS: Windows 10 64 bit Enterprise N (10.0.10586 Build 10586)
Boost version: 1.62.0.
IDE: Microsoft Visual Studio Community 15.
Here is what I came up with:
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/icmp.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/placeholders.hpp>
#include <boost/system/error_code.hpp>
#include <boost/array.hpp>
#include <boost/bind.hpp>
#include <iostream>
class ICMPReceiver {
public:
ICMPReceiver( boost::asio::io_service & IOS ):
m_sock( IOS ),
m_localEP( boost::asio::ip::icmp::v4(), 0 )
{
}
bool open() {
boost::system::error_code ec;
if ( !m_sock.is_open() ) {
m_sock.open( boost::asio::ip::icmp::v4(), ec );
if ( ec ) {
std::cerr << "Error in socket.open():\n" << ec.message() << '\n';
return false;
}
}
return true;
}
bool bind() {
boost::system::error_code ec;
m_sock.bind( m_localEP, ec );
if ( ec ) {
std::cerr << "Error in socket.bind():\n" << ec.message() << '\n';
return false;
}
}
bool startReceiving() {
try {
m_sock.async_receive_from( boost::asio::buffer( m_receiveBuffer ),
m_remoteEP,
boost::bind( &ICMPReceiver::receiveHandle,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred ) );
} catch ( std::exception const & e ) {
std::cerr << "Exception in socket.async_receive_from():\n" << e.what() << '\n';
return false;
}
return true;
}
private:
void receiveHandle( boost::system::error_code const & ec, size_t bytes ) {
if ( ec ) {
if ( ec != boost::asio::error::operation_aborted ) {
std::cerr << "Error in receiveHandle():\n" << "Code: " << ec << ": " << ec.message() << '\n';
return;
} else {
std::cerr << "operation aborted\n";
return;
}
} else {
std::cout << "ICMP packet received\n";
}
startReceiving();
}
boost::asio::ip::icmp::socket m_sock;
boost::asio::ip::icmp::endpoint m_localEP;
boost::asio::ip::icmp::endpoint m_remoteEP;
boost::array< char, 2048 > m_receiveBuffer;
};
int main() {
try {
boost::asio::io_service IOS;
ICMPReceiver receiver( IOS );
receiver.open();
receiver.bind();
receiver.startReceiving();
IOS.run();
return 0;
} catch ( std::exception const & e ) {
std::cerr << "Unhandled exception: " << e.what() << '\n';
return 1;
}
}

Related

Boost ASIO performing async write/read/write handshake with a timer

I have an application where I need to connect to a socket, send a handshake message (send command1, get response, send command2), and then receive data. It is set to expire after a timeout, stop the io_service, and then attempt to reconnect. There is no error message when I do my first async_write but the following async_read waits until the timer expires, and then reconnects in an infinite loop.
My code looks like:
#include <boost/asio.hpp>
#include <boost/bind/bind.hpp>
#include <iostream>
#include <string>
#include <memory>
#include <boost/date_time/posix_time/posix_time.hpp>
using namespace std;
using boost::asio::ip::tcp;
static shared_ptr<boost::asio::io_service> _ios;
static shared_ptr<boost::asio::deadline_timer> timer;
static shared_ptr<boost::asio::ip::tcp::socket> tcp_sock;
static shared_ptr<tcp::resolver> _resolver;
static boost::asio::ip::tcp::resolver::results_type eps;
string buffer(1024,0);
void handle_read(const boost::system::error_code& ec, size_t bytes)
{
if (ec)
{
cout << "error: " << ec.message() << endl;
_ios->stop();
return;
}
// got first response, send off reply
if (buffer == "response")
{
boost::asio::async_write(*tcp_sock, boost::asio::buffer("command2",7),
[](auto ec, auto bytes)
{
if (ec)
{
cout << "write error: " << ec.message() << endl;
_ios->stop();
return;
}
});
}
else
{
// parse incoming data
}
// attempt next read
timer->expires_from_now(boost::posix_time::seconds(10));
boost::asio::async_read(*tcp_sock, boost::asio::buffer(buffer,buffer.size()), handle_read);
}
void get_response()
{
timer->expires_from_now(boost::posix_time::seconds(10));
boost::asio::async_read(*tcp_sock, boost::asio::buffer(buffer,buffer.size()), handle_read);
}
void on_connected(const boost::system::error_code& ec, tcp::endpoint)
{
if (!tcp_sock->is_open())
{
cout << "socket is not open" << endl;
_ios->stop();
}
else if (ec)
{
cout << "error: " << ec.message() << endl;
_ios->stop();
return;
}
else
{
cout << "connected" << endl;
// do handshake (no errors?)
boost::asio::async_write(*tcp_sock, boost::asio::buffer("command1",7),
[](auto ec, auto bytes)
{
if (ec)
{
cout << "write error: " << ec.message() << endl;
_ios->stop();
return;
}
get_response();
});
}
}
void check_timer()
{
if (timer->expires_at() <= boost::asio::deadline_timer::traits_type::now())
{
tcp_sock->close();
timer->expires_at(boost::posix_time::pos_infin);
}
timer->async_wait(boost::bind(check_deadline));
}
void init(string ip, string port)
{
// set/reset data and connect
_resolver.reset(new tcp::resolver(*_ios));
eps = _resolver->resolve(ip, port);
timer.reset(new boost::asio::deadline_timer(*_ios));
tcp_sock.reset(new boost::asio::ip::tcp::socket(*_ios));
timer->expires_from_now(boost::posix_time::seconds(5));
// start async connect
boost::asio::async_connect(*tcp_sock, eps, on_connected);
timer->async_wait(boost::bind(check_timer));
}
int main(int argc, char** argv)
{
while (1)
{
// start new io context
_ios.reset(new boost::asio::io_service);
init(argv[1],argv[2]);
_ios->run();
cout << "try reconnect" << endl;
}
return 0;
}
Why would I be timing out? When I do a netcat and follow the same procedure things look ok. I get no errors from the async_write indicating that there are any errors and I am making sure to not call the async_read for the response until I am in the write handler.
Others have been spot on. You use "blanket" read, which means it only completes at error (like EOF) or when the buffer is full (docs)
Besides your code is over-complicated (excess dynamic allocation, manual new, globals, etc).
The following simplified/cleaned up version still exhibits your problem: http://coliru.stacked-crooked.com/a/8f5d0820b3cee186
Since it looks like you just want to limit over-all time of the request, I'd suggest dropping the timer and just limit the time to run the io_context.
Also showing how to use '\n' for message delimiter and avoid manually managing dynamic buffers:
Live On Coliru
#include <boost/asio.hpp>
#include <iomanip>
#include <iostream>
#include <memory>
#include <string>
namespace asio = boost::asio;
using asio::ip::tcp;
using boost::system::error_code;
using namespace std::literals;
struct Client {
#define HANDLE(memfun) std::bind(&Client::memfun, this, std::placeholders::_1, std::placeholders::_2)
Client(std::string const& ip, std::string const& port) {
async_connect(_sock, tcp::resolver{_ios}.resolve(ip, port), HANDLE(on_connected));
}
void run() { _ios.run_for(10s); }
private:
asio::io_service _ios;
asio::ip::tcp::socket _sock{_ios};
std::string _buffer;
void on_connected(error_code ec, tcp::endpoint) {
std::cout << "on_connected: " << ec.message() << std::endl;
if (ec)
return;
async_write(_sock, asio::buffer("command1\n"sv), [this](error_code ec, size_t) {
std::cout << "write: " << ec.message() << std::endl;
if (!ec)
get_response();
});
}
void get_response() {
async_read_until(_sock, asio::dynamic_buffer(_buffer /*, 1024*/), "\n", HANDLE(on_read));
}
void on_read(error_code ec, size_t bytes) {
std::cout << "handle_read: " << ec.message() << " " << bytes << std::endl;
if (ec)
return;
auto cmd = _buffer.substr(0, bytes);
_buffer.erase(0, bytes);
// got first response, send off reply
std::cout << "Handling command " << quoted(cmd) << std::endl;
if (cmd == "response\n") {
async_write(_sock, asio::buffer("command2\n"sv), [](error_code ec, size_t) {
std::cout << "write2: " << ec.message() << std::endl;
});
} else {
// TODO parse cmd
}
get_response(); // attempt next read
}
};
int main(int argc, char** argv) {
assert(argc == 3);
while (1) {
Client(argv[1], argv[2]).run();
std::this_thread::sleep_for(1s); // for demo on COLIRU
std::cout << "try reconnect" << std::endl;
}
}
With output live on coliru:
on_connected: Connection refused
try reconnect
on_connected: Success
write: Success
command1
handle_read: Success 4
Handling command "one
"
handle_read: Success 9
Handling command "response
"
write2: Success
command2
handle_read: Success 6
Handling command "three
"
handle_read: End of file 0
try reconnect
on_connected: Success
write: Success
command1
Local interactive demo:
Sidenote: as long as resolve() isn't happening asynchronously it will not be subject to the timeouts.

Boost Buffer trouble String

I'm having troubles wiith socket and packets... Let me explain it
When a client send "Se|Bla|Blu", the output is "Re|Bla|Blu", like I want, but the problem is, that in the consol server, the output is "DataAsString = Se|Bla|BluouhWe are here now Noooo".
So we can see that it doesn't printthe "I am here" in writeAsync(), so we can assume that there is an error, and this is what I want to fix, the pop-up means :
Debug Assertion Failed!
Program: C:\Windows\system32\MSVCP120D.dll File: c:\program files
(x86)\microsoft visual studio 12.0\vc\include\xstring Line: 79
Expression: string iterator not deferencable
For information on how your program can cause an assertion failure,
see the visual C++ documentation on asserts.
(Press Retry to debug the application) [Abandon] [Retry] [ignore]
I tried to debug it, but no way, I don't know where the problem comes from. Any idea ?
Maybe the buffer doesn't allow string ? Maybe I have to reinitialize dataAsString and m_dataToRead to "" just after ?
Thanks in advance, see you.
Edit : The complete :
#include <boost/asio.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string.hpp>
#include <cstdlib>
#include <string>
#include <vector>
#include <iostream>
#include <memory>
#include <utility>
#include "../configuration/constantes.h"
class client : public std::enable_shared_from_this<client>
{
public:
client(boost::asio::ip::tcp::socket socket) : m_socket(std::move(socket)){}
void start()
{
readAsync();
}
private:
void readAsync()
{
auto self(shared_from_this());
m_socket.async_read_some(boost::asio::buffer(m_dataToRead, 512), [this, self](boost::system::error_code error, std::size_t length)
{
if (!error)
{
packetsTreating(m_dataToRead, length);
}
start();
});
}
void writeAsync(std::string m_dataToSend, size_t length)
{
auto self(shared_from_this());
boost::asio::async_write(m_socket, boost::asio::buffer(m_dataToSend, length), [this, self](boost::system::error_code error, std::size_t)
{
if (!error)
{
std::cout << "I am here";
start();
}
});
}
void speak(std::string channel, std::string object)
{
std::cout << "Bouh";
packetLength = 2 + sizeof(canal) + sizeof(objet);
std::cout << "We are here now";
writeAsync(("Re|" + channel + "|" + object), packetLength);
std::cout << "Noooo";
}
void logIn(std::string id, std::string wp)
{
}
void logOut(std::string whatDC)
{
}
void packetsTreating(char* data, size_t length)
{
std::string dataAsString;
dataAsString.assign(data, length);
std::cout << "DataAsString = " << dataAsString;
std::vector<std::string> fields;
boost::split(fields, dataAsString, boost::is_any_of("|"));
if (fields[0] == "Co" && fields.size() == 3)
logIn(fields[1], fields[2]);
else if (fields[0] == "Dc" && fields.size() == 2)
logOut(fields[1]);
else if (fields[0] == "Se" && fields.size() == 3)
speak(fields[1], fields[2]);
else
std::cout << "Unknown command." << std::endl;
}
size_t packetLength = 0;
boost::asio::ip::tcp::socket m_socket;
char m_dataToRead[512];
};
class server
{
public:
server(boost::asio::io_service& ios, short port) : m_acceptor(ios, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)), m_socket(ios)
{
acceptConnection();
}
private:
void acceptConnection()
{
m_acceptor.async_accept(m_socket, [this](boost::system::error_code error)
{
if (!error && connectedPerso <= maxConnec) // maxConnec is in constantes.h, = 250
{
connectedPerso++; // Btw, do you know how to -- it when a client disconnect ?
std::cout << "Connection, there is " << connectedPerso << " clients." << std::endl;
std::make_shared<client>(std::move(m_socket))->start();
}
acceptConnection();
});
}
unsigned short connectedPerso = 0;
boost::asio::ip::tcp::acceptor m_acceptor;
boost::asio::ip::tcp::socket m_socket;
};
void main()
{
try
{
std::cout << "TCP open on port " << port << ". maxConnec is " << maxConnec << "." << std::endl;
boost::asio::io_service iosConnector;
serveur serveur(iosConnector, port); // port = 2013
iosConnector.run();
}
catch (std::exception& e)
{
std::cerr << "Exception : " << e.what() << "\n";
}
}
There is a logic error. You are calling start() from both the sync write handler and the sync read handler. Since start() calls async_read on the socket, you will end up having 2 active async reads on the socket after the first write. This is illegal.
answer supplied to demonstrate that the code you posted seemed to work on my system:
$ telnet localhost 2013
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
hello
hello
ccccc
Re|1|hello|
Se|channel|text
Re|channel|text
Se|channel|text
Re|channel|text
Se|channel|text
Re|channel|text
^]
telnet> close
Connection closed.
stdout:
Connection, there is 1 clients.
DataAsString = hello
Unknown command.
DataAsString = hello
Unknown command.
DataAsString = ccccc
Unknown command.
DataAsString = Re|1|hello|
Unknown command.
DataAsString = Se|channel|text
BouhWe are here nowNooooI am hereDataAsString = Se|channel|text
BouhWe are here nowNooooI am hereDataAsString = Se|channel|text

correctly terminate boost::asio::ip::tcp::accept (Linux)

I wonder how to implement a synchronous socket accept with boost which can be terminated.
To demonstrate my problem I slightly modified the synchonous tcp echo example.
Note: the provided code seems to be working on Windows platforms but i'm having problems on a Linux machine.
Let's say the server receives a quit message and now wants to terminate an endless loop which accepts new connections.
Most tutorials etc. recommend you to run acceptor->close() in this case. But as
this post states, the results might be undefined if close() is called from another thread.
If you do so, accept() won't terminate this time but when another client tries to connect it returnes an error (on Linux!)
So my question again: how do I correctly terminate a server which is based on boost::asio which continuously synchronously accepts connections?
Here the code:
#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/smart_ptr.hpp>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
using boost::asio::ip::tcp;
void session(boost::shared_ptr<tcp::socket> sock, tcp::acceptor *acceptor )
{
try {
for (;;) {
char data[ 1024 ];
boost::system::error_code error;
size_t length = sock->read_some(boost::asio::buffer(data), error);
if (error == boost::asio::error::eof) { break; }
else if (error) { throw boost::system::system_error(error); }
if( std::string("quit") == data ) { // TRY TO CANCEL THE ACCEPT
acceptor->close();
break;
}
boost::asio::write(*sock, boost::asio::buffer(data, length));
}
}
catch (std::exception& e) { std::cerr << "exception in thread: " << e.what() << "\n"; }
}
void server(boost::asio::io_service& io_service, short port)
{
tcp::acceptor a( io_service, tcp::endpoint(tcp::v4(), port) );
for (;;) {
boost::shared_ptr<tcp::socket> sock(new tcp::socket(io_service));
boost::system::error_code error;
a.accept( *sock, error );
if( !error ) {
boost::thread t( boost::bind( session, sock, &a ) );
}
else {
std::cout << "acceptor canceled "<< error << std::endl;
break;
}
}
}
int main(int argc, char* argv[])
{
try{
// ..check args here...
boost::asio::io_service io_service;
server(io_service, std::atoi(argv[1]));
}
catch (std::exception& e) {std::cerr << "Exception: " << e.what() << "\n";}
return 0;
}

c++, boost: how to fill buffer and transfer (image) file data over network?

I'm working on an application where I need to send a "filename", "filesize" and the filedata over the network. I created a server using boost which, for now, reads in the filesize and name.
I'm wondering how I can fill a buffer with the file data (if necessary) and how to transfer it to the server.
This is what I've got now:
#ifndef OFXFILETRANSFERSENDH
#define OFXFILETRANSFERSENDH
#undef check // necessary to get Boost running on Mac
#include <vector>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include "ofxFileTransferConnection.h"
using boost::asio::ip::tcp;
class ofxFileTransferSend : public boost::enable_shared_from_this<connection> {
public:
typedef boost::shared_ptr<ofxFileTransferSend> pointer;
static pointer create(
boost::asio::io_service& rIOService
,std::string sServer
,const char* sPort)
{
return pointer(new ofxFileTransferSend(rIOService,sServer, sPort));
}
private:
//--------------------------------------------------------------
ofxFileTransferSend(
boost::asio::io_service &rIOService
,const std::string sServer
,const char* sPort
)
:port(sPort)
,socket_(rIOService)
,resolver_(rIOService)
{
tcp::resolver::query query(sServer, sPort);
resolver_.async_resolve(
query
,boost::bind(
&ofxFileTransferSend::handleResolve
,this
,boost::asio::placeholders::error
,boost::asio::placeholders::iterator
)
);
}
//--------------------------------------------------------------
void handleResolve(
const boost::system::error_code &rError
,tcp::resolver::iterator oEndPointIterator
)
{
if (!rError) {
tcp::endpoint end_point = *oEndPointIterator;
socket_.async_connect(
end_point
,boost::bind(
&ofxFileTransferSend::handleConnect
,this
,boost::asio::placeholders::error
,++oEndPointIterator
)
);
}
else {
std::cout << "Error while resolving server: " << std::endl;
}
}
//--------------------------------------------------------------
void handleConnect(
const boost::system::error_code &rError
,tcp::resolver::iterator rOEndPointIterator
)
{
if(!rError) {
std::cout << "Connected to remote server!" << std::endl;
std::size_t size = 1235;
std::ostream send_data_stream(&send_data);
send_data_stream << "filename.jpg" << "\r\n";
send_data_stream << size << "\r\n";
boost::asio::async_write(
socket_
,send_data
,boost::bind(
&ofxFileTransferSend::handleSendFileInfo
,this
,boost::asio::placeholders::error
)
);
}
else {
// #todo on failure retry!
std::cout << "Error connecting to ofxFileTransferServer:" << rError.message()<< std::endl;
}
}
//--------------------------------------------------------------
void handleSendFileInfo(
const boost::system::error_code &rError
)
{
if(!rError) {
cout << "okay nice, send file data done!\n";
}
else {
std::cout << "Error sending file info: " << rError.message() << std::endl;
}
}
tcp::resolver resolver_;
tcp::socket socket_;
boost::asio::streambuf send_data;
const char* port;
};
#endif
How about just dumping the file on the wire? That's how HTTP does it. In fact, I see you are using almost the same protocol as HTTP. Send all metadata as clear text first (name, size, etc), put in an empty line as a terminator for the metadata (\r\n) and now all you need is to dump the file itself:
void handleSendFileInfo(
const boost::system::error_code &rError
)
{
if(!rError) {
std::ofstream fileData(fileName);
boost::asio::async_write(
socket_
,fileData
,boost::bind(
&ofxFileTransferSend::handleSendFileData
,this
,boost::asio::placeholders::error
)
);
}
else {
std::cout << "Error sending file info: " << rError.message() << std::endl;
}
}

Setting TTL on outgoing ICMP packets?

I have been trying to set the TTL on ICMP packets using the
boost::asio::ip::unicast::hops option (using Boost 1.43) and then reading it out with get_option.
get_option gets 1 regardless what I use in set_option. And when inspecting the
packets sent using wireshark, the TTL is 128. Am I missing something here?
Should I use another option to set the TTL? Is it at all possible through Asio?
Regards,
Peter
Update 2010-08-01 17:37 UTC: Here is the code I am using:
#include <sstream>
#include <stdexcept>
#include <boost/asio.hpp>
class MyClass: public boost::noncopyable
{
public:
MyClass(const char* host):
io(),
resolver(io),
query( boost::asio::ip::icmp::v4(), host, "" ),
socket(io, boost::asio::ip::icmp::v4())
{
destination = *resolver.resolve(query);
}
~MyClass()
{
socket.close();
}
void run()
{
const int ttl = 2;
// set TTL ?
const boost::asio::ip::unicast::hops option( ttl );
socket.set_option(option);
boost::asio::ip::unicast::hops op;
socket.get_option(op);
if( op.value() != ttl )
{
std::ostringstream o;
o << "TTL not set properly. Should be " << ttl << " but was set"
" to " << op.value() << '.';
throw std::runtime_error( o.str() );
}
}
private:
boost::asio::io_service io;
boost::asio::ip::icmp::resolver resolver;
boost::asio::ip::icmp::resolver::query query;
boost::asio::ip::icmp::socket socket ;
boost::asio::ip::icmp::endpoint destination;
};
#include <iostream>
int main( int argc, char** argv)
{
try
{
if( argc != 2 )
{
throw std::invalid_argument("Missing argument. First argument = host");
}
MyClass T( argv[1] );
T.run();
}
catch( const std::exception& e )
{
std::cerr << "Exception: " << e.what() << '\n';
}
}
From this I get:
"Exception: TTL not set properly. Should be 2 but was set to 1."
Linux platform? Based on the documentation you appear to be doing it correctly.