1 /* BitTorrent tracker HTTP protocol implementation */
9 #include "cache/cache.h"
10 #include "config/options.h"
11 #include "main/select.h"
12 #include "main/timer.h"
13 #include "network/connection.h"
14 #include "network/socket.h"
15 #include "protocol/bittorrent/bencoding.h"
16 #include "protocol/bittorrent/bittorrent.h"
17 #include "protocol/bittorrent/common.h"
18 #include "protocol/bittorrent/connection.h"
19 #include "protocol/bittorrent/tracker.h"
20 #include "protocol/protocol.h"
21 #include "protocol/uri.h"
22 #include "util/memory.h"
23 #include "util/string.h"
26 /* Y'all bases iz belong ta us. */
28 /* TODO: Support for the scrape convention. It should be optional through an
29 * update interval option indicating in seconds how often to request the scrape
30 * info. Zero means off. */
32 struct uri_list bittorrent_stopped_requests
;
34 static void do_send_bittorrent_tracker_request(struct connection
*conn
);
37 set_bittorrent_tracker_interval(struct connection
*conn
)
39 struct bittorrent_connection
*bittorrent
= conn
->info
;
40 int interval
= get_opt_int("protocol.bittorrent.tracker.interval",
43 /* The default HTTP_KEEPALIVE_TIMEOUT is set to 60 seconds so it
44 * probably makes sense to have a default tracker interval that is below
45 * that limit to get good performance. On the other hand it is probably
46 * also good to be nice and follow the trackers intructions. Rate limit
47 * the interval to be minimum twice each minute so it doesn't end up
48 * hogging the CPU too much. */
50 interval
= int_min(bittorrent
->tracker
.interval
, 60 * 5);
55 install_timer(&bittorrent
->tracker
.timer
, sec_to_ms(interval
),
56 (void (*)(void *)) do_send_bittorrent_tracker_request
,
60 /* Handling of the regular HTTP request/response connnection to the tracker, */
61 /* XXX: The data pointer may be NULL when doing event=stopped because no
62 * connection is attached anymore. */
64 bittorrent_tracker_callback(void *data
, struct connection_state state
,
65 struct bittorrent_const_string
*response
)
67 struct connection
*conn
= data
;
68 struct bittorrent_connection
*bittorrent
= conn
? conn
->info
: NULL
;
70 /* We just did event=stopped and don't have any connection attached. */
71 if (!bittorrent
) return;
73 assert(bittorrent
->tracker
.event
!= BITTORRENT_EVENT_STOPPED
);
75 bittorrent
->fetch
= NULL
;
77 /* FIXME: We treat any error as fatal here, however, it might be better
78 * to relax that and allow a few errors before ending the connection. */
79 if (!is_in_state(state
, S_OK
)) {
80 if (is_in_state(state
, S_INTERRUPTED
))
82 bittorrent
->tracker
.failed
= 1;
83 add_bittorrent_message(conn
->uri
, state
, NULL
);
84 abort_connection(conn
, connection_state(S_OK
));
88 switch (parse_bittorrent_tracker_response(bittorrent
, response
)) {
89 case BITTORRENT_STATE_ERROR
:
90 /* This error means a parsing error, and it actually seems to
91 * happen so frequently that they are worth ignoring from a
92 * usability perspective, e.g. they may be caused by the peer
93 * info list suddenly ending without no notice. */
94 case BITTORRENT_STATE_OK
:
95 if (bittorrent
->tracker
.event
== BITTORRENT_EVENT_STARTED
) {
96 assert(bittorrent
->timer
== TIMER_ID_UNDEF
);
97 bittorrent
->tracker
.started
= 1;
98 update_bittorrent_connection_state(conn
);
101 set_bittorrent_tracker_interval(conn
);
102 bittorrent
->tracker
.event
= BITTORRENT_EVENT_REGULAR
;
105 case BITTORRENT_STATE_OUT_OF_MEM
:
106 state
= connection_state(S_OUT_OF_MEM
);
109 case BITTORRENT_STATE_REQUEST_FAILURE
:
110 add_bittorrent_message(conn
->uri
, connection_state(S_OK
),
112 state
= connection_state(S_OK
);
116 state
= connection_state(S_BITTORRENT_TRACKER
);
119 abort_connection(conn
, state
);
123 check_bittorrent_stopped_request(void *____
)
128 foreach_uri (uri
, index
, &bittorrent_stopped_requests
) {
129 init_bittorrent_fetch(NULL
, uri
, bittorrent_tracker_callback
, NULL
, 1);
132 free_uri_list(&bittorrent_stopped_requests
);
135 /* Timer callback for @bittorrent->tracker.timer. As explained in
136 * @install_timer, this function must erase the expired timer ID from
137 * all variables. Also called from the request sending front-end. */
138 /* XXX: When called with event set to ``stopped'' failure handling should not
139 * end the connection, since that is already in progress. */
140 /* TODO: Make a special timer callback entry point that can check if
141 * rerequestion is needed, that is if more peer info is needed etc. */
143 do_send_bittorrent_tracker_request(struct connection
*conn
)
145 struct bittorrent_connection
*bittorrent
= conn
->info
;
146 int stopped
= (bittorrent
->tracker
.event
== BITTORRENT_EVENT_STOPPED
);
147 unsigned char *ip
, *key
;
148 struct string request
;
149 struct uri
*uri
= NULL
;
150 int numwant
, index
, min_size
;
152 bittorrent
->tracker
.timer
= TIMER_ID_UNDEF
;
153 /* The expired timer ID has now been erased. */
155 /* If the previous request didn't make it, nuke it. This shouldn't
156 * happen but not doing this makes it a potential leak. */
157 if (bittorrent
->fetch
)
158 done_bittorrent_fetch(&bittorrent
->fetch
);
160 if (!init_string(&request
)) {
161 done_string(&request
);
163 abort_connection(conn
,
164 connection_state(S_OUT_OF_MEM
));
168 foreach_uri (uri
, index
, &bittorrent
->meta
.tracker_uris
)
169 /* Pick the first ... */
173 done_string(&request
);
175 abort_connection(conn
,
176 connection_state(S_BITTORRENT_ERROR
));
180 add_uri_to_string(&request
, uri
, URI_BASE
);
182 add_to_string(&request
, "?info_hash=");
183 encode_uri_string(&request
, bittorrent
->meta
.info_hash
,
184 sizeof(bittorrent
->meta
.info_hash
), 1);
186 add_to_string(&request
, "&peer_id=");
187 encode_uri_string(&request
, bittorrent
->peer_id
,
188 sizeof(bittorrent
->peer_id
), 1);
190 add_format_to_string(&request
, "&uploaded=%ld", bittorrent
->uploaded
);
191 add_format_to_string(&request
, "&downloaded=%ld", bittorrent
->downloaded
);
192 add_format_to_string(&request
, "&left=%ld", bittorrent
->left
);
194 /* Sending no IP-address is valid. The tracker figures it out
195 * automatically which is much easier. However, the user might want to
196 * configure a specific IP-address to send. */
197 ip
= get_opt_str("protocol.bittorrent.tracker.ip_address", NULL
);
198 if (*ip
) add_format_to_string(&request
, "&ip=%s", ip
);
200 /* This one is required for each request. */
201 add_format_to_string(&request
, "&port=%u", bittorrent
->port
);
203 key
= get_opt_str("protocol.bittorrent.tracker.key", NULL
);
205 add_to_string(&request
, "&key=");
206 encode_uri_string(&request
, key
, strlen(key
), 1);
209 if (bittorrent
->tracker
.event
!= BITTORRENT_EVENT_REGULAR
) {
210 unsigned char *event
;
212 switch (bittorrent
->tracker
.event
) {
213 case BITTORRENT_EVENT_STARTED
:
217 case BITTORRENT_EVENT_STOPPED
:
221 case BITTORRENT_EVENT_COMPLETED
:
226 INTERNAL("Bad tracker event.");
230 add_format_to_string(&request
, "&event=%s", event
);
233 min_size
= get_opt_int("protocol.bittorrent.tracker.min_skip_size",
235 if (!min_size
|| list_size(&bittorrent
->peer_pool
) < min_size
) {
236 numwant
= get_opt_int("protocol.bittorrent.tracker.numwant",
238 /* Should the server default be used? */
246 add_format_to_string(&request
, "&numwant=%d", numwant
);
248 if (get_opt_bool("protocol.bittorrent.tracker.compact", NULL
))
249 add_to_string(&request
, "&compact=1");
251 uri
= get_uri(request
.source
, 0);
252 done_string(&request
);
255 abort_connection(conn
,
256 connection_state(S_BITTORRENT_ERROR
));
261 /* We cannot start the event=stopped requesting directly here
262 * since we are nested inside a connection shutdown which means
263 * it will trigger a queue bug if we start adding a new
264 * connection. Solution: send the request in a bottom half. */
265 if (register_bottom_half(check_bittorrent_stopped_request
, NULL
) == 0)
266 add_to_uri_list(&bittorrent_stopped_requests
, uri
);
269 init_bittorrent_fetch(&bittorrent
->fetch
, uri
,
270 bittorrent_tracker_callback
, conn
, 1);
278 send_bittorrent_tracker_request(struct connection
*conn
)
280 struct bittorrent_connection
*bittorrent
= conn
->info
;
282 /* Kill the timer when we are not sending a periodic request to make
283 * sure that there are only one tracker request at any time. */
284 kill_timer(&bittorrent
->tracker
.timer
);
286 do_send_bittorrent_tracker_request(conn
);
290 done_bittorrent_tracker_connection(struct connection
*conn
)
292 struct bittorrent_connection
*bittorrent
= conn
->info
;
294 kill_timer(&bittorrent
->tracker
.timer
);
296 /* Nothing to shut down. */
297 if (!bittorrent
->tracker
.started
|| bittorrent
->tracker
.failed
)
300 /* Send a tracker request with event=stopped. Note, the request won't be
301 * sent if we are shutting down due to an emergency, because the
302 * connection subsystem will be shut down soonish. */
303 bittorrent
->tracker
.event
= BITTORRENT_EVENT_STOPPED
;
304 send_bittorrent_tracker_request(conn
);