Refactored (#31)

* Build system changed to CMake
* Converted tabs to spaces
* Removed all usages of boost::log
* Replaced boost::thread with std::thread
* Update copyright year
* Added a tiny bit of unit tests
* Added a simple message queue
* Added basic independent logging
* Added CONTRIBUTING
This commit is contained in:
Naim A 2017-09-27 04:33:21 +03:00 committed by Naim A
parent 7193f945a9
commit f34fcdbd04
No known key found for this signature in database
GPG key ID: FD7948915D9EF8B9
29 changed files with 3243 additions and 3091 deletions

44
.github/CONTRIBUTING.md vendored Normal file
View file

@ -0,0 +1,44 @@
# Contributing
Thank you for looking into [UDPT](https://github.com/naim94a/udpt)!
This document lists various ways to contribute to UDPT. If you can, please do.
## Bug Reports
Bug reports are always welcome, they help us maintain a quality product.
Before submitting a bug report, please first check that the issue does not already exist and that the effected version
is the most recent one.
* If a similar (or same) issue already exists, please upvote, comment, elaborate on the existing issue.
* If you found a new issue, please attach the used configuration and how to cause the bug to appear.
* Core dumps are always welcome
Bugs should be reported at [UDPT's issue tracker](https://github.com/naim94a/udpt) on GitHub.
## Suggesting new features
New features are welcome, it doesn't mean they will enter immediately.
Suggestions should be filed as issues along with bug reports, just add the "enhancement" label to the created issue.
## Donations
Please show us your appreciation by donating BitCoin at bitcoin:1KMeZvcgnmWdHitu51yEFWBNcSTXL1eBk3.
![bitcoin:1KMeZvcgnmWdHitu51yEFWBNcSTXL1eBk3](bitcoin-qr.png)
## Pull requests
Before submitting a pull request, please check that the changes don't effect other platforms or have any unwanted
side-effects.
Pull Requests should address open issue(s), where the project owner and contributors can discuss how the issue should be
addressed.
Pull requests that add new features should be first mentioned in issues in order to verify that the project owner will
allow such changes to the project.
Code in pull requests should meet the following requirements:
* Class names should be CamelCase, and the first letter should be capital.
* Functions should be camelCase, and the first letter should be lower-case.
* Class members should be prefixed with `m_`, for example: `bool m_isRunning`
* All variables, classes, functions and class members should have an indicative name.
* Adding external libraries is allowed, but frowned upon

BIN
.github/bitcoin-qr.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View file

@ -1,15 +1,24 @@
project(udpt)
cmake_minimum_required(VERSION 3.6)
cmake_minimum_required(VERSION 3.2)
enable_testing()
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_definitions(-DBOOST_LOG_DYN_LINK)
file(GLOB src_files "src/*.c"
"src/*.cpp"
"src/db/*.cpp"
"src/http/*.cpp")
LIST(APPEND LIBS "pthread" "sqlite3" "boost_program_options" "boost_system")
add_executable(udpt ${src_files})
target_link_libraries(udpt pthread sqlite3 boost_log boost_program_options boost_thread boost_system)
target_link_libraries(udpt ${LIBS})
add_executable(udpt_tests tests/main.cpp ${src_files})
target_compile_definitions(udpt_tests PRIVATE TEST=1)
target_link_libraries(udpt_tests gtest ${LIBS})
add_test(NAME udpt_tests COMMAND udpt_tests)

64
src/MessageQueue.hpp Normal file
View file

@ -0,0 +1,64 @@
/*
* Copyright © 2012-2017 Naim A.
*
* This file is part of UDPT.
*
* UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <queue>
#include <mutex>
namespace UDPT
{
namespace Utils {
template<class T>
class MessageQueue {
public:
MessageQueue() {}
virtual ~MessageQueue() {}
bool IsEmpty() const {
return m_queue.empty();
}
T Pop() {
m_queueMutex.lock();
T val = m_queue.front();
m_queue.pop();
m_queueMutex.unlock();
return val;
}
void Push(T obj) {
m_queueMutex.lock();
m_queue.push(obj);
m_queueMutex.unlock();
}
size_t Count() const {
return m_queue.size();
}
private:
std::queue<T> m_queue;
std::mutex m_queueMutex;
};
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2012-2016 Naim A.
* Copyright © 2012-2017 Naim A.
*
* This file is part of UDPT.
*
@ -21,100 +21,100 @@
namespace UDPT
{
namespace Data
{
DatabaseDriver::DatabaseDriver(const boost::program_options::variables_map& conf, bool isDynamic) : m_conf(conf)
{
this->is_dynamic = isDynamic;
}
namespace Data
{
DatabaseDriver::DatabaseDriver(const boost::program_options::variables_map& conf, bool isDynamic) : m_conf(conf)
{
this->is_dynamic = isDynamic;
}
bool DatabaseDriver::addTorrent(uint8_t hash [20])
{
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
}
bool DatabaseDriver::addTorrent(uint8_t hash [20])
{
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
}
bool DatabaseDriver::removeTorrent(uint8_t hash[20])
{
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
}
bool DatabaseDriver::removeTorrent(uint8_t hash[20])
{
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
}
bool DatabaseDriver::isDynamic()
{
return this->is_dynamic;
}
bool DatabaseDriver::isDynamic()
{
return this->is_dynamic;
}
bool DatabaseDriver::genConnectionId(uint64_t *cid, uint32_t ip, uint16_t port)
{
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
}
bool DatabaseDriver::genConnectionId(uint64_t *cid, uint32_t ip, uint16_t port)
{
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
}
bool DatabaseDriver::verifyConnectionId(uint64_t cid, uint32_t ip, uint16_t port)
{
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
}
bool DatabaseDriver::verifyConnectionId(uint64_t cid, uint32_t ip, uint16_t port)
{
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
}
bool DatabaseDriver::updatePeer(uint8_t peer_id [20], uint8_t info_hash [20],
uint32_t ip, uint16_t port,
int64_t downloaded, int64_t left, int64_t uploaded,
enum TrackerEvents event)
{
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
}
bool DatabaseDriver::updatePeer(uint8_t peer_id [20], uint8_t info_hash [20],
uint32_t ip, uint16_t port,
int64_t downloaded, int64_t left, int64_t uploaded,
enum TrackerEvents event)
{
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
}
bool DatabaseDriver::removePeer (uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port)
{
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
}
bool DatabaseDriver::removePeer (uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port)
{
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
}
bool DatabaseDriver::getTorrentInfo (TorrentEntry *e)
{
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
}
bool DatabaseDriver::getTorrentInfo (TorrentEntry *e)
{
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
}
bool DatabaseDriver::getPeers (uint8_t info_hash [20], int *max_count, PeerEntry *pe)
{
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
}
bool DatabaseDriver::getPeers (uint8_t info_hash [20], int *max_count, PeerEntry *pe)
{
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
}
void DatabaseDriver::cleanup()
{
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
}
void DatabaseDriver::cleanup()
{
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
}
bool DatabaseDriver::isTorrentAllowed(uint8_t info_hash[20])
{
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
}
bool DatabaseDriver::isTorrentAllowed(uint8_t info_hash[20])
{
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
}
DatabaseDriver::~DatabaseDriver()
{
}
DatabaseDriver::~DatabaseDriver()
{
}
/*-- Exceptions --*/
static const char *EMessages[] = {
"Unknown Error",
"Not Implemented",
"Failed to connect to database"
};
/*-- Exceptions --*/
static const char *EMessages[] = {
"Unknown Error",
"Not Implemented",
"Failed to connect to database"
};
DatabaseException::DatabaseException()
{
this->errorNum = E_UNKNOWN;
}
DatabaseException::DatabaseException()
{
this->errorNum = E_UNKNOWN;
}
DatabaseException::DatabaseException(enum EType e)
{
this->errorNum = e;
}
DatabaseException::DatabaseException(enum EType e)
{
this->errorNum = e;
}
enum DatabaseException::EType DatabaseException::getErrorType()
{
return this->errorNum;
}
enum DatabaseException::EType DatabaseException::getErrorType()
{
return this->errorNum;
}
const char* DatabaseException::getErrorMessage()
{
return EMessages[this->errorNum];
}
};
const char* DatabaseException::getErrorMessage()
{
return EMessages[this->errorNum];
}
};
};

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2012-2016 Naim A.
* Copyright © 2012-2017 Naim A.
*
* This file is part of UDPT.
*
@ -21,154 +21,152 @@
#define DATABASE_HPP_
#include <boost/program_options.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/sources/severity_channel_logger.hpp>
namespace UDPT
{
namespace Data
{
class DatabaseException
{
public:
enum EType {
E_UNKNOWN = 0, // Unknown error
E_NOT_IMPLEMENTED = 1, // not implemented
E_CONNECTION_FAILURE = 2
};
namespace Data
{
class DatabaseException
{
public:
enum EType {
E_UNKNOWN = 0, // Unknown error
E_NOT_IMPLEMENTED = 1, // not implemented
E_CONNECTION_FAILURE = 2
};
DatabaseException();
DatabaseException(EType);
EType getErrorType();
const char* getErrorMessage();
private:
EType errorNum;
};
DatabaseException();
DatabaseException(EType);
EType getErrorType();
const char* getErrorMessage();
private:
EType errorNum;
};
class DatabaseDriver
{
public:
typedef struct {
uint8_t *info_hash;
int32_t seeders;
int32_t leechers;
int32_t completed;
} TorrentEntry;
typedef struct {
uint32_t ip;
uint16_t port;
} PeerEntry;
class DatabaseDriver
{
public:
typedef struct {
uint8_t *info_hash;
int32_t seeders;
int32_t leechers;
int32_t completed;
} TorrentEntry;
typedef struct {
uint32_t ip;
uint16_t port;
} PeerEntry;
enum TrackerEvents {
EVENT_UNSPEC = 0,
EVENT_COMPLETE = 1,
EVENT_START = 2,
EVENT_STOP = 3
};
enum TrackerEvents {
EVENT_UNSPEC = 0,
EVENT_COMPLETE = 1,
EVENT_START = 2,
EVENT_STOP = 3
};
/**
* Opens the DB's connection
* @param dClass Settings class ('database' class).
*/
DatabaseDriver(const boost::program_options::variables_map& conf, bool isDynamic = false);
/**
* Opens the DB's connection
* @param dClass Settings class ('database' class).
*/
DatabaseDriver(const boost::program_options::variables_map& conf, bool isDynamic = false);
/**
* Adds a torrent to the Database. automatically done if in dynamic mode.
* @param hash The info_hash of the torrent.
* @return true on success. false on failure.
*/
virtual bool addTorrent(uint8_t hash[20]);
/**
* Adds a torrent to the Database. automatically done if in dynamic mode.
* @param hash The info_hash of the torrent.
* @return true on success. false on failure.
*/
virtual bool addTorrent(uint8_t hash[20]);
/**
* Removes a torrent from the database. should be used only for non-dynamic trackers or by cleanup.
* @param hash The info_hash to drop.
* @return true if torrent's database was dropped or no longer exists. otherwise false (shouldn't happen - critical)
*/
virtual bool removeTorrent(uint8_t hash[20]);
/**
* Removes a torrent from the database. should be used only for non-dynamic trackers or by cleanup.
* @param hash The info_hash to drop.
* @return true if torrent's database was dropped or no longer exists. otherwise false (shouldn't happen - critical)
*/
virtual bool removeTorrent(uint8_t hash[20]);
/**
* Checks if the Database is acting as a dynamic tracker DB.
* @return true if dynamic. otherwise false.
*/
bool isDynamic();
/**
* Checks if the Database is acting as a dynamic tracker DB.
* @return true if dynamic. otherwise false.
*/
bool isDynamic();
/**
* Checks if the torrent can be used in the tracker.
* @param info_hash The torrent's info_hash.
* @return true if allowed. otherwise false.
*/
virtual bool isTorrentAllowed(uint8_t info_hash [20]);
/**
* Checks if the torrent can be used in the tracker.
* @param info_hash The torrent's info_hash.
* @return true if allowed. otherwise false.
*/
virtual bool isTorrentAllowed(uint8_t info_hash [20]);
/**
* Generate a Connection ID for the peer.
* @param connectionId (Output) the generated connection ID.
* @param ip The peer's IP (requesting peer. not remote)
* @param port The peer's IP (remote port if tracker accepts)
* @return
*/
virtual bool genConnectionId(uint64_t *connectionId, uint32_t ip, uint16_t port);
/**
* Generate a Connection ID for the peer.
* @param connectionId (Output) the generated connection ID.
* @param ip The peer's IP (requesting peer. not remote)
* @param port The peer's IP (remote port if tracker accepts)
* @return
*/
virtual bool genConnectionId(uint64_t *connectionId, uint32_t ip, uint16_t port);
virtual bool verifyConnectionId(uint64_t connectionId, uint32_t ip, uint16_t port);
virtual bool verifyConnectionId(uint64_t connectionId, uint32_t ip, uint16_t port);
/**
* Updates/Adds a peer to/in the database.
* @param peer_id the peer's peer_id
* @param info_hash the torrent info_hash
* @param ip IP of peer (remote ip if tracker accepts)
* @param port TCP port of peer (remote port if tracker accepts)
* @param downloaded total Bytes downloaded
* @param left total bytes left
* @param uploaded total bytes uploaded
* @return true on success, false on failure.
*/
virtual bool updatePeer(uint8_t peer_id [20], uint8_t info_hash [20],
uint32_t ip, uint16_t port,
int64_t downloaded, int64_t left, int64_t uploaded,
enum TrackerEvents event);
/**
* Updates/Adds a peer to/in the database.
* @param peer_id the peer's peer_id
* @param info_hash the torrent info_hash
* @param ip IP of peer (remote ip if tracker accepts)
* @param port TCP port of peer (remote port if tracker accepts)
* @param downloaded total Bytes downloaded
* @param left total bytes left
* @param uploaded total bytes uploaded
* @return true on success, false on failure.
*/
virtual bool updatePeer(uint8_t peer_id [20], uint8_t info_hash [20],
uint32_t ip, uint16_t port,
int64_t downloaded, int64_t left, int64_t uploaded,
enum TrackerEvents event);
/**
* Remove a peer from a torrent (if stop action occurred, or if peer is inactive in cleanup)
* @param peer_id The peer's peer_id
* @param info_hash Torrent's info_hash
* @param ip The IP of the peer (remote IP if tracker accepts)
* @param port The TCP port (remote port if tracker accepts)
* @return true on success. false on failure (shouldn't happen - critical)
*/
virtual bool removePeer(uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port);
/**
* Remove a peer from a torrent (if stop action occurred, or if peer is inactive in cleanup)
* @param peer_id The peer's peer_id
* @param info_hash Torrent's info_hash
* @param ip The IP of the peer (remote IP if tracker accepts)
* @param port The TCP port (remote port if tracker accepts)
* @return true on success. false on failure (shouldn't happen - critical)
*/
virtual bool removePeer(uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port);
/**
* Gets stats on a torrent
* @param e TorrentEntry, only this info_hash has to be set
* @return true on success, false on failure.
*/
virtual bool getTorrentInfo(TorrentEntry *e);
/**
* Gets stats on a torrent
* @param e TorrentEntry, only this info_hash has to be set
* @return true on success, false on failure.
*/
virtual bool getTorrentInfo(TorrentEntry *e);
/**
* Gets a list of peers from the database.
* @param info_hash The torrent's info_hash
* @param max_count The maximum amount of peers to load from the database. The amount of loaded peers is returned through this variable.
* @param pe The list of peers. Must be pre-allocated to the size of max_count.
* @return true on success, otherwise false (shouldn't happen).
*/
virtual bool getPeers(uint8_t info_hash [20], int *max_count, PeerEntry *pe);
/**
* Gets a list of peers from the database.
* @param info_hash The torrent's info_hash
* @param max_count The maximum amount of peers to load from the database. The amount of loaded peers is returned through this variable.
* @param pe The list of peers. Must be pre-allocated to the size of max_count.
* @return true on success, otherwise false (shouldn't happen).
*/
virtual bool getPeers(uint8_t info_hash [20], int *max_count, PeerEntry *pe);
/**
* Cleanup the database.
* Other actions may be locked when using this depending on the driver.
*/
virtual void cleanup();
/**
* Cleanup the database.
* Other actions may be locked when using this depending on the driver.
*/
virtual void cleanup();
/**
* Closes the connections, and releases all other resources.
*/
virtual ~DatabaseDriver();
/**
* Closes the connections, and releases all other resources.
*/
virtual ~DatabaseDriver();
protected:
const boost::program_options::variables_map& m_conf;
private:
bool is_dynamic;
};
};
protected:
const boost::program_options::variables_map& m_conf;
private:
bool is_dynamic;
};
};
};

View file

