Add source files from old repository.
This commit is contained in:
parent
261df114d2
commit
6b846aff05
6
.gitignore
vendored
6
.gitignore
vendored
@ -26,3 +26,9 @@
|
|||||||
*.exe
|
*.exe
|
||||||
*.out
|
*.out
|
||||||
*.app
|
*.app
|
||||||
|
|
||||||
|
build/
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
*.iml
|
||||||
|
.idea/
|
||||||
|
59
CMakeLists.txt
Normal file
59
CMakeLists.txt
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.1.1)
|
||||||
|
project(libmumble)
|
||||||
|
|
||||||
|
list(APPEND CMAKE_C_FLAGS " -std=c99 -g -Wall -pedantic ${CMAKE_C_FLAGS}")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||||
|
add_definitions(-DOPT_TLS_GNUTLS -D_POSIX_C_SOURCE=200112L)
|
||||||
|
|
||||||
|
INCLUDE(FindPkgConfig)
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
find_package(Boost COMPONENTS system unit_test_framework program_options filesystem REQUIRED)
|
||||||
|
find_package(OpenSSL REQUIRED)
|
||||||
|
find_package(Protobuf REQUIRED)
|
||||||
|
|
||||||
|
pkg_check_modules(LOG4CPP "log4cpp")
|
||||||
|
pkg_check_modules(OPUS "opus")
|
||||||
|
|
||||||
|
|
||||||
|
INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIR})
|
||||||
|
include_directories(${OPENSSL_INCLUDE_DIR})
|
||||||
|
include_directories(${PROTOBUF_INCLUDE_DIRS})
|
||||||
|
include_directories(${OPUS_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
include_directories(${LOG4CPP_INCLUDE_DIRS})
|
||||||
|
include_directories(include)
|
||||||
|
|
||||||
|
file(GLOB ProtoFiles "mumble.proto")
|
||||||
|
|
||||||
|
set(MUMLIB_PUBLIC_HEADERS include/mumlib.hpp include/mumlib/VarInt.hpp)
|
||||||
|
|
||||||
|
set(MUMLIB_PRIVATE_HEADERS
|
||||||
|
include/mumlib/Callback.hpp
|
||||||
|
include/mumlib/CryptState.hpp
|
||||||
|
include/mumlib/Transport.hpp
|
||||||
|
include/mumlib/Audio.hpp
|
||||||
|
include/mumlib/enums.hpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(MUMLIB_SRC
|
||||||
|
src/mumlib.cpp
|
||||||
|
src/Callback.cpp
|
||||||
|
src/CryptState.cpp
|
||||||
|
src/VarInt.cpp
|
||||||
|
src/Transport.cpp
|
||||||
|
src/Audio.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS mumble.proto)
|
||||||
|
|
||||||
|
add_library(mumlib SHARED ${MUMLIB_SRC} ${MUMLIB_PUBLIC_HEADERS} ${MUMLIB_PRIVATE_HEADERS} ${PROTO_SRCS} ${PROTO_HDRS})
|
||||||
|
target_link_libraries(mumlib ${PROTOBUF_LIBRARIES})
|
||||||
|
target_link_libraries(mumlib ${Boost_LIBRARIES})
|
||||||
|
target_link_libraries(mumlib ${OPENSSL_LIBRARIES})
|
||||||
|
target_link_libraries(mumlib ${LOG4CPP_LIBRARIES})
|
||||||
|
target_link_libraries(mumlib ${OPUS_LIBRARIES})
|
||||||
|
|
||||||
|
|
||||||
|
add_executable(mumlib_example mumlib_example.cpp)
|
||||||
|
target_link_libraries(mumlib_example mumlib)
|
47
include/mumlib.hpp
Normal file
47
include/mumlib.hpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "mumlib/Callback.hpp"
|
||||||
|
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <mumlib/enums.hpp>
|
||||||
|
|
||||||
|
namespace mumlib {
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace boost::asio;
|
||||||
|
|
||||||
|
class MumlibException : public runtime_error {
|
||||||
|
public:
|
||||||
|
MumlibException(string message) : runtime_error(message) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _Mumlib_Private;
|
||||||
|
|
||||||
|
|
||||||
|
class Mumlib : boost::noncopyable {
|
||||||
|
public:
|
||||||
|
Mumlib();
|
||||||
|
|
||||||
|
Mumlib(io_service &ioService);
|
||||||
|
|
||||||
|
~Mumlib();
|
||||||
|
|
||||||
|
void setCallback(Callback &callback);
|
||||||
|
|
||||||
|
void connect(string host, int port, string user, string password);
|
||||||
|
|
||||||
|
void disconnect();
|
||||||
|
|
||||||
|
void run();
|
||||||
|
|
||||||
|
ConnectionState getConnectionState();
|
||||||
|
|
||||||
|
void sendAudioData(int16_t *pcmData, int pcmLength);
|
||||||
|
|
||||||
|
private:
|
||||||
|
_Mumlib_Private *impl;
|
||||||
|
};
|
||||||
|
}
|
41
include/mumlib/Audio.hpp
Normal file
41
include/mumlib/Audio.hpp
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Transport.hpp"
|
||||||
|
|
||||||
|
#include <opus.h>
|
||||||
|
|
||||||
|
namespace mumlib {
|
||||||
|
|
||||||
|
constexpr int SAMPLE_RATE = 48000;
|
||||||
|
|
||||||
|
class AudioException : public MumlibException {
|
||||||
|
public:
|
||||||
|
AudioException(string message) : MumlibException(message) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Audio : boost::noncopyable {
|
||||||
|
public:
|
||||||
|
Audio();
|
||||||
|
|
||||||
|
~Audio();
|
||||||
|
|
||||||
|
|
||||||
|
int decodeAudioPacket(AudioPacketType type, uint8_t *inputBuffer, int inputLength, int16_t *pcmBuffer,
|
||||||
|
int pcmBufferSize);
|
||||||
|
|
||||||
|
int encodeAudioPacket(
|
||||||
|
int target,
|
||||||
|
int16_t *inputPcmBuffer,
|
||||||
|
int inputLength,
|
||||||
|
uint8_t *outputBuffer,
|
||||||
|
int outputBufferSize = MAX_UDP_LENGTH);
|
||||||
|
|
||||||
|
private:
|
||||||
|
log4cpp::Category &logger;
|
||||||
|
|
||||||
|
OpusDecoder *opusDecoder;
|
||||||
|
OpusEncoder *opusEncoder;
|
||||||
|
|
||||||
|
int64_t outgoingSequenceNumber;
|
||||||
|
};
|
||||||
|
}
|
318
include/mumlib/Callback.hpp
Normal file
318
include/mumlib/Callback.hpp
Normal file
@ -0,0 +1,318 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace mumlib {
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
class Callback {
|
||||||
|
public:
|
||||||
|
virtual void version_callback(
|
||||||
|
uint16_t major,
|
||||||
|
uint8_t minor,
|
||||||
|
uint8_t patch,
|
||||||
|
string release,
|
||||||
|
string os,
|
||||||
|
string os_version) { };
|
||||||
|
|
||||||
|
virtual void audio_callback(
|
||||||
|
uint8_t *pcm_data,
|
||||||
|
uint32_t pcm_data_size) { };
|
||||||
|
|
||||||
|
virtual void unsupported_audio_callback(
|
||||||
|
uint8_t *encoded_audio_data,
|
||||||
|
uint32_t encoded_audio_data_size) { };
|
||||||
|
|
||||||
|
virtual void serversync_callback(
|
||||||
|
string welcome_text,
|
||||||
|
int32_t session,
|
||||||
|
int32_t max_bandwidth,
|
||||||
|
int64_t permissions) { };
|
||||||
|
|
||||||
|
virtual void channelremove_callback(uint32_t channel_id) { };
|
||||||
|
|
||||||
|
virtual void channelstate_callback(
|
||||||
|
string name,
|
||||||
|
int32_t channel_id,
|
||||||
|
int32_t parent,
|
||||||
|
string description,
|
||||||
|
vector<uint32_t> links,
|
||||||
|
vector<uint32_t> inks_add,
|
||||||
|
vector<uint32_t> links_remove,
|
||||||
|
bool temporary,
|
||||||
|
int32_t position) { };
|
||||||
|
|
||||||
|
virtual void userremove_callback(
|
||||||
|
uint32_t session,
|
||||||
|
int32_t actor,
|
||||||
|
string reason,
|
||||||
|
bool ban) { };
|
||||||
|
|
||||||
|
virtual void userstate_callback(
|
||||||
|
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) { };
|
||||||
|
|
||||||
|
virtual void banlist_callback(
|
||||||
|
uint8_t *ip_data,
|
||||||
|
uint32_t ip_data_size,
|
||||||
|
uint32_t mask,
|
||||||
|
string name,
|
||||||
|
string hash,
|
||||||
|
string reason,
|
||||||
|
string start,
|
||||||
|
int32_t duration) { };
|
||||||
|
|
||||||
|
virtual void textmessage_callback(
|
||||||
|
uint32_t actor,
|
||||||
|
uint32_t n_session,
|
||||||
|
uint32_t *session,
|
||||||
|
uint32_t n_channel_id,
|
||||||
|
uint32_t *channel_id,
|
||||||
|
uint32_t n_tree_id,
|
||||||
|
uint32_t *tree_id,
|
||||||
|
string message) { };
|
||||||
|
|
||||||
|
virtual void permissiondenied_callback(
|
||||||
|
int32_t permission,
|
||||||
|
int32_t channel_id,
|
||||||
|
int32_t session,
|
||||||
|
string reason,
|
||||||
|
int32_t deny_type,
|
||||||
|
string name) { };
|
||||||
|
|
||||||
|
virtual void acl_callback() { };
|
||||||
|
|
||||||
|
virtual void queryusers_callback(
|
||||||
|
uint32_t n_ids,
|
||||||
|
uint32_t *ids,
|
||||||
|
uint32_t n_names,
|
||||||
|
string *names) { };
|
||||||
|
|
||||||
|
virtual void cryptsetup_callback(
|
||||||
|
uint32_t key_size,
|
||||||
|
uint8_t *key,
|
||||||
|
uint32_t client_nonce_size,
|
||||||
|
uint8_t *client_nonce,
|
||||||
|
uint32_t server_nonce_size,
|
||||||
|
uint8_t *server_nonce) { };
|
||||||
|
|
||||||
|
virtual void contextactionmodify_callback(
|
||||||
|
string action,
|
||||||
|
string text,
|
||||||
|
uint32_t m_context,
|
||||||
|
uint32_t operation) { };
|
||||||
|
|
||||||
|
virtual void contextaction_callback(
|
||||||
|
int32_t session,
|
||||||
|
int32_t channel_id,
|
||||||
|
string action) { };
|
||||||
|
|
||||||
|
virtual void userlist_callback(
|
||||||
|
uint32_t user_id,
|
||||||
|
string name,
|
||||||
|
string last_seen,
|
||||||
|
int32_t last_channel) { };
|
||||||
|
|
||||||
|
virtual void voicetarget_callback() { };
|
||||||
|
|
||||||
|
virtual void permissionquery_callback(
|
||||||
|
int32_t channel_id,
|
||||||
|
uint32_t permissions,
|
||||||
|
int32_t flush) { };
|
||||||
|
|
||||||
|
virtual void codecversion_callback(
|
||||||
|
int32_t alpha,
|
||||||
|
int32_t beta,
|
||||||
|
uint32_t prefer_alpha,
|
||||||
|
int32_t opus) { };
|
||||||
|
|
||||||
|
virtual void userstats_callback() { };
|
||||||
|
|
||||||
|
virtual void requestblob_callback() { };
|
||||||
|
|
||||||
|
virtual void serverconfig_callback(
|
||||||
|
uint32_t max_bandwidth,
|
||||||
|
string welcome_text,
|
||||||
|
uint32_t allow_html,
|
||||||
|
uint32_t message_length,
|
||||||
|
uint32_t image_message_length) { };
|
||||||
|
|
||||||
|
virtual void suggestconfig_callback(
|
||||||
|
uint32_t version,
|
||||||
|
uint32_t positional,
|
||||||
|
uint32_t push_to_talk) { };
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class _BasicCallback_Private;
|
||||||
|
|
||||||
|
class BasicCallback : public Callback {
|
||||||
|
public:
|
||||||
|
BasicCallback();
|
||||||
|
|
||||||
|
~BasicCallback();
|
||||||
|
|
||||||
|
virtual void version_callback(
|
||||||
|
uint16_t major,
|
||||||
|
uint8_t minor,
|
||||||
|
uint8_t patch,
|
||||||
|
string release,
|
||||||
|
string os,
|
||||||
|
string os_version);
|
||||||
|
|
||||||
|
virtual void audio_callback(
|
||||||
|
uint8_t *pcm_data,
|
||||||
|
uint32_t pcm_data_size);
|
||||||
|
|
||||||
|
virtual void unsupported_audio_callback(
|
||||||
|
uint8_t *encoded_audio_data,
|
||||||
|
uint32_t encoded_audio_data_size);
|
||||||
|
|
||||||
|
virtual void serversync_callback(
|
||||||
|
string welcome_text,
|
||||||
|
int32_t session,
|
||||||
|
int32_t max_bandwidth,
|
||||||
|
int64_t permissions);
|
||||||
|
|
||||||
|
virtual void channelremove_callback(uint32_t channel_id);
|
||||||
|
|
||||||
|
virtual void channelstate_callback(
|
||||||
|
string name,
|
||||||
|
int32_t channel_id,
|
||||||
|
int32_t parent,
|
||||||
|
string description,
|
||||||
|
vector<uint32_t> links,
|
||||||
|
vector<uint32_t> inks_add,
|
||||||
|
vector<uint32_t> links_remove,
|
||||||
|
bool temporary,
|
||||||
|
int32_t position);
|
||||||
|
|
||||||
|
virtual void userremove_callback(
|
||||||
|
uint32_t session,
|
||||||
|
int32_t actor,
|
||||||
|
string reason,
|
||||||
|
bool ban);
|
||||||
|
|
||||||
|
virtual void userstate_callback(
|
||||||
|
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);
|
||||||
|
|
||||||
|
virtual void banlist_callback(
|
||||||
|
uint8_t *ip_data,
|
||||||
|
uint32_t ip_data_size,
|
||||||
|
uint32_t mask,
|
||||||
|
string name,
|
||||||
|
string hash,
|
||||||
|
string reason,
|
||||||
|
string start,
|
||||||
|
int32_t duration);
|
||||||
|
|
||||||
|
virtual void textmessage_callback(
|
||||||
|
uint32_t actor,
|
||||||
|
uint32_t n_session,
|
||||||
|
uint32_t *session,
|
||||||
|
uint32_t n_channel_id,
|
||||||
|
uint32_t *channel_id,
|
||||||
|
uint32_t n_tree_id,
|
||||||
|
uint32_t *tree_id,
|
||||||
|
string message);
|
||||||
|
|
||||||
|
virtual void permissiondenied_callback(
|
||||||
|
int32_t permission,
|
||||||
|
int32_t channel_id,
|
||||||
|
int32_t session,
|
||||||
|
string reason,
|
||||||
|
int32_t deny_type,
|
||||||
|
string name);
|
||||||
|
|
||||||
|
virtual void acl_callback();
|
||||||
|
|
||||||
|
virtual void queryusers_callback(
|
||||||
|
uint32_t n_ids,
|
||||||
|
uint32_t *ids,
|
||||||
|
uint32_t n_names,
|
||||||
|
string *names);
|
||||||
|
|
||||||
|
virtual void cryptsetup_callback(
|
||||||
|
uint32_t key_size,
|
||||||
|
uint8_t *key,
|
||||||
|
uint32_t client_nonce_size,
|
||||||
|
uint8_t *client_nonce,
|
||||||
|
uint32_t server_nonce_size,
|
||||||
|
uint8_t *server_nonce);
|
||||||
|
|
||||||
|
virtual void contextactionmodify_callback(
|
||||||
|
string action,
|
||||||
|
string text,
|
||||||
|
uint32_t m_context,
|
||||||
|
uint32_t operation);
|
||||||
|
|
||||||
|
virtual void contextaction_callback(
|
||||||
|
int32_t session,
|
||||||
|
int32_t channel_id,
|
||||||
|
string action);
|
||||||
|
|
||||||
|
virtual void userlist_callback(
|
||||||
|
uint32_t user_id,
|
||||||
|
string name,
|
||||||
|
string last_seen,
|
||||||
|
int32_t last_channel);
|
||||||
|
|
||||||
|
virtual void voicetarget_callback();
|
||||||
|
|
||||||
|
virtual void permissionquery_callback(
|
||||||
|
int32_t channel_id,
|
||||||
|
uint32_t permissions,
|
||||||
|
int32_t flush);
|
||||||
|
|
||||||
|
virtual void codecversion_callback(
|
||||||
|
int32_t alpha,
|
||||||
|
int32_t beta,
|
||||||
|
uint32_t prefer_alpha,
|
||||||
|
int32_t opus);
|
||||||
|
|
||||||
|
virtual void userstats_callback();
|
||||||
|
|
||||||
|
virtual void requestblob_callback();
|
||||||
|
|
||||||
|
virtual void serverconfig_callback(
|
||||||
|
uint32_t max_bandwidth,
|
||||||
|
string welcome_text,
|
||||||
|
uint32_t allow_html,
|
||||||
|
uint32_t message_length,
|
||||||
|
uint32_t image_message_length);
|
||||||
|
|
||||||
|
virtual void suggestconfig_callback(
|
||||||
|
uint32_t version,
|
||||||
|
uint32_t positional,
|
||||||
|
uint32_t push_to_talk);
|
||||||
|
|
||||||
|
private:
|
||||||
|
_BasicCallback_Private *impl;
|
||||||
|
};
|
||||||
|
}
|
80
include/mumlib/CryptState.hpp
Normal file
80
include/mumlib/CryptState.hpp
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/* Copyright (C) 2005-2011, Thorvald Natvig <thorvald@natvig.com>
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
- Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
- Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
- Neither the name of the Mumble Developers nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from this
|
||||||
|
software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
|
||||||
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <openssl/aes.h>
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
|
||||||
|
namespace mumlib {
|
||||||
|
|
||||||
|
class CryptState : boost::noncopyable {
|
||||||
|
public:
|
||||||
|
unsigned char raw_key[AES_BLOCK_SIZE];
|
||||||
|
unsigned char encrypt_iv[AES_BLOCK_SIZE];
|
||||||
|
unsigned char decrypt_iv[AES_BLOCK_SIZE];
|
||||||
|
unsigned char decrypt_history[0x100];
|
||||||
|
|
||||||
|
unsigned int uiGood;
|
||||||
|
unsigned int uiLate;
|
||||||
|
unsigned int uiLost;
|
||||||
|
unsigned int uiResync;
|
||||||
|
|
||||||
|
unsigned int uiRemoteGood;
|
||||||
|
unsigned int uiRemoteLate;
|
||||||
|
unsigned int uiRemoteLost;
|
||||||
|
unsigned int uiRemoteResync;
|
||||||
|
|
||||||
|
AES_KEY encrypt_key;
|
||||||
|
AES_KEY decrypt_key;
|
||||||
|
bool bInit;
|
||||||
|
|
||||||
|
CryptState();
|
||||||
|
|
||||||
|
bool isValid() const;
|
||||||
|
|
||||||
|
void setKey(const unsigned char *rkey, const unsigned char *eiv, const unsigned char *div);
|
||||||
|
|
||||||
|
void setDecryptIV(const unsigned char *iv);
|
||||||
|
|
||||||
|
void ocb_encrypt(const unsigned char *plain, unsigned char *encrypted, unsigned int len,
|
||||||
|
const unsigned char *nonce,
|
||||||
|
unsigned char *tag);
|
||||||
|
|
||||||
|
void ocb_decrypt(const unsigned char *encrypted, unsigned char *plain, unsigned int len,
|
||||||
|
const unsigned char *nonce,
|
||||||
|
unsigned char *tag);
|
||||||
|
|
||||||
|
bool decrypt(const unsigned char *source, unsigned char *dst, unsigned int crypted_length);
|
||||||
|
|
||||||
|
void encrypt(const unsigned char *source, unsigned char *dst, unsigned int plain_length);
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
128
include/mumlib/Transport.hpp
Normal file
128
include/mumlib/Transport.hpp
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "mumlib/CryptState.hpp"
|
||||||
|
#include "mumlib/VarInt.hpp"
|
||||||
|
#include "enums.hpp"
|
||||||
|
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
#include <boost/asio/ssl.hpp>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
#include <boost/pool/pool.hpp>
|
||||||
|
|
||||||
|
#include <log4cpp/Category.hh>
|
||||||
|
#include <google/protobuf/message.h>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
namespace mumlib {
|
||||||
|
|
||||||
|
constexpr int MAX_UDP_LENGTH = 1024;
|
||||||
|
constexpr int MAX_TCP_LENGTH = 2048;
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace boost::asio;
|
||||||
|
using namespace boost::asio::ip;
|
||||||
|
|
||||||
|
typedef function<bool(MessageType, uint8_t *, int)> ProcessControlMessageFunction;
|
||||||
|
|
||||||
|
typedef function<bool(AudioPacketType, uint8_t *, int)> ProcessEncodedAudioPacketFunction;
|
||||||
|
|
||||||
|
class TransportException : public MumlibException {
|
||||||
|
public:
|
||||||
|
TransportException(string message) : MumlibException(message) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Transport : boost::noncopyable {
|
||||||
|
public:
|
||||||
|
Transport(io_service &ioService,
|
||||||
|
ProcessControlMessageFunction processControlMessageFunc,
|
||||||
|
ProcessEncodedAudioPacketFunction processEncodedAudioPacketFunction,
|
||||||
|
bool noUdp = false);
|
||||||
|
|
||||||
|
void connect(string host,
|
||||||
|
int port,
|
||||||
|
string user,
|
||||||
|
string password);
|
||||||
|
|
||||||
|
void disconnect();
|
||||||
|
|
||||||
|
ConnectionState getConnectionState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isUdpActive();
|
||||||
|
|
||||||
|
void sendControlMessage(MessageType type, google::protobuf::Message &message);
|
||||||
|
|
||||||
|
void sendEncodedAudioPacket(uint8_t *buffer, int length);
|
||||||
|
|
||||||
|
private:
|
||||||
|
log4cpp::Category &logger;
|
||||||
|
|
||||||
|
io_service &ioService;
|
||||||
|
|
||||||
|
pair<string, int> connectionParams;
|
||||||
|
|
||||||
|
pair<string, string> credentials;
|
||||||
|
|
||||||
|
ProcessControlMessageFunction processMessageFunction;
|
||||||
|
|
||||||
|
ProcessEncodedAudioPacketFunction processEncodedAudioPacketFunction;
|
||||||
|
|
||||||
|
const bool noUdp;
|
||||||
|
|
||||||
|
volatile bool udpActive;
|
||||||
|
|
||||||
|
ConnectionState state;
|
||||||
|
|
||||||
|
udp::socket udpSocket;
|
||||||
|
ip::udp::endpoint udpReceiverEndpoint;
|
||||||
|
uint8_t udpIncomingBuffer[MAX_UDP_LENGTH];
|
||||||
|
CryptState cryptState;
|
||||||
|
|
||||||
|
ssl::context sslContext;
|
||||||
|
ssl::stream<tcp::socket> sslSocket;
|
||||||
|
uint8_t sslIncomingBuffer[MAX_TCP_LENGTH];
|
||||||
|
|
||||||
|
|
||||||
|
deadline_timer pingTimer;
|
||||||
|
std::chrono::time_point<std::chrono::system_clock> lastReceivedUdpPacketTimestamp;
|
||||||
|
|
||||||
|
boost::pool<> asyncBufferPool;
|
||||||
|
|
||||||
|
void pingTimerTick(const boost::system::error_code &e);
|
||||||
|
|
||||||
|
void sslConnectHandler(const boost::system::error_code &error);
|
||||||
|
|
||||||
|
void sslHandshakeHandler(const boost::system::error_code &error);
|
||||||
|
|
||||||
|
void doReceiveSsl();
|
||||||
|
|
||||||
|
void sendSsl(uint8_t *buff, int length);
|
||||||
|
|
||||||
|
void sendSslAsync(uint8_t *buff, int length);
|
||||||
|
|
||||||
|
void sendControlMessagePrivate(MessageType type, google::protobuf::Message &message);
|
||||||
|
|
||||||
|
void sendSslPing();
|
||||||
|
|
||||||
|
void sendVersion();
|
||||||
|
|
||||||
|
void sendAuthentication();
|
||||||
|
|
||||||
|
void processMessageInternal(MessageType messageType, uint8_t *buffer, int length);
|
||||||
|
|
||||||
|
void doReceiveUdp();
|
||||||
|
|
||||||
|
void sendUdpAsync(uint8_t *buff, int length);
|
||||||
|
|
||||||
|
void sendUdpPing();
|
||||||
|
|
||||||
|
void throwTransportException(string message);
|
||||||
|
|
||||||
|
void processAudioPacket(uint8_t *buff, int length);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
34
include/mumlib/VarInt.hpp
Normal file
34
include/mumlib/VarInt.hpp
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <mumlib.hpp>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace mumlib {
|
||||||
|
class VarIntException : public MumlibException {
|
||||||
|
public:
|
||||||
|
VarIntException(std::string message) : MumlibException(message) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
class VarInt {
|
||||||
|
public:
|
||||||
|
VarInt(uint8_t *encoded);
|
||||||
|
|
||||||
|
VarInt(std::vector<uint8_t> encoded);
|
||||||
|
|
||||||
|
VarInt(int64_t value);
|
||||||
|
|
||||||
|
int64_t getValue() const {
|
||||||
|
return this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> getEncoded() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const int64_t value;
|
||||||
|
|
||||||
|
int64_t parseVariant(uint8_t *buffer);
|
||||||
|
};
|
||||||
|
}
|
48
include/mumlib/enums.hpp
Normal file
48
include/mumlib/enums.hpp
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace mumlib {
|
||||||
|
enum class MessageType {
|
||||||
|
VERSION = 0,
|
||||||
|
UDPTUNNEL = 1,
|
||||||
|
AUTHENTICATE = 2,
|
||||||
|
PING = 3,
|
||||||
|
REJECT = 4,
|
||||||
|
SERVERSYNC = 5,
|
||||||
|
CHANNELREMOVE = 6,
|
||||||
|
CHANNELSTATE = 7,
|
||||||
|
USERREMOVE = 8,
|
||||||
|
USERSTATE = 9,
|
||||||
|
BANLIST = 10,
|
||||||
|
TEXTMESSAGE = 11,
|
||||||
|
PERMISSIONDENIED = 12,
|
||||||
|
ACL = 13,
|
||||||
|
QUERYUSERS = 14,
|
||||||
|
CRYPTSETUP = 15,
|
||||||
|
CONTEXTACTIONMODIFY = 16,
|
||||||
|
CONTEXTACTION = 17,
|
||||||
|
USERLIST = 18,
|
||||||
|
VOICETARGET = 19,
|
||||||
|
PERMISSIONQUERY = 20,
|
||||||
|
CODECVERSION = 21,
|
||||||
|
USERSTATS = 22,
|
||||||
|
REQUESTBLOB = 23,
|
||||||
|
SERVERCONFIG = 24,
|
||||||
|
SUGGESTCONFIG = 25
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ConnectionState {
|
||||||
|
NOT_CONNECTED,
|
||||||
|
IN_PROGRESS,
|
||||||
|
CONNECTED,
|
||||||
|
FAILED
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class AudioPacketType {
|
||||||
|
CELT_Alpha,
|
||||||
|
Ping,
|
||||||
|
Speex,
|
||||||
|
CELT_Beta,
|
||||||
|
OPUS
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
295
mumble.proto
Normal file
295
mumble.proto
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
package MumbleProto;
|
||||||
|
|
||||||
|
option optimize_for = SPEED;
|
||||||
|
|
||||||
|
message Version {
|
||||||
|
optional uint32 version = 1;
|
||||||
|
optional string release = 2;
|
||||||
|
optional string os = 3;
|
||||||
|
optional string os_version = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UDPTunnel {
|
||||||
|
required bytes packet = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Authenticate {
|
||||||
|
optional string username = 1;
|
||||||
|
optional string password = 2;
|
||||||
|
repeated string tokens = 3;
|
||||||
|
repeated int32 celt_versions = 4;
|
||||||
|
optional bool opus = 5 [default = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message Ping {
|
||||||
|
optional uint64 timestamp = 1;
|
||||||
|
optional uint32 good = 2;
|
||||||
|
optional uint32 late = 3;
|
||||||
|
optional uint32 lost = 4;
|
||||||
|
optional uint32 resync = 5;
|
||||||
|
optional uint32 udp_packets = 6;
|
||||||
|
optional uint32 tcp_packets = 7;
|
||||||
|
optional float udp_ping_avg = 8;
|
||||||
|
optional float udp_ping_var = 9;
|
||||||
|
optional float tcp_ping_avg = 10;
|
||||||
|
optional float tcp_ping_var = 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Reject {
|
||||||
|
enum RejectType {
|
||||||
|
None = 0;
|
||||||
|
WrongVersion = 1;
|
||||||
|
InvalidUsername = 2;
|
||||||
|
WrongUserPW = 3;
|
||||||
|
WrongServerPW = 4;
|
||||||
|
UsernameInUse = 5;
|
||||||
|
ServerFull = 6;
|
||||||
|
NoCertificate = 7;
|
||||||
|
AuthenticatorFail = 8;
|
||||||
|
}
|
||||||
|
optional RejectType type = 1;
|
||||||
|
optional string reason = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ServerSync {
|
||||||
|
optional uint32 session = 1;
|
||||||
|
optional uint32 max_bandwidth = 2;
|
||||||
|
optional string welcome_text = 3;
|
||||||
|
optional uint64 permissions = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ChannelRemove {
|
||||||
|
required uint32 channel_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ChannelState {
|
||||||
|
optional uint32 channel_id = 1;
|
||||||
|
optional uint32 parent = 2;
|
||||||
|
optional string name = 3;
|
||||||
|
repeated uint32 links = 4;
|
||||||
|
optional string description = 5;
|
||||||
|
repeated uint32 links_add = 6;
|
||||||
|
repeated uint32 links_remove = 7;
|
||||||
|
optional bool temporary = 8 [default = false];
|
||||||
|
optional int32 position = 9 [default = 0];
|
||||||
|
optional bytes description_hash = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UserRemove {
|
||||||
|
required uint32 session = 1;
|
||||||
|
optional uint32 actor = 2;
|
||||||
|
optional string reason = 3;
|
||||||
|
optional bool ban = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UserState {
|
||||||
|
optional uint32 session = 1;
|
||||||
|
optional uint32 actor = 2;
|
||||||
|
optional string name = 3;
|
||||||
|
optional uint32 user_id = 4;
|
||||||
|
optional uint32 channel_id = 5;
|
||||||
|
optional bool mute = 6;
|
||||||
|
optional bool deaf = 7;
|
||||||
|
optional bool suppress = 8;
|
||||||
|
optional bool self_mute = 9;
|
||||||
|
optional bool self_deaf = 10;
|
||||||
|
optional bytes texture = 11;
|
||||||
|
optional bytes plugin_context = 12;
|
||||||
|
optional string plugin_identity = 13;
|
||||||
|
optional string comment = 14;
|
||||||
|
optional string hash = 15;
|
||||||
|
optional bytes comment_hash = 16;
|
||||||
|
optional bytes texture_hash = 17;
|
||||||
|
optional bool priority_speaker = 18;
|
||||||
|
optional bool recording = 19;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BanList {
|
||||||
|
message BanEntry {
|
||||||
|
required bytes address = 1;
|
||||||
|
required uint32 mask = 2;
|
||||||
|
optional string name = 3;
|
||||||
|
optional string hash = 4;
|
||||||
|
optional string reason = 5;
|
||||||
|
optional string start = 6;
|
||||||
|
optional uint32 duration = 7;
|
||||||
|
}
|
||||||
|
repeated BanEntry bans = 1;
|
||||||
|
optional bool query = 2 [default = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message TextMessage {
|
||||||
|
optional uint32 actor = 1;
|
||||||
|
repeated uint32 session = 2;
|
||||||
|
repeated uint32 channel_id = 3;
|
||||||
|
repeated uint32 tree_id = 4;
|
||||||
|
required string message = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PermissionDenied {
|
||||||
|
enum DenyType {
|
||||||
|
Text = 0;
|
||||||
|
Permission = 1;
|
||||||
|
SuperUser = 2;
|
||||||
|
ChannelName = 3;
|
||||||
|
TextTooLong = 4;
|
||||||
|
H9K = 5;
|
||||||
|
TemporaryChannel = 6;
|
||||||
|
MissingCertificate = 7;
|
||||||
|
UserName = 8;
|
||||||
|
ChannelFull = 9;
|
||||||
|
NestingLimit = 10;
|
||||||
|
}
|
||||||
|
optional uint32 permission = 1;
|
||||||
|
optional uint32 channel_id = 2;
|
||||||
|
optional uint32 session = 3;
|
||||||
|
optional string reason = 4;
|
||||||
|
optional DenyType type = 5;
|
||||||
|
optional string name = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ACL {
|
||||||
|
message ChanGroup {
|
||||||
|
required string name = 1;
|
||||||
|
optional bool inherited = 2 [default = true];
|
||||||
|
optional bool inherit = 3 [default = true];
|
||||||
|
optional bool inheritable = 4 [default = true];
|
||||||
|
repeated uint32 add = 5;
|
||||||
|
repeated uint32 remove = 6;
|
||||||
|
repeated uint32 inherited_members = 7;
|
||||||
|
}
|
||||||
|
message ChanACL {
|
||||||
|
optional bool apply_here = 1 [default = true];
|
||||||
|
optional bool apply_subs = 2 [default = true];
|
||||||
|
optional bool inherited = 3 [default = true];
|
||||||
|
optional uint32 user_id = 4;
|
||||||
|
optional string group = 5;
|
||||||
|
optional uint32 grant = 6;
|
||||||
|
optional uint32 deny = 7;
|
||||||
|
}
|
||||||
|
required uint32 channel_id = 1;
|
||||||
|
optional bool inherit_acls = 2 [default = true];
|
||||||
|
repeated ChanGroup groups = 3;
|
||||||
|
repeated ChanACL acls = 4;
|
||||||
|
optional bool query = 5 [default = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message QueryUsers {
|
||||||
|
repeated uint32 ids = 1;
|
||||||
|
repeated string names = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CryptSetup {
|
||||||
|
optional bytes key = 1;
|
||||||
|
optional bytes client_nonce = 2;
|
||||||
|
optional bytes server_nonce = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ContextActionModify {
|
||||||
|
enum Context {
|
||||||
|
Server = 0x01;
|
||||||
|
Channel = 0x02;
|
||||||
|
User = 0x04;
|
||||||
|
}
|
||||||
|
enum Operation {
|
||||||
|
Add = 0;
|
||||||
|
Remove = 1;
|
||||||
|
}
|
||||||
|
required string action = 1;
|
||||||
|
optional string text = 2;
|
||||||
|
optional uint32 context = 3;
|
||||||
|
optional Operation operation = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ContextAction {
|
||||||
|
optional uint32 session = 1;
|
||||||
|
optional uint32 channel_id = 2;
|
||||||
|
required string action = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UserList {
|
||||||
|
message User {
|
||||||
|
required uint32 user_id = 1;
|
||||||
|
optional string name = 2;
|
||||||
|
optional string last_seen = 3;
|
||||||
|
optional uint32 last_channel = 4;
|
||||||
|
}
|
||||||
|
repeated User users = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message VoiceTarget {
|
||||||
|
message Target {
|
||||||
|
repeated uint32 session = 1;
|
||||||
|
optional uint32 channel_id = 2;
|
||||||
|
optional string group = 3;
|
||||||
|
optional bool links = 4 [default = false];
|
||||||
|
optional bool children = 5 [default = false];
|
||||||
|
}
|
||||||
|
optional uint32 id = 1;
|
||||||
|
repeated Target targets = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PermissionQuery {
|
||||||
|
optional uint32 channel_id = 1;
|
||||||
|
optional uint32 permissions = 2;
|
||||||
|
optional bool flush = 3 [default = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message CodecVersion {
|
||||||
|
required int32 alpha = 1;
|
||||||
|
required int32 beta = 2;
|
||||||
|
required bool prefer_alpha = 3 [default = true];
|
||||||
|
optional bool opus = 4 [default = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message UserStats {
|
||||||
|
message Stats {
|
||||||
|
optional uint32 good = 1;
|
||||||
|
optional uint32 late = 2;
|
||||||
|
optional uint32 lost = 3;
|
||||||
|
optional uint32 resync = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
optional uint32 session = 1;
|
||||||
|
optional bool stats_only = 2 [default = false];
|
||||||
|
repeated bytes certificates = 3;
|
||||||
|
optional Stats from_client = 4;
|
||||||
|
optional Stats from_server = 5;
|
||||||
|
|
||||||
|
optional uint32 udp_packets = 6;
|
||||||
|
optional uint32 tcp_packets = 7;
|
||||||
|
optional float udp_ping_avg = 8;
|
||||||
|
optional float udp_ping_var = 9;
|
||||||
|
optional float tcp_ping_avg = 10;
|
||||||
|
optional float tcp_ping_var = 11;
|
||||||
|
|
||||||
|
optional Version version = 12;
|
||||||
|
repeated int32 celt_versions = 13;
|
||||||
|
optional bytes address = 14;
|
||||||
|
optional uint32 bandwidth = 15;
|
||||||
|
optional uint32 onlinesecs = 16;
|
||||||
|
optional uint32 idlesecs = 17;
|
||||||
|
optional bool strong_certificate = 18 [default = false];
|
||||||
|
optional bool opus = 19 [default = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
message RequestBlob {
|
||||||
|
repeated uint32 session_texture = 1;
|
||||||
|
repeated uint32 session_comment = 2;
|
||||||
|
repeated uint32 channel_description = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ServerConfig {
|
||||||
|
optional uint32 max_bandwidth = 1;
|
||||||
|
optional string welcome_text = 2;
|
||||||
|
optional bool allow_html = 3;
|
||||||
|
optional uint32 message_length = 4;
|
||||||
|
optional uint32 image_message_length = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SuggestConfig {
|
||||||
|
optional uint32 version = 1;
|
||||||
|
optional bool positional = 2;
|
||||||
|
optional bool push_to_talk = 3;
|
||||||
|
}
|
||||||
|
|
54
mumlib_example.cpp
Normal file
54
mumlib_example.cpp
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#include "mumlib.hpp"
|
||||||
|
|
||||||
|
#include "log4cpp/Category.hh"
|
||||||
|
#include "log4cpp/FileAppender.hh"
|
||||||
|
#include "log4cpp/OstreamAppender.hh"
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
#include <cmath>
|
||||||
|
#include <mumlib/Audio.hpp>
|
||||||
|
|
||||||
|
void audioSenderThreadFunction(mumlib::Mumlib *mum) {
|
||||||
|
while (mum->getConnectionState() != mumlib::ConnectionState::FAILED) {
|
||||||
|
if (mum->getConnectionState() == mumlib::ConnectionState::CONNECTED) {
|
||||||
|
constexpr double FREQUENCY = 1000; // Hz
|
||||||
|
constexpr int BUFF_SIZE = mumlib::SAMPLE_RATE / 100; // 10 ms
|
||||||
|
int16_t buff[BUFF_SIZE];
|
||||||
|
|
||||||
|
for (int i = 0; i < BUFF_SIZE; ++i) {
|
||||||
|
buff[i] = 10000 * std::sin(2.0 * M_PI * FREQUENCY * ((double) i) / ((double) mumlib::SAMPLE_RATE));
|
||||||
|
}
|
||||||
|
|
||||||
|
mum->sendAudioData(buff, BUFF_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
|
log4cpp::Appender *appender1 = new log4cpp::OstreamAppender("console", &std::cout);
|
||||||
|
appender1->setLayout(new log4cpp::BasicLayout());
|
||||||
|
log4cpp::Category &logger = log4cpp::Category::getRoot();
|
||||||
|
logger.setPriority(log4cpp::Priority::WARN);
|
||||||
|
logger.addAppender(appender1);
|
||||||
|
|
||||||
|
if (argc < 3) {
|
||||||
|
logger.crit("Usage: %s {server} {password}", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mumlib::Mumlib mum;
|
||||||
|
|
||||||
|
mumlib::BasicCallback callback;
|
||||||
|
mum.setCallback(callback);
|
||||||
|
|
||||||
|
mum.connect(argv[1], 64738, "mumlib_example", argv[2]);
|
||||||
|
|
||||||
|
std::thread audioSenderThread(audioSenderThreadFunction, &mum);
|
||||||
|
|
||||||
|
mum.run();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
124
src/Audio.cpp
Normal file
124
src/Audio.cpp
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
#include "mumlib/Audio.hpp"
|
||||||
|
|
||||||
|
#include <boost/format.hpp>
|
||||||
|
|
||||||
|
mumlib::Audio::Audio()
|
||||||
|
:
|
||||||
|
logger(log4cpp::Category::getInstance("Mumlib.Audio")),
|
||||||
|
opusDecoder(nullptr),
|
||||||
|
opusEncoder(nullptr),
|
||||||
|
outgoingSequenceNumber(1) {
|
||||||
|
|
||||||
|
int error;
|
||||||
|
|
||||||
|
opusDecoder = opus_decoder_create(SAMPLE_RATE, 1, &error);
|
||||||
|
if (error != OPUS_OK) {
|
||||||
|
throw AudioException((boost::format("failed to initialize OPUS decoder: %s") % opus_strerror(error)).str());
|
||||||
|
}
|
||||||
|
|
||||||
|
opusEncoder = opus_encoder_create(SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP, &error);
|
||||||
|
if (error != OPUS_OK) {
|
||||||
|
throw AudioException((boost::format("failed to initialize OPUS encoder: %s") % opus_strerror(error)).str());
|
||||||
|
}
|
||||||
|
|
||||||
|
opus_encoder_ctl(opusEncoder, OPUS_SET_VBR(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
mumlib::Audio::~Audio() {
|
||||||
|
if (opusDecoder) {
|
||||||
|
opus_decoder_destroy(opusDecoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opusEncoder) {
|
||||||
|
opus_encoder_destroy(opusEncoder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int mumlib::Audio::decodeAudioPacket(AudioPacketType type,
|
||||||
|
uint8_t *inputBuffer,
|
||||||
|
int inputLength,
|
||||||
|
int16_t *pcmBuffer,
|
||||||
|
int pcmBufferSize) {
|
||||||
|
|
||||||
|
if (type != AudioPacketType::OPUS) {
|
||||||
|
throw AudioException("codecs other than OPUS are not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
int target = inputBuffer[0] & 0x1F;
|
||||||
|
|
||||||
|
int64_t sessionId;
|
||||||
|
int64_t sequenceNumber;
|
||||||
|
int64_t opusDataLength;
|
||||||
|
|
||||||
|
std::array<int64_t *, 3> varInts = {&sessionId, &sequenceNumber, &opusDataLength};
|
||||||
|
|
||||||
|
int dataPointer = 1;
|
||||||
|
for (int64_t *val : varInts) {
|
||||||
|
VarInt varInt(&inputBuffer[dataPointer]);
|
||||||
|
*val = varInt.getValue();
|
||||||
|
dataPointer += varInt.getEncoded().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lastPacket = (opusDataLength & 0x2000) != 0;
|
||||||
|
opusDataLength = opusDataLength & 0x1fff;
|
||||||
|
|
||||||
|
int outputSize = opus_decode(opusDecoder,
|
||||||
|
reinterpret_cast<const unsigned char *>(&inputBuffer[dataPointer]),
|
||||||
|
opusDataLength,
|
||||||
|
pcmBuffer,
|
||||||
|
pcmBufferSize,
|
||||||
|
0);
|
||||||
|
|
||||||
|
if (outputSize <= 0) {
|
||||||
|
throw AudioException((boost::format("failed to decode %d B of OPUS data: %s") % inputLength %
|
||||||
|
opus_strerror(outputSize)).str());
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
"Received %d B of OPUS data, decoded to %d B (target: %d, sessionID: %ld, seq num: %ld, last: %d).",
|
||||||
|
opusDataLength,
|
||||||
|
outputSize,
|
||||||
|
target,
|
||||||
|
sessionId,
|
||||||
|
sequenceNumber,
|
||||||
|
lastPacket);
|
||||||
|
|
||||||
|
|
||||||
|
return outputSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mumlib::Audio::encodeAudioPacket(int target, int16_t *inputPcmBuffer, int inputLength, uint8_t *outputBuffer,
|
||||||
|
int outputBufferSize) {
|
||||||
|
//if (!bPreviousVoice)
|
||||||
|
// opus_encoder_ctl(opusState, OPUS_RESET_STATE, NULL); //todo do something with it
|
||||||
|
|
||||||
|
std::vector<uint8_t> header;
|
||||||
|
|
||||||
|
header.push_back(0x80 | target);
|
||||||
|
|
||||||
|
auto sequenceNumberEnc = VarInt(outgoingSequenceNumber).getEncoded();
|
||||||
|
header.insert(header.end(), sequenceNumberEnc.begin(), sequenceNumberEnc.end());
|
||||||
|
|
||||||
|
uint8_t tmpOpusBuffer[1024];
|
||||||
|
const int outputSize = opus_encode(opusEncoder,
|
||||||
|
inputPcmBuffer,
|
||||||
|
inputLength,
|
||||||
|
tmpOpusBuffer,
|
||||||
|
min(outputBufferSize, 1024)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (outputSize <= 0) {
|
||||||
|
throw AudioException((boost::format("failed to encode %d B of PCM data: %s") % inputLength %
|
||||||
|
opus_strerror(outputSize)).str());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto outputSizeEnc = VarInt(outputSize).getEncoded();
|
||||||
|
header.insert(header.end(), outputSizeEnc.begin(), outputSizeEnc.end());
|
||||||
|
|
||||||
|
memcpy(outputBuffer, &header[0], header.size());
|
||||||
|
memcpy(outputBuffer + header.size(), tmpOpusBuffer, outputSize);
|
||||||
|
|
||||||
|
outgoingSequenceNumber += 2;
|
||||||
|
|
||||||
|
return outputSize + header.size();
|
||||||
|
}
|
182
src/Callback.cpp
Normal file
182
src/Callback.cpp
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
#include "mumlib/Callback.hpp"
|
||||||
|
|
||||||
|
#include <boost/core/noncopyable.hpp>
|
||||||
|
#include <log4cpp/Category.hh>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace mumlib;
|
||||||
|
|
||||||
|
namespace mumlib {
|
||||||
|
struct _BasicCallback_Private : boost::noncopyable {
|
||||||
|
public:
|
||||||
|
_BasicCallback_Private() : logger(log4cpp::Category::getInstance("BasicCallback")) { }
|
||||||
|
|
||||||
|
log4cpp::Category &logger;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
mumlib::BasicCallback::BasicCallback() {
|
||||||
|
impl = new _BasicCallback_Private();
|
||||||
|
}
|
||||||
|
|
||||||
|
mumlib::BasicCallback::~BasicCallback() {
|
||||||
|
delete impl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::BasicCallback::version_callback(
|
||||||
|
uint16_t major,
|
||||||
|
uint8_t minor,
|
||||||
|
uint8_t patch,
|
||||||
|
string release,
|
||||||
|
string os,
|
||||||
|
string os_version) {
|
||||||
|
impl->logger.debug("Version Callback: v%d.%d.%d. %s/%s/%s\n", major, minor, patch, release.c_str(), os.c_str(),
|
||||||
|
os_version.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::BasicCallback::audio_callback(
|
||||||
|
uint8_t *pcm_data,
|
||||||
|
uint32_t pcm_data_size) {
|
||||||
|
impl->logger.debug("Received %d bytes of raw PCM data.", pcm_data_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::BasicCallback::unsupported_audio_callback(
|
||||||
|
uint8_t *encoded_audio_data,
|
||||||
|
uint32_t encoded_audio_data_size) {
|
||||||
|
impl->logger.debug("Received %d bytes of encoded audio data.", encoded_audio_data_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::BasicCallback::serversync_callback(
|
||||||
|
string welcome_text,
|
||||||
|
int32_t session,
|
||||||
|
int32_t max_bandwidth,
|
||||||
|
int64_t permissions) {
|
||||||
|
impl->logger.debug("Text: %s, session: %d, max bandwidth: %d, permissions: %d", welcome_text.c_str(), session,
|
||||||
|
max_bandwidth, permissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::BasicCallback::channelremove_callback(uint32_t channel_id) { }
|
||||||
|
|
||||||
|
void mumlib::BasicCallback::channelstate_callback(
|
||||||
|
string name,
|
||||||
|
int32_t channel_id,
|
||||||
|
int32_t parent,
|
||||||
|
string description,
|
||||||
|
vector<uint32_t> links,
|
||||||
|
vector<uint32_t> inks_add,
|
||||||
|
vector<uint32_t> links_remove,
|
||||||
|
bool temporary,
|
||||||
|
int32_t position) {
|
||||||
|
impl->logger.debug("Obtained channel state %d: %s, %s", channel_id, name.c_str(), description.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::BasicCallback::userremove_callback(
|
||||||
|
uint32_t session,
|
||||||
|
int32_t actor,
|
||||||
|
string reason,
|
||||||
|
bool ban) { }
|
||||||
|
|
||||||
|
void mumlib::BasicCallback::userstate_callback(
|
||||||
|
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) { }
|
||||||
|
|
||||||
|
void mumlib::BasicCallback::banlist_callback(
|
||||||
|
uint8_t *ip_data,
|
||||||
|
uint32_t ip_data_size,
|
||||||
|
uint32_t mask,
|
||||||
|
string name,
|
||||||
|
string hash,
|
||||||
|
string reason,
|
||||||
|
string start,
|
||||||
|
int32_t duration) { }
|
||||||
|
|
||||||
|
void mumlib::BasicCallback::textmessage_callback(
|
||||||
|
uint32_t actor,
|
||||||
|
uint32_t n_session,
|
||||||
|
uint32_t *session,
|
||||||
|
uint32_t n_channel_id,
|
||||||
|
uint32_t *channel_id,
|
||||||
|
uint32_t n_tree_id,
|
||||||
|
uint32_t *tree_id,
|
||||||
|
string message) { }
|
||||||
|
|
||||||
|
void mumlib::BasicCallback::permissiondenied_callback(
|
||||||
|
int32_t permission,
|
||||||
|
int32_t channel_id,
|
||||||
|
int32_t session,
|
||||||
|
string reason,
|
||||||
|
int32_t deny_type,
|
||||||
|
string name) { }
|
||||||
|
|
||||||
|
void mumlib::BasicCallback::acl_callback() { }
|
||||||
|
|
||||||
|
void mumlib::BasicCallback::queryusers_callback(
|
||||||
|
uint32_t n_ids,
|
||||||
|
uint32_t *ids,
|
||||||
|
uint32_t n_names,
|
||||||
|
string *names) { }
|
||||||
|
|
||||||
|
void mumlib::BasicCallback::cryptsetup_callback(
|
||||||
|
uint32_t key_size,
|
||||||
|
uint8_t *key,
|
||||||
|
uint32_t client_nonce_size,
|
||||||
|
uint8_t *client_nonce,
|
||||||
|
uint32_t server_nonce_size,
|
||||||
|
uint8_t *server_nonce) { }
|
||||||
|
|
||||||
|
void mumlib::BasicCallback::contextactionmodify_callback(
|
||||||
|
string action,
|
||||||
|
string text,
|
||||||
|
uint32_t m_context,
|
||||||
|
uint32_t operation) { }
|
||||||
|
|
||||||
|
void mumlib::BasicCallback::contextaction_callback(
|
||||||
|
int32_t session,
|
||||||
|
int32_t channel_id,
|
||||||
|
string action) { }
|
||||||
|
|
||||||
|
void mumlib::BasicCallback::userlist_callback(
|
||||||
|
uint32_t user_id,
|
||||||
|
string name,
|
||||||
|
string last_seen,
|
||||||
|
int32_t last_channel) { }
|
||||||
|
|
||||||
|
void mumlib::BasicCallback::voicetarget_callback() { }
|
||||||
|
|
||||||
|
void mumlib::BasicCallback::permissionquery_callback(
|
||||||
|
int32_t channel_id,
|
||||||
|
uint32_t permissions,
|
||||||
|
int32_t flush) { }
|
||||||
|
|
||||||
|
void mumlib::BasicCallback::codecversion_callback(
|
||||||
|
int32_t alpha,
|
||||||
|
int32_t beta,
|
||||||
|
uint32_t prefer_alpha,
|
||||||
|
int32_t opus) { }
|
||||||
|
|
||||||
|
void mumlib::BasicCallback::userstats_callback() { }
|
||||||
|
|
||||||
|
void mumlib::BasicCallback::requestblob_callback() { }
|
||||||
|
|
||||||
|
void mumlib::BasicCallback::serverconfig_callback(
|
||||||
|
uint32_t max_bandwidth,
|
||||||
|
string welcome_text,
|
||||||
|
uint32_t allow_html,
|
||||||
|
uint32_t message_length,
|
||||||
|
uint32_t image_message_length) { }
|
||||||
|
|
||||||
|
void mumlib::BasicCallback::suggestconfig_callback(
|
||||||
|
uint32_t version,
|
||||||
|
uint32_t positional,
|
||||||
|
uint32_t push_to_talk) { }
|
286
src/CryptState.cpp
Normal file
286
src/CryptState.cpp
Normal file
@ -0,0 +1,286 @@
|
|||||||
|
/* Copyright (C) 2005-2011, Thorvald Natvig <thorvald@natvig.com>
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
- Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
- Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
- Neither the name of the Mumble Developers nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from this
|
||||||
|
software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
|
||||||
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This code implements OCB-AES128.
|
||||||
|
* In the US, OCB is covered by patents. The inventor has given a license
|
||||||
|
* to all programs distributed under the GPL.
|
||||||
|
* Mumble is BSD (revised) licensed, meaning you can use the code in a
|
||||||
|
* closed-source program. If you do, you'll have to either replace
|
||||||
|
* OCB with something else or get yourself a license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "mumlib/CryptState.hpp"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
mumlib::CryptState::CryptState() {
|
||||||
|
for (int i = 0; i < 0x100; i++)
|
||||||
|
decrypt_history[i] = 0;
|
||||||
|
bInit = false;
|
||||||
|
uiGood = uiLate = uiLost = uiResync = 0;
|
||||||
|
uiRemoteGood = uiRemoteLate = uiRemoteLost = uiRemoteResync = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mumlib::CryptState::isValid() const {
|
||||||
|
return bInit;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::CryptState::setKey(const unsigned char *rkey, const unsigned char *eiv, const unsigned char *div) {
|
||||||
|
memcpy(raw_key, rkey, AES_BLOCK_SIZE);
|
||||||
|
memcpy(encrypt_iv, eiv, AES_BLOCK_SIZE);
|
||||||
|
memcpy(decrypt_iv, div, AES_BLOCK_SIZE);
|
||||||
|
AES_set_encrypt_key(raw_key, 128, &encrypt_key);
|
||||||
|
AES_set_decrypt_key(raw_key, 128, &decrypt_key);
|
||||||
|
bInit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::CryptState::setDecryptIV(const unsigned char *iv) {
|
||||||
|
memcpy(decrypt_iv, iv, AES_BLOCK_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::CryptState::encrypt(const unsigned char *source, unsigned char *dst, unsigned int plain_length) {
|
||||||
|
unsigned char tag[AES_BLOCK_SIZE];
|
||||||
|
|
||||||
|
// First, increase our IV.
|
||||||
|
for (int i = 0; i < AES_BLOCK_SIZE; i++)
|
||||||
|
if (++encrypt_iv[i])
|
||||||
|
break;
|
||||||
|
|
||||||
|
ocb_encrypt(source, dst + 4, plain_length, encrypt_iv, tag);
|
||||||
|
|
||||||
|
dst[0] = encrypt_iv[0];
|
||||||
|
dst[1] = tag[0];
|
||||||
|
dst[2] = tag[1];
|
||||||
|
dst[3] = tag[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mumlib::CryptState::decrypt(const unsigned char *source, unsigned char *dst, unsigned int crypted_length) {
|
||||||
|
if (crypted_length < 4)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
unsigned int plain_length = crypted_length - 4;
|
||||||
|
|
||||||
|
unsigned char saveiv[AES_BLOCK_SIZE];
|
||||||
|
unsigned char ivbyte = source[0];
|
||||||
|
bool restore = false;
|
||||||
|
unsigned char tag[AES_BLOCK_SIZE];
|
||||||
|
|
||||||
|
int lost = 0;
|
||||||
|
int late = 0;
|
||||||
|
|
||||||
|
memcpy(saveiv, decrypt_iv, AES_BLOCK_SIZE);
|
||||||
|
|
||||||
|
if (((decrypt_iv[0] + 1) & 0xFF) == ivbyte) {
|
||||||
|
// In order as expected.
|
||||||
|
if (ivbyte > decrypt_iv[0]) {
|
||||||
|
decrypt_iv[0] = ivbyte;
|
||||||
|
} else if (ivbyte < decrypt_iv[0]) {
|
||||||
|
decrypt_iv[0] = ivbyte;
|
||||||
|
for (int i = 1; i < AES_BLOCK_SIZE; i++)
|
||||||
|
if (++decrypt_iv[i])
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// This is either out of order or a repeat.
|
||||||
|
|
||||||
|
int diff = ivbyte - decrypt_iv[0];
|
||||||
|
if (diff > 128)
|
||||||
|
diff = diff - 256;
|
||||||
|
else if (diff < -128)
|
||||||
|
diff = diff + 256;
|
||||||
|
|
||||||
|
if ((ivbyte < decrypt_iv[0]) && (diff > -30) && (diff < 0)) {
|
||||||
|
// Late packet, but no wraparound.
|
||||||
|
late = 1;
|
||||||
|
lost = -1;
|
||||||
|
decrypt_iv[0] = ivbyte;
|
||||||
|
restore = true;
|
||||||
|
} else if ((ivbyte > decrypt_iv[0]) && (diff > -30) && (diff < 0)) {
|
||||||
|
// Last was 0x02, here comes 0xff from last round
|
||||||
|
late = 1;
|
||||||
|
lost = -1;
|
||||||
|
decrypt_iv[0] = ivbyte;
|
||||||
|
for (int i = 1; i < AES_BLOCK_SIZE; i++)
|
||||||
|
if (decrypt_iv[i]--)
|
||||||
|
break;
|
||||||
|
restore = true;
|
||||||
|
} else if ((ivbyte > decrypt_iv[0]) && (diff > 0)) {
|
||||||
|
// Lost a few packets, but beyond that we're good.
|
||||||
|
lost = ivbyte - decrypt_iv[0] - 1;
|
||||||
|
decrypt_iv[0] = ivbyte;
|
||||||
|
} else if ((ivbyte < decrypt_iv[0]) && (diff > 0)) {
|
||||||
|
// Lost a few packets, and wrapped around
|
||||||
|
lost = 256 - decrypt_iv[0] + ivbyte - 1;
|
||||||
|
decrypt_iv[0] = ivbyte;
|
||||||
|
for (int i = 1; i < AES_BLOCK_SIZE; i++)
|
||||||
|
if (++decrypt_iv[i])
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decrypt_history[decrypt_iv[0]] == decrypt_iv[1]) {
|
||||||
|
memcpy(decrypt_iv, saveiv, AES_BLOCK_SIZE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ocb_decrypt(source + 4, dst, plain_length, decrypt_iv, tag);
|
||||||
|
|
||||||
|
if (memcmp(tag, source + 1, 3) != 0) {
|
||||||
|
memcpy(decrypt_iv, saveiv, AES_BLOCK_SIZE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
decrypt_history[decrypt_iv[0]] = decrypt_iv[1];
|
||||||
|
|
||||||
|
if (restore)
|
||||||
|
memcpy(decrypt_iv, saveiv, AES_BLOCK_SIZE);
|
||||||
|
|
||||||
|
uiGood++;
|
||||||
|
uiLate += late;
|
||||||
|
uiLost += lost;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BLOCKSIZE 2
|
||||||
|
#define SHIFTBITS 63
|
||||||
|
typedef uint64_t subblock;
|
||||||
|
|
||||||
|
#define SWAP64(x) ({register uint64_t __out, __in = (x); __asm__("bswap %q0" : "=r"(__out) : "0"(__in)); __out;})
|
||||||
|
#define SWAPPED(x) SWAP64(x)
|
||||||
|
|
||||||
|
typedef subblock keyblock[BLOCKSIZE];
|
||||||
|
|
||||||
|
static void inline XOR(subblock *dst, const subblock *a, const subblock *b) {
|
||||||
|
for (int i = 0; i < BLOCKSIZE; i++) {
|
||||||
|
dst[i] = a[i] ^ b[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void inline S2(subblock *block) {
|
||||||
|
subblock carry = SWAPPED(block[0]) >> SHIFTBITS;
|
||||||
|
for (int i = 0; i < BLOCKSIZE - 1; i++)
|
||||||
|
block[i] = SWAPPED((SWAPPED(block[i]) << 1) | (SWAPPED(block[i + 1]) >> SHIFTBITS));
|
||||||
|
block[BLOCKSIZE - 1] = SWAPPED((SWAPPED(block[BLOCKSIZE - 1]) << 1) ^ (carry * 0x87));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void inline S3(subblock *block) {
|
||||||
|
subblock carry = SWAPPED(block[0]) >> SHIFTBITS;
|
||||||
|
for (int i = 0; i < BLOCKSIZE - 1; i++)
|
||||||
|
block[i] ^= SWAPPED((SWAPPED(block[i]) << 1) | (SWAPPED(block[i + 1]) >> SHIFTBITS));
|
||||||
|
block[BLOCKSIZE - 1] ^= SWAPPED((SWAPPED(block[BLOCKSIZE - 1]) << 1) ^ (carry * 0x87));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void inline ZERO(keyblock &block) {
|
||||||
|
for (int i = 0; i < BLOCKSIZE; i++)
|
||||||
|
block[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define AESencrypt(src, dst, key) AES_encrypt(reinterpret_cast<const unsigned char *>(src),reinterpret_cast<unsigned char *>(dst), key);
|
||||||
|
#define AESdecrypt(src, dst, key) AES_decrypt(reinterpret_cast<const unsigned char *>(src),reinterpret_cast<unsigned char *>(dst), key);
|
||||||
|
|
||||||
|
void mumlib::CryptState::ocb_encrypt(const unsigned char *plain, unsigned char *encrypted, unsigned int len,
|
||||||
|
const unsigned char *nonce, unsigned char *tag) {
|
||||||
|
keyblock checksum, delta, tmp, pad;
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
AESencrypt(nonce, delta, &encrypt_key);
|
||||||
|
ZERO(checksum);
|
||||||
|
|
||||||
|
while (len > AES_BLOCK_SIZE) {
|
||||||
|
S2(delta);
|
||||||
|
XOR(tmp, delta, reinterpret_cast<const subblock *>(plain));
|
||||||
|
AESencrypt(tmp, tmp, &encrypt_key);
|
||||||
|
XOR(reinterpret_cast<subblock *>(encrypted), delta, tmp);
|
||||||
|
XOR(checksum, checksum, reinterpret_cast<const subblock *>(plain));
|
||||||
|
len -= AES_BLOCK_SIZE;
|
||||||
|
plain += AES_BLOCK_SIZE;
|
||||||
|
encrypted += AES_BLOCK_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
S2(delta);
|
||||||
|
ZERO(tmp);
|
||||||
|
tmp[BLOCKSIZE - 1] = SWAPPED(len * 8);
|
||||||
|
XOR(tmp, tmp, delta);
|
||||||
|
AESencrypt(tmp, pad, &encrypt_key);
|
||||||
|
memcpy(tmp, plain, len);
|
||||||
|
memcpy(reinterpret_cast<unsigned char *>(tmp) + len, reinterpret_cast<const unsigned char *>(pad) + len,
|
||||||
|
AES_BLOCK_SIZE - len);
|
||||||
|
XOR(checksum, checksum, tmp);
|
||||||
|
XOR(tmp, pad, tmp);
|
||||||
|
memcpy(encrypted, tmp, len);
|
||||||
|
|
||||||
|
S3(delta);
|
||||||
|
XOR(tmp, delta, checksum);
|
||||||
|
AESencrypt(tmp, tag, &encrypt_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::CryptState::ocb_decrypt(const unsigned char *encrypted, unsigned char *plain, unsigned int len,
|
||||||
|
const unsigned char *nonce, unsigned char *tag) {
|
||||||
|
keyblock checksum, delta, tmp, pad;
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
AESencrypt(nonce, delta, &encrypt_key);
|
||||||
|
ZERO(checksum);
|
||||||
|
|
||||||
|
while (len > AES_BLOCK_SIZE) {
|
||||||
|
S2(delta);
|
||||||
|
XOR(tmp, delta, reinterpret_cast<const subblock *>(encrypted));
|
||||||
|
AESdecrypt(tmp, tmp, &decrypt_key);
|
||||||
|
XOR(reinterpret_cast<subblock *>(plain), delta, tmp);
|
||||||
|
XOR(checksum, checksum, reinterpret_cast<const subblock *>(plain));
|
||||||
|
len -= AES_BLOCK_SIZE;
|
||||||
|
plain += AES_BLOCK_SIZE;
|
||||||
|
encrypted += AES_BLOCK_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
S2(delta);
|
||||||
|
ZERO(tmp);
|
||||||
|
tmp[BLOCKSIZE - 1] = SWAPPED(len * 8);
|
||||||
|
XOR(tmp, tmp, delta);
|
||||||
|
AESencrypt(tmp, pad, &encrypt_key);
|
||||||
|
memset(tmp, 0, AES_BLOCK_SIZE);
|
||||||
|
memcpy(tmp, encrypted, len);
|
||||||
|
XOR(tmp, tmp, pad);
|
||||||
|
XOR(checksum, checksum, tmp);
|
||||||
|
memcpy(plain, tmp, len);
|
||||||
|
|
||||||
|
S3(delta);
|
||||||
|
XOR(tmp, delta, checksum);
|
||||||
|
AESencrypt(tmp, tag, &encrypt_key);
|
||||||
|
}
|
520
src/Transport.cpp
Normal file
520
src/Transport.cpp
Normal file
@ -0,0 +1,520 @@
|
|||||||
|
#include "mumlib/Transport.hpp"
|
||||||
|
|
||||||
|
#include "mumble.pb.h"
|
||||||
|
|
||||||
|
#include <boost/format.hpp>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
static boost::posix_time::seconds PING_INTERVAL(5);
|
||||||
|
|
||||||
|
const long CLIENT_VERSION = 0x010203;
|
||||||
|
const string CLIENT_RELEASE("Mumlib");
|
||||||
|
const string CLIENT_OS("OS Unknown");
|
||||||
|
const string CLIENT_OS_VERSION("1");
|
||||||
|
|
||||||
|
static map<MumbleProto::Reject_RejectType, string> rejectMessages = {
|
||||||
|
{MumbleProto::Reject_RejectType_None, "no reason provided"},
|
||||||
|
{MumbleProto::Reject_RejectType_WrongVersion, "wrong version"},
|
||||||
|
{MumbleProto::Reject_RejectType_InvalidUsername, "invalid username"},
|
||||||
|
{MumbleProto::Reject_RejectType_WrongUserPW, "wrong user password"},
|
||||||
|
{MumbleProto::Reject_RejectType_WrongServerPW, "wrong server password"},
|
||||||
|
{MumbleProto::Reject_RejectType_UsernameInUse, "username in use"},
|
||||||
|
{MumbleProto::Reject_RejectType_ServerFull, "server full"},
|
||||||
|
{MumbleProto::Reject_RejectType_NoCertificate, "no certificate provided"},
|
||||||
|
{MumbleProto::Reject_RejectType_AuthenticatorFail, "authenticator fail"}
|
||||||
|
};
|
||||||
|
|
||||||
|
mumlib::Transport::Transport(
|
||||||
|
io_service &ioService,
|
||||||
|
mumlib::ProcessControlMessageFunction processMessageFunc,
|
||||||
|
ProcessEncodedAudioPacketFunction processEncodedAudioPacketFunction,
|
||||||
|
bool noUdp) :
|
||||||
|
logger(log4cpp::Category::getInstance("Mumlib.Transport")),
|
||||||
|
ioService(ioService),
|
||||||
|
processMessageFunction(processMessageFunc),
|
||||||
|
processEncodedAudioPacketFunction(processEncodedAudioPacketFunction),
|
||||||
|
noUdp(noUdp),
|
||||||
|
state(ConnectionState::NOT_CONNECTED),
|
||||||
|
udpSocket(ioService),
|
||||||
|
sslContext(ssl::context::sslv23),
|
||||||
|
sslSocket(ioService, sslContext),
|
||||||
|
pingTimer(ioService, PING_INTERVAL),
|
||||||
|
asyncBufferPool(max(MAX_UDP_LENGTH, MAX_TCP_LENGTH)) {
|
||||||
|
|
||||||
|
pingTimer.async_wait(boost::bind(&Transport::pingTimerTick, this, _1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::Transport::connect(
|
||||||
|
std::string host,
|
||||||
|
int port,
|
||||||
|
std::string user,
|
||||||
|
std::string password) {
|
||||||
|
|
||||||
|
state = ConnectionState::IN_PROGRESS;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (not noUdp) {
|
||||||
|
ip::udp::resolver resolverUdp(ioService);
|
||||||
|
ip::udp::resolver::query queryUdp(ip::udp::v4(), host, to_string(port));
|
||||||
|
udpReceiverEndpoint = *resolverUdp.resolve(queryUdp);
|
||||||
|
udpSocket.open(ip::udp::v4());
|
||||||
|
|
||||||
|
doReceiveUdp();
|
||||||
|
}
|
||||||
|
|
||||||
|
ip::tcp::resolver resolverTcp(ioService);
|
||||||
|
ip::tcp::resolver::query queryTcp(host, to_string(port));
|
||||||
|
|
||||||
|
async_connect(sslSocket.lowest_layer(), resolverTcp.resolve(queryTcp),
|
||||||
|
bind(&Transport::sslConnectHandler, this, boost::asio::placeholders::error));
|
||||||
|
} catch (runtime_error &exp) {
|
||||||
|
throwTransportException(string("failed to establish connection: ") + exp.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::Transport::disconnect() {
|
||||||
|
|
||||||
|
state = ConnectionState::NOT_CONNECTED;
|
||||||
|
|
||||||
|
sslSocket.shutdown();
|
||||||
|
sslSocket.lowest_layer().shutdown(tcp::socket::shutdown_both);
|
||||||
|
|
||||||
|
udpSocket.shutdown(udp::socket::shutdown_both);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
logger.info("Sending version information.");
|
||||||
|
|
||||||
|
sendControlMessagePrivate(MessageType::VERSION, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::Transport::sendAuthentication() {
|
||||||
|
string user, password;
|
||||||
|
tie(user, password) = credentials;
|
||||||
|
|
||||||
|
MumbleProto::Authenticate authenticate;
|
||||||
|
authenticate.set_username(user);
|
||||||
|
authenticate.set_password(password);
|
||||||
|
authenticate.clear_celt_versions();
|
||||||
|
authenticate.clear_tokens();
|
||||||
|
authenticate.set_opus(true);
|
||||||
|
|
||||||
|
logger.info("Sending authententication.");
|
||||||
|
|
||||||
|
sendControlMessagePrivate(MessageType::AUTHENTICATE, authenticate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::Transport::sendSslPing() {
|
||||||
|
MumbleProto::Ping ping;
|
||||||
|
ping.set_timestamp(std::time(nullptr));
|
||||||
|
|
||||||
|
logger.debug("Sending SSL ping.");
|
||||||
|
|
||||||
|
sendControlMessagePrivate(MessageType::PING, ping);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool mumlib::Transport::isUdpActive() {
|
||||||
|
return udpActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::Transport::doReceiveUdp() {
|
||||||
|
udpSocket.async_receive_from(
|
||||||
|
buffer(udpIncomingBuffer, MAX_UDP_LENGTH),
|
||||||
|
udpReceiverEndpoint,
|
||||||
|
[this](const boost::system::error_code &ec, size_t bytesTransferred) {
|
||||||
|
if (!ec and bytesTransferred > 0) {
|
||||||
|
logger.debug("Received UDP packet of %d B.", bytesTransferred);
|
||||||
|
|
||||||
|
if (not cryptState.isValid()) {
|
||||||
|
throwTransportException("received UDP packet before CRYPT SETUP message");
|
||||||
|
} else {
|
||||||
|
lastReceivedUdpPacketTimestamp = std::chrono::system_clock::now();
|
||||||
|
|
||||||
|
if (udpActive == false) {
|
||||||
|
udpActive = true;
|
||||||
|
logger.info("UDP is up.");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t plainBuffer[1024];
|
||||||
|
const int plainBufferLength = bytesTransferred - 4;
|
||||||
|
|
||||||
|
bool success = cryptState.decrypt(
|
||||||
|
udpIncomingBuffer, plainBuffer, bytesTransferred);
|
||||||
|
|
||||||
|
if (not success) {
|
||||||
|
throwTransportException("UDP packet decryption failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
processAudioPacket(plainBuffer, plainBufferLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
doReceiveUdp();
|
||||||
|
} else {
|
||||||
|
throwTransportException("UDP receive failed: " + ec.message());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::Transport::sslConnectHandler(const boost::system::error_code &error) {
|
||||||
|
if (!error) {
|
||||||
|
sslSocket.async_handshake(ssl::stream_base::client,
|
||||||
|
boost::bind(&Transport::sslHandshakeHandler, this,
|
||||||
|
boost::asio::placeholders::error));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throwTransportException((boost::format("Connect failed: %s.") % error.message()).str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::Transport::sslHandshakeHandler(const boost::system::error_code &error) {
|
||||||
|
if (!error) {
|
||||||
|
doReceiveSsl();
|
||||||
|
|
||||||
|
sendVersion();
|
||||||
|
sendAuthentication();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throwTransportException((boost::format("Handshake failed: %s.") % error.message()).str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::Transport::pingTimerTick(const boost::system::error_code &e) {
|
||||||
|
if (state == ConnectionState::CONNECTED) {
|
||||||
|
|
||||||
|
sendSslPing();
|
||||||
|
|
||||||
|
if (not noUdp) {
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
sendUdpPing();
|
||||||
|
|
||||||
|
if (udpActive) {
|
||||||
|
const int lastUdpReceivedMilliseconds = duration_cast<milliseconds>(
|
||||||
|
system_clock::now() - lastReceivedUdpPacketTimestamp).count();
|
||||||
|
|
||||||
|
if (lastUdpReceivedMilliseconds > PING_INTERVAL.total_milliseconds() + 1000) {
|
||||||
|
udpActive = false;
|
||||||
|
logger.warn("Didn't receive UDP ping in %d ms, falling back to TCP.", lastUdpReceivedMilliseconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
throwTransportException("maximum allowed data length is %d" + to_string(MAX_UDP_LENGTH - 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *encryptedMsgBuff = asyncBufferPool.malloc();
|
||||||
|
const int encryptedMsgLength = length + 4;
|
||||||
|
|
||||||
|
cryptState.encrypt(buff, reinterpret_cast<uint8_t *>(encryptedMsgBuff), length);
|
||||||
|
|
||||||
|
logger.debug("Sending %d B of data UDP asynchronously.", encryptedMsgLength);
|
||||||
|
|
||||||
|
udpSocket.async_send_to(
|
||||||
|
boost::asio::buffer(encryptedMsgBuff, length + 4),
|
||||||
|
udpReceiverEndpoint,
|
||||||
|
[this, encryptedMsgBuff](const boost::system::error_code &ec, size_t bytesTransferred) {
|
||||||
|
asyncBufferPool.free(encryptedMsgBuff);
|
||||||
|
if (!ec and bytesTransferred > 0) {
|
||||||
|
logger.debug("Sent %d B via UDP.", bytesTransferred);
|
||||||
|
} else {
|
||||||
|
throwTransportException("UDP send failed: " + ec.message());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::Transport::doReceiveSsl() {
|
||||||
|
async_read(
|
||||||
|
sslSocket,
|
||||||
|
boost::asio::buffer(sslIncomingBuffer, MAX_TCP_LENGTH),
|
||||||
|
[this](const boost::system::error_code &error, size_t bytesTransferred) -> size_t {
|
||||||
|
if (bytesTransferred < 6) {
|
||||||
|
// we need the message header to determine the payload length
|
||||||
|
return 6 - bytesTransferred;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int payloadSize = ntohl(*reinterpret_cast<uint32_t *>(sslIncomingBuffer + 2));
|
||||||
|
size_t remaining = payloadSize + 6 - bytesTransferred;
|
||||||
|
remaining = max(remaining, (size_t) 0);
|
||||||
|
|
||||||
|
return remaining;
|
||||||
|
},
|
||||||
|
[this](const boost::system::error_code &ec, size_t bytesTransferred) {
|
||||||
|
if (!ec and bytesTransferred > 0) {
|
||||||
|
|
||||||
|
int messageType = ntohs(*reinterpret_cast<uint16_t *>(sslIncomingBuffer));
|
||||||
|
|
||||||
|
logger.debug("Received %d B of data (%d B payload, type %d).", bytesTransferred,
|
||||||
|
bytesTransferred - 6, messageType);
|
||||||
|
|
||||||
|
processMessageInternal(
|
||||||
|
static_cast<MessageType>(messageType),
|
||||||
|
&sslIncomingBuffer[6],
|
||||||
|
bytesTransferred - 6);
|
||||||
|
|
||||||
|
doReceiveSsl();
|
||||||
|
} else {
|
||||||
|
throwTransportException("receive failed: " + ec.message());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::Transport::processMessageInternal(MessageType messageType, uint8_t *buffer, int length) {
|
||||||
|
switch (messageType) {
|
||||||
|
|
||||||
|
case MessageType::UDPTUNNEL: {
|
||||||
|
logger.debug("Received %d B of encoded audio data via TCP.", length);
|
||||||
|
processAudioPacket(buffer, length);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MessageType::AUTHENTICATE: {
|
||||||
|
logger.warn("Authenticate message received after authenticated.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MessageType::PING: {
|
||||||
|
MumbleProto::Ping ping;
|
||||||
|
ping.ParseFromArray(buffer, length);
|
||||||
|
stringstream log;
|
||||||
|
log << "Received ping.";
|
||||||
|
if (ping.has_good()) {
|
||||||
|
log << " good: " << ping.good();
|
||||||
|
}
|
||||||
|
if (ping.has_late()) {
|
||||||
|
log << " late: " << ping.late();
|
||||||
|
}
|
||||||
|
if (ping.has_lost()) {
|
||||||
|
log << " lost: " << ping.lost();
|
||||||
|
}
|
||||||
|
if (ping.has_tcp_ping_avg()) {
|
||||||
|
log << " TCP avg: " << ping.tcp_ping_avg() << " ms";
|
||||||
|
}
|
||||||
|
if (ping.has_udp_ping_avg()) {
|
||||||
|
log << " UDP avg: " << ping.udp_ping_avg() << " ms";
|
||||||
|
}
|
||||||
|
logger.debug(log.str());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MessageType::REJECT: {
|
||||||
|
MumbleProto::Reject reject;
|
||||||
|
reject.ParseFromArray(buffer, length);
|
||||||
|
|
||||||
|
stringstream errorMesg;
|
||||||
|
errorMesg << "failed to authenticate";
|
||||||
|
|
||||||
|
if (reject.has_type()) {
|
||||||
|
errorMesg << ": " << rejectMessages.at(reject.type());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reject.has_reason()) {
|
||||||
|
errorMesg << ", reason: " << reject.reason();
|
||||||
|
}
|
||||||
|
|
||||||
|
throwTransportException(errorMesg.str());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MessageType::SERVERSYNC: {
|
||||||
|
state = ConnectionState::CONNECTED;
|
||||||
|
|
||||||
|
logger.debug("SERVERSYNC. Calling external ProcessControlMessageFunction.");
|
||||||
|
processMessageFunction(messageType, buffer, length);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MessageType::CRYPTSETUP: {
|
||||||
|
if (not noUdp) {
|
||||||
|
MumbleProto::CryptSetup cryptsetup;
|
||||||
|
cryptsetup.ParseFromArray(buffer, length);
|
||||||
|
|
||||||
|
if (cryptsetup.client_nonce().length() != AES_BLOCK_SIZE
|
||||||
|
or cryptsetup.server_nonce().length() != AES_BLOCK_SIZE
|
||||||
|
or cryptsetup.key().length() != AES_BLOCK_SIZE) {
|
||||||
|
throwTransportException("one of cryptographic parameters has invalid length");
|
||||||
|
}
|
||||||
|
|
||||||
|
cryptState.setKey(
|
||||||
|
reinterpret_cast<const unsigned char *>(cryptsetup.key().c_str()),
|
||||||
|
reinterpret_cast<const unsigned char *>(cryptsetup.client_nonce().c_str()),
|
||||||
|
reinterpret_cast<const unsigned char *>(cryptsetup.server_nonce().c_str()));
|
||||||
|
|
||||||
|
if (not cryptState.isValid()) {
|
||||||
|
throwTransportException("crypt setup data not valid");
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Set up cryptography for UDP transport. Sending UDP ping.");
|
||||||
|
|
||||||
|
sendUdpPing();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
logger.info("Ignoring crypt setup message, because UDP is disabled.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MessageType::VOICETARGET: {
|
||||||
|
MumbleProto::VoiceTarget voiceTarget;
|
||||||
|
voiceTarget.ParseFromArray(buffer, length);
|
||||||
|
logger.warn("VoiceTarget Message: I don't think the server ever sends this structure....");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: {
|
||||||
|
logger.debug("Calling external ProcessControlMessageFunction.");
|
||||||
|
processMessageFunction(messageType, buffer, length);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::Transport::sendUdpPing() {
|
||||||
|
logger.debug("Sending UDP ping.");
|
||||||
|
|
||||||
|
vector<uint8_t> message;
|
||||||
|
message.push_back(0x20);
|
||||||
|
|
||||||
|
auto timestampVarint = VarInt(time(nullptr)).getEncoded();
|
||||||
|
message.insert(message.end(), timestampVarint.begin(), timestampVarint.end());
|
||||||
|
|
||||||
|
sendUdpAsync(&message[0], message.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::Transport::sendSsl(uint8_t *buff, int length) {
|
||||||
|
if (length > MAX_TCP_LENGTH) {
|
||||||
|
logger.warn("Sending %d B of data via SSL. Maximal allowed data length to receive is %d B.", length,
|
||||||
|
MAX_TCP_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("Sending %d bytes of data.", length);
|
||||||
|
|
||||||
|
write(sslSocket, boost::asio::buffer(buff, length));
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::Transport::sendSslAsync(uint8_t *buff, int length) {
|
||||||
|
if (length > MAX_TCP_LENGTH) {
|
||||||
|
logger.warn("Sending %d B of data via SSL. Maximal allowed data length to receive is %d B.", length,
|
||||||
|
MAX_TCP_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *asyncBuff = asyncBufferPool.malloc();
|
||||||
|
|
||||||
|
memcpy(asyncBuff, buff, length);
|
||||||
|
|
||||||
|
logger.debug("Sending %d B of data asynchronously.", length);
|
||||||
|
|
||||||
|
async_write(
|
||||||
|
sslSocket,
|
||||||
|
boost::asio::buffer(asyncBuff, length),
|
||||||
|
[this, asyncBuff](const boost::system::error_code &ec, size_t bytesTransferred) {
|
||||||
|
asyncBufferPool.free(asyncBuff);
|
||||||
|
logger.debug("Sent %d B.", bytesTransferred);
|
||||||
|
if (!ec and bytesTransferred > 0) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throwTransportException("send failed: " + ec.message());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::Transport::sendControlMessage(MessageType type, google::protobuf::Message &message) {
|
||||||
|
if (state != ConnectionState::CONNECTED) {
|
||||||
|
logger.warn("Connection not established.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sendControlMessagePrivate(type, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::Transport::sendControlMessagePrivate(MessageType type, google::protobuf::Message &message) {
|
||||||
|
|
||||||
|
|
||||||
|
const uint16_t type_network = htons(static_cast<uint16_t>(type));
|
||||||
|
|
||||||
|
const int size = message.ByteSize();
|
||||||
|
const uint32_t size_network = htonl(size);
|
||||||
|
|
||||||
|
const int length = sizeof(type_network) + sizeof(size_network) + size;
|
||||||
|
|
||||||
|
uint8_t buff[MAX_TCP_LENGTH];
|
||||||
|
|
||||||
|
memcpy(buff, &type_network, sizeof(type_network));
|
||||||
|
|
||||||
|
memcpy(buff + sizeof(type_network), &size_network, sizeof(size_network));
|
||||||
|
|
||||||
|
message.SerializeToArray(buff + sizeof(type_network) + sizeof(size_network), size);
|
||||||
|
|
||||||
|
sendSsl(buff, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::Transport::throwTransportException(string message) {
|
||||||
|
state = ConnectionState::FAILED;
|
||||||
|
|
||||||
|
throw TransportException(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::Transport::sendEncodedAudioPacket(uint8_t *buffer, int length) {
|
||||||
|
if (state != ConnectionState::CONNECTED) {
|
||||||
|
logger.warn("Connection not established.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (udpActive) {
|
||||||
|
logger.info("Sending %d B of audio data via UDP.", length);
|
||||||
|
sendUdpAsync(buffer, length);
|
||||||
|
} else {
|
||||||
|
logger.info("Sending %d B of audio data via TCP.", length);
|
||||||
|
|
||||||
|
const uint16_t netUdptunnelType = htons(static_cast<uint16_t>(MessageType::UDPTUNNEL));
|
||||||
|
|
||||||
|
const uint32_t netLength = htonl(length);
|
||||||
|
|
||||||
|
const int packet = sizeof(netUdptunnelType) + sizeof(netLength) + length;
|
||||||
|
|
||||||
|
uint8_t packetBuff[MAX_TCP_LENGTH];
|
||||||
|
|
||||||
|
memcpy(packetBuff, &netUdptunnelType, sizeof(netUdptunnelType));
|
||||||
|
memcpy(packetBuff + sizeof(netUdptunnelType), &netLength, sizeof(netLength));
|
||||||
|
memcpy(packetBuff + sizeof(netUdptunnelType) + sizeof(netLength), buffer, length);
|
||||||
|
|
||||||
|
sendSslAsync(packetBuff, length + sizeof(netUdptunnelType) + sizeof(netLength));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::Transport::processAudioPacket(uint8_t *buff, int length) {
|
||||||
|
AudioPacketType type = static_cast<AudioPacketType >((buff[0] & 0xE0) >> 5);
|
||||||
|
switch (type) {
|
||||||
|
case AudioPacketType::CELT_Alpha:
|
||||||
|
case AudioPacketType::Speex:
|
||||||
|
case AudioPacketType::CELT_Beta:
|
||||||
|
case AudioPacketType::OPUS:
|
||||||
|
processEncodedAudioPacketFunction(type, buff, length);
|
||||||
|
break;
|
||||||
|
case AudioPacketType::Ping:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
logger.error("Not recognized audio type: %xd.", buff[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
83
src/VarInt.cpp
Normal file
83
src/VarInt.cpp
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#include "mumlib/VarInt.hpp"
|
||||||
|
|
||||||
|
#include <boost/format.hpp>
|
||||||
|
|
||||||
|
mumlib::VarInt::VarInt(int64_t value) : value(value) { }
|
||||||
|
|
||||||
|
mumlib::VarInt::VarInt(uint8_t *encoded) : value(parseVariant(encoded)) { }
|
||||||
|
|
||||||
|
mumlib::VarInt::VarInt(std::vector<uint8_t> encoded) : value(parseVariant(&encoded[0])) { }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This code was taken from Mumble source code
|
||||||
|
* https://github.com/mumble-voip/mumble/blob/master/src/PacketDataStream.h
|
||||||
|
*/
|
||||||
|
int64_t mumlib::VarInt::parseVariant(uint8_t *buffer) {
|
||||||
|
int64_t v = buffer[0];
|
||||||
|
if ((v & 0x80) == 0x00) {
|
||||||
|
return (v & 0x7F);
|
||||||
|
} else if ((v & 0xC0) == 0x80) {
|
||||||
|
return (v & 0x3F) << 8 | buffer[1];
|
||||||
|
} else if ((v & 0xF0) == 0xF0) {
|
||||||
|
switch (v & 0xFC) {
|
||||||
|
case 0xF0:
|
||||||
|
return buffer[1] << 24 | buffer[2] << 16 | buffer[3] << 8 | buffer[4];
|
||||||
|
case 0xF4:
|
||||||
|
throw VarIntException("currently unsupported 8-byte varint size");
|
||||||
|
case 0xF8:
|
||||||
|
case 0xFC:
|
||||||
|
throw VarIntException("currently negative varints aren't supported");
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if ((v & 0xF0) == 0xE0) {
|
||||||
|
return (v & 0x0F) << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
|
||||||
|
} else if ((v & 0xE0) == 0xC0) {
|
||||||
|
return (v & 0x1F) << 16 | buffer[1] << 8 | buffer[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw VarIntException("invalid varint");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> mumlib::VarInt::getEncoded() const {
|
||||||
|
std::vector<uint8_t> encoded;
|
||||||
|
int64_t i = this->value;
|
||||||
|
|
||||||
|
if ((i & 0x8000000000000000LL) && (~i < 0x100000000LL)) {
|
||||||
|
i = ~i;
|
||||||
|
if (i <= 0x3) {
|
||||||
|
encoded.push_back(0xFC | i);
|
||||||
|
return encoded;
|
||||||
|
} else {
|
||||||
|
encoded.push_back(0xF8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < 0x80) {
|
||||||
|
encoded.push_back(i);
|
||||||
|
} else if (i < 0x4000) {
|
||||||
|
encoded.push_back(0x80 | (i >> 8));
|
||||||
|
encoded.push_back(i & 0xFF);
|
||||||
|
} else if (i < 0x200000) {
|
||||||
|
encoded.push_back(0xC0 | (i >> 16));
|
||||||
|
encoded.push_back((i >> 8) & 0xFF);
|
||||||
|
encoded.push_back(i & 0xFF);
|
||||||
|
} else if (i < 0x10000000) {
|
||||||
|
encoded.push_back(0xE0 | (i >> 24));
|
||||||
|
encoded.push_back((i >> 16) & 0xFF);
|
||||||
|
encoded.push_back((i >> 8) & 0xFF);
|
||||||
|
encoded.push_back(i & 0xFF);
|
||||||
|
} else {
|
||||||
|
encoded.push_back(0xF4);
|
||||||
|
encoded.push_back((i >> 56) & 0xFF);
|
||||||
|
encoded.push_back((i >> 48) & 0xFF);
|
||||||
|
encoded.push_back((i >> 40) & 0xFF);
|
||||||
|
encoded.push_back((i >> 32) & 0xFF);
|
||||||
|
encoded.push_back((i >> 24) & 0xFF);
|
||||||
|
encoded.push_back((i >> 16) & 0xFF);
|
||||||
|
encoded.push_back((i >> 8) & 0xFF);
|
||||||
|
encoded.push_back(i & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
return encoded;
|
||||||
|
}
|
244
src/mumlib.cpp
Normal file
244
src/mumlib.cpp
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
#include "mumlib.hpp"
|
||||||
|
|
||||||
|
#include "mumlib/CryptState.hpp"
|
||||||
|
#include "mumlib/VarInt.hpp"
|
||||||
|
#include "mumlib/enums.hpp"
|
||||||
|
#include "mumlib/Transport.hpp"
|
||||||
|
#include "mumlib/Audio.hpp"
|
||||||
|
|
||||||
|
#include <boost/asio/ssl.hpp>
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
#include <log4cpp/Category.hh>
|
||||||
|
|
||||||
|
#include <mumble.pb.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace boost::asio;
|
||||||
|
|
||||||
|
using namespace mumlib;
|
||||||
|
|
||||||
|
namespace mumlib {
|
||||||
|
struct _Mumlib_Private : boost::noncopyable {
|
||||||
|
bool externalIoService;
|
||||||
|
io_service *ioService;
|
||||||
|
|
||||||
|
Callback *callback;
|
||||||
|
|
||||||
|
Transport *transport;
|
||||||
|
|
||||||
|
Audio *audio;
|
||||||
|
|
||||||
|
log4cpp::Category *logger;
|
||||||
|
|
||||||
|
bool processIncomingTcpMessage(MessageType messageType, uint8_t *buffer, int length) {
|
||||||
|
switch (messageType) {
|
||||||
|
case MessageType::VERSION: {
|
||||||
|
MumbleProto::Version version;
|
||||||
|
version.ParseFromArray(buffer, length);
|
||||||
|
callback->version_callback(
|
||||||
|
version.version() >> 16,
|
||||||
|
version.version() >> 8 & 0xff,
|
||||||
|
version.version() & 0xff,
|
||||||
|
version.release(),
|
||||||
|
version.os(),
|
||||||
|
version.os_version());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MessageType::SERVERSYNC: {
|
||||||
|
MumbleProto::ServerSync serverSync;
|
||||||
|
serverSync.ParseFromArray(buffer, length);
|
||||||
|
callback->serversync_callback(
|
||||||
|
serverSync.welcome_text(),
|
||||||
|
serverSync.session(),
|
||||||
|
serverSync.max_bandwidth(),
|
||||||
|
serverSync.permissions()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MessageType::CHANNELREMOVE: {
|
||||||
|
MumbleProto::ChannelRemove channelRemove;
|
||||||
|
channelRemove.ParseFromArray(buffer, length);
|
||||||
|
callback->channelremove_callback(channelRemove.channel_id());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MessageType::CHANNELSTATE: {
|
||||||
|
MumbleProto::ChannelState channelState;
|
||||||
|
channelState.ParseFromArray(buffer, length);
|
||||||
|
|
||||||
|
int32_t channel_id = channelState.has_channel_id() ? channelState.channel_id() : -1;
|
||||||
|
int32_t parent = channelState.has_parent() ? channelState.parent() : -1;
|
||||||
|
|
||||||
|
|
||||||
|
bool temporary = channelState.has_temporary() ? channelState.temporary()
|
||||||
|
: false; //todo make sure it's correct to assume it's false
|
||||||
|
int position = channelState.has_position() ? channelState.position() : 0;
|
||||||
|
|
||||||
|
vector<uint32_t> links;
|
||||||
|
std::copy(channelState.links().begin(), channelState.links().end(), links.begin());
|
||||||
|
|
||||||
|
vector<uint32_t> links_add;
|
||||||
|
std::copy(channelState.links_add().begin(), channelState.links_add().end(), links_add.begin());
|
||||||
|
|
||||||
|
vector<uint32_t> links_remove;
|
||||||
|
std::copy(channelState.links_remove().begin(), channelState.links_remove().end(),
|
||||||
|
links_remove.begin());
|
||||||
|
|
||||||
|
callback->channelstate_callback(
|
||||||
|
channelState.name(),
|
||||||
|
channel_id,
|
||||||
|
parent,
|
||||||
|
channelState.description(),
|
||||||
|
links,
|
||||||
|
links_add,
|
||||||
|
links_remove,
|
||||||
|
temporary,
|
||||||
|
position
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MessageType::USERREMOVE: {
|
||||||
|
MumbleProto::UserRemove user_remove;
|
||||||
|
user_remove.ParseFromArray(buffer, length);
|
||||||
|
|
||||||
|
int32_t actor = user_remove.has_actor() ? user_remove.actor() : -1;
|
||||||
|
bool ban = user_remove.has_ban() ? user_remove.ban()
|
||||||
|
: false; //todo make sure it's correct to assume it's false
|
||||||
|
|
||||||
|
callback->userremove_callback(
|
||||||
|
user_remove.session(),
|
||||||
|
actor,
|
||||||
|
user_remove.reason(),
|
||||||
|
ban
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MessageType::USERSTATE: // 9
|
||||||
|
// return MessageType::private_process_userstate(context, message, message_size);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case MessageType::BANLIST: // 10
|
||||||
|
// return MessageType::private_process_banlist(context, message, message_size);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case MessageType::TEXTMESSAGE: // 11
|
||||||
|
// return MessageType::private_process_textmessage(context, message, message_size);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case MessageType::PERMISSIONDENIED: // 12
|
||||||
|
// return MessageType::private_process_permissiondenied(context, message, message_size);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case MessageType::ACL: // 13
|
||||||
|
// return MessageType::private_process_acl(context, message, message_size);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case MessageType::QUERYUSERS: // 14
|
||||||
|
// return MessageType::private_process_queryusers(context, message, message_size);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case MessageType::CONTEXTACTIONMODIFY: // 16
|
||||||
|
// return MessageType::private_process_contextactionmodify(context, message, message_size);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case MessageType::CONTEXTACTION: // 17
|
||||||
|
// return MessageType::private_process_contextaction(context, message, message_size);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case MessageType::USERLIST: // 18
|
||||||
|
// return MessageType::private_process_userlist(context, message, message_size);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case MessageType::PERMISSIONQUERY: // 20
|
||||||
|
// return MessageType::private_process_permission_query(context, message, message_size);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case MessageType::CODECVERSION: // 21
|
||||||
|
// return MessageType::private_process_codecversion(context, message, message_size);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case MessageType::USERSTATS: // 22
|
||||||
|
// return MessageType::private_process_userstats(context, message, message_size);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case MessageType::REQUESTBLOB: // 23
|
||||||
|
// return MessageType::private_process_requestblob(context, message, message_size);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case MessageType::SERVERCONFIG: // 24
|
||||||
|
// return MessageType::private_process_serverconfig(context, message, message_size);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case MessageType::SUGGESTCONFIG: // 25
|
||||||
|
// return MessageType::private_process_suggestconfig(context, message, message_size);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw MumlibException("unknown message type: " + to_string(static_cast<int>(messageType)));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool processAudioPacket(AudioPacketType type, uint8_t *buffer, int length) {
|
||||||
|
logger->info("Got %d B of encoded audio data.", length);
|
||||||
|
int16_t pcmData[5000];
|
||||||
|
audio->decodeAudioPacket(type, buffer, length, pcmData, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
ConnectionState Mumlib::getConnectionState() {
|
||||||
|
return impl->transport->getConnectionState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mumlib::Mumlib::Mumlib() : impl(new _Mumlib_Private) {
|
||||||
|
impl->logger = &(log4cpp::Category::getInstance("Mumlib.Mumlib"));
|
||||||
|
impl->externalIoService = false;
|
||||||
|
impl->ioService = new io_service();
|
||||||
|
impl->audio = new Audio();
|
||||||
|
impl->transport = new Transport(
|
||||||
|
*(impl->ioService),
|
||||||
|
boost::bind(&_Mumlib_Private::processIncomingTcpMessage, impl, _1, _2, _3),
|
||||||
|
boost::bind(&_Mumlib_Private::processAudioPacket, impl, _1, _2, _3)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
mumlib::Mumlib::Mumlib(io_service &ioService) : impl(new _Mumlib_Private) {
|
||||||
|
//todo do this constructor
|
||||||
|
throw mumlib::MumlibException("not implented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
mumlib::Mumlib::~Mumlib() {
|
||||||
|
|
||||||
|
if (not impl->externalIoService) {
|
||||||
|
delete impl->ioService;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete impl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::Mumlib::setCallback(Callback &callback) {
|
||||||
|
impl->callback = &callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::Mumlib::connect(string host, int port, string user, string password) {
|
||||||
|
impl->transport->connect(host, port, user, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::Mumlib::disconnect() {
|
||||||
|
impl->transport->disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::Mumlib::run() {
|
||||||
|
if (impl->externalIoService) {
|
||||||
|
throw MumlibException("can't call run() when using external io_service");
|
||||||
|
}
|
||||||
|
|
||||||
|
impl->ioService->run();
|
||||||
|
}
|
||||||
|
|
||||||
|
void mumlib::Mumlib::sendAudioData(int16_t *pcmData, int pcmLength) {
|
||||||
|
uint8_t encodedData[5000];
|
||||||
|
int length = impl->audio->encodeAudioPacket(0, pcmData, pcmLength, encodedData, 5000);
|
||||||
|
impl->transport->sendEncodedAudioPacket(encodedData, length);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user