| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272 | /* <!-- copyright *//* * aria2 - The high speed download utility * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL.  If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so.  If you * do not wish to do so, delete this exception statement from your * version.  If you delete this exception statement from all source * files in the program, then also delete it here. *//* copyright --> */#include "PeerConnection.h"#include <cstring>#include <cassert>#include <algorithm>#include "message.h"#include "DlAbortEx.h"#include "LogFactory.h"#include "Logger.h"#include "BtHandshakeMessage.h"#include "Socket.h"#include "a2netcompat.h"#include "ARC4Encryptor.h"#include "ARC4Decryptor.h"#include "StringFormat.h"namespace aria2 {PeerConnection::PeerConnection(int32_t cuid, const SocketHandle& socket)  :cuid(cuid),   socket(socket),   logger(LogFactory::getInstance()),   resbuf(new unsigned char[MAX_PAYLOAD_LEN]),   resbufLength(0),   currentPayloadLength(0),   lenbufLength(0),   _socketBuffer(socket),   _encryptionEnabled(false),   _prevPeek(false){}PeerConnection::~PeerConnection(){  delete [] resbuf;}void PeerConnection::pushStr(const std::string& data){  if(_encryptionEnabled) {    const size_t len = data.size();    unsigned char* chunk = new unsigned char[len];    try {      _encryptor->encrypt        (chunk, len, reinterpret_cast<const unsigned char*>(data.data()), len);    } catch(RecoverableException& e) {      delete [] chunk;      throw;    }    _socketBuffer.pushBytes(chunk, len);  } else {    _socketBuffer.pushStr(data);  }}void PeerConnection::pushBytes(unsigned char* data, size_t len){  if(_encryptionEnabled) {    unsigned char* chunk = new unsigned char[len];    try {      _encryptor->encrypt(chunk, len, data, len);    } catch(RecoverableException& e) {      delete [] data;      delete [] chunk;      throw;    }    delete [] data;    _socketBuffer.pushBytes(chunk, len);  } else {    _socketBuffer.pushBytes(data, len);  }}bool PeerConnection::receiveMessage(unsigned char* data, size_t& dataLength) {  if(resbufLength == 0 && 4 > lenbufLength) {    if(!socket->isReadable(0)) {      return false;    }    // read payload size, 32bit unsigned integer    size_t remaining = 4-lenbufLength;    size_t temp = remaining;    readData(lenbuf+lenbufLength, remaining, _encryptionEnabled);    if(remaining == 0) {      if(socket->wantRead() || socket->wantWrite()) {        return false;      }      // we got EOF      if(logger->debug()) {        logger->debug("CUID#%d - In PeerConnection::receiveMessage(),"                      " remain=%lu",                      cuid, static_cast<unsigned long>(temp));      }      throw DL_ABORT_EX(EX_EOF_FROM_PEER);    }    lenbufLength += remaining;    if(4 > lenbufLength) {      // still 4-lenbufLength bytes to go      return false;    }    uint32_t payloadLength;    memcpy(&payloadLength, lenbuf, sizeof(payloadLength));    payloadLength = ntohl(payloadLength);    if(payloadLength > MAX_PAYLOAD_LEN) {      throw DL_ABORT_EX(StringFormat(EX_TOO_LONG_PAYLOAD, payloadLength).str());    }    currentPayloadLength = payloadLength;  }  if(!socket->isReadable(0)) {    return false;  }  // we have currentPayloadLen-resbufLen bytes to read  size_t remaining = currentPayloadLength-resbufLength;  size_t temp = remaining;  if(remaining > 0) {    readData(resbuf+resbufLength, remaining, _encryptionEnabled);    if(remaining == 0) {      if(socket->wantRead() || socket->wantWrite()) {        return false;      }      // we got EOF      if(logger->debug()) {        logger->debug("CUID#%d - In PeerConnection::receiveMessage(),"                      " payloadlen=%lu, remaining=%lu",                      cuid,                      static_cast<unsigned long>(currentPayloadLength),                      static_cast<unsigned long>(temp));      }      throw DL_ABORT_EX(EX_EOF_FROM_PEER);    }    resbufLength += remaining;    if(currentPayloadLength > resbufLength) {      return false;    }  }  // we got whole payload.  resbufLength = 0;  lenbufLength = 0;  if(data) {    memcpy(data, resbuf, currentPayloadLength);  }  dataLength = currentPayloadLength;  return true;}bool PeerConnection::receiveHandshake(unsigned char* data, size_t& dataLength,                                      bool peek) {  assert(BtHandshakeMessage::MESSAGE_LENGTH >= resbufLength);  bool retval = true;  if(_prevPeek && !peek && resbufLength) {    // We have data in previous peek.    // There is a chance that socket is readable because of EOF, for example,    // official bttrack shutdowns socket after sending first 48 bytes of    // handshake in its NAT checking.    // So if there are data in resbuf, return it without checking socket    // status.    _prevPeek = false;    retval = BtHandshakeMessage::MESSAGE_LENGTH <= resbufLength;  } else {    _prevPeek = peek;    size_t remaining = BtHandshakeMessage::MESSAGE_LENGTH-resbufLength;    if(remaining > 0 && !socket->isReadable(0)) {      dataLength = 0;      return false;    }    if(remaining > 0) {      size_t temp = remaining;      readData(resbuf+resbufLength, remaining, _encryptionEnabled);      if(remaining == 0) {        if(socket->wantRead() || socket->wantWrite()) {          return false;        }        // we got EOF        if(logger->debug()) {          logger->debug            ("CUID#%d - In PeerConnection::receiveHandshake(), remain=%lu",             cuid, static_cast<unsigned long>(temp));        }        throw DL_ABORT_EX(EX_EOF_FROM_PEER);      }      resbufLength += remaining;      if(BtHandshakeMessage::MESSAGE_LENGTH > resbufLength) {        retval = false;      }    }  }  size_t writeLength = std::min(resbufLength, dataLength);  memcpy(data, resbuf, writeLength);  dataLength = writeLength;  if(retval && !peek) {    resbufLength = 0;  }  return retval;}void PeerConnection::readData(unsigned char* data, size_t& length, bool encryption){  if(encryption) {    unsigned char temp[MAX_PAYLOAD_LEN];    assert(MAX_PAYLOAD_LEN >= length);    socket->readData(temp, length);    _decryptor->decrypt(data, length, temp, length);  } else {    socket->readData(data, length);  }}void PeerConnection::enableEncryption(const SharedHandle<ARC4Encryptor>& encryptor,                                      const SharedHandle<ARC4Decryptor>& decryptor){  _encryptor = encryptor;  _decryptor = decryptor;  _encryptionEnabled = true;}void PeerConnection::presetBuffer(const unsigned char* data, size_t length){  size_t nwrite = std::min((size_t)MAX_PAYLOAD_LEN, length);  memcpy(resbuf, data, nwrite);  resbufLength = length;}bool PeerConnection::sendBufferIsEmpty() const{  return _socketBuffer.sendBufferIsEmpty();}ssize_t PeerConnection::sendPendingData(){  ssize_t writtenLength = _socketBuffer.send();  if(logger->debug()) {    logger->debug("sent %d byte(s).", writtenLength);  }  return writtenLength;}} // namespace aria2
 |