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