diff options
| author | Richard Nyberg <rnyberg@murmeldjur.se> | 2006-01-09 13:41:07 +0000 |
|---|---|---|
| committer | Richard Nyberg <rnyberg@murmeldjur.se> | 2006-01-09 13:41:07 +0000 |
| commit | 5377243d177d4b7a6675678b78bec02143d0a639 (patch) | |
| tree | d54a3e5eb5e1941638bd1bd9a64492b706c61fe6 | |
| parent | d4082e3a7a3ac85fd31375eed5d7a76055fda799 (diff) | |
| download | btpd-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.c | 2 | ||||
| -rw-r--r-- | btpd/torrent.c | 7 | ||||
| -rw-r--r-- | btpd/torrent.h | 1 | ||||
| -rw-r--r-- | btpd/tracker_req.c | 345 | ||||
| -rw-r--r-- | btpd/tracker_req.h | 12 |
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 |