From b90d6fb777bb81377fa6298707c6fdcc272b9398 Mon Sep 17 00:00:00 2001 From: Babushka Date: Thu, 5 Oct 2017 21:15:29 +0200 Subject: [PATCH] ssl cert support. kick/ban handler (unhandled kick/ban caused high cpu usage for some time period followed by io_service crash) make exceptions optional (exceprions causing lot's of problem if used more than one mumlib instance on external io_service) make log4cpp optionsl --- include/mumlib.hpp | 18 ++- include/mumlib/Audio.hpp | 13 +- include/mumlib/Callback.hpp | 4 +- include/mumlib/Transport.hpp | 40 ++++-- include/mumlib/VarInt.hpp | 4 +- src/Audio.cpp | 37 ++++- src/Callback.cpp | 61 +++++++- src/CryptState.cpp | 2 +- src/Transport.cpp | 264 ++++++++++++++++++++++++++++------- src/VarInt.cpp | 7 +- src/mumlib.cpp | 79 +++++++++-- 11 files changed, 433 insertions(+), 96 deletions(-) diff --git a/include/mumlib.hpp b/include/mumlib.hpp index 95afe02..e738ad1 100644 --- a/include/mumlib.hpp +++ b/include/mumlib.hpp @@ -3,8 +3,10 @@ #include "mumlib/Callback.hpp" #include +#include #include + #include #include @@ -15,14 +17,28 @@ namespace mumlib { using namespace std; using namespace boost::asio; +#ifdef MUMLIB_USE_EXCEPTIONS class MumlibException : public runtime_error { public: MumlibException(string message) : runtime_error(message) { } }; +#endif + struct MumlibConfigurationSSLOptions + { + std::string ssl_cert, ssl_key, ssl_rsa_key; + }; + struct MumlibConfigurationVersionOptions + { + string os_version = "Not set", os_build = "Not set", mumble_version = "Not set"; + int mumble_version_int = 0x010300; + + }; struct MumlibConfiguration { int opusEncoderBitrate = DEFAULT_OPUS_ENCODER_BITRATE; - // additional fields will be added in the future + MumlibConfigurationVersionOptions version; + MumlibConfigurationSSLOptions ssl; + }; struct _Mumlib_Private; diff --git a/include/mumlib/Audio.hpp b/include/mumlib/Audio.hpp index d98f41a..dcf75fc 100644 --- a/include/mumlib/Audio.hpp +++ b/include/mumlib/Audio.hpp @@ -1,21 +1,22 @@ #pragma once -#include "Transport.hpp" +#include -#include +#include #include namespace mumlib { constexpr int SAMPLE_RATE = 48000; - +#ifdef MUMLIB_USE_EXCEPTIONS class MumlibException; class AudioException : public MumlibException { public: AudioException(string message) : MumlibException(message) { } }; +#endif struct IncomingAudioPacket { AudioPacketType type; @@ -53,7 +54,9 @@ namespace mumlib { void resetEncoder(); private: - log4cpp::Category &logger; +#ifdef MUMLIB_USE_LOG4CPP + MUMLIB_USE_LOG4CPP::Category &logger; +#endif OpusDecoder *opusDecoder; OpusEncoder *opusEncoder; @@ -62,4 +65,4 @@ namespace mumlib { std::chrono::time_point lastEncodedAudioPacketTimestamp; }; -} \ No newline at end of file +} diff --git a/include/mumlib/Callback.hpp b/include/mumlib/Callback.hpp index a11b7b4..2cba39d 100644 --- a/include/mumlib/Callback.hpp +++ b/include/mumlib/Callback.hpp @@ -142,6 +142,7 @@ namespace mumlib { uint32_t version, uint32_t positional, uint32_t push_to_talk) { }; + virtual void disconnected () {}; }; @@ -285,8 +286,9 @@ namespace mumlib { uint32_t version, uint32_t positional, uint32_t push_to_talk) override; + virtual void disconnected() override; private: _BasicCallback_Private *impl; }; -} \ No newline at end of file +} diff --git a/include/mumlib/Transport.hpp b/include/mumlib/Transport.hpp index ccf94a7..7c41616 100644 --- a/include/mumlib/Transport.hpp +++ b/include/mumlib/Transport.hpp @@ -1,16 +1,15 @@ #pragma once -#include "mumlib/CryptState.hpp" -#include "mumlib/VarInt.hpp" -#include "enums.hpp" +#include +#include +#include #include -#include -#include #include #include - -#include +#ifdef MUMLIB_USE_LOG4CPP +#include +#endif #include #include @@ -27,18 +26,20 @@ namespace mumlib { typedef function ProcessControlMessageFunction; typedef function ProcessEncodedAudioPacketFunction; - +#ifdef MUMLIB_USE_EXCEPTIONS class TransportException : public MumlibException { public: TransportException(string message) : MumlibException(message) { } }; +#endif class Transport : boost::noncopyable { public: Transport(io_service &ioService, ProcessControlMessageFunction processControlMessageFunc, ProcessEncodedAudioPacketFunction processEncodedAudioPacketFunction, - bool noUdp = false); + MumlibConfiguration MumblConfig, + bool noUdp = true); ~Transport(); @@ -59,8 +60,15 @@ namespace mumlib { void sendEncodedAudioPacket(uint8_t *buffer, int length); + void set_callback(Callback *cb) + { + this->cb = cb; + } + private: - log4cpp::Category &logger; +#ifdef MUMLIB_USE_LOG4CPP + MUMLIB_USE_LOG4CPP::Category &logger; +#endif io_service &ioService; @@ -84,7 +92,7 @@ namespace mumlib { CryptState cryptState; ssl::context sslContext; - ssl::stream sslSocket; + ssl::stream *sslSocket; //must be created after context already configured uint8_t *sslIncomingBuffer; deadline_timer pingTimer; @@ -92,6 +100,13 @@ namespace mumlib { boost::pool<> asyncBufferPool; + MumlibConfiguration MumbleConfig; + //session related data + std::string name; + uint32_t session; + // + Callback *cb = nullptr; + void pingTimerTick(const boost::system::error_code &e); void sslConnectHandler(const boost::system::error_code &error); @@ -119,8 +134,9 @@ namespace mumlib { void sendUdpAsync(uint8_t *buff, int length); void sendUdpPing(); - +#ifdef MUMLIB_USE_EXCEPTIONS void throwTransportException(string message); +#endif void processAudioPacket(uint8_t *buff, int length); }; diff --git a/include/mumlib/VarInt.hpp b/include/mumlib/VarInt.hpp index 451c61e..760e25f 100644 --- a/include/mumlib/VarInt.hpp +++ b/include/mumlib/VarInt.hpp @@ -7,10 +7,12 @@ #include namespace mumlib { +#ifdef MUMLIB_USE_EXCEPTIONS class VarIntException : public MumlibException { public: VarIntException(std::string message) : MumlibException(message) { } }; +#endif class VarInt { public: @@ -31,4 +33,4 @@ namespace mumlib { int64_t parseVariant(uint8_t *buffer); }; -} \ No newline at end of file +} diff --git a/src/Audio.cpp b/src/Audio.cpp index 3ce7594..31b3682 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -1,25 +1,32 @@ -#include "mumlib/Audio.hpp" +#include "mumlib/include/mumlib/Audio.hpp" #include static boost::posix_time::seconds RESET_SEQUENCE_NUMBER_INTERVAL(5); - mumlib::Audio::Audio(int opusEncoderBitrate) - : logger(log4cpp::Category::getInstance("mumlib.Audio")), + : +#ifdef MUMLIB_USE_LOG4CPP + logger(MUMLIB_USE_LOG4CPP::Category::getInstance("mumlib.Audio")), +#endif opusDecoder(nullptr), opusEncoder(nullptr), - outgoingSequenceNumber(0) { + outgoingSequenceNumber(0) +{ int error; opusDecoder = opus_decoder_create(SAMPLE_RATE, 1, &error); if (error != OPUS_OK) { +#ifdef MUMLIB_USE_EXCEPTIONS throw AudioException((boost::format("failed to initialize OPUS decoder: %s") % opus_strerror(error)).str()); +#endif } opusEncoder = opus_encoder_create(SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP, &error); if (error != OPUS_OK) { +#ifdef MUMLIB_USE_EXCEPTIONS throw AudioException((boost::format("failed to initialize OPUS encoder: %s") % opus_strerror(error)).str()); +#endif } opus_encoder_ctl(opusEncoder, OPUS_SET_VBR(0)); @@ -42,8 +49,10 @@ mumlib::Audio::~Audio() { void mumlib::Audio::setOpusEncoderBitrate(int bitrate) { int error = opus_encoder_ctl(opusEncoder, OPUS_SET_BITRATE(bitrate)); if (error != OPUS_OK) { +#ifdef MUMLIB_USE_EXCEPTIONS throw AudioException((boost::format("failed to initialize transmission bitrate to %d B/s: %s") % bitrate % opus_strerror(error)).str()); +#endif } } @@ -51,7 +60,9 @@ int mumlib::Audio::getOpusEncoderBitrate() { opus_int32 bitrate; int error = opus_encoder_ctl(opusEncoder, OPUS_GET_BITRATE(&bitrate)); if (error != OPUS_OK) { +#ifdef MUMLIB_USE_EXCEPTIONS throw AudioException((boost::format("failed to read Opus bitrate: %s") % opus_strerror(error)).str()); +#endif } return bitrate; } @@ -71,8 +82,10 @@ std::pair mumlib::Audio::decodeOpusPayload(uint8_t *inputBuffer, opusDataLength = opusDataLength & 0x1fff; if (inputLength < opusDataLength + dataPointer) { +#ifdef MUMLIB_USE_EXCEPTIONS throw AudioException((boost::format("invalid Opus payload (%d B): header %d B, expected Opus data length %d B") % inputLength % dataPointer % opusDataLength).str()); +#endif } int outputSize = opus_decode(opusDecoder, @@ -83,12 +96,15 @@ std::pair mumlib::Audio::decodeOpusPayload(uint8_t *inputBuffer, 0); if (outputSize <= 0) { +#ifdef MUMLIB_USE_EXCEPTIONS throw AudioException((boost::format("failed to decode %d B of OPUS data: %s") % inputLength % opus_strerror(outputSize)).str()); +#endif } - +#ifdef MUMLIB_USE_LOG4CPP logger.debug("%d B of Opus data decoded to %d PCM samples, last packet: %d.", opusDataLength, outputSize, lastPacket); +#endif return std::make_pair(outputSize, lastPacket); } @@ -102,7 +118,9 @@ int mumlib::Audio::encodeAudioPacket(int target, int16_t *inputPcmBuffer, int in system_clock::now() - lastEncodedAudioPacketTimestamp).count(); if (lastAudioPacketSentInterval > RESET_SEQUENCE_NUMBER_INTERVAL.total_milliseconds() + 1000) { +#ifdef MUMLIB_USE_LOG4CPP logger.debug("Last audio packet was sent %d ms ago, resetting encoder.", lastAudioPacketSentInterval); +#endif resetEncoder(); } @@ -122,8 +140,10 @@ int mumlib::Audio::encodeAudioPacket(int target, int16_t *inputPcmBuffer, int in ); if (outputSize <= 0) { +#ifdef MUMLIB_USE_EXCEPTIONS throw AudioException((boost::format("failed to encode %d B of PCM data: %s") % inputLength % opus_strerror(outputSize)).str()); +#endif } auto outputSizeEnc = VarInt(outputSize).getEncoded(); @@ -145,7 +165,9 @@ void mumlib::Audio::resetEncoder() { int status = opus_encoder_ctl(opusEncoder, OPUS_RESET_STATE, nullptr); if (status != OPUS_OK) { +#ifdef MUMLIB_USE_EXCEPTIONS throw AudioException((boost::format("failed to reset encoder: %s") % opus_strerror(status)).str()); +#endif } outgoingSequenceNumber = 0; @@ -170,10 +192,12 @@ mumlib::IncomingAudioPacket mumlib::Audio::decodeIncomingAudioPacket(uint8_t *in incomingAudioPacket.audioPayloadLength = inputBufferLength - dataPointer; if (dataPointer >= inputBufferLength) { +#ifdef MUMLIB_USE_EXCEPTIONS throw AudioException((boost::format("invalid incoming audio packet (%d B): header %d B") % inputBufferLength % dataPointer).str()); +#endif } - +#ifdef MUMLIB_USE_LOG4CPP logger.debug( "Received %d B of audio packet, %d B header, %d B payload (target: %d, sessionID: %ld, seq num: %ld).", inputBufferLength, @@ -182,6 +206,7 @@ mumlib::IncomingAudioPacket mumlib::Audio::decodeIncomingAudioPacket(uint8_t *in incomingAudioPacket.target, incomingAudioPacket.sessionId, incomingAudioPacket.sequenceNumber); +#endif return incomingAudioPacket; } diff --git a/src/Callback.cpp b/src/Callback.cpp index ea8fb80..66a51e7 100644 --- a/src/Callback.cpp +++ b/src/Callback.cpp @@ -1,7 +1,9 @@ -#include "mumlib/Callback.hpp" +#include "mumlib/include/mumlib/Callback.hpp" #include -#include +#ifdef MUMLIB_USE_LOG4CPP +#include +#endif using namespace std; using namespace mumlib; @@ -9,9 +11,13 @@ using namespace mumlib; namespace mumlib { struct _BasicCallback_Private : boost::noncopyable { public: - _BasicCallback_Private() : logger(log4cpp::Category::getInstance("mumlib.BasicCallback")) { } - - log4cpp::Category &logger; +#ifdef MUMLIB_USE_LOG4CPP + _BasicCallback_Private() : + logger(MUMLIB_USE_LOG4CPP::Category::getInstance("mumlib.BasicCallback")) { } + MUMLIB_USE_LOG4CPP::Category &logger; +#else + _BasicCallback_Private() { } +#endif }; } @@ -31,8 +37,10 @@ void mumlib::BasicCallback::version( string release, string os, string os_version) { +#ifdef MUMLIB_USE_LOG4CPP impl->logger.debug("version: v%d.%d.%d. %s/%s/%s\n", major, minor, patch, release.c_str(), os.c_str(), os_version.c_str()); +#endif } void mumlib::BasicCallback::audio( @@ -41,8 +49,10 @@ void mumlib::BasicCallback::audio( int sequenceNumber, int16_t *pcmData, uint32_t pcm_data_size) { +#ifdef MUMLIB_USE_LOG4CPP impl->logger.debug("audio: %d bytes of raw PCM data, target: %d, session: %d, seq: %d.", pcm_data_size, target, sessionId, sequenceNumber); +#endif } void BasicCallback::unsupportedAudio( @@ -51,40 +61,54 @@ void BasicCallback::unsupportedAudio( int sequenceNumber, uint8_t *encoded_audio_data, uint32_t encoded_audio_data_size) { +#ifdef MUMLIB_USE_LOG4CPP impl->logger.debug("unsupportedAudio: received %d bytes of encoded data, target: %d, session: %d, seq: %d.", encoded_audio_data_size, target, sessionId, sequenceNumber); +#endif } void BasicCallback::serverSync(string welcome_text, int32_t session, int32_t max_bandwidth, int64_t permissions) { +#ifdef MUMLIB_USE_LOG4CPP impl->logger.debug("serverSync: text: %s, session: %d, max bandwidth: %d, permissions: %d", welcome_text.c_str(), session, max_bandwidth, permissions); +#endif } void BasicCallback::channelRemove(uint32_t channel_id) { +#ifdef MUMLIB_USE_LOG4CPP impl->logger.debug("channelRemove: %d", channel_id); +#endif } void BasicCallback::channelState(string name, int32_t channel_id, int32_t parent, string description, vector links, vector inks_add, vector links_remove, bool temporary, int32_t position) { +#ifdef MUMLIB_USE_LOG4CPP impl->logger.debug("channelState: %d: %s, %s", channel_id, name.c_str(), description.c_str()); +#endif } void BasicCallback::userRemove(uint32_t session, int32_t actor, string reason, bool ban) { +#ifdef MUMLIB_USE_LOG4CPP impl->logger.debug("userRemove: session: %d, actor: %d, reason: %s, ban: %d.", session, actor, reason.c_str(), ban); +#endif } void BasicCallback::userState(int32_t session, int32_t actor, string name, int32_t user_id, int32_t channel_id, int32_t mute, int32_t deaf, int32_t suppress, int32_t self_mute, int32_t self_deaf, string comment, int32_t priority_speaker, int32_t recording) { +#ifdef MUMLIB_USE_LOG4CPP impl->logger.debug("userState: %s: mute: %d, deaf: %d, suppress: %d, self mute: %d, self deaf: %d", name.c_str(), mute, deaf, suppress, self_mute, self_deaf); +#endif } void BasicCallback::banList(const uint8_t *ip_data, uint32_t ip_data_size, uint32_t mask, string name, string hash, string reason, string start, int32_t duration) { +#ifdef MUMLIB_USE_LOG4CPP impl->logger.debug("banList: %s, hash: %s, reason: %s", name.c_str(), hash.c_str(), reason.c_str()); +#endif } void BasicCallback::textMessage( @@ -93,43 +117,70 @@ void BasicCallback::textMessage( std::vector channel_id, std::vector tree_id, string message) { +#ifdef MUMLIB_USE_LOG4CPP impl->logger.debug("textMessage: %d: %s", actor, message.c_str()); +#endif } void BasicCallback::permissionDenied(int32_t permission, int32_t channel_id, int32_t session, string reason, int32_t deny_type, string name) { +#ifdef MUMLIB_USE_LOG4CPP impl->logger.debug("permissionDenied: %s %s", name.c_str(), reason.c_str()); +#endif } void BasicCallback::queryUsers(uint32_t n_ids, uint32_t *ids, uint32_t n_names, string *names) { +#ifdef MUMLIB_USE_LOG4CPP impl->logger.debug("queryUsers: %d users", n_names); //todo make it more high-level +#endif } void BasicCallback::contextActionModify(string action, string text, uint32_t m_context, uint32_t operation) { +#ifdef MUMLIB_USE_LOG4CPP impl->logger.debug("contextActionModify: "); +#endif } void BasicCallback::contextAction(int32_t session, int32_t channel_id, string action) { +#ifdef MUMLIB_USE_LOG4CPP impl->logger.debug("contextAction."); +#endif } void BasicCallback::userList(uint32_t user_id, string name, string last_seen, int32_t last_channel) { +#ifdef MUMLIB_USE_LOG4CPP impl->logger.debug("userList."); +#endif } void BasicCallback::permissionQuery(int32_t channel_id, uint32_t permissions, int32_t flush) { +#ifdef MUMLIB_USE_LOG4CPP impl->logger.debug("permissionQuery."); +#endif } void BasicCallback::codecVersion(int32_t alpha, int32_t beta, uint32_t prefer_alpha, int32_t opus) { +#ifdef MUMLIB_USE_LOG4CPP impl->logger.debug("codecVersion."); +#endif } void BasicCallback::serverConfig(uint32_t max_bandwidth, string welcome_text, uint32_t allow_html, uint32_t message_length, uint32_t image_message_length) { +#ifdef MUMLIB_USE_LOG4CPP impl->logger.debug("serverConfig: %s", welcome_text.c_str()); +#endif } void BasicCallback::suggestConfig(uint32_t version, uint32_t positional, uint32_t push_to_talk) { +#ifdef MUMLIB_USE_LOG4CPP impl->logger.debug("suggestConfig."); +#endif +} + +void BasicCallback::disconnected() +{ +#ifdef MUMLIB_USE_LOG4CPP + impl->logger.debug("Disconnected from server."); +#endif } diff --git a/src/CryptState.cpp b/src/CryptState.cpp index 9c5937a..c00cd85 100644 --- a/src/CryptState.cpp +++ b/src/CryptState.cpp @@ -38,7 +38,7 @@ */ -#include "mumlib/CryptState.hpp" +#include "mumlib/include/mumlib/CryptState.hpp" #include #include diff --git a/src/Transport.cpp b/src/Transport.cpp index f134512..0d649b4 100644 --- a/src/Transport.cpp +++ b/src/Transport.cpp @@ -1,4 +1,4 @@ -#include "mumlib/Transport.hpp" +#include "mumlib/include/mumlib/Transport.hpp" #include "Mumble.pb.h" @@ -6,12 +6,9 @@ using namespace std; -static boost::posix_time::seconds PING_INTERVAL(5); +static boost::posix_time::seconds PING_INTERVAL(40); + -const long CLIENT_VERSION = 0x01020A; -const string CLIENT_RELEASE("Mumlib"); -const string CLIENT_OS("OS Unknown"); -const string CLIENT_OS_VERSION("1"); static map rejectMessages = { {MumbleProto::Reject_RejectType_None, "no reason provided"}, @@ -25,31 +22,48 @@ static map rejectMessages = { {MumbleProto::Reject_RejectType_AuthenticatorFail, "authenticator fail"} }; -mumlib::Transport::Transport( - io_service &ioService, +mumlib::Transport::Transport(io_service &ioService, mumlib::ProcessControlMessageFunction processMessageFunc, - ProcessEncodedAudioPacketFunction processEncodedAudioPacketFunction, + ProcessEncodedAudioPacketFunction processEncodedAudioPacketFunction, MumlibConfiguration MumblConfig, bool noUdp) : - logger(log4cpp::Category::getInstance("mumlib.Transport")), +#ifdef MUMLIB_USE_LOG4CPP + logger(MUMLIB_USE_LOG4CPP::Category::getInstance("mumlib.Transport")), +#endif ioService(ioService), + sslContext(ssl::context::sslv23_client), processMessageFunction(processMessageFunc), processEncodedAudioPacketFunction(processEncodedAudioPacketFunction), noUdp(noUdp), state(ConnectionState::NOT_CONNECTED), udpSocket(ioService), - sslContext(ssl::context::sslv23), - sslSocket(ioService, sslContext), + sslSocket(nullptr), pingTimer(ioService, PING_INTERVAL), - asyncBufferPool(max(MAX_UDP_LENGTH, MAX_TCP_LENGTH)) { + asyncBufferPool(max(MAX_UDP_LENGTH, MAX_TCP_LENGTH)), + MumbleConfig(MumblConfig) +{ sslIncomingBuffer = new uint8_t[MAX_TCP_LENGTH]; - pingTimer.async_wait(boost::bind(&Transport::pingTimerTick, this, _1)); + boost::system::error_code ec; + sslContext.use_certificate(boost::asio::buffer((void *)MumbleConfig.ssl.ssl_cert.c_str(), MumbleConfig.ssl.ssl_cert.length()), boost::asio::ssl::context::pem, ec); + //TODO: handle error + sslContext.use_private_key(boost::asio::buffer((void *)MumbleConfig.ssl.ssl_key.c_str(), (size_t)MumbleConfig.ssl.ssl_key.length()), boost::asio::ssl::context::pem, ec); + //TODO: handle error + sslContext.use_rsa_private_key(boost::asio::buffer((void *)MumbleConfig.ssl.ssl_rsa_key.c_str(), (size_t)MumbleConfig.ssl.ssl_rsa_key.length()), boost::asio::ssl::context::pem, ec); + //TODO: handle error + sslContext.set_options(boost::asio::ssl::context::default_workarounds); + + sslSocket = new ssl::stream(ioService, sslContext); + + sslSocket->set_verify_mode(boost::asio::ssl::verify_none); + + } mumlib::Transport::~Transport() { disconnect(); delete[] sslIncomingBuffer; + delete sslSocket; } void mumlib::Transport::connect( @@ -59,18 +73,19 @@ void mumlib::Transport::connect( std::string password) { state = ConnectionState::IN_PROGRESS; + name = user; connectionParams = make_pair(host, port); credentials = make_pair(user, password); udpActive = false; - sslSocket.set_verify_mode(boost::asio::ssl::verify_peer); + //todo for now it accepts every certificate, move it to callback - sslSocket.set_verify_callback([](bool preverified, boost::asio::ssl::verify_context &ctx) { +/* sslSocket.set_verify_callback([](bool preverified, boost::asio::ssl::verify_context &ctx) { return true; - }); + }); */ try { if (not noUdp) { @@ -85,35 +100,55 @@ void mumlib::Transport::connect( ip::tcp::resolver resolverTcp(ioService); ip::tcp::resolver::query queryTcp(host, to_string(port)); - async_connect(sslSocket.lowest_layer(), resolverTcp.resolve(queryTcp), + async_connect(sslSocket->lowest_layer(), resolverTcp.resolve(queryTcp), bind(&Transport::sslConnectHandler, this, boost::asio::placeholders::error)); } catch (runtime_error &exp) { +#ifdef MUMLIB_USE_EXCEPTIONS throwTransportException(string("failed to establish connection: ") + exp.what()); +#endif + disconnect(); } } void mumlib::Transport::disconnect() { + pingTimer.cancel(); if (state != ConnectionState::NOT_CONNECTED) { boost::system::error_code errorCode; // todo perform different operations for each ConnectionState - sslSocket.shutdown(errorCode); + sslSocket->shutdown(errorCode); +#ifdef MUMLIB_USE_LOG4CPP if (errorCode) { logger.warn("SSL socket shutdown returned an error: %s.", errorCode.message().c_str()); } +#endif + + sslSocket->lowest_layer().shutdown(tcp::socket::shutdown_both, errorCode); +#ifdef MUMLIB_USE_LOG4CPP + if (errorCode) { + logger.warn("SSL socket lowest layer shutdown returned an error: %s.", errorCode.message().c_str()); + } +#endif - sslSocket.lowest_layer().shutdown(tcp::socket::shutdown_both, errorCode); + sslSocket->lowest_layer().close(errorCode); + +#ifdef MUMLIB_USE_LOG4CPP if (errorCode) { - logger.warn("SSL socket lowest layer shutdown returned an error: %s.", errorCode.message().c_str()); + logger.warn("SSL socket lowest layer close returned an error: %s.", errorCode.message().c_str()); } +#endif + - udpSocket.close(errorCode); + if (not noUdp) { + udpSocket.close(errorCode); + } +#ifdef MUMLIB_USE_LOG4CPP if (errorCode) { logger.warn("UDP socket close returned error: %s.", errorCode.message().c_str()); } - +#endif state = ConnectionState::NOT_CONNECTED; } } @@ -122,12 +157,13 @@ void mumlib::Transport::disconnect() { void mumlib::Transport::sendVersion() { MumbleProto::Version version; - version.set_version(CLIENT_VERSION); - version.set_os(CLIENT_OS); - version.set_release(CLIENT_RELEASE); - version.set_os_version(CLIENT_OS_VERSION); - + version.set_version(MumbleConfig.version.mumble_version_int); + version.set_release(MumbleConfig.version.mumble_version); + version.set_os(MumbleConfig.version.os_version); + version.set_os_version(MumbleConfig.version.os_build); +#ifdef MUMLIB_USE_LOG4CPP logger.info("Sending version information."); +#endif sendControlMessagePrivate(MessageType::VERSION, version); } @@ -142,8 +178,9 @@ void mumlib::Transport::sendAuthentication() { authenticate.clear_celt_versions(); authenticate.clear_tokens(); authenticate.set_opus(true); - +#ifdef MUMLIB_USE_LOG4CPP logger.info("Sending authententication."); +#endif sendControlMessagePrivate(MessageType::AUTHENTICATE, authenticate); } @@ -151,8 +188,9 @@ void mumlib::Transport::sendAuthentication() { void mumlib::Transport::sendSslPing() { MumbleProto::Ping ping; ping.set_timestamp(std::time(nullptr)); - +#ifdef MUMLIB_USE_LOG4CPP logger.debug("Sending SSL ping."); +#endif sendControlMessagePrivate(MessageType::PING, ping); } @@ -172,16 +210,25 @@ void mumlib::Transport::doReceiveUdp() { udpReceiverEndpoint, [this](const boost::system::error_code &ec, size_t bytesTransferred) { if (!ec and bytesTransferred > 0) { +#ifdef MUMLIB_USE_LOG4CPP logger.debug("Received UDP packet of %d B.", bytesTransferred); +#endif if (not cryptState.isValid()) { +#ifdef MUMLIB_USE_EXCEPTIONS throwTransportException("received UDP packet before CRYPT SETUP message"); +#else + disconnect(); + return; +#endif } else { lastReceivedUdpPacketTimestamp = std::chrono::system_clock::now(); if (udpActive == false) { udpActive = true; +#ifdef MUMLIB_USE_LOG4CPP logger.notice("UDP is up."); +#endif } uint8_t plainBuffer[1024]; @@ -191,7 +238,12 @@ void mumlib::Transport::doReceiveUdp() { udpIncomingBuffer, plainBuffer, bytesTransferred); if (not success) { +#ifdef MUMLIB_USE_EXCEPTIONS throwTransportException("UDP packet decryption failed"); +#else + disconnect(); + return; +#endif } processAudioPacket(plainBuffer, plainBufferLength); @@ -199,21 +251,31 @@ void mumlib::Transport::doReceiveUdp() { doReceiveUdp(); } else if (ec == boost::asio::error::operation_aborted) { +#ifdef MUMLIB_USE_LOG4CPP logger.debug("UDP receive function cancelled."); +#endif } else { +#ifdef MUMLIB_USE_EXCEPTIONS throwTransportException("UDP receive failed: " + ec.message()); +#else + disconnect(); +#endif } }); } void mumlib::Transport::sslConnectHandler(const boost::system::error_code &error) { if (!error) { - sslSocket.async_handshake(ssl::stream_base::client, + sslSocket->async_handshake(ssl::stream_base::client, boost::bind(&Transport::sslHandshakeHandler, this, boost::asio::placeholders::error)); } else { +#ifdef MUMLIB_USE_EXCEPTIONS throwTransportException((boost::format("Connect failed: %s.") % error.message()).str()); +#else + disconnect(); +#endif } } @@ -225,7 +287,11 @@ void mumlib::Transport::sslHandshakeHandler(const boost::system::error_code &err sendAuthentication(); } else { +#ifdef MUMLIB_USE_EXCEPTIONS throwTransportException((boost::format("Handshake failed: %s.") % error.message()).str()); +#else + disconnect(); +#endif } } @@ -245,27 +311,33 @@ void mumlib::Transport::pingTimerTick(const boost::system::error_code &e) { if (lastUdpReceivedMilliseconds > PING_INTERVAL.total_milliseconds() + 1000) { udpActive = false; +#ifdef MUMLIB_USE_LOG4CPP logger.warn("Didn't receive UDP ping in %d ms, falling back to TCP.", lastUdpReceivedMilliseconds); +#endif } } } + pingTimer.expires_at(pingTimer.expires_at() + PING_INTERVAL); + pingTimer.async_wait(boost::bind(&Transport::pingTimerTick, this, _1)); } - - pingTimer.expires_at(pingTimer.expires_at() + PING_INTERVAL); - pingTimer.async_wait(boost::bind(&Transport::pingTimerTick, this, _1)); } void mumlib::Transport::sendUdpAsync(uint8_t *buff, int length) { if (length > MAX_UDP_LENGTH - 4) { +#ifdef MUMLIB_USE_EXCEPTIONS throwTransportException("maximum allowed data length is %d" + to_string(MAX_UDP_LENGTH - 4)); +#else + disconnect(); +#endif } auto *encryptedMsgBuff = asyncBufferPool.malloc(); const int encryptedMsgLength = length + 4; cryptState.encrypt(buff, reinterpret_cast(encryptedMsgBuff), length); - +#ifdef MUMLIB_USE_LOG4CPP logger.debug("Sending %d B of data UDP asynchronously.", encryptedMsgLength); +#endif udpSocket.async_send_to( boost::asio::buffer(encryptedMsgBuff, length + 4), @@ -273,9 +345,15 @@ void mumlib::Transport::sendUdpAsync(uint8_t *buff, int length) { [this, encryptedMsgBuff](const boost::system::error_code &ec, size_t bytesTransferred) { asyncBufferPool.free(encryptedMsgBuff); if (!ec and bytesTransferred > 0) { +#ifdef MUMLIB_USE_LOG4CPP logger.debug("Sent %d B via UDP.", bytesTransferred); +#endif } else { +#ifdef MUMLIB_USE_EXCEPTIONS throwTransportException("UDP send failed: " + ec.message()); +#else + disconnect(); +#endif } }); } @@ -286,7 +364,7 @@ void mumlib::Transport::doReceiveSsl() { } async_read( - sslSocket, + *sslSocket, boost::asio::buffer(sslIncomingBuffer, MAX_TCP_LENGTH), [this](const boost::system::error_code &error, size_t bytesTransferred) -> size_t { if (bytesTransferred < 6) { @@ -300,9 +378,14 @@ void mumlib::Transport::doReceiveSsl() { remaining = max(remaining, (size_t) 0); if (wholeMessageLength > MAX_TCP_LENGTH) { +#ifdef MUMLIB_USE_EXCEPTIONS throwTransportException( (boost::format("message bigger (%d B) than max allowed size (%d B)") % wholeMessageLength % MAX_TCP_LENGTH).str()); +#else + disconnect(); + return 0; +#endif } return remaining; @@ -311,9 +394,10 @@ void mumlib::Transport::doReceiveSsl() { if (!ec and bytesTransferred > 0) { int messageType = ntohs(*reinterpret_cast(sslIncomingBuffer)); - +#ifdef MUMLIB_USE_LOG4CPP logger.debug("Received %d B of data (%d B payload, type %d).", bytesTransferred, bytesTransferred - 6, messageType); +#endif processMessageInternal( static_cast(messageType), @@ -322,10 +406,15 @@ void mumlib::Transport::doReceiveSsl() { doReceiveSsl(); } else { +#ifdef MUMLIB_USE_LOG4CPP logger.error("SSL receiver error: %s. Bytes transferred: %d.", ec.message().c_str(), bytesTransferred); - //todo temporarily disable exception throwing until issue #6 is solved - //throwTransportException("receive failed: " + ec.message()); +#endif +#ifdef MUMLIB_USE_EXCEPTIONS + throwTransportException("receive failed: " + ec.message()); +#else + disconnect(); +#endif } }); } @@ -334,12 +423,16 @@ void mumlib::Transport::processMessageInternal(MessageType messageType, uint8_t switch (messageType) { case MessageType::UDPTUNNEL: { +#ifdef MUMLIB_USE_LOG4CPP logger.debug("Received %d B of encoded audio data via TCP.", length); +#endif processAudioPacket(buffer, length); } break; case MessageType::AUTHENTICATE: { +#ifdef MUMLIB_USE_LOG4CPP logger.warn("Authenticate message received after authenticated."); +#endif } break; case MessageType::PING: { @@ -362,7 +455,9 @@ void mumlib::Transport::processMessageInternal(MessageType messageType, uint8_t if (ping.has_udp_ping_avg()) { log << " UDP avg: " << ping.udp_ping_avg() << " ms"; } +#ifdef MUMLIB_USE_LOG4CPP logger.debug(log.str()); +#endif } break; case MessageType::REJECT: { @@ -380,13 +475,19 @@ void mumlib::Transport::processMessageInternal(MessageType messageType, uint8_t errorMesg << ", reason: " << reject.reason(); } +#ifdef MUMLIB_USE_EXCEPTIONS throwTransportException(errorMesg.str()); +#else + disconnect(); +#endif } break; case MessageType::SERVERSYNC: { state = ConnectionState::CONNECTED; - +#ifdef MUMLIB_USE_LOG4CPP logger.debug("SERVERSYNC. Calling external ProcessControlMessageFunction."); +#endif + pingTimer.async_wait(boost::bind(&Transport::pingTimerTick, this, _1)); processMessageFunction(messageType, buffer, length); } break; @@ -398,7 +499,12 @@ void mumlib::Transport::processMessageInternal(MessageType messageType, uint8_t if (cryptsetup.client_nonce().length() != AES_BLOCK_SIZE or cryptsetup.server_nonce().length() != AES_BLOCK_SIZE or cryptsetup.key().length() != AES_BLOCK_SIZE) { +#ifdef MUMLIB_USE_EXCEPTIONS throwTransportException("one of cryptographic parameters has invalid length"); +#else + disconnect(); + break; +#endif } cryptState.setKey( @@ -407,20 +513,52 @@ void mumlib::Transport::processMessageInternal(MessageType messageType, uint8_t reinterpret_cast(cryptsetup.server_nonce().c_str())); if (not cryptState.isValid()) { +#ifdef MUMLIB_USE_EXCEPTIONS throwTransportException("crypt setup data not valid"); +#else + disconnect(); + break; +#endif } - +#ifdef MUMLIB_USE_LOG4CPP logger.info("Set up cryptography for UDP transport. Sending UDP ping."); +#endif sendUdpPing(); } else { +#ifdef MUMLIB_USE_LOG4CPP logger.info("Ignoring crypt setup message, because UDP is disabled."); +#endif } } break; + case MessageType::USERSTATE: + { + MumbleProto::UserState us; + us.ParseFromArray(buffer, length); + //check name + session, save session for our name + if(us.name() == name) + session = us.session(); + processMessageFunction(messageType, buffer, length); + + } + break; + case MessageType::USERREMOVE: + { + MumbleProto::UserRemove ur; + ur.ParseFromArray(buffer, length); + //check if sssion is our + if(ur.session() == session) + disconnect(); + else + processMessageFunction(messageType, buffer, length); + } + break; default: { +#ifdef MUMLIB_USE_LOG4CPP logger.debug("Calling external ProcessControlMessageFunction."); +#endif processMessageFunction(messageType, buffer, length); } break; @@ -429,11 +567,14 @@ void mumlib::Transport::processMessageInternal(MessageType messageType, uint8_t void mumlib::Transport::sendUdpPing() { if (state == ConnectionState::NOT_CONNECTED) { +#ifdef MUMLIB_USE_LOG4CPP logger.debug("State changed to NOT_CONNECTED, skipping UDP ping."); +#endif return; } - +#ifdef MUMLIB_USE_LOG4CPP logger.debug("Sending UDP ping."); +#endif vector message; message.push_back(0x20); @@ -446,48 +587,66 @@ void mumlib::Transport::sendUdpPing() { void mumlib::Transport::sendSsl(uint8_t *buff, int length) { if (length > MAX_TCP_LENGTH) { +#ifdef MUMLIB_USE_LOG4CPP logger.warn("Sending %d B of data via SSL. Maximal allowed data length to receive is %d B.", length, MAX_TCP_LENGTH); +#endif } - +#ifdef MUMLIB_USE_LOG4CPP logger.debug("Sending %d bytes of data.", length); +#endif try { - write(sslSocket, boost::asio::buffer(buff, length)); + write(*sslSocket, boost::asio::buffer(buff, length)); } catch (boost::system::system_error &err) { +#ifdef MUMLIB_USE_EXCEPTIONS throwTransportException(std::string("SSL send failed: ") + err.what()); +#else + disconnect(); +#endif } } void mumlib::Transport::sendSslAsync(uint8_t *buff, int length) { if (length > MAX_TCP_LENGTH) { +#ifdef MUMLIB_USE_LOG4CPP logger.warn("Sending %d B of data via SSL. Maximal allowed data length to receive is %d B.", length, MAX_TCP_LENGTH); +#endif } auto *asyncBuff = asyncBufferPool.malloc(); memcpy(asyncBuff, buff, length); - +#ifdef MUMLIB_USE_LOG4CPP logger.debug("Sending %d B of data asynchronously.", length); +#endif async_write( - sslSocket, + *sslSocket, boost::asio::buffer(asyncBuff, length), [this, asyncBuff](const boost::system::error_code &ec, size_t bytesTransferred) { asyncBufferPool.free(asyncBuff); +#ifdef MUMLIB_USE_LOG4CPP logger.debug("Sent %d B.", bytesTransferred); +#endif if (!ec and bytesTransferred > 0) { } else { +#ifdef MUMLIB_USE_EXCEPTIONS throwTransportException("async SSL send failed: " + ec.message()); +#else + disconnect(); +#endif } }); } void mumlib::Transport::sendControlMessage(MessageType type, google::protobuf::Message &message) { if (state != ConnectionState::CONNECTED) { +#ifdef MUMLIB_USE_LOG4CPP logger.warn("Connection not established."); +#endif return; } sendControlMessagePrivate(type, message); @@ -514,23 +673,31 @@ void mumlib::Transport::sendControlMessagePrivate(MessageType type, google::prot sendSsl(buff, length); } +#ifdef MUMLIB_USE_EXCEPTIONS void mumlib::Transport::throwTransportException(string message) { state = ConnectionState::FAILED; - throw TransportException(message); + } +#endif void mumlib::Transport::sendEncodedAudioPacket(uint8_t *buffer, int length) { if (state != ConnectionState::CONNECTED) { +#ifdef MUMLIB_USE_LOG4CPP logger.warn("Connection not established."); +#endif return; } if (udpActive) { +#ifdef MUMLIB_USE_LOG4CPP logger.info("Sending %d B of audio data via UDP.", length); +#endif sendUdpAsync(buffer, length); } else { +#ifdef MUMLIB_USE_LOG4CPP logger.info("Sending %d B of audio data via TCP.", length); +#endif const uint16_t netUdptunnelType = htons(static_cast(MessageType::UDPTUNNEL)); @@ -560,7 +727,10 @@ void mumlib::Transport::processAudioPacket(uint8_t *buff, int length) { case AudioPacketType::Ping: break; default: +#ifdef MUMLIB_USE_LOG4CPP logger.error("Not recognized audio type: %xd.", buff[0]); +#endif + break; } } diff --git a/src/VarInt.cpp b/src/VarInt.cpp index 2eeebf8..c326c28 100644 --- a/src/VarInt.cpp +++ b/src/VarInt.cpp @@ -1,4 +1,4 @@ -#include "mumlib/VarInt.hpp" +#include "mumlib/include/mumlib/VarInt.hpp" #include @@ -22,11 +22,13 @@ int64_t mumlib::VarInt::parseVariant(uint8_t *buffer) { switch (v & 0xFC) { case 0xF0: return buffer[1] << 24 | buffer[2] << 16 | buffer[3] << 8 | buffer[4]; +#ifdef MUMLIB_USE_EXCEPTIONS case 0xF4: throw VarIntException("currently unsupported 8-byte varint size"); case 0xF8: case 0xFC: throw VarIntException("currently negative varints aren't supported"); +#endif default: break; } @@ -35,8 +37,9 @@ int64_t mumlib::VarInt::parseVariant(uint8_t *buffer) { } else if ((v & 0xE0) == 0xC0) { return (v & 0x1F) << 16 | buffer[1] << 8 | buffer[2]; } - +#ifdef MUMLIB_USE_EXCEPTIONS throw VarIntException("invalid varint"); +#endif } std::vector mumlib::VarInt::getEncoded() const { diff --git a/src/mumlib.cpp b/src/mumlib.cpp index 08197d1..9993614 100644 --- a/src/mumlib.cpp +++ b/src/mumlib.cpp @@ -1,16 +1,16 @@ -#include "mumlib/CryptState.hpp" -#include "mumlib/VarInt.hpp" -#include "mumlib/enums.hpp" -#include "mumlib/Transport.hpp" -#include "mumlib/Audio.hpp" +#include "mumlib/include/mumlib/CryptState.hpp" +#include "mumlib/include/mumlib/VarInt.hpp" +#include "mumlib/include/mumlib/enums.hpp" +#include "mumlib/include/mumlib/Transport.hpp" +#include "mumlib/include/mumlib/Audio.hpp" -#include "mumlib.hpp" +#include "mumlib/include/mumlib.hpp" #include #include #include -#include +#include "Mumble.pb.h" using namespace std; using namespace boost::asio; @@ -19,7 +19,9 @@ using namespace mumlib; namespace mumlib { struct _Mumlib_Private : boost::noncopyable { - log4cpp::Category &logger = log4cpp::Category::getInstance("mumlib.Mumlib"); +#ifdef MUMLIB_USE_LOG4CPP + MUMLIB_USE_LOG4CPP::Category &logger = MUMLIB_USE_LOG4CPP::Category::getInstance("mumlib.Mumlib"); +#endif bool externalIoService; io_service &ioService; @@ -42,9 +44,13 @@ namespace mumlib { : callback(callback), ioService(ioService), externalIoService(true), - transport(ioService, boost::bind(&_Mumlib_Private::processIncomingTcpMessage, this, _1, _2, _3), - boost::bind(&_Mumlib_Private::processAudioPacket, this, _1, _2, _3)) { + transport(ioService, + boost::bind(&_Mumlib_Private::processIncomingTcpMessage, this, _1, _2, _3), + boost::bind(&_Mumlib_Private::processAudioPacket, this, _1, _2, _3), + configuration) + { + transport.set_callback(&callback); audio.setOpusEncoderBitrate(configuration.opusEncoderBitrate); } @@ -55,8 +61,12 @@ namespace mumlib { } bool processAudioPacket(AudioPacketType type, uint8_t *buffer, int length) { +#ifdef MUMLIB_USE_LOG4CPP logger.info("Got %d B of encoded audio data.", length); +#endif +#ifdef MUMLIB_USE_EXCEPTIONS try { +#endif auto incomingAudioPacket = audio.decodeIncomingAudioPacket(buffer, length); if (type == AudioPacketType::OPUS) { @@ -72,17 +82,22 @@ namespace mumlib { pcmData, status.first); } else { +#ifdef MUMLIB_USE_LOG4CPP logger.warn("Incoming audio packet doesn't contain Opus data, calling unsupportedAudio callback."); +#endif callback.unsupportedAudio(incomingAudioPacket.target, incomingAudioPacket.sessionId, incomingAudioPacket.sequenceNumber, incomingAudioPacket.audioPayload, incomingAudioPacket.audioPayloadLength); } - +#ifdef MUMLIB_USE_EXCEPTIONS } catch (mumlib::AudioException &exp) { +#ifdef MUMLIB_USE_LOG4CPP logger.error("Audio decode error: %s.", exp.what()); +#endif } +#endif return true; } @@ -90,7 +105,9 @@ namespace mumlib { private: bool processIncomingTcpMessage(MessageType messageType, uint8_t *buffer, int length) { +#ifdef MUMLIB_USE_LOG4CPP logger.debug("Process incoming message: type %d, length: %d.", messageType, length); +#endif switch (messageType) { case MessageType::VERSION: { @@ -261,27 +278,43 @@ namespace mumlib { callback.textMessage(actor, sessions, channel_ids, tree_ids, text_message.message()); } break; + case MessageType::PERMISSIONDENIED: // 12 +#ifdef MUMLIB_USE_LOG4CPP logger.warn("PermissionDenied Message: support not implemented yet"); +#endif break; case MessageType::ACL: // 13 +#ifdef MUMLIB_USE_LOG4CPP logger.warn("ACL Message: support not implemented yet."); +#endif break; case MessageType::QUERYUSERS: // 14 +#ifdef MUMLIB_USE_LOG4CPP logger.warn("QueryUsers Message: support not implemented yet"); +#endif break; case MessageType::CONTEXTACTIONMODIFY: // 16 +#ifdef MUMLIB_USE_LOG4CPP logger.warn("ContextActionModify Message: support not implemented yet"); +#endif break; case MessageType::CONTEXTACTION: // 17 +#ifdef MUMLIB_USE_LOG4CPP logger.warn("ContextAction Message: support not implemented yet"); +#endif break; case MessageType::USERLIST: // 18 +#ifdef MUMLIB_USE_LOG4CPP logger.warn("UserList Message: support not implemented yet"); +#endif break; case MessageType::VOICETARGET: +#ifdef MUMLIB_USE_LOG4CPP logger.warn("VoiceTarget Message: I don't think the server ever sends this structure."); +#endif break; + case MessageType::PERMISSIONQUERY: { MumbleProto::PermissionQuery permissionQuery; permissionQuery.ParseFromArray(buffer, length); @@ -305,11 +338,16 @@ namespace mumlib { callback.codecVersion(alpha, beta, prefer_alpha, opus); } break; + case MessageType::USERSTATS: +#ifdef MUMLIB_USE_LOG4CPP logger.warn("UserStats Message: support not implemented yet"); +#endif break; case MessageType::REQUESTBLOB: // 23 +#ifdef MUMLIB_USE_LOG4CPP logger.warn("RequestBlob Message: I don't think this is sent by the server."); +#endif break; case MessageType::SERVERCONFIG: { MumbleProto::ServerConfig serverConfig; @@ -325,11 +363,18 @@ namespace mumlib { image_message_length); } break; + case MessageType::SUGGESTCONFIG: // 25 - logger.warn("SuggestConfig Message: support not implemented yet"); +#ifdef MUMLIB_USE_LOG4CPP + logger.warn("SuggestConfig Message: support not implemented yet"); +#endif break; + default: +#ifdef MUMLIB_USE_EXCEPTIONS throw MumlibException("unknown message type: " + to_string(static_cast(messageType))); +#endif + break; } return true; } @@ -351,7 +396,9 @@ namespace mumlib { : impl(new _Mumlib_Private(callback, configuration)) { } Mumlib::Mumlib(Callback &callback, io_service &ioService, MumlibConfiguration &configuration) - : impl(new _Mumlib_Private(callback, ioService, configuration)) { } + { + impl = new _Mumlib_Private(callback, ioService, configuration); + } Mumlib::~Mumlib() { disconnect(); @@ -378,10 +425,12 @@ namespace mumlib { void Mumlib::run() { if (impl->externalIoService) { +#ifdef MUMLIB_USE_EXCEPTIONS throw MumlibException("can't call run() when using external io_service"); +#endif } - - impl->ioService.run(); + else + impl->ioService.run(); } void Mumlib::sendAudioData(int16_t *pcmData, int pcmLength) {