@ -1,431 +1,435 @@
/*
* Copyright © 2012-2016 Naim A.
*
* This file is part of UDPT.
*
* UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/
#include "driver_sqlite.hpp"
#include "../tools.h"
#include <ctime>
#include <sstream>
#include <fstream>
#include <iostream>
#include <cassert>
#include <cstring> // memcpy
#include "../multiplatform.h"
using namespace std;
namespace UDPT
{
namespace Data
{
static const char hexadecimal[] = "0123456789abcdef";
static char* _to_hex_str (const uint8_t *hash, char *data)
{
int i;
for (i = 0;i < 20;i++)
{
data[i * 2] = hexadecimal[hash[i] / 16];
data[i * 2 + 1] = hexadecimal[hash[i] % 16];
}
data[40] = '\0';
return data;
}
static uint8_t* _hash_to_bin (const char *hash, uint8_t *data)
{
for (int i = 0;i < 20;i++)
{
data [i] = 0;
char a = hash[i * 2];
char b = hash[i * 2 + 1];
assert ( (a >= 'a' && a <= 'f') || (a >= '0' && a <= '9') );
assert ( (b >= 'a' && b <= 'f') || (b >= '0' && b <= '9') );
data[i] = ( (a >= '0' && a <= 'f') ? (a - '0') : (a - 'f' + 10) );
data[i] <<= 4;
data[i] = ( (b >= '0' && b <= 'f') ? (b - '0') : (b - 'f' + 10) );
}
return data;
}
SQLite3Driver::SQLite3Driver(const boost::program_options::variables_map& conf, bool isDyn) : DatabaseDriver(conf, isDyn), m_logger(boost::log::keywords::channel="SQLite3")
{
int r;
bool doSetup;
fstream fCheck;
string filename = m_conf["db.param"].as<std::string>();
fCheck.open(filename.c_str(), ios::binary | ios::in);
if (fCheck.is_open())
{
doSetup = false;
fCheck.close();
}
else
doSetup = true;
r = sqlite3_open(filename.c_str(), &this->db);
if (r != SQLITE_OK)
{
sqlite3_close(this->db);
throw DatabaseException (DatabaseException::E_CONNECTION_FAILURE);
}
if (doSetup)
this->doSetup();
}
void SQLite3Driver::doSetup()
{
char *eMsg = NULL;
// for quicker stats.
sqlite3_exec(this->db, "CREATE TABLE stats ("
"info_hash blob(20) UNIQUE,"
"completed INTEGER DEFAULT 0,"
"leechers INTEGER DEFAULT 0,"
"seeders INTEGER DEFAULT 0,"
"last_mod INTEGER DEFAULT 0"
")", NULL, NULL, &eMsg);
sqlite3_exec(this->db, "CREATE TABLE torrents ("
"info_hash blob(20) UNIQUE,"
"created INTEGER"
")", NULL, NULL, &eMsg);
}
bool SQLite3Driver::getTorrentInfo(TorrentEntry *e)
{
bool gotInfo = false;
const char sql[] = "SELECT seeders,leechers,completed FROM 'stats' WHERE info_hash=?";
sqlite3_stmt *stmt;
e->seeders = 0;
e->leechers = 0;
e->completed = 0;
sqlite3_prepare (this->db, sql, -1, &stmt, NULL);
sqlite3_bind_blob (stmt, 1, (void*)e->info_hash, 20, NULL);
if (sqlite3_step(stmt) == SQLITE_ROW)
{
e->seeders = sqlite3_column_int (stmt, 0);
e->leechers = sqlite3_column_int (stmt, 1);
e->completed = sqlite3_column_int (stmt, 2);
gotInfo = true;
}
sqlite3_finalize (stmt);
return gotInfo;
}
bool SQLite3Driver::getPeers (uint8_t info_hash [20], int *max_count, PeerEntry *pe)
{
string sql;
char hash [50];
sqlite3_stmt *stmt;
int r, i;
to_hex_str(info_hash, hash);
sql = "SELECT ip,port FROM 't";
sql += hash;
sql += "' LIMIT ?";
sqlite3_prepare(this->db, sql.c_str(), sql.length(), &stmt, NULL);
sqlite3_bind_int(stmt, 1, *max_count);
i = 0;
while (*max_count > i)
{
r = sqlite3_step(stmt);
if (r == SQLITE_ROW)
{
const char *ip = (const char*)sqlite3_column_blob (stmt, 0);
const char *port = (const char*)sqlite3_column_blob (stmt, 1);
memcpy(&pe[i].ip, ip, 4);
memcpy(&pe[i].port, port, 2);
i++;
}
else
{
break;
}
}
BOOST_LOG_SEV(m_logger, boost::log::trivial::debug) << "Retrieved " << i << " peers";
sqlite3_finalize(stmt);
*max_count = i;
return true;
}
bool SQLite3Driver::updatePeer(uint8_t peer_id[20], uint8_t info_hash[20], uint32_t ip, uint16_t port, int64_t downloaded, int64_t left, int64_t uploaded, enum TrackerEvents event)
{
char xHash [50]; // we just need 40 + \0 = 41.
sqlite3_stmt *stmt;
string sql;
int r;
char *hash = xHash;
to_hex_str(info_hash, hash);
addTorrent (info_hash);
sql = "REPLACE INTO 't";
sql += hash;
sql += "' (peer_id,ip,port,uploaded,downloaded,left,last_seen) VALUES (?,?,?,?,?,?,?)";
sqlite3_prepare(this->db, sql.c_str(), sql.length(), &stmt, NULL);
sqlite3_bind_blob(stmt, 1, (void*)peer_id, 20, NULL);
sqlite3_bind_blob(stmt, 2, (void*)&ip, 4, NULL);
sqlite3_bind_blob(stmt, 3, (void*)&port, 2, NULL);
sqlite3_bind_blob(stmt, 4, (void*)&uploaded, 8, NULL);
sqlite3_bind_blob(stmt, 5, (void*)&downloaded, 8, NULL);
sqlite3_bind_blob(stmt, 6, (void*)&left, 8, NULL);
sqlite3_bind_int(stmt, 7, time(NULL));
r = sqlite3_step(stmt);
sqlite3_finalize(stmt);
sql = "REPLACE INTO stats (info_hash,last_mod) VALUES (?,?)";
sqlite3_prepare (this->db, sql.c_str(), sql.length(), &stmt, NULL);
sqlite3_bind_blob (stmt, 1, hash, 20, NULL);
sqlite3_bind_int (stmt, 2, time(NULL));
sqlite3_step (stmt);
sqlite3_finalize (stmt);
return r;
}
bool SQLite3Driver::addTorrent (uint8_t info_hash[20])
{
char xHash [41];
char *err_msg;
int r;
_to_hex_str(info_hash, xHash);
sqlite3_stmt *stmt;
sqlite3_prepare(this->db, "INSERT INTO torrents (info_hash,created) VALUES (?,?)", -1, &stmt, NULL);
sqlite3_bind_blob(stmt, 1, info_hash, 20, NULL);
sqlite3_bind_int(stmt, 2, time(NULL));
sqlite3_step(stmt);
sqlite3_finalize(stmt);
string sql = "CREATE TABLE IF NOT EXISTS 't";
sql += xHash;
sql += "' (";
sql += "peer_id blob(20),"
"ip blob(4),"
"port blob(2),"
"uploaded blob(8)," // uint64
"downloaded blob(8),"
"left blob(8),"
"last_seen INT DEFAULT 0";
sql += ", CONSTRAINT c1 UNIQUE (ip,port) ON CONFLICT REPLACE)";
// create table.
r = sqlite3_exec(this->db, sql.c_str(), NULL, NULL, &err_msg);
if (SQLITE_OK == r)
{
BOOST_LOG_SEV(m_logger, boost::log::trivial::info) << "Added torrent";
return true;
}
else
{
BOOST_LOG_SEV(m_logger, boost::log::trivial::error) << "Failed to add torrent: SQLite3 error code = " << r;
return false;
}
}
bool SQLite3Driver::isTorrentAllowed(uint8_t *info_hash)
{
if (this->isDynamic())
return true;
sqlite3_stmt *stmt;
sqlite3_prepare(this->db, "SELECT COUNT(*) FROM torrents WHERE info_hash=?", -1, &stmt, NULL);
sqlite3_bind_blob(stmt, 1, info_hash, 20, NULL);
sqlite3_step(stmt);
int n = sqlite3_column_int(stmt, 0);
sqlite3_finalize(stmt);
return (n == 1);
}
void SQLite3Driver::cleanup()
{
int exp = time (NULL) - 7200; // 2 hours, expired.
// drop all peers with no activity for 2 hours.
sqlite3_stmt *getTables;
// torrent table names: t<hex-of-sha-1>
sqlite3_prepare(this->db, "SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 't________________________________________'", -1, &getTables, NULL);
uint8_t buff [20];
sqlite3_stmt *updateStats;
assert (sqlite3_prepare(this->db, "REPLACE INTO stats (info_hash,seeders,leechers,last_mod) VALUES (?,?,?,?)", -1, &updateStats, NULL) == SQLITE_OK);
while (sqlite3_step(getTables) == SQLITE_ROW)
{
char* tblN = (char*)sqlite3_column_text(getTables, 0);
stringstream sStr;
sStr << "DELETE FROM " << tblN << " WHERE last_seen<" << exp;
assert (sqlite3_exec(this->db, sStr.str().c_str(), NULL, NULL, NULL) == SQLITE_OK);
sStr.str (string());
sStr << "SELECT left,COUNT(*) FROM " << tblN << " GROUP BY left==0";
sqlite3_stmt *collectStats;
sqlite3_prepare(this->db, sStr.str().c_str(), sStr.str().length(), &collectStats, NULL);
if (sqlite3_errcode(this->db) != SQLITE_OK)
{
// TODO: Log this error
}
int seeders = 0, leechers = 0;
while (sqlite3_step(collectStats) == SQLITE_ROW) // expecting two results.
{
if (sqlite3_column_int(collectStats, 0) == 0)
seeders = sqlite3_column_int (collectStats, 1);
else
leechers = sqlite3_column_int (collectStats, 1);
}
sqlite3_finalize(collectStats);
sqlite3_bind_blob(updateStats, 1, _hash_to_bin((const char*)(tblN + 1), buff), 20, NULL);
sqlite3_bind_int(updateStats, 2, seeders);
sqlite3_bind_int(updateStats, 3, leechers);
sqlite3_bind_int(updateStats, 4, time (NULL));
sqlite3_step(updateStats);
sqlite3_reset (updateStats);
}
sqlite3_finalize(updateStats);
sqlite3_finalize(getTables);
}
bool SQLite3Driver::removeTorrent(uint8_t info_hash[20])
{
// if non-dynamic, remove from table
sqlite3_stmt *stmt;
sqlite3_prepare(this->db, "DELETE FROM torrents WHERE info_hash=?", -1, &stmt, NULL);
sqlite3_bind_blob(stmt, 1, info_hash, 20, NULL);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
// remove from stats
sqlite3_stmt *rmS;
if (sqlite3_prepare(this->db, "DELETE FROM stats WHERE info_hash=?", -1, &rmS, NULL) != SQLITE_OK)
{
sqlite3_finalize(rmS);
return false;
}
sqlite3_bind_blob(rmS, 1, (const void*)info_hash, 20, NULL);
sqlite3_step(rmS);
sqlite3_finalize(rmS);
// remove table
string str = "DROP TABLE IF EXISTS 't";
char buff [41];
str += _to_hex_str(info_hash, buff);
str += "'";
sqlite3_exec(this->db, str.c_str(), NULL, NULL, NULL);
BOOST_LOG_SEV(m_logger, boost::log::trivial::info) << "Torrent removed.";
return true;
}
bool SQLite3Driver::removePeer(uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port)
{
string sql;
char xHash [50];
sqlite3_stmt *stmt;
_to_hex_str (info_hash, xHash);
sql += "DELETE FROM 't";
sql += xHash;
sql += "' WHERE ip=? AND port=? AND peer_id=?";
sqlite3_prepare (this->db, sql.c_str(), sql.length(), &stmt, NULL);
sqlite3_bind_blob(stmt, 0, (const void*)&ip, 4, NULL);
sqlite3_bind_blob(stmt, 1, (const void*)&port, 2, NULL);
sqlite3_bind_blob(stmt, 2, (const void*)peer_id, 20, NULL);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
return true;
}
static uint64_t _genCiD (uint32_t ip, uint16_t port)
{
uint64_t x;
x = (time(NULL) / 3600) * port; // x will probably overload.
x = (ip ^ port);
x <<= 16;
x |= (~port);
return x;
}
bool SQLite3Driver::genConnectionId (uint64_t *connectionId, uint32_t ip, uint16_t port)
{
*connectionId = _genCiD(ip, port);
return true;
}
bool SQLite3Driver::verifyConnectionId(uint64_t cId, uint32_t ip, uint16_t port)
{
if (cId == _genCiD(ip, port))
return true;
else
return false;
}
SQLite3Driver::~SQLite3Driver()
{
BOOST_LOG_SEV(m_logger, boost::log::trivial::info) << "Closing SQLite";
sqlite3_close(this->db);
}
};
};
/*
* Copyright © 2012-2017 Naim A.
*
* This file is part of UDPT.
*
* UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/
#include "driver_sqlite.hpp"
#include "../tools.h"
#include <ctime>
#include <sstream>
#include <fstream>
#include <iostream>
#include <cassert>
#include <cstring> // memcpy
#include "../multiplatform.h"
#include "../logging.hpp"
using namespace std;
namespace UDPT
{
namespace Data
{
static const char hexadecimal[] = "0123456789abcdef";
static char* _to_hex_str (const uint8_t *hash, char *data)
{
int i;
for (i = 0;i < 20;i++)
{
data[i * 2] = hexadecimal[hash[i] / 16];
data[i * 2 + 1] = hexadecimal[hash[i] % 16];
}
data[40] = '\0';
return data;
}
static uint8_t* _hash_to_bin (const char *hash, uint8_t *data)
{
for (int i = 0;i < 20;i++)
{
data [i] = 0;
char a = hash[i * 2];
char b = hash[i * 2 + 1];
assert ( (a >= 'a' && a <= 'f') || (a >= '0' && a <= '9') );
assert ( (b >= 'a' && b <= 'f') || (b >= '0' && b <= '9') );
data[i] = ( (a >= '0' && a <= 'f') ? (a - '0') : (a - 'f' + 10) );
data[i] <<= 4;
data[i] = ( (b >= '0' && b <= 'f') ? (b - '0') : (b - 'f' + 10) );
}
return data;
}
SQLite3Driver::SQLite3Driver(const boost::program_options::variables_map& conf, bool isDyn) : DatabaseDriver(conf, isDyn)
{
int r;
bool doSetup;
fstream fCheck;
string filename = m_conf["db.param"].as<std::string>();
fCheck.open(filename.c_str(), ios::binary | ios::in);
if (fCheck.is_open())
{
doSetup = false;
fCheck.close();
}
else
doSetup = true;
r = sqlite3_open(filename.c_str(), &this->db);
if (r != SQLITE_OK)
{
LOG_FATAL("db-sqlite", "Failed to connect DB. sqlite returned " << r);
sqlite3_close(this->db);
throw DatabaseException (DatabaseException::E_CONNECTION_FAILURE);
}
if (doSetup)
this->doSetup();
}
void SQLite3Driver::doSetup()
{
char *eMsg = NULL;
LOG_INFO("db-sqlite", "Setting up database...");
// for quicker stats.
sqlite3_exec(this->db, "CREATE TABLE stats ("
"info_hash blob(20) UNIQUE,"
"completed INTEGER DEFAULT 0,"
"leechers INTEGER DEFAULT 0,"
"seeders INTEGER DEFAULT 0,"
"last_mod INTEGER DEFAULT 0"
")", NULL, NULL, &eMsg);
sqlite3_exec(this->db, "CREATE TABLE torrents ("
"info_hash blob(20) UNIQUE,"
"created INTEGER"
")", NULL, NULL, &eMsg);
}
bool SQLite3Driver::getTorrentInfo(TorrentEntry *e)
{
bool gotInfo = false;
const char sql[] = "SELECT seeders,leechers,completed FROM 'stats' WHERE info_hash=?";
sqlite3_stmt *stmt;
e->seeders = 0;
e->leechers = 0;
e->completed = 0;
sqlite3_prepare (this->db, sql, -1, &stmt, NULL);
sqlite3_bind_blob (stmt, 1, (void*)e->info_hash, 20, NULL);
if (sqlite3_step(stmt) == SQLITE_ROW)
{
e->seeders = sqlite3_column_int (stmt, 0);
e->leechers = sqlite3_column_int (stmt, 1);
e->completed = sqlite3_column_int (stmt, 2);
gotInfo = true;
}
sqlite3_finalize (stmt);
return gotInfo;
}
bool SQLite3Driver::getPeers (uint8_t info_hash [20], int *max_count, PeerEntry *pe)
{
string sql;
char hash [50];
sqlite3_stmt *stmt;
int r, i;
to_hex_str(info_hash, hash);
sql = "SELECT ip,port FROM 't";
sql += hash;
sql += "' LIMIT ?";
sqlite3_prepare(this->db, sql.c_str(), sql.length(), &stmt, NULL);
sqlite3_bind_int(stmt, 1, *max_count);
i = 0;
while (*max_count > i)
{
r = sqlite3_step(stmt);
if (r == SQLITE_ROW)
{
const char *ip = (const char*)sqlite3_column_blob (stmt, 0);
const char *port = (const char*)sqlite3_column_blob (stmt, 1);
memcpy(&pe[i].ip, ip, 4);
memcpy(&pe[i].port, port, 2);
i++;
}
else
{
break;
}
}
sqlite3_finalize(stmt);
*max_count = i;
return true;
}
bool SQLite3Driver::updatePeer(uint8_t peer_id[20], uint8_t info_hash[20], uint32_t ip, uint16_t port, int64_t downloaded, int64_t left, int64_t uploaded, enum TrackerEvents event)
{
char xHash [50]; // we just need 40 + \0 = 41.
sqlite3_stmt *stmt;
string sql;
int r;
char *hash = xHash;
to_hex_str(info_hash, hash);
addTorrent (info_hash);
sql = "REPLACE INTO 't";
sql += hash;
sql += "' (peer_id,ip,port,uploaded,downloaded,left,last_seen) VALUES (?,?,?,?,?,?,?)";
sqlite3_prepare(this->db, sql.c_str(), sql.length(), &stmt, NULL);
sqlite3_bind_blob(stmt, 1, (void*)peer_id, 20, NULL);
sqlite3_bind_blob(stmt, 2, (void*)&ip, 4, NULL);
sqlite3_bind_blob(stmt, 3, (void*)&port, 2, NULL);
sqlite3_bind_blob(stmt, 4, (void*)&uploaded, 8, NULL);
sqlite3_bind_blob(stmt, 5, (void*)&downloaded, 8, NULL);
sqlite3_bind_blob(stmt, 6, (void*)&left, 8, NULL);
sqlite3_bind_int(stmt, 7, time(NULL));
r = sqlite3_step(stmt);
sqlite3_finalize(stmt);
sql = "REPLACE INTO stats (info_hash,last_mod) VALUES (?,?)";
sqlite3_prepare (this->db, sql.c_str(), sql.length(), &stmt, NULL);
sqlite3_bind_blob (stmt, 1, hash, 20, NULL);
sqlite3_bind_int (stmt, 2, time(NULL));
sqlite3_step (stmt);
sqlite3_finalize (stmt);
return r;
}
bool SQLite3Driver::addTorrent (uint8_t info_hash[20])
{
char xHash [41];
char *err_msg;
int r;
_to_hex_str(info_hash, xHash);
sqlite3_stmt *stmt;
sqlite3_prepare(this->db, "INSERT INTO torrents (info_hash,created) VALUES (?,?)", -1, &stmt, NULL);
sqlite3_bind_blob(stmt, 1, info_hash, 20, NULL);
sqlite3_bind_int(stmt, 2, time(NULL));
sqlite3_step(stmt);
sqlite3_finalize(stmt);
string sql = "CREATE TABLE IF NOT EXISTS 't";
sql += xHash;
sql += "' (";
sql += "peer_id blob(20),"
"ip blob(4),"
"port blob(2),"
"uploaded blob(8)," // uint64
"downloaded blob(8),"
"left blob(8),"
"last_seen INT DEFAULT 0";
sql += ", CONSTRAINT c1 UNIQUE (ip,port) ON CONFLICT REPLACE)";
// create table.
r = sqlite3_exec(this->db, sql.c_str(), NULL, NULL, &err_msg);
if (SQLITE_OK == r)
{
return true;
}
else
{
return false;
}
}
bool SQLite3Driver::isTorrentAllowed(uint8_t *info_hash)
{
if (this->isDynamic())
return true;
sqlite3_stmt *stmt;
sqlite3_prepare(this->db, "SELECT COUNT(*) FROM torrents WHERE info_hash=?", -1, &stmt, NULL);
sqlite3_bind_blob(stmt, 1, info_hash, 20, NULL);
sqlite3_step(stmt);
int n = sqlite3_column_int(stmt, 0);
sqlite3_finalize(stmt);
return (n == 1);
}
void SQLite3Driver::cleanup()
{
LOG_INFO("db-sqlite", "Cleaning up...");
int exp = time (NULL) - 7200; // 2 hours, expired.
int r = 0;
// drop all peers with no activity for 2 hours.
sqlite3_stmt *getTables;
// torrent table names: t<hex-of-sha-1>
r = sqlite3_prepare(this->db, "SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 't________________________________________'", -1, &getTables, NULL);
if (r != SQLITE_OK) {
LOG_ERR("db-sqlite", "Failed fetch tables from DB for cleanup.");
return;
}
uint8_t buff [20];
sqlite3_stmt *updateStats;
r = sqlite3_prepare(this->db, "REPLACE INTO stats (info_hash,seeders,leechers,last_mod) VALUES (?,?,?,?)", -1, &updateStats, NULL);
if (r != SQLITE_OK) {
LOG_ERR("db-sqlite", "Failed to prepare update stats query.");
return;
}
while (sqlite3_step(getTables) == SQLITE_ROW)
{
char* tblN = (char*)sqlite3_column_text(getTables, 0);
stringstream sStr;
sStr << "DELETE FROM " << tblN << " WHERE last_seen<" << exp;
r = sqlite3_exec(this->db, sStr.str().c_str(), NULL, NULL, NULL);
if (r != SQLITE_OK) {
LOG_ERR("db-sqlite", "Failed to execute cleanup for table '" << tblN << "'.");
continue;
}
sStr.str (string());
sStr << "SELECT left,COUNT(*) FROM " << tblN << " GROUP BY left==0";
sqlite3_stmt *collectStats;
r = sqlite3_prepare(this->db, sStr.str().c_str(), sStr.str().length(), &collectStats, NULL);
if (r != SQLITE_OK)
{
LOG_ERR("db-sqlite", "Failed while trying to prepare stats query for '" << tblN << "', sqlite returned " << r);
continue;
}
int seeders = 0, leechers = 0;
while (sqlite3_step(collectStats) == SQLITE_ROW) // expecting two results.
{
if (sqlite3_column_int(collectStats, 0) == 0)
seeders = sqlite3_column_int (collectStats, 1);
else
leechers = sqlite3_column_int (collectStats, 1);
}
sqlite3_finalize(collectStats);
sqlite3_bind_blob(updateStats, 1, _hash_to_bin((const char*)(tblN + 1), buff), 20, NULL);
sqlite3_bind_int(updateStats, 2, seeders);
sqlite3_bind_int(updateStats, 3, leechers);
sqlite3_bind_int(updateStats, 4, time (NULL));
sqlite3_step(updateStats);
sqlite3_reset (updateStats);
}
sqlite3_finalize(updateStats);
sqlite3_finalize(getTables);
}
bool SQLite3Driver::removeTorrent(uint8_t info_hash[20]) {
// if non-dynamic, remove from table
sqlite3_stmt *stmt;
sqlite3_prepare(this->db, "DELETE FROM torrents WHERE info_hash=?", -1, &stmt, NULL);
sqlite3_bind_blob(stmt, 1, info_hash, 20, NULL);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
// remove from stats
sqlite3_stmt *rmS;
if (sqlite3_prepare(this->db, "DELETE FROM stats WHERE info_hash=?", -1, &rmS, NULL) != SQLITE_OK)
{
sqlite3_finalize(rmS);
return false;
}
sqlite3_bind_blob(rmS, 1, (const void*)info_hash, 20, NULL);
sqlite3_step(rmS);
sqlite3_finalize(rmS);
// remove table
string str = "DROP TABLE IF EXISTS 't";
char buff [41];
str += _to_hex_str(info_hash, buff);
str += "'";
sqlite3_exec(this->db, str.c_str(), NULL, NULL, NULL);
return true;
}
bool SQLite3Driver::removePeer(uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port) {
string sql;
char xHash [50];
sqlite3_stmt *stmt;
_to_hex_str (info_hash, xHash);
sql += "DELETE FROM 't";
sql += xHash;
sql += "' WHERE ip=? AND port=? AND peer_id=?";
sqlite3_prepare (this->db, sql.c_str(), sql.length(), &stmt, NULL);
sqlite3_bind_blob(stmt, 0, (const void*)&ip, 4, NULL);
sqlite3_bind_blob(stmt, 1, (const void*)&port, 2, NULL);
sqlite3_bind_blob(stmt, 2, (const void*)peer_id, 20, NULL);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
return true;
}
static uint64_t _genCiD (uint32_t ip, uint16_t port) {
uint64_t x;
x = (time(NULL) / 3600) * port; // x will probably overload.
x = (ip ^ port);
x <<= 16;
x |= (~port);
return x;
}
bool SQLite3Driver::genConnectionId (uint64_t *connectionId, uint32_t ip, uint16_t port) {
*connectionId = _genCiD(ip, port);
return true;
}
bool SQLite3Driver::verifyConnectionId(uint64_t cId, uint32_t ip, uint16_t port) {
if (cId == _genCiD(ip, port))
return true;
else
return false;
}
SQLite3Driver::~SQLite3Driver() {
sqlite3_close(this->db);
}
};
};

View file

@ -1,56 +1,55 @@
/*
* Copyright © 2012-2016 Naim A.
*
* This file is part of UDPT.
*
* UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DATABASE_H_
#define DATABASE_H_
#include <stdint.h>
#include "database.hpp"
#include <sqlite3.h>
namespace UDPT
{
namespace Data
{
class SQLite3Driver : public DatabaseDriver
{
public:
SQLite3Driver(const boost::program_options::variables_map& conf, bool isDyn = false);
bool addTorrent(uint8_t info_hash[20]);
bool removeTorrent(uint8_t info_hash[20]);
bool genConnectionId(uint64_t *connId, uint32_t ip, uint16_t port);
bool verifyConnectionId(uint64_t connId, uint32_t ip, uint16_t port);
bool updatePeer(uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port, int64_t downloaded, int64_t left, int64_t uploaded, enum TrackerEvents event);
bool removePeer(uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port);
bool getTorrentInfo(TorrentEntry *e);
bool isTorrentAllowed(uint8_t info_hash[20]);
bool getPeers(uint8_t info_hash [20], int *max_count, PeerEntry *pe);
void cleanup();
virtual ~SQLite3Driver();
private:
sqlite3 *db;
boost::log::sources::severity_channel_logger_mt<> m_logger;
void doSetup();
};
};
};
#endif /* DATABASE_H_ */
/*
* Copyright © 2012-2017 Naim A.
*
* This file is part of UDPT.
*
* UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DATABASE_H_
#define DATABASE_H_
#include <stdint.h>
#include "database.hpp"
#include <sqlite3.h>
namespace UDPT
{
namespace Data
{
class SQLite3Driver : public DatabaseDriver
{
public:
SQLite3Driver(const boost::program_options::variables_map& conf, bool isDyn = false);
bool addTorrent(uint8_t info_hash[20]);
bool removeTorrent(uint8_t info_hash[20]);
bool genConnectionId(uint64_t *connId, uint32_t ip, uint16_t port);
bool verifyConnectionId(uint64_t connId, uint32_t ip, uint16_t port);
bool updatePeer(uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port, int64_t downloaded, int64_t left, int64_t uploaded, enum TrackerEvents event);
bool removePeer(uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port);
bool getTorrentInfo(TorrentEntry *e);
bool isTorrentAllowed(uint8_t info_hash[20]);
bool getPeers(uint8_t info_hash [20], int *max_count, PeerEntry *pe);
void cleanup();
virtual ~SQLite3Driver();
private:
sqlite3 *db;
void doSetup();
};
};
};
#endif /* DATABASE_H_ */

