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