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 + -
显示快捷键?