diff --git a/btpd/content.c b/btpd/content.c
index df19849..10c3181 100644
--- a/btpd/content.c
+++ b/btpd/content.c
@@ -159,7 +159,7 @@ add_todo(struct content *cm, struct cm_op *op)
 }
 
 void
-cm_destroy(struct torrent *tp)
+cm_kill(struct torrent *tp)
 {
     struct content *cm = tp->cm;
     bts_close(cm->rds);
@@ -168,7 +168,6 @@ cm_destroy(struct torrent *tp)
     free(cm->hold_field);
     free(cm->pos_field);
     tp->cm = NULL;
-    torrent_on_cm_stopped(tp);
 }
 
 void
@@ -215,7 +214,14 @@ cm_stop(struct torrent *tp)
         cm_write_done(tp);
 
     if (BTPDQ_EMPTY(&cm->todoq))
-        cm_destroy(tp);
+        torrent_on_cm_stopped(tp);
+}
+
+int
+cm_active(struct torrent *tp)
+{
+    struct content *cm = tp->cm;
+    return cm->active || !BTPDQ_EMPTY(&cm->todoq);
 }
 
 #define SAVE_INTERVAL (& (struct timeval) { 15, 0 })
@@ -261,14 +267,14 @@ cm_td_cb(void *arg)
             assert(cm->npieces_got < tp->meta.npieces);
             cm->npieces_got++;
             set_bit(cm->piece_field, op->u.test.piece);
-            if (tp->net != NULL)
+            if (net_active(tp))
                 dl_on_ok_piece(op->tp->net, op->u.test.piece);
             if (cm_full(tp))
                 cm_write_done(tp);
         } else {
             cm->ncontent_bytes -= torrent_piece_size(tp, op->u.test.piece);
             bzero(cm->block_field + op->u.test.piece * cm->bppbf, cm->bppbf);
-            if (tp->net != NULL)
+            if (net_active(tp))
                 dl_on_bad_piece(tp->net, op->u.test.piece);
         }
         break;
@@ -281,16 +287,14 @@ cm_td_cb(void *arg)
     if (!BTPDQ_EMPTY(&cm->todoq))
         run_todo(cm);
     else if (!cm->active)
-        cm_destroy(tp);
+        torrent_on_cm_stopped(tp);
 }
 
-int
-cm_start(struct torrent *tp)
+void
+cm_create(struct torrent *tp)
 {
-    int err;
-    struct content *cm = btpd_calloc(1, sizeof(*cm));
     size_t pfield_size = ceil(tp->meta.npieces / 8.0);
-    cm->active = 1;
+    struct content *cm = btpd_calloc(1, sizeof(*cm));
     cm->bppbf = ceil((double)tp->meta.piece_length / (1 << 17));
     cm->piece_field = btpd_calloc(pfield_size, 1);
     cm->hold_field = btpd_calloc(pfield_size, 1);
@@ -298,18 +302,24 @@ cm_start(struct torrent *tp)
     cm->block_field = btpd_calloc(tp->meta.npieces * cm->bppbf, 1);
 
     BTPDQ_INIT(&cm->todoq);
+    evtimer_set(&cm->save_timer, save_timer_cb, tp);
 
-    if ((err = bts_open(&cm->rds, &tp->meta, fd_cb_rd, tp)) != 0)
-        btpd_err("Error opening stream (%s).\n", strerror(err));
     tp->cm = cm;
+}
 
-    evtimer_set(&cm->save_timer, save_timer_cb, tp);
+void
+cm_start(struct torrent *tp)
+{
+    struct content *cm = tp->cm;
+
+    if ((errno = bts_open(&cm->rds, &tp->meta, fd_cb_rd, tp)) != 0)
+        btpd_err("Error opening stream (%s).\n", strerror(errno));
 
+    cm->active = 1;
     struct cm_op *op = btpd_calloc(1, sizeof(*op));
     op->tp = tp;
     op->type = CM_START;
     add_todo(cm, op);
-    return 0;
 }
 
 int
