New Database Interface,
The Ability to add more Database Drivers.
This commit is contained in:
parent
51e2c80ba7
commit
d1c49c6b6f
121
src/db/database.cpp
Normal file
121
src/db/database.cpp
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* 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 "database.hpp"
|
||||
|
||||
namespace UDPT
|
||||
{
|
||||
namespace Data
|
||||
{
|
||||
DatabaseDriver::DatabaseDriver(Settings::SettingClass *sc, bool isDynamic)
|
||||
{
|
||||
this->dClass = sc;
|
||||
this->is_dynamic = isDynamic;
|
||||
}
|
||||
|
||||
bool DatabaseDriver::addTorrent(uint8_t hash [20])
|
||||
{
|
||||
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
bool DatabaseDriver::removeTorrent(uint8_t hash[20])
|
||||
{
|
||||
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
bool DatabaseDriver::isDynamic()
|
||||
{
|
||||
return this->is_dynamic;
|
||||
}
|
||||
|
||||
bool DatabaseDriver::genConnectionId(uint64_t *cid, uint32_t ip, uint16_t port)
|
||||
{
|
||||
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
bool DatabaseDriver::verifyConnectionId(uint64_t cid, uint32_t ip, uint16_t port)
|
||||
{
|
||||
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
bool DatabaseDriver::updatePeer(uint8_t peer_id [20], uint8_t info_hash [20],
|
||||
uint32_t ip, uint16_t port,
|
||||
int64_t downloaded, int64_t left, int64_t uploaded,
|
||||
enum TrackerEvents event)
|
||||
{
|
||||
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
bool DatabaseDriver::removePeer (uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port)
|
||||
{
|
||||
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
bool DatabaseDriver::getTorrentInfo (TorrentEntry *e)
|
||||
{
|
||||
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
bool DatabaseDriver::getPeers (uint8_t info_hash [20], int *max_count, PeerEntry *pe)
|
||||
{
|
||||
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
void DatabaseDriver::cleanup()
|
||||
{
|
||||
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
bool DatabaseDriver::isTorrentAllowed(uint8_t info_hash[20])
|
||||
{
|
||||
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
DatabaseDriver::~DatabaseDriver()
|
||||
{
|
||||
}
|
||||
|
||||
/*-- Exceptions --*/
|
||||
static const char *EMessages[] = {
|
||||
"Unknown Error",
|
||||
"Not Implemented",
|
||||
"Failed to connect to database"
|
||||
};
|
||||
|
||||
DatabaseException::DatabaseException()
|
||||
{
|
||||
this->errorNum = E_UNKNOWN;
|
||||
}
|
||||
|
||||
DatabaseException::DatabaseException(enum EType e)
|
||||
{
|
||||
this->errorNum = e;
|
||||
}
|
||||
|
||||
enum DatabaseException::EType DatabaseException::getErrorType()
|
||||
{
|
||||
return this->errorNum;
|
||||
}
|
||||
|
||||
const char* DatabaseException::getErrorMessage()
|
||||
{
|
||||
return EMessages[this->errorNum];
|
||||
}
|
||||
};
|
||||
};
|
|
@ -1,105 +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 DATABASE_H_
|
||||
#define DATABASE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct dbConnection dbConnection;
|
||||
|
||||
/**
|
||||
* Opens a database connection.
|
||||
* @param pdb Pointer to database instance.
|
||||
* @param cStr Connection string for the active driver.
|
||||
* @return 0 on success; otherwise non-zero.
|
||||
*/
|
||||
int db_open (dbConnection **pdb, const char *cStr);
|
||||
|
||||
/**
|
||||
* Closes the database connection.
|
||||
* @param db Database instance.
|
||||
* @return 0 on success; otherwise non-zero.
|
||||
*/
|
||||
int db_close (dbConnection *db);
|
||||
|
||||
typedef struct {
|
||||
uint8_t *peer_id;
|
||||
uint64_t downloaded;
|
||||
uint64_t uploaded;
|
||||
uint64_t left;
|
||||
|
||||
uint32_t ip; // currently only support IPv4.
|
||||
uint16_t port;
|
||||
} db_peerEntry;
|
||||
|
||||
/**
|
||||
* Adds/Updates the list of peers.
|
||||
* @param db The database's instance.
|
||||
* @param hash The info_hash of the torrent.
|
||||
* @param pE Peer's information.
|
||||
* @return 0 on success; otherwise non-zero.
|
||||
*/
|
||||
int db_add_peer (dbConnection *db, uint8_t hash[20], db_peerEntry *pE);
|
||||
|
||||
/**
|
||||
* Loads peers for the requested torrent.
|
||||
* @param db Database instance.
|
||||
* @param hash The info_hash of the requested torrent.
|
||||
* @param lst A allocated array to store results in.
|
||||
* @param sZ in: The maximum amount of entries to load. out: Amount of loaded entries.
|
||||
* @return 0 on success; otherwise non-zero.
|
||||
*/
|
||||
int db_load_peers (dbConnection *db, uint8_t hash[20], db_peerEntry *lst, int *sZ);
|
||||
|
||||
/**
|
||||
* Gets stats for the requested torrent.
|
||||
* @param db The Database connection
|
||||
* @param hash info_hash of the torrent.
|
||||
* @param seeders Returns the Seeders for the requested torrent.
|
||||
* @param leechers Returns the Leechers for the requested torrent.
|
||||
* @param completed Returns the count of completed downloaded reported.
|
||||
* @return 0 on success, otherwise non-zero.
|
||||
*/
|
||||
int db_get_stats (dbConnection *db, uint8_t hash[20], int32_t *seeders, int32_t *leechers, int32_t *completed);
|
||||
|
||||
/**
|
||||
* Maintenance routine, Calculates stats & releases space from old entries.
|
||||
* @param db The database connection.
|
||||
* @return 0 on success; otherwise non-zero.
|
||||
*/
|
||||
int db_cleanup (dbConnection *db);
|
||||
|
||||
/**
|
||||
* Deletes a peer from the database.
|
||||
* @param db Database connection
|
||||
* @param hash info_hash of the torrent.
|
||||
* @param pE The peer's information.
|
||||
* @return 0 on success; otherwise non-zero.
|
||||
*/
|
||||
int db_remove_peer (dbConnection *db, uint8_t hash [20], db_peerEntry *pE);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* DATABASE_H_ */
|
173
src/db/database.hpp
Normal file
173
src/db/database.hpp
Normal file
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* 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 DATABASE_HPP_
|
||||
#define DATABASE_HPP_
|
||||
|
||||
#include "../settings.hpp"
|
||||
|
||||
namespace UDPT
|
||||
{
|
||||
namespace Data
|
||||
{
|
||||
class DatabaseException
|
||||
{
|
||||
public:
|
||||
enum EType {
|
||||
E_UNKNOWN = 0, // Unknown error
|
||||
E_NOT_IMPLEMENTED = 1, // not implemented
|
||||
E_CONNECTION_FAILURE = 2
|
||||
};
|
||||
|
||||
DatabaseException ();
|
||||
DatabaseException (EType);
|
||||
EType getErrorType ();
|
||||
const char* getErrorMessage ();
|
||||
private:
|
||||
EType errorNum;
|
||||
};
|
||||
|
||||
class DatabaseDriver
|
||||
{
|
||||
public:
|
||||
typedef struct {
|
||||
uint8_t *info_hash;
|
||||
int32_t seeders;
|
||||
int32_t leechers;
|
||||
int32_t completed;
|
||||
} TorrentEntry;
|
||||
typedef struct {
|
||||
uint32_t ip;
|
||||
uint16_t port;
|
||||
} PeerEntry;
|
||||
|
||||
enum TrackerEvents {
|
||||
EVENT_UNSPEC = 0,
|
||||
EVENT_COMPLETE = 1,
|
||||
EVENT_START = 2,
|
||||
EVENT_STOP = 3
|
||||
};
|
||||
|
||||
/**
|
||||
* Opens the DB's connection
|
||||
* @param dClass Settings class ('database' class).
|
||||
*/
|
||||
DatabaseDriver (Settings::SettingClass *dClass, 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]);
|
||||
|
||||
/**
|
||||
* Removes a torrent from the database. should be used only for non-dynamic trackers or by cleanup.
|
||||
* @param hash The info_hash to drop.
|
||||
* @return true if torrent's database was dropped or no longer exists. otherwise false (shouldn't happen - critical)
|
||||
*/
|
||||
virtual bool removeTorrent (uint8_t hash[20]);
|
||||
|
||||
/**
|
||||
* Checks if the Database is acting as a dynamic tracker DB.
|
||||
* @return true if dynamic. otherwise false.
|
||||
*/
|
||||
bool isDynamic ();
|
||||
|
||||
/**
|
||||
* Checks if the torrent can be used in the tracker.
|
||||
* @param info_hash The torrent's info_hash.
|
||||
* @return true if allowed. otherwise false.
|
||||
*/
|
||||
virtual bool isTorrentAllowed (uint8_t info_hash [20]);
|
||||
|
||||
/**
|
||||
* Generate a Connection ID for the peer.
|
||||
* @param connectionId (Output) the generated connection ID.
|
||||
* @param ip The peer's IP (requesting peer. not remote)
|
||||
* @param port The peer's IP (remote port if tracker accepts)
|
||||
* @return
|
||||
*/
|
||||
virtual bool genConnectionId (uint64_t *connectionId, uint32_t ip, uint16_t port);
|
||||
|
||||
virtual bool verifyConnectionId (uint64_t connectionId, uint32_t ip, uint16_t port);
|
||||
|
||||
/**
|
||||
* Updates/Adds a peer to/in the database.
|
||||
* @param peer_id the peer's peer_id
|
||||
* @param info_hash the torrent info_hash
|
||||
* @param ip IP of peer (remote ip if tracker accepts)
|
||||
* @param port TCP port of peer (remote port if tracker accepts)
|
||||
* @param downloaded total Bytes downloaded
|
||||
* @param left total bytes left
|
||||
* @param uploaded total bytes uploaded
|
||||
* @return true on success, false on failure.
|
||||
*/
|
||||
virtual bool updatePeer (uint8_t peer_id [20], uint8_t info_hash [20],
|
||||
uint32_t ip, uint16_t port,
|
||||
int64_t downloaded, int64_t left, int64_t uploaded,
|
||||
enum TrackerEvents event);
|
||||
|
||||
/**
|
||||
* Remove a peer from a torrent (if stop action occurred, or if peer is inactive in cleanup)
|
||||
* @param peer_id The peer's peer_id
|
||||
* @param info_hash Torrent's info_hash
|
||||
* @param ip The IP of the peer (remote IP if tracker accepts)
|
||||
* @param port The TCP port (remote port if tracker accepts)
|
||||
* @return true on success. false on failure (shouldn't happen - critical)
|
||||
*/
|
||||
virtual bool removePeer (uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port);
|
||||
|
||||
/**
|
||||
* Gets stats on a torrent
|
||||
* @param e TorrentEntry, only this info_hash has to be set
|
||||
* @return true on success, false on failure.
|
||||
*/
|
||||
virtual bool getTorrentInfo (TorrentEntry *e);
|
||||
|
||||
/**
|
||||
* Gets a list of peers from the database.
|
||||
* @param info_hash The torrent's info_hash
|
||||
* @param max_count The maximum amount of peers to load from the database. The amount of loaded peers is returned through this variable.
|
||||
* @param pe The list of peers. Must be pre-allocated to the size of max_count.
|
||||
* @return true on success, otherwise false (shouldn't happen).
|
||||
*/
|
||||
virtual bool getPeers (uint8_t info_hash [20], int *max_count, PeerEntry *pe);
|
||||
|
||||
/**
|
||||
* Cleanup the database.
|
||||
* Other actions may be locked when using this depending on the driver.
|
||||
*/
|
||||
virtual void cleanup ();
|
||||
|
||||
/**
|
||||
* Closes the connections, and releases all other resources.
|
||||
*/
|
||||
virtual ~DatabaseDriver ();
|
||||
|
||||
protected:
|
||||
Settings::SettingClass *dClass;
|
||||
private:
|
||||
bool is_dynamic;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
#endif /* DATABASE_HPP_ */
|
|
@ -1,343 +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 "database.h"
|
||||
#include "../multiplatform.h"
|
||||
#include "../tools.h"
|
||||
#include <sqlite3.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
struct dbConnection
|
||||
{
|
||||
sqlite3 *db;
|
||||
HANDLE janitor;
|
||||
};
|
||||
|
||||
static const char hexadecimal[] = "0123456789abcdef";
|
||||
|
||||
void _to_hex_str (const uint8_t *hash, char *data)
|
||||
{
|
||||
int i;
|
||||
for (i = 0;i < 20;i++)
|
||||
{
|
||||
data[i * 2] = hexadecimal[hash[i] / 16];
|
||||
data[i * 2 + 1] = hexadecimal[hash[i] % 16];
|
||||
}
|
||||
data[40] = '\0';
|
||||
}
|
||||
|
||||
static int _db_make_torrent_table (sqlite3 *db, char *hash)
|
||||
{
|
||||
char sql [2000];
|
||||
char *err_msg;
|
||||
int r;
|
||||
|
||||
sql[0] = '\0';
|
||||
|
||||
strcat(sql, "CREATE TABLE IF NOT EXISTS 't");
|
||||
strcat(sql, hash);
|
||||
strcat (sql, "' (");
|
||||
|
||||
strcat (sql, "peer_id blob(20),"
|
||||
"ip blob(4),"
|
||||
"port blob(2),"
|
||||
"uploaded blob(8)," // uint64
|
||||
"downloaded blob(8),"
|
||||
"left blob(8),"
|
||||
"last_seen INT DEFAULT 0");
|
||||
|
||||
strcat(sql, ", CONSTRAINT c1 UNIQUE (ip,port) ON CONFLICT REPLACE)");
|
||||
|
||||
// create table.
|
||||
r = sqlite3_exec(db, sql, NULL, NULL, &err_msg);
|
||||
printf("E:%s\n", err_msg);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void _db_setup (sqlite3 *db)
|
||||
{
|
||||
sqlite3_exec(db, "CREATE TABLE stats ("
|
||||
"info_hash blob(20) UNIQUE,"
|
||||
"completed INTEGER DEFAULT 0,"
|
||||
"leechers INTEGER DEFAULT 0,"
|
||||
"seeders INTEGER DEFAULT 0,"
|
||||
"last_mod INTEGER DEFAULT 0"
|
||||
")", NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
int db_open (dbConnection **db, const char *cStr)
|
||||
{
|
||||
FILE *f;
|
||||
int doSetup, // check if to build DB, or it already exists?
|
||||
r;
|
||||
|
||||
f = fopen (cStr, "rb");
|
||||
doSetup = 0;
|
||||
if (f == NULL)
|
||||
doSetup = 1;
|
||||
else
|
||||
fclose (f);
|
||||
|
||||
*db = malloc (sizeof(struct dbConnection));
|
||||
r = sqlite3_open (cStr, &((*db)->db));
|
||||
if (doSetup)
|
||||
_db_setup((*db)->db);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int db_close (dbConnection *db)
|
||||
{
|
||||
int r = sqlite3_close(db->db);
|
||||
free (db);
|
||||
return r;
|
||||
}
|
||||
|
||||
int db_add_peer (dbConnection *db, uint8_t info_hash[20], db_peerEntry *pE)
|
||||
{
|
||||
char xHash [50]; // we just need 40 + \0 = 41.
|
||||
sqlite3_stmt *stmt;
|
||||
char sql [1000];
|
||||
int r;
|
||||
|
||||
char *hash = xHash;
|
||||
to_hex_str(info_hash, hash);
|
||||
|
||||
_db_make_torrent_table(db->db, hash);
|
||||
|
||||
|
||||
sql[0] = '\0';
|
||||
strcat(sql, "REPLACE INTO 't");
|
||||
strcat(sql, hash);
|
||||
strcat(sql, "' (peer_id,ip,port,uploaded,downloaded,left,last_seen) VALUES (?,?,?,?,?,?,?)");
|
||||
|
||||
// printf("IP->%x::%u\n", pE->ip, pE->port);
|
||||
|
||||
sqlite3_prepare(db->db, sql, -1, &stmt, NULL);
|
||||
|
||||
sqlite3_bind_blob(stmt, 1, (void*)pE->peer_id, 20, NULL);
|
||||
sqlite3_bind_blob(stmt, 2, (void*)&pE->ip, 4, NULL);
|
||||
sqlite3_bind_blob(stmt, 3, (void*)&pE->port, 2, NULL);
|
||||
sqlite3_bind_blob(stmt, 4, (void*)&pE->uploaded, 8, NULL);
|
||||
sqlite3_bind_blob(stmt, 5, (void*)&pE->downloaded, 8, NULL);
|
||||
sqlite3_bind_blob(stmt, 6, (void*)&pE->left, 8, NULL);
|
||||
sqlite3_bind_int(stmt, 7, time(NULL));
|
||||
|
||||
r = sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
strcpy(sql, "REPLACE INTO stats (info_hash,last_mod) VALUES (?,?)");
|
||||
sqlite3_prepare (db->db, sql, -1, &stmt, NULL);
|
||||
sqlite3_bind_blob (stmt, 1, hash, 20, NULL);
|
||||
sqlite3_bind_int (stmt, 2, time(NULL));
|
||||
sqlite3_step (stmt);
|
||||
sqlite3_finalize (stmt);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int db_load_peers (dbConnection *db, uint8_t info_hash[20], db_peerEntry *lst, int *sZ)
|
||||
{
|
||||
char sql [1000];
|
||||
char hash [50];
|
||||
sqlite3_stmt *stmt;
|
||||
int r,
|
||||
i;
|
||||
|
||||
sql[0] = '\0';
|
||||
|
||||
to_hex_str(info_hash, hash);
|
||||
|
||||
strcat(sql, "SELECT ip,port FROM 't");
|
||||
strcat(sql, hash);
|
||||
strcat(sql, "' LIMIT ?");
|
||||
|
||||
sqlite3_prepare(db->db, sql, -1, &stmt, NULL);
|
||||
sqlite3_bind_int(stmt, 1, *sZ);
|
||||
|
||||
i = 0;
|
||||
while (*sZ > i)
|
||||
{
|
||||
r = sqlite3_step(stmt);
|
||||
if (r == SQLITE_ROW)
|
||||
{
|
||||
const char *ip = (const char*)sqlite3_column_blob (stmt, 0);
|
||||
const char *port = (const char*)sqlite3_column_blob (stmt, 1);
|
||||
|
||||
memcpy(&lst[i].ip, ip, 4);
|
||||
memcpy(&lst[i].port, port, 2);
|
||||
|
||||
i++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
printf("%d Clients Dumped.\n", i);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
*sZ = i;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int db_get_stats (dbConnection *db, uint8_t hash[20], int32_t *seeders, int32_t *leechers, int32_t *completed)
|
||||
{
|
||||
const char sql[] = "SELECT seeders,leechers,completed FROM 'stats' WHERE info_hash=?";
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
*seeders = 0;
|
||||
*leechers = 0;
|
||||
*completed = 0;
|
||||
|
||||
|
||||
sqlite3_prepare (db->db, sql, -1, &stmt, NULL);
|
||||
sqlite3_bind_blob (stmt, 1, (void*)hash, 20, NULL);
|
||||
|
||||
if (sqlite3_step(stmt) == SQLITE_ROW)
|
||||
{
|
||||
*seeders = sqlite3_column_int (stmt, 0);
|
||||
*leechers = sqlite3_column_int (stmt, 1);
|
||||
*completed = sqlite3_column_int (stmt, 2);
|
||||
}
|
||||
|
||||
sqlite3_finalize (stmt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int db_cleanup (dbConnection *db)
|
||||
{
|
||||
const char sql[] = "SELECT info_hash FROM stats WHERE last_mod<?";
|
||||
char hash [50],
|
||||
temp [1000];
|
||||
sqlite3_stmt *stmt;
|
||||
int timeframe;
|
||||
uint32_t leechers, seeders;
|
||||
sqlite3_stmt *sTmp, *uStat;
|
||||
|
||||
return 0; // TODO: Fix problems and than allow use of this function.
|
||||
printf("Cleanup...\n");
|
||||
|
||||
timeframe = time(NULL);
|
||||
|
||||
// remove "dead" torrents (non-active for two hours).
|
||||
sqlite3_prepare (db->db, sql, -1, &stmt, NULL);
|
||||
sqlite3_bind_int (stmt, 1, timeframe - 7200);
|
||||
|
||||
while (sqlite3_step(stmt) == SQLITE_ROW)
|
||||
{
|
||||
to_hex_str(sqlite3_column_blob(stmt, 0), hash);
|
||||
|
||||
// drop table:
|
||||
strcpy(temp, "DROP TABLE IF EXISTS 't");
|
||||
strcat(temp, hash);
|
||||
strcat(temp, "'");
|
||||
sqlite3_exec(db->db, temp, NULL, NULL, NULL);
|
||||
}
|
||||
sqlite3_finalize (stmt);
|
||||
|
||||
// update 'dead' torrents
|
||||
sqlite3_prepare(db->db, "UPDATE stats SET seeders=0,leechers=0 WHERE last_mod<?", -1, &stmt, NULL);
|
||||
sqlite3_bind_int (stmt, 1, timeframe - 7200);
|
||||
sqlite3_step (stmt);
|
||||
sqlite3_finalize (stmt);
|
||||
|
||||
// update regular torrents.
|
||||
sqlite3_prepare(db->db, "SELECT info_hash FROM stats WHERE last_mod>=?", -1, &stmt, NULL);
|
||||
sqlite3_bind_int (stmt, 1, timeframe - 7200);
|
||||
|
||||
sqlite3_prepare (db->db, "UPDATE stats SET seeders=?,leechers=?,last_mod=? WHERE info_hash=?", -1, &uStat, NULL);
|
||||
|
||||
while (sqlite3_step(stmt) == SQLITE_ROW)
|
||||
{
|
||||
uint8_t *binHash = (uint8_t*)sqlite3_column_blob(stmt, 0);
|
||||
to_hex_str (binHash, hash);
|
||||
|
||||
// total users...
|
||||
strcpy (temp, "SELECT COUNT(*) FROM 't");
|
||||
strcat (temp, hash);
|
||||
strcat (temp, "'");
|
||||
|
||||
sqlite3_prepare (db->db, temp, -1, &sTmp, NULL);
|
||||
if (sqlite3_step(sTmp) == SQLITE_ROW)
|
||||
{
|
||||
leechers = sqlite3_column_int (sTmp, 0);
|
||||
}
|
||||
sqlite3_finalize (sTmp);
|
||||
|
||||
// seeders...
|
||||
strcpy (temp, "SELECT COUNT(*) FROM 't");
|
||||
strcat (temp, hash);
|
||||
strcat (temp, "' WHERE left=0");
|
||||
|
||||
sqlite3_prepare (db->db, temp, -1, &sTmp, NULL);
|
||||
if (sqlite3_step(sTmp) == SQLITE_ROW)
|
||||
{
|
||||
seeders = sqlite3_column_int (sTmp, 0);
|
||||
}
|
||||
sqlite3_finalize (sTmp);
|
||||
|
||||
leechers -= seeders;
|
||||
|
||||
sqlite3_bind_int (uStat, 1, seeders);
|
||||
sqlite3_bind_int (uStat, 2, leechers);
|
||||
sqlite3_bind_int (uStat, 3, timeframe);
|
||||
sqlite3_bind_blob (uStat, 4, binHash, 20, NULL);
|
||||
sqlite3_step (uStat);
|
||||
sqlite3_reset (uStat);
|
||||
|
||||
printf("%s: %d seeds/%d leechers;\n", hash, seeders, leechers);
|
||||
}
|
||||
sqlite3_finalize (stmt);
|
||||
|
||||
sqlite3_finalize (stmt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int db_remove_peer (dbConnection *db, uint8_t hash[20], db_peerEntry *pE)
|
||||
{
|
||||
char sql [1000];
|
||||
char xHash [50];
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
_to_hex_str (hash, xHash);
|
||||
|
||||
strcpy (sql, "DELETE FROM 't");
|
||||
strcat (sql, xHash);
|
||||
strcat (sql, "' WHERE ip=? AND port=? AND peer_id=?");
|
||||
|
||||
sqlite3_prepare (db->db, sql, -1, &stmt, NULL);
|
||||
|
||||
sqlite3_bind_blob(stmt, 0, (const void*)&pE->ip, 4, NULL);
|
||||
sqlite3_bind_blob(stmt, 1, (const void*)&pE->port, 2, NULL);
|
||||
sqlite3_bind_blob(stmt, 2, (const void*)pE->peer_id, 20, NULL);
|
||||
|
||||
sqlite3_step(stmt);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return 0;
|
||||
}
|
430
src/db/driver_sqlite.cpp
Normal file
430
src/db/driver_sqlite.cpp
Normal file
|
@ -0,0 +1,430 @@
|
|||
/*
|
||||
* 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 "driver_sqlite.hpp"
|
||||
#include "../multiplatform.h"
|
||||
#include "../tools.h"
|
||||
#include <sqlite3.h>
|
||||
#include <time.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <cassert>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace UDPT
|
||||
{
|
||||
namespace Data
|
||||
{
|
||||
static const char hexadecimal[] = "0123456789abcdef";
|
||||
|
||||
static char* _to_hex_str (const uint8_t *hash, char *data)
|
||||
{
|
||||
int i;
|
||||
for (i = 0;i < 20;i++)
|
||||
{
|
||||
data[i * 2] = hexadecimal[hash[i] / 16];
|
||||
data[i * 2 + 1] = hexadecimal[hash[i] % 16];
|
||||
}
|
||||
data[40] = '\0';
|
||||
return data;
|
||||
}
|
||||
|
||||
static uint8_t* _hash_to_bin (const char *hash, uint8_t *data)
|
||||
{
|
||||
for (int i = 0;i < 20;i++)
|
||||
{
|
||||
data [i] = 0;
|
||||
char a = hash[i * 2];
|
||||
char b = hash[i * 2 + 1];
|
||||
|
||||
assert ( (a >= 'a' && a <= 'f') || (a >= '0' && a <= '9') );
|
||||
assert ( (b >= 'a' && b <= 'f') || (b >= '0' && b <= '9') );
|
||||
|
||||
data[i] = ( (a >= '0' && a <= 'f') ? (a - '0') : (a - 'f' + 10) );
|
||||
data[i] <<= 4;
|
||||
data[i] = ( (b >= '0' && b <= 'f') ? (b - '0') : (b - 'f' + 10) );
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
SQLite3Driver::SQLite3Driver (Settings::SettingClass *sc, bool isDyn) : DatabaseDriver(sc, isDyn)
|
||||
{
|
||||
int r;
|
||||
bool doSetup;
|
||||
|
||||
fstream fCheck;
|
||||
string filename = sc->get("file");
|
||||
|
||||
fCheck.open(filename.c_str(), ios::binary | ios::in);
|
||||
if (fCheck.is_open())
|
||||
{
|
||||
doSetup = false;
|
||||
fCheck.close();
|
||||
}
|
||||
else
|
||||
doSetup = true;
|
||||
|
||||
r = sqlite3_open(filename.c_str(), &this->db);
|
||||
if (r != SQLITE_OK)
|
||||
{
|
||||
sqlite3_close(this->db);
|
||||
throw DatabaseException (DatabaseException::E_CONNECTION_FAILURE);
|
||||
}
|
||||
|
||||
if (doSetup)
|
||||
this->doSetup();
|
||||
}
|
||||
|
||||
void SQLite3Driver::doSetup()
|
||||
{
|
||||
// cout << "Creating DB..." << endl;
|
||||
char *eMsg = NULL;
|
||||
// for quicker stats.
|
||||
sqlite3_exec(this->db, "CREATE TABLE stats ("
|
||||
"info_hash blob(20) UNIQUE,"
|
||||
"completed INTEGER DEFAULT 0,"
|
||||
"leechers INTEGER DEFAULT 0,"
|
||||
"seeders INTEGER DEFAULT 0,"
|
||||
"last_mod INTEGER DEFAULT 0"
|
||||
")", NULL, NULL, &eMsg);
|
||||
// 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)
|
||||
{
|
||||
bool gotInfo = false;
|
||||
|
||||
const char sql[] = "SELECT seeders,leechers,completed FROM 'stats' WHERE info_hash=?";
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
e->seeders = 0;
|
||||
e->leechers = 0;
|
||||
e->completed = 0;
|
||||
|
||||
|
||||
sqlite3_prepare (this->db, sql, -1, &stmt, NULL);
|
||||
sqlite3_bind_blob (stmt, 1, (void*)e->info_hash, 20, NULL);
|
||||
|
||||
if (sqlite3_step(stmt) == SQLITE_ROW)
|
||||
{
|
||||
e->seeders = sqlite3_column_int (stmt, 0);
|
||||
e->leechers = sqlite3_column_int (stmt, 1);
|
||||
e->completed = sqlite3_column_int (stmt, 2);
|
||||
|
||||
gotInfo = true;
|
||||
}
|
||||
|
||||
sqlite3_finalize (stmt);
|
||||
|
||||
return gotInfo;
|
||||
}
|
||||
|
||||
bool SQLite3Driver::getPeers (uint8_t info_hash [20], int *max_count, PeerEntry *pe)
|
||||
{
|
||||
char sql [1000];
|
||||
char hash [50];
|
||||
sqlite3_stmt *stmt;
|
||||
int r,
|
||||
i;
|
||||
|
||||
sql[0] = '\0';
|
||||
|
||||
to_hex_str(info_hash, hash);
|
||||
|
||||
strcat(sql, "SELECT ip,port FROM 't");
|
||||
strcat(sql, hash);
|
||||
strcat(sql, "' LIMIT ?");
|
||||
|
||||
sqlite3_prepare(this->db, sql, -1, &stmt, NULL);
|
||||
sqlite3_bind_int(stmt, 1, *max_count);
|
||||
|
||||
i = 0;
|
||||
while (*max_count > i)
|
||||
{
|
||||
r = sqlite3_step(stmt);
|
||||
if (r == SQLITE_ROW)
|
||||
{
|
||||
const char *ip = (const char*)sqlite3_column_blob (stmt, 0);
|
||||
const char *port = (const char*)sqlite3_column_blob (stmt, 1);
|
||||
|
||||
memcpy(&pe[i].ip, ip, 4);
|
||||
memcpy(&pe[i].port, port, 2);
|
||||
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("%d Clients Dumped.\n", i);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
*max_count = i;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SQLite3Driver::updatePeer(uint8_t peer_id[20], uint8_t info_hash[20], uint32_t ip, uint16_t port, int64_t downloaded, int64_t left, int64_t uploaded, enum TrackerEvents event)
|
||||
{
|
||||
char xHash [50]; // we just need 40 + \0 = 41.
|
||||
sqlite3_stmt *stmt;
|
||||
char sql [1000];
|
||||
int r;
|
||||
|
||||
char *hash = xHash;
|
||||
to_hex_str(info_hash, hash);
|
||||
|
||||
addTorrent (info_hash);
|
||||
|
||||
|
||||
sql[0] = '\0';
|
||||
strcat(sql, "REPLACE INTO 't");
|
||||
strcat(sql, hash);
|
||||
strcat(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, -1, &stmt, NULL);
|
||||
|
||||
sqlite3_bind_blob(stmt, 1, (void*)peer_id, 20, NULL);
|
||||
sqlite3_bind_blob(stmt, 2, (void*)&ip, 4, NULL);
|
||||
sqlite3_bind_blob(stmt, 3, (void*)&port, 2, NULL);
|
||||
sqlite3_bind_blob(stmt, 4, (void*)&uploaded, 8, NULL);
|
||||
sqlite3_bind_blob(stmt, 5, (void*)&downloaded, 8, NULL);
|
||||
sqlite3_bind_blob(stmt, 6, (void*)&left, 8, NULL);
|
||||
sqlite3_bind_int(stmt, 7, time(NULL));
|
||||
|
||||
r = sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
strcpy(sql, "REPLACE INTO stats (info_hash,last_mod) VALUES (?,?)");
|
||||
sqlite3_prepare (this->db, sql, -1, &stmt, NULL);
|
||||
sqlite3_bind_blob (stmt, 1, hash, 20, NULL);
|
||||
sqlite3_bind_int (stmt, 2, time(NULL));
|
||||
sqlite3_step (stmt);
|
||||
sqlite3_finalize (stmt);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
bool SQLite3Driver::addTorrent (uint8_t info_hash[20])
|
||||
{
|
||||
char xHash [41];
|
||||
char *err_msg;
|
||||
int r;
|
||||
|
||||
_to_hex_str(info_hash, xHash);
|
||||
|
||||
// if non-dynamic, called only when adding to DB.
|
||||
if (!this->isDynamic())
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
sqlite3_prepare(this->db, "INSERT INTO torrents (info_hash,created) VALUES (?,?)", -1, &stmt, NULL);
|
||||
sqlite3_bind_blob(stmt, 1, info_hash, 20, NULL);
|
||||
sqlite3_bind_int(stmt, 1, time(NULL));
|
||||
sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
string sql = "CREATE TABLE IF NOT EXISTS 't";
|
||||
sql += xHash;
|
||||
sql += "' (";
|
||||
sql += "peer_id blob(20),"
|
||||
"ip blob(4),"
|
||||
"port blob(2),"
|
||||
"uploaded blob(8)," // uint64
|
||||
"downloaded blob(8),"
|
||||
"left blob(8),"
|
||||
"last_seen INT DEFAULT 0";
|
||||
|
||||
sql += ", CONSTRAINT c1 UNIQUE (ip,port) ON CONFLICT REPLACE)";
|
||||
|
||||
// create table.
|
||||
r = sqlite3_exec(this->db, sql.c_str(), NULL, NULL, &err_msg);
|
||||
printf("E:%s\n", err_msg);
|
||||
|
||||
return (r == SQLITE_OK);
|
||||
}
|
||||
|
||||
bool SQLite3Driver::isTorrentAllowed(uint8_t info_hash[20])
|
||||
{
|
||||
if (this->isDynamic())
|
||||
return true;
|
||||
sqlite3_stmt *stmt;
|
||||
sqlite3_prepare(this->db, "SELECT COUNT(*) FROM torrents WHERE info_hash=?", -1, &stmt, NULL);
|
||||
sqlite3_bind_blob(stmt, 1, info_hash, 20, NULL);
|
||||
sqlite3_step(stmt);
|
||||
|
||||
int n = sqlite3_column_int(stmt, 1);
|
||||
sqlite3_finalize(stmt);
|
||||
return (n == 1);
|
||||
}
|
||||
|
||||
void SQLite3Driver::cleanup()
|
||||
{
|
||||
int exp = time (NULL) - 7200; // 2 hours, expired.
|
||||
|
||||
// drop all peers with no activity for 2 hours.
|
||||
sqlite3_stmt *getTables;
|
||||
// torrent table names: t<hex-of-sha-1>
|
||||
sqlite3_prepare(this->db, "SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 't________________________________________'", -1, &getTables, NULL);
|
||||
|
||||
uint8_t buff [20];
|
||||
sqlite3_stmt *updateStats;
|
||||
assert (sqlite3_prepare(this->db, "REPLACE INTO stats (info_hash,seeders,leechers,last_mod) VALUES (?,?,?,?)", -1, &updateStats, NULL) == SQLITE_OK);
|
||||
|
||||
|
||||
while (sqlite3_step(getTables) == SQLITE_ROW)
|
||||
{
|
||||
char* tblN = (char*)sqlite3_column_text(getTables, 0);
|
||||
stringstream sStr;
|
||||
sStr << "DELETE FROM " << tblN << " WHERE last_seen<" << exp;
|
||||
|
||||
assert (sqlite3_exec(this->db, sStr.str().c_str(), NULL, NULL, NULL) == SQLITE_OK);
|
||||
|
||||
sStr.str (string());
|
||||
sStr << "SELECT left,COUNT(*) FROM " << tblN << " GROUP BY left==0";
|
||||
|
||||
sqlite3_stmt *collectStats;
|
||||
|
||||
sqlite3_prepare(this->db, sStr.str().c_str(), sStr.str().length(), &collectStats, NULL);
|
||||
cout << "[" << sqlite3_errmsg(this->db) << "]" << endl;
|
||||
int seeders = 0, leechers = 0;
|
||||
while (sqlite3_step(collectStats) == SQLITE_ROW) // expecting two results.
|
||||
{
|
||||
if (sqlite3_column_int(collectStats, 0) == 0)
|
||||
seeders = sqlite3_column_int (collectStats, 1);
|
||||
else
|
||||
leechers = sqlite3_column_int (collectStats, 1);
|
||||
}
|
||||
sqlite3_finalize(collectStats);
|
||||
|
||||
sqlite3_bind_blob(updateStats, 1, _hash_to_bin((const char*)(tblN + 1), buff), 20, NULL);
|
||||
sqlite3_bind_int(updateStats, 2, seeders);
|
||||
sqlite3_bind_int(updateStats, 3, leechers);
|
||||
sqlite3_bind_int(updateStats, 4, time (NULL));
|
||||
|
||||
sqlite3_step(updateStats);
|
||||
sqlite3_reset (updateStats);
|
||||
}
|
||||
sqlite3_finalize(updateStats);
|
||||
sqlite3_finalize(getTables);
|
||||
}
|
||||
|
||||
bool SQLite3Driver::removeTorrent(uint8_t info_hash[20])
|
||||
{
|
||||
// if non-dynamic, remove from table
|
||||
if (!this->isDynamic())
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
sqlite3_prepare(this->db, "DELETE FROM torrents WHERE info_hash=?", -1, &stmt, NULL);
|
||||
sqlite3_bind_blob(stmt, 1, info_hash, 20, NULL);
|
||||
sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
// remove from stats
|
||||
sqlite3_stmt *rmS;
|
||||
if (sqlite3_prepare(this->db, "DELETE FROM stats WHERE info_hash=?", -1, &rmS, NULL) != SQLITE_OK)
|
||||
{
|
||||
sqlite3_finalize(rmS);
|
||||
return false;
|
||||
}
|
||||
sqlite3_bind_blob(rmS, 1, (const void*)info_hash, 20, NULL);
|
||||
sqlite3_step(rmS);
|
||||
sqlite3_finalize(rmS);
|
||||
|
||||
// remove table
|
||||
string str = "DROP TABLE IF EXISTS 't";
|
||||
char buff [41];
|
||||
str += _to_hex_str(info_hash, buff);
|
||||
str += "'";
|
||||
|
||||
sqlite3_exec(this->db, str.c_str(), NULL, NULL, NULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SQLite3Driver::removePeer(uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port)
|
||||
{
|
||||
char sql [1000];
|
||||
char xHash [50];
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
_to_hex_str (info_hash, xHash);
|
||||
|
||||
strcpy (sql, "DELETE FROM 't");
|
||||
strcat (sql, xHash);
|
||||
strcat (sql, "' WHERE ip=? AND port=? AND peer_id=?");
|
||||
|
||||
sqlite3_prepare (this->db, sql, -1, &stmt, NULL);
|
||||
|
||||
sqlite3_bind_blob(stmt, 0, (const void*)&ip, 4, NULL);
|
||||
sqlite3_bind_blob(stmt, 1, (const void*)&port, 2, NULL);
|
||||
sqlite3_bind_blob(stmt, 2, (const void*)peer_id, 20, NULL);
|
||||
|
||||
sqlite3_step(stmt);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint64_t _genCiD (uint32_t ip, uint16_t port)
|
||||
{
|
||||
uint64_t x;
|
||||
x = (time(NULL) / 3600) * port; // x will probably overload.
|
||||
x = (ip ^ port);
|
||||
x <<= 16;
|
||||
x |= (~port);
|
||||
return x;
|
||||
}
|
||||
|
||||
bool SQLite3Driver::genConnectionId (uint64_t *connectionId, uint32_t ip, uint16_t port)
|
||||
{
|
||||
*connectionId = _genCiD(ip, port);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SQLite3Driver::verifyConnectionId(uint64_t cId, uint32_t ip, uint16_t port)
|
||||
{
|
||||
if (cId == _genCiD(ip, port))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
SQLite3Driver::~SQLite3Driver()
|
||||
{
|
||||
sqlite3_close(this->db);
|
||||
}
|
||||
};
|
||||
};
|
55
src/db/driver_sqlite.hpp
Normal file
55
src/db/driver_sqlite.hpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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 DATABASE_H_
|
||||
#define DATABASE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "database.hpp"
|
||||
#include <sqlite3.h>
|
||||
|
||||
namespace UDPT
|
||||
{
|
||||
namespace Data
|
||||
{
|
||||
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 ();
|
||||
private:
|
||||
sqlite3 *db;
|
||||
|
||||
void doSetup ();
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
#endif /* DATABASE_H_ */
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
using namespace UDPT::Data;
|
||||
|
||||
#define UDP_BUFFER_SIZE 2048
|
||||
|
||||
|
@ -91,8 +92,6 @@ namespace UDPT
|
|||
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;
|
||||
|
@ -131,7 +130,7 @@ namespace UDPT
|
|||
cout << "Thread (" << ( i + 1) << "/" << ((int)this->thread_count) << ") terminated." << endl;
|
||||
}
|
||||
if (this->conn != NULL)
|
||||
db_close(this->conn);
|
||||
delete this->conn;
|
||||
delete[] this->threads;
|
||||
}
|
||||
|
||||
|
@ -148,11 +147,7 @@ namespace UDPT
|
|||
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);
|
||||
|
||||
|
@ -173,11 +168,7 @@ namespace UDPT
|
|||
|
||||
this->sock = sock;
|
||||
|
||||
dbname = this->o_settings->get ("database", "file");
|
||||
if (dbname == "")
|
||||
dbname = "tracker.db";
|
||||
|
||||
db_open(&this->conn, dbname.c_str());
|
||||
this->conn = new Data::SQLite3Driver (this->o_settings->getClass("database"), true);
|
||||
|
||||
this->isRunning = true;
|
||||
cout << "Starting maintenance thread (1/" << ((int)this->thread_count) << ")" << endl;
|
||||
|
@ -186,7 +177,7 @@ namespace UDPT
|
|||
#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);
|
||||
pthread_create (&usi->threads[0], NULL, _maintainance_start, (void*)this);
|
||||
#endif
|
||||
|
||||
for (i = 1;i < this->thread_count; i++)
|
||||
|
@ -195,26 +186,13 @@ namespace UDPT
|
|||
#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);
|
||||
pthread_create (&(this->threads[i]), NULL, _thread_start, (void*)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;
|
||||
|
@ -248,7 +226,13 @@ static uint64_t _get_connID (SOCKADDR_IN *remote)
|
|||
|
||||
resp.action = m_hton32(0);
|
||||
resp.transaction_id = req->transaction_id;
|
||||
resp.connection_id = _get_connID(remote);
|
||||
|
||||
if (!usi->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));
|
||||
|
||||
|
@ -262,16 +246,16 @@ static uint64_t _get_connID (SOCKADDR_IN *remote)
|
|||
int q, // peer counts
|
||||
bSize, // message size
|
||||
i; // loop index
|
||||
db_peerEntry *peers;
|
||||
int32_t seeders,
|
||||
leechers,
|
||||
completed;
|
||||
db_peerEntry pE; // info for DB
|
||||
DatabaseDriver::PeerEntry *peers;
|
||||
DatabaseDriver::TorrentEntry tE;
|
||||
|
||||
uint8_t buff [1028]; // Reasonable buffer size. (header+168 peers)
|
||||
|
||||
req = (AnnounceRequest*)data;
|
||||
|
||||
if (req->connection_id != _get_connID(remote))
|
||||
if (!usi->conn->verifyConnectionId(req->connection_id,
|
||||
m_hton32(remote->sin_addr.s_addr),
|
||||
m_hton16(remote->sin_port)))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
@ -291,25 +275,54 @@ static uint64_t _get_connID (SOCKADDR_IN *remote)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (!usi->conn->isTorrentAllowed(req->info_hash))
|
||||
{
|
||||
UDPTracker::sendError(usi, remote, req->transaction_id, "info_hash not registered.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// load peers
|
||||
q = 30;
|
||||
if (req->num_want >= 1)
|
||||
q = min (q, req->num_want);
|
||||
|
||||
peers = (db_peerEntry*)malloc (sizeof(db_peerEntry) * q);
|
||||
peers = new DatabaseDriver::PeerEntry [q];
|
||||
|
||||
db_load_peers(usi->conn, req->info_hash, peers, &q);
|
||||
|
||||
DatabaseDriver::TrackerEvents event;
|
||||
switch (req->event)
|
||||
{
|
||||
case 1:
|
||||
event = DatabaseDriver::EVENT_COMPLETE;
|
||||
break;
|
||||
case 2:
|
||||
event = DatabaseDriver::EVENT_START;
|
||||
break;
|
||||
case 3:
|
||||
event = DatabaseDriver::EVENT_STOP;
|
||||
break;
|
||||
default:
|
||||
event = DatabaseDriver::EVENT_UNSPEC;
|
||||
break;
|
||||
}
|
||||
|
||||
if (event == DatabaseDriver::EVENT_STOP)
|
||||
q = 0; // no need for peers when stopping.
|
||||
|
||||
if (q > 0)
|
||||
usi->conn->getPeers(req->info_hash, &q, peers);
|
||||
|
||||
bSize = 20; // header is 20 bytes
|
||||
bSize += (6 * q); // + 6 bytes per peer.
|
||||
|
||||
db_get_stats (usi->conn, req->info_hash, &seeders, &leechers, &completed);
|
||||
tE.info_hash = req->info_hash;
|
||||
usi->conn->getTorrentInfo(&tE);
|
||||
|
||||
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->leechers = m_hton32(tE.leechers);
|
||||
resp->seeders = m_hton32 (tE.seeders);
|
||||
resp->transaction_id = req->transaction_id;
|
||||
|
||||
for (i = 0;i < q;i++)
|
||||
|
@ -328,24 +341,17 @@ static uint64_t _get_connID (SOCKADDR_IN *remote)
|
|||
buff[25 + x] = (peers[i].port & 0xff);
|
||||
|
||||
}
|
||||
free (peers);
|
||||
delete[] 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;
|
||||
// update DB.
|
||||
uint32_t ip;
|
||||
if (req->ip_address == 0) // default
|
||||
{
|
||||
pE.ip = m_hton32 (remote->sin_addr.s_addr);
|
||||
}
|
||||
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);
|
||||
ip = req->ip_address;
|
||||
usi->conn->updatePeer(req->peer_id, req->info_hash, ip, req->port,
|
||||
req->downloaded, req->left, req->uploaded, event);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -373,6 +379,13 @@ static uint64_t _get_connID (SOCKADDR_IN *remote)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (!usi->conn->verifyConnectionId(sR->connection_id,
|
||||
m_hton32(remote->sin_addr.s_addr),
|
||||
m_hton16(remote->sin_port)))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// get torrent count.
|
||||
c = v / 20;
|
||||
|
||||
|
@ -382,7 +395,6 @@ static uint64_t _get_connID (SOCKADDR_IN *remote)
|
|||
|
||||
for (i = 0;i < c;i++)
|
||||
{
|
||||
int32_t s, c, l;
|
||||
int32_t *seeders,
|
||||
*completed,
|
||||
*leechers;
|
||||
|
@ -398,11 +410,17 @@ static uint64_t _get_connID (SOCKADDR_IN *remote)
|
|||
completed = (int32_t*)&buffer[i*12+12];
|
||||
leechers = (int32_t*)&buffer[i*12+16];
|
||||
|
||||
db_get_stats (usi->conn, hash, &s, &l, &c);
|
||||
DatabaseDriver::TorrentEntry tE;
|
||||
tE.info_hash = hash;
|
||||
if (!usi->conn->getTorrentInfo(&tE))
|
||||
{
|
||||
sendError(usi, remote, sR->transaction_id, "Scrape Failed: couldn't retrieve torrent data");
|
||||
return 0;
|
||||
}
|
||||
|
||||
*seeders = m_hton32 (s);
|
||||
*completed = m_hton32 (c);
|
||||
*leechers = m_hton32 (l);
|
||||
*seeders = m_hton32 (tE.seeders);
|
||||
*completed = m_hton32 (tE.completed);
|
||||
*leechers = m_hton32 (tE.leechers);
|
||||
}
|
||||
cout.flush();
|
||||
|
||||
|
@ -436,7 +454,7 @@ static int _isIANA_IP (uint32_t ip)
|
|||
}
|
||||
}
|
||||
|
||||
cout << ":: " << (void*)remote->sin_addr.s_addr << ": " << remote->sin_port << " ACTION=" << action << endl;
|
||||
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);
|
||||
|
@ -499,10 +517,10 @@ static int _isIANA_IP (uint32_t ip)
|
|||
|
||||
while (usi->isRunning)
|
||||
{
|
||||
db_cleanup (usi->conn);
|
||||
usi->conn->cleanup();
|
||||
|
||||
#ifdef WIN32
|
||||
Sleep (usi->cleanup_interval * 1000); // wait 2 minutes between every cleanup.
|
||||
Sleep (usi->cleanup_interval * 1000);
|
||||
#elif defined (linux)
|
||||
sleep (usi->cleanup_interval);
|
||||
#else
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
#include <stdint.h>
|
||||
#include "multiplatform.h"
|
||||
#include "db/database.h"
|
||||
#include "db/driver_sqlite.hpp"
|
||||
#include "settings.hpp"
|
||||
|
||||
#include <string>
|
||||
|
@ -142,7 +142,7 @@ namespace UDPT
|
|||
|
||||
uint8_t settings;
|
||||
Settings *o_settings;
|
||||
dbConnection *conn;
|
||||
Data::DatabaseDriver *conn;
|
||||
|
||||
#ifdef WIN32
|
||||
static DWORD _thread_start (LPVOID arg);
|
||||
|
|
Loading…
Reference in a new issue