/*
Copyright © 2015 Gluzskiy Alexandr (sss)
This file is part of Unknown Download Manager (UDM).
UDM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
UDM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with UDM. If not, see .
*/
#include "client_session.h"
#include
#include
#include
std::shared_ptr pack_data(const std::string &buf, int *size_)
{
BOOST_LOG_TRIVIAL(debug)<<__FILE__<<":"<<__LINE__<<"\t"<<__func__;
std::shared_ptr ptr = std::make_shared(new char[buf.length() + 4]);
int32_t size = buf.length();
*size_ = size + 4;
(*ptr)[3] = size & 0xff;
(*ptr)[2] = (size>>8) & 0xff;
(*ptr)[1] = (size>>16) & 0xff;
(*ptr)[0] = (size>>24) & 0xff;
char *fck = (*ptr)+4;
memcpy(fck, buf.data(), buf.length());
return ptr;
}
std::shared_ptr pack_msg(client_msg *msg, int *size_)
{
BOOST_LOG_TRIVIAL(debug)<<__FILE__<<":"<<__LINE__<<"\t"<<__func__;
BOOST_LOG_TRIVIAL(trace)<<"packing message:\n"<DebugString();
std::string msg_buf;
msg->SerializeToString(&msg_buf);
return pack_data(msg_buf, size_);
}
client_session::client_session(QObject *parent, boost::asio::io_service *io_service) : QObject(parent), io_service_(*io_service)
{
socket_ssl_ = nullptr;
ssl_enabled = false;
}
tcp::socket& client_session::socket()
{
return *socket_;
}
void client_session::client_connect(QString host, QString password, int port)
{
//TODO: settings for connection timeout
this->pasword = password;
if(!password.isEmpty())
{
unsigned char hash[64];
SHA512((unsigned char*)password.toUtf8().data(), password.length(), hash);
password_sha512 = (char*)hash;
}
char port_buf[6];
snprintf(port_buf, 5, "%d", port);
boost::asio::ip::tcp::resolver r(io_service_);
boost::asio::ip::tcp::resolver::query query(host.toUtf8().data(), port_buf);
boost::asio::ip::tcp::resolver::iterator it = r.resolve(query);
socket_ = new tcp::socket(io_service_);
if(!ssl_enabled)
boost::asio::async_connect(*socket_, it, boost::bind(&client_session::handle_connect, this, boost::asio::placeholders::error));
else
boost::asio::async_connect(socket_ssl_->lowest_layer(), it, boost::bind(&client_session::handle_connect, this, boost::asio::placeholders::error));
// socket_.async_connect(ep, boost::bind(&client_session::handle_connect, this, boost::asio::placeholders::error));
io_service_.run();
}
void client_session::client_connect_ssl(QString host, QString password, int port, QString ssl_ca, QString ssl_crt, QString ssl_key)
{
ssl_enabled = true;
this->pasword = password;
boost::asio::ssl::context ctx(boost::asio::ssl::context::tlsv12);
ctx.load_verify_file(ssl_ca.toUtf8().data());
ctx.use_certificate_file(ssl_crt.toUtf8().data(), boost::asio::ssl::context::pem);
ctx.use_private_key_file(ssl_key.toUtf8().data(), boost::asio::ssl::context::pem);
socket_ssl_ = new boost::asio::ssl::stream(io_service_, ctx);
socket_ssl_->set_verify_mode(boost::asio::ssl::verify_peer);
socket_ssl_->set_verify_callback(boost::bind(&client_session::verify_certificate, this, _1, _2 ));
char port_buf[6];
snprintf(port_buf, 5, "%d", port);
boost::asio::ip::tcp::resolver r(io_service_);
boost::asio::ip::tcp::resolver::query query(host.toUtf8().data(), port_buf);
boost::asio::ip::tcp::resolver::iterator it = r.resolve(query);
boost::asio::async_connect(socket_ssl_->lowest_layer(), it, boost::bind(&client_session::handle_connect, this, boost::asio::placeholders::error));
}
bool client_session::verify_certificate(bool preverified, boost::asio::ssl::verify_context& /*&ctx*/)
{
// The verify callback can be used to check whether the certificate that is
// being presented is valid for the peer. For example, RFC 2818 describes
// the steps involved in doing this for HTTPS. Consult the OpenSSL
// documentation for more details. Note that the callback is called once
// for each certificate in the certificate chain, starting from the root
// certificate authority.
// In this example we will simply print the certificate's subject name.
/* char subject_name[256];
X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
std::cout << "Verifying " << subject_name << "\n"; */
return preverified;
}
void client_session::handle_connect(const boost::system::error_code &e)
{
BOOST_LOG_TRIVIAL(debug)<<__FILE__<<":"<<__LINE__<<"\t"<read_some(boost::asio::buffer(buf, size), ec);
else
socket_ssl_->read_some(boost::asio::buffer(buf, size), ec);
if(ec)
{
}
//TODO: check for error
s.append(buf, size);
delete [] buf;
if(size != s.length())
{
delete this;
return;
}
//TODO:
server_msg msg;
if(msg.ParseFromString(s))
{
//parsed = true;
emit server_message_received(msg); //at least one more copy ...., i hope messages will be always small
//another way:
//make signal work with pointer to server_msg
//allocate msg via new server_msg and pass pointer to signal
//check if someone connected to signal and delete message if noone
//this will be limited to only one client connection because client will need to delete message manually
recv_data_ = new char[4];
if(!ssl_enabled)
boost::asio::async_read(*socket_, boost::asio::buffer(recv_data_, 4), boost::bind(&client_session::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
else
boost::asio::async_read(*socket_ssl_, boost::asio::buffer(recv_data_, 4), boost::bind(&client_session::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
// if(!parsed)
else
{
BOOST_LOG_TRIVIAL(error)<<"failed to parse client message";
delete this; //close connection
return;
}
}
else
{
delete this;
}
}
void client_session::send_message(client_msg &msg)
{
int size = 0;
std::shared_ptr ptr = pack_msg(&msg, &size);
if(!ssl_enabled)
boost::asio::async_write(*socket_, boost::asio::buffer(*ptr, size), boost::bind(&client_session::handle_write, this, boost::asio::placeholders::error));
else
boost::asio::async_write(*socket_ssl_, boost::asio::buffer(*ptr, size), boost::bind(&client_session::handle_write, this, boost::asio::placeholders::error));
}
void client_session::handle_write(const boost::system::error_code& error)
{
if(error)
{
//TODO: handle error
}
}
client_session::~client_session()
{
//TODO: correct thread termination, reimplement
io_service_.stop();
if(ssl_enabled)
delete socket_ssl_;
else
delete socket_;
boost::asio::io_service *s = &io_service_;
delete s;
//emit terminate_thread();
}
/*boost::asio::io_service &client_session::io_service()
{
return io_service_;
} */