diff --git a/btpd/content.h b/btpd/content.h
index ecb9467..7ac10e0 100644
--- a/btpd/content.h
+++ b/btpd/content.h
@@ -3,9 +3,13 @@
 
 void cm_init(void);
 
-int cm_start(struct torrent *tp);
+void cm_create(struct torrent *tp);
+void cm_kill(struct torrent *tp);
+
+void cm_start(struct torrent *tp);
 void cm_stop(struct torrent * tp);
 
+int cm_active(struct torrent *tp);
 int cm_full(struct torrent *tp);
 
 uint8_t *cm_get_piece_field(struct torrent *tp);
diff --git a/btpd/net.c b/btpd/net.c
index 1fc25dc..ba59943 100644
--- a/btpd/net.c
+++ b/btpd/net.c
@@ -52,7 +52,7 @@ net_torrent_has_peer(struct net *n, const uint8_t *id)
 }
 
 void
-net_add_torrent(struct torrent *tp)
+net_create(struct torrent *tp)
 {
     size_t field_size = ceil(tp->meta.npieces / 8.0);
     size_t mem = sizeof(*(tp->net)) + field_size +
@@ -62,21 +62,32 @@ net_add_torrent(struct torrent *tp)
     n->tp = tp;
     tp->net = n;
 
-    n->active = 1;
     BTPDQ_INIT(&n->getlst);
 
     n->busy_field = (uint8_t *)(n + 1);
     n->piece_count = (unsigned *)(n->busy_field + field_size);
+}
+
+void
+net_kill(struct torrent *tp)
+{
+    free(tp->net);
+    tp->net = NULL;
+}
 
+void
+net_start(struct torrent *tp)
+{
+    struct net *n = tp->net;
     BTPDQ_INSERT_HEAD(&m_torrents, n, entry);
     m_ntorrents++;
+    n->active = 1;
 }
 
 void
-net_del_torrent(struct torrent *tp)
+net_stop(struct torrent *tp)
 {
     struct net *n = tp->net;
-    tp->net = NULL;
 
     assert(m_ntorrents > 0);
     m_ntorrents--;
@@ -104,8 +115,12 @@ net_del_torrent(struct torrent *tp)
         peer_kill(p);
         p = next;
     }
+}
 
-    free(n);
+int
+net_active(struct torrent *tp)
+{
+    return tp->net->active;
 }
 
 void
diff --git a/btpd/net.h b/btpd/net.h
index 6a79b78..793386e 100644
--- a/btpd/net.h
+++ b/btpd/net.h
@@ -20,8 +20,12 @@ extern unsigned net_npeers;
 
 void net_init(void);
 
-void net_add_torrent(struct torrent *tp);
-void net_del_torrent(struct torrent *tp);
+void net_create(struct torrent *tp);
+void net_kill(struct torrent *tp);
+
+void net_start(struct torrent *tp);
+void net_stop(struct torrent *tp);
+int net_active(struct torrent *tp);
 
 int net_torrent_has_peer(struct net *n, const uint8_t *id);
 
diff --git a/btpd/torrent.c b/btpd/torrent.c
index 1202039..500c66a 100644
--- a/btpd/torrent.c
+++ b/btpd/torrent.c
@@ -126,11 +126,18 @@ torrent_start(const uint8_t *hash)
     bcopy(relpath, tp->relpath, RELPATH_SIZE);
     tp->meta = *mi;
     free(mi);
-    BTPDQ_INSERT_TAIL(&m_torrents, tp, entry);
-    m_ntorrents++;
-    cm_start(tp);
 
-    return 0;
+    if ((error = tr_create(tp)) == 0) {
+        net_create(tp);
+        cm_create(tp);
+        BTPDQ_INSERT_TAIL(&m_torrents, tp, entry);
+        m_ntorrents++;
+        cm_start(tp);
+    } else {
+        clear_metainfo(&tp->meta);
+        free(tp);
+    }
+    return error;
 }
 
 void
@@ -140,16 +147,13 @@ torrent_stop(struct torrent *tp)
     case T_STARTING:
     case T_ACTIVE:
         tp->state = T_STOPPING;
