about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRichard Nyberg <rnyberg@murmeldjur.se>2006-01-09 13:41:07 +0000
committerRichard Nyberg <rnyberg@murmeldjur.se>2006-01-09 13:41:07 +0000
commit5377243d177d4b7a6675678b78bec02143d0a639 (patch)
treed54a3e5eb5e1941638bd1bd9a64492b706c61fe6
parentd4082e3a7a3ac85fd31375eed5d7a76055fda799 (diff)
downloadbtpd-5377243d177d4b7a6675678b78bec02143d0a639.tar.gz
btpd-5377243d177d4b7a6675678b78bec02143d0a639.zip
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.

-rw-r--r--btpd/download.c2
-rw-r--r--btpd/torrent.c7
-rw-r--r--btpd/torrent.h1
-rw-r--r--btpd/tracker_req.c345
-rw-r--r--btpd/tracker_req.h12
5 files changed, 164 insertions, 203 deletions
diff --git a/btpd/download.c b/btpd/download.c
index c2ff7f6..a4398db 100644
--- a/btpd/download.c
+++ b/btpd/download.c
@@ -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);
     }
diff --git a/btpd/torrent.c b/btpd/torrent.c
index f317e67..902e765 100644
--- a/btpd/torrent.c
+++ b/btpd/torrent.c
@@ -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;
     }
 }
diff --git a/btpd/torrent.h b/btpd/torrent.h
index 1797a73..b00f8a8 100644
--- a/btpd/torrent.h
+++ b/btpd/torrent.h
@@ -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;
diff --git a/btpd/tracker_req.c b/btpd/tracker_req.c
index 1a55f5f..db65e54 100644
--- a/btpd/tracker_req.c
+++ b/btpd/tracker_req.c
@@ -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);
 }
diff --git a/btpd/tracker_req.h b/btpd/tracker_req.h
index 2d2a8c6..586bbb6 100644
--- a/btpd/tracker_req.h
+++ b/btpd/tracker_req.h
@@ -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