⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 peerconnect.c

📁 elinks下lynx是最重要的二个文本浏览器, 在linux下非常实用, elinks也是gentoo安装过程中默认使用的浏览器, 这是elinks源代码
💻 C
字号:
/* BitTorrent peer-wire connection management */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <errno.h>#include <stdio.h>#include <sys/types.h>#ifdef HAVE_SYS_SOCKET_H#include <sys/socket.h> /* OS/2 needs this after sys/types.h */#endif#ifdef HAVE_NETINET_IN_H#include <netinet/in.h>#endif#ifdef HAVE_ARPA_INET_H#include <arpa/inet.h>#endif#include "elinks.h"#include "config/options.h"#include "main/select.h"#include "main/timer.h"#include "network/connection.h"#include "network/socket.h"#include "network/state.h"#include "osdep/osdep.h"#include "protocol/bittorrent/common.h"#include "protocol/bittorrent/peerwire.h"#include "protocol/bittorrent/peerconnect.h"#include "protocol/bittorrent/piececache.h"#include "protocol/protocol.h"#include "protocol/uri.h"#include "util/bitfield.h"#include "util/memory.h"#include "util/string.h"#include "util/time.h"/* Only one port is opened and shared between all running BitTorrent * connections. This holds the descripter of the listening socket. */static int bittorrent_socket = -1;/* The active BitTorrent connections sharing the above listening port. */static INIT_LIST_HEAD(bittorrent_connections);/* The incoming (and pending anonymous) peer connections which has not yet been * assigned to a BitTorrent connection because the info hash has not been read * from the handshake.  */static INIT_LIST_HEAD(bittorrent_peer_connections);/* Loop the bittorrent connection list and return matching connection * or NULL. */struct bittorrent_connection *find_bittorrent_connection(bittorrent_id_T info_hash){	struct bittorrent_connection *bittorrent;	foreach (bittorrent, bittorrent_connections)		if (!memcmp(bittorrent->meta.info_hash, info_hash, sizeof(info_hash)))			return bittorrent;	return NULL;}static voidcheck_bittorrent_peer_blacklisting(struct bittorrent_peer_connection *peer,				   enum connection_state state){	enum bittorrent_blacklist_flags flags = BITTORRENT_BLACKLIST_NONE;	if (bittorrent_id_is_empty(peer->id)	    || !get_opt_bool("protocol.http.bugs.allow_blacklist"))		return;	switch (state) {	case -ECONNREFUSED:	case -ENETUNREACH:		flags |= BITTORRENT_BLACKLIST_PEER_POOL;		break;	case S_CANT_WRITE:	case S_CANT_READ:		if (!peer->local.handshake			|| !peer->remote.handshake)			flags |= BITTORRENT_BLACKLIST_PEER_POOL;		break;	default:		break;	}	if (flags != BITTORRENT_BLACKLIST_NONE) {		add_bittorrent_blacklist_flags(peer->id, flags);	}}/* ************************************************************************** *//* Timeout scheduling: *//* ************************************************************************** */static voidbittorrent_peer_connection_timeout(struct bittorrent_peer_connection *peer){	/* Unset the timer so it won't get stopped when removing the peer	 * connection. */	peer->timer = TIMER_ID_UNDEF;	done_bittorrent_peer_connection(peer);}/* The timeout mechanism is used for both inactive incoming peer connections * and peer connections attached to a BitTorrent (master) connection. */voidset_bittorrent_peer_connection_timeout(struct bittorrent_peer_connection *peer){	milliseconds_T timeout = sec_to_ms(get_opt_int("protocol.bittorrent.peerwire.timeout"));	kill_timer(&peer->timer);	install_timer(&peer->timer, timeout,		      (void (*)(void *)) bittorrent_peer_connection_timeout,		      peer);}/* ************************************************************************** *//* Socket callback implementation: *//* ************************************************************************** *//* Called when the connection changes state. Usually state starts out being * S_DMS (while looking up the host) then moves to S_CONN (while connecting), * and should hopefully become S_TRANS (while transfering). Note, state can hold * both internally defined connection states as described above and errno * values, such as ECONNREFUSED. The errno values are passed negative so in the * previous example the errno would be passed as -ECONNREFUSED. */static voidset_bittorrent_socket_state(struct socket *socket, enum connection_state state){	struct bittorrent_peer_connection *peer = socket->conn;	if (state == S_TRANS && peer->bittorrent)		set_connection_state(peer->bittorrent->conn, S_TRANS);}/* Called when progress is made such as when the select() loop detects and * schedules reads and writes. The state variable must be ignored. */static voidset_bittorrent_socket_timeout(struct socket *socket, enum connection_state state){	assert(state == 0);	set_bittorrent_peer_connection_timeout(socket->conn);}/* Called when a non-fatal  error condition has appeared, i.e. the condition is * caused by some internal or local system error or simply a timeout. */static voidretry_bittorrent_socket(struct socket *socket, enum connection_state state){	struct bittorrent_peer_connection *peer = socket->conn;	check_bittorrent_peer_blacklisting(peer, state);	/* FIXME: Maybe we should try to reconnect (or simply add connect info	 * to the peer info list) , but only if we initiated the connection,	 * i.e. if peer->local.initiater == 1, since it could be just the	 * tracker probing us. */	done_bittorrent_peer_connection(peer);}/* Called when a fatal and unrecoverable error condition has appeared, such as a * DNS query failed. */static voiddone_bittorrent_socket(struct socket *socket, enum connection_state state){	struct bittorrent_peer_connection *peer = socket->conn;	check_bittorrent_peer_blacklisting(peer, state);	done_bittorrent_peer_connection(peer);}/* All the above socket handlers are attached to the socket via this table. */static struct socket_operations bittorrent_socket_operations = {	set_bittorrent_socket_state,	set_bittorrent_socket_timeout,	retry_bittorrent_socket,	done_bittorrent_socket,};/* ************************************************************************** *//* Peer connection management: *//* ************************************************************************** *//* Allocate and initialize a basic peer connection either for incoming or * outgoing connection. */static struct bittorrent_peer_connection *init_bittorrent_peer_connection(int socket){	struct bittorrent_peer_connection *peer;	peer = mem_calloc(1, sizeof(*peer));	if (!peer) return NULL;	peer->socket = init_socket(peer, &bittorrent_socket_operations);	if (!peer->socket) {		mem_free(peer);		return NULL;	}	/* We want simultaneous reads and writes. */	peer->socket->duplex = 1;	peer->socket->fd = socket;	init_list(peer->local.requests);	init_list(peer->remote.requests);	init_list(peer->queue);	/* Peers start out being choked and not being interested. */	peer->local.choked = 1;	peer->remote.choked = 1;	return peer;}/* Shutdown an remove a peer connection from what ever context it * is currently attached, be it the. */voiddone_bittorrent_peer_connection(struct bittorrent_peer_connection *peer){	del_from_list(peer);	/* The peer might not have been associated with a BitTorrent connection,	 * yet. */	if (peer->bittorrent) {		add_requests_to_bittorrent_piece_cache(peer, &peer->local);		if (peer->bitfield) {			remove_bittorrent_peer_from_piece_cache(peer);			mem_free(peer->bitfield);		}	}	free_list(peer->remote.requests);	free_list(peer->queue);	kill_timer(&peer->timer);	/* Unregister the socket from the select() loop mechanism. */	done_socket(peer->socket);	mem_free(peer->socket);	mem_free(peer);}/* Establish connection to a peer. As a backend, it uses the internal and more * generic connection creater which takes care of DNS querying etc. */enum bittorrent_statemake_bittorrent_peer_connection(struct bittorrent_connection *bittorrent,				struct bittorrent_peer *peer_info){	struct uri uri;	struct bittorrent_peer_connection *peer;	unsigned char port[5];	peer = init_bittorrent_peer_connection(-1);	if (!peer) return BITTORRENT_STATE_OUT_OF_MEM;	peer->local.initiater = 1;	add_to_list(bittorrent->peers, peer);	peer->bittorrent = bittorrent;	peer->bitfield = init_bitfield(bittorrent->meta.pieces);	if (!peer->bitfield) {		done_bittorrent_peer_connection(peer);		return BITTORRENT_STATE_OUT_OF_MEM;	}	memcpy(peer->id, peer_info->id, sizeof(peer->id));	/* XXX: Very hacky; construct a fake URI from which make_connection()	 * can extract the IP address and port number. */	/* FIXME: Rather change the make_connection() interface. This is an ugly	 * hack. */	/* FIXME: Set the ipv6 flag iff ... */	memset(&uri, 0, sizeof(uri));	uri.protocol = PROTOCOL_BITTORRENT;	uri.host     = peer_info->ip;	uri.hostlen  = strlen(peer_info->ip);	uri.port     = port;	uri.portlen  = snprintf(port, sizeof(port), "%u", peer_info->port);	make_connection(peer->socket, &uri, send_bittorrent_peer_handshake, 1);	return BITTORRENT_STATE_OK;}/* ************************************************************************** *//* Listening socket management: *//* ************************************************************************** *//* Number of connections to keep in the listening backlog before dropping new * ones. */#define LISTEN_BACKLOG	\	get_opt_int("protocol.bittorrent.peerwire.connections")/* Called when we receive a connection on the listening socket. */static voidaccept_bittorrent_peer_connection(void *____){	struct sockaddr_in addr;	int peer_sock;	int addrlen = sizeof(addr);	struct bittorrent_peer_connection *peer;	struct read_buffer *buffer;	peer_sock = accept(bittorrent_socket, (struct sockaddr *) &addr, &addrlen);	if (peer_sock < 0) return;	if (set_nonblocking_fd(peer_sock) < 0) {		close(peer_sock);		return;	}	peer = init_bittorrent_peer_connection(peer_sock);	if (!peer) {		close(peer_sock);		return;	}	peer->remote.initiater = 1;	/* Just return. Failure is handled by alloc_read_buffer(). */	buffer = alloc_read_buffer(peer->socket);	if (!buffer) return;	read_from_socket(peer->socket, buffer, S_TRANS,			 read_bittorrent_peer_handshake);	add_to_list(bittorrent_peer_connections, peer);}/* Based on network/socket.c:get_pasv_socket() but modified to try and bind to a * port range instead of any port. */enum connection_stateinit_bittorrent_listening_socket(struct connection *conn){	struct bittorrent_connection *bittorrent = conn->info;	struct sockaddr_in addr, addr2;	uint16_t port, max_port;	int len;	/* XXX: Always add the connection to the list even if we fail so we can	 * safely assume it is in done_bittorrent_listening_socket(). */	add_to_list(bittorrent_connections, bittorrent);	/* Has the socket already been initialized? */	if (!list_is_singleton(bittorrent_connections))		return S_OK;	/* We could have bailed out from an earlier attempt. */	if (bittorrent_socket != -1)		close(bittorrent_socket);	bittorrent_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);	if (bittorrent_socket < 0)		return -errno;	/* Set it non-blocking */	if (set_nonblocking_fd(bittorrent_socket) < 0)		return -errno;	/* Bind it to some port */	port	 = get_opt_int("protocol.bittorrent.ports.min");	max_port = get_opt_int("protocol.bittorrent.ports.max");	memset(&addr, 0, sizeof(addr));	addr.sin_port = htons(port);	/* Repeatedly try the configured port range. */	while (bind(bittorrent_socket, (struct sockaddr *) &addr, sizeof(addr))) {		if (errno != EADDRINUSE)			return -errno;		/* If all ports was in use fail with EADDRINUSE. */		if (++port > max_port)			return -errno;		memset(&addr, 0, sizeof(addr));		addr.sin_port = htons(port);	}	/* Get the endpoint info about the new socket and save it */	memset(&addr2, 0, sizeof(addr2));	len = sizeof(addr2);	if (getsockname(bittorrent_socket, (struct sockaddr *) &addr2, &len))		return -errno;	bittorrent->port = ntohs(addr2.sin_port);	/* Go listen */	if (listen(bittorrent_socket, LISTEN_BACKLOG))		return -errno;	set_ip_tos_throughput(bittorrent_socket);	set_handlers(bittorrent_socket, accept_bittorrent_peer_connection,		     NULL, NULL, NULL);	return S_OK;}voiddone_bittorrent_listening_socket(struct connection *conn){	struct bittorrent_connection *connection, *bittorrent = conn->info;	/* The bittorrent connection might not even have been added if the	 * request for the metainfo file failed so carefully look it up. */	foreach (connection, bittorrent_connections)		if (connection == bittorrent) {			del_from_list(bittorrent);			break;		}	/* If there are no more connections left remove all pending peer	 * connections. */	if (list_empty(bittorrent_connections)) {		struct bittorrent_peer_connection *peer, *next;		foreachsafe (peer, next, bittorrent_peer_connections)			done_bittorrent_peer_connection(peer);	}	/* Close the listening socket. */	if (bittorrent_socket != -1) {		/* Unregister the socket from the select() loop mechanism. */		clear_handlers(bittorrent_socket);		close(bittorrent_socket);		bittorrent_socket = -1;	}}

⌨️ 快捷键说明

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