-        if (tp->tr != NULL)
-            tr_stop(tp);
-        if (tp->net != NULL)
-            net_del_torrent(tp);
-        if (tp->cm != NULL)
-            cm_stop(tp);
+        tr_stop(tp);
+        net_stop(tp);
+        cm_stop(tp);
         break;
     case T_STOPPING:
-        if (tp->tr != NULL)
-            tr_destroy(tp);
+        if (tr_active(tp))
+            tr_stop(tp);
         break;
     }
 }
@@ -162,6 +166,9 @@ torrent_kill(struct torrent *tp)
     m_ntorrents--;
     BTPDQ_REMOVE(&m_torrents, tp, entry);
     clear_metainfo(&tp->meta);
+    tr_kill(tp);
+    net_kill(tp);
+    cm_kill(tp);
     free(tp);
     if (m_ntorrents == 0)
         btpd_on_no_torrents();
@@ -171,16 +178,15 @@ void
 torrent_on_cm_started(struct torrent *tp)
 {
     tp->state = T_ACTIVE;
-    net_add_torrent(tp);
-    if (tr_start(tp) != 0)
-        torrent_stop(tp);
+    net_start(tp);
+    tr_start(tp);
 }
 
 void
 torrent_on_cm_stopped(struct torrent *tp)
 {
     assert(tp->state == T_STOPPING);
-    if (tp->tr == NULL)
+    if (!tr_active(tp))
         torrent_kill(tp);
 }
 
@@ -188,6 +194,6 @@ void
 torrent_on_tr_stopped(struct torrent *tp)
 {
     assert(tp->state == T_STOPPING);
-    if (tp->cm == NULL)
+    if (!cm_active(tp))
         torrent_kill(tp);
 }
