2006-03-21 14:12:51 +00:00
|
|
|
/* <!-- copyright */
|
|
|
|
/*
|
|
|
|
* aria2 - a simple utility for downloading files faster
|
|
|
|
*
|
|
|
|
* Copyright (C) 2006 Tatsuhiro Tsujikawa
|
|
|
|
*
|
|
|
|
* This program 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 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program 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 this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
/* copyright --> */
|
|
|
|
#include "PeerConnection.h"
|
|
|
|
#include "message.h"
|
|
|
|
#include "DlAbortEx.h"
|
|
|
|
#include "PeerMessageUtil.h"
|
|
|
|
#include "Util.h"
|
2006-04-17 16:17:20 +00:00
|
|
|
#include "LogFactory.h"
|
2006-03-21 14:12:51 +00:00
|
|
|
#include <netinet/in.h>
|
|
|
|
|
|
|
|
PeerConnection::PeerConnection(int cuid, const Socket* socket,
|
2006-04-17 16:17:20 +00:00
|
|
|
const Option* op,
|
2006-03-21 14:12:51 +00:00
|
|
|
Peer* peer, TorrentMan* torrentMan)
|
|
|
|
:cuid(cuid), socket(socket), option(op), logger(logger), peer(peer),
|
|
|
|
torrentMan(torrentMan),
|
2006-04-17 16:17:20 +00:00
|
|
|
resbufLength(0), currentPayloadLength(0), lenbufLength(0) {
|
|
|
|
logger = LogFactory::getInstance();
|
|
|
|
}
|
2006-03-21 14:12:51 +00:00
|
|
|
|
|
|
|
PeerConnection::~PeerConnection() {}
|
|
|
|
|
|
|
|
void PeerConnection::sendHandshake() const {
|
|
|
|
/**
|
|
|
|
* pstrlen --- '19', 1byte
|
|
|
|
* pstr --- "BitTorrent protocol", 19bytes
|
|
|
|
* reserved --- \0 * 8, 8bytes
|
|
|
|
* info_hash --- info_hash, 20bytes
|
|
|
|
* peer_id --- peer_id, 20bytes
|
|
|
|
* total: 68bytes
|
|
|
|
*/
|
|
|
|
char msg[HANDSHAKE_MESSAGE_LENGTH];
|
|
|
|
memset(msg, 0, sizeof(msg));
|
|
|
|
msg[0] = 19;
|
|
|
|
memcpy(&msg[1], PSTR, strlen(PSTR));
|
|
|
|
memcpy(&msg[28], torrentMan->getInfoHash(), 20);
|
|
|
|
memcpy(&msg[48], torrentMan->peerId.c_str(), 20);
|
|
|
|
writeOutgoingMessageLog("handshake");
|
|
|
|
socket->writeData(msg, sizeof(msg));
|
|
|
|
}
|
|
|
|
|
|
|
|
void PeerConnection::sendKeepAlive() const {
|
|
|
|
/**
|
|
|
|
* len --- 0, 4bytes
|
|
|
|
* total: 4bytes
|
|
|
|
*/
|
|
|
|
char msg[4];
|
|
|
|
memset(msg, 0, sizeof(msg));
|
2006-03-27 Tatsuhiro Tsujikawa <tujikawa at rednoah dot com>
* configure.in: Added gnutls support. Added several CPP macros.
* m4/openssl.m4: Added.
* src/SocketCore.{h,cc}: Drop const quarifier from writeData(),
readData(), peekData(). Added gnutls support.
* src/HttpDownloadCommand.cc: Removed SleepCommand.h
* src/TrackerWatcherCommand.{h,cc}: Added. This command creates
TrackerInitCommand periodicaly(TorrentMan::minInterval).
* src/TorrentMan.cc: Remove downloadedSize == 0 check from
save().
Instead, added a check for whether setup method has executed
successfully.
* src/TorrentMan.h: Added member vaiable setupComplete. Updated
DEFAULT_ANNOUNCE_INTERVAL and DEFAULT_ANNOUNCE_MIN_INTERVAL to
300.
* src/Makefile.am: Updated.
* src/messageDigest.h: Added. This is a macro calculating SHA1
digest
using whether OpenSSL or gcrypt, depending on the result of
configure
script.
* src/ShaVisitor.{h,cc}: Removed direct dependency on OpenSSL by
using
messageDigest.h.
* src/TorrentAutoSaveCommand.h: Removed unused variable cuid.
* src/PeerListenCommand.cc: Added log about port binded
successfully.
Fixed memory leak.
* src/main.cc: Added gnutls support. Replaced LIB_SSL with
ENABLE_BITTORRENT where they are not related to OpenSSL but
BitTorrent.
Removed instantiation of TrackerInitCommand. Instead,
TrackerWatcherCommand is instantiated and pushed to the command
queue.
* src/InitiateConnectionCommandFactory.cc: Replaced HAVE_LIBSSL
with
ENABLE_SSL.
* src/Request.cc: Replaced HAVE_LIBSSL with ENABLE_SSL.
* src/RequestSlotMan.cc:
(deleteCompletedRequestSlot)
If a piece is already acquired by another command, delete the
request
slots for the piece.
* src/TrackerUpdateCommand.cc:
(execute)
Changed log level of MSG_TRACKER_WARNING_MESSAGE from info to
warn.
Added a check whether peer list is null.
Fixed the bug that causes sending completed event to the tracker
several times.
* src/TrackerInitCommand.cc:
(execute)
Fixed the bug that causes sending completed event to the tracker
several times.
* src/AbstractDiskWriter.{h,cc}: Removed direct dependency on
OpenSSL
by using messageDigest.h.
2006-03-26 Tatsuhiro Tsujikawa <tujikawa at rednoah dot com>
* PeerConnection.cc: Replaced log message "keep-alive" with
"keep alive".
* PeerInteractionCommand.{h,cc}: Close connection if peer is
choking
localhost long time.
* TorrentMan.cc: When adding new peer with duplicate = true, if
the
number of peer list is equal to or grater than MAX_PEER_LIST,
delete
at most 100 failure entry from the list. If with duplicate =
false,
MAX_PEER_LIST is not checked.
* PeerListenCommand.cc: Fixed the argument order of log message.
2006-03-25 Tatsuhiro Tsujikawa <tujikawa at rednoah dot com>
* Logger.h: Moved enum LEVEL from SimpleLogger.h to here.
Added warn().
* SimpleLogger.h: Moved enum LEVEL to Logger.h.
Implemented warn().
Defined 2 macros(WRITE_LOG, WRITE_LOG_EX) to avoid duplicated
code.
2006-03-27 14:47:26 +00:00
|
|
|
writeOutgoingMessageLog("keep alive");
|
2006-03-21 14:12:51 +00:00
|
|
|
socket->writeData(msg, sizeof(msg));
|
|
|
|
}
|
|
|
|
|
|
|
|
void PeerConnection::createNLengthMessage(char* msg, int msgLen, int payloadLen, int id) const {
|
|
|
|
assert(msgLen >= 5);
|
|
|
|
memset(msg, 0, msgLen);
|
|
|
|
setIntParam(msg, payloadLen);
|
|
|
|
msg[4] = (char)id;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PeerConnection::setIntParam(char* dest, int param) const {
|
|
|
|
int nParam = htonl(param);
|
|
|
|
memcpy(dest, &nParam, 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PeerConnection::writeOutgoingMessageLog(const char* msg) const {
|
|
|
|
logger->info(MSG_SEND_PEER_MESSAGE, cuid, peer->ipaddr.c_str(), peer->port, msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PeerConnection::writeOutgoingMessageLog(const char* msg, int index) const {
|
|
|
|
logger->info(MSG_SEND_PEER_MESSAGE_WITH_INDEX, cuid, peer->ipaddr.c_str(), peer->port, msg, index);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PeerConnection::writeOutgoingMessageLog(const char* msg, const unsigned char* bitfield, int bitfieldLength) const {
|
|
|
|
logger->info(MSG_SEND_PEER_MESSAGE_WITH_BITFIELD, cuid, peer->ipaddr.c_str(), peer->port, msg, Util::toHex(bitfield, bitfieldLength).c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
void PeerConnection::writeOutgoingMessageLog(const char* msg, int index, int begin, int length) const {
|
|
|
|
logger->info(MSG_SEND_PEER_MESSAGE_WITH_INDEX_BEGIN_LENGTH, cuid, peer->ipaddr.c_str(), peer->port, msg, index, begin, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PeerConnection::sendChoke() const {
|
|
|
|
/**
|
|
|
|
* len --- 1, 4bytes
|
|
|
|
* id --- 0, 1byte
|
|
|
|
* total: 5bytes
|
|
|
|
*/
|
|
|
|
char msg[5];
|
|
|
|
createNLengthMessage(msg, sizeof(msg), 1, 0);
|
|
|
|
writeOutgoingMessageLog("choke");
|
|
|
|
socket->writeData(msg, sizeof(msg));
|
|
|
|
}
|
|
|
|
|
|
|
|
void PeerConnection::sendUnchoke() const {
|
|
|
|
/**
|
|
|
|
* len --- 1, 4bytes
|
|
|
|
* id --- 1, 1byte
|
|
|
|
* total: 5bytes
|
|
|
|
*/
|
|
|
|
char msg[5];
|
|
|
|
createNLengthMessage(msg, sizeof(msg), 1, 1);
|
|
|
|
writeOutgoingMessageLog("unchoke");
|
|
|
|
socket->writeData(msg, sizeof(msg));
|
|
|
|
}
|
|
|
|
|
|
|
|
void PeerConnection::sendInterested() const {
|
|
|
|
/**
|
|
|
|
* len --- 1, 4bytes
|
|
|
|
* id --- 2, 1byte
|
|
|
|
* total: 5bytes
|
|
|
|
*/
|
|
|
|
char msg[5];
|
|
|
|
createNLengthMessage(msg, sizeof(msg), 1, 2);
|
|
|
|
writeOutgoingMessageLog("interested");
|
|
|
|
socket->writeData(msg, sizeof(msg));
|
|
|
|
}
|
|
|
|
|
|
|
|
void PeerConnection::sendNotInterested() const {
|
|
|
|
/**
|
|
|
|
* len --- 1, 4bytes
|
|
|
|
* id --- 3, 1byte
|
|
|
|
* total: 5bytes
|
|
|
|
*/
|
|
|
|
char msg[5];
|
|
|
|
createNLengthMessage(msg, sizeof(msg), 1, 3);
|
|
|
|
writeOutgoingMessageLog("not interested");
|
|
|
|
socket->writeData(msg, sizeof(msg));
|
|
|
|
}
|
|
|
|
|
|
|
|
void PeerConnection::sendHave(int index) const {
|
|
|
|
/**
|
|
|
|
* len --- 5, 4bytes
|
|
|
|
* id --- 4, 1byte
|
|
|
|
* piece index --- index, 4bytes
|
|
|
|
* total: 9bytes
|
|
|
|
*/
|
|
|
|
char msg[9];
|
|
|
|
createNLengthMessage(msg, sizeof(msg), 5, 4);
|
|
|
|
setIntParam(&msg[5], index);
|
|
|
|
writeOutgoingMessageLog("have", index);
|
|
|
|
socket->writeData(msg, sizeof(msg));
|
|
|
|
}
|
|
|
|
|
|
|
|
void PeerConnection::sendBitfield() const {
|
|
|
|
int len = torrentMan->getBitfieldLength();
|
|
|
|
const unsigned char* bitfield = torrentMan->getBitfield();
|
|
|
|
/**
|
|
|
|
* len --- 1+len, 4bytes
|
|
|
|
* id --- 5, 1byte
|
|
|
|
* bitfield --- bitfield, len bytes
|
|
|
|
* total: 5+len bytes
|
|
|
|
*/
|
|
|
|
int msgLen = 5+len;
|
|
|
|
char* msg = new char[msgLen];
|
|
|
|
try {
|
|
|
|
createNLengthMessage(msg, msgLen, 1+len, 5);
|
|
|
|
writeOutgoingMessageLog("bitfield", bitfield, len);
|
|
|
|
socket->writeData(msg, msgLen);
|
|
|
|
delete [] msg;
|
|
|
|
} catch(Exception* ex) {
|
|
|
|
delete [] msg;
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PeerConnection::sendRequest(int index, int begin, int length) const {
|
|
|
|
/**
|
|
|
|
* len --- 13, 4bytes
|
|
|
|
* id --- 6, 1byte
|
|
|
|
* index --- index, 4bytes
|
|
|
|
* begin --- begin, 4bytes
|
|
|
|
* length --- length, 4bytes
|
|
|
|
* total: 17bytes
|
|
|
|
*/
|
|
|
|
char msg[17];
|
|
|
|
createNLengthMessage(msg, sizeof(msg), 13, 6);
|
|
|
|
setIntParam(&msg[5], index);
|
|
|
|
setIntParam(&msg[9], begin);
|
|
|
|
setIntParam(&msg[13], length);
|
|
|
|
writeOutgoingMessageLog("request", index, begin, length);
|
|
|
|
socket->writeData(msg, sizeof(msg));
|
|
|
|
}
|
|
|
|
|
|
|
|
void PeerConnection::sendPiece(int index, int begin, int length) const {
|
|
|
|
/**
|
|
|
|
* len --- 9+length, 4bytes
|
|
|
|
* id --- 7, 1byte
|
|
|
|
* index --- index, 4bytes
|
|
|
|
* begin --- begin, 4bytes
|
|
|
|
* sub total: 13bytes
|
|
|
|
* additionally,
|
|
|
|
* block --- data, X bytes
|
|
|
|
*/
|
|
|
|
char msg[13];
|
|
|
|
createNLengthMessage(msg, sizeof(msg), 9+length, 7);
|
|
|
|
setIntParam(&msg[5], index);
|
|
|
|
setIntParam(&msg[9], begin);
|
|
|
|
writeOutgoingMessageLog("piece", index, begin, length);
|
|
|
|
socket->writeData(msg, sizeof(msg));
|
|
|
|
int BUF_SIZE = 4096;
|
|
|
|
char buf[BUF_SIZE];
|
|
|
|
int iteration = length/BUF_SIZE;
|
|
|
|
long long int pieceOffset = ((long long int)index*torrentMan->pieceLength)+begin;
|
|
|
|
for(int i = 0; i < iteration; i++) {
|
2006-04-16 14:38:19 +00:00
|
|
|
if(torrentMan->diskAdaptor->readData(buf, BUF_SIZE, pieceOffset+i*BUF_SIZE) < BUF_SIZE) {
|
2006-03-21 14:12:51 +00:00
|
|
|
throw new DlAbortEx("piece reading failed.");
|
|
|
|
}
|
|
|
|
socket->writeData(buf, BUF_SIZE);
|
|
|
|
}
|
|
|
|
int rem = length%BUF_SIZE;
|
|
|
|
if(rem > 0) {
|
2006-04-16 14:38:19 +00:00
|
|
|
if(torrentMan->diskAdaptor->readData(buf, rem, pieceOffset+iteration*BUF_SIZE) < rem) {
|
2006-03-21 14:12:51 +00:00
|
|
|
throw new DlAbortEx("piece reading failed.");
|
|
|
|
}
|
|
|
|
socket->writeData(buf, rem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PeerConnection::sendPieceHeader(int index, int begin, int length) const {
|
|
|
|
/**
|
|
|
|
* len --- 9+length, 4bytes
|
|
|
|
* id --- 7, 1byte
|
|
|
|
* index --- index, 4bytes
|
|
|
|
* begin --- begin, 4bytes
|
|
|
|
* sub total: 13bytes
|
|
|
|
* additionally,
|
|
|
|
* block --- data, X bytes
|
|
|
|
*/
|
|
|
|
char msg[13];
|
|
|
|
createNLengthMessage(msg, sizeof(msg), 9+length, 7);
|
|
|
|
setIntParam(&msg[5], index);
|
|
|
|
setIntParam(&msg[9], begin);
|
|
|
|
writeOutgoingMessageLog("piece", index, begin, length);
|
|
|
|
socket->writeData(msg, sizeof(msg));
|
|
|
|
}
|
|
|
|
|
|
|
|
int PeerConnection::sendPieceData(long long int offset, int length) const {
|
|
|
|
int BUF_SIZE = 256;
|
|
|
|
char buf[BUF_SIZE];
|
|
|
|
int iteration = length/BUF_SIZE;
|
|
|
|
int writtenLength = 0;
|
|
|
|
bool isWritable = true;
|
|
|
|
for(int i = 0; i < iteration; i++) {
|
|
|
|
isWritable = socket->isWritable(0);
|
|
|
|
if(!isWritable) {
|
|
|
|
return writtenLength;
|
|
|
|
}
|
2006-04-16 14:38:19 +00:00
|
|
|
if(torrentMan->diskAdaptor->readData(buf, BUF_SIZE, offset+i*BUF_SIZE) < BUF_SIZE) {
|
2006-03-21 14:12:51 +00:00
|
|
|
throw new DlAbortEx("piece reading failed.");
|
|
|
|
}
|
|
|
|
socket->writeData(buf, BUF_SIZE);
|
|
|
|
writtenLength += BUF_SIZE;
|
|
|
|
}
|
|
|
|
if(socket->isWritable(0)) {
|
|
|
|
int rem = length%BUF_SIZE;
|
|
|
|
if(rem > 0) {
|
2006-04-16 14:38:19 +00:00
|
|
|
if(torrentMan->diskAdaptor->readData(buf, rem, offset+iteration*BUF_SIZE) < rem) {
|
2006-03-21 14:12:51 +00:00
|
|
|
throw new DlAbortEx("piece reading failed.");
|
|
|
|
}
|
|
|
|
socket->writeData(buf, rem);
|
|
|
|
writtenLength += rem;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return writtenLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PeerConnection::sendCancel(int index, int begin, int length) const {
|
|
|
|
/**
|
|
|
|
* len --- 13, 4bytes
|
|
|
|
* id --- 8, 1byte
|
|
|
|
* index --- index, 4bytes
|
|
|
|
* begin --- begin, 4bytes
|
|
|
|
* length -- length, 4bytes
|
|
|
|
* total: 17bytes
|
|
|
|
*/
|
|
|
|
char msg[17];
|
|
|
|
createNLengthMessage(msg, sizeof(msg), 13, 8);
|
|
|
|
setIntParam(&msg[5], index);
|
|
|
|
setIntParam(&msg[9], begin);
|
|
|
|
setIntParam(&msg[13], length);
|
|
|
|
writeOutgoingMessageLog("cancel", index, begin, length);
|
|
|
|
socket->writeData(msg, sizeof(msg));
|
|
|
|
}
|
|
|
|
|
2006-05-09 15:54:14 +00:00
|
|
|
bool PeerConnection::receiveMessage(char* msg, int& length) {
|
2006-03-21 14:12:51 +00:00
|
|
|
if(!socket->isReadable(0)) {
|
2006-05-09 15:54:14 +00:00
|
|
|
return false;
|
2006-03-21 14:12:51 +00:00
|
|
|
}
|
|
|
|
if(resbufLength == 0 && lenbufLength != 4) {
|
|
|
|
// read payload size, 4-byte integer
|
|
|
|
int remain = 4-lenbufLength;
|
|
|
|
int temp = remain;
|
|
|
|
socket->readData(lenbuf+lenbufLength, temp);
|
|
|
|
if(temp == 0) {
|
|
|
|
// we got EOF
|
|
|
|
throw new DlAbortEx(EX_EOF_FROM_PEER);
|
|
|
|
}
|
|
|
|
if(remain != temp) {
|
|
|
|
// still 4-temp bytes to go
|
|
|
|
lenbufLength += temp;
|
2006-05-09 15:54:14 +00:00
|
|
|
return false;
|
2006-03-21 14:12:51 +00:00
|
|
|
}
|
|
|
|
//payloadLen = ntohl(nPayloadLen);
|
|
|
|
int payloadLength = ntohl(*((int*)lenbuf));
|
|
|
|
if(payloadLength > MAX_PAYLOAD_LEN) {
|
|
|
|
throw new DlAbortEx("max payload length exceeded. length = %d",
|
|
|
|
payloadLength);
|
|
|
|
}
|
|
|
|
currentPayloadLength = payloadLength;
|
|
|
|
}
|
|
|
|
// we have currentPayloadLen-resbufLen bytes to read
|
|
|
|
int remaining = currentPayloadLength-resbufLength;
|
|
|
|
if(remaining > 0) {
|
|
|
|
socket->readData(resbuf+resbufLength, remaining);
|
|
|
|
if(remaining == 0) {
|
|
|
|
// we got EOF
|
|
|
|
throw new DlAbortEx(EX_EOF_FROM_PEER);
|
|
|
|
}
|
|
|
|
resbufLength += remaining;
|
|
|
|
if(currentPayloadLength != resbufLength) {
|
2006-05-09 15:54:14 +00:00
|
|
|
return false;
|
2006-03-21 14:12:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// we got whole payload.
|
|
|
|
resbufLength = 0;
|
|
|
|
lenbufLength = 0;
|
2006-05-09 15:54:14 +00:00
|
|
|
|
|
|
|
memcpy(msg, resbuf, currentPayloadLength);
|
|
|
|
length = currentPayloadLength;
|
|
|
|
return true;
|
2006-03-21 14:12:51 +00:00
|
|
|
}
|
|
|
|
|
2006-05-09 15:54:14 +00:00
|
|
|
bool PeerConnection::receiveHandshake(char* msg, int& length) {
|
2006-03-21 14:12:51 +00:00
|
|
|
if(!socket->isReadable(0)) {
|
2006-05-09 15:54:14 +00:00
|
|
|
return false;
|
2006-03-21 14:12:51 +00:00
|
|
|
}
|
|
|
|
int remain = HANDSHAKE_MESSAGE_LENGTH-resbufLength;
|
|
|
|
int temp = remain;
|
|
|
|
socket->readData(resbuf+resbufLength, temp);
|
|
|
|
if(temp == 0) {
|
|
|
|
// we got EOF
|
|
|
|
throw new DlAbortEx(EX_EOF_FROM_PEER);
|
|
|
|
}
|
|
|
|
if(remain != temp) {
|
|
|
|
resbufLength += temp;
|
2006-05-09 15:54:14 +00:00
|
|
|
return false;
|
2006-03-21 14:12:51 +00:00
|
|
|
}
|
|
|
|
// we got whole handshake payload
|
|
|
|
resbufLength = 0;
|
2006-05-09 15:54:14 +00:00
|
|
|
|
|
|
|
memcpy(msg, resbuf, HANDSHAKE_MESSAGE_LENGTH);
|
|
|
|
length = HANDSHAKE_MESSAGE_LENGTH;
|
|
|
|
return true;
|
2006-03-21 14:12:51 +00:00
|
|
|
}
|