Reinitialized project to reduce the git branch size

This commit is contained in:
WolverinDEV 2019-07-17 19:37:18 +02:00
parent 3130cc33fe
commit 5715acbcb2
252 changed files with 51928 additions and 4 deletions

1
.git-old Normal file
View File

@ -0,0 +1 @@
gitdir: ../.git/modules/TeaSpeak

28
.gitignore vendored Normal file
View File

@ -0,0 +1,28 @@
# Created by .ignore support plugin (hsz.mobi)
**/.idea
**/cmake-build-*
**/build/
server/environment/*
!server/environment/geoloc
!server/environment/providers
!server/environment/resources
#The remote repro
server/repro/test
MusicBot/enviroment/
MusicBot/libs
MusicBot/*.opus
server/buildData.txt
server/repro/env/buildVersion.txt
server/repro/TeaSpeak-*
server/repro/unstripped/
server/repro/build_version.txt
server/gui/ui/
server/repro/crashes/
server/repro/symbols/
license/src/
server/tmp.txt
license/environment/**/*
server/src/web/
.git-old/

4
.gitmodules vendored
View File

@ -1,7 +1,3 @@
[submodule "TeaSpeak - Music"]
path = music
url = https://github.com/TeaSpeak/TeaMusic-Providers.git
branch = master
[submodule "git-teaspeak"]
path = git-teaspeak
url = https://github.com/TeaSpeak/TeaSpeak.git

68
CMakeLists.txt Normal file
View File

@ -0,0 +1,68 @@
cmake_minimum_required(VERSION 3.6)
#project(TeamSpeak)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/Modules")
set(TEASPEAK_SERVER ON)
include_directories(/usr/local/mysql/connector-c++-8.0/include/jdbc/)
#end now
#set(MEMORY_DEBUG_FLAGS " -fsanitize=leak -fsanitize=address -fstack-protector-all ")
#set(MEMORY_DEBUG_FLAGS "-fsanitize=address")
set(LIBRARY_PATH "${CMAKE_SOURCE_DIR}/../libraries/")
set(CMAKE_PREFIX_PATH "/home/wolverindev/clib/qt/5.6.1/5.6/gcc_64/lib/cmake")
set(LIBEVENT_PATH "${LIBRARY_PATH}/event/build/lib/")
function(resolve_library VARIABLE FALLBACK PATHS)
set( _PATHS ${PATHS} ${ARGN} ) # Merge them together
foreach(PATH IN ITEMS ${_PATHS})
message(STATUS "Try to use path ${PATH} for ${VARIABLE}")
if(EXISTS ${PATH})
message(STATUS "Setting ${VARIABLE} to ${PATH}")
set(${VARIABLE} ${PATH} PARENT_SCOPE)
return()
endif()
endforeach()
if(FALLBACK)
message(WARNING "Failed to resolve library path for ${VARIABLE}. Using default ${VARIABLE}")
else()
message(FATAL_ERROR "Failed to find requited library. Variable: ${VARIABLE} Paths: ${_PATHS}")
endif()
endfunction()
resolve_library(LIBRARY_TOM_MATH OFF "${LIBRARY_PATH}/tommath/build/libtommathStatic.a")
resolve_library(LIBRARY_TOM_CRYPT OFF "${LIBRARY_PATH}/tomcrypt/libtomcrypt.a")
resolve_library(LIBRARY_PATH_BREAKPAD OFF "${LIBRARY_PATH}/breakpad/build/src/client/linux/libbreakpad_client.a")
resolve_library(LIBRARY_PATH_PROTOBUF OFF "${LIBRARY_PATH}/protobuf/build/libprotobuf.a")
resolve_library(LIBRARY_PATH_JDBC OFF "${LIBRARY_PATH}/mysqlconnector/build/jdbc/driver/libmysqlcppconn.so.7" "${LIBRARY_PATH}/mysqlconnector/build/jdbc/libmysqlcppconn.so.7")
resolve_library(LIBRARY_PATH_BORINGSSL_SSL OFF "${LIBRARY_PATH}/boringssl/build/ssl/libssl.so")
resolve_library(LIBRARY_PATH_BORINGSSL_CRYPTO OFF "${LIBRARY_PATH}/boringssl/build/crypto/libcrypto.so")
resolve_library(LIBRARY_PATH_THREAD_POOL OFF "${LIBRARY_PATH}/Thread-Pool/build/libThreadPoolStatic.a")
resolve_library(LIBRARY_PATH_TERMINAL OFF "${LIBRARY_PATH}/CXXTerminal/build/libCXXTerminalStatic.a")
resolve_library(LIBRARY_PATH_VARIBALES OFF "${LIBRARY_PATH}/StringVariable/build/libStringVariablesStatic.a")
resolve_library(LIBRARY_PATH_YAML OFF "${LIBRARY_PATH}/yaml-cpp/build/libyaml-cpp.a")
resolve_library(LIBRARY_PATH_JSON OFF "${LIBRARY_PATH}/jsoncpp/build/src/lib_json/libjsoncpp.a")
resolve_library(LIBRARY_PATH_ED255 OFF "${LIBRARY_PATH}/ed25519/build/libed25519.a")
resolve_library(LIBRARY_PATH_DATA_PIPES OFF "${LIBRARY_PATH}/DataPipes/build/libDataPipes.so" "${LIBRARY_PATH}/DataPipes/cmake-build-release/libDataPipes.so" "${LIBRARY_PATH}/DataPipes/cmake-build-debug/libDataPipes.so")
resolve_library(LIBRARY_PATH_OPUS OFF "${LIBRARY_PATH}/opus/build/.libs/libopus.a")
#if(EXISTS ${CMAKE_SOURCE_DIR}/../libraries/mysqlconnector/build/libmysqlcppconn8.so.1)
# set(LIBRARY_PATH_MYSQL "${CMAKE_SOURCE_DIR}/../libraries/mysqlconnector/build/libmysqlcppconn8.so.1")
#else()
# set(LIBRARY_PATH_MYSQL "libmysqlcppconn8-static.a")
# message(WARNING "Could not resolve mysql! Using default resolver")
#endif()
add_definitions(-DINET -DINET6)
add_subdirectory(shared/)
add_subdirectory(client/)
add_subdirectory(server/)
add_subdirectory(license/)
add_subdirectory(flooder/)
add_subdirectory(MusicBot/)
add_subdirectory(music/)

21
MusicBot/CMakeLists.txt Normal file
View File

@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 3.6)
project(TeaMusic)
set(CMAKE_VERBOSE_MAKEFILE ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fpermissive -Wall -Wno-sign-compare -Wno-reorder -static-libgcc -static-libstdc++ -Wl,--enable-new-dtags,--export-dynamic")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELEASE} -O3")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/enviroment/)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/libs/)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
include_directories(../music/include/)
include_directories(../shared/src)
add_definitions(-DLTM_DESC)
#The basic library
add_library(TeaMusic SHARED src/MusicPlayer.cpp)
#The test file
add_executable(TeaMusicTest ${MUSIC_SOURCE_FILES} main.cpp)
target_link_libraries(TeaMusicTest ProviderFFMpeg ProviderYT ProviderOpus TeaMusic pthread ThreadPool opus asound opusfile stdc++fs dl mpg123 avformat avcodec avutil TeaSpeak CXXTerminal event jsoncpp)

198
MusicBot/MavStuff.cpp Normal file
View File

@ -0,0 +1,198 @@
//
// Created by wolverindev on 14.01.18.
//
#include <iostream>
#include <providers/shared/pstream.h>
#include <alsa/asoundlib.h>
#include <src/provider/yt/YTVManager.h>
#include <fstream>
#include <opus/opus.h>
#define PCM_DEVICE "default"
using namespace std;
using namespace std::chrono;
typedef struct WAV_HEADER
{
/* RIFF Chunk Descriptor */
uint8_t RIFF[4]; // RIFF Header Magic header
uint32_t ChunkSize; // RIFF Chunk Size | All stuff - 8 bytes?
uint8_t WAVE[4]; // WAVE Header
/* "fmt" sub-chunk */
uint8_t fmt[4]; // FMT header
uint32_t Subchunk1Size; // Size of the fmt chunk
uint16_t AudioFormat; // Audio format 1=PCM,6=mulaw,7=alaw, 257=IBM Mu-Law, 258=IBM A-Law, 259=ADPCM
uint16_t NumOfChan; // Number of channels 1=Mono 2=Sterio
uint32_t SamplesPerSec; // Sampling Frequency in Hz
uint32_t bytesPerSec; // bytes per second
uint16_t blockAlign; // 2=16-bit mono, 4=16-bit stereo
uint16_t bitsPerSample; // Number of bits per sample
/* "data" sub-chunk */
uint8_t Subchunk2ID[4]; // "data" string
uint32_t Subchunk2Size; // Sampled data length
};
int main(int, char**){
//youtube-dl --newline -x --audio-format opus --audio-quality 0 -o "vid01.%(ext)s" https://www.youtube.com/watch?v=AlXfbVpDUdo
// run a process and create a streambuf that reads its stdout and stderr
/*
redi::ipstream proc("youtube-dl --help", redi::pstreams::pstdout | redi::pstreams::pstderr);
std::string line;
// read child's stdout
while (std::getline(proc.out(), line))
std::cout << "stdout: " << line << '\n';
// read child's stderr
while (std::getline(proc.err(), line))
std::cout << "stderr: " << line << '\n';
*/
yt::YTVManager yt(nullptr);
//yt.downloadAudio("AlXfbVpDUdo").waitAndGet();
cout << "Downloaded" << endl;
/*
COpusCodec codec(48000, 1);
std::fstream fin ("vAlXfbVpDUdo.opus", std::ios::binary | fstream::in);
std::fstream fout("vAlXfbVpDUdo.raw", std::ios::binary | fstream::out);
if(!fin) throw std::runtime_error("Could not open input file");
if(!fout) throw std::runtime_error("Could not open output file");
try
{
COpusCodec codec(48000, 1);
size_t frames = 0;
while(codec.decode_frame(fin, fout))
{
frames++;
}
std::cout << "Successfully decoded " << frames << " frames\n";
}
catch(OpusErrorException const& e)
{
std::cerr << "OpusErrorException: " << e.what() << "\n";
return 255;
}
*/
//Read the header
WAV_HEADER header{};
std::fstream fin ("vAlXfbVpDUdo.wav", std::ios::binary | fstream::in);
fin.read((char *) &header, sizeof(header));
//Read the data
uint16_t bytesPerSample = header.bitsPerSample / 8; //Number of bytes per sample
uint64_t numSamples = header.ChunkSize / bytesPerSample; //How many samples are in the wav file?
cout << "RIFF header :" << header.RIFF[0] << header.RIFF[1] << header.RIFF[2] << header.RIFF[3] << endl;
cout << "WAVE header :" << header.WAVE[0] << header.WAVE[1] << header.WAVE[2] << header.WAVE[3] << endl;
cout << "FMT :" << header.fmt[0] << header.fmt[1] << header.fmt[2] << header.fmt[3] << endl;
cout << "Data size :" << header.ChunkSize << endl;
// Display the sampling Rate from the header
cout << "Sampling Rate :" << header.SamplesPerSec << endl;
cout << "Number of bits used :" << header.bitsPerSample << endl;
cout << "Number of channels :" << header.NumOfChan << endl;
cout << "Number of bytes per second :" << header.bytesPerSec << endl;
cout << "Data length :" << header.Subchunk2Size << endl;
cout << "Audio Format :" << header.AudioFormat << endl;
cout << "Block align :" << header.blockAlign << endl;
cout << "Data string :" << header.Subchunk2ID[0] << header.Subchunk2ID[1] << header.Subchunk2ID[2] << header.Subchunk2ID[3] << endl;
cout << "Samples :" << numSamples << endl;
cout << "Length :" << numSamples / header.SamplesPerSec << endl;
cout << "Header size " << sizeof(header) << endl;
unsigned int pcm, tmp, dir;
snd_pcm_t *pcm_handle;
snd_pcm_hw_params_t *params;
snd_pcm_uframes_t frames;
char *buff;
int buff_size, loops;
int channels = 2;
int rate = 44100;
int seconds = 5;
/* Open the PCM device in playback mode */
if (pcm = snd_pcm_open(&pcm_handle, PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0) < 0)
printf("ERROR: Can't open \"%s\" PCM device. %s\n",
PCM_DEVICE, snd_strerror(pcm));
/* Allocate parameters object and fill it with default values*/
snd_pcm_hw_params_alloca(&params);
snd_pcm_hw_params_any(pcm_handle, params);
/* Set parameters */
if (pcm = snd_pcm_hw_params_set_access(pcm_handle, params,
SND_PCM_ACCESS_RW_INTERLEAVED) < 0)
printf("ERROR: Can't set interleaved mode. %s\n", snd_strerror(pcm));
if (pcm = snd_pcm_hw_params_set_format(pcm_handle, params,
SND_PCM_FORMAT_S16_LE) < 0)
printf("ERROR: Can't set format. %s\n", snd_strerror(pcm));
if (pcm = snd_pcm_hw_params_set_channels(pcm_handle, params, channels) < 0)
printf("ERROR: Can't set channels number. %s\n", snd_strerror(pcm));
if (pcm = snd_pcm_hw_params_set_rate_near(pcm_handle, params, &rate, 0) < 0)
printf("ERROR: Can't set rate. %s\n", snd_strerror(pcm));
/* Write parameters */
if (pcm = snd_pcm_hw_params(pcm_handle, params) < 0)
printf("ERROR: Can't set harware parameters. %s\n", snd_strerror(pcm));
/* Resume information */
printf("PCM name: '%s'\n", snd_pcm_name(pcm_handle));
printf("PCM state: %s\n", snd_pcm_state_name(snd_pcm_state(pcm_handle)));
snd_pcm_hw_params_get_channels(params, &tmp);
printf("channels: %i ", tmp);
if (tmp == 1)
printf("(mono)\n");
else if (tmp == 2)
printf("(stereo)\n");
snd_pcm_hw_params_get_rate(params, &tmp, 0);
printf("rate: %d bps\n", tmp);
printf("seconds: %d\n", seconds);
/* Allocate buffer to hold single period */
snd_pcm_hw_params_get_period_size(params, &frames, 0);
cout << "perd size: " << frames << endl;
//buff_size = frames * channels * 2 /* 2 -> sample size */;
//buff = (char *) malloc(buff_size);
snd_pcm_hw_params_get_period_time(params, &tmp, NULL);
auto last = system_clock::now();
for (loops = seconds * 1000000 / tmp; loops > 0; loops--) {
if(loops == 25) usleep(5 * 1000 * 1000);
cout << " dur: " << duration_cast<microseconds>(system_clock::now() - last).count() << endl;
last = system_clock::now();
int readSize = frames * channels * 2;
char buffer[frames * channels * 2];
fin.read(buffer, readSize);
if (pcm = snd_pcm_writei(pcm_handle, buffer, frames) == -EPIPE) {
printf("XRUN.\n");
snd_pcm_prepare(pcm_handle);
} else if (pcm < 0) {
printf("ERROR. Can't write to PCM device. %s\n", snd_strerror(pcm));
}
}
snd_pcm_drain(pcm_handle);
snd_pcm_close(pcm_handle);
return 0;
}

249
MusicBot/Protocol.md Normal file
View File

@ -0,0 +1,249 @@
# Musik bot websocket protocol
## General structure
Transmitted data is in json format
The json format has the structure as following:
```
{
"type": <PacketType>,
"data": [
{
<key>: value
},
...
]
}
```
Example:
```
{
"type": "showMessage",
"data": [
{
"message": "A simple info modal",
"type": "info"
},
{
"message": "A simple error modal",
"type": "error"
}
]
}
```
## TODO list
* Music bot queue
* Music bot ts3 access rights (allow other clients to use this music bot etc.)
## Packet types
### General types
#### Server `showMessage`:
This packet should show up a message modal.
* **[~]**
* `message`: <msg>
* `type`: {info|error}
#### Server `reqError`:
The server sends this if you applay a invalid request
* **[1]**
* `message`: <msg>
* `requestId`: <reqestId>
#### Server `disconnect`
I send this packet before i close the comunication
* **[1]**
* `message`
___
### Login packets
#### Client `login`
Try login
* **[1]**
* `username`
* `password`
* `requestId`
#### Server `notifylogin`
* **[1]**
* `requestId`
* `succeeded`: {0|1}
* `uid` own uid. only set if login failed
* `message` only set if login failed
#### Client `logout`
* **[1]**
* `requestId`
#### Server `notifylogout`
Could be send at any time (force logout)
* **[1]**
* `requestId` (empty of not requested)
* `succeeded`: {0|1}
___
### Server Management
#### Client `serverlist`
* **[1]**
* `requestId`
#### Server `notifyserverlist`
Sends when requested or list updated
(Lists online avariable server for the client view)
* **[~]**
* [1] `requestId` (empty of not requested)
* `name`
* `uid`
* `serverId`
* `status`: {online|offline}
* `clientOnline`
* `maxClients`
#### Server `notifyserverupdate`
Sends when a server changes display properties
* **[1]**
* `serverId`
* `key`: {name|onlineClients|maxClients}
* `value`
___
### Channel Management
#### Client `channellist`
Request a channel list
* **[1]**
* `requestId`
* `serverId`
#### Server `notifychannellist`
The channel response is ordered:
This packet would also be send if the channel tree gets updated
```
root
- sub 1
- sub sub 1
- sub sub 2
- sub 2
root 2
...
```
* **[~]**
* [1] `requestId` (empty of not requested)
* [1] `serverId`
* `name`
* `channelId`
* `channelParent`
* `channelOrder`
___
### Music bot management
#### Client `musicbotlist`
* **[1]**
* `requestId`
* `serverId`
#### Server `notifymusikmusicbotlist`
* **[~]**
* [1] `requestId` (empty of not requested)
* [1] `serverId`
* `id`
* `connected`: {1|0}
* `name`
* `channelId`
* `ownerUid` (its your own if its matching with our own id)
* `ownerCldbid`
#### Client `musicbotcreate`
* **[1]**
* `requestId`
* `serverId`
* `name`
* `channelId`
#### Server `notifymusikbotcreated`
* **[1]**
* `requestId` (empty of not requested)
* `serverId`
* `id`
* `connected`: {1|0}
* `name`
* `channelId`
* `ownerUid` (its your own if its matching with our own id)
* `ownerCldbid`
#### Client `musicbotdelete`
* **[1]**
* `requestId`
* `serverId`
* `name`
* `channelId`
#### Server `notifymusikbotdelete`
* **[1]**
* `requestId` (empty of not requested)
* `serverId`
* `id`
#### Client `musicbotinfo`
Request music bot info
* **[1]**
* `requestId`
* `serverId`
* `id`
#### Server `notifymusicbotinfo`
* **[1]**
* `requestId`
* `serverId`
* `id`
* `name`
* `connected`
* `phoeticName`
* `channelId`
* `playing`
* `playingInfo`: <string|Current playing title etc. May need to be inproved> Empty if noting selected
* `description`
* `textCurrentSong`
#### Client `musicbotedit`
* **[1]**
* `requestId`
* `serverId`
* `id`
* `key`
* `value`
#### Server `notifymusicbotedit`
* **[~]**
* [1] `requestId` (empty of not requested)
* [1] `serverId`
* `id`
* `key`: {connected|name|channelId|description|playing|playingInfo}
#### Client `musicbotplay`
* **[1]**
* `requestId`
* `serverId`
* `id`
* `type`: {yt|file}
* `value`
#### Server `notifymusicbotplay`
Send only as answer for `musicbotplay`
You would recive the play state update via `notifymusicbotedit`
* **[1]**
* `requestId`
* `succeeded`
*
#### Client `musicbotstop`
* **[1]**
* `requestId`
* `serverId`
* `id`
* `paused`: {1|0}
#### Server `notifymusicbotstop`
Send only as answer for `musicbotstop`
You would recive the play state update via `notifymusicbotedit`
* **[1]**
* `requestId`
* `succeeded`

210
MusicBot/main.cpp Normal file
View File

@ -0,0 +1,210 @@
#include <iostream>
#include "../music/providers/shared/pstream.h"
#include <alsa/asoundlib.h>
#include <fstream>
#include <chrono>
#include <MusicPlayer.h>
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
}
#include <log/LogUtils.h>
#include <CXXTerminal/Terminal.h>
#define PCM_DEVICE "default"
using namespace std;
using namespace std::chrono;
using namespace music;
void die(const char* message)
{
fprintf(stderr, "%s\n", message);
exit(1);
}
int main(int, char**){
logger::config::logLevel = spdlog::level::trace;
logger::config::terminalLevel = spdlog::level::trace;
//terminal::install();
logger::setup();
logger::updateLogLevels();
//youtube-dl -s --dump-json https://www.youtube.com/watch?v=1ifTLj_glhc
//ffmpeg -hide_banner -nostats -i <url> -ac 2 -ar 48000 -f s16le -acodec pcm_s16le pipe:1
//
//printf("Length: %d\n", codec_context->frame_size / codec_context->framerate.den / codec_context->channels); //576 | 3
/*
// To initalize libao for playback
ao_initialize();
int driver = ao_default_driver_id();
// The format of the decoded PCM samples
ao_sample_format sample_format;
sample_format.bits = 16;
sample_format.channels = 2;
sample_format.rate = 44100;
sample_format.byte_format = AO_FMT_NATIVE;
sample_format.matrix = 0;
ao_device* device = ao_open_live(driver, &sample_format, NULL);
AVPacket packet;
int buffer_size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
int8_t buffer[AVCODEC_MAX_AUDIO_FRAME_SIZE];
while (1) {
buffer_size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
// Read one packet into `packet`
if (av_read_frame(container, &packet) < 0) {
break; // End of lstream. Done decoding.
}
// Decodes from `packet` into the buffer
if (avcodec_decode_audio3(codec_context, (int16_t*)buffer, &buffer_size, &packet) < 1) {
break; // Error in decoding
}
// Send the buffer contents to the audio device
ao_play(device, (char*)buffer, buffer_size);
}
av_close_input_file(container);
ao_shutdown();
fprintf(stdout, "Done playing. Exiting...");
*/
music::manager::loadProviders("providers/");
std::string file = "https://www.youtube.com/watch?v=GVC5adzPpiE"; //https://www.youtube.com/watch?v=eBlg2oX0Z0Q
auto provider = music::manager::resolveProvider("YouTube", file); //test.mp3
if(!provider) return 0;
cout << "Using provider -> " << provider->providerName << endl;
cout << "Using provider -> " << provider->providerDescription << endl;
if(true) return 0;
/*
auto fut = provider->createPlayer(file);
fut.waitAndGet(nullptr);
if(fut.failed()){
log::log(log::err, "Could not create: " + fut.errorMegssage());
} else log::log(log::info, "Got!");
//VALID
/*
auto player = provider->createPlayer("https://r5---sn-4g5edned.googlevideo.com/videoplayback?lmt=1491636391724572&key=yt6&itag=251&keepalive=yes&signature=8220B0B2D85AFE6A44CAE901A9C5A5D9A2564DA4.59183C41417E91E04552835738ED0AD1592BD05E&source=youtube&clen=4017657&sparams=clen%2Cdur%2Cei%2Cgir%2Cid%2Cinitcwndbps%2Cip%2Cipbits%2Citag%2Ckeepalive%2Clmt%2Cmime%2Cmm%2Cmn%2Cms%2Cmv%2Cpl%2Crequiressl%2Csource%2Cexpire&gir=yes&expire=1516917262&initcwndbps=856250&ei=rv1pWuvYBI-AV7SlqaAE&dur=234.061&mv=m&mt=1516895537&ms=au&requiressl=yes&ip=93.230.21.99&ipbits=0&mn=sn-4g5edned&mm=31&pl=26&mime=audio%2Fwebm&id=o-ABQnAlwQxtPQJTqHMc9SiTD-bGc1YR2yogmPcVUy1Xxp&ratebypass=yes").waitAndGet(nullptr);
/*
auto player = provider->createPlayer("https://r5---sn-4g5edned.googlevideo.com/videoplayback?mn=sn-4g5edned&mm=31&gir=yes&clen=4017657&requiressl=yes&mv=m&mt=1516820571&ms=au&ei=tdhoWtv0Fd5bY1wLY5qVA&lmt=1491636391724572&key=yt6&ip=80.133.238.232&expire=1516842261&dur=234.061&beids=%5B9466594%5D&id=o-AKeOawWpz0BT9Qxuqria6qw1_eTa3is7UXit4BLIC8re&initcwndbps=591250&source=youtube&sparams=clen%2Cdur%2Cei%2Cgir%2Cid%2Cinitcwndbps%2Cip%2Cipbits%2Citag%2Ckeepalive%2Clmt%2Cmime%2Cmm%2Cmn%2Cms%2Cmv%2Cpl%2Crequiressl%2Csource%2Cexpire&ipbits=0&signature=230305B8592A57E939FDBF2D4C19C16DC2FE0D68.AC475B47E4533071F4A6EA96960384398A15D801&pl=26&itag=251&mime=audio%2Fwebm&keepalive=yes&ratebypass=yes").waitAndGet(nullptr);
*/
auto player = provider->createPlayer(file).waitAndGet(nullptr);
if(!player){
cerr << "Could not load youtube video" << endl;
return -1;
}
if(!player->initialize()){
log::log(log::err, "Could not inizalisze ffmpeg player -> " + player->error());
return 1;
}
player->registerEventHandler("main", [player](music::MusicEvent event){ //FIXME weak ptr
log::log(log::info, "Got event " + to_string(event));
if(event == music::EVENT_ERROR) {
log::log(log::err, "Recived error: " + player->error());
player->clearError();
}
});
player->play();
player->forward(chrono::minutes(0));
cout << "Song length " << duration_cast<seconds>(player->length()).count() << " seconds" << endl;
unsigned int pcm, tmp, dir;
snd_pcm_t *pcm_handle;
snd_pcm_hw_params_t *params;
snd_pcm_uframes_t frames;
int loops;
int channels = 2;
int rate = 48000;
int seconds = duration_cast<std::chrono::seconds>(player->length()).count() + 1;
seconds = 10000;
if (pcm = snd_pcm_open(&pcm_handle, PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0) < 0)
printf("ERROR: Can't open \"%s\" PCM device. %s\n",
PCM_DEVICE, snd_strerror(pcm));
snd_pcm_hw_params_alloca(&params);
snd_pcm_hw_params_any(pcm_handle, params);
if (pcm = snd_pcm_hw_params_set_access(pcm_handle, params,
SND_PCM_ACCESS_RW_INTERLEAVED) < 0)
printf("ERROR: Can't set interleaved mode. %s\n", snd_strerror(pcm));
if (pcm = snd_pcm_hw_params_set_format(pcm_handle, params,
SND_PCM_FORMAT_S16_LE) < 0)
printf("ERROR: Can't set format. %s\n", snd_strerror(pcm));
if (pcm = snd_pcm_hw_params_set_channels(pcm_handle, params, channels) < 0)
printf("ERROR: Can't set channels number. %s\n", snd_strerror(pcm));
if (pcm = snd_pcm_hw_params_set_rate_near(pcm_handle, params, &rate, 0) < 0)
printf("ERROR: Can't set rate. %s\n", snd_strerror(pcm));
if (pcm = snd_pcm_hw_params(pcm_handle, params) < 0)
printf("ERROR: Can't set harware parameters. %s\n", snd_strerror(pcm));
printf("PCM name: '%s'\n", snd_pcm_name(pcm_handle));
printf("PCM state: %s\n", snd_pcm_state_name(snd_pcm_state(pcm_handle)));
snd_pcm_hw_params_get_channels(params, &tmp);
printf("channels: %i ", tmp);
if (tmp == 1)
printf("(mono)\n");
else if (tmp == 2)
printf("(stereo)\n");
snd_pcm_hw_params_get_rate(params, &tmp, 0);
printf("rate: %d bps\n", tmp);
printf("seconds: %d\n", seconds);
snd_pcm_hw_params_get_period_size(params, &frames, 0);
cout << "perd size: " << frames << endl;
snd_pcm_hw_params_get_period_time(params, &tmp, NULL);
player->preferredSampleCount(frames);
auto last = system_clock::now();
for (loops = seconds * 1000000 / tmp; loops > 0; loops--) {
//cout << " dur: " << duration_cast<microseconds>(system_clock::now() - last).count() << endl;
auto next = player->popNextSegment();
if(!next) {
log::log(log::info, "END!");
continue;
}
retry:
if (pcm = snd_pcm_writei(pcm_handle, next->segments, next->segmentLength) == -EPIPE) {
printf("XRUN.\n");
snd_pcm_prepare(pcm_handle);
goto retry;
} else if (pcm < 0) {
printf("ERROR. Can't write to PCM device. %s\n", snd_strerror(pcm));
}
//if((system_clock::now() - last) > ::seconds(1)) {
log::log(log::debug, "Time: " + to_string(duration_cast<milliseconds>(system_clock::now() - last).count()) + " | " + to_string(duration_cast<milliseconds>(player->currentIndex()).count()));
last = system_clock::now();
//}
//TODO!
}
snd_pcm_drain(pcm_handle);
snd_pcm_close(pcm_handle);
return 0;
}

View File

@ -0,0 +1,115 @@
#include <dlfcn.h>
#include <algorithm>
#include <log/LogUtils.h>
#include <experimental/filesystem>
#include "MusicPlayer.h"
using namespace std;
using namespace music;
using namespace music::manager;
namespace fs = std::experimental::filesystem;
void log::log(const Level& lvl, const std::string& msg) {
logger::logger(0)->log((spdlog::level::level_enum) lvl, "[Music] " + msg);
}
void AbstractMusicPlayer::registerEventHandler(const std::string& key, const std::function<void(MusicEvent)>& function) {
threads::MutexLock lock(this->eventLock);
this->eventHandlers.push_back({key, function});
}
void AbstractMusicPlayer::unregisterEventHandler(const std::string& string) {
threads::MutexLock lock(this->eventLock);
for(const auto& entry : this->eventHandlers){
if(entry.first == string){
this->eventHandlers.erase(find_if(this->eventHandlers.begin(), this->eventHandlers.end(), [string](const std::pair<std::string, std::function<void(MusicEvent)>>& elm){ return elm.first == string; }));
break;
}
}
}
void AbstractMusicPlayer::fireEvent(MusicEvent event) {
threads::MutexLock lock(this->eventLock);
auto listCopy = this->eventHandlers; //Copy for remove while fire
for(const auto& entry : listCopy)
entry.second(event);
}
const char* music::stateNames[] = {"uninitialised", "playing", "paused", "stopped"};
static threads::Mutex staticLock;
static std::deque<std::shared_ptr<PlayerProvider>> types;
std::deque<std::shared_ptr<PlayerProvider>> manager::registeredTypes(){ return types; }
void registerType(const std::shared_ptr<PlayerProvider>& provider) {
threads::MutexLock l(staticLock);
types.push_back(provider);
}
//empty for not set
std::shared_ptr<PlayerProvider> manager::resolveProvider(const std::string& provName, const std::string& str) {
threads::MutexLock l(staticLock);
vector<std::shared_ptr<PlayerProvider>> provs;
for(const auto& prov : types){
auto p = prov.get();
if(!str.empty() && prov->acceptString(str))
provs.push_back(prov);
else if(!provName.empty() && prov->providerName == provName)
provs.push_back(prov);
}
sort(provs.begin(), provs.end(), [str](const std::shared_ptr<PlayerProvider>& a, const std::shared_ptr<PlayerProvider>& b){
return a->weight(str) > b->weight(str);
});
return provs.empty() ? nullptr : provs.front();
}
typedef std::shared_ptr<music::manager::PlayerProvider>(*create_provider_fn)();
void manager::loadProviders(const std::string& path) {
auto dir = fs::u8path(path);
if(!fs::exists(dir)){
try {
fs::create_directories(dir);
} catch (std::exception& e) {}
return;
}
deque<fs::path> paths;
for(const auto& entry : fs::directory_iterator(dir)){
if(!entry.path().has_extension()) continue;
if(entry.path().extension().string() == ".so")
paths.push_back(entry.path());
}
std::sort(paths.begin(), paths.end(), [](const fs::path& a, const fs::path& b){ return a.filename().string() < b.filename().string(); });
int index = 0;
log::log(log::debug, "Provider load order:");
for(const auto& entry : paths)
log::log(log::debug, "[" + to_string(index++) + "] " + entry.string());
for(const auto& entry : paths){
void* provider = dlopen(entry.string().c_str(), RTLD_NOW);
if(!provider){
log::log(log::err, string() + "Could not load music provider " + entry.string() + ". Error: " + dlerror());
continue;
}
auto create_provider = reinterpret_cast<create_provider_fn>(dlsym(provider, "create_provider"));
if(!create_provider){
log::log(log::err, string() + "Could not find entry point create_provider()@" + entry.string());
dlclose(provider);
continue;
}
auto mprovider = (*create_provider)();
if(!mprovider){
log::log(log::err, string() + "Could not create music provider for " + entry.string());
dlclose(provider);
continue;
}
log::log(log::info, string() + "Loaded successfully provider " + mprovider->providerName);
types.push_back(mprovider);
}
}
void manager::register_provider(const std::shared_ptr<music::manager::PlayerProvider> &provider) {
types.push_back(provider);
}

79
client/CMakeLists.txt Normal file
View File

@ -0,0 +1,79 @@
cmake_minimum_required(VERSION 3.6)
project(TeamSpeak)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -static-libgcc -static-libstdc++ -pthread ${MEMORY_DEBUG_FLAGS}")
include_directories(../shared/src)
add_definitions(-DLTM_DESC)
set(SOURCE_FILES
main.cpp
src/protocol/Connection.cpp
src/protocol/ConnectionHandschake.cpp
src/protocol/ConnectionPacketHandler.cpp
src/protocol/socket/FilteredUDPSocket.cpp
src/protocol/socket/RawUDPSocket.cpp
src/Identity.cpp
src/MultithreadedIdentity.cpp
src/protocol/HandshakeNew.cpp
../shared/src/License.cpp
)
find_package(Protobuf REQUIRED)
include_directories(${Protobuf_INCLUDE_DIRS})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS proto/LicenseKey.proto)
add_executable(TeamSpeakClient ${SOURCE_FILES} ${PROTO_SRCS})
target_link_libraries(TeamSpeakClient
${LIBRARY_PATH_THREAD_POOL} #Static
TeaSpeak #Static
TeaLicenseHelper #Static
TeaMusic #Static
${LIBRARY_PATH_TERMINAL} #Static
${LIBRARY_PATH_VARIBALES}
${LIBRARY_PATH_YAML}
pthread
stdc++fs
${LIBEVENT_PATH}/libevent.a
${LIBEVENT_PATH}/libevent_pthreads.a
opus.a
${LIBRARY_PATH_JSON}
${LIBRARY_PATH_PROTOBUF}
DataPipes
#${LIBWEBRTC_LIBRARIES} #ATTENTIAN! WebRTC does not work with crypto! (Already contains a crypto version)
${LIBRARY_TOM_CRYPT}
${LIBRARY_TOM_MATH}
#We're forsed to use boringssl caused by the fact that boringssl is already within webrtc!
#Require a so
sqlite3
${LIBRARY_PATH_ED255}
${LIBRARY_PATH_BREAKPAD}
${LIBRARY_PATH_JDBC}
${LIBRARY_PATH_PROTOBUF}
${LIBRARY_PATH_BORINGSSL_SSL}
${LIBRARY_PATH_BORINGSSL_CRYPTO}
)
#strip -s -p -v TeamSpeakHash
add_executable(TeamSpeakHash src/Identity.cpp src/MultithreadedIdentity.cpp identityHash.cpp)
#set_target_properties(TeamSpeakHash PROPERTIES CMAKE_CXX_FLAGS "-o1")
target_link_libraries(TeamSpeakHash
TeaSpeak
ThreadPoolStatic
pthread
${TOM_LIBRARIES}
${LIBRARY_TOM_CRYPT}
${LIBRARY_TOM_MATH}
${LIBRARY_PATH_BORINGSSL_SSL}
${LIBRARY_PATH_BORINGSSL_CRYPTO}
)

29
client/HideString.h Normal file
View File

@ -0,0 +1,29 @@
#pragma once
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#define CRYPT_MACRO(r, d, i, elem) ( elem ^ ( d - i ) )
#define DEFINE_HIDDEN_STRING(NAME, SEED, SEQ)\
static const char* BOOST_PP_CAT(Get, NAME)()\
{\
static char data[] = {\
BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH_I(CRYPT_MACRO, SEED, SEQ)),\
'\0'\
};\
\
static bool isEncrypted = true;\
if ( isEncrypted )\
{\
for (unsigned i = 0; i < ( sizeof(data) / sizeof(data[0]) ) - 1; ++i)\
{\
data[i] = CRYPT_MACRO(_, SEED, i, data[i]);\
}\
\
isEncrypted = false;\
}\
\
return data;\
}

27
client/changes.md Normal file
View File

@ -0,0 +1,27 @@
[OUT] clientinitiv alpha=kXZSTZ7qaOhblA== omega=MEwDAgcAAgEgAiA5+ycSoMRRdugndbohYtH7PqS6Q6h3TV1VnGRKoZNMsQIhANn2rszHVipB13LBcY2Zf3APg7HX7ix3WWiaT5hjQrUv ot=1 ip=84.200.62.248
[ IN] initivexpand2 l=AQCBIiOUkbXLeQ\/6UhNB+8iJnwLYGZJglZoDFSTt+7awWgAJFtlwLxmo8AAAACVUZWFtU3BlYWsgU3lzdGVtcyBHbWJIAADtwskuiFg0FkafvNvS1uwz67b2lEhMnOkAygPvo0waGAAJQrh2HBxqvQAAACRUZWFtU3BlYWsgc3lzdGVtcyBHbWJIAADd85KDiA8UeqB5G9MBtNZ879CYBnfbjc\/OVj0j0xMcqQIJQ7qACi5lgAYAAAAAVGVhbVNwZWFrIFN5c3RlbXMgR21iSAAAitZK9P6JEXiiy+uERaapfw\/I5na7S5M+Se5IoOb\/24kgCV6OnAlfN1w= beta=5cB\/vUHLQ7GbVPNRStMLnZlyo572IFPzdgTTgxJ3zF4JBehh7nLudpdzk1hdbcglx4n3JmqX omega=MEwDAgcAAgEgAiEA83r2Er9vHr5sSEcm9g\/\/rdLjOdTMtwX\/Lkbi02+24FkCIB2P804l26yketcG8WxZQJ1z4ukU0e3SqEzm4pKHzMRY ot=1 proof=MEUCIAgYSvrPo0OKKD8aRmjeKVS1EVF6L\/DXLBT+XFkZlMVPAiEAnSMVOp8DOEMzcE42hna72+aWr+Z4+a76x23hmDJa4Uk= tvd=AQAAAABaQjKEblQQgGczKfB+CSS7IY5qli8LRS9jDltkab8leo6ltwQH82VUU\/WwnpCbazVbnh4iNbcobdxv\/MTX5Y3+6xpqDgEAEa1\/SLpE\/CIg2X7GX9mtDCCvP1KdQGaT\/RKoofPSsTkACRbZcAr4DPAAAAASVGVhbVNwZWFrIFN5c3RlbXMgR21iSAAAPgFaQLsu0h3FTQax+i0qXFXX\/Bn7VcGJH8SE5AOAiuQECVvUYwlenQk= time=1514211836
[OUT] clientek ek=n4+LesWAJf+8ha6LlF5kWilUbj59qX+zV31asaniK8U= proof=MEQCIC+NxeqTNUQWqHz914BHkFfC3LTF8BkYAYktMB62sET7AiBPmnfN6yoB381g\/GAnZVwMkeCPIjKi9vHUk15yOdT5FQ==
[OUT] clientinit client_nickname=P3nisBaum client_version=3.1.7\s[Build:\s1513163251] client_platform=Windows client_input_hardware=1 client_output_hardware=1 client_default_channel client_default_channel_password client_server_password client_meta_data client_version_sign=tdNngCAZ1ImAf7BxJzO4RXv5nBRsUERsrSOnMKVUFNQg6BS4Bzag0RFgLVzs2DRj19AC8+q5cXgH+5Ms50mTCA== client_key_offset=15848377 client_nickname_phonetic client_default_token client_badges=Overwolf=0 hwid=3036580947951600a7de06cd796bb0f1,7209e96ac1f60ff7772b7301919e0878
[OUT] clientinit client_nickname=P3nisBaum client_version=3.1.7\s[Build:\s1513163251] client_platform=Windows client_input_hardware=1 client_output_hardware=1 client_default_channel client_default_channel_password client_server_password client_meta_data client_version_sign=tdNngCAZ1ImAf7BxJzO4RXv5nBRsUERsrSOnMKVUFNQg6BS4Bzag0RFgLVzs2DRj19AC8+q5cXgH+5Ms50mTCA== client_key_offset=15848377 client_nickname_phonetic client_default_token client_badges=Overwolf=0 hwid=3036580947951600a7de06cd796bb0f1,7209e96ac1f60ff7772b7301919e0878
[ IN] error id=521 msg=too\smany\sclones\salready\sconnected
-> ot & ip needs to be set
-> ack needs to be unencripted as look clientinitiv not done
initivexpand2 l=AQCBIiOUkbXLeQ\/6UhNB+8iJnwLYGZJglZoDFSTt+7awWgAJFtlwLxmo8AAAACVUZWFtU3BlYWsgU3lzdGVtcyBHbWJIAADtwskuiFg0FkafvNvS1uwz67b2lEhMnOkAygPvo0waGAAJQrh2HBxqvQAAACRUZWFtU3BlYWsgc3lzdGVtcyBHbWJIAADd85KDiA8UeqB5G9MBtNZ879CYBnfbjc\/OVj0j0xMcqQIJQ7qACi5lgAYAAAAAVGVhbVNwZWFrIFN5c3RlbXMgR21iSAAA5Szd\/YkuL5NUF7SeHfvSKi5byOOMvYME0\/WRwlTHK8ogCV6hYglfSiI= beta=W\/D9NTwuZi6L1sbFpA+KiDBMSgl8y6Jy6vFVSGszJy1BPy8zEhNDQ62msazyAgk0CdbY0B+E omega=MEwDAgcAAgEgAiEA83r2Er9vHr5sSEcm9g\/\/rdLjOdTMtwX\/Lkbi02+24FkCIB2P804l26yketcG8WxZQJ1z4ukU0e3SqEzm4pKHzMRY ot=1 proof=MEUCIA3dgrIlXFIjkLrxJ1Yni7OqQYwPing03Xz\/zkQFrmdHAiEA8a0delCktJ21TIrj91QZbA76vtZUL38XVm+P2bR9M7c= tvd=AQAAAABaQjKEblQQgGczKfB+CSS7IY5qli8LRS9jDltkab8leo6ltwQH82VUU\/WwnpCbazVbnh4iNbcobdxv\/MTX5Y3+6xpqDgEAEa1\/SLpE\/CIg2X7GX9mtDCCvP1KdQGaT\/RKoofPSsTkACRbZcAr4DPAAAAASVGVhbVNwZWFrIFN5c3RlbXMgR21iSAAAPgFaQLsu0h3FTQax+i0qXFXX\/Bn7VcGJH8SE5AOAiuQECVvUYwlenQk= time=1514216642
Nr 1
[OUT] clientinitiv alpha=DBiidVqi8kTYAQ== omega=MEwDAgcAAgEgAiA5+ycSoMRRdugndbohYtH7PqS6Q6h3TV1VnGRKoZNMsQIhANn2rszHVipB13LBcY2Zf3APg7HX7ix3WWiaT5hjQrUv ot=1 ip=84.200.62.248
[ IN] initivexpand2 l=AQCBIiOUkbXLeQ\/6UhNB+8iJnwLYGZJglZoDFSTt+7awWgAJFtlwLxmo8AAAACVUZWFtU3BlYWsgU3lzdGVtcyBHbWJIAADtwskuiFg0FkafvNvS1uwz67b2lEhMnOkAygPvo0waGAAJQrh2HBxqvQAAACRUZWFtU3BlYWsgc3lzdGVtcyBHbWJIAADd85KDiA8UeqB5G9MBtNZ879CYBnfbjc\/OVj0j0xMcqQIJQ7qACi5lgAYAAAAAVGVhbVNwZWFrIFN5c3RlbXMgR21iSAAALYVev\/rqwinxd2zrGZla8+69t03410Iyo9N6lV7bajMgCV6ioQlfS2E= beta=\/Eg8SA2j45sSVi28kVwBVpxT98G1w0tlenDVYuKy\/7Cn1ZZiNiwu8Z9XhGBxwrzPXrMHPEMJ omega=MEwDAgcAAgEgAiEA83r2Er9vHr5sSEcm9g\/\/rdLjOdTMtwX\/Lkbi02+24FkCIB2P804l26yketcG8WxZQJ1z4ukU0e3SqEzm4pKHzMRY ot=1 proof=MEUCIFFa+q1zdgA0OtXgdgn9gxSOMp7GBvc3vPW0YDTU2+e6AiEA8sjW3XDikxQADPoSaSw7lwL6gfXr6gwO0Hvro3RZBHU= tvd=AQAAAABaQjKEblQQgGczKfB+CSS7IY5qli8LRS9jDltkab8leo6ltwQH82VUU\/WwnpCbazVbnh4iNbcobdxv\/MTX5Y3+6xpqDgEAEa1\/SLpE\/CIg2X7GX9mtDCCvP1KdQGaT\/RKoofPSsTkACRbZcAr4DPAAAAASVGVhbVNwZWFrIFN5c3RlbXMgR21iSAAAPgFaQLsu0h3FTQax+i0qXFXX\/Bn7VcGJH8SE5AOAiuQECVvUYwlenQk= time=1514216961
[OUT] clientek ek=19doGY4WwwAeW\/zHnmFJTpnE8h9MUJZSYFNpz4aUabs= proof=MEUCIGTwtZ1E+HjDPLqlyTuaHtvWmjYW\/\/tnQgugaC5u1swtAiEA9DN+kuwxuXG80FDzKnICGQYQGZZBySQv8Nhflg\/1FwQ=
[OUT] clientinitiv alpha=3Pcn19zISkIB5g== omega=MEwDAgcAAgEgAiA5+ycSoMRRdugndbohYtH7PqS6Q6h3TV1VnGRKoZNMsQIhANn2rszHVipB13LBcY2Zf3APg7HX7ix3WWiaT5hjQrUv ot=1 ip=84.200.62.248
[ IN] initivexpand2 l=AQCBIiOUkbXLeQ\/6UhNB+8iJnwLYGZJglZoDFSTt+7awWgAJFtlwLxmo8AAAACVUZWFtU3BlYWsgU3lzdGVtcyBHbWJIAADtwskuiFg0FkafvNvS1uwz67b2lEhMnOkAygPvo0waGAAJQrh2HBxqvQAAACRUZWFtU3BlYWsgc3lzdGVtcyBHbWJIAADd85KDiA8UeqB5G9MBtNZ879CYBnfbjc\/OVj0j0xMcqQIJQ7qACi5lgAYAAAAAVGVhbVNwZWFrIFN5c3RlbXMgR21iSAAASpEEQfPJOP8aeecPLtzcu8iYmtqBJn8MWq+8lSQljYcgCV6iyAlfS4g= beta=JLFcgyWwlYNzy5lsTGJKGz6rvmShHrb6YEZfq4TXwTLSiWDGqckf6ywiMOP2MGAc1y1yQsEi omega=MEwDAgcAAgEgAiEA83r2Er9vHr5sSEcm9g\/\/rdLjOdTMtwX\/Lkbi02+24FkCIB2P804l26yketcG8WxZQJ1z4ukU0e3SqEzm4pKHzMRY ot=1 proof=MEYCIQCpKZJCmQCHyC2PLIleZVAl1fSTdnzqcNxhiXxVAHtUHgIhAPvPfziHtiqn36nB7KSVuKV+gMA29OL87bI1HP6CJNw2 tvd=AQAAAABaQjKEblQQgGczKfB+CSS7IY5qli8LRS9jDltkab8leo6ltwQH82VUU\/WwnpCbazVbnh4iNbcobdxv\/MTX5Y3+6xpqDgEAEa1\/SLpE\/CIg2X7GX9mtDCCvP1KdQGaT\/RKoofPSsTkACRbZcAr4DPAAAAASVGVhbVNwZWFrIFN5c3RlbXMgR21iSAAAPgFaQLsu0h3FTQax+i0qXFXX\/Bn7VcGJH8SE5AOAiuQECVvUYwlenQk= time=1514217000
[OUT] clientek ek=yGZb1Qp0Y\/V7T+rMwcqtzUeOTIGgE6+7gcKxAw2OMPU= proof=MEQCIHdgsGlufstf\/ab1TvvM740Azs56jyGFmo93ijr53k1FAiBHNhWkV9xLUG19jpZGGe86SgCDli7GdSrv61ZGr3jD9g==
bM5HB+Ox/Wht38dd6/VeB7L76ebcpP9YgKzWAk4lmQ8=

63
client/identityHash.cpp Normal file
View File

@ -0,0 +1,63 @@
#include <iostream>
#include <src/Identity.h>
#include <sstream>
#include "HideString.h"
DEFINE_HIDDEN_STRING(HeaderMessage, 0x44, ('#')('#')('#')('#')('#')('#')('#')('#')('#')('#')('#')('#')('#')('#')('#')('#')('#')(' ')('T')('S')('3')(' ')('H')('a')('s')('h')(' ')('b')('y')(' ')('W')('o')('l')('v')('e')('r')('i')('n')('D')('E')('V')(' ')('#')('#')('#')('#')('#')('#')('#')('#')('#')('#')('#')('#')('#')('#')('#')('#')('#'));
using namespace ts;
using namespace std;
inline string crypt(string in){
stringstream result;
result << "DEFINE_HIDDEN_STRING(EncryptionKey, 0xA5, ";
for(char c : in)
result << "('" << c << "')";
result << ");";
return result.str();
}
int main(int argc, char** argv) {
cout << GetHeaderMessage() << endl;
if(argc < 3) {
cerr << "Invalid arguments. ./TSHash <threads> <MaxLevel> [<BlockSize> = 1.000.000]" << endl;
return 0;
}
init_LTM();
if(register_prng(&sprng_desc) == -1) {
cerr << "could not setup prng" << endl;
return EXIT_FAILURE;
}
if (register_cipher(&rijndael_desc) == -1) {
cerr << "could not setup rijndael" << endl;
return EXIT_FAILURE;
}
int threads = atoi(argv[1]);
int level = atoi(argv[2]);
int blockSize = argc > 3 ? atoi(argv[3]) : 1 * 1000 * 1000;
cout << "# Creating new identity (Threads: " << threads << " Level: " << level << " Size: " << blockSize << ")" << endl;
auto identity = Identity::createNew();
cout << "IDENTITY: " << identity->exportIdentity() << endl;
cout << "# Start hashing" << endl;
if(!identity->improveSecurityLevelMultithreaded(level, threads, blockSize, 0, true)) {
cout << "ERROR: Could not improve identity!" << endl;
return 1;
}
cout << "INFO: Found identity!" << endl;
cout << "IDENTITY: " << identity->exportIdentity() << endl;
cout << "LEVEL: " << identity->getSecurityLevel() << endl;
return 0;
}
/*
#IDENTITY: 5513931022VPXfjXSN1qRa2IZvTpS3xs+L7YvcBI2lTfXgOWAkaAQwQVgR8BXtWJ1MKUSh/HGNGNgdVGWYHcCg1BgQie3pValZZB39hVE4HBStcAA5SUz9Tf1JbXVABBVUJUwFmFkJZLlZUfCtaZXFBaUVBZ1poOXlQd09KSzN6ejYxMjVnaGN3bmtSKzRPOXdvZWJObVpaT3gyOTA3ND0=
#LEVEL: 36
#IDENTITY: 0VdYzXyIWNlPR991zQZLQoGLDYA4JvCX9GQ3MAYzpBRABWAHZTAmJvNQUAMxEEKGQDJVlnanF1YDdQQn0GewlXVWoAQmVMLX9FVyZaMAI7KlAIeXpPKGMCJ3BTVAJHLGlUHARbBlpqSUNJUURISXY1UXJCYmlBMHlZOVV3dFdRRXovM0lkU1hzNFJvWTkwT1JienA5bVRRPT0=
Current level = 41 at 1233527956186
Current offset index: 3750576000000 block size: 1000000
*/

1176
client/main.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,17 @@
syntax = "proto2";
package ts.proto.license.teamspeak;
message LicenseData { //_ZTIN3com10teamspeak310accounting8protobuf19Signed_License_InfoE => Signed_License_Info
required bytes chain = 1;
required bytes root = 2;
required int32 slots = 3;
required int32 servers = 4;
required string type = 5;
}
message License {
required LicenseData license = 1;
required bytes sign_chain = 2;
required bytes license_sign = 3;
}

218
client/src/Identity.cpp Normal file
View File

@ -0,0 +1,218 @@
//
// Created by wolverindev on 07.10.17.
//
#include <iostream>
#include <openssl/sha.h>
#include "misc/base64.h"
#include "Identity.h"
#define ECC_TYPE_INDEX 5
using namespace std;
static const char *TSKEY =
"b9dfaa7bee6ac57ac7b65f1094a1c155"
"e747327bc2fe5d51c512023fe54a2802"
"01004e90ad1daaae1075d53b7d571c30"
"e063b5a62a4a017bb394833aa0983e6e";
static int obfuscateInplace(char *data, uint32_t length) {
int dataSize = min((uint32_t) 100, length);
for (int i = 0; i < dataSize; i++) {
data[i] ^= TSKEY[i];
}
char hash[20];
hash_state ctx;
if (sha1_init(&ctx) != CRYPT_OK)
{ return -1; }
if (sha1_process(&ctx, (uint8_t*)data + 20, strlen(data + 20)) != CRYPT_OK)
{ return -1; }
if (sha1_done(&ctx, (uint8_t*)hash) != CRYPT_OK)
{ return -1; }
for (int i = 0; i < 20; i++) {
data[i] ^= hash[i];
}
return 0;
}
static int deObfuscateInplace(char *data, uint32_t length) {
char hash[20];
hash_state ctx;
if (sha1_init(&ctx) != CRYPT_OK)
{ return -1; }
if (sha1_process(&ctx, (uint8_t*)data + 20, strlen(data + 20)) != CRYPT_OK)
{ return -1; }
if (sha1_done(&ctx, (uint8_t*)hash) != CRYPT_OK)
{ return -1; }
for (int i = 0; i < 20; i++) {
data[i] ^= hash[i];
}
int dataSize = min((uint32_t) 100, length);
for (int i = 0; i < dataSize; i++) {
data[i] ^= TSKEY[i];
}
return 0;
}
namespace ts {
Identity* Identity::createNew() {
auto result = new Identity();
prng_state rndState{};
memset(&rndState, 0, sizeof(prng_state));
int err;
result->keyPair = new ecc_key;
cout << " -> " << find_prng("sprng") << endl;
if((err = ecc_make_key_ex(&rndState, find_prng("sprng"), result->keyPair, &ltc_ecc_sets[ECC_TYPE_INDEX])) != CRYPT_OK) {
cerr << "Cant create a new identity (Keygen)" << endl;
cerr << "Message: " << error_to_string(err) << endl;
delete result;
return nullptr;
}
return result;
}
Identity::Identity(std::string asnStruct, int64_t keyOffset, int64_t lastCheckedOffset) {
this->keyOffset = keyOffset;
this->lastCheckedOffset = lastCheckedOffset;
importKey(asnStruct);
}
Identity::Identity(std::string data) : Identity() {
int vindex = data.find('V');
assert(vindex > 0);
auto slevel = data.substr(0, vindex);
assert(slevel.find_first_not_of("0123456789") == std::string::npos);
this->keyOffset = stol(slevel);
data = data.substr(vindex + 1);
data = base64::decode(data);
if(deObfuscateInplace((char *) data.data(), data.length()) < 0) {
cerr << "Cand decript identitry data" << endl;
return;
}
importKey(base64::decode(data));
}
Identity::Identity() {
this->keyOffset = 0;
this->lastCheckedOffset = 0;
this->keyPair = nullptr;
}
Identity::~Identity() {
delete this->keyPair;
this->keyPair = nullptr;
}
void Identity::importKey(std::string asnStruct) {
this->keyPair = new ecc_key;
int err;
if((err = ecc_import_ex((const unsigned char *) asnStruct.data(), asnStruct.length(), this->keyPair, &ltc_ecc_sets[ECC_TYPE_INDEX])) != CRYPT_OK){
delete this->keyPair;
this->keyPair = nullptr;
cerr << "Cant import identity from asn structure" << endl;
cerr << "Message: " << error_to_string(err) << endl;
return;
}
}
std::string Identity::exportIdentity() {
string data = privateKey();
obfuscateInplace((char *) data.data(), data.length());
return to_string(this->lastValidKeyOffset()) + "V" + base64_encode(data);
}
std::string Identity::publicKey() {
assert(this->keyPair);
size_t bufferLength = 1028;
char buffer[bufferLength];
ecc_export((unsigned char *) buffer, &bufferLength, PK_PUBLIC, this->keyPair);
return base64_encode(string(buffer, bufferLength));
}
std::string Identity::privateKey() {
assert(this->keyPair);
size_t bufferLength = 1028;
char buffer[bufferLength];
ecc_export((unsigned char *) buffer, &bufferLength, PK_PRIVATE, this->keyPair);
return base64_encode(string(buffer, bufferLength));
}
ecc_key& Identity::getPrivateKey() {
return *keyPair;
}
#define MaxUlongString 20
bool Identity::improveSecurityLevel(int target) {
auto publicKey = this->publicKey();
char hashBuffer[publicKey.length() + MaxUlongString];
memcpy(hashBuffer, publicKey.data(), publicKey.length());
this->lastCheckedOffset = max(this->lastCheckedOffset, this->keyOffset);
int best = getSecurityLevel(hashBuffer, publicKey.length(), this->lastCheckedOffset);
while(true){
if(best >= target) return true;
int currentLevel = getSecurityLevel(hashBuffer, publicKey.length(), this->lastCheckedOffset);
if(currentLevel >= best){
this->keyOffset = this->lastCheckedOffset;
best = currentLevel;
}
this->lastCheckedOffset++;
}
}
int Identity::getSecurityLevel() {
auto length = publicKey().length();
char hashBuffer[length + MaxUlongString];
auto publicKey = this->publicKey();
memcpy(hashBuffer, publicKey.data(), publicKey.length());
return getSecurityLevel(hashBuffer, publicKey.length(), this->keyOffset);
}
int Identity::getSecurityLevel(char *hashBuffer, size_t keyLength, int64_t offset) {
char numBuffer[MaxUlongString];
int numLen = 0;
do {
numBuffer[numLen] = '0' + (offset % 10);
offset /= 10;
numLen++;
} while(offset > 0);
for(int i = 0; i < numLen; i++)
hashBuffer[keyLength + i] = numBuffer[numLen - (i + 1)];
char shaBuffer[SHA_DIGEST_LENGTH];
SHA1((const unsigned char *) hashBuffer, keyLength + numLen, (unsigned char *) shaBuffer);
//Leading zero bits
register int zeroBits = 0;
register int i;
for(i = 0; i < SHA_DIGEST_LENGTH; i++)
if(shaBuffer[i] == 0) zeroBits += 8;
else break;
if(i < SHA_DIGEST_LENGTH)
for(int bit = 0; bit < 8; bit++)
if((shaBuffer[i] & (1 << bit)) == 0) zeroBits++;
else break;
return zeroBits;
}
}

44
client/src/Identity.h Normal file
View File

@ -0,0 +1,44 @@
#pragma once
#include <tomcrypt.h>
#include <string>
namespace ts {
class Identity {
public:
static Identity* createNew();
explicit Identity(std::string data);
Identity(std::string asnStruct,int64_t keyOffset,int64_t lastCheckedOffset);
~Identity();
bool valid(){ return keyPair != nullptr; }
std::string publicKey();
std::string privateKey();
std::string exportIdentity();
ecc_key* getKeyPair(){
return keyPair;
}
ecc_key& getPrivateKey();
bool improveSecurityLevel(int target);
bool improveSecurityLevelMultithreaded(int target, int nthread = 4, size_t nblockSize = 1000, size_t baseOffset = 0, bool verbose = false);
int getSecurityLevel();
int64_t lastValidKeyOffset(){ return keyOffset; }
int64_t lastTestedKeyOffset(){ return lastCheckedOffset; }
private:
Identity();
int getSecurityLevel(char* hasBuffer, size_t keyLength, int64_t offset);
void importKey(std::string asn1);
ecc_key* keyPair = nullptr;
size_t keyOffset;
size_t lastCheckedOffset;
};
}

View File

@ -0,0 +1,189 @@
//
// Created by wolverindev on 14.10.17.
//
#include <iostream>
#include <openssl/sha.h>
#include <ThreadPool/Thread.h>
#include <vector>
#include <ThreadPool/Mutex.h>
#include <zconf.h>
#include "misc/base64.h"
#include "Identity.h"
#define ECC_TYPE_INDEX 5
#define USE_OPENSSL_SHA1
using namespace std::chrono;
using namespace std;
namespace ts {
inline int calculateSecutityLevel(uint8_t* hashBuffer, uint8_t* bufferToHash, int length){
register int zeroBits = 0;
register int bit;
register int i;
SHA1(bufferToHash, length, hashBuffer);
//Leading zero bits
for(i = 0; i < SHA_DIGEST_LENGTH; i++)
if(hashBuffer[i] == 0) zeroBits += 8;
else break;
if(i < SHA_DIGEST_LENGTH)
for(bit = 0; bit < 8; bit++)
if((hashBuffer[i] & (1 << bit)) == 0) zeroBits++;
else break;
return zeroBits;
}
inline void increaseNumBuffer(char* buffer, int numOffset, int* length){
int index = *length - 1;
while(true){
if(buffer[index] == '9'){
if(index - 1 < numOffset) {
buffer[index] = '1';
buffer[*length] = '0';
*length += 1;
return;
}
buffer[index--] = '0';
} else {
buffer[index] += 1;
return;
}
}
}
#define MaxUlongString 20
bool Identity::improveSecurityLevelMultithreaded(int target, int nthread, size_t nblockSize, size_t baseOffset, bool verbose) {
if(this->getSecurityLevel() >= target)
return false;
vector<threads::Thread*> threads;
auto publicKey = this->publicKey();
size_t currentBlockIndex = max(max(this->lastCheckedOffset, this->keyOffset), baseOffset);
threads::Mutex blockIndexLock;
auto activeDigging = new bool(true);
threads::Thread* thread;
for(int threadId = 0; threadId < nthread; threadId++){
thread = new threads::Thread([&](){
size_t offset = 0;
size_t endOffset = 0;
size_t bestOffset = 0;
char shaHashBuffer[SHA_DIGEST_LENGTH];
char hashBuffer[publicKey.length() + MaxUlongString];
memcpy(hashBuffer, publicKey.data(), publicKey.length());
#ifndef SLOW
//Setup some stuff
size_t pubKeyLength = publicKey.length();
#endif
while(*activeDigging){
//Get next range
blockIndexLock.lock();
offset = currentBlockIndex;
endOffset = currentBlockIndex + nblockSize;
currentBlockIndex += nblockSize;
blockIndexLock.unlock();
int bestLevel = this->getSecurityLevel(hashBuffer, publicKey.length(), this->keyOffset);
#ifdef SLOW
while(offset < endOffset){
int currentLevel = getSecurityLevel(hashBuffer, publicKey.length(), offset);
if(currentLevel >= best){
bestOffset = offset;
best = currentLevel;
}
offset++;
}
blockIndexLock.lock();
if(best > this->getSecurityLevel()){
if(verbose) cout << "Improved -> " << best << "/" << target << endl;
this->lastCheckedOffset = bestOffset;
this->keyOffset = bestOffset;
cout << "Got level: " << best << endl;
if(best >= target){
cout << "Done!" << endl;
*activeDigging = false;
}
}
blockIndexLock.unlock();
if(verbose) cout << "round done: highest -> " << best << endl;
#else
string strStartOffset = to_string(offset);
ssize_t roundsLeft = nblockSize;
auto hashBufferLength = static_cast<int>(publicKey.length() + strStartOffset.length());
memcpy(&hashBuffer[pubKeyLength], strStartOffset.c_str(), strStartOffset.length());
while(roundsLeft-- >= 0){
auto level = calculateSecutityLevel(reinterpret_cast<uint8_t *>(shaHashBuffer), reinterpret_cast<uint8_t *>(hashBuffer), hashBufferLength);
if(level > bestLevel){
{
threads::MutexLock l(blockIndexLock);
auto strOffset = string(&hashBuffer[pubKeyLength], hashBufferLength - pubKeyLength);
bestOffset = stoull(strOffset);
auto got = this->getSecurityLevel();
if(got >= level) {
cout << "Already got bedder level! (" << got << ")" << endl;
bestLevel = got;
continue;
}
if(verbose) cout << "Improved -> " << level << "/" << target << " [" << strOffset << "]" << endl;
this->lastCheckedOffset = bestOffset;
this->keyOffset = bestOffset;
bestLevel = level;
if(bestLevel >= target){
cout << "Done! (" << bestLevel << ")" << endl;
*activeDigging = false;
}
}
}
increaseNumBuffer(hashBuffer, pubKeyLength + 1, &hashBufferLength);
}
#endif
}
});
thread->name("IdentityHashDigger #" + to_string(threadId));
threads.push_back(thread);
}
if(verbose){
thread = new threads::Thread([&](){
auto lastIndex = currentBlockIndex;
while(*activeDigging){
for(int count = 0; count < 1000 && *activeDigging; count++)
usleep(1000);
blockIndexLock.lock();
cout << "Current level = " << this->getSecurityLevel() << " at " << this->keyOffset << endl;
cout << "Current offset index: " << currentBlockIndex << " block size: " << nblockSize << endl;
cout << "speed: " << (currentBlockIndex - lastIndex) / 1000 / 1000.f << " Mio. attemps/sec" << endl;
lastIndex = currentBlockIndex;
blockIndexLock.unlock();
}
});
thread->name("Status printer");
threads.push_back(thread);
}
for(auto elm : threads){
if(elm->state() == threads::ThreadState::RUNNING)
elm->join();
delete elm;
}
return true;
}
}

196
client/src/TinySHA1.hpp.h Normal file
View File

@ -0,0 +1,196 @@
/*
*
* TinySHA1 - a header only implementation of the SHA1 algorithm in C++. Based
* on the implementation in boost::uuid::details.
*
* SHA1 Wikipedia Page: http://en.wikipedia.org/wiki/SHA-1
*
* Copyright (c) 2012-22 SAURAV MOHAPATRA <mohaps@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _TINY_SHA1_HPP_
#define _TINY_SHA1_HPP_
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <stdint.h>
namespace sha1
{
class SHA1
{
public:
typedef uint32_t digest32_t[5];
typedef uint8_t digest8_t[20];
inline static uint32_t LeftRotate(uint32_t value, size_t count) {
return (value << count) ^ (value >> (32-count));
}
SHA1(){ reset(); }
virtual ~SHA1() {}
SHA1(const SHA1& s) { *this = s; }
const SHA1& operator = (const SHA1& s) {
memcpy(m_digest, s.m_digest, 5 * sizeof(uint32_t));
memcpy(m_block, s.m_block, 64);
m_blockByteIndex = s.m_blockByteIndex;
m_byteCount = s.m_byteCount;
return *this;
}
SHA1& reset() {
m_digest[0] = 0x67452301;
m_digest[1] = 0xEFCDAB89;
m_digest[2] = 0x98BADCFE;
m_digest[3] = 0x10325476;
m_digest[4] = 0xC3D2E1F0;
m_blockByteIndex = 0;
m_byteCount = 0;
return *this;
}
SHA1& processByte(uint8_t octet) {
this->m_block[this->m_blockByteIndex++] = octet;
++this->m_byteCount;
if(m_blockByteIndex == 64) {
this->m_blockByteIndex = 0;
processBlock();
}
return *this;
}
SHA1& processBlock(const void* const start, const void* const end) {
const uint8_t* begin = static_cast<const uint8_t*>(start);
const uint8_t* finish = static_cast<const uint8_t*>(end);
while(begin != finish) {
processByte(*begin);
begin++;
}
return *this;
}
SHA1& processBytes(const void* const data, size_t len) {
const uint8_t* block = static_cast<const uint8_t*>(data);
processBlock(block, block + len);
return *this;
}
const uint32_t* getDigest(digest32_t digest) {
size_t bitCount = this->m_byteCount * 8;
processByte(0x80);
if (this->m_blockByteIndex > 56) {
while (m_blockByteIndex != 0) {
processByte(0);
}
while (m_blockByteIndex < 56) {
processByte(0);
}
} else {
while (m_blockByteIndex < 56) {
processByte(0);
}
}
processByte(0);
processByte(0);
processByte(0);
processByte(0);
processByte( static_cast<unsigned char>((bitCount>>24) & 0xFF));
processByte( static_cast<unsigned char>((bitCount>>16) & 0xFF));
processByte( static_cast<unsigned char>((bitCount>>8 ) & 0xFF));
processByte( static_cast<unsigned char>((bitCount) & 0xFF));
memcpy(digest, m_digest, 5 * sizeof(uint32_t));
return digest;
}
const uint8_t* getDigestBytes(digest8_t digest) {
digest32_t d32;
getDigest(d32);
size_t di = 0;
digest[di++] = ((d32[0] >> 24) & 0xFF);
digest[di++] = ((d32[0] >> 16) & 0xFF);
digest[di++] = ((d32[0] >> 8) & 0xFF);
digest[di++] = ((d32[0]) & 0xFF);
digest[di++] = ((d32[1] >> 24) & 0xFF);
digest[di++] = ((d32[1] >> 16) & 0xFF);
digest[di++] = ((d32[1] >> 8) & 0xFF);
digest[di++] = ((d32[1]) & 0xFF);
digest[di++] = ((d32[2] >> 24) & 0xFF);
digest[di++] = ((d32[2] >> 16) & 0xFF);
digest[di++] = ((d32[2] >> 8) & 0xFF);
digest[di++] = ((d32[2]) & 0xFF);
digest[di++] = ((d32[3] >> 24) & 0xFF);
digest[di++] = ((d32[3] >> 16) & 0xFF);
digest[di++] = ((d32[3] >> 8) & 0xFF);
digest[di++] = ((d32[3]) & 0xFF);
digest[di++] = ((d32[4] >> 24) & 0xFF);
digest[di++] = ((d32[4] >> 16) & 0xFF);
digest[di++] = ((d32[4] >> 8) & 0xFF);
digest[di++] = ((d32[4]) & 0xFF);
return digest;
}
protected:
void processBlock() {
uint32_t w[80];
for (size_t i = 0; i < 16; i++) {
w[i] = (m_block[i*4 + 0] << 24);
w[i] |= (m_block[i*4 + 1] << 16);
w[i] |= (m_block[i*4 + 2] << 8);
w[i] |= (m_block[i*4 + 3]);
}
for (size_t i = 16; i < 80; i++) {
w[i] = LeftRotate((w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]), 1);
}
uint32_t a = m_digest[0];
uint32_t b = m_digest[1];
uint32_t c = m_digest[2];
uint32_t d = m_digest[3];
uint32_t e = m_digest[4];
for (std::size_t i=0; i<80; ++i) {
uint32_t f = 0;
uint32_t k = 0;
if (i<20) {
f = (b & c) | (~b & d);
k = 0x5A827999;
} else if (i<40) {
f = b ^ c ^ d;
k = 0x6ED9EBA1;
} else if (i<60) {
f = (b & c) | (b & d) | (c & d);
k = 0x8F1BBCDC;
} else {
f = b ^ c ^ d;
k = 0xCA62C1D6;
}
uint32_t temp = LeftRotate(a, 5) + f + e + k + w[i];
e = d;
d = c;
c = LeftRotate(b, 30);
b = a;
a = temp;
}
m_digest[0] += a;
m_digest[1] += b;
m_digest[2] += c;
m_digest[3] += d;
m_digest[4] += e;
}
private:
digest32_t m_digest;
uint8_t m_block[64];
size_t m_blockByteIndex;
size_t m_byteCount;
};
}
#endif

View File

@ -0,0 +1,454 @@
//
// Created by wolverindev on 07.10.17.
//
#include <log/LogUtils.h>
#include "Connection.h"
#include "misc/base64.h"
#include <misc/endianness.h>
#include <arpa/inet.h>
#include <poll.h>
#include <openssl/sha.h>
#include <bitset>
#include <protocol/Packet.h>
using namespace std;
using namespace ts;
using namespace ts::connection;
using namespace ts::protocol;
ServerConnection::ServerConnection() {
cryptionHandler = new CryptionHandler();
cryptionHandler->reset();
compressionHandler = new CompressionHandler();
readQueue = (buffer::SortedBufferQueue<ServerPacket> **) malloc(16 * sizeof(void*));
for(int i = 0; i < 16; i++) {
auto type = ts::protocol::PacketTypeInfo::fromid(i);
if(type != PacketTypeInfo::Undefined){
readQueue[i] = new buffer::SortedBufferQueue<ServerPacket>(ts::protocol::PacketTypeInfo::fromid(i), PacketTypeInfo::Command != type); //Ignore command low
} else {
readQueue[i] = nullptr;
}
}
}
ServerConnection::~ServerConnection() {
for(int i = 0; i < 16; i++)
if(readQueue[i])
delete readQueue[i];
free(readQueue);
this->rwThread->join();
}
static int sourcePort = 50000;
void ServerConnection::disconnect() {
//this->rwThread->cancel();
//this->handleThread->cancel();
this->connected = false;
if(this->socket) this->socket->close();
}
bool ServerConnection::connect(std::string host, std::string port, Identity *identity) {
this->clientIdentity = identity;
memset(&remoteAddress, 0, sizeof(remoteAddress));
remoteAddress.sin_family = AF_INET;
remoteAddress.sin_port = htons((uint16_t) std::stoi(port));
remoteAddress.sin_addr.s_addr = inet_addr(host.c_str());
#ifdef NoQt
/*
this->socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
int allow = 1;
setsockopt(this->socketfd, SOL_SOCKET, SO_REUSEADDR, &allow, sizeof(int));
memset(&localAddress, 0, sizeof(localAddress));
localAddress.sin_family = AF_INET;
localAddress.sin_addr.s_addr = htonl (INADDR_ANY);
localAddress.sin_port = htons (sourcePort++);
::connect(this->socketfd, (const sockaddr *) &remoteHost, sizeof(this->remoteAddress));
//bind(this->socketfd, (struct sockaddr *) &localAddress, sizeof(localAddress));
*/
this->socket = new UdpSocket;
if(!this->socket->setup(&remoteAddress)){
cerr << "Invalid socket setup" << endl;
}
#else
#endif
this->rwThread = new threads::Thread(THREAD_SAVE_OPERATIONS, [&]() {
#ifndef NoQt
this->qtSocket = new QUdpSocket();
QObject::connect(qtSocket,SIGNAL(bytesWritten(qint64)),this,SLOT(bytesWritten(qint64)));
QObject::connect(this->qtSocket, SIGNAL(readyRead()), this, SLOT(attempDatagramRead()));
this->qtSocket->bind(QHostAddress::Any, 23111);
this->socketfd = qtSocket->socketDescriptor();
cout << "Sock fd: " << this->socketfd << endl;
#endif
/*
auto cthread = QThread::currentThread();
cout << "ex" << endl;
runOnThread(qtSocket->thread(), [&](){
cout << "try" << endl;
qtSocket->moveToThread(cthread);
cout << "Moved" << endl;
});
cout << "Start rw" << endl;
*/
this->rwExecutor();
});
/*
this->handleThread = new threads::Thread([&]() {
this->handleExecutor();
});
*/
return true;
}
#ifndef NoQt
void ServerConnection::bytesWritten(qint64 b) {
cout << "written " << b << endl;
}
void ServerConnection::attempDatagramRead() {
cout << "Data " << endl;
}
#endif
//this->socket->socketDescriptor()
void ServerConnection::rwExecutor() {
pollfd pollData = {this->socket->getSocketDescriptor(), POLLRDHUP | POLLIN | POLLOUT, 0};
buffer::RawBuffer readBuffer(512);
std::shared_ptr<protocol::ServerPacket> readedPacket;
while (socket->getSocketDescriptor() > 0 && this->connected) {
int rfds = poll(&pollData, 1, -1);
bool select = false;
if(rfds == 0) {
usleep(5 * 1000);;
continue;
} else if(rfds < 0) {
break;
}
if (pollData.revents & POLLRDHUP || pollData.revents & POLLHUP) {
select = 1;
cerr << "Connection hang up!" << endl;
return;
}
if (pollData.revents & POLLIN) {
select = 1;
ssize_t readedBytes = -1;
#ifdef NoQt
readedBytes = socket->read(readBuffer.buffer, readBuffer.length);
#ifdef DEBUG
cout << "Read bytes (" << readedBytes << ")" << endl;
#endif
#else
QHostAddress senderAddr;
quint16 senderPort;
readedBytes = this->qtSocket->readDatagram(readBuffer.buffer, readBuffer.length, &senderAddr, &senderPort);
#endif
if (readedBytes < 0) {
cout << "fatal read error: " << errno << "/" << strerror(errno) << endl;
return;
}
readedPacket = std::make_shared<ServerPacket>(pipes::buffer_view(readBuffer.buffer, readedBytes));
if(!preProgressPacket(readedPacket)){
cerr << "Invalid packet preprocess!" << endl;
readedPacket = nullptr;
goto exitRead;
}
if(readedPacket->type().type() < 0 || readedPacket->type().type() > 16){
cerr << "Invalid packet id!" << endl;
readedPacket = nullptr;
goto exitRead;
}
//Deserelize packet
this->bufferQueueLock.lock();
if(!this->readQueue[readedPacket->type().type()]->push_pack(readedPacket)){
//TODO error handling
cout << "pkId: " << be2le16((char*) readedPacket->data().data_ptr()) << " -> " << readedPacket->type().name() << endl;
}
this->bufferQueueLock.unlock();
while(this->handleNextPacket());
exitRead:;
}
if (pollData.revents & POLLOUT) {
this->bufferQueueLock.lock();
if (!this->writeQueue.empty()) {
select = 1;
buffer::RawBuffer buffer = this->writeQueue.front();
#ifdef NoQt
auto res = this->socket->write(buffer.buffer, buffer.length);
if (res == -1) {
cout << "having write error: " << errno << "/" << strerror(errno) << " -> " << buffer.length << endl;
}
//cout << string() + "Write: " + PacketType::fromid(buffer.type()).name() << endl;
#else
this->qtSocket->writeDatagram(buffer.buffer, buffer.length, QHostAddress("localhost"), htons(this->remoteAddress.sin_port));
#endif
/*
if(!PacketTypeInfo::fromid(buffer.type()).requireAcknowledge()){ //Than we need a ack!
//Wait for acknowlage
this->acknowlageQueueLock.lock();
this->acknowlageQueue.push_back(buffer);
this->acknowlageQueueLock.unlock();
}
*/
this->writeQueue.pop_front();
}
this->bufferQueueLock.unlock();
}
if (!select) {
usleep(5 * 10000);
continue;
}
}
cerr << "rw loop broken!" << endl;
}
bool ServerConnection::preProgressPacket(std::shared_ptr<protocol::ServerPacket> packet){
packet->setEncrypted(!packet->hasFlag(PacketFlag::Unencrypted));
packet->setCompressed(packet->hasFlag(PacketFlag::Compressed));
packet->setFragmentedEntry(packet->hasFlag(PacketFlag::Fragmented));
if(packet->type() == PacketTypeInfo::Init1){
}
if (packet->isEncrypted()) {
string error = "success";
if (!cryptionHandler->progressPacketIn(packet.get(), error, false)) {
cerr << "Cant decript packet! Message: " << error << endl;
cerr << "Dropping it!" << endl;
return false;
}
}
#ifdef DEBUG
cout << "[IN] Packet type -> " << packet->type().name() << " flags " << packet->flags() << " Length: " << packet->data().length() << endl;
#endif
if(packet->type() == PacketTypeInfo::Command || packet->type() == PacketTypeInfo::CommandLow){ //needs an acknowledge
sendAcknowledge(packet->packetId(), packet->type() == PacketTypeInfo::CommandLow);
}
return true;
}
//TODO right packet recive order!
void ServerConnection::handleExecutor() {
shared_ptr<protocol::ServerPacket> packet = nullptr;
string error = "success";
while(this->connected){
while(this->handleNextPacket());
usleep(10 * 1000);
}
}
bool ServerConnection::handleNextPacket() {
shared_ptr<protocol::ServerPacket> packet = nullptr;
string error = "success";
if(this->autoHandle){
handleQueueLock.lock();
if(!this->handleQueue.empty()) {
packet = this->handleQueue.front();
this->handleQueue.pop_front();
}
handleQueueLock.unlock();
if(packet){
if(packet->type() == PacketTypeInfo::Ack || packet->type() == PacketTypeInfo::AckLow){
handlePacketAck(packet);
} else if(packet->type() == PacketTypeInfo::Command || packet->type() == PacketTypeInfo::CommandLow){
handlePacketCommand(packet);
} else if(packet->type() == PacketTypeInfo::Ping || packet->type() == PacketTypeInfo::Pong){
handlePacketPing(packet);
} else if(packet->type() == PacketTypeInfo::Voice || packet->type() == PacketTypeInfo::VoiceWhisper){
handlePacketVoice(packet);
}
return true;
}
}
for(int index = 0; index < 16; index++){
if(this->readQueue[index]) {
if(this->readQueue[index]->available() > 0){
auto npacket = this->readQueue[index]->peekNext(0);
packet = make_shared<ServerPacket>(npacket->buffer());
packet->setEncrypted(npacket->isEncrypted());
packet->setCompressed(npacket->isCompressed());
packet->setFragmentedEntry(npacket->isFragmentEntry());
break;
}
}
}
if(!packet) return false;
if(packet->isFragmentEntry()){
packet->setFragmentedEntry(false);
int deltaPacketIndex = 0;
while(this->connected){
std::shared_ptr<protocol::ServerPacket> nextElm = this->readQueue[packet->type().type()]->peekNext(++deltaPacketIndex);
if(!nextElm)
return false;
if(!nextElm) {
cerr << "Dropped fragment?" << endl;
packet = nullptr;
break;
}
packet->append_data({nextElm->data()});
if(nextElm->hasFlag(protocol::PacketFlag::Fragmented)) break; //Tail end
nextElm = nullptr;
}
this->readQueue[packet->type().type()]->pop_packets(deltaPacketIndex);
}
this->readQueue[packet->type().type()]->pop_packets(1);
if(packet->type() != PacketTypeInfo::Init1 && !this->compressionHandler->progressPacketIn(packet.get(), error)){
cerr << "Cant decompress packet! (" << error << ")" << endl;
packet = nullptr;
return true;
}
#if defined(DEBUG_PACKET_LOG)
cout << "Parsed packet " << packet->type().name() << " with id " << packet->packetId() << ". Data:" << endl;
hexDump((void *) packet->data().data_ptr(), packet->data().length(), 16, 8, [](std::string line) {
cout << "[IN] " << line << endl;
});
#endif
handleQueueLock.lock();
this->handleQueue.push_back(packet);
handleQueueLock.unlock();
return true;
}
using namespace std::chrono;
std::shared_ptr<protocol::ServerPacket> ServerConnection::readNextPacket(bool block) {
auto start = system_clock::now();
attempGet:
if(system_clock::now() - start > seconds(5)) return nullptr;
this->handleQueueLock.lock();
if (this->handleQueue.empty()) {
this->handleQueueLock.unlock();
if (!block) return nullptr;
usleep(5 * 1000);
goto attempGet;
}
std::shared_ptr<protocol::ServerPacket> packet = std::move(this->handleQueue.front());
this->handleQueue.pop_front();
this->handleQueueLock.unlock();
return packet;
}
bool ServerConnection::setupSharedSecret(std::string alpha, std::string beta, std::string sharedKey, std::string &error) {
return this->cryptionHandler->setupSharedSecret(alpha, beta, sharedKey, error);
}
//Packet splitting not working correctly! (On clientinit dosnt wait for the second)
void ServerConnection::sendPacket(ts::protocol::ClientPacket &packet) {
int maxDataLength = 500 - packet.header().length();
if(packet.data().length() > maxDataLength){
string error;
/*
packet.enableFlag(PacketFlag::Compressed);
if(!this->compressionHandler->progressPacketOut(&packet, error)){
cerr << "Compress error!" << endl;
return;
}
packet.enableFlag(PacketFlag::Compressed);
*/
if(packet.data().length() > maxDataLength){
std::vector<shared_ptr<ClientPacket>> siblings;
siblings.reserve(8);
{ //Split packets
auto buffer = packet.data();
const auto max_length = packet.type().max_length();
while(buffer.length() > max_length * 2) {
siblings.push_back(make_shared<ClientPacket>(packet.type(), buffer.view(0, max_length)));
buffer = buffer.range(max_length);
}
if(buffer.length() > max_length) { //Divide rest by 2
siblings.push_back(make_shared<ClientPacket>(packet.type(), buffer.view(0, buffer.length() / 2)));
buffer = buffer.range(buffer.length() / 2);
}
siblings.push_back(make_shared<ClientPacket>(packet.type(), buffer));
for(const auto& frag : siblings) {
frag->setFragmentedEntry(true);
frag->enableFlag(PacketFlag::NewProtocol);
}
}
for(const auto& entry : siblings)
this->sendPacket(*entry);
return;
}
}
if(!packet.memory_state.id_branded)
packet.applyPacketId(idManager);
packet.clientId(this->clientId);
string error = "success";
if (!this->cryptionHandler->progressPacketOut(&packet, error, false)) {
cerr << "Invalid crypt -> " << error << endl;
return;
}
buffer::RawBuffer buffer(packet.buffer().length());
memcpy(&buffer.buffer[0], packet.buffer().data_ptr(), packet.buffer().length());
this->bufferQueueLock.lock();
this->writeQueue.push_back(buffer);
#if defined(DEBUG_PACKET_LOG)
cout << "Send packet " << packet.type().name() << " fragmented -> " << packet.isFragmentEntry() << " length " << packet.data().length() << " flags " << packet.flags() << " ID: " << packet.packetId() << endl;
hexDump(buffer.buffer, buffer.length, buffer.length, buffer.length);
#endif
this->bufferQueueLock.unlock();
}
void ServerConnection::sendCommand(ts::Command command, bool low) {
auto data = command.build();
protocol::ClientPacket pkt(low ? protocol::PacketTypeInfo::CommandLow : protocol::PacketTypeInfo::Command, pipes::buffer_view{(void*) data.data(), data.length()});
#ifdef DEBUG
cout << "[Client -> Server][" << pkt.type().name() << "] " << pkt.data() << endl;
#endif
if(!low) pkt.enableFlag(PacketFlag::NewProtocol);
sendPacket(pkt);
}
void ServerConnection::sendAcknowledge(uint16_t packetId, bool low) {
if(breakAck) return;
char buffer[2];
le2be16(packetId, buffer);
protocol::ClientPacket pkt(low ? protocol::PacketTypeInfo::AckLow : protocol::PacketTypeInfo::Ack, pipes::buffer_view(buffer, 2));
#ifdef DEBUG
cout << "Sending packet acknowledge for " << packetId << " (Encrypt: " << encriptAck << ")" << endl;
#endif
if(!encriptAck)
pkt.enableFlag(PacketFlag::Unencrypted);
if(!low) pkt.toggle(protocol::PacketFlag::NewProtocol, true);
sendPacket(pkt);
}

View File

@ -0,0 +1,139 @@
#pragma once
#include <ThreadPool/Thread.h>
#include <ThreadPool/Mutex.h>
#include <string>
#include <thread>
#include <netinet/in.h>
#include <deque>
#include <list>
#include <protocol/Packet.h>
#include <protocol/buffers.h>
#include <src/protocol/socket/FilteredUDPSocket.h>
#include <src/Identity.h>
#include <protocol/CryptionHandler.h>
#include <protocol/CompressionHandler.h>
#define NoQt
#ifndef NoQt
#include <QUdpSocket>
#endif
#define DEBUG_PACKET_LOG
//#define LOG_CMD
namespace ts {
namespace connection {
namespace ConnectionState {
enum ConnectionState {
UNCONNECTED,
LLHANDSCHAKE,
HANDSCHAKE,
CONNECTED,
DISCONNECTED
};
}
class ServerConnection
#ifndef NoQt
: public QObject {
Q_OBJECT
#else
{
#endif
public:
ServerConnection();
~ServerConnection();
bool connect(std::string host, std::string port, Identity* identity);
void disconnect();
ConnectionState::ConnectionState getConnectionState(){ return cstate; }
bool handshake(std::string &errorMessage);
bool handshakeNew(Command &cmd, const std::string& alpha, std::string &errorMessage);
void sendPacket(ts::protocol::ClientPacket& packet);
void sendCommand(ts::Command command, bool low = false);
void sendAcknowledge(uint16_t packetId, bool low = false);
std::shared_ptr<protocol::ServerPacket> readNextPacket(bool block = true);
uint16_t getClientId(){
return this->clientId;
}
void setClientId(uint16_t id){
this->clientId = id;
}
#ifndef NoQt
public slots:
void attempDatagramRead();
void bytesWritten(qint64);
#endif
private:
bool encriptAck = false;
bool preProgressPacket(std::shared_ptr<protocol::ServerPacket> packet);
void rwExecutor();
void handleExecutor();
bool handleNextPacket();
bool setupSharedSecret(std::string alpha, std::string beta, std::string sharedKey, std::string& error);
void handlePacketPing(std::shared_ptr<protocol::ServerPacket> packet);
void handlePacketCommand(std::shared_ptr<protocol::ServerPacket> packet);
void handlePacketAck(std::shared_ptr<protocol::ServerPacket> packet);
void handlePacketVoice(std::shared_ptr<protocol::ServerPacket> packet);
bool connected = true;
sockaddr_in remoteAddress;
sockaddr_in localAddress;
UdpSocket* socket;
threads::Thread* rwThread = nullptr;
std::deque<buffer::RawBuffer> writeQueue;
#ifndef NoQt
QUdpSocket* qtSocket = nullptr;
#endif
threads::Mutex bufferQueueLock;
buffer::SortedBufferQueue<protocol::ServerPacket>** readQueue = nullptr;
//std::deque<RawBuffer> readQueue;
std::deque<buffer::RawBuffer> acknowlageQueue;
threads::Mutex acknowlageQueueLock;
protocol::PacketIdManager idManager;
threads::Thread* handleThread = nullptr;
std::list<std::shared_ptr<protocol::ServerPacket>> handleQueue; //Parsed packets
threads::Mutex handleQueueLock;
bool autoHandle = false;
ts::connection::CryptionHandler* cryptionHandler = nullptr;
ts::connection::CompressionHandler* compressionHandler = nullptr;
std::string remoteHost;
uint16_t remotePort;
Identity* clientIdentity;
ConnectionState::ConnectionState cstate = ConnectionState::UNCONNECTED;
bool breakAck = false;
/**
* TS3 Client data
*/
uint16_t clientId = 0;
std::deque<ChannelId> channels;
};
}
}

View File

@ -0,0 +1,361 @@
//
// Created by wolverindev on 08.10.17.
//
#include <tommath.h>
#include <bitset>
#include <openssl/sha.h>
#include "Connection.h"
#include "misc/base64.h"
#include "misc/endianness.h"
using namespace std;
using namespace std::chrono;
using namespace ts;
using namespace ts::connection;
using namespace ts::protocol;
const int InitVersionLength = 4;
const uint8_t InitVersion[InitVersionLength] = {0x09, 0x83, 0x8C, 0xCF};
/**
* Maybe memset to 0 for security?
*/
#define RESET_DATA \
bufferIndex = 0; \
delete pkt; \
pkt = nullptr;
inline ClientPacket *solvePuzzle(shared_ptr<ServerPacket> response, Identity *, std::string &);
inline std::string toString(mp_int* num){
char buffer[2048];
memset(buffer, 0, 2048);
auto len = mp_todecimal(num, buffer);
return string(buffer);
}
extern void hexdump(std::ostream& outs, const std::string& s, size_t line_len = 16);
bool ServerConnection::handshake(std::string &errorMessage) {
//setup the init mac
/**
* Low level
*/
ts::protocol::ClientPacket *pkt;
shared_ptr<ServerPacket> response;
int maxBufferSize = 512;
size_t bufferIndex = 0;
uint8_t buffer[maxBufferSize];
memset(buffer, 0, maxBufferSize);
int err = 0;
string error = "success";
beginCoocie:
memcpy(buffer, InitVersion, InitVersionLength);
bufferIndex += InitVersionLength;
buffer[bufferIndex++] = 0x00; //Login state
auto millis = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
memcpy(&buffer[bufferIndex], &millis, 4);
bufferIndex += 4;
//generate the alpha key
for (int i = 0; i < 4; i++) buffer[bufferIndex++] = (uint8_t) std::rand();
bufferIndex += 8; //Reserved bytes
pkt = new ts::protocol::ClientPacket(ts::protocol::PacketTypeInfo::Init1, pipes::buffer_view((void *) buffer, bufferIndex));
pkt->clientId(0);
pkt->toggle(ts::protocol::PacketFlag::Unencrypted, true);
pkt->applyPacketId(101, 0);
this->sendPacket(*pkt);
RESET_DATA;
response = readNextPacket();
if (!response) {
errorMessage = "could not get a valid response!";
return false;
}
if (response->type() != protocol::PacketTypeInfo::Init1) {
errorMessage = "invalid response type. Got: " + response->type().name();
return false;
}
if (response->data()[0] != 1) {
errorMessage = "iInvalid requested login type (" + to_string((int) response->data()[0]) + " == 1)";
return false;
}
//the second request of the manager
memcpy(buffer, InitVersion, InitVersionLength);
bufferIndex += InitVersionLength;
buffer[bufferIndex++] = 0x02; //Login state
if(response)
memcpy(&buffer[bufferIndex], response->data().string().substr(1, 16).data(), 16);
bufferIndex += 16; //Servers 16 bytes
if(response)
memcpy(&buffer[bufferIndex], response->data().string().substr(17, 4).data(), 4);
bufferIndex += 4; //My own 16 bytes, reversed
pkt = new ts::protocol::ClientPacket(ts::protocol::PacketTypeInfo::Init1, pipes::buffer_view((void *) buffer, bufferIndex));
pkt->clientId(0);
pkt->toggle(ts::protocol::PacketFlag::Unencrypted, true);
pkt->applyPacketId(101, 0);
this->sendPacket(*pkt);
RESET_DATA;
//We got the RSA challenge
response = readNextPacket();
if (!response) {
errorMessage = "could not get a valid response!";
return false;
}
if (response->type() != protocol::PacketTypeInfo::Init1) {
errorMessage = "invalid response type";
return false;
}
if (response->data()[0] != 3) {
if(response->data()[0] == 127) {
cout << "COOCIE RESET!" << endl;
goto beginCoocie;
}
hexdump(cout, response->data().string());
errorMessage = "Invalid requested login type (" + to_string((int) response->data()[0]) + " == 3 | unencripted -> " + (response->hasFlag(PacketFlag::Unencrypted) ? "true" : "false") + ")";
return false;
}
//Generate puzzel response
std::string alpha;
pkt = solvePuzzle(response, this->clientIdentity, alpha);
pkt->applyPacketId(101, 0);
this->sendPacket(*pkt);
RESET_DATA;
cout << "manager init done" << endl;
this->encriptAck = true;
response = readNextPacket();
if (!response) {
errorMessage = "could not get a valid response!";
return false;
}
if (response->type() != protocol::PacketTypeInfo::Command) {
errorMessage = "invalid response type: " + response->type().name();
return false;
}
auto command = response->asCommand();
if (command.getCommand().compare("initivexpand") != 0) {
// errorMessage = "invalid response command. Got: " + command.getCommand() + " Expected: initivexpand";
return this->handshakeNew(command, alpha, errorMessage);
}
//std::string alpha = base64::decode(command[0]["alpha"]);
std::string beta = base64::decode(command[0]["beta"]);
std::string omega = base64::decode(command[0]["omega"]); //Remotes public key
cout << "RESPONSE! -> " << command.build() << endl;
//Read public key
ecc_key remotePublicKey{};
if ((err = ecc_import((const unsigned char *) omega.data(), omega.length(), &remotePublicKey)) != CRYPT_OK) {
errorMessage = "ecc_import(...) returned " + to_string(err) + "/" + error_to_string(err);
return false;
}
if(strcmp(remotePublicKey.dp->name, "ECC-256") != 0){
errorMessage = "invalid imported public key! Curve found " + string(remotePublicKey.dp->name);
return false;
}
size_t sharedSecretLength = 32;
char sharedSecret[sharedSecretLength];
if ((err = ecc_shared_secret(clientIdentity->getKeyPair(), &remotePublicKey, (unsigned char *) sharedSecret, &sharedSecretLength)) != CRYPT_OK) {
errorMessage = "ecc_shared_secret(...) returned " + to_string(err) + "/" + error_to_string(err);
return false;
}
if (!setupSharedSecret(alpha, beta, string(sharedSecret, sharedSecretLength), error)) {
errorMessage = "setupSharedSecret(...) failed: " + error;
return false;
}
//this->readQueue[PacketType::Command.type()]->reset();
//TS 3.1
/*
response = readNextPacket();
if (!response) {
errorMessage = "could not get a valid response!";
return false;
}
if (response->type() != protocol::PacketType::Command) {
errorMessage = "invalid response type: " + response->type().name();
return false;
}
command = response->asCommand();
cout << "Having initiv2 -> " << response->data() << endl;
*/
this->idManager.nextPacketId(PacketTypeInfo::Command);
Command clientinit("clientinit");
//94ec66de-5940-4e38-b002-970df0cf6c94,62444179-0d99-42ba-a45c-c6b1557d079a,d95f9901-c42d-4bac-8849-7164fd9e2310
//clientinit["client_badges"] = "badges=450f81c1-ab41-4211-a338-222fa94ed157,c9e97536-5a2d-4c8e-a135-af404587a472,94ec66de-5940-4e38-b002-970df0cf6c94"; //,62444179-0d99-42ba-a45c-c6b1557d079a
clientinit["client_nickname"] = "Wolf C++ XXXX";
clientinit["client_version"] = "3.1 [Build: 1471417187]";
clientinit["client_platform"] = "Windows";
clientinit["client_version_sign"] = "Vr9F7kbVorcrkV5b/Iw+feH9qmDGvfsW8tpa737zhc1fDpK5uaEo6M5l2DzgaGqqOr3GKl5A7PF9Sj6eTM26Aw==";
clientinit["client_input_hardware"] = true;
clientinit["client_output_hardware"] = true;
clientinit["client_default_channel"] = "";
clientinit["client_default_channel_password"] = "";
string password;
if(!password.empty()){
char passwordBuffer[SHA_DIGEST_LENGTH];
SHA1((const unsigned char *) password.data(), password.length(), (unsigned char *) passwordBuffer);
password = base64_encode(string(passwordBuffer, SHA_DIGEST_LENGTH));
}
clientinit["client_server_password"] = password;
clientinit["client_meta_data"] = "";
clientinit["client_key_offset"] = this->clientIdentity->lastValidKeyOffset();
clientinit["client_nickname_phonetic"] = "";
clientinit["client_default_token"] = "";
clientinit["hwid"] = "123,456123123123";
sendCommand(clientinit);
while(true){
response = readNextPacket();
if (!response) {
errorMessage = "could not get a valid response!";
return false;
}
if(response->type() == PacketTypeInfo::Ack) continue;
break;
}
//TODO check ack id
if (!response) {
errorMessage = "could not get a valid response!";
return false;
}
if (response->type() != protocol::PacketTypeInfo::Command) {
errorMessage = "invalid response type: " + response->type().name();
return false;
}
if(response->asCommand().getCommand() == "initserver"){ //Got success
this->handleQueueLock.lock();
this->handleQueue.push_front(response);
this->handleQueueLock.unlock();
this->setClientId(response->asCommand()["aclid"]);
this->autoHandle = true;
cout << "Successfull connected!" << endl;
/*
std::thread([&](){
usleep(1000 * 1000);
cout << " -> send extra command" << endl;
//this->sendCommand(Command("channelsubscribeall return_code=1:i"));
while(true){
//this->sendCommand(Command("getconnectioninfo clid=320 return_code=1:112"));
//this->sendCommand(Command("ftgetfilelist cid=0 cpw path=\\/icons return_code=1:z0"));
this->sendCommand(Command("servergrouppermlist sgid=6 return_code=1:112"));
usleep(10 * 1000 * 1000);
}
//Command cmd("channelgetdescription cid=1 return_code=1:3o");
//Command cmd("clientupdate client_nickname=WolverinDEV22 return_code=__1_");
//Command cmd("clientdisconnect reasonid=8 reasonmsg=leaving");
//this->sendCommand(Command("permissionlist return_code=__1_"));
//this->sendCommand(Command("clientgetvariables clid=" + to_string(this->clientId)));
}).detach();
*/
return true;
}
cout << "Invalid connect: " << response->data() << endl;
//TODO error handling
return true;
}
inline ClientPacket* solvePuzzle(shared_ptr<ServerPacket> response, Identity *identity, std::string &alpha) {
uint32_t puzzelLength = be2le32(&((char*) response->data().data_ptr())[1 + 128]); //1 for the first byte (the state byte)
auto buffer = (char*) response->data().data_ptr();
char alphaBuffer[10];
for (int index = 0; index < 10; index++)
alphaBuffer[index] = 0; //rand();
alpha = string(alphaBuffer, 10);
//Generating command
auto pkey = identity->publicKey();
ts::Command command("clientinitiv", {
{"alpha", base64_encode(alphaBuffer, 10)},
{"omega", pkey},
{"ip", ""},
{"ot", 1} //Required by 3.1
});
std::string cmd = command.build();
//Sloving puzzel
mp_int x{};
mp_int n{};
mp_int result{};
//mp_init_multi(&x, &n, &result);
mp_init(&x);
mp_init(&n);
mp_init(&result);
char numBuffer[2048];
mp_read_unsigned_bin(&x, (const unsigned char *) &response->data()[1], 64); //One offset
mp_read_unsigned_bin(&n, (const unsigned char *) &response->data()[1 + 64], 64); //1 + 64 offset
cout << "X: " << toString(&x) << endl;
cout << "N: " << toString(&n) << endl;
cout << "Length: " << puzzelLength << endl;
mp_int exp{};
mp_init(&exp);
mp_2expt(&exp, puzzelLength);
//x ** (2 ** puzzelLength) mod n
int err = 0;
if ((err = mp_exptmod(&x, &exp, &n, &result)) != CRYPT_OK) {
cerr << "Invalid crypt: " << err << "/" << error_to_string(err) << endl;
}
int resultBufferLength = mp_unsigned_bin_size(&result);
char resultBuffer[resultBufferLength];
mp_to_unsigned_bin(&result, (unsigned char *) resultBuffer);
//mp_clear_multi(&x, &n, &exp, &result);
mp_clear(&x);
mp_clear(&n);
mp_clear(&exp);
mp_clear(&result);
size_t packetBufferLength = InitVersionLength + 1 + 232 + 64 + cmd.length();
char packetBuffer[packetBufferLength];
memset(packetBuffer, 0, packetBufferLength);
memcpy(packetBuffer, InitVersion, InitVersionLength);
packetBuffer[InitVersionLength] = 0x04;
//Copy old data
memcpy(&packetBuffer[InitVersionLength + 1], &response->data()[1], 232);
memcpy(&packetBuffer[InitVersionLength + 1 + 232 + (64 - resultBufferLength)], resultBuffer, resultBufferLength);
memcpy(&packetBuffer[InitVersionLength + 1 + 232 + 64], cmd.data(), cmd.length());
cout << "sending puzzel sulution" << endl;
auto pkt = new ts::protocol::ClientPacket(ts::protocol::PacketTypeInfo::Init1, pipes::buffer_view((void *) packetBuffer, packetBufferLength));
pkt->clientId(0);
pkt->toggle(ts::protocol::PacketFlag::Unencrypted, true);
return pkt;
}

View File

@ -0,0 +1,158 @@
#include <protocol/Packet.h>
#include <arpa/inet.h>
#include "Connection.h"
#include "misc/base64.h"
#include "misc/endianness.h"
using namespace std;
using namespace ts;
using namespace ts::connection;
using namespace ts::protocol;
//notifystatusfiletransfer clientftfid=4093 status=2063 msg=lost\sfile\stransfer\sconnection size=16384
extern void hexdump(std::ostream& outs, const std::string& s, size_t line_len = 16);
inline void downloadStuff(std::string key, uint16_t port, uint64_t size){
threads::Thread([key, port, size](){
int socketId = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
assert(socketId > 1);
sockaddr_in server;
server.sin_addr.s_addr = inet_addr("127.0.0.1");
server.sin_family = AF_INET;
server.sin_port = htons( port );
//Connect to remote server
if (connect(socketId , (struct sockaddr *)&server , sizeof(server)) < 0)
{
perror("connect failed. Error");
return;
}
uint32_t readed = 0;
assert(send(socketId, key.data(), key.length(), 0) > 0);
while(readed < size + 3){
char buffer[size];
auto readedBytes = recv(socketId, buffer, size - readed, MSG_DONTWAIT);
if(readedBytes < 0) {
//cerr << "Invalid ft read" << endl;
continue;
}
if(readedBytes == 0){
continue;
}
hexdump(cout, string(buffer, readedBytes));
readed += readedBytes;
}
cout << "File downloaded!" << endl;
}).detach();
}
void ServerConnection::handlePacketAck(std::shared_ptr<protocol::ServerPacket> packet) {
auto packetId = be2le16((const char*) packet->data().data_ptr());
#if defined(DEBUG_PACKET_LOG) || defined(LOG_ACK)
cout << "Got ack for " << packetId << endl;
#endif
}
void ServerConnection::handlePacketCommand(std::shared_ptr<protocol::ServerPacket> packet) {
auto command = packet->asCommand();
#if defined(DEBUG_PACKET_LOG) || defined(LOG_CMD)
cout << "[Server -> Client][" << packet->type().name() << "] " << packet->data() << endl;
#endif
if (command.getCommand().compare("notifyconnectioninforequest") == 0) { //TODO
cout << "Send response" << endl;
Command cmd(
string("setconnectioninfo"), {
{"connection_ping", 10000000},
{"connection_ping_deviation", 10000000},
{"connection_packets_sent_speech", 0},
{"connection_packets_sent_keepalive", 0},
{"connection_packets_sent_control", rand()},
{"connection_bytes_sent_speech", 0},
{"connection_bytes_sent_keepalive", 0},
{"connection_bytes_sent_control", 0},
{"connection_packets_received_speech", 0},
{"connection_packets_received_keepalive", 0},
{"connection_packets_received_control", 0},
{"connection_bytes_received_speech", 0},
{"connection_bytes_received_keepalive", 0},
{"connection_bytes_received_control", 0},
{"connection_server2client_packetloss_speech", 10000000},
{"connection_server2client_packetloss_keepalive", 10000000},
{"connection_server2client_packetloss_control", 10000000},
{"connection_server2client_packetloss_total", 10000000},
{"connection_bandwidth_sent_last_second_speech", 0},
{"connection_bandwidth_sent_last_second_keepalive", 0},
{"connection_bandwidth_sent_last_second_control", 0},
{"connection_bandwidth_sent_last_minute_speech", 0},
{"connection_bandwidth_sent_last_minute_keepalive", 0},
{"connection_bandwidth_sent_last_minute_control", 0},
{"connection_bandwidth_received_last_second_speech", 0},
{"connection_bandwidth_received_last_second_keepalive", 0},
{"connection_bandwidth_received_last_second_control", 0},
{"connection_bandwidth_received_last_minute_speech", 0},
{"connection_bandwidth_received_last_minute_keepalive", 0},
{"connection_bandwidth_received_last_minute_control", 0}
}
);
sendCommand(cmd, true);
} else if (command.command() == "notifyserverupdated") {
#if defined(DEBUG_PACKET_LOG) || defined(LOG_CMD)
cout << "notifyserverupdated -> " << endl;
cout << "Last data: " << packet->data().string().substr(packet->data().length() - 10) << endl;
#endif
} else if (command.command() == "notifystartdownload") {
cout << "Client download: " << command.build() << endl;
auto port = command["port"].as<uint16_t>();
auto key = command["ftkey"].string();
auto size = command["size"].as<uint64_t>();
downloadStuff(key, port, size);
} else if (command.command() == "channellist") {
cout << "Breaking ack" << endl;
for (int index = 0; index < command.bulkCount(); index++) {
this->channels.push_back(command[index]["cid"].as<ChannelId>());
}
}
}
void ServerConnection::handlePacketVoice(std::shared_ptr<protocol::ServerPacket> packet) {}
static int pingIndex = 0;
void ServerConnection::handlePacketPing(std::shared_ptr<protocol::ServerPacket> packet) {
if(packet->type() == PacketTypeInfo::Pong){
//cout << "[PING] gota " << be2le16(packet->data().data()) << endl;
return;
}
char buffer[2];
le2be16(packet->packetId(), buffer);
ClientPacket pkt(PacketTypeInfo::Pong, pipes::buffer_view{buffer, 2});
pkt.enableFlag(PacketFlag::Unencrypted);
sendPacket(pkt);
ClientPacket ping(PacketTypeInfo::Ping, pipes::buffer_view{buffer, 0});
ping.enableFlag(PacketFlag::Unencrypted);
sendPacket(ping);
//cout << "[PING] Reqe " << ping.packetId() << endl;
//cout << "[PONG] Send " << packet->packetId() << endl;
if(this->clientId > 0 && this->channels.size() > 0) {
Command command("clientmove");
command["clid"] = this->clientId;
auto idx = rand() % this->channels.size();
command["cid"] = this->channels[idx];
this->sendCommand(command);
std::thread([&] {
threads::self::sleep_for(chrono::seconds(1));
Command cmd("channelcreate");
cmd["channel_name"] = to_string(rand()) + "_" + to_string(rand());
//this->sendCommand(cmd);
}).detach();
}
}

View File

@ -0,0 +1,154 @@
#include <ed25519/ed25519.h>
#include <ed25519/sha512.h>
#include <misc/base64.h>
#include <misc/digest.h>
#include "Connection.h"
#include "License.h"
#include <log/LogUtils.h>
using namespace std;
using namespace std::chrono;
using namespace ts;
using namespace license::teamspeak;
using namespace ts::connection;
using namespace ts::protocol;
int __ed_sha512_init(sha512_context* ctx) {
//assert(!ctx->context);
ctx->context = new hash_state{};
return sha512_init((hash_state*) ctx->context) == CRYPT_OK;
}
int __ed_sha512_final(sha512_context* ctx, unsigned char *out) {
assert(ctx->context);
auto result = sha512_done((hash_state*) ctx->context, out) == CRYPT_OK;
delete (hash_state*) ctx->context;
return result;
}
int __ed_sha512_update(sha512_context* ctx, const unsigned char *msg, size_t len) {
assert(ctx->context);
return sha512_process((hash_state*) ctx->context, msg, len) == CRYPT_OK;
}
static sha512_functions __ed_sha512_functions {
__ed_sha512_init,
__ed_sha512_final,
__ed_sha512_update
};
bool ServerConnection::handshakeNew(Command &initivexpand2, const std::string& alpha, std::string &errorMessage) {
if(&__ed_sha512_functions != &_ed_sha512_functions)
_ed_sha512_functions = __ed_sha512_functions;
cout << initivexpand2.build() << endl;
u_char seed[32 * 2];
u_char clientPrivateKey[32];
u_char clientPublicKey[32];
ed25519_create_keypair(clientPublicKey, clientPrivateKey, seed);
cout << "Client key: " << base64::encode((char*) clientPrivateKey, 32) << endl;
cout << "Privet key:" << endl;
hexDump(clientPrivateKey, 32);
cout << "Public key:" << endl;
hexDump(clientPublicKey, 32);
auto license = base64::decode(initivexpand2["l"]);
auto licensestream = stringstream(license);
auto chain = LicenseChain::parse(licensestream, errorMessage);
if(!chain) return false;
chain->print();
unique_ptr<ecc_key> serverPublic(new ecc_key{});
auto omega = base64::decode(initivexpand2["omega"]);
ecc_import((u_char*) omega.data(), omega.length(), serverPublic.get());
//7B 1E AC 02 CE 77 35 0E EF C4 5C 1C F7 54 04 87 A9 A7 64 A7 8F 04 F7 53 58 64 84 D7 0A 97 F2 63
//[0x7b, 0x1e, 0xac, 0x2, 0xce, 0x77, 0x35, 0xe, 0xef, 0xc4, 0x5c, 0x1c, 0xf7, 0x54, 0x4, 0x87, 0xa9, 0xa7, 0x64, 0xa7, 0x8f, 0x4, 0xf7, 0x53, 0x58, 0x64, 0x84, 0xd7, 0xa, 0x97, 0xf2, 0xe3]
//License signed from server :)
auto licenseHash = digest::sha256(license);
auto licenseSign = base64::decode(initivexpand2["proof"]);
int state;
assert(ecc_verify_hash((u_char*) licenseSign.c_str(), licenseSign.length(), (u_char*) licenseHash.c_str(), licenseHash.length(), &state, serverPublic.get()) == CRYPT_OK);
cout << "State: " << state << endl;
assert(state == 1);
//EK!
this->idManager.nextPacketId(PacketTypeInfo::Command);
cout << this->idManager.currentPacketId(PacketTypeInfo::Command) << endl;
Command clientek("clientek");
clientek["ek"] = base64::encode((char*) clientPublicKey, 32);
auto rawProof = string((char*) clientPublicKey, 32) + base64::decode(initivexpand2["beta"]);
cout << " -> " << rawProof.length() << endl;
size_t signBufferLength = 120;
char signBuffer[signBufferLength];
prng_state prngState{};
memset(&prngState, 0, sizeof(prngState));
cout << "Data: " << base64::encode(rawProof) << endl;
cout << "KEY: " << this->clientIdentity->privateKey() << endl;
rawProof = digest::sha256(rawProof);
assert(ecc_sign_hash((u_char*) rawProof.data(), rawProof.length(), (u_char*) signBuffer, &signBufferLength, &prngState, find_prng("sprng"), this->clientIdentity->getKeyPair()) == CRYPT_OK);
cout << "ecc_sign_hash() -> " << base64::encode(signBuffer, signBufferLength) << endl;
clientek["proof"] = base64::encode(signBuffer, signBufferLength);
this->sendCommand(clientek, false);
//TODO magic stuff
shared_ptr<ServerPacket> response;
response = readNextPacket();
if (!response) {
errorMessage = "could not get a valid response!";
return false;
}
cout << "Type: " << response->type().name() << endl;
cout << "ID: " << (int) response->data()[0] << " " << (int) response->data()[1] << endl;
LicensePublicKey serverroot;
memcpy(serverroot, public_root, 32);
if(initivexpand2[0].has("root")) {
cout << "Cot costume server root!" << endl;
auto root = base64::decode(initivexpand2["root"]);
memcpy(serverroot, root.data(), 32);
}
cout << "Public root key: " << endl;
for(const auto& e : serverroot)
cout << hex << "0x" << (int) (uint8_t) e << " " << endl;
string sharedData;
this->cryptionHandler->setupSharedSecretNew(alpha, base64::decode(initivexpand2["beta"]), (char*) clientPrivateKey, (char*) chain->generatePublicKey(serverroot).data());
//this->cryptionHandler->setupSharedSecretNew(alpha, base64::decode(initivexpand2["beta"]), (char*) clientPrivateKey, (char*) public_tea_root);
threads::self::sleep_for(milliseconds(250));
Command clientinit("clientinit");
//94ec66de-5940-4e38-b002-970df0cf6c94,62444179-0d99-42ba-a45c-c6b1557d079a,d95f9901-c42d-4bac-8849-7164fd9e2310
//clientinit["client_badges"] = "badges=450f81c1-ab41-4211-a338-222fa94ed157,c9e97536-5a2d-4c8e-a135-af404587a472,94ec66de-5940-4e38-b002-970df0cf6c94"; //,62444179-0d99-42ba-a45c-c6b1557d079a
clientinit["client_nickname"] = "Wolf C++ XX";
clientinit["client_version"] = "3.1.8 [Build: 1516614607]";
clientinit["client_platform"] = "Windows";
clientinit["client_version_sign"] = "gDEgQf/BiOQZdAheKccM1XWcMUj2OUQqt75oFuvF2c0MQMXyv88cZQdUuckKbcBRp7RpmLInto4PIgd7mPO7BQ==";
clientinit["client_input_hardware"] = true;
clientinit["client_output_hardware"] = true;
clientinit["client_default_channel"] = "";
clientinit["client_default_channel_password"] = "";
clientinit["client_server_password"] = "";
clientinit["client_meta_data"] = "";
clientinit["client_key_offset"] = this->clientIdentity->lastValidKeyOffset();
clientinit["client_nickname_phonetic"] = "";
clientinit["client_default_token"] = "";
clientinit["hwid"] = "123,456123123123";
sendCommand(clientinit);
this->autoHandle = true;
/*
response = readNextPacket();
if (!response) {
errorMessage = "could not get a valid response!";
return false;
}
*/
return true;
}

View File

@ -0,0 +1,115 @@
//
// Created by root on 13.10.17.
//
#include "FilteredUDPSocket.h"
#include <iostream>
#include <cstring>
#include <ifaddrs.h>
#include <netinet/udp.h> //Provides declarations for udp header
#include <netinet/ip.h> //Provides declarations for ip header
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <linux/if_ether.h>
#include <linux/filter.h>
#include <chrono>
#include <zconf.h>
using namespace std;
using namespace std::chrono;
using namespace ts::connection;
FilteredUdpSocket::FilteredUdpSocket() {}
FilteredUdpSocket::~FilteredUdpSocket() {}
bool FilteredUdpSocket::setup(sockaddr_in * remoteAddr) {
srand(system_clock::now().time_since_epoch().count()); // should only be called once
int r = (lrand48() % (50 * 1000)) + 1000; // returns a pseudo-random integer between 0 and RAND_MAX
this->remoteAdress = new sockaddr_in;
memcpy(this->remoteAdress, remoteAddr, sizeof(sockaddr_in));
this->socketDescriptor = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(this->socketDescriptor < 0){
cerr << "Invalid socket create: " << errno << " - " << this->socketDescriptor << " -> " << strerror(errno) << endl;
}
int allow = 1;
setsockopt(this->socketDescriptor, SOL_SOCKET, SO_REUSEADDR, &allow, sizeof(int));
this->localAdress = new sockaddr_in;
memset((char *) this->localAdress, 0, sizeof(sockaddr_in));
localAdress->sin_family = AF_INET;
localAdress->sin_addr.s_addr = htonl(INADDR_ANY);
localAdress->sin_port = htons(r);
/* sudo tcpdump -q udp port 256 -dd */
struct sock_filter code[ ] = {
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 8, 0x000086dd },
{ 0x30, 0, 0, 0x00000014 },
{ 0x15, 2, 0, 0x00000084 },
{ 0x15, 1, 0, 0x00000006 },
{ 0x15, 0, 17, 0x00000011 },
{ 0x28, 0, 0, 0x00000036 },
{ 0x15, 14, 0, 0x00002fbd },
{ 0x28, 0, 0, 0x00000038 },
{ 0x15, 12, 13, 0x00002fbd },
{ 0x15, 0, 12, 0x00000800 },
{ 0x30, 0, 0, 0x00000017 },
{ 0x15, 2, 0, 0x00000084 },
{ 0x15, 1, 0, 0x00000006 },
{ 0x15, 0, 8, 0x00000011 },
{ 0x28, 0, 0, 0x00000014 },
{ 0x45, 6, 0, 0x00001fff },
{ 0xb1, 0, 0, 0x0000000e },
{ 0x48, 0, 0, 0x0000000e },
{ 0x15, 2, 0, 0x00002fbd },
{ 0x48, 0, 0, 0x00000010 },
{ 0x15, 0, 1, 0x00002fbd },
{ 0x6, 0, 0, 0x00040000 },
{ 0x6, 0, 0, 0x00000000 },
};
struct sock_fprog bpf = {
.len = sizeof(code) / sizeof(*code),
.filter = code,
};
//auto response = setsockopt(this->socketDescriptor, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf));
//if (response < 0) cerr << "Invalid attach!" << endl;
/* ... bail out ... */
/*
*
if(connect(this->socketDescriptor, (const sockaddr *) remoteAddr, sizeof(sockaddr_in)) < 0){
cerr << "Invalid connect" << endl;
}
*/
if(bind(this->socketDescriptor, (const sockaddr *) localAdress, sizeof(sockaddr_in)) < 0) cout << "XXX" << endl;
return true;
}
void FilteredUdpSocket::close() {
if(this->socketDescriptor > 0) {
shutdown(this->socketDescriptor, SHUT_RDWR);
::close(this->socketDescriptor);
this->socketDescriptor = 0;
}
}
ssize_t FilteredUdpSocket::write(const char *buffer, size_t size) {
return sendto(this->socketDescriptor, buffer, size, 0, (const sockaddr *) this->remoteAdress, sizeof(sockaddr_in));
}
ssize_t FilteredUdpSocket::read(char *buffer, size_t size) {
return recv(this->socketDescriptor, (void *) buffer, size, 0);
}

View File

@ -0,0 +1,27 @@
#pragma once
#include <netinet/in.h>
namespace ts {
namespace connection {
class FilteredUdpSocket {
public:
FilteredUdpSocket();
~FilteredUdpSocket();
bool setup(sockaddr_in*);
void close();
ssize_t read(char* buffer, size_t size);
ssize_t write(const char* buffer, size_t size);
int getSocketDescriptor(){ return socketDescriptor; }
private:
int socketDescriptor;
sockaddr_in* remoteAdress = nullptr;
sockaddr_in* localAdress = nullptr;
};
typedef FilteredUdpSocket UdpSocket;
}
}

View File

@ -0,0 +1,151 @@
//
// Created by wolverindev on 12.10.17.
//
#include <iostream>
#include <cstring>
#include <ifaddrs.h>
#include <netinet/udp.h> //Provides declarations for udp header
#include <netinet/ip.h> //Provides declarations for ip header
#include <arpa/inet.h>
#include "RawUDPSocket.h"
using namespace std;
using namespace ts::connection;
/*
96 bit (12 bytes) pseudo header needed for udp header checksum calculation
*/
struct pseudo_header
{
u_int32_t source_address;
u_int32_t dest_address;
u_int8_t placeholder;
u_int8_t protocol;
u_int16_t udp_length;
};
RawUdpSocket::RawUdpSocket() {}
RawUdpSocket::~RawUdpSocket() {}
uint16_t RawUdpSocket::buildCheckSum(uint16_t* buffer, size_t size) {
register long sum;
unsigned short oddbyte;
register short answer;
sum = 0;
while (size > 1) {
sum += *buffer++;
size -= 2;
}
if (size == 1) {
oddbyte = 0;
*((u_char *) &oddbyte) = *(u_char *) buffer;
sum += oddbyte;
}
sum = (sum >> 16) + (sum & 0xffff);
sum = sum + (sum >> 16);
answer = (short) ~sum;
return (answer);
}
int RawUdpSocket::read(char *buffer, size_t size) {}
int RawUdpSocket::write(const char *buffer, size_t size) {
char datagram[4096];
memset(datagram, 0, 4096);
memcpy(&datagram[sizeof(iphdr) + sizeof(udphdr)], buffer, size);
//IP header
struct iphdr *iph = (struct iphdr *) datagram;
//UDP header
struct udphdr *udph = (struct udphdr *) (datagram + sizeof(struct ip));
//Setup the ip header
iph->ihl = 5;
iph->version = 4;
iph->tos = 0;
iph->tot_len = sizeof(iphdr) + sizeof(udphdr) + size; //Total length
iph->id = htonl(12); //TODO increase
iph->frag_off = 0;
iph->ttl = 255; //Max 255 hops maybe change it to default 64
iph->protocol = IPPROTO_UDP;
iph->saddr = localAdress->sin_addr.s_addr;
iph->daddr = remoteAdress->sin_addr.s_addr;
iph->check = this->buildCheckSum ((unsigned short *) datagram, iph->tot_len);
udph->source = localAdress->sin_port;
udph->dest = remoteAdress->sin_port;
udph->len = htons(8 + size);
size_t csumLength = sizeof(struct pseudo_header) + sizeof(struct udphdr) + size;
char csumData[csumLength];
pseudo_header psh;
//Now the UDP checksum using the pseudo header
psh.source_address = localAdress->sin_addr.s_addr;
psh.dest_address = remoteAdress->sin_addr.s_addr;
psh.placeholder = 0;
psh.protocol = IPPROTO_UDP;
psh.udp_length = htons(sizeof(struct udphdr) + size);
memcpy(csumData , (char*) &psh , sizeof (struct pseudo_header));
memcpy(csumData + sizeof(struct pseudo_header) , udph , sizeof(struct udphdr) + size);
udph->check = buildCheckSum((uint16_t *) csumData, csumLength);
auto written = sendto(this->socketDescriptor, datagram, iph->tot_len, 0, (struct sockaddr *) this->remoteAdress, sizeof(sockaddr));
if(written != iph->tot_len){
cerr << "Invalid write: " << written << endl;
return -1;
}
cout << "Write: " << written << endl;
return size;
}
bool RawUdpSocket::setup(sockaddr_in *remoteAdress) {
this->remoteAdress = new sockaddr_in;
memcpy(this->remoteAdress, remoteAdress, sizeof(sockaddr_in));
this->socketDescriptor = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if(this->socketDescriptor < 0){
if(this->socketDescriptor == EPERM){
cerr << "Invalid permission. Dont have permission to create a new RAW socket!";
return false;
}
cerr << "Invalid socket create: " << errno << " - " << this->socketDescriptor << " -> " << strerror(errno) << endl;
}
//get local addr
this->localAdress = new sockaddr_in;
struct ifaddrs *ifAddrStruct = NULL;
getifaddrs(&ifAddrStruct);
for (ifaddrs* ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
if (!ifa->ifa_addr) {
continue;
}
if (ifa->ifa_addr->sa_family == AF_INET) { // check it is IP4
// is a valid IP4 Address
memcpy(this->localAdress, ifa->ifa_addr, sizeof(sockaddr_in));
/*
tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
char addressBuffer[INET_ADDRSTRLEN];
inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
*/
}
/*
else if (ifa->ifa_addr->sa_family == AF_INET6) { // check it is IP6
// is a valid IP6 Address
tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
char addressBuffer[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
}
*/
}
if (ifAddrStruct != NULL) freeifaddrs(ifAddrStruct);
this->localAdress->sin_port = 1232;
return true;
}

View File

@ -0,0 +1,28 @@
#pragma once
#include <netinet/in.h>
namespace ts {
namespace connection {
class RawUdpSocket {
public:
RawUdpSocket();
~RawUdpSocket();
bool setup(sockaddr_in*);
int read(char* buffer, size_t size);
int write(const char* buffer, size_t size);
uint16_t buildCheckSum(uint16_t* buffer, size_t size);
int getSocketDescriptor(){ return socketDescriptor; }
private:
int socketDescriptor;
sockaddr_in* remoteAdress = nullptr;
sockaddr_in* localAdress = nullptr;
};
typedef RawUdpSocket UdpSocket;
}
}

View File

@ -0,0 +1,122 @@
# - Try to find Glib and its components (gio, gobject etc)
# Once done, this will define
#
# GLIB_FOUND - system has Glib
# GLIB_INCLUDE_DIRS - the Glib include directories
# GLIB_LIBRARIES - link these to use Glib
#
# Optionally, the COMPONENTS keyword can be passed to find_package()
# and Glib components can be looked for. Currently, the following
# components can be used, and they define the following variables if
# found:
#
# gio: GLIB_GIO_LIBRARIES
# gobject: GLIB_GOBJECT_LIBRARIES
# gmodule: GLIB_GMODULE_LIBRARIES
# gthread: GLIB_GTHREAD_LIBRARIES
#
# Note that the respective _INCLUDE_DIR variables are not set, since
# all headers are in the same directory as GLIB_INCLUDE_DIRS.
#
# Copyright (C) 2012 Raphael Kubo da Costa <rakuco@webkit.org>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. 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.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS 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 COPYRIGHT HOLDER OR ITS
# 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.
find_package(PkgConfig)
pkg_check_modules(PC_GLIB QUIET glib-2.0)
find_library(GLIB_LIBRARIES
NAMES glib-2.0
HINTS ${PC_GLIB_LIBDIR}
${PC_GLIB_LIBRARY_DIRS}
)
# Files in glib's main include path may include glibconfig.h, which,
# for some odd reason, is normally in $LIBDIR/glib-2.0/include.
get_filename_component(_GLIB_LIBRARY_DIR ${GLIB_LIBRARIES} PATH)
find_path(GLIBCONFIG_INCLUDE_DIR
NAMES glibconfig.h
HINTS ${PC_LIBDIR} ${PC_LIBRARY_DIRS} ${_GLIB_LIBRARY_DIR}
${PC_GLIB_INCLUDEDIR} ${PC_GLIB_INCLUDE_DIRS}
PATH_SUFFIXES glib-2.0/include
)
find_path(GLIB_INCLUDE_DIR
NAMES glib.h
HINTS ${PC_GLIB_INCLUDEDIR}
${PC_GLIB_INCLUDE_DIRS}
PATH_SUFFIXES glib-2.0
)
set(GLIB_INCLUDE_DIRS ${GLIB_INCLUDE_DIR} ${GLIBCONFIG_INCLUDE_DIR})
# Version detection
if (EXISTS "${GLIBCONFIG_INCLUDE_DIR}/glibconfig.h")
file(READ "${GLIBCONFIG_INCLUDE_DIR}/glibconfig.h" GLIBCONFIG_H_CONTENTS)
string(REGEX MATCH "#define GLIB_MAJOR_VERSION ([0-9]+)" _dummy "${GLIBCONFIG_H_CONTENTS}")
set(GLIB_VERSION_MAJOR "${CMAKE_MATCH_1}")
string(REGEX MATCH "#define GLIB_MINOR_VERSION ([0-9]+)" _dummy "${GLIBCONFIG_H_CONTENTS}")
set(GLIB_VERSION_MINOR "${CMAKE_MATCH_1}")
string(REGEX MATCH "#define GLIB_MICRO_VERSION ([0-9]+)" _dummy "${GLIBCONFIG_H_CONTENTS}")
set(GLIB_VERSION_MICRO "${CMAKE_MATCH_1}")
set(GLIB_VERSION "${GLIB_VERSION_MAJOR}.${GLIB_VERSION_MINOR}.${GLIB_VERSION_MICRO}")
endif ()
# Additional Glib components. We only look for libraries, as not all of them
# have corresponding headers and all headers are installed alongside the main
# glib ones.
foreach (_component ${GLIB_FIND_COMPONENTS})
if (${_component} STREQUAL "gio")
find_library(GLIB_GIO_LIBRARIES NAMES gio-2.0 HINTS ${_GLIB_LIBRARY_DIR})
set(ADDITIONAL_REQUIRED_VARS ${ADDITIONAL_REQUIRED_VARS} GLIB_GIO_LIBRARIES)
elseif (${_component} STREQUAL "gobject")
find_library(GLIB_GOBJECT_LIBRARIES NAMES gobject-2.0 HINTS ${_GLIB_LIBRARY_DIR})
set(ADDITIONAL_REQUIRED_VARS ${ADDITIONAL_REQUIRED_VARS} GLIB_GOBJECT_LIBRARIES)
elseif (${_component} STREQUAL "gmodule")
find_library(GLIB_GMODULE_LIBRARIES NAMES gmodule-2.0 HINTS ${_GLIB_LIBRARY_DIR})
set(ADDITIONAL_REQUIRED_VARS ${ADDITIONAL_REQUIRED_VARS} GLIB_GMODULE_LIBRARIES)
elseif (${_component} STREQUAL "gthread")
find_library(GLIB_GTHREAD_LIBRARIES NAMES gthread-2.0 HINTS ${_GLIB_LIBRARY_DIR})
set(ADDITIONAL_REQUIRED_VARS ${ADDITIONAL_REQUIRED_VARS} GLIB_GTHREAD_LIBRARIES)
elseif (${_component} STREQUAL "gio-unix")
# gio-unix is compiled as part of the gio library, but the include paths
# are separate from the shared glib ones. Since this is currently only used
# by WebKitGTK+ we don't go to extraordinary measures beyond pkg-config.
pkg_check_modules(GIO_UNIX QUIET gio-unix-2.0)
endif ()
endforeach ()
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(GLIB REQUIRED_VARS GLIB_INCLUDE_DIRS GLIB_LIBRARIES ${ADDITIONAL_REQUIRED_VARS}
VERSION_VAR GLIB_VERSION)
mark_as_advanced(
GLIBCONFIG_INCLUDE_DIR
GLIB_GIO_LIBRARIES
GLIB_GIO_UNIX_LIBRARIES
GLIB_GMODULE_LIBRARIES
GLIB_GOBJECT_LIBRARIES
GLIB_GTHREAD_LIBRARIES
GLIB_INCLUDE_DIR
GLIB_INCLUDE_DIRS
GLIB_LIBRARIES
)

View File

@ -0,0 +1,34 @@
if (NOT TARGET LibNice::LibNice)
find_package(PkgConfig)
pkg_check_modules(PC_LIBNICE nice)
set(LIBNICE_DEFINITIONS ${PC_LIBNICE_CFLAGS_OTHER})
find_path(LIBNICE_INCLUDE_DIR nice/agent.h
HINTS ${PC_LIBNICE_INCLUDEDIR} ${PC_LIBNICE_INCLUDE_DIRS}
PATH_SUFFICES libnice)
find_library(LIBNICE_LIBRARY NAMES nice libnice
HINTS ${PC_LIBNICE_LIBDIR} ${PC_LIBNICE_LIBRARY_DIRS})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Libnice DEFAULT_MSG
LIBNICE_LIBRARY LIBNICE_INCLUDE_DIR)
mark_as_advanced(LIBNICE_INCLUDE_DIR LIBNICE_LIBRARY)
set(LIBNICE_LIBRARIES ${LIBNICE_LIBRARY})
set(LIBNICE_INCLUDE_DIRS ${LIBNICE_INCLUDE_DIR})
find_package(GLIB REQUIRED COMPONENTS gio gobject gmodule gthread)
list(APPEND LIBNICE_INCLUDE_DIRS ${GLIB_INCLUDE_DIRS})
list(APPEND LIBNICE_LIBRARIES ${GLIB_GOBJECT_LIBRARIES} ${GLIB_LIBRARIES})
if (LIBNICE_FOUND)
add_library(LibNice::LibNice UNKNOWN IMPORTED)
set_target_properties(LibNice::LibNice PROPERTIES
IMPORTED_LOCATION "${LIBNICE_LIBRARY}"
INTERFACE_COMPILE_DEFINITIONS "_REENTRANT"
INTERFACE_INCLUDE_DIRECTORIES "${LIBNICE_INCLUDE_DIRS}"
INTERFACE_LINK_LIBRARIES "${LIBNICE_LIBRARIES}"
IMPORTED_LINK_INTERFACE_LANGUAGES "C")
endif ()
endif ()

View File

@ -0,0 +1,8 @@
if (NOT TARGET Gabime::Spdlog)
include(FindPackageHandleStandardArgs)
find_path(SPDLOG_INCLUDE_DIR NAMES spdlog/spdlog.h)
find_package_handle_standard_args(Spdlog DEFAULT_MSG SPDLOG_INCLUDE_DIR)
add_library(spdlog INTERFACE)
target_include_directories(spdlog INTERFACE ${SPDLOG_INCLUDE_DIR})
add_library(Gabime::Spdlog ALIAS spdlog)
endif ()

View File

@ -0,0 +1,24 @@
# Simple libnice cmake find
if (NOT TARGET SctpLab::UsrSCTP)
set(USRSCTP_DEFINITIONS INET INET6)
find_path(USRSCTP_INCLUDE_DIR usrsctp.h PATH_SUFFICES usrsctp)
find_library(USRSCTP_LIBRARY NAMES usrsctp libusrsctp)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Usrsctp DEFAULT_MSG USRSCTP_LIBRARY USRSCTP_INCLUDE_DIR)
mark_as_advanced(USRSCTP_INCLUDE_DIR USRSCTP_LIBRARY)
set(USRSCTP_LIBRARIES ${USRSCTP_LIBRARY})
set(USRSCTP_INCLUDE_DIRS ${USRSCTP_INCLUDE_DIR})
if (USRSCTP_FOUND)
add_library(SctpLab::UsrSCTP UNKNOWN IMPORTED)
set_target_properties(SctpLab::UsrSCTP PROPERTIES
IMPORTED_LOCATION "${USRSCTP_LIBRARY}"
INTERFACE_COMPILE_DEFINITIONS "${USRSCTP_DEFINITIONS}"
INTERFACE_INCLUDE_DIRECTORIES "${USRSCTP_INCLUDE_DIRS}"
IMPORTED_LINK_INTERFACE_LANGUAGES "C")
endif ()
endif ()

25
flooder/CMakeLists.txt Normal file
View File

@ -0,0 +1,25 @@
cmake_minimum_required(VERSION 3.6)
project(TeamSpeak)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
include_directories(../shared/src)
add_definitions(-DLTM_DESC)
set(SOURCE_FILES
main.cpp
src/ProxiedClient.cpp
src/PorxiedClientSock5.cpp
src/TSClient.cpp
)
add_executable(TeamSpeakFloodClient ${SOURCE_FILES})
target_link_libraries(TeamSpeakFloodClient
TeaSpeak
pthread ThreadPool
${TOM_LIBRARIES}
crypto
event
event_pthreads
/usr/local/lib/libjsoncpp.so
)

81
flooder/main.cpp Normal file
View File

@ -0,0 +1,81 @@
#include <arpa/inet.h>
#include <string>
#include <cstring>
#include <src/ProxiedClient.h>
#include <event2/thread.h>
#include <iomanip>
using namespace std;
using namespace ts::flood;
void hexout(std::ostream& os, unsigned char c)
{
unsigned char uc = static_cast<unsigned char>(c);
os << std::setw(2) << std::setfill('0') << (unsigned int)uc << ' ';
}
void hexdump(std::ostream& outs, const std::string& s, size_t line_len = 16)
{
std::ostringstream os;
const std::string::size_type slen(s.size());
int i(0);
std::string::size_type pos(0);
const std::streamsize lines(slen / line_len);
const std::streamsize chars(slen % line_len);
std::ios::fmtflags f(os.flags());
os << "Length: " << s.length() << "/" << std::hex << "0x" << s.length() << endl;
for(std::streamsize line = 0; line <= lines - (chars == 0 ? 1 : 0); ++line)
{
os << std::hex << setfill('0') << setw(3) << line * line_len << " | ";
for(i = 0; i < line_len; ++i)
{
if(pos < s.length())
hexout(os, s[pos]);
else os << " ";
pos++;
}
os << " | ";
if(pos - line_len < s.length()){
auto av = s.substr(pos - line_len);
for(char c : av.substr(0, min(av.length(), line_len))){
if(isprint(c))
os << c << " ";
else
os << "." << " ";
}
}
os << '\n';
}
os.flags(f);
outs << os.str() << endl;
}
int main(int argc, char** argv){
sockaddr_in remoteAddress{}, proxyAddress{};
memset(&remoteAddress, 0, sizeof(remoteAddress));
memset(&proxyAddress, 0, sizeof(proxyAddress));
proxyAddress.sin_family = AF_INET;
proxyAddress.sin_port = htons(1085);
proxyAddress.sin_addr.s_addr = inet_addr("185.89.100.17");
/*
proxyAddress.sin_family = AF_INET;
proxyAddress.sin_port = htons(1080);
proxyAddress.sin_addr.s_addr = inet_addr("54.38.22.7");
*/
remoteAddress.sin_family = AF_INET;
remoteAddress.sin_port = htons(1100);
remoteAddress.sin_addr.s_addr = inet_addr("87.106.252.164");
assert(evthread_use_pthreads() == 0);
event_base* evBase = event_base_new();
ProxiedClient client(evBase, proxyAddress, remoteAddress);
client.connect();
event_base_dispatch(evBase);
}

View File

@ -0,0 +1,111 @@
#include <mutex>
#include <arpa/inet.h>
#include <ThreadPool/Thread.h>
#include "ProxiedClient.h"
using namespace std;
using namespace std::chrono;
using namespace ts::flood;
#define CERROR(msg) \
do { \
cerr << msg << endl;\
this->disconnect();\
return;\
} while(0)
static int port = 10000;
static threads::Mutex portLock;
void ProxiedClient::handleProxyMessage(const std::string &msg) {
if(this->state == PROXY_INIT_METHODS){
if(msg[0] != 0x05) CERROR("Invalid proxy version response (methode exchange)");
if(msg[1] != 0x00) CERROR("Invalid respond methode");
this->state = PROXY_INIT_CONNECTION;
char buffer[128];
int index = 0;
buffer[index++] = 0x05; //Version
buffer[index++] = 0x03; //Udp weiterleitung
buffer[index++] = 0x00; //Resv
buffer[index++] = 0x01; //Addr type = IPv4
auto addr = IPv4{this->remoteAddr->sin_addr.s_addr};
buffer[index++] = addr._1;
buffer[index++] = addr._2;
buffer[index++] = addr._3;
buffer[index++] = addr._4;
buffer[index++] = (ntohs(this->remoteAddr->sin_port) >> 8) & 0xFF;
buffer[index++] = (ntohs(this->remoteAddr->sin_port) >> 0) & 0xFF;
this->sendMessage(string(buffer, index));
} else if(this->state = PROXY_INIT_CONNECTION){
cout << "res!" << endl;
int index = 0;
if(msg[index++] != 0x05) CERROR("Invalid proxy version response (connection request)");
if(msg[index++] != 0x00) CERROR("Could not create connection (" + to_string((int) msg[1]) + ")");
if(msg[index++] != 0x00) CERROR("Invalid proxy rsv response (connection request)");
if(msg[index++] != 0x01) CERROR("Invalid proxy ip response type");
auto rAddr = IPv4{};
rAddr._1 = msg[index++];
rAddr._2 = msg[index++];
rAddr._3 = msg[index++];
rAddr._4 = msg[index++];
uint16_t pHigh = ((uint16_t) msg[index++]) & 0xFF;
uint16_t pLow = ((uint16_t) msg[index++]) & 0xFF;
uint16_t rPort = (pHigh << 8) | pLow;
cout << "Got udp relay " << rAddr.string() << ":" << rPort << endl;
//Delete old connection
//shutdown(this->fileDescriptor, SHUT_RDWR);
event_del(this->wEvent);
event_del(this->rEvent);
this->fileDescriptor = 0;
//Setup relay
this->relayAddr = new sockaddr_in{};
memset(relayAddr, 0, sizeof(*relayAddr));
relayAddr->sin_family = AF_INET;
relayAddr->sin_port = htons(rPort);
relayAddr->sin_addr.s_addr = rAddr.addr;
cout << "Relay addr: " << inet_ntoa(relayAddr->sin_addr) << ":" << ntohs(relayAddr->sin_port) << endl;
this->localAddr = new sockaddr_in{};
memset(localAddr, 0, sizeof(*localAddr));
localAddr->sin_family = AF_INET;
{
lock_guard<threads::Mutex> l(portLock);
localAddr->sin_port = this->remoteAddr->sin_port; // = htons(10000 + (port++ % 30000));
}
localAddr->sin_addr.s_addr = htonl(INADDR_ANY);
this->fileDescriptor = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
cout << "fd -> " << this->fileDescriptor << endl;
int allow = 1;
if(setsockopt(this->fileDescriptor, SOL_SOCKET, SO_REUSEADDR, &allow, sizeof(int)) < 0) CERROR("Could not enable reuse addr");
if(bind(this->fileDescriptor, reinterpret_cast<const sockaddr *>(this->localAddr), sizeof(*this->localAddr)) < 0) CERROR("Could nto bind to relay");
cout << "Bind on " << inet_ntoa(this->localAddr->sin_addr) << ":" << ntohs(this->localAddr->sin_port) << endl;
this->state = PROXY_CONNECTED;
this->rEvent = event_new(this->evBase, this->fileDescriptor, EV_READ | EV_PERSIST, ProxiedClient::handleEventRead, this);
this->wEvent = event_new(this->evBase, this->fileDescriptor, EV_WRITE, ProxiedClient::handleEventWrite, this);
event_add(rEvent, nullptr);
threads::Thread([&](){
threads::self::sleep_for(seconds(1));
this->proxyInizalisized();
});
}
}
void ProxiedClient::requestProxyConnection() {
char buffer[3];
buffer[0] = 0x05; //Version
buffer[1] = 1; //One methode
buffer[2] = 0x00; //No auth required
this->sendMessage(string(buffer, 3));
}

View File

@ -0,0 +1,175 @@
#include <mutex>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include "ProxiedClient.h"
#include "TSClient.h"
using namespace std;
using namespace ts::flood;
ProxiedClient::ProxiedClient(event_base* base, const sockaddr_in &proxyAddr, const sockaddr_in &remoteAddr) : evBase(base) {
this->proxyAddr = new sockaddr_in{};
this->remoteAddr = new sockaddr_in{};
memcpy(this->proxyAddr, &proxyAddr, sizeof(proxyAddr));
memcpy(this->remoteAddr, &remoteAddr, sizeof(proxyAddr));
this->client = new TSClient(this);
}
ProxiedClient::~ProxiedClient() {
delete this->proxyAddr;
delete this->remoteAddr;
}
#define CERR(msg) \
do { \
cerr << "Could not connect: " << msg << "(" << errno << "/" << strerror(errno) << ")" << endl; \
return false; \
} while(0)
#if defined(TCP_CORK) && !defined(TCP_NOPUSH)
#define TCP_NOPUSH TCP_CORK
#endif
static int enabled = 1;
static int disabled = 0;
bool ProxiedClient::connect() {
assert(this->state == ProxyState::PROXY_UNCONNECTED);
TAILQ_INIT(&this->writeQueue);
this->fileDescriptor = socket(AF_INET, SOCK_STREAM, 0);
if(this->fileDescriptor < 0) CERR("Socket setup failed");
if(::connect(this->fileDescriptor, reinterpret_cast<const sockaddr *>(this->proxyAddr), sizeof(*this->proxyAddr)) < 0) CERR("connect() failed");
if(setsockopt(this->fileDescriptor, SOL_SOCKET, SO_REUSEADDR, &enabled, sizeof(enabled)) < 0) CERR("could not set reuse addr");
if(setsockopt(this->fileDescriptor, IPPROTO_TCP, TCP_NOPUSH, &disabled, sizeof(disabled)) < 0) CERR("could not set no push");
cout << "Connected to " << inet_ntoa(this->proxyAddr->sin_addr) << endl;
this->rEvent = event_new(this->evBase, this->fileDescriptor, EV_READ | EV_PERSIST, ProxiedClient::handleEventRead, this);
this->wEvent = event_new(this->evBase, this->fileDescriptor, EV_WRITE, ProxiedClient::handleEventWrite, this);
event_add(rEvent, nullptr);
this->state = ProxyState::PROXY_INIT_METHODS;
this->requestProxyConnection();
return true;
}
void ProxiedClient::disconnect() {
this->closeConnection();
}
void ProxiedClient::closeConnection() {
{
lock_guard<threads::Mutex> lock(this->stateLock);
if(this->state == PROXY_UNCONNECTED) return;
this->state = PROXY_UNCONNECTED;
}
event_del(this->wEvent);
event_del(this->rEvent);
this->wEvent = nullptr;
this->rEvent = nullptr;
}
void ProxiedClient::sendMessage(const std::string& message) {
buffer::RawBuffer* buffer;
if(this->state != PROXY_CONNECTED){
buffer = new buffer::RawBuffer{message.length()};
memcpy(buffer->buffer, message.data(), message.length());
} else {
cout << "Send " << message.length() << " bytes with relay" << endl;
int relayHeaderLength = 2 + 1 + 1 + 4 + 2;
buffer = new buffer::RawBuffer{relayHeaderLength + message.length()};
buffer->index = 0;
char preBuffer[relayHeaderLength];
preBuffer[0] = 0x00;
preBuffer[1] = 0x00;
preBuffer[2] = 0x00;
preBuffer[3] = 0x01;
IPv4 addr{this->relayAddr->sin_addr.s_addr};
preBuffer[4] = addr._1;
preBuffer[5] = addr._2;
preBuffer[6] = addr._3;
preBuffer[7] = addr._4;
preBuffer[8] = (ntohs(this->relayAddr->sin_port) >> 8) & 0xFF;
preBuffer[9] = (ntohs(this->relayAddr->sin_port) >> 0) & 0xFF;
//memset(&preBuffer[4], 0, 6);
memcpy(&buffer->buffer[0], preBuffer, relayHeaderLength);
memcpy(&buffer->buffer[relayHeaderLength], message.data(), message.length());
}
{
lock_guard<threads::Mutex> lock(this->queueLock);
TAILQ_INSERT_TAIL(&this->writeQueue, buffer, tail);
}
event_add(this->wEvent, nullptr);
}
void ProxiedClient::handleMessage(const std::string &message) {
if(this->state == PROXY_UNCONNECTED) return;
if(this->state == PROXY_CONNECTED) return; //TODO
this->handleProxyMessage(message);
}
extern void hexdump(std::ostream& outs, const std::string& s, size_t line_len = 16);
void ProxiedClient::handleEventWrite(int fd, short, void* ptrClient) {
auto* client = static_cast<ProxiedClient *>(ptrClient);
buffer::RawBuffer* buffer = nullptr;
{
lock_guard<threads::Mutex> lock(client->queueLock);
buffer = TAILQ_FIRST(&client->writeQueue);
if(!buffer) return;
ssize_t writtenBytes = 0;
if(client->state == PROXY_CONNECTED){
cout << "Write bytes to relay - " << fd << " - " << inet_ntoa(client->relayAddr->sin_addr) << ":" << ntohs(client->relayAddr->sin_port) << endl;
hexdump(cout, string((const char*) buffer->buffer, buffer->length));
writtenBytes = sendto(fd, buffer->buffer, buffer->length, 0, (const sockaddr *) client->relayAddr, sizeof(*client->relayAddr));
} else
writtenBytes = send(fd, &buffer->buffer[buffer->index], buffer->length - buffer->index, 0);
buffer->index += writtenBytes;
cout << "Written: " << writtenBytes << endl;
if(buffer->index >= buffer->length || client->state == PROXY_CONNECTED) {
TAILQ_REMOVE(&client->writeQueue, buffer, tail);
delete buffer;
}
if(!TAILQ_EMPTY(&client->writeQueue))
event_add(client->wEvent, nullptr);
}
}
void ProxiedClient::handleEventRead(int fd, short, void* ptrClient) {
auto* client = static_cast<ProxiedClient *>(ptrClient);
char buffer[1024];
sockaddr_in remoteAddr{};
socklen_t remoteAddrSize = sizeof(remoteAddr);
auto read = recvfrom(fd, buffer, 1024, MSG_DONTWAIT, reinterpret_cast<sockaddr *>(&remoteAddr), &remoteAddrSize);
cout << "Read " << read << " bytes" << endl;
if(read < 0){
if(errno == EWOULDBLOCK) return;
cerr << "Invalid read: " << errno << "/" << strerror(errno) << endl;
client->disconnect();
return;
} else if(read == 0){
cerr << "Client hangs up!" << endl;
client->closeConnection();
return;
}
hexdump(cout, string(buffer, read));
client->handleMessage(string(buffer, read));
}
void ProxiedClient::proxyInizalisized() {
this->client->startConnect();
}

View File

@ -0,0 +1,72 @@
#pragma once
#include <string>
#include <netinet/in.h>
#include <misc/queue.h>
#include <protocol/buffers.h>
#include <event.h>
namespace ts {
namespace flood {
union IPv4 {
uint32_t addr;
struct __attribute__ ((__packed__)) {
uint8_t _1;
uint8_t _2;
uint8_t _3;
uint8_t _4;
};
inline std::string string(){
std::stringstream ss;
ss << (int) _1 << "." << (int) _2 << "." << (int) _3 << "." << (int) _4;
return ss.str();
}
};
class TSClient;
enum ProxyState {
PROXY_UNCONNECTED,
PROXY_INIT_METHODS,
PROXY_INIT_CONNECTION,
PROXY_CONNECTED
};
class ProxiedClient {
public:
ProxiedClient(event_base*,const sockaddr_in& proxyAddr, const sockaddr_in& remoteAddr);
~ProxiedClient();
bool connect();
void disconnect();
void closeConnection();
void handleMessage(const std::string& message);
void sendMessage(const std::string &);
private:
static void handleEventRead(int, short, void*);
static void handleEventWrite(int, short, void*);
void requestProxyConnection();
void handleProxyMessage(const std::string &);
void proxyInizalisized();
event_base* evBase = nullptr;
event* rEvent = nullptr;
event* wEvent = nullptr;
int fileDescriptor;
ProxyState state = ProxyState::PROXY_UNCONNECTED;
threads::Mutex stateLock;
sockaddr_in* proxyAddr = nullptr;
sockaddr_in* relayAddr = nullptr;
sockaddr_in* localAddr = nullptr;
sockaddr_in* remoteAddr = nullptr;
TAILQ_HEAD(, buffer::RawBuffer) writeQueue;
threads::Mutex queueLock;
TSClient* client = nullptr;
};
}
}

108
flooder/src/TSClient.cpp Normal file
View File

@ -0,0 +1,108 @@
//
// Created by wolverindev on 06.01.18.
//
#include "TSClient.h"
using namespace std;
using namespace std::chrono;
using namespace ts::flood;
using namespace ts::protocol;
TSClient::TSClient(ProxiedClient* con) : connection(con) {
this->cryptionHandler = new connection::CryptionHandler();
this->cryptionHandler->reset();
}
TSClient::~TSClient() {}
void TSClient::handleMessageRead(const std::string& message) {
if(message.length() < MAC_SIZE + SERVER_HEADER_SIZE) {
cerr << "Invalid pkt length!" << endl;
return;
}
shared_ptr<ServerPacket> packet = make_shared<ServerPacket>(message);
cout << "Having packet " << packet->type().name() << endl;
}
void TSClient::sendPacket(ts::protocol::ClientPacket &packet, int32_t packetId) {
size_t maxDataLength = 500 - packet.header().length();
if(packet.data().length() > maxDataLength){
cout << "Split packet" << endl;
string error;
/*
if(!compressPacket(&packet, error)){
cerr << "Compress error!" << endl;
return;
}
packet.enableFlag(PacketFlag::Compressed);
*/
if(packet.data().length() > maxDataLength){
std::vector<ClientPacket*> siblings;
ClientPacket* root = new ClientPacket(packet.type(), packet.flagMask(), packet.data());
root->enableFlag(PacketFlag::Fragmented);
//assert(root->hasFlag(PacketFlag::Compressed));
siblings.push_back(root);
//Max len - mac - header
while(siblings.back()->data().length() > maxDataLength){
auto overhead = siblings.back()->data().substr(maxDataLength);
siblings.back()->data(siblings.back()->data().substr(0, maxDataLength));
ClientPacket* sib = new ClientPacket(packet.type(), packet.flagMask(), overhead);
sib->toggle(PacketFlag::Fragmented, false);
siblings.push_back(sib);
}
siblings.back()->enableFlag(PacketFlag::Fragmented);
for(auto elm : siblings){
sendPacket(*elm);
delete elm;
}
return;
}
}
if (packetId == -1)
packet.applyPacketId(pktIdManager);
else packet.applyPacketId((uint16_t) packetId, 0);
packet.clientId(this->clientId);
string error = "success";
if (!this->cryptionHandler->progressPacketOut(&packet, error)) {
cerr << "Invalid crypt -> " << error << endl;
return;
}
this->connection->sendMessage(packet.mac() + packet.header() + packet.data());
}
const int InitVersionLength = 4;
const uint8_t InitVersion[InitVersionLength] = {0x06, 0x3b, 0xEC, 0xE9};
void TSClient::startConnect() {
int maxBufferSize = 512;
size_t bufferIndex = 0;
uint8_t buffer[maxBufferSize];
string error = "success";
memcpy(buffer, InitVersion, InitVersionLength);
bufferIndex += InitVersionLength;
buffer[bufferIndex++] = 0x00; //Login state
int64_t millis = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
memcpy(&buffer[5], &millis, 4);
bufferIndex += 4;
//generate the alpha key
for (int i = 0; i < 4; i++) buffer[bufferIndex++] = (uint8_t) std::rand();
bufferIndex += 8; //Reserved bytes
ClientPacket pkt(ts::protocol::PacketTypeInfo::Init1, string((char *) buffer, bufferIndex));
pkt.clientId(0);
pkt.toggle(ts::protocol::PacketFlag::Unencrypted, true);
this->sendPacket(pkt, 101);
}

36
flooder/src/TSClient.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#include "ProxiedClient.h"
#include <string>
#include <Definitions.h>
#include <protocol/CryptionHandler.h>
namespace ts {
namespace flood {
enum TSClientConnectionState {
TSC_UNCONNECTED,
TSC_PRE,
TSC_RSA,
TSC_HIGH,
TSC_CONNECTED
};
class TSClient {
public:
TSClient(ProxiedClient*);
~TSClient();
void startConnect();
void handleMessageRead(const std::string&);
void sendPacket(ts::protocol::ClientPacket &packet, int32_t packetId = -1);
private:
ProxiedClient* connection;
TSClientConnectionState state = TSClientConnectionState::TSC_UNCONNECTED;
protocol::PacketIdManager pktIdManager;
ts::connection::CryptionHandler* cryptionHandler = nullptr;
ClientId clientId = 0;
};
}
}

162
license/CMakeLists.txt Normal file
View File

@ -0,0 +1,162 @@
cmake_minimum_required(VERSION 3.6)
project(TeaSpeakLicence)
set(CMAKE_VERBOSE_MAKEFILE ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fpermissive -Wall -Wno-sign-compare -Wno-reorder -static-libgcc -static-libstdc++")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELEASE} -O3")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/environment/)
#disable for debug
#add_definitions(-DRELEASE_MODE)
include_directories(../shared/src)
add_definitions(-DLTM_DESC)
set(LICENCE_SOURCE_FILES
shared/LicenseRequest.cpp
shared/LicenseRequestHandler.cpp
shared/License.cpp
../shared/src/log/LogUtils.cpp
)
#Protobuf
find_package(Protobuf REQUIRED)
include_directories(${Protobuf_INCLUDE_DIRS})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS packets/LicenseRequest.proto packets/LicenseManager.proto)
#The actual librarie
add_library(TeaLicenseHelper STATIC ${LICENCE_SOURCE_FILES} ${PROTO_SRCS} ${PROTO_HDRS})
#The license server
add_executable(TeaLicenseServer ${LICENCE_SOURCE_FILES} ${PROTO_SRCS} ${PROTO_HDRS}
server/KeyIdCache.cpp
server/LicenseServer.cpp
server/LicenseServerHandler.cpp
server/LicenseManager.cpp
LicenseServerMain.cpp
server/WebAPI.cpp
server/StatisticManager.cpp
server/UserManager.cpp
)
target_link_libraries(TeaLicenseServer
TeaSpeak #Static
${LIBRARY_PATH_DATA_PIPES}
${LIBRARY_PATH_TERMINAL} #Static
${LIBRARY_PATH_THREAD_POOL} #Static
${PROJECT_SOURCE_DIR}/../../libraries/event/build/lib/libevent.a
${PROJECT_SOURCE_DIR}/../../libraries/event/build/lib/libevent_pthreads.a
pthread
${LIBRARY_PATH_BORINGSSL_SSL}
${LIBRARY_PATH_BORINGSSL_CRYPTO}
${LIBRARY_PATH_VARIBALES}
${LIBRARY_PATH_BREAKPAD}
${LIBRARY_PATH_PROTOBUF}
${LIBRARY_TOM_MATH}
${LIBRARY_TOM_CRYPT}
${LIBRARY_PATH_BREAKPAD}
${TOM_LIBRARIES}
${LIBRARY_PATH_JDBC}
jsoncpp.a
stdc++fs.a
)
#The test license client
add_executable(TeaLicenseClient
LicenseClientMain.cpp
${LICENCE_SOURCE_FILES} ${PROTO_SRCS} ${PROTO_HDRS}
)
target_link_libraries(TeaLicenseClient
${LIBRARY_PATH_DATA_PIPES}
${LIBRARY_PATH_TERMINAL} #Static
${LIBRARY_PATH_THREAD_POOL} #Static
${PROJECT_SOURCE_DIR}/../../libraries/event/build/lib/libevent.a
${PROJECT_SOURCE_DIR}/../../libraries/event/build/lib/libevent_pthreads.a
pthread
${LIBRARY_PATH_VARIBALES}
${LIBRARY_PATH_BREAKPAD}
${LIBRARY_PATH_PROTOBUF}
${LIBRARY_TOM_MATH}
${LIBRARY_TOM_CRYPT}
stdc++fs.a
${LIBRARY_PATH_BORINGSSL_SSL}
${LIBRARY_PATH_BORINGSSL_CRYPTO}
${LIBRARY_PATH_BREAKPAD}
${TOM_LIBRARIES}
${LIBRARY_PATH_JDBC}
TeaSpeak #Static
jsoncpp.a
)
#The license manager
if(NOT DISABLE_QT)
find_package(Qt5Widgets)
include_directories(${Qt5Widgets_INCLUDE_DIRS})
add_definitions(${Qt5Widgets_DEFINITIONS})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
set(UI_FILES manager/ui/licensegenerator.ui manager/ui/loginwindow.ui manager/ui/owerview.ui manager/ui/licenseinfo.ui)
set(DESIGN_HEADER_FILES manager/ui/LicenseGenerator.h manager/ui/LoginWindow.h manager/ui/Overview.h manager/ui/UiLicenseInfo.h)
qt5_wrap_ui(UI_WARPED_FILES ${UI_FILES})
qt5_wrap_cpp(UI_HEADER_FILES ${DESIGN_HEADER_FILES})
add_executable(LicenseManager
${LICENCE_SOURCE_FILES}
LicenseManager.cpp
manager/ui/LicenseGenerator.cpp
manager/ui/LoginWindow.cpp
manager/ui/Overview.cpp
manager/ui/UiLicenseInfo.cpp
manager/ServerConnection.cpp
manager/ServerConnectionHandler.cpp
manager/ServerConnectionExecutor.cpp
${UI_WARPED_FILES} ${UI_HEADER_FILES} ${PROTO_SRCS} ${PROTO_HDRS})
target_link_libraries(LicenseManager Qt5::Widgets Qt5::Core Qt5::Gui)
target_link_libraries(LicenseManager
${LIBRARY_PATH_THREAD_POOL} #Static
TeaSpeak #Static
${LIBRARY_PATH_TERMINAL} #Static
${LIBRARY_PATH_BORINGSSL_CRYPTO}
${PROJECT_SOURCE_DIR}/../../libraries/event/build/lib/libevent.a
${PROJECT_SOURCE_DIR}/../../libraries/event/build/lib/libevent_pthreads.a
pthread
${LIBRARY_PATH_VARIBALES}
${LIBRARY_PATH_BREAKPAD}
${LIBRARY_PATH_PROTOBUF}
${LIBRARY_TOM_MATH}
${LIBRARY_TOM_CRYPT}
stdc++fs
jsoncpp.a
${LIBRARY_PATH_DATA_PIPES}
${LIBRARY_PATH_BORINGSSL_SSL}
${LIBRARY_PATH_BORINGSSL_CRYPTO}
)
endif()
add_executable(LicenseCLI
LicenseCreatorCLI.cpp
manager/ServerConnection.cpp
manager/ServerConnectionExecutor.cpp
manager/ServerConnectionHandler.cpp
shared/License.cpp
${PROTO_SRCS} ${PROTO_HDRS}
)
target_link_libraries(LicenseCLI
${LIBRARY_PATH_THREAD_POOL} #Static
${PROJECT_SOURCE_DIR}/../../libraries/event/build/lib/libevent.a
${PROJECT_SOURCE_DIR}/../../libraries/event/build/lib/libevent_pthreads.a
${LIBRARY_PATH_PROTOBUF}
${LIBRARY_TOM_MATH} #Static
${LIBRARY_TOM_CRYPT} #Static
pthread
)

View File

@ -0,0 +1,87 @@
#include <iostream>
#include <shared/License.h>
#include <shared/LicenseRequest.h>
#include <event2/thread.h>
using namespace std;
using namespace std::chrono;
using namespace license;
/*
struct LicenseInfo {
LicenseType type;
std::string username;
std::string first_name;
std::string last_name;
std::string email;
std::chrono::system_clock::time_point start;
std::chrono::system_clock::time_point end;
std::chrono::system_clock::time_point creation;
inline bool isValid() { return (end.time_since_epoch().count() == 0 || std::chrono::system_clock::now() < this->end); }
};
*/
int main(int ac, char** av){
auto state = evthread_use_pthreads();
assert(state == 0);
srand(system_clock::now().time_since_epoch().count());
cout << "Generating new license" << endl;
std::string name = "WolverinDEV";
auto raw_license = createLocalLicence(LicenseType::PREMIUM, system_clock::now() - chrono::hours((int) (24 * 30.5 * 3)), "WolverinDEV");
auto license = readLocalLicence(raw_license, error);
assert(license);
sockaddr_in serv_addr{};
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = ((in_addr*) gethostbyname("localhost")->h_addr)->s_addr;
serv_addr.sin_port = htons(27786);
/*
* struct LicenseRequestData {
std::shared_ptr<License> license;
int64_t speach_total;
int64_t speach_dead;
int64_t speach_online;
int64_t speach_varianz;
int64_t client_online;
int64_t bots_online;
int64_t queries_online;
int64_t servers_online;
};
*/
auto data = make_shared<LicenseRequestData>();
data->license = license;
data->info = make_shared<ServerInfo>();
while(true) {
LicenceRequest request(data, serv_addr);
try {
cout << "Requesting license" << endl;
auto info = request.requestInfo().waitAndGet(nullptr);
if(!info) {
cout << "Invalid result! Error: " << (request.exception() ? "yes => " + string(request.exception()->what()) : "no") << endl;
throw *request.exception();
}
cout << "Got result!" << endl;
cout << "Valid: " << info->license_valid << endl;
if(info->license) {
cout << "License:" << endl;
cout << " Type: " << info->license->type << endl;
cout << " User name: " << info->license->username << endl;
cout << " First name: " << info->license->first_name << endl;
cout << " Last name: " << info->license->last_name << endl;
cout << " EMail: " << info->license->email << endl;
} else cout << "License: none";
} catch (const std::exception& ex){
cerr << "Could not load info after throwing: " << endl << ex.what() << endl;
}
}
return 0;
}

View File

@ -0,0 +1,138 @@
#include <iostream>
#include <algorithm>
#include <vector>
#include <event2/thread.h>
#include "manager/ServerConnection.h"
using namespace std;
using namespace license;
using namespace license::manager;
using namespace std::chrono;
std::string url_decode(const std::string& source) {
std::string ret;
char ch;
size_t i;
int ii;
for (i=0; i < source.length(); i++) {
if ((int) source[i] == 37) {
sscanf(source.substr(i + 1, 2).c_str(), "%x", &ii);
ch = static_cast<char>(ii);
ret += ch;
i = i + 2;
} else {
ret += source[i];
}
}
return ret;
}
class CLIParser {
public:
CLIParser(int &argc, char **argv) {
for (int i = 1; i < argc; ++i)
this->tokens.emplace_back(argv[i]);
}
/// @author iain
const std::string& get_option(const std::vector<std::string> &options) const {
for(const auto& option : options) {
std::vector<std::string>::const_iterator itr;
itr = std::find(this->tokens.begin(), this->tokens.end(), option);
if (itr != this->tokens.end() && ++itr != this->tokens.end())
return *itr;
}
static const std::string empty_string;
return empty_string;
}
/// @author iain
bool option_exists(const std::vector<std::string> &options, bool allow_empty = true) const {
for(const auto& option : options) {
auto index = std::find(this->tokens.begin(), this->tokens.end(), option);
if(index != this->tokens.end())
if(allow_empty || !index->empty())
return true;
}
return false;
}
private:
std::vector<std::string> tokens;
};
#define REQ_CMD(variable, message, ...) \
if(!options.option_exists({__VA_ARGS__}, false)) { \
cerr << "missing user" << endl; \
return 1; \
} \
auto variable = url_decode(options.get_option({__VA_ARGS__})) \
#define NO_OPEN_SSL
#include <misc/digest.h>
#include <misc/base64.h>
int main(int argc, char **argv) {
CLIParser options(argc, argv);
REQ_CMD(user, "missing user", "-u", "--user");
REQ_CMD(first_name, "missing first name", "-n", "--first_name");
REQ_CMD(last_name, "missing last name", "-s", "--last_name");
REQ_CMD(begin, "missing begin timestamp", "-b", "--begin");
REQ_CMD(end, "missing end timestamp", "-e", "--end");
REQ_CMD(email, "missing email", "-m", "--email");
REQ_CMD(license_type, "missing license_type", "-l", "--license-type");
REQ_CMD(auth_user, "missing authentification user", "-au", "--auth-user");
REQ_CMD(auth_pass, "missing authentification user", "-ap", "--auth-pass");
REQ_CMD(server_host, "missing server host", "-h", "--server-host");
REQ_CMD(server_port, "missing server port", "-p", "--server-port");
auto state = evthread_use_pthreads();
if(state != 0) {
cerr << "failed to setup pthreads" << endl;
return 1;
}
ServerConnection connection;
connection.verbose = false;
try {
auto future = connection.connect(server_host, (uint16_t) stol(server_port));
if(!future.waitAndGet(false, system_clock::now() + seconds(5))) {
cerr << "failed to connect (" << future.errorMegssage() << ")" << endl;
return 1;
}
} catch(std::exception& ex) {
cerr << "invalid port" << endl;
return 1;
}
{
auto future = connection.login(auth_user, auth_pass);
if(!future.waitAndGet(false, system_clock::now() + seconds(5))) {
cerr << "failed to athentificate (" << future.errorMegssage() << ")" << endl;
return 1;
}
};
try {
system_clock::time_point timestamp_begin = system_clock::time_point() + seconds(stoll(begin));
system_clock::time_point timestamp_end = system_clock::time_point() + seconds(stoll(end));
auto future = connection.registerLicense(first_name, last_name, user, email, (LicenseType) stoll(license_type), timestamp_end, timestamp_begin);
auto result = future.waitAndGet({nullptr, nullptr}, system_clock::now() + seconds(5));
if(!result.first || !result.second) {
cerr << "failed to create license! (" << future.errorMegssage() << ")" << endl;
return 1;
}
auto license_key = license::exportLocalLicense(result.first);
cout << license_key << endl;
} catch(std::exception& ex) {
cerr << "invalid timestamps or license type" << endl;
return 1;
}
return 0;
}

View File

@ -0,0 +1,97 @@
#include <iostream>
#include <shared/License.h>
#include <shared/LicenseRequest.h>
#include <QtWidgets/QApplication>
#include <manager/ui/LicenseGenerator.h>
#include <manager/ServerConnection.h>
#include <manager/ui/LoginWindow.h>
#include <manager/ui/Overview.h>
#include <event2/thread.h>
#include <misc/base64.h>
using namespace std;
using namespace std::chrono;
using namespace license;
using namespace license::manager;
ServerConnection* connection = nullptr;
QWidget* current_widget = nullptr;
std::string string_to_hex(const std::string& input)
{
static const char* const lut = "0123456789ABCDEF";
size_t len = input.length();
std::string output;
output.reserve(2 * len);
for (size_t i = 0; i < len; ++i)
{
const unsigned char c = input[i];
output.push_back(lut[c >> 4]);
output.push_back(lut[c & 15]);
}
return output;
}
int main(int ac, char** av){
evthread_use_pthreads();
auto data = "AQBUAd26AAAAACnNuqvy++NGfMJU+Bvo5412Wi5jM5/JmmYyDbcxWKNaJV0FF1jpXtSrss3Gm7RUEQ6CdEEhPdyHVgHdugAAAAAphOsSMO1976JoEaTLru5boYKp2A62gUVdGvLZYqrDjBnsiumfxPlxfF/PYai/Khvth7FgDzDc7WIBBTPyOV8uw4Db2ZmOkA4dpjGWfY2L6QJaRyjh9XySDL+c6/eoKOv75Wbu1FO1mr83jr8H9pWoCalwm7SEPngSdYa3ZOa+Q7c=";
string error;
auto license = license::readLocalLicence(data, error);
cout << "Key: " << base64::encode(license->key()) << endl;
cout << "Key: " << string_to_hex(license->key()) << endl;
return false;
connection = new ServerConnection();
if(!connection->connect("mcgalaxy.de", 27786).waitAndGet(false)) {
cerr << "Failed to connect!" << endl;
return 2;
}
auto login = connection->login("WolverinDEV", "HelloWorld").waitAndGet(false);
if(!login) {
cerr << "Failed to login!" << endl;
return 1;
}
/*
cout << "Generating new license" << endl;
std::string name = "Hyp3rX (timo-games@gmx.de )";
auto licenses = createLocalLicence(LicenseType::PREMIUM, system_clock::now() + chrono::hours((int) (24 * 30.5 * 3))/* system_clock::time_point(), name);
cout << name << " " << licenses << endl;
readLocalLicence(licenses, name);
cout << name << endl;
return 0;
string error = "";
auto binary = readLocalLicence(licenses, error);
if(!binary){
cerr << "Could not read local license " << error << endl;
return -1;
}
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = ((in_addr*) gethostbyname("0.0.0.0")->h_addr)->s_addr;
serv_addr.sin_port = htons(27786);
LicenceRequest request(binary, serv_addr);
try {
auto info = request.requestInfo().waitAndGet(nullptr);
cout << "Got info -> " << info->owner << "|" << info->licenseInfo << " -> " << info->valid << endl;
} catch (const std::exception& ex){
cerr << "Could not load info after throwing " << ex.what() << endl;
cerr << "Exception: " << request.exception()->what() << endl;
}
return 0;
*/
QApplication app(ac, av);
//license::ui::LoginWindow gen;
license::ui::Overview gen;
gen.show();
return app.exec();
}

View File

@ -0,0 +1,240 @@
#include <iostream>
#include <shared/License.h>
#include <server/LicenseServer.h>
#include <log/LogUtils.h>
#include <CXXTerminal/Terminal.h>
#include <sql/mysql/MySQL.h>
#include <pipes/misc/http.h>
#include "server/WebAPI.h"
#include "server/StatisticManager.h"
#include <event2/thread.h>
#include "server/UserManager.h"
#include <misc/base64.h>
#include <misc/digest.h>
using namespace std;
using namespace std::chrono;
using namespace license;
/*
* Requests/license: SELECT `tmp`.`keyId`, `tmp`.`key`, `tmp`.`ip`, `tmp`.`count`, `license_info`.`username`, `tmp`.`type` FROM (SELECT DISTINCT `license_request`.`keyId`, `key`, `license_request`.`ip`, COUNT(`license_request`.`keyId`) AS `count`, `license`.`type` FROM `license_request` INNER JOIN `license` ON `license`.`keyId` = `license_request`.`keyId` GROUP BY (`license_request`.`ip`)) AS `tmp` INNER JOIN `license_info` ON `license_info`.`keyId` = `tmp`.`keyId`
* Different IP's: SELECT `tmp`.`keyId`, `license_info`.`username`, COUNT(`ip`) FROM (SELECT DISTINCT `ip`, `keyId` FROM `license_request`) AS `tmp` LEFT JOIN `license_info` ON `license_info`.`keyId` = `tmp`.`keyId` GROUP BY (`tmp`.`keyId`)
*
* Requests (license) / ip: SELECT DISTINCT `ip`, COUNT(`ip`) FROM `license_request` WHERE `keyId` = ? GROUP BY `ip`
*
* SELECT DISTINCT(`ip`), `keyId` FROM `license_request` WHERE `timestamp` > (UNIX_TIMESTAMP() - 2 * 60 * 60) * 1000
*
* SELECT DISTINCT(`ip`), `license_request`.`keyId`, `license_info`.`username` FROM `license_request` LEFT JOIN `license_info` ON `license_info`.`keyId` = `license_request`.`keyId` WHERE `timestamp` > (UNIX_TIMESTAMP() - 2 * 60 * 60) * 1000
*
*
* Online clients: SELECT SUM(`clients`) FROM (SELECT DISTINCT(`ip`), `clients` FROM `history_online` WHERE `timestamp` > (UNIX_TIMESTAMP() - 60 * 60 * 2) * 1000) AS `a`
* Online bots: SELECT SUM(`clients`) FROM (SELECT DISTINCT(`ip`), `clients` FROM `history_online` WHERE `timestamp` > (UNIX_TIMESTAMP() - 60 * 60 * 2) * 1000) AS `a`
* Online VS Server: SELECT SUM(`music`) FROM (SELECT DISTINCT(`ip`), `music` FROM `history_online` WHERE `timestamp` > (UNIX_TIMESTAMP() - 60 * 60 * 2) * 1000) AS `a`
*/
bool handle_command(string& line);
shared_ptr<server::LicenseManager> license_manager;
shared_ptr<stats::StatisticManager> statistic_manager;
shared_ptr<ts::ssl::SSLManager> ssl_manager;
shared_ptr<license::web::WebStatistics> web_server;
shared_ptr<LicenseServer> license_server;
shared_ptr<UserManager> user_manager;
int main(int argc, char** argv) {
if(argc < 2) {
cerr << "Invalid arguments! Need MySQL connection" << endl;
return 0;
}
evthread_use_pthreads();
http::decode_url("xxx");
srand(system_clock::now().time_since_epoch().count());
terminal::install();
if(!terminal::active()){ cerr << "could not setup terminal!" << endl; return -1; }
auto config = std::make_shared<logger::LoggerConfig>();
config->vs_group_size = 0;
config->logfileLevel = spdlog::level::trace;
config->terminalLevel = spdlog::level::trace;
config->logPath = "logs/log_${time}(%Y-%m-%d_%H:%M:%S).log";
logger::setup(config);
string error;
sql::SqlManager* database = new sql::mysql::MySQLManager();
bool db_connected = true;
((sql::mysql::MySQLManager*) database)->listener_disconnected = [&](bool wanted){
if(wanted) return;
logCritical("Lost connection to MySQL server!");
logCritical("Stopping server!");
db_connected = false;
};
//mysql://localhost:3306/license?userName=root&password=markus
logMessage(LOG_GENERAL, "Connecting to {}", argv[1]);
auto connect_result = database->connect(string(argv[1]));
if(!connect_result) {
logError("Could not connect to mysql server! (" + connect_result.fmtStr() + ")");
return 0;
}
#if false
sql::command(database, "INSERT INTO license (`key`, type, deleted, issuer) VALUES ('0020', 1, 1, 'Test'); ").execute();
cout << sql::command(database, "SELECT LAST_INSERT_ID();").query([](void*, int length, string* values, string* names) {
for(int i = 0; i < length; i++)
cout << names[i] << " -> " << values[i] << endl;
return 0;
}, (void*) nullptr) << endl;
#endif
license_manager = make_shared<server::LicenseManager>(database);
if(!license_manager->setup(error)) {
logError("Could not start license manager! (" +error + ")");
return 0;
}
statistic_manager = make_shared<stats::StatisticManager>(license_manager);
/*
{
auto _now = system_clock::now();
auto statistics = license_manager->list_statistics_user(_now - hours(24) * 32 * 4, _now, duration_cast<milliseconds>(hours(2)));
cout << "Date,Instances,Servers,Clients,Web Clients,Queries,Music Bots" << endl;
for(const auto& entry : statistics) {
auto time = system_clock::to_time_t(entry->timestamp);
tm* localtm = localtime(&time);
string string_time = asctime(localtm);
string_time = string_time.substr(0, string_time.length() - 1);
//cout << "[" << string_time << "] Users online: " << entry->clients_online << " | Instances: " << entry->instance_online << endl;
cout << string_time << "," << entry->instance_online << "," << entry->servers_online << "," << entry->clients_online << "," << entry->web_clients_online << "," << entry->queries_online << "," << entry->bots_online << endl;
}
}
return 0;
{
auto _now = system_clock::now();
auto statistics = license_manager->list_statistics_version(_now - hours(24) * 9, _now, duration_cast<milliseconds>(hours(1)));
std::deque<std::string> versions;
const auto version_name = [](const std::string& key) {
auto space = key.find(' ');
return key.substr(0, space);
};
for(const auto& entry : statistics) {
for(const auto& version : entry->versions) {
const auto name = version_name(version.first);
if(name.empty()) {
continue;
}
auto it = find(versions.begin(), versions.end(), name);
if(it == versions.end())
versions.push_back(name);
}
}
sort(versions.begin(), versions.end(), [](const std::string& a, const std::string& b) {
const auto index_a = a.find_last_of('.');
const auto index_b = b.find_last_of('.');
const auto length_a = a.find('-', index_a) - index_a - 1;
const auto length_b = b.find('-', index_b) - index_b - 1;
const auto patch_a = stoll(a.substr(index_a + 1, length_a));
const auto patch_b = stoll(b.substr(index_b + 1, length_b));
return patch_a > patch_b;
});
cout << "Date";
for(auto it = versions.begin(); it != versions.end(); it++)
cout << "," << *it;
cout << endl;
for(const auto& entry : statistics) {
auto time = system_clock::to_time_t(entry->timestamp);
tm* localtm = localtime(&time);
string string_time = asctime(localtm);
string_time = string_time.substr(0, string_time.length() - 1);
map<string, int> version_count;
for(const auto& version : entry->versions) {
const auto name = version_name(version.first);
version_count[name] += version.second;
}
cout << string_time;
for(const auto& name : versions) {
cout << "," << version_count[name];
}
cout << endl;
}
}
*/
ssl_manager = make_shared<ts::ssl::SSLManager>();
{
string key_file = "certificates/web_stats_prv.pem";
string cert_file = "certificates/web_stats_crt.pem";
if(!ssl_manager->initializeContext("web_stats", key_file, cert_file, error, false, make_shared<ts::ssl::SSLGenerator>(ts::ssl::SSLGenerator{
.subjects = {},
.issues = {{"O", "TeaSpeak"}, {"OU", "License server"}, {"creator", "WolverinDEV"}}
}))) {
logCritical(LOG_LICENSE_WEB, "Failed to initialize ssl certificate! Stopping server.");
}
}
{
web_server = make_shared<license::web::WebStatistics>(license_manager, statistic_manager);
logMessage("Starting web server on [:::]:27788");
if(!web_server->start(error, 27788, ssl_manager->getContext("web_stats"))) {
logError(LOG_GENERAL, "Failed to start web statistics server!");
return 0;
}
}
{
user_manager = make_shared<UserManager>(database);
}
{
logMessage("Starting license server on [:::]:27786");
struct sockaddr_in listen_addr{};
memset(&listen_addr, 0, sizeof(listen_addr));
listen_addr.sin_family = AF_INET;
listen_addr.sin_addr.s_addr = INADDR_ANY;
listen_addr.sin_port = htons(27786);
license_server = make_shared<LicenseServer>(listen_addr, license_manager, statistic_manager, web_server, user_manager);
license_server->startServer();
}
while(db_connected && web_server->running() && license_server->isRunning()) {
auto line = terminal::instance()->readLine("§a> §f");
if(line.empty()){
usleep(500);
continue;
}
if(!handle_command(line)) {
terminal::instance()->writeMessage("§aStopping server...");
break;
}
}
web_server->stop();
license_server->stopServer();
if(database) database->disconnect();
logger::uninstall();
terminal::uninstall();
return 0;
}
bool handle_command(string& line) {
if(line == "end" || line == "stop") return false; //Exit loop
if(line == "info web") {
logMessage(LOG_LICENSE_WEB, "Currently online clients:");
auto clients = web_server->get_clients();
for(const auto& client : clients)
logMessage(LOG_LICENSE_WEB, " - {}", client->client_prefix());
logMessage(LOG_LICENSE_WEB, " {} clients are currently connected!", clients.size());
return true;
}
logError("Invalid command: " + line);
return true;
}

View File

@ -0,0 +1,225 @@
//
// Created by wolverindev on 08.05.18.
//
#include <netinet/tcp.h>
#include <shared/crypt.h>
#include <shared/License.h>
#include <memory>
#include <misc/std_unique_ptr.h>
#include "ServerConnection.h"
using namespace std;
using namespace std::chrono;
using namespace license;
using namespace license::manager;
ServerConnection::ServerConnection() {}
ServerConnection::~ServerConnection() {
this->disconnect("deallocation");
if(this->network.flush_thread)
this->network.flush_thread->join();
}
#define CERR(message) \
do { \
FLERROR(this->listener.future_connect, message); \
return; \
} while(0)
threads::Future<bool> ServerConnection::connect(const std::string &host, uint16_t port) {
this->listener.future_connect = std::make_unique<threads::Future<bool>>();
this->network.state = ConnectionState::CONNECTING;
threads::Thread([&, host, port](){
this->network.address_remote.sin_family = AF_INET;
{
auto address = gethostbyname(host.c_str());
if(!address)
CERR("invalid address");
auto inet_address = (in_addr*) address->h_addr;
if(!inet_address)
CERR("invalid address (2)");
this->network.address_remote.sin_addr.s_addr = inet_address->s_addr;
}
this->network.address_remote.sin_port = htons(port); //27786
this->network.file_descriptor = socket(AF_INET, SOCK_STREAM, 0);
if(this->network.file_descriptor < 0) CERR("Socket setup failed");
if(::connect(this->network.file_descriptor, reinterpret_cast<const sockaddr *>(&this->network.address_remote), sizeof(this->network.address_remote)) < 0) CERR("connect() failed (" + to_string(errno) + " | " + strerror(errno) + ")");
int enabled = 1, disabled = 0;
if(setsockopt(this->network.file_descriptor, SOL_SOCKET, SO_REUSEADDR, &enabled, sizeof(enabled)) < 0) CERR("could not set reuse addr");
if(setsockopt(this->network.file_descriptor, IPPROTO_TCP, TCP_CORK, &disabled, sizeof(disabled)) < 0) CERR("could not set no push");
this->network.event_base = event_base_new();
this->network.event_read = event_new(this->network.event_base, this->network.file_descriptor, EV_READ | EV_PERSIST, ServerConnection::handleEventRead, this);
this->network.event_write = event_new(this->network.event_base, this->network.file_descriptor, EV_WRITE, ServerConnection::handleEventWrite, this);
event_add(this->network.event_read, nullptr);
this->network.event_base_dispatch = new threads::Thread(THREAD_SAVE_OPERATIONS, [&](){
event_base_dispatch(this->network.event_base);
if(this->verbose)
cout << "ev ended!" << endl;
});
this->network.state = ConnectionState::CONNECTED;
this->protocol.state = protocol::HANDSCAKE;
this->protocol.ping_thread = thread([&]{
while(true) {
{
unique_lock lock(this->protocol.ping_lock);
this->protocol.ping_notify.wait_until(lock, system_clock::now() + seconds(30), [&]{
return this->network.state != ConnectionState::CONNECTED;
});
if(this->network.state != ConnectionState::CONNECTED) return;
}
this->ping();
}
});
uint8_t handshakeBuffer[5];
handshakeBuffer[0] = 0xC0;
handshakeBuffer[1] = 0xFF;
handshakeBuffer[2] = 0xEE;
handshakeBuffer[3] = LICENSE_PROT_VERSION;
handshakeBuffer[4] = 1; //Im a manager
this->sendPacket(protocol::packet{protocol::PACKET_CLIENT_HANDSHAKE, string((const char*) handshakeBuffer, 5)}); //Initialise packet
}).detach();
return *this->listener.future_connect;
}
void ServerConnection::disconnect(const std::string& reason) {
this->network.state = ConnectionState::DISCONNECTING;
this->local_disconnect_message = reason;
//TODO
this->closeConnection();
}
#define F_ERROR_DISCONNECT(name) \
FLERROR(name, "connection closed locally" + (this->local_disconnect_message.empty() ? "" : " (" + this->local_disconnect_message + ")"))
void ServerConnection::closeConnection() {
if(this->network.state == ConnectionState::UNCONNECTED) return;
this->network.state = ConnectionState::DISCONNECTING;
if(this->network.event_base_dispatch && *this->network.event_base_dispatch == threads::self::id()) {
this->network.flush_thread = new threads::Thread(THREAD_SAVE_OPERATIONS, [&](){ this->closeConnection(); });
return;
}
if(this->network.event_write) {
event_del(this->network.event_write);
event_free(this->network.event_write);
this->network.event_write = nullptr;
}
if(this->network.event_read) {
event_del(this->network.event_read);
event_free(this->network.event_read);
this->network.event_read = nullptr;
}
if(this->network.event_base) {
event_base_loopbreak(this->network.event_base);
if(event_base_loopexit(this->network.event_base, nullptr) < 0) {
if(this->verbose)
cerr << "could not stop event loop!" << endl;
}
}
if(this->network.event_base_dispatch) {
this->network.event_base_dispatch->join();
delete this->network.event_base_dispatch;
this->network.event_base_dispatch = nullptr;
}
if(this->network.event_base) {
event_base_free(this->network.event_base);
this->network.event_base = nullptr;
}
if(this->network.file_descriptor > 0) {
shutdown(this->network.file_descriptor, SHUT_RDWR);
this->network.file_descriptor = 0;
}
this->network.state = ConnectionState::UNCONNECTED;
{
this->protocol.ping_notify.notify_all();
if(this->protocol.ping_thread.joinable())
this->protocol.ping_thread.join();
}
F_ERROR_DISCONNECT(this->listener.future_register);
F_ERROR_DISCONNECT(this->listener.future_connect);
F_ERROR_DISCONNECT(this->listener.future_delete);
F_ERROR_DISCONNECT(this->listener.future_list);
F_ERROR_DISCONNECT(this->listener.future_login);
}
void ServerConnection::handleEventRead(int fd, short, void* _connection) {
auto connection = (ServerConnection*) _connection;
char buffer[1024];
auto read = recv(fd, buffer, 1024, SOCK_NONBLOCK);
if(read < 0) {
if(connection->verbose)
cout << "Invalid read: " << strerror(errno) << endl;
connection->local_disconnect_message = "invalid read";
connection->closeConnection();
return;
}
if(read > 0) {
if(connection->verbose)
cout << "Read: " << read << endl;
connection->local_disconnect_message = "invalid read";
connection->handleMessage(string(buffer, read));
}
}
void ServerConnection::handleEventWrite(int fd, short, void* _connection) {
auto connection = (ServerConnection*) _connection;
threads::MutexLock lock(connection->network.queue_lock);
auto& queue = connection->network.queue_write;
if(queue.empty()) return;
auto message = queue.front();
queue.pop_front();
auto wrote = send(fd, message.data(), message.length(), 0);
if(wrote < 0) {
if(connection->verbose)
cout << "Invalid write: " << strerror(errno) << endl;
connection->local_disconnect_message = "invalid write";
connection->closeConnection();
return;
}
if(connection->verbose)
cout << "Wrote: " << wrote << endl;
if(wrote < message.length()) {
queue.push_front(message.substr(wrote));
event_add(connection->network.event_write, nullptr);
}
}
void ServerConnection::sendPacket(const protocol::packet& packet) {
if(this->network.state == ConnectionState::UNCONNECTED || this->network.state == ConnectionState::DISCONNECTING) {
if(this->verbose)
cout << "Tried to send a packet to an unconnected remote!" << endl;
return;
}
packet.prepare();
string buffer;
buffer.resize(packet.data.length() + sizeof(packet.header));
memcpy((void*) buffer.data(), &packet.header, sizeof(packet.header));
memcpy((void*) &buffer.data()[sizeof(packet.header)], packet.data.data(), packet.data.length());
if(!this->protocol.crypt_key.empty())
xorBuffer(&buffer[sizeof(packet.header)], packet.data.length(), this->protocol.crypt_key.data(), this->protocol.crypt_key.length());
{
threads::MutexLock lock(this->network.queue_lock);
this->network.queue_write.push_back(buffer);
}
event_add(this->network.event_write, nullptr);
}

View File

@ -0,0 +1,113 @@
#pragma once
#include <netinet/in.h>
#include <event.h>
#include <ThreadPool/Future.h>
#include <deque>
#include "shared/License.h"
#define FLSUCCESS(listener, object) \
do { \
auto l = listener.release(); \
if(l) \
l->executionSucceed(object); \
delete l; \
} while(0)
#define FLERROR(listener, object) \
do { \
auto l = listener.release(); \
if(l) \
l->executionFailed(object); \
delete l; \
} while(0)
namespace license {
namespace manager {
enum ConnectionState {
UNCONNECTED,
CONNECTING,
CONNECTED,
DISCONNECTING
};
class ServerConnection {
public:
ServerConnection();
~ServerConnection();
threads::Future<bool> connect(const std::string& host, uint16_t port);
void disconnect(const std::string&);
void ping();
threads::Future<bool> login(const std::string&, const std::string&);
threads::Future<std::pair<std::shared_ptr<license::License>, std::shared_ptr<license::LicenseInfo>>> registerLicense(
const std::string& first_name,
const std::string& last_name,
const std::string& username,
const std::string& email,
license::LicenseType type,
const std::chrono::system_clock::time_point& end,
const std::chrono::system_clock::time_point& begin = std::chrono::system_clock::now()
);
threads::Future<std::map<std::string, std::shared_ptr<license::LicenseInfo>>> list(int offset, int count);
threads::Future<bool> deleteLicense(const std::string& key, bool full = false);
bool verbose = true;
private:
struct {
ConnectionState state = ConnectionState::UNCONNECTED;
sockaddr_in address_remote;
int file_descriptor = 0;
event* event_read = nullptr;
event* event_write = nullptr;
struct event_base* event_base = nullptr;
threads::Thread* event_base_dispatch = nullptr;
threads::Thread* flush_thread = nullptr;
threads::Mutex queue_lock;
std::deque<std::string> queue_write;
std::unique_ptr<protocol::packet> current_packet;
} network;
struct {
protocol::RequestState state;
std::string crypt_key = "";
std::mutex ping_lock;
std::condition_variable ping_notify;
std::thread ping_thread;
} protocol;
struct {
std::unique_ptr<threads::Future<bool>> future_connect;
std::unique_ptr<threads::Future<bool>> future_login;
std::unique_ptr<threads::Future<std::pair<std::shared_ptr<license::License>, std::shared_ptr<license::LicenseInfo>>>> future_register;
std::unique_ptr<threads::Future<std::map<std::string, std::shared_ptr<license::LicenseInfo>>>> future_list;
std::unique_ptr<threads::Future<bool>> future_delete;
} listener;
std::string local_disconnect_message;
static void handleEventRead(int, short, void*);
static void handleEventWrite(int, short, void*);
void closeConnection();
void sendPacket(const protocol::packet&);
void handleMessage(const std::string&);
void handlePacketDisconnect(const std::string&);
void handlePacketHandshake(const std::string&);
void handlePacketAuthResponse(const std::string&);
void handlePacketCreateResponse(const std::string&);
void handlePacketListResponse(const std::string&);
void handlePacketDeleteResponse(const std::string&);
};
}
}

View File

@ -0,0 +1,93 @@
//
// Created by wolverindev on 08.05.18.
//
#include <netinet/tcp.h>
#include <misc/endianness.h>
#include <LicenseRequest.pb.h>
#include <LicenseManager.pb.h>
#include <misc/std_unique_ptr.h>
#include "ServerConnection.h"
using namespace std;
using namespace std::chrono;
using namespace license;
using namespace license::manager;
threads::Future<bool> ServerConnection::login(const std::string& username, const std::string& password) {
this->listener.future_login = unique_ptr<threads::Future<bool>>(new threads::Future<bool>());
ts::proto::license::AuthorizationRequest request;
request.set_username(username);
request.set_password(password);
this->sendPacket({protocol::PACKET_CLIENT_AUTH_REQUEST, request});
if(this->network.state != ConnectionState::CONNECTED)
this->listener.future_login->executionFailed("not connected");
return *this->listener.future_login;
}
threads::Future<std::pair<std::shared_ptr<license::License>, std::shared_ptr<license::LicenseInfo>>> ServerConnection::registerLicense(
const std::string &first_name,
const std::string &last_name,
const std::string &username,
const std::string &email,
license::LicenseType type,
const std::chrono::system_clock::time_point& end,
const std::chrono::system_clock::time_point& start
) {
this->listener.future_register = std::make_unique<threads::Future<std::pair<std::shared_ptr<license::License>, std::shared_ptr<license::LicenseInfo>>>>();
ts::proto::license::LicenseCreateRequest request;
request.set_issuer_first_name(first_name);
request.set_issuer_last_name(last_name);
request.set_issuer_username(username);
request.set_issuer_email(email);
request.set_type(type);
request.set_begin(duration_cast<milliseconds>(start.time_since_epoch()).count());
request.set_end(duration_cast<milliseconds>(end.time_since_epoch()).count());
this->sendPacket({protocol::PACKET_CLIENT_LICENSE_CREATE_REQUEST, request});
if(this->network.state != ConnectionState::CONNECTED)
this->listener.future_register->executionFailed("not connected");
return *this->listener.future_register;
}
threads::Future<std::map<std::string, std::shared_ptr<license::LicenseInfo>>> ServerConnection::list(int offset,
int count) {
this->listener.future_list = std::make_unique<threads::Future<std::map<std::string, std::shared_ptr<license::LicenseInfo>>>>();
ts::proto::license::LicenseListRequest request;
request.set_offset(offset);
request.set_count(count);
this->sendPacket({protocol::PACKET_CLIENT_LIST_REQUEST, request});
if(this->network.state != ConnectionState::CONNECTED)
this->listener.future_register->executionFailed("not connected");
return *this->listener.future_list;
}
threads::Future<bool> ServerConnection::deleteLicense(const std::string &key, bool full) {
this->listener.future_delete = make_unique<threads::Future<bool>>();
ts::proto::license::LicenseDeleteRequest request;
request.set_key(key);
request.set_full(full);
this->sendPacket({protocol::PACKET_CLIENT_DELETE_REQUEST, request});
if(this->network.state != ConnectionState::CONNECTED)
this->listener.future_register->executionFailed("not connected");
return *this->listener.future_delete;
}
void ServerConnection::ping() {
this->list(0, 1);
return; //FIXME
cout << "Sending ping" << endl;
this->sendPacket({protocol::PACKET_PING, nullptr});
}

View File

@ -0,0 +1,180 @@
#include <netinet/tcp.h>
#include <misc/endianness.h>
#include <LicenseRequest.pb.h>
#include <LicenseManager.pb.h>
#include <shared/crypt.h>
#include <shared/License.h>
#include <misc/std_unique_ptr.h>
#include "ServerConnection.h"
using namespace std;
using namespace std::chrono;
using namespace license;
using namespace license::manager;
#define LERROR(message) \
do { \
FLERROR(this->listener.future_connect, message); \
return; \
} while(0)
void ServerConnection::handleMessage(const std::string& message) {
auto& packet = this->network.current_packet;
if(packet) {
auto left = packet->header.length - packet->data.size();
if(left >= message.length()) {
packet->data += message;
} else {
packet->data += message.substr(0, left);
if(this->verbose)
cerr << "Dropping overhead! FIXME!" << endl;
}
} else {
if(message.length() < sizeof(protocol::packet::header)) {
if(this->verbose)
cout << "Invalid packet header size!" << endl;
return;
}
packet = std::make_unique<protocol::packet>(protocol::PACKET_DISCONNECT, "");
memcpy(packet.get(), message.data(), sizeof(protocol::packet::header));
packet->data = message.substr(sizeof(protocol::packet::header));
}
if(packet->data.length() < packet->header.length) return;
if(!this->protocol.crypt_key.empty())
xorBuffer((char*) packet->data.data(), packet->data.length(), this->protocol.crypt_key.data(), this->protocol.crypt_key.length());
switch (packet->header.packetId) {
case protocol::PACKET_SERVER_HANDSHAKE:
this->handlePacketHandshake(packet->data);
break;
case protocol::PACKET_SERVER_AUTH_RESPONSE:
this->handlePacketAuthResponse(packet->data);
break;
case protocol::PACKET_DISCONNECT:
this->handlePacketDisconnect(packet->data);
break;
case protocol::PACKET_SERVER_LICENSE_CREATE_RESPONSE:
this->handlePacketCreateResponse(packet->data);
break;
case protocol::PACKET_SERVER_LIST_RESPONSE:
this->handlePacketListResponse(packet->data);
break;
case protocol::PACKET_CLIENT_DELETE_RESPONSE:
this->handlePacketDeleteResponse(packet->data);
break;
default:
if(this->verbose)
cout << "Invalid packet type: " << packet->header.packetId << endl;
}
packet.reset();
}
void ServerConnection::handlePacketDisconnect(const std::string& message) {
if(this->verbose)
cout << "Got disconnect: " << message << endl;
this->closeConnection();
}
void ServerConnection::handlePacketHandshake(const std::string& data) {
if(this->protocol.state != protocol::HANDSCAKE) LERROR("Protocol state mismatch");
if(data.length() < 3) LERROR("Invalid packet size");
if((uint8_t) data[0] != 0xAF || (uint8_t) data[1] != 0xFE) LERROR("Invalid handshake");
if((uint8_t) data[2] != LICENSE_PROT_VERSION) LERROR("Invalid license protocol version. Please update TeaSpeak!");
auto key_length = be2le16(data.data(), 3);
if(data.length() < key_length + 3) LERROR("Invalid packet size");
this->protocol.crypt_key = data.substr(5, key_length);
FLSUCCESS(this->listener.future_connect, true);
this->protocol.state = protocol::MANAGER_AUTHORIZATION;
}
void ServerConnection::handlePacketAuthResponse(const std::string& data) {
if(this->protocol.state != protocol::MANAGER_AUTHORIZATION) {
FLERROR(this->listener.future_login, "Invalid state");
return;
}
ts::proto::license::AuthorizationResponse pkt;
if(!pkt.ParseFromString(data)) {
FLERROR(this->listener.future_login, "Invalid response");
return;
}
if(pkt.has_success() && pkt.success()) {
FLSUCCESS(this->listener.future_login, true);
} else {
FLERROR(this->listener.future_login, pkt.has_message() ? "Login error: " + pkt.message() : "invalid login");
}
}
void ServerConnection::handlePacketCreateResponse(const std::string& data) {
ts::proto::license::LicenseCreateResponse pkt;
if(!pkt.ParseFromString(data)) {
FLERROR(this->listener.future_register, "Invalid response");
return;
}
if(!pkt.has_error()) {
auto info = make_shared<LicenseInfo>();
info->first_name = pkt.license().first_name();
info->last_name = pkt.license().last_name();
info->username = pkt.license().username();
info->email = pkt.license().email();
info->type = static_cast<LicenseType>(pkt.license().type());
info->start = system_clock::time_point() + milliseconds(pkt.license().begin());
info->creation = system_clock::time_point() + milliseconds(pkt.license().created());
info->end = system_clock::time_point() + milliseconds(pkt.license().end());
string error;
auto license = license::readLocalLicence(pkt.exported_key(), error);
if(!license) FLERROR(this->listener.future_register, "Failed to read license!");
if(this->listener.future_register) {
auto l = this->listener.future_register.release();
l->executionSucceed({license, info});
delete l;
}
} else {
FLERROR(this->listener.future_register, pkt.error());
}
}
void ServerConnection::handlePacketListResponse(const std::string& data) {
ts::proto::license::LicenseListResponse pkt;
if(!pkt.ParseFromString(data)) {
FLERROR(this->listener.future_list, "Invalid response");
return;
}
std::map<std::string, std::shared_ptr<license::LicenseInfo>> response;
for(const auto& entry : pkt.entries()) {
auto info = make_shared<LicenseInfo>();
info->first_name = entry.first_name();
info->last_name = entry.last_name();
info->username = entry.username();
info->email = entry.email();
info->type = static_cast<LicenseType>(entry.type());
info->start = system_clock::time_point() + milliseconds(entry.begin());
info->creation = system_clock::time_point() + milliseconds(entry.created());
info->end = system_clock::time_point() + milliseconds(entry.end());
response[entry.key()] = info;
}
FLSUCCESS(this->listener.future_list, response);
}
void ServerConnection::handlePacketDeleteResponse(const std::string& data) {
ts::proto::license::LicenseDeleteResponse pkt;
if(!pkt.ParseFromString(data)) {
FLERROR(this->listener.future_list, "Invalid response");
return;
}
FLSUCCESS(this->listener.future_delete, pkt.succeed());
}

View File

@ -0,0 +1,24 @@
#pragma once
#include <QTimer>
#include <QThread>
#include <QApplication>
template <typename Func>
inline void runOnThread(QThread *qThread, Func &&func)
{
if(qThread == QThread::currentThread()){
func();
return;
}
QTimer *t = new QTimer();
t->moveToThread(qThread);
t->setSingleShot(true);
QObject::connect(t, &QTimer::timeout, [=]()
{
func();
t->deleteLater();
});
QMetaObject::invokeMethod(t, "start", Qt::QueuedConnection, Q_ARG(int, 0));
}

View File

@ -0,0 +1,193 @@
#include <shared/License.h>
#include <QtWidgets/QMessageBox>
#include <manager/qtHelper.h>
#include "LicenseGenerator.h"
#include "manager/ServerConnection.h"
using namespace std;
using namespace std::chrono;
using namespace license;
using namespace license::manager;
using namespace license::ui;
enum TimeType : uint8_t {
PERM,
YEARS_1,
MONTHS_6,
MONTHS_3,
MONTHS_1,
COSTUME
};
LicenseGenerator::LicenseGenerator(QWidget* owner) : QMainWindow(owner) {
ui.setupUi(this);
ui.licenseType->addItem("Demo", qVariantFromValue((uint8_t) license::DEMO));
ui.licenseType->addItem("Premium", qVariantFromValue((uint8_t) license::PREMIUM));
ui.licenseType->addItem("Hoster", qVariantFromValue((uint8_t) license::HOSTER));
ui.licenseType->addItem("Private", qVariantFromValue((uint8_t) license::PRIVATE));
ui.licenseType->setCurrentIndex(ui.licenseType->findData(qVariantFromValue((uint8_t) license::PREMIUM)));
ui.datePickerType->addItem("Permanent", qVariantFromValue((uint8_t) TimeType::PERM));
ui.datePickerType->addItem("1 Year", qVariantFromValue((uint8_t) TimeType::YEARS_1));
ui.datePickerType->addItem("6 Months", qVariantFromValue((uint8_t) TimeType::MONTHS_6));
ui.datePickerType->addItem("3 Months", qVariantFromValue((uint8_t) TimeType::MONTHS_3));
ui.datePickerType->addItem("1 Month", qVariantFromValue((uint8_t) TimeType::MONTHS_1));
ui.datePickerType->addItem("Costume", qVariantFromValue((uint8_t) TimeType::COSTUME));
QObject::connect(ui.datePickerType, SIGNAL(currentIndexChanged(int)), this, SLOT(handleTimeTypeChanged(int)));
ui.datePickerType->setCurrentIndex(ui.datePickerType->findData(qVariantFromValue((uint8_t) TimeType::MONTHS_3)));
//QObject::connect(ui.generateLicense, SIGNAL(clicked()), this, SLOT(handleGenerateLicense()));
QObject::connect(ui.registerLicense, SIGNAL(clicked()), this, SLOT(handleRegisterLicense()));
QObject::connect(ui.username, SIGNAL(textChanged(const QString&)), this, SLOT(handleInformationChanged()));
QObject::connect(ui.email, SIGNAL(textChanged(const QString&)), this, SLOT(handleInformationChanged()));
QObject::connect(ui.name_last, SIGNAL(textChanged(const QString&)), this, SLOT(handleInformationChanged()));
QObject::connect(ui.name_first, SIGNAL(textChanged(const QString&)), this, SLOT(handleInformationChanged()));
this->handleInformationChanged();
this->setAttribute(Qt::WA_DeleteOnClose);
}
LicenseGenerator::~LicenseGenerator() {}
void LicenseGenerator::handleTimeTypeChanged(int type) {
if(type == TimeType::COSTUME) {
ui.datePicker->setEnabled(true);
ui.datePicker->setDateTime(QDateTime::currentDateTimeUtc());
} else {
ui.datePicker->setEnabled(false);
auto current = system_clock::now();
switch (type){
case TimeType::YEARS_1:
current += hours(24 * 30 * 12);
break;
case TimeType::MONTHS_6:
current += hours(24 * 30 * 6);
break;
case TimeType::MONTHS_3:
current += hours(24 * 30 * 3);
break;
case TimeType::MONTHS_1:
current += hours(24 * 30 * 1);
break;
case TimeType::PERM:
current = system_clock::time_point();
break;
default:
break;
}
ui.datePicker->setDateTime(QDateTime::fromMSecsSinceEpoch(duration_cast<milliseconds>(current.time_since_epoch()).count()));
}
}
#define BACKGROUND(var, color) \
do { \
QPalette pal = (var)->palette(); \
pal.setColor(QPalette::ColorRole::Base, color); \
(var)->setPalette(pal); \
} while(false)
bool LicenseGenerator::validInput() {
bool error = false;
if(ui.username->text().isEmpty()) {
BACKGROUND(ui.username, QColor(255, 0, 0));
error |= true;
} else
BACKGROUND(ui.username, QColor(255, 255, 255));
if(ui.email->text().isEmpty()) {
BACKGROUND(ui.email, QColor(255, 0, 0));
error |= true;
} else BACKGROUND(ui.email, QColor(255, 255, 255));
if(ui.name_last->text().isEmpty()) {
BACKGROUND(ui.name_last, QColor(255, 0, 0));
error |= true;
} else BACKGROUND(ui.name_last, QColor(255, 255, 255));
if(ui.name_first->text().isEmpty()) {
BACKGROUND(ui.name_first, QColor(255, 0, 0));
error |= true;
} else BACKGROUND(ui.name_first, QColor(255, 255, 255));
return !error;
}
void LicenseGenerator::handleInformationChanged() {
std::string info = ui.username->text().toStdString() + "(" + ui.email->text().toStdString() + ")";
QPalette pal = this->ui.character_counter->palette();
if(info.length() >= 64) {
this->ui.character_counter->setText(QString::fromStdString("Input is " + to_string(info.length() - 63) + " characters to long!"));
pal.setColor(QPalette::ColorRole::Foreground, QColor(0xFF, 0, 0));
} else {
this->ui.character_counter->setText(QString::fromStdString(to_string(64 - info.length()) + " characters left"));
pal.setColor(QPalette::ColorRole::Foreground, QColor(0, 0, 0));
}
this->ui.character_counter->setPalette(pal);
}
void LicenseGenerator::handleGenerateLicense() {
if(!validInput()) {
QMessageBox::warning(this, "Invalid arguments", "Please check your provided arguments");
return;
}
std::string info = ui.username->text().toStdString() + "(" + ui.email->text().toStdString() + ")";
if(info.length() >= 64) {
QMessageBox::warning(this, "Invalid arguments", "Username + E-Mail are too long!");
return;
}
system_clock::time_point duration;
duration += milliseconds(ui.datePicker->dateTime().toMSecsSinceEpoch());
if(duration.time_since_epoch().count() != 0 && system_clock::now() > duration) {
auto res = QMessageBox::warning(this, "Invalid arguments", "Invalid end time. Are you sure you want to create this license?", QMessageBox::Ok | QMessageBox::Abort);
if(res != QMessageBox::Ok) return;
}
auto ltType = (license::LicenseType) ui.licenseType->itemData(ui.licenseType->currentIndex()).toInt();
auto license = license::createLocalLicence(ltType, duration, info);
ui.license->setText(QString::fromStdString(license));
QMessageBox::information(this, "License", "License successfully generated!");
}
extern ServerConnection* connection;
void LicenseGenerator::handleRegisterLicense() {
if(!validInput()) {
QMessageBox::warning(this, "Invalid arguments", "Please check your provided arguments");
return;
}
std::string info = ui.username->text().toStdString() + "(" + ui.email->text().toStdString() + ")";
if(info.length() >= 64) {
QMessageBox::warning(this, "Invalid arguments", "Username + E-Mail are too long!");
return;
}
system_clock::time_point duration;
duration += milliseconds(ui.datePicker->dateTime().toMSecsSinceEpoch());
if(duration.time_since_epoch().count() != 0 && system_clock::now() > duration) {
auto res = QMessageBox::warning(this, "Invalid arguments", "Invalid end time. Are you sure you want to create this license?", QMessageBox::Ok | QMessageBox::Abort);
if(res != QMessageBox::Ok) return;
}
auto ltType = (license::LicenseType) ui.licenseType->itemData(ui.licenseType->currentIndex()).toInt();
auto result = connection->registerLicense(ui.name_first->text().toStdString(), ui.name_last->text().toStdString(), ui.username->text().toStdString(), ui.email->text().toStdString(), ltType, duration);
result.waitAndGetLater([&, result](std::pair<std::shared_ptr<license::License>, std::shared_ptr<license::LicenseInfo>>* response) {
if(result.state() == threads::FutureState::FAILED || !response) {
runOnThread(this->thread(), [&, result]{
QMessageBox::warning(this, "Creation failed", QString::fromStdString("Failed to create license (" + result.errorMegssage() + ")"));
return;
});
} else {
runOnThread(this->thread(), [&, result, response]{
ui.license->setText(QString::fromStdString(license::exportLocalLicense(response->first)));
QMessageBox::information(this, "License", "License successfully generated and registered!");
});
}
});
}

View File

@ -0,0 +1,26 @@
#pragma once
#include <QtCore/QtCore>
#include <QtWidgets/QMainWindow>
#include <ui_licensegenerator.h>
namespace license {
namespace ui {
class LicenseGenerator : public QMainWindow {
Q_OBJECT;
public:
LicenseGenerator(QWidget*);
~LicenseGenerator();
private slots:
void handleTimeTypeChanged(int);
void handleGenerateLicense();
void handleRegisterLicense();
void handleInformationChanged();
private:
Ui::LicenseGenerator ui;
bool validInput();
};
}
}

View File

@ -0,0 +1,68 @@
#include <manager/ServerConnection.h>
#include "LicenseGenerator.h"
#include <QProgressDialog>
#include <QMessageBox>
#include "LoginWindow.h"
#include "manager/qtHelper.h"
#include "Overview.h"
using namespace license;
using namespace license::manager;
using namespace license::ui;
using namespace std;
using namespace std::chrono;
LoginWindow::LoginWindow() : QDialog(nullptr) {
this->ui.setupUi(this);
this->setFixedSize(this->size());
QObject::connect(ui.btn_login, SIGNAL(clicked()), this, SLOT(onClickConnect()));
//this->setAttribute(Qt::WA_DeleteOnClose);
}
LoginWindow::~LoginWindow() { }
extern ServerConnection* connection;
extern QWidget* current_widget;
void LoginWindow::onClickConnect() {
progress = unique_ptr<QProgressDialog>(new QProgressDialog(this));
progress->setMinimum(0);
progress->setMaximum(2);
progress->setLabelText("Connecting...");
progress->show();
auto future = connection->connect("0.0.0.0", 27786);
future.waitAndGetLater([&, future](bool* flag) {
runOnThread(this->thread(), [&, future, flag](){
if(!flag || !*flag) {
if(future.state() == threads::FutureState::WORKING)
QMessageBox::critical(this, "Connect error", "Could not connect to remote!<br>Error: Connect timeout");
else
QMessageBox::critical(this, "Connect error", QString::fromStdString("Could not connect to remote!<br>Error: " + future.errorMegssage()));
progress.reset();
connection->disconnect("timeout");
} else {
progress->setValue(1);
progress->setLabelText("Logging in");
auto login_future = connection->login(this->ui.field_user->text().toStdString(), this->ui.field_password->text().toStdString());
login_future.waitAndGetLater([&, login_future](bool* flag){
runOnThread(this->thread(), [&, login_future, flag](){
if(!flag || !*flag) {
if(future.state() == threads::FutureState::WORKING)
QMessageBox::critical(this, "Login error", "Could not login!<br>Error: Login timeout");
else
QMessageBox::critical(this, "Login error", QString::fromStdString("Could not login!<br>Error: " + future.errorMegssage()));
progress.reset();
connection->disconnect("timeout");
} else {
current_widget = new Overview();
current_widget->show();
progress.reset();
this->close();
}
});
}, system_clock::now() + seconds(5));
}
});
}, system_clock::now() + seconds(5));
};

View File

@ -0,0 +1,24 @@
#pragma once
#include <QtCore/QtCore>
#include <QtWidgets/QMainWindow>
#include <QProgressDialog>
#include <ui_loginwindow.h>
#include <memory>
namespace license {
namespace ui {
class LoginWindow : public QDialog {
Q_OBJECT;
public:
LoginWindow();
virtual ~LoginWindow();
private slots:
void onClickConnect();
private:
std::unique_ptr<QProgressDialog> progress;
Ui::LoginWindow ui;
};
}
}

View File

@ -0,0 +1,173 @@
#include <manager/ServerConnection.h>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QTableWidgetItem>
#include <misc/base64.h>
#include <QtWidgets/QStyle>
#include <QtWidgets/QMessageBox>
#include <manager/qtHelper.h>
#include <QtWidgets/QtWidgets>
#include "LicenseGenerator.h"
#include "Overview.h"
#include "UiLicenseInfo.h"
using namespace license;
using namespace license::manager;
using namespace license::ui;
using namespace std;
using namespace std::chrono;
extern ServerConnection* connection;
Overview::Overview() {
this->ui.setupUi(this);
this->ui.licenses->setColumnCount(RowEntry::ENDMARKER);
this->ui.licenses->setHorizontalHeaderItem(RowEntry::ID, new QTableWidgetItem("ID"));
this->ui.licenses->setHorizontalHeaderItem(RowEntry::USERNAME, new QTableWidgetItem("Username"));
this->ui.licenses->setHorizontalHeaderItem(RowEntry::NAME, new QTableWidgetItem("Name"));
this->ui.licenses->setHorizontalHeaderItem(RowEntry::END, new QTableWidgetItem("End timestamp"));
this->ui.licenses->setHorizontalHeaderItem(RowEntry::ACTIVE, new QTableWidgetItem("Active"));
this->ui.licenses->setHorizontalHeaderItem(RowEntry::ACTION_EDIT, new QTableWidgetItem(""));
this->ui.licenses->setHorizontalHeaderItem(RowEntry::ACTION_DELETE, new QTableWidgetItem(""));
this->ui.licenses->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
this->ui.licenses->verticalHeader()->hide();
this->ui.licenses->setSelectionBehavior(QAbstractItemView::SelectRows);
//this->ui.licenses->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
QObject::connect(this->ui.btn_create_license, SIGNAL(clicked()), this, SLOT(clickedNewLicense()));
QObject::connect(this->ui.btn_refresh, SIGNAL(clicked()), this, SLOT(btn_refresh_clicked()));
if(!connection) {
auto test = make_shared<license::LicenseInfo>();
test->type = LicenseType::PREMIUM;
test->email = "test@test.de";
test->first_name = "Markus";
test->last_name = "Hadenfeldt";
test->username = "WolverinDEV";
test->end = system_clock::now() + minutes(60);
test->creation = system_clock::now();
test->start = system_clock::now();
addLicenseEntry("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", test);
} else {
this->refresh_license();
}
}
Overview::~Overview() {}
#define M(var) \
var = new QTableWidgetItem(); \
var->setData(Qt::UserRole, qVariantFromValue<void*>(this)); \
var->setFlags(var->flags() & ~Qt::ItemIsEditable)
RowEntry::RowEntry(const shared_ptr<license::LicenseInfo> &license, const string &licenseId) : license(license), licenseId(licenseId) {
M(widget_id);
M(widget_username);
M(widget_name);
M(widget_end_timestamp);
M(widget_active);
widget_id->setText(QString::fromStdString(base64::encode(licenseId)));
widget_username->setText(QString::fromStdString(license->username));
widget_name->setText(QString::fromStdString(license->first_name + " " + license->last_name));
{
if(license->end.time_since_epoch() > hours(1)) {
char buffer[128];
auto tm = chrono::system_clock::to_time_t(license->end);
strftime(buffer, sizeof(buffer), "%Y-%m-%d.%X", localtime(&tm));
widget_end_timestamp->setText(buffer);
} else {
widget_end_timestamp->setText("never");
}
}
widget_active->setTextColor(license->isValid() ? Qt::green : Qt::red);
widget_active->setText(QString::fromStdString(license->isValid() ? "yes" : "no"));
this->button_delete = new QPushButton();
this->button_edit = new QPushButton();
this->button_delete->setIcon(QApplication::style()->standardIcon(QStyle::SP_BrowserStop));
this->button_edit->setIcon(QApplication::style()->standardIcon(QStyle::SP_DialogOpenButton));
QObject::connect(this->button_edit, SIGNAL(clicked()), this, SLOT(btn_edit_clicked()));
QObject::connect(this->button_delete, SIGNAL(clicked()), this, SLOT(btn_delete_clicked()));
}
RowEntry::~RowEntry() {}
void RowEntry::btn_delete_clicked() {
auto message = "Do you really want to delete this license?<br>"
"Name: " + this->license->first_name + " " + this->license->last_name + "<br>"
"Username: " + this->license->username;
if(QMessageBox::question(this->handle, "Are you sure?", QString::fromStdString(message)) == QMessageBox::Yes) {
bool full = QMessageBox::question(this->handle, "You want a full delete?", "Do you even want to erase the license from the database?") == QMessageBox::Yes;
connection->deleteLicense(this->licenseId, full).waitAndGetLater([&](bool flag) {
if(!flag)
runOnThread(this->thread(), [&]{
QMessageBox::warning(this->handle, "Delete error", "Failed to delete license!");
});
else
this->handle->refresh_license();
}, false);
}
}
void RowEntry::btn_edit_clicked() {
auto info = new ui::UiLicenseInfo(this->license, this->licenseId, this->handle);
info->show();
}
void Overview::addLicenseEntry(std::string licenseId, shared_ptr<license::LicenseInfo> info) {
if(this->thread() != QThread::currentThread()) {
runOnThread(this->thread(), [&, licenseId, info]{
this->addLicenseEntry(licenseId, info);
});
return;
}
auto entry = make_shared<RowEntry>(info, licenseId);
entry->handle = this;
int row = this->ui.licenses->rowCount();
this->ui.licenses->insertRow(row);
this->ui.licenses->setItem(row, RowEntry::ID, entry->widget_id);
this->ui.licenses->setItem(row, RowEntry::USERNAME, entry->widget_username);
this->ui.licenses->setItem(row, RowEntry::NAME, entry->widget_name);
this->ui.licenses->setItem(row, RowEntry::END, entry->widget_end_timestamp);
this->ui.licenses->setItem(row, RowEntry::ACTIVE, entry->widget_active);
this->ui.licenses->setIndexWidget(this->ui.licenses->model()->index(row, RowEntry::ACTION_EDIT), entry->button_edit);
this->ui.licenses->setIndexWidget(this->ui.licenses->model()->index(row, RowEntry::ACTION_DELETE), entry->button_delete);
this->entries.push_back(entry);
}
void Overview::clickedNewLicense() {
auto instance = new LicenseGenerator(this);
instance->show();
}
void Overview::refresh_license() {
this->btn_refresh_clicked();
}
void Overview::btn_refresh_clicked() {
runOnThread(this->thread(), [&]{
while(this->ui.licenses->rowCount() > 0)
this->ui.licenses->removeRow(0);
this->entries.clear();
auto fut = connection->list(0, 0);
fut.waitAndGetLater([&, fut](std::map<std::string, std::shared_ptr<license::LicenseInfo>> response) {
if(!fut.succeeded()) {
runOnThread(this->thread(), [&, fut](){
QMessageBox::warning(this, "Failed to load data", "Failed to load data:<br>" + QString::fromStdString(fut.errorMegssage()));
});
} else {
for(const auto& entry : response)
this->addLicenseEntry(entry.first, entry.second);
}
}, {});
});
}

View File

@ -0,0 +1,69 @@
#pragma once
#include "shared/License.h"
#include <QtCore/QtCore>
#include <QtWidgets/QMainWindow>
#include <QProgressDialog>
#include <ui_loginwindow.h>
#include <memory>
#include <deque>
#include <ui_owerview.h>
namespace license {
namespace ui {
class Overview;
struct RowEntry : public QObject {
Q_OBJECT;
public:
enum Row {
ID,
USERNAME,
NAME,
END,
ACTIVE,
ACTION_EDIT,
ACTION_DELETE,
ENDMARKER
};
Overview* handle;
QTableWidgetItem* widget_id;
QTableWidgetItem* widget_username;
QTableWidgetItem* widget_name;
QTableWidgetItem* widget_end_timestamp;
QTableWidgetItem* widget_active;
QPushButton* button_edit;
QPushButton* button_delete;
std::shared_ptr<LicenseInfo> license;
std::string licenseId;
RowEntry(const std::shared_ptr<LicenseInfo> &license, const std::string &licenseId);
~RowEntry();
private slots:
void btn_edit_clicked();
void btn_delete_clicked();
};
class Overview : public QMainWindow {
Q_OBJECT;
public:
Overview();
virtual ~Overview();
void refresh_license();
private slots:
void clickedNewLicense();
void btn_refresh_clicked();
private:
Ui::Overview ui;
std::deque<std::shared_ptr<RowEntry>> entries;
void addLicenseEntry(std::string, std::shared_ptr<LicenseInfo>);
};
}
}

View File

@ -0,0 +1,112 @@
#include <QProgressDialog>
#include <QMessageBox>
#include "manager/ServerConnection.h"
#include "UiLicenseInfo.h"
#include "LoginWindow.h"
#include "manager/qtHelper.h"
#include "Overview.h"
using namespace license;
using namespace license::manager;
using namespace license::ui;
using namespace std;
using namespace std::chrono;
#define q(str) QString::fromStdString(str)
UiLicenseInfo::UiLicenseInfo(const std::shared_ptr<license::LicenseInfo> &info, const std::string &key, QWidget* h) : QDialog(h), key(key), info(info) {
this->ui.setupUi(this);
this->set_editable(this->flag_editable);
this->ui.edit_name_first->setText(q(info->first_name));
this->ui.edit_name_last->setText(q(info->last_name));
this->ui.edit_email->setText(q(info->email));
this->ui.edit_username->setText(q(info->username));
this->ui.edit_time_created->setSpecialValueText("never");
this->ui.edit_time_begin->setSpecialValueText("never");
this->ui.edit_time_end->setSpecialValueText("never");
this->ui.edit_time_created->setDateTime(QDateTime::fromMSecsSinceEpoch(duration_cast<milliseconds>(this->info->creation.time_since_epoch()).count()));
this->ui.edit_time_begin->setDateTime(QDateTime::fromMSecsSinceEpoch(duration_cast<milliseconds>(this->info->start.time_since_epoch()).count()));
this->ui.edit_time_end->setDateTime(QDateTime::fromMSecsSinceEpoch(duration_cast<milliseconds>(this->info->end.time_since_epoch()).count()));
this->update_length();
QObject::connect(this->ui.btn_edit, SIGNAL(clicked()), this, SLOT(btn_edit_clicked()));
}
UiLicenseInfo::~UiLicenseInfo() {}
//The VA ars are for my ide
#define M(var, ...) \
do { \
(var)->setFrame(flag); \
(var)->setReadOnly(!flag); \
QPalette p = (var)->palette(); \
p.setColor(QPalette::Base, QColor(255,255,255,flag ? 255 : 0)); \
(var)->setPalette(p); \
} while(0)
void UiLicenseInfo::set_editable(bool flag) {
M(this->ui.edit_email);
M(this->ui.edit_name_first);
M(this->ui.edit_name_last);
M(this->ui.edit_username);
M(this->ui.edit_time_created);
M(this->ui.edit_time_begin);
M(this->ui.edit_time_end);
if(flag) {
this->ui.edit_time_created->setButtonSymbols(QAbstractSpinBox::UpDownArrows);
this->ui.edit_time_begin->setButtonSymbols(QAbstractSpinBox::UpDownArrows);
this->ui.edit_time_end->setButtonSymbols(QAbstractSpinBox::UpDownArrows);
} else {
this->ui.edit_time_created->setButtonSymbols(QAbstractSpinBox::NoButtons);
this->ui.edit_time_begin->setButtonSymbols(QAbstractSpinBox::NoButtons);
this->ui.edit_time_end->setButtonSymbols(QAbstractSpinBox::NoButtons);
}
}
#undef M
void UiLicenseInfo::btn_edit_clicked() {
this->set_editable(this->flag_editable ^= 1);
}
using days = std::chrono::duration<int, std::ratio_multiply<std::ratio<24>, std::chrono::hours::period>>;
using years = std::chrono::duration<int, std::ratio_multiply<std::ratio<365>, days::period>>;
//The VA ars are for my ide
#define M(unit, name, ...) \
do { \
auto num = duration_cast<unit>(length); \
if(num.count() > 0) { \
result += " " + to_string(num.count()) + " " + name; \
length -= num; \
} \
} while (0)
void UiLicenseInfo::update_length() {
if(this->info->end.time_since_epoch().count() == 0)
this->ui.text_length->setText("unlimited");
else {
if(this->info->end < this->info->start) {
this->ui.text_length->setText("error");
} else {
auto length = this->info->end - this->info->start;
length += seconds(1);
string result;
M(years, "years");
M(days, "days");
M(hours, "hours");
M(minutes, "minutes");
M(seconds, "seconds");
if(!result.empty())
result = result.substr(1);
this->ui.text_length->setText(q(result));
}
}
}

View File

@ -0,0 +1,29 @@
#pragma once
#include <QtCore/QtCore>
#include <QtWidgets/QMainWindow>
#include <ui_licenseinfo.h>
#include <shared/License.h>
namespace license {
namespace ui {
class UiLicenseInfo : public QDialog {
Q_OBJECT;
public:
UiLicenseInfo(const std::shared_ptr<license::LicenseInfo>& info, const std::string& key, QWidget*);
~UiLicenseInfo();
private slots:
void btn_edit_clicked();
private:
std::string key;
std::shared_ptr<license::LicenseInfo> info;
Ui::LicenseInfo ui;
bool flag_editable = false;
void set_editable(bool);
void update_length();
};
}
}

View File

@ -0,0 +1,351 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LicenseGenerator</class>
<widget class="QMainWindow" name="LicenseGenerator">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>681</width>
<height>444</height>
</rect>
</property>
<property name="font">
<font>
<family>Ubuntu Mono</family>
<pointsize>12</pointsize>
</font>
</property>
<property name="windowTitle">
<string>TeaSpeak license generator</string>
</property>
<property name="tabShape">
<enum>QTabWidget::Triangular</enum>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout">
<item alignment="Qt::AlignHCenter">
<widget class="QLabel" name="label_5">
<property name="maximumSize">
<size>
<width>200</width>
<height>20</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="text">
<string>License</string>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="license">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>130</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>300</height>
</size>
</property>
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="documentTitle">
<string>asdasdasd</string>
</property>
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;title&gt;asdasdasd&lt;/title&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu Mono'; font-size:12pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Ubuntu'; font-size:11pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
<property name="placeholderText">
<string>generated license</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="registerLicense">
<property name="text">
<string>Register License</string>
</property>
</widget>
</item>
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>License Information</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>First Name:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="name_first">
<property name="maximumSize">
<size>
<width>600</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Last Name:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="name_last">
<property name="maximumSize">
<size>
<width>600</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label">
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>Username:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="username">
<property name="maximumSize">
<size>
<width>600</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>E-Mail:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="email">
<property name="maximumSize">
<size>
<width>600</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="character_counter">
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>30 Characters left</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QFormLayout" name="type_layout">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>Duration</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="licenseType">
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>License Type:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="datePickerType">
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDateTimeEdit" name="datePicker">
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>681</width>
<height>24</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<widget class="QToolBar" name="toolBar">
<property name="windowTitle">
<string>toolBar</string>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,242 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LicenseInfo</class>
<widget class="QDialog" name="LicenseInfo">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>782</width>
<height>541</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>400</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="btn_edit">
<property name="text">
<string>enable edit</string>
</property>
</widget>
</item>
<item row="2" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0" colspan="2">
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>First Name</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="edit_name_first">
<property name="frame">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Last Name</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="edit_name_last">
<property name="frame">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>E-Mail</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="edit_email">
<property name="frame">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Username</string>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="edit_username">
<property name="frame">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_forum">
<property name="text">
<string>Forum Profile</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
<property name="default">
<bool>false</bool>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Created</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QDateTimeEdit" name="edit_time_created">
<property name="frame">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>License begin</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QDateTimeEdit" name="edit_time_begin"/>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>License end</string>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Status</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QLabel" name="status">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="7" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QDateTimeEdit" name="edit_time_end">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="text_length">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>1200</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>1 Tag 20 Stunden</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LoginWindow</class>
<widget class="QDialog" name="LoginWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>330</width>
<height>105</height>
</rect>
</property>
<property name="windowTitle">
<string>Login</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:14pt;&quot;&gt;Username:&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:14pt;&quot;&gt;Password:&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="field_user"/>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="field_password">
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="btn_login">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>800</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>login</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,151 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Overview</class>
<widget class="QMainWindow" name="Overview">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1219</width>
<height>554</height>
</rect>
</property>
<property name="windowTitle">
<string>Overview</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="btn_refresh">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Refresh</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btn_create_license">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="text">
<string>Create license</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTableWidget" name="licenses"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="btn_last">
<property name="text">
<string>Last side</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="text_side">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>1/2</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btn_next">
<property name="text">
<string>Next side</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1219</width>
<height>19</height>
</rect>
</property>
<widget class="QMenu" name="menuDisconnect">
<property name="title">
<string>Disconnect</string>
</property>
</widget>
<addaction name="menuDisconnect"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,54 @@
syntax = "proto2";
package ts.proto.license;
import "LicenseRequest.proto";
//Requesting stuff
message LicenseListRequest {
optional int64 offset = 1;
optional int64 count = 2;
}
message LicenseListResponse {
repeated LicenseInfo entries = 1;
required bool end = 2;
}
//Deleting stuff
message LicenseDeleteRequest {
required bytes key = 1;
required bool full = 2;
}
message LicenseDeleteResponse {
required bool succeed = 1;
}
//License request
message LicenseCreateRequest {
required string issuer_username = 1;
required string issuer_first_name = 2;
required string issuer_last_name = 3;
required string issuer_email = 4;
required int64 type = 5;
required int64 begin = 6;
required int64 end = 7;
}
message LicenseCreateResponse {
oneof result {
string error = 2;
LicenseInfo license = 3;
}
optional string exported_key = 4;
}
//Auth
message AuthorizationRequest {
required string username = 1;
required string password = 2;
}
message AuthorizationResponse {
oneof result {
string message = 1;
bool success = 2;
}
}

View File

@ -0,0 +1,72 @@
syntax = "proto2";
package ts.proto.license;
enum BlacklistState {
VALID = 0;
SUSPICIOUS = 1;
BLACKLISTED = 2;
}
message Blacklist {
required BlacklistState state = 1;
optional string reason = 2;
}
message LicenseInfo {
required bytes key = 1;
required string username = 2;
required string first_name = 3;
required string last_name = 4;
required string email = 5;
required int32 type = 6;
required int64 begin = 7;
required int64 end = 8;
required int64 created = 9;
reserved
10, //music_limit
11; //speach_limit
}
message ServerInfo {
required string version = 1;
required string uname = 2;
required int64 timestamp = 3;
optional string unique_id = 4;
}
message ServerValidation {
required bool licensed = 1;
required bool license_info = 2;
optional bytes license = 3;
optional ServerInfo info = 4; //Change somewhen to required but its currently for lagacy support
}
message LicenseResponse {
required bool valid = 1;
required Blacklist blacklist = 2;
optional LicenseInfo license_info = 3; //Only availible when ServerValidation::license_info = true
}
message PropertyUpdateRequest {
required int64 speach_total = 1;
required int64 speach_dead = 2;
required int64 speach_online = 3;
required int64 speach_varianz = 4;
required int64 clients_online = 7;
required int64 web_clients_online = 8;
required int64 bots_online = 9;
required int64 queries_online = 10;
required int64 servers_online = 11;
}
message PropertyUpdateResponse {
required bool accepted = 1;
required int64 speach_total_remote = 2;
required int64 speach_varianz_corrector = 3;
optional bool reset_speach = 4;
}

View File

@ -0,0 +1,67 @@
#include "log/LogUtils.h"
#include "LicenseManager.h"
using namespace license;
using namespace license::server;
using namespace std;
using namespace std::chrono;
using KeyIdCache = LicenseManager::KeyIdCache;
KeyIdCache::KeyIdCache(license::server::LicenseManager *handle) : handle(handle) {}
std::string KeyIdCache::getKey(size_t keyId) {
{
threads::MutexLock lock(this->entry_lock);
for(const auto& entry : this->entries)
if(entry->keyId == keyId) return entry->key;
}
sql::command(this->handle->database, "SELECT `key`, `keyId` FROM `license` WHERE `keyId` = :key", variable{":key", keyId})
.query(&KeyIdCache::insert_entry, this);
{
threads::MutexLock lock(this->entry_lock);
for(const auto& entry : this->entries)
if(entry->keyId == keyId) return entry->key;
return ""; //Key not found!
}
}
size_t KeyIdCache::getKeyId(const std::string &key) {
{
threads::MutexLock lock(this->entry_lock);
for(const auto& entry : this->entries)
if(entry->key == key) return entry->keyId;
}
auto result = sql::command(this->handle->database, "SELECT `key`, `keyId` FROM `license` WHERE `key` = :key", variable{":key", key})
.query(&KeyIdCache::insert_entry, this);
if(!result)
logError(LOG_GENERAL, "Failed to query key id for license. Query resulted in {}", result.fmtStr());
{
threads::MutexLock lock(this->entry_lock);
for(const auto& entry : this->entries)
if(entry->key == key)
return entry->keyId;
return 0; //Key not found!
}
}
int KeyIdCache::insert_entry(int length, std::string *value, std::string *names) {
string key;
size_t keyId = 0;
for(int index = 0; index < length; index++)
if(names[index] == "key")
key = value[index];
else if(names[index] == "keyId")
keyId = stoll(value[index]);
{
threads::MutexLock lock(this->entry_lock);
this->entries.push_back(new KeyIdCache::CacheEntry{key, keyId, system_clock::now()});
}
return 0;
}

View File

@ -0,0 +1,463 @@
#include "LicenseManager.h"
#include <log/LogUtils.h>
using namespace std;
using namespace std::chrono;
using namespace license;
using namespace license::server;
using namespace sql;
LicenseManager::LicenseManager(sql::SqlManager* database) : database(database){
this->id_cache = make_shared<KeyIdCache>(this);
}
LicenseManager::~LicenseManager() { }
/*
LicenseType type;
std::string username;
std::string first_name;
std::string last_name;
std::string email;
std::chrono::system_clock::time_point start;
std::chrono::system_clock::time_point end;
std::chrono::system_clock::time_point creation;
*/
#define CTBL(cmd) \
res = command(this->database, cmd).execute(); \
if(!res) { \
error = "Could not setup tables! (" + res.fmtStr() + ")"; \
return false; \
}
#define CIDX(cmd) \
res = command(this->database, cmd).execute(); \
if(!res && res.msg().find("Duplicate") == string::npos && res.msg().find("exist") == string::npos) { \
error = "Could not setup indexes! (" + res.fmtStr() + ")"; \
return false; \
}
#define SET_VERSION(ver) \
version = ver; \
sql::command(this->database, "UPDATE `general` SET `value` = :version WHERE `key` = 'version'", variable{":version", version}).execute();
bool LicenseManager::setup(std::string& error) {
result res;
int version = -1;
sql::command(this->database, "SELECT `value` FROM `general` WHERE `key` = 'version'").query([](int* version, int lnegth, string* values, string* names) {
*version = stoll(values[0]);
return 0;
}, &version);
switch (version) {
case -1:
CTBL("CREATE TABLE IF NOT EXISTS `license` (`key` BINARY(64) NOT NULL PRIMARY KEY, `type` INT, `deleted` BOOL, `issuer` TEXT)");
CTBL("CREATE TABLE IF NOT EXISTS `license_info` (`key` BINARY(64) NOT NULL PRIMARY KEY, `username` VARCHAR(128), `first_name` TEXT, `last_name` TEXT, `email` TEXT, `begin` BIGINT, `end` BIGINT, `generated` BIGINT)");
CTBL("CREATE TABLE IF NOT EXISTS `license_request` (`key` BINARY(64) NOT NULL, `ip` TEXT, `timestamp` BIGINT, `result` INT)");
CTBL("CREATE TABLE IF NOT EXISTS `general` (`key` VARCHAR(64), `value` TEXT)");
CTBL("INSERT INTO `general` (`key`, `value`) VALUES ('version', '0');");
SET_VERSION(0);
case 0:
logMessage("Updating database! To version 1");
CTBL("CREATE TABLE IF NOT EXISTS `history_speach` (`keyId` INT, `timestamp` BIGINT, `total` BIGINT, `dead` BIGINT, `online` BIGINT, `varianz` BIGINT)");
CTBL("CREATE TABLE IF NOT EXISTS `history_online` (`keyId` INT, `timestamp` BIGINT, `server` INT, `clients` INT, `web` INT, `music` INT, `queries` INT)");
CIDX("CREATE INDEX `license_key` ON `license` (`key`)");
CIDX("CREATE INDEX `license_info_key` ON `license_info` (`key`)");
CTBL("ALTER TABLE `license` DROP PRIMARY KEY, ADD `keyId` INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST;");
CTBL("ALTER TABLE `license_info` DROP PRIMARY KEY, ADD `keyId` INT NOT NULL PRIMARY KEY AUTO_INCREMENT FIRST;");
//Fixing key id's
CTBL("UPDATE `license_info` LEFT JOIN `license` ON `license`.`key` = `license_info`.`key` SET `license_info`.`keyId` = `license`.`keyId`");
//Deleting the key blob and removing auto
CTBL("ALTER TABLE `license_info` CHANGE `keyId` `keyId` INT NOT NULL UNIQUE, DROP COLUMN `key`");
//Fixing request table
CTBL("ALTER TABLE license_request ADD COLUMN `keyId` INT(0) NOT NULL;");
CTBL("UPDATE license_request LEFT JOIN license ON license.key = license_request.key SET license_request.keyId = license.keyId;");
CTBL("ALTER TABLE license_request DROP COLUMN `key`;");
//IDK why but i failed stuff :D
CTBL("ALTER TABLE license DROP PRIMARY KEY, CHANGE `keyId` `keyId` INT NOT NULL AUTO_INCREMENT PRIMARY KEY");
SET_VERSION(1);
case 1:
CTBL("ALTER TABLE history_online ADD COLUMN `ip` TEXT AFTER `keyId`;");
CTBL("ALTER TABLE history_speach ADD COLUMN `ip` TEXT AFTER `keyId`;");
SET_VERSION(2);
case 2:
CTBL("CREATE TABLE `history_version` (`keyId` INT, `ip` VARCHAR(64), `timestamp` BIGINT, `version` VARCHAR(126));");
SET_VERSION(3);
case 3:
/*
CTBL("CREATE TABLE `save_history_online` AS SELECT * FROM `history_online`;");
CTBL("CREATE TABLE `save_history_speach` AS SELECT * FROM `history_speach`;");
CTBL("CREATE TABLE `save_history_version` AS SELECT * FROM `history_version`;");
*/
CTBL("ALTER TABLE `history_online` CHANGE `ip` `unique_id` VARCHAR(64) NOT NULL;");
CTBL("ALTER TABLE `history_speach` CHANGE `ip` `unique_id` VARCHAR(64) NOT NULL;");
CTBL("ALTER TABLE `history_version` CHANGE `ip` `unique_id` VARCHAR(64) NOT NULL;");
CTBL("ALTER TABLE `license_request` ADD `unique_id` VARCHAR(64) NOT NULL AFTER `ip`;");
SET_VERSION(4);
case 4:
CTBL("CREATE TABLE IF NOT EXISTS `users` (`username` VARCHAR(64) NOT NULL PRIMARY KEY, `password_hash` VARCHAR(128), `status` INT, `owner` VARCHAR(64))");
default:;
}
return true;
}
bool LicenseManager::registerLicense(const std::string& key, const shared_ptr<LicenseInfo>& info, const std::string& issuer) {
result res;
res = command(this->database, "INSERT INTO `license` (`key`, `type`, `deleted`, `issuer`) VALUES (:key, :type, :deleted, :issuer)", variable{":key", key}, variable{":type", (uint32_t) info->type}, variable{":deleted", false}, variable{":issuer", issuer}).execute();
if(!res) {
logError("Could not register new license (" + res.fmtStr() + ")");
return false;
}
auto keyId = this->id_cache->getKeyId(key);
if(keyId == 0) return false;
res = command(this->database, "INSERT INTO `license_info` (`keyId`, `username`, `first_name`, `last_name`, `email`, `begin`, `end`, `generated`) VALUES (:key, :username, :first_name, :last_name, :email, :begin, :end, :generated)",
variable{":key", keyId},
variable{":username", info->username},
variable{":first_name", info->first_name},
variable{":last_name", info->last_name},
variable{":email", info->email},
variable{":generated", duration_cast<milliseconds>(info->creation.time_since_epoch()).count()},
variable{":begin", duration_cast<milliseconds>(info->start.time_since_epoch()).count()},
variable{":end", duration_cast<milliseconds>(info->end.time_since_epoch()).count()}
).execute();
if(!res) {
logError("Could not register new license info (" + res.fmtStr() + ")");
return false;
}
return true;
}
bool LicenseManager::deleteLicense(const std::string& key, bool full) {
if(full) {
auto keyId = this->id_cache->getKeyId(key);
if(keyId == 0) return false; //Never exists
auto res = command(this->database, "DELETE FROM `license` WHERE `key` = :key", variable{":key", key}).execute();
if(!res) logError("Could not delete license (" + res.fmtStr() + ")");
res = command(this->database, "DELETE FROM `license_info` WHERE `keyId` = :key", variable{":keyId", keyId}).execute();
if(!res) logError("Could not delete license (" + res.fmtStr() + ")");
return !!res;
} else {
auto res = command(this->database, "UPDATE `license` SET `deleted` = :true WHERE `key` = :key", variable{":true", true}, variable{":key", key}).execute();
if(!res) logError("Could not delete license (" + res.fmtStr() + ")");
return !!res;
}
}
bool LicenseManager::validLicenseKey(const std::string& key) {
bool valid = false;
auto res = command(this->database, "SELECT * FROM `license` WHERE `key` = :key AND `deleted` = :false LIMIT 1", variable{":false", false}, variable{":key", key}).query([](bool* flag, int, char**, char**) {
*flag = true;
return 0;
}, &valid);
if(!res) logError("Could not validate license (" + res.fmtStr() + ")");
return !!res && valid;
}
inline std::map<std::string, std::shared_ptr<LicenseInfo>> query_license(SqlManager* mgr, std::string key, int offset, int length) {
std::map<std::string, std::shared_ptr<LicenseInfo>> result;
auto query = string() + "SELECT `key`, `username`, `first_name`, `last_name`, `email`, `begin`, `end`, `generated`, `deleted` FROM `license_info` INNER JOIN `license` ON `license_info`.`keyId` = `license`.`keyId`";
if(!key.empty())
query += "WHERE `key` = :key";
else
query += "WHERE `deleted` = :false";
if(offset > 0 || length > 0)
query += " LIMIT " + to_string(offset) + ", " + to_string(length);
logTrace(LOG_GENERAL, "Executing query {}", query);
auto res = command(mgr, query, variable{":key", key}, variable{":false", false}).query([](std::map<std::string, std::shared_ptr<LicenseInfo>>* list, int length, std::string* values, std::string* names) {
auto info = make_shared<LicenseInfo>();
info->deleted = false;
string k;
for(int index = 0; index < length; index++) {
try {
if(names[index] == "username")
info->username = values[index];
else if(names[index] == "first_name")
info->first_name = values[index];
else if(names[index] == "key")
k = values[index];
else if(names[index] == "last_name")
info->last_name = values[index];
else if(names[index] == "email")
info->email = values[index];
else if(names[index] == "begin")
info->start = system_clock::time_point() + milliseconds(stoll(values[index]));
else if(names[index] == "end")
info->end = system_clock::time_point() + milliseconds(stoll(values[index]));
else if(names[index] == "generated")
info->creation = system_clock::time_point() + milliseconds(stoll(values[index]));
else if(names[index] == "deleted")
info->deleted = values[index] == "1" || values[index] == "true";
else
logError(LOG_GENERAL, "Unknown field {}", names[index]);
} catch (std::exception& ex) {
logError(LOG_GENERAL, "Failed to parse field {} ({}). Message: {}", names[index], values[index], ex.what());
return 1;
}
}
(*list)[k] = info;
return 0;
}, &result);
logTrace(LOG_GENERAL, "Query returned {} results", result.size());
if(!res) logError("Could not query license (" + res.fmtStr() + ")");
return result;
}
std::shared_ptr<LicenseInfo> LicenseManager::licenseInfo(const std::string& key) {
auto result = query_license(this->database, key, 0, 0);
if(result.empty()) return nullptr;
return result.begin()->second;
}
std::map<std::string, std::shared_ptr<LicenseInfo>> LicenseManager::listLicenses(int offset, int limit) {
return query_license(this->database, "", offset, limit);
}
bool LicenseManager::logRequest(const std::string& key, const std::string& unique_id, const std::string& ip, const std::string& version, int state) {
result res;
auto keyId = this->id_cache->getKeyId(key);
if(keyId == 0) {
logError(LOG_GENERAL, "Failed to log license request (could not resolve key id)");
return false;
}
auto timestamp = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
//SELECT * FROM `license_info` WHERE `keyId` IN (SELECT `keyId` FROM `license` WHERE `key` = '000')
res = command(this->database, "INSERT INTO `license_request` (`keyId`, `ip`, `unique_id`, `timestamp`, `result`) VALUES (:key, :ip, :unique_id, :timestamp, :result)",
variable{":key", keyId},
variable{":ip", ip},
variable{":timestamp", timestamp},
variable{":unique_id", unique_id},
variable{":result", state}).execute();
if(!res) {
logError("Could not log license validation (" + res.fmtStr() + ")");
return false;
}
{ //Log version
res = command(this->database, "INSERT INTO `history_version`(`keyId`, `unique_id`, `timestamp`, `version`) VALUES (:key, :unique_id, :time, :version)",
variable{":key", keyId},
variable{":time", timestamp},
variable{":unique_id", unique_id},
variable{":version", version}
).execute();
if(!res)
logError("Could not log license version statistic (" + res.fmtStr() + ")");
res = {};
}
return true;
}
bool LicenseManager::logStatistic(const std::string &key, const std::string& unique_id, const std::string &ip,
const ts::proto::license::PropertyUpdateRequest &data) {
result res;
auto keyId = this->id_cache->getKeyId(key);
if(keyId == 0) return false;
auto time = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
{ //Log online
res = command(this->database, "INSERT INTO `history_online`(`keyId`, `unique_id`, `timestamp`, `server`, `clients`, `web`, `music`, `queries`) VALUES (:key, :unique_id, :time, :server, :client, :web, :music, :query)",
variable{":key", keyId},
variable{":unique_id", unique_id},
variable{":time", time},
variable{":server", data.servers_online()},
variable{":client", data.clients_online()},
variable{":web", data.web_clients_online()},
variable{":music", data.bots_online()},
variable{":query", data.queries_online()}
).execute();
if(!res)
logError("Could not log license statistics (" + res.fmtStr() + ")");
res = {};
}
//SELECT * FROM `license_info` WHERE `keyId` IN (SELECT `keyId` FROM `license` WHERE `key` = '000')
{
res = command(this->database, "INSERT INTO `history_speach`(`keyId`, `unique_id`, `timestamp`, `total`, `dead`, `online`, `varianz`) VALUES (:key, :unique_id, :time, :total, :dead, :online, :varianz)",
variable{":key", keyId},
variable{":unique_id", unique_id},
variable{":time", time},
variable{":total", data.speach_total()},
variable{":dead", data.speach_dead()},
variable{":online", data.speach_online()},
variable{":varianz", data.speach_varianz()}
).execute();
if(!res)
logError("Could not log license statistics (" + res.fmtStr() + ")");
res = {};
}
return true;
}
std::deque<std::unique_ptr<LicenseManager::GlobalUserStatistics>> LicenseManager::list_statistics_user(const system_clock::time_point &begin, const system_clock::time_point &end, const milliseconds &interval) {
map<std::string, deque<unique_ptr<LicenseManager::UserStatistics>>> server_statistics;
auto timeout = hours(2) + minutes(10); //TODO timeout configurable?
auto state = command(this->database,"SELECT * FROM `history_online` WHERE `timestamp` >= :timestamp_start AND `timestamp` <= :timestamp_end ORDER BY `timestamp` ASC",
variable{":timestamp_start", duration_cast<milliseconds>(begin.time_since_epoch() - timeout).count()},
variable{":timestamp_end", duration_cast<milliseconds>(end.time_since_epoch()).count()}
).query([&server_statistics](int columns, std::string* values, std::string* names){
size_t key_id = 0;
std::string unique_id;
auto info = make_unique<LicenseManager::UserStatistics>();
for(int index = 0; index < columns; index++) {
try {
if(names[index] == "keyId")
key_id = stoull(values[index]);
else if(names[index] == "unique_id")
unique_id = values[index];
else if(names[index] == "timestamp")
info->timestamp = system_clock::time_point() + milliseconds(stoll(values[index]));
else if(names[index] == "server")
info->servers_online = stoull(values[index]);
else if(names[index] == "clients")
info->clients_online = stoull(values[index]);
else if(names[index] == "web")
info->web_clients_online = stoull(values[index]);
else if(names[index] == "music")
info->bots_online = stoull(values[index]);
else if(names[index] == "queries")
info->queries_online = stoull(values[index]);
} catch (std::exception& ex) {
logError("Failed to parse column " + names[index] + " => " + ex.what() + " (Value: " + values[index] + ")");
return 0;
}
}
if(key_id == 0) return 0;
server_statistics[to_string(key_id) + "_" + unique_id].push_back(std::move(info));
return 0;
});
if(!state) {
logError("Could not read license statistics (" + state.fmtStr() + ")");
return {};
}
std::deque<std::unique_ptr<LicenseManager::GlobalUserStatistics>> result;
system_clock::time_point current_timestamp = begin;
while(current_timestamp <= end) {
auto info = make_unique<LicenseManager::GlobalUserStatistics>();
info->timestamp = current_timestamp;
for(auto& server : server_statistics) {
while(!server.second.empty()) {
auto& first = *server.second.begin();
if(first->timestamp > current_timestamp) break; //Entry within the future
if(first->timestamp + timeout < current_timestamp) { //Entry within the past
server.second.pop_front();
continue;
}
if(server.second.size() > 1) {
auto& second = *(server.second.begin() + 1);
if(second->timestamp <= current_timestamp) {
server.second.pop_front(); //The next entry is more up 2 date
continue;
}
}
info->instance_online++;
info->queries_online += first->queries_online;
info->bots_online += first->bots_online;
info->web_clients_online += first->web_clients_online;
info->clients_online += first->clients_online;
info->servers_online += first->servers_online;
break;
}
}
result.push_back(std::move(info));
current_timestamp += interval;
}
return result;
}
std::deque<std::unique_ptr<LicenseManager::GlobalVersionsStatistic>> LicenseManager::list_statistics_version(const std::chrono::system_clock::time_point &begin, const std::chrono::system_clock::time_point &end, const std::chrono::milliseconds &interval) {
map<std::string, deque<unique_ptr<LicenseManager::GlobalVersionsStatistic>>> server_statistics;
auto timeout = hours(2) + minutes(10); //TODO timeout configurable?
auto state = command(this->database,"SELECT * FROM `history_version` WHERE `timestamp` >= :timestamp_start AND `timestamp` <= :timestamp_end ORDER BY `timestamp` ASC",
variable{":timestamp_start", duration_cast<milliseconds>(begin.time_since_epoch() - timeout).count()},
variable{":timestamp_end", duration_cast<milliseconds>(end.time_since_epoch()).count()}
).query([&server_statistics](int columns, std::string* values, std::string* names){
size_t key_id = 0;
std::string unique_id;
auto info = make_unique<LicenseManager::GlobalVersionsStatistic>();
for(int index = 0; index < columns; index++) {
try {
if(names[index] == "keyId")
key_id = stoull(values[index]);
else if(names[index] == "unique_id")
unique_id = values[index];
else if(names[index] == "timestamp")
info->timestamp = system_clock::time_point() + milliseconds(stoll(values[index]));
else if(names[index] == "version")
info->versions[values[index]] = 1;
} catch (std::exception& ex) {
logError("Failed to parse column " + names[index] + " => " + ex.what() + " (Value: " + values[index] + ")");
return 0;
}
}
if(key_id == 0) return 0;
server_statistics[to_string(key_id) + "_" + unique_id].push_back(std::move(info));
return 0;
});
if(!state) {
logError("Could not read license statistics (" + state.fmtStr() + ")");
return {};
}
std::deque<std::unique_ptr<LicenseManager::GlobalVersionsStatistic>> result;
system_clock::time_point current_timestamp = begin;
while(current_timestamp <= end) {
auto info = make_unique<LicenseManager::GlobalVersionsStatistic>();
info->timestamp = current_timestamp;
for(auto& server : server_statistics) {
while(!server.second.empty()) {
auto& first = *server.second.begin();
if(first->timestamp > current_timestamp) break; //Entry within the future
if(first->timestamp + timeout < current_timestamp) { //Entry within the past
server.second.pop_front();
continue;
}
if(server.second.size() > 1) {
auto& second = *(server.second.begin() + 1);
if(second->timestamp <= current_timestamp) {
server.second.pop_front(); //The next entry is more up 2 date
continue;
}
}
for(const auto& entry : first->versions)
info->versions[entry.first] += entry.second;
break;
}
}
result.push_back(std::move(info));
current_timestamp += interval;
}
return result;
}

View File

@ -0,0 +1,80 @@
#pragma once
#include <sql/SqlQuery.h>
#include <shared/License.h>
#include <LicenseRequest.pb.h>
namespace license {
namespace server {
class LicenseManager {
public:
class KeyIdCache {
struct CacheEntry {
std::string key;
size_t keyId;
std::chrono::system_clock::time_point age;
};
public:
KeyIdCache(LicenseManager*);
void cache_keys(const std::deque<std::string>& /* keys */);
std::string getKey(size_t keyId);
size_t getKeyId(const std::string&);
private:
int insert_entry(int, std::string*, std::string*);
LicenseManager* handle;
threads::Mutex entry_lock;
threads::Mutex entry_update_lock;
std::deque<CacheEntry*> entries;
};
struct UserStatistics {
std::chrono::system_clock::time_point timestamp;
uint64_t clients_online;
uint64_t web_clients_online;
uint64_t bots_online;
uint64_t queries_online;
uint64_t servers_online;
};
struct ExtendedUserStatistics : public UserStatistics{
std::string ip_address;
};
struct GlobalUserStatistics : public UserStatistics {
uint64_t instance_online;
};
struct GlobalVersionsStatistic {
std::chrono::system_clock::time_point timestamp;
std::map<std::string, uint32_t> versions;
};
public:
LicenseManager(sql::SqlManager* database);
~LicenseManager();
bool setup(std::string&);
bool validLicenseKey(const std::string& /* key */);
std::shared_ptr<LicenseInfo> licenseInfo(const std::string&);
std::map<std::string, std::shared_ptr<LicenseInfo>> listLicenses(int offset = 0, int limit = -1);
bool registerLicense(const std::string& /* key */, const std::shared_ptr<LicenseInfo>& /* info */, const std::string& /* issuer */);
bool updateLicenseInfo(const std::string&, const std::shared_ptr<LicenseInfo>&);
bool deleteLicense(const std::string& /* key */, bool /* full */ = false);
bool logRequest(const std::string& /* key */, const std::string& /* unique_id */, const std::string& /* ip */, const std::string& /* version */, int /* result */);
bool logStatistic(const std::string& /* key */, const std::string& /* unique_id */, const std::string& /* ip */, const ts::proto::license::PropertyUpdateRequest&);
//std::deque<std::unique_ptr<ExtendedUserStatistics>> list_statistics_user(const std::string& key, const std::chrono::system_clock::time_point& begin, const std::chrono::system_clock::time_point& end);
std::deque<std::unique_ptr<GlobalUserStatistics>> list_statistics_user(const std::chrono::system_clock::time_point& begin, const std::chrono::system_clock::time_point& end, const std::chrono::milliseconds& /* interval */);
std::deque<std::unique_ptr<GlobalVersionsStatistic>> list_statistics_version(const std::chrono::system_clock::time_point& begin, const std::chrono::system_clock::time_point& end, const std::chrono::milliseconds& /* interval */);
inline sql::SqlManager* sql() { return this->database; }
private:
std::shared_ptr<KeyIdCache> id_cache;
sql::SqlManager* database;
};
}
}

View File

@ -0,0 +1,374 @@
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <algorithm>
#include <arpa/inet.h>
#include <log/LogUtils.h>
#include <misc/endianness.h>
#include <LicenseRequest.pb.h>
#include <shared/License.h>
#include <shared/crypt.h>
#include <misc/base64.h>
#include "LicenseServer.h"
#include "crypt.h"
#include "UserManager.h"
using namespace std;
using namespace std::chrono;
using namespace license;
using namespace ts;
LicenseServer::LicenseServer(const sockaddr_in& addr,
const std::shared_ptr<server::LicenseManager>& manager,
const shared_ptr<license::stats::StatisticManager> &stats,
const shared_ptr<license::web::WebStatistics> &wstats,
const std::shared_ptr<UserManager>& user_manager) : manager(manager), statistics(stats), web_statistics(wstats), user_manager(user_manager) {
this->localAddr = new sockaddr_in{};
memcpy(this->localAddr, &addr, sizeof(addr));
}
LicenseServer::~LicenseServer() {
this->stopServer();
delete this->localAddr;
}
#define SFAIL(message) \
do { \
logError(lstream << (message) << " Message: " << errno << "/" << strerror(errno)); \
this->stopServer(); \
return false; \
} while(0)
static int enabled = 1;
static int disabled = 0;
bool LicenseServer::startServer() {
{
lock_guard lock(this->lock);
if(this->running) return false;
this->running = true;
}
fileDescriptor = socket(AF_INET, SOCK_STREAM, 0);
if (fileDescriptor < 0) SFAIL("Could not create new socket");
if(setsockopt(this->fileDescriptor, SOL_SOCKET, SO_REUSEADDR, &enabled, sizeof(enabled)) < 0) SFAIL("could not set reuse address");
if(setsockopt(this->fileDescriptor, IPPROTO_TCP, TCP_CORK, &disabled, sizeof(disabled)) < 0) SFAIL("could not set no push");
if(bind(this->fileDescriptor, (struct sockaddr *) this->localAddr, sizeof(sockaddr_in)) < 0) SFAIL("Could not bind socket on " + string(inet_ntoa(this->localAddr->sin_addr)));
if(listen(this->fileDescriptor, 32) < 0) SFAIL("Could not listen on socket");
this->evBase = event_base_new();
this->acceptEvent = event_new(this->evBase, this->fileDescriptor, EV_READ | EV_PERSIST, LicenseServer::handleEventAccept, this);
this->client_cleanup = evtimer_new(this->evBase, LicenseServer::handleEventCleanup, this);
event_add(this->acceptEvent, nullptr);
{
timeval now{1, 0};
evtimer_add(this->client_cleanup, &now);
}
evBaseDispatch = new threads::Thread(THREAD_SAVE_OPERATIONS, [&](){
signal(SIGPIPE, SIG_IGN);
event_base_dispatch(this->evBase);
});
return true;
}
void LicenseServer::stopServer() {
{
lock_guard lock(this->lock);
if(!this->running) return;
this->running = false;
}
for(const auto& client : this->getClients())
this->closeConnection(client);
if(this->acceptEvent) {
event_del(this->acceptEvent);
event_free(this->acceptEvent);
}
this->acceptEvent = nullptr;
if(this->client_cleanup) {
event_del_block(this->client_cleanup);
event_free(this->client_cleanup);
this->client_cleanup = nullptr;
}
if(this->evBase)
event_base_loopbreak(this->evBase);
if(this->evBaseDispatch)
this->evBaseDispatch->join();
delete this->evBaseDispatch;
this->evBaseDispatch = nullptr;
if(this->evBase) {
event_base_loopbreak(this->evBase); /* again for some reason */
event_base_free(this->evBase);
}
this->evBase = nullptr;
if(this->fileDescriptor != 0) {
shutdown(this->fileDescriptor, SHUT_RDWR);
close(this->fileDescriptor);
this->fileDescriptor = 0;
}
}
void LicenseServer::handleEventCleanup(int, short, void* ptrServer) {
auto server = static_cast<LicenseServer *>(ptrServer);
server->cleanup_clients();
timeval next{1, 0};
if(server->client_cleanup)
event_add(server->client_cleanup, &next);
}
//Basic IO
void LicenseServer::handleEventWrite(int fd, short, void* ptrServer) {
auto server = static_cast<LicenseServer *>(ptrServer);
auto client = server->findClient(fd);
if(!client) return;
buffer::RawBuffer* buffer = nullptr;
{
threads::MutexLock lock(client->network.lock);
buffer = TAILQ_FIRST(&client->network.writeQueue);
if(!buffer) return;
auto writtenBytes = send(fd, &buffer->buffer[buffer->index], buffer->length - buffer->index, 0);
buffer->index += writtenBytes;
if(buffer->index >= buffer->length) {
TAILQ_REMOVE(&client->network.writeQueue, buffer, tail);
delete buffer;
}
if(!TAILQ_EMPTY(&client->network.writeQueue))
event_add(client->network.writeEvent, nullptr);
}
}
void ConnectedClient::sendPacket(const protocol::packet& packet) {
packet.prepare();
auto buffer = new buffer::RawBuffer(packet.data.length() + sizeof(packet.header));
memcpy(buffer->buffer, &packet.header, sizeof(packet.header));
memcpy(&buffer->buffer[sizeof(packet.header)], packet.data.data(), packet.data.length());
if(!this->protocol.cryptKey.empty())
xorBuffer(&buffer->buffer[sizeof(packet.header)], packet.data.length(), this->protocol.cryptKey.data(), this->protocol.cryptKey.length());
{
threads::MutexLock lock(this->network.lock);
TAILQ_INSERT_TAIL(&this->network.writeQueue, buffer, tail);
}
event_add(this->network.writeEvent, nullptr);
}
void ConnectedClient::init() {
protocol.last_read = std::chrono::system_clock::now();
TAILQ_INIT(&network.writeQueue);
}
void ConnectedClient::uninit() {
{
threads::MutexLock lock(this->network.lock);
ts::buffer::RawBuffer* buffer;
while ((buffer = TAILQ_FIRST(&this->network.writeQueue))) {
TAILQ_REMOVE(&this->network.writeQueue, buffer, tail);
delete buffer;
}
}
if(network.fileDescriptor > 0) {
shutdown(this->network.fileDescriptor, SHUT_RDWR);
close(this->network.fileDescriptor);
network.fileDescriptor = 0;
}
if(this->network.readEvent) event_del(this->network.readEvent);
this->network.readEvent = nullptr;
if(this->network.writeEvent) event_del(this->network.writeEvent);
this->network.writeEvent = nullptr;
}
void LicenseServer::handleEventRead(int fd, short, void* ptrServer) {
auto server = static_cast<LicenseServer *>(ptrServer);
auto client = server->findClient(fd);
if(!client) return;
char buffer[1024];
sockaddr_in remoteAddress{};
socklen_t remoteAddressSize = sizeof(remoteAddress);
auto read = recvfrom(fd, buffer, 1024, 0, reinterpret_cast<sockaddr *>(&remoteAddress), &remoteAddressSize);
if(read < 0){
if(errno == EWOULDBLOCK) return;
logError(LOG_LICENSE_CONTROLL, "Invalid read. Disconnecting remote manager. Message: {}/{}", errno, strerror(errno));
event_del_noblock(client->network.readEvent);
server->closeConnection(client);
return;
} else if(read == 0) {
logError(LOG_LICENSE_CONTROLL, "Invalid read. Disconnecting remote client");
event_del_noblock(client->network.readEvent);
server->closeConnection(client);
return;
}
client->protocol.last_read = std::chrono::system_clock::now();
server->handleMessage(client, string(buffer, read));
}
void LicenseServer::handleEventAccept(int fd, short, void* ptrServer) {
auto server = static_cast<LicenseServer *>(ptrServer);
auto client = make_shared<ConnectedClient>();
client->init();
socklen_t client_len = sizeof(client->network.remoteAddr);
client->network.fileDescriptor = accept(fd, (struct sockaddr *)&client->network.remoteAddr, &client_len);
if(setsockopt(client->network.fileDescriptor, SOL_SOCKET, SO_REUSEADDR, &enabled, sizeof(enabled)) < 0);// CERR("could not set reuse addr");
if(setsockopt(client->network.fileDescriptor, IPPROTO_TCP, TCP_CORK, &disabled, sizeof(disabled)) < 0);// CERR("could not set no push");
if (client->network.fileDescriptor < 0) {
logCritical("Could not accept new client! (" + to_string(client->network.fileDescriptor) + "|" + to_string(errno) + "|" + strerror(errno) + ")");
return;
}
client->protocol.state = protocol::HANDSCAKE;
{
lock_guard lock(server->lock);
server->currentClients.push_back(client);
}
client->network.readEvent = event_new(server->evBase, client->network.fileDescriptor, EV_READ | EV_PERSIST, LicenseServer::handleEventRead, server);
client->network.writeEvent = event_new(server->evBase, client->network.fileDescriptor, EV_WRITE, LicenseServer::handleEventWrite, server);
event_add(client->network.readEvent, nullptr);
logMessage(lstream << "Got new client from " << inet_ntoa(client->network.remoteAddr.sin_addr));
}
void LicenseServer::disconnectClient(const std::shared_ptr<ConnectedClient>& client, const std::string &reason) {
client->sendPacket({protocol::PACKET_DISCONNECT, reason});
}
void LicenseServer::closeConnection(const std::shared_ptr<ConnectedClient> &client, bool blocking) {
if(this->evBaseDispatch && threads::self::id() == *this->evBaseDispatch) {
std::thread(std::bind(&LicenseServer::closeConnection, this, client, true)).detach();
return;
}
{
unique_lock lock(client->network.lock);
if(!TAILQ_EMPTY(&client->network.writeQueue)) {
lock.unlock();
if(!blocking) {
std::thread(std::bind(&LicenseServer::closeConnection, this, client, true)).detach();
return;
}
auto start = system_clock::now();
while(system_clock::now() - start < seconds(5)){
{
lock.lock();
if(TAILQ_EMPTY(&client->network.writeQueue)) break;
lock.unlock();
}
threads::self::sleep_for(milliseconds(5));
}
}
}
this->unregisterClient(client);
}
void LicenseServer::unregisterClient(const std::shared_ptr<ConnectedClient> &client) {
{
lock_guard lock(this->lock);
auto it = find(this->currentClients.begin(), this->currentClients.end(), client);
if(it != this->currentClients.end())
this->currentClients.erase(it);
}
client->protocol.state = protocol::UNCONNECTED;
client->uninit();
}
void LicenseServer::cleanup_clients() {
unique_lock lock(this->lock);
auto clients = this->currentClients;
lock.unlock();
for(const auto& client : clients) {
if(client->protocol.last_read + minutes(1) < system_clock::now()) {
if(client->protocol.state != protocol::DISCONNECTING && client->protocol.state != protocol::UNCONNECTED) {
this->disconnectClient(client, "timeout");
this->closeConnection(client);
client->protocol.state = protocol::DISCONNECTING;
} else {
auto it = find(this->currentClients.begin(), this->currentClients.end(), client);
if(it != this->currentClients.end())
this->currentClients.erase(it);
}
}
}
debugMessage("Client's cleaned up");
}
std::shared_ptr<ConnectedClient> LicenseServer::findClient(int fileDescriptor) {
lock_guard lock(this->lock);
for(const auto& cl : this->currentClients)
if(cl->network.fileDescriptor == fileDescriptor)
return cl;
return nullptr;
}
#define ERR(message) \
do { \
logError(lstream << message); \
this->closeConnection(client); \
return; \
} while(0)
void LicenseServer::handleMessage(shared_ptr<ConnectedClient>& client, const std::string& message) {
if(message.length() < sizeof(protocol::packet::header)) ERR("A client tried to send a invalid packet (too small header)");
protocol::packet packet{protocol::PACKET_DISCONNECT, ""};
memcpy(&packet.header, message.data(), sizeof(protocol::packet::header));
packet.data = message.substr(sizeof(protocol::packet::header), packet.header.length);
if(!client->protocol.cryptKey.empty())
xorBuffer((char*) packet.data.data(), packet.data.length(), client->protocol.cryptKey.data(), client->protocol.cryptKey.length());
bool success = false;
string error;
try {
if(packet.header.packetId == protocol::PACKET_CLIENT_HANDSHAKE) {
success = this->handleHandshake(client, packet, error);
} else if(packet.header.packetId == protocol::PACKET_DISCONNECT) {
success = this->handleDisconnect(client, packet, error);
} else if(packet.header.packetId == protocol::PACKET_CLIENT_SERVER_VALIDATION) {
success = this->handleServerValidation(client, packet, error);
} else if(packet.header.packetId == protocol::PACKET_CLIENT_PROPERTY_ADJUSTMENT) {
success = this->handlePacketPropertyUpdate(client, packet, error);
} else if(packet.header.packetId == protocol::PACKET_CLIENT_AUTH_REQUEST) {
success = this->handlePacketAuth(client, packet, error);
} else if(packet.header.packetId == protocol::PACKET_CLIENT_LICENSE_CREATE_REQUEST) {
success = this->handlePacketLicenseCreate(client, packet, error);
} else if(packet.header.packetId == protocol::PACKET_CLIENT_LIST_REQUEST) {
success = this->handlePacketLicenseList(client, packet, error);
} else if(packet.header.packetId == protocol::PACKET_CLIENT_DELETE_REQUEST) {
success = this->handlePacketLicenseDelete(client, packet, error);
} else if(packet.header.packetId == protocol::PACKET_PING) {
/* nothing todo */
} else error = "Invalid packet id!";
} catch (std::exception& ex) {
success = false;
error = "Caught exception while handle packet: " + string(ex.what());
}
if(!success) {
logError("[CLIENT][" + client->address() + "] Failed to handle packet. message: " + error);
this->disconnectClient(client, error);
}
}

View File

@ -0,0 +1,115 @@
#pragma once
#include <event.h>
#include <protocol/buffers.h>
#include <deque>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <ThreadPool/Thread.h>
#include <shared/License.h>
#include <arpa/inet.h>
#include "LicenseManager.h"
namespace license {
namespace web {
class WebStatistics;
}
namespace stats {
class StatisticManager;
}
class UserManager;
enum ClientType {
SERVER,
MANAGER
};
struct ConnectedClient {
public:
struct {
sockaddr_in remoteAddr;
int fileDescriptor = 0;
event* readEvent = nullptr;
event* writeEvent = nullptr;
threads::Mutex lock;
TAILQ_HEAD(, ts::buffer::RawBuffer) writeQueue;
} network;
struct {
protocol::RequestState state = protocol::UNCONNECTED;
std::chrono::system_clock::time_point last_read;
std::string cryptKey = "";
} protocol;
ClientType type = ClientType::SERVER;
std::string username;
std::string key;
std::string unique_identifier;
void init();
void uninit();
void sendPacket(const protocol::packet&);
inline std::string address() { return inet_ntoa(network.remoteAddr.sin_addr); }
};
class LicenseServer {
public:
explicit LicenseServer(const sockaddr_in&, const std::shared_ptr<server::LicenseManager>&, const std::shared_ptr<stats::StatisticManager>& /* stats */, const std::shared_ptr<web::WebStatistics>& /* web stats */, const std::shared_ptr<UserManager>& /* user manager */);
~LicenseServer();
bool startServer();
bool isRunning(){ return this->running; }
void stopServer();
std::shared_ptr<ConnectedClient> findClient(int fileDescriptor);
void disconnectClient(const std::shared_ptr<ConnectedClient>&, const std::string& reason);
void closeConnection(const std::shared_ptr<ConnectedClient>&, bool blocking = false);
std::deque<std::shared_ptr<ConnectedClient>> getClients() {
std::lock_guard lock(this->lock);
return currentClients;
}
private:
void unregisterClient(const std::shared_ptr<ConnectedClient>&);
void cleanup_clients();
std::shared_ptr<web::WebStatistics> web_statistics;
std::shared_ptr<stats::StatisticManager> statistics;
std::shared_ptr<server::LicenseManager> manager;
std::shared_ptr<UserManager> user_manager;
bool running = false;
std::mutex lock;
std::deque<std::shared_ptr<ConnectedClient>> currentClients;
sockaddr_in* localAddr = nullptr;
int fileDescriptor = 0;
event_base* evBase = nullptr;
event* acceptEvent = nullptr;
event* client_cleanup = nullptr;
threads::Thread* evBaseDispatch = nullptr;
static void handleEventCleanup(int, short, void*);
static void handleEventAccept(int, short, void*);
static void handleEventRead(int, short, void*);
static void handleEventWrite(int, short, void*);
void handleMessage(std::shared_ptr<ConnectedClient>&, const std::string&);
bool handleDisconnect(std::shared_ptr<ConnectedClient>&, protocol::packet&, std::string& error);
bool handleHandshake(std::shared_ptr<ConnectedClient>&, protocol::packet&, std::string& error);
bool handleServerValidation(std::shared_ptr<ConnectedClient> &, protocol::packet &, std::string &error);
bool handlePacketPropertyUpdate(std::shared_ptr<ConnectedClient> &, protocol::packet &, std::string &error);
bool handlePacketAuth(std::shared_ptr<ConnectedClient> &, protocol::packet &, std::string &error);
bool handlePacketLicenseCreate(std::shared_ptr<ConnectedClient> &, protocol::packet &, std::string &error);
bool handlePacketLicenseList(std::shared_ptr<ConnectedClient> &, protocol::packet &, std::string &error);
bool handlePacketLicenseDelete(std::shared_ptr<ConnectedClient> &, protocol::packet &, std::string &error);
};
}

View File

@ -0,0 +1,338 @@
#include <misc/endianness.h>
#include <misc/base64.h>
#include <log/LogUtils.h>
#include <LicenseManager.pb.h>
#include <shared/License.h>
#include "LicenseRequest.pb.h"
#include "shared/License.h"
#include "LicenseServer.h"
#include "WebAPI.h"
#include "StatisticManager.h"
#include "UserManager.h"
using namespace std;
using namespace std::chrono;
using namespace license;
using namespace ts;
inline void generate(char* buffer, size_t length){
for(int index = 0; index < length; index++)
buffer[index] = rand();
}
#define TEST_PROTOCOL_STATE(expected) \
if(client->protocol.state != protocol::expected) { \
error = "invalid protocol state"; \
return false; \
}
#define ENSURE_PACKET_SIZE(expected) \
if(packet.data.length() < (expected)) { \
error = "too small packet!"; \
return false; \
}
#define PARSE_PROTO(class, var) \
ts::proto::license::class var; \
if(!var.ParseFromString(packet.data)) { \
error = "invalid data!"; \
return false; \
}
#define CRYPT_KEY_LENGTH 32
bool LicenseServer::handleHandshake(shared_ptr<ConnectedClient>& client, protocol::packet& packet, std::string &error) {
TEST_PROTOCOL_STATE(HANDSCAKE);
ENSURE_PACKET_SIZE(4);
if((uint8_t) packet.data[0] != 0xC0 || (uint8_t) packet.data[1] != 0xFF || (uint8_t) packet.data[2] != 0xEE) {
error = "invalid magic!";
return false;
}
if((uint8_t) packet.data[3] != LICENSE_PROT_VERSION) {
error = "invalid version!";
return false;
}
bool manager = false;
if(packet.data.length() >= 4 && (uint8_t) packet.data[4] == 1) {
manager = true;
//Its a manager!
}
char buffer_cryptkey[CRYPT_KEY_LENGTH];
generate(buffer_cryptkey, CRYPT_KEY_LENGTH);
uint8_t buffer[128];
size_t buffer_index = 0;
buffer[buffer_index++] = 0xAF;
buffer[buffer_index++] = 0xFE;
buffer[buffer_index++] = LICENSE_PROT_VERSION;
le2be16(CRYPT_KEY_LENGTH, buffer, buffer_index, &buffer_index);
memcpy(&buffer[buffer_index], buffer_cryptkey, CRYPT_KEY_LENGTH);
buffer_index += CRYPT_KEY_LENGTH;
if(manager)
buffer[buffer_index++] = 0x01; //Manager accepted
client->sendPacket({protocol::PACKET_SERVER_HANDSHAKE, string((char*) buffer, buffer_index)});
client->protocol.cryptKey = string(buffer_cryptkey, 32);
if(manager) {
client->protocol.state = protocol::MANAGER_AUTHORIZATION;
client->type = ClientType::MANAGER;
} else {
client->protocol.state = protocol::SERVER_VALIDATION;
client->type = ClientType::SERVER;
}
return true;
}
bool LicenseServer::handleDisconnect(shared_ptr<ConnectedClient>& client, protocol::packet& packet, std::string &error) {
logMessage("[CLIENT][" + client->address() + "] Remote disconnect. Reason: " + packet.data);
this->closeConnection(client);
return true;
}
inline void fill_info(proto::license::LicenseInfo* proto, const shared_ptr<LicenseInfo>& info, const std::string& key) {
proto->set_key(key);
proto->set_username(info->username);
proto->set_first_name(info->first_name);
proto->set_last_name(info->last_name);
proto->set_email(info->email);
proto->set_type(info->type);
proto->set_created(duration_cast<milliseconds>(info->creation.time_since_epoch()).count());
proto->set_begin(duration_cast<milliseconds>(info->start.time_since_epoch()).count());
proto->set_end(duration_cast<milliseconds>(info->end.time_since_epoch()).count());
}
std::string string_to_hex(const std::string& input)
{
static const char* const lut = "0123456789ABCDEF";
size_t len = input.length();
std::string output;
output.reserve(2 * len);
for (size_t i = 0; i < len; ++i)
{
const unsigned char c = input[i];
output.push_back(lut[c >> 4]);
output.push_back(lut[c & 15]);
}
return output;
}
bool LicenseServer::handleServerValidation(shared_ptr<ConnectedClient> &client, protocol::packet &packet, std::string &error) {
TEST_PROTOCOL_STATE(SERVER_VALIDATION);
PARSE_PROTO(ServerValidation, pkt);
shared_ptr<License> remoteLicense = nullptr;
if(pkt.licensed() && !pkt.has_license()) {
//TODO shutdown server
}
if(!pkt.has_info()) {
error = "invalid data or missing data";
return false;
}
if(pkt.has_license()){ //Client has license
remoteLicense = readLocalLicence(pkt.license(), error);
if(!remoteLicense) {
error = "Could not read remote key: " + error;
return false;
};
logMessage(LOG_GENERAL, "[CLIENT][{}] Got remote license. Registered to {}. Key: {} (0x{})", client->address(), remoteLicense->owner(), base64::encode(remoteLicense->key()), string_to_hex(remoteLicense->key()));
client->key = remoteLicense->key();
} else { }
if(pkt.licensed() && !pkt.has_license_info()) {
error = "Invalid content!";
return false;
}
logMessage(LOG_GENERAL, "[CLIENT][{}] Got some server information. TeaSpeak-Version: {} uname: {}", client->address(), pkt.info().version(), pkt.info().uname());
ts::proto::license::LicenseResponse response;
//Forces
client->unique_identifier = pkt.info().has_unique_id() ? pkt.info().unique_id() : client->address();
if(remoteLicense && pkt.licensed()) {
auto info = this->manager->licenseInfo(remoteLicense->key());
if(!info) {
response.set_valid(false);
/*
logMessage(LOG_GENERAL, "[CLIENT][{}] Unknown license! Adding it to database!", client->address());
auto db_info = make_shared<LicenseInfo>();
db_info->start = system_clock::now();
db_info->end = remoteLicense->end();
db_info->last_name = "unknown";
db_info->first_name = "unknown";
db_info->username = remoteLicense->owner();
db_info->email = "unknonw@unknown";
db_info->creation = system_clock::now();
db_info->type = remoteLicense->data.type;
this->manager->registerLicense(remoteLicense->key(), db_info, "teaforo-fix");
info = this->manager->licenseInfo(remoteLicense->key());
if(!info) {
error = "could not insert key!";
return false;
}
*/
logMessage(LOG_GENERAL, "[CLIENT][{}] Remote license hasn't been found in database. Shutting down server!", client->address());
} else {
if(info->deleted) {
response.set_valid(false);
logMessage(LOG_GENERAL, "[CLIENT][{}] Remote license has been deleted! Shutting down server!", client->address());
} else {
fill_info(response.mutable_license_info(), info, remoteLicense->data.licenceKey);
response.set_valid(info->isValid());
}
}
this->manager->logRequest(remoteLicense->key(), client->unique_identifier, client->address(), pkt.info().version(), response.valid());
} else {
response.set_valid(true);
}
response.mutable_blacklist()->set_state(ts::proto::license::VALID);
client->sendPacket(protocol::packet{protocol::PACKET_SERVER_VALIDATION_RESPONSE, response});
client->protocol.state = protocol::PROPERTY_ADJUSTMENT;
return true;
}
bool LicenseServer::handlePacketPropertyUpdate(shared_ptr<ConnectedClient> &client, protocol::packet &packet, std::string &error) {
TEST_PROTOCOL_STATE(PROPERTY_ADJUSTMENT);
PARSE_PROTO(PropertyUpdateRequest, pkt);
logMessage("[CLIENT][" + client->address() + "] Got server statistics:");
logMessage("[CLIENT][" + client->address() + "] Spoken total : " + to_string(pkt.speach_total()));
logMessage("[CLIENT][" + client->address() + "] Spoken dead : " + to_string(pkt.speach_dead()));
logMessage("[CLIENT][" + client->address() + "] Spoken online : " + to_string(pkt.speach_online()));
logMessage("[CLIENT][" + client->address() + "] Spoken varianz : " + to_string(pkt.speach_varianz()));
logMessage("[CLIENT][" + client->address() + "] -------------------------------");
logMessage("[CLIENT][" + client->address() + "] Users online : " + to_string(pkt.clients_online()));
logMessage("[CLIENT][" + client->address() + "] Web Users online : " + to_string(pkt.web_clients_online()));
logMessage("[CLIENT][" + client->address() + "] Queries online : " + to_string(pkt.queries_online()));
logMessage("[CLIENT][" + client->address() + "] Bots online : " + to_string(pkt.bots_online()));
logMessage("[CLIENT][" + client->address() + "] Servers : " + to_string(pkt.servers_online()));
this->manager->logStatistic(client->key, client->unique_identifier, client->address(), pkt);
//TODO test stuff!
ts::proto::license::PropertyUpdateResponse response;
response.set_accepted(true);
response.set_reset_speach(pkt.speach_total() < 0);
response.set_speach_total_remote(pkt.speach_total());
response.set_speach_varianz_corrector(0);
client->sendPacket(protocol::packet{protocol::PACKET_SERVER_PROPERTY_ADJUSTMENT, response});
this->disconnectClient(client, "finished");
if(this->statistics)
this->statistics->reset_cache_general();
if(this->web_statistics)
this->web_statistics->async_broadcast_notify_general_update();
return true;
}
bool LicenseServer::handlePacketAuth(shared_ptr<ConnectedClient> &client, protocol::packet &packet, std::string &error) {
TEST_PROTOCOL_STATE(MANAGER_AUTHORIZATION);
PARSE_PROTO(AuthorizationRequest, pkt);
logMessage("[MANAGER][" + client->address() + "] Got login. User: " + pkt.username() + " Password: " + pkt.password());
ts::proto::license::AuthorizationResponse response;
response.set_success(false);
if(this->user_manager) {
auto user_account = this->user_manager->find_user(pkt.username());
if(user_account) {
if(user_account->verify_password(pkt.password())) {
switch(user_account->status()) {
case User::Status::ACTIVE:
response.set_success(true);
break;
case User::Status::BANNED:
response.set_message("you have been banned");
break;
case User::Status::DISABLED:
response.set_message("you have been disabled");
break;
default:
response.set_message("Your account hasn't been activated");
break;
}
}
}
} else {
response.set_success(pkt.password() == "HelloWorld");
}
if(!response.has_message() && !response.success())
response.set_message("username or password mismatch");
if(response.success()) {
logMessage("[MANAGER][" + client->address() + "] Got succeeded user login. User: " + pkt.username() + " Password: " + pkt.password());
client->username = pkt.username();
client->protocol.state = protocol::MANAGER_CONNECTED;
} else
logMessage("[MANAGER][" + client->address() + "] Got failed user login. User: " + pkt.username() + " Password: " + pkt.password());
client->sendPacket(protocol::packet{protocol::PACKET_SERVER_AUTH_RESPONSE, response});
return true;
}
bool LicenseServer::handlePacketLicenseCreate(shared_ptr<ConnectedClient> &client, protocol::packet &packet, std::string &error) {
TEST_PROTOCOL_STATE(MANAGER_CONNECTED);
PARSE_PROTO(LicenseCreateRequest, pkt);
logMessage(LOG_GENERAL, "[MANAGER][" + client->address() + "] Register new license to {} {} ({}). E-Mail: {}", pkt.issuer_first_name(), pkt.issuer_last_name(), pkt.issuer_username(), pkt.issuer_email());
ts::proto::license::LicenseCreateResponse response;
auto db_info = make_shared<LicenseInfo>();
db_info->start = system_clock::time_point() + milliseconds(pkt.begin());
db_info->end = system_clock::time_point() + milliseconds(pkt.end());
db_info->last_name = pkt.issuer_last_name();
db_info->first_name = pkt.issuer_first_name();
db_info->username = pkt.issuer_username();
db_info->email = pkt.issuer_email();
db_info->creation = system_clock::now();
db_info->type = static_cast<LicenseType>(pkt.type());
auto license = license::createLocalLicence(db_info->type, db_info->end, db_info->first_name + db_info->last_name);
auto parsed_license = license::readLocalLicence(license, error);
if(!parsed_license) {
response.set_error("failed to register license (parse)");
} else {
if(!this->manager->registerLicense(parsed_license->key(), db_info, client->username)) {
response.set_error("failed to register license");
} else {
fill_info(response.mutable_license(), db_info, parsed_license->key());
response.set_exported_key(license);
}
}
client->sendPacket(protocol::packet{protocol::PACKET_SERVER_LICENSE_CREATE_RESPONSE, response});
return true;
}
bool LicenseServer::handlePacketLicenseList(shared_ptr<ConnectedClient> &client, protocol::packet &packet, std::string &error) {
TEST_PROTOCOL_STATE(MANAGER_CONNECTED);
PARSE_PROTO(LicenseListRequest, pkt);
proto::license::LicenseListResponse response;
response.set_end(false);
for(const auto& info : this->manager->listLicenses(pkt.offset(), pkt.count())) {
auto entry = response.add_entries();
fill_info(entry, info.second, info.first);
}
client->sendPacket(protocol::packet{protocol::PACKET_SERVER_LIST_RESPONSE, response});
return true;
}
bool LicenseServer::handlePacketLicenseDelete(shared_ptr<ConnectedClient> &client, protocol::packet &packet, std::string &error) {
TEST_PROTOCOL_STATE(MANAGER_CONNECTED);
PARSE_PROTO(LicenseDeleteRequest, pkt);
proto::license::LicenseDeleteResponse response;
response.set_succeed(this->manager->deleteLicense(pkt.key(), pkt.full()));
client->sendPacket(protocol::packet{protocol::PACKET_CLIENT_DELETE_RESPONSE, response});
return true;
}

View File

@ -0,0 +1,201 @@
//
// Created by wolverindev on 04.09.18.
//
#include <sql/SqlQuery.h>
#include <misc/std_unique_ptr.h>
#include "StatisticManager.h"
#include "LicenseManager.h"
using namespace std;
using namespace std::chrono;
using namespace license;
using namespace license::server;
using namespace license::stats;
std::chrono::milliseconds HistoryStatistics::time_period(license::stats::HistoryStatistics::HistoryType type) {
switch (type) {
case HistoryType::LAST_DAY:
case HistoryType::DAY_YESTERDAY:
case HistoryType::DAY_7DAYS_AGO:
return minutes(15);
case HistoryType::LAST_WEEK:
return hours(1);
case HistoryType::LAST_MONTH:
case HistoryType::LAST_HALF_YEAR:
default:
return hours(2);
}
}
std::chrono::milliseconds HistoryStatistics::cache_timeout(license::stats::HistoryStatistics::HistoryType type) {
switch (type) {
case HistoryType::LAST_DAY:
case HistoryType::DAY_YESTERDAY:
case HistoryType::DAY_7DAYS_AGO:
return minutes(15);
case HistoryType::LAST_WEEK:
return hours(1);
case HistoryType::LAST_MONTH:
return hours(2);
case HistoryType::LAST_HALF_YEAR:
default:
return hours(8);
}
}
std::chrono::milliseconds HistoryStatistics::type_duration(license::stats::HistoryStatistics::HistoryType type) {
switch (type) {
case HistoryType::LAST_DAY:
case HistoryType::DAY_YESTERDAY:
case HistoryType::DAY_7DAYS_AGO:
return hours(24);
case HistoryType::LAST_WEEK:
return hours(24) * 7;
case HistoryType::LAST_MONTH:
return hours(24) * 32;
case HistoryType::LAST_HALF_YEAR:
return hours(24) * 32 * 6;
default:
return hours(24);
}
}
system_clock::time_point HistoryStatistics::align_type(license::stats::HistoryStatistics::HistoryType type, const std::chrono::system_clock::time_point &tp) {
switch (type) {
case HistoryType::LAST_DAY:
case HistoryType::DAY_YESTERDAY:
case HistoryType::DAY_7DAYS_AGO:
return system_clock::time_point() + minutes(duration_cast<minutes>(tp.time_since_epoch()).count());
case HistoryType::LAST_WEEK:
case HistoryType::LAST_MONTH:
case HistoryType::LAST_HALF_YEAR:
default:
return system_clock::time_point() + hours(duration_cast<hours>(tp.time_since_epoch()).count());
}
}
StatisticManager::StatisticManager(const std::shared_ptr<license::server::LicenseManager> &manager) : license_manager(manager) {}
StatisticManager::~StatisticManager() {}
struct GeneralStatisticEntry {
std::chrono::system_clock::time_point age;
string unique_id = "";
uint64_t key_id = 0;
uint64_t servers = 0;
uint64_t clients = 0;
uint64_t bots = 0;
};
void StatisticManager::reset_cache_general() {
lock_guard<recursive_mutex> lock(this->_general_statistics_lock);
this->_general_statistics = nullptr;
}
void parse_general_entry(deque<unique_ptr<GeneralStatisticEntry>>& entries, bool unique, int length, string* values, string* names) {
auto entry = make_unique<GeneralStatisticEntry>();
for(int index = 0; index < length; index++) {
if(names[index] == "keyId") {
entry->key_id = stoull(values[index]);
} else if(names[index] == "timestamp") {
entry->age = system_clock::time_point() + milliseconds(stoll(values[index]));
} else if(names[index] == "server") {
entry->servers = stoull(values[index]);
} else if(names[index] == "clients") {
entry->clients = stoull(values[index]);
} else if(names[index] == "music") {
entry->bots = stoull(values[index]);
} else if(names[index] == "unique_id")
entry->unique_id = values[index];
}
if(unique) {
for(auto& e : entries) {
if(e->key_id == entry->key_id && e->unique_id == entry->unique_id) {
if(e->age < entry->age) {
entries.erase(find(entries.begin(), entries.end(), e));
break;
} else {
return;
}
}
}
}
entries.push_back(std::move(entry));
}
std::shared_ptr<GeneralStatistics> StatisticManager::general_statistics() {
unique_lock<recursive_mutex> lock(this->_general_statistics_lock);
if(this->_general_statistics && system_clock::now() < this->_general_statistics->age + seconds(300)) return this->_general_statistics;
lock.unlock();
unique_lock create_lock(this->_general_statistics_generate_lock);
lock.lock();
if(this->_general_statistics && system_clock::now() < this->_general_statistics->age + seconds(300)) return this->_general_statistics;
lock.unlock();
deque<unique_ptr<GeneralStatisticEntry>> entries;
auto result = sql::command(this->license_manager->sql(), "SELECT `keyId`, `unique_id`, `timestamp`,`server`,`clients`,`music` FROM `history_online` WHERE `timestamp` > :time ORDER BY `timestamp` ASC",
variable{":time", duration_cast<milliseconds>(system_clock::now().time_since_epoch() - hours(2) - minutes(10)).count()}) //10min as buffer
.query(std::function<decltype(parse_general_entry)>(parse_general_entry), entries, true);
auto stats = make_shared<GeneralStatistics>();
for(auto& entry : entries) {
stats->bots += entry->bots;
stats->clients += entry->clients;
stats->servers += entry->servers;
stats->instances++;
}
stats->age = system_clock::now();
lock.lock();
this->_general_statistics = stats;
lock.unlock();
create_lock.unlock();
return stats;
}
std::shared_ptr<HistoryStatistics> StatisticManager::history(license::stats::HistoryStatistics::HistoryType type) {
lock_guard<recursive_mutex> lock(this->_history_locks[type]);
auto current_time = system_clock::now();
auto& entry = this->_history[type];
if(entry && entry->evaluated + HistoryStatistics::cache_timeout(type) > current_time)
return entry;
entry = make_shared<HistoryStatistics>();
entry->period = HistoryStatistics::time_period(type);
entry->begin = HistoryStatistics::align_type(type, current_time - HistoryStatistics::type_duration(type));
entry->end = HistoryStatistics::align_type(type, current_time);
if(type == HistoryStatistics::DAY_YESTERDAY || type == HistoryStatistics::DAY_7DAYS_AGO) {
auto& reference = this->_history[HistoryStatistics::LAST_DAY];
if(reference) {
entry->begin = reference->begin;
entry->end = reference->end;
entry->evaluated = reference->evaluated;
}
if(type == HistoryStatistics::DAY_YESTERDAY) {
entry->begin -= hours(24);
entry->end -= hours(24);
} else if(type == HistoryStatistics::DAY_7DAYS_AGO) {
entry->begin -= hours(24) * 7;
entry->end -= hours(24) * 7;
}
}
auto statistics = this->license_manager->list_statistics_user(entry->begin, entry->end, entry->period);
entry->statistics = std::move(statistics);
if(entry->evaluated.time_since_epoch().count() == 0)
entry->evaluated = current_time;
if(type == HistoryStatistics::LAST_DAY) {
this->history(HistoryStatistics::DAY_YESTERDAY);
this->history(HistoryStatistics::DAY_7DAYS_AGO);
}
return entry;
}

View File

@ -0,0 +1,61 @@
#pragma once
#include <mutex>
#include <memory>
#include <chrono>
#include "LicenseManager.h"
namespace license {
namespace stats {
struct GeneralStatistics {
std::chrono::system_clock::time_point age;
uint64_t instances = 0;
uint64_t servers = 0;
uint64_t clients = 0;
uint64_t bots = 0;
};
struct HistoryStatistics {
enum HistoryType {
LAST_DAY,
DAY_YESTERDAY,
DAY_7DAYS_AGO,
LAST_WEEK,
LAST_MONTH,
LAST_HALF_YEAR
};
static std::chrono::system_clock::time_point align_type(HistoryType type, const std::chrono::system_clock::time_point&);
static std::chrono::milliseconds time_period(HistoryType type);
static std::chrono::milliseconds cache_timeout(HistoryType type);
static std::chrono::milliseconds type_duration(HistoryType type);
std::chrono::system_clock::time_point evaluated;
std::chrono::system_clock::time_point begin;
std::chrono::system_clock::time_point end;
std::chrono::milliseconds period;
HistoryType type;
std::deque<std::unique_ptr<server::LicenseManager::GlobalUserStatistics>> statistics;
};
class StatisticManager {
public:
explicit StatisticManager(const std::shared_ptr<server::LicenseManager>& /* manager */);
virtual ~StatisticManager();
void reset_cache_general();
std::shared_ptr<GeneralStatistics> general_statistics();
std::shared_ptr<HistoryStatistics> history(HistoryStatistics::HistoryType);
private:
std::shared_ptr<server::LicenseManager> license_manager;
std::recursive_mutex _general_statistics_lock;
std::recursive_mutex _general_statistics_generate_lock;
std::shared_ptr<GeneralStatistics> _general_statistics;
std::map<HistoryStatistics::HistoryType, std::recursive_mutex> _history_locks;
std::map<HistoryStatistics::HistoryType, std::shared_ptr<HistoryStatistics>> _history;
};
}
}

View File

@ -0,0 +1,64 @@
#include "UserManager.h"
#include <misc/base64.h>
#include <misc/digest.h>
#include <log/LogUtils.h>
using namespace license;
using namespace std;
UserManager::UserManager(sql::SqlManager *db) : database(db) {}
UserManager::~UserManager() {}
std::shared_ptr<User> UserManager::find_user(const std::string &username) {
unique_lock lock(this->loaded_user_lock);
for(const auto& user : this->loaded_user)
if(user->username() == username)
return user;
lock.unlock();
lock_guard load_lock(this->load_user_lock);
lock.lock();
for(const auto& user : this->loaded_user)
if(user->username() == username)
return user;
lock.unlock();
std::shared_ptr<User> user;
sql::command(this->database, "SELECT `username`, `password_hash`, `status`, `owner` FROM `users` WHERE `username` = :username", variable{":username", username}).query([&](int length, string* values, string* key) {
string username, password_hash, owner;
int status;
for(int index = 0; index < length; index++) {
try {
if(key[index] == "username")
username = values[index];
else if(key[index] == "password_hash")
password_hash = values[index];
else if(key[index] == "status")
status = (User::Status::value) stoll(values[index]);
else if(key[index] == "owner")
owner = values[index];
} catch(std::exception& ex) {
logError(LOG_LICENSE_CONTROLL, "Failed to load user {}. Failed to parse field {} ({})", username, key[index], values[index]);
return;
}
}
user = make_shared<User>(this, username, password_hash, status);
user->_owner = owner;
lock.lock();
this->loaded_user.push_back(user);
lock.unlock();
});
return user;
}
User::User(license::UserManager *handle, const std::string &name, const std::string &password, license::User::Status::value status) : handle(handle), _username(name), _password_hash(password), _status(status) {}
bool User::verify_password(const std::string &password) {
auto hashed_password = base64::encode(digest::sha1(password));
return this->_password_hash == hashed_password;
}

View File

@ -0,0 +1,68 @@
#pragma once
#include <sql/SqlQuery.h>
namespace license {
class UserManager;
class User;
class UserPermissions {
friend class User;
public:
struct Permission {
static constexpr auto LICENSE_CREATE = "license_create";
static constexpr auto LICENSE_CREATE_FROM_TEMPLATE = "license_create_from_template";
};
private:
User* handle;
};
class User {
friend class UserManager;
public:
struct Status {
enum value {
ACTIVE,
DISABLED,
BANNED
};
};
User(UserManager* /* manager */, const std::string& /* username */,const std::string& /* password hash */, Status::value /* status */);
double balance();
inline std::string username() { return this->_username; }
inline std::string password_hash() { return this->_password_hash; }
inline Status::value status() { return this->_status; }
bool verify_password(const std::string& /* password */);
private:
UserManager* handle;
Status::value _status;
std::string _username;
std::string _password_hash;
std::string _owner;
double _balance = 0;
};
class UserOffer {
public:
private:
};
class UserManager {
public:
explicit UserManager(sql::SqlManager* /* database */);
virtual ~UserManager();
std::shared_ptr<User> find_user(const std::string& /* name */);
private:
std::mutex load_user_lock;
std::mutex loaded_user_lock;
std::deque<std::shared_ptr<User>> loaded_user;
sql::SqlManager* database;
};
}

562
license/server/WebAPI.cpp Normal file
View File

@ -0,0 +1,562 @@
//
// Created by wolverindev on 04.09.18.
//
#include <csignal>
#include <misc/std_unique_ptr.h>
#include <misc/net.h>
#include <misc/endianness.h>
#include <json/json.h>
#include "StatisticManager.h"
#include "WebAPI.h"
using namespace license;
using namespace license::server;
using namespace license::web;
using namespace ts::ssl;
using namespace std;
using namespace std::chrono;
WebStatistics::WebStatistics(const shared_ptr<LicenseManager> &manager, const std::shared_ptr<stats::StatisticManager>& stats) : license_manager(manager), statistics_manager(stats) {}
WebStatistics::~WebStatistics() {}
#define SFAIL(message) \
do { \
error = message; \
this->stop(); \
return false; \
} while(0)
static int enabled = 1;
static int disabled = 0;
bool WebStatistics::start(std::string &error, uint16_t port, const std::shared_ptr<ts::ssl::SSLContext> &ssl) {
{
std::lock_guard<std::recursive_mutex> lock(this->running_lock);
if(this->_running) return false;
this->_running = true;
}
this->ssl = ssl;
{
this->socket.local_address = make_unique<sockaddr_in>();
memset(this->socket.local_address.get(), 0, sizeof(sockaddr_in));
this->socket.local_address->sin_family = AF_INET;
this->socket.local_address->sin_addr.s_addr = INADDR_ANY;
this->socket.local_address->sin_port = htons(port);
}
this->socket.file_descriptor = ::socket(AF_INET, SOCK_STREAM, 0);
if (this->socket.file_descriptor < 0) SFAIL("Could not create new socket");
if(setsockopt(this->socket.file_descriptor, SOL_SOCKET, SO_REUSEADDR, &enabled, sizeof(enabled)) < 0) SFAIL("could not set reuse address");
if(setsockopt(this->socket.file_descriptor, IPPROTO_TCP, TCP_CORK, &disabled, sizeof(disabled)) < 0) SFAIL("could not set no push");
if(bind(this->socket.file_descriptor, (struct sockaddr *) this->socket.local_address.get(), sizeof(sockaddr_in)) < 0) SFAIL("Could not bind socket on " + string(inet_ntoa(this->socket.local_address->sin_addr)));
if(listen(this->socket.file_descriptor, 32) < 0) SFAIL("Could not listen on socket");
this->socket.event_base = event_base_new();
this->socket.event_accept = event_new(this->socket.event_base, this->socket.file_descriptor, EV_READ | EV_PERSIST, WebStatistics::handleEventAccept, this);
event_add(this->socket.event_accept, nullptr);
this->socket.event_base_dispatch = make_unique<threads::Thread>(THREAD_SAVE_OPERATIONS, [&](){
signal(SIGABRT, SIG_IGN);
event_base_dispatch(this->socket.event_base);
});
return true;
}
void WebStatistics::stop() {
{
std::lock_guard<std::recursive_mutex> lock(this->running_lock);
if(!this->_running) return;
this->_running = false;
}
for(const auto& client : this->get_clients())
this->close_connection(client);
if(this->socket.event_accept) {
event_del(this->socket.event_accept);
event_free(this->socket.event_accept);
}
this->socket.event_accept = nullptr;
if(this->socket.event_base)
event_base_loopbreak(this->socket.event_base);
if(this->socket.event_base_dispatch)
this->socket.event_base_dispatch->join(seconds(5));
this->socket.event_base_dispatch = nullptr;
if(this->socket.event_base) {
event_base_free(this->socket.event_base);
this->socket.event_base = nullptr;
}
if(this->socket.file_descriptor != 0) {
shutdown(this->socket.file_descriptor, SHUT_RDWR);
close(this->socket.file_descriptor);
this->socket.file_descriptor = 0;
}
}
void WebStatistics::initialize_client(const std::shared_ptr<license::web::WebStatistics::Client> &client) {
weak_ptr<Client> weak_client = client;
auto send_message = [&](const std::shared_ptr<Client>& client, const pipes::buffer_view& message) {
{
std::lock_guard<std::recursive_mutex> lock(client->execute_lock);
client->buffer_write.push_back(message.string());
}
if(client->event_write)
event_add(client->event_write, nullptr);
};
{ //WebSocket and SSL setup
client->pipe_websocket = make_unique<pipes::WebSocket>();
client->pipe_websocket->direct_process(pipes::PROCESS_DIRECTION_IN, true);
client->pipe_websocket->direct_process(pipes::PROCESS_DIRECTION_OUT, true);
client->pipe_websocket->callback_error([&, weak_client](int code, const std::string &reason) {
auto _client = weak_client.lock();
if(!_client) return;
logError(LOG_LICENSE_WEB, "[{}][WS] Catched an error. code: {} reason: {}.", _client->client_prefix(), code, reason);
logError(LOG_LICENSE_WEB, "[{}][WS] Disconnecting client", _client->client_prefix());
if(_client->pipe_websocket && _client->pipe_websocket->getState() == pipes::CONNECTED) _client->pipe_websocket->disconnect(1100, "Catched a server sided error");
else this->close_connection(_client);
});
client->pipe_websocket->callback_write([weak_client, send_message](const pipes::buffer_view& message) {
auto _client = weak_client.lock();
if(!_client) return;
if(_client->pipe_ssl)
_client->pipe_ssl->send(message);
else send_message(_client, message);
});
client->pipe_websocket->callback_data([&, weak_client](const pipes::WSMessage& message) {
auto _client = weak_client.lock();
if(!_client) return;
this->handle_message(_client, message); //TODO if return false error handling!
});
client->pipe_websocket->on_connect = [&, weak_client] {
auto _client = weak_client.lock();
if(!_client) return;
logMessage(LOG_LICENSE_WEB, "[{}] WebSocket handshake completed!", _client->client_prefix());
};
client->pipe_websocket->on_disconnect = [&, weak_client](const std::string& reason) {
auto _client = weak_client.lock();
if(!_client) return;
logMessage(LOG_LICENSE_WEB, "[{}] Remote connection disconnected ({} | {})", _client->client_prefix(), reason.length() >= 2 ? be2le16(reason.data()) : -1, reason.length() > 2 ? reason.substr(2) : "");
this->close_connection(_client);
};
client->pipe_websocket->callback_invalid_request = [&, weak_client](const http::HttpRequest& request, http::HttpResponse& response) {
auto _client = weak_client.lock();
if(!_client) return;
auto lmethod = request.method;
transform(lmethod.begin(), lmethod.end(), lmethod.begin(), ::tolower);
if(lmethod == "get" && !request.parameters["type"].empty())
this->handle_request(_client, request, response);
};
client->pipe_websocket->initialize();
//FIXME Setup ssl
}
{
client->pipe_ssl = make_unique<pipes::SSL>();
client->pipe_ssl->direct_process(pipes::PROCESS_DIRECTION_IN, true);
client->pipe_ssl->direct_process(pipes::PROCESS_DIRECTION_OUT, true);
client->pipe_ssl->callback_error([&, weak_client](int code, const std::string &reason) {
auto _client = weak_client.lock();
if(!_client) return;
logError(LOG_LICENSE_WEB, "[{}][SSL] Catched an error. code: {} reason: {}.", _client->client_prefix(), code, reason);
logError(LOG_LICENSE_WEB, "[{}][SSL] Disconnecting client", _client->client_prefix());
if(_client->pipe_websocket && _client->pipe_websocket->getState() == pipes::CONNECTED) _client->pipe_websocket->disconnect(1100, "Catched a server sided error (SSL)");
else this->close_connection(_client);
});
client->pipe_ssl->callback_write([weak_client, send_message](const pipes::buffer_view& message) {
auto _client = weak_client.lock();
if(!_client) return;
send_message(_client, message);
});
client->pipe_ssl->callback_data([&, weak_client](const pipes::buffer_view& message) {
auto _client = weak_client.lock();
if(!_client) return;
if(_client->pipe_websocket) _client->pipe_websocket->process_incoming_data(message);
});
{
auto options = make_shared<pipes::SSL::Options>();
options->type = pipes::SSL::SERVER;
options->context_method = TLS_method();
options->free_unused_keypairs = false; /* we dont want our keys get removed */
options->default_keypair({this->ssl->privateKey, this->ssl->certificate});
if(!client->pipe_ssl->initialize(options)) {
logError(LOG_LICENSE_WEB, "[{}][SSL] Failed to setup ssl! Disconnecting client", client->client_prefix());
this->close_connection(client);
}
}
}
}
void WebStatistics::handleEventAccept(int fd, short, void *ptr_server) {
auto server = (WebStatistics*) ptr_server;
auto client = make_shared<Client>();
{ //Network accept
auto address = make_unique<sockaddr_in>();
auto address_length = (socklen_t) sizeof(*address);
client->file_descriptor = accept(fd, (struct sockaddr *) address.get(), &address_length);
if (client->file_descriptor < 0) {
logCritical(LOG_LICENSE_WEB, "Failed to accept new client. ({} | {}/{})", client->file_descriptor, errno, strerror(errno));
return;
}
if(setsockopt(client->file_descriptor, SOL_SOCKET, SO_REUSEADDR, &enabled, sizeof(enabled)) < 0);// CERR("could not set reuse addr");
if(setsockopt(client->file_descriptor, IPPROTO_TCP, TCP_CORK, &disabled, sizeof(disabled)) < 0);// CERR("could not set no push");
client->peer_address = std::move(address);
}
server->initialize_client(client);
{ //Client registration
std::lock_guard<std::recursive_mutex> lock(server->clients_lock);
server->clients.push_back(client);
}
{ //IO Init
client->event_read = event_new(server->socket.event_base, client->file_descriptor, EV_READ | EV_PERSIST, WebStatistics::handleEventRead, server);
client->event_write = event_new(server->socket.event_base, client->file_descriptor, EV_WRITE, WebStatistics::handleEventWrite, server);
event_add(client->event_read, nullptr);
}
logMessage(LOG_LICENSE_WEB, "Accepted new client from {}", net::to_string(client->peer_address->sin_addr));
}
void WebStatistics::handleEventRead(int file_descriptor, short, void* ptr_server) {
auto server = (WebStatistics*) ptr_server;
auto client = server->find_client_by_fd(file_descriptor);
if(!client || client->file_descriptor == 0) {
//TODO error
return;
}
pipes::buffer buffer(1024);
sockaddr_in remote_address{};
socklen_t remote_address_size = sizeof(remote_address);
auto read = recvfrom(file_descriptor, buffer.data_ptr(), buffer.length(), 0, reinterpret_cast<sockaddr *>(&remote_address), &remote_address_size);
if(read < 0){
if(errno == EWOULDBLOCK) return;
logError(LOG_LICENSE_WEB, "[{}] Invalid read: {}/{}. Closing connection.", client->client_prefix(), errno, strerror(errno));
if(client->event_read)
event_del_noblock(client->event_read);
server->close_connection(client);
return;
} else if(read == 0) {
debugMessage(LOG_LICENSE_WEB, "[{}] Invalid read (eof). Closing connection", client->client_prefix());
if(client->event_read)
event_del_noblock(client->event_read);
server->close_connection(client);
return;
}
buffer.resize(read);
lock_guard<recursive_mutex> lock(client->execute_lock);
if(client->file_descriptor == 0) return;
if(client->pipe_ssl) {
client->pipe_ssl->process_incoming_data(buffer);
} else if(client->pipe_websocket) {
client->pipe_websocket->process_incoming_data(buffer);
}
else; //TODO error handling
}
void WebStatistics::handleEventWrite(int file_descriptor, short, void* ptr_server) {
auto server = (WebStatistics*) ptr_server;
auto client = server->find_client_by_fd(file_descriptor);
if(!client) {
//TODO error
return;
}
std::lock_guard<std::recursive_mutex> lock(client->execute_lock);
if(client->buffer_write.empty()) return;
auto& buffer = client->buffer_write.front();
auto written = send(file_descriptor, buffer.data(), buffer.length(), MSG_DONTWAIT | MSG_NOSIGNAL);
if(written < 0){
if(errno == EWOULDBLOCK) return;
logError(LOG_LICENSE_WEB, "[{}] Invalid write: {}/{}. Closing connection.", client->client_prefix(), errno, strerror(errno));
server->close_connection(client);
return;
} else if(written == 0) {
logError(LOG_LICENSE_WEB, "[{}] Invalid write (eof). Closing connection", client->client_prefix());
server->close_connection(client);
return;
}
if(written >= buffer.length())
client->buffer_write.pop_front();
else buffer = buffer.substr(written);
if(!client->buffer_write.empty()) event_add(client->event_write, nullptr);
}
void WebStatistics::close_connection(const std::shared_ptr<license::web::WebStatistics::Client> &client) {
if(this->socket.event_base_dispatch && *this->socket.event_base_dispatch == pthread_self()) {
std::thread(bind(&WebStatistics::close_connection, this, client)).detach();
return;
}
{
std::lock_guard<std::recursive_mutex> lock(this->clients_lock);
auto entry = find(this->clients.begin(), this->clients.end(), client);
if(entry != this->clients.end())
this->clients.erase(entry);
else; //TODO Error handling?
}
std::lock_guard<std::recursive_mutex> lock(client->execute_lock);
if(client->event_read) {
event_del(client->event_read);
event_free(client->event_read);
client->event_read = nullptr;
}
if(client->event_write) {
event_del(client->event_write);
event_free(client->event_write);
client->event_write = nullptr;
}
if(client->file_descriptor > 0) {
if(shutdown(client->file_descriptor, SHUT_RDWR) < 0); //TODO error handling
if(close(client->file_descriptor) < 0); //TODO error handling
client->file_descriptor = 0;
}
if(client->pipe_websocket)
client->pipe_websocket = nullptr;
if(client->pipe_ssl) {
client->pipe_ssl->finalize();
client->pipe_ssl = nullptr;
}
logMessage(LOG_LICENSE_WEB, "[{}] Connection closed", client->client_prefix());
}
std::shared_ptr<WebStatistics::Client> WebStatistics::find_client_by_fd(int file_descriptor) {
std::lock_guard<std::recursive_mutex> lock(this->clients_lock);
for(const auto& client : this->clients)
if(client->file_descriptor == file_descriptor) return client;
return nullptr;
}
#define HERR(message, ...) \
do {\
logError(LOG_LICENSE_WEB, "[{}] " message, client->client_prefix(), ##__VA_ARGS__); \
return false; \
} while(0)
inline pipes::buffer json_dump(const Json::Value& value) {
Json::StreamWriterBuilder builder;
builder["indentation"] = ""; // If you want whitespace-less output
auto json = Json::writeString(builder, value);
return pipes::buffer((void*) json.c_str(), json.length());
}
Json::Value::Value(long value) : Value(to_string(value)) {}
Json::Value::Value(unsigned long value) : Value(to_string(value)) {}
bool WebStatistics::handle_message(const std::shared_ptr<license::web::WebStatistics::Client> &client, const pipes::WSMessage &raw_message) {
if(this->update_flood(client, 10)) {
static pipes::buffer _response;
if(_response.empty()) {
Json::Value response;
response["type"] = "error";
response["code"] = "general";
response["msg"] = "action not available due flood prevention";
_response = json_dump(response);
}
client->pipe_websocket->send({pipes::TEXT, _response});
return true;
}
logTrace(LOG_LICENSE_WEB, "[{}] Received message {}", client->client_prefix(), raw_message.data.string());
Json::Value message;
try {
istringstream ss(raw_message.data.string());
ss >> message;
} catch (std::exception& ex) {
logError(LOG_LICENSE_WEB, "[{}] Received an invalid message: {}", client->client_prefix(), raw_message.data.string());
return false;
}
try {
if(!message["type"].isString()) HERR("Missing/invalid type");
if(message["type"].asString() == "request") {
if(!message["request_type"].isString()) HERR("Missing/invalid request type");
if(message["request_type"].asString() == "general") {
this->update_flood(client, 50);
Json::Value response;
response["type"] = "response";
response["code"] = message["code"];
auto stats = this->statistics_manager->general_statistics();
response["statistics"]["instances"] = to_string(stats->instances);
response["statistics"]["servers"] = to_string(stats->servers);
response["statistics"]["clients"] = to_string(stats->clients);
response["statistics"]["music"] = to_string(stats->bots);
client->pipe_websocket->send({pipes::TEXT, json_dump(response)});
return true;
} else if(message["request_type"].asString() == "history") {
auto type = message["history_type"].asInt();
if(type < 0 || type > stats::HistoryStatistics::LAST_HALF_YEAR)
__throw_range_error("invalid range!");
if(type == stats::HistoryStatistics::LAST_DAY)
this->update_flood(client, 50);
if(type == stats::HistoryStatistics::DAY_YESTERDAY)
this->update_flood(client, 50);
if(type == stats::HistoryStatistics::LAST_HALF_YEAR)
this->update_flood(client, 100);
if(type == stats::HistoryStatistics::DAY_7DAYS_AGO)
this->update_flood(client, 60);
if(type == stats::HistoryStatistics::LAST_WEEK)
this->update_flood(client, 70);
if(type == stats::HistoryStatistics::LAST_MONTH)
this->update_flood(client, 80);
std::thread([&, client, type, message]() {
auto history = this->statistics_manager->history((stats::HistoryStatistics::HistoryType) type);
Json::Value response;
response["type"] = "response";
response["code"] = message["code"];
response["history"]["timestamp"] = duration_cast<milliseconds>(history->evaluated.time_since_epoch()).count();
response["history"]["begin"] = duration_cast<milliseconds>(history->begin.time_since_epoch()).count();
response["history"]["end"] = duration_cast<milliseconds>(history->end.time_since_epoch()).count();
response["history"]["interval"] = duration_cast<milliseconds>(history->period).count();
int index = 0;
for(auto& element : history->statistics) {
response["history"]["data"][index]["instances"] = element->instance_online;
response["history"]["data"][index]["servers"] = element->servers_online;
response["history"]["data"][index]["clients"] = element->clients_online;
response["history"]["data"][index]["music"] = element->bots_online;
index++;
}
lock_guard lock(client->execute_lock);
if(client->pipe_websocket)
client->pipe_websocket->send({pipes::TEXT, json_dump(response)});
}).detach();
return true;
}
}
} catch (const std::exception& ex) {
logError(LOG_LICENSE_WEB, "[{}] Message handling throws exception: {}", client->client_prefix(), ex.what());
Json::Value response;
response["type"] = "error";
response["code"] = message["code"];
response["message"] = "could not assign action";
client->pipe_websocket->send({pipes::TEXT, json_dump(response)});
return false;
}
{
Json::Value response;
response["type"] = "error";
response["code"] = message["code"];
response["message"] = "could not assign action";
client->pipe_websocket->send({pipes::TEXT, json_dump(response)});
}
return true;
}
bool WebStatistics::handle_request(const std::shared_ptr<license::web::WebStatistics::Client> &client, const http::HttpRequest &request, http::HttpResponse &response) {
auto type = request.parameters.at("type");
logMessage(LOG_LICENSE_WEB, "[{}] Received HTTP status request of type {}", client->client_prefix(), type);
if(type == "request" && request.parameters.at("request_type") == "general") {
Json::Value json;
json["type"] = "response";
auto stats = this->statistics_manager->general_statistics();
json["statistics"]["instances"] = to_string(stats->instances);
json["statistics"]["servers"] = to_string(stats->servers);
json["statistics"]["clients"] = to_string(stats->clients);
json["statistics"]["music"] = to_string(stats->bots);
response.setHeader("data", {json_dump(json).string()});
response.code = http::code::_200;
}
return false;
}
bool WebStatistics::update_flood(const std::shared_ptr<license::web::WebStatistics::Client> &client, int flood_points) {
if(client->flood_reset.time_since_epoch().count() == 0)
client->flood_reset = system_clock::now();
client->flood_points += flood_points;
auto diff = duration_cast<milliseconds>(system_clock::now() - client->flood_reset);
if(diff.count() > 1000) {
diff -= milliseconds(1000);
auto reduce = diff.count() / 10; //Reduce 100fp per second
if(client->flood_points > reduce)
client->flood_points = 0;
else
client->flood_points -= reduce;
client->flood_reset = system_clock::now();
}
return client->flood_points > 150;
}
void WebStatistics::broadcast_message(const Json::Value &value) {
auto raw_value = json_dump(value);
for(const auto& client : this->get_clients()) {
std::lock_guard<std::recursive_mutex> lock(client->execute_lock);
if(client->pipe_websocket && client->pipe_websocket->getState() == pipes::WebSocketState::CONNECTED)
client->pipe_websocket->send({pipes::TEXT, raw_value});
}
}
void WebStatistics::broadcast_notify_general_update() {
Json::Value message;
message["type"] = "notify";
message["target"] = "general_update";
this->broadcast_message(message);
}
void WebStatistics::async_broadcast_notify_general_update() {
this->scheduler.execute([&]{
this->broadcast_notify_general_update();
});
}

106
license/server/WebAPI.h Normal file
View File

@ -0,0 +1,106 @@
#pragma once
#include <event.h>
#include <protocol/buffers.h>
#include <deque>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <ThreadPool/Thread.h>
#include <shared/License.h>
#include <arpa/inet.h>
#include <misc/net.h>
#include <json/json.h>
#include <pipes/ws.h>
#include <pipes/ssl.h>
#include <ssl/SSLManager.h>
namespace license {
namespace server {
class LicenseManager;
}
namespace stats {
class StatisticManager;
}
namespace web {
class WebStatistics {
struct Client {
std::unique_ptr<sockaddr_in> peer_address;
int file_descriptor = 0;
event* event_read = nullptr;
event* event_write = nullptr;
std::recursive_mutex execute_lock;
std::deque<std::string> buffer_write;
std::unique_ptr<pipes::WebSocket> pipe_websocket;
std::unique_ptr<pipes::SSL> pipe_ssl;
std::chrono::system_clock::time_point flood_reset;
int flood_points;
inline std::string client_prefix() { return peer_address ? net::to_string(peer_address->sin_addr) : "unconnected"; }
};
public:
WebStatistics(const std::shared_ptr<server::LicenseManager>& /* license manager */, const std::shared_ptr<stats::StatisticManager>& /* stats manager */);
virtual ~WebStatistics();
bool start(std::string& /* error */, uint16_t /* port */, const std::shared_ptr<ts::ssl::SSLContext>& /* ssl */);
inline bool running() {
std::lock_guard<std::recursive_mutex> lock(this->running_lock);
return this->_running;
}
void stop();
inline std::deque<std::shared_ptr<Client>> get_clients() {
std::lock_guard<std::recursive_mutex> lock(this->clients_lock);
return this->clients;
}
void close_connection(const std::shared_ptr<Client>& /* client */);
std::shared_ptr<Client> find_client_by_fd(int /* file descriptor */);
void broadcast_message(const Json::Value& /* message */);
void async_broadcast_notify_general_update();
void broadcast_notify_general_update();
private:
bool _running = false;
std::recursive_mutex running_lock;
std::shared_ptr<server::LicenseManager> license_manager;
std::shared_ptr<stats::StatisticManager> statistics_manager;
struct {
std::unique_ptr<sockaddr_in> local_address;
int file_descriptor = 0;
event* event_accept = nullptr;
event_base* event_base = nullptr;
std::unique_ptr<threads::Thread> event_base_dispatch;
} socket;
std::shared_ptr<ts::ssl::SSLContext> ssl;
std::recursive_mutex clients_lock;
std::deque<std::shared_ptr<Client>> clients;
threads::ThreadPool scheduler{1, "WebStatistics #"};
protected:
static void handleEventAccept(int, short, void*);
static void handleEventRead(int, short, void*);
static void handleEventWrite(int, short, void*);
void initialize_client(const std::shared_ptr<Client>& /* client */);
virtual bool handle_message(const std::shared_ptr<Client>& /* client */, const pipes::WSMessage& message);
virtual bool handle_request(const std::shared_ptr<license::web::WebStatistics::Client> &client, const http::HttpRequest& /* request */, http::HttpResponse& /* response */);
bool update_flood(const std::shared_ptr<license::web::WebStatistics::Client> &client, int flood_points);
};
}
}

102
license/shared/License.cpp Normal file
View File

@ -0,0 +1,102 @@
#include <google/protobuf/message.h>
#include <misc/base64.h>
//#define NO_OPEN_SSL
#include <misc/digest.h>
#include <cstring>
#include <cassert>
#include "crypt.h"
#include "License.h"
using namespace std;
using namespace std::chrono;
inline void generate(char* buffer, size_t length){
for(int index = 0; index < length; index++)
buffer[index] = rand();
}
namespace license {
std::string LicenseTypeNames[] = LT_NAMES;
std::shared_ptr<License> readLocalLicence(const std::string& buffer, std::string& error){
string bbuffer = base64::decode(buffer);
if(bbuffer.length() < sizeof(License)) {
error = "Invalid license size";
return nullptr;
}
auto license = static_cast<License *>(malloc(sizeof(License)));
memcpy(license, bbuffer.data(), sizeof(License));
if(license->header.version != LICENSE_VERSION){
error = "Invalid license version (" + to_string(license->header.version) + ")";
return nullptr;
}
xorBuffer(&((char*) license)[sizeof(License::header)], sizeof(License::data), license->header.cryptKey, sizeof(license->header.cryptKey));
auto hash = digest::sha1(reinterpret_cast<const char *>(&license->data), sizeof(license->data));
uint64_t checkSum = 0;
for(int i = 0; i < SHA_DIGEST_LENGTH; i++)
checkSum += (uint8_t) hash[i] << (i % 8);
if((checkSum ^ *(uint64_t*) &license->header.cryptKey) != MAGIC_NUMER) {
error = "invalid check sum";
return nullptr;
}
return shared_ptr<License>(license, [](License* l){
if(l) free(l);
});
}
std::string exportLocalLicense(const std::shared_ptr<License>& ref){
auto copy = static_cast<License *>(malloc(sizeof(License)));
memcpy(copy, ref.get(), sizeof(License));
auto hash = digest::sha1(reinterpret_cast<const char *>(&copy->data), sizeof(copy->data));
uint64_t checkSum = 0;
for(int i = 0; i < SHA_DIGEST_LENGTH; i++)
checkSum += (uint8_t) hash[i] << (i % 8);
checkSum ^= MAGIC_NUMER;
generate(const_cast<char *>(copy->header.cryptKey), sizeof(copy->header.cryptKey));
*(uint64_t*) &copy->header.cryptKey = checkSum;
xorBuffer(&((char*) copy)[sizeof(License::header)], sizeof(License::data), copy->header.cryptKey, sizeof(copy->header.cryptKey));
auto result = base64_encode((const char*) copy, sizeof(License));
free(copy);
return result;
}
std::string createLocalLicence(LicenseType type, std::chrono::system_clock::time_point until, std::string licenseOwner){
auto license = shared_ptr<License>(static_cast<License *>(malloc(sizeof(License))), [](License* l) { if(l) free(l); });
assert(licenseOwner.length() < sizeof(license->data.licenceOwner));
license->header.version = LICENSE_VERSION;
generate(const_cast<char *>(license->data.licenceKey), sizeof(license->data.licenceKey));
generate(const_cast<char *>(license->data.licenceOwner), sizeof(license->data.licenceOwner)); //Crap data :)
license->data.type = type;
license->data.endTimestamp = duration_cast<milliseconds>(until.time_since_epoch()).count();
memcpy((void *) license->data.licenceOwner, licenseOwner.c_str(), strlen(licenseOwner.c_str()) + 1); //Copy the string into it
return exportLocalLicense(license);
}
const char *exceptions::LicenseException::what() const throw() {
return this->errorMessage.c_str();
}
protocol::packet::packet(PacketType packetId, const ::google::protobuf::Message& message) {
this->header.packetId = packetId;
this->data = message.SerializeAsString();
}
protocol::packet::packet(license::protocol::PacketType packetId, nullptr_t) {
this->header.packetId = packetId;
this->data = "";
}
}

186
license/shared/License.h Normal file
View File

@ -0,0 +1,186 @@
#pragma once
#include <string>
#include <chrono>
#include <memory>
#include <utility>
#include <ThreadPool/Mutex.h>
#include <ThreadPool/Future.h>
#include <Variable.h>
#define LICENSE_VERSION 1
#define LICENSE_PROT_VERSION 2
#define MAGIC_NUMER 0xBADC0DED
namespace license {
namespace exceptions {
class LicenseException : public std::exception {
public:
LicenseException() = delete;
LicenseException(std::string message) : errorMessage(std::move(message)) {}
LicenseException(const LicenseException& ref) : errorMessage(ref.errorMessage) {}
LicenseException(LicenseException&& ref) : errorMessage(std::move(ref.errorMessage)) {}
const char* what() const noexcept override;
private:
std::string errorMessage;
};
}
struct LicenseHeader {
uint16_t version;
const char cryptKey[64]; //The dummy key for data de/encryption
} __attribute__ ((__packed__));
/*
namespace v2 {
namespace data {
struct ChainHead {
uint32_t chain_version;
uint32_t chain_magic;
uint8_t sign[32];
} __attribute__ ((__packed__));
struct ChainEntryHead {
uint8_t entry_type; //sign bit = contains private
int64_t entry_begin;
int32_t entry_length;
uint8_t key[32];
} __attribute__ ((__packed__));
}
struct LicenseIssuer {
std::string name;
std::string email;
};
struct LicenseChainEntry {
data::ChainEntryHead head;
struct {
bool contains;
uint8_t key[32];
} prv_key;
LicenseIssuer issuer;
};
struct LicenseChain {
public:
data::ChainHead head;
std::deque<LicenseChainEntry> entries;
private:
};
}
*/
enum LicenseType : uint8_t {
INVALID,
DEMO,
PREMIUM,
HOSTER,
PRIVATE,
};
inline bool isPremiumLicense(LicenseType type){ return type == HOSTER || type == PREMIUM || type == PRIVATE; }
#define LT_NAMES {"Invalid", "Demo", "Premium", "Hoster", "Private"};
extern std::string LicenseTypeNames[5];
struct License {
LicenseHeader header;
//Crypted part
struct {
LicenseType type;
int64_t endTimestamp;
const char licenceKey[64]; //The actual key
const char licenceOwner[64];
} __attribute__ ((__packed__)) data;
inline std::string key() { return std::string(data.licenceKey, 64); }
inline std::chrono::time_point<std::chrono::system_clock> end() { return std::chrono::system_clock::time_point() + std::chrono::milliseconds(this->data.endTimestamp); }
inline std::string owner() { return std::string(this->data.licenceOwner); }
inline bool isValid() { return data.endTimestamp == 0 || std::chrono::system_clock::now() < this->end(); }
inline bool isPremium(){ return isPremiumLicense(data.type); }
} __attribute__ ((__packed__));
struct LicenseInfo {
LicenseType type;
std::string username;
std::string first_name;
std::string last_name;
std::string email;
std::chrono::system_clock::time_point start;
std::chrono::system_clock::time_point end;
std::chrono::system_clock::time_point creation;
bool deleted = false;
inline bool isValid() { return (end.time_since_epoch().count() == 0 || std::chrono::system_clock::now() < this->end); }
};
extern std::shared_ptr<License> readLocalLicence(const std::string &, std::string &);
extern std::string exportLocalLicense(const std::shared_ptr<License>&);
extern std::string createLocalLicence(LicenseType type, std::chrono::time_point<std::chrono::system_clock> until, std::string licenseOwner);
namespace protocol {
enum RequestState {
UNCONNECTED,
CONNECTING,
HANDSCAKE,
SERVER_VALIDATION,
LICENSE_INFO,
PROPERTY_ADJUSTMENT,
MANAGER_AUTHORIZATION,
MANAGER_CONNECTED,
DISCONNECTING
};
enum PacketType : uint8_t {
PACKET_CLIENT_HANDSHAKE,
PACKET_SERVER_HANDSHAKE,
PACKET_CLIENT_SERVER_VALIDATION,
PACKET_SERVER_VALIDATION_RESPONSE,
PACKET_CLIENT_PROPERTY_ADJUSTMENT,
PACKET_SERVER_PROPERTY_ADJUSTMENT,
PACKET_CLIENT_AUTH_REQUEST,
PACKET_SERVER_AUTH_RESPONSE,
PACKET_CLIENT_LICENSE_CREATE_REQUEST,
PACKET_SERVER_LICENSE_CREATE_RESPONSE,
PACKET_CLIENT_LIST_REQUEST,
PACKET_SERVER_LIST_RESPONSE,
PACKET_CLIENT_DELETE_REQUEST,
PACKET_CLIENT_DELETE_RESPONSE,
PACKET_PING = 0xF0,
PACKET_DISCONNECT = 0xFF
};
struct packet {
struct {
PacketType packetId;
mutable uint16_t length;
} header;
std::string data;
inline void prepare() const {
this->header.length = (uint16_t) data.length();
}
packet(PacketType packetId, std::string data) : data(std::move(data)), header({packetId, 0}) {}
#ifdef GOOGLE_PROTOBUF_MESSAGE_H__
packet(PacketType packetId, const ::google::protobuf::Message&);
#endif
packet(PacketType packetId, nullptr_t);
};
}
}
//DEFINE_TRANSFORMS(license::LicenseType, uint8_t);

View File

@ -0,0 +1,301 @@
#include <netinet/tcp.h>
#include <log/LogUtils.h>
#include <misc/memtracker.h>
#include "crypt.h"
#define DEFINE_HELPER
#include "LicenseRequest.h"
#include "License.h"
#include <csignal>
using namespace std;
using namespace std::chrono;
using namespace ts;
using namespace license;
#define DEBUG_LICENSE_CLIENT
#define CERR(message) LICENSE_FERR(this, CouldNotConnectException, message)
LicenceRequest::LicenceRequest(const std::shared_ptr<LicenseRequestData> & license, const sockaddr_in& remoteAddr) : data(license) {
#ifdef DEBUG_LICENSE_CLIENT
memtrack::allocated<LicenceRequest>(this);
#endif
memcpy(&this->remote_address, &remoteAddr, sizeof(remoteAddr));
assert(license->info);
}
LicenceRequest::~LicenceRequest() {
#ifdef DEBUG_LICENSE_CLIENT
memtrack::freed<LicenceRequest>(this);
#endif
this->abortRequest();
if(this->closeThread) {
this->closeThread->join();
delete this->closeThread;
this->closeThread = nullptr;
}
delete this->currentFuture;
this->currentFuture = nullptr;
}
threads::Future<std::shared_ptr<LicenseRequestResponse>> LicenceRequest::requestInfo() {
{
lock_guard lock(this->lock);
if(this->currentFuture) return *this->currentFuture;
this->currentFuture = new threads::Future<std::shared_ptr<LicenseRequestResponse>>();
}
this->beginRequest();
return *this->currentFuture;
}
//Basic IO
void LicenceRequest::handleEventWrite(int fd, short event, void* ptrClient) {
auto* client = static_cast<LicenceRequest *>(ptrClient);
buffer::RawBuffer* buffer = nullptr;
{
lock_guard lock(client->lock);
if((event & EV_TIMEOUT) > 0) { //Connect timeout
LICENSE_FERR(client, ConnectionException, "Connect timeout");
return;
}
if(client->state == protocol::CONNECTING){
client->handleConnected();
}
if(client->state == protocol::UNCONNECTED || !client->event_write)
return;
buffer = TAILQ_FIRST(&client->writeQueue);
if(!buffer) return;
auto writtenBytes = send(fd, &buffer->buffer[buffer->index], buffer->length - buffer->index, MSG_NOSIGNAL | MSG_DONTWAIT);
buffer->index += writtenBytes;
if(buffer->index >= buffer->length) {
TAILQ_REMOVE(&client->writeQueue, buffer, tail);
delete buffer;
}
if(!TAILQ_EMPTY(&client->writeQueue))
event_add(client->event_write, nullptr);
}
}
void LicenceRequest::sendPacket(const protocol::packet& packet) {
if(this->state == protocol::UNCONNECTED || this->state == protocol::DISCONNECTING) {
if(this->verbose)
logError("Tried to send a packet to an unconnected remote!");
return;
}
packet.prepare();
auto buffer = new buffer::RawBuffer(packet.data.length() + sizeof(packet.header));
memcpy(buffer->buffer, &packet.header, sizeof(packet.header));
memcpy(&buffer->buffer[sizeof(packet.header)], packet.data.data(), packet.data.length());
if(!this->cryptKey.empty())
xorBuffer(&buffer->buffer[sizeof(packet.header)], packet.data.length(), this->cryptKey.data(), this->cryptKey.length());
{
lock_guard lock(this->lock);
TAILQ_INSERT_TAIL(&this->writeQueue, buffer, tail);
if(this->event_write)
event_add(this->event_write, nullptr);
}
}
void LicenceRequest::handleEventRead(int fd, short, void* ptrClient) {
auto* client = static_cast<LicenceRequest *>(ptrClient);
auto buffer = std::unique_ptr<void, decltype(free)*>{malloc(1024), free};
sockaddr_in remoteAddr{};
socklen_t remoteAddrSize = sizeof(remoteAddr);
auto read = recvfrom(fd, buffer.get(), 1024, MSG_NOSIGNAL | MSG_DONTWAIT, reinterpret_cast<sockaddr *>(&remoteAddr), &remoteAddrSize);
if(read < 0){
if(errno == EWOULDBLOCK) return;
if(client->event_read)
event_del_noblock(client->event_read);
LICENSE_FERR(client, ConnectionException, "Invalid read: " + string(strerror(errno)) + "/" + to_string(errno));
return;
} else if(read == 0) {
if(client->event_read)
event_del_noblock(client->event_read);
LICENSE_FERR(client, ConnectionException, "IO error (" + to_string(errno) + "): " + string(strerror(errno)));
return;
}
client->handleMessage(string((char*) buffer.get(), read));
}
static int enabled = 1;
static int disabled = 0;
void LicenceRequest::beginRequest() {
lock_guard lock(this->lock);
TAILQ_INIT(&this->writeQueue);
this->file_descriptor = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
if(this->file_descriptor < 0) CERR("Socket setup failed");
signal(SIGPIPE, SIG_IGN);
auto state = ::connect(this->file_descriptor, reinterpret_cast<const sockaddr *>(&this->remote_address), sizeof(this->remote_address));
if(state < 0 && errno != EINPROGRESS) CERR("connect() failed (" + string(strerror(errno)) + ")");
if(setsockopt(this->file_descriptor, SOL_SOCKET, SO_REUSEADDR, &enabled, sizeof(enabled)) < 0) CERR("could not set reuse addr");
if(setsockopt(this->file_descriptor, IPPROTO_TCP, TCP_CORK, &disabled, sizeof(disabled)) < 0) CERR("could not set no push");
if(fcntl(this->file_descriptor, F_SETFD, fcntl(this->file_descriptor, F_GETFL, 0) | FD_CLOEXEC | O_NONBLOCK) < 0) CERR("Failed to set FD_CLOEXEC and O_NONBLOCK");
this->event_base = event_base_new();
this->event_read = event_new(this->event_base, this->file_descriptor, EV_READ | EV_PERSIST, LicenceRequest::handleEventRead, this);
this->event_write = event_new(this->event_base, this->file_descriptor, EV_WRITE, LicenceRequest::handleEventWrite, this);
this->state = protocol::CONNECTING; //First set connected, then we could enable the event loop
event_dispatch = std::thread([&]() {
signal(SIGPIPE, SIG_IGN);
{ /* now we could start listening */
lock_guard _lock(this->lock);
if(!this->event_read || !this->event_write) return;
event_add(this->event_read, nullptr);
timeval connect_timeout{5, 0};
event_add(this->event_write, &connect_timeout);
}
event_base_dispatch(this->event_base);
});
}
void LicenceRequest::handleConnected() {
this->state = protocol::HANDSCAKE;
uint8_t handshakeBuffer[4];
handshakeBuffer[0] = 0xC0;
handshakeBuffer[1] = 0xFF;
handshakeBuffer[2] = 0xEE;
handshakeBuffer[3] = LICENSE_PROT_VERSION;
this->sendPacket(protocol::packet{protocol::PACKET_CLIENT_HANDSHAKE, string((const char*) handshakeBuffer, 4)}); //Initialise packet
}
void LicenceRequest::handleMessage(const std::string& message) {
if(message.length() < sizeof(protocol::packet::header)) LICENSE_FERR(this, ConnectionException, "Invalid packet size");
protocol::packet packet{protocol::PACKET_DISCONNECT, ""};
memcpy(&packet.header, message.data(), sizeof(protocol::packet::header));
packet.data = message.substr(sizeof(protocol::packet::header));
if(!this->cryptKey.empty()) {
xorBuffer((char*) packet.data.data(), packet.data.length(), this->cryptKey.data(), this->cryptKey.length());
}
if(packet.header.packetId == protocol::PACKET_SERVER_HANDSHAKE) {
this->handlePacketHandshake(packet.data);
} else if(packet.header.packetId == protocol::PACKET_DISCONNECT) {
this->handlePacketDisconnect(packet.data);
} else if(packet.header.packetId == protocol::PACKET_SERVER_VALIDATION_RESPONSE) {
this->handlePacketLicenseInfo(packet.data);
} else if(packet.header.packetId == protocol::PACKET_SERVER_PROPERTY_ADJUSTMENT) {
this->handlePacketInfoAdjustment(packet.data);
} else
LICENSE_FERR(this, ConnectionException, "Invalid packet id (" + to_string(packet.header.packetId) + ")");
}
void LicenceRequest::disconnect(const std::string& message) {
if(this->state != protocol::UNCONNECTED && this->state != protocol::DISCONNECTING)
this->sendPacket({protocol::PACKET_DISCONNECT, message});
this->closeConnection();
//TODO flush?
}
void LicenceRequest::closeConnection() {
event *event_read, *event_write;
{
lock_guard lock(this->lock);
if(this->state == protocol::UNCONNECTED) return;
if(this->event_dispatch.get_id() == this_thread::get_id()) { //We could not close in the same thread as we read/write (we're joining it later)
if(this->state == protocol::DISCONNECTING) return;
this->state = protocol::DISCONNECTING;
this->closeThread = new threads::Thread(THREAD_SAVE_OPERATIONS, [&]() { this->closeConnection(); });
#ifdef DEBUG_LICENSE_CLIENT
if(this->verbose) {
debugMessage("Running close in a new thread");
this->closeThread->name("License request close");
}
#endif
return;
}
this->state = protocol::UNCONNECTED;
event_read = this->event_read;
event_write = this->event_write;
this->event_write = nullptr;
this->event_read = nullptr;
}
if(event_read) {
event_del_block(event_read);
event_free(event_read);
}
if(event_write) {
event_del_block(event_write);
event_free(event_write);
}
/* close before base shutdown (else epoll hangup) */
if(this->file_descriptor > 0) {
shutdown(this->file_descriptor, SHUT_RDWR);
close(this->file_descriptor);
}
this->file_descriptor = 0;
{
lock_guard lock(this->lock);
ts::buffer::RawBuffer* buffer;
while ((buffer = TAILQ_FIRST(&this->writeQueue))) {
TAILQ_REMOVE(&this->writeQueue, buffer, tail);
delete buffer;
}
}
{
if(this->event_base) {
timeval seconds{1, 0};
event_base_loopexit(this->event_base, &seconds);
event_base_loopexit(this->event_base, nullptr);
}
if(this->event_dispatch.joinable()) {
this->event_dispatch.join();
}
if(this->event_base) {
event_base_free(this->event_base);
this->event_base = nullptr;
}
}
#ifdef DEBUG_LICENSE_CLIENT
if(this->verbose)
debugMessage("Executing close done");
#endif
}
void LicenceRequest::abortRequest(const std::chrono::system_clock::time_point &timeout) {
this->closeConnection();
}

View File

@ -0,0 +1,129 @@
#pragma once
#include <protocol/buffers.h>
#include <ThreadPool/Mutex.h>
#include <ThreadPool/Thread.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <event.h>
#include "License.h"
#ifdef DEFINE_HELPER
#define LICENSE_FERR(this, class, message) \
do { \
this->currentException = std::make_shared<exceptions::class>(message); \
if(this->currentFuture && this->currentFuture->state() == threads::FutureState::WORKING) this->currentFuture->executionFailed(); \
this->disconnect("internal error"); \
return; \
} while(0)
#endif
namespace license {
namespace exceptions {
class CouldNotConnectException : public LicenseException {
public:
explicit CouldNotConnectException(const std::string &message) : LicenseException(message) {}
};
class ConnectionException : public LicenseException {
public:
explicit ConnectionException(const std::string &message) : LicenseException(message) {}
};
class UnexcpectedDisconnectException : public LicenseException {
public:
explicit UnexcpectedDisconnectException(const std::string &message) : LicenseException(message) {}
};
class InvalidResponseException : public LicenseException {
public:
explicit InvalidResponseException(const std::string &message) : LicenseException(message) {}
};
}
struct ServerInfo {
std::string unique_identifier;
int64_t timestamp;
std::string uname;
std::string version;
};
struct LicenseRequestResponse {
std::shared_ptr<LicenseInfo> license;
bool license_valid;
int64_t speach_varianz_adjustment;
bool speach_reset;
bool properties_valid;
std::chrono::system_clock::time_point age;
};
struct LicenseRequestData {
std::shared_ptr<License> license = nullptr;
std::shared_ptr<ServerInfo> info = nullptr;
int64_t speach_total = 0;
int64_t speach_dead = 0;
int64_t speach_online = 0;
int64_t speach_varianz = 0;
int64_t client_online = 0;
int64_t web_clients_online = 0;
int64_t bots_online = 0;
int64_t queries_online = 0;
int64_t servers_online = 0;
};
class LicenceRequest {
public:
typedef threads::Future<std::shared_ptr<LicenseRequestResponse>> ResponseFuture;
LicenceRequest(const std::shared_ptr<LicenseRequestData>&, const sockaddr_in&);
~LicenceRequest();
std::shared_ptr<exceptions::LicenseException> exception(){ return this->currentException; }
void clearExceptions(){ this->currentException = nullptr; }
ResponseFuture requestInfo();
void abortRequest(const std::chrono::system_clock::time_point& timeout = std::chrono::system_clock::time_point());
void sendPacket(const protocol::packet&);
bool verbose = true;
private:
std::shared_ptr<LicenseRequestData> data;
threads::Future<std::shared_ptr<LicenseRequestResponse>>* currentFuture = nullptr;
std::shared_ptr<LicenseRequestResponse> response = nullptr;
std::shared_ptr<exceptions::LicenseException> currentException;
std::recursive_mutex lock;
protocol::RequestState state = protocol::UNCONNECTED;
sockaddr_in remote_address;
int file_descriptor = 0;
std::thread event_dispatch;
threads::Thread* closeThread = nullptr;
struct event_base* event_base = nullptr;
event* event_read = nullptr;
event* event_write = nullptr;
TAILQ_HEAD(, ts::buffer::RawBuffer) writeQueue;
std::string cryptKey = "";
void beginRequest();
void handleConnected();
static void handleEventRead(int, short, void*);
static void handleEventWrite(int, short, void*);
void handleMessage(const std::string&);
void disconnect(const std::string&);
void closeConnection();
void handlePacketHandshake(const std::string&);
void handlePacketDisconnect(const std::string&);
void handlePacketLicenseInfo(const std::string&);
void handlePacketInfoAdjustment(const std::string&);
};
}

View File

@ -0,0 +1,105 @@
#include <misc/endianness.h>
#include <LicenseRequest.pb.h>
#define DEFINE_HELPER
#include "LicenseRequest.h"
using namespace license;
using namespace std;
using namespace std::chrono;
void LicenceRequest::handlePacketDisconnect(const std::string& message) {
if(this->state != protocol::DISCONNECTING)
LICENSE_FERR(this, UnexcpectedDisconnectException, "Remote side closed the connection (" + message + ")");
}
void LicenceRequest::handlePacketHandshake(const std::string& data) {
if(this->state != protocol::HANDSCAKE) LICENSE_FERR(this, InvalidResponseException, "Protocol state mismatch");
if(data.length() < 3) LICENSE_FERR(this, InvalidResponseException, "Invalid packet size");
if((uint8_t) data[0] != 0xAF || (uint8_t) data[1] != 0xFE) LICENSE_FERR(this, InvalidResponseException, "Invalid handshake");
if((uint8_t) data[2] != LICENSE_PROT_VERSION) LICENSE_FERR(this, InvalidResponseException, "Invalid license protocol version. Please update TeaSpeak!");
auto key_length = be2le16(data.data(), 3);
if(data.length() < key_length + 3) LICENSE_FERR(this, InvalidResponseException, "Invalid packet size");
this->cryptKey = data.substr(5, key_length);
ts::proto::license::ServerValidation request;
if(this->data->license) {
request.set_licensed(true);
request.set_license_info(true);
request.set_license(exportLocalLicense(this->data->license));
} else {
request.set_licensed(false);
request.set_license_info(false);
}
request.mutable_info()->set_uname(this->data->info->uname);
request.mutable_info()->set_version(this->data->info->version);
request.mutable_info()->set_timestamp(this->data->info->timestamp);
request.mutable_info()->set_unique_id(this->data->info->unique_identifier);
this->sendPacket(protocol::packet{protocol::PACKET_CLIENT_SERVER_VALIDATION, request});
this->state = protocol::LICENSE_INFO;
}
void LicenceRequest::handlePacketLicenseInfo(const std::string& message) {
ts::proto::license::LicenseResponse response;
if(!response.ParseFromString(message)) LICENSE_FERR(this, InvalidResponseException, "Could not parse response");
auto result = make_shared<LicenseRequestResponse>();
auto licenseInfo = make_shared<LicenseInfo>();
if(!response.has_license_info() && this->data->license) LICENSE_FERR(this, InvalidResponseException, "Missing license info");
if(this->data->license) {
licenseInfo->type = (LicenseType) response.license_info().type();
licenseInfo->email = response.license_info().email();
licenseInfo->username = response.license_info().username();
licenseInfo->first_name = response.license_info().first_name();
licenseInfo->last_name = response.license_info().last_name();
licenseInfo->creation = system_clock::time_point() + milliseconds(response.license_info().created());
licenseInfo->start = system_clock::time_point() + milliseconds(response.license_info().begin());
licenseInfo->end = system_clock::time_point() + milliseconds(response.license_info().end());
} else {
licenseInfo->type = LicenseType::DEMO;
licenseInfo->email = "license@teaspeak.de";
licenseInfo->username = "WolverinDEV";
licenseInfo->first_name = "Max";
licenseInfo->last_name = "Musterman";
licenseInfo->creation = system_clock::now();
licenseInfo->start = system_clock::now();
licenseInfo->end = system_clock::time_point();
}
result->license_valid = response.blacklist().state() == ts::proto::license::VALID; //TODO more detailed
result->age = system_clock::now();
result->license = licenseInfo;
this->response = result;
ts::proto::license::PropertyUpdateRequest infos;
infos.set_speach_total(this->data->speach_total);
infos.set_speach_dead(this->data->speach_dead);
infos.set_speach_online(this->data->speach_online);
infos.set_speach_varianz(this->data->speach_varianz);
infos.set_clients_online(this->data->client_online);
infos.set_bots_online(this->data->bots_online);
infos.set_queries_online(this->data->queries_online);
infos.set_servers_online(this->data->servers_online);
infos.set_web_clients_online(this->data->web_clients_online);
this->sendPacket({protocol::PACKET_CLIENT_PROPERTY_ADJUSTMENT, infos});
this->state = protocol::PROPERTY_ADJUSTMENT;
}
void LicenceRequest::handlePacketInfoAdjustment(const std::string& message) {
ts::proto::license::PropertyUpdateResponse response;
if(!response.ParseFromString(message)) LICENSE_FERR(this, InvalidResponseException, "Could not parse response");
this->response->properties_valid = response.accepted();
this->response->speach_varianz_adjustment = response.speach_varianz_corrector();
this->response->speach_reset = response.reset_speach();
this->currentFuture->executionSucceed(this->response);
this->response = nullptr;
this->disconnect("query succeeded!");
}

6
license/shared/crypt.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
inline void xorBuffer(char *buffer, size_t bufferLength, const char *xOr, size_t xorLength){
for(int index = 0; index < bufferLength; index++)
buffer[index] ^= xOr[index % xorLength];
}

270
server/CMakeLists.txt Normal file
View File

@ -0,0 +1,270 @@
cmake_minimum_required(VERSION 3.6)
project(TeaSpeak-Server)
set(CMAKE_VERBOSE_MAKEFILE ON)
#--allow-multiple-definition
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive -Wall -Wno-reorder -Wno-sign-compare -static-libgcc -static-libstdc++ -g -Wl,-no-whole-archive -pthread ${MEMORY_DEBUG_FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -O3")
#NDEBUG
#set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELEASE} -O2")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/environment/)
#disable for debug
#add_definitions(-DRELEASE_MODE)
include_directories(../music/include/)
include_directories(../shared/src)
include_directories(../license/src)
include_directories(../MusicBot/src)
include_directories(/usr/local/include/breakpad)
include_directories(${LIBRARY_PATH}/tomcrypt/src/headers)
include_directories(${LIBRARY_PATH}/spdlog/include)
add_definitions(-DLTM_DESC)
add_definitions(-DMUSIC_BOT)
add_definitions(-DUSE_BORINGSSL)
#0 = STABLE
#1 = BETA
#2 = ALPHA
#3 = PRIVATE
option(BUILD_TYPE "Sets the build type" OFF)
option(BUILD_TYPE_NAME "Sets the build type name" OFF)
option(COMPILE_WEB_CLIENT "Enable/Disable the web cleint future" OFF)
set(COMPILE_WEB_CLIENT "ON")
set(CMAKE_VERBOSE_MAKEFILE ON)
set(SERVER_SOURCE_FILES
main.cpp
src/client/ConnectedClient.cpp
src/client/voice/PrecomputedPuzzles.cpp
src/client/voice/VoiceClient.cpp
src/client/voice/VoiceClientHandschake.cpp
src/client/voice/VoiceClientCommandHandler.cpp
src/client/voice/VoiceClientPacketHandler.cpp
src/client/voice/VoiceClientView.cpp
src/TS3ServerClientManager.cpp
src/TSServer.cpp
src/TS3ServerHeartbeat.cpp
src/SignalHandler.cpp
src/server/VoiceServer.cpp
src/server/POWHandler.cpp
src/client/voice/VoiceClientConnection.cpp
src/client/ConnectedClientCommandHandler.cpp
src/client/ConnectedClientNotifyHandler.cpp
src/ServerManager.cpp
src/server/file/FileServer.cpp
src/channel/ServerChannel.cpp
src/channel/ClientChannelView.cpp
src/client/file/FileClient.cpp
src/client/file/FileClientIO.cpp
src/Group.cpp
src/manager/BanManager.cpp
src/client/InternalClient.cpp
#src/weblist/WeblistClient.cpp
#src/weblist/WebList.cpp
src/client/DataClient.cpp
src/server/QueryServer.cpp
src/client/query/QueryClient.cpp
src/client/query/QueryClientCommands.cpp
src/client/query/QueryClientNotify.cpp
src/manager/IpListManager.cpp
src/ConnectionStatistics.cpp
src/manager/TokeManager.cpp
src/terminal/CommandHandler.cpp
src/manager/ComplainManager.cpp
src/DatabaseHelper.cpp
src/manager/LetterManager.cpp
tomcryptTest.cpp
src/pinteraction/ApplicationInteraction.cpp
src/ServerManagerSnapshot.cpp
src/ServerManagerSnapshotDeploy.cpp
src/client/music/Song.cpp
src/music/PlayablePlaylist.cpp
src/InstanceHandler.cpp
src/InstanceHandlerSetup.cpp
src/Configuration.cpp
src/build.cpp
src/music/MusicPlaylist.cpp
src/client/music/MusicClient.cpp
src/client/music/MusicClientPlayer.cpp
src/client/ConnectedClientTextCommandHandler.cpp
src/music/MusicBotManager.cpp
src/client/music/internal_provider/channel_replay/ChannelProvider.cpp
src/geo/GeoLocation.cpp
src/geo/IP2Location.cpp
src/geo/VPNBlocker.cpp
src/client/query/XMacroEventTypes.h
src/server/VoiceIOManager.cpp
src/server/WebIoManager.cpp
src/client/SpeakingClient.cpp
src/lincense/LicenseHelper.cpp
../shared/src/ssl/SSLManager.cpp
src/manager/SqlDataManager.cpp
src/ShutdownHelper.cpp
src/client/music/MusicQueue.cpp
src/lincense/TeamSpeakLicense.cpp
src/weblist/WebListManager.cpp
src/weblist/TeamSpeakWebClient.cpp
)
if(COMPILE_WEB_CLIENT)
add_definitions(-DCOMPILE_WEB_CLIENT)
set(SERVER_SOURCE_FILES
${SERVER_SOURCE_FILES}
src/server/WebServer.cpp
src/client/web/WebClient.cpp
# src/server/web/WebRTCServer.cpp
src/client/web/WSWebClient.cpp
src/client/web/SampleHandler.cpp
src/client/SpeakingClientHandshake.cpp
src/client/web/VoiceBridge.cpp
)
endif()
add_executable(PermHelper helpers/permgen.cpp)
target_link_libraries(PermHelper
${LIBRARY_PATH_ED255}
TeaSpeak #Static
TeaLicenseHelper #Static
TeaMusic #Static
${LIBRARY_PATH_THREAD_POOL} #Static
${LIBRARY_PATH_TERMINAL} #Static
${LIBRARY_PATH_VARIBALES}
${LIBRARY_PATH_YAML}
pthread
stdc++fs
${LIBEVENT_PATH}/libevent.a
${LIBEVENT_PATH}/libevent_pthreads.a
${LIBRARY_PATH_OPUS}
${LIBRARY_PATH_JSON}
${LIBRARY_PATH_PROTOBUF}
#${LIBWEBRTC_LIBRARIES} #ATTENTIAN! WebRTC does not work with crypto! (Already contains a crypto version)
${LIBRARY_TOM_CRYPT}
${LIBRARY_TOM_MATH}
#We're forsed to use boringssl caused by the fact that boringssl is already within webrtc!
#Require a so
sqlite3
${LIBRARY_PATH_BREAKPAD}
${LIBRARY_PATH_JDBC}
${LIBRARY_PATH_PROTOBUF}
${LIBRARY_PATH_DATA_PIPES}
${LIBRARY_PATH_BORINGSSL_SSL}
${LIBRARY_PATH_BORINGSSL_CRYPTO}
dl
jemalloc
)
SET(CPACK_PACKAGE_VERSION_MAJOR "1")
SET(CPACK_PACKAGE_VERSION_MINOR "3")
SET(CPACK_PACKAGE_VERSION_PATCH "22")
if(BUILD_TYPE_NAME EQUAL OFF)
SET(CPACK_PACKAGE_VERSION_DATA "beta")
elseif(BUILD_TYPE_NAME STREQUAL "")
SET(CPACK_PACKAGE_VERSION_DATA "")
else()
SET(CPACK_PACKAGE_VERSION_DATA "-${BUILD_TYPE_NAME}")
endif()
if(BUILD_TYPE EQUAL OFF)
SET(BUILD_TYPE "1")
endif()
set_source_files_properties(src/build.cpp PROPERTIES
COMPILE_FLAGS "-DBUILD_MAJOR=${CPACK_PACKAGE_VERSION_MAJOR} -DBUILD_MINOR=${CPACK_PACKAGE_VERSION_MINOR} -DBUILD_PATCH=${CPACK_PACKAGE_VERSION_PATCH} -DBUILD_DATA=\"${CPACK_PACKAGE_VERSION_DATA}\" -DBUILD_TYPE=${BUILD_TYPE} -DBUILD_COUNT=0")
file(WRITE repro/env/buildVersion.txt "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}${CPACK_PACKAGE_VERSION_DATA}")
add_executable(TeaSpeakServer ${SERVER_SOURCE_FILES})
target_link_libraries(TeaSpeakServer
${LIBRARY_PATH_THREAD_POOL} #Static
TeaSpeak #Static
TeaLicenseHelper #Static
TeaMusic #Static
${LIBRARY_PATH_TERMINAL} #Static
${LIBRARY_PATH_VARIBALES}
${LIBRARY_PATH_YAML}
pthread
stdc++fs
${LIBEVENT_PATH}/libevent.a
${LIBEVENT_PATH}/libevent_pthreads.a
${LIBRARY_PATH_OPUS}
${LIBRARY_PATH_JSON}
${LIBRARY_PATH_PROTOBUF}
#We're forsed to use boringssl caused by the fact that boringssl is already within webrtc!
#Require a so
sqlite3
${LIBRARY_PATH_BREAKPAD}
${LIBRARY_PATH_JDBC}
${LIBRARY_PATH_PROTOBUF}
#${LIBWEBRTC_LIBRARIES} #ATTENTIAN! WebRTC does not work with crypto! (Already contains a crypto version)
${LIBRARY_TOM_CRYPT}
${LIBRARY_TOM_MATH}
${LIBRARY_PATH_ED255}
)
if(${COMPILE_WEB_CLIENT})
find_package(LibNice REQUIRED)
find_package(UsrSCTP REQUIRED)
target_link_libraries(TeaSpeakServer
LibNice::LibNice
${LIBRARY_PATH_DATA_PIPES}
)
endif()
include_directories(${LIBRARY_PATH}/boringssl/include/)
target_link_libraries(TeaSpeakServer
${LIBRARY_PATH_BORINGSSL_SSL}
${LIBRARY_PATH_BORINGSSL_CRYPTO}
dl
)
set(DISABLE_JEMALLOC ON)
if(NOT DISABLE_JEMALLOC)
target_link_libraries(TeaSpeakServer
jemalloc
)
add_definitions(-DHAVE_JEMALLOC)
endif()
#Fix RPATH
#patchelf --set-rpath ./libs/ TeaSpeakServer
#patchelf --remove-rpath TeaSpeakServer
#add_custom_command(
# TARGET TeaSpeakServer
## COMMAND bash -c "patchelf --set-rpath ./libs/ ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}TeaSpeakServer"
# COMMAND bash -c "patchelf --remove-rpath ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}TeaSpeakServer"
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
# COMMENT "Cleaning RPATH"
#)

View File

@ -0,0 +1,5 @@
Build opus:
./configure --with-pic CFLAGS="-O2"
Build libevent 2.1.8 (with -fPIC)

13
server/VersionHelper.sh Normal file
View File

@ -0,0 +1,13 @@
#!/usr/bin/env bash
if [ -e "build.data" ]; then
echo "File exists"
DATA=$(cat "build.data")
else
echo "Create new file"
echo "0" > "build.data"
fi
DATA=$(($DATA+1))
echo "Data: $DATA"

1
server/environment/geoloc Symbolic link
View File

@ -0,0 +1 @@
../repro/env/geoloc/

View File

@ -0,0 +1 @@
../../music/bin/providers/

View File

@ -0,0 +1 @@
../repro/env/resources/

20
server/flood.sh Executable file
View File

@ -0,0 +1,20 @@
#!/usr/bin/env bash
while [ true ]; do
# nc -w 10 172.17.0.2 $1&
#echo "login serveradmin LiPdiKB"; echo "servercreate"
(echo "login serveradmin markus"; echo "use 3"; echo "channellist"; echo "clientlist") | nc -w 10 localhost $1&
#nc -w 10 localhost $1&
# echo "quit" | nc localhost $1&
# echo "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" | openssl s_client -connect localhost:10101
#nc localhost $1&
PID=$!
ps -p ${PID} > /dev/null 2>&1
if [ "$?" -ne "0" ]; then
echo -e "\nInvalid command"
else
echo ""
#kill -9 ${PID}
fi
done

80
server/gui/mainwindow.ui Normal file
View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="general">
<property name="enabled">
<bool>true</bool>
</property>
<attribute name="title">
<string>General</string>
</attribute>
<widget class="QPushButton" name="pushButton_2">
<property name="geometry">
<rect>
<x>260</x>
<y>180</y>
<width>181</width>
<height>31</height>
</rect>
</property>
<property name="text">
<string>Stop Instance</string>
</property>
</widget>
</widget>
<widget class="ts::gui::ServerTabWidget" name="servers">
<attribute name="title">
<string>Servers</string>
</attribute>
</widget>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Log</string>
</attribute>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>19</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<customwidgets>
<customwidget>
<class>ts::gui::ServerTabWidget</class>
<extends>QWidget</extends>
<header>src/ServerTabWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@ -0,0 +1,9 @@
Channel Admin
0
permsid=i_channel_create_modify_with_codec_latency_factor_min permvalue=1 permnegated=0 permskip=0|permsid=b_channel_modify_make_temporary permvalue=1 permnegated=0 permskip=0|permsid=b_channel_modify_name permvalue=1 permnegated=0 permskip=0|permsid=b_channel_modify_topic permvalue=1 permnegated=0 permskip=0|permsid=b_channel_modify_description permvalue=1 permnegated=0 permskip=0|permsid=b_channel_modify_password permvalue=1 permnegated=0 permskip=0|permsid=b_channel_modify_codec permvalue=1 permnegated=0 permskip=0|permsid=b_channel_modify_codec_quality permvalue=1 permnegated=0 permskip=0|permsid=b_channel_modify_codec_latency_factor permvalue=1 permnegated=0 permskip=0|permsid=b_channel_modify_maxclients permvalue=1 permnegated=0 permskip=0|permsid=b_channel_modify_maxfamilyclients permvalue=1 permnegated=0 permskip=0|permsid=b_channel_modify_needed_talk_power permvalue=1 permnegated=0 permskip=0|permsid=b_channel_modify_make_codec_encrypted permvalue=1 permnegated=0 permskip=0|permsid=b_channel_delete_semi_permanent permvalue=1 permnegated=0 permskip=0|permsid=b_channel_delete_temporary permvalue=1 permnegated=0 permskip=0|permsid=b_channel_delete_flag_force permvalue=1 permnegated=0 permskip=0|permsid=i_channel_delete_power permvalue=50 permnegated=0 permskip=0|permsid=i_channel_description_view_power permvalue=50 permnegated=0 permskip=0|permsid=i_icon_id permvalue=100 permnegated=0 permskip=0|permsid=b_group_is_permanent permvalue=1 permnegated=0 permskip=0|permsid=i_group_auto_update_type permvalue=40 permnegated=0 permskip=0|permsid=i_group_needed_modify_power permvalue=75 permnegated=0 permskip=0|permsid=i_group_member_add_power permvalue=50 permnegated=0 permskip=0|permsid=i_group_needed_member_add_power permvalue=50 permnegated=0 permskip=0|permsid=i_group_member_remove_power permvalue=50 permnegated=0 permskip=0|permsid=i_group_needed_member_remove_power permvalue=50 permnegated=0 permskip=0|permsid=b_client_use_channel_commander permvalue=1 permnegated=0 permskip=0|permsid=i_client_kick_from_channel_power permvalue=50 permnegated=0 permskip=0|permsid=i_client_needed_kick_from_channel_power permvalue=50 permnegated=0 permskip=0|permsid=i_client_move_power permvalue=50 permnegated=0 permskip=0|permsid=i_client_needed_complain_power permvalue=25 permnegated=0 permskip=0|permsid=i_client_talk_power permvalue=60 permnegated=0 permskip=0|permsid=b_client_set_flag_talker permvalue=1 permnegated=0 permskip=0
Operator
0
permsid=i_icon_id permvalue=200 permnegated=0 permskip=0|permsid=b_group_is_permanent permvalue=1 permnegated=0 permskip=0|permsid=i_group_auto_update_type permvalue=35 permnegated=0 permskip=0|permsid=i_group_needed_modify_power permvalue=75 permnegated=0 permskip=0|permsid=i_group_member_add_power permvalue=30 permnegated=0 permskip=0|permsid=i_group_needed_member_add_power permvalue=30 permnegated=0 permskip=0|permsid=i_group_member_remove_power permvalue=30 permnegated=0 permskip=0|permsid=i_group_needed_member_remove_power permvalue=30 permnegated=0 permskip=0|permsid=i_client_needed_complain_power permvalue=25 permnegated=0 permskip=0|permsid=i_client_talk_power permvalue=50 permnegated=0 permskip=0|permsid=b_client_set_flag_talker permvalue=1 permnegated=0 permskip=0
Guest
0
permsid=i_group_auto_update_type permvalue=10 permnegated=0 permskip=0|permsid=i_group_needed_modify_power permvalue=75 permnegated=0 permskip=0

267
server/helpers/permgen.cpp Normal file
View File

@ -0,0 +1,267 @@
#include <fstream>
#include <query/Command.h>
#include <cstring>
#include <utility>
#include <functional> /* required from permission manager */
#include "log/LogUtils.h"
#include "Definitions.h"
#include "PermissionManager.h"
using namespace std;
using namespace ts;
enum GroupType {
GENERAL,
SERVER,
CHANNEL
};
enum GroupUpdateType {
NONE = 0,
CHANNEL_GUEST = 10,
CHANNEL_VOICE = 25,
CHANNEL_OPERATOR = 35,
CHANNEL_ADMIN = 40,
SERVER_GUEST = 15,
SERVER_NORMAL = 30,
SERVER_ADMIN = 45,
QUERY_GUEST = 20,
QUERY_ADMIN = 50
};
/*
Value 10: The group will be handled like 'Channel Guest'
Value 15: The group will be handled like 'Server Guest'
Value 20: The group will be handled like 'Query Guest'
Value 25: The group will be handled like 'Channel Voice'
Value 30: The group will be handled like 'Server Normal'
Value 35: The group will be handled like 'Channel Operator'
Value 40: The group will be handled like 'Channel Admin'
Value 45: The group will be handled like 'Server Admin'
Value 50: The group will be handled like 'Query Admin'
*/
enum Target {
TARGET_QUERY = 0,
TARGET_SERVER = 1,
TARGET_CHANNEL = 2
};
struct Group {
Target target;
string name;
deque<permission::update::UpdatePermission> permissions;
};
map<Target, map<string, string>> property_mapping = {
{TARGET_QUERY, {
{"Guest Server Query", "serverinstance_guest_serverquery_group"},
{"Admin Server Query", "serverinstance_admin_serverquery_group"}
}},
{TARGET_SERVER, {
{"Server Admin", "serverinstance_template_serveradmin_group"},
{"Guest", "serverinstance_template_serverdefault_group"}
}},
{TARGET_CHANNEL, {
{"Channel Admin", "serverinstance_template_channeladmin_group"},
{"Guest", "serverinstance_template_channeldefault_group"}
}},
};
inline bool read_line(ifstream& in, string& line) {
if(!getline(in, line)) return false;
while(!line.empty()) {
if(line.back() == '\r') line = line.substr(0, line.length() - 1);
else if(line.front() == ' ' || line.front() == '\r' || (unsigned char) line.front() > 0x80) line = line.substr(1);
else break;
}
return true;
}
#define PRINT_UNMAP(type) \
do { \
cout << type << " => {"; \
auto e = permission::teamspeak::unmap_key(type, permission::teamspeak::GroupType::SERVER); \
for(auto it = e.begin(); it != e.end(); it++) { \
cout << *it; \
if(it + 1 != e.end()) \
cout << ", "; \
} \
cout << "}" << endl; \
} while(0)
#define PRINT_MAP(type) \
do { \
cout << type << " => {"; \
auto e = permission::teamspeak::map_key(type, permission::teamspeak::GroupType::SERVER); \
for(auto it = e.begin(); it != e.end(); it++) { \
cout << *it; \
if(it + 1 != e.end()) \
cout << ", "; \
} \
cout << "}" << endl; \
} while(0)
static constexpr bool USE_MAPPING = false;
int main(int argc, char** argv) {
PRINT_UNMAP("i_client_music_needed_rename_power");
PRINT_UNMAP("b_client_music_channel_list");
PRINT_UNMAP("i_client_music_info");
PRINT_UNMAP("i_client_max_clones_ip"); //=> i_client_max_clones_uid
PRINT_UNMAP("i_client_max_clones_hwid"); //=> i_client_max_clones_uid
PRINT_UNMAP("i_client_max_clones_uid"); //=> i_client_max_clones_uid
PRINT_UNMAP("i_server_group_needed_modify_power"); //=> i_client_max_clones_uid
PRINT_UNMAP("i_displayed_group_needed_modify_power"); //=> i_client_max_clones_uid
cout << "--------- map ----------" << endl;
PRINT_MAP("i_client_max_clones_uid");
PRINT_MAP("i_group_needed_modify_power");
deque<Group> groups;
{
ifstream file("../helpers/server_groups_new"); /* the new file is already mapped! */
string line;
while (read_line(file, line))
{
Group group{};
group.name = line;
read_line(file, line);
group.target = line == "2" ? TARGET_QUERY : TARGET_SERVER;
read_line(file, line);
auto data = "perms " + line;
ts::Command group_parms = ts::Command::parse(pipes::buffer_view(data.data(), data.length()));
map<permission::PermissionType, permission::update::UpdatePermission> grantMapping;
for (int index = 0; index < group_parms.bulkCount(); index++) {
auto permission_name = group_parms[index]["permsid"].string();
auto permissions = USE_MAPPING ? permission::teamspeak::map_key(permission_name, permission::teamspeak::SERVER) : std::deque<std::string>({permission_name});
for(const auto& permission : permissions) {
auto type = permission::resolvePermissionData(permission);
if(type->type == permission::unknown) {
cerr << "Failed to parse type " << permission << " (" << permission_name << ")!" << endl;
continue;
}
if(type->grantName() == permission) {
permission::update::UpdatePermission entry;
for(auto& perm : group.permissions)
if(perm.name == type->name) {
perm.granted = group_parms[index]["permvalue"];
goto jmp_out_a;
}
entry.name = type->name;
entry.granted = group_parms[index]["permvalue"];
group.permissions.push_back(entry);
jmp_out_a:;
} else {
permission::update::UpdatePermission entry;
entry.name = permission;
entry.value = group_parms[index]["permvalue"];
entry.negated = group_parms[index]["permnegated"];
entry.skipped = group_parms[index]["permskip"];
group.permissions.push_back(entry);
}
}
}
groups.push_back(group);
}
file.close();
}
{
ifstream file("../helpers/channel_groups");
string line;
while (read_line(file, line))
{
Group group{};
group.name = line;
read_line(file, line);
group.target = TARGET_CHANNEL;
read_line(file, line);
auto data = "perms " + line;
ts::Command group_parms = ts::Command::parse(pipes::buffer_view(data.data(), data.length()));
map<permission::PermissionType, permission::update::UpdatePermission> grantMapping;
for (int index = 0; index < group_parms.bulkCount(); index++) {
auto permission_name = group_parms[index]["permsid"].string();
auto permissions = permission::teamspeak::map_key(permission_name, permission::teamspeak::CHANNEL);
for(const auto& permission : permissions) {
auto type = permission::resolvePermissionData(permission);
if(type->type == permission::unknown) {
cerr << "Failed to parse type " << permission << " (" << permission_name << ")!" << endl;
continue;
}
if(type->grantName() == permission) {
permission::update::UpdatePermission entry;
for(auto& perm : group.permissions)
if(perm.name == type->name) {
perm.granted = group_parms[index]["permvalue"];
goto jmp_out_b;
}
entry.name = type->name;
entry.granted = group_parms[index]["permvalue"];
group.permissions.push_back(entry);
jmp_out_b:;
} else {
permission::update::UpdatePermission entry;
entry.name = permission;
entry.value = group_parms[index]["permvalue"];
entry.negated = group_parms[index]["permnegated"];
entry.skipped = group_parms[index]["permskip"];
group.permissions.push_back(entry);
}
}
}
groups.push_back(group);
}
file.close();
}
cout << "Got " << groups.size() << " groups" << endl;
ofstream of("permissions.template");
of << "# This is a auto generated template file!" << endl;
of << "# DO NOT EDIT IF YOU'RE NOT SURE WHAT YOU'RE DOING!" << endl;
of << "# Syntax:" << endl;
of << "# Every entry starts with a '--start' and ends with a '--end'" << endl;
of << "# Every entry has the following properties:" << endl;
of << "# name [string] -> The name of the entry" << endl;
of << "# target [numeric] -> The type of the entry {QUERY | SERVER | CHANNEL}" << endl;
of << "# property [string] -> The applied property of the entry" << endl;
of << "# permission [[string],[numeric],[numeric],[flag],[flag]] -> A permission applied on the entry ([name],[value],[granted],[skipped],[negated])" << endl;
of << "#" << endl;
for(const auto& group : groups) {
of << "--start" << endl;
of << "name:" << group.name << endl;
of << "target:" << group.target << endl;
of << "property:" << property_mapping[group.target][group.name] << endl;
for(const auto& perm : group.permissions) {
of << "permission:" << perm.name << "=" << perm.value << "," << perm.granted << "," << perm.skipped << "," << perm.negated << endl;
}
if(USE_MAPPING) {
for(const auto& perm : group.permissions) {
if(perm.name == "i_group_auto_update_type") {
for(const auto& insert : permission::update::migrate) {
if(insert.type == perm.value) {
of << "permission:" << insert.permission.name << "=" << insert.permission.value << "," << insert.permission.granted << "," << insert.permission.skipped << "," << insert.permission.negated << endl;
cout << "Auto insert permission " << insert.permission.name << " (" << insert.type << ")" << endl;
}
}
break;
}
}
}
of << "--end" << endl;
}
of.close();
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

58
server/lock_concept Normal file
View File

@ -0,0 +1,58 @@
CommandResult handleCommandClientUpdate(Command&);
CommandResult handleCommandClientEdit(Command&);
CommandResult handleCommandClientEdit(Command&, const std::shared_ptr<ConnectedClient>& /* target */);
CommandResult handleCommandClientMove(Command&);
CommandResult handleCommandClientGetVariables(Command&);
CommandResult handleCommandClientKick(Command&);
CommandResult handleCommandClientPoke(Command&);
CommandResult handleCommandChannelSubscribe(Command&); read lock: server channel tree => client lock write
CommandResult handleCommandChannelSubscribeAll(Command&); read lock: server channel tree => client lock write
CommandResult handleCommandChannelUnsubscribe(Command&); read lock: server channel tree => client lock write
CommandResult handleCommandChannelUnsubscribeAll(Command&); read lock: server channel tree => client lock write
CommandResult handleCommandChannelCreate(Command&); write lock server channel tree => iterate clients lock (write)
CommandResult handleCommandChannelDelete(Command&); write lock server channel tree => iterate clients lock (write)
CommandResult handleCommandChannelEdit(Command&); write lock server channel tree
CommandResult handleCommandChannelGetDescription(Command&); read lock: server channel tree
CommandResult handleCommandChannelMove(Command&); write lock server channel tree
CommandResult handleCommandChannelAddPerm(Command&); read lock: server channel tree => all clients write lock
CommandResult handleCommandChannelDelPerm(Command&); read lock: server channel tree => all clients write lock
CommandResult handleCommandChannelGroupDel(Command&); read lock: server channel tree => all clients in the group should not be allowed to switch a channel
CommandResult handleCommandSetClientChannelGroup(Command&); read lock: server channel tree => client should not be allowed to switch channel
CommandResult handleCommandPluginCmd(Command&);
CommandResult handleCommandClientMute(Command&);
CommandResult handleCommandClientUnmute(Command&);
//Original from query but still reachable for all
CommandResult handleCommandClientList(Command&);
CommandResult handleCommandClientFind(Command&);
CommandResult handleCommandClientInfo(Command&);
CommandResult handleCommandVerifyChannelPassword(Command&); read lock: server channel tree
handleCommandChannelFind read lock: server channel tree
handleCommandChannelInfo read lock: server channel tree
General command handling: client_command_lock
Ensure that only one command at time will be handeled
Read access server channel tree: read lock channel_tree_lock
Write access server channel tree: lock channel_tree_lock
Write access client channel tree: read lock channel_tree_lock => lock client channel tree
if we write to the server channel tree no client should have their channel tree updated
Read access client channel tree: no lock required
Note: the server channel tree should not be accessed!
Move client acts like access server channel tree: write lock channel_tree_lock => for each client write lock their tree
TODO: Some kind of perm channel lock
TODO: Fix handleCommandChannelEdit
Test: Channel hide & show with clients! Multible clients as well!

434
server/main.cpp Normal file
View File

@ -0,0 +1,434 @@
#include <client/linux/handler/exception_handler.h>
#include <iostream>
#include <misc/endianness.h>
#include <CXXTerminal/QuickTerminal.h>
#include <event2/thread.h>
#include <log/LogUtils.h>
#include <ThreadPool/Timer.h>
#include "src/Configuration.h"
#include "src/TSServer.h"
#include "src/InstanceHandler.h"
#include "src/server/QueryServer.h"
#include "src/server/file/FileServer.h"
#include "src/terminal/CommandHandler.h"
#include "src/client/InternalClient.h"
#include "src/SignalHandler.h"
#include "src/build.h"
using namespace std;
using namespace std::chrono;
#define BUILD_CREATE_TABLE(tblName, types) "CREATE TABLE IF NOT EXISTS `" tblName "` (" types ")"
#define CREATE_TABLE(table, types) \
result = sql::command(sqlData, BUILD_CREATE_TABLE(table, types)).execute();\
if(!result){\
logger::logger(0)->critical("Could not setup sql tables. Command '{}' returns {}", BUILD_CREATE_TABLE(table, types), result.fmtStr());\
goto stopApp;\
}
bool mainThreadActive = true;
bool mainThreadDone = false;
ts::server::InstanceHandler* serverInstance = nullptr;
extern void testTomMath();
#ifndef FUCK_CLION
#define DB_NAME_BEG "TeaData"
#define DB_NAME_END ".sqlite"
#define DB_NAME DB_NAME_BEG DB_NAME_END
#else
#define DB_NAME "TeaData.sqlite"
#endif
#include <regex>
#include <codecvt>
#include "src/client/music/internal_provider/channel_replay/ChannelProvider.h"
class CLIParser{
public:
CLIParser (int &argc, char **argv){
for (int i = 1; i < argc; i++)
this->tokens.emplace_back(argv[i]);
}
std::deque<std::string> getCmdOptions(const std::string &option) const {
std::deque<std::string> result;
auto itr = this->tokens.begin();
while(true) {
itr = std::find(itr, this->tokens.end(), option);
if (itr != this->tokens.end() && ++itr != this->tokens.end()){
result.push_back(*itr);
itr++;
} else break;
}
return result;
}
std::deque<std::string> getCmdOptionsBegins(const std::string &option) const {
std::deque<std::string> result;
for(const auto& token : this->tokens)
if(token.find(option) == 0)
result.push_back(token);
return result;
}
const std::string& get_option(const std::string &option) const {
auto itr = std::find(this->tokens.begin(), this->tokens.end(), option);
if (itr != this->tokens.end() && ++itr != this->tokens.end()){
return *itr;
}
static const std::string empty_string;
return empty_string;
}
bool cmdOptionExists(const std::string &option) const{
return std::find(this->tokens.begin(), this->tokens.end(), option) != this->tokens.end();
}
private:
std::vector <std::string> tokens;
};
/* addr is where the exception identifier is stored
id is the exception identifier. */
void __raise_exception (void **addr, void *id);
#define T(address) \
std::cout << "Testing: " << address << " => "; \
{\
sockaddr_storage storage;\
net::resolve_address(address, storage);\
std::cout << manager.contains(storage) << std::endl;\
}
#define CONFIG_NAME "config.yml"
const char *malloc_conf = ""; //retain:false"; //,dirty_decay_ms:0";
int main(int argc, char** argv) {
#ifdef HAVE_JEMALLOC
(void*) malloc_conf;
#endif
CLIParser arguments(argc, argv);
SSL_load_error_strings();
OpenSSL_add_ssl_algorithms();
ts::permission::setup_permission_resolve();
{
u_char buffer[SHA512_DIGEST_LENGTH];
SHA512_CTX md{};
SHA512_Init(&md);
SHA512_Update(&md, "Hello World", 11);
SHA512_Final(buffer,&md);
}
{
auto evthread_use_pthreads_result = evthread_use_pthreads();
assert(evthread_use_pthreads_result == 0);
}
terminal::install();
if(!terminal::active()){ cerr << "could not setup terminal!" << endl; return -1; }
assert(ts::property::impl::validateUnique());
if(arguments.cmdOptionExists("--help") || arguments.cmdOptionExists("-h")) {
#define HELP_FMT " {} {} | {}"
logMessageFmt(true, LOG_GENERAL, "Available command line parameters:");
logMessageFmt(true, LOG_GENERAL, HELP_FMT, "-h", "--help", "Shows this page");
logMessageFmt(true, LOG_GENERAL, HELP_FMT, "-q", "--set_query_password", "Changed the server admin query password");
logMessageFmt(true, LOG_GENERAL, HELP_FMT, "-P<property>=<value>", "--property:<property>=<value>", "Override a config value manual");
logMessageFmt(true, LOG_GENERAL, HELP_FMT, "-l", "--property-list", "List all available properties");
terminal::uninstall();
return 0;
}
if(arguments.cmdOptionExists("--property-list") || arguments.cmdOptionExists("-l")) {
logMessageFmt(true, LOG_GENERAL, "Available properties:");
auto properties = ts::config::create_bindings();
for(const auto& property : properties) {
logMessageFmt(true, LOG_GENERAL, " " + property->key);
for(const auto& entry : property->description) {
if(entry.first.empty()) {
for(const auto& line : entry.second)
logMessageFmt(true, LOG_GENERAL, " " + line);
} else {
logMessageFmt(true, LOG_GENERAL, " " + entry.first + ":");
for(const auto& line : entry.second)
logMessageFmt(true, LOG_GENERAL, " " + line);
}
}
logMessageFmt(true, LOG_GENERAL, " " + property->value_description());
}
return 0;
}
if(!arguments.cmdOptionExists("--valgrind")) {
ts::syssignal::setup();
}
ts::syssignal::setup_threads();
map<string, string> override_settings;
{
auto short_override = arguments.getCmdOptionsBegins("-P");
for(const auto& entry : short_override) {
if(entry.length() < 2) continue;
auto ei = entry.find('=');
if(ei == string::npos || ei == 2) {
logErrorFmt(true, LOG_GENERAL, "Invalid command line parameter. (\"" + entry + "\")");
return 1;
}
auto key = entry.substr(2, ei - 2);
auto value = entry.substr(ei + 1);
override_settings[key] = value;
}
}
{
auto short_override = arguments.getCmdOptionsBegins("--property:");
for(const auto& entry : short_override) {
if(entry.length() < 11) continue;
auto ei = entry.find('=');
if(ei == string::npos || ei == 11) {
logErrorFmt(true, LOG_GENERAL, "Invalid command line parameter. (\"" + entry + "\")");
return 1;
}
auto key = entry.substr(11, ei - 11);
auto value = entry.substr(ei + 1);
override_settings[key] = value;
}
}
{
auto bindings = ts::config::create_bindings();
for(const auto& setting : bindings) {
for(auto it = override_settings.begin(); it != override_settings.end(); it++) {
if(it->first == setting->key) {
try {
setting->read_argument(it->second);
} catch(const std::exception& ex) {
logErrorFmt(true, LOG_GENERAL, "Failed to apply value for given property '" + it->first + "': " + ex.what());
}
override_settings.erase(it);
break;
}
}
}
for(const auto& entry : override_settings) {
logMessageFmt(true, LOG_GENERAL, "Missing property " + entry.first + ". Value unused!");
}
}
/*
std::string error;
if(!interaction::waitForAttach(error)){
cerr << "Rsult: " << error << endl;
}
while(interaction::memoryInfo()){
usleep(1 * 1000 * 1000);
logMessage("Current instances: " + to_string(interaction::memoryInfo()->instanceCount) + "/" + to_string(interaction::memoryInfo()->maxInstances));
}
interaction::removeMemoryHook();
if(true) return 0;
*/
//debugMessage(LOG_GENERAL, "Sizeof ViewEntry {} Sizeof LinkedTreeEntry {} Sizeof shared_ptr<ViewEntry> {} Sizeof ClientChannelView {}", sizeof(ts::ViewEntry), sizeof(ts::TreeView::LinkedTreeEntry), sizeof(shared_ptr<ts::ViewEntry>), sizeof(ts::ClientChannelView));
{
//http://git.mcgalaxy.de/WolverinDEV/tomcrypt/blob/develop/src/misc/crypt/crypt_inits.c#L40-86
std::string descriptors = "LTGE";
bool crypt_init = false;
for(const auto& c : descriptors)
if(crypt_init |= crypt_mp_init(&c))
break;
if(!crypt_init) {
logCritical(LOG_GENERAL, "Could not initialise libtomcrypt mp descriptors!");
return 1;
}
if(register_prng(&sprng_desc) == -1) {
cerr << "could not setup prng" << endl;
return EXIT_FAILURE;
}
if (register_cipher(&rijndael_desc) == -1) {
cerr << "could not setup rijndael" << endl;
return EXIT_FAILURE;
}
testTomMath();
}
ts::server::SqlDataManager* sql = nullptr;
std::string errorMessage;
shared_ptr<logger::LoggerConfig> logConfig = nullptr;
std::string line;
logMessage("Loading configuration");
terminal::instance()->writeMessage("Loading configuration");
auto cfgErrors = ts::config::parseConfig(CONFIG_NAME);
if(!cfgErrors.empty()){
logError("Could not load configuration. Errors: (" + to_string(cfgErrors.size()) + ")");
for(const auto& entry : cfgErrors)
logError(" - " + entry);
logError("Stopping server...");
goto stopApp;
}
logMessage("Setting up log");
logConfig = make_shared<logger::LoggerConfig>();
logConfig->logfileLevel = (spdlog::level::level_enum) ts::config::log::logfileLevel;
logConfig->terminalLevel = (spdlog::level::level_enum) ts::config::log::terminalLevel;
logConfig->file_colored = ts::config::log::logfileColored;
logConfig->logPath = ts::config::log::path;
logConfig->vs_group_size = ts::config::log::vs_size;
logger::setup(logConfig);
threads::timer::function_log = [](const std::string& message, bool debug) {
if(debug)
debugMessage(LOG_GENERAL, message);
else
logWarning(LOG_GENERAL, message);
};
logger::updateLogLevels();
if(ts::config::license_original && ts::config::license_original->data.type != license::LicenseType::DEMO){
logMessageFmt(true, LOG_GENERAL, "[]---------------------------------------------------------[]");
logMessageFmt(true, LOG_GENERAL, " §aThank you for buying the TeaSpeak-§lPremium-§aSoftware! ");
logMessageFmt(true, LOG_GENERAL, " §aLicense information:");
logMessageFmt(true, LOG_GENERAL, " §aLicense owner : §e" + ts::config::license_original->owner());
logMessageFmt(true, LOG_GENERAL, " §aLicense type : §e" + license::LicenseTypeNames[ts::config::license_original->data.type]);
if(ts::config::license_original->end().time_since_epoch().count() == 0){
logMessageFmt(true, LOG_GENERAL, " §aLicense expires: §enever");
} else {
char timeBuffer[32];
time_t t = duration_cast<seconds>(ts::config::license_original->end().time_since_epoch()).count();
tm* stime = localtime(&t);
strftime(timeBuffer, 32, "%c", stime);
logMessageFmt(true, LOG_GENERAL, " §aLicense expires: §e" + string(timeBuffer));
}
logMessage(string() + " §aLicense valid : " + (ts::config::license_original->isValid() ? "§ayes" : "§cno"));
logMessageFmt(true, LOG_GENERAL, "[]---------------------------------------------------------[]");
}
logMessage(LOG_GENERAL, "Starting TeaSpeak-Server v{}", build::version()->string(true));
logMessage(LOG_GENERAL, "Starting music providers");
terminal::instance()->setPrompt("§aStarting server. §7[§aloading music§7]");
if(ts::config::music::enabled && !arguments.cmdOptionExists("--valgrind")) {
::music::manager::loadProviders("providers");
::music::manager::register_provider(::music::provider::ChannelProvider::create_provider());
}
terminal::instance()->setPrompt("§aStarting server. §7[§aloading geoloc§7]");
if(!ts::config::geo::staticFlag) {
if(ts::config::geo::type == geoloc::PROVIDER_SOFTWARE77)
geoloc::provider = new geoloc::Software77Provider(ts::config::geo::mappingFile);
else if(ts::config::geo::type == geoloc::PROVIDER_IP2LOCATION)
geoloc::provider = new geoloc::IP2LocationProvider(ts::config::geo::mappingFile);
else {
logCritical("Invalid geo resolver type!");
}
if(geoloc::provider && !geoloc::provider->load(errorMessage)) {
logCritical("Could not setup geoloc! Fallback to default flag!");
logCritical("Message: " + errorMessage);
geoloc::provider = nullptr;
errorMessage = "";
}
}
if(ts::config::geo::vpn_block) {
geoloc::provider_vpn = new geoloc::IPCatBlocker(ts::config::geo::vpn_file);
if(geoloc::provider_vpn && !geoloc::provider_vpn->load(errorMessage)) {
logCritical("Could not setup vpn detector!");
logCritical("Message: " + errorMessage);
geoloc::provider_vpn = nullptr;
errorMessage = "";
}
}
terminal::instance()->setPrompt("§aStarting server. §7[§aloading sql§7]");
sql = new ts::server::SqlDataManager();
if(!sql->initialize(errorMessage)) {
logCritical("Could not initialize SQL!");
if(errorMessage.find("database is locked") != string::npos) {
logCriticalFmt(true, LOG_GENERAL, "----------------------------[ ATTENTION ]----------------------------");
logCriticalFmt(true, LOG_GENERAL, "{:^69}", "You're database is already in use!");
logCriticalFmt(true, LOG_GENERAL, "{:^69}", "Stop the other instance first!");
logCriticalFmt(true, LOG_GENERAL, "----------------------------[ ATTENTION ]----------------------------");
} else {
logCriticalFmt(true, LOG_GENERAL, errorMessage);
}
goto stopApp;
}
terminal::instance()->setPrompt("§aStarting server. §7[§astarting instance§7]");
serverInstance = new ts::server::InstanceHandler(sql); //if error than mainThreadActive = false
if(!mainThreadActive || !serverInstance->startInstance())
goto stopApp;
if(arguments.cmdOptionExists("-q") || arguments.cmdOptionExists("--set_query_password")) {
auto password = arguments.cmdOptionExists("-q") ? arguments.get_option("-q") : arguments.get_option("--set_query_password");
if(!password.empty()) {
logMessageFmt(true, LOG_GENERAL, "Updating server admin query password to \"{}\"", password);
auto accounts = serverInstance->getQueryServer()->find_query_accounts_by_unique_id(serverInstance->getInitalServerAdmin()->getUid());
bool found = false;
for(const auto& account : accounts) {
if(account->bound_server != 0) continue;
if(!serverInstance->getQueryServer()->change_query_password(account, password)) {
logErrorFmt(true, LOG_GENERAL, "Failed to update server admin query password! (Internal error)");
}
found = true;
break;
}
if(!found) {
logErrorFmt(true, LOG_GENERAL, "Failed to update server admin query password! Login does not exists!");
}
}
}
terminal::instance()->setPrompt("§7> §f");
while(mainThreadActive) {
usleep(5 * 1000);
if(terminal::instance()->linesAvailable() > 0){
while(!(line = terminal::instance()->readLine("§7> §f")).empty())
threads::Thread(THREAD_DETACHED, [line](){ terminal::chandler::handleCommand(line); });
}
}
stopApp:
logMessage("Stopping application");
if(serverInstance)
serverInstance->stopInstance();
delete serverInstance;
serverInstance = nullptr;
if(sql)
sql->finalize();
delete sql;
logMessage("Application suspend successful!");
logger::uninstall();
terminal::uninstall();
mainThreadDone = true;
return 0;
}
/*
* 2][OUT] (188.225.34.225:9988) ftinitdownload clientftfid=4096 name=\/icon_166694597 cid=0 cpw seekpos=0 proto=1 return_code=
[02][OUT] (188.225.34.225:9988) ftinitdownload clientftfid=4095 name=\/icon_4113966246 cid=0 cpw seekpos=0 proto=1 return_code=
[02][OUT] (188.225.34.225:9988) ftinitdownload clientftfid=4094 name=\/icon_3002705295 cid=0 cpw seekpos=0 proto=1 return_code=
[02][OUT] (188.225.34.225:9988) ftinitdownload clientftfid=4093 name=\/icon_494035633 cid=0 cpw seekpos=0 proto=1 return_code=
[02][OUT] (188.225.34.225:9988) ftinitdownload clientftfid=4092 name=\/icon_847789427 cid=0 cpw seekpos=0 proto=1 return_code=
[02][ IN] (188.225.34.225:9988) notifyclientupdated clid=5 client_version=3.2.0\s[Build:\s1533739581] client_platform=Linux client_login_name=WolverinDEV client_created=1536521950 client_lastconnected=1536522252 client_totalconnections=2 client_month_bytes_uploaded=0 client_month_bytes_downloaded=0 client_total_bytes_uploaded=0 client_total_bytes_downloaded=0 client_icon_id=0 client_country=DE
[02][ IN] (188.225.34.225:9988) notifystartdownload clientftfid=4096 proto=1 serverftfid=1 ftkey=R0Vcnx4fNdrXuMFg port=30303 size=1086
[02][ IN] (188.225.34.225:9988) notifystartdownload clientftfid=4095 proto=1 serverftfid=1 ftkey=3eYwsuviQvTWme42 port=30303 size=822
[02][ IN] (188.225.34.225:9988) notifystartdownload clientftfid=4094 proto=1 serverftfid=1 ftkey=dM5oaVuLYLwia2me port=30303 size=852
[02][ IN] (188.225.34.225:9988) notifystartdownload clientftfid=4093 proto=1 serverftfid=1 ftkey=60BltUu8fbUqgLhj port=30303 size=3441
[02][ IN] (188.225.34.225:9988) notifystartdownload clientftfid=4092 proto=1 serverftfid=1 ftkey=a0wmURVHqhNE71H2 port=30303 size=1452
*/

25
server/repro/build.sh Executable file
View File

@ -0,0 +1,25 @@
#!/usr/bin/env bash
BUILD_PATH=$1
if [ -z ${BUILD_PATH} ]; then
echo "Missing versions path!"
exit 1
fi
./generate_version.sh ${BUILD_PATH}
if [ $? -ne 0 ]; then
echo "Failed to generate version!"
exit 1
fi
./package_server.sh ${BUILD_PATH}
if [ $? -ne 0 ]; then
echo "Failed to package server!"
exit 1
fi
./deploy_build.sh ${BUILD_PATH}
if [ $? -ne 0 ]; then
echo "Failed to deploy package!"
exit 1
fi

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA8Vu+RBxR2fIKke2b3yb731nB+BwkkVUO0cHAw2p/WJGyZOZv
o4y5gQzfTxWbOGLM33BrneaWmmQIz59bStaBhW7MABP+wTpRHUFktITHMZmvz4Xm
GHqQWFoU8mlyndzv9l4tdtApbGE3Yn6jiToKZJX+5EyUTZe6YwBAcuzxyO7YVEmB
87cPCsciir+ZQUS+MqyPY7GSy1jxCBZ80AMIOSIuEZaOBZaDEhvDJbBAugYIaw1P
jp06DmImudXoXi71Wv0xmM7f0KFryeO0Hxq01ds1pes8S5bslCaNRX395R3Q1sdl
R27eskVuXMLDPJSQTwd3WfQ9d1r5/BYTofzmcQIDAQABAoIBAQDqpBdQHfwRFvbh
sY8kncCl/ZvOOoXuaDO1BlkBYeqVz2cQItqLtIaviDUcrFOvuJWV77Qf2Qm25OOP
/UuCcRGQCAv5U3cKoUg0Wduuh5sjhFbgODtetuDXlBPjK1KLWDxNVnd6l5p9y/FN
JvKTuUJbUVtw1WYkHQrNrnP5hpL0lCN098F+aaXPgdXtyJhGGj5dlyFwezC1nTxB
LIjjINg9JlWfmhnsszkLYLKIzj2x+i42k/bQwlXvxiPOUmDELc4Z6NZRpsMy2og3
qNqMGp895R3tSjaPEgISpXPZJovIJutJO/ci3MeIMRV1vtqv54qeJpNgoQZYyHkE
RsZecRT1AoGBAP6q9bZdN1mAD0+xsYLsFBEXUIWbaLsb3rOtBgaYCQYrWxweD6wo
rbg2J9lJ9k0mZtSy86a3sPB83H3no5oE8HOoofnepE9FVhXQE39jwAg4NwzDh2WZ
feEcVF/8SSvlPwkFZ8SlkJmkVZ21TJC7/w5OiTrVJtiEL64XIq26zZxLAoGBAPKe
9cF2OLfIhk7OJeTNyvt3iO5w+XhGPebQYhyYFrL/1Cpu7esQowAen/MHiB40+luo
wjvhSfPfKG9htAwpXPhZlRzFutQCGJqjlAwhRDt1UamXhY3wt2PrBwxKpa/MoTDK
wDcSVTnBgfCR6GVJd8rfurrEoYWNh7IT26MNaxqzAoGBANt4O98YgF3KRee5TDB3
AWglPoiWAPDXONqBbyL5nTVK4e7eXUVRnTyWt0rEOdYNFSuSuPXhckQeZuq+WLig
LxSNrGGpJNMH/wM3WcY8Eb9DqvV+AE5ntBrvDivWQLzqiKNenCnVQUOXYw1RtBU7
XrhWqF8iWKLZcDnIyGMODKn3AoGAfQhqgg7Y3+2ZOZeHc8iSaNGLYcYa6l7Ym9Cc
HzxJxmmM/2k3d0KVngQqTeZ9wYNv2ji4EH/jyqgggHYLgZD3do1ECRXlWEjUQS03
qKCkNzgYo5uQmjuJZxbCBRWbGWQNVcXHFRp/jUoqGr4206vu7kAqTQH0c40idyVA
tWC+530CgYBjacl9NtsJD7ulcS9gtQ5J+d3SrQuYVjCNyCJP0YuDBGZ8IE04qGJE
1JTmrZFTpFe/Em27N3PETB/Tm3PHgxmATcHXJOS+K63lwkgqY20+5fBCEqHb6hgj
i5n/Z6GgtZQYUusrLSBsFrUH9cYtcmmOPYIgnmr/CaEJQx1AByeTTg==
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDxW75EHFHZ8gqR7ZvfJvvfWcH4HCSRVQ7RwcDDan9YkbJk5m+jjLmBDN9PFZs4YszfcGud5paaZAjPn1tK1oGFbswAE/7BOlEdQWS0hMcxma/PheYYepBYWhTyaXKd3O/2Xi120ClsYTdifqOJOgpklf7kTJRNl7pjAEBy7PHI7thUSYHztw8KxyKKv5lBRL4yrI9jsZLLWPEIFnzQAwg5Ii4Rlo4FloMSG8MlsEC6BghrDU+OnToOYia51eheLvVa/TGYzt/QoWvJ47QfGrTV2zWl6zxLluyUJo1Fff3lHdDWx2VHbt6yRW5cwsM8lJBPB3dZ9D13Wvn8FhOh/OZx build@teaspeak.de

53
server/repro/deploy_build.sh Executable file
View File

@ -0,0 +1,53 @@
#!/usr/bin/env bash
BUILD_PATH=$1
if [[ -z ${BUILD_PATH} ]]; then
echo "Missing versions path!"
#exit 1
fi
BUILD_INFO=($(cat build_version.txt))
BUILD_FULL_NAME=${BUILD_INFO[0]}
BUILD_NAME=${BUILD_INFO[1]}
BUILD_VERSION=${BUILD_INFO[2]}
BUILD_FILENAME=${BUILD_INFO[3]}
echo "Publishing build ${BUILD_FILENAME}"
if [[ ! -f ${BUILD_FILENAME} ]]; then
echo "Failed to find file!"
exit 1
fi
if [[ -d symbols ]]; then
echo "Uploading symbols"
scp -i build_private_key -rpC symbols/ TeaSpeak-Jenkins@mcgalaxy.de:symbols/
if [[ $? -ne 0 ]]; then
echo "Failed to upload symbols!"
exit 1
fi
rm -r symbols/
else
echo "Failed to find symbols! Skipping step!"
fi
echo "Creating versions mark"
ssh -i build_private_key TeaSpeak-Jenkins@mcgalaxy.de "
if [ ! -d versions/${BUILD_PATH} ]; then
mkdir -p versions/${BUILD_PATH}
fi
if [ ! -d files/${BUILD_PATH} ]; then #Creating for files as well
mkdir -p files/${BUILD_PATH}
fi
echo '' > versions/${BUILD_PATH}/${BUILD_FULL_NAME}
echo '${BUILD_FULL_NAME}' > versions/${BUILD_PATH}/latest"
if [[ $? -ne 0 ]]; then
echo "Failed to create versions mark!"
exit 1
fi
echo "Uploading build (${BUILD_FILENAME})"
scp -i build_private_key -pC "${BUILD_FILENAME}" "TeaSpeak-Jenkins@mcgalaxy.de:files/${BUILD_PATH}/"
if [[ $? -ne 0 ]]; then
echo "Failed to upload version!"
exit 1
fi

Some files were not shown because too many files have changed in this diff Show More