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 | ||||
| *.out | ||||
| *.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
	 Michał Słomkowski
						Michał Słomkowski