View file

@ -4,73 +4,73 @@
namespace UDPT
{
class UDPTException
{
public:
UDPTException(const char* errorMsg, int errorCode = 0) : m_error(errorMsg), m_errorCode(errorCode)
{
class UDPTException
{
public:
UDPTException(const char* errorMsg, int errorCode = 0) : m_error(errorMsg), m_errorCode(errorCode)
{
}
}
UDPTException(int errorCode = 0) : m_errorCode(errorCode), m_error("")
{
}
UDPTException(int errorCode = 0) : m_errorCode(errorCode), m_error("")
{
}
virtual const char* what() const
{
return m_error;
}
virtual const char* what() const
{
return m_error;
}
virtual int getErrorCode() const
{
return m_errorCode;
}
virtual int getErrorCode() const
{
return m_errorCode;
}
virtual ~UDPTException()
{
virtual ~UDPTException()
{
}
}
protected:
const char* m_error;
const int m_errorCode;
};
protected:
const char* m_error;
const int m_errorCode;
};
class OSError : public UDPTException
{
public:
OSError(int errorCode
class OSError : public UDPTException
{
public:
OSError(int errorCode
#ifdef WIN32
= ::GetLastError()
= ::GetLastError()
#endif
) : UDPTException(errorCode)
{
}
) : UDPTException(errorCode)
{
}
virtual ~OSError() {}
virtual ~OSError() {}
const char* what() const
{
if (m_errorMessage.length() > 0)
{
return m_errorMessage.c_str();
}
const char* what() const
{
if (m_errorMessage.length() > 0)
{
return m_errorMessage.c_str();
}
#ifdef WIN32
char *buffer = nullptr;
DWORD msgLen = ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, 0, m_errorCode, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), reinterpret_cast<LPSTR>(&buffer), 1, NULL);
std::shared_ptr<void> formatStr = std::shared_ptr<void>(
buffer,
::LocalFree);
m_errorMessage = std::string(reinterpret_cast<char*>(formatStr.get()));
char *buffer = nullptr;
DWORD msgLen = ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, 0, m_errorCode, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), reinterpret_cast<LPSTR>(&buffer), 1, NULL);
std::shared_ptr<void> formatStr = std::shared_ptr<void>(
buffer,
::LocalFree);
m_errorMessage = std::string(reinterpret_cast<char*>(formatStr.get()));
return m_errorMessage.c_str();
return m_errorMessage.c_str();
#else
return "OSError";
return "OSError";
#endif
}
private:
// allow to generate a message only when needed.
mutable std::string m_errorMessage;
};
}
private:
// allow to generate a message only when needed.
mutable std::string m_errorMessage;
};
}

File diff suppressed because it is too large Load diff

View file

