From e4658756229c1bc386516b87dffe32de6ec79613 Mon Sep 17 00:00:00 2001 From: Naim Abda Date: Tue, 20 Nov 2012 15:32:44 +0200 Subject: [PATCH] Initial Commit --- src/db/database.h | 48 +++++++++ src/db/driver_sqlite.c | 177 +++++++++++++++++++++++++++++++ src/main.c | 52 ++++++++++ src/multiplatform.h | 26 +++++ src/tools.c | 17 +++ src/tools.h | 19 ++++ src/udpTracker.c | 230 +++++++++++++++++++++++++++++++++++++++++ src/udpTracker.h | 89 ++++++++++++++++ 8 files changed, 658 insertions(+) create mode 100644 src/db/database.h create mode 100644 src/db/driver_sqlite.c create mode 100644 src/main.c create mode 100644 src/multiplatform.h create mode 100644 src/tools.c create mode 100644 src/tools.h create mode 100644 src/udpTracker.c create mode 100644 src/udpTracker.h diff --git a/src/db/database.h b/src/db/database.h new file mode 100644 index 0000000..b74c2c2 --- /dev/null +++ b/src/db/database.h @@ -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 + +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_ */ diff --git a/src/db/driver_sqlite.c b/src/db/driver_sqlite.c new file mode 100644 index 0000000..0ec226b --- /dev/null +++ b/src/db/driver_sqlite.c @@ -0,0 +1,177 @@ +#include "database.h" + +#include +#include +#include +#include +#include + +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; +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..d6f58cc --- /dev/null +++ b/src/main.c @@ -0,0 +1,52 @@ +/* + ============================================================================ + Name : udpBitTorrentTracker.c + Author : + Version : + Copyright : + Description : Hello World in C, Ansi-style + ============================================================================ + */ + +#include +#include + +//#include +//#include +#include "multiplatform.h" + +#include "udpTracker.h" +#include "tools.h" +#include +#include + +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; +} diff --git a/src/multiplatform.h b/src/multiplatform.h new file mode 100644 index 0000000..89e0cbe --- /dev/null +++ b/src/multiplatform.h @@ -0,0 +1,26 @@ + +#include + +#ifdef WIN32 +#include +#include +#elif defined (linux) +#include +#include +#include +#include + +#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 + diff --git a/src/tools.c b/src/tools.c new file mode 100644 index 0000000..00910b4 --- /dev/null +++ b/src/tools.c @@ -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; +} diff --git a/src/tools.h b/src/tools.h new file mode 100644 index 0000000..71d3ac3 --- /dev/null +++ b/src/tools.h @@ -0,0 +1,19 @@ +/* + * tools.h + * + * Created on: Nov 18, 2012 + * Author: Naim + */ + +#ifndef TOOLS_H_ +#define TOOLS_H_ + +#include + +#define m_hton16(n) htons(n) + +uint32_t m_hton32 (uint32_t n); + +uint64_t m_hton64 (uint64_t n); + +#endif /* TOOLS_H_ */ diff --git a/src/udpTracker.c b/src/udpTracker.c new file mode 100644 index 0000000..0a302da --- /dev/null +++ b/src/udpTracker.c @@ -0,0 +1,230 @@ + +#include +#include +#include "udpTracker.h" +#include "tools.h" +#include +#include +#include +#include + +#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; +} diff --git a/src/udpTracker.h b/src/udpTracker.h new file mode 100644 index 0000000..b37640f --- /dev/null +++ b/src/udpTracker.h @@ -0,0 +1,89 @@ +/* + * udpTracker.h + * + * Created on: Nov 14, 2012 + * Author: Naim + */ + +#ifndef UDPTRACKER_H_ +#define UDPTRACKER_H_ + +#include +#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_ */