diff --git a/btpd/tracker_req.c b/btpd/tracker_req.c
index 94a0960..155a03c 100644
--- a/btpd/tracker_req.c
+++ b/btpd/tracker_req.c
@@ -37,7 +37,7 @@ struct tracker {
 
 static void tr_send(struct torrent *tp, enum tr_event event);
 
-void
+static void
 maybe_connect_to(struct torrent *tp, const char *pinfo)
 {
     const char *pid;
@@ -66,21 +66,25 @@ maybe_connect_to(struct torrent *tp, const char *pinfo)
 
 
 static int
-parse_reply(struct torrent *tp, const char *content, size_t size)
+parse_reply(struct torrent *tp, const char *content, size_t size, int parse)
 {
     const char *buf;
     size_t len;
     const char *peers;
     int interval;
 
+    if (benc_validate(content, size) != 0)
+        goto bad_data;
+
     if ((buf = benc_dget_mem(content, "failure reason", &len)) != NULL) {
         btpd_log(BTPD_L_ERROR, "Tracker failure: %.*s.\n", (int)len, buf);
         return 1;
     }
 
-    if ((benc_validate(content, size) != 0 ||
-            !benc_dct_chk(content, 2, BE_INT, 1, "interval",
-                BE_ANY, 1, "peers")))
+    if (!parse)
+        return 0;
+
+    if (!benc_dct_chk(content, 2, BE_INT, 1, "interval", BE_ANY, 1, "peers"))
         goto bad_data;
 
     interval = benc_dget_int(content, "interval");
@@ -109,6 +113,19 @@ bad_data:
     return 1;
 }
 
+static void
+tr_set_stopped(struct torrent *tp)
+{
+    struct tracker *tr = tp->tr;
+    event_del(&tr->timer);
+    tr->ttype = TIMER_NONE;
+    if (tr->req != NULL) {
+        http_cancel(tr->req);
+        tr->req = NULL;
+    }
+    torrent_on_tr_stopped(tp);
+}
+
 static void
 http_cb(struct http *req, struct http_res *res, void *arg)
 {
@@ -116,9 +133,8 @@ http_cb(struct http *req, struct http_res *res, void *arg)
     struct tracker *tr = tp->tr;
     assert(tr->ttype == TIMER_TIMEOUT);
     tr->req = NULL;
-    if (res->res == HRES_OK &&
-        (tr->event == TR_EV_STOPPED
-            || parse_reply(tp, res->content, res->length) == 0)) {
+    if (res->res == HRES_OK && parse_reply(tp, res->content, res->length,
+            tr->event != TR_EV_STOPPED) == 0) {
         tr->nerrors = 0;
         tr->ttype = TIMER_INTERVAL;
         event_add(&tr->timer, (& (struct timeval) { tr->interval, 0 }));
@@ -128,7 +144,7 @@ http_cb(struct http *req, struct http_res *res, void *arg)
         event_add(&tr->timer, RETRY_WAIT);
     }
     if (tr->event == TR_EV_STOPPED && (tr->nerrors == 0 || tr->nerrors >= 5))
-        tr_destroy(tp);
+        tr_set_stopped(tp);
 }
 
 static void
@@ -138,17 +154,15 @@ timer_cb(int fd, short type, void *arg)
     struct tracker *tr = tp->tr;
     switch (tr->ttype) {
     case TIMER_TIMEOUT:
+        btpd_log(BTPD_L_ERROR, "Tracker request timed out for '%s'.\n",
+            tp->meta.name);
         tr->nerrors++;
         if (tr->event == TR_EV_STOPPED && tr->nerrors >= 5) {
-            tr_destroy(tp);
+            tr_set_stopped(tp);
             break;
         }
     case TIMER_RETRY:
-        if (tr->event == TR_EV_STOPPED) {
-            event_add(&tr->timer, REQ_TIMEOUT);
-            http_redo(&tr->req);
-        } else
-            tr_send(tp, tr->event);
+        tr_send(tp, tr->event);
         break;
     case TIMER_INTERVAL:
         tr_send(tp, TR_EV_EMPTY);
@@ -188,27 +202,21 @@ tr_send(struct torrent *tp, enum tr_event event)
 }
 
 int
-tr_start(struct torrent *tp)
+tr_create(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;
-
-    tr_send(tp, TR_EV_STARTED);
-
+    tp->tr = btpd_calloc(1, sizeof(*tp->tr));
+    evtimer_set(&tp->tr->timer, timer_cb, tp);
     return 0;
 }
 
 void
-tr_destroy(struct torrent *tp)
+tr_kill(struct torrent *tp)
 {
     struct tracker *tr = tp->tr;
     tp->tr = NULL;
@@ -216,7 +224,12 @@ tr_destroy(struct torrent *tp)
     if (tr->req != NULL)
         http_cancel(tr->req);
     free(tr);
-    torrent_on_tr_stopped(tp);
+}
+
+void
+tr_start(struct torrent *tp)
+{
+    tr_send(tp, TR_EV_STARTED);
 }
 
 void
@@ -234,7 +247,16 @@ tr_complete(struct torrent *tp)
 void
 tr_stop(struct torrent *tp)
 {
-    tr_send(tp, TR_EV_STOPPED);
+    if (tp->tr->event == TR_EV_STOPPED)
+        tr_set_stopped(tp);
+    else
+        tr_send(tp, TR_EV_STOPPED);
+}
+
+int
+tr_active(struct torrent *tp)
+{
+    return tp->tr->ttype != TIMER_NONE;
 }
 
 unsigned
diff --git a/btpd/tracker_req.h b/btpd/tracker_req.h
index 6106ef7..1e85163 100644
--- a/btpd/tracker_req.h
+++ b/btpd/tracker_req.h
@@ -1,11 +1,13 @@
 #ifndef TRACKER_REQ_H
 #define TRACKER_REQ_H
 
-int tr_start(struct torrent *tp);
+int tr_create(struct torrent *tp);
+void tr_kill(struct torrent *tp);
+void tr_start(struct torrent *tp);
 void tr_stop(struct torrent *tp);
 void tr_refresh(struct torrent *tp);
 void tr_complete(struct torrent *tp);
-void tr_destroy(struct torrent *tp);
 unsigned tr_errors(struct torrent *tp);
+int tr_active(struct torrent *tp);
 
 #endif