@ -1,170 +1,170 @@
/*
* Copyright © 2013-2016 Naim A.
*
* This file is part of UDPT.
*
* UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include <map>
#include <string>
#include <sstream>
#include <list>
#include <boost/program_options.hpp>
#include "../multiplatform.h"
using namespace std;
#define REQUEST_BUFFER_SIZE 2048
namespace UDPT
{
namespace Server
{
class ServerException
{
public:
inline ServerException (int ec)
{
this->ec = ec;
this->em = NULL;
}
inline ServerException (int ec, const char *em)
{
this->ec = ec;
this->em = em;
}
inline const char *getErrorMsg () const
{
return this->em;
}
inline int getErrorCode () const
{
return this->ec;
}
private:
int ec;
const char *em;
};
class HTTPServer
{
public:
class Request
{
public:
enum RequestMethod
{
RM_UNKNOWN = 0,
RM_GET = 1,
RM_POST = 2
};
Request (SOCKET, const SOCKADDR_IN *);
list<string>* getPath ();
string getParam (const string key);
multimap<string, string>::iterator getHeader (const string name);
RequestMethod getRequestMethod ();
string getRequestMethodStr ();
string getCookie (const string name);
const SOCKADDR_IN* getAddress ();
private:
const SOCKADDR_IN *addr;
SOCKET conn;
struct {
int major;
int minor;
} httpVer;
struct {
string str;
RequestMethod rm;
} requestMethod;
list<string> path;
map<string, string> params;
map<string, string> cookies;
multimap<string, string> headers;
void parseRequest ();
};
class Response
{
public:
Response (SOCKET conn);
void setStatus (int, const string);
void addHeader (string key, string value);
int writeRaw (const char *data, int len);
void write (const char *data, int len = -1);
private:
friend class HTTPServer;
SOCKET conn;
int status_code;
string status_msg;
multimap<string, string> headers;
stringstream msg;
void finalize ();
};
typedef void (reqCallback)(HTTPServer*,Request*,Response*);
HTTPServer (uint16_t port, int threads);
HTTPServer(const boost::program_options::variables_map& conf);
void addApp (list<string> *path, reqCallback *);
void setData (string, void *);
void* getData (string);
virtual ~HTTPServer ();
private:
typedef struct appNode
{
reqCallback *callback;
map<string, appNode> nodes;
} appNode;
SOCKET srv;
int thread_count;
HANDLE *threads;
bool isRunning;
appNode rootNode;
map<string, void*> customData;
void init (SOCKADDR_IN &localEndpoint, int threads);
static void handleConnections (HTTPServer *);
#ifdef WIN32
static DWORD _thread_start (LPVOID);
#else
static void* _thread_start (void*);
#endif
static reqCallback* getRequestHandler (appNode *, list<string> *);
};
};
};
/*
* Copyright © 2013-2017 Naim A.
*
* This file is part of UDPT.
*
* UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include <map>
#include <string>
#include <sstream>
#include <list>
#include <boost/program_options.hpp>
#include "../multiplatform.h"
using namespace std;
#define REQUEST_BUFFER_SIZE 2048
namespace UDPT
{
namespace Server
{
class ServerException
{
public:
inline ServerException (int ec)
{
this->ec = ec;
this->em = NULL;
}
inline ServerException (int ec, const char *em)
{
this->ec = ec;
this->em = em;
}
inline const char *getErrorMsg () const
{
return this->em;
}
inline int getErrorCode () const
{
return this->ec;
}
private:
int ec;
const char *em;
};
class HTTPServer
{
public:
class Request
{
public:
enum RequestMethod
{
RM_UNKNOWN = 0,
RM_GET = 1,
RM_POST = 2
};
Request (SOCKET, const SOCKADDR_IN *);
list<string>* getPath ();
string getParam (const string key);
multimap<string, string>::iterator getHeader (const string name);
RequestMethod getRequestMethod ();
string getRequestMethodStr ();
string getCookie (const string name);
const SOCKADDR_IN* getAddress ();
private:
const SOCKADDR_IN *addr;
SOCKET conn;
struct {
int major;
int minor;
} httpVer;
struct {
string str;
RequestMethod rm;
} requestMethod;
list<string> path;
map<string, string> params;
map<string, string> cookies;
multimap<string, string> headers;
void parseRequest ();
};
class Response
{
public:
Response (SOCKET conn);
void setStatus (int, const string);
void addHeader (string key, string value);
int writeRaw (const char *data, int len);
void write (const char *data, int len = -1);
private:
friend class HTTPServer;
SOCKET conn;
int status_code;
string status_msg;
multimap<string, string> headers;
stringstream msg;
void finalize ();
};
typedef void (reqCallback)(HTTPServer*,Request*,Response*);
HTTPServer (uint16_t port, int threads);
HTTPServer(const boost::program_options::variables_map& conf);
void addApp (list<string> *path, reqCallback *);
void setData (string, void *);
void* getData (string);
virtual ~HTTPServer ();
private:
typedef struct appNode
{
reqCallback *callback;
map<string, appNode> nodes;
} appNode;
SOCKET srv;
int thread_count;
HANDLE *threads;
bool isRunning;
appNode rootNode;
map<string, void*> customData;
void init (SOCKADDR_IN &localEndpoint, int threads);
static void handleConnections (HTTPServer *);
#ifdef WIN32
static DWORD _thread_start (LPVOID);
#else
static void* _thread_start (void*);
#endif
static reqCallback* getRequestHandler (appNode *, list<string> *);
};
};
};

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2013-2016 Naim A.
* Copyright © 2013-2017 Naim A.
*
* This file is part of UDPT.
*
@ -25,190 +25,190 @@ using namespace std;
namespace UDPT
{
namespace Server
{
namespace Server
{
static uint32_t _getNextIPv4 (string::size_type &i, string &line)
{
string::size_type len = line.length();
char c;
while (i < len)
{
c = line.at(i);
if (c >= '0' && c <= '9')
break;
i++;
}
static uint32_t _getNextIPv4 (string::size_type &i, string &line)
{
string::size_type len = line.length();
char c;
while (i < len)
{
c = line.at(i);
if (c >= '0' && c <= '9')
break;
i++;
}
uint32_t ip = 0;
for (int n = 0;n < 4;n++)
{
int cn = 0;
while (i < len)
{
c = line.at (i++);
if (c == '.' || ((c == ' ' || c == ',' || c == ';') && n == 3))
break;
else if (!(c >= '0' && c <= '9'))
return 0;
cn *= 10;
cn += (c - '0');
}
ip *= 256;
ip += cn;
}
return ip;
}
uint32_t ip = 0;
for (int n = 0;n < 4;n++)
{
int cn = 0;
while (i < len)
{
c = line.at (i++);
if (c == '.' || ((c == ' ' || c == ',' || c == ';') && n == 3))
break;
else if (!(c >= '0' && c <= '9'))
return 0;
cn *= 10;
cn += (c - '0');
}
ip *= 256;
ip += cn;
}
return ip;
}
static bool _hex2bin (uint8_t *data, const string str)
{
int len = str.length();
static bool _hex2bin (uint8_t *data, const string str)
{
int len = str.length();
if (len % 2 != 0)
return false;
if (len % 2 != 0)
return false;
char a, b;
uint8_t c;
for (int i = 0;i < len;i+=2)
{
a = str.at (i);
b = str.at (i + 1);
c = 0;
char a, b;
uint8_t c;
for (int i = 0;i < len;i+=2)
{
a = str.at (i);
b = str.at (i + 1);
c = 0;
if (a >= 'a' && a <= 'f')
a = (a - 'a') + 10;
else if (a >= '0' && a <= '9')
a = (a - '0');
else
return false;
if (a >= 'a' && a <= 'f')
a = (a - 'a') + 10;
else if (a >= '0' && a <= '9')
a = (a - '0');
else
return false;
if (b >= 'a' && b <= 'f')
b = (b - 'a') + 10;
else if (b >= '0' && b <= '9')
b = (b - '0');
else
return false;
if (b >= 'a' && b <= 'f')
b = (b - 'a') + 10;
else if (b >= '0' && b <= '9')
b = (b - '0');
else
return false;
c = (a * 16) + b;
c = (a * 16) + b;
data [i / 2] = c;
}
data [i / 2] = c;
}
return true;
}
return true;
}
WebApp::WebApp(std::shared_ptr<HTTPServer> srv, DatabaseDriver *db, const boost::program_options::variables_map& conf) : m_conf(conf), m_server(srv)
{
this->db = db;
// TODO: Implement authentication by keys
WebApp::WebApp(std::shared_ptr<HTTPServer> srv, DatabaseDriver *db, const boost::program_options::variables_map& conf) : m_conf(conf), m_server(srv)
{
this->db = db;
// TODO: Implement authentication by keys
m_server->setData("webapp", this);
}
m_server->setData("webapp", this);
}
WebApp::~WebApp()
{
}
WebApp::~WebApp()
{
}
void WebApp::deploy()
{
list<string> path;
m_server->addApp(&path, &WebApp::handleRoot);
void WebApp::deploy()
{
list<string> path;
m_server->addApp(&path, &WebApp::handleRoot);
path.push_back("api");
m_server->addApp(&path, &WebApp::handleAPI); // "/api"
path.push_back("api");
m_server->addApp(&path, &WebApp::handleAPI); // "/api"
path.pop_back();
path.push_back("announce");
m_server->addApp(&path, &WebApp::handleAnnounce);
}
path.pop_back();
path.push_back("announce");
m_server->addApp(&path, &WebApp::handleAnnounce);
}
void WebApp::handleRoot(HTTPServer *srv, HTTPServer::Request *req, HTTPServer::Response *resp)
{
// It would be very appreciated to keep this in the code.
resp->write("<html>"
"<head><title>UDPT Torrent Tracker</title></head>"
"<body>"
"<div style=\"vertical-align:top;\">This tracker is running on UDPT Software.</div>"
"<br /><hr /><div style=\"text-align:center;font-size:small;\"><a href=\"http://github.com/naim94a/udpt\">UDPT</a></div>"
"</body>"
"</html>");
}
void WebApp::handleRoot(HTTPServer *srv, HTTPServer::Request *req, HTTPServer::Response *resp)
{
// It would be very appreciated to keep this in the code.
resp->write("<html>"
"<head><title>UDPT Torrent Tracker</title></head>"
"<body>"
"<div style=\"vertical-align:top;\">This tracker is running on UDPT Software.</div>"
"<br /><hr /><div style=\"text-align:center;font-size:small;\"><a href=\"http://github.com/naim94a/udpt\">UDPT</a></div>"
"</body>"
"</html>");
}
void WebApp::doRemoveTorrent (HTTPServer::Request *req, HTTPServer::Response *resp)
{
string strHash = req->getParam("hash");
if (strHash.length() != 40)
{
resp->write("{\"error\":\"Hash length must be 40 characters.\"}");
return;
}
uint8_t hash [20];
if (!_hex2bin(hash, strHash))
{
resp->write("{\"error\":\"invalid info_hash.\"}");
return;
}
void WebApp::doRemoveTorrent (HTTPServer::Request *req, HTTPServer::Response *resp)
{
string strHash = req->getParam("hash");
if (strHash.length() != 40)
{
resp->write("{\"error\":\"Hash length must be 40 characters.\"}");
return;
}
uint8_t hash [20];
if (!_hex2bin(hash, strHash))
{
resp->write("{\"error\":\"invalid info_hash.\"}");
return;
}
if (this->db->removeTorrent(hash))
resp->write("{\"success\":true}");
else
resp->write("{\"error\":\"failed to remove torrent from DB\"}");
}
if (this->db->removeTorrent(hash))
resp->write("{\"success\":true}");
else
resp->write("{\"error\":\"failed to remove torrent from DB\"}");
}
void WebApp::doAddTorrent (HTTPServer::Request *req, HTTPServer::Response *resp)
{
std::string strHash = req->getParam("hash");
if (strHash.length() != 40)
{
resp->write("{\"error\":\"Hash length must be 40 characters.\"}");
return;
}
uint8_t hash [20];
if (!_hex2bin(hash, strHash))
{
resp->write("{\"error\":\"invalid info_hash.\"}");
return;
}
void WebApp::doAddTorrent (HTTPServer::Request *req, HTTPServer::Response *resp)
{
std::string strHash = req->getParam("hash");
if (strHash.length() != 40)
{
resp->write("{\"error\":\"Hash length must be 40 characters.\"}");
return;
}
uint8_t hash [20];
if (!_hex2bin(hash, strHash))
{
resp->write("{\"error\":\"invalid info_hash.\"}");
return;
}
if (this->db->addTorrent(hash))
resp->write("{\"success\":true}");
else
resp->write("{\"error\":\"failed to add torrent to DB\"}");
}
if (this->db->addTorrent(hash))
resp->write("{\"success\":true}");
else
resp->write("{\"error\":\"failed to add torrent to DB\"}");
}
void WebApp::handleAnnounce (HTTPServer *srv, HTTPServer::Request *req, HTTPServer::Response *resp)
{
resp->write("d14:failure reason42:this is a UDP tracker, not a HTTP tracker.e");
}
void WebApp::handleAnnounce (HTTPServer *srv, HTTPServer::Request *req, HTTPServer::Response *resp)
{
resp->write("d14:failure reason42:this is a UDP tracker, not a HTTP tracker.e");
}
void WebApp::handleAPI(HTTPServer *srv, HTTPServer::Request *req, HTTPServer::Response *resp)
{
if (req->getAddress()->sin_family != AF_INET)
{
throw ServerException (0, "IPv4 supported Only.");
}
void WebApp::handleAPI(HTTPServer *srv, HTTPServer::Request *req, HTTPServer::Response *resp)
{
if (req->getAddress()->sin_family != AF_INET)
{
throw ServerException (0, "IPv4 supported Only.");
}
WebApp *app = (WebApp*)srv->getData("webapp");
if (app == NULL)
throw ServerException(0, "WebApp object wasn't found");
WebApp *app = (WebApp*)srv->getData("webapp");
if (app == NULL)
throw ServerException(0, "WebApp object wasn't found");
if (req->getAddress()->sin_addr.s_addr != 0x0100007f)
{
resp->setStatus(403, "Forbidden");
resp->write("Access Denied. Only 127.0.0.1 can access this method.");
return;
}
if (req->getAddress()->sin_addr.s_addr != 0x0100007f)
{
resp->setStatus(403, "Forbidden");
resp->write("Access Denied. Only 127.0.0.1 can access this method.");
return;
}
std::string action = req->getParam("action");
if (action == "add")
app->doAddTorrent(req, resp);
else if (action == "remove")
app->doRemoveTorrent(req, resp);
else
{
resp->write("{\"error\":\"unknown action\"}");
}
}
};
std::string action = req->getParam("action");
if (action == "add")
app->doAddTorrent(req, resp);
else if (action == "remove")
app->doRemoveTorrent(req, resp);
else
{
resp->write("{\"error\":\"unknown action\"}");
}
}
};
};

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2013-2016 Naim A.
* Copyright © 2013-2017 Naim A.
*
* This file is part of UDPT.
*
@ -33,27 +33,27 @@ using namespace UDPT::Data;
namespace UDPT
{
namespace Server
{
class WebApp
{
public:
WebApp(std::shared_ptr<HTTPServer> , DatabaseDriver *, const boost::program_options::variables_map& conf);
virtual ~WebApp();
void deploy ();
namespace Server
{
class WebApp
{
public:
WebApp(std::shared_ptr<HTTPServer> , DatabaseDriver *, const boost::program_options::variables_map& conf);
virtual ~WebApp();
void deploy ();
private:
std::shared_ptr<HTTPServer> m_server;
UDPT::Data::DatabaseDriver *db;
const boost::program_options::variables_map& m_conf;
static void handleRoot (HTTPServer*,HTTPServer::Request*, HTTPServer::Response*);
static void handleAnnounce (HTTPServer*,HTTPServer::Request*, HTTPServer::Response*);
static void handleAPI (HTTPServer*,HTTPServer::Request*, HTTPServer::Response*);
private:
std::shared_ptr<HTTPServer> m_server;
UDPT::Data::DatabaseDriver *db;
const boost::program_options::variables_map& m_conf;
void doAddTorrent (HTTPServer::Request*, HTTPServer::Response*);
void doRemoveTorrent (HTTPServer::Request*, HTTPServer::Response*);
};
};
static void handleRoot (HTTPServer*,HTTPServer::Request*, HTTPServer::Response*);
static void handleAnnounce (HTTPServer*,HTTPServer::Request*, HTTPServer::Response*);
static void handleAPI (HTTPServer*,HTTPServer::Request*, HTTPServer::Response*);
void doAddTorrent (HTTPServer::Request*, HTTPServer::Response*);
void doRemoveTorrent (HTTPServer::Request*, HTTPServer::Response*);
};
};
};

140
src/logging.cpp Normal file
View file

@ -0,0 +1,140 @@
/*
* Copyright © 2012-2017 Naim A.
*
* This file is part of UDPT.
*
* UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/
#include "logging.hpp"
#include <sstream>
#include <chrono>
#include <cstring>
#include <iostream>
namespace UDPT {
namespace Logging {
Logger::Logger() : m_cleaningUp(false), m_workerThread(Logger::worker, this), m_minLogLevel(Severity::FATAL) {
}
Logger::~Logger() {
try {
// prevent new log messages from entering queue.
m_cleaningUp = true;
// tell thread to exit
m_runningCondition.notify_one();
// wait for worker to terminate
m_workerThread.join();
// flush any remaining logs
flush();
// flush iostreams
for (std::vector<std::pair<std::ostream*, Severity>>::const_iterator it = m_outputStreams.begin(); it != m_outputStreams.end(); ++it) {
it->first->flush();
}
} catch (...) {
// can't do much here... this is a logging class...
}
}
Logger& Logger::getLogger() {
static Logger instance;
return instance;
}
void Logger::log(Severity severity, const std::string& channel, const std::string& message) {
if (severity < m_minLogLevel) {
return;
}
m_queue.Push(LogEntry{
.when=std::chrono::system_clock::now(),
.severity=severity,
.channel=channel,
.message=message
});
}
void Logger::addStream(std::ostream *stream, Severity severity) {
m_minLogLevel = m_minLogLevel > severity ? severity : m_minLogLevel;
m_outputStreams.push_back(std::pair<std::ostream*, Severity>(stream, severity));
}
void Logger::flush() {
while (!m_queue.IsEmpty()) {
LogEntry entry = m_queue.Pop();
std::stringstream sstream;
//TODO: Write the log time in a more elegant manner.
time_t timestamp = std::chrono::system_clock::to_time_t(entry.when);
char* time_buffer = ctime(&timestamp);
time_buffer[strlen(time_buffer) - 1] = '\0';
sstream << time_buffer << "\t";
switch (entry.severity) {
case Severity::DEBUG:
sstream << "DEBUG";
break;
case Severity::INFO:
sstream << "INFO ";
break;
case Severity::WARNING:
sstream << "WARN ";
break;
case Severity::ERROR:
sstream << "ERROR";
break;
case Severity::FATAL:
sstream << "FATAL";
break;
}
sstream << " [" << entry.channel << "]\t" << entry.message;
const std::string& result_log = sstream.str();
for (std::vector<std::pair<std::ostream*, Severity>>::const_iterator it = m_outputStreams.begin(); it != m_outputStreams.end(); ++it) {
if (entry.severity < it->second) {
// message severity isn't high enough for this logger.
continue;
}
std::ostream& current_stream = *(it->first);
// catch an exception in case we get a broken pipe or something...
try {
current_stream << result_log << std::endl;
} catch (...) {
// do nothing.
}
}
}
}
void Logger::worker(Logger *me) {
std::unique_lock<std::mutex> lk (me->m_runningMutex);
while (true) {
me->flush();
if (std::cv_status::no_timeout == me->m_runningCondition.wait_for(lk, std::chrono::seconds(5))) {
break;
}
}
}
}
}

83
src/logging.hpp Normal file
View file

@ -0,0 +1,83 @@
/*
* Copyright © 2012-2017 Naim A.
*
* This file is part of UDPT.
*
* UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <string>
#include <ostream>
#include <thread>
#include <atomic>
#include <condition_variable>
#include "MessageQueue.hpp"
#define LOG(severity, channel, message) \
{\
std::stringstream __sstream; \
__sstream << message; \
UDPT::Logging::Logger::getLogger().log(severity, channel, __sstream.str()); \
}
#define LOG_INFO(channel, message) LOG(UDPT::Logging::Severity::INFO, channel, message)
#define LOG_DEBUG(channel, message) LOG(UDPT::Logging::Severity::DEBUG, channel, message)
#define LOG_WARN(channel, message) LOG(UDPT::Logging::Severity::WARNING, channel, message)
#define LOG_ERR(channel, message) LOG(UDPT::Logging::Severity::ERROR, channel, message)
#define LOG_FATAL(channel, message) LOG(UDPT::Logging::Severity::FATAL, channel, message)
namespace UDPT {
namespace Logging {
enum Severity {
UNSET = 0,
DEBUG = 10,
INFO = 20,
WARNING = 30,
ERROR = 40,
FATAL = 50
};
struct LogEntry {
const std::chrono::time_point<std::chrono::system_clock> when;
Severity severity;
const std::string channel;
const std::string message;
};
class Logger {
public:
static Logger& getLogger();
void log(Severity severity, const std::string& channel, const std::string& message);
void addStream(std::ostream *, Severity minSeverity=INFO);
private:
Logger();
virtual ~Logger();
static void worker(Logger*);
void flush();
std::vector<std::pair<std::ostream*, UDPT::Logging::Severity>> m_outputStreams;
UDPT::Utils::MessageQueue<struct LogEntry> m_queue;
std::thread m_workerThread;
std::atomic_bool m_cleaningUp;
std::mutex m_runningMutex;
std::condition_variable m_runningCondition;
Severity m_minLogLevel;
};
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2012-2016 Naim A.
* Copyright © 2012-2017 Naim A.
*
* This file is part of UDPT.
*
@ -25,8 +25,6 @@
#include <memory>
#include <algorithm>
#include <boost/program_options.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/sources/severity_channel_logger.hpp>
#include "multiplatform.h"
#include "udpTracker.hpp"
@ -34,35 +32,39 @@
#include "http/webapp.hpp"
#include "tracker.hpp"
#include "service.hpp"
#include "logging.hpp"
static void _signal_handler(int sig)
extern "C" void _signal_handler(int sig)
{
switch (sig)
{
case SIGTERM:
UDPT::Tracker::getInstance().stop();
break;
}
switch (sig) {
case SIGTERM:
case SIGQUIT:
case SIGINT: {
LOG_INFO("core", "Received signal " << sig << ", requesting to stop tracker");
UDPT::Tracker::getInstance().stop();
break;
}
}
}
#ifdef linux
static void daemonize(const boost::program_options::variables_map& conf)
{
if (1 == ::getppid()) return; // already a daemon
int r = ::fork();
if (0 > r) ::exit(-1); // failed to daemonize.
if (0 < r) ::exit(0); // parent exists.
if (1 == ::getppid()) return; // already a daemon
int r = ::fork();
if (0 > r) ::exit(-1); // failed to daemonize.
if (0 < r) ::exit(0); // parent exists.
::umask(0);
::setsid();
::umask(0);
::setsid();
// close all fds.
for (int i = ::getdtablesize(); i >=0; --i)
{
::close(i);
}
// close all fds.
for (int i = ::getdtablesize(); i >=0; --i)
{
::close(i);
}
::chdir(conf["daemon.chdir"].as<std::string>().c_str());
::chdir(conf["daemon.chdir"].as<std::string>().c_str());
}
#endif
@ -70,159 +72,163 @@ static void daemonize(const boost::program_options::variables_map& conf)
#ifdef WIN32
void _close_wsa()
{
::WSACleanup();
::WSACleanup();
}
#endif
#ifdef TEST
int real_main(int argc, char *argv[])
#else
int main(int argc, char *argv[])
#endif
{
#ifdef WIN32
WSADATA wsadata;
::WSAStartup(MAKEWORD(2, 2), &wsadata);
::atexit(_close_wsa);
WSADATA wsadata;
::WSAStartup(MAKEWORD(2, 2), &wsadata);
::atexit(_close_wsa);
#endif
boost::program_options::options_description commandLine("Command line options");
commandLine.add_options()
("help,h", "produce help message")
("all-help", "displays all help")
("test,t", "test configuration file")
("config,c", boost::program_options::value<std::string>()->default_value("/etc/udpt.conf"), "configuration file to use")
boost::program_options::options_description commandLine("Command line options");
commandLine.add_options()
("help,h", "produce help message")
("all-help", "displays all help")
("test,t", "test configuration file")
("config,c", boost::program_options::value<std::string>()->default_value("/etc/udpt.conf"), "configuration file to use")
#ifdef linux
("interactive,i", "doesn't start as daemon")
("interactive,i", "doesn't start as daemon")
#endif
#ifdef WIN32
("service,s", boost::program_options::value<std::string>(), "start/stop/install/uninstall service")
("service,s", boost::program_options::value<std::string>(), "start/stop/install/uninstall service")
#endif
;
;
const boost::program_options::options_description& configOptions = Tracker::getConfigOptions();
const boost::program_options::options_description& configOptions = Tracker::getConfigOptions();
boost::program_options::variables_map var_map;
boost::program_options::store(boost::program_options::parse_command_line(argc, argv, commandLine), var_map);
boost::program_options::notify(var_map);
boost::program_options::variables_map var_map;
boost::program_options::store(boost::program_options::parse_command_line(argc, argv, commandLine), var_map);
boost::program_options::notify(var_map);
if (var_map.count("help"))
{
std::cout << "UDP Tracker (UDPT) " << VERSION << " (" << PLATFORM << ")" << std::endl
<< "Copyright 2012-2016 Naim A. <naim94a@gmail.com>" << std::endl
<< "Build Date: " << __DATE__ << std::endl << std::endl;
std::cout << commandLine << std::endl;
return 0;
}
if (var_map.count("help"))
{
std::cout << "UDP Tracker (UDPT) " << VERSION << " (" << PLATFORM << ")" << std::endl
<< "Copyright 2012-2016 Naim A. <naim94a@gmail.com>" << std::endl
<< "Build Date: " << __DATE__ << std::endl << std::endl;
if (var_map.count("all-help"))
{
std::cout << commandLine << std::endl;
std::cout << configOptions << std::endl;
return 0;
}
std::cout << commandLine << std::endl;
return 0;
}
std::string config_filename(var_map["config"].as<std::string>());
bool isTest = (0 != var_map.count("test"));
if (var_map.count("all-help"))
{
std::cout << commandLine << std::endl;
std::cout << configOptions << std::endl;
return 0;
}
if (var_map.count("config"))
{
try
{
boost::program_options::basic_parsed_options<wchar_t> parsed_options = boost::program_options::parse_config_file<wchar_t>(config_filename.c_str(), configOptions);
boost::program_options::store(
parsed_options,
var_map);
}
catch (const boost::program_options::error& ex)
{
std::cerr << "ERROR: " << ex.what() << std::endl;
return -1;
}
std::string config_filename(var_map["config"].as<std::string>());
bool isTest = (0 != var_map.count("test"));
if (isTest)
{
std::cout << "Config OK" << std::endl;
return 0;
}
}
if (var_map.count("config"))
{
try
{
boost::program_options::basic_parsed_options<wchar_t> parsed_options = boost::program_options::parse_config_file<wchar_t>(config_filename.c_str(), configOptions);
boost::program_options::store(
parsed_options,
var_map);
}
catch (const boost::program_options::error& ex)
{
std::cerr << "ERROR: " << ex.what() << std::endl;
return -1;
}
boost::log::sources::severity_channel_logger_mt<> logger(boost::log::keywords::channel = "main");
Tracker::setupLogging(var_map, logger);
if (isTest)
{
std::cout << "Config OK" << std::endl;
return 0;
}
}
#ifdef linux
if (!var_map.count("interactive"))
{
daemonize(var_map);
}
::signal(SIGTERM, _signal_handler);
if (!var_map.count("interactive"))
{
daemonize(var_map);
}
::signal(SIGTERM, _signal_handler);
::signal(SIGINT, _signal_handler);
#endif
#ifdef WIN32
UDPT::Service svc(var_map);
if (var_map.count("service"))
{
const std::string& action = var_map["service"].as<std::string>();
try
{
if ("install" == action)
{
std::cerr << "Installing service..." << std::endl;
svc.install(var_map["config"].as<std::string>());
std::cerr << "Installed." << std::endl;
}
else if ("uninstall" == action)
{
std::cerr << "Removing service..." << std::endl;
svc.uninstall();
std::cerr << "Removed." << std::endl;
}
else if ("start" == action)
{
svc.start();
}
else if ("stop" == action)
{
svc.stop();
}
else
{
std::cerr << "No such service command." << std::endl;
return -1;
}
}
catch (const UDPT::OSError& ex)
{
std::cerr << "An operating system error occurred: " << ex.what() << std::endl;
return -1;
}
UDPT::Service svc(var_map);
if (var_map.count("service"))
{
const std::string& action = var_map["service"].as<std::string>();
try
{
if ("install" == action)
{
std::cerr << "Installing service..." << std::endl;
svc.install(var_map["config"].as<std::string>());
std::cerr << "Installed." << std::endl;
}
else if ("uninstall" == action)
{
std::cerr << "Removing service..." << std::endl;
svc.uninstall();
std::cerr << "Removed." << std::endl;
}
else if ("start" == action)
{
svc.start();
}
else if ("stop" == action)
{
svc.stop();
}
else
{
std::cerr << "No such service command." << std::endl;
return -1;
}
}
catch (const UDPT::OSError& ex)
{
std::cerr << "An operating system error occurred: " << ex.what() << std::endl;
return -1;
}
return 0;
}
return 0;
}
try
{
svc.setup();
return 0;
}
catch (const OSError& err)
{
if (ERROR_FAILED_SERVICE_CONTROLLER_CONNECT != err.getErrorCode())
{
BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "Failed to start as a Windows service: (" << err.getErrorCode() << "): " << err.what();
return -1;
}
}
try
{
svc.setup();
return 0;
}
catch (const OSError& err)
{
if (ERROR_FAILED_SERVICE_CONTROLLER_CONNECT != err.getErrorCode())
{
BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "Failed to start as a Windows service: (" << err.getErrorCode() << "): " << err.what();
return -1;
}
}
#endif
try
{
Tracker& tracker = UDPT::Tracker::getInstance();
tracker.start(var_map);
tracker.wait();
}
catch (const UDPT::UDPTException& ex)
{
BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "UDPT exception: (" << ex.getErrorCode() << "): " << ex.what();
return -1;
}
try
{
Tracker& tracker = UDPT::Tracker::getInstance();
tracker.start(var_map);
tracker.wait();
}
catch (const UDPT::UDPTException& ex)
{
std::cerr << "UDPT exception: (" << ex.getErrorCode() << "): " << ex.what();
return -1;
}
return 0;
LOG_INFO("core", "UDPT terminated.");
return 0;
}

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2012-2016 Naim A.
* Copyright © 2012-2017 Naim A.
*
* This file is part of UDPT.
*

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2012-2016 Naim A.
* Copyright © 2012-2017 Naim A.
*
* This file is part of UDPT.
*
@ -23,234 +23,234 @@
namespace UDPT
{
SERVICE_STATUS_HANDLE Service::s_hServiceStatus = nullptr;
SERVICE_STATUS Service::s_serviceStatus = { 0 };
SERVICE_STATUS_HANDLE Service::s_hServiceStatus = nullptr;
SERVICE_STATUS Service::s_serviceStatus = { 0 };
Service::Service(const boost::program_options::variables_map& conf) : m_conf(conf)
{
Service::Service(const boost::program_options::variables_map& conf) : m_conf(conf)
{
}
}
Service::~Service()
{
Service::~Service()
{
}
}
void Service::install(const std::string& config_path)
{
std::string& binaryPath = getFilename();
binaryPath = "\"" + binaryPath + "\" -c \"" + config_path + "\"";
std::shared_ptr<void> svcMgr = getServiceManager(SC_MANAGER_CREATE_SERVICE);
{
SC_HANDLE installedService = ::CreateService(reinterpret_cast<SC_HANDLE>(svcMgr.get()),
m_conf["service.name"].as<std::string>().c_str(),
"UDPT Tracker",
SC_MANAGER_CREATE_SERVICE,
SERVICE_WIN32_OWN_PROCESS,
SERVICE_AUTO_START,
SERVICE_ERROR_NORMAL,
binaryPath.c_str(),
NULL,
NULL,
NULL,
NULL,
NULL
);
if (nullptr == installedService)
{
throw OSError();
}
void Service::install(const std::string& config_path)
{
std::string& binaryPath = getFilename();
binaryPath = "\"" + binaryPath + "\" -c \"" + config_path + "\"";
std::shared_ptr<void> svcMgr = getServiceManager(SC_MANAGER_CREATE_SERVICE);
{
SC_HANDLE installedService = ::CreateService(reinterpret_cast<SC_HANDLE>(svcMgr.get()),
m_conf["service.name"].as<std::string>().c_str(),
"UDPT Tracker",
SC_MANAGER_CREATE_SERVICE,
SERVICE_WIN32_OWN_PROCESS,
SERVICE_AUTO_START,
SERVICE_ERROR_NORMAL,
binaryPath.c_str(),
NULL,
NULL,
NULL,
NULL,
NULL
);
if (nullptr == installedService)
{
throw OSError();
}
::CloseServiceHandle(installedService);
}
}
::CloseServiceHandle(installedService);
}
}
void Service::uninstall()
{
std::shared_ptr<void> service = getService(DELETE);
BOOL bRes = ::DeleteService(reinterpret_cast<SC_HANDLE>(service.get()));
if (FALSE == bRes)
{
throw OSError();
}
}
void Service::uninstall()
{
std::shared_ptr<void> service = getService(DELETE);
BOOL bRes = ::DeleteService(reinterpret_cast<SC_HANDLE>(service.get()));
if (FALSE == bRes)
{
throw OSError();
}
}
void Service::start()
{
std::shared_ptr<void> hSvc = getService(SERVICE_START);
BOOL bRes = ::StartService(reinterpret_cast<SC_HANDLE>(hSvc.get()), 0, NULL);
if (FALSE == bRes)
{
throw OSError();
}
}
void Service::start()
{
std::shared_ptr<void> hSvc = getService(SERVICE_START);
BOOL bRes = ::StartService(reinterpret_cast<SC_HANDLE>(hSvc.get()), 0, NULL);
if (FALSE == bRes)
{
throw OSError();
}
}
void Service::stop()
{
SERVICE_STATUS status = { 0 };
void Service::stop()
{
SERVICE_STATUS status = { 0 };
std::shared_ptr<void> hSvc = getService(SERVICE_STOP);
BOOL bRes = ::ControlService(reinterpret_cast<SC_HANDLE>(hSvc.get()), SERVICE_CONTROL_STOP, &status);
if (FALSE == bRes)
{
throw OSError();
}
}
std::shared_ptr<void> hSvc = getService(SERVICE_STOP);
BOOL bRes = ::ControlService(reinterpret_cast<SC_HANDLE>(hSvc.get()), SERVICE_CONTROL_STOP, &status);
if (FALSE == bRes)
{
throw OSError();
}
}
void Service::setup()
{
SERVICE_TABLE_ENTRY service[] = {
{ const_cast<char*>(m_conf["service.name"].as<std::string>().c_str()), reinterpret_cast<LPSERVICE_MAIN_FUNCTION>(&Service::serviceMain) },
{0, 0}
};
void Service::setup()
{
SERVICE_TABLE_ENTRY service[] = {
{ const_cast<char*>(m_conf["service.name"].as<std::string>().c_str()), reinterpret_cast<LPSERVICE_MAIN_FUNCTION>(&Service::serviceMain) },
{0, 0}
};
if (FALSE == ::StartServiceCtrlDispatcher(service))
{
throw OSError();
}
}
if (FALSE == ::StartServiceCtrlDispatcher(service))
{
throw OSError();
}
}
DWORD Service::handler(DWORD controlCode, DWORD dwEventType, LPVOID eventData, LPVOID context)
{
switch (controlCode)
{
case SERVICE_CONTROL_INTERROGATE:
return NO_ERROR;
DWORD Service::handler(DWORD controlCode, DWORD dwEventType, LPVOID eventData, LPVOID context)
{
switch (controlCode)
{
case SERVICE_CONTROL_INTERROGATE:
return NO_ERROR;
case SERVICE_CONTROL_STOP:
{
reportServiceStatus(SERVICE_STOP_PENDING, 0, 3000);
Tracker::getInstance().stop();
case SERVICE_CONTROL_STOP:
{
reportServiceStatus(SERVICE_STOP_PENDING, 0, 3000);
Tracker::getInstance().stop();
return NO_ERROR;
}
return NO_ERROR;
}
default:
return ERROR_CALL_NOT_IMPLEMENTED;
}
}
default:
return ERROR_CALL_NOT_IMPLEMENTED;
}
}
void Service::reportServiceStatus(DWORD currentState, DWORD dwExitCode, DWORD dwWaitHint)
{
static DWORD checkpoint = 1;
void Service::reportServiceStatus(DWORD currentState, DWORD dwExitCode, DWORD dwWaitHint)
{
static DWORD checkpoint = 1;
if (currentState == SERVICE_STOPPED || currentState == SERVICE_RUNNING)
{
checkpoint = 0;
}
else
{
++checkpoint;
}
if (currentState == SERVICE_STOPPED || currentState == SERVICE_RUNNING)
{
checkpoint = 0;
}
else
{
++checkpoint;
}
switch (currentState)
{
case SERVICE_RUNNING:
s_serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
break;
switch (currentState)
{
case SERVICE_RUNNING:
s_serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
break;
default:
s_serviceStatus.dwControlsAccepted = 0;
}
default:
s_serviceStatus.dwControlsAccepted = 0;
}
s_serviceStatus.dwCheckPoint = checkpoint;
s_serviceStatus.dwCurrentState = currentState;
s_serviceStatus.dwWin32ExitCode = dwExitCode;
s_serviceStatus.dwWaitHint = dwWaitHint;
s_serviceStatus.dwCheckPoint = checkpoint;
s_serviceStatus.dwCurrentState = currentState;
s_serviceStatus.dwWin32ExitCode = dwExitCode;
s_serviceStatus.dwWaitHint = dwWaitHint;
::SetServiceStatus(s_hServiceStatus, &s_serviceStatus);
}
::SetServiceStatus(s_hServiceStatus, &s_serviceStatus);
}
VOID Service::serviceMain(DWORD argc, LPCSTR argv[])
{
boost::log::sources::severity_channel_logger_mt<> logger(boost::log::keywords::channel = "service");
wchar_t *commandLine = ::GetCommandLineW();
int argCount = 0;
std::shared_ptr<LPWSTR> args(::CommandLineToArgvW(commandLine, &argCount), ::LocalFree);
if (nullptr == args)
{
BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "Failed parse command-line.";
::exit(-1);
}
VOID Service::serviceMain(DWORD argc, LPCSTR argv[])
{
boost::log::sources::severity_channel_logger_mt<> logger(boost::log::keywords::channel = "service");
if (3 != argCount)
{
BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "Bad command-line length (must have exactly 2 arguments).";
::exit(-1);
}
wchar_t *commandLine = ::GetCommandLineW();
int argCount = 0;
std::shared_ptr<LPWSTR> args(::CommandLineToArgvW(commandLine, &argCount), ::LocalFree);
if (nullptr == args)
{
BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "Failed parse command-line.";
::exit(-1);
}
if (std::wstring(args.get()[1]) != L"-c")
{
BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "Argument 1 must be \"-c\".";
::exit(-1);
}
if (3 != argCount)
{
BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "Bad command-line length (must have exactly 2 arguments).";
::exit(-1);
}
std::wstring wFilename(args.get()[2]);
std::string cFilename(wFilename.begin(), wFilename.end());
if (std::wstring(args.get()[1]) != L"-c")
{
BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "Argument 1 must be \"-c\".";
::exit(-1);
}
boost::program_options::options_description& configOptions = UDPT::Tracker::getConfigOptions();
boost::program_options::variables_map config;
boost::program_options::basic_parsed_options<wchar_t> parsed_options = boost::program_options::parse_config_file<wchar_t>(cFilename.c_str(), configOptions);
boost::program_options::store(parsed_options, config);
std::wstring wFilename(args.get()[2]);
std::string cFilename(wFilename.begin(), wFilename.end());
s_hServiceStatus = ::RegisterServiceCtrlHandlerEx(config["service.name"].as<std::string>().c_str(), Service::handler, NULL);
if (nullptr == s_hServiceStatus)
{
BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "Failed to register service control handler.";
::exit(-1);
}
boost::program_options::options_description& configOptions = UDPT::Tracker::getConfigOptions();
boost::program_options::variables_map config;
boost::program_options::basic_parsed_options<wchar_t> parsed_options = boost::program_options::parse_config_file<wchar_t>(cFilename.c_str(), configOptions);
boost::program_options::store(parsed_options, config);
s_serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
s_serviceStatus.dwServiceSpecificExitCode = 0;
s_hServiceStatus = ::RegisterServiceCtrlHandlerEx(config["service.name"].as<std::string>().c_str(), Service::handler, NULL);
if (nullptr == s_hServiceStatus)
{
BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "Failed to register service control handler.";
::exit(-1);
}
reportServiceStatus(SERVICE_START_PENDING, 0, 0);
s_serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
s_serviceStatus.dwServiceSpecificExitCode = 0;
{
UDPT::Tracker& tracker = UDPT::Tracker::getInstance();
tracker.start(config);
reportServiceStatus(SERVICE_START_PENDING, 0, 0);
reportServiceStatus(SERVICE_RUNNING, 0, 0);
{
UDPT::Tracker& tracker = UDPT::Tracker::getInstance();
tracker.start(config);
tracker.wait();
reportServiceStatus(SERVICE_RUNNING, 0, 0);
reportServiceStatus(SERVICE_STOPPED, 0, 0);
}
}
tracker.wait();
std::shared_ptr<void> Service::getService(DWORD access)
{
std::shared_ptr<void> serviceManager = getServiceManager(access);
{
SC_HANDLE service = ::OpenService(reinterpret_cast<SC_HANDLE>(serviceManager.get()), m_conf["service.name"].as<std::string>().c_str(), access);
if (nullptr == service)
{
throw OSError();
}
return std::shared_ptr<void>(service, ::CloseServiceHandle);
}
}
reportServiceStatus(SERVICE_STOPPED, 0, 0);
}
}
std::shared_ptr<void> Service::getServiceManager(DWORD access)
{
SC_HANDLE svcMgr = ::OpenSCManager(NULL, NULL, access);
if (nullptr == svcMgr)
{
throw OSError();
}
return std::shared_ptr<void>(svcMgr, ::CloseServiceHandle);
}
std::shared_ptr<void> Service::getService(DWORD access)
{
std::shared_ptr<void> serviceManager = getServiceManager(access);
{
SC_HANDLE service = ::OpenService(reinterpret_cast<SC_HANDLE>(serviceManager.get()), m_conf["service.name"].as<std::string>().c_str(), access);
if (nullptr == service)
{
throw OSError();
}
return std::shared_ptr<void>(service, ::CloseServiceHandle);
}
}
std::string Service::getFilename()
{
char filename[MAX_PATH];
DWORD dwRet = ::GetModuleFileName(NULL, filename, sizeof(filename) / sizeof(char));
if (0 == dwRet)
{
throw OSError();
}
return std::string(filename);
}
std::shared_ptr<void> Service::getServiceManager(DWORD access)
{
SC_HANDLE svcMgr = ::OpenSCManager(NULL, NULL, access);
if (nullptr == svcMgr)
{
throw OSError();
}
return std::shared_ptr<void>(svcMgr, ::CloseServiceHandle);
}
std::string Service::getFilename()
{
char filename[MAX_PATH];
DWORD dwRet = ::GetModuleFileName(NULL, filename, sizeof(filename) / sizeof(char));
if (0 == dwRet)
{
throw OSError();
}
return std::string(filename);
}
}
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2012-2016 Naim A.
* Copyright © 2012-2017 Naim A.
*
* This file is part of UDPT.
*
@ -28,42 +28,42 @@
#ifdef WIN32
namespace UDPT
{
class Service
{
public:
Service(const boost::program_options::variables_map& conf);
class Service
{
public:
Service(const boost::program_options::variables_map& conf);
virtual ~Service();
virtual ~Service();
void install(const std::string& config_path);
void install(const std::string& config_path);
void uninstall();
void uninstall();
void start();
void start();
void stop();
void stop();
void setup();
private:
const boost::program_options::variables_map& m_conf;
void setup();
private:
const boost::program_options::variables_map& m_conf;
static SERVICE_STATUS_HANDLE s_hServiceStatus;
static SERVICE_STATUS_HANDLE s_hServiceStatus;
static SERVICE_STATUS s_serviceStatus;
static SERVICE_STATUS s_serviceStatus;
std::shared_ptr<void> getService(DWORD access);
std::shared_ptr<void> getService(DWORD access);
static DWORD WINAPI handler(DWORD controlCode, DWORD dwEventType, LPVOID eventData, LPVOID context);
static DWORD WINAPI handler(DWORD controlCode, DWORD dwEventType, LPVOID eventData, LPVOID context);
static void reportServiceStatus(DWORD currentState, DWORD dwExitCode, DWORD dwWaitHint);
static void reportServiceStatus(DWORD currentState, DWORD dwExitCode, DWORD dwWaitHint);
static VOID WINAPI serviceMain(DWORD argc, LPCSTR argv[]);
static VOID WINAPI serviceMain(DWORD argc, LPCSTR argv[]);
static std::shared_ptr<void> getServiceManager(DWORD access);
static std::shared_ptr<void> getServiceManager(DWORD access);
static std::string getFilename();
};
static std::string getFilename();
};
}
#endif

View file

@ -1,65 +1,65 @@
/*
* Copyright © 2012-2016 Naim A.
*
* This file is part of UDPT.
*
* UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/
#include "tools.h"
#include "multiplatform.h"
void m_byteswap (void *dest, void *src, int sz)
{
int i;
for (i = 0;i < sz;i++)
{
((char*)dest)[i] = ((char*)src)[(sz - 1) - i];
}
}
uint16_t m_hton16(uint16_t n)
{
uint16_t r;
m_byteswap (&r, &n, 2);
return r;
}
uint64_t m_hton64 (uint64_t n)
{
uint64_t r;
m_byteswap (&r, &n, 8);
return r;
}
uint32_t m_hton32 (uint32_t n)
{
uint64_t r;
m_byteswap (&r, &n, 4);
return r;
}
static const char hexadecimal[] = "0123456789abcdef";
void to_hex_str (const uint8_t *hash, char *data)
{
int i;
for (i = 0;i < 20;i++)
{
data[i * 2] = hexadecimal[hash[i] / 16];
data[i * 2 + 1] = hexadecimal[hash[i] % 16];
}
data[40] = '\0';
}
/*
* Copyright © 2012-2017 Naim A.
*
* This file is part of UDPT.
*
* UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/
#include "tools.h"
#include "multiplatform.h"
void m_byteswap (void *dest, void *src, int sz)
{
int i;
for (i = 0;i < sz;i++)
{
((char*)dest)[i] = ((char*)src)[(sz - 1) - i];
}
}
uint16_t m_hton16(uint16_t n)
{
uint16_t r;
m_byteswap (&r, &n, 2);
return r;
}
uint64_t m_hton64 (uint64_t n)
{
uint64_t r;
m_byteswap (&r, &n, 8);
return r;
}
uint32_t m_hton32 (uint32_t n)
{
uint64_t r;
m_byteswap (&r, &n, 4);
return r;
}
static const char hexadecimal[] = "0123456789abcdef";
void to_hex_str (const uint8_t *hash, char *data)
{
int i;
for (i = 0;i < 20;i++)
{
data[i * 2] = hexadecimal[hash[i] / 16];
data[i * 2 + 1] = hexadecimal[hash[i] % 16];
}
data[40] = '\0';
}

View file

@ -1,50 +1,50 @@
/*
* Copyright © 2012-2016 Naim A.
*
* This file is part of UDPT.
*
* UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TOOLS_H_
#define TOOLS_H_
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Swaps Bytes:
* example (htons):
* short a = 1234;
* short b;
* m_byteswap (&b, &a, sizeof(a));
*/
void m_byteswap (void *dest, void *src, int sz);
uint16_t m_hton16(uint16_t n);
uint32_t m_hton32 (uint32_t n);
uint64_t m_hton64 (uint64_t n);
void to_hex_str (const uint8_t *hash, char *data);
#ifdef __cplusplus
}
#endif
#endif /* TOOLS_H_ */
/*
* Copyright © 2012-2017 Naim A.
*
* This file is part of UDPT.
*
* UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TOOLS_H_
#define TOOLS_H_
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Swaps Bytes:
* example (htons):
* short a = 1234;
* short b;
* m_byteswap (&b, &a, sizeof(a));
*/
void m_byteswap (void *dest, void *src, int sz);
uint16_t m_hton16(uint16_t n);
uint32_t m_hton32 (uint32_t n);
uint64_t m_hton64 (uint64_t n);
void to_hex_str (const uint8_t *hash, char *data);
#ifdef __cplusplus
}
#endif
#endif /* TOOLS_H_ */

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2012-2016 Naim A.
* Copyright © 2012-2017 Naim A.
*
* This file is part of UDPT.
*
@ -16,52 +16,60 @@
* You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <fstream>
#include "tracker.hpp"
#include "logging.hpp"
namespace UDPT
{
Tracker::Tracker() : m_logger(boost::log::keywords::channel = "TRACKER")
Tracker::Tracker() : m_logStream(nullptr)
{
BOOST_LOG_SEV(m_logger, boost::log::trivial::debug) << "Initialized Tracker";
}
Tracker::~Tracker()
{
BOOST_LOG_SEV(m_logger, boost::log::trivial::debug) << "Destroying Tracker...";
try {
if (nullptr != m_logStream) {
m_logStream->flush();
delete m_logStream;
m_logStream = nullptr;
}
} catch (...) {
}
}
void Tracker::stop()
{
BOOST_LOG_SEV(m_logger, boost::log::trivial::info) << "Requesting Tracker to terminate.";
{
LOG_INFO("tracker", "Requesting components to terminate...");
m_udpTracker->stop();
wait();
// cause other components to destruct.
m_apiSrv = nullptr;
m_webApp = nullptr;
m_udpTracker = nullptr;
}
void Tracker::wait()
{
m_udpTracker->wait();
BOOST_LOG_SEV(m_logger, boost::log::trivial::info) << "Tracker terminated.";
{
m_udpTracker->wait();
}
void Tracker::start(const boost::program_options::variables_map& conf)
{
BOOST_LOG_SEV(m_logger, boost::log::trivial::debug) << "Starting Tracker...";
m_udpTracker = std::shared_ptr<UDPTracker>(new UDPTracker(conf));
{
setupLogging(conf);
LOG_INFO("core", "Initializing...");
m_udpTracker = std::shared_ptr<UDPTracker>(new UDPTracker(conf));
if (conf["apiserver.enable"].as<bool>())
{
BOOST_LOG_SEV(m_logger, boost::log::trivial::info) << "Initializing and deploying WebAPI...";
{
m_apiSrv = std::shared_ptr<UDPT::Server::HTTPServer>(new UDPT::Server::HTTPServer(conf));
m_webApp = std::shared_ptr<UDPT::Server::WebApp>(new UDPT::Server::WebApp(m_apiSrv, m_udpTracker->m_conn.get(), conf));
m_webApp->deploy();
}
m_udpTracker->start();
BOOST_LOG_SEV(m_logger, boost::log::trivial::info) << "Tracker started.";
m_udpTracker->start();
}
Tracker& Tracker::getInstance()
@ -71,75 +79,73 @@ namespace UDPT
return s_tracker;
}
boost::program_options::options_description Tracker::getConfigOptions()
{
boost::program_options::options_description configOptions("Configuration options");
configOptions.add_options()
("db.driver", boost::program_options::value<std::string>()->default_value("sqlite3"), "database driver to use")
("db.param", boost::program_options::value<std::string>()->default_value("/var/lib/udpt.db"), "database connection parameters")
boost::program_options::options_description Tracker::getConfigOptions()
{
boost::program_options::options_description configOptions("Configuration options");
configOptions.add_options()
("db.driver", boost::program_options::value<std::string>()->default_value("sqlite3"), "database driver to use")
("db.param", boost::program_options::value<std::string>()->default_value("/var/lib/udpt.db"), "database connection parameters")
("tracker.is_dynamic", boost::program_options::value<bool>()->default_value(true), "Sets if the tracker is dynamic")
("tracker.port", boost::program_options::value<unsigned short>()->default_value(6969), "UDP port to listen on")
("tracker.threads", boost::program_options::value<unsigned>()->default_value(5), "threads to run (UDP only)")
("tracker.allow_remotes", boost::program_options::value<bool>()->default_value(true), "allows clients to report remote IPs")
("tracker.allow_iana_ips", boost::program_options::value<bool>()->default_value(false), "allows IANA reserved IPs to connect (useful for debugging)")
("tracker.announce_interval", boost::program_options::value<unsigned>()->default_value(1800), "announce interval")
("tracker.cleanup_interval", boost::program_options::value<unsigned>()->default_value(120), "sets database cleanup interval")
("tracker.is_dynamic", boost::program_options::value<bool>()->default_value(true), "Sets if the tracker is dynamic")
("tracker.port", boost::program_options::value<unsigned short>()->default_value(6969), "UDP port to listen on")
("tracker.threads", boost::program_options::value<unsigned>()->default_value(5), "threads to run (UDP only)")
("tracker.allow_remotes", boost::program_options::value<bool>()->default_value(true), "allows clients to report remote IPs")
("tracker.allow_iana_ips", boost::program_options::value<bool>()->default_value(false), "allows IANA reserved IPs to connect (useful for debugging)")
("tracker.announce_interval", boost::program_options::value<unsigned>()->default_value(1800), "announce interval")
("tracker.cleanup_interval", boost::program_options::value<unsigned>()->default_value(120), "sets database cleanup interval")
("apiserver.enable", boost::program_options::value<bool>()->default_value(0), "Enable API server?")
("apiserver.threads", boost::program_options::value<unsigned short>()->default_value(1), "threads for API server")
("apiserver.port", boost::program_options::value<unsigned short>()->default_value(6969), "TCP port to listen on")
("apiserver.enable", boost::program_options::value<bool>()->default_value(0), "Enable API server?")
("apiserver.threads", boost::program_options::value<unsigned short>()->default_value(1), "threads for API server")
("apiserver.port", boost::program_options::value<unsigned short>()->default_value(6969), "TCP port to listen on")
("logging.filename", boost::program_options::value<std::string>()->default_value("/var/log/udpt.log"), "file to write logs to")
("logging.level", boost::program_options::value<std::string>()->default_value("warning"), "log level (fatal/error/warning/info/debug/trace)")
("logging.filename", boost::program_options::value<std::string>()->default_value("/var/log/udpt.log"), "file to write logs to")
("logging.level", boost::program_options::value<std::string>()->default_value("warning"), "log level (fatal/error/warning/info/debug)")
#ifdef linux
("daemon.chdir", boost::program_options::value<std::string>()->default_value("/"), "home directory for daemon")
("daemon.chdir", boost::program_options::value<std::string>()->default_value("/"), "home directory for daemon")
#endif
#ifdef WIN32
("service.name", boost::program_options::value<std::string>()->default_value("udpt"), "service name to use")
("service.name", boost::program_options::value<std::string>()->default_value("udpt"), "service name to use")
#endif
;
;
return configOptions;
}
return configOptions;
}
void Tracker::setupLogging(const boost::program_options::variables_map& config, boost::log::sources::severity_channel_logger_mt<>& logger)
{
boost::log::add_common_attributes();
boost::shared_ptr<boost::log::sinks::text_file_backend> logBackend = boost::make_shared<boost::log::sinks::text_file_backend>(
boost::log::keywords::file_name = config["logging.filename"].as<std::string>(),
boost::log::keywords::auto_flush = true,
boost::log::keywords::open_mode = std::ios::out | std::ios::app
);
typedef boost::log::sinks::asynchronous_sink<boost::log::sinks::text_file_backend> udptSink_t;
boost::shared_ptr<udptSink_t> async_sink(new udptSink_t(logBackend));
async_sink->set_formatter(
boost::log::expressions::stream
<< boost::log::expressions::format_date_time<boost::posix_time::ptime>("TimeStamp", "%Y-%m-%d %H:%M:%S") << " "
<< boost::log::expressions::attr<int>("Severity")
<< " [" << boost::log::expressions::attr<std::string>("Channel") << "] \t"
<< boost::log::expressions::smessage
);
auto loggingCore = boost::log::core::get();
loggingCore->add_sink(async_sink);
void Tracker::setupLogging(const boost::program_options::variables_map& va_map) {
Logging::Logger& logger = Logging::Logger::getLogger();
std::string severity = config["logging.level"].as<std::string>();
std::transform(severity.begin(), severity.end(), severity.begin(), ::tolower);
int severityVal = boost::log::trivial::warning;
if ("fatal" == severity) severityVal = boost::log::trivial::fatal;
else if ("error" == severity) severityVal = boost::log::trivial::error;
else if ("warning" == severity) severityVal = boost::log::trivial::warning;
else if ("info" == severity) severityVal = boost::log::trivial::info;
else if ("debug" == severity) severityVal = boost::log::trivial::debug;
else if ("trace" == severity) severityVal = boost::log::trivial::trace;
else
{
BOOST_LOG_SEV(logger, boost::log::trivial::warning) << "Unknown debug level \"" << severity << "\" defaulting to warning";
}
logger.addStream(&std::cerr, Logging::Severity::FATAL);
loggingCore->set_filter(
boost::log::trivial::severity >= severityVal
);
}
Logging::Severity severity;
std::string severity_text = va_map["logging.level"].as<std::string>();
std::transform(severity_text.begin(), severity_text.end(), severity_text.begin(), ::tolower);
if (severity_text == "fatal") {
severity = Logging::Severity::FATAL;
} else if (severity_text == "error") {
severity = Logging::Severity::ERROR;
} else if (severity_text == "warning") {
severity = Logging::Severity::WARNING;
} else if (severity_text == "info") {
severity = Logging::Severity::INFO;
} else if (severity_text == "debug") {
severity = Logging::Severity::DEBUG;
} else {
severity = Logging::Severity::UNSET;
}
const std::string& logFileName = va_map["logging.filename"].as<std::string>();
Logging::Severity real_severity = (severity == Logging::Severity::UNSET ? Logging::Severity::INFO : severity);
if (logFileName.length() == 0 || logFileName == "--") {
logger.addStream(&std::cerr, real_severity);
} else {
m_logStream = new ofstream(logFileName, std::ios::app | std::ios::out);
logger.addStream(m_logStream, real_severity);
}
if (severity != real_severity) {
LOG_WARN("core", "'" << severity_text << "' is invalid, log level set to " << real_severity);
}
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2012-2016 Naim A.
* Copyright © 2012-2017 Naim A.
*
* This file is part of UDPT.
*
@ -21,16 +21,7 @@
#include <memory>
#include <boost/program_options.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/sources/severity_channel_logger.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/sinks/text_file_backend.hpp>
#include <boost/log/sinks/async_frontend.hpp>
#include <boost/log/keywords/format.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include "multiplatform.h"
#include "udpTracker.hpp"
@ -51,18 +42,18 @@ namespace UDPT
void wait();
static Tracker& getInstance();
static Tracker& getInstance();
static boost::program_options::options_description getConfigOptions();
static void setupLogging(const boost::program_options::variables_map& config, boost::log::sources::severity_channel_logger_mt<>& logger);
static boost::program_options::options_description getConfigOptions();
private:
std::shared_ptr<UDPT::UDPTracker> m_udpTracker;
std::shared_ptr<UDPT::Server::HTTPServer> m_apiSrv;
std::shared_ptr<UDPT::Server::WebApp> m_webApp;
boost::log::sources::severity_channel_logger_mt<> m_logger;
Tracker();
void setupLogging(const boost::program_options::variables_map& va_map);
std::ostream *m_logStream;
};
}

View file

@ -1,477 +1,450 @@
/*
* Copyright © 2012-2016 Naim A.
*
* This file is part of UDPT.
*
* UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/
#include "udpTracker.hpp"
using namespace UDPT::Data;
#define UDP_BUFFER_SIZE 2048
namespace UDPT
{
UDPTracker::UDPTracker(const boost::program_options::variables_map& conf) : m_conf(conf), m_logger(boost::log::keywords::channel = "UDPTracker")
{
this->m_allowRemotes = conf["tracker.allow_remotes"].as<bool>();
this->m_allowIANA_IPs = conf["tracker.allow_iana_ips"].as<bool>();
this->m_isDynamic = conf["tracker.is_dynamic"].as<bool>();
this->m_announceInterval = conf["tracker.announce_interval"].as<unsigned>();
this->m_cleanupInterval = conf["tracker.cleanup_interval"].as<unsigned>();
this->m_port = conf["tracker.port"].as<unsigned short>();
this->m_threadCount = conf["tracker.threads"].as<unsigned>() + 1;
std::list<SOCKADDR_IN> addrs;
if (addrs.empty())
{
SOCKADDR_IN sa;
sa.sin_port = m_hton16(m_port);
sa.sin_addr.s_addr = 0L;
addrs.push_back(sa);
}
this->m_localEndpoint = addrs.front();
this->m_conn = std::shared_ptr<DatabaseDriver>(new Data::SQLite3Driver(m_conf, this->m_isDynamic));
}
UDPTracker::~UDPTracker()
{
stop();
}
void UDPTracker::start()
{
SOCKET sock;
int r, // saves results
i, // loop index
yup; // just to set TRUE
std::string dbname;// saves the Database name.
sock = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock == INVALID_SOCKET)
{
throw UDPT::UDPTException("Failed to create socket");
}
yup = 1;
::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&yup, 1);
{
// don't block recvfrom for too long.
#if defined(linux)
timeval timeout = { 0 };
timeout.tv_sec = 5;
#elif defined(WIN32)
DWORD timeout = 5000;
#else
#error Unsupported OS.
#endif
::setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<const char*>(&timeout), sizeof(timeout));
}
this->m_localEndpoint.sin_family = AF_INET;
r = ::bind(sock, reinterpret_cast<SOCKADDR*>(&this->m_localEndpoint), sizeof(SOCKADDR_IN));
if (r == SOCKET_ERROR)
{
#ifdef WIN32
::closesocket(sock);
#elif defined (linux)
::close(sock);
#endif
throw UDPT::UDPTException("Failed to bind socket.");
}
{
char buff[INET_ADDRSTRLEN];
BOOST_LOG_SEV(m_logger, boost::log::trivial::info) << "UDP tracker bound on " << ::inet_ntop(AF_INET, reinterpret_cast<LPVOID>(&m_localEndpoint.sin_addr), buff, sizeof(buff)) << ":" << htons(m_localEndpoint.sin_port);
}
this->m_sock = sock;
// create maintainer thread.
m_threads.push_back(boost::thread(UDPTracker::_maintainance_start, this));
for (i = 1;i < this->m_threadCount; i++)
{
m_threads.push_back(boost::thread(UDPTracker::_thread_start, this));
}
}
void UDPTracker::stop()
{
#ifdef linux
::close(m_sock);
#elif defined (WIN32)
::closesocket(m_sock);
#endif
BOOST_LOG_SEV(m_logger, boost::log::trivial::warning) << "Interrupting workers...";
for (std::vector<boost::thread>::iterator it = m_threads.begin(); it != m_threads.end(); ++it)
{
it->interrupt();
}
wait();
}
void UDPTracker::wait()
{
BOOST_LOG_SEV(m_logger, boost::log::trivial::warning) << "Waiting for threads to terminate...";
for (std::vector<boost::thread>::iterator it = m_threads.begin(); it != m_threads.end(); ++it)
{
it->join();
}
}
int UDPTracker::sendError(UDPTracker* usi, SOCKADDR_IN* remote, uint32_t transactionID, const std::string &msg)
{
struct udp_error_response error;
int msg_sz, // message size to send.
i; // copy loop
char buff [1024]; // more than reasonable message size...
error.action = m_hton32 (3);
error.transaction_id = transactionID;
error.message = (char*)msg.c_str();
msg_sz = 4 + 4 + 1 + msg.length();
// test against overflow message. resolves issue 4.
if (msg_sz > 1024)
return -1;
::memcpy(buff, &error, 8);
for (i = 8;i <= msg_sz;i++)
{
buff[i] = msg[i - 8];
}
::sendto(usi->m_sock, buff, msg_sz, 0, reinterpret_cast<SOCKADDR*>(remote), sizeof(*remote));
return 0;
}
int UDPTracker::handleConnection(UDPTracker *usi, SOCKADDR_IN *remote, char *data)
{
ConnectionRequest *req = reinterpret_cast<ConnectionRequest*>(data);
ConnectionResponse resp;
resp.action = m_hton32(0);
resp.transaction_id = req->transaction_id;
if (!usi->m_conn->genConnectionId(&resp.connection_id,
m_hton32(remote->sin_addr.s_addr),
m_hton16(remote->sin_port)))
{
return 1;
}
::sendto(usi->m_sock, (char*)&resp, sizeof(ConnectionResponse), 0, (SOCKADDR*)remote, sizeof(SOCKADDR_IN));
{
char buffer[INET_ADDRSTRLEN];
BOOST_LOG_SEV(usi->m_logger, boost::log::trivial::debug) << "Connection Request from " << ::inet_ntop(AF_INET, &remote->sin_addr, buffer, sizeof(buffer)) << "; cId=" << resp.connection_id << "; tId=" << resp.transaction_id;
}
return 0;
}
int UDPTracker::handleAnnounce(UDPTracker *usi, SOCKADDR_IN *remote, char *data)
{
AnnounceRequest *req;
AnnounceResponse *resp;
int q, // peer counts
bSize, // message size
i; // loop index
DatabaseDriver::TorrentEntry tE;
uint8_t buff[1028]; // Reasonable buffer size. (header+168 peers)
req = (AnnounceRequest*)data;
if (!usi->m_conn->verifyConnectionId(req->connection_id,
m_hton32(remote->sin_addr.s_addr),
m_hton16(remote->sin_port)))
{
return 1;
}
// change byte order:
req->port = m_hton16(req->port);
req->ip_address = m_hton32(req->ip_address);
req->downloaded = m_hton64(req->downloaded);
req->event = m_hton32(req->event); // doesn't really matter for this tracker
req->uploaded = m_hton64(req->uploaded);
req->num_want = m_hton32(req->num_want);
req->left = m_hton64(req->left);
if (!usi->m_allowRemotes && req->ip_address != 0)
{
UDPTracker::sendError(usi, remote, req->transaction_id, "Tracker doesn't allow remote IP's; Request ignored.");
return 0;
}
if (!usi->m_conn->isTorrentAllowed(req->info_hash))
{
UDPTracker::sendError(usi, remote, req->transaction_id, "info_hash not registered.");
return 0;
}
// load peers
q = 30;
if (req->num_want >= 1)
q = std::min<int>(q, req->num_want);
DatabaseDriver::TrackerEvents event;
{
std::shared_ptr<DatabaseDriver::PeerEntry> peersSptr = std::shared_ptr<DatabaseDriver::PeerEntry>(new DatabaseDriver::PeerEntry[q]);
DatabaseDriver::PeerEntry *peers = peersSptr.get();
switch (req->event)
{
case 1:
event = DatabaseDriver::EVENT_COMPLETE;
break;
case 2:
event = DatabaseDriver::EVENT_START;
break;
case 3:
event = DatabaseDriver::EVENT_STOP;
break;
default:
event = DatabaseDriver::EVENT_UNSPEC;
break;
}
if (event == DatabaseDriver::EVENT_STOP)
q = 0; // no need for peers when stopping.
if (q > 0)
usi->m_conn->getPeers(req->info_hash, &q, peers);
bSize = 20; // header is 20 bytes
bSize += (6 * q); // + 6 bytes per peer.
tE.info_hash = req->info_hash;
usi->m_conn->getTorrentInfo(&tE);
resp = (AnnounceResponse*)buff;
resp->action = m_hton32(1);
resp->interval = m_hton32(usi->m_announceInterval);
resp->leechers = m_hton32(tE.leechers);
resp->seeders = m_hton32(tE.seeders);
resp->transaction_id = req->transaction_id;
for (i = 0; i < q; i++)
{
int x = i * 6;
// network byte order!!!
// IP
buff[20 + x] = ((peers[i].ip & (0xff << 24)) >> 24);
buff[21 + x] = ((peers[i].ip & (0xff << 16)) >> 16);
buff[22 + x] = ((peers[i].ip & (0xff << 8)) >> 8);
buff[23 + x] = (peers[i].ip & 0xff);
// port
buff[24 + x] = ((peers[i].port & (0xff << 8)) >> 8);
buff[25 + x] = (peers[i].port & 0xff);
}
}
::sendto(usi->m_sock, (char*)buff, bSize, 0, (SOCKADDR*)remote, sizeof(SOCKADDR_IN));
// update DB.
uint32_t ip;
if (req->ip_address == 0) // default
ip = m_hton32 (remote->sin_addr.s_addr);
else
ip = req->ip_address;
usi->m_conn->updatePeer(req->peer_id, req->info_hash, ip, req->port,
req->downloaded, req->left, req->uploaded, event);
return 0;
}
int UDPTracker::handleScrape(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int len)
{
ScrapeRequest *sR = reinterpret_cast<ScrapeRequest*>(data);
int v, // validation helper
c, // torrent counter
i, // loop counter
j; // loop counter
uint8_t hash [20];
ScrapeResponse *resp;
uint8_t buffer [1024]; // up to 74 torrents can be scraped at once (17*74+8) < 1024
// validate request length:
v = len - 16;
if (v < 0 || v % 20 != 0)
{
UDPTracker::sendError(usi, remote, sR->transaction_id, "Bad scrape request.");
return 0;
}
if (!usi->m_conn->verifyConnectionId(sR->connection_id,
m_hton32(remote->sin_addr.s_addr),
m_hton16(remote->sin_port)))
{
return 1;
}
// get torrent count.
c = v / 20;
resp = reinterpret_cast<ScrapeResponse*>(buffer);
resp->action = m_hton32(2);
resp->transaction_id = sR->transaction_id;
for (i = 0;i < c;i++)
{
int32_t *seeders,
*completed,
*leechers;
for (j = 0; j < 20;j++)
hash[j] = data[j + (i*20)+16];
seeders = (int32_t*)&buffer[i*12+8];
completed = (int32_t*)&buffer[i*12+12];
leechers = (int32_t*)&buffer[i*12+16];
DatabaseDriver::TorrentEntry tE;
tE.info_hash = hash;
if (!usi->m_conn->getTorrentInfo(&tE))
{
sendError(usi, remote, sR->transaction_id, "Scrape Failed: couldn't retrieve torrent data");
return 0;
}
*seeders = m_hton32(tE.seeders);
*completed = m_hton32(tE.completed);
*leechers = m_hton32(tE.leechers);
}
::sendto(usi->m_sock, reinterpret_cast<const char*>(buffer), sizeof(buffer), 0, reinterpret_cast<SOCKADDR*>(remote), sizeof(SOCKADDR_IN));
return 0;
}
int UDPTracker::isIANAIP(uint32_t ip)
{
uint8_t x = (ip % 256);
if (x == 0 || x == 10 || x == 127 || x >= 224)
return 1;
return 0;
}
int UDPTracker::resolveRequest(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int r)
{
ConnectionRequest* cR = reinterpret_cast<ConnectionRequest*>(data);
uint32_t action;
action = m_hton32(cR->action);
if (!usi->m_allowIANA_IPs)
{
if (isIANAIP(remote->sin_addr.s_addr))
{
char buffer[INET_ADDRSTRLEN];
BOOST_LOG_SEV(usi->m_logger, boost::log::trivial::warning) << "Client ignored (IANA IP): " << ::inet_ntop(AF_INET, &remote->sin_addr, buffer, sizeof(buffer));
return 0; // Access Denied: IANA reserved IP.
}
}
{
char buffer[INET_ADDRSTRLEN];
BOOST_LOG_SEV(usi->m_logger, boost::log::trivial::debug) << "Client request from " << ::inet_ntop(AF_INET, &remote->sin_addr, buffer, sizeof(buffer));
}
if (action == 0 && r >= 16)
return UDPTracker::handleConnection(usi, remote, data);
else if (action == 1 && r >= 98)
return UDPTracker::handleAnnounce(usi, remote, data);
else if (action == 2)
return UDPTracker::handleScrape(usi, remote, data, r);
else
{
UDPTracker::sendError(usi, remote, cR->transaction_id, "Tracker couldn't understand Client's request.");
return -1;
}
return 0;
}
void UDPTracker::_thread_start(UDPTracker *usi)
{
BOOST_LOG_SEV(usi->m_logger, boost::log::trivial::info) << "Worker thread started with PID=" << boost::this_thread::get_id() << ".";
SOCKADDR_IN remoteAddr;
char tmpBuff[UDP_BUFFER_SIZE];
#ifdef linux
socklen_t addrSz;
#else
int addrSz;
#endif
addrSz = sizeof(SOCKADDR_IN);
while (true)
{
// peek into the first 12 bytes of data; determine if connection request or announce request.
int r = ::recvfrom(usi->m_sock, (char*)tmpBuff, UDP_BUFFER_SIZE, 0, (SOCKADDR*)&remoteAddr, &addrSz);
if (r <= 0)
{
boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
continue;
}
{
boost::this_thread::disable_interruption di;
UDPTracker::resolveRequest(usi, &remoteAddr, tmpBuff, r);
}
}
}
void UDPTracker::_maintainance_start(UDPTracker* usi)
{
BOOST_LOG_SEV(usi->m_logger, boost::log::trivial::info) << "Maintenance thread started with PID=" << boost::this_thread::get_id() << ".";
while (true)
{
{
boost::this_thread::disable_interruption di;
BOOST_LOG_SEV(usi->m_logger, boost::log::trivial::info) << "Running cleanup...";
usi->m_conn->cleanup();
}
boost::this_thread::sleep_for(boost::chrono::seconds(usi->m_cleanupInterval));
}
}
};
/*
* Copyright © 2012-2017 Naim A.
*
* This file is part of UDPT.
*
* UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/
#include "udpTracker.hpp"
#include "logging.hpp"
using namespace UDPT::Data;
#define UDP_BUFFER_SIZE 2048
namespace UDPT
{
UDPTracker::UDPTracker(const boost::program_options::variables_map& conf) : m_conf(conf) {
this->m_allowRemotes = conf["tracker.allow_remotes"].as<bool>();
this->m_allowIANA_IPs = conf["tracker.allow_iana_ips"].as<bool>();
this->m_isDynamic = conf["tracker.is_dynamic"].as<bool>();
this->m_announceInterval = conf["tracker.announce_interval"].as<unsigned>();
this->m_cleanupInterval = conf["tracker.cleanup_interval"].as<unsigned>();
this->m_port = conf["tracker.port"].as<unsigned short>();
this->m_threadCount = conf["tracker.threads"].as<unsigned>() + 1;
this->m_localEndpoint.sin_family = AF_INET;
this->m_localEndpoint.sin_port = m_hton16(m_port);
this->m_localEndpoint.sin_addr.s_addr = 0L;
this->m_conn = std::shared_ptr<DatabaseDriver>(new Data::SQLite3Driver(m_conf, this->m_isDynamic));
}
UDPTracker::~UDPTracker() {
}
void UDPTracker::start() {
SOCKET sock;
int r, // saves results
i, // loop index
yup; // just to set TRUE
std::string dbname;// saves the Database name.
sock = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock == INVALID_SOCKET)
{
LOG_FATAL("udp-tracker", "Failed to create socket. error=" << errno);
throw UDPT::UDPTException("Failed to create socket");
}
yup = 1;
::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&yup, 1);
{
// don't block recvfrom for too long.
#if defined(linux)
timeval timeout = { 0 };
timeout.tv_sec = 5;
#elif defined(WIN32)
DWORD timeout = 5000;
#else
#error Unsupported OS.
#endif
::setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<const char*>(&timeout), sizeof(timeout));
}
this->m_localEndpoint.sin_family = AF_INET;
r = ::bind(sock, reinterpret_cast<SOCKADDR*>(&this->m_localEndpoint), sizeof(SOCKADDR_IN));
if (r == SOCKET_ERROR)
{
LOG_FATAL("udp-tracker", "Failed to bind socket. error=" << errno);
#ifdef WIN32
::closesocket(sock);
#elif defined (linux)
::close(sock);
#endif
throw UDPT::UDPTException("Failed to bind socket.");
}
this->m_sock = sock;
LOG_INFO("udp-tracker", "Tracker bound to " << inet_ntoa(this->m_localEndpoint.sin_addr));
// create maintainer thread.
m_threads.push_back(std::thread(UDPTracker::_maintainance_start, this));
LOG_INFO("udp-tracker", "Started maintenance thread @" << m_threads.back().get_id());
m_shouldRun = true;
for (i = 1;i < this->m_threadCount; i++)
{
m_threads.push_back(std::thread(UDPTracker::_thread_start, this));
LOG_INFO("udp-tracker", "Started worker thread @" << m_threads.back().get_id());
}
}
void UDPTracker::stop() {
// tell workers to stop running...
m_shouldRun = false;
// stop maintenance thread's sleep...
m_maintenanceCondition.notify_one();
}
/**
* @brief blocks until all threads die.
* @note This method should be called only once, preferably by the main thread.
* **/
void UDPTracker::wait() {
LOG_INFO("udp-tracker", "Waiting for threads to terminate...");
for (std::vector<std::thread>::iterator it = m_threads.begin(); it != m_threads.end(); ++it)
{
it->join();
}
LOG_INFO("udp-tracker", "UDP Tracker terminated");
#ifdef linux
::close(m_sock);
#elif defined (WIN32)
::closesocket(m_sock);
#endif
}
int UDPTracker::sendError(UDPTracker* usi, SOCKADDR_IN* remote, uint32_t transactionID, const std::string &msg) {
struct udp_error_response error;
int msg_sz, // message size to send.
i; // copy loop
char buff [1024]; // more than reasonable message size...
error.action = m_hton32 (3);
error.transaction_id = transactionID;
error.message = (char*)msg.c_str();
msg_sz = 4 + 4 + 1 + msg.length();
// test against overflow message. resolves issue 4.
if (msg_sz > 1024)
return -1;
::memcpy(buff, &error, 8);
for (i = 8;i <= msg_sz;i++)
{
buff[i] = msg[i - 8];
}
::sendto(usi->m_sock, buff, msg_sz, 0, reinterpret_cast<SOCKADDR*>(remote), sizeof(*remote));
LOG_DEBUG("udp-tracker", "Error sent to " << inet_ntoa(remote->sin_addr) << ", '" << msg << "' (len=" << msg_sz << ")");
return 0;
}
int UDPTracker::handleConnection(UDPTracker *usi, SOCKADDR_IN *remote, char *data) {
ConnectionRequest *req = reinterpret_cast<ConnectionRequest*>(data);
ConnectionResponse resp;
resp.action = m_hton32(0);
resp.transaction_id = req->transaction_id;
if (!usi->m_conn->genConnectionId(&resp.connection_id,
m_hton32(remote->sin_addr.s_addr),
m_hton16(remote->sin_port)))
{
return 1;
}
::sendto(usi->m_sock, (char*)&resp, sizeof(ConnectionResponse), 0, (SOCKADDR*)remote, sizeof(SOCKADDR_IN));
return 0;
}
int UDPTracker::handleAnnounce(UDPTracker *usi, SOCKADDR_IN *remote, char *data) {
AnnounceRequest *req;
AnnounceResponse *resp;
int q, // peer counts
bSize, // message size
i; // loop index
DatabaseDriver::TorrentEntry tE;
uint8_t buff[1028]; // Reasonable buffer size. (header+168 peers)
req = (AnnounceRequest*)data;
if (!usi->m_conn->verifyConnectionId(req->connection_id,
m_hton32(remote->sin_addr.s_addr),
m_hton16(remote->sin_port)))
{
return 1;
}
// change byte order:
req->port = m_hton16(req->port);
req->ip_address = m_hton32(req->ip_address);
req->downloaded = m_hton64(req->downloaded);
req->event = m_hton32(req->event); // doesn't really matter for this tracker
req->uploaded = m_hton64(req->uploaded);
req->num_want = m_hton32(req->num_want);
req->left = m_hton64(req->left);
if (!usi->m_allowRemotes && req->ip_address != 0)
{
UDPTracker::sendError(usi, remote, req->transaction_id, "Tracker doesn't allow remote IP's; Request ignored.");
return 0;
}
if (!usi->m_conn->isTorrentAllowed(req->info_hash))
{
UDPTracker::sendError(usi, remote, req->transaction_id, "info_hash not registered.");
return 0;
}
// load peers
q = 30;
if (req->num_want >= 1)
q = std::min<int>(q, req->num_want);
DatabaseDriver::TrackerEvents event;
{
std::shared_ptr<DatabaseDriver::PeerEntry> peersSptr = std::shared_ptr<DatabaseDriver::PeerEntry>(new DatabaseDriver::PeerEntry[q]);
DatabaseDriver::PeerEntry *peers = peersSptr.get();
switch (req->event)
{
case 1:
event = DatabaseDriver::EVENT_COMPLETE;
break;
case 2:
event = DatabaseDriver::EVENT_START;
break;
case 3:
event = DatabaseDriver::EVENT_STOP;
break;
default:
event = DatabaseDriver::EVENT_UNSPEC;
break;
}
if (event == DatabaseDriver::EVENT_STOP)
q = 0; // no need for peers when stopping.
if (q > 0)
usi->m_conn->getPeers(req->info_hash, &q, peers);
bSize = 20; // header is 20 bytes
bSize += (6 * q); // + 6 bytes per peer.
tE.info_hash = req->info_hash;
usi->m_conn->getTorrentInfo(&tE);
resp = (AnnounceResponse*)buff;
resp->action = m_hton32(1);
resp->interval = m_hton32(usi->m_announceInterval);
resp->leechers = m_hton32(tE.leechers);
resp->seeders = m_hton32(tE.seeders);
resp->transaction_id = req->transaction_id;
for (i = 0; i < q; i++)
{
int x = i * 6;
// network byte order!!!
// IP
buff[20 + x] = ((peers[i].ip & (0xff << 24)) >> 24);
buff[21 + x] = ((peers[i].ip & (0xff << 16)) >> 16);
buff[22 + x] = ((peers[i].ip & (0xff << 8)) >> 8);
buff[23 + x] = (peers[i].ip & 0xff);
// port
buff[24 + x] = ((peers[i].port & (0xff << 8)) >> 8);
buff[25 + x] = (peers[i].port & 0xff);
}
}
::sendto(usi->m_sock, (char*)buff, bSize, 0, (SOCKADDR*)remote, sizeof(SOCKADDR_IN));
LOG_DEBUG("udp-tracker", "Announce request from " << inet_ntoa(remote->sin_addr) << " (event=" << event << "), Sent " << q << " peers");
// update DB.
uint32_t ip;
if (req->ip_address == 0) // default
ip = m_hton32 (remote->sin_addr.s_addr);
else
ip = req->ip_address;
usi->m_conn->updatePeer(req->peer_id, req->info_hash, ip, req->port,
req->downloaded, req->left, req->uploaded, event);
return 0;
}
int UDPTracker::handleScrape(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int len) {
ScrapeRequest *sR = reinterpret_cast<ScrapeRequest*>(data);
int v, // validation helper
c, // torrent counter
i, // loop counter
j; // loop counter
uint8_t hash [20];
ScrapeResponse *resp;
uint8_t buffer [1024]; // up to 74 torrents can be scraped at once (17*74+8) < 1024
// validate request length:
v = len - 16;
if (v < 0 || v % 20 != 0)
{
UDPTracker::sendError(usi, remote, sR->transaction_id, "Bad scrape request.");
return 0;
}
if (!usi->m_conn->verifyConnectionId(sR->connection_id,
m_hton32(remote->sin_addr.s_addr),
m_hton16(remote->sin_port)))
{
LOG_DEBUG("udp-tracker", "Bad connection id from " << inet_ntoa(remote->sin_addr));
return 1;
}
// get torrent count.
c = v / 20;
resp = reinterpret_cast<ScrapeResponse*>(buffer);
resp->action = m_hton32(2);
resp->transaction_id = sR->transaction_id;
for (i = 0;i < c;i++)
{
int32_t *seeders,
*completed,
*leechers;
for (j = 0; j < 20;j++)
hash[j] = data[j + (i*20)+16];
seeders = (int32_t*)&buffer[i*12+8];
completed = (int32_t*)&buffer[i*12+12];
leechers = (int32_t*)&buffer[i*12+16];
DatabaseDriver::TorrentEntry tE;
tE.info_hash = hash;
if (!usi->m_conn->getTorrentInfo(&tE))
{
sendError(usi, remote, sR->transaction_id, "Scrape Failed: couldn't retrieve torrent data");
return 0;
}
*seeders = m_hton32(tE.seeders);
*completed = m_hton32(tE.completed);
*leechers = m_hton32(tE.leechers);
}
::sendto(usi->m_sock, reinterpret_cast<const char*>(buffer), sizeof(buffer), 0, reinterpret_cast<SOCKADDR*>(remote), sizeof(SOCKADDR_IN));
LOG_DEBUG("udp-tracker", "Scrape request from " << inet_ntoa(remote->sin_addr) << ", Sent " << c << " torrents");
return 0;
}
int UDPTracker::isIANAIP(uint32_t ip) {
uint8_t x = (ip % 256);
if (x == 0 || x == 10 || x == 127 || x >= 224)
return 1;
return 0;
}
int UDPTracker::resolveRequest(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int r) {
ConnectionRequest* cR = reinterpret_cast<ConnectionRequest*>(data);
uint32_t action;
action = m_hton32(cR->action);
if (!usi->m_allowIANA_IPs)
{
if (isIANAIP(remote->sin_addr.s_addr))
{
LOG_DEBUG("udp-tracker", "Request from IANA reserved IP rejected (" << inet_ntoa(remote->sin_addr) << ")");
return 0; // Access Denied: IANA reserved IP.
}
}
if (action == 0 && r >= 16)
return UDPTracker::handleConnection(usi, remote, data);
else if (action == 1 && r >= 98)
return UDPTracker::handleAnnounce(usi, remote, data);
else if (action == 2)
return UDPTracker::handleScrape(usi, remote, data, r);
else
{
UDPTracker::sendError(usi, remote, cR->transaction_id, "Tracker couldn't understand Client's request.");
return -1;
}
}
void UDPTracker::_thread_start(UDPTracker *usi) {
SOCKADDR_IN remoteAddr;
char tmpBuff[UDP_BUFFER_SIZE];
#ifdef linux
socklen_t addrSz;
#else
int addrSz;
#endif
addrSz = sizeof(SOCKADDR_IN);
while (usi->m_shouldRun)
{
// peek into the first 12 bytes of data; determine if connection request or announce request.
int r = ::recvfrom(usi->m_sock, (char*)tmpBuff, UDP_BUFFER_SIZE, 0, (SOCKADDR*)&remoteAddr, &addrSz);
if (r <= 0)
{
std::this_thread::yield();
continue;
}
UDPTracker::resolveRequest(usi, &remoteAddr, tmpBuff, r);
}
LOG_INFO("udp-tracker", "worker " << std::this_thread::get_id() << " exited.");
}
void UDPTracker::_maintainance_start(UDPTracker* usi) {
std::unique_lock<std::mutex> lk (usi->m_maintenanceMutex);
while (true)
{
if (std::cv_status::no_timeout == usi->m_maintenanceCondition.wait_for(lk, std::chrono::seconds(usi->m_cleanupInterval))) {
break;
}
LOG_INFO("udp-tracker", "Maintenance running...");
usi->m_conn->cleanup();
}
lk.unlock();
LOG_INFO("udp-tracker", "Maintenance thread " << std::this_thread::get_id() << " existed.");
}
};

View file

@ -1,185 +1,188 @@
/*
* Copyright © 2012-2016 Naim A.
*
* This file is part of UDPT.
*
* UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef UDPTRACKER_H_
#define UDPTRACKER_H_
#include <stdint.h>
#include <chrono>
#include <algorithm>
#include <string>
#include <sstream>
#include <list>
#include <ctime>
#include <boost/thread.hpp>
#include <boost/program_options.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/sources/severity_channel_logger.hpp>
#include "tools.h"
#include "exceptions.h"
#include "multiplatform.h"
#include "db/driver_sqlite.hpp"
#define UDPT_DYNAMIC (0x01) // Track Any info_hash?
#define UDPT_ALLOW_REMOTE_IP (0x02) // Allow client's to send other IPs?
#define UDPT_ALLOW_IANA_IP (0x04) // allow IP's like 127.0.0.1 or other IANA reserved IPs?
#define UDPT_VALIDATE_CLIENT (0x08) // validate client before adding to Database? (check if connection is open?)
namespace UDPT
{
class UDPTracker
{
public:
typedef struct udp_connection_request
{
uint64_t connection_id;
uint32_t action;
uint32_t transaction_id;
} ConnectionRequest;
typedef struct udp_connection_response
{
uint32_t action;
uint32_t transaction_id;
uint64_t connection_id;
} ConnectionResponse;
typedef struct udp_announce_request
{
uint64_t connection_id;
uint32_t action;
uint32_t transaction_id;
uint8_t info_hash [20];
uint8_t peer_id [20];
uint64_t downloaded;
uint64_t left;
uint64_t uploaded;
uint32_t event;
uint32_t ip_address;
uint32_t key;
int32_t num_want;
uint16_t port;
} AnnounceRequest;
typedef struct udp_announce_response
{
uint32_t action;
uint32_t transaction_id;
uint32_t interval;
uint32_t leechers;
uint32_t seeders;
uint8_t *peer_list_data;
} AnnounceResponse;
typedef struct udp_scrape_request
{
uint64_t connection_id;
uint32_t action;
uint32_t transaction_id;
uint8_t *torrent_list_data;
} ScrapeRequest;
typedef struct udp_scrape_response
{
uint32_t action;
uint32_t transaction_id;
uint8_t *data;
} ScrapeResponse;
typedef struct udp_error_response
{
uint32_t action;
uint32_t transaction_id;
char *message;
} ErrorResponse;
enum StartStatus
{
START_OK = 0,
START_ESOCKET_FAILED = 1,
START_EBIND_FAILED = 2
};
/**
* Initializes the UDP Tracker.
* @param settings Settings to start server with
*/
UDPTracker(const boost::program_options::variables_map& conf);
/**
* Starts the Initialized instance.
*/
void start();
/**
* Terminates tracker.
*/
void stop();
/**
* Joins worker threads
*/
void wait();
/**
* Destroys resources that were created by constructor
* @param usi Instance to destroy.
*/
virtual ~UDPTracker();
std::shared_ptr<UDPT::Data::DatabaseDriver> m_conn;
private:
SOCKET m_sock;
SOCKADDR_IN m_localEndpoint;
uint16_t m_port;
uint8_t m_threadCount;
bool m_isDynamic;
bool m_allowRemotes;
bool m_allowIANA_IPs;
std::vector<boost::thread> m_threads;
uint32_t m_announceInterval;
uint32_t m_cleanupInterval;
boost::log::sources::severity_channel_logger_mt<> m_logger;
const boost::program_options::variables_map& m_conf;
static void _thread_start(UDPTracker *usi);
static void _maintainance_start(UDPTracker* usi);
static int resolveRequest(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int r);
static int handleConnection(UDPTracker *usi, SOCKADDR_IN *remote, char *data);
static int handleAnnounce(UDPTracker *usi, SOCKADDR_IN *remote, char *data);
static int handleScrape(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int len);
static int sendError(UDPTracker *, SOCKADDR_IN *remote, uint32_t transId, const std::string &);
static int isIANAIP(uint32_t ip);
};
};
#endif /* UDPTRACKER_H_ */
/*
* Copyright © 2012-2017 Naim A.
*
* This file is part of UDPT.
*
* UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef UDPTRACKER_H_
#define UDPTRACKER_H_
#include <stdint.h>
#include <chrono>
#include <algorithm>
#include <string>
#include <sstream>
#include <list>
#include <ctime>
#include <thread>
#include <boost/program_options.hpp>
#include <mutex>
#include <condition_variable>
#include "tools.h"
#include "exceptions.h"
#include "multiplatform.h"
#include "db/driver_sqlite.hpp"
#define UDPT_DYNAMIC (0x01) // Track Any info_hash?
#define UDPT_ALLOW_REMOTE_IP (0x02) // Allow client's to send other IPs?
#define UDPT_ALLOW_IANA_IP (0x04) // allow IP's like 127.0.0.1 or other IANA reserved IPs?
#define UDPT_VALIDATE_CLIENT (0x08) // validate client before adding to Database? (check if connection is open?)
namespace UDPT
{
class UDPTracker
{
public:
typedef struct udp_connection_request
{
uint64_t connection_id;
uint32_t action;
uint32_t transaction_id;
} ConnectionRequest;
typedef struct udp_connection_response
{
uint32_t action;
uint32_t transaction_id;
uint64_t connection_id;
} ConnectionResponse;
typedef struct udp_announce_request
{
uint64_t connection_id;
uint32_t action;
uint32_t transaction_id;
uint8_t info_hash [20];
uint8_t peer_id [20];
uint64_t downloaded;
uint64_t left;
uint64_t uploaded;
uint32_t event;
uint32_t ip_address;
uint32_t key;
int32_t num_want;
uint16_t port;
} AnnounceRequest;
typedef struct udp_announce_response
{
uint32_t action;
uint32_t transaction_id;
uint32_t interval;
uint32_t leechers;
uint32_t seeders;
uint8_t *peer_list_data;
} AnnounceResponse;
typedef struct udp_scrape_request
{
uint64_t connection_id;
uint32_t action;
uint32_t transaction_id;
uint8_t *torrent_list_data;
} ScrapeRequest;
typedef struct udp_scrape_response
{
uint32_t action;
uint32_t transaction_id;
uint8_t *data;
} ScrapeResponse;
typedef struct udp_error_response
{
uint32_t action;
uint32_t transaction_id;
char *message;
} ErrorResponse;
enum StartStatus
{
START_OK = 0,
START_ESOCKET_FAILED = 1,
START_EBIND_FAILED = 2
};
/**
* Initializes the UDP Tracker.
* @param settings Settings to start server with
*/
UDPTracker(const boost::program_options::variables_map& conf);
/**
* Starts the Initialized instance.
*/
void start();
/**
* Terminates tracker.
*/
void stop();
/**
* Joins worker threads
*/
void wait();
/**
* Destroys resources that were created by constructor
* @param usi Instance to destroy.
*/
virtual ~UDPTracker();
std::shared_ptr<UDPT::Data::DatabaseDriver> m_conn;
private:
SOCKET m_sock;
SOCKADDR_IN m_localEndpoint;
uint16_t m_port;
uint8_t m_threadCount;
bool m_isDynamic;
bool m_allowRemotes;
bool m_allowIANA_IPs;
std::vector<std::thread> m_threads;
uint32_t m_announceInterval;
uint32_t m_cleanupInterval;
std::atomic_bool m_shouldRun;
std::mutex m_maintenanceMutex;
std::condition_variable m_maintenanceCondition;
const boost::program_options::variables_map& m_conf;
static void _thread_start(UDPTracker *usi);
static void _maintainance_start(UDPTracker* usi);
static int resolveRequest(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int r);
static int handleConnection(UDPTracker *usi, SOCKADDR_IN *remote, char *data);
static int handleAnnounce(UDPTracker *usi, SOCKADDR_IN *remote, char *data);
static int handleScrape(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int len);
static int sendError(UDPTracker *, SOCKADDR_IN *remote, uint32_t transId, const std::string &);
static int isIANAIP(uint32_t ip);
};
};
#endif /* UDPTRACKER_H_ */

