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:
parent
7193f945a9
commit
f34fcdbd04
44
.github/CONTRIBUTING.md
vendored
Normal file
44
.github/CONTRIBUTING.md
vendored
Normal 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
BIN
.github/bitcoin-qr.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.6 KiB |
|
@ -1,15 +1,24 @@
|
||||||
project(udpt)
|
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 11)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
add_definitions(-DBOOST_LOG_DYN_LINK)
|
|
||||||
|
|
||||||
file(GLOB src_files "src/*.c"
|
file(GLOB src_files "src/*.c"
|
||||||
"src/*.cpp"
|
"src/*.cpp"
|
||||||
"src/db/*.cpp"
|
"src/db/*.cpp"
|
||||||
"src/http/*.cpp")
|
"src/http/*.cpp")
|
||||||
|
|
||||||
|
LIST(APPEND LIBS "pthread" "sqlite3" "boost_program_options" "boost_system")
|
||||||
|
|
||||||
|
|
||||||
add_executable(udpt ${src_files})
|
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
64
src/MessageQueue.hpp
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2012-2016 Naim A.
|
* Copyright © 2012-2017 Naim A.
|
||||||
*
|
*
|
||||||
* This file is part of UDPT.
|
* This file is part of UDPT.
|
||||||
*
|
*
|
||||||
|
@ -21,100 +21,100 @@
|
||||||
|
|
||||||
namespace UDPT
|
namespace UDPT
|
||||||
{
|
{
|
||||||
namespace Data
|
namespace Data
|
||||||
{
|
{
|
||||||
DatabaseDriver::DatabaseDriver(const boost::program_options::variables_map& conf, bool isDynamic) : m_conf(conf)
|
DatabaseDriver::DatabaseDriver(const boost::program_options::variables_map& conf, bool isDynamic) : m_conf(conf)
|
||||||
{
|
{
|
||||||
this->is_dynamic = isDynamic;
|
this->is_dynamic = isDynamic;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DatabaseDriver::addTorrent(uint8_t hash [20])
|
bool DatabaseDriver::addTorrent(uint8_t hash [20])
|
||||||
{
|
{
|
||||||
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DatabaseDriver::removeTorrent(uint8_t hash[20])
|
bool DatabaseDriver::removeTorrent(uint8_t hash[20])
|
||||||
{
|
{
|
||||||
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DatabaseDriver::isDynamic()
|
bool DatabaseDriver::isDynamic()
|
||||||
{
|
{
|
||||||
return this->is_dynamic;
|
return this->is_dynamic;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DatabaseDriver::genConnectionId(uint64_t *cid, uint32_t ip, uint16_t port)
|
bool DatabaseDriver::genConnectionId(uint64_t *cid, uint32_t ip, uint16_t port)
|
||||||
{
|
{
|
||||||
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DatabaseDriver::verifyConnectionId(uint64_t cid, uint32_t ip, uint16_t port)
|
bool DatabaseDriver::verifyConnectionId(uint64_t cid, uint32_t ip, uint16_t port)
|
||||||
{
|
{
|
||||||
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DatabaseDriver::updatePeer(uint8_t peer_id [20], uint8_t info_hash [20],
|
bool DatabaseDriver::updatePeer(uint8_t peer_id [20], uint8_t info_hash [20],
|
||||||
uint32_t ip, uint16_t port,
|
uint32_t ip, uint16_t port,
|
||||||
int64_t downloaded, int64_t left, int64_t uploaded,
|
int64_t downloaded, int64_t left, int64_t uploaded,
|
||||||
enum TrackerEvents event)
|
enum TrackerEvents event)
|
||||||
{
|
{
|
||||||
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
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)
|
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);
|
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DatabaseDriver::getTorrentInfo (TorrentEntry *e)
|
bool DatabaseDriver::getTorrentInfo (TorrentEntry *e)
|
||||||
{
|
{
|
||||||
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DatabaseDriver::getPeers (uint8_t info_hash [20], int *max_count, PeerEntry *pe)
|
bool DatabaseDriver::getPeers (uint8_t info_hash [20], int *max_count, PeerEntry *pe)
|
||||||
{
|
{
|
||||||
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseDriver::cleanup()
|
void DatabaseDriver::cleanup()
|
||||||
{
|
{
|
||||||
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DatabaseDriver::isTorrentAllowed(uint8_t info_hash[20])
|
bool DatabaseDriver::isTorrentAllowed(uint8_t info_hash[20])
|
||||||
{
|
{
|
||||||
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
DatabaseDriver::~DatabaseDriver()
|
DatabaseDriver::~DatabaseDriver()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-- Exceptions --*/
|
/*-- Exceptions --*/
|
||||||
static const char *EMessages[] = {
|
static const char *EMessages[] = {
|
||||||
"Unknown Error",
|
"Unknown Error",
|
||||||
"Not Implemented",
|
"Not Implemented",
|
||||||
"Failed to connect to database"
|
"Failed to connect to database"
|
||||||
};
|
};
|
||||||
|
|
||||||
DatabaseException::DatabaseException()
|
DatabaseException::DatabaseException()
|
||||||
{
|
{
|
||||||
this->errorNum = E_UNKNOWN;
|
this->errorNum = E_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
DatabaseException::DatabaseException(enum EType e)
|
DatabaseException::DatabaseException(enum EType e)
|
||||||
{
|
{
|
||||||
this->errorNum = e;
|
this->errorNum = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum DatabaseException::EType DatabaseException::getErrorType()
|
enum DatabaseException::EType DatabaseException::getErrorType()
|
||||||
{
|
{
|
||||||
return this->errorNum;
|
return this->errorNum;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* DatabaseException::getErrorMessage()
|
const char* DatabaseException::getErrorMessage()
|
||||||
{
|
{
|
||||||
return EMessages[this->errorNum];
|
return EMessages[this->errorNum];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2012-2016 Naim A.
|
* Copyright © 2012-2017 Naim A.
|
||||||
*
|
*
|
||||||
* This file is part of UDPT.
|
* This file is part of UDPT.
|
||||||
*
|
*
|
||||||
|
@ -21,154 +21,152 @@
|
||||||
#define DATABASE_HPP_
|
#define DATABASE_HPP_
|
||||||
|
|
||||||
#include <boost/program_options.hpp>
|
#include <boost/program_options.hpp>
|
||||||
#include <boost/log/trivial.hpp>
|
|
||||||
#include <boost/log/sources/severity_channel_logger.hpp>
|
|
||||||
|
|
||||||
namespace UDPT
|
namespace UDPT
|
||||||
{
|
{
|
||||||
namespace Data
|
namespace Data
|
||||||
{
|
{
|
||||||
class DatabaseException
|
class DatabaseException
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum EType {
|
enum EType {
|
||||||
E_UNKNOWN = 0, // Unknown error
|
E_UNKNOWN = 0, // Unknown error
|
||||||
E_NOT_IMPLEMENTED = 1, // not implemented
|
E_NOT_IMPLEMENTED = 1, // not implemented
|
||||||
E_CONNECTION_FAILURE = 2
|
E_CONNECTION_FAILURE = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
DatabaseException();
|
DatabaseException();
|
||||||
DatabaseException(EType);
|
DatabaseException(EType);
|
||||||
EType getErrorType();
|
EType getErrorType();
|
||||||
const char* getErrorMessage();
|
const char* getErrorMessage();
|
||||||
private:
|
private:
|
||||||
EType errorNum;
|
EType errorNum;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DatabaseDriver
|
class DatabaseDriver
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t *info_hash;
|
uint8_t *info_hash;
|
||||||
int32_t seeders;
|
int32_t seeders;
|
||||||
int32_t leechers;
|
int32_t leechers;
|
||||||
int32_t completed;
|
int32_t completed;
|
||||||
} TorrentEntry;
|
} TorrentEntry;
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t ip;
|
uint32_t ip;
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
} PeerEntry;
|
} PeerEntry;
|
||||||
|
|
||||||
enum TrackerEvents {
|
enum TrackerEvents {
|
||||||
EVENT_UNSPEC = 0,
|
EVENT_UNSPEC = 0,
|
||||||
EVENT_COMPLETE = 1,
|
EVENT_COMPLETE = 1,
|
||||||
EVENT_START = 2,
|
EVENT_START = 2,
|
||||||
EVENT_STOP = 3
|
EVENT_STOP = 3
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the DB's connection
|
* Opens the DB's connection
|
||||||
* @param dClass Settings class ('database' class).
|
* @param dClass Settings class ('database' class).
|
||||||
*/
|
*/
|
||||||
DatabaseDriver(const boost::program_options::variables_map& conf, bool isDynamic = false);
|
DatabaseDriver(const boost::program_options::variables_map& conf, bool isDynamic = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a torrent to the Database. automatically done if in dynamic mode.
|
* Adds a torrent to the Database. automatically done if in dynamic mode.
|
||||||
* @param hash The info_hash of the torrent.
|
* @param hash The info_hash of the torrent.
|
||||||
* @return true on success. false on failure.
|
* @return true on success. false on failure.
|
||||||
*/
|
*/
|
||||||
virtual bool addTorrent(uint8_t hash[20]);
|
virtual bool addTorrent(uint8_t hash[20]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a torrent from the database. should be used only for non-dynamic trackers or by cleanup.
|
* Removes a torrent from the database. should be used only for non-dynamic trackers or by cleanup.
|
||||||
* @param hash The info_hash to drop.
|
* @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)
|
* @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]);
|
virtual bool removeTorrent(uint8_t hash[20]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the Database is acting as a dynamic tracker DB.
|
* Checks if the Database is acting as a dynamic tracker DB.
|
||||||
* @return true if dynamic. otherwise false.
|
* @return true if dynamic. otherwise false.
|
||||||
*/
|
*/
|
||||||
bool isDynamic();
|
bool isDynamic();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the torrent can be used in the tracker.
|
* Checks if the torrent can be used in the tracker.
|
||||||
* @param info_hash The torrent's info_hash.
|
* @param info_hash The torrent's info_hash.
|
||||||
* @return true if allowed. otherwise false.
|
* @return true if allowed. otherwise false.
|
||||||
*/
|
*/
|
||||||
virtual bool isTorrentAllowed(uint8_t info_hash [20]);
|
virtual bool isTorrentAllowed(uint8_t info_hash [20]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a Connection ID for the peer.
|
* Generate a Connection ID for the peer.
|
||||||
* @param connectionId (Output) the generated connection ID.
|
* @param connectionId (Output) the generated connection ID.
|
||||||
* @param ip The peer's IP (requesting peer. not remote)
|
* @param ip The peer's IP (requesting peer. not remote)
|
||||||
* @param port The peer's IP (remote port if tracker accepts)
|
* @param port The peer's IP (remote port if tracker accepts)
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
virtual bool genConnectionId(uint64_t *connectionId, uint32_t ip, uint16_t port);
|
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.
|
* Updates/Adds a peer to/in the database.
|
||||||
* @param peer_id the peer's peer_id
|
* @param peer_id the peer's peer_id
|
||||||
* @param info_hash the torrent info_hash
|
* @param info_hash the torrent info_hash
|
||||||
* @param ip IP of peer (remote ip if tracker accepts)
|
* @param ip IP of peer (remote ip if tracker accepts)
|
||||||
* @param port TCP port of peer (remote port if tracker accepts)
|
* @param port TCP port of peer (remote port if tracker accepts)
|
||||||
* @param downloaded total Bytes downloaded
|
* @param downloaded total Bytes downloaded
|
||||||
* @param left total bytes left
|
* @param left total bytes left
|
||||||
* @param uploaded total bytes uploaded
|
* @param uploaded total bytes uploaded
|
||||||
* @return true on success, false on failure.
|
* @return true on success, false on failure.
|
||||||
*/
|
*/
|
||||||
virtual bool updatePeer(uint8_t peer_id [20], uint8_t info_hash [20],
|
virtual bool updatePeer(uint8_t peer_id [20], uint8_t info_hash [20],
|
||||||
uint32_t ip, uint16_t port,
|
uint32_t ip, uint16_t port,
|
||||||
int64_t downloaded, int64_t left, int64_t uploaded,
|
int64_t downloaded, int64_t left, int64_t uploaded,
|
||||||
enum TrackerEvents event);
|
enum TrackerEvents event);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a peer from a torrent (if stop action occurred, or if peer is inactive in cleanup)
|
* 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 peer_id The peer's peer_id
|
||||||
* @param info_hash Torrent's info_hash
|
* @param info_hash Torrent's info_hash
|
||||||
* @param ip The IP of the peer (remote IP if tracker accepts)
|
* @param ip The IP of the peer (remote IP if tracker accepts)
|
||||||
* @param port The TCP port (remote port if tracker accepts)
|
* @param port The TCP port (remote port if tracker accepts)
|
||||||
* @return true on success. false on failure (shouldn't happen - critical)
|
* @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);
|
virtual bool removePeer(uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets stats on a torrent
|
* Gets stats on a torrent
|
||||||
* @param e TorrentEntry, only this info_hash has to be set
|
* @param e TorrentEntry, only this info_hash has to be set
|
||||||
* @return true on success, false on failure.
|
* @return true on success, false on failure.
|
||||||
*/
|
*/
|
||||||
virtual bool getTorrentInfo(TorrentEntry *e);
|
virtual bool getTorrentInfo(TorrentEntry *e);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a list of peers from the database.
|
* Gets a list of peers from the database.
|
||||||
* @param info_hash The torrent's info_hash
|
* @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 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.
|
* @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).
|
* @return true on success, otherwise false (shouldn't happen).
|
||||||
*/
|
*/
|
||||||
virtual bool getPeers(uint8_t info_hash [20], int *max_count, PeerEntry *pe);
|
virtual bool getPeers(uint8_t info_hash [20], int *max_count, PeerEntry *pe);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cleanup the database.
|
* Cleanup the database.
|
||||||
* Other actions may be locked when using this depending on the driver.
|
* Other actions may be locked when using this depending on the driver.
|
||||||
*/
|
*/
|
||||||
virtual void cleanup();
|
virtual void cleanup();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the connections, and releases all other resources.
|
* Closes the connections, and releases all other resources.
|
||||||
*/
|
*/
|
||||||
virtual ~DatabaseDriver();
|
virtual ~DatabaseDriver();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const boost::program_options::variables_map& m_conf;
|
const boost::program_options::variables_map& m_conf;
|
||||||
private:
|
private:
|
||||||
bool is_dynamic;
|
bool is_dynamic;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,431 +1,435 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2012-2016 Naim A.
|
* Copyright © 2012-2017 Naim A.
|
||||||
*
|
*
|
||||||
* This file is part of UDPT.
|
* This file is part of UDPT.
|
||||||
*
|
*
|
||||||
* UDPT is free software: you can redistribute it and/or modify
|
* UDPT is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* UDPT is distributed in the hope that it will be useful,
|
* UDPT is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
|
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "driver_sqlite.hpp"
|
#include "driver_sqlite.hpp"
|
||||||
#include "../tools.h"
|
#include "../tools.h"
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstring> // memcpy
|
#include <cstring> // memcpy
|
||||||
#include "../multiplatform.h"
|
#include "../multiplatform.h"
|
||||||
|
#include "../logging.hpp"
|
||||||
using namespace std;
|
|
||||||
|
using namespace std;
|
||||||
namespace UDPT
|
|
||||||
{
|
namespace UDPT
|
||||||
namespace Data
|
{
|
||||||
{
|
namespace Data
|
||||||
static const char hexadecimal[] = "0123456789abcdef";
|
{
|
||||||
|
static const char hexadecimal[] = "0123456789abcdef";
|
||||||
static char* _to_hex_str (const uint8_t *hash, char *data)
|
|
||||||
{
|
static char* _to_hex_str (const uint8_t *hash, char *data)
|
||||||
int i;
|
{
|
||||||
for (i = 0;i < 20;i++)
|
int i;
|
||||||
{
|
for (i = 0;i < 20;i++)
|
||||||
data[i * 2] = hexadecimal[hash[i] / 16];
|
{
|
||||||
data[i * 2 + 1] = hexadecimal[hash[i] % 16];
|
data[i * 2] = hexadecimal[hash[i] / 16];
|
||||||
}
|
data[i * 2 + 1] = hexadecimal[hash[i] % 16];
|
||||||
data[40] = '\0';
|
}
|
||||||
return data;
|
data[40] = '\0';
|
||||||
}
|
return data;
|
||||||
|
}
|
||||||
static uint8_t* _hash_to_bin (const char *hash, uint8_t *data)
|
|
||||||
{
|
static uint8_t* _hash_to_bin (const char *hash, uint8_t *data)
|
||||||
for (int i = 0;i < 20;i++)
|
{
|
||||||
{
|
for (int i = 0;i < 20;i++)
|
||||||
data [i] = 0;
|
{
|
||||||
char a = hash[i * 2];
|
data [i] = 0;
|
||||||
char b = hash[i * 2 + 1];
|
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') );
|
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] = ( (a >= '0' && a <= 'f') ? (a - '0') : (a - 'f' + 10) );
|
||||||
data[i] = ( (b >= '0' && b <= 'f') ? (b - '0') : (b - 'f' + 10) );
|
data[i] <<= 4;
|
||||||
}
|
data[i] = ( (b >= '0' && b <= 'f') ? (b - '0') : (b - 'f' + 10) );
|
||||||
|
}
|
||||||
return data;
|
|
||||||
}
|
return data;
|
||||||
|
}
|
||||||
SQLite3Driver::SQLite3Driver(const boost::program_options::variables_map& conf, bool isDyn) : DatabaseDriver(conf, isDyn), m_logger(boost::log::keywords::channel="SQLite3")
|
|
||||||
{
|
SQLite3Driver::SQLite3Driver(const boost::program_options::variables_map& conf, bool isDyn) : DatabaseDriver(conf, isDyn)
|
||||||
int r;
|
{
|
||||||
bool doSetup;
|
int r;
|
||||||
|
bool doSetup;
|
||||||
fstream fCheck;
|
|
||||||
string filename = m_conf["db.param"].as<std::string>();
|
fstream fCheck;
|
||||||
|
string filename = m_conf["db.param"].as<std::string>();
|
||||||
fCheck.open(filename.c_str(), ios::binary | ios::in);
|
|
||||||
if (fCheck.is_open())
|
fCheck.open(filename.c_str(), ios::binary | ios::in);
|
||||||
{
|
if (fCheck.is_open())
|
||||||
doSetup = false;
|
{
|
||||||
fCheck.close();
|
doSetup = false;
|
||||||
}
|
fCheck.close();
|
||||||
else
|
}
|
||||||
doSetup = true;
|
else
|
||||||
|
doSetup = true;
|
||||||
r = sqlite3_open(filename.c_str(), &this->db);
|
|
||||||
if (r != SQLITE_OK)
|
r = sqlite3_open(filename.c_str(), &this->db);
|
||||||
{
|
if (r != SQLITE_OK)
|
||||||
sqlite3_close(this->db);
|
{
|
||||||
throw DatabaseException (DatabaseException::E_CONNECTION_FAILURE);
|
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();
|
|
||||||
}
|
if (doSetup)
|
||||||
|
this->doSetup();
|
||||||
void SQLite3Driver::doSetup()
|
}
|
||||||
{
|
|
||||||
char *eMsg = NULL;
|
void SQLite3Driver::doSetup()
|
||||||
// for quicker stats.
|
{
|
||||||
sqlite3_exec(this->db, "CREATE TABLE stats ("
|
char *eMsg = NULL;
|
||||||
"info_hash blob(20) UNIQUE,"
|
LOG_INFO("db-sqlite", "Setting up database...");
|
||||||
"completed INTEGER DEFAULT 0,"
|
// for quicker stats.
|
||||||
"leechers INTEGER DEFAULT 0,"
|
sqlite3_exec(this->db, "CREATE TABLE stats ("
|
||||||
"seeders INTEGER DEFAULT 0,"
|
"info_hash blob(20) UNIQUE,"
|
||||||
"last_mod INTEGER DEFAULT 0"
|
"completed INTEGER DEFAULT 0,"
|
||||||
")", NULL, NULL, &eMsg);
|
"leechers INTEGER DEFAULT 0,"
|
||||||
|
"seeders INTEGER DEFAULT 0,"
|
||||||
sqlite3_exec(this->db, "CREATE TABLE torrents ("
|
"last_mod INTEGER DEFAULT 0"
|
||||||
"info_hash blob(20) UNIQUE,"
|
")", NULL, NULL, &eMsg);
|
||||||
"created INTEGER"
|
|
||||||
")", NULL, NULL, &eMsg);
|
sqlite3_exec(this->db, "CREATE TABLE torrents ("
|
||||||
}
|
"info_hash blob(20) UNIQUE,"
|
||||||
|
"created INTEGER"
|
||||||
bool SQLite3Driver::getTorrentInfo(TorrentEntry *e)
|
")", NULL, NULL, &eMsg);
|
||||||
{
|
}
|
||||||
bool gotInfo = false;
|
|
||||||
|
bool SQLite3Driver::getTorrentInfo(TorrentEntry *e)
|
||||||
const char sql[] = "SELECT seeders,leechers,completed FROM 'stats' WHERE info_hash=?";
|
{
|
||||||
sqlite3_stmt *stmt;
|
bool gotInfo = false;
|
||||||
|
|
||||||
e->seeders = 0;
|
const char sql[] = "SELECT seeders,leechers,completed FROM 'stats' WHERE info_hash=?";
|
||||||
e->leechers = 0;
|
sqlite3_stmt *stmt;
|
||||||
e->completed = 0;
|
|
||||||
|
e->seeders = 0;
|
||||||
|
e->leechers = 0;
|
||||||
sqlite3_prepare (this->db, sql, -1, &stmt, NULL);
|
e->completed = 0;
|
||||||
sqlite3_bind_blob (stmt, 1, (void*)e->info_hash, 20, NULL);
|
|
||||||
|
|
||||||
if (sqlite3_step(stmt) == SQLITE_ROW)
|
sqlite3_prepare (this->db, sql, -1, &stmt, NULL);
|
||||||
{
|
sqlite3_bind_blob (stmt, 1, (void*)e->info_hash, 20, NULL);
|
||||||
e->seeders = sqlite3_column_int (stmt, 0);
|
|
||||||
e->leechers = sqlite3_column_int (stmt, 1);
|
if (sqlite3_step(stmt) == SQLITE_ROW)
|
||||||
e->completed = sqlite3_column_int (stmt, 2);
|
{
|
||||||
|
e->seeders = sqlite3_column_int (stmt, 0);
|
||||||
gotInfo = true;
|
e->leechers = sqlite3_column_int (stmt, 1);
|
||||||
}
|
e->completed = sqlite3_column_int (stmt, 2);
|
||||||
|
|
||||||
sqlite3_finalize (stmt);
|
gotInfo = true;
|
||||||
|
}
|
||||||
return gotInfo;
|
|
||||||
}
|
sqlite3_finalize (stmt);
|
||||||
|
|
||||||
bool SQLite3Driver::getPeers (uint8_t info_hash [20], int *max_count, PeerEntry *pe)
|
return gotInfo;
|
||||||
{
|
}
|
||||||
string sql;
|
|
||||||
char hash [50];
|
bool SQLite3Driver::getPeers (uint8_t info_hash [20], int *max_count, PeerEntry *pe)
|
||||||
sqlite3_stmt *stmt;
|
{
|
||||||
int r, i;
|
string sql;
|
||||||
|
char hash [50];
|
||||||
to_hex_str(info_hash, hash);
|
sqlite3_stmt *stmt;
|
||||||
|
int r, i;
|
||||||
sql = "SELECT ip,port FROM 't";
|
|
||||||
sql += hash;
|
to_hex_str(info_hash, hash);
|
||||||
sql += "' LIMIT ?";
|
|
||||||
|
sql = "SELECT ip,port FROM 't";
|
||||||
sqlite3_prepare(this->db, sql.c_str(), sql.length(), &stmt, NULL);
|
sql += hash;
|
||||||
sqlite3_bind_int(stmt, 1, *max_count);
|
sql += "' LIMIT ?";
|
||||||
|
|
||||||
i = 0;
|
sqlite3_prepare(this->db, sql.c_str(), sql.length(), &stmt, NULL);
|
||||||
while (*max_count > i)
|
sqlite3_bind_int(stmt, 1, *max_count);
|
||||||
{
|
|
||||||
r = sqlite3_step(stmt);
|
i = 0;
|
||||||
if (r == SQLITE_ROW)
|
while (*max_count > i)
|
||||||
{
|
{
|
||||||
const char *ip = (const char*)sqlite3_column_blob (stmt, 0);
|
r = sqlite3_step(stmt);
|
||||||
const char *port = (const char*)sqlite3_column_blob (stmt, 1);
|
if (r == SQLITE_ROW)
|
||||||
|
{
|
||||||
memcpy(&pe[i].ip, ip, 4);
|
const char *ip = (const char*)sqlite3_column_blob (stmt, 0);
|
||||||
memcpy(&pe[i].port, port, 2);
|
const char *port = (const char*)sqlite3_column_blob (stmt, 1);
|
||||||
|
|
||||||
i++;
|
memcpy(&pe[i].ip, ip, 4);
|
||||||
}
|
memcpy(&pe[i].port, port, 2);
|
||||||
else
|
|
||||||
{
|
i++;
|
||||||
break;
|
}
|
||||||
}
|
else
|
||||||
}
|
{
|
||||||
|
break;
|
||||||
BOOST_LOG_SEV(m_logger, boost::log::trivial::debug) << "Retrieved " << i << " peers";
|
}
|
||||||
|
}
|
||||||
sqlite3_finalize(stmt);
|
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
*max_count = i;
|
|
||||||
|
*max_count = i;
|
||||||
return true;
|
|
||||||
}
|
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)
|
|
||||||
{
|
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;
|
char xHash [50]; // we just need 40 + \0 = 41.
|
||||||
string sql;
|
sqlite3_stmt *stmt;
|
||||||
int r;
|
string sql;
|
||||||
|
int r;
|
||||||
char *hash = xHash;
|
|
||||||
to_hex_str(info_hash, hash);
|
char *hash = xHash;
|
||||||
|
to_hex_str(info_hash, hash);
|
||||||
addTorrent (info_hash);
|
|
||||||
|
addTorrent (info_hash);
|
||||||
|
|
||||||
sql = "REPLACE INTO 't";
|
|
||||||
sql += hash;
|
sql = "REPLACE INTO 't";
|
||||||
sql += "' (peer_id,ip,port,uploaded,downloaded,left,last_seen) VALUES (?,?,?,?,?,?,?)";
|
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_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, 1, (void*)peer_id, 20, NULL);
|
||||||
sqlite3_bind_blob(stmt, 3, (void*)&port, 2, NULL);
|
sqlite3_bind_blob(stmt, 2, (void*)&ip, 4, NULL);
|
||||||
sqlite3_bind_blob(stmt, 4, (void*)&uploaded, 8, NULL);
|
sqlite3_bind_blob(stmt, 3, (void*)&port, 2, NULL);
|
||||||
sqlite3_bind_blob(stmt, 5, (void*)&downloaded, 8, NULL);
|
sqlite3_bind_blob(stmt, 4, (void*)&uploaded, 8, NULL);
|
||||||
sqlite3_bind_blob(stmt, 6, (void*)&left, 8, NULL);
|
sqlite3_bind_blob(stmt, 5, (void*)&downloaded, 8, NULL);
|
||||||
sqlite3_bind_int(stmt, 7, time(NULL));
|
sqlite3_bind_blob(stmt, 6, (void*)&left, 8, NULL);
|
||||||
|
sqlite3_bind_int(stmt, 7, time(NULL));
|
||||||
r = sqlite3_step(stmt);
|
|
||||||
sqlite3_finalize(stmt);
|
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);
|
sql = "REPLACE INTO stats (info_hash,last_mod) VALUES (?,?)";
|
||||||
sqlite3_bind_blob (stmt, 1, hash, 20, NULL);
|
sqlite3_prepare (this->db, sql.c_str(), sql.length(), &stmt, NULL);
|
||||||
sqlite3_bind_int (stmt, 2, time(NULL));
|
sqlite3_bind_blob (stmt, 1, hash, 20, NULL);
|
||||||
sqlite3_step (stmt);
|
sqlite3_bind_int (stmt, 2, time(NULL));
|
||||||
sqlite3_finalize (stmt);
|
sqlite3_step (stmt);
|
||||||
|
sqlite3_finalize (stmt);
|
||||||
return r;
|
|
||||||
}
|
return r;
|
||||||
|
}
|
||||||
bool SQLite3Driver::addTorrent (uint8_t info_hash[20])
|
|
||||||
{
|
bool SQLite3Driver::addTorrent (uint8_t info_hash[20])
|
||||||
char xHash [41];
|
{
|
||||||
char *err_msg;
|
char xHash [41];
|
||||||
int r;
|
char *err_msg;
|
||||||
|
int r;
|
||||||
_to_hex_str(info_hash, xHash);
|
|
||||||
|
_to_hex_str(info_hash, xHash);
|
||||||
sqlite3_stmt *stmt;
|
|
||||||
sqlite3_prepare(this->db, "INSERT INTO torrents (info_hash,created) VALUES (?,?)", -1, &stmt, NULL);
|
sqlite3_stmt *stmt;
|
||||||
sqlite3_bind_blob(stmt, 1, info_hash, 20, NULL);
|
sqlite3_prepare(this->db, "INSERT INTO torrents (info_hash,created) VALUES (?,?)", -1, &stmt, NULL);
|
||||||
sqlite3_bind_int(stmt, 2, time(NULL));
|
sqlite3_bind_blob(stmt, 1, info_hash, 20, NULL);
|
||||||
sqlite3_step(stmt);
|
sqlite3_bind_int(stmt, 2, time(NULL));
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_step(stmt);
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
string sql = "CREATE TABLE IF NOT EXISTS 't";
|
|
||||||
sql += xHash;
|
string sql = "CREATE TABLE IF NOT EXISTS 't";
|
||||||
sql += "' (";
|
sql += xHash;
|
||||||
sql += "peer_id blob(20),"
|
sql += "' (";
|
||||||
"ip blob(4),"
|
sql += "peer_id blob(20),"
|
||||||
"port blob(2),"
|
"ip blob(4),"
|
||||||
"uploaded blob(8)," // uint64
|
"port blob(2),"
|
||||||
"downloaded blob(8),"
|
"uploaded blob(8)," // uint64
|
||||||
"left blob(8),"
|
"downloaded blob(8),"
|
||||||
"last_seen INT DEFAULT 0";
|
"left blob(8),"
|
||||||
|
"last_seen INT DEFAULT 0";
|
||||||
sql += ", CONSTRAINT c1 UNIQUE (ip,port) ON CONFLICT REPLACE)";
|
|
||||||
|
sql += ", CONSTRAINT c1 UNIQUE (ip,port) ON CONFLICT REPLACE)";
|
||||||
// create table.
|
|
||||||
r = sqlite3_exec(this->db, sql.c_str(), NULL, NULL, &err_msg);
|
// create table.
|
||||||
|
r = sqlite3_exec(this->db, sql.c_str(), NULL, NULL, &err_msg);
|
||||||
if (SQLITE_OK == r)
|
|
||||||
{
|
if (SQLITE_OK == r)
|
||||||
BOOST_LOG_SEV(m_logger, boost::log::trivial::info) << "Added torrent";
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
BOOST_LOG_SEV(m_logger, boost::log::trivial::error) << "Failed to add torrent: SQLite3 error code = " << r;
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
bool SQLite3Driver::isTorrentAllowed(uint8_t *info_hash)
|
||||||
bool SQLite3Driver::isTorrentAllowed(uint8_t *info_hash)
|
{
|
||||||
{
|
if (this->isDynamic())
|
||||||
if (this->isDynamic())
|
return true;
|
||||||
return true;
|
sqlite3_stmt *stmt;
|
||||||
sqlite3_stmt *stmt;
|
sqlite3_prepare(this->db, "SELECT COUNT(*) FROM torrents WHERE info_hash=?", -1, &stmt, NULL);
|
||||||
sqlite3_prepare(this->db, "SELECT COUNT(*) FROM torrents WHERE info_hash=?", -1, &stmt, NULL);
|
sqlite3_bind_blob(stmt, 1, info_hash, 20, NULL);
|
||||||
sqlite3_bind_blob(stmt, 1, info_hash, 20, NULL);
|
sqlite3_step(stmt);
|
||||||
sqlite3_step(stmt);
|
|
||||||
|
int n = sqlite3_column_int(stmt, 0);
|
||||||
int n = sqlite3_column_int(stmt, 0);
|
sqlite3_finalize(stmt);
|
||||||
sqlite3_finalize(stmt);
|
|
||||||
|
return (n == 1);
|
||||||
return (n == 1);
|
}
|
||||||
}
|
|
||||||
|
void SQLite3Driver::cleanup()
|
||||||
void SQLite3Driver::cleanup()
|
{
|
||||||
{
|
LOG_INFO("db-sqlite", "Cleaning up...");
|
||||||
int exp = time (NULL) - 7200; // 2 hours, expired.
|
int exp = time (NULL) - 7200; // 2 hours, expired.
|
||||||
|
int r = 0;
|
||||||
// drop all peers with no activity for 2 hours.
|
|
||||||
sqlite3_stmt *getTables;
|
// drop all peers with no activity for 2 hours.
|
||||||
// torrent table names: t<hex-of-sha-1>
|
sqlite3_stmt *getTables;
|
||||||
sqlite3_prepare(this->db, "SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 't________________________________________'", -1, &getTables, NULL);
|
// 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);
|
||||||
uint8_t buff [20];
|
if (r != SQLITE_OK) {
|
||||||
sqlite3_stmt *updateStats;
|
LOG_ERR("db-sqlite", "Failed fetch tables from DB for cleanup.");
|
||||||
assert (sqlite3_prepare(this->db, "REPLACE INTO stats (info_hash,seeders,leechers,last_mod) VALUES (?,?,?,?)", -1, &updateStats, NULL) == SQLITE_OK);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
while (sqlite3_step(getTables) == SQLITE_ROW)
|
uint8_t buff [20];
|
||||||
{
|
sqlite3_stmt *updateStats;
|
||||||
char* tblN = (char*)sqlite3_column_text(getTables, 0);
|
r = sqlite3_prepare(this->db, "REPLACE INTO stats (info_hash,seeders,leechers,last_mod) VALUES (?,?,?,?)", -1, &updateStats, NULL);
|
||||||
stringstream sStr;
|
if (r != SQLITE_OK) {
|
||||||
sStr << "DELETE FROM " << tblN << " WHERE last_seen<" << exp;
|
LOG_ERR("db-sqlite", "Failed to prepare update stats query.");
|
||||||
|
return;
|
||||||
assert (sqlite3_exec(this->db, sStr.str().c_str(), NULL, NULL, NULL) == SQLITE_OK);
|
}
|
||||||
|
|
||||||
sStr.str (string());
|
while (sqlite3_step(getTables) == SQLITE_ROW)
|
||||||
sStr << "SELECT left,COUNT(*) FROM " << tblN << " GROUP BY left==0";
|
{
|
||||||
|
char* tblN = (char*)sqlite3_column_text(getTables, 0);
|
||||||
sqlite3_stmt *collectStats;
|
stringstream sStr;
|
||||||
|
sStr << "DELETE FROM " << tblN << " WHERE last_seen<" << exp;
|
||||||
sqlite3_prepare(this->db, sStr.str().c_str(), sStr.str().length(), &collectStats, NULL);
|
|
||||||
|
r = sqlite3_exec(this->db, sStr.str().c_str(), NULL, NULL, NULL);
|
||||||
if (sqlite3_errcode(this->db) != SQLITE_OK)
|
if (r != SQLITE_OK) {
|
||||||
{
|
LOG_ERR("db-sqlite", "Failed to execute cleanup for table '" << tblN << "'.");
|
||||||
// TODO: Log this error
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int seeders = 0, leechers = 0;
|
sStr.str (string());
|
||||||
while (sqlite3_step(collectStats) == SQLITE_ROW) // expecting two results.
|
sStr << "SELECT left,COUNT(*) FROM " << tblN << " GROUP BY left==0";
|
||||||
{
|
|
||||||
if (sqlite3_column_int(collectStats, 0) == 0)
|
sqlite3_stmt *collectStats;
|
||||||
seeders = sqlite3_column_int (collectStats, 1);
|
|
||||||
else
|
r = sqlite3_prepare(this->db, sStr.str().c_str(), sStr.str().length(), &collectStats, NULL);
|
||||||
leechers = sqlite3_column_int (collectStats, 1);
|
|
||||||
}
|
if (r != SQLITE_OK)
|
||||||
sqlite3_finalize(collectStats);
|
{
|
||||||
|
LOG_ERR("db-sqlite", "Failed while trying to prepare stats query for '" << tblN << "', sqlite returned " << r);
|
||||||
sqlite3_bind_blob(updateStats, 1, _hash_to_bin((const char*)(tblN + 1), buff), 20, NULL);
|
continue;
|
||||||
sqlite3_bind_int(updateStats, 2, seeders);
|
}
|
||||||
sqlite3_bind_int(updateStats, 3, leechers);
|
|
||||||
sqlite3_bind_int(updateStats, 4, time (NULL));
|
int seeders = 0, leechers = 0;
|
||||||
|
while (sqlite3_step(collectStats) == SQLITE_ROW) // expecting two results.
|
||||||
sqlite3_step(updateStats);
|
{
|
||||||
sqlite3_reset (updateStats);
|
if (sqlite3_column_int(collectStats, 0) == 0)
|
||||||
}
|
seeders = sqlite3_column_int (collectStats, 1);
|
||||||
sqlite3_finalize(updateStats);
|
else
|
||||||
sqlite3_finalize(getTables);
|
leechers = sqlite3_column_int (collectStats, 1);
|
||||||
}
|
}
|
||||||
|
sqlite3_finalize(collectStats);
|
||||||
bool SQLite3Driver::removeTorrent(uint8_t info_hash[20])
|
|
||||||
{
|
sqlite3_bind_blob(updateStats, 1, _hash_to_bin((const char*)(tblN + 1), buff), 20, NULL);
|
||||||
// if non-dynamic, remove from table
|
sqlite3_bind_int(updateStats, 2, seeders);
|
||||||
sqlite3_stmt *stmt;
|
sqlite3_bind_int(updateStats, 3, leechers);
|
||||||
sqlite3_prepare(this->db, "DELETE FROM torrents WHERE info_hash=?", -1, &stmt, NULL);
|
sqlite3_bind_int(updateStats, 4, time (NULL));
|
||||||
sqlite3_bind_blob(stmt, 1, info_hash, 20, NULL);
|
|
||||||
sqlite3_step(stmt);
|
sqlite3_step(updateStats);
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_reset (updateStats);
|
||||||
|
}
|
||||||
// remove from stats
|
sqlite3_finalize(updateStats);
|
||||||
sqlite3_stmt *rmS;
|
sqlite3_finalize(getTables);
|
||||||
if (sqlite3_prepare(this->db, "DELETE FROM stats WHERE info_hash=?", -1, &rmS, NULL) != SQLITE_OK)
|
}
|
||||||
{
|
|
||||||
sqlite3_finalize(rmS);
|
bool SQLite3Driver::removeTorrent(uint8_t info_hash[20]) {
|
||||||
return false;
|
// if non-dynamic, remove from table
|
||||||
}
|
sqlite3_stmt *stmt;
|
||||||
sqlite3_bind_blob(rmS, 1, (const void*)info_hash, 20, NULL);
|
sqlite3_prepare(this->db, "DELETE FROM torrents WHERE info_hash=?", -1, &stmt, NULL);
|
||||||
sqlite3_step(rmS);
|
sqlite3_bind_blob(stmt, 1, info_hash, 20, NULL);
|
||||||
sqlite3_finalize(rmS);
|
sqlite3_step(stmt);
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
// remove table
|
|
||||||
string str = "DROP TABLE IF EXISTS 't";
|
// remove from stats
|
||||||
char buff [41];
|
sqlite3_stmt *rmS;
|
||||||
str += _to_hex_str(info_hash, buff);
|
if (sqlite3_prepare(this->db, "DELETE FROM stats WHERE info_hash=?", -1, &rmS, NULL) != SQLITE_OK)
|
||||||
str += "'";
|
{
|
||||||
|
sqlite3_finalize(rmS);
|
||||||
sqlite3_exec(this->db, str.c_str(), NULL, NULL, NULL);
|
return false;
|
||||||
|
}
|
||||||
BOOST_LOG_SEV(m_logger, boost::log::trivial::info) << "Torrent removed.";
|
sqlite3_bind_blob(rmS, 1, (const void*)info_hash, 20, NULL);
|
||||||
|
sqlite3_step(rmS);
|
||||||
return true;
|
sqlite3_finalize(rmS);
|
||||||
}
|
|
||||||
|
// remove table
|
||||||
bool SQLite3Driver::removePeer(uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port)
|
string str = "DROP TABLE IF EXISTS 't";
|
||||||
{
|
char buff [41];
|
||||||
string sql;
|
str += _to_hex_str(info_hash, buff);
|
||||||
char xHash [50];
|
str += "'";
|
||||||
sqlite3_stmt *stmt;
|
|
||||||
|
sqlite3_exec(this->db, str.c_str(), NULL, NULL, NULL);
|
||||||
_to_hex_str (info_hash, xHash);
|
|
||||||
|
return true;
|
||||||
sql += "DELETE FROM 't";
|
}
|
||||||
sql += xHash;
|
|
||||||
sql += "' WHERE ip=? AND port=? AND peer_id=?";
|
bool SQLite3Driver::removePeer(uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port) {
|
||||||
|
string sql;
|
||||||
sqlite3_prepare (this->db, sql.c_str(), sql.length(), &stmt, NULL);
|
char xHash [50];
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
sqlite3_bind_blob(stmt, 0, (const void*)&ip, 4, NULL);
|
|
||||||
sqlite3_bind_blob(stmt, 1, (const void*)&port, 2, NULL);
|
_to_hex_str (info_hash, xHash);
|
||||||
sqlite3_bind_blob(stmt, 2, (const void*)peer_id, 20, NULL);
|
|
||||||
|
sql += "DELETE FROM 't";
|
||||||
sqlite3_step(stmt);
|
sql += xHash;
|
||||||
|
sql += "' WHERE ip=? AND port=? AND peer_id=?";
|
||||||
sqlite3_finalize(stmt);
|
|
||||||
|
sqlite3_prepare (this->db, sql.c_str(), sql.length(), &stmt, NULL);
|
||||||
return true;
|
|
||||||
}
|
sqlite3_bind_blob(stmt, 0, (const void*)&ip, 4, NULL);
|
||||||
|
sqlite3_bind_blob(stmt, 1, (const void*)&port, 2, NULL);
|
||||||
static uint64_t _genCiD (uint32_t ip, uint16_t port)
|
sqlite3_bind_blob(stmt, 2, (const void*)peer_id, 20, NULL);
|
||||||
{
|
|
||||||
uint64_t x;
|
sqlite3_step(stmt);
|
||||||
x = (time(NULL) / 3600) * port; // x will probably overload.
|
|
||||||
x = (ip ^ port);
|
sqlite3_finalize(stmt);
|
||||||
x <<= 16;
|
|
||||||
x |= (~port);
|
return true;
|
||||||
return x;
|
}
|
||||||
}
|
|
||||||
|
static uint64_t _genCiD (uint32_t ip, uint16_t port) {
|
||||||
bool SQLite3Driver::genConnectionId (uint64_t *connectionId, uint32_t ip, uint16_t port)
|
uint64_t x;
|
||||||
{
|
x = (time(NULL) / 3600) * port; // x will probably overload.
|
||||||
*connectionId = _genCiD(ip, port);
|
x = (ip ^ port);
|
||||||
return true;
|
x <<= 16;
|
||||||
}
|
x |= (~port);
|
||||||
|
return x;
|
||||||
bool SQLite3Driver::verifyConnectionId(uint64_t cId, uint32_t ip, uint16_t port)
|
}
|
||||||
{
|
|
||||||
if (cId == _genCiD(ip, port))
|
bool SQLite3Driver::genConnectionId (uint64_t *connectionId, uint32_t ip, uint16_t port) {
|
||||||
return true;
|
*connectionId = _genCiD(ip, port);
|
||||||
else
|
return true;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
bool SQLite3Driver::verifyConnectionId(uint64_t cId, uint32_t ip, uint16_t port) {
|
||||||
SQLite3Driver::~SQLite3Driver()
|
if (cId == _genCiD(ip, port))
|
||||||
{
|
return true;
|
||||||
BOOST_LOG_SEV(m_logger, boost::log::trivial::info) << "Closing SQLite";
|
else
|
||||||
sqlite3_close(this->db);
|
return false;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
};
|
SQLite3Driver::~SQLite3Driver() {
|
||||||
|
sqlite3_close(this->db);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
|
@ -1,56 +1,55 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2012-2016 Naim A.
|
* Copyright © 2012-2017 Naim A.
|
||||||
*
|
*
|
||||||
* This file is part of UDPT.
|
* This file is part of UDPT.
|
||||||
*
|
*
|
||||||
* UDPT is free software: you can redistribute it and/or modify
|
* UDPT is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* UDPT is distributed in the hope that it will be useful,
|
* UDPT is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
|
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef DATABASE_H_
|
#ifndef DATABASE_H_
|
||||||
#define DATABASE_H_
|
#define DATABASE_H_
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "database.hpp"
|
#include "database.hpp"
|
||||||
#include <sqlite3.h>
|
#include <sqlite3.h>
|
||||||
|
|
||||||
namespace UDPT
|
namespace UDPT
|
||||||
{
|
{
|
||||||
namespace Data
|
namespace Data
|
||||||
{
|
{
|
||||||
class SQLite3Driver : public DatabaseDriver
|
class SQLite3Driver : public DatabaseDriver
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SQLite3Driver(const boost::program_options::variables_map& conf, bool isDyn = false);
|
SQLite3Driver(const boost::program_options::variables_map& conf, bool isDyn = false);
|
||||||
bool addTorrent(uint8_t info_hash[20]);
|
bool addTorrent(uint8_t info_hash[20]);
|
||||||
bool removeTorrent(uint8_t info_hash[20]);
|
bool removeTorrent(uint8_t info_hash[20]);
|
||||||
bool genConnectionId(uint64_t *connId, uint32_t ip, uint16_t port);
|
bool genConnectionId(uint64_t *connId, uint32_t ip, uint16_t port);
|
||||||
bool verifyConnectionId(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 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 removePeer(uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port);
|
||||||
bool getTorrentInfo(TorrentEntry *e);
|
bool getTorrentInfo(TorrentEntry *e);
|
||||||
bool isTorrentAllowed(uint8_t info_hash[20]);
|
bool isTorrentAllowed(uint8_t info_hash[20]);
|
||||||
bool getPeers(uint8_t info_hash [20], int *max_count, PeerEntry *pe);
|
bool getPeers(uint8_t info_hash [20], int *max_count, PeerEntry *pe);
|
||||||
void cleanup();
|
void cleanup();
|
||||||
|
|
||||||
virtual ~SQLite3Driver();
|
virtual ~SQLite3Driver();
|
||||||
private:
|
private:
|
||||||
sqlite3 *db;
|
sqlite3 *db;
|
||||||
boost::log::sources::severity_channel_logger_mt<> m_logger;
|
|
||||||
|
void doSetup();
|
||||||
void doSetup();
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
#endif /* DATABASE_H_ */
|
||||||
#endif /* DATABASE_H_ */
|
|
||||||
|
|
104
src/exceptions.h
104
src/exceptions.h
|
@ -4,73 +4,73 @@
|
||||||
|
|
||||||
namespace UDPT
|
namespace UDPT
|
||||||
{
|
{
|
||||||
class UDPTException
|
class UDPTException
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
UDPTException(const char* errorMsg, int errorCode = 0) : m_error(errorMsg), m_errorCode(errorCode)
|
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
|
virtual const char* what() const
|
||||||
{
|
{
|
||||||
return m_error;
|
return m_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int getErrorCode() const
|
virtual int getErrorCode() const
|
||||||
{
|
{
|
||||||
return m_errorCode;
|
return m_errorCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~UDPTException()
|
virtual ~UDPTException()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const char* m_error;
|
const char* m_error;
|
||||||
const int m_errorCode;
|
const int m_errorCode;
|
||||||
};
|
};
|
||||||
|
|
||||||
class OSError : public UDPTException
|
class OSError : public UDPTException
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
OSError(int errorCode
|
OSError(int errorCode
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
= ::GetLastError()
|
= ::GetLastError()
|
||||||
#endif
|
#endif
|
||||||
) : UDPTException(errorCode)
|
) : UDPTException(errorCode)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~OSError() {}
|
virtual ~OSError() {}
|
||||||
|
|
||||||
const char* what() const
|
const char* what() const
|
||||||
{
|
{
|
||||||
if (m_errorMessage.length() > 0)
|
if (m_errorMessage.length() > 0)
|
||||||
{
|
{
|
||||||
return m_errorMessage.c_str();
|
return m_errorMessage.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
char *buffer = nullptr;
|
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);
|
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>(
|
std::shared_ptr<void> formatStr = std::shared_ptr<void>(
|
||||||
buffer,
|
buffer,
|
||||||
::LocalFree);
|
::LocalFree);
|
||||||
m_errorMessage = std::string(reinterpret_cast<char*>(formatStr.get()));
|
m_errorMessage = std::string(reinterpret_cast<char*>(formatStr.get()));
|
||||||
|
|
||||||
return m_errorMessage.c_str();
|
return m_errorMessage.c_str();
|
||||||
#else
|
#else
|
||||||
return "OSError";
|
return "OSError";
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
// allow to generate a message only when needed.
|
// allow to generate a message only when needed.
|
||||||
mutable std::string m_errorMessage;
|
mutable std::string m_errorMessage;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,170 +1,170 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2013-2016 Naim A.
|
* Copyright © 2013-2017 Naim A.
|
||||||
*
|
*
|
||||||
* This file is part of UDPT.
|
* This file is part of UDPT.
|
||||||
*
|
*
|
||||||
* UDPT is free software: you can redistribute it and/or modify
|
* UDPT is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* UDPT is distributed in the hope that it will be useful,
|
* UDPT is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
|
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <boost/program_options.hpp>
|
#include <boost/program_options.hpp>
|
||||||
#include "../multiplatform.h"
|
#include "../multiplatform.h"
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
#define REQUEST_BUFFER_SIZE 2048
|
#define REQUEST_BUFFER_SIZE 2048
|
||||||
|
|
||||||
namespace UDPT
|
namespace UDPT
|
||||||
{
|
{
|
||||||
namespace Server
|
namespace Server
|
||||||
{
|
{
|
||||||
class ServerException
|
class ServerException
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
inline ServerException (int ec)
|
inline ServerException (int ec)
|
||||||
{
|
{
|
||||||
this->ec = ec;
|
this->ec = ec;
|
||||||
this->em = NULL;
|
this->em = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ServerException (int ec, const char *em)
|
inline ServerException (int ec, const char *em)
|
||||||
{
|
{
|
||||||
this->ec = ec;
|
this->ec = ec;
|
||||||
this->em = em;
|
this->em = em;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const char *getErrorMsg () const
|
inline const char *getErrorMsg () const
|
||||||
{
|
{
|
||||||
return this->em;
|
return this->em;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int getErrorCode () const
|
inline int getErrorCode () const
|
||||||
{
|
{
|
||||||
return this->ec;
|
return this->ec;
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
int ec;
|
int ec;
|
||||||
const char *em;
|
const char *em;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HTTPServer
|
class HTTPServer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
class Request
|
class Request
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum RequestMethod
|
enum RequestMethod
|
||||||
{
|
{
|
||||||
RM_UNKNOWN = 0,
|
RM_UNKNOWN = 0,
|
||||||
RM_GET = 1,
|
RM_GET = 1,
|
||||||
RM_POST = 2
|
RM_POST = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
Request (SOCKET, const SOCKADDR_IN *);
|
Request (SOCKET, const SOCKADDR_IN *);
|
||||||
list<string>* getPath ();
|
list<string>* getPath ();
|
||||||
|
|
||||||
string getParam (const string key);
|
string getParam (const string key);
|
||||||
multimap<string, string>::iterator getHeader (const string name);
|
multimap<string, string>::iterator getHeader (const string name);
|
||||||
RequestMethod getRequestMethod ();
|
RequestMethod getRequestMethod ();
|
||||||
string getRequestMethodStr ();
|
string getRequestMethodStr ();
|
||||||
string getCookie (const string name);
|
string getCookie (const string name);
|
||||||
const SOCKADDR_IN* getAddress ();
|
const SOCKADDR_IN* getAddress ();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const SOCKADDR_IN *addr;
|
const SOCKADDR_IN *addr;
|
||||||
SOCKET conn;
|
SOCKET conn;
|
||||||
struct {
|
struct {
|
||||||
int major;
|
int major;
|
||||||
int minor;
|
int minor;
|
||||||
} httpVer;
|
} httpVer;
|
||||||
struct {
|
struct {
|
||||||
string str;
|
string str;
|
||||||
RequestMethod rm;
|
RequestMethod rm;
|
||||||
} requestMethod;
|
} requestMethod;
|
||||||
list<string> path;
|
list<string> path;
|
||||||
map<string, string> params;
|
map<string, string> params;
|
||||||
map<string, string> cookies;
|
map<string, string> cookies;
|
||||||
multimap<string, string> headers;
|
multimap<string, string> headers;
|
||||||
|
|
||||||
void parseRequest ();
|
void parseRequest ();
|
||||||
};
|
};
|
||||||
|
|
||||||
class Response
|
class Response
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Response (SOCKET conn);
|
Response (SOCKET conn);
|
||||||
|
|
||||||
void setStatus (int, const string);
|
void setStatus (int, const string);
|
||||||
void addHeader (string key, string value);
|
void addHeader (string key, string value);
|
||||||
|
|
||||||
int writeRaw (const char *data, int len);
|
int writeRaw (const char *data, int len);
|
||||||
void write (const char *data, int len = -1);
|
void write (const char *data, int len = -1);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class HTTPServer;
|
friend class HTTPServer;
|
||||||
|
|
||||||
SOCKET conn;
|
SOCKET conn;
|
||||||
int status_code;
|
int status_code;
|
||||||
string status_msg;
|
string status_msg;
|
||||||
multimap<string, string> headers;
|
multimap<string, string> headers;
|
||||||
stringstream msg;
|
stringstream msg;
|
||||||
|
|
||||||
void finalize ();
|
void finalize ();
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef void (reqCallback)(HTTPServer*,Request*,Response*);
|
typedef void (reqCallback)(HTTPServer*,Request*,Response*);
|
||||||
|
|
||||||
HTTPServer (uint16_t port, int threads);
|
HTTPServer (uint16_t port, int threads);
|
||||||
HTTPServer(const boost::program_options::variables_map& conf);
|
HTTPServer(const boost::program_options::variables_map& conf);
|
||||||
|
|
||||||
void addApp (list<string> *path, reqCallback *);
|
void addApp (list<string> *path, reqCallback *);
|
||||||
|
|
||||||
void setData (string, void *);
|
void setData (string, void *);
|
||||||
void* getData (string);
|
void* getData (string);
|
||||||
|
|
||||||
virtual ~HTTPServer ();
|
virtual ~HTTPServer ();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef struct appNode
|
typedef struct appNode
|
||||||
{
|
{
|
||||||
reqCallback *callback;
|
reqCallback *callback;
|
||||||
map<string, appNode> nodes;
|
map<string, appNode> nodes;
|
||||||
} appNode;
|
} appNode;
|
||||||
|
|
||||||
SOCKET srv;
|
SOCKET srv;
|
||||||
int thread_count;
|
int thread_count;
|
||||||
HANDLE *threads;
|
HANDLE *threads;
|
||||||
bool isRunning;
|
bool isRunning;
|
||||||
appNode rootNode;
|
appNode rootNode;
|
||||||
map<string, void*> customData;
|
map<string, void*> customData;
|
||||||
|
|
||||||
void init (SOCKADDR_IN &localEndpoint, int threads);
|
void init (SOCKADDR_IN &localEndpoint, int threads);
|
||||||
|
|
||||||
static void handleConnections (HTTPServer *);
|
static void handleConnections (HTTPServer *);
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
static DWORD _thread_start (LPVOID);
|
static DWORD _thread_start (LPVOID);
|
||||||
#else
|
#else
|
||||||
static void* _thread_start (void*);
|
static void* _thread_start (void*);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static reqCallback* getRequestHandler (appNode *, list<string> *);
|
static reqCallback* getRequestHandler (appNode *, list<string> *);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2013-2016 Naim A.
|
* Copyright © 2013-2017 Naim A.
|
||||||
*
|
*
|
||||||
* This file is part of UDPT.
|
* This file is part of UDPT.
|
||||||
*
|
*
|
||||||
|
@ -25,190 +25,190 @@ using namespace std;
|
||||||
|
|
||||||
namespace UDPT
|
namespace UDPT
|
||||||
{
|
{
|
||||||
namespace Server
|
namespace Server
|
||||||
{
|
{
|
||||||
|
|
||||||
static uint32_t _getNextIPv4 (string::size_type &i, string &line)
|
static uint32_t _getNextIPv4 (string::size_type &i, string &line)
|
||||||
{
|
{
|
||||||
string::size_type len = line.length();
|
string::size_type len = line.length();
|
||||||
char c;
|
char c;
|
||||||
while (i < len)
|
while (i < len)
|
||||||
{
|
{
|
||||||
c = line.at(i);
|
c = line.at(i);
|
||||||
if (c >= '0' && c <= '9')
|
if (c >= '0' && c <= '9')
|
||||||
break;
|
break;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t ip = 0;
|
uint32_t ip = 0;
|
||||||
for (int n = 0;n < 4;n++)
|
for (int n = 0;n < 4;n++)
|
||||||
{
|
{
|
||||||
int cn = 0;
|
int cn = 0;
|
||||||
while (i < len)
|
while (i < len)
|
||||||
{
|
{
|
||||||
c = line.at (i++);
|
c = line.at (i++);
|
||||||
if (c == '.' || ((c == ' ' || c == ',' || c == ';') && n == 3))
|
if (c == '.' || ((c == ' ' || c == ',' || c == ';') && n == 3))
|
||||||
break;
|
break;
|
||||||
else if (!(c >= '0' && c <= '9'))
|
else if (!(c >= '0' && c <= '9'))
|
||||||
return 0;
|
return 0;
|
||||||
cn *= 10;
|
cn *= 10;
|
||||||
cn += (c - '0');
|
cn += (c - '0');
|
||||||
}
|
}
|
||||||
ip *= 256;
|
ip *= 256;
|
||||||
ip += cn;
|
ip += cn;
|
||||||
}
|
}
|
||||||
return ip;
|
return ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _hex2bin (uint8_t *data, const string str)
|
static bool _hex2bin (uint8_t *data, const string str)
|
||||||
{
|
{
|
||||||
int len = str.length();
|
int len = str.length();
|
||||||
|
|
||||||
if (len % 2 != 0)
|
if (len % 2 != 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
char a, b;
|
char a, b;
|
||||||
uint8_t c;
|
uint8_t c;
|
||||||
for (int i = 0;i < len;i+=2)
|
for (int i = 0;i < len;i+=2)
|
||||||
{
|
{
|
||||||
a = str.at (i);
|
a = str.at (i);
|
||||||
b = str.at (i + 1);
|
b = str.at (i + 1);
|
||||||
c = 0;
|
c = 0;
|
||||||
|
|
||||||
if (a >= 'a' && a <= 'f')
|
if (a >= 'a' && a <= 'f')
|
||||||
a = (a - 'a') + 10;
|
a = (a - 'a') + 10;
|
||||||
else if (a >= '0' && a <= '9')
|
else if (a >= '0' && a <= '9')
|
||||||
a = (a - '0');
|
a = (a - '0');
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (b >= 'a' && b <= 'f')
|
if (b >= 'a' && b <= 'f')
|
||||||
b = (b - 'a') + 10;
|
b = (b - 'a') + 10;
|
||||||
else if (b >= '0' && b <= '9')
|
else if (b >= '0' && b <= '9')
|
||||||
b = (b - '0');
|
b = (b - '0');
|
||||||
else
|
else
|
||||||
return false;
|
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)
|
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;
|
this->db = db;
|
||||||
// TODO: Implement authentication by keys
|
// TODO: Implement authentication by keys
|
||||||
|
|
||||||
m_server->setData("webapp", this);
|
m_server->setData("webapp", this);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebApp::~WebApp()
|
WebApp::~WebApp()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebApp::deploy()
|
void WebApp::deploy()
|
||||||
{
|
{
|
||||||
list<string> path;
|
list<string> path;
|
||||||
m_server->addApp(&path, &WebApp::handleRoot);
|
m_server->addApp(&path, &WebApp::handleRoot);
|
||||||
|
|
||||||
path.push_back("api");
|
path.push_back("api");
|
||||||
m_server->addApp(&path, &WebApp::handleAPI); // "/api"
|
m_server->addApp(&path, &WebApp::handleAPI); // "/api"
|
||||||
|
|
||||||
path.pop_back();
|
path.pop_back();
|
||||||
path.push_back("announce");
|
path.push_back("announce");
|
||||||
m_server->addApp(&path, &WebApp::handleAnnounce);
|
m_server->addApp(&path, &WebApp::handleAnnounce);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebApp::handleRoot(HTTPServer *srv, HTTPServer::Request *req, HTTPServer::Response *resp)
|
void WebApp::handleRoot(HTTPServer *srv, HTTPServer::Request *req, HTTPServer::Response *resp)
|
||||||
{
|
{
|
||||||
// It would be very appreciated to keep this in the code.
|
// It would be very appreciated to keep this in the code.
|
||||||
resp->write("<html>"
|
resp->write("<html>"
|
||||||
"<head><title>UDPT Torrent Tracker</title></head>"
|
"<head><title>UDPT Torrent Tracker</title></head>"
|
||||||
"<body>"
|
"<body>"
|
||||||
"<div style=\"vertical-align:top;\">This tracker is running on UDPT Software.</div>"
|
"<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>"
|
"<br /><hr /><div style=\"text-align:center;font-size:small;\"><a href=\"http://github.com/naim94a/udpt\">UDPT</a></div>"
|
||||||
"</body>"
|
"</body>"
|
||||||
"</html>");
|
"</html>");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebApp::doRemoveTorrent (HTTPServer::Request *req, HTTPServer::Response *resp)
|
void WebApp::doRemoveTorrent (HTTPServer::Request *req, HTTPServer::Response *resp)
|
||||||
{
|
{
|
||||||
string strHash = req->getParam("hash");
|
string strHash = req->getParam("hash");
|
||||||
if (strHash.length() != 40)
|
if (strHash.length() != 40)
|
||||||
{
|
{
|
||||||
resp->write("{\"error\":\"Hash length must be 40 characters.\"}");
|
resp->write("{\"error\":\"Hash length must be 40 characters.\"}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint8_t hash [20];
|
uint8_t hash [20];
|
||||||
if (!_hex2bin(hash, strHash))
|
if (!_hex2bin(hash, strHash))
|
||||||
{
|
{
|
||||||
resp->write("{\"error\":\"invalid info_hash.\"}");
|
resp->write("{\"error\":\"invalid info_hash.\"}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (this->db->removeTorrent(hash))
|
if (this->db->removeTorrent(hash))
|
||||||
resp->write("{\"success\":true}");
|
resp->write("{\"success\":true}");
|
||||||
else
|
else
|
||||||
resp->write("{\"error\":\"failed to remove torrent from DB\"}");
|
resp->write("{\"error\":\"failed to remove torrent from DB\"}");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebApp::doAddTorrent (HTTPServer::Request *req, HTTPServer::Response *resp)
|
void WebApp::doAddTorrent (HTTPServer::Request *req, HTTPServer::Response *resp)
|
||||||
{
|
{
|
||||||
std::string strHash = req->getParam("hash");
|
std::string strHash = req->getParam("hash");
|
||||||
if (strHash.length() != 40)
|
if (strHash.length() != 40)
|
||||||
{
|
{
|
||||||
resp->write("{\"error\":\"Hash length must be 40 characters.\"}");
|
resp->write("{\"error\":\"Hash length must be 40 characters.\"}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint8_t hash [20];
|
uint8_t hash [20];
|
||||||
if (!_hex2bin(hash, strHash))
|
if (!_hex2bin(hash, strHash))
|
||||||
{
|
{
|
||||||
resp->write("{\"error\":\"invalid info_hash.\"}");
|
resp->write("{\"error\":\"invalid info_hash.\"}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->db->addTorrent(hash))
|
if (this->db->addTorrent(hash))
|
||||||
resp->write("{\"success\":true}");
|
resp->write("{\"success\":true}");
|
||||||
else
|
else
|
||||||
resp->write("{\"error\":\"failed to add torrent to DB\"}");
|
resp->write("{\"error\":\"failed to add torrent to DB\"}");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebApp::handleAnnounce (HTTPServer *srv, HTTPServer::Request *req, HTTPServer::Response *resp)
|
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");
|
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)
|
void WebApp::handleAPI(HTTPServer *srv, HTTPServer::Request *req, HTTPServer::Response *resp)
|
||||||
{
|
{
|
||||||
if (req->getAddress()->sin_family != AF_INET)
|
if (req->getAddress()->sin_family != AF_INET)
|
||||||
{
|
{
|
||||||
throw ServerException (0, "IPv4 supported Only.");
|
throw ServerException (0, "IPv4 supported Only.");
|
||||||
}
|
}
|
||||||
|
|
||||||
WebApp *app = (WebApp*)srv->getData("webapp");
|
WebApp *app = (WebApp*)srv->getData("webapp");
|
||||||
if (app == NULL)
|
if (app == NULL)
|
||||||
throw ServerException(0, "WebApp object wasn't found");
|
throw ServerException(0, "WebApp object wasn't found");
|
||||||
|
|
||||||
if (req->getAddress()->sin_addr.s_addr != 0x0100007f)
|
if (req->getAddress()->sin_addr.s_addr != 0x0100007f)
|
||||||
{
|
{
|
||||||
resp->setStatus(403, "Forbidden");
|
resp->setStatus(403, "Forbidden");
|
||||||
resp->write("Access Denied. Only 127.0.0.1 can access this method.");
|
resp->write("Access Denied. Only 127.0.0.1 can access this method.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string action = req->getParam("action");
|
std::string action = req->getParam("action");
|
||||||
if (action == "add")
|
if (action == "add")
|
||||||
app->doAddTorrent(req, resp);
|
app->doAddTorrent(req, resp);
|
||||||
else if (action == "remove")
|
else if (action == "remove")
|
||||||
app->doRemoveTorrent(req, resp);
|
app->doRemoveTorrent(req, resp);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
resp->write("{\"error\":\"unknown action\"}");
|
resp->write("{\"error\":\"unknown action\"}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2013-2016 Naim A.
|
* Copyright © 2013-2017 Naim A.
|
||||||
*
|
*
|
||||||
* This file is part of UDPT.
|
* This file is part of UDPT.
|
||||||
*
|
*
|
||||||
|
@ -33,27 +33,27 @@ using namespace UDPT::Data;
|
||||||
|
|
||||||
namespace UDPT
|
namespace UDPT
|
||||||
{
|
{
|
||||||
namespace Server
|
namespace Server
|
||||||
{
|
{
|
||||||
class WebApp
|
class WebApp
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
WebApp(std::shared_ptr<HTTPServer> , DatabaseDriver *, const boost::program_options::variables_map& conf);
|
WebApp(std::shared_ptr<HTTPServer> , DatabaseDriver *, const boost::program_options::variables_map& conf);
|
||||||
virtual ~WebApp();
|
virtual ~WebApp();
|
||||||
void deploy ();
|
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*);
|
private:
|
||||||
static void handleAnnounce (HTTPServer*,HTTPServer::Request*, HTTPServer::Response*);
|
std::shared_ptr<HTTPServer> m_server;
|
||||||
static void handleAPI (HTTPServer*,HTTPServer::Request*, HTTPServer::Response*);
|
UDPT::Data::DatabaseDriver *db;
|
||||||
|
const boost::program_options::variables_map& m_conf;
|
||||||
|
|
||||||
void doAddTorrent (HTTPServer::Request*, HTTPServer::Response*);
|
static void handleRoot (HTTPServer*,HTTPServer::Request*, HTTPServer::Response*);
|
||||||
void doRemoveTorrent (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
140
src/logging.cpp
Normal 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(×tamp);
|
||||||
|
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
83
src/logging.hpp
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
300
src/main.cpp
300
src/main.cpp
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2012-2016 Naim A.
|
* Copyright © 2012-2017 Naim A.
|
||||||
*
|
*
|
||||||
* This file is part of UDPT.
|
* This file is part of UDPT.
|
||||||
*
|
*
|
||||||
|
@ -25,8 +25,6 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <boost/program_options.hpp>
|
#include <boost/program_options.hpp>
|
||||||
#include <boost/log/trivial.hpp>
|
|
||||||
#include <boost/log/sources/severity_channel_logger.hpp>
|
|
||||||
|
|
||||||
#include "multiplatform.h"
|
#include "multiplatform.h"
|
||||||
#include "udpTracker.hpp"
|
#include "udpTracker.hpp"
|
||||||
|
@ -34,35 +32,39 @@
|
||||||
#include "http/webapp.hpp"
|
#include "http/webapp.hpp"
|
||||||
#include "tracker.hpp"
|
#include "tracker.hpp"
|
||||||
#include "service.hpp"
|
#include "service.hpp"
|
||||||
|
#include "logging.hpp"
|
||||||
|
|
||||||
static void _signal_handler(int sig)
|
extern "C" void _signal_handler(int sig)
|
||||||
{
|
{
|
||||||
switch (sig)
|
switch (sig) {
|
||||||
{
|
case SIGTERM:
|
||||||
case SIGTERM:
|
case SIGQUIT:
|
||||||
UDPT::Tracker::getInstance().stop();
|
case SIGINT: {
|
||||||
break;
|
LOG_INFO("core", "Received signal " << sig << ", requesting to stop tracker");
|
||||||
}
|
UDPT::Tracker::getInstance().stop();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef linux
|
#ifdef linux
|
||||||
static void daemonize(const boost::program_options::variables_map& conf)
|
static void daemonize(const boost::program_options::variables_map& conf)
|
||||||
{
|
{
|
||||||
if (1 == ::getppid()) return; // already a daemon
|
if (1 == ::getppid()) return; // already a daemon
|
||||||
int r = ::fork();
|
int r = ::fork();
|
||||||
if (0 > r) ::exit(-1); // failed to daemonize.
|
if (0 > r) ::exit(-1); // failed to daemonize.
|
||||||
if (0 < r) ::exit(0); // parent exists.
|
if (0 < r) ::exit(0); // parent exists.
|
||||||
|
|
||||||
::umask(0);
|
::umask(0);
|
||||||
::setsid();
|
::setsid();
|
||||||
|
|
||||||
// close all fds.
|
// close all fds.
|
||||||
for (int i = ::getdtablesize(); i >=0; --i)
|
for (int i = ::getdtablesize(); i >=0; --i)
|
||||||
{
|
{
|
||||||
::close(i);
|
::close(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
::chdir(conf["daemon.chdir"].as<std::string>().c_str());
|
::chdir(conf["daemon.chdir"].as<std::string>().c_str());
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -70,159 +72,163 @@ static void daemonize(const boost::program_options::variables_map& conf)
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
void _close_wsa()
|
void _close_wsa()
|
||||||
{
|
{
|
||||||
::WSACleanup();
|
::WSACleanup();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef TEST
|
||||||
|
int real_main(int argc, char *argv[])
|
||||||
|
#else
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
WSADATA wsadata;
|
WSADATA wsadata;
|
||||||
::WSAStartup(MAKEWORD(2, 2), &wsadata);
|
::WSAStartup(MAKEWORD(2, 2), &wsadata);
|
||||||
::atexit(_close_wsa);
|
::atexit(_close_wsa);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
boost::program_options::options_description commandLine("Command line options");
|
boost::program_options::options_description commandLine("Command line options");
|
||||||
commandLine.add_options()
|
commandLine.add_options()
|
||||||
("help,h", "produce help message")
|
("help,h", "produce help message")
|
||||||
("all-help", "displays all help")
|
("all-help", "displays all help")
|
||||||
("test,t", "test configuration file")
|
("test,t", "test configuration file")
|
||||||
("config,c", boost::program_options::value<std::string>()->default_value("/etc/udpt.conf"), "configuration file to use")
|
("config,c", boost::program_options::value<std::string>()->default_value("/etc/udpt.conf"), "configuration file to use")
|
||||||
#ifdef linux
|
#ifdef linux
|
||||||
("interactive,i", "doesn't start as daemon")
|
("interactive,i", "doesn't start as daemon")
|
||||||
#endif
|
#endif
|
||||||
#ifdef WIN32
|
#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
|
#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::variables_map var_map;
|
||||||
boost::program_options::store(boost::program_options::parse_command_line(argc, argv, commandLine), 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::notify(var_map);
|
||||||
|
|
||||||
if (var_map.count("help"))
|
if (var_map.count("help"))
|
||||||
{
|
{
|
||||||
std::cout << "UDP Tracker (UDPT) " << VERSION << " (" << PLATFORM << ")" << std::endl
|
std::cout << "UDP Tracker (UDPT) " << VERSION << " (" << PLATFORM << ")" << std::endl
|
||||||
<< "Copyright 2012-2016 Naim A. <naim94a@gmail.com>" << std::endl
|
<< "Copyright 2012-2016 Naim A. <naim94a@gmail.com>" << std::endl
|
||||||
<< "Build Date: " << __DATE__ << std::endl << std::endl;
|
<< "Build Date: " << __DATE__ << std::endl << std::endl;
|
||||||
|
|
||||||
std::cout << commandLine << std::endl;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (var_map.count("all-help"))
|
std::cout << commandLine << std::endl;
|
||||||
{
|
return 0;
|
||||||
std::cout << commandLine << std::endl;
|
}
|
||||||
std::cout << configOptions << std::endl;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string config_filename(var_map["config"].as<std::string>());
|
if (var_map.count("all-help"))
|
||||||
bool isTest = (0 != var_map.count("test"));
|
{
|
||||||
|
std::cout << commandLine << std::endl;
|
||||||
|
std::cout << configOptions << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (var_map.count("config"))
|
std::string config_filename(var_map["config"].as<std::string>());
|
||||||
{
|
bool isTest = (0 != var_map.count("test"));
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isTest)
|
if (var_map.count("config"))
|
||||||
{
|
{
|
||||||
std::cout << "Config OK" << std::endl;
|
try
|
||||||
return 0;
|
{
|
||||||
}
|
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");
|
if (isTest)
|
||||||
Tracker::setupLogging(var_map, logger);
|
{
|
||||||
|
std::cout << "Config OK" << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef linux
|
#ifdef linux
|
||||||
if (!var_map.count("interactive"))
|
if (!var_map.count("interactive"))
|
||||||
{
|
{
|
||||||
daemonize(var_map);
|
daemonize(var_map);
|
||||||
}
|
}
|
||||||
::signal(SIGTERM, _signal_handler);
|
::signal(SIGTERM, _signal_handler);
|
||||||
|
::signal(SIGINT, _signal_handler);
|
||||||
#endif
|
#endif
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
UDPT::Service svc(var_map);
|
UDPT::Service svc(var_map);
|
||||||
if (var_map.count("service"))
|
if (var_map.count("service"))
|
||||||
{
|
{
|
||||||
const std::string& action = var_map["service"].as<std::string>();
|
const std::string& action = var_map["service"].as<std::string>();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if ("install" == action)
|
if ("install" == action)
|
||||||
{
|
{
|
||||||
std::cerr << "Installing service..." << std::endl;
|
std::cerr << "Installing service..." << std::endl;
|
||||||
svc.install(var_map["config"].as<std::string>());
|
svc.install(var_map["config"].as<std::string>());
|
||||||
std::cerr << "Installed." << std::endl;
|
std::cerr << "Installed." << std::endl;
|
||||||
}
|
}
|
||||||
else if ("uninstall" == action)
|
else if ("uninstall" == action)
|
||||||
{
|
{
|
||||||
std::cerr << "Removing service..." << std::endl;
|
std::cerr << "Removing service..." << std::endl;
|
||||||
svc.uninstall();
|
svc.uninstall();
|
||||||
std::cerr << "Removed." << std::endl;
|
std::cerr << "Removed." << std::endl;
|
||||||
}
|
}
|
||||||
else if ("start" == action)
|
else if ("start" == action)
|
||||||
{
|
{
|
||||||
svc.start();
|
svc.start();
|
||||||
}
|
}
|
||||||
else if ("stop" == action)
|
else if ("stop" == action)
|
||||||
{
|
{
|
||||||
svc.stop();
|
svc.stop();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::cerr << "No such service command." << std::endl;
|
std::cerr << "No such service command." << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const UDPT::OSError& ex)
|
catch (const UDPT::OSError& ex)
|
||||||
{
|
{
|
||||||
std::cerr << "An operating system error occurred: " << ex.what() << std::endl;
|
std::cerr << "An operating system error occurred: " << ex.what() << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
svc.setup();
|
svc.setup();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
catch (const OSError& err)
|
catch (const OSError& err)
|
||||||
{
|
{
|
||||||
if (ERROR_FAILED_SERVICE_CONTROLLER_CONNECT != err.getErrorCode())
|
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();
|
BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "Failed to start as a Windows service: (" << err.getErrorCode() << "): " << err.what();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Tracker& tracker = UDPT::Tracker::getInstance();
|
Tracker& tracker = UDPT::Tracker::getInstance();
|
||||||
tracker.start(var_map);
|
tracker.start(var_map);
|
||||||
tracker.wait();
|
tracker.wait();
|
||||||
}
|
}
|
||||||
catch (const UDPT::UDPTException& ex)
|
catch (const UDPT::UDPTException& ex)
|
||||||
{
|
{
|
||||||
BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "UDPT exception: (" << ex.getErrorCode() << "): " << ex.what();
|
std::cerr << "UDPT exception: (" << ex.getErrorCode() << "): " << ex.what();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
LOG_INFO("core", "UDPT terminated.");
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2012-2016 Naim A.
|
* Copyright © 2012-2017 Naim A.
|
||||||
*
|
*
|
||||||
* This file is part of UDPT.
|
* This file is part of UDPT.
|
||||||
*
|
*
|
||||||
|
|
384
src/service.cpp
384
src/service.cpp
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2012-2016 Naim A.
|
* Copyright © 2012-2017 Naim A.
|
||||||
*
|
*
|
||||||
* This file is part of UDPT.
|
* This file is part of UDPT.
|
||||||
*
|
*
|
||||||
|
@ -23,234 +23,234 @@
|
||||||
|
|
||||||
namespace UDPT
|
namespace UDPT
|
||||||
{
|
{
|
||||||
SERVICE_STATUS_HANDLE Service::s_hServiceStatus = nullptr;
|
SERVICE_STATUS_HANDLE Service::s_hServiceStatus = nullptr;
|
||||||
SERVICE_STATUS Service::s_serviceStatus = { 0 };
|
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)
|
void Service::install(const std::string& config_path)
|
||||||
{
|
{
|
||||||
std::string& binaryPath = getFilename();
|
std::string& binaryPath = getFilename();
|
||||||
binaryPath = "\"" + binaryPath + "\" -c \"" + config_path + "\"";
|
binaryPath = "\"" + binaryPath + "\" -c \"" + config_path + "\"";
|
||||||
std::shared_ptr<void> svcMgr = getServiceManager(SC_MANAGER_CREATE_SERVICE);
|
std::shared_ptr<void> svcMgr = getServiceManager(SC_MANAGER_CREATE_SERVICE);
|
||||||
{
|
{
|
||||||
SC_HANDLE installedService = ::CreateService(reinterpret_cast<SC_HANDLE>(svcMgr.get()),
|
SC_HANDLE installedService = ::CreateService(reinterpret_cast<SC_HANDLE>(svcMgr.get()),
|
||||||
m_conf["service.name"].as<std::string>().c_str(),
|
m_conf["service.name"].as<std::string>().c_str(),
|
||||||
"UDPT Tracker",
|
"UDPT Tracker",
|
||||||
SC_MANAGER_CREATE_SERVICE,
|
SC_MANAGER_CREATE_SERVICE,
|
||||||
SERVICE_WIN32_OWN_PROCESS,
|
SERVICE_WIN32_OWN_PROCESS,
|
||||||
SERVICE_AUTO_START,
|
SERVICE_AUTO_START,
|
||||||
SERVICE_ERROR_NORMAL,
|
SERVICE_ERROR_NORMAL,
|
||||||
binaryPath.c_str(),
|
binaryPath.c_str(),
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
if (nullptr == installedService)
|
if (nullptr == installedService)
|
||||||
{
|
{
|
||||||
throw OSError();
|
throw OSError();
|
||||||
}
|
}
|
||||||
|
|
||||||
::CloseServiceHandle(installedService);
|
::CloseServiceHandle(installedService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Service::uninstall()
|
void Service::uninstall()
|
||||||
{
|
{
|
||||||
std::shared_ptr<void> service = getService(DELETE);
|
std::shared_ptr<void> service = getService(DELETE);
|
||||||
BOOL bRes = ::DeleteService(reinterpret_cast<SC_HANDLE>(service.get()));
|
BOOL bRes = ::DeleteService(reinterpret_cast<SC_HANDLE>(service.get()));
|
||||||
if (FALSE == bRes)
|
if (FALSE == bRes)
|
||||||
{
|
{
|
||||||
throw OSError();
|
throw OSError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Service::start()
|
void Service::start()
|
||||||
{
|
{
|
||||||
std::shared_ptr<void> hSvc = getService(SERVICE_START);
|
std::shared_ptr<void> hSvc = getService(SERVICE_START);
|
||||||
BOOL bRes = ::StartService(reinterpret_cast<SC_HANDLE>(hSvc.get()), 0, NULL);
|
BOOL bRes = ::StartService(reinterpret_cast<SC_HANDLE>(hSvc.get()), 0, NULL);
|
||||||
if (FALSE == bRes)
|
if (FALSE == bRes)
|
||||||
{
|
{
|
||||||
throw OSError();
|
throw OSError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Service::stop()
|
void Service::stop()
|
||||||
{
|
{
|
||||||
SERVICE_STATUS status = { 0 };
|
SERVICE_STATUS status = { 0 };
|
||||||
|
|
||||||
std::shared_ptr<void> hSvc = getService(SERVICE_STOP);
|
std::shared_ptr<void> hSvc = getService(SERVICE_STOP);
|
||||||
BOOL bRes = ::ControlService(reinterpret_cast<SC_HANDLE>(hSvc.get()), SERVICE_CONTROL_STOP, &status);
|
BOOL bRes = ::ControlService(reinterpret_cast<SC_HANDLE>(hSvc.get()), SERVICE_CONTROL_STOP, &status);
|
||||||
if (FALSE == bRes)
|
if (FALSE == bRes)
|
||||||
{
|
{
|
||||||
throw OSError();
|
throw OSError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Service::setup()
|
void Service::setup()
|
||||||
{
|
{
|
||||||
SERVICE_TABLE_ENTRY service[] = {
|
SERVICE_TABLE_ENTRY service[] = {
|
||||||
{ const_cast<char*>(m_conf["service.name"].as<std::string>().c_str()), reinterpret_cast<LPSERVICE_MAIN_FUNCTION>(&Service::serviceMain) },
|
{ const_cast<char*>(m_conf["service.name"].as<std::string>().c_str()), reinterpret_cast<LPSERVICE_MAIN_FUNCTION>(&Service::serviceMain) },
|
||||||
{0, 0}
|
{0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (FALSE == ::StartServiceCtrlDispatcher(service))
|
if (FALSE == ::StartServiceCtrlDispatcher(service))
|
||||||
{
|
{
|
||||||
throw OSError();
|
throw OSError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD Service::handler(DWORD controlCode, DWORD dwEventType, LPVOID eventData, LPVOID context)
|
DWORD Service::handler(DWORD controlCode, DWORD dwEventType, LPVOID eventData, LPVOID context)
|
||||||
{
|
{
|
||||||
switch (controlCode)
|
switch (controlCode)
|
||||||
{
|
{
|
||||||
case SERVICE_CONTROL_INTERROGATE:
|
case SERVICE_CONTROL_INTERROGATE:
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
|
|
||||||
case SERVICE_CONTROL_STOP:
|
case SERVICE_CONTROL_STOP:
|
||||||
{
|
{
|
||||||
reportServiceStatus(SERVICE_STOP_PENDING, 0, 3000);
|
reportServiceStatus(SERVICE_STOP_PENDING, 0, 3000);
|
||||||
Tracker::getInstance().stop();
|
Tracker::getInstance().stop();
|
||||||
|
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return ERROR_CALL_NOT_IMPLEMENTED;
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Service::reportServiceStatus(DWORD currentState, DWORD dwExitCode, DWORD dwWaitHint)
|
void Service::reportServiceStatus(DWORD currentState, DWORD dwExitCode, DWORD dwWaitHint)
|
||||||
{
|
{
|
||||||
static DWORD checkpoint = 1;
|
static DWORD checkpoint = 1;
|
||||||
|
|
||||||
if (currentState == SERVICE_STOPPED || currentState == SERVICE_RUNNING)
|
if (currentState == SERVICE_STOPPED || currentState == SERVICE_RUNNING)
|
||||||
{
|
{
|
||||||
checkpoint = 0;
|
checkpoint = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
++checkpoint;
|
++checkpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (currentState)
|
switch (currentState)
|
||||||
{
|
{
|
||||||
case SERVICE_RUNNING:
|
case SERVICE_RUNNING:
|
||||||
s_serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
|
s_serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
s_serviceStatus.dwControlsAccepted = 0;
|
s_serviceStatus.dwControlsAccepted = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
s_serviceStatus.dwCheckPoint = checkpoint;
|
s_serviceStatus.dwCheckPoint = checkpoint;
|
||||||
s_serviceStatus.dwCurrentState = currentState;
|
s_serviceStatus.dwCurrentState = currentState;
|
||||||
s_serviceStatus.dwWin32ExitCode = dwExitCode;
|
s_serviceStatus.dwWin32ExitCode = dwExitCode;
|
||||||
s_serviceStatus.dwWaitHint = dwWaitHint;
|
s_serviceStatus.dwWaitHint = dwWaitHint;
|
||||||
|
|
||||||
::SetServiceStatus(s_hServiceStatus, &s_serviceStatus);
|
::SetServiceStatus(s_hServiceStatus, &s_serviceStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID Service::serviceMain(DWORD argc, LPCSTR argv[])
|
VOID Service::serviceMain(DWORD argc, LPCSTR argv[])
|
||||||
{
|
{
|
||||||
boost::log::sources::severity_channel_logger_mt<> logger(boost::log::keywords::channel = "service");
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (3 != argCount)
|
wchar_t *commandLine = ::GetCommandLineW();
|
||||||
{
|
int argCount = 0;
|
||||||
BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "Bad command-line length (must have exactly 2 arguments).";
|
std::shared_ptr<LPWSTR> args(::CommandLineToArgvW(commandLine, &argCount), ::LocalFree);
|
||||||
::exit(-1);
|
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")
|
if (3 != argCount)
|
||||||
{
|
{
|
||||||
BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "Argument 1 must be \"-c\".";
|
BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "Bad command-line length (must have exactly 2 arguments).";
|
||||||
::exit(-1);
|
::exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::wstring wFilename(args.get()[2]);
|
if (std::wstring(args.get()[1]) != L"-c")
|
||||||
std::string cFilename(wFilename.begin(), wFilename.end());
|
{
|
||||||
|
BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "Argument 1 must be \"-c\".";
|
||||||
|
::exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
boost::program_options::options_description& configOptions = UDPT::Tracker::getConfigOptions();
|
std::wstring wFilename(args.get()[2]);
|
||||||
boost::program_options::variables_map config;
|
std::string cFilename(wFilename.begin(), wFilename.end());
|
||||||
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_hServiceStatus = ::RegisterServiceCtrlHandlerEx(config["service.name"].as<std::string>().c_str(), Service::handler, NULL);
|
boost::program_options::options_description& configOptions = UDPT::Tracker::getConfigOptions();
|
||||||
if (nullptr == s_hServiceStatus)
|
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_LOG_SEV(logger, boost::log::trivial::fatal) << "Failed to register service control handler.";
|
boost::program_options::store(parsed_options, config);
|
||||||
::exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
s_serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
s_hServiceStatus = ::RegisterServiceCtrlHandlerEx(config["service.name"].as<std::string>().c_str(), Service::handler, NULL);
|
||||||
s_serviceStatus.dwServiceSpecificExitCode = 0;
|
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;
|
||||||
|
|
||||||
{
|
reportServiceStatus(SERVICE_START_PENDING, 0, 0);
|
||||||
UDPT::Tracker& tracker = UDPT::Tracker::getInstance();
|
|
||||||
tracker.start(config);
|
|
||||||
|
|
||||||
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)
|
reportServiceStatus(SERVICE_STOPPED, 0, 0);
|
||||||
{
|
}
|
||||||
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::shared_ptr<void> Service::getServiceManager(DWORD access)
|
std::shared_ptr<void> Service::getService(DWORD access)
|
||||||
{
|
{
|
||||||
SC_HANDLE svcMgr = ::OpenSCManager(NULL, NULL, access);
|
std::shared_ptr<void> serviceManager = getServiceManager(access);
|
||||||
if (nullptr == svcMgr)
|
{
|
||||||
{
|
SC_HANDLE service = ::OpenService(reinterpret_cast<SC_HANDLE>(serviceManager.get()), m_conf["service.name"].as<std::string>().c_str(), access);
|
||||||
throw OSError();
|
if (nullptr == service)
|
||||||
}
|
{
|
||||||
return std::shared_ptr<void>(svcMgr, ::CloseServiceHandle);
|
throw OSError();
|
||||||
}
|
}
|
||||||
|
return std::shared_ptr<void>(service, ::CloseServiceHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string Service::getFilename()
|
std::shared_ptr<void> Service::getServiceManager(DWORD access)
|
||||||
{
|
{
|
||||||
char filename[MAX_PATH];
|
SC_HANDLE svcMgr = ::OpenSCManager(NULL, NULL, access);
|
||||||
DWORD dwRet = ::GetModuleFileName(NULL, filename, sizeof(filename) / sizeof(char));
|
if (nullptr == svcMgr)
|
||||||
if (0 == dwRet)
|
{
|
||||||
{
|
throw OSError();
|
||||||
throw OSError();
|
}
|
||||||
}
|
return std::shared_ptr<void>(svcMgr, ::CloseServiceHandle);
|
||||||
return std::string(filename);
|
}
|
||||||
}
|
|
||||||
|
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
|
#endif
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2012-2016 Naim A.
|
* Copyright © 2012-2017 Naim A.
|
||||||
*
|
*
|
||||||
* This file is part of UDPT.
|
* This file is part of UDPT.
|
||||||
*
|
*
|
||||||
|
@ -28,42 +28,42 @@
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
namespace UDPT
|
namespace UDPT
|
||||||
{
|
{
|
||||||
class Service
|
class Service
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Service(const boost::program_options::variables_map& conf);
|
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();
|
void setup();
|
||||||
private:
|
private:
|
||||||
const boost::program_options::variables_map& m_conf;
|
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
|
#endif
|
130
src/tools.c
130
src/tools.c
|
@ -1,65 +1,65 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2012-2016 Naim A.
|
* Copyright © 2012-2017 Naim A.
|
||||||
*
|
*
|
||||||
* This file is part of UDPT.
|
* This file is part of UDPT.
|
||||||
*
|
*
|
||||||
* UDPT is free software: you can redistribute it and/or modify
|
* UDPT is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* UDPT is distributed in the hope that it will be useful,
|
* UDPT is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
|
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
#include "multiplatform.h"
|
#include "multiplatform.h"
|
||||||
|
|
||||||
void m_byteswap (void *dest, void *src, int sz)
|
void m_byteswap (void *dest, void *src, int sz)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
for (i = 0;i < sz;i++)
|
for (i = 0;i < sz;i++)
|
||||||
{
|
{
|
||||||
((char*)dest)[i] = ((char*)src)[(sz - 1) - i];
|
((char*)dest)[i] = ((char*)src)[(sz - 1) - i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t m_hton16(uint16_t n)
|
uint16_t m_hton16(uint16_t n)
|
||||||
{
|
{
|
||||||
uint16_t r;
|
uint16_t r;
|
||||||
m_byteswap (&r, &n, 2);
|
m_byteswap (&r, &n, 2);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t m_hton64 (uint64_t n)
|
uint64_t m_hton64 (uint64_t n)
|
||||||
{
|
{
|
||||||
uint64_t r;
|
uint64_t r;
|
||||||
m_byteswap (&r, &n, 8);
|
m_byteswap (&r, &n, 8);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t m_hton32 (uint32_t n)
|
uint32_t m_hton32 (uint32_t n)
|
||||||
{
|
{
|
||||||
uint64_t r;
|
uint64_t r;
|
||||||
m_byteswap (&r, &n, 4);
|
m_byteswap (&r, &n, 4);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static const char hexadecimal[] = "0123456789abcdef";
|
static const char hexadecimal[] = "0123456789abcdef";
|
||||||
|
|
||||||
void to_hex_str (const uint8_t *hash, char *data)
|
void to_hex_str (const uint8_t *hash, char *data)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
for (i = 0;i < 20;i++)
|
for (i = 0;i < 20;i++)
|
||||||
{
|
{
|
||||||
data[i * 2] = hexadecimal[hash[i] / 16];
|
data[i * 2] = hexadecimal[hash[i] / 16];
|
||||||
data[i * 2 + 1] = hexadecimal[hash[i] % 16];
|
data[i * 2 + 1] = hexadecimal[hash[i] % 16];
|
||||||
}
|
}
|
||||||
data[40] = '\0';
|
data[40] = '\0';
|
||||||
}
|
}
|
||||||
|
|
100
src/tools.h
100
src/tools.h
|
@ -1,50 +1,50 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2012-2016 Naim A.
|
* Copyright © 2012-2017 Naim A.
|
||||||
*
|
*
|
||||||
* This file is part of UDPT.
|
* This file is part of UDPT.
|
||||||
*
|
*
|
||||||
* UDPT is free software: you can redistribute it and/or modify
|
* UDPT is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* UDPT is distributed in the hope that it will be useful,
|
* UDPT is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
|
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef TOOLS_H_
|
#ifndef TOOLS_H_
|
||||||
#define TOOLS_H_
|
#define TOOLS_H_
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Swaps Bytes:
|
* Swaps Bytes:
|
||||||
* example (htons):
|
* example (htons):
|
||||||
* short a = 1234;
|
* short a = 1234;
|
||||||
* short b;
|
* short b;
|
||||||
* m_byteswap (&b, &a, sizeof(a));
|
* m_byteswap (&b, &a, sizeof(a));
|
||||||
*/
|
*/
|
||||||
void m_byteswap (void *dest, void *src, int sz);
|
void m_byteswap (void *dest, void *src, int sz);
|
||||||
|
|
||||||
uint16_t m_hton16(uint16_t n);
|
uint16_t m_hton16(uint16_t n);
|
||||||
|
|
||||||
uint32_t m_hton32 (uint32_t n);
|
uint32_t m_hton32 (uint32_t n);
|
||||||
|
|
||||||
uint64_t m_hton64 (uint64_t n);
|
uint64_t m_hton64 (uint64_t n);
|
||||||
|
|
||||||
void to_hex_str (const uint8_t *hash, char *data);
|
void to_hex_str (const uint8_t *hash, char *data);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* TOOLS_H_ */
|
#endif /* TOOLS_H_ */
|
||||||
|
|
160
src/tracker.cpp
160
src/tracker.cpp
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2012-2016 Naim A.
|
* Copyright © 2012-2017 Naim A.
|
||||||
*
|
*
|
||||||
* This file is part of UDPT.
|
* This file is part of UDPT.
|
||||||
*
|
*
|
||||||
|
@ -16,52 +16,60 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
|
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
#include "tracker.hpp"
|
#include "tracker.hpp"
|
||||||
|
#include "logging.hpp"
|
||||||
|
|
||||||
namespace UDPT
|
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()
|
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()
|
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();
|
m_udpTracker->stop();
|
||||||
wait();
|
|
||||||
|
|
||||||
|
// cause other components to destruct.
|
||||||
m_apiSrv = nullptr;
|
m_apiSrv = nullptr;
|
||||||
m_webApp = nullptr;
|
m_webApp = nullptr;
|
||||||
m_udpTracker = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tracker::wait()
|
void Tracker::wait()
|
||||||
{
|
{
|
||||||
m_udpTracker->wait();
|
m_udpTracker->wait();
|
||||||
BOOST_LOG_SEV(m_logger, boost::log::trivial::info) << "Tracker terminated.";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tracker::start(const boost::program_options::variables_map& conf)
|
void Tracker::start(const boost::program_options::variables_map& conf)
|
||||||
{
|
{
|
||||||
BOOST_LOG_SEV(m_logger, boost::log::trivial::debug) << "Starting Tracker...";
|
setupLogging(conf);
|
||||||
m_udpTracker = std::shared_ptr<UDPTracker>(new UDPTracker(conf));
|
LOG_INFO("core", "Initializing...");
|
||||||
|
|
||||||
|
m_udpTracker = std::shared_ptr<UDPTracker>(new UDPTracker(conf));
|
||||||
|
|
||||||
if (conf["apiserver.enable"].as<bool>())
|
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_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 = std::shared_ptr<UDPT::Server::WebApp>(new UDPT::Server::WebApp(m_apiSrv, m_udpTracker->m_conn.get(), conf));
|
||||||
m_webApp->deploy();
|
m_webApp->deploy();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_udpTracker->start();
|
m_udpTracker->start();
|
||||||
BOOST_LOG_SEV(m_logger, boost::log::trivial::info) << "Tracker started.";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Tracker& Tracker::getInstance()
|
Tracker& Tracker::getInstance()
|
||||||
|
@ -71,75 +79,73 @@ namespace UDPT
|
||||||
return s_tracker;
|
return s_tracker;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::program_options::options_description Tracker::getConfigOptions()
|
boost::program_options::options_description Tracker::getConfigOptions()
|
||||||
{
|
{
|
||||||
boost::program_options::options_description configOptions("Configuration options");
|
boost::program_options::options_description configOptions("Configuration options");
|
||||||
configOptions.add_options()
|
configOptions.add_options()
|
||||||
("db.driver", boost::program_options::value<std::string>()->default_value("sqlite3"), "database driver to use")
|
("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")
|
("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.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.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.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_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.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.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.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.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.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.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.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.level", boost::program_options::value<std::string>()->default_value("warning"), "log level (fatal/error/warning/info/debug)")
|
||||||
|
|
||||||
#ifdef linux
|
#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
|
#endif
|
||||||
#ifdef WIN32
|
#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
|
#endif
|
||||||
;
|
;
|
||||||
|
|
||||||
return configOptions;
|
return configOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tracker::setupLogging(const boost::program_options::variables_map& config, boost::log::sources::severity_channel_logger_mt<>& logger)
|
void Tracker::setupLogging(const boost::program_options::variables_map& va_map) {
|
||||||
{
|
Logging::Logger& logger = Logging::Logger::getLogger();
|
||||||
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);
|
|
||||||
|
|
||||||
std::string severity = config["logging.level"].as<std::string>();
|
logger.addStream(&std::cerr, Logging::Severity::FATAL);
|
||||||
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";
|
|
||||||
}
|
|
||||||
|
|
||||||
loggingCore->set_filter(
|
Logging::Severity severity;
|
||||||
boost::log::trivial::severity >= severityVal
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2012-2016 Naim A.
|
* Copyright © 2012-2017 Naim A.
|
||||||
*
|
*
|
||||||
* This file is part of UDPT.
|
* This file is part of UDPT.
|
||||||
*
|
*
|
||||||
|
@ -21,16 +21,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <boost/program_options.hpp>
|
#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/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 "multiplatform.h"
|
||||||
#include "udpTracker.hpp"
|
#include "udpTracker.hpp"
|
||||||
|
@ -51,18 +42,18 @@ namespace UDPT
|
||||||
|
|
||||||
void wait();
|
void wait();
|
||||||
|
|
||||||
static Tracker& getInstance();
|
static Tracker& getInstance();
|
||||||
|
|
||||||
static boost::program_options::options_description getConfigOptions();
|
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);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<UDPT::UDPTracker> m_udpTracker;
|
std::shared_ptr<UDPT::UDPTracker> m_udpTracker;
|
||||||
std::shared_ptr<UDPT::Server::HTTPServer> m_apiSrv;
|
std::shared_ptr<UDPT::Server::HTTPServer> m_apiSrv;
|
||||||
std::shared_ptr<UDPT::Server::WebApp> m_webApp;
|
std::shared_ptr<UDPT::Server::WebApp> m_webApp;
|
||||||
boost::log::sources::severity_channel_logger_mt<> m_logger;
|
|
||||||
|
|
||||||
Tracker();
|
Tracker();
|
||||||
|
|
||||||
|
void setupLogging(const boost::program_options::variables_map& va_map);
|
||||||
|
std::ostream *m_logStream;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,477 +1,450 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2012-2016 Naim A.
|
* Copyright © 2012-2017 Naim A.
|
||||||
*
|
*
|
||||||
* This file is part of UDPT.
|
* This file is part of UDPT.
|
||||||
*
|
*
|
||||||
* UDPT is free software: you can redistribute it and/or modify
|
* UDPT is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* UDPT is distributed in the hope that it will be useful,
|
* UDPT is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
|
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "udpTracker.hpp"
|
#include "udpTracker.hpp"
|
||||||
|
#include "logging.hpp"
|
||||||
|
|
||||||
using namespace UDPT::Data;
|
|
||||||
|
using namespace UDPT::Data;
|
||||||
#define UDP_BUFFER_SIZE 2048
|
|
||||||
|
#define UDP_BUFFER_SIZE 2048
|
||||||
namespace UDPT
|
|
||||||
{
|
namespace UDPT
|
||||||
UDPTracker::UDPTracker(const boost::program_options::variables_map& conf) : m_conf(conf), m_logger(boost::log::keywords::channel = "UDPTracker")
|
{
|
||||||
{
|
UDPTracker::UDPTracker(const boost::program_options::variables_map& conf) : m_conf(conf) {
|
||||||
this->m_allowRemotes = conf["tracker.allow_remotes"].as<bool>();
|
this->m_allowRemotes = conf["tracker.allow_remotes"].as<bool>();
|
||||||
this->m_allowIANA_IPs = conf["tracker.allow_iana_ips"].as<bool>();
|
this->m_allowIANA_IPs = conf["tracker.allow_iana_ips"].as<bool>();
|
||||||
this->m_isDynamic = conf["tracker.is_dynamic"].as<bool>();
|
this->m_isDynamic = conf["tracker.is_dynamic"].as<bool>();
|
||||||
|
|
||||||
this->m_announceInterval = conf["tracker.announce_interval"].as<unsigned>();
|
this->m_announceInterval = conf["tracker.announce_interval"].as<unsigned>();
|
||||||
this->m_cleanupInterval = conf["tracker.cleanup_interval"].as<unsigned>();
|
this->m_cleanupInterval = conf["tracker.cleanup_interval"].as<unsigned>();
|
||||||
this->m_port = conf["tracker.port"].as<unsigned short>();
|
this->m_port = conf["tracker.port"].as<unsigned short>();
|
||||||
this->m_threadCount = conf["tracker.threads"].as<unsigned>() + 1;
|
this->m_threadCount = conf["tracker.threads"].as<unsigned>() + 1;
|
||||||
|
|
||||||
std::list<SOCKADDR_IN> addrs;
|
this->m_localEndpoint.sin_family = AF_INET;
|
||||||
|
this->m_localEndpoint.sin_port = m_hton16(m_port);
|
||||||
if (addrs.empty())
|
this->m_localEndpoint.sin_addr.s_addr = 0L;
|
||||||
{
|
|
||||||
SOCKADDR_IN sa;
|
this->m_conn = std::shared_ptr<DatabaseDriver>(new Data::SQLite3Driver(m_conf, this->m_isDynamic));
|
||||||
sa.sin_port = m_hton16(m_port);
|
}
|
||||||
sa.sin_addr.s_addr = 0L;
|
|
||||||
addrs.push_back(sa);
|
UDPTracker::~UDPTracker() {
|
||||||
}
|
}
|
||||||
|
|
||||||
this->m_localEndpoint = addrs.front();
|
void UDPTracker::start() {
|
||||||
|
SOCKET sock;
|
||||||
this->m_conn = std::shared_ptr<DatabaseDriver>(new Data::SQLite3Driver(m_conf, this->m_isDynamic));
|
int r, // saves results
|
||||||
}
|
i, // loop index
|
||||||
|
yup; // just to set TRUE
|
||||||
UDPTracker::~UDPTracker()
|
std::string dbname;// saves the Database name.
|
||||||
{
|
|
||||||
stop();
|
sock = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
}
|
if (sock == INVALID_SOCKET)
|
||||||
|
{
|
||||||
void UDPTracker::start()
|
LOG_FATAL("udp-tracker", "Failed to create socket. error=" << errno);
|
||||||
{
|
throw UDPT::UDPTException("Failed to create socket");
|
||||||
SOCKET sock;
|
}
|
||||||
int r, // saves results
|
|
||||||
i, // loop index
|
yup = 1;
|
||||||
yup; // just to set TRUE
|
::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&yup, 1);
|
||||||
std::string dbname;// saves the Database name.
|
|
||||||
|
{
|
||||||
sock = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
// don't block recvfrom for too long.
|
||||||
if (sock == INVALID_SOCKET)
|
#if defined(linux)
|
||||||
{
|
timeval timeout = { 0 };
|
||||||
throw UDPT::UDPTException("Failed to create socket");
|
timeout.tv_sec = 5;
|
||||||
}
|
#elif defined(WIN32)
|
||||||
|
DWORD timeout = 5000;
|
||||||
yup = 1;
|
#else
|
||||||
::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&yup, 1);
|
#error Unsupported OS.
|
||||||
|
#endif
|
||||||
{
|
::setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<const char*>(&timeout), sizeof(timeout));
|
||||||
// don't block recvfrom for too long.
|
}
|
||||||
#if defined(linux)
|
|
||||||
timeval timeout = { 0 };
|
this->m_localEndpoint.sin_family = AF_INET;
|
||||||
timeout.tv_sec = 5;
|
r = ::bind(sock, reinterpret_cast<SOCKADDR*>(&this->m_localEndpoint), sizeof(SOCKADDR_IN));
|
||||||
#elif defined(WIN32)
|
|
||||||
DWORD timeout = 5000;
|
if (r == SOCKET_ERROR)
|
||||||
#else
|
{
|
||||||
#error Unsupported OS.
|
LOG_FATAL("udp-tracker", "Failed to bind socket. error=" << errno);
|
||||||
#endif
|
|
||||||
::setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<const char*>(&timeout), sizeof(timeout));
|
#ifdef WIN32
|
||||||
}
|
::closesocket(sock);
|
||||||
|
#elif defined (linux)
|
||||||
this->m_localEndpoint.sin_family = AF_INET;
|
::close(sock);
|
||||||
r = ::bind(sock, reinterpret_cast<SOCKADDR*>(&this->m_localEndpoint), sizeof(SOCKADDR_IN));
|
#endif
|
||||||
|
throw UDPT::UDPTException("Failed to bind socket.");
|
||||||
if (r == SOCKET_ERROR)
|
}
|
||||||
{
|
|
||||||
#ifdef WIN32
|
this->m_sock = sock;
|
||||||
::closesocket(sock);
|
|
||||||
#elif defined (linux)
|
LOG_INFO("udp-tracker", "Tracker bound to " << inet_ntoa(this->m_localEndpoint.sin_addr));
|
||||||
::close(sock);
|
|
||||||
#endif
|
// create maintainer thread.
|
||||||
throw UDPT::UDPTException("Failed to bind socket.");
|
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;
|
||||||
char buff[INET_ADDRSTRLEN];
|
for (i = 1;i < this->m_threadCount; i++)
|
||||||
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);
|
{
|
||||||
}
|
m_threads.push_back(std::thread(UDPTracker::_thread_start, this));
|
||||||
|
LOG_INFO("udp-tracker", "Started worker thread @" << m_threads.back().get_id());
|
||||||
this->m_sock = sock;
|
}
|
||||||
|
}
|
||||||
// create maintainer thread.
|
|
||||||
|
void UDPTracker::stop() {
|
||||||
m_threads.push_back(boost::thread(UDPTracker::_maintainance_start, this));
|
// tell workers to stop running...
|
||||||
|
m_shouldRun = false;
|
||||||
for (i = 1;i < this->m_threadCount; i++)
|
|
||||||
{
|
// stop maintenance thread's sleep...
|
||||||
m_threads.push_back(boost::thread(UDPTracker::_thread_start, this));
|
m_maintenanceCondition.notify_one();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
void UDPTracker::stop()
|
* @brief blocks until all threads die.
|
||||||
{
|
* @note This method should be called only once, preferably by the main thread.
|
||||||
#ifdef linux
|
* **/
|
||||||
::close(m_sock);
|
void UDPTracker::wait() {
|
||||||
#elif defined (WIN32)
|
LOG_INFO("udp-tracker", "Waiting for threads to terminate...");
|
||||||
::closesocket(m_sock);
|
|
||||||
#endif
|
for (std::vector<std::thread>::iterator it = m_threads.begin(); it != m_threads.end(); ++it)
|
||||||
|
{
|
||||||
BOOST_LOG_SEV(m_logger, boost::log::trivial::warning) << "Interrupting workers...";
|
it->join();
|
||||||
for (std::vector<boost::thread>::iterator it = m_threads.begin(); it != m_threads.end(); ++it)
|
}
|
||||||
{
|
|
||||||
it->interrupt();
|
LOG_INFO("udp-tracker", "UDP Tracker terminated");
|
||||||
}
|
|
||||||
|
#ifdef linux
|
||||||
wait();
|
::close(m_sock);
|
||||||
}
|
#elif defined (WIN32)
|
||||||
|
::closesocket(m_sock);
|
||||||
void UDPTracker::wait()
|
#endif
|
||||||
{
|
}
|
||||||
BOOST_LOG_SEV(m_logger, boost::log::trivial::warning) << "Waiting for threads to terminate...";
|
|
||||||
|
int UDPTracker::sendError(UDPTracker* usi, SOCKADDR_IN* remote, uint32_t transactionID, const std::string &msg) {
|
||||||
for (std::vector<boost::thread>::iterator it = m_threads.begin(); it != m_threads.end(); ++it)
|
struct udp_error_response error;
|
||||||
{
|
int msg_sz, // message size to send.
|
||||||
it->join();
|
i; // copy loop
|
||||||
}
|
char buff [1024]; // more than reasonable message size...
|
||||||
}
|
|
||||||
|
error.action = m_hton32 (3);
|
||||||
int UDPTracker::sendError(UDPTracker* usi, SOCKADDR_IN* remote, uint32_t transactionID, const std::string &msg)
|
error.transaction_id = transactionID;
|
||||||
{
|
error.message = (char*)msg.c_str();
|
||||||
struct udp_error_response error;
|
|
||||||
int msg_sz, // message size to send.
|
msg_sz = 4 + 4 + 1 + msg.length();
|
||||||
i; // copy loop
|
|
||||||
char buff [1024]; // more than reasonable message size...
|
// test against overflow message. resolves issue 4.
|
||||||
|
if (msg_sz > 1024)
|
||||||
error.action = m_hton32 (3);
|
return -1;
|
||||||
error.transaction_id = transactionID;
|
|
||||||
error.message = (char*)msg.c_str();
|
::memcpy(buff, &error, 8);
|
||||||
|
for (i = 8;i <= msg_sz;i++)
|
||||||
msg_sz = 4 + 4 + 1 + msg.length();
|
{
|
||||||
|
buff[i] = msg[i - 8];
|
||||||
// test against overflow message. resolves issue 4.
|
}
|
||||||
if (msg_sz > 1024)
|
|
||||||
return -1;
|
::sendto(usi->m_sock, buff, msg_sz, 0, reinterpret_cast<SOCKADDR*>(remote), sizeof(*remote));
|
||||||
|
|
||||||
::memcpy(buff, &error, 8);
|
LOG_DEBUG("udp-tracker", "Error sent to " << inet_ntoa(remote->sin_addr) << ", '" << msg << "' (len=" << msg_sz << ")");
|
||||||
for (i = 8;i <= msg_sz;i++)
|
|
||||||
{
|
return 0;
|
||||||
buff[i] = msg[i - 8];
|
}
|
||||||
}
|
|
||||||
|
int UDPTracker::handleConnection(UDPTracker *usi, SOCKADDR_IN *remote, char *data) {
|
||||||
::sendto(usi->m_sock, buff, msg_sz, 0, reinterpret_cast<SOCKADDR*>(remote), sizeof(*remote));
|
ConnectionRequest *req = reinterpret_cast<ConnectionRequest*>(data);
|
||||||
|
ConnectionResponse resp;
|
||||||
return 0;
|
|
||||||
}
|
resp.action = m_hton32(0);
|
||||||
|
resp.transaction_id = req->transaction_id;
|
||||||
int UDPTracker::handleConnection(UDPTracker *usi, SOCKADDR_IN *remote, char *data)
|
|
||||||
{
|
if (!usi->m_conn->genConnectionId(&resp.connection_id,
|
||||||
ConnectionRequest *req = reinterpret_cast<ConnectionRequest*>(data);
|
m_hton32(remote->sin_addr.s_addr),
|
||||||
ConnectionResponse resp;
|
m_hton16(remote->sin_port)))
|
||||||
|
{
|
||||||
resp.action = m_hton32(0);
|
return 1;
|
||||||
resp.transaction_id = req->transaction_id;
|
}
|
||||||
|
|
||||||
if (!usi->m_conn->genConnectionId(&resp.connection_id,
|
::sendto(usi->m_sock, (char*)&resp, sizeof(ConnectionResponse), 0, (SOCKADDR*)remote, sizeof(SOCKADDR_IN));
|
||||||
m_hton32(remote->sin_addr.s_addr),
|
|
||||||
m_hton16(remote->sin_port)))
|
return 0;
|
||||||
{
|
}
|
||||||
return 1;
|
|
||||||
}
|
int UDPTracker::handleAnnounce(UDPTracker *usi, SOCKADDR_IN *remote, char *data) {
|
||||||
|
AnnounceRequest *req;
|
||||||
::sendto(usi->m_sock, (char*)&resp, sizeof(ConnectionResponse), 0, (SOCKADDR*)remote, sizeof(SOCKADDR_IN));
|
AnnounceResponse *resp;
|
||||||
|
int q, // peer counts
|
||||||
{
|
bSize, // message size
|
||||||
char buffer[INET_ADDRSTRLEN];
|
i; // loop index
|
||||||
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;
|
DatabaseDriver::TorrentEntry tE;
|
||||||
}
|
|
||||||
|
uint8_t buff[1028]; // Reasonable buffer size. (header+168 peers)
|
||||||
|
|
||||||
return 0;
|
req = (AnnounceRequest*)data;
|
||||||
}
|
|
||||||
|
if (!usi->m_conn->verifyConnectionId(req->connection_id,
|
||||||
int UDPTracker::handleAnnounce(UDPTracker *usi, SOCKADDR_IN *remote, char *data)
|
m_hton32(remote->sin_addr.s_addr),
|
||||||
{
|
m_hton16(remote->sin_port)))
|
||||||
AnnounceRequest *req;
|
{
|
||||||
AnnounceResponse *resp;
|
return 1;
|
||||||
int q, // peer counts
|
}
|
||||||
bSize, // message size
|
|
||||||
i; // loop index
|
// change byte order:
|
||||||
DatabaseDriver::TorrentEntry tE;
|
req->port = m_hton16(req->port);
|
||||||
|
req->ip_address = m_hton32(req->ip_address);
|
||||||
uint8_t buff[1028]; // Reasonable buffer size. (header+168 peers)
|
req->downloaded = m_hton64(req->downloaded);
|
||||||
|
req->event = m_hton32(req->event); // doesn't really matter for this tracker
|
||||||
req = (AnnounceRequest*)data;
|
req->uploaded = m_hton64(req->uploaded);
|
||||||
|
req->num_want = m_hton32(req->num_want);
|
||||||
if (!usi->m_conn->verifyConnectionId(req->connection_id,
|
req->left = m_hton64(req->left);
|
||||||
m_hton32(remote->sin_addr.s_addr),
|
|
||||||
m_hton16(remote->sin_port)))
|
if (!usi->m_allowRemotes && req->ip_address != 0)
|
||||||
{
|
{
|
||||||
return 1;
|
UDPTracker::sendError(usi, remote, req->transaction_id, "Tracker doesn't allow remote IP's; Request ignored.");
|
||||||
}
|
return 0;
|
||||||
|
}
|
||||||
// change byte order:
|
|
||||||
req->port = m_hton16(req->port);
|
if (!usi->m_conn->isTorrentAllowed(req->info_hash))
|
||||||
req->ip_address = m_hton32(req->ip_address);
|
{
|
||||||
req->downloaded = m_hton64(req->downloaded);
|
UDPTracker::sendError(usi, remote, req->transaction_id, "info_hash not registered.");
|
||||||
req->event = m_hton32(req->event); // doesn't really matter for this tracker
|
return 0;
|
||||||
req->uploaded = m_hton64(req->uploaded);
|
}
|
||||||
req->num_want = m_hton32(req->num_want);
|
|
||||||
req->left = m_hton64(req->left);
|
// load peers
|
||||||
|
q = 30;
|
||||||
if (!usi->m_allowRemotes && req->ip_address != 0)
|
if (req->num_want >= 1)
|
||||||
{
|
q = std::min<int>(q, req->num_want);
|
||||||
UDPTracker::sendError(usi, remote, req->transaction_id, "Tracker doesn't allow remote IP's; Request ignored.");
|
|
||||||
return 0;
|
DatabaseDriver::TrackerEvents event;
|
||||||
}
|
|
||||||
|
{
|
||||||
if (!usi->m_conn->isTorrentAllowed(req->info_hash))
|
std::shared_ptr<DatabaseDriver::PeerEntry> peersSptr = std::shared_ptr<DatabaseDriver::PeerEntry>(new DatabaseDriver::PeerEntry[q]);
|
||||||
{
|
DatabaseDriver::PeerEntry *peers = peersSptr.get();
|
||||||
UDPTracker::sendError(usi, remote, req->transaction_id, "info_hash not registered.");
|
|
||||||
return 0;
|
switch (req->event)
|
||||||
}
|
{
|
||||||
|
case 1:
|
||||||
// load peers
|
event = DatabaseDriver::EVENT_COMPLETE;
|
||||||
q = 30;
|
break;
|
||||||
if (req->num_want >= 1)
|
case 2:
|
||||||
q = std::min<int>(q, req->num_want);
|
event = DatabaseDriver::EVENT_START;
|
||||||
|
break;
|
||||||
DatabaseDriver::TrackerEvents event;
|
case 3:
|
||||||
|
event = DatabaseDriver::EVENT_STOP;
|
||||||
{
|
break;
|
||||||
std::shared_ptr<DatabaseDriver::PeerEntry> peersSptr = std::shared_ptr<DatabaseDriver::PeerEntry>(new DatabaseDriver::PeerEntry[q]);
|
default:
|
||||||
DatabaseDriver::PeerEntry *peers = peersSptr.get();
|
event = DatabaseDriver::EVENT_UNSPEC;
|
||||||
|
break;
|
||||||
switch (req->event)
|
}
|
||||||
{
|
|
||||||
case 1:
|
if (event == DatabaseDriver::EVENT_STOP)
|
||||||
event = DatabaseDriver::EVENT_COMPLETE;
|
q = 0; // no need for peers when stopping.
|
||||||
break;
|
|
||||||
case 2:
|
if (q > 0)
|
||||||
event = DatabaseDriver::EVENT_START;
|
usi->m_conn->getPeers(req->info_hash, &q, peers);
|
||||||
break;
|
|
||||||
case 3:
|
bSize = 20; // header is 20 bytes
|
||||||
event = DatabaseDriver::EVENT_STOP;
|
bSize += (6 * q); // + 6 bytes per peer.
|
||||||
break;
|
|
||||||
default:
|
tE.info_hash = req->info_hash;
|
||||||
event = DatabaseDriver::EVENT_UNSPEC;
|
usi->m_conn->getTorrentInfo(&tE);
|
||||||
break;
|
|
||||||
}
|
resp = (AnnounceResponse*)buff;
|
||||||
|
resp->action = m_hton32(1);
|
||||||
if (event == DatabaseDriver::EVENT_STOP)
|
resp->interval = m_hton32(usi->m_announceInterval);
|
||||||
q = 0; // no need for peers when stopping.
|
resp->leechers = m_hton32(tE.leechers);
|
||||||
|
resp->seeders = m_hton32(tE.seeders);
|
||||||
if (q > 0)
|
resp->transaction_id = req->transaction_id;
|
||||||
usi->m_conn->getPeers(req->info_hash, &q, peers);
|
|
||||||
|
for (i = 0; i < q; i++)
|
||||||
bSize = 20; // header is 20 bytes
|
{
|
||||||
bSize += (6 * q); // + 6 bytes per peer.
|
int x = i * 6;
|
||||||
|
// network byte order!!!
|
||||||
tE.info_hash = req->info_hash;
|
|
||||||
usi->m_conn->getTorrentInfo(&tE);
|
// IP
|
||||||
|
buff[20 + x] = ((peers[i].ip & (0xff << 24)) >> 24);
|
||||||
resp = (AnnounceResponse*)buff;
|
buff[21 + x] = ((peers[i].ip & (0xff << 16)) >> 16);
|
||||||
resp->action = m_hton32(1);
|
buff[22 + x] = ((peers[i].ip & (0xff << 8)) >> 8);
|
||||||
resp->interval = m_hton32(usi->m_announceInterval);
|
buff[23 + x] = (peers[i].ip & 0xff);
|
||||||
resp->leechers = m_hton32(tE.leechers);
|
|
||||||
resp->seeders = m_hton32(tE.seeders);
|
// port
|
||||||
resp->transaction_id = req->transaction_id;
|
buff[24 + x] = ((peers[i].port & (0xff << 8)) >> 8);
|
||||||
|
buff[25 + x] = (peers[i].port & 0xff);
|
||||||
for (i = 0; i < q; i++)
|
|
||||||
{
|
}
|
||||||
int x = i * 6;
|
}
|
||||||
// network byte order!!!
|
::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");
|
||||||
// IP
|
|
||||||
buff[20 + x] = ((peers[i].ip & (0xff << 24)) >> 24);
|
// update DB.
|
||||||
buff[21 + x] = ((peers[i].ip & (0xff << 16)) >> 16);
|
uint32_t ip;
|
||||||
buff[22 + x] = ((peers[i].ip & (0xff << 8)) >> 8);
|
if (req->ip_address == 0) // default
|
||||||
buff[23 + x] = (peers[i].ip & 0xff);
|
ip = m_hton32 (remote->sin_addr.s_addr);
|
||||||
|
else
|
||||||
// port
|
ip = req->ip_address;
|
||||||
buff[24 + x] = ((peers[i].port & (0xff << 8)) >> 8);
|
usi->m_conn->updatePeer(req->peer_id, req->info_hash, ip, req->port,
|
||||||
buff[25 + x] = (peers[i].port & 0xff);
|
req->downloaded, req->left, req->uploaded, event);
|
||||||
|
|
||||||
}
|
return 0;
|
||||||
}
|
}
|
||||||
::sendto(usi->m_sock, (char*)buff, bSize, 0, (SOCKADDR*)remote, sizeof(SOCKADDR_IN));
|
|
||||||
|
int UDPTracker::handleScrape(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int len) {
|
||||||
// update DB.
|
ScrapeRequest *sR = reinterpret_cast<ScrapeRequest*>(data);
|
||||||
uint32_t ip;
|
int v, // validation helper
|
||||||
if (req->ip_address == 0) // default
|
c, // torrent counter
|
||||||
ip = m_hton32 (remote->sin_addr.s_addr);
|
i, // loop counter
|
||||||
else
|
j; // loop counter
|
||||||
ip = req->ip_address;
|
uint8_t hash [20];
|
||||||
usi->m_conn->updatePeer(req->peer_id, req->info_hash, ip, req->port,
|
ScrapeResponse *resp;
|
||||||
req->downloaded, req->left, req->uploaded, event);
|
uint8_t buffer [1024]; // up to 74 torrents can be scraped at once (17*74+8) < 1024
|
||||||
|
|
||||||
return 0;
|
// validate request length:
|
||||||
}
|
v = len - 16;
|
||||||
|
if (v < 0 || v % 20 != 0)
|
||||||
int UDPTracker::handleScrape(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int len)
|
{
|
||||||
{
|
UDPTracker::sendError(usi, remote, sR->transaction_id, "Bad scrape request.");
|
||||||
ScrapeRequest *sR = reinterpret_cast<ScrapeRequest*>(data);
|
return 0;
|
||||||
int v, // validation helper
|
}
|
||||||
c, // torrent counter
|
|
||||||
i, // loop counter
|
if (!usi->m_conn->verifyConnectionId(sR->connection_id,
|
||||||
j; // loop counter
|
m_hton32(remote->sin_addr.s_addr),
|
||||||
uint8_t hash [20];
|
m_hton16(remote->sin_port)))
|
||||||
ScrapeResponse *resp;
|
{
|
||||||
uint8_t buffer [1024]; // up to 74 torrents can be scraped at once (17*74+8) < 1024
|
LOG_DEBUG("udp-tracker", "Bad connection id from " << inet_ntoa(remote->sin_addr));
|
||||||
|
return 1;
|
||||||
// validate request length:
|
}
|
||||||
v = len - 16;
|
|
||||||
if (v < 0 || v % 20 != 0)
|
// get torrent count.
|
||||||
{
|
c = v / 20;
|
||||||
UDPTracker::sendError(usi, remote, sR->transaction_id, "Bad scrape request.");
|
|
||||||
return 0;
|
resp = reinterpret_cast<ScrapeResponse*>(buffer);
|
||||||
}
|
resp->action = m_hton32(2);
|
||||||
|
resp->transaction_id = sR->transaction_id;
|
||||||
if (!usi->m_conn->verifyConnectionId(sR->connection_id,
|
|
||||||
m_hton32(remote->sin_addr.s_addr),
|
for (i = 0;i < c;i++)
|
||||||
m_hton16(remote->sin_port)))
|
{
|
||||||
{
|
int32_t *seeders,
|
||||||
return 1;
|
*completed,
|
||||||
}
|
*leechers;
|
||||||
|
|
||||||
// get torrent count.
|
for (j = 0; j < 20;j++)
|
||||||
c = v / 20;
|
hash[j] = data[j + (i*20)+16];
|
||||||
|
|
||||||
resp = reinterpret_cast<ScrapeResponse*>(buffer);
|
seeders = (int32_t*)&buffer[i*12+8];
|
||||||
resp->action = m_hton32(2);
|
completed = (int32_t*)&buffer[i*12+12];
|
||||||
resp->transaction_id = sR->transaction_id;
|
leechers = (int32_t*)&buffer[i*12+16];
|
||||||
|
|
||||||
for (i = 0;i < c;i++)
|
DatabaseDriver::TorrentEntry tE;
|
||||||
{
|
tE.info_hash = hash;
|
||||||
int32_t *seeders,
|
if (!usi->m_conn->getTorrentInfo(&tE))
|
||||||
*completed,
|
{
|
||||||
*leechers;
|
sendError(usi, remote, sR->transaction_id, "Scrape Failed: couldn't retrieve torrent data");
|
||||||
|
return 0;
|
||||||
for (j = 0; j < 20;j++)
|
}
|
||||||
hash[j] = data[j + (i*20)+16];
|
|
||||||
|
*seeders = m_hton32(tE.seeders);
|
||||||
seeders = (int32_t*)&buffer[i*12+8];
|
*completed = m_hton32(tE.completed);
|
||||||
completed = (int32_t*)&buffer[i*12+12];
|
*leechers = m_hton32(tE.leechers);
|
||||||
leechers = (int32_t*)&buffer[i*12+16];
|
}
|
||||||
|
|
||||||
DatabaseDriver::TorrentEntry tE;
|
::sendto(usi->m_sock, reinterpret_cast<const char*>(buffer), sizeof(buffer), 0, reinterpret_cast<SOCKADDR*>(remote), sizeof(SOCKADDR_IN));
|
||||||
tE.info_hash = hash;
|
LOG_DEBUG("udp-tracker", "Scrape request from " << inet_ntoa(remote->sin_addr) << ", Sent " << c << " torrents");
|
||||||
if (!usi->m_conn->getTorrentInfo(&tE))
|
|
||||||
{
|
return 0;
|
||||||
sendError(usi, remote, sR->transaction_id, "Scrape Failed: couldn't retrieve torrent data");
|
}
|
||||||
return 0;
|
|
||||||
}
|
int UDPTracker::isIANAIP(uint32_t ip) {
|
||||||
|
uint8_t x = (ip % 256);
|
||||||
*seeders = m_hton32(tE.seeders);
|
if (x == 0 || x == 10 || x == 127 || x >= 224)
|
||||||
*completed = m_hton32(tE.completed);
|
return 1;
|
||||||
*leechers = m_hton32(tE.leechers);
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
::sendto(usi->m_sock, reinterpret_cast<const char*>(buffer), sizeof(buffer), 0, reinterpret_cast<SOCKADDR*>(remote), sizeof(SOCKADDR_IN));
|
int UDPTracker::resolveRequest(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int r) {
|
||||||
|
ConnectionRequest* cR = reinterpret_cast<ConnectionRequest*>(data);
|
||||||
return 0;
|
uint32_t action;
|
||||||
}
|
|
||||||
|
action = m_hton32(cR->action);
|
||||||
int UDPTracker::isIANAIP(uint32_t ip)
|
|
||||||
{
|
if (!usi->m_allowIANA_IPs)
|
||||||
uint8_t x = (ip % 256);
|
{
|
||||||
if (x == 0 || x == 10 || x == 127 || x >= 224)
|
if (isIANAIP(remote->sin_addr.s_addr))
|
||||||
return 1;
|
{
|
||||||
return 0;
|
LOG_DEBUG("udp-tracker", "Request from IANA reserved IP rejected (" << inet_ntoa(remote->sin_addr) << ")");
|
||||||
}
|
return 0; // Access Denied: IANA reserved IP.
|
||||||
|
}
|
||||||
int UDPTracker::resolveRequest(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int r)
|
}
|
||||||
{
|
|
||||||
ConnectionRequest* cR = reinterpret_cast<ConnectionRequest*>(data);
|
if (action == 0 && r >= 16)
|
||||||
uint32_t action;
|
return UDPTracker::handleConnection(usi, remote, data);
|
||||||
|
else if (action == 1 && r >= 98)
|
||||||
action = m_hton32(cR->action);
|
return UDPTracker::handleAnnounce(usi, remote, data);
|
||||||
|
else if (action == 2)
|
||||||
if (!usi->m_allowIANA_IPs)
|
return UDPTracker::handleScrape(usi, remote, data, r);
|
||||||
{
|
else
|
||||||
if (isIANAIP(remote->sin_addr.s_addr))
|
{
|
||||||
{
|
UDPTracker::sendError(usi, remote, cR->transaction_id, "Tracker couldn't understand Client's request.");
|
||||||
char buffer[INET_ADDRSTRLEN];
|
return -1;
|
||||||
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.
|
}
|
||||||
}
|
|
||||||
}
|
void UDPTracker::_thread_start(UDPTracker *usi) {
|
||||||
|
SOCKADDR_IN remoteAddr;
|
||||||
{
|
char tmpBuff[UDP_BUFFER_SIZE];
|
||||||
|
|
||||||
char buffer[INET_ADDRSTRLEN];
|
#ifdef linux
|
||||||
BOOST_LOG_SEV(usi->m_logger, boost::log::trivial::debug) << "Client request from " << ::inet_ntop(AF_INET, &remote->sin_addr, buffer, sizeof(buffer));
|
socklen_t addrSz;
|
||||||
}
|
#else
|
||||||
|
int addrSz;
|
||||||
if (action == 0 && r >= 16)
|
#endif
|
||||||
return UDPTracker::handleConnection(usi, remote, data);
|
|
||||||
else if (action == 1 && r >= 98)
|
addrSz = sizeof(SOCKADDR_IN);
|
||||||
return UDPTracker::handleAnnounce(usi, remote, data);
|
|
||||||
else if (action == 2)
|
while (usi->m_shouldRun)
|
||||||
return UDPTracker::handleScrape(usi, remote, data, r);
|
{
|
||||||
else
|
// 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);
|
||||||
UDPTracker::sendError(usi, remote, cR->transaction_id, "Tracker couldn't understand Client's request.");
|
if (r <= 0)
|
||||||
return -1;
|
{
|
||||||
}
|
std::this_thread::yield();
|
||||||
|
continue;
|
||||||
return 0;
|
}
|
||||||
}
|
|
||||||
|
UDPTracker::resolveRequest(usi, &remoteAddr, tmpBuff, r);
|
||||||
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() << ".";
|
LOG_INFO("udp-tracker", "worker " << std::this_thread::get_id() << " exited.");
|
||||||
SOCKADDR_IN remoteAddr;
|
}
|
||||||
char tmpBuff[UDP_BUFFER_SIZE];
|
|
||||||
|
void UDPTracker::_maintainance_start(UDPTracker* usi) {
|
||||||
#ifdef linux
|
std::unique_lock<std::mutex> lk (usi->m_maintenanceMutex);
|
||||||
socklen_t addrSz;
|
|
||||||
#else
|
while (true)
|
||||||
int addrSz;
|
{
|
||||||
#endif
|
if (std::cv_status::no_timeout == usi->m_maintenanceCondition.wait_for(lk, std::chrono::seconds(usi->m_cleanupInterval))) {
|
||||||
|
break;
|
||||||
addrSz = sizeof(SOCKADDR_IN);
|
}
|
||||||
|
|
||||||
|
LOG_INFO("udp-tracker", "Maintenance running...");
|
||||||
while (true)
|
usi->m_conn->cleanup();
|
||||||
{
|
}
|
||||||
// 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);
|
lk.unlock();
|
||||||
if (r <= 0)
|
LOG_INFO("udp-tracker", "Maintenance thread " << std::this_thread::get_id() << " existed.");
|
||||||
{
|
}
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,185 +1,188 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2012-2016 Naim A.
|
* Copyright © 2012-2017 Naim A.
|
||||||
*
|
*
|
||||||
* This file is part of UDPT.
|
* This file is part of UDPT.
|
||||||
*
|
*
|
||||||
* UDPT is free software: you can redistribute it and/or modify
|
* UDPT is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* UDPT is distributed in the hope that it will be useful,
|
* UDPT is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
|
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef UDPTRACKER_H_
|
#ifndef UDPTRACKER_H_
|
||||||
#define UDPTRACKER_H_
|
#define UDPTRACKER_H_
|
||||||
|
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
|
||||||
#include <boost/thread.hpp>
|
#include <thread>
|
||||||
#include <boost/program_options.hpp>
|
#include <boost/program_options.hpp>
|
||||||
#include <boost/log/trivial.hpp>
|
#include <mutex>
|
||||||
#include <boost/log/sources/severity_channel_logger.hpp>
|
#include <condition_variable>
|
||||||
|
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
#include "exceptions.h"
|
#include "exceptions.h"
|
||||||
#include "multiplatform.h"
|
#include "multiplatform.h"
|
||||||
#include "db/driver_sqlite.hpp"
|
#include "db/driver_sqlite.hpp"
|
||||||
|
|
||||||
#define UDPT_DYNAMIC (0x01) // Track Any info_hash?
|
#define UDPT_DYNAMIC (0x01) // Track Any info_hash?
|
||||||
#define UDPT_ALLOW_REMOTE_IP (0x02) // Allow client's to send other IPs?
|
#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_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?)
|
#define UDPT_VALIDATE_CLIENT (0x08) // validate client before adding to Database? (check if connection is open?)
|
||||||
|
|
||||||
|
|
||||||
namespace UDPT
|
namespace UDPT
|
||||||
{
|
{
|
||||||
class UDPTracker
|
class UDPTracker
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef struct udp_connection_request
|
typedef struct udp_connection_request
|
||||||
{
|
{
|
||||||
uint64_t connection_id;
|
uint64_t connection_id;
|
||||||
uint32_t action;
|
uint32_t action;
|
||||||
uint32_t transaction_id;
|
uint32_t transaction_id;
|
||||||
} ConnectionRequest;
|
} ConnectionRequest;
|
||||||
|
|
||||||
typedef struct udp_connection_response
|
typedef struct udp_connection_response
|
||||||
{
|
{
|
||||||
uint32_t action;
|
uint32_t action;
|
||||||
uint32_t transaction_id;
|
uint32_t transaction_id;
|
||||||
uint64_t connection_id;
|
uint64_t connection_id;
|
||||||
} ConnectionResponse;
|
} ConnectionResponse;
|
||||||
|
|
||||||
typedef struct udp_announce_request
|
typedef struct udp_announce_request
|
||||||
{
|
{
|
||||||
uint64_t connection_id;
|
uint64_t connection_id;
|
||||||
uint32_t action;
|
uint32_t action;
|
||||||
uint32_t transaction_id;
|
uint32_t transaction_id;
|
||||||
uint8_t info_hash [20];
|
uint8_t info_hash [20];
|
||||||
uint8_t peer_id [20];
|
uint8_t peer_id [20];
|
||||||
uint64_t downloaded;
|
uint64_t downloaded;
|
||||||
uint64_t left;
|
uint64_t left;
|
||||||
uint64_t uploaded;
|
uint64_t uploaded;
|
||||||
uint32_t event;
|
uint32_t event;
|
||||||
uint32_t ip_address;
|
uint32_t ip_address;
|
||||||
uint32_t key;
|
uint32_t key;
|
||||||
int32_t num_want;
|
int32_t num_want;
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
} AnnounceRequest;
|
} AnnounceRequest;
|
||||||
|
|
||||||
typedef struct udp_announce_response
|
typedef struct udp_announce_response
|
||||||
{
|
{
|
||||||
uint32_t action;
|
uint32_t action;
|
||||||
uint32_t transaction_id;
|
uint32_t transaction_id;
|
||||||
uint32_t interval;
|
uint32_t interval;
|
||||||
uint32_t leechers;
|
uint32_t leechers;
|
||||||
uint32_t seeders;
|
uint32_t seeders;
|
||||||
|
|
||||||
uint8_t *peer_list_data;
|
uint8_t *peer_list_data;
|
||||||
} AnnounceResponse;
|
} AnnounceResponse;
|
||||||
|
|
||||||
typedef struct udp_scrape_request
|
typedef struct udp_scrape_request
|
||||||
{
|
{
|
||||||
uint64_t connection_id;
|
uint64_t connection_id;
|
||||||
uint32_t action;
|
uint32_t action;
|
||||||
uint32_t transaction_id;
|
uint32_t transaction_id;
|
||||||
|
|
||||||
uint8_t *torrent_list_data;
|
uint8_t *torrent_list_data;
|
||||||
} ScrapeRequest;
|
} ScrapeRequest;
|
||||||
|
|
||||||
typedef struct udp_scrape_response
|
typedef struct udp_scrape_response
|
||||||
{
|
{
|
||||||
uint32_t action;
|
uint32_t action;
|
||||||
uint32_t transaction_id;
|
uint32_t transaction_id;
|
||||||
|
|
||||||
uint8_t *data;
|
uint8_t *data;
|
||||||
} ScrapeResponse;
|
} ScrapeResponse;
|
||||||
|
|
||||||
typedef struct udp_error_response
|
typedef struct udp_error_response
|
||||||
{
|
{
|
||||||
uint32_t action;
|
uint32_t action;
|
||||||
uint32_t transaction_id;
|
uint32_t transaction_id;
|
||||||
char *message;
|
char *message;
|
||||||
} ErrorResponse;
|
} ErrorResponse;
|
||||||
|
|
||||||
enum StartStatus
|
enum StartStatus
|
||||||
{
|
{
|
||||||
START_OK = 0,
|
START_OK = 0,
|
||||||
START_ESOCKET_FAILED = 1,
|
START_ESOCKET_FAILED = 1,
|
||||||
START_EBIND_FAILED = 2
|
START_EBIND_FAILED = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the UDP Tracker.
|
* Initializes the UDP Tracker.
|
||||||
* @param settings Settings to start server with
|
* @param settings Settings to start server with
|
||||||
*/
|
*/
|
||||||
UDPTracker(const boost::program_options::variables_map& conf);
|
UDPTracker(const boost::program_options::variables_map& conf);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the Initialized instance.
|
* Starts the Initialized instance.
|
||||||
*/
|
*/
|
||||||
void start();
|
void start();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Terminates tracker.
|
* Terminates tracker.
|
||||||
*/
|
*/
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Joins worker threads
|
* Joins worker threads
|
||||||
*/
|
*/
|
||||||
void wait();
|
void wait();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroys resources that were created by constructor
|
* Destroys resources that were created by constructor
|
||||||
* @param usi Instance to destroy.
|
* @param usi Instance to destroy.
|
||||||
*/
|
*/
|
||||||
virtual ~UDPTracker();
|
virtual ~UDPTracker();
|
||||||
|
|
||||||
std::shared_ptr<UDPT::Data::DatabaseDriver> m_conn;
|
std::shared_ptr<UDPT::Data::DatabaseDriver> m_conn;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SOCKET m_sock;
|
SOCKET m_sock;
|
||||||
SOCKADDR_IN m_localEndpoint;
|
SOCKADDR_IN m_localEndpoint;
|
||||||
uint16_t m_port;
|
uint16_t m_port;
|
||||||
uint8_t m_threadCount;
|
uint8_t m_threadCount;
|
||||||
bool m_isDynamic;
|
bool m_isDynamic;
|
||||||
bool m_allowRemotes;
|
bool m_allowRemotes;
|
||||||
bool m_allowIANA_IPs;
|
bool m_allowIANA_IPs;
|
||||||
std::vector<boost::thread> m_threads;
|
std::vector<std::thread> m_threads;
|
||||||
uint32_t m_announceInterval;
|
uint32_t m_announceInterval;
|
||||||
uint32_t m_cleanupInterval;
|
uint32_t m_cleanupInterval;
|
||||||
boost::log::sources::severity_channel_logger_mt<> m_logger;
|
std::atomic_bool m_shouldRun;
|
||||||
|
|
||||||
const boost::program_options::variables_map& m_conf;
|
std::mutex m_maintenanceMutex;
|
||||||
|
std::condition_variable m_maintenanceCondition;
|
||||||
static void _thread_start(UDPTracker *usi);
|
|
||||||
static void _maintainance_start(UDPTracker* usi);
|
const boost::program_options::variables_map& m_conf;
|
||||||
|
|
||||||
static int resolveRequest(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int r);
|
static void _thread_start(UDPTracker *usi);
|
||||||
|
static void _maintainance_start(UDPTracker* usi);
|
||||||
static int handleConnection(UDPTracker *usi, SOCKADDR_IN *remote, char *data);
|
|
||||||
static int handleAnnounce(UDPTracker *usi, SOCKADDR_IN *remote, char *data);
|
static int resolveRequest(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int r);
|
||||||
static int handleScrape(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int len);
|
|
||||||
|
static int handleConnection(UDPTracker *usi, SOCKADDR_IN *remote, char *data);
|
||||||
static int sendError(UDPTracker *, SOCKADDR_IN *remote, uint32_t transId, const std::string &);
|
static int handleAnnounce(UDPTracker *usi, SOCKADDR_IN *remote, char *data);
|
||||||
|
static int handleScrape(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int len);
|
||||||
static int isIANAIP(uint32_t ip);
|
|
||||||
};
|
static int sendError(UDPTracker *, SOCKADDR_IN *remote, uint32_t transId, const std::string &);
|
||||||
};
|
|
||||||
|
static int isIANAIP(uint32_t ip);
|
||||||
#endif /* UDPTRACKER_H_ */
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* UDPTRACKER_H_ */
|
||||||
|
|
55
tests/main.cpp
Normal file
55
tests/main.cpp
Normal 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();
|
||||||
|
}
|
22
vs/UDPT.sln
22
vs/UDPT.sln
|
@ -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
|
|
|
@ -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>
|
|
|
@ -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>
|
|
Loading…
Reference in a new issue