Moving from C to C++... Some progress...

This commit is contained in:
Naim A 2013-02-26 21:02:50 +02:00
parent 84ab27bfde
commit 51e2c80ba7
13 changed files with 1253 additions and 1258 deletions

View file

@ -22,6 +22,10 @@
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct dbConnection dbConnection;
/**
@ -30,7 +34,7 @@ typedef struct dbConnection dbConnection;
* @param cStr Connection string for the active driver.
* @return 0 on success; otherwise non-zero.
*/
int db_open (dbConnection **pdb, char *cStr);
int db_open (dbConnection **pdb, const char *cStr);
/**
* Closes the database connection.
@ -95,4 +99,7 @@ int db_cleanup (dbConnection *db);
*/
int db_remove_peer (dbConnection *db, uint8_t hash [20], db_peerEntry *pE);
#ifdef __cplusplus
}
#endif
#endif /* DATABASE_H_ */

View file

@ -85,7 +85,7 @@ static void _db_setup (sqlite3 *db)
")", NULL, NULL, NULL);
}
int db_open (dbConnection **db, char *cStr)
int db_open (dbConnection **db, const char *cStr)
{
FILE *f;
int doSetup, // check if to build DB, or it already exists?

View file

@ -1,114 +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 <stdio.h>
#include <stdlib.h>
#include "multiplatform.h"
#include "udpTracker.h"
#include "tools.h"
#include <math.h>
#include <time.h>
#include <string.h>
#include "settings.h"
static void _print_usage ()
{
printf ("Usage: udpt [<configuration file>]\n");
}
int main(int argc, char *argv[])
{
Settings settings;
udpServerInstance usi;
char *config_file;
int r;
#ifdef WIN32
WSADATA wsadata;
WSAStartup(MAKEWORD(2, 2), &wsadata);
#endif
printf("UDP Tracker (UDPT) %s\nCopyright: (C) 2012 Naim Abda <naim94a@gmail.com>\n", VERSION);
printf("Build Date: %s\n\n", __DATE__);
config_file = "udpt.conf";
if (argc <= 1)
{
_print_usage ();
}
settings_init (&settings, config_file);
if (settings_load (&settings) != 0)
{
const char strDATABASE[] = "database";
const char strTRACKER[] = "tracker";
// set default settings:
settings_set (&settings, strDATABASE, "driver", "sqlite3");
settings_set (&settings, strDATABASE, "file", "tracker.db");
settings_set (&settings, strTRACKER, "port", "6969");
settings_set (&settings, strTRACKER, "threads", "5");
settings_set (&settings, strTRACKER, "allow_remotes", "yes");
settings_set (&settings, strTRACKER, "allow_iana_ips", "yes");
settings_set (&settings, strTRACKER, "announce_interval", "1800");
settings_set (&settings, strTRACKER, "cleanup_interval", "120");
settings_save (&settings);
printf("Failed to read from '%s'. Using default settings.\n", config_file);
}
UDPTracker_init(&usi, &settings);
r = UDPTracker_start(&usi);
if (r != 0)
{
printf("Error While trying to start server.\n");
switch (r)
{
case 1:
printf("Failed to create socket.\n");
break;
case 2:
printf("Failed to bind socket.\n");
break;
default:
printf ("Unknown Error\n");
break;
}
goto cleanup;
}
printf("Press Any key to exit.\n");
getchar ();
cleanup:
printf("\nGoodbye.\n");
settings_destroy (&settings);
UDPTracker_destroy(&usi);
#ifdef WIN32
WSACleanup();
#endif
return 0;
}

115
src/main.cpp Normal file
View file

@ -0,0 +1,115 @@
/*
* 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 <iostream>
#include "multiplatform.h"
#include "udpTracker.hpp"
#include "settings.hpp"
using namespace std;
using namespace UDPT;
static void _print_usage ()
{
cout << "Usage: udpt [<configuration file>]" << endl;
}
int main(int argc, char *argv[])
{
Settings *settings = NULL;
UDPTracker *usi = NULL;
string config_file;
int r;
#ifdef WIN32
WSADATA wsadata;
WSAStartup(MAKEWORD(2, 2), &wsadata);
#endif
cout << "UDP Tracker (UDPT) " << VERSION << endl;
cout << "Copyright 2012,2013 Naim Abda <naim94a@gmail.com>\n\tReleased under the GPLv3 License." << endl;
cout << "Build Date: " << __DATE__ << endl << endl;
config_file = "udpt.conf";
if (argc <= 1)
{
_print_usage ();
}
settings = new Settings (config_file);
if (!settings->load())
{
const char strDATABASE[] = "database";
const char strTRACKER[] = "tracker";
// set default settings:
settings->set (strDATABASE, "driver", "sqlite3");
settings->set (strDATABASE, "file", "tracker.db");
settings->set (strTRACKER, "port", "6969");
settings->set (strTRACKER, "threads", "5");
settings->set (strTRACKER, "allow_remotes", "yes");
settings->set (strTRACKER, "allow_iana_ips", "yes");
settings->set (strTRACKER, "announce_interval", "1800");
settings->set (strTRACKER, "cleanup_interval", "120");
settings->save();
cout << "Failed to read from '" << config_file.c_str() << "'. Using default settings." << endl;
}
usi = new UDPTracker (settings);
r = usi->start();
if (r != UDPTracker::START_OK)
{
cout << "Error While trying to start server." << endl;
switch (r)
{
case UDPTracker::START_ESOCKET_FAILED:
cout << "Failed to create socket." << endl;
break;
case UDPTracker::START_EBIND_FAILED:
cout << "Failed to bind socket." << endl;
break;
default:
cout << "Unknown Error" << endl;
break;
}
goto cleanup;
}
cout << "Press Any key to exit." << endl;
cin.get();
cleanup:
cout << endl << "Goodbye." << endl;
delete usi;
delete settings;
#ifdef WIN32
WSACleanup();
#endif
return 0;
}

View file

@ -1,373 +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.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
SettingClass* settings_get_class (Settings *s, const char *classname)
{
int i;
if (s == NULL || classname == NULL)
return NULL;
for (i = 0;i < s->class_count;i++)
{
if (strcmp(classname, s->classes[i].classname) == 0)
{
return &s->classes[i];
}
}
return NULL;
}
void settings_init (Settings *s, const char *filename)
{
s->buffer = NULL;
s->filename = (char*)filename;
s->classes = NULL;
s->class_count = s->class_size = 0;
}
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;
}
}
static
void _settings_parser (Settings *s, 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 == ';')
{
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...
settings_set(s, className, key, value);
cil = 0;
break;
}
i++;
}
break;
}
i++;
}
continue;
}
if (isgraph(c) != 0)
{
cil++;
}
}
}
int settings_load (Settings *s)
{
FILE *f;
int len,
r,
offset; // file length
char *buffer;
char tmp [512];
if (s->buffer != NULL)
{
free (s->buffer);
s->buffer = NULL;
}
// ini file format.
f = fopen(s->filename, "rb");
if (f == NULL)
return 1;
fseek (f, 0, SEEK_END);
len = ftell(f);
fseek(f, 0, SEEK_SET);
s->buffer = (char*)malloc (len);
buffer = s->buffer;
r = offset = 0;
while (!feof(f) && !ferror(f))
{
int i;
r = fread (tmp, 1, 512, f);
for (i = 0;i < r;i++)
{
buffer[offset + i] = tmp[i];
}
offset += r;
}
fclose (f);
// printf("File loaded into buffer. size=%d\n", len);
_settings_parser (s, buffer, len);
return 0;
}
int settings_save (Settings *s)
{
char buffer [2048];
SettingClass *sclass;
FILE *f;
int c, e;
f = fopen(s->filename, "wb");
fprintf(f, "; udpt Settings File - Created Automatically.\n");
setbuf(f, buffer);
for (c = 0;c < s->class_count;c++)
{
sclass = &s->classes[c];
fprintf(f, "[%s]\n", sclass->classname);
for (e = 0;e < sclass->entry_count;e++)
{
fprintf(f, "%s=%s\n", sclass->entries[e].key, sclass->entries[e].values);
}
fprintf(f, "\n");
}
fclose (f);
return 0;
}
void settings_destroy (Settings *s)
{
if (s->classes != NULL)
{
int i;
for (i = 0;i < s->class_count;i++)
{
if (s->classes[i].entries != NULL)
free (s->classes[i].entries);
}
free (s->classes);
}
if (s->buffer != NULL)
{
free (s->buffer);
s->buffer = NULL;
}
}
char* settings_get (Settings *s, const char *class, const char *name)
{
SettingClass *c;
if (s == NULL || class == NULL || name == NULL)
return NULL;
c = settings_get_class (s, class);
return settingclass_get (c, name);
}
int settings_set (Settings *s, const char *class, const char *name, const char *value)
{
SettingClass *c;
if (s == NULL || class == NULL || name == NULL)
return 1;
c = settings_get_class (s, class);
if (c == NULL)
{
if (s->class_count + 1 >= s->class_size)
{
int ns = s->class_size + 1;
SettingClass *sc = realloc (s->classes, sizeof(SettingClass) * ns);
if (sc == NULL)
return 1;
s->classes = sc;
s->class_size = ns;
}
c = &s->classes[s->class_count];
s->class_count++;
c->classname = (char*)class;
c->entries = NULL;
c->entry_size = c->entry_count = 0;
}
return settingclass_set (c, name, value);
}
char* settingclass_get (SettingClass *c, const char *name)
{
KeyValue *kv;
int i;
if (c == NULL)
return NULL;
for (i = 0;i < c->entry_count;i++)
{
kv = &c->entries[i];
if (strcmp(kv->key, name) == 0)
return kv->values;
}
return NULL;
}
int settingclass_set (SettingClass *c, const char *name, const char *value)
{
int i,
ni;
for (i = 0;i < c->entry_count;i++)
{
if (strcmp(name, c->entries[i].key) == 0)
{
c->entries[i].values = (char*)value;
return 0;
}
}
if (c->entry_count + 1 >= c->entry_size)
{
int ns;
KeyValue *n;
ns = c->entry_size + 5;
n = realloc (c->entries, sizeof(KeyValue) * ns);
if (n == NULL)
return 1;
c->entries = n;
c->entry_size = ns;
}
ni = c->entry_count;
c->entry_count++;
c->entries[ni].key = (char*)name;
c->entries[ni].values = (char*)value;
return 0;
}

294
src/settings.cpp Normal file
View file

@ -0,0 +1,294 @@
/*
*
* 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 <iostream>
#include <fstream>
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 == ';')
{
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);
return c->get(name);
}
bool Settings::set (const string classN, const string name, const string value)
{
SettingClass *c;
if (classN == "" || name == "")
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)
{
return this->entries[name];
}
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,101 +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>
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;
/**
* Initializes the settings type.
* @param s Pointer to settings to initialize.
* @param filename the settings filename.
*/
void settings_init (Settings *s, const char *filename);
/**
* Loads settings from file
* @param s pointer to settings type
* @return 0 on success, otherwise non-zero.
*/
int settings_load (Settings *s);
/**
* Saves settings to file.
* @param s Pointer to settings.
* @return 0 on success; otherwise non-zero.
*/
int settings_save (Settings *s);
/**
* Destroys the settings "object"
* @param s Pointer to settings.
*/
void settings_destroy (Settings *s);
/**
* Gets the requested SettingClass.
* @param s Settings Object.
* @param classname The name of the class to find (case sensitive).
* @return a pointer to the found class, or NULL if not found.
*/
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);
/**
* Gets a setting from a Settings type.
* @param s Pointer to a setting 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.
*/
char* settings_get (Settings *s, const char *classn, const char *name);
/**
* Sets a setting in a settings type.
* @param s Pointer to settings type.
* @param class The class of the setting.
* @param name The name of the setting.
* @param value The value to set for the setting.
* @return 0 on success, otherwise non-zero.
*/
int settings_set (Settings *s, const char *classn, const char *name, const char *value);

