3524712015-05-26Martin Nilsson // Bittorrent client - originally by Mirar
68d7c12003-12-13Mirar (Pontus Hagland) #pike __REAL_VERSION__
4fc9d22014-02-15Martin Nilsson #require constant(Protocols.Bittorrent.Torrent)
bb3c3b2005-08-30Henrik Grubbström (Grubba) 
2997fb2004-01-30Martin Nilsson constant dont_dump_program = 1;
fb397a2003-12-09Mirar (Pontus Hagland) Stdio.File fd=Stdio.File();
99e5fd2004-05-31Mirar (Pontus Hagland) 
fb397a2003-12-09Mirar (Pontus Hagland) .Torrent parent; string ip; int port; string id;
4d278a2003-12-17Mirar (Pontus Hagland) string client_version="unknown";
fb397a2003-12-09Mirar (Pontus Hagland)  string mode; int online=0;
ecc8142003-12-13Mirar (Pontus Hagland) int strangled=0; // choke because of tit-for-tat int were_choking=1;
222e6c2003-12-10Mirar (Pontus Hagland) int were_interested=0; int peer_choking=1;
fb397a2003-12-09Mirar (Pontus Hagland) int peer_interested=0;
222e6c2003-12-10Mirar (Pontus Hagland) int peer_choking_since=0; int uploading=0;
ecc8142003-12-13Mirar (Pontus Hagland) multiset uploading_pieces=(<>);
fb397a2003-12-09Mirar (Pontus Hagland)  string bitfield;
222e6c2003-12-10Mirar (Pontus Hagland) int is_complete=0;
fb397a2003-12-09Mirar (Pontus Hagland)  // #define BT_PEER_DEBUG constant KEEPALIVE_DELAY=60;
ecc8142003-12-13Mirar (Pontus Hagland) constant CONNECTION_TIMEOUT=30; constant CONNECTION_DEAD_RETRY=-1; constant CONNECTION_DROP_RETRY=30; constant CONNECTION_HANDSHAKE_DROP_RETRY=-1;
99e5fd2004-05-31Mirar (Pontus Hagland) constant GARB_DELAY=600;
fb397a2003-12-09Mirar (Pontus Hagland)  constant MSG_CHOKE = 0; constant MSG_UNCHOKE = 1; constant MSG_INTERESTED = 2; constant MSG_NOT_INTERESTED = 3; constant MSG_HAVE = 4; constant MSG_BITFIELD = 5; constant MSG_REQUEST = 6; constant MSG_PIECE = 7; constant MSG_CANCEL = 8; typedef int(0..8) message_id;
a5dbb32004-08-17Martin Nilsson constant msg_to_string=([ MSG_CHOKE:"choke", MSG_UNCHOKE:"unchoke", MSG_INTERESTED:"interested", MSG_NOT_INTERESTED:"not_interested", MSG_HAVE:"have", MSG_BITFIELD:"bitfield", MSG_REQUEST:"request", MSG_PIECE:"piece", MSG_CANCEL:"cancel",
99e5fd2004-05-31Mirar (Pontus Hagland) ]);
fb397a2003-12-09Mirar (Pontus Hagland) function(string,mixed...:void|mixed) warning=werror;
222e6c2003-12-10Mirar (Pontus Hagland) int cancelled=0; int bandwidth_out=0; // bytes/s int bandwidth_in=0; // bytes/s array(int) bandwidth_out_a=({}); array(int) bandwidth_in_a=({});
fb397a2003-12-09Mirar (Pontus Hagland) 
ecc8142003-12-13Mirar (Pontus Hagland) int bytes_in=0; int bytes_out=0;
fb397a2003-12-09Mirar (Pontus Hagland) constant BANDWIDTH_O_METER_DELAY=0.5;
222e6c2003-12-10Mirar (Pontus Hagland) constant BANDWIDTH_O_METER_C=(int)(20/BANDWIDTH_O_METER_DELAY);
fb397a2003-12-09Mirar (Pontus Hagland)  // ---------------------------------------------------------------- // created with a peer-info dictionary
869c132003-12-11Henrik Grubbström (Grubba) void create(.Torrent _parent,mapping m)
fb397a2003-12-09Mirar (Pontus Hagland) { parent=_parent; ip=m->ip; port=m->port;
99e5fd2004-05-31Mirar (Pontus Hagland)  id=m["peer id"]||"?";
3524712015-05-26Martin Nilsson  if (id!="?")
127cff2003-12-17Mirar (Pontus Hagland)  {
4d278a2003-12-17Mirar (Pontus Hagland)  client_version=.PeerID.identify_peer(id);
99e5fd2004-05-31Mirar (Pontus Hagland) // werror("%O is %O is %O (out)\n",ip,id,client_version);
127cff2003-12-17Mirar (Pontus Hagland)  } // else // werror("incoming from %O\n",ip);
fb397a2003-12-09Mirar (Pontus Hagland)  _status("created"); warning=parent->warning;
222e6c2003-12-10Mirar (Pontus Hagland)  bitfield="\0"*strlen(parent->all_pieces_bits);
68d7c12003-12-13Mirar (Pontus Hagland) 
3524712015-05-26Martin Nilsson  if (m->fd)
68d7c12003-12-13Mirar (Pontus Hagland)  { fd=m->fd; bandwidth_in_a=({}); bandwidth_out_a=({}); online=1; fd->set_nonblocking(peer_read,0,peer_close);
c3e79c2003-12-16Martin Nilsson  parent->peers_ordered+=({this});
68d7c12003-12-13Mirar (Pontus Hagland)  _status("incoming");
99e5fd2004-05-31Mirar (Pontus Hagland)  call_out(connection_timeout,CONNECTION_TIMEOUT);
68d7c12003-12-13Mirar (Pontus Hagland)  }
fb397a2003-12-09Mirar (Pontus Hagland) }
9eaf1d2008-06-28Martin Nilsson private protected inline void _status(string type,void|string|int data)
fb397a2003-12-09Mirar (Pontus Hagland) { #ifdef BT_PEER_DEBUG werror("%O: %O(%O)\n",ip,type,data); #endif mode=type;
808e3f2003-12-14Mirar (Pontus Hagland)  Function.call_callback(parent->peer_update_status,type,data);
fb397a2003-12-09Mirar (Pontus Hagland)  status(type,data); }
99e5fd2004-05-31Mirar (Pontus Hagland) #ifdef BT_TRAFFIC_DEBUG int traffic_start=time(1); float traffic_last_choke=time(traffic_start); void traffic_debug(string fmt,mixed...args) { fmt=sprintf(fmt,@args); int t=time(); mapping tm=localtime(t); werror("%02d:%02d:%02d %7.1f %6.1f %-15s %-20s %s", tm->hour,tm->min,tm->sec, time(traffic_start), time(traffic_start)-traffic_last_choke, ip, client_version, fmt); } #endif
c3e79c2003-12-16Martin Nilsson //! Returns true if we can connect to this peer, //! when new or disconnected but not fatally.
3524712015-05-26Martin Nilsson int is_connectable() { return !online && mode!="dead" && mode!="failed";
fb397a2003-12-09Mirar (Pontus Hagland) }
c3e79c2003-12-16Martin Nilsson //! Returns true if this peer is online and connected.
3524712015-05-26Martin Nilsson int is_online() {
fb397a2003-12-09Mirar (Pontus Hagland)  return !!online; }
c3e79c2003-12-16Martin Nilsson //! Returns true if this peer is completed, as in
ecc8142003-12-13Mirar (Pontus Hagland) //! has downloaded everything already - and we
c3e79c2003-12-16Martin Nilsson //! shouldn't need to upload to get stuff.
222e6c2003-12-10Mirar (Pontus Hagland) int is_completed() { return is_complete; }
c3e79c2003-12-16Martin Nilsson //! Returns true if this peer is available, as in //! we can use it to download stuff.
92d16f2003-12-10Mirar (Pontus Hagland) int is_available()
222e6c2003-12-10Mirar (Pontus Hagland) {
3524712015-05-26Martin Nilsson  return !peer_choking && online==2 &&
92d16f2003-12-10Mirar (Pontus Hagland)  (handover || !sizeof(piece_callback)); }
c3e79c2003-12-16Martin Nilsson //! Returns true if this peer is activated, as in //! we're downloading from it.
92d16f2003-12-10Mirar (Pontus Hagland) int is_activated() { return !!sizeof(piece_callback);
222e6c2003-12-10Mirar (Pontus Hagland) }
c3e79c2003-12-16Martin Nilsson //! Returns true if this peer is strangled;
ecc8142003-12-13Mirar (Pontus Hagland) //! as in we don't want to upload more, because we're not
c3e79c2003-12-16Martin Nilsson //! getting any back.
ecc8142003-12-13Mirar (Pontus Hagland) int is_strangled() { return strangled; }
c3e79c2003-12-16Martin Nilsson //! Returns true if this peer is choking, as in //! doesn't send more data to us.
ecc8142003-12-13Mirar (Pontus Hagland) int is_choked() { return peer_choking; }
c3e79c2003-12-16Martin Nilsson //! Returns as multiset what this peer is downloading.
ecc8142003-12-13Mirar (Pontus Hagland) multiset(int) downloading_pieces() { return (multiset)(array(int))indices(piece_callback); }
c3e79c2003-12-16Martin Nilsson //! Connect to the peer; this is done async. //! status/mode will change from @expr{"connecting"@} to //! @expr{"dead"@} or to @expr{"connected"@} depending on result.
fb397a2003-12-09Mirar (Pontus Hagland) //! Will throw error if already online. //! //! Upon connect, protocol will be initiated in choked mode.
c3e79c2003-12-16Martin Nilsson //! When the protocol is up, status will change to @expr{"online"@} //! (or @expr{"failed"@} if the handshake failed).
fb397a2003-12-09Mirar (Pontus Hagland) //! void connect() { if (online)
c3e79c2003-12-16Martin Nilsson  error("Can't connect a peer that is already online.\n");
fb397a2003-12-09Mirar (Pontus Hagland)  _status("connecting");
ecc8142003-12-13Mirar (Pontus Hagland)  remove_call_out(connect); // just in case call_out(connection_timeout,CONNECTION_TIMEOUT);
bafa8a2003-12-14Mirar (Pontus Hagland)  string ip2; sscanf(ip,"%[0-9.]",ip2); if (ip2==ip) connect2(ip); else {
99e5fd2004-05-31Mirar (Pontus Hagland)  parent->dns_async->
3524712015-05-26Martin Nilsson  host_to_ip(ip,
bafa8a2003-12-14Mirar (Pontus Hagland)  lambda(string name,string ip) {
127cff2003-12-17Mirar (Pontus Hagland) // werror("%O = %O\n",name,ip);
bafa8a2003-12-14Mirar (Pontus Hagland)  connect2(ip); }); } } void connect2(string ip2) {
99e5fd2004-05-31Mirar (Pontus Hagland)  fd=Stdio.File();
fb397a2003-12-09Mirar (Pontus Hagland)  fd->async_connect(
bafa8a2003-12-14Mirar (Pontus Hagland)  ip2,port,
fb397a2003-12-09Mirar (Pontus Hagland)  lambda(int success) { if (success) {
222e6c2003-12-10Mirar (Pontus Hagland)  bandwidth_in_a=({}); bandwidth_out_a=({});
fb397a2003-12-09Mirar (Pontus Hagland)  online=1; peer_connected(); _status("connected"); } else {
ecc8142003-12-13Mirar (Pontus Hagland)  remove_call_out(connection_timeout);
99e5fd2004-05-31Mirar (Pontus Hagland)  drop("dead","failed"); if (CONNECTION_DEAD_RETRY>0) call_out(connect,CONNECTION_DEAD_RETRY);
fb397a2003-12-09Mirar (Pontus Hagland)  } }); }
99e5fd2004-05-31Mirar (Pontus Hagland) void drop(string how,void|string misc)
ecc8142003-12-13Mirar (Pontus Hagland) { online=0;
99e5fd2004-05-31Mirar (Pontus Hagland)  if (how) _status(how,misc); catch { fd->set_blocking(); }; catch { fd->close(); }; destruct(fd); fd=0; remove_call_out(keepalive); remove_call_out(bandwidth_o_meter); #ifdef BT_TRAFFIC_DEBUG traffic_debug("-- connection dropped %O,%O\n",how,misc); #endif map(Array.uniq(values(piece_callback)), lambda(function f) { catch { f(-1,0,"disconnected",this); }; }); piece_callback=([]); catch { parent->peer_lost(this); }; call_out(garb,GARB_DELAY); } void garb() { if (online || find_call_out(connect)!=-1) return;
f960bd2015-07-31Martin Nilsson  parent->peers_ordered-=({this}); parent->peers_unused-=({this});
99e5fd2004-05-31Mirar (Pontus Hagland)  m_delete(parent->peers,id);
f960bd2015-07-31Martin Nilsson  destruct(this);
99e5fd2004-05-31Mirar (Pontus Hagland) } void connection_timeout() {
ecc8142003-12-13Mirar (Pontus Hagland)  if (CONNECTION_DEAD_RETRY>0) call_out(connect,CONNECTION_DEAD_RETRY);
99e5fd2004-05-31Mirar (Pontus Hagland)  drop("dead","timeout");
ecc8142003-12-13Mirar (Pontus Hagland) }
c3e79c2003-12-16Martin Nilsson //! Disconnect a peer. //! Does nothing if we aren't online. //! status/mode will change to @expr{"disconnected"@},1 if we were online.
fb397a2003-12-09Mirar (Pontus Hagland) void disconnect() { if (online) {
ecc8142003-12-13Mirar (Pontus Hagland) #ifdef BT_PEER_DEBUG
c3e79c2003-12-16Martin Nilsson  werror("%O call disconnected %O\n",this,piece_callback);
ecc8142003-12-13Mirar (Pontus Hagland) #endif
99e5fd2004-05-31Mirar (Pontus Hagland)  drop("disconnected","we");
fb397a2003-12-09Mirar (Pontus Hagland)  } } // initialize the connection
9eaf1d2008-06-28Martin Nilsson protected void peer_connected()
fb397a2003-12-09Mirar (Pontus Hagland) { fd->set_nonblocking(peer_read,0,peer_close); transmit("\23BitTorrent protocol%-8c%20s%20s", 0, // 8 reserved bytes parent->info_sha1, parent->my_peer_id); }
9eaf1d2008-06-28Martin Nilsson protected private string sendbuf=""; protected private void transmit(string fmt,mixed ...args)
fb397a2003-12-09Mirar (Pontus Hagland) { if (sizeof(args)) fmt=sprintf(fmt,@args);
99e5fd2004-05-31Mirar (Pontus Hagland) #ifdef BT_TRAFFIC_DEBUG traffic_debug("-> %db: %O\n", strlen(fmt), strlen(fmt)>40?fmt[..29]+"..."+fmt[strlen(fmt)-10..]:fmt); #endif
fb397a2003-12-09Mirar (Pontus Hagland) // hexdump(fmt,0,0,"> "); if (sendbuf=="") { sendbuf=fmt;
222e6c2003-12-10Mirar (Pontus Hagland)  fd->set_write_callback(peer_write);
fb397a2003-12-09Mirar (Pontus Hagland)  } else sendbuf+=fmt; } array lastmsg=({-1,""}); void send_message(message_id n,string fmt,mixed ...args) { fmt=sprintf("%c"+fmt,n,@args); #ifdef BT_PEER_DEBUG werror("%O send message %d, %O\n",ip,n,sizeof(fmt)); #endif
99e5fd2004-05-31Mirar (Pontus Hagland) #ifdef BT_TRAFFIC_DEBUG traffic_debug("-> message %d (%s)\n", n,msg_to_string[n]); #endif
3524712015-05-26Martin Nilsson 
fb397a2003-12-09Mirar (Pontus Hagland)  lastmsg=({n,fmt}); transmit("%4c%s",strlen(fmt),fmt); // no need for a keepalive message for a while
3524712015-05-26Martin Nilsson  remove_call_out(keepalive);
fb397a2003-12-09Mirar (Pontus Hagland)  call_out(keepalive,KEEPALIVE_DELAY); }
9eaf1d2008-06-28Martin Nilsson protected private void peer_write()
fb397a2003-12-09Mirar (Pontus Hagland) {
5a80602003-12-28Mirar (Pontus Hagland)  for (;;)
fb397a2003-12-09Mirar (Pontus Hagland)  {
5a80602003-12-28Mirar (Pontus Hagland)  if (sendbuf=="") {
ecc8142003-12-13Mirar (Pontus Hagland) #ifdef BT_PEER_DEBUG
5a80602003-12-28Mirar (Pontus Hagland)  werror("%O: should send piece? %d left - i:%O,o:%O - %O\n",ip, sizeof(queued_pieces), bytes_in,bytes_out,bytes_in-bytes_out);
ecc8142003-12-13Mirar (Pontus Hagland) #endif
5a80602003-12-28Mirar (Pontus Hagland)  if (were_choking || !sizeof(queued_pieces)) { fd->set_write_callback(0); return; // we shouldn't do anything }
fb397a2003-12-09Mirar (Pontus Hagland) 
5a80602003-12-28Mirar (Pontus Hagland)  // tit-for-tat
3524712015-05-26Martin Nilsson  if (parent->do_we_strangle(this,bytes_in,bytes_out) &&
5a80602003-12-28Mirar (Pontus Hagland)  !parent->we_are_completed) {
537fb12004-01-11Johan Sundström #ifdef BT_PEER_DEBUG
5a80602003-12-28Mirar (Pontus Hagland)  werror("%O doesn't give us enough, i:%d o:%d i-o:%O: choking\n", ip,bytes_in,bytes_out,bytes_in-bytes_out);
537fb12004-01-11Johan Sundström #endif
5a80602003-12-28Mirar (Pontus Hagland)  strangle();
3524712015-05-26Martin Nilsson  fd->set_write_callback(0); return;
5a80602003-12-28Mirar (Pontus Hagland)  }
ecc8142003-12-13Mirar (Pontus Hagland) 
5a80602003-12-28Mirar (Pontus Hagland)  remove_call_out(fill_queue); if (queued_pieces[0][3]==0) fill_queue(); remove_call_out(fill_queue); call_out(fill_queue,0.0001); // after this transmission
fb397a2003-12-09Mirar (Pontus Hagland) 
ecc8142003-12-13Mirar (Pontus Hagland) #ifdef BT_PEER_DEBUG
5a80602003-12-28Mirar (Pontus Hagland)  werror("%O: sending piece %d,%d,%db\n",ip, queued_pieces[0][0], queued_pieces[0][1], strlen(queued_pieces[0][3]));
ecc8142003-12-13Mirar (Pontus Hagland) #endif
5a80602003-12-28Mirar (Pontus Hagland)  send_message(MSG_PIECE,"%4c%4c%s", queued_pieces[0][0], queued_pieces[0][1], queued_pieces[0][3]); bytes_out+=strlen(queued_pieces[0][3]);
ecc8142003-12-13Mirar (Pontus Hagland) 
5a80602003-12-28Mirar (Pontus Hagland)  queued_pieces=queued_pieces[1..]; }
fb397a2003-12-09Mirar (Pontus Hagland) 
5a80602003-12-28Mirar (Pontus Hagland)  int i=fd->write(sendbuf); bandwidth_out_count+=i;
fb397a2003-12-09Mirar (Pontus Hagland) #ifdef BT_PEER_DEBUG
5a80602003-12-28Mirar (Pontus Hagland)  werror("%O: wrote %d bytes: %O\n",ip,i,sendbuf[..min(40,i)]);
fb397a2003-12-09Mirar (Pontus Hagland) #endif
5a80602003-12-28Mirar (Pontus Hagland)  sendbuf=sendbuf[i..];
3524712015-05-26Martin Nilsson 
5a80602003-12-28Mirar (Pontus Hagland)  if (sendbuf!="")
3524712015-05-26Martin Nilsson  {
5a80602003-12-28Mirar (Pontus Hagland) // werror("%O: %d bytes left to send\n",ip,sizeof(sendbuf)); return; } else if (!sizeof(queued_pieces)) { fd->set_write_callback(0); uploading=0,uploading_pieces=(<>); return; }
3524712015-05-26Martin Nilsson  if (were_choking)
99e5fd2004-05-31Mirar (Pontus Hagland)  werror("%O: choking but uploading?\n",ip);
bafa8a2003-12-14Mirar (Pontus Hagland)  } }
9eaf1d2008-06-28Martin Nilsson protected private string readbuf=""; protected private void peer_read(mixed dummy,string s)
fb397a2003-12-09Mirar (Pontus Hagland) {
bafa8a2003-12-14Mirar (Pontus Hagland)  remove_call_out(peer_read_timeout); call_out(peer_read_timeout,300);
fb397a2003-12-09Mirar (Pontus Hagland)  bandwidth_in_count+=strlen(s); #ifdef BT_PEER_DEBUG werror("%O: read %d bytes: %O\n", ip,strlen(s),s[..min(100,strlen(s))]); #endif readbuf+=s;
68d7c12003-12-13Mirar (Pontus Hagland)  if (mode=="connected" || mode=="incoming")
fb397a2003-12-09Mirar (Pontus Hagland)  {
68d7c12003-12-13Mirar (Pontus Hagland)  if (readbuf[..min(strlen(readbuf)-1,19)] != "\23BitTorrent protocol"[..min(strlen(readbuf)-1,19)]) {
99e5fd2004-05-31Mirar (Pontus Hagland) #ifdef BT_TRAFFIC_DEBUG traffic_debug("<- %db: %O\n", strlen(readbuf), strlen(readbuf)>40?readbuf[..29]+"..."+ readbuf[strlen(readbuf)-10..]:readbuf); #endif drop("failed","not bittorrent");
68d7c12003-12-13Mirar (Pontus Hagland) 
127cff2003-12-17Mirar (Pontus Hagland)  warning("got non-bittorrent connection from %O (%s %s): %O\n", ip,
99e5fd2004-05-31Mirar (Pontus Hagland)  mode,
127cff2003-12-17Mirar (Pontus Hagland)  client_version,
4d278a2003-12-17Mirar (Pontus Hagland)  readbuf[..40]);
99e5fd2004-05-31Mirar (Pontus Hagland) 
68d7c12003-12-13Mirar (Pontus Hagland)  return; }
fb397a2003-12-09Mirar (Pontus Hagland)  if (strlen(readbuf)<68) return; // wait for more data
99e5fd2004-05-31Mirar (Pontus Hagland)  #ifdef BT_TRAFFIC_DEBUG traffic_debug("<- %db: %O\n", strlen(readbuf), strlen(readbuf)>40?readbuf[..29]+"..."+readbuf[strlen(readbuf)-10..]:readbuf); #endif if (readbuf[28..47]!=parent->info_sha1)
fb397a2003-12-09Mirar (Pontus Hagland)  {
99e5fd2004-05-31Mirar (Pontus Hagland)  drop("failed","torrent metainfo mismatch"); return; } if (id=="?") { id=readbuf[48..67]; client_version=.PeerID.identify_peer(id); // werror("(inc) %O is %O is %O\n",ip,id,client_version);
bafa8a2003-12-14Mirar (Pontus Hagland) 
99e5fd2004-05-31Mirar (Pontus Hagland)  if (parent->peers[id]) // no cheating by reconnecting :) { bytes_in=parent->peers[id]->bytes_in; bytes_out=parent->peers[id]->bytes_out;
127cff2003-12-17Mirar (Pontus Hagland) 
5a80602003-12-28Mirar (Pontus Hagland) #if 1
99e5fd2004-05-31Mirar (Pontus Hagland)  parent->peers[id]->disconnect();
127cff2003-12-17Mirar (Pontus Hagland) 
99e5fd2004-05-31Mirar (Pontus Hagland)  warning("%O (%s): disconnected old connection, we got a new\n", ip,client_version);
5a80602003-12-28Mirar (Pontus Hagland) #endif
fb397a2003-12-09Mirar (Pontus Hagland)  }
99e5fd2004-05-31Mirar (Pontus Hagland)  parent->peers[id]=this; } else if (readbuf[48..67]!=id) { drop("failed","peer id mismatch"); return; } if (mode=="incoming") peer_connected(); // send handshake
68d7c12003-12-13Mirar (Pontus Hagland) 
99e5fd2004-05-31Mirar (Pontus Hagland)  readbuf=readbuf[68..]; call_out(keepalive,KEEPALIVE_DELAY); bandwidth_o_meter();
fb397a2003-12-09Mirar (Pontus Hagland) 
3524712015-05-26Martin Nilsson  send_message(MSG_BITFIELD,
99e5fd2004-05-31Mirar (Pontus Hagland)  "%s",parent->file_got_bitfield());
222e6c2003-12-10Mirar (Pontus Hagland) // if (!parent->default_is_choked)
3524712015-05-26Martin Nilsson // send_message(MSG_UNCHOKE,"");
ecc8142003-12-13Mirar (Pontus Hagland) 
99e5fd2004-05-31Mirar (Pontus Hagland)  if (were_interested) send_message(MSG_INTERESTED,""); if (!were_choking) send_message(MSG_UNCHOKE,"");
fb397a2003-12-09Mirar (Pontus Hagland) 
bafa8a2003-12-14Mirar (Pontus Hagland) 
99e5fd2004-05-31Mirar (Pontus Hagland)  if (-1==search(parent->peers_ordered,this)) parent->peers_ordered+=({this}); parent->peers_unused-=({this});
bafa8a2003-12-14Mirar (Pontus Hagland) 
99e5fd2004-05-31Mirar (Pontus Hagland)  online=2; _status("online"); remove_call_out(connection_timeout);
fb397a2003-12-09Mirar (Pontus Hagland)  } if (mode=="online") { while (strlen(readbuf)>3) { int n; sscanf(readbuf,"%4c",n); if (strlen(readbuf)>=4+n) {
99e5fd2004-05-31Mirar (Pontus Hagland) #ifdef BT_TRAFFIC_DEBUG traffic_debug("<- %db: %O\n", 4+n, 4+n>40?readbuf[..29]+"..."+ readbuf[strlen(readbuf)-10..]:readbuf[..3+n]); #endif
fb397a2003-12-09Mirar (Pontus Hagland)  string msg=readbuf[4..3+n]; readbuf=readbuf[4+n..]; got_message_from_peer(msg); } else break; // wait for more } } }
bafa8a2003-12-14Mirar (Pontus Hagland) // no action for 5 minutes, drop and reconnect void peer_read_timeout() {
99e5fd2004-05-31Mirar (Pontus Hagland) #ifdef BT_TRAFFIC_DEBUG traffic_debug("-- timeout, reconnecting\n"); #endif
bafa8a2003-12-14Mirar (Pontus Hagland)  disconnect();
99e5fd2004-05-31Mirar (Pontus Hagland)  fd=Stdio.File();
bafa8a2003-12-14Mirar (Pontus Hagland)  connect(); }
fb397a2003-12-09Mirar (Pontus Hagland) void got_message_from_peer(string msg) { if (msg=="") { #ifdef BT_PEER_DEBUG werror("%O: got keepalive message\n",ip); #endif return; } #ifdef BT_PEER_DEBUG
99e5fd2004-05-31Mirar (Pontus Hagland)  werror("%O: got message type %d: %O (%d bytes)\n", ip,msg[0],msg[1..40],strlen(msg));
fb397a2003-12-09Mirar (Pontus Hagland) #endif int n,o,l; string data;
99e5fd2004-05-31Mirar (Pontus Hagland) #ifdef BT_TRAFFIC_DEBUG traffic_debug("<- - got message %d (%s)\n", msg[0],msg_to_string[msg[0]]); #endif
fb397a2003-12-09Mirar (Pontus Hagland)  switch (msg[0]) { case MSG_CHOKE:
222e6c2003-12-10Mirar (Pontus Hagland)  if (!peer_choking) { peer_choking=1; peer_choking_since=time(1);
ecc8142003-12-13Mirar (Pontus Hagland) #ifdef BT_PEER_DEBUG
c3e79c2003-12-16Martin Nilsson  werror("%O choke %O\n",this,piece_callback,this);
ecc8142003-12-13Mirar (Pontus Hagland) #endif
222e6c2003-12-10Mirar (Pontus Hagland)  Array.uniq(values(piece_callback))
c3e79c2003-12-16Martin Nilsson  (-1,0,"choked",this); // choked
222e6c2003-12-10Mirar (Pontus Hagland)  }
fb397a2003-12-09Mirar (Pontus Hagland)  _status("online","choked"); break; case MSG_UNCHOKE:
222e6c2003-12-10Mirar (Pontus Hagland)  if (peer_choking) { peer_choking=0;
ecc8142003-12-13Mirar (Pontus Hagland) #ifdef BT_PEER_DEBUG
c3e79c2003-12-16Martin Nilsson  werror("%O unchoke %O\n",this,piece_callback);
ecc8142003-12-13Mirar (Pontus Hagland) #endif
222e6c2003-12-10Mirar (Pontus Hagland)  Array.uniq(values(piece_callback))
c3e79c2003-12-16Martin Nilsson  (-1,0,"unchoked",this); // choked
ecc8142003-12-13Mirar (Pontus Hagland) 
c3e79c2003-12-16Martin Nilsson  parent->peer_unchoked(this);
222e6c2003-12-10Mirar (Pontus Hagland)  }
fb397a2003-12-09Mirar (Pontus Hagland)  _status("online","unchoked"); break; case MSG_INTERESTED: peer_interested=1; _status("online","interested");
ecc8142003-12-13Mirar (Pontus Hagland) 
99e5fd2004-05-31Mirar (Pontus Hagland)  if (were_choking)
3524712015-05-26Martin Nilsson  if (sizeof(parent->file_got)>sizeof(parent->file_want))
99e5fd2004-05-31Mirar (Pontus Hagland)  ; else { Function.call_callback( warning, "%O (%s): sent interested but we have none " "(want=%d got=%d)\n", ip,client_version, sizeof(parent->file_got),sizeof(parent->file_want) ); } if (!strangled) unchoke();
fb397a2003-12-09Mirar (Pontus Hagland)  break; case MSG_NOT_INTERESTED: peer_interested=0; _status("online","uninterested"); break; case MSG_HAVE: sscanf(msg,"%*c%4c",n);
222e6c2003-12-10Mirar (Pontus Hagland)  bitfield[n/8]=bitfield[n/8]|(128>>(n&7)); is_complete=(bitfield==parent->all_pieces_bits);
fb397a2003-12-09Mirar (Pontus Hagland) 
ecc8142003-12-13Mirar (Pontus Hagland)  if (is_complete && !sizeof(parent->file_want)) disconnect(); // we're complete, they're complete else if (!parent->file_available[n])
c3e79c2003-12-16Martin Nilsson  parent->peer_have(this,n);
fb397a2003-12-09Mirar (Pontus Hagland)  break; case MSG_BITFIELD: bitfield=msg[1..];
222e6c2003-12-10Mirar (Pontus Hagland)  is_complete=(bitfield==parent->all_pieces_bits);
c3e79c2003-12-16Martin Nilsson  parent->peer_gained(this);
ecc8142003-12-13Mirar (Pontus Hagland)  if (is_complete && !sizeof(parent->file_want)) disconnect(); // we're complete, they're complete
fb397a2003-12-09Mirar (Pontus Hagland)  break; case MSG_REQUEST: if (sscanf(msg,"%*c%4c%4c%4c",n,o,l)<4) { Function.call_callback(
127cff2003-12-17Mirar (Pontus Hagland)  warning,"%O (%s): got illegal piece message, " "too short (%d bytes)\n", ip,client_version,msg[0],strlen(msg));
fb397a2003-12-09Mirar (Pontus Hagland)  return; } else queue_piece(n,o,l); break; case MSG_PIECE: if (sscanf(msg,"%*c%4c%4c%s",n,o,data)<4) { Function.call_callback(
127cff2003-12-17Mirar (Pontus Hagland)  warning,"%O (%s): got illegal piece message, " "too short (%d bytes)\n", ip,client_version,msg[0],strlen(msg));
fb397a2003-12-09Mirar (Pontus Hagland)  return; } got_piece_from_peer(n,o,data); break; case MSG_CANCEL: queued_pieces=({}); break;
3524712015-05-26Martin Nilsson  default:
fb397a2003-12-09Mirar (Pontus Hagland)  Function.call_callback(
127cff2003-12-17Mirar (Pontus Hagland)  warning,"%O (%s): got unknown message type %d, %d bytes\n", ip,client_version,msg[0],strlen(msg));
fb397a2003-12-09Mirar (Pontus Hagland)  } } void peer_close() {
c3e79c2003-12-16Martin Nilsson  parent->peer_lost(this);
fb397a2003-12-09Mirar (Pontus Hagland) 
3524712015-05-26Martin Nilsson  remove_call_out(keepalive); remove_call_out(bandwidth_o_meter);
fb397a2003-12-09Mirar (Pontus Hagland) 
99e5fd2004-05-31Mirar (Pontus Hagland)  drop(0);
fb397a2003-12-09Mirar (Pontus Hagland)  if (online) { if (online==2) {
222e6c2003-12-10Mirar (Pontus Hagland)  online=0;
ecc8142003-12-13Mirar (Pontus Hagland) #ifdef BT_PEER_DEBUG
c3e79c2003-12-16Martin Nilsson  werror("%O call disconnected %O\n",this,piece_callback);
ecc8142003-12-13Mirar (Pontus Hagland) #endif
222e6c2003-12-10Mirar (Pontus Hagland)  Array.uniq(values(piece_callback))
c3e79c2003-12-16Martin Nilsson  (-1,0,"disconnected",this);
fb397a2003-12-09Mirar (Pontus Hagland)  piece_callback=([]); }
222e6c2003-12-10Mirar (Pontus Hagland)  else online=0;
fb397a2003-12-09Mirar (Pontus Hagland)  if (mode=="connected")
ecc8142003-12-13Mirar (Pontus Hagland)  {
fb397a2003-12-09Mirar (Pontus Hagland)  _status("failed","remote host closed connection in handshake");
ecc8142003-12-13Mirar (Pontus Hagland)  if (CONNECTION_HANDSHAKE_DROP_RETRY>0) call_out(connect,CONNECTION_HANDSHAKE_DROP_RETRY); }
fb397a2003-12-09Mirar (Pontus Hagland)  else {
ecc8142003-12-13Mirar (Pontus Hagland) // werror("%O disconnected: last message was %d:%O\n", // ip,@lastmsg); if (sizeof(parent->file_want)) // reconnect, we want more stuf {
c3e79c2003-12-16Martin Nilsson  parent->peers_unused+=({this}); parent->peers_ordered-=({this});
ecc8142003-12-13Mirar (Pontus Hagland)  }
fb397a2003-12-09Mirar (Pontus Hagland)  _status("disconnected","peer"); } } }
9eaf1d2008-06-28Martin Nilsson protected private void keepalive()
fb397a2003-12-09Mirar (Pontus Hagland) { call_out(keepalive,KEEPALIVE_DELAY); #ifdef BT_PEER_DEBUG
222e6c2003-12-10Mirar (Pontus Hagland)  werror("%O: sending keepalive\n",ip);
fb397a2003-12-09Mirar (Pontus Hagland) #endif
222e6c2003-12-10Mirar (Pontus Hagland)  transmit("\0\0\0\0"); // keepalive empty message
fb397a2003-12-09Mirar (Pontus Hagland) }
c3e79c2003-12-16Martin Nilsson //! Send a have message to tell I now have piece n. //! Ignored if not online.
fb397a2003-12-09Mirar (Pontus Hagland) void send_have(int n) { if (online<2) return; send_message(MSG_HAVE,"%4c",n); } // ----------------------------------------------------------------
222e6c2003-12-10Mirar (Pontus Hagland) mapping(string:function(int,int,string,object:void|mixed)) piece_callback=([]);
92d16f2003-12-10Mirar (Pontus Hagland) int handover=0; // more downloads is ok
fb397a2003-12-09Mirar (Pontus Hagland) 
c3e79c2003-12-16Martin Nilsson //! Called to request a chunk from this peer.
fb397a2003-12-09Mirar (Pontus Hagland) void request(int piece,int offset,int bytes,
222e6c2003-12-10Mirar (Pontus Hagland)  function(int,int,string,object:void|mixed) callback)
fb397a2003-12-09Mirar (Pontus Hagland) {
ecc8142003-12-13Mirar (Pontus Hagland)  if (!were_interested) show_interest(); // foreach (map(values(piece_callback),function_object)->peer;;.Peer p)
c3e79c2003-12-16Martin Nilsson // if (p!=this)
ecc8142003-12-13Mirar (Pontus Hagland) // { // werror("%O: funny thing in queue: %O\n",
c3e79c2003-12-16Martin Nilsson // this,piece_callback);
ecc8142003-12-13Mirar (Pontus Hagland) // exit(1); // }
222e6c2003-12-10Mirar (Pontus Hagland) 
fb397a2003-12-09Mirar (Pontus Hagland)  piece_callback[piece+":"+offset+":"+bytes]=callback; send_message(MSG_REQUEST, "%4c%4c%4c",piece,offset,bytes);
92d16f2003-12-10Mirar (Pontus Hagland)  handover=0;
fb397a2003-12-09Mirar (Pontus Hagland) } void got_piece_from_peer(int piece,int offset,string data) {
222e6c2003-12-10Mirar (Pontus Hagland)  function(int,int,string,object:void|mixed) f;
fb397a2003-12-09Mirar (Pontus Hagland)  string s; if ((f=piece_callback[s=piece+":"+offset+":"+strlen(data)])) { m_delete(piece_callback,s);
ecc8142003-12-13Mirar (Pontus Hagland) #ifdef BT_PEER_DEBUG
c3e79c2003-12-16Martin Nilsson  werror("%O got piece %O\n",this,piece);
ecc8142003-12-13Mirar (Pontus Hagland) #endif
c3e79c2003-12-16Martin Nilsson  f(piece,offset,data,this);
fb397a2003-12-09Mirar (Pontus Hagland)  }
3524712015-05-26Martin Nilsson  else if (cancelled)
222e6c2003-12-10Mirar (Pontus Hagland)  cancelled--;
fb397a2003-12-09Mirar (Pontus Hagland)  else { Function.call_callback(
127cff2003-12-17Mirar (Pontus Hagland)  warning,"%O (%s): got unrequested piece %d/%d+%db\n", ip,client_version,piece,offset,strlen(data));
fb397a2003-12-09Mirar (Pontus Hagland)  }
ecc8142003-12-13Mirar (Pontus Hagland)  bytes_in+=strlen(data);
127cff2003-12-17Mirar (Pontus Hagland)  if (strangled && !parent->do_we_strangle(this,bytes_in,bytes_out))
ecc8142003-12-13Mirar (Pontus Hagland)  {
537fb12004-01-11Johan Sundström #ifdef BT_PEER_DEBUG
ecc8142003-12-13Mirar (Pontus Hagland)  werror("%O gave us enough, i:%d o:%d i-o:%O: unchoking\n", ip,bytes_in,bytes_out,bytes_in-bytes_out);
537fb12004-01-11Johan Sundström #endif
ecc8142003-12-13Mirar (Pontus Hagland)  unstrangle(); }
fb397a2003-12-09Mirar (Pontus Hagland) }
ecc8142003-12-13Mirar (Pontus Hagland) void cancel_requests(int real)
222e6c2003-12-10Mirar (Pontus Hagland) {
ecc8142003-12-13Mirar (Pontus Hagland)  if (real) send_message(MSG_CANCEL,"");
222e6c2003-12-10Mirar (Pontus Hagland)  if (readbuf!="") cancelled=1; // expect one more without complaint piece_callback=([]);
ecc8142003-12-13Mirar (Pontus Hagland) #ifdef BT_PEER_DEBUG
c3e79c2003-12-16Martin Nilsson  werror("%O cancel_requests\n",this);
ecc8142003-12-13Mirar (Pontus Hagland) #endif
222e6c2003-12-10Mirar (Pontus Hagland) }
fb397a2003-12-09Mirar (Pontus Hagland) // ----------------------------------------------------------------
ecc8142003-12-13Mirar (Pontus Hagland) array(array(int|string)) queued_pieces=({});
fb397a2003-12-09Mirar (Pontus Hagland) 
9eaf1d2008-06-28Martin Nilsson protected void queue_piece(int piece,int offset,int length)
fb397a2003-12-09Mirar (Pontus Hagland) {
99e5fd2004-05-31Mirar (Pontus Hagland)  if (were_choking) { #if 0 // lots of clients to this, so don't warn Function.call_callback( warning, "%O (%s): got request for a piece while we're choking\n", ip,client_version); #endif return; }
3524712015-05-26Martin Nilsson  if (length>524288 ||
fb397a2003-12-09Mirar (Pontus Hagland)  length>parent->info["piece length"]) { Function.call_callback( warning,
127cff2003-12-17Mirar (Pontus Hagland)  "%O (%s): got request for a too large chunk, %d bytes - ignored\n", ip,client_version,length);
fb397a2003-12-09Mirar (Pontus Hagland)  return; }
ecc8142003-12-13Mirar (Pontus Hagland) #ifdef BT_PEER_DEBUG werror("%O: request for %d:o %d %db: %s\n", ip,piece,offset,length, parent->file_want[piece]?"we don't have it??!":"we have it"); #endif
222e6c2003-12-10Mirar (Pontus Hagland) 
bafa8a2003-12-14Mirar (Pontus Hagland)  if (parent->file_want[piece]) {
127cff2003-12-17Mirar (Pontus Hagland)  warning("%O (%s) requested piece %d, but we don't have it?\n", ip,client_version,piece);
bafa8a2003-12-14Mirar (Pontus Hagland)  return; }
fb397a2003-12-09Mirar (Pontus Hagland)  queued_pieces+=({({piece,offset,length,0})}); if (queued_pieces[0][3]==0) { remove_call_out(fill_queue); fill_queue(); }
222e6c2003-12-10Mirar (Pontus Hagland)  if (sendbuf=="")
fb397a2003-12-09Mirar (Pontus Hagland)  fd->set_write_callback(peer_write); }
9eaf1d2008-06-28Martin Nilsson protected void fill_queue()
fb397a2003-12-09Mirar (Pontus Hagland) { if (!sizeof(queued_pieces) ||
3524712015-05-26Martin Nilsson  queued_pieces[0][3]!=0) { uploading=0;
ecc8142003-12-13Mirar (Pontus Hagland)  uploading_pieces=(<>);
3524712015-05-26Martin Nilsson  return;
ecc8142003-12-13Mirar (Pontus Hagland)  }
fb397a2003-12-09Mirar (Pontus Hagland)  queued_pieces[0][3]=parent->get_piece_chunk(@queued_pieces[0][..2]);
ecc8142003-12-13Mirar (Pontus Hagland)  if (!uploading || !uploading_pieces[queued_pieces[0][0]]) { uploading=1; uploading_pieces=(<queued_pieces[0][0]>); Function.call_callback(parent->downloads_update_status); } } void unchoke() {
99e5fd2004-05-31Mirar (Pontus Hagland)  werror("%O (%s): unchoke\n",ip,client_version);
ecc8142003-12-13Mirar (Pontus Hagland)  were_choking=0;
99e5fd2004-05-31Mirar (Pontus Hagland)  send_message(MSG_UNCHOKE,"");
ecc8142003-12-13Mirar (Pontus Hagland) 
99e5fd2004-05-31Mirar (Pontus Hagland) // if (queued_pieces && sendbuf=="") // wont happen (transmit above) // fd->set_write_callback(peer_write);
ecc8142003-12-13Mirar (Pontus Hagland)  _status("online","unchoking"); } void choke() {
99e5fd2004-05-31Mirar (Pontus Hagland) // werror("%O (%s): choke\n",ip,client_version); #ifdef BT_TRAFFIC_DEBUG traffic_last_choke=time(traffic_start); #endif
ecc8142003-12-13Mirar (Pontus Hagland)  send_message(MSG_CHOKE,""); were_choking=1; _status("online","choking");
bafa8a2003-12-14Mirar (Pontus Hagland)  // if choke means "I'll drop all pieces in the queue" #if 1 queued_pieces=({}); uploading_pieces=(<>); uploading=0; #endif
ecc8142003-12-13Mirar (Pontus Hagland) }
9eaf1d2008-06-28Martin Nilsson protected int last_strangle;
99e5fd2004-05-31Mirar (Pontus Hagland) 
ecc8142003-12-13Mirar (Pontus Hagland) void strangle() { strangled=1; choke();
99e5fd2004-05-31Mirar (Pontus Hagland)  last_strangle=time(1);
ecc8142003-12-13Mirar (Pontus Hagland) } void unstrangle() {
99e5fd2004-05-31Mirar (Pontus Hagland)  if (time(1)-last_strangle<10) { void unstrangle2() { if (strangled) unstrangle(); }; remove_call_out(unstrangle2); call_out(unstrangle2,time(1)+10-last_strangle); } else { strangled=0; unchoke(); }
ecc8142003-12-13Mirar (Pontus Hagland) } void show_interest() {
0eb3642003-12-15Mirar (Pontus Hagland) #ifdef BT_PEER_DEBUG werror("%O: interested\n",ip); #endif
3524712015-05-26Martin Nilsson  send_message(MSG_INTERESTED,"");
ecc8142003-12-13Mirar (Pontus Hagland)  were_interested=1; } void show_uninterest() {
0eb3642003-12-15Mirar (Pontus Hagland) #ifdef BT_PEER_DEBUG werror("%O: uninterested\n",ip); #endif
3524712015-05-26Martin Nilsson  send_message(MSG_NOT_INTERESTED,"");
ecc8142003-12-13Mirar (Pontus Hagland)  were_interested=0;
fb397a2003-12-09Mirar (Pontus Hagland) } // ---------------------------------------------------------------- // stubs
3524712015-05-26Martin Nilsson 
c3e79c2003-12-16Martin Nilsson //! Called whenever there is a status change for this peer. //! Always called with @expr{"created"@} when the object is created.
fb397a2003-12-09Mirar (Pontus Hagland) //!
c3e79c2003-12-16Martin Nilsson //! Does not need to call inherited function.
fb397a2003-12-09Mirar (Pontus Hagland) void status(string type,void|int|string data) { } // ---------------------------------------------------------------- string _sprintf(int t) {
3524712015-05-26Martin Nilsson  if (t=='O')
99e5fd2004-05-31Mirar (Pontus Hagland)  return sprintf("Bittorrent.Peer(%s:%d %O%s%s%s%s%s%s%s v%d ^%d b/s q:%d p:%d sb:%d)",
222e6c2003-12-10Mirar (Pontus Hagland)  ip,port,mode,
92d16f2003-12-10Mirar (Pontus Hagland)  is_activated()?" dl":"",
222e6c2003-12-10Mirar (Pontus Hagland)  uploading?" ul":"",
ecc8142003-12-13Mirar (Pontus Hagland)  is_complete?" complete":"", peer_choking?" choking":"", strangled?" strangled":"", is_activated()?" activated":"", handover?" handover":"",
99e5fd2004-05-31Mirar (Pontus Hagland)  bandwidth_in,bandwidth_out, sizeof(queued_pieces), sizeof(piece_callback), sizeof(sendbuf));
fb397a2003-12-09Mirar (Pontus Hagland)  return 0; }
c071bc2017-11-05Henrik Grubbström (Grubba) protected void _destruct()
fb397a2003-12-09Mirar (Pontus Hagland) {
99e5fd2004-05-31Mirar (Pontus Hagland)  drop(0);
fb397a2003-12-09Mirar (Pontus Hagland) } // ----------------------------------------------------------------
9eaf1d2008-06-28Martin Nilsson protected private int bandwidth_in_count=0; protected private int bandwidth_out_count=0; protected private int bandwidth_t0=time(1); protected private float bandwidth_t=time(bandwidth_t0);
fb397a2003-12-09Mirar (Pontus Hagland)  void bandwidth_o_meter() { call_out(bandwidth_o_meter,BANDWIDTH_O_METER_DELAY); float t1=time(bandwidth_t0); float t=t1-bandwidth_t; if (t<0.001) return;
222e6c2003-12-10Mirar (Pontus Hagland)  bandwidth_in_a+=({(int)(bandwidth_in_count/t)}); bandwidth_out_a+=({(int)(bandwidth_out_count/t)}); if (sizeof(bandwidth_in_a)>BANDWIDTH_O_METER_C) { bandwidth_in_a=bandwidth_in_a[1..]; bandwidth_out_a=bandwidth_out_a[1..]; } bandwidth_in=Array.sum(bandwidth_in_a)/sizeof(bandwidth_in_a); bandwidth_out=Array.sum(bandwidth_out_a)/sizeof(bandwidth_out_a);
fb397a2003-12-09Mirar (Pontus Hagland)  bandwidth_in_count=0; bandwidth_out_count=0; bandwidth_t=t1; }