Browse Source

Better tracker code. Mostly done but needs a little more work.

It now uses the new http thread instead of forked proceses for
tracker requests.
master
Richard Nyberg 19 years ago
parent
commit
5377243d17
5 changed files with 164 additions and 203 deletions
  1. +1
    -1
      btpd/download.c
  2. +4
    -3
      btpd/torrent.c
  3. +1
    -0
      btpd/torrent.h
  4. +154
    -191
      btpd/tracker_req.c
  5. +4
    -8
      btpd/tracker_req.h

+ 1
- 1
btpd/download.c View File

@@ -117,7 +117,7 @@ dl_on_ok_piece(struct piece *pc)

if (cm_full(tp)) {
btpd_log(BTPD_L_BTPD, "Finished: %s.\n", tp->relpath);
tracker_req(tp, TR_COMPLETED);
tr_complete(tp);
BTPDQ_FOREACH(p, &tp->peers, p_entry)
assert(p->nwant == 0);
}


+ 4
- 3
btpd/torrent.c View File

@@ -108,10 +108,11 @@ void torrent_cm_cb(struct torrent *tp, enum cm_state state)
switch (state) {
case CM_STARTED:
net_add_torrent(tp);
tracker_req(tp, TR_STARTED);
tr_start(tp);
break;
case CM_STOPPED:
abort();
break;
case CM_ERROR:
abort();
break;
}
}

+ 1
- 0
btpd/torrent.h View File