55
tests/main.cpp Normal file
View file

@ -0,0 +1,55 @@
#include <gtest/gtest.h>
#include "../src/tools.h"
#include "../src/db/driver_sqlite.hpp"
TEST(Utility, SanityCheck) {
const uint32_t MAGIC = 0xDEADBEEF;
const unsigned char MAGIC_BYTES[4] = {0xEF, 0xBE, 0xAD, 0xDE};
ASSERT_TRUE(memcmp(&MAGIC, MAGIC_BYTES, 4) == 0);
}
TEST(Utility, CheckMTON) {
EXPECT_EQ(m_hton16(0xDEAD), 0xADDE);
EXPECT_EQ(m_hton32(0xDEADBEEF), 0xEFBEADDE);
EXPECT_EQ(m_hton64(0xDEADBEEFA1B2C3E4), 0xE4C3B2A1EFBEADDE);
}
TEST(Utility, HashToHexStr) {
const char EXPECTED_OUTPUT[] = "c670606edd22fd0e3b432c977559a687cc5d9bd2";
const unsigned char DATA[20] = {198, 112, 96, 110, 221, 34, 253, 14, 59, 67, 44, 151, 117, 89, 166, 135, 204, 93, 155, 210};
char OUTPUT_BUFFER[41] = {0};
to_hex_str(DATA, OUTPUT_BUFFER);
ASSERT_EQ(std::string(EXPECTED_OUTPUT), OUTPUT_BUFFER);
}
class SQLiteDriverTest:
public ::testing::Test {
protected:
SQLiteDriverTest(): va_map(), driver(nullptr) {
va_map.insert(std::pair<std::string, boost::program_options::variable_value>("db.param", boost::program_options::variable_value(std::string(":memory:"), true)));
}
virtual void SetUp() {
if (nullptr == driver) {
driver = new UDPT::Data::SQLite3Driver(va_map, false);
}
}
virtual void TearDown() {
if (nullptr != driver) {
delete driver;
driver = nullptr;
}
}
UDPT::Data::SQLite3Driver *driver;
boost::program_options::variables_map va_map;
};
int main(int argc, char *argv[]) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View file

