boost::asio async_accept Refuse a connection - c++

My application have an asio server socket that must accept connections from a defined List of IPs.
This filter must be done by the application, (not by the system), because it can change at any time (i must be able to update this list at any time)
The client must receive an acces_denied error.
I suppose when the handle_accept callback is called, SYN/ACK has already be sent, so don't want to accept then close brutally when i detect the connected ip est not allowed. I don't manage the client behavior, maybe it doesn't act the same when the connection is refused and just closed by peer, so i want to do everything clean.
(but it's what im soing for the moment)
Do you know how i can do that???
My access list is a container of std::strings (but i can convert it to a countainer of something else....)
Thank you very much

The async_accept method has an overload to obtain the peer endpoint. You can compare that value inside your async_accept handler. If it does not match an entry in your container, let the socket go out of scope. Otherwise, handle it as required by your appliation.

I don't know the details of your app, but this is how I'd do it.
In the accept handler/lambda
void onAccept(shared_ptr<connection> c, error_code ec)
{
if (ec) { /*... */ }
if (isOnBlackList(c->endpoint_))
{
c->socket_.async_write( /* a refusal message */,
[c](error_code, n)
{
c->socket_.shutdown();
// c fizzles out of all contexts...
});
}
else
{
// successful connection execution path
}
}

Related

How QTcpSocket client can understand that it is in the queue? (and does not go to connected state)

I am developing a server and client project with Qt. I want to print the connection status for the users. For this propose I use state() like:
socketState = mySocket.state();
if (socketState == 3) {
Print("we have connected");
}
However, it does not work when the server queues new connections. To make it clear, my client state is 3 even if the server has paused accepting new connections:
//server side:
myServer->pauseAccepting();
//client side:
connectToHost()
socketState = mySocket.state();
Now the socketState is 3 instead of 0 or a special number for queue state.
To sum it up, I want to know how to inform the client that it is in the queue? Is there anything like state() that has a return value for queue state?
Finally I could find the answer.
The refused client that goes to the queue (by OS) is not different with other clients. Therefore, we can make a special socket for it (which means it is NOT in queue anymore) and start communication (inform and close).
for example:
QTcpSocket clientSocket
QTcpSocket queueSocket
In my project, the server first send a message to the queue client.
The message is:
"We can NOT accept a new client because we can NOT handle more than one". So, it knows what is the problem.
Then, the server closes the queue client socket.
We SHOULD close it because we do NOT want to handle lots of clients.
However, the main point is that we can work with clients in the queue and decide how to deal with them . I prefer to accept them just to inform and close them.
if (we have NOT a client) {
work with clientSocket
}
else {
queueSocket.wirte("We can NOT accept a new client because we can NOT handle more than one")
close queueSocket
}
I hope it helps those who want to inform rejected clients.

How to properly use QWebSocket::sendBinaryMessage() method from a C++ client?

There are C++ Qt client & server. Following code works fine and the connection happens between the client and the server:
QWebSocket webSocket; // defined somewhere
...
QUrl url;
url.setScheme("ws"); // SSL encryption disabled
url.setHost(serverName); // "127.0.0.1" (can be "www.abc.com" too)
url.setPort(portNumber); // 2000
webSocket.open(url); // connects with the server properly
PRINT(url.toString()); // output: "ws://127.0.0.1:2000"
While sending the binary data, the function returns 0 instead of the number of bytes:
// though the message.size() is 80 bytes; the method returns 0
webSocket.sendBinaryMessage(QByteArray(message.data(), message.size()));
Note that, the QWebSocketServer works as expected.
We also have a Javascript client. That connects & sends the binary message properly. The only addition in that client is below:
webSocketJS.binaryType = "arraybuffer"; // <--- Javascript code
But such provision is not found in QWebSocket or I may have missed it.
Question: How to correctly send the binary data over the web connection?
For those interested, the server [pseudo] code is like below:
auto pWebSocket = WebServer.nextPendingConnection();
QObject::connect(pWebSocket, &QWebSocket::binaryMessageReceived,
[&] (const QByteArray& message) { DataRead(message, rManager); }); // This slot is not called as of now
It seems that there is no mention of how the QWebSocket::connected() signal is treated.
Due to internet delay and initial handshakes, the WebSocketServer may take some time to establish a connection. Ideally the binary/text message should be sent only after the connected() is received.
Before making a connection using webSocket.open(url), you should be handling this signal:
... // same code
QObject::connect(&webSocket, &QWebSocket::connected,
[&] ()
{
webSocket.sendBinaryMessage(QByteArray(message.data(), message.size()));
// ... set some internal state suggesting the established connection
}
webSocket.open(url);
Above is just a pseudo code to show that the first sendBinaryMessage() should be sent after the connect() signal. Ideally in real world code, you may want to set some state, which informs the client that the connection is established.
Similarly as mentioned in the comments, we should be checking for errors and disconnections as well.

Implementing a client connection function that waits for the server in capnproto

I'm trying to implement in capnproto something like boost's connect function which blocks and retries until the server is up, or basic_socket::async_connect that lets me implement a callback with which to try connecting once more.
For example, running this code:
auto ioContext = kj::setupAsyncIo();
auto address = ioContext.provider->getNetwork()
.parseAddress("localhost:7500").wait(ioContext.waitScope);
auto connection = address->connect().wait(ioContext.waitScope);
Would obviously throw an exception if the server is down.
So my questions are:
Is there a way to register a callback that will handle
connection/failure to connect?
Is there a built in mechanism in capnproto that already enables automatic
reconnection?
There isn't anything built-in for this, but you could implement it fairly easily like so:
kj::Promise<kj::AsyncIoStream> keepTryingConnect(kj::NetworkAddress& addr) {
return addr.connect().catch_(
[&addr](kj::Exception&& e) -> kj::Promise<kj::Own<kj::AsyncIoStream>> {
if (e.getType() == kj::Exception::Type::DISCONNECTED) {
// Try again.
return keepTryingConnect(addr);
} else {
// Propagate error.
return kj::mv(e);
}
});
}
Note that the DISCONNECTED exception type is a catch-all for any type of transient network error, and is explicitly intended for this kind of purpose.
As for reconnecting on disconnect: The system cannot do this automatically, because Cap'n Proto won't know whether it's safe to retry in-flight requests and won't know how to rebuild any capabilities that were present on the connection. What you need to do is find places in your application where it makes sense to catch the DISCONNECTED exception type and then retry, much like in the code above.

boost asio for sync server keeping TCP session open (with google proto buffers)

I currently have a very simple boost::asio server that sends a status update upon connecting (using google proto buffers):
try
{
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service,tcp::endpoint(tcp::v4(), 13));
for (;;)
{
tcp::socket socket(io_service);
acceptor.accept(socket);
...
std::stringstream message;
protoMsg.SerializeToOstream(&message);
boost::system::error_code ignored_error;
boost::asio::write(socket, boost::asio::buffer(message.str()), ignored_error);
}
}
catch (std::exception& e) { }
I would like to extend it to first read after accepting a new connection, check what request was received, and send different messages back depending on this message. I'd also like to keep the TCP connection open so the client doesn't have to re-connect, and would like to handle multiple clients (not many, maybe 2 or 3).
I had a look at a few examples on boost asio, namely the async time tcp server and the chat server, but both are a bit over my head tbh. I don't even understand whether I need an async server. I guess I could just do a read after acceptor.accept(socket), but I guess then I wouldn't keep on listening for further requests. And if I go into a loop I guess that would mean I could only handle one client. So I guess that means I have to go async? Is there a simpler example maybe that isn't 250 lines of code? Or do I just have to bite my way through those examples? Thanks
The examples you mention from the Boost.Asio documentation are actually pretty good to see how things work. You're right that at first it might look a bit difficult to understand, especially if you're new to these concepts. However, I would recommend that you start with the chat server example and get that built on your machine. This will allow you to closer look into things and start changing things in order to learn how it works. Let me guide you through a few things I find important to get started.
From your description what you want to do, it seems that the chat server gives you a good starting point as it already has similar pieces you need. Having the server asynchronous is what you want as you then quite easily can handle multiple clients with a single thread. Nothing too complicated from the start.
Simplified, asynchronous in this case means that your server works off a queue, taking a handler (task) and executes it. If there is nothing on the queue, it just waits for something to be put on the queue. In your case that means it could be a connect from a client, a new read of a message from a client or something like this. In order for this to work, each handler (the function handling the reaction to a particular event) needs to be set up.
Let me explain a bit using code from the chat server example.
In the server source file, you see the chat_server class which calls start_accept in the constructor. Here the accept handler gets set up.
void start_accept()
{
chat_session_ptr new_session(new chat_session(io_service_, room_)); // 1
acceptor_.async_accept(new_session->socket(), // 2
boost::bind(&chat_server::handle_accept, this, new_session, // 3
boost::asio::placeholders::error)); // 4
}
Line 1: A chat_session object is created which represents a session between one client and the server. A session is created for the accept (no client has connected yet).
Line 2: An asynchronous accept for the socket...
Line 3: ...bound to call chat_server::handle_accept when it happens. The session is passed along to be used by the first client which connects.
Now, if we look at the handle_accept we see that upon client connect, start is called for the session (this just starts stuff between the server and this client). Lastly a new accept is put outstanding in case other clients want to connect as well.
void handle_accept(chat_session_ptr session,
const boost::system::error_code& error)
{
if (!error)
{
session->start();
}
start_accept();
}
This is what you want to have as well. An outstanding accept for incoming connections. And if multiple clients can connect, there should always be one of these outstanding so the server can handle the accept.
How the server and the client(s) interact is all in the session and you could follow the same design and modify this to do what you want. You mention that the server needs to look at what is sent and do different things. Take a look at chat_session and the start function which was called by the server in handle_accept.
void start()
{
room_.join(shared_from_this());
boost::asio::async_read(socket_,
boost::asio::buffer(read_msg_.data(), chat_message::header_length),
boost::bind(
&chat_session::handle_read_header, shared_from_this(),
boost::asio::placeholders::error));
}
What is important here is the call to boost::asio::async_read. This is what you want too. This puts an outstanding read on the socket, so the server can read what the client sends. There is a handler (function) which is bound to this event chat_session::handle_read_header. This will be called whenever the server reads something on the socket. In this handler function you could start putting your specific code to determine what to do if a specific message is sent and so on.
What is important to know is that whenever calling these asynchronous boost::asio functions things will not happen within that call (i.e. the socket is not read if you call the function read). This is the asynchronous aspect. You just kind of register a handler for something and your code is called back when this happens. Hence, when this read is called it will immediately return and you're back in the handle_accept for the server (if you follow how things get called). And if you remember there we also call start_accept to set up another asynchronous accept. At this point you have two outstanding handlers waiting for either another client to connect or the first client to send something. Depending on what happens first, that specific handler will be called.
Also what is important to understand is that whenever something is run, it will run uninterrupted until everything it needs to do has been done. Other handlers have to wait even if there is are outstanding events which trigger them.
Finally, in order to run the server you'll need the io_service which is a central concept in Asio.
io_service.run();
This is one line you see in the main function. This just says that the thread (only one in the example) should run the io_service, which is the queue where handlers get enqueued when there is work to be done. When nothing, the io_service just waits (blocking the main thread there of course).
I hope this helps you get started with what you want to do. There is a lot of stuff you can do and things to learn. I find it a great piece of software! Good luck!
In case anyone else wants to do this, here is the minimum to get above going: (similar to the tutorials, but a bit shorter and a bit different)
class Session : public boost::enable_shared_from_this<Session>
{
tcp::socket socket;
char buf[1000];
public:
Session(boost::asio::io_service& io_service)
: socket(io_service) { }
tcp::socket& SocketRef() { return socket; }
void Read() {
boost::asio::async_read( socket,boost::asio::buffer(buf),boost::asio::transfer_at_least(1),boost::bind(&Session::Handle_Read,shared_from_this(),boost::asio::placeholders::error));
}
void Handle_Read(const boost::system::error_code& error) {
if (!error)
{
//read from buffer and handle requests
//if you want to write sth, you can do it sync. here: e.g. boost::asio::write(socket, ..., ignored_error);
Read();
}
}
};
typedef boost::shared_ptr<Session> SessionPtr;
class Server
{
boost::asio::io_service io_service;
tcp::acceptor acceptor;
public:
Server() : acceptor(io_service,tcp::endpoint(tcp::v4(), 13)) { }
~Server() { }
void operator()() { StartAccept(); io_service.run(); }
void StartAccept() {
SessionPtr session_ptr(new Session(io_service));
acceptor.async_accept(session_ptr->SocketRef(),boost::bind(&Server::HandleAccept,this,session_ptr,boost::asio::placeholders::error));
}
void HandleAccept(SessionPtr session,const boost::system::error_code& error) {
if (!error)
session->Read();
StartAccept();
}
};
From what I gathered through trial and error and reading: I kick it off in the operator()() so you can have it run in the background in an additional thread. You run one Server instance. To handle multiple clients, you need an extra class, I called this a session class. For asio to clean up dead sessions, you need a shared pointer as pointed out above. Otherwise the code should get you started.

boost asio: maintaining a list of connected clients

I'm looking for the best way to modify the Boost Asio HTTP Server 3 example to maintain a list of the currently connected clients.
If I modify server.hpp from the example as:
class server : private boost::noncopyable
{
public:
typedef std::vector< connection_ptr > ConnectionList;
// ...
ConnectionList::const_iterator GetClientList() const
{
return connection_list_.begin();
};
void handle_accept(const boost::system::error_code& e)
{
if (!e)
{
connection_list_.push_back( new_connection_ );
new_connection_->start();
// ...
}
}
private:
ConnectionList connection_list_;
};
Then I mess up the lifetime of the connection object such that it doesn't go out of scope and disconnect from the client because it still has a reference maintained in the ConnectionList.
If instead my ConnectionList is defined as typedef std::vector< boost::weak_ptr< connection > > ConnectionList; then I run the risk of the client disconnecting and nullifying its pointer while somebody is using it from GetClientList().
Anybody have a suggestion on a good & safe way to do this?
Thanks,
PaulH
HTTP is stateless. That means it's difficult to even define what "currently connected client" means, not to mention keeping track of which clients are at any given time. The only time there's really a "current client" is from the time a request is received to the time that request is serviced (often only a few milliseconds). A connection is not maintained even for the duration of downloading one page -- rather, each item on the page is requested and sent separately.
The typical method for handling this is to use a fairly simple timeout -- a client is considered "connected" for some arbitrary length of time (a few minutes) after they send in a request. A cookie of some sort is used to identify the client sending in a particular request.
The rest of what you're talking about is just a matter of making sure the collection you use to hold connection information is thread safe. You have one thread that adds connections, one thread that deletes them, and N threads that use the data currently in the list. The standard collections don't guarantee any thread safety, but there are others around that do.