Reinitialized project to reduce the git branch size
This commit is contained in:
parent
3130cc33fe
commit
5715acbcb2
28
.gitignore
vendored
Normal file
28
.gitignore
vendored
Normal 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
4
.gitmodules
vendored
@ -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
68
CMakeLists.txt
Normal 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
21
MusicBot/CMakeLists.txt
Normal 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
198
MusicBot/MavStuff.cpp
Normal 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(¶ms);
|
||||
|
||||
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
249
MusicBot/Protocol.md
Normal 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
210
MusicBot/main.cpp
Normal 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(¶ms);
|
||||
|
||||
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;
|
||||
}
|
||||
115
MusicBot/src/MusicPlayer.cpp
Normal file
115
MusicBot/src/MusicPlayer.cpp
Normal 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
79
client/CMakeLists.txt
Normal 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
29
client/HideString.h
Normal 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
27
client/changes.md
Normal 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
63
client/identityHash.cpp
Normal 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
1176
client/main.cpp
Normal file
File diff suppressed because it is too large
Load Diff
17
client/proto/LicenseKey.proto
Normal file
17
client/proto/LicenseKey.proto
Normal 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
218
client/src/Identity.cpp
Normal 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, <c_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, <c_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
44
client/src/Identity.h
Normal 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;
|
||||
};
|
||||
}
|
||||
189
client/src/MultithreadedIdentity.cpp
Normal file
189
client/src/MultithreadedIdentity.cpp
Normal 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
196
client/src/TinySHA1.hpp.h
Normal 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
|
||||
454
client/src/protocol/Connection.cpp
Normal file
454
client/src/protocol/Connection.cpp
Normal 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);
|
||||
}
|
||||
139
client/src/protocol/Connection.h
Normal file
139
client/src/protocol/Connection.h
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
||||
361
client/src/protocol/ConnectionHandschake.cpp
Normal file
361
client/src/protocol/ConnectionHandschake.cpp
Normal 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;
|
||||
}
|
||||
158
client/src/protocol/ConnectionPacketHandler.cpp
Normal file
158
client/src/protocol/ConnectionPacketHandler.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
154
client/src/protocol/HandshakeNew.cpp
Normal file
154
client/src/protocol/HandshakeNew.cpp
Normal 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;
|
||||
}
|
||||
115
client/src/protocol/socket/FilteredUDPSocket.cpp
Normal file
115
client/src/protocol/socket/FilteredUDPSocket.cpp
Normal 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);
|
||||
}
|
||||
27
client/src/protocol/socket/FilteredUDPSocket.h
Normal file
27
client/src/protocol/socket/FilteredUDPSocket.h
Normal 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;
|
||||
}
|
||||
}
|
||||
151
client/src/protocol/socket/RawUDPSocket.cpp
Normal file
151
client/src/protocol/socket/RawUDPSocket.cpp
Normal 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;
|
||||
}
|
||||
28
client/src/protocol/socket/RawUDPSocket.h
Normal file
28
client/src/protocol/socket/RawUDPSocket.h
Normal 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;
|
||||
}
|
||||
}
|
||||
122
cmake/Modules/FindGLIB.cmake
Normal file
122
cmake/Modules/FindGLIB.cmake
Normal 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
|
||||
)
|
||||
34
cmake/Modules/FindLibNice.cmake
Normal file
34
cmake/Modules/FindLibNice.cmake
Normal 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 ()
|
||||
8
cmake/Modules/FindSpdlog.cmake
Normal file
8
cmake/Modules/FindSpdlog.cmake
Normal 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 ()
|
||||
24
cmake/Modules/FindUsrSCTP.cmake
Normal file
24
cmake/Modules/FindUsrSCTP.cmake
Normal 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
25
flooder/CMakeLists.txt
Normal 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
81
flooder/main.cpp
Normal 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);
|
||||
}
|
||||
111
flooder/src/PorxiedClientSock5.cpp
Normal file
111
flooder/src/PorxiedClientSock5.cpp
Normal 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));
|
||||
}
|
||||
175
flooder/src/ProxiedClient.cpp
Normal file
175
flooder/src/ProxiedClient.cpp
Normal 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();
|
||||
}
|
||||
72
flooder/src/ProxiedClient.h
Normal file
72
flooder/src/ProxiedClient.h
Normal 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
108
flooder/src/TSClient.cpp
Normal 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
36
flooder/src/TSClient.h
Normal 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
162
license/CMakeLists.txt
Normal 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
|
||||
)
|
||||
87
license/LicenseClientMain.cpp
Normal file
87
license/LicenseClientMain.cpp
Normal 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;
|
||||
}
|
||||
138
license/LicenseCreatorCLI.cpp
Normal file
138
license/LicenseCreatorCLI.cpp
Normal 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;
|
||||
}
|
||||
97
license/LicenseManager.cpp
Normal file
97
license/LicenseManager.cpp
Normal 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();
|
||||
}
|
||||
240
license/LicenseServerMain.cpp
Normal file
240
license/LicenseServerMain.cpp
Normal 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;
|
||||
}
|
||||
225
license/manager/ServerConnection.cpp
Normal file
225
license/manager/ServerConnection.cpp
Normal 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);
|
||||
}
|
||||
113
license/manager/ServerConnection.h
Normal file
113
license/manager/ServerConnection.h
Normal 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&);
|
||||
};
|
||||
}
|
||||
}
|
||||
93
license/manager/ServerConnectionExecutor.cpp
Normal file
93
license/manager/ServerConnectionExecutor.cpp
Normal 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});
|
||||
}
|
||||
180
license/manager/ServerConnectionHandler.cpp
Normal file
180
license/manager/ServerConnectionHandler.cpp
Normal 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());
|
||||
}
|
||||
24
license/manager/qtHelper.h
Normal file
24
license/manager/qtHelper.h
Normal 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));
|
||||
}
|
||||
193
license/manager/ui/LicenseGenerator.cpp
Normal file
193
license/manager/ui/LicenseGenerator.cpp
Normal 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!");
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
26
license/manager/ui/LicenseGenerator.h
Normal file
26
license/manager/ui/LicenseGenerator.h
Normal 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();
|
||||
};
|
||||
}
|
||||
}
|
||||
68
license/manager/ui/LoginWindow.cpp
Normal file
68
license/manager/ui/LoginWindow.cpp
Normal 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));
|
||||
};
|
||||
24
license/manager/ui/LoginWindow.h
Normal file
24
license/manager/ui/LoginWindow.h
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
||||
173
license/manager/ui/Overview.cpp
Normal file
173
license/manager/ui/Overview.cpp
Normal 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);
|
||||
}
|
||||
}, {});
|
||||
});
|
||||
}
|
||||
69
license/manager/ui/Overview.h
Normal file
69
license/manager/ui/Overview.h
Normal 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>);
|
||||
};
|
||||
}
|
||||
}
|
||||
112
license/manager/ui/UiLicenseInfo.cpp
Normal file
112
license/manager/ui/UiLicenseInfo.cpp
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
29
license/manager/ui/UiLicenseInfo.h
Normal file
29
license/manager/ui/UiLicenseInfo.h
Normal 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();
|
||||
};
|
||||
}
|
||||
}
|
||||
351
license/manager/ui/licensegenerator.ui
Normal file
351
license/manager/ui/licensegenerator.ui
Normal 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><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><title>asdasdasd</title><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'Ubuntu Mono'; font-size:12pt; font-weight:400; font-style:normal;">
|
||||
<p style="-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;"><br /></p></body></html></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>
|
||||
242
license/manager/ui/licenseinfo.ui
Normal file
242
license/manager/ui/licenseinfo.ui
Normal 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>
|
||||
92
license/manager/ui/loginwindow.ui
Normal file
92
license/manager/ui/loginwindow.ui
Normal 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><html><head/><body><p><span style=" font-size:14pt;">Username:</span></p></body></html></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><html><head/><body><p><span style=" font-size:14pt;">Password:</span></p></body></html></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>
|
||||