Initial Commit

This commit is contained in:
Naim Abda 2012-11-20 15:32:44 +02:00
commit e465875622
8 changed files with 658 additions and 0 deletions

48
src/db/database.h Normal file
View file

@ -0,0 +1,48 @@
/*
* database.h
*
* Created on: Nov 18, 2012
* Author: Naim
*
* This is just a API implementation; Actual management is done in the driver_*.c source.
*
*
*/
#ifndef DATABASE_H_
#define DATABASE_H_
#include <stdint.h>
typedef struct dbConnection dbConnection;
int db_open (dbConnection **, char *cStr);
int db_close (dbConnection *);
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 a peer to the torrent's list.
int db_add_peer (dbConnection *, uint8_t [20], db_peerEntry*);
/*
* lst: pointer to an array whose maximum size is passed to sZ.
* sZ returns the amount of peers returned.
*/
int db_load_peers (dbConnection *, uint8_t [20], db_peerEntry **lst, int *sZ);
int db_get_stats (dbConnection *, uint8_t [20], uint32_t *seeders, uint32_t *leechers, uint32_t *completed);
/**
* Calculates Stats, Removes expired data.
*/
int db_cleanup (dbConnection *);
#endif /* DATABASE_H_ */

177
src/db/driver_sqlite.c Normal file
View file

@ -0,0 +1,177 @@
#include "database.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";
static void _to_hex_str (uint8_t hash[20], 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];
sql[0] = '\0';
strcat(sql, "CREATE TABLE IF NOT EXISTS '");
strcat(sql, hash);
strcat (sql, "' (");
strcat (sql, "peer_id blob(20) UNIQUE,"
"ip blob(4),"
"port blob(2),"
"uploaded blob(8)," // uint64
"downloaded blob(8),"
"left blob(8),"
"last_seen INTEGER(8)");
strcat(sql, ")");
// create table.
char *err_msg;
int 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),"
"completed INTEGER DEFAULT 0,"
"leechers INTEGER DEFAULT 0,"
"seeders INTEGER DEFAULT 0"
")", NULL, NULL, NULL);
}
int db_open (dbConnection **db, char *cStr)
{
FILE *f = fopen (cStr, "rb");
int doSetup = 0;
if (f == NULL)
doSetup = 1;
else
fclose (f);
*db = malloc (sizeof(struct dbConnection));
int 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.
char *hash = xHash;
_to_hex_str(info_hash, hash);
_db_make_torrent_table(db->db, hash);
sqlite3_stmt *stmt;
char sql [1000];
sql[0] = '\0';
strcat(sql, "REPLACE INTO '");
strcat(sql, hash);
strcat(sql, "' (peer_id,ip,port,uploaded,downloaded,left,last_seen) VALUES (?,?,?,?,?,?,?)");
sqlite3_prepare(db->db, sql, -1, &stmt, NULL);
sqlite3_bind_blob(stmt, 1, pE->peer_id, 20, NULL);
sqlite3_bind_blob(stmt, 2, &pE->ip, 4, NULL);
sqlite3_bind_blob(stmt, 3, &pE->port, 2, NULL);
sqlite3_bind_blob(stmt, 4, &pE->uploaded, 8, NULL);
sqlite3_bind_blob(stmt, 5, &pE->downloaded, 8, NULL);
sqlite3_bind_blob(stmt, 6, &pE->left, 8, NULL);
sqlite3_bind_int64(stmt, 7, time(NULL));
int r = 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];
sql[0] = '\0';
char hash [50];
_to_hex_str(info_hash, hash);
strcat(sql, "SELECT ip,port FROM '");
strcat(sql, hash);
strcat(sql, "' LIMIT ?");
sqlite3_stmt *stmt;
sqlite3_prepare(db->db, sql, -1, &stmt, NULL);
sqlite3_bind_int(stmt, 1, *sZ);
int i = 0;
int r;
while (1)
{
r = sqlite3_step(stmt);
if (r == SQLITE_ROW)
{
lst[i]->ip = sqlite3_column_int(stmt, 0);
lst[i]->port = sqlite3_column_int(stmt, 1);
i++;
}
else
break;
}
sqlite3_finalize(stmt);
*sZ = i;
return 0;
}
int db_get_stats (dbConnection *db, uint8_t hash[20], uint32_t *seeders, uint32_t *leechers, uint32_t *completed)
{
return 0;
}
int db_cleanup (dbConnection *db)
{
printf("Cleanup...\n");
sqlite3_stmt *stmt;
return 0;
}

52
src/main.c Normal file
View file