@ -1,22 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.31101.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UDPT", "UDPT\UDPT.vcxproj", "{9F399AF8-861E-4E50-A94C-1388089E3FA0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9F399AF8-861E-4E50-A94C-1388089E3FA0}.Debug|Win32.ActiveCfg = Debug|Win32
{9F399AF8-861E-4E50-A94C-1388089E3FA0}.Debug|Win32.Build.0 = Debug|Win32
{9F399AF8-861E-4E50-A94C-1388089E3FA0}.Release|Win32.ActiveCfg = Release|Win32
{9F399AF8-861E-4E50-A94C-1388089E3FA0}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View file

@ -1,111 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{9F399AF8-861E-4E50-A94C-1388089E3FA0}</ProjectGuid>
<RootNamespace>UDPT</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140_xp</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<IncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath);D:\libs\sqlite3;D:\libs\boost\boost_1_59_0</IncludePath>
<LibraryPath>D:\libs\boost\boost_1_59_0\stage\lib;D:\libs\sqlite3\Release;$(LibraryPath)</LibraryPath>
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<IncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath);D:\libs\sqlite3;D:\libs\boost\boost_1_59_0</IncludePath>
<LibraryPath>D:\libs\boost\boost_1_59_0\stage\lib;D:\libs\sqlite3\Release;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<ExceptionHandling>Async</ExceptionHandling>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>ws2_32.lib;sqlite3.lib;advapi32.lib;shell32.lib</AdditionalDependencies>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<ExceptionHandling>Async</ExceptionHandling>
</ClCompile>
<Link>
<EnableCOMDATFolding>
</EnableCOMDATFolding>
<OptimizeReferences>
</OptimizeReferences>
<AdditionalDependencies>ws2_32.lib;sqlite3.lib;advapi32.lib;shell32.lib</AdditionalDependencies>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\src\db\database.cpp" />
<ClCompile Include="..\..\src\db\driver_sqlite.cpp" />
<ClCompile Include="..\..\src\http\httpserver.cpp" />
<ClCompile Include="..\..\src\http\webapp.cpp" />
<ClCompile Include="..\..\src\main.cpp" />
<ClCompile Include="..\..\src\service.cpp" />
<ClCompile Include="..\..\src\tools.c" />
<ClCompile Include="..\..\src\tracker.cpp" />
<ClCompile Include="..\..\src\udpTracker.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\src\db\database.hpp" />
<ClInclude Include="..\..\src\db\driver_sqlite.hpp" />
<ClInclude Include="..\..\src\exceptions.h" />
<ClInclude Include="..\..\src\http\httpserver.hpp" />
<ClInclude Include="..\..\src\http\webapp.hpp" />
<ClInclude Include="..\..\src\multiplatform.h" />
<ClInclude Include="..\..\src\service.hpp" />
<ClInclude Include="..\..\src\tools.h" />
<ClInclude Include="..\..\src\tracker.hpp" />
<ClInclude Include="..\..\src\udpTracker.hpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -1,90 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Header Files\db">
<UniqueIdentifier>{f5a85f67-2777-4fa5-828e-9c79af9f4b13}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\db">
<UniqueIdentifier>{643f664e-8751-4440-8b90-fe95b15e0aac}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\http">
<UniqueIdentifier>{bcb1fa9f-c9ec-4398-97f9-544baf69f2a9}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\http">
<UniqueIdentifier>{f65a2e69-6a71-4cd0-ad64-36a73c6c94c4}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\src\main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\src\tools.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\src\udpTracker.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\src\db\database.cpp">
<Filter>Source Files\db</Filter>
</ClCompile>
<ClCompile Include="..\..\src\db\driver_sqlite.cpp">
<Filter>Source Files\db</Filter>
</ClCompile>
<ClCompile Include="..\..\src\http\httpserver.cpp">
<Filter>Source Files\http</Filter>
</ClCompile>
<ClCompile Include="..\..\src\http\webapp.cpp">
<Filter>Source Files\http</Filter>
</ClCompile>
<ClCompile Include="..\..\src\tracker.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\src\service.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\src\multiplatform.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\tools.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\udpTracker.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\db\database.hpp">
<Filter>Header Files\db</Filter>
</ClInclude>
<ClInclude Include="..\..\src\db\driver_sqlite.hpp">
<Filter>Header Files\db</Filter>
</ClInclude>
<ClInclude Include="..\..\src\http\httpserver.hpp">
<Filter>Header Files\http</Filter>
</ClInclude>
<ClInclude Include="..\..\src\http\webapp.hpp">
<Filter>Header Files\http</Filter>
</ClInclude>
<ClInclude Include="..\..\src\exceptions.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\tracker.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\service.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>