5f7465 | 2004-07-30 | Martin Nilsson | |
#pike __REAL_VERSION__
|
deadd4 | 2004-08-17 | Martin Nilsson | |
int(0..) interval = 1800;
|
5f7465 | 2004-07-30 | Martin Nilsson | |
|
a2dc3d | 2004-08-21 | Martin Nilsson | | int(0..) client_life = 1800*2;
int(0..) torrent_life = 0;
|
5f7465 | 2004-07-30 | Martin Nilsson | | int(1..) default_numwants = 50;
int(0..1) dynamic_add_torrents = 1;
class Client (string ip, int port) {
int uploaded;
int downloaded;
int left;
int poked;
void update_stats( string u, string d, string l ) {
uploaded = (int)u;
downloaded = (int)d;
left = (int)l;
int poked = time();
}
}
class TorrentInfo {
mapping(string:Client) clients = ([]);
array(string) seeds = ({});
|
deadd4 | 2004-08-17 | Martin Nilsson | | int completed;
|
a2dc3d | 2004-08-21 | Martin Nilsson | | int poked = time();
|
deadd4 | 2004-08-17 | Martin Nilsson | |
int num_seeds() {
return sizeof(seeds);
}
int num_leechers() {
return sizeof(clients)-num_seeds();
}
int num_completed() {
return completed;
}
|
5f7465 | 2004-07-30 | Martin Nilsson | |
Client get_client(string peer_id) {
return clients[peer_id];
}
void remove_client(string peer_id) {
m_delete(clients, peer_id);
seeds -= ({ peer_id });
}
void add_client(string peer_id, Client c) {
clients[peer_id] = c;
}
void rebuild_seed_list() {
array s = ({});
foreach(clients; string id; Client c)
if(!c->left) s += ({ id });
seeds = s;
}
string compact_peerlist(array peers) {
|
a2dc3d | 2004-08-21 | Martin Nilsson | | poked = time(1);
|
5f7465 | 2004-07-30 | Martin Nilsson | | String.Buffer buf = String.Buffer(sizeof(peers)*6);
foreach(peers, string id) {
Client c = clients[id];
string ip = c->ip;
for(int i; i<4; i++)
buf->putchar( ip[i] );
buf->add( sprintf("%2c", c->port) );
}
return (string)buf;
}
array(mapping(string:string|int)) peerless_peerlist(array peers) {
|
a2dc3d | 2004-08-21 | Martin Nilsson | | poked = time(1);
|
5f7465 | 2004-07-30 | Martin Nilsson | | array ret = ({});
foreach(peers, string id) {
Client c = clients[id];
ret += ({ ([ "ip":c->ip, "port":c->port ]) });
}
return ret;
}
array(mapping(string:string|int)) peerlist(array peers) {
|
a2dc3d | 2004-08-21 | Martin Nilsson | | poked = time(1);
|
5f7465 | 2004-07-30 | Martin Nilsson | | array ret = ({});
foreach(peers, string id) {
Client c = clients[id];
ret += ({ ([ "peer id":id, "ip":c->ip, "port":c->port ]) });
}
return ret;
}
void gc() {
foreach(clients; string id; Client c)
|
a2dc3d | 2004-08-21 | Martin Nilsson | | if( c->poked < time(1)-client_life )
|
5f7465 | 2004-07-30 | Martin Nilsson | | remove_client(id);
}
}
mapping(string:TorrentInfo) torrents = ([]);
void add_torrent(string id) {
torrents[id] = TorrentInfo();
}
|
bcdea3 | 2004-08-21 | Martin Nilsson | | mapping report_failure(string why) {
return ([ "failure reason" : why ]);
|
5f7465 | 2004-07-30 | Martin Nilsson | | }
|
deadd4 | 2004-08-17 | Martin Nilsson | |
string announce(mapping args, string ip) {
|
bcdea3 | 2004-08-21 | Martin Nilsson | | return .Bencoding.encode( low_announce(args, ip) );
}
mapping(string:mixed) low_announce(mapping args, string ip) {
|
5f7465 | 2004-07-30 | Martin Nilsson | |
if( !args->info_hash )
return report_failure( "No info_hash query variable." );
if( sizeof(args->info_hash)!=20 )
return report_failure( "Wrong size of info_hash data." );
if( !torrents[ args->info_hash ] ) {
if( !dynamic_add_torrents )
return report_failure( "No such torrent tracked by this tracker." );
add_torrent( args->info_hash );
}
|
32c1ce | 2009-09-07 | Martin Nilsson | | TorrentInfo torrent = torrents[ args->info_hash ];
|
5f7465 | 2004-07-30 | Martin Nilsson | |
if( args->ip ) {
array temp_ip=array_sscanf(args->ip, "%d.%d.%d.%d");
if( sizeof(temp_ip)!=4 )
return report_failure( "Could not parse ip query variable." );
#if 1
if( temp_ip[0]!=10 ||
(temp_ip[0]!=172 && temp_ip[1]!=16) ||
(temp_ip[0]!=192 && temp_ip[1]!=168) )
return report_failure( "This tracker only allows remapping "
"RFC 1918 addresses." );
#endif
ip = sprintf("%c%c%c%c", @temp_ip);
}
else
ip = sprintf("%c%c%c%c", @array_sscanf(ip, "%d.%d.%d.%d"));
if( !args->peer_id )
return report_failure( "No peer_id query variable." );
if( sizeof(args->peer_id)!=20 )
return report_failure( "Wrong size of peer_id data." );
string peer_id = args->peer_id;
if( !args->port )
return report_failure( "No port query variable." );
int port = (int)args->port;
if( port<1 || port>65535 )
return report_failure( "Port out of range." );
Client c = torrent->get_client(peer_id);
if( args->event == "stopped" ) {
if( c ) {
if( c->ip!=ip )
return report_failure( "Unregsiter your own clients!!" );
torrent->remove_client(peer_id);
}
|
bcdea3 | 2004-08-21 | Martin Nilsson | | return ([]);
|
5f7465 | 2004-07-30 | Martin Nilsson | | }
|
deadd4 | 2004-08-17 | Martin Nilsson | | else if( args->event == "complete" ) {
torrent->completed++;
}
|
5f7465 | 2004-07-30 | Martin Nilsson | |
if( !c ) {
c = Client(ip, port);
torrent->add_client(peer_id, c);
}
c->update_stats( args->uploaded, args->downloaded, args->left );
if(args->left && !(int)args->left)
torrent->rebuild_seed_list();
array(string) peers = indices(torrent->clients);
peers -= ({ peer_id });
if( !c->left )
peers -= torrent->seeds;
#if 0
if( args->event == "started" ) {
}
else
#endif
peers = Array.shuffle(peers);
peers = peers[..((int)args->numwants || 50)];
mapping m = ([ "interval":interval ]);
if( args->compact )
m->peers = torrent->compact_peerlist(peers);
else if ( args->no_peer_id )
m->peers = torrent->peerless_peerlist(peers);
else
m->peers = torrent->peerlist(peers);
|
bcdea3 | 2004-08-21 | Martin Nilsson | | return m;
}
string be_report_failure( string err ) {
return .Bencoding.encode( report_failure(err) );
|
5f7465 | 2004-07-30 | Martin Nilsson | | }
|
deadd4 | 2004-08-17 | Martin Nilsson | |
string scrape(mapping args) {
mapping res = ([ "files" : ([]) ]);
array ts;
if(args->info_hash) {
if( sizeof(args->info_hash)!=20 )
|
bcdea3 | 2004-08-21 | Martin Nilsson | | return be_report_failure( "Wrong size of info_hash data." );
|
deadd4 | 2004-08-17 | Martin Nilsson | | if( !torrents[ args->info_hash ] )
|
bcdea3 | 2004-08-21 | Martin Nilsson | | return be_report_failure( "No such torrent tracked by this tracker." );
|
deadd4 | 2004-08-17 | Martin Nilsson | | ts = ({ args->info_hash });
}
else
ts = indices(torrents);
foreach(ts, string hash) {
TorrentInfo t = torrents[hash];
mapping f = ([ "complete" : t->num_seeds(),
"downloaded" : t->num_completed(),
"incomplete" : t->num_leechers() ]);
|
004df8 | 2004-08-17 | Martin Nilsson | | res->files[hash] = f;
|
deadd4 | 2004-08-17 | Martin Nilsson | | }
|
004df8 | 2004-08-17 | Martin Nilsson | | return .Bencoding.encode(res);
|
deadd4 | 2004-08-17 | Martin Nilsson | | }
|
bcdea3 | 2004-08-21 | Martin Nilsson | |
class Msg {
inherit ADT.Struct;
}
class MsgConnectReq {
inherit Msg;
Item connection_id = uint64();
Item action = uint32();
Item transaction_id = uint32();
}
class MsgConnectAns {
inherit Msg;
Item action = uint32(0);
Item transaction_id = uint32();
Item connection_id = uint64();
}
string make_udp_header(int action, int id) {
return sprintf("%4c%4c", action, id);
}
string udp_error(int id, string msg) {
return make_udp_header(3, id) + msg;
}
class MsgReqHeader {
inherit Msg;
Item connection_id = uint64();
Item action = uint32();
}
class MsgAnnounceReq {
inherit Msg;
Item connection_id = uint64();
Item action = uint32();
Item transaction_id = uint32();
Item info_hash = Chars(20);
Item peer_id = Chars(20);
Item downloaded = uint64();
Item left = uint64();
Item uploaded = uint64();
Item event = uint32();
Item ip = uint32();
Item key = uint32();
Item num_want = int32();
Item port = uint16();
Item extensions = uint16();
}
class MsgAnnounceAns {
inherit Msg;
Item action = uint32(1);
Item transaction_id = uint32();
Item interval = uint32();
Item leechers = uint32();
Item seeders = uint32();
}
class MsgScrapeReq {
inherit Msg;
Item connection_id = uint64();
Item action = uint32();
Item transaction_id = uint32();
Item num_info_hashes = uint16();
Item extensions = uint16();
}
int udp_id = random( pow(2,64) );
string udp(string msg, string ip) {
if(msg[..8] == String.hex2string("0000041727101980")) {
Msg m = MsgConnectReq(msg);
if(m->action) return udp_error(0, "Wrong handshake action.");
udp_id++;
if(udp_id==0x41727101980)
udp_id++;
udp_id %= pow(2,64);
Msg r = MsgConnectAns();
r->transaction_id = m->transaction_id;
r->connection_id = udp_id;
return (string)r;
}
Msg header = MsgReqHeader(msg);
switch(header->action) {
case 1: {
Msg m = MsgAnnounceReq(msg);
mapping args = ([ "info_hash" : m->info_hash,
"peer_id" : m->peer_id,
"port" : m->port,
"uploaded" : m->uploaded,
"downloaded" : m->downloaded,
"left" : m->left,
"compact" : 1,
"key" : m->key ]);
if(m->num_want != -1) args->numwant = m->num_want;
if(m->event) {
args->event = ([ 1 : "completed",
2 : "started",
3 : "stopped" ])[ m->event ];
if(!args->event)
return udp_error(header->transaction_id, "Unknown event.");
}
if(m->ip) args->ip = sprintf("%d.%d.%d.%d", @(array)sprintf("%4c",m->ip) );
mapping res = low_announce(args, ip);
if(res["failure reason"])
return udp_error(header->transaction_id, res["Failure reason"]);
Msg ret = MsgAnnounceAns();
ret->transaction_id = m->transaction_id;
ret->interval = res->interval;
TorrentInfo t = torrents[m->info_hash];
ret->leechers = t->num_leechers();
ret->seeders = t->num_seeds();
return (string)ret + res->peers;
break;
}
case 2: {
Msg m = MsgScrapeReq(msg);
msg = msg[..sizeof(m)-1];
array ts = msg/20;
ts = ts[..m->num_info_hashes-1];
if(sizeof(ts)<m->num_info_hashes)
return udp_error(header->transaction_id, "Too few info hashes.");
string ret = "";
foreach(ts, string hash) {
TorrentInfo t = torrents[hash];
if( !t )
ret += "\0\0\0\0""\0\0\0\0""\0\0\0\0";
else
ret += sprintf("%4c%4c%4c", t->num_completed(),
|
af151d | 2022-10-03 | Henrik Grubbström (Grubba) | | t->num_seeds(), t->num_leechers());
|
bcdea3 | 2004-08-21 | Martin Nilsson | | }
return make_udp_header(2, header->transaction_id) + ret;
break;
}
default:
return udp_error(0, "Unkown action.");
}
}
|
a2dc3d | 2004-08-21 | Martin Nilsson | | void tracker_gc() {
if(torrent_life)
foreach(torrents; string id; TorrentInfo t)
if(t->poked < time()-torrent_life)
m_delete(torrents, id);
if(client_life)
values(torrents)->gc();
call_out(tracker_gc, interval);
|
5f7465 | 2004-07-30 | Martin Nilsson | | }
|
e092c3 | 2019-04-16 | Henrik Grubbström (Grubba) | | protected void create() {
|
a2dc3d | 2004-08-21 | Martin Nilsson | | call_out(tracker_gc, interval);
|
5f7465 | 2004-07-30 | Martin Nilsson | | }
|