@ -0,0 +1,52 @@
/*
============================================================================
Name : udpBitTorrentTracker.c
Author :
Version :
Copyright :
Description : Hello World in C, Ansi-style
============================================================================
*/
#include <stdio.h>
#include <stdlib.h>
//#include <winsock2.h>
//#include <windows.h>
#include "multiplatform.h"
#include "udpTracker.h"
#include "tools.h"
#include <math.h>
#include <time.h>
int main(void)
{
printf("UDP BitTorrentTracker\t\tCopyright: (C) 2012 Naim Abda.\n\n");
#ifdef WIN32
WSADATA wsadata;
WSAStartup(MAKEWORD(2, 2), &wsadata);
#endif
udpServerInstance usi;
UDPTracker_init(&usi, 6969, 1);
if (UDPTracker_start(&usi) != 0)
{
printf("Error While trying to start server.");
return 1;
}
system("pause");
printf("\n");
UDPTracker_destroy(&usi);
#ifdef WIN32
WSACleanup();
#endif
return 0;
}

26
src/multiplatform.h Normal file
View file

@ -0,0 +1,26 @@
#include <stdint.h>
#ifdef WIN32
#include <winsock2.h>
#include <windows.h>
#elif defined (linux)
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <pthread.h>
#define SOCKET int
#define DWORD uint64_t
typedef struct hostent HOSTENT;
typedef struct sockaddr SOCKADDR;
typedef struct sockaddr_in SOCKADDR_IN;
typedef struct in_addr IN_ADDR;
typedef struct hostent HOSTENT;
typedef void* LPVOID;
typedef void (LPTHREAD_START_ROUTINE)(LPVOID);
typedef void* HANDLE;
#define IPPROTO_UDP 0 // no protocol set.. SOCK_DGRAM is enough.
#endif

17
src/tools.c Normal file
View file

@ -0,0 +1,17 @@
#include "tools.h"
uint64_t m_hton64 (uint64_t n)
{
uint64_t r = m_hton32 (n & 0xffffffff);
r <<= 32;
r |= m_hton32 (n & 0xffffffff00000000);
return r;
}
uint32_t m_hton32 (uint32_t n)
{
uint32_t r = m_hton16 (n & 0xffff);
r <<= 16;
r |= m_hton16 (n & 0xffff0000);
return r;
}

19
src/tools.h Normal file
View file

@ -0,0 +1,19 @@
/*
* tools.h
*
* Created on: Nov 18, 2012
* Author: Naim
*/
#ifndef TOOLS_H_
#define TOOLS_H_
#include <stdint.h>
#define m_hton16(n) htons(n)
uint32_t m_hton32 (uint32_t n);
uint64_t m_hton64 (uint64_t n);
#endif /* TOOLS_H_ */

230
src/udpTracker.c Normal file
View file

