/* 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_; } */