peerwire.c

来自「elinks下lynx是最重要的二个文本浏览器, 在linux下非常实用, el」· C语言 代码 · 共 961 行 · 第 1/2 页

C
961
字号
		peer->local.choked = 1;		add_requests_to_bittorrent_piece_cache(peer, &peer->local);		{			struct bittorrent_peer_request *message, *next;			foreachsafe (message, next, peer->queue) {				if (message->id == BITTORRENT_MESSAGE_CANCEL) {					del_from_list(message);					mem_free(message);				}			}		}		break;	case BITTORRENT_MESSAGE_UNCHOKE:		peer->local.choked = 0;		break;	case BITTORRENT_MESSAGE_INTERESTED:		peer->remote.interested = 1;		break;	case BITTORRENT_MESSAGE_NOT_INTERESTED:		peer->remote.interested = 0;		break;	case BITTORRENT_MESSAGE_HAVE:		if (message_length < sizeof(uint32_t))			return BITTORRENT_STATE_ERROR;		piece = get_bittorrent_peer_integer(buffer, 5);		/* Is piece out of bound? */		if (piece >= bittorrent->meta.pieces)			break;		/* Is the piece already recorded? */		if (test_bitfield_bit(peer->bitfield, piece))			break;		length = get_bittorrent_piece_length(&bittorrent->meta, piece);		update_bittorrent_peer_connection_stats(peer, 0, length, 0);		set_bitfield_bit(peer->bitfield, piece);		peer->remote.seeder = bitfield_is_set(peer->bitfield);		update_bittorrent_piece_cache(peer, piece);		if (peer->local.interested		    || test_bitfield_bit(bittorrent->cache->bitfield, piece))			break;		set_bittorrent_peer_interested(peer);		break;	case BITTORRENT_MESSAGE_BITFIELD:		data   = buffer->data + 5;		length = message_length - 1; /* Message ID byte. */		if (length > get_bitfield_byte_size(peer->bitfield->bitsize))			break;		/* Are bitfield messages allowed at this point? */		if (peer->remote.bitfield)			break;		/* XXX: The function tail will set the bitfield flag ... */		copy_bitfield(peer->bitfield, data, length);		peer->remote.seeder = bitfield_is_set(peer->bitfield);		update_bittorrent_piece_cache_from_bitfield(peer);		/* Force checking of the interested flag. */		foreach_bitfield_set (piece, peer->bitfield) {		    	if (test_bitfield_bit(bittorrent->cache->bitfield, piece))				continue;			set_bittorrent_peer_interested(peer);			break;		}		break;	case BITTORRENT_MESSAGE_REQUEST:	case BITTORRENT_MESSAGE_CANCEL:		if (message_length < sizeof(uint32_t) * 3)			return BITTORRENT_STATE_ERROR;		piece  = get_bittorrent_peer_integer(buffer, 5);		offset = get_bittorrent_peer_integer(buffer, 9);		length = get_bittorrent_peer_integer(buffer, 13);		/* FIXME: Should requests be allowed to overlap pieces? */		if (peer->remote.choked		    || piece >= bittorrent->meta.pieces		    || offset + length > get_bittorrent_piece_length(&bittorrent->meta, piece)		    || !test_bitfield_bit(bittorrent->cache->bitfield, piece))			break;		if (length > get_bittorrent_peerwire_max_request_length())			return BITTORRENT_STATE_ERROR;		if (message_id == BITTORRENT_MESSAGE_REQUEST) {			add_bittorrent_peer_request(&peer->remote, piece, offset, length);		} else {			del_bittorrent_peer_request(&peer->remote, piece, offset, length);		}		break;	case BITTORRENT_MESSAGE_PIECE:		if (message_length < sizeof(uint32_t) * 2)			return BITTORRENT_STATE_ERROR;		piece  = get_bittorrent_peer_integer(buffer, 5);		offset = get_bittorrent_peer_integer(buffer, 9);		length = message_length - 9; /* Msg ID byte + 2 ints */		data   = buffer->data + 13; /* Offset includes msg len */		if (peer->local.choked		    || piece >= bittorrent->meta.pieces		    || offset + length > get_bittorrent_piece_length(&bittorrent->meta, piece)		    || length == 0)			break;		update_bittorrent_peer_connection_stats(peer, length, 0, 0);		state = add_to_bittorrent_piece_cache(peer, piece, offset, data, length, write_errno);		if (state != BITTORRENT_STATE_OK)			return state;		break;	case BITTORRENT_MESSAGE_KEEP_ALIVE:	default:		/* Keep-alive messages doesn't require any special handling.		 * Unknown peer messages are simply dropped. */		break;	}	/* Any message will cause bitfield messages to become invalid. */	peer->remote.bitfield = 1;	return BITTORRENT_STATE_OK;}static voidread_bittorrent_peer_data(struct socket *socket, struct read_buffer *buffer){	struct bittorrent_peer_connection *peer = socket->conn;	if (!peer->remote.handshake) {		enum bittorrent_handshake_state state;		/* We still needs to read the peer ID from the handshake. */		state = do_read_bittorrent_peer_handshake(socket, buffer);		if (state != BITTORRENT_PEER_HANDSHAKE_OK)			return;		/* If the handshake was ok'ed see if there are any messages to		 * read. */		assert(peer->remote.handshake);	}	/* All messages atleast contains an integer prefix. */	while (buffer->length > sizeof(uint32_t)) {		enum bittorrent_message_id message_id;		uint32_t length;		int write_errno = 0;		message_id = check_bittorrent_peer_message(peer, buffer, &length);		if (message_id == BITTORRENT_MESSAGE_INCOMPLETE)			break;		if (message_id == BITTORRENT_MESSAGE_ERROR) {			done_bittorrent_peer_connection(peer);			return;		}		switch (read_bittorrent_peer_message(peer, message_id, buffer, length, &write_errno)) {		case BITTORRENT_STATE_OK:			break;		case BITTORRENT_STATE_OUT_OF_MEM:			abort_connection(peer->bittorrent->conn, S_OUT_OF_MEM);			return;		case BITTORRENT_STATE_ERROR:		default:			if (!write_errno) {				done_bittorrent_peer_connection(peer);				return;			}			/* Shutdown on fatal errors! */			abort_connection(peer->bittorrent->conn, -write_errno);			return;		}		/* Remove the processed data from the input buffer. */		kill_buffer_data(buffer, length + sizeof(length));		update_bittorrent_connection_stats(peer->bittorrent, 0, 0,						   length + sizeof(length));	}	update_bittorrent_peer_connection_state(peer);}/* ************************************************************************** *//* Peer handshake exchanging: *//* ************************************************************************** *//* XXX: Note, handshake messages are also handled above in the function * read_bittorrnt_peer_data() if it is incomplete when reading it below. * Often, peers will only send up to and including the info hash, so that the * peer ID arrives later. *//* The handshake was sent, so notify the remote peer about completed piece in a * bitfield message and start reading any remaining bits of the handshake or any * other message.  */static voidsent_bittorrent_peer_handshake(struct socket *socket){	struct bittorrent_peer_connection *peer = socket->conn;	struct read_buffer *buffer = peer->socket->read_buffer;	assert(buffer);	/* Only send the bitfield message if there is anything interesting to	 * report. */	if (peer->bittorrent->cache->completed_pieces) {		assert(list_empty(peer->queue));		send_bittorrent_peer_message(peer, BITTORRENT_MESSAGE_BITFIELD);	}	read_from_socket(peer->socket, buffer, S_TRANS,			 read_bittorrent_peer_data);}/* This function is called when a handhake has been read from an incoming * connection and is used as a callback for when a new peer connection has * successfully been established. */voidsend_bittorrent_peer_handshake(struct socket *socket){	struct bittorrent_peer_connection *peer = socket->conn;	struct bittorrent_connection *bittorrent = peer->bittorrent;	struct bittorrent_meta *meta = &bittorrent->meta;	unsigned char reserved[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };	unsigned char handshake[BITTORRENT_PEER_HANDSHAKE_SIZE];	int i = 0;#define add_to_handshake(handshake, i, data) \	do { \		memcpy((handshake) + (i), data, sizeof(data)); \		i += sizeof(data); \	} while (0)	add_to_handshake(handshake, i, BITTORRENT_ID);	add_to_handshake(handshake, i, reserved);	add_to_handshake(handshake, i, meta->info_hash);	add_to_handshake(handshake, i, bittorrent->peer_id);#undef	add_to_handshake	assert(handshake[0] == 19);	if (!peer->socket->read_buffer) {		/* Just return. Failure is handled by alloc_read_buffer(). */		peer->socket->read_buffer = alloc_read_buffer(peer->socket);		if (!peer->socket->read_buffer) {			done_bittorrent_peer_connection(peer);			return;		}	}	peer->local.handshake = 1;	/* Temporarily stop processing of all incoming messages while we send	 * the handshake so that reading of especially the bitfield message	 * won't cause any interested message to be queued _before_ we have a	 * chance to send the bitfield message which MUST be first. */	clear_handlers(peer->socket->fd);	/* Can't use request_.. version because it will create a new read buffer	 * and we might want to hold on to the old buffer if the peer ID of the	 * handshake was not read. */	write_to_socket(peer->socket, handshake, sizeof(handshake),			S_TRANS, sent_bittorrent_peer_handshake);}#if 0/* DHT is not supported, so commented out. *//* Checks for the DHT flags used by atleast Brams client to indicate it supports * trackerless BitTorrent. */static inline intbittorrent_peer_supports_dht(unsigned char flags[8]){	return !!(flags[7] & 1);}#endif/* This function is called each time there is something from the handshake * message to read. */static enum bittorrent_handshake_statecheck_bittorrent_peer_handshake(struct bittorrent_peer_connection *peer,				struct read_buffer *buffer){	bittorrent_id_T info_hash;	struct bittorrent_peer *peer_info;	enum bittorrent_handshake_state state;	if (buffer->length < 20)		return BITTORRENT_PEER_HANDSHAKE_INCOMPLETE;	if (memcmp(buffer->data, BITTORRENT_ID, sizeof(BITTORRENT_ID)))		return BITTORRENT_PEER_HANDSHAKE_ERROR;	if (buffer->length < 28)		return BITTORRENT_PEER_HANDSHAKE_INCOMPLETE;#if 0	/* DHT is not supported, so commented out. */	/* Check the reserved flags */	peer->remote.dht = bittorrent_peer_supports_dht(&buffer->data[20]);#endif	if (buffer->length < 48)		return BITTORRENT_PEER_HANDSHAKE_INCOMPLETE;	memcpy(info_hash, &buffer->data[28], sizeof(info_hash));	if (peer->local.initiater) {		struct bittorrent_meta *meta = &peer->bittorrent->meta;		assert(peer->bittorrent);		/* Check if the info_hash matches the one in the associated		 * bittorrent connection. */		if (memcmp(meta->info_hash, info_hash, sizeof(info_hash)))			return BITTORRENT_PEER_HANDSHAKE_ERROR;		if (buffer->length < BITTORRENT_PEER_HANDSHAKE_SIZE)			return BITTORRENT_PEER_HANDSHAKE_INFO_HASH;		/* If we got the peer info using the compact tracker flag there		 * is no peer ID, so set it. Else check if the peer has sent the		 * expected ID. */		if (bittorrent_id_is_empty(peer->id))			memcpy(peer->id, &buffer->data[48], sizeof(peer->id));		else if (memcmp(peer->id, &buffer->data[48], sizeof(peer->id)))				return BITTORRENT_PEER_HANDSHAKE_ERROR;	} else {		struct bittorrent_connection *bittorrent = peer->bittorrent;		/* If the peer ID is empty we didn't establish the connection. */		assert(bittorrent_id_is_empty(peer->id));		/* Look-up the bittorrent connection and drop peer connections		 * to unknown torrents. */		if (!bittorrent) {			bittorrent = find_bittorrent_connection(info_hash);			if (!bittorrent)				return BITTORRENT_PEER_HANDSHAKE_ERROR;			peer->bittorrent = bittorrent;			/* Don't know if this is the right place to do this			 * initialization, but it is the first time we know how			 * many bits there should be room for. Also, it feels			 * safer to have it created before the peer connection			 * is moved to a bittorrent connection so all other code			 * can assume it is always there. */			peer->bitfield = init_bitfield(bittorrent->meta.pieces);			if (!peer->bitfield)				return BITTORRENT_PEER_HANDSHAKE_ERROR;		}		/* FIXME: It would be possible to already add the peer to the		 * neighbor list here. Should we? --jonas */		if (buffer->length < BITTORRENT_PEER_HANDSHAKE_SIZE)			return BITTORRENT_PEER_HANDSHAKE_INFO_HASH;		memcpy(peer->id, &buffer->data[48], sizeof(peer->id));	}	assert(peer->bittorrent);	/* Remove any recorded peer from the peer info list. */	/* XXX: This has to be done before checking if the peer is known. Since	 * known in this case means whether the peer already has a connection	 * associated. */	peer_info = get_peer_from_bittorrent_pool(peer->bittorrent, peer->id);	if (peer_info) {		del_from_list(peer_info);		mem_free(peer_info);	}	/* Even if the peer is already associated with a connection we still	 * needs to check if the ID is known since we might have just gotten it.	 * Removing it first makes that possible. */	del_from_list(peer);	/* Check if the peer is already connected to us. */	state = bittorrent_id_is_known(peer->bittorrent, peer->id)	      ? BITTORRENT_PEER_HANDSHAKE_ERROR : BITTORRENT_PEER_HANDSHAKE_OK;	/* For unassociated connections; move the peer connection from the list	 * of unknown pending peer connections to a bittorrent connection's list	 * of active peers. */	add_to_list(peer->bittorrent->peers, peer);	return state;}/* Common backend for reading handshakes. */static enum bittorrent_handshake_statedo_read_bittorrent_peer_handshake(struct socket *socket, struct read_buffer *buffer){	struct bittorrent_peer_connection *peer = socket->conn;	enum bittorrent_handshake_state state;	state = check_bittorrent_peer_handshake(peer, buffer);	switch (state) {	case BITTORRENT_PEER_HANDSHAKE_OK:		/* The whole handshake was successfully read. */		peer->remote.handshake = 1;		kill_buffer_data(buffer, BITTORRENT_PEER_HANDSHAKE_SIZE);		update_bittorrent_connection_stats(peer->bittorrent, 0, 0,						   BITTORRENT_PEER_HANDSHAKE_SIZE);		switch (get_bittorrent_blacklist_flags(peer->id)) {		case BITTORRENT_BLACKLIST_PEER_POOL:		case BITTORRENT_BLACKLIST_NONE:			break;		case BITTORRENT_BLACKLIST_MALICIOUS:		case BITTORRENT_BLACKLIST_BEHAVIOUR:		default:			done_bittorrent_peer_connection(peer);			return BITTORRENT_PEER_HANDSHAKE_ERROR;		}		if (!peer->local.handshake)			send_bittorrent_peer_handshake(socket);		break;	case BITTORRENT_PEER_HANDSHAKE_INFO_HASH:		if (!peer->local.handshake) {			send_bittorrent_peer_handshake(socket);			/* XXX: The peer connection might have disappear from			 * under us at this point so do not reregister the			 * socket for reading. */			break;		}		read_from_socket(peer->socket, buffer, S_TRANS,				 read_bittorrent_peer_handshake);		break;	case BITTORRENT_PEER_HANDSHAKE_ERROR:		done_bittorrent_peer_connection(peer);		break;	case BITTORRENT_PEER_HANDSHAKE_INCOMPLETE:		/* The whole handshake was not read so wait for more. */		read_from_socket(peer->socket, buffer, S_TRANS,				 read_bittorrent_peer_handshake);		break;	}	return state;}voidread_bittorrent_peer_handshake(struct socket *socket, struct read_buffer *buffer){	do_read_bittorrent_peer_handshake(socket, buffer);}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?