/*
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_);
}
class socket_wraper
{
public:
socket_wraper(boost::asio::ssl::stream *s) : is_ssl(true)
{
socket_ssl_ = s;
}
socket_wraper(tcp::socket *s) : is_ssl(false)
{
socket_ = s;
}
void operator=(boost::asio::ssl::stream *s)
{
socket_ssl_ = s;
is_ssl = true;
}
void operator=(tcp::socket *s)
{
socket_ = s;
is_ssl = false;
}
boost::asio::ip::tcp::socket& get_socket()
{
return *socket_;
}
boost::asio::ssl::stream& get_ssl_socket()
{
return *socket_ssl_;
}
template std::size_t read_some(const MutableBufferSequence & buffers)
{
if(is_ssl)
return socket_ssl_->read_some(buffers);
else
return socket_->read_some(buffers);
}
template std::size_t read_some(const MutableBufferSequence & buffers, boost::system::error_code & ec)
{
if(is_ssl)
return socket_ssl_->read_some(buffers, ec);
else
return socket_->read_some(buffers, ec);
}
template void async_read_some(const MutableBufferSequence & buffers, ReadHandler handler)
{
if(is_ssl)
socket_ssl_->async_read_some(buffers, handler);
else
socket_->async_read_some(buffers, handler);
}
template void async_write_some(const ConstBufferSequence & buffers, WriteHandler handler)
{
if(is_ssl)
socket_ssl_->async_write_some(buffers, handler);
else
socket_->async_write_some(buffers, handler);
}
~socket_wraper()
{
if(is_ssl)
delete socket_ssl_;
else
delete socket_;
}
private:
bool is_ssl;
tcp::socket *socket_;
boost::asio::ssl::stream *socket_ssl_;
};
client_session::client_session(QObject *parent, boost::asio::io_service *io_service) : QObject(parent), io_service_(*io_service)
{
}
/*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 socket_wraper(new tcp::socket(io_service_));
boost::asio::async_connect(socket_->get_socket(), 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_client);
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);
auto 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 ));
socket_ = new socket_wraper(socket_ssl_);
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_->get_ssl_socket().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"< ptr = pack_msg(&msg, &size);
boost::asio::async_write(*socket_, 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();
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_;
} */