@@ -45,6 +45,7 @@ struct torrent {
enum torrent_state state;

struct content *cp;
struct tracker *tr;

BTPDQ_ENTRY(torrent) entry;
BTPDQ_ENTRY(torrent) net_entry;


+ 154
- 191
btpd/tracker_req.c View File

@@ -1,31 +1,50 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#include <sys/wait.h>
#include <sys/mman.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <inttypes.h>

#include <curl/curl.h>

#include "btpd.h"
#include "tracker_req.h"
#include "benc.h"
#include "subr.h"
#include "http.h"

#define REQ_TIMEOUT (& (struct timeval) { 120, 0 })
#define RETRY_WAIT (& (struct timeval) { rand_between(35, 70), 0 })

enum tr_event {
TR_EV_STARTED,
TR_EV_STOPPED,
TR_EV_COMPLETED,
TR_EV_EMPTY
};

#define REQ_SIZE (getpagesize() * 2)
static const char *m_events[] = { "started", "stopped", "completed", "" };

struct tracker_req {
enum tr_event tr_event;
uint8_t info_hash[20];
struct io_buffer *res;
enum timer_type {
TIMER_NONE,
TIMER_TIMEOUT,
TIMER_INTERVAL,
TIMER_RETRY
};

static void
enum http_type {
HTTP_NONE,
HTTP_NORMAL,
HTTP_RETRY,
HTTP_NEW
};

struct tracker {
enum timer_type ttype;
enum http_type htype;
enum tr_event event;
int interval;
unsigned nerrors;
struct http *req;
struct event timer;
};

static void tr_send(struct torrent *tp, enum tr_event event);

void
maybe_connect_to(struct torrent *tp, const char *pinfo)
{
const char *pid = NULL;
@@ -58,50 +77,36 @@ out:
free(ip);
}

static void
tracker_done(pid_t pid, void *arg)
static int
parse_reply(struct torrent *tp, const char *content, size_t size)
{
struct tracker_req *req = arg;
int failed = 0;
char *buf;
const char *peers;
uint32_t interval;
struct torrent *tp;

if ((tp = btpd_get_torrent(req->info_hash)) == NULL)
goto out;

if (benc_validate(req->res->buf, req->res->buf_off) != 0
|| !benc_isdct(req->res->buf)) {
if (req->res->buf_off != 0) {
fwrite(req->res->buf, 1, req->res->buf_off, (stdout));
putchar('\n');
}

if (benc_validate(content, size) != 0 || !benc_isdct(content)) {
btpd_log(BTPD_L_ERROR, "Bad data from tracker.\n");
failed = 1;
goto out;
return 1;
}

if ((benc_dget_strz(req->res->buf, "failure reason", &buf, NULL)) == 0) {
if ((benc_dget_strz(content, "failure reason", &buf, NULL)) == 0) {
btpd_log(BTPD_L_ERROR, "Tracker failure: %s.\n", buf);
free(buf);
failed = 1;
goto out;
return 1;
}

if ((benc_dget_uint32(req->res->buf, "interval", &interval)) != 0) {
if ((benc_dget_uint32(content, "interval", &interval)) != 0) {
btpd_log(BTPD_L_ERROR, "Bad data from tracker.\n");
failed = 1;
goto out;
return 1;
}

//tp->tracker_time = btpd_seconds + interval;
tp->tr->interval = interval;
btpd_log(BTPD_L_BTPD, "Got interval %d.\n", interval);

int error = 0;
size_t length;

if ((error = benc_dget_lst(req->res->buf, "peers", &peers)) == 0) {
if ((error = benc_dget_lst(content, "peers", &peers)) == 0) {
for (peers = benc_first(peers);
peers != NULL && net_npeers < net_max_peers;
peers = benc_next(peers))
@@ -109,7 +114,7 @@ tracker_done(pid_t pid, void *arg)
}

if (error == EINVAL) {
error = benc_dget_str(req->res->buf, "peers", &peers, &length);
error = benc_dget_str(content, "peers", &peers, &length);
if (error == 0 && length % 6 == 0) {
size_t i;
for (i = 0; i < length && net_npeers < net_max_peers; i += 6)
@@ -119,181 +124,139 @@ tracker_done(pid_t pid, void *arg)

if (error != 0) {
btpd_log(BTPD_L_ERROR, "Bad data from tracker.\n");
failed = 1;
goto out;
return 1;
}

out:
if (failed)
;//tp->tracker_time = btpd_seconds + 10;
return 0;
}

munmap(req->res, REQ_SIZE);
free(req);
static void
http_cb(struct http *req, struct http_res *res, void *arg)
{
struct torrent *tp = arg;
struct tracker *tr = tp->tr;
switch (tr->htype) {
case HTTP_NORMAL:
if ((http_succeeded(res) &&
parse_reply(tp, res->content, res->length) == 0)) {
tr->htype = HTTP_NONE;
tr->ttype = TIMER_INTERVAL;
event_add(&tr->timer, (& (struct timeval) { tr->interval, 0 }));
break;
}
case HTTP_RETRY:
tr->htype = HTTP_NONE;
tr->ttype = TIMER_RETRY;
event_add(&tr->timer, RETRY_WAIT);
break;
case HTTP_NEW:
tr->htype = HTTP_NONE;
tr_send(tp, tr->event);
break;
default:
abort();
}
}

static const char *
event2str(enum tr_event ev)
static void
timer_cb(int fd, short type, void *arg)
{
switch (ev) {
case TR_STARTED:
return "started";
case TR_STOPPED:
return "stopped";
case TR_COMPLETED:
return "completed";
case TR_EMPTY:
return "";
struct torrent *tp = arg;
struct tracker *tr = tp->tr;
switch (tr->ttype) {
case TIMER_TIMEOUT:
http_cancel(tr->req);
tr->htype = HTTP_RETRY;
tr->ttype = TIMER_NONE;
break;
case TIMER_INTERVAL:
tr_send(tp, TR_EV_EMPTY);
break;
case TIMER_RETRY:
tr_send(tp, tr->event);
break;
default:
btpd_err("Bad tracker event %d.\n", ev);
return ""; // Shut GCC up!
abort();
}
}

static int
create_url(struct tracker_req *req, struct torrent *tp, char **url)
static void
tr_send(struct torrent *tp, enum tr_event event)
{
char e_hash[61], e_id[61];
char e_hash[61], e_id[61], qc;;
const uint8_t *peer_id = btpd_get_peer_id();
char qc;
int i;
off_t left;
const char *event;

event = event2str(req->tr_event);
struct tracker *tr = tp->tr;
tr->event = event;
switch (tr->htype) {
case HTTP_NORMAL:
tr->htype = HTTP_NEW;
tr->ttype = TIMER_NONE;
event_del(&tr->timer);
http_cancel(tr->req);
return;
case HTTP_RETRY:
tr->htype = HTTP_NEW;
return;
case HTTP_NEW:
return;
default:
tr->htype = HTTP_NORMAL;
}

tr->ttype = TIMER_TIMEOUT;
event_add(&tr->timer, REQ_TIMEOUT);

qc = (strchr(tp->meta.announce, '?') == NULL) ? '?' : '&';

for (i = 0; i < 20; i++)
for (int i = 0; i < 20; i++)
snprintf(e_hash + i * 3, 4, "%%%.2x", tp->meta.info_hash[i]);

for (i = 0; i < 20; i++)
for (int i = 0; i < 20; i++)
snprintf(e_id + i * 3, 4, "%%%.2x", peer_id[i]);

left = cm_bytes_left(tp);
http_get(&tr->req, http_cb, tp,
"%s%cinfo_hash=%s&peer_id=%s&port=%d&uploaded=%ju"
"&downloaded=%ju&left=%ju&compact=1%s%s",
tp->meta.announce, qc, e_hash, e_id, net_port,
(intmax_t)tp->uploaded, (intmax_t)tp->downloaded,
(intmax_t)cm_bytes_left(tp),
event == TR_EV_EMPTY ? "" : "&event=", m_events[event]);
}

int
tr_start(struct torrent *tp)
{
assert(tp->tr == NULL);
if (strncmp(tp->meta.announce, "http://", sizeof("http://") - 1) != 0) {
btpd_log(BTPD_L_ERROR,
"btpd currently has no support for the protocol specified in "
"'%s'.\n", tp->meta.announce);
return EINVAL;
}

struct tracker *tr = btpd_calloc(1, sizeof(*tr));
evtimer_set(&tr->timer, timer_cb, tp);
tp->tr = tr;

i =
asprintf(url, "%s%cinfo_hash=%s&peer_id=%s&port=%d&uploaded=%ju"
"&downloaded=%ju&left=%ju&compact=1%s%s",
tp->meta.announce, qc, e_hash, e_id, net_port,
(intmax_t)tp->uploaded, (intmax_t)tp->downloaded, (intmax_t)left,
req->tr_event == TR_EMPTY ? "" : "&event=",
event);
tr_send(tp, TR_EV_STARTED);

if (i < 0)
return ENOMEM;
return 0;
}

static size_t
http_cb(void *ptr, size_t size, size_t nmemb, void *stream)
void
tr_refresh(struct torrent *tp)
{
struct tracker_req *req = (struct tracker_req *)stream;
size_t nbytes = size * nmemb;
if (nbytes <= req->res->buf_len - req->res->buf_off) {
memcpy(req->res->buf + req->res->buf_off, ptr, nbytes);
req->res->buf_off += nbytes;
return nbytes;
}
else
return 0;
tr_send(tp, TR_EV_EMPTY);
}

static void
http_helper(struct tracker_req *req, struct torrent *tp)
void
tr_complete(struct torrent *tp)
{
char cerror[CURL_ERROR_SIZE];
char fr[] = "failure reason";
CURL *handle;
char *url;
int err;

if (create_url(req, tp, &url) != 0)
goto memory_error;

if (curl_global_init(0) != 0)
goto libcurl_error;

if ((handle = curl_easy_init()) == NULL)
goto libcurl_error;

err = curl_easy_setopt(handle, CURLOPT_URL, url);
if (err == 0)
err = curl_easy_setopt(handle, CURLOPT_USERAGENT, BTPD_VERSION);
if (err == 0)
err = curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, http_cb);
if (err == 0)
err = curl_easy_setopt(handle, CURLOPT_WRITEDATA, req);
if (err == 0)
err = curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, cerror);
if (err != 0) {
strncpy(cerror, curl_easy_strerror(err), CURL_ERROR_SIZE - 1);
goto handle_error;
}

req->res->buf_off = 0;
if (curl_easy_perform(handle) != 0)
goto handle_error;

#if 0
curl_easy_cleanup(handle);
curl_global_cleanup();
free(url);
#endif
exit(0);

memory_error:
strncpy(cerror, "Out of memory", CURL_ERROR_SIZE - 1);
goto handle_error;

libcurl_error:
strncpy(cerror, "Generic libcurl error", CURL_ERROR_SIZE - 1);
goto handle_error;

handle_error:
req->res->buf_off =
snprintf(req->res->buf, req->res->buf_len,
"d%d:%s%d:%se", (int)strlen(fr), fr, (int)strlen(cerror), cerror);
if (req->res->buf_off >= req->res->buf_len)
req->res->buf_off = 0;

exit(1);
tr_send(tp, TR_EV_COMPLETED);
}

void
tracker_req(struct torrent *tp, enum tr_event tr_event)
tr_stop(struct torrent *tp)
{
struct tracker_req *req;
pid_t pid;

btpd_log(BTPD_L_TRACKER,
"request for %s, event: %s.\n",
tp->relpath, event2str(tr_event));

req = (struct tracker_req *)btpd_calloc(1, sizeof(*req));

req->res = mmap(NULL, REQ_SIZE, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_SHARED, -1, 0);

if (req->res == MAP_FAILED)
btpd_err("Failed mmap: %s\n", strerror(errno));

req->res->buf_len = REQ_SIZE - sizeof(*req->res);
req->res->buf_off = 0;
req->res->buf = (char *)req->res + sizeof(*req->res);

req->tr_event = tr_event;
bcopy(tp->meta.info_hash, req->info_hash, 20);

fflush(NULL);

pid = fork();
if (pid < 0) {
btpd_err("Couldn't fork (%s).\n", strerror(errno));
} else if (pid == 0) { // Child
int nfiles = getdtablesize();
for (int i = 0; i < nfiles; i++)
close(i);
http_helper(req, tp);
} else
btpd_add_child(pid, tracker_done, req);
tr_send(tp, TR_EV_STOPPED);
}

+ 4
- 8
btpd/tracker_req.h View File

@@ -1,13 +1,9 @@
#ifndef TRACKER_REQ_H
#define TRACKER_REQ_H

enum tr_event {
TR_STARTED = 1,
TR_STOPPED,
TR_COMPLETED,
TR_EMPTY
};

void tracker_req(struct torrent *tp, enum tr_event tr_event);
int tr_start(struct torrent *tp);
void tr_stop(struct torrent *tp);
void tr_refresh(struct torrent *tp);
void tr_complete(struct torrent *tp);

#endif

||||||
x
 
000:0
Loading…
Cancel
Save