Merge pull request #16 from naim94a/feature/daemon

Feature/daemon
This commit is contained in:
Naim A 2016-01-29 05:15:27 +02:00
commit 965f9d87c3
25 changed files with 572 additions and 1252 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
*.o
*.sublime-*

View file

@ -18,23 +18,22 @@
#
objects = main.o udpTracker.o database.o driver_sqlite.o \
settings.o tools.o httpserver.o webapp.o \
logging.o
tools.o httpserver.o webapp.o logging.o tracker.o
target = udpt
%.o: src/%.c
$(CC) -c -o $@ $< $(CFLAGS)
%.o: src/%.cpp
$(CXX) -c -o $@ $< $(CXXFLAGS)
$(CXX) -g -std=gnu++11 -c -o $@ $< $(CXXFLAGS)
%.o: src/db/%.cpp
$(CXX) -c -o $@ $< $(CXXFLAGS)
$(CXX) -g -std=gnu++11 -c -o $@ $< $(CXXFLAGS)
%.o: src/http/%.cpp
$(CXX) -c -o $@ $< $(CXXFLAGS)
$(CXX) -g -std=gnu++11 -c -o $@ $< $(CXXFLAGS)
all: $(target)
$(target): $(objects)
@echo Linking...
$(CXX) -O3 -o $(target) $(objects) $(LDFLAGS) -lsqlite3 -lpthread
$(CXX) -O3 -o $(target) $(objects) $(LDFLAGS) -lboost_program_options -lsqlite3 -lpthread -lboost_thread -lboost_system
@echo Done.
clean:
@echo Cleaning Up...

20
README
View file

@ -1,20 +0,0 @@
What is UDPT?
The UDPT project is a BitTorrent Tracking software.
It uses the UDP protocol (instead of the HTTP protocol) to track
peers downloading the same software. UDPT was written according
to BEP 15 of the BitTorrent standard.
Who Wrote UDPT, and when?
UDPT's development started on Nevember 20th, 2012.
It was written by Naim A. <naim94a@gmail.com>. Naim wrote this
program for fun in his free time.
Required Software:
UDPT depends on SQLite3 (public-domain, compatible with GPL).
UDPT's source is designed to be platform portable. It is
supported with Windows and with Linux based systems, It should
work with Apple's systems but it might not (no officially supported).
Project page: http://www.github.com/naim94a/udpt

39
README.md Normal file
View file