145
src/settings.hpp Normal file
View file

@ -0,0 +1,145 @@
/*
*
* 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>
using namespace std;
namespace UDPT
{
class Settings
{
public:
class SettingClass
{
public:
SettingClass (const string className);
bool set (const string key, const string value);
string get (const string key);
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

@ -22,6 +22,10 @@
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Swaps Bytes:
* example (htons):
@ -39,4 +43,8 @@ uint64_t m_hton64 (uint64_t n);
void to_hex_str (const uint8_t *hash, char *data);
#ifdef __cplusplus
}
#endif
#endif /* TOOLS_H_ */

View file

@ -1,520 +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 "multiplatform.h"
#include "udpTracker.h"
#include "tools.h"
#include "settings.h"
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#define FLAG_RUNNING 0x01
#define UDP_BUFFER_SIZE 2048
#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 int _isTrue (char *str)
{
int i, // loop index
len; // string's length
if (str == NULL)
return -1;
len = strlen (str);
for (i = 0;i < len;i++)
{
if (str[i] >= 'A' && str[i] <= 'Z')
{
str[i] = (str[i] - 'A' + 'a');
}
}
if (strcmp(str, "yes") == 0)
return 1;
if (strcmp(str, "no") == 0)
return 0;
if (strcmp(str, "true") == 0)
return 1;
if (strcmp(str, "false") == 0)
return 0;
if (strcmp(str, "1") == 0)
return 1;
if (strcmp(str, "0") == 0)
return 0;
return -1;
}
void UDPTracker_init (udpServerInstance *usi, Settings *settings)
{
SettingClass *sc_tracker;
uint8_t n_settings = 0;
char *s_port, // port
*s_threads, // threads
*s_allow_remotes, // remotes allowed?
*s_allow_iana_ip, // IANA IPs allowed?
*s_int_announce, // announce interval
*s_int_cleanup; // cleanup interval
sc_tracker = settings_get_class (settings, "tracker");
s_port = settingclass_get(sc_tracker, "port");
s_threads = settingclass_get(sc_tracker, "threads");
s_allow_remotes = settingclass_get (sc_tracker, "allow_remotes");
s_allow_iana_ip = settingclass_get (sc_tracker, "allow_iana_ips");
s_int_announce = settingclass_get (sc_tracker, "announce_interval");
s_int_cleanup = settingclass_get (sc_tracker, "cleanup_interval");
if (_isTrue(s_allow_remotes) == 1)
n_settings |= UDPT_ALLOW_REMOTE_IP;
if (_isTrue(s_allow_iana_ip) != 0)
n_settings |= UDPT_ALLOW_IANA_IP;
usi->announce_interval = (s_int_announce == NULL ? 1800 : atoi (s_int_announce));
usi->cleanup_interval = (s_int_cleanup == NULL ? 120 : atoi (s_int_cleanup));
usi->port = (s_port == NULL ? 6969 : atoi (s_port));
usi->thread_count = (s_threads == NULL ? 5 : atoi (s_threads)) + 1;
usi->threads = (HANDLE*)malloc (sizeof(HANDLE) * usi->thread_count);
usi->flags = 0;
usi->conn = NULL;
usi->settings = n_settings;
usi->o_settings = settings;
}
void UDPTracker_destroy (udpServerInstance *usi)
{
int i; // loop index
usi->flags &= ~FLAG_RUNNING;
// drop listener connection to continue thread loops.
// wait for request to finish (1 second max; allot of time for a computer!).
#ifdef linux
close (usi->sock);
sleep (1);
#elif defined (WIN32)
closesocket (usi->sock);
Sleep (1000);
#endif
for (i = 0;i < usi->thread_count;i++)
{
#ifdef WIN32
TerminateThread (usi->threads[0], 0x00);
#elif defined (linux)
pthread_detach (usi->threads[i]);
pthread_cancel (usi->threads[i]);
#endif
printf ("Thread (%d/%u) terminated.\n", i + 1, usi->thread_count);
}
if (usi->conn != NULL)
db_close(usi->conn);
free (usi->threads);
}
int UDPTracker_start (udpServerInstance *usi)
{
SOCKET sock;
SOCKADDR_IN recvAddr;
int r, // saves results
i, // loop index
yup; // just to set TRUE
char *dbname;// saves the Database name.
sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock == INVALID_SOCKET)
return 1;
#ifdef WIN32
recvAddr.sin_addr.S_un.S_addr = 0L;
#elif defined (linux)
recvAddr.sin_addr.s_addr = 0L;
#endif
recvAddr.sin_family = AF_INET;
recvAddr.sin_port = m_hton16 (usi->port);
yup = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&yup, 1);
r = bind (sock, (SOCKADDR*)&recvAddr, sizeof(SOCKADDR_IN));
if (r == SOCKET_ERROR)
{
#ifdef WIN32
closesocket (sock);
#elif defined (linux)
close (sock);
#endif
return 2;
}
usi->sock = sock;
dbname = settings_get (usi->o_settings, "database", "file");
if (dbname == NULL)
dbname = "tracker.db";
db_open(&usi->conn, dbname);
usi->flags |= FLAG_RUNNING;
// create maintainer thread.
#ifdef WIN32
usi->threads[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)_maintainance_start, (LPVOID)usi, 0, NULL);
#elif defined (linux)
printf("Starting maintenance thread (1/%u)...\n", usi->thread_count);
pthread_create (&usi->threads[0], NULL, _maintainance_start, usi);
#endif
for (i = 1;i < usi->thread_count; i++)
{
printf("Starting Thread (%d/%u)\n", (i + 1), usi->thread_count);
#ifdef WIN32
usi->threads[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)_thread_start, (LPVOID)usi, 0, NULL);
#elif defined (linux)
pthread_create (&(usi->threads[i]), NULL, _thread_start, usi);
#endif
}
return 0;
}
static uint64_t _get_connID (SOCKADDR_IN *remote)
{
int base;
uint64_t x;
base = time(NULL);
base /= 3600; // changes every day.
x = base;
x += remote->sin_addr.s_addr;
return x;
}
static int _send_error (udpServerInstance *usi, SOCKADDR_IN *remote, uint32_t transactionID, char *msg)
{
struct udp_error_response error;
int msg_sz, // message size to send.
i; // copy loop
char buff [1024]; // more than reasonable message size...
error.action = m_hton32 (3);
error.transaction_id = transactionID;
error.message = msg;
msg_sz = 4 + 4 + 1 + strlen(msg);
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));
return 0;
}
static int _handle_connection (udpServerInstance *usi, SOCKADDR_IN *remote, char *data)
{
ConnectionRequest *req;
ConnectionResponse resp;
req = (ConnectionRequest*)data;
resp.action = m_hton32(0);
resp.transaction_id = req->transaction_id;
resp.connection_id = _get_connID(remote);
sendto(usi->sock, (char*)&resp, sizeof(ConnectionResponse), 0, (SOCKADDR*)remote, sizeof(SOCKADDR_IN));
return 0;
}
static int _handle_announce (udpServerInstance *usi, SOCKADDR_IN *remote, char *data)
{
AnnounceRequest *req;
AnnounceResponse *resp;
int q, // peer counts
bSize, // message size
i; // loop index
db_peerEntry *peers;
int32_t seeders,
leechers,
completed;
db_peerEntry pE; // info for DB
uint8_t buff [1028]; // Reasonable buffer size. (header+168 peers)
req = (AnnounceRequest*)data;
if (req->connection_id != _get_connID(remote))
{
return 1;
}
// change byte order:
req->port = m_hton16 (req->port);
req->ip_address = m_hton32 (req->ip_address);
req->downloaded = m_hton64 (req->downloaded);
req->event = m_hton32 (req->event); // doesn't really matter for this tracker
req->uploaded = m_hton64 (req->uploaded);
req->num_want = m_hton32 (req->num_want);
req->left = m_hton64 (req->left);
if ((usi->settings & UDPT_ALLOW_REMOTE_IP) == 0 && req->ip_address != 0)
{
_send_error (usi, remote, req->transaction_id, "Tracker doesn't allow remote IP's; Request ignored.");
return 0;
}
// load peers
q = 30;
if (req->num_want >= 1)
q = min (q, req->num_want);
peers = (db_peerEntry*)malloc (sizeof(db_peerEntry) * q);
db_load_peers(usi->conn, req->info_hash, peers, &q);
// printf("%d peers found.\n", q);
bSize = 20; // header is 20 bytes
bSize += (6 * q); // + 6 bytes per peer.
db_get_stats (usi->conn, req->info_hash, &seeders, &leechers, &completed);
resp = (AnnounceResponse*)buff;
resp->action = m_hton32(1);
resp->interval = m_hton32 ( usi->announce_interval );
resp->leechers = m_hton32(leechers);
resp->seeders = m_hton32 (seeders);
resp->transaction_id = req->transaction_id;
for (i = 0;i < q;i++)
{
int x = i * 6;
// network byte order!!!
// IP
buff[20 + x] = ((peers[i].ip & (0xff << 24)) >> 24);
buff[21 + x] = ((peers[i].ip & (0xff << 16)) >> 16);
buff[22 + x] = ((peers[i].ip & (0xff << 8)) >> 8);
buff[23 + x] = (peers[i].ip & 0xff);
// port
buff[24 + x] = ((peers[i].port & (0xff << 8)) >> 8);
buff[25 + x] = (peers[i].port & 0xff);
}
free (peers);
sendto(usi->sock, (char*)buff, bSize, 0, (SOCKADDR*)remote, sizeof(SOCKADDR_IN));
// Add peer to list:
pE.downloaded = req->downloaded;
pE.uploaded = req->uploaded;
pE.left = req->left;
pE.peer_id = req->peer_id;
if (req->ip_address == 0) // default
{
pE.ip = m_hton32 (remote->sin_addr.s_addr);
}
else
{
pE.ip = req->ip_address;
}
pE.port = req->port;
db_add_peer(usi->conn, req->info_hash, &pE);
return 0;
}
static int _handle_scrape (udpServerInstance *usi, SOCKADDR_IN *remote, char *data, int len)
{
ScrapeRequest *sR;
int v, // validation helper
c, // torrent counter
i, // loop counter
j; // loop counter
uint8_t hash [20];
char xHash [50];
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)
{
_send_error (usi, remote, sR->transaction_id, "Bad scrape request.");
return 0;
}
// get torrent count.
c = v / 20;
resp = (ScrapeResponse*)buffer;
resp->action = m_hton32 (2);
resp->transaction_id = sR->transaction_id;
for (i = 0;i < c;i++)
{
int32_t s, c, l;
int32_t *seeders,
*completed,
*leechers;
for (j = 0; j < 20;j++)
hash[j] = data[j + (i*20)+16];
to_hex_str (hash, xHash);
printf("\t%s\n", xHash);
seeders = (int32_t*)&buffer[i*12+8];
completed = (int32_t*)&buffer[i*12+12];
leechers = (int32_t*)&buffer[i*12+16];
db_get_stats (usi->conn, hash, &s, &l, &c);
*seeders = m_hton32 (s);
*completed = m_hton32 (c);
*leechers = m_hton32 (l);
}
fflush (stdout);
sendto (usi->sock, (const char*)buffer, sizeof(buffer), 0, (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;
}
static int _resolve_request (udpServerInstance *usi, SOCKADDR_IN *remote, char *data, int r)
{
ConnectionRequest *cR;
uint32_t action;
cR = (ConnectionRequest*)data;
action = m_hton32(cR->action);
if ((usi->settings & UDPT_ALLOW_IANA_IP) > 0)
{
if (_isIANA_IP (remote->sin_addr.s_addr))
{
return 0; // Access Denied: IANA reserved IP.
}
}
printf(":: %x:%u ACTION=%d\n", (unsigned int)remote->sin_addr.s_addr , remote->sin_port, action);
if (action == 0 && r >= 16)
return _handle_connection(usi, remote, data);
else if (action == 1 && r >= 98)
return _handle_announce(usi, remote, data);
else if (action == 2)
return _handle_scrape (usi, remote, data, r);
else
{
printf("E: action=%d; r=%d\n", action, r);
_send_error(usi, remote, cR->transaction_id, "Tracker couldn't understand Client's request.");
return -1;
}
return 0;
}
#ifdef WIN32
static DWORD _thread_start (LPVOID arg)
#elif defined (linux)
static void* _thread_start (void *arg)
#endif
{
udpServerInstance *usi;
SOCKADDR_IN remoteAddr;
int addrSz,
r;
char tmpBuff [UDP_BUFFER_SIZE];
usi = arg;
addrSz = sizeof (SOCKADDR_IN);
while ((usi->flags & FLAG_RUNNING) > 0)
{
fflush(stdout);
// peek into the first 12 bytes of data; determine if connection request or announce request.
r = recvfrom(usi->sock, tmpBuff, UDP_BUFFER_SIZE, 0, (SOCKADDR*)&remoteAddr, (unsigned*)&addrSz);
if (r <= 0)
continue; // bad request...
r = _resolve_request(usi, &remoteAddr, tmpBuff, r);
}
#ifdef linux
pthread_exit (NULL);
#endif
return 0;
}
#ifdef WIN32
static DWORD _maintainance_start (LPVOID arg)
#elif defined (linux)
static void* _maintainance_start (void *arg)
#endif
{
udpServerInstance *usi;
usi = (udpServerInstance *)arg;
while ((usi->flags & FLAG_RUNNING) > 0)
{
db_cleanup (usi->conn);
#ifdef WIN32
Sleep (usi->cleanup_interval * 1000); // wait 2 minutes between every cleanup.
#elif defined (linux)
sleep (usi->cleanup_interval);
#else
#error Unsupported OS.
#endif
}
return 0;
}

516
src/udpTracker.cpp Normal file
View file

@ -0,0 +1,516 @@
/*
* 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 "multiplatform.h"
#include "udpTracker.hpp"
#include "tools.h"
#include <stdlib.h>
#include <time.h>
#include <iostream>
using namespace std;
#define UDP_BUFFER_SIZE 2048
namespace UDPT
{
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;
}
UDPTracker::UDPTracker (Settings *settings)
{
Settings::SettingClass *sc_tracker;
uint8_t n_settings = 0;
string s_port, // port
s_threads, // threads
s_allow_remotes, // remotes allowed?
s_allow_iana_ip, // IANA IPs allowed?
s_int_announce, // announce interval
s_int_cleanup; // cleanup interval
sc_tracker = settings->getClass("tracker");
s_port = sc_tracker->get ("port");
s_threads = sc_tracker->get ("threads");
s_allow_remotes = sc_tracker->get ("allow_remotes");
s_allow_iana_ip = sc_tracker->get ("allow_iana_ips");
s_int_announce = sc_tracker->get ("announce_interval");
s_int_cleanup = sc_tracker-> get ("cleanup_interval");
if (_isTrue(s_allow_remotes) == 1)
n_settings |= UDPT_ALLOW_REMOTE_IP;
if (_isTrue(s_allow_iana_ip) != 0)
n_settings |= UDPT_ALLOW_IANA_IP;
this->announce_interval = (s_int_announce == "" ? 1800 : atoi (s_int_announce.c_str()));
this->cleanup_interval = (s_int_cleanup == "" ? 120 : atoi (s_int_cleanup.c_str()));
this->port = (s_port == "" ? 6969 : atoi (s_port.c_str()));
this->thread_count = (s_threads == "" ? 5 : atoi (s_threads.c_str())) + 1;
cout << "port=" << this->port << endl;
this->threads = new HANDLE[this->thread_count];
this->isRunning = false;
this->conn = NULL;
this->settings = n_settings;
this->o_settings = settings;
}
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 (usi->threads[i]);
pthread_cancel (usi->threads[i]);
#endif
cout << "Thread (" << ( i + 1) << "/" << ((int)this->thread_count) << ") terminated." << endl;
}
if (this->conn != NULL)
db_close(this->conn);
delete[] this->threads;
}
enum UDPTracker::StartStatus UDPTracker::start ()
{
SOCKET sock;
SOCKADDR_IN recvAddr;
int r, // saves results
i, // loop index
yup; // just to set TRUE
string dbname;// saves the Database name.
sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock == INVALID_SOCKET)
return START_ESOCKET_FAILED;
#ifdef WIN32
recvAddr.sin_addr.S_un.S_addr = 0L;
#elif defined (linux)
recvAddr.sin_addr.s_addr = 0L;
#endif
recvAddr.sin_family = AF_INET;
recvAddr.sin_port = m_hton16 (this->port);
yup = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&yup, 1);
r = bind (sock, (SOCKADDR*)&recvAddr, sizeof(SOCKADDR_IN));
if (r == SOCKET_ERROR)
{
#ifdef WIN32
closesocket (sock);
#elif defined (linux)
close (sock);
#endif
return START_EBIND_FAILED;
}
this->sock = sock;
dbname = this->o_settings->get ("database", "file");
if (dbname == "")
dbname = "tracker.db";
db_open(&this->conn, dbname.c_str());
this->isRunning = true;
cout << "Starting maintenance thread (1/" << ((int)this->thread_count) << ")" << endl;
// 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 (&usi->threads[0], NULL, _maintainance_start, usi);
#endif
for (i = 1;i < this->thread_count; i++)
{
cout << "Starting thread (" << (i + 1) << "/" << ((int)this->thread_count) << ")" << endl;
#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, this);
#endif
}
return START_OK;
}
static uint64_t _get_connID (SOCKADDR_IN *remote)
{
int base;
uint64_t x;
base = time(NULL);
base /= 3600; // changes every hour.
x = base;
x += remote->sin_addr.s_addr;
return x;
}
int UDPTracker::sendError (UDPTracker *usi, SOCKADDR_IN *remote, uint32_t transactionID, const string &msg)
{
struct udp_error_response error;
int msg_sz, // message size to send.
i; // copy loop
char buff [1024]; // more than reasonable message size...
error.action = m_hton32 (3);
error.transaction_id = transactionID;
error.message = (char*)msg.c_str();
msg_sz = 4 + 4 + 1 + msg.length();
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));
return 0;
}
int UDPTracker::handleConnection (UDPTracker *usi, SOCKADDR_IN *remote, char *data)
{
ConnectionRequest *req;
ConnectionResponse resp;
req = (ConnectionRequest*)data;
resp.action = m_hton32(0);
resp.transaction_id = req->transaction_id;
resp.connection_id = _get_connID(remote);
sendto(usi->sock, (char*)&resp, sizeof(ConnectionResponse), 0, (SOCKADDR*)remote, sizeof(SOCKADDR_IN));
return 0;
}
int UDPTracker::handleAnnounce (UDPTracker *usi, SOCKADDR_IN *remote, char *data)
{
AnnounceRequest *req;
AnnounceResponse *resp;
int q, // peer counts
bSize, // message size
i; // loop index
db_peerEntry *peers;
int32_t seeders,
leechers,
completed;
db_peerEntry pE; // info for DB
uint8_t buff [1028]; // Reasonable buffer size. (header+168 peers)
req = (AnnounceRequest*)data;
if (req->connection_id != _get_connID(remote))
{
return 1;
}
// change byte order:
req->port = m_hton16 (req->port);
req->ip_address = m_hton32 (req->ip_address);
req->downloaded = m_hton64 (req->downloaded);
req->event = m_hton32 (req->event); // doesn't really matter for this tracker
req->uploaded = m_hton64 (req->uploaded);
req->num_want = m_hton32 (req->num_want);
req->left = m_hton64 (req->left);
if ((usi->settings & UDPT_ALLOW_REMOTE_IP) == 0 && req->ip_address != 0)
{
UDPTracker::sendError (usi, remote, req->transaction_id, "Tracker doesn't allow remote IP's; Request ignored.");
return 0;
}
// load peers
q = 30;
if (req->num_want >= 1)
q = min (q, req->num_want);
peers = (db_peerEntry*)malloc (sizeof(db_peerEntry) * q);
db_load_peers(usi->conn, req->info_hash, peers, &q);
bSize = 20; // header is 20 bytes
bSize += (6 * q); // + 6 bytes per peer.
db_get_stats (usi->conn, req->info_hash, &seeders, &leechers, &completed);
resp = (AnnounceResponse*)buff;
resp->action = m_hton32(1);
resp->interval = m_hton32 ( usi->announce_interval );
resp->leechers = m_hton32(leechers);
resp->seeders = m_hton32 (seeders);
resp->transaction_id = req->transaction_id;
for (i = 0;i < q;i++)
{
int x = i * 6;
// network byte order!!!
// IP
buff[20 + x] = ((peers[i].ip & (0xff << 24)) >> 24);
buff[21 + x] = ((peers[i].ip & (0xff << 16)) >> 16);
buff[22 + x] = ((peers[i].ip & (0xff << 8)) >> 8);
buff[23 + x] = (peers[i].ip & 0xff);
// port
buff[24 + x] = ((peers[i].port & (0xff << 8)) >> 8);
buff[25 + x] = (peers[i].port & 0xff);
}
free (peers);
sendto(usi->sock, (char*)buff, bSize, 0, (SOCKADDR*)remote, sizeof(SOCKADDR_IN));
// Add peer to list:
pE.downloaded = req->downloaded;
pE.uploaded = req->uploaded;
pE.left = req->left;
pE.peer_id = req->peer_id;
if (req->ip_address == 0) // default
{
pE.ip = m_hton32 (remote->sin_addr.s_addr);
}
else
{
pE.ip = req->ip_address;
}
pE.port = req->port;
db_add_peer(usi->conn, req->info_hash, &pE);
return 0;
}
int UDPTracker::handleScrape (UDPTracker *usi, SOCKADDR_IN *remote, char *data, int len)
{
ScrapeRequest *sR;
int v, // validation helper
c, // torrent counter
i, // loop counter
j; // loop counter
uint8_t hash [20];
char xHash [50];
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.");
return 0;
}
// get torrent count.
c = v / 20;
resp = (ScrapeResponse*)buffer;
resp->action = m_hton32 (2);
resp->transaction_id = sR->transaction_id;
for (i = 0;i < c;i++)
{
int32_t s, c, l;
int32_t *seeders,
*completed,
*leechers;
for (j = 0; j < 20;j++)
hash[j] = data[j + (i*20)+16];
to_hex_str (hash, xHash);
cout << "\t" << xHash << endl;
seeders = (int32_t*)&buffer[i*12+8];
completed = (int32_t*)&buffer[i*12+12];
leechers = (int32_t*)&buffer[i*12+16];
db_get_stats (usi->conn, hash, &s, &l, &c);
*seeders = m_hton32 (s);
*completed = m_hton32 (c);
*leechers = m_hton32 (l);
}
cout.flush();
sendto (usi->sock, (const char*)buffer, sizeof(buffer), 0, (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)
{
ConnectionRequest *cR;
uint32_t action;
cR = (ConnectionRequest*)data;
action = m_hton32(cR->action);
if ((usi->settings & UDPT_ALLOW_IANA_IP) == 0)
{
if (_isIANA_IP (remote->sin_addr.s_addr))
{
return 0; // Access Denied: IANA reserved IP.
}
}
cout << ":: " << (void*)remote->sin_addr.s_addr << ": " << remote->sin_port << " ACTION=" << action << endl;
if (action == 0 && r >= 16)
return UDPTracker::handleConnection (usi, remote, data);
else if (action == 1 && r >= 98)
return UDPTracker::handleAnnounce (usi, remote, data);
else if (action == 2)
return UDPTracker::handleScrape (usi, remote, data, r);
else
{
cout << "E: action=" << action << ", r=" << r << endl;
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
{
UDPTracker *usi;
SOCKADDR_IN remoteAddr;
int addrSz,
r;
char tmpBuff [UDP_BUFFER_SIZE];
usi = (UDPTracker*)arg;
addrSz = sizeof (SOCKADDR_IN);
while (usi->isRunning)
{
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);
if (r <= 0)
continue; // bad request...
r = UDPTracker::resolveRequest (usi, &remoteAddr, tmpBuff, r);
}
#ifdef linux
pthread_exit (NULL);
#endif
return 0;
}
#ifdef WIN32
DWORD UDPTracker::_maintainance_start (LPVOID arg)
#elif defined (linux)
void* UDPTracker::_maintainance_start (void *arg)
#endif
{
UDPTracker *usi;
usi = (UDPTracker *)arg;
while (usi->isRunning)
{
db_cleanup (usi->conn);
#ifdef WIN32
Sleep (usi->cleanup_interval * 1000); // wait 2 minutes between every cleanup.
#elif defined (linux)
sleep (usi->cleanup_interval);
#else
#error Unsupported OS.
#endif
}
return 0;
}
};

View file

@ -1,148 +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/>.
*/
#ifndef UDPTRACKER_H_
#define UDPTRACKER_H_
#include <stdint.h>
#include "multiplatform.h"
#include "db/database.h"
#include "settings.h"
struct udp_connection_request
{
uint64_t connection_id;
uint32_t action;
uint32_t transaction_id;
};
struct udp_connection_response
{
uint32_t action;
uint32_t transaction_id;
uint64_t connection_id;
};
struct udp_announce_request
{
uint64_t connection_id;
uint32_t action;
uint32_t transaction_id;
uint8_t info_hash [20];
uint8_t peer_id [20];
uint64_t downloaded;
uint64_t left;
uint64_t uploaded;
uint32_t event;
uint32_t ip_address;
uint32_t key;
int32_t num_want;
uint16_t port;
};
struct udp_announce_response
{
uint32_t action;
uint32_t transaction_id;
uint32_t interval;
uint32_t leechers;
uint32_t seeders;
uint8_t *peer_list_data;
};
struct udp_scrape_request
{
uint64_t connection_id;
uint32_t action;
uint32_t transaction_id;
uint8_t *torrent_list_data;
};
struct udp_scrape_response
{
uint32_t action;
uint32_t transaction_id;
uint8_t *data;
};
struct udp_error_response
{
uint32_t action;
uint32_t transaction_id;
char *message;
};
#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?)
typedef struct
{
SOCKET sock;
uint16_t port;
uint8_t thread_count;
uint8_t flags;
uint8_t settings;
HANDLE *threads;
uint32_t announce_interval;
uint32_t cleanup_interval;
Settings *o_settings;
dbConnection *conn;
} udpServerInstance;
typedef struct udp_connection_request ConnectionRequest;
typedef struct udp_connection_response ConnectionResponse;
typedef struct udp_announce_request AnnounceRequest;
typedef struct udp_announce_response AnnounceResponse;
typedef struct udp_scrape_request ScrapeRequest;
typedef struct udp_scrape_response ScrapeResponse;
typedef struct udp_error_response ErrorResponse;
/**
* Initializes the UDP Tracker.
* @param usi The Instancfe to initialize.
* @param port The port to bind the server to
* @param threads Amount of threads to start the server with.
*/
void UDPTracker_init (udpServerInstance *usi, Settings *);
/**
* Destroys resources that were created by UDPTracker_init.
* @param usi Instance to destroy.
*/
void UDPTracker_destroy (udpServerInstance *usi);
/**
* Starts the Initialized instance.
* @param usi Instance to start
* @return 0 on success, otherwise non-zero.
*/
int UDPTracker_start (udpServerInstance *usi);
#endif /* UDPTRACKER_H_ */

166
src/udpTracker.hpp Normal file
View file

@ -0,0 +1,166 @@
/*
* 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/>.
*/
#ifndef UDPTRACKER_H_
#define UDPTRACKER_H_
#include <stdint.h>
#include "multiplatform.h"
#include "db/database.h"
#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?)
namespace UDPT
{
class UDPTracker
{
public:
typedef struct udp_connection_request
{
uint64_t connection_id;
uint32_t action;
uint32_t transaction_id;
} ConnectionRequest;
typedef struct udp_connection_response
{
uint32_t action;
uint32_t transaction_id;
uint64_t connection_id;
} ConnectionResponse;
typedef struct udp_announce_request
{
uint64_t connection_id;
uint32_t action;
uint32_t transaction_id;
uint8_t info_hash [20];
uint8_t peer_id [20];
uint64_t downloaded;
uint64_t left;
uint64_t uploaded;
uint32_t event;
uint32_t ip_address;
uint32_t key;
int32_t num_want;
uint16_t port;
} AnnounceRequest;
typedef struct udp_announce_response
{
uint32_t action;
uint32_t transaction_id;
uint32_t interval;
uint32_t leechers;
uint32_t seeders;
uint8_t *peer_list_data;
} AnnounceResponse;
typedef struct udp_scrape_request
{
uint64_t connection_id;
uint32_t action;
uint32_t transaction_id;
uint8_t *torrent_list_data;
} ScrapeRequest;
typedef struct udp_scrape_response
{
uint32_t action;
uint32_t transaction_id;
uint8_t *data;
} ScrapeResponse;
typedef struct udp_error_response
{
uint32_t action;
uint32_t transaction_id;
char *message;
} ErrorResponse;
enum StartStatus
{
START_OK = 0,
START_ESOCKET_FAILED = 1,
START_EBIND_FAILED = 2
};
/**
* Initializes the UDP Tracker.
* @param settings Settings to start server with
*/
UDPTracker (Settings *);
/**
* Starts the Initialized instance.
* @return 0 on success, otherwise non-zero.
*/
enum StartStatus start ();
/**
* Destroys resources that were created by constructor
* @param usi Instance to destroy.
*/
virtual ~UDPTracker ();
private:
SOCKET sock;
uint16_t port;
uint8_t thread_count;
bool isRunning;
HANDLE *threads;
uint32_t announce_interval;
uint32_t cleanup_interval;
uint8_t settings;
Settings *o_settings;
dbConnection *conn;
#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 int resolveRequest (UDPTracker *usi, SOCKADDR_IN *remote, char *data, int r);
static int handleConnection (UDPTracker *usi, SOCKADDR_IN *remote, char *data);
static int handleAnnounce (UDPTracker *usi, SOCKADDR_IN *remote, char *data);
static int handleScrape (UDPTracker *usi, SOCKADDR_IN *remote, char *data, int len);
static int sendError (UDPTracker *, SOCKADDR_IN *remote, uint32_t transId, const string &);
};
};
#endif /* UDPTRACKER_H_ */