1
0
mirror of https://github.com/rkd77/elinks.git synced 2024-09-29 03:17:53 -04:00
elinks/src/protocol/bittorrent/bittorrent.c
2022-01-16 21:08:50 +01:00

351 lines
12 KiB
C

/* Internal "bittorrent" protocol implementation */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h> /* OS/2 needs this after sys/types.h */
#endif
#include "elinks.h"
#include "config/options.h"
#include "intl/libintl.h"
#include "main/module.h"
#include "network/state.h"
#include "protocol/bittorrent/bittorrent.h"
#include "protocol/bittorrent/common.h"
#include "protocol/bittorrent/dialogs.h"
#include "protocol/uri.h"
#include "session/session.h"
/* Hey, anything is possible, I am about to drink a carrot! */
static union option_info bittorrent_protocol_options[] = {
INIT_OPT_TREE("protocol", N_("BitTorrent"),
"bittorrent", OPT_ZERO,
N_("BitTorrent specific options.")),
/* ****************************************************************** */
/* Listening socket options: */
/* ****************************************************************** */
INIT_OPT_TREE("protocol.bittorrent", N_("Port range"),
"ports", OPT_ZERO,
N_("Port range allowed to be used for listening on.")),
INIT_OPT_INT("protocol.bittorrent.ports", N_("Minimum port"),
"min", OPT_ZERO, LOWEST_PORT, HIGHEST_PORT, 6881,
N_("The minimum port to try and listen on.")),
INIT_OPT_INT("protocol.bittorrent.ports", N_("Maximum port"),
"max", OPT_ZERO, LOWEST_PORT, HIGHEST_PORT, 6999,
N_("The maximum port to try and listen on.")),
/* ****************************************************************** */
/* Tracker connection options: */
/* ****************************************************************** */
INIT_OPT_TREE("protocol.bittorrent", N_("Tracker"),
"tracker", OPT_ZERO,
N_("Tracker options.")),
INIT_OPT_BOOL("protocol.bittorrent.tracker", N_("Use compact tracker format"),
"compact", OPT_ZERO, 0,
N_("Whether to request that the tracker returns peer info "
"in compact format. Note, the compact format only supports "
"IPv4 addresses.")),
INIT_OPT_INT("protocol.bittorrent.tracker", N_("Tracker announce interval"),
"interval", OPT_ZERO, 0, INT_MAX, 0,
N_("The number of seconds to wait between periodically "
"contacting the tracker for announcing progress and "
"requesting more peers. Set to zero to use the interval "
"requested by the tracker.")),
INIT_OPT_STRING("protocol.bittorrent.tracker", N_("IP-address to announce"),
"ip_address", OPT_ZERO, "",
N_("What IP address to report to the tracker. If set to \"\" "
"no IP address will be sent and the tracker will "
"automatically determine an appropriate IP address.")),
INIT_OPT_STRING("protocol.bittorrent.tracker", N_("User identification string"),
"key", OPT_ZERO, "",
N_("An additional identification that is not shared with any "
"users. It is intended to allow a client to prove their "
"identity should their IP address change. It is an optional "
"parameter, but some trackers require this parameter. "
"If set to \"\" no user key will be sent to the tracker.")),
INIT_OPT_INT("protocol.bittorrent.tracker", N_("Maximum number of peers to request"),
"numwant", OPT_ZERO, 0, INT_MAX, 50,
N_("The maximum number of peers to request from the tracker. "
"Set to 0 to use the server default.")),
INIT_OPT_INT("protocol.bittorrent.tracker", N_("Minimum peers to skip rerequesting"),
"min_skip_size", OPT_ZERO, 0, INT_MAX, 20,
N_("The minimum number of peers to have in the current peer "
"info pool before skipping requesting of more peers. I.e. "
"setting numwant to zero. Set to 0 to not have any limit.")),
/* ****************************************************************** */
/* Lowlevel peer-wire options: */
/* ****************************************************************** */
INIT_OPT_TREE("protocol.bittorrent", N_("Peer-wire"),
"peerwire", OPT_ZERO,
N_("Lowlevel peer-wire options.")),
INIT_OPT_INT("protocol.bittorrent.peerwire", N_("Maximum number of peer connections"),
"connections", OPT_ZERO, 1, INT_MAX, 55,
N_("The maximum number of allowed connections to both active "
"and non-active peers. By increasing the number of allowed "
"connections, the chance of finding good peers to download "
"from is increased. However, too many connections can lead to "
"TCP congestion. If the maximum is reached all new incoming "
"connections will be closed.")),
INIT_OPT_INT("protocol.bittorrent.peerwire", N_("Maximum peer message length"),
"max_message_length", OPT_ZERO, 1, INT_MAX, BITTORRENT_MESSAGE_MAX_SIZE,
N_("The maximum length of messages to accept over the wire. "
"Larger values will cause the connection to be dropped.")),
INIT_OPT_INT("protocol.bittorrent.peerwire", N_("Maximum allowed request length"),
"max_request_length", OPT_ZERO, 1, INT_MAX, BITTORRENT_REQUEST_ACCEPT_LENGTH,
N_("The maximum length to allow for incoming requests. "
"Larger requests will cause the connection to be dropped.")),
INIT_OPT_INT("protocol.bittorrent.peerwire", N_("Length of requests"),
"request_length", OPT_ZERO, 1, INT_MAX, BITTORRENT_REQUEST_LENGTH,
N_("How many bytes to query for per request. This is "
"complementary to the max_request_length option. "
"If the configured length is bigger than the piece length "
"it will be truncated.")),
INIT_OPT_INT("protocol.bittorrent.peerwire", N_("Peer inactivity timeout"),
"timeout", OPT_ZERO, 0, INT_MAX, 300,
N_("The number of seconds to wait before closing a socket on "
"which nothing has been received or sent.")),
INIT_OPT_INT("protocol.bittorrent.peerwire", N_("Maximum peer pool size"),
"pool_size", OPT_ZERO, 0, INT_MAX, 55,
N_("Maximum number of items in the peer pool. The peer pool "
"contains information used for establishing connections to "
"new peers.\n"
"\n"
"Set to 0 to have unlimited size.")),
/* ****************************************************************** */
/* Piece management options: */
/* ****************************************************************** */
INIT_OPT_INT("protocol.bittorrent", N_("Maximum piece cache size"),
"piece_cache_size", OPT_ZERO, 0, INT_MAX, 1024 * 1024,
N_("The maximum amount of memory used to hold recently "
"downloaded pieces.\n"
"\n"
"Set to 0 to have unlimited size.")),
/* ****************************************************************** */
/* Strategy options: */
/* ****************************************************************** */
#if 0
INIT_OPT_STRING("protocol.bittorrent", N_("Sharing rate"),
"sharing_rate", OPT_ZERO, "1.0",
N_("The minimum sharing rate to achieve before stop seeding. "
"The sharing rate is computed as the number of uploaded bytes "
"divided with the number of downloaded bytes. The value "
"should be a double value between 0.0 and 1.0 both included. "
"Set to 1.0 to atleast upload a complete copy of all data and "
"set to 0.0 to have unlimited sharing rate.")),
#endif
INIT_OPT_INT("protocol.bittorrent", N_("Maximum number of uploads"),
"max_uploads", OPT_ZERO, 0, INT_MAX, 7,
N_("The maximum number of uploads to allow at once.")),
/* The number of uploads to fill out to with extra optimistic unchokes */
INIT_OPT_INT("protocol.bittorrent", N_("Minimum number of uploads"),
"min_uploads", OPT_ZERO, 0, INT_MAX, 2,
N_("The minimum number of uploads which should at least "
"be used for new connections.")),
#if 0
INIT_OPT_INT("protocol.bittorrent", N_("Keepalive interval"),
"keepalive_interval", OPT_ZERO, 0, INT_MAX, 120,
N_("The number of seconds to pause between sending keepalive "
"messages.")),
#endif
INIT_OPT_INT("protocol.bittorrent", N_("Number of pending requests"),
"request_queue_size", OPT_ZERO, 1, INT_MAX, 5,
N_("How many piece requests to continuously keep in queue. "
"Pipelining of requests is essential to saturate connections "
"and get a good connection performance and thus a faster "
"download. However, a very big queue size can lead to wasting "
"bandwidth near the end of the connection since remaining "
"piece blocks will be requested from multiple peers.")),
#if 0
/* Bram uses 30 seconds here. */
INIT_OPT_INT("protocol.bittorrent", N_("Peer snubbing interval"),
"snubbing_interval", OPT_ZERO, 0, INT_MAX, 30,
N_("The number of seconds to wait for file data before "
"assuming the peer has been snubbed.")),
#endif
INIT_OPT_INT("protocol.bittorrent", N_("Peer choke interval"),
"choke_interval", OPT_ZERO, 0, INT_MAX, BITTORRENT_DEFAULT_CHOKE_INTERVAL,
N_("The number of seconds between updating the connection "
"state and most importantly choke and unchoke peer "
"connections. The choke period should be big enough for newly "
"unchoked connections to get started but small enough to not "
"allow freeriders too much room for stealing bandwidth.")),
INIT_OPT_INT("protocol.bittorrent", N_("Rarest first piece selection cutoff"),
"rarest_first_cutoff", OPT_ZERO, 0, INT_MAX, 4,
N_("The number of pieces to obtain before switching piece "
"selection strategy from random to rarest first.")),
INIT_OPT_BOOL("protocol.bittorrent", N_("Allow blacklisting"),
"allow_blacklist", OPT_ZERO, 1,
N_("Allow blacklisting of buggy peers.")),
NULL_OPTION_INFO,
};
uint32_t
get_bittorrent_peerwire_max_message_length(void)
{
return get_opt_int_tree(&bittorrent_protocol_options[0].option,
"peerwire.max_message_length", NULL);
}
uint32_t
get_bittorrent_peerwire_max_request_length(void)
{
return get_opt_int_tree(&bittorrent_protocol_options[0].option,
"peerwire.max_request_length", NULL);
}
/* File selection store. */
static INIT_LIST_OF(struct bittorrent_selection_info, bittorrent_selections);
struct bittorrent_selection_info {
LIST_HEAD(struct bittorrent_selection_info);
/* Identifier used for saving and getting meta info from the meta info
* store. */
struct uri *uri;
size_t size;
int *selection;
};
int *
get_bittorrent_selection(struct uri *uri, size_t size)
{
struct bittorrent_selection_info *info;
foreach (info, bittorrent_selections) {
if (compare_uri(info->uri, uri, 0)) {
int *selection;
del_from_list(info);
if (info->size == size) {
selection = info->selection;
} else {
mem_free(info->selection);
selection = NULL;
}
done_uri(info->uri);
mem_free(info);
return selection;
}
}
return NULL;
}
void
add_bittorrent_selection(struct uri *uri, int *selection, size_t size)
{
struct bittorrent_selection_info *info;
int *duplicate;
duplicate = get_bittorrent_selection(uri, size);
mem_free_if(duplicate);
info = (struct bittorrent_selection_info *)mem_calloc(1, sizeof(*info));
if (!info) return;
info->uri = get_uri_reference(uri);
info->size = size;
info->selection = selection;
add_to_list(bittorrent_selections, info);
}
/* Message queue. */
static INIT_LIST_OF(struct bittorrent_message, bittorrent_messages);
void
add_bittorrent_message(struct uri *uri, struct connection_state state,
struct bittorrent_const_string *string)
{
struct bittorrent_message *message;
int length = string ? string->length : 0;
message = (struct bittorrent_message *)mem_calloc(1, sizeof(*message) + length);
if (!message) return;
message->uri = get_uri_reference(uri);
message->state = state;
if (length)
memcpy(message->string, string->source, string->length);
add_to_list(bittorrent_messages, message);
add_questions_entry(bittorrent_message_dialog, message);
}
static void
done_bittorrent(struct module *module)
{
struct bittorrent_message *message, *message_next;
struct bittorrent_selection_info *info, *info_next;
done_bittorrent_blacklist();
foreachsafe (message, message_next, bittorrent_messages)
done_bittorrent_message(message);
foreachsafe (info, info_next, bittorrent_selections) {
del_from_list(info);
mem_free(info->selection);
mem_free(info);
}
}
struct module bittorrent_protocol_module = struct_module(
/* name: */ N_("BitTorrent"),
/* options: */ bittorrent_protocol_options,
/* hooks: */ NULL,
/* submodules: */ NULL,
/* data: */ NULL,
/* init: */ NULL,
/* done: */ done_bittorrent
);