@ -0,0 +1,39 @@
# UDPT
The UDPT project is a BitTorrent Tracking software.
It uses the UDP protocol (instead of the HTTP protocol) to track
peers downloading the same software. UDPT was written according
to [BEP 15](http://www.bittorrent.org/beps/bep_0015.html) of the BitTorrent standard.
UDPT is designed to run on both Windows and Linux-based platform (It may run on Apple systems too).
### License
UDPT is released under the [GPL](http://www.gnu.org/licenses/gpl-3.0.en.html) license, a copy is included in this repository.
We use [SQLite3](http://www.sqlite.org/) which is public-domain, and [Boost](http://www.boost.org/) which is released under the [boost license](http://www.boost.org/LICENSE_1_0.txt).
### Building
We didn't really work on creating any installer, at the moment you can just run udpt from anywhere on your filesystem.
Building udpt is pretty straightforward, just download the project or clone the repo:
UDPT requires the SQLite3, boost_program_options and boost_thread develpment packages to be installed.
<pre>
$ git clone https://github.com/naim94a/udpt.git
$ cd udpt
$ make
</pre>
And finally:
<pre>
$ ./udpt
</pre>
### Links
* Documentation can be found at our wiki: https://github.com/naim94a/udpt/wiki
* If you have any suggestions or find any bugs, please report them here: https://github.com/naim94a/udpt/issues
* Project Page: http://www.github.com/naim94a/udpt
### Author(s)
UDPT was developed by [Naim A.](http://www.github.com/naim94a) at for fun at his free time.
The development started on November 20th, 2012.

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2012,2013 Naim A.
* Copyright © 2012-2016 Naim A.
*
* This file is part of UDPT.
*
@ -23,9 +23,8 @@ namespace UDPT
{
namespace Data
{
DatabaseDriver::DatabaseDriver(Settings::SettingClass *sc, bool isDynamic)
DatabaseDriver::DatabaseDriver(const boost::program_options::variables_map& conf, bool isDynamic) : m_conf(conf)
{
this->dClass = sc;
this->is_dynamic = isDynamic;
}

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2012,2013 Naim A.
* Copyright © 2012-2016 Naim A.
*
* This file is part of UDPT.
*
@ -20,7 +20,7 @@
#ifndef DATABASE_HPP_
#define DATABASE_HPP_
#include "../settings.hpp"
#include <boost/program_options.hpp>
namespace UDPT
{
@ -35,10 +35,10 @@ namespace UDPT
E_CONNECTION_FAILURE = 2
};
DatabaseException ();
DatabaseException (EType);
EType getErrorType ();
const char* getErrorMessage ();
DatabaseException();
DatabaseException(EType);
EType getErrorType();
const char* getErrorMessage();
private:
EType errorNum;
};
@ -68,34 +68,34 @@ namespace UDPT
* Opens the DB's connection
* @param dClass Settings class ('database' class).
*/
DatabaseDriver (Settings::SettingClass *dClass, 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.
* @param hash The info_hash of the torrent.
* @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.
* @param hash The info_hash to drop.
* @return true if torrent's database was dropped or no longer exists. otherwise false (shouldn't happen - critical)
*/
virtual bool removeTorrent (uint8_t hash[20]);
virtual bool removeTorrent(uint8_t hash[20]);
/**
* Checks if the Database is acting as a dynamic tracker DB.
* @return true if dynamic. otherwise false.
*/
bool isDynamic ();
bool isDynamic();
/**
* Checks if the torrent can be used in the tracker.
* @param info_hash The torrent's info_hash.
* @return true if allowed. otherwise false.
*/
virtual bool isTorrentAllowed (uint8_t info_hash [20]);
virtual bool isTorrentAllowed(uint8_t info_hash [20]);
/**
* Generate a Connection ID for the peer.
@ -104,9 +104,9 @@ namespace UDPT
* @param port The peer's IP (remote port if tracker accepts)
* @return
*/
virtual bool genConnectionId (uint64_t *connectionId, uint32_t ip, uint16_t port);
virtual bool 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.
@ -119,7 +119,7 @@ namespace UDPT
* @param uploaded total bytes uploaded
* @return true on success, false on failure.
*/
virtual bool updatePeer (uint8_t peer_id [20], uint8_t info_hash [20],
virtual bool updatePeer(uint8_t peer_id [20], uint8_t info_hash [20],
uint32_t ip, uint16_t port,
int64_t downloaded, int64_t left, int64_t uploaded,
enum TrackerEvents event);
@ -132,14 +132,14 @@ namespace UDPT
* @param port The TCP port (remote port if tracker accepts)
* @return true on success. false on failure (shouldn't happen - critical)
*/
virtual bool removePeer (uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port);
virtual bool removePeer(uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port);
/**
* Gets stats on a torrent
* @param e TorrentEntry, only this info_hash has to be set
* @return true on success, false on failure.
*/
virtual bool getTorrentInfo (TorrentEntry *e);
virtual bool getTorrentInfo(TorrentEntry *e);
/**
* Gets a list of peers from the database.
@ -148,21 +148,21 @@ namespace UDPT
* @param pe The list of peers. Must be pre-allocated to the size of max_count.
* @return true on success, otherwise false (shouldn't happen).
*/
virtual bool getPeers (uint8_t info_hash [20], int *max_count, PeerEntry *pe);
virtual bool getPeers(uint8_t info_hash [20], int *max_count, PeerEntry *pe);
/**
* Cleanup the database.
* Other actions may be locked when using this depending on the driver.
*/
virtual void cleanup ();
virtual void cleanup();
/**
* Closes the connections, and releases all other resources.
*/
virtual ~DatabaseDriver ();
virtual ~DatabaseDriver();
protected:
Settings::SettingClass *dClass;
const boost::program_options::variables_map& m_conf;
private:
bool is_dynamic;
};

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2012,2013 Naim A.
* Copyright © 2012-2016 Naim A.
*
* This file is part of UDPT.
*
@ -69,13 +69,13 @@ namespace UDPT
return data;
}
SQLite3Driver::SQLite3Driver (Settings::SettingClass *sc, bool isDyn) : DatabaseDriver(sc, isDyn)
SQLite3Driver::SQLite3Driver(const boost::program_options::variables_map& conf, bool isDyn) : DatabaseDriver(conf, isDyn)
{
int r;
bool doSetup;
fstream fCheck;
string filename = sc->get("file");
string filename = m_conf["db.param"].as<std::string>();
fCheck.open(filename.c_str(), ios::binary | ios::in);
if (fCheck.is_open())
@ -99,7 +99,6 @@ namespace UDPT
void SQLite3Driver::doSetup()
{
// cout << "Creating DB..." << endl;
char *eMsg = NULL;
// for quicker stats.
sqlite3_exec(this->db, "CREATE TABLE stats ("
@ -109,13 +108,11 @@ namespace UDPT
"seeders INTEGER DEFAULT 0,"
"last_mod INTEGER DEFAULT 0"
")", NULL, NULL, &eMsg);
// cout << "stats: " << (eMsg == NULL ? "OK" : eMsg) << endl;
// for non-Dynamic trackers
sqlite3_exec(this->db, "CREATE TABLE torrents ("
"info_hash blob(20) UNIQUE,"
"created INTEGER"
")", NULL, NULL, &eMsg);
// cout << "torrents: " << (eMsg == NULL ? "OK" : eMsg) << endl;
}
bool SQLite3Driver::getTorrentInfo(TorrentEntry *e)
@ -209,8 +206,6 @@ namespace UDPT
sql += hash;
sql += "' (peer_id,ip,port,uploaded,downloaded,left,last_seen) VALUES (?,?,?,?,?,?,?)";
// printf("IP->%x::%u\n", pE->ip, pE->port);
sqlite3_prepare(this->db, sql.c_str(), sql.length(), &stmt, NULL);
sqlite3_bind_blob(stmt, 1, (void*)peer_id, 20, NULL);

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2012,2013 Naim A.
* Copyright © 2012-2016 Naim A.
*
* This file is part of UDPT.
*
@ -31,23 +31,23 @@ namespace UDPT
class SQLite3Driver : public DatabaseDriver
{
public:
SQLite3Driver (Settings::SettingClass *sc, bool isDyn = false);
bool addTorrent (uint8_t info_hash[20]);
bool removeTorrent (uint8_t info_hash[20]);
bool genConnectionId (uint64_t *connId, uint32_t ip, uint16_t port);
bool verifyConnectionId (uint64_t connId, uint32_t ip, uint16_t port);
bool updatePeer (uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port, int64_t downloaded, int64_t left, int64_t uploaded, enum TrackerEvents event);
bool removePeer (uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port);
bool getTorrentInfo (TorrentEntry *e);
bool isTorrentAllowed (uint8_t info_hash[20]);
bool getPeers (uint8_t info_hash [20], int *max_count, PeerEntry *pe);
void cleanup ();
SQLite3Driver(const boost::program_options::variables_map& conf, bool isDyn = false);
bool addTorrent(uint8_t info_hash[20]);
bool removeTorrent(uint8_t info_hash[20]);
bool genConnectionId(uint64_t *connId, uint32_t ip, uint16_t port);
bool verifyConnectionId(uint64_t connId, uint32_t ip, uint16_t port);
bool updatePeer(uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port, int64_t downloaded, int64_t left, int64_t uploaded, enum TrackerEvents event);
bool removePeer(uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port);
bool getTorrentInfo(TorrentEntry *e);
bool isTorrentAllowed(uint8_t info_hash[20]);
bool getPeers(uint8_t info_hash [20], int *max_count, PeerEntry *pe);
void cleanup();
~SQLite3Driver ();
virtual ~SQLite3Driver();
private:
sqlite3 *db;
void doSetup ();
void doSetup();
};
};
};

33
src/exceptions.h Normal file
View file

@ -0,0 +1,33 @@
#pragma once
namespace UDPT
{
class UDPTException
{
public:
UDPTException(const char* errorMsg = "", int errorCode = 0) : m_error(errorMsg), m_errorCode(errorCode)
{
}
virtual const char* what() const
{
return m_error;
}
virtual int getErrorCode() const
{
return m_errorCode;
}
virtual ~UDPTException()
{
}
protected:
const char* m_error;
const int m_errorCode;
};
}

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2013 Naim A.
* Copyright © 2013-2016 Naim A.
*
* This file is part of UDPT.
*
@ -23,6 +23,7 @@
#include <cstring>
#include <map>
#include "httpserver.hpp"
#include <boost/program_options.hpp>
using namespace std;
@ -43,26 +44,18 @@ namespace UDPT
this->init(sa, threads);
}
HTTPServer::HTTPServer(Settings *s)
HTTPServer::HTTPServer(const boost::program_options::variables_map& conf)
{
Settings::SettingClass *sc = s->getClass("apiserver");
list<SOCKADDR_IN> localEndpoints;
uint16_t port;
int threads;
port = 6969;
threads = 1;
if (sc != NULL)
{
port = sc->getInt("port", 6969);
threads = sc->getInt("threads", 1);
sc->getIPs("bind", localEndpoints);
}
port = conf["apiserver.port"].as<unsigned short>();
threads = conf["apiserver.threads"].as<unsigned short>();
if (threads <= 0)
threads = 1;
if (localEndpoints.empty())
{
SOCKADDR_IN sa;
@ -85,16 +78,16 @@ namespace UDPT
this->rootNode.callback = NULL;
this->srv = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
this->srv = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (this->srv == INVALID_SOCKET)
{
throw ServerException (1, "Failed to create Socket");
}
r = bind (this->srv, (SOCKADDR*)&localEndpoint, sizeof(localEndpoint));
r = ::bind(this->srv, (SOCKADDR*)&localEndpoint, sizeof(localEndpoint));
if (r == SOCKET_ERROR)
{
throw ServerException (2, "Failed to bind socket");
throw ServerException(2, "Failed to bind socket");
}
this->isRunning = true;
@ -118,7 +111,7 @@ namespace UDPT
doSrv:
try {
HTTPServer::handleConnections (s);
} catch (ServerException &se)
} catch (const ServerException &se)
{
cerr << "SRV ERR #" << se.getErrorCode() << ": " << se.getErrorMsg () << endl;
goto doSrv;
@ -139,13 +132,13 @@ doSrv:
while (server->isRunning)
{
r = listen (server->srv, 50);
r = ::listen(server->srv, 50);
if (r == SOCKET_ERROR)
{
#ifdef WIN32
Sleep (500);
::Sleep(500);
#else
sleep (1);
::sleep(1);
#endif
continue;
}
@ -167,7 +160,7 @@ doSrv:
stringstream stream;
stream << "<html>";
stream << "<head><title>Not Found</title></head>";
stream << "<body><h1>Not Found</h1><div>The server couldn't find the request resource.</div><br /><hr /><div style=\"font-size:small;text-align:center;\">&copy; 2013 Naim A. | <a href=\"http://udpt.googlecode.com/\">The UDPT Project</a></div></body>";
stream << "<body><h1>Not Found</h1><div>The server couldn't find the request resource.</div><br /><hr /><div style=\"font-size:small;text-align:center;\"><a href=\"http://github.com/naim94a/udpt\">UDPT</a></div></body>";
stream << "</html>";
string str = stream.str();
resp.write (str.c_str(), str.length());
@ -183,7 +176,7 @@ doSrv:
stringstream stream;
stream << "<html>";
stream << "<head><title>Internal Server Error</title></head>";
stream << "<body><h1>Internal Server Error</h1><div>An Error Occurred while trying to process your request.</div><br /><hr /><div style=\"font-size:small;text-align:center;\">&copy; 2013 Naim A. | <a href=\"http://udpt.googlecode.com/\">The UDPT Project</a></div></body>";
stream << "<body><h1>Internal Server Error</h1><div>An Error Occurred while trying to process your request.</div><br /><hr /><div style=\"font-size:small;text-align:center;\"><a href=\"http://github.com/naim94a/udpt\">UDPT</a></div></body>";
stream << "</html>";
string str = stream.str();
resp.write (str.c_str(), str.length());

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2013 Naim A.
* Copyright © 2013-2016 Naim A.
*
* This file is part of UDPT.
*
@ -24,8 +24,8 @@
#include <string>
#include <sstream>
#include <list>
#include <boost/program_options.hpp>
#include "../multiplatform.h"
#include "../settings.hpp"
using namespace std;
#define REQUEST_BUFFER_SIZE 2048
@ -131,7 +131,7 @@ namespace UDPT
typedef void (reqCallback)(HTTPServer*,Request*,Response*);
HTTPServer (uint16_t port, int threads);
HTTPServer (Settings *s);
HTTPServer(const boost::program_options::variables_map& conf);
void addApp (list<string> *path, reqCallback *);

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2013 Naim A.
* Copyright © 2013-2016 Naim A.
*
* This file is part of UDPT.
*
@ -97,36 +97,12 @@ namespace UDPT
return true;
}
WebApp::WebApp(HTTPServer *srv, DatabaseDriver *db, Settings *settings)
WebApp::WebApp(std::shared_ptr<HTTPServer> srv, DatabaseDriver *db, const boost::program_options::variables_map& conf) : m_conf(conf), m_server(srv)
{
this->instance = srv;
this->db = db;
this->sc_api = settings->getClass("api");
// TODO: Implement authentication by keys
Settings::SettingClass *apiKeys = settings->getClass("api.keys");
if (apiKeys != NULL)
{
map<string, string>* aK = apiKeys->getMap();
map<string, string>::iterator it, end;
end = aK->end();
for (it = aK->begin();it != end;it++)
{
string key = it->first;
list<uint32_t> ips;
string::size_type strp = 0;
uint32_t ip;
while ((ip = _getNextIPv4(strp, it->second)) != 0)
{
ips.push_back( m_hton32(ip) );
}
this->ip_whitelist.insert(pair<string, list<uint32_t> >(key, ips));
}
}
srv->setData("webapp", this);
m_server->setData("webapp", this);
}
WebApp::~WebApp()
@ -136,29 +112,24 @@ namespace UDPT
void WebApp::deploy()
{
list<string> path;
this->instance->addApp(&path, &WebApp::handleRoot);
m_server->addApp(&path, &WebApp::handleRoot);
path.push_back("api");
this->instance->addApp(&path, &WebApp::handleAPI); // "/api"
m_server->addApp(&path, &WebApp::handleAPI); // "/api"
path.pop_back();
path.push_back("announce");
this->instance->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.
resp->write("<html>"
"<head><title>Powered by UDPT</title></head>"
"<head><title>UDPT Torrent Tracker</title></head>"
"<body>"
"<h2>The UDPT Project</h2>"
"<div style=\"vertical-align:top;\">This tracker is running on UDPT Software.<br />"
"UDPT is a open-source project, freely available for anyone to use. If you would like to obtain a copy of the software, you can get it here: <a href=\"http://code.google.com/p/udpt\">http://code.googe.com/p/udpt</a>."
"<br /><br />If you would like to help the project grow, please donate for our hard work, effort &amp; time: <a class=\"dbtn\" href=\"https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=6735UFXPUM7BG\">"
"<img src=\"data:image/gif;base64,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\" alt=\"Donate via PayPal\" />"
"</a></div>"
"<br /><hr /><div style=\"text-align:center;font-size:small;\">&copy; 2013 Naim A. | Powered by <a href=\"http://code.google.com/p/udpt\">UDPT</a></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>"
"</body>"
"</html>");
}
@ -206,7 +177,7 @@ namespace UDPT
void WebApp::doAddTorrent (HTTPServer::Request *req, HTTPServer::Response *resp)
{
string strHash = req->getParam("hash");
std::string strHash = req->getParam("hash");
if (strHash.length() != 40)
{
resp->write("{\"error\":\"Hash length must be 40 characters.\"}");
@ -237,7 +208,7 @@ namespace UDPT
throw ServerException (0, "IPv4 supported Only.");
}
string key = req->getParam("auth");
std::string key = req->getParam("auth");
if (key.length() <= 0)
throw ServerException (0, "Bad Authentication Key");
@ -252,7 +223,7 @@ namespace UDPT
return;
}
string action = req->getParam("action");
std::string action = req->getParam("action");
if (action == "add")
app->doAddTorrent(req, resp);
else if (action == "remove")

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2013 Naim A.
* Copyright © 2013-2016 Naim A.
*
* This file is part of UDPT.
*
@ -21,10 +21,11 @@
#include "httpserver.hpp"
#include "../db/database.hpp"
#include "../settings.hpp"
#include <stdint.h>
#include <map>
#include <string>
#include <memory>
#include <boost/program_options.hpp>
using namespace std;
using namespace UDPT;
@ -37,15 +38,15 @@ namespace UDPT
class WebApp
{
public:
WebApp (HTTPServer *, DatabaseDriver *, Settings *);
~WebApp ();
WebApp(std::shared_ptr<HTTPServer> , DatabaseDriver *, const boost::program_options::variables_map& conf);
virtual ~WebApp();
void deploy ();
private:
HTTPServer *instance;
std::shared_ptr<HTTPServer> m_server;
UDPT::Data::DatabaseDriver *db;
Settings::SettingClass *sc_api;
const boost::program_options::variables_map& m_conf;
std::map<std::string, list<uint32_t> > ip_whitelist;
static void handleRoot (HTTPServer*,HTTPServer::Request*, HTTPServer::Response*);

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2012,2013 Naim A.
* Copyright © 2012-2016 Naim A.
*
* This file is part of UDPT.
*
@ -26,30 +26,14 @@ using namespace std;
namespace UDPT {
Logger::Logger(Settings *s)
: logfile (&std::cout)
Logger::Logger(const boost::program_options::variables_map& s)
: m_logfile (std::cout)
{
Settings::SettingClass *sc;
string filename = "stdout";
string level = "error";
const std::string& filename = s["logging.filename"].as<std::string>();
const std::string& level = s["logging.level"].as<std::string>();
closeStreamOnDestroy = false;
sc = s->getClass("logging");
if (sc != NULL)
{
string::size_type i;
filename = sc->get("filename");
level = sc->get("level");
for (i = 0;i < level.length(); i++)
{
if (level[i] >= 'A' && level[i] <= 'Z')
level[i] = 'a' + (level[i] - 'A');
}
}
if (level == "debug" || level == "d")
this->loglevel = LL_DEBUG;
else if (level == "warning" || level == "w")
@ -58,35 +42,10 @@ namespace UDPT {
this->loglevel = LL_INFO;
else
this->loglevel = LL_ERROR;
if (filename.compare("stdout") != 0 && filename.length() > 0)
{
fstream fs;
fs.open(filename.c_str(), ios::binary | ios::out | ios::app);
if (!fs.is_open())
{
this->log(LL_ERROR, "Failed to open log file.");
return;
}
this->logfile = &fs;
closeStreamOnDestroy = true;
}
}
Logger::Logger(Settings *s, ostream &os)
: logfile (&os), loglevel (LL_ERROR)
{
closeStreamOnDestroy = false;
}
Logger::~Logger()
{
fstream *f = (fstream*)this->logfile;
f->flush();
if (closeStreamOnDestroy)
{
f->close();
}
}
void Logger::log(enum LogLevel lvl, string msg)
@ -94,7 +53,7 @@ namespace UDPT {
const char letters[] = "EWID";
if (lvl <= this->loglevel)
{
(*logfile) << time (NULL) << ": ("
m_logfile << time (NULL) << ": ("
<< ((char)letters[lvl]) << "): "
<< msg << "\n";
}

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2012,2013 Naim A.
* Copyright © 2012-2016 Naim A.
*
* This file is part of UDPT.
*
@ -20,15 +20,15 @@
#ifndef LOGGING_H_
#define LOGGING_H_
#include "settings.hpp"
#include <string>
#include <iostream>
#include <queue>
#include <time.h>
#include <boost/program_options.hpp>
namespace UDPT {
using namespace std;
class Logger {
class Logger
{
public:
enum LogLevel {
@ -38,19 +38,17 @@ namespace UDPT {
LL_DEBUG = 3
};
Logger (Settings *s);
Logger(const boost::program_options::variables_map& s);
Logger (Settings *s, ostream &os);
virtual ~Logger();
virtual ~Logger ();
void log (enum LogLevel, string msg);
void log(enum LogLevel, std::string msg);
private:
ostream *logfile;
std::ostream& m_logfile;
enum LogLevel loglevel;
bool closeStreamOnDestroy;
static void setStream (Logger *logger, ostream &s);
static void setStream(Logger *logger, std::ostream &s);
};
};

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2012,2013 Naim A.
* Copyright © 2012-2016 Naim A.
*
* This file is part of UDPT.
*
@ -19,230 +19,173 @@
#include <iostream>
#include "logging.h"
#include "multiplatform.h"
#include "udpTracker.hpp"
#include "settings.hpp"
#include "http/httpserver.hpp"
#include "http/webapp.hpp"
#include <cstdlib> // atoi
#include <csignal> // signal
#include <cstring> // strlen
#include <memory>
#include <boost/program_options.hpp>
using namespace std;
using namespace UDPT;
using namespace UDPT::Server;
#include "logging.h"
#include "multiplatform.h"
#include "udpTracker.hpp"
#include "http/httpserver.hpp"
#include "http/webapp.hpp"
#include "tracker.hpp"
Logger *logger;
static struct {
Settings *settings;
UDPTracker *usi;
WebApp *wa;
HTTPServer *httpserver;
} Instance;
UDPT::Logger *logger = NULL;
static void _print_usage ()
static void _signal_handler(int sig)
{
cout << "Usage: udpt [<configuration file>]" << endl;
}
static void _doAPIStart (Settings *settings, WebApp **wa, HTTPServer **srv, DatabaseDriver *drvr)
{
if (settings == NULL)
return;
Settings::SettingClass *sc = settings->getClass("apiserver");
if (sc == NULL)
return; // no settings set!
if (!sc->getBool("enable", false))
switch (sig)
{
cerr << "API Server not enabled." << endl;
return;
}
try {
*srv = Instance.httpserver = new HTTPServer (settings);
*wa = Instance.wa = new WebApp (*srv, drvr, settings);
(*wa)->deploy();
} catch (ServerException &e)
{
cerr << "ServerException #" << e.getErrorCode() << ": " << e.getErrorMsg() << endl;
case SIGTERM:
UDPT::Tracker::getInstance().stop();
break;
case SIGHUP:
// TODO: Reload config.
break;
}
}
/**
* Sets current working directory to executables directory.
*/
static void _setCWD (char *argv0)
#ifdef linux
static void daemonize(const boost::program_options::variables_map& conf)
{
#ifdef WIN32
wchar_t strFileName [MAX_PATH];
DWORD r, i;
r = GetModuleFileNameW(NULL, strFileName, MAX_PATH);
for (i = r;i >= 0;i--)
{
if (strFileName[i] == '\\')
{
strFileName[i] = '\0';
break;
}
}
SetCurrentDirectoryW(strFileName);
if (1 == ::getppid()) return; // already a daemon
int r = ::fork();
if (0 > r) ::exit(-1); // failed to daemonize.
if (0 < r) ::exit(0); // parent exists.
#elif defined(linux)
int len, i;
char *strFN;
if (argv0 != NULL)
{
len = strlen (argv0);
strFN = new char [len + 1];
::umask(0);
::setsid();
for (i = len;i >= 0;i--)
{
if (strFN[i] == '/')
{
strFN = '\0';
break;
}
}
chdir (strFN);
delete [] strFN;
}
// close all fds.
for (int i = ::getdtablesize(); i >=0; --i)
{
::close(i);
}
::chdir(conf["daemon.chdir"].as<std::string>().c_str());
}
#endif
}
/**
* Releases resources before exit.
*/
static void _doCleanup ()
{
delete Instance.wa;
delete Instance.httpserver;
delete Instance.usi;
delete Instance.settings;
delete logger;
memset (&Instance, 0, sizeof(Instance));
logger = NULL;
}
static void _signal_handler (int sig)
{
stringstream ss;
ss << "Signal " << sig << " raised. Terminating...";
logger->log(Logger::LL_INFO, ss.str());
_doCleanup();
}
int main(int argc, char *argv[])
{
Settings *settings;
UDPTracker *usi;
string config_file;
int r;
Tracker& tracker = UDPT::Tracker::getInstance();
#ifdef WIN32
WSADATA wsadata;
WSAStartup(MAKEWORD(2, 2), &wsadata);
::WSAStartup(MAKEWORD(2, 2), &wsadata);
#endif
cout << "UDP Tracker (UDPT) " << VERSION << " (" << PLATFORM << ")" << endl;
cout << "Copyright 2012,2013 Naim Abda <naim94a@gmail.com>\n\tReleased under the GPLv3 License." << endl;
cout << "Build Date: " << __DATE__ << endl << endl;
boost::program_options::options_description commandLine("Command line options");
commandLine.add_options()
("help,h", "produce help message")
("all-help", "displays all help")
("test,t", "test configuration file")
("config,c", boost::program_options::value<std::string>()->default_value("/etc/udpt.conf"), "configuration file to use")
#ifdef linux
("interactive,i", "doesn't start as daemon")
#endif
;
config_file = "udpt.conf";
memset(&Instance, 0, sizeof(Instance));
boost::program_options::options_description configOptions("Configuration options");
configOptions.add_options()
("db.driver", boost::program_options::value<std::string>()->default_value("sqlite3"), "database driver to use")
("db.param", boost::program_options::value<std::string>()->default_value("/var/lib/udpt.db"), "database connection parameters")
("tracker.is_dynamic", boost::program_options::value<bool>()->default_value(true), "Sets if the tracker is dynamic")
("tracker.port", boost::program_options::value<unsigned short>()->default_value(6969), "UDP port to listen on")
("tracker.threads", boost::program_options::value<unsigned>()->default_value(5), "threads to run (UDP only)")
("tracker.allow_remotes", boost::program_options::value<bool>()->default_value(true), "allows clients to report remote IPs")
("tracker.allow_iana_ips", boost::program_options::value<bool>()->default_value(false), "allows IANA reserved IPs to connect (useful for debugging)")
("tracker.announce_interval", boost::program_options::value<unsigned>()->default_value(1800), "announce interval")
("tracker.cleanup_interval", boost::program_options::value<unsigned>()->default_value(120), "sets database cleanup interval")
("apiserver.enable", boost::program_options::value<bool>()->default_value(0), "Enable API server?")
("apiserver.threads", boost::program_options::value<unsigned>()->default_value(1), "threads for API server")
("apiserver.port", boost::program_options::value<unsigned short>()->default_value(6969), "TCP port to listen on")
#ifdef SIGBREAK
signal(SIGBREAK, &_signal_handler);
#endif
#ifdef SIGTERM
signal(SIGTERM, &_signal_handler);
#endif
#ifdef SIGABRT
signal(SIGABRT, &_signal_handler);
#endif
#ifdef SIGINT
signal(SIGINT, &_signal_handler);
#endif
("logging.filename", boost::program_options::value<std::string>()->default_value("stdout"), "file to write logs to")
("logging.level", boost::program_options::value<std::string>()->default_value("warning"), "log level (error/warning/info/debug)")
if (argc <= 1)
#ifdef linux
("daemon.chdir", boost::program_options::value<std::string>()->default_value("/"), "home directory for daemon")
#endif
;
boost::program_options::variables_map var_map;
boost::program_options::store(boost::program_options::parse_command_line(argc, argv, commandLine), var_map);
boost::program_options::notify(var_map);
if (var_map.count("help"))
{
// set current directory when no filename is present.
_setCWD(argv[0]);
_print_usage ();
}
else if (argc >= 2)
{
config_file = argv[1]; // reported in issue #5.
std::cout << "UDP Tracker (UDPT) " << VERSION << " (" << PLATFORM << ")" << std::endl
<< "Copyright 2012-2016 Naim A. <naim94a@gmail.com>" << std::endl
<< "Build Date: " << __DATE__ << std::endl << std::endl;
std::cout << commandLine << std::endl;
return 0;
}
settings = Instance.settings = new Settings (config_file);
if (!settings->load())
if (var_map.count("all-help"))
{
const char strDATABASE[] = "database";
const char strTRACKER[] = "tracker";
const char strAPISRV [] = "apiserver";
// set default settings:
settings->set (strDATABASE, "driver", "sqlite3");
settings->set (strDATABASE, "file", "tracker.db");
settings->set (strTRACKER, "is_dynamic", "0");
settings->set (strTRACKER, "port", "6969"); // UDP PORT
settings->set (strTRACKER, "threads", "5");
settings->set (strTRACKER, "allow_remotes", "1");
settings->set (strTRACKER, "allow_iana_ips", "1");
settings->set (strTRACKER, "announce_interval", "1800");
settings->set (strTRACKER, "cleanup_interval", "120");
settings->set (strAPISRV, "enable", "1");
settings->set (strAPISRV, "threads", "1");
settings->set (strAPISRV, "port", "6969"); // TCP PORT
settings->save();
cerr << "Failed to read from '" << config_file.c_str() << "'. Using default settings." << endl;
std::cout << commandLine << std::endl;
std::cout << configOptions << std::endl;
return 0;
}
logger = new Logger (settings);
usi = Instance.usi = new UDPTracker (settings);
std::string config_filename(var_map["config"].as<std::string>());
bool isTest = (0 != var_map.count("test"));
HTTPServer *apiSrv = NULL;
WebApp *wa = NULL;
r = usi->start();
if (r != UDPTracker::START_OK)
if (var_map.count("config"))
{
cerr << "Error While trying to start server." << endl;
switch (r)
try
{
case UDPTracker::START_ESOCKET_FAILED:
cerr << "Failed to create socket." << endl;
break;
case UDPTracker::START_EBIND_FAILED:
cerr << "Failed to bind socket." << endl;
break;
default:
cerr << "Unknown Error" << endl;
break;
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)
{
std::cout << "Config OK" << std::endl;
return 0;
}
goto cleanup;
}
_doAPIStart(settings, &wa, &apiSrv, usi->conn);
std::shared_ptr<UDPT::Logger> spLogger;
try
{
spLogger = std::shared_ptr<UDPT::Logger>(new UDPT::Logger(var_map));
logger = spLogger.get();
}
catch (const std::exception& ex)
{
std::cerr << "Failed to initialize logger: " << ex.what() << std::endl;
return -1;
}
cout << "Hit Control-C to exit." << endl;
#ifdef linux
if (!var_map.count("interactive"))
{
daemonize(var_map);
}
::signal(SIGHUP, _signal_handler);
::signal(SIGTERM, _signal_handler);
#endif
usi->wait();
cleanup:
cout << endl << "Goodbye." << endl;
tracker.start(var_map);
tracker.wait();
#ifdef WIN32
WSACleanup();
::WSACleanup();
#endif
return 0;

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2012,2013 Naim A.
* Copyright © 2012-2016 Naim A.
*
* This file is part of UDPT.
*
@ -28,7 +28,7 @@
#define linux
#endif
#define VERSION "1.0.0-beta"
#define VERSION "1.0.2-dev"
#ifdef WIN32
#include <winsock2.h>
@ -36,11 +36,13 @@
#elif defined (linux)
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <unistd.h>
#include <netdb.h>
#include <pthread.h>
#include <fcntl.h>
#define SOCKET int
#define INVALID_SOCKET 0

View file

@ -1,451 +0,0 @@
/*
*
* Copyright © 2012,2013 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 "settings.hpp"
#include <string.h> // still primitive - need for strlen()
#include <ctype.h> // need for isspace()
#include <exception>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include "tools.h"
using namespace std;
namespace UDPT
{
Settings::SettingClass* Settings::getClass(const string classname)
{
if (classname == "")
return NULL;
map<string, SettingClass*>::iterator it;
it = this->classes.find(classname);
if (it == this->classes.end())
return NULL;
else
return it->second;
return NULL;
}
Settings::Settings (const string filename)
{
this->filename = filename;
this->classes.clear();
}
static
void _settings_clean_string (char **str)
{
int len,
i,
offset;
len = strlen(*str);
//strip leading whitespaces.
offset = 0;
for (i = 0;i < len;i++)
{
if (isspace(*str[i]) == 0)
break;
offset++;
}
(*str) += offset;
len -= offset;
for (i = len - 1;i >= 0;i--)
{
if (isspace( (*str)[i] ) != 0)
{
(*str)[i] = '\0';
}
else
break;
}
}
void Settings::parseSettings (char *data, int len)
{
char *className, *key, *value;
int i,
cil; // cil = Chars in line.
char c;
className = key = value = NULL;
cil = 0;
for (i = 0;i < len;i++)
{
c = data[i];
if (c == '\n')
{
cil = 0;
continue;
}
if (cil == 0 && (c == ';' || c == '#'))
{
while (i < len)
{
if (data[i] == '\n')
break;
i++;
}
continue;
}
if (isspace(c) != 0 && cil == 0)
{
continue;
}
if (cil == 0 && c == '[')
{
className = (char*)(i + data + 1);
while (i < len)
{
if (data[i] != ']')
{
i++;
continue;
}
data[i] = '\0';
break;
}
continue;
}
if (isgraph(c) != 0 && cil == 0) // must be a key.
{
key = (char*)(i + data);
while (i < len)
{
if (data[i] == '\n')
{
key = NULL;
break;
}
if (data[i] == '=')
{
data[i] = '\0';
value = (char*)(data + i + 1);
while (i < len)
{
if (data[i] == '\n')
{
data[i] = '\0';
_settings_clean_string(&key);
_settings_clean_string(&value);
// printf("KEY: '%s'\tVALUE: '%s'\n", key, value);
// add to settings...
this->set (className, key, value);
cil = 0;
break;
}
i++;
}
break;
}
i++;
}
continue;
}
if (isgraph(c) != 0)
{
cil++;
}
}
}
bool Settings::load()
{
int len;
char *buffer;
fstream cfg;
cfg.open(this->filename.c_str(), ios::in | ios::binary);
if (!cfg.is_open())
return false;
cfg.seekg(0, ios::end);
len = cfg.tellg();
cfg.seekg(0, ios::beg);
buffer = new char [len];
cfg.read(buffer, len);
cfg.close();
this->parseSettings(buffer, len);
delete[] buffer;
return true;
}
bool Settings::save ()
{
SettingClass *sclass;
fstream cfg (this->filename.c_str(), ios::binary | ios::out);
if (!cfg.is_open())
return false;
cfg << "; udpt Settings File - Created Automatically.\n";
map<string, SettingClass*>::iterator it;
for (it = this->classes.begin();it != this->classes.end();it++)
{
sclass = it->second;
cfg << "[" << it->first.c_str() << "]\n";
map<string, string>::iterator rec;
for (rec = sclass->entries.begin();rec != sclass->entries.end();rec++)
{
cfg << rec->first.c_str() << "=" << rec->second.c_str() << "\n";
}
cfg << "\n";
}
cfg.close();
return 0;
}
Settings::~Settings()
{
map<string, SettingClass*>::iterator it;
for (it = this->classes.begin();it != this->classes.end();it++)
{
SettingClass *sc = it->second;
delete sc;
}
this->classes.clear();
}
string Settings::get (const string classN, const string name)
{
SettingClass *c;
c = this->getClass(classN);
if (c == NULL)
return "";
return c->get(name);
}
bool Settings::set (const string classN, const string name, const string value)
{
SettingClass *c;
if (classN == "" || name == "" || value == "")
return false;
c = this->getClass (classN);
if (c == NULL)
{
c = new SettingClass(classN);
this->classes.insert(pair<string, SettingClass*>(classN, c));
}
return c->set (name, value);
}
Settings::SettingClass::SettingClass(const string cn)
{
this->className = cn;
}
string Settings::SettingClass::get (const string& name)
{
if (this->entries.find(name) == this->entries.end())
return "";
return this->entries[name];
}
inline static int _isTrue (string str)
{
int i, // loop index
len; // string's length
if (str == "")
return -1;
len = str.length();
for (i = 0;i < len;i++)
{
if (str[i] >= 'A' && str[i] <= 'Z')
{
str[i] = (str[i] - 'A' + 'a');
}
}
if (str.compare ("yes") == 0)
return 1;
if (str.compare ("no") == 0)
return 0;
if (str.compare("true") == 0)
return 1;
if (str.compare ("false") == 0)
return 0;
if (str.compare("1") == 0)
return 1;
if (str.compare ("0") == 0)
return 0;
return -1;
}
bool Settings::SettingClass::getBool(const string& name)
{
string v = this->get(name);
int r = _isTrue(v);
if (r == 0 || r == 1)
return (bool)r;
throw SettingsException("Invalid boolean value.");
}
bool Settings::SettingClass::getBool (const string& key, bool defaultValue)
{
try {
return this->getBool(key);
} catch (SettingsException &e)
{ }
return defaultValue;
}
void Settings::SettingClass::getIPs (const string& key, list<SOCKADDR_IN> &ip)
{
string v = this->get(key) + " "; // add padding for last entry.
// expect a.b.c.d[:port], IPv4 only supported with BEP-15.
string::size_type s, e;
s = e = 0;
char c;
for (string::size_type i = 0;i < v.length();i++)
{
c = v[i];
if (isspace(c) != 0 || c == ';' || c == ',')
{
if (s == e)
s = e = i;
else
{
string addr = v.substr(s, (e - s) + 1);
SOCKADDR_IN saddr;
memset(&saddr, 0, sizeof (SOCKADDR_IN));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = 0L;
saddr.sin_port = (6969);
{
uint8_t b; // temporary container for IP byte
uint16_t port;
uint32_t ip;
unsigned i, // loop index
stage; // 0,1,2,3=IP[a.b.c.d], 4=port
ip = 0;
b = 0;
stage = 0;
for (i = 0;i < addr.length();i++)
{
if (addr[i] >= '0' && addr[i] <= '9')
{
if (stage <= 3)
{
b *= 10;
b += (addr[i] - '0');
}
else if (stage == 4)
{
port *= 10;
port += (addr[i] - '0');
}
}
else if (addr[i] == '.' && stage < 3)
{
stage ++;
ip *= 256;
ip += b;
b = 0;
}
else if (addr[i] == ':')
{
stage++;
port = 0;
ip *= 256;
ip += b;
}
}
if (stage == 3) // port not provided.
{
port = 6969;
// add last byte.
ip *= 256;
ip += b;
}
saddr.sin_addr.s_addr = m_hton32(ip);
saddr.sin_port = m_hton16(port);
}
ip.push_back(saddr);
s = e = i + 1;
}
}
else
{
e = i;
}
}
}
int Settings::SettingClass::getInt (const string& key, int def)
{
string v = this->get (key);
if (v.length() == 0)
return def;
return std::atoi(v.c_str());
}
map<string, string>* Settings::SettingClass::getMap()
{
return &this->entries;
}
bool Settings::SettingClass::set (const string name, const string value)
{
pair<map<string, string>::iterator, bool> r;
r = this->entries.insert(pair<string, string>(name, value));
if (!r.second)
{
r.first->second = value;
}
return true;
}
};

View file

@ -1,169 +0,0 @@
/*
*
* Copyright © 2012,2013 Naim A.
*
* This file is part of UDPT.
*
* UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include <map>
#include <string>
#include <list>
#include "multiplatform.h"
using namespace std;
namespace UDPT
{
class Settings
{
public:
class SettingsException : public std::exception
{
public:
SettingsException (const char *str)
{
this->str = str;
}
const char * what ()
{
return str;
}
private:
const char *str;
};
class SettingClass
{
public:
SettingClass (const string className);
bool set (const string key, const string value);
string get (const string& key);
bool getBool (const string& key);
bool getBool (const string& key, bool defaultValue);
int getInt (const string& key, int def = -1);
map<string, string>* getMap ();
void getIPs (const string& key, list<SOCKADDR_IN> &ip);
private:
friend class Settings;
string className;
map<string, string> entries;
};
/**
* Initializes the settings type.
* @param filename the settings filename.
*/
Settings (const string filename);
/**
* Gets a setting from a Settings type.
* @param class The class of the requested setting.
* @param name The name of the requested setting.
* @return The value for the requested setting, NULL if not available.
*/
SettingClass* getClass (const string name);
/**
* Loads settings from file
* @return true on success, otherwise false.
*/
bool load ();
/**
* Saves settings to file.
* @return true on success; otherwise false.
*/
bool save ();
/**
* Sets a setting in a settings type.
* @param className The class of the setting.
* @param key The name of the setting.
* @param value The value to set for the setting.
* @return true on success, otherwise false.
*/
bool set (const string className, const string key, const string value);
/**
* Gets the requested SettingClass.
* @param classname The name of the class to find (case sensitive).
* @return a pointer to the found class, or NULL if not found.
*/
string get (const string className, const string key);
/**
* Destroys the settings "object"
*/
virtual ~Settings ();
private:
string filename;
map<string, SettingClass*> classes;
void parseSettings (char *data, int len);
};
};
//#ifdef __cplusplus
//extern "C" {
//#endif
//
//typedef struct {
// char *key;
// char *values;
//} KeyValue;
//
//typedef struct {
// char *classname;
// KeyValue *entries;
// uint32_t entry_count, entry_size;
//} SettingClass;
//
//typedef struct {
// char *filename;
//
// SettingClass *classes;
// uint32_t class_count, class_size;
//
// char *buffer;
//} Settings;
//
//
//void settings_init (Settings *s, const char *filename);
//
//int settings_load (Settings *s);
//
//int settings_save (Settings *s);
//
//void settings_destroy (Settings *s);
//
//SettingClass* settings_get_class (Settings *s, const char *classname);
//
//char* settingclass_get (SettingClass *s, const char *name);
//
//int settingclass_set (SettingClass *s, const char *name, const char *value);
//
//char* settings_get (Settings *s, const char *classn, const char *name);
//
//
//int settings_set (Settings *s, const char *classn, const char *name, const char *value);
//
//#ifdef __cplusplus
//}
//#endif

View file

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

View file

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

50
src/tracker.cpp Normal file
View file

@ -0,0 +1,50 @@
#include "tracker.hpp"
namespace UDPT
{
Tracker::Tracker()
{
}
Tracker::~Tracker()
{
}
void Tracker::stop()
{
m_udpTracker->stop();
wait();
m_apiSrv = nullptr;
m_webApp = nullptr;
m_udpTracker = nullptr;
}
void Tracker::wait()
{
m_udpTracker->wait();
}
void Tracker::start(const boost::program_options::variables_map& conf)
{
m_udpTracker = std::shared_ptr<UDPTracker>(new UDPTracker(conf));
if (conf["apiserver.enable"].as<bool>())
{
m_apiSrv = std::shared_ptr<UDPT::Server::HTTPServer>(new UDPT::Server::HTTPServer(conf));
m_webApp = std::shared_ptr<UDPT::Server::WebApp>(new UDPT::Server::WebApp(m_apiSrv, m_udpTracker->m_conn.get(), conf));
m_webApp->deploy();
}
m_udpTracker->start();
}
Tracker& Tracker::getInstance()
{
static Tracker s_tracker;
return s_tracker;
}
}

35
src/tracker.hpp Normal file
View file

@ -0,0 +1,35 @@
#pragma once
#include <memory>
#include <boost/program_options.hpp>
#include "logging.h"
#include "multiplatform.h"
#include "udpTracker.hpp"
#include "http/httpserver.hpp"
#include "http/webapp.hpp"
namespace UDPT
{
class Tracker
{
public:
virtual ~Tracker();
void stop();
void start(const boost::program_options::variables_map& conf);
void wait();
static Tracker& getInstance();
private:
std::shared_ptr<UDPT::UDPTracker> m_udpTracker;
std::shared_ptr<UDPT::Server::HTTPServer> m_apiSrv;
std::shared_ptr<UDPT::Server::WebApp> m_webApp;
Tracker();
};
}

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2012,2013 Naim A.
* Copyright © 2012-2016 Naim A.
*
* This file is part of UDPT.
*
@ -17,174 +17,145 @@
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/
#include "udpTracker.hpp"
#include "tools.h"
#include <cstdlib> // atoi
#include <cstring>
#include <ctime>
#include <iostream>
#include <sstream>
#include <list>
#include "udpTracker.hpp"
#include "tools.h"
#include "multiplatform.h"
#include "logging.h"
extern UDPT::Logger *logger;
using namespace std;
using namespace UDPT::Data;
#define UDP_BUFFER_SIZE 2048
namespace UDPT
{
UDPTracker::UDPTracker (Settings *settings)
UDPTracker::UDPTracker(const boost::program_options::variables_map& conf) : m_conf(conf)
{
Settings::SettingClass *sc_tracker;
this->m_allowRemotes = conf["tracker.allow_remotes"].as<bool>();
this->m_allowIANA_IPs = conf["tracker.allow_iana_ips"].as<bool>();
this->m_isDynamic = conf["tracker.is_dynamic"].as<bool>();
sc_tracker = settings->getClass("tracker");
this->m_announceInterval = conf["tracker.announce_interval"].as<unsigned>();
this->m_cleanupInterval = conf["tracker.cleanup_interval"].as<unsigned>();
this->m_port = conf["tracker.port"].as<unsigned short>();
this->m_threadCount = conf["tracker.threads"].as<unsigned>() + 1;
this->allowRemotes = sc_tracker->getBool("allow_remotes", true);
this->allowIANA_IPs = sc_tracker->getBool("allow_iana_ips", false);
this->isDynamic = sc_tracker->getBool("is_dynamic", true);
this->announce_interval = sc_tracker->getInt("announce_interval", 1800);
this->cleanup_interval = sc_tracker->getInt("cleanup_interval", 120);
this->port = sc_tracker->getInt("port", 6969);
this->thread_count = abs (sc_tracker->getInt("threads", 5)) + 1;
list<SOCKADDR_IN> addrs;
sc_tracker->getIPs("bind", addrs);
std::list<SOCKADDR_IN> addrs;
if (addrs.empty())
{
SOCKADDR_IN sa;
sa.sin_port = m_hton16(port);
sa.sin_port = m_hton16(m_port);
sa.sin_addr.s_addr = 0L;
addrs.push_back(sa);
}
this->localEndpoint = addrs.front();
this->threads = new HANDLE[this->thread_count];
this->isRunning = false;
this->conn = NULL;
this->o_settings = settings;
this->m_localEndpoint = addrs.front();
}
UDPTracker::~UDPTracker ()
UDPTracker::~UDPTracker()
{
int i; // loop index
this->isRunning = false;
// drop listener connection to continue thread loops.
// wait for request to finish (1 second max; allot of time for a computer!).
#ifdef linux
close (this->sock);
sleep (1);
#elif defined (WIN32)
closesocket (this->sock);
Sleep (1000);
#endif
for (i = 0;i < this->thread_count;i++)
{
#ifdef WIN32
TerminateThread (this->threads[i], 0x00);
#elif defined (linux)
pthread_detach (this->threads[i]);
pthread_cancel (this->threads[i]);
#endif
stringstream str;
str << "Thread (" << (i + 1) << "/" << ((int)this->thread_count) << ") terminated.";
logger->log(Logger::LL_INFO, str.str());
}
if (this->conn != NULL)
delete this->conn;
delete[] this->threads;
stop();
}
void UDPTracker::wait()
void UDPTracker::start()
{
#ifdef WIN32
WaitForMultipleObjects(this->thread_count, this->threads, TRUE, INFINITE);
#else
int i;
for (i = 0;i < this->thread_count; i++)
{
pthread_join (this->threads[i], NULL);
}
#endif
}
enum UDPTracker::StartStatus UDPTracker::start ()
{
stringstream ss;
std::stringstream ss;
SOCKET sock;
int r, // saves results
i, // loop index
yup; // just to set TRUE
string dbname;// saves the Database name.
std::string dbname;// saves the Database name.
sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
sock = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock == INVALID_SOCKET)
return START_ESOCKET_FAILED;
{
throw UDPT::UDPTException("Failed to create socket");
}
yup = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&yup, 1);
::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&yup, 1);
this->localEndpoint.sin_family = AF_INET;
r = bind (sock, (SOCKADDR*)&this->localEndpoint, sizeof(SOCKADDR_IN));
{
// don't block recvfrom for too long.
#if defined(linux)
timeval timeout = { 0 };
timeout.tv_sec = 5;
#elif defined(WIN32)
DWORD timeout = 5000;
#else
#error Unsupported OS.
#endif
::setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<const char*>(&timeout), sizeof(timeout));
}
this->m_localEndpoint.sin_family = AF_INET;
r = ::bind(sock, reinterpret_cast<SOCKADDR*>(&this->m_localEndpoint), sizeof(SOCKADDR_IN));
if (r == SOCKET_ERROR)
{
#ifdef WIN32
closesocket (sock);
::closesocket(sock);
#elif defined (linux)
close (sock);
::close(sock);
#endif
return START_EBIND_FAILED;
throw UDPT::UDPTException("Failed to bind socket.");
}
this->sock = sock;
this->m_sock = sock;
this->conn = new Data::SQLite3Driver (this->o_settings->getClass("database"),
this->isDynamic);
this->isRunning = true;
this->m_conn = std::shared_ptr<DatabaseDriver>(new Data::SQLite3Driver(m_conf, this->m_isDynamic));
ss.str("");
ss << "Starting maintenance thread (1/" << ((int)this->thread_count) << ")";
ss << "Starting maintenance thread (1/" << ((int)this->m_threadCount) << ")";
logger->log(Logger::LL_INFO, ss.str());
// create maintainer thread.
#ifdef WIN32
this->threads[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)_maintainance_start, (LPVOID)this, 0, NULL);
#elif defined (linux)
pthread_create (&this->threads[0], NULL, _maintainance_start, (void*)this);
#endif
for (i = 1;i < this->thread_count; i++)
m_threads.push_back(boost::thread(UDPTracker::_maintainance_start, this));
for (i = 1;i < this->m_threadCount; i++)
{
ss.str("");
ss << "Starting thread (" << (i + 1) << "/" << ((int)this->thread_count) << ")";
ss << "Starting thread (" << (i + 1) << "/" << ((int)this->m_threadCount) << ")";
logger->log(Logger::LL_INFO, ss.str());
#ifdef WIN32
this->threads[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)_thread_start, (LPVOID)this, 0, NULL);
#elif defined (linux)
pthread_create (&(this->threads[i]), NULL, _thread_start, (void*)this);
#endif
m_threads.push_back(boost::thread(UDPTracker::_thread_start, this));
}
return START_OK;
}
int UDPTracker::sendError (UDPTracker *usi, SOCKADDR_IN *remote, uint32_t transactionID, const string &msg)
void UDPTracker::stop()
{
#ifdef linux
::close(m_sock);
#elif defined (WIN32)
::closesocket(m_sock);
#endif
for (std::vector<boost::thread>::iterator it = m_threads.begin(); it != m_threads.end(); ++it)
{
it->interrupt();
}
wait();
}
void UDPTracker::wait()
{
for (std::vector<boost::thread>::iterator it = m_threads.begin(); it != m_threads.end(); ++it)
{
it->join();
}
}
int UDPTracker::sendError(UDPTracker* usi, SOCKADDR_IN* remote, uint32_t transactionID, const std::string &msg)
{
struct udp_error_response error;
int msg_sz, // message size to send.
@ -201,35 +172,33 @@ namespace UDPT
if (msg_sz > 1024)
return -1;
memcpy(buff, &error, 8);
::memcpy(buff, &error, 8);
for (i = 8;i <= msg_sz;i++)
{
buff[i] = msg[i - 8];
}
sendto(usi->sock, buff, msg_sz, 0, (SOCKADDR*)remote, sizeof(*remote));
::sendto(usi->m_sock, buff, msg_sz, 0, reinterpret_cast<SOCKADDR*>(remote), sizeof(*remote));
return 0;
}
int UDPTracker::handleConnection (UDPTracker *usi, SOCKADDR_IN *remote, char *data)
int UDPTracker::handleConnection(UDPTracker *usi, SOCKADDR_IN *remote, char *data)
{
ConnectionRequest *req;
ConnectionRequest *req = reinterpret_cast<ConnectionRequest*>(data);
ConnectionResponse resp;
req = (ConnectionRequest*)data;
resp.action = m_hton32(0);
resp.transaction_id = req->transaction_id;
if (!usi->conn->genConnectionId(&resp.connection_id,
if (!usi->m_conn->genConnectionId(&resp.connection_id,
m_hton32(remote->sin_addr.s_addr),
m_hton16(remote->sin_port)))
{
return 1;
}
sendto(usi->sock, (char*)&resp, sizeof(ConnectionResponse), 0, (SOCKADDR*)remote, sizeof(SOCKADDR_IN));
::sendto(usi->m_sock, (char*)&resp, sizeof(ConnectionResponse), 0, (SOCKADDR*)remote, sizeof(SOCKADDR_IN));
return 0;
}
@ -248,7 +217,7 @@ namespace UDPT
req = (AnnounceRequest*)data;
if (!usi->conn->verifyConnectionId(req->connection_id,
if (!usi->m_conn->verifyConnectionId(req->connection_id,
m_hton32(remote->sin_addr.s_addr),
m_hton16(remote->sin_port)))
{
@ -264,13 +233,13 @@ namespace UDPT
req->num_want = m_hton32 (req->num_want);
req->left = m_hton64 (req->left);
if (!usi->allowRemotes && req->ip_address != 0)
if (!usi->m_allowRemotes && req->ip_address != 0)
{
UDPTracker::sendError (usi, remote, req->transaction_id, "Tracker doesn't allow remote IP's; Request ignored.");
UDPTracker::sendError(usi, remote, req->transaction_id, "Tracker doesn't allow remote IP's; Request ignored.");
return 0;
}
if (!usi->conn->isTorrentAllowed(req->info_hash))
if (!usi->m_conn->isTorrentAllowed(req->info_hash))
{
UDPTracker::sendError(usi, remote, req->transaction_id, "info_hash not registered.");
return 0;
@ -279,7 +248,7 @@ namespace UDPT
// load peers
q = 30;
if (req->num_want >= 1)
q = min (q, req->num_want);
q = std::min<int>(q, req->num_want);
peers = new DatabaseDriver::PeerEntry [q];
@ -305,17 +274,17 @@ namespace UDPT
q = 0; // no need for peers when stopping.
if (q > 0)
usi->conn->getPeers(req->info_hash, &q, peers);
usi->m_conn->getPeers(req->info_hash, &q, peers);
bSize = 20; // header is 20 bytes
bSize += (6 * q); // + 6 bytes per peer.
tE.info_hash = req->info_hash;
usi->conn->getTorrentInfo(&tE);
usi->m_conn->getTorrentInfo(&tE);
resp = (AnnounceResponse*)buff;
resp->action = m_hton32(1);
resp->interval = m_hton32 ( usi->announce_interval );
resp->interval = m_hton32 ( usi->m_announceInterval );
resp->leechers = m_hton32(tE.leechers);
resp->seeders = m_hton32 (tE.seeders);
resp->transaction_id = req->transaction_id;
@ -337,7 +306,7 @@ namespace UDPT
}
delete[] peers;
sendto(usi->sock, (char*)buff, bSize, 0, (SOCKADDR*)remote, sizeof(SOCKADDR_IN));
::sendto(usi->m_sock, (char*)buff, bSize, 0, (SOCKADDR*)remote, sizeof(SOCKADDR_IN));
// update DB.
uint32_t ip;
@ -345,15 +314,15 @@ namespace UDPT
ip = m_hton32 (remote->sin_addr.s_addr);
else
ip = req->ip_address;
usi->conn->updatePeer(req->peer_id, req->info_hash, ip, req->port,
usi->m_conn->updatePeer(req->peer_id, req->info_hash, ip, req->port,
req->downloaded, req->left, req->uploaded, event);
return 0;
}
int UDPTracker::handleScrape (UDPTracker *usi, SOCKADDR_IN *remote, char *data, int len)
int UDPTracker::handleScrape(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int len)
{
ScrapeRequest *sR;
ScrapeRequest *sR = reinterpret_cast<ScrapeRequest*>(data);
int v, // validation helper
c, // torrent counter
i, // loop counter
@ -362,18 +331,15 @@ namespace UDPT
ScrapeResponse *resp;
uint8_t buffer [1024]; // up to 74 torrents can be scraped at once (17*74+8) < 1024
sR = (ScrapeRequest*)data;
// validate request length:
v = len - 16;
if (v < 0 || v % 20 != 0)
{
UDPTracker::sendError (usi, remote, sR->transaction_id, "Bad scrape request.");
UDPTracker::sendError(usi, remote, sR->transaction_id, "Bad scrape request.");
return 0;
}
if (!usi->conn->verifyConnectionId(sR->connection_id,
if (!usi->m_conn->verifyConnectionId(sR->connection_id,
m_hton32(remote->sin_addr.s_addr),
m_hton16(remote->sin_port)))
{
@ -383,8 +349,8 @@ namespace UDPT
// get torrent count.
c = v / 20;
resp = (ScrapeResponse*)buffer;
resp->action = m_hton32 (2);
resp = reinterpret_cast<ScrapeResponse*>(buffer);
resp->action = m_hton32(2);
resp->transaction_id = sR->transaction_id;
for (i = 0;i < c;i++)
@ -402,73 +368,64 @@ namespace UDPT
DatabaseDriver::TorrentEntry tE;
tE.info_hash = hash;
if (!usi->conn->getTorrentInfo(&tE))
if (!usi->m_conn->getTorrentInfo(&tE))
{
sendError(usi, remote, sR->transaction_id, "Scrape Failed: couldn't retrieve torrent data");
return 0;
}
*seeders = m_hton32 (tE.seeders);
*completed = m_hton32 (tE.completed);
*leechers = m_hton32 (tE.leechers);
*seeders = m_hton32(tE.seeders);
*completed = m_hton32(tE.completed);
*leechers = m_hton32(tE.leechers);
}
sendto (usi->sock, (const char*)buffer, sizeof(buffer), 0, (SOCKADDR*)remote, sizeof(SOCKADDR_IN));
::sendto(usi->m_sock, reinterpret_cast<const char*>(buffer), sizeof(buffer), 0, reinterpret_cast<SOCKADDR*>(remote), sizeof(SOCKADDR_IN));
return 0;
}
static int _isIANA_IP (uint32_t ip)
{
uint8_t x = (ip % 256);
if (x == 0 || x == 10 || x == 127 || x >= 224)
return 1;
return 0;
}
int UDPTracker::resolveRequest (UDPTracker *usi, SOCKADDR_IN *remote, char *data, int r)
int UDPTracker::isIANAIP(uint32_t ip)
{
ConnectionRequest *cR;
uint32_t action;
uint8_t x = (ip % 256);
if (x == 0 || x == 10 || x == 127 || x >= 224)
return 1;
return 0;
}
cR = (ConnectionRequest*)data;
int UDPTracker::resolveRequest(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int r)
{
ConnectionRequest* cR = reinterpret_cast<ConnectionRequest*>(data);
uint32_t action;
action = m_hton32(cR->action);
if (!usi->allowIANA_IPs)
if (!usi->m_allowIANA_IPs)
{
if (_isIANA_IP (remote->sin_addr.s_addr))
if (isIANAIP(remote->sin_addr.s_addr))
{
return 0; // Access Denied: IANA reserved IP.
}
}
// cout << ":: " << (void*)m_hton32(remote->sin_addr.s_addr) << ": " << m_hton16(remote->sin_port) << " ACTION=" << action << endl;
if (action == 0 && r >= 16)
return UDPTracker::handleConnection (usi, remote, data);
return UDPTracker::handleConnection(usi, remote, data);
else if (action == 1 && r >= 98)
return UDPTracker::handleAnnounce (usi, remote, data);
return UDPTracker::handleAnnounce(usi, remote, data);
else if (action == 2)
return UDPTracker::handleScrape (usi, remote, data, r);
return UDPTracker::handleScrape(usi, remote, data, r);
else
{
// cout << "E: action=" << action << ", r=" << r << endl;
UDPTracker::sendError (usi, remote, cR->transaction_id, "Tracker couldn't understand Client's request.");
UDPTracker::sendError(usi, remote, cR->transaction_id, "Tracker couldn't understand Client's request.");
return -1;
}
return 0;
}
#ifdef WIN32
DWORD UDPTracker::_thread_start (LPVOID arg)
#elif defined (linux)
void* UDPTracker::_thread_start (void *arg)
#endif
void UDPTracker::_thread_start(UDPTracker *usi)
{
UDPTracker *usi;
SOCKADDR_IN remoteAddr;
char tmpBuff[UDP_BUFFER_SIZE];
#ifdef linux
socklen_t addrSz;
@ -476,54 +433,37 @@ static int _isIANA_IP (uint32_t ip)
int addrSz;
#endif
int r;
char tmpBuff [UDP_BUFFER_SIZE];
usi = (UDPTracker*)arg;
addrSz = sizeof (SOCKADDR_IN);
addrSz = sizeof(SOCKADDR_IN);
while (usi->isRunning)
while (true)
{
cout.flush();
// peek into the first 12 bytes of data; determine if connection request or announce request.
r = recvfrom(usi->sock, (char*)tmpBuff, UDP_BUFFER_SIZE, 0, (SOCKADDR*)&remoteAddr, &addrSz);
int r = ::recvfrom(usi->m_sock, (char*)tmpBuff, UDP_BUFFER_SIZE, 0, (SOCKADDR*)&remoteAddr, &addrSz);
if (r <= 0)
continue; // bad request...
r = UDPTracker::resolveRequest (usi, &remoteAddr, tmpBuff, r);
}
{
boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
continue;
}
#ifdef linux
pthread_exit (NULL);
#endif
return 0;
{
boost::this_thread::disable_interruption di;
r = UDPTracker::resolveRequest(usi, &remoteAddr, tmpBuff, r);
}
}
}
#ifdef WIN32
DWORD UDPTracker::_maintainance_start (LPVOID arg)
#elif defined (linux)
void* UDPTracker::_maintainance_start (void *arg)
#endif
void UDPTracker::_maintainance_start(UDPTracker* usi)
{
UDPTracker *usi;
usi = (UDPTracker *)arg;
while (usi->isRunning)
while (true)
{
usi->conn->cleanup();
{
boost::this_thread::disable_interruption di;
usi->m_conn->cleanup();
}
#ifdef WIN32
Sleep (usi->cleanup_interval * 1000);
#elif defined (linux)
sleep (usi->cleanup_interval);
#else
#error Unsupported OS.
#endif
boost::this_thread::sleep_for(boost::chrono::seconds(usi->m_cleanupInterval));
}
return 0;
}
};

View file

@ -1,5 +1,5 @@
/*
* Copyright © 2012,2013 Naim A.
* Copyright © 2012-2016 Naim A.
*
* This file is part of UDPT.
*
@ -22,17 +22,19 @@
#include <stdint.h>
#include <boost/thread.hpp>
#include <chrono>
#include <algorithm>
#include <boost/program_options.hpp>
#include <string>
#include "exceptions.h"
#include "multiplatform.h"
#include "db/driver_sqlite.hpp"
#include "settings.hpp"
#include <string>
using namespace std;
#define UDPT_DYNAMIC 0x01 // Track Any info_hash?
#define UDPT_ALLOW_REMOTE_IP 0x02 // Allow client's to send other IPs?
#define UDPT_ALLOW_IANA_IP 0x04 // allow IP's like 127.0.0.1 or other IANA reserved IPs?
#define UDPT_VALIDATE_CLIENT 0x08 // validate client before adding to Database? (check if connection is open?)
#define UDPT_DYNAMIC (0x01) // Track Any info_hash?
#define UDPT_ALLOW_REMOTE_IP (0x02) // Allow client's to send other IPs?
#define UDPT_ALLOW_IANA_IP (0x04) // allow IP's like 127.0.0.1 or other IANA reserved IPs?
#define UDPT_VALIDATE_CLIENT (0x08) // validate client before adding to Database? (check if connection is open?)
namespace UDPT
@ -117,57 +119,57 @@ namespace UDPT
* Initializes the UDP Tracker.
* @param settings Settings to start server with
*/
UDPTracker (Settings *);
UDPTracker(const boost::program_options::variables_map& conf);
/**
* Starts the Initialized instance.
* @return 0 on success, otherwise non-zero.
*/
enum StartStatus start ();
void start();
/**
* Joins all threads, and waits for all of them to terminate.
/**
* Terminates tracker.
*/
void wait ();
void stop();
/**
* Joins worker threads
*/
void wait();
/**
* Destroys resources that were created by constructor
* @param usi Instance to destroy.
*/
virtual ~UDPTracker ();
virtual ~UDPTracker();
std::shared_ptr<UDPT::Data::DatabaseDriver> m_conn;
Data::DatabaseDriver *conn;
private:
SOCKET sock;
SOCKADDR_IN localEndpoint;
uint16_t port;
uint8_t thread_count;
bool isRunning;
bool isDynamic;
bool allowRemotes;
bool allowIANA_IPs;
HANDLE *threads;
uint32_t announce_interval;
uint32_t cleanup_interval;
SOCKET m_sock;
SOCKADDR_IN m_localEndpoint;
uint16_t m_port;
uint8_t m_threadCount;
bool m_isDynamic;
bool m_allowRemotes;
bool m_allowIANA_IPs;
std::vector<boost::thread> m_threads;
uint32_t m_announceInterval;
uint32_t m_cleanupInterval;
Settings *o_settings;
const boost::program_options::variables_map& m_conf;
#ifdef WIN32
static DWORD _thread_start (LPVOID arg);
static DWORD _maintainance_start (LPVOID arg);
#elif defined (linux)
static void* _thread_start (void *arg);
static void* _maintainance_start (void *arg);
#endif
static void _thread_start(UDPTracker *usi);
static void _maintainance_start(UDPTracker* usi);
static int resolveRequest (UDPTracker *usi, SOCKADDR_IN *remote, char *data, int r);
static int resolveRequest(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int r);
static int handleConnection (UDPTracker *usi, SOCKADDR_IN *remote, char *data);
static int handleAnnounce (UDPTracker *usi, SOCKADDR_IN *remote, char *data);
static int handleScrape (UDPTracker *usi, SOCKADDR_IN *remote, char *data, int len);
static int handleConnection(UDPTracker *usi, SOCKADDR_IN *remote, char *data);
static int handleAnnounce(UDPTracker *usi, SOCKADDR_IN *remote, char *data);
static int handleScrape(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int len);
static int sendError (UDPTracker *, SOCKADDR_IN *remote, uint32_t transId, const string &);
static int sendError(UDPTracker *, SOCKADDR_IN *remote, uint32_t transId, const std::string &);
static int isIANAIP(uint32_t ip);
};
};