@ -0,0 +1,230 @@
#include <winsock2.h>
#include <windows.h>
#include "udpTracker.h"
#include "tools.h"
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include <winerror.h>
#define FLAG_RUNNING 0x01
#define UDP_BUFFER_SIZE 256
static DWORD _thread_start (LPVOID);
void UDPTracker_init (udpServerInstance *usi, uint16_t port, uint8_t threads)
{
usi->port = port;
usi->thread_count = threads;
usi->threads = malloc (sizeof(HANDLE) * threads);
usi->flags = 0;
}
void UDPTracker_destroy (udpServerInstance *usi)
{
db_close(usi->conn);
free (usi->threads);
}
int UDPTracker_start (udpServerInstance *usi)
{
SOCKET sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock == INVALID_SOCKET)
return 1;
int r;
SOCKADDR_IN recvAddr;
recvAddr.sin_addr.S_un.S_addr = 0L;
recvAddr.sin_family = AF_INET;
recvAddr.sin_port = htons (usi->port);
BOOL yup = TRUE;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&yup, sizeof(BOOL));
r = bind (sock, (SOCKADDR*)&recvAddr, sizeof(SOCKADDR_IN));
if (r == SOCKET_ERROR)
{
closesocket (sock);
return 2;
}
printf("SOCK=%d\n", sock);
usi->sock = sock;
db_open(&usi->conn, "tracker.db");
usi->flags |= FLAG_RUNNING;
int i;
for (i = 0;i < usi->thread_count; i++)
{
printf("Starting Thread %d of %u\n", (i + 1), usi->thread_count);
usi->threads[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)_thread_start, (LPVOID)usi, 0, NULL);
}
return 0;
}
static uint64_t _get_connID (SOCKADDR_IN *remote)
{
int base = time(NULL);
base /= 3600; // changes every day.
uint64_t x = base;
x += remote->sin_addr.S_un.S_addr;
return x;
}
static int _send_error (udpServerInstance *usi, SOCKADDR_IN *remote, uint32_t transactionID, char *msg)
{
struct udp_error_response error;
error.action = htonl(3);
error.transaction_id = transactionID;
error.message = msg;
int msg_sz = 4 + 4 + 1 + strlen(msg);
char buff [msg_sz];
memcpy(buff, &error, 8);
int i;
for (i = 8;i <= msg_sz;i++)
{
buff[i] = msg[i - 8];
}
printf("ERROR SENT\n");
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 = (ConnectionRequest*)data;
ConnectionResponse resp;
resp.action = htonl(0);
resp.transaction_id = req->transaction_id;
resp.connection_id = _get_connID(remote);
int r = sendto(usi->sock, (char*)&resp, sizeof(ConnectionResponse), 0, (SOCKADDR*)remote, sizeof(SOCKADDR_IN));
printf("_h_c=%d\n", r);
return 0;
}
static int _handle_announce (udpServerInstance *usi, SOCKADDR_IN *remote, char *data)
{
AnnounceRequest *req = (AnnounceRequest*)data;
if (req->connection_id != _get_connID(remote))
{
printf("ConnID mismatch.\n");
return 1;
}
db_peerEntry pE;
pE.downloaded = req->downloaded;
pE.uploaded = req->uploaded;
pE.left = req->left;
pE.peer_id = req->peer_id;
pE.ip = req->ip_address;
pE.port = req->port;
db_add_peer(usi->conn, req->info_hash, &pE);
// _send_error(usi, remote, req->transaction_id, "Not Implemented :-(.");
int q = 30;
if (req->num_want >= 1)
q = min (q, req->num_want);
db_peerEntry *peers = malloc (sizeof(db_peerEntry) * q);
db_load_peers(usi->conn, req->info_hash, &peers, &q);
printf("%d peers found.\n", q);
int bSize = 20; // header is 20 bytes
bSize += (6 * q); // + 6 bytes per peer.
uint8_t buff [bSize];
AnnounceResponse *resp = (AnnounceResponse*)buff;
resp->action = htonl(1);
resp->interval = htonl ( 1800 );
resp->leechers = htonl( 1);
resp->seeders = 0;
resp->transaction_id = req->transaction_id;
int i;
for (i = 0;i < q;i++)
{
int x = i * 6;
// network byte order!!!
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);
buff[24 + x] = ((peers[i].port & (0xff << 8)) >> 8);
buff[25 + x] = (peers[i].port & 0xff);
printf("%u.%u.%u.%u:%u\n", buff[20 + x], buff[21 + x], buff[22 + x], buff[23 + x], peers[i].port);
}
free (peers);
return sendto(usi->sock, (char*)buff, bSize, 0, (SOCKADDR*)remote, sizeof(SOCKADDR_IN));
}
// returns 1 if connection request. returns 2 if announce. returns 3 if scrape.
static int _resolve_request (udpServerInstance *usi, SOCKADDR_IN *remote, char *data)
{
ConnectionRequest *cR;
cR = (ConnectionRequest*)data;
uint32_t action = htonl(cR->action);
printf("ACTION=%d\n", action);
if (action == 0)
return _handle_connection(usi, remote, data);
else if (action == 1)
return _handle_announce(usi, remote, data);
else
{
_send_error(usi, remote, cR->transaction_id, "Method not implemented.");
return -1;
}
}
static DWORD _thread_start (LPVOID arg)
{
udpServerInstance *usi = arg;
SOCKADDR_IN remoteAddr;
int addrSz = sizeof (SOCKADDR_IN);
int r;
char *tmpBuff = malloc (UDP_BUFFER_SIZE); // 98 is the maximum request size.
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, &addrSz);
printf("RECV:%d\n", r);
r = _resolve_request(usi, &remoteAddr, tmpBuff);
printf("R=%d\n", r);
}
free (tmpBuff);
return 0;
}

89
src/udpTracker.h Normal file
View file

@ -0,0 +1,89 @@
/*
* udpTracker.h
*
* Created on: Nov 14, 2012
* Author: Naim
*/
#ifndef UDPTRACKER_H_
#define UDPTRACKER_H_
#include <stdint.h>
#include "multiplatform.h"
#include "db/database.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_error_response
{
uint32_t action;
uint32_t transaction_id;
char *message;
};
typedef struct
{
SOCKET sock;
uint16_t port;
uint8_t thread_count;
uint8_t flags;
HANDLE *threads;
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_error_response ErrorResponse;
void UDPTracker_init (udpServerInstance *, uint16_t port, uint8_t threads);
void UDPTracker_destroy (udpServerInstance *);
int UDPTracker_start (udpServerInstance *);
#endif /* UDPTRACKER_H_ */