A clone of btpd with my configuration changes.

300 line
7.2 KiB

  1. #include <sys/types.h>
  2. #include <sys/socket.h>
  3. #include <netinet/in.h>
  4. #include <netdb.h>
  5. #include <sys/wait.h>
  6. #include <sys/mman.h>
  7. #include <stdio.h>
  8. #include <errno.h>
  9. #include <stdlib.h>
  10. #include <unistd.h>
  11. #include <string.h>
  12. #include <inttypes.h>
  13. #include <curl/curl.h>
  14. #include "btpd.h"
  15. #include "tracker_req.h"
  16. #define REQ_SIZE (getpagesize() * 2)
  17. struct tracker_req {
  18. enum tr_event tr_event;
  19. uint8_t info_hash[20];
  20. struct io_buffer *res;
  21. };
  22. static void
  23. maybe_connect_to(struct torrent *tp, const char *pinfo)
  24. {
  25. const char *pid = NULL;
  26. char *ip = NULL;
  27. int64_t port;
  28. size_t len;
  29. if (!benc_isdct(pinfo))
  30. return;
  31. if (benc_dget_str(pinfo, "peer id", &pid, &len) != 0 || len != 20)
  32. return;
  33. if (bcmp(btpd_get_peer_id(), pid, 20) == 0)
  34. return;
  35. if (torrent_has_peer(tp, pid))
  36. return;
  37. if (benc_dget_strz(pinfo, "ip", &ip, NULL) != 0)
  38. goto out;
  39. if (benc_dget_int64(pinfo, "port", &port) != 0)
  40. goto out;
  41. peer_create_out(tp, pid, ip, port);
  42. out:
  43. if (ip != NULL)
  44. free(ip);
  45. }
  46. static void
  47. tracker_done(pid_t pid, void *arg)
  48. {
  49. struct tracker_req *req = arg;
  50. int failed = 0;
  51. char *buf;
  52. const char *peers;
  53. uint32_t interval;
  54. struct torrent *tp;
  55. if ((tp = btpd_get_torrent(req->info_hash)) == NULL)
  56. goto out;
  57. if (benc_validate(req->res->buf, req->res->buf_off) != 0
  58. || !benc_isdct(req->res->buf)) {
  59. if (req->res->buf_off != 0) {
  60. fwrite(req->res->buf, 1, req->res->buf_off, (stdout));
  61. putchar('\n');
  62. }
  63. btpd_log(BTPD_L_ERROR, "Bad data from tracker.\n");
  64. failed = 1;
  65. goto out;
  66. }
  67. if ((benc_dget_strz(req->res->buf, "failure reason", &buf, NULL)) == 0) {
  68. btpd_log(BTPD_L_ERROR, "Tracker failure: %s.\n", buf);
  69. free(buf);
  70. failed = 1;
  71. goto out;
  72. }
  73. if ((benc_dget_uint32(req->res->buf, "interval", &interval)) != 0) {
  74. btpd_log(BTPD_L_ERROR, "Bad data from tracker.\n");
  75. failed = 1;
  76. goto out;
  77. }
  78. //tp->tracker_time = btpd_seconds + interval;
  79. int error = 0;
  80. size_t length;
  81. if ((error = benc_dget_lst(req->res->buf, "peers", &peers)) == 0) {
  82. for (peers = benc_first(peers);
  83. peers != NULL && net_npeers < net_max_peers;
  84. peers = benc_next(peers))
  85. maybe_connect_to(tp, peers);
  86. }
  87. if (error == EINVAL) {
  88. error = benc_dget_str(req->res->buf, "peers", &peers, &length);
  89. if (error == 0 && length % 6 == 0) {
  90. size_t i;
  91. for (i = 0; i < length && net_npeers < net_max_peers; i += 6)
  92. peer_create_out_compact(tp, peers + i);
  93. }
  94. }
  95. if (error != 0) {
  96. btpd_log(BTPD_L_ERROR, "Bad data from tracker.\n");
  97. failed = 1;
  98. goto out;
  99. }
  100. out:
  101. if (failed)
  102. ;//tp->tracker_time = btpd_seconds + 10;
  103. munmap(req->res, REQ_SIZE);
  104. free(req);
  105. }
  106. static const char *
  107. event2str(enum tr_event ev)
  108. {
  109. switch (ev) {
  110. case TR_STARTED:
  111. return "started";
  112. case TR_STOPPED:
  113. return "stopped";
  114. case TR_COMPLETED:
  115. return "completed";
  116. case TR_EMPTY:
  117. return "";
  118. default:
  119. btpd_err("Bad tracker event %d.\n", ev);
  120. return ""; // Shut GCC up!
  121. }
  122. }
  123. static int
  124. create_url(struct tracker_req *req, struct torrent *tp, char **url)
  125. {
  126. char e_hash[61], e_id[61];
  127. const uint8_t *peer_id = btpd_get_peer_id();
  128. char qc;
  129. int i;
  130. off_t left;
  131. const char *event;
  132. event = event2str(req->tr_event);
  133. qc = (strchr(tp->meta.announce, '?') == NULL) ? '?' : '&';
  134. for (i = 0; i < 20; i++)
  135. snprintf(e_hash + i * 3, 4, "%%%.2x", tp->meta.info_hash[i]);
  136. for (i = 0; i < 20; i++)
  137. snprintf(e_id + i * 3, 4, "%%%.2x", peer_id[i]);
  138. left = cm_bytes_left(tp);
  139. i =
  140. asprintf(url, "%s%cinfo_hash=%s&peer_id=%s&port=%d&uploaded=%ju"
  141. "&downloaded=%ju&left=%ju&compact=1%s%s",
  142. tp->meta.announce, qc, e_hash, e_id, net_port,
  143. (intmax_t)tp->uploaded, (intmax_t)tp->downloaded, (intmax_t)left,
  144. req->tr_event == TR_EMPTY ? "" : "&event=",
  145. event);
  146. if (i < 0)
  147. return ENOMEM;
  148. return 0;
  149. }
  150. static size_t
  151. http_cb(void *ptr, size_t size, size_t nmemb, void *stream)
  152. {
  153. struct tracker_req *req = (struct tracker_req *)stream;
  154. size_t nbytes = size * nmemb;
  155. if (nbytes <= req->res->buf_len - req->res->buf_off) {
  156. memcpy(req->res->buf + req->res->buf_off, ptr, nbytes);
  157. req->res->buf_off += nbytes;
  158. return nbytes;
  159. }
  160. else
  161. return 0;
  162. }
  163. static void
  164. http_helper(struct tracker_req *req, struct torrent *tp)
  165. {
  166. char cerror[CURL_ERROR_SIZE];
  167. char fr[] = "failure reason";
  168. CURL *handle;
  169. char *url;
  170. int err;
  171. if (create_url(req, tp, &url) != 0)
  172. goto memory_error;
  173. if (curl_global_init(0) != 0)
  174. goto libcurl_error;
  175. if ((handle = curl_easy_init()) == NULL)
  176. goto libcurl_error;
  177. err = curl_easy_setopt(handle, CURLOPT_URL, url);
  178. if (err == 0)
  179. err = curl_easy_setopt(handle, CURLOPT_USERAGENT, BTPD_VERSION);
  180. if (err == 0)
  181. err = curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, http_cb);
  182. if (err == 0)
  183. err = curl_easy_setopt(handle, CURLOPT_WRITEDATA, req);
  184. if (err == 0)
  185. err = curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, cerror);
  186. if (err != 0) {
  187. strncpy(cerror, curl_easy_strerror(err), CURL_ERROR_SIZE - 1);
  188. goto handle_error;
  189. }
  190. req->res->buf_off = 0;
  191. if (curl_easy_perform(handle) != 0)
  192. goto handle_error;
  193. #if 0
  194. curl_easy_cleanup(handle);
  195. curl_global_cleanup();
  196. free(url);
  197. #endif
  198. exit(0);
  199. memory_error:
  200. strncpy(cerror, "Out of memory", CURL_ERROR_SIZE - 1);
  201. goto handle_error;
  202. libcurl_error:
  203. strncpy(cerror, "Generic libcurl error", CURL_ERROR_SIZE - 1);
  204. goto handle_error;
  205. handle_error:
  206. req->res->buf_off =
  207. snprintf(req->res->buf, req->res->buf_len,
  208. "d%d:%s%d:%se", (int)strlen(fr), fr, (int)strlen(cerror), cerror);
  209. if (req->res->buf_off >= req->res->buf_len)
  210. req->res->buf_off = 0;
  211. exit(1);
  212. }
  213. void
  214. tracker_req(struct torrent *tp, enum tr_event tr_event)
  215. {
  216. struct tracker_req *req;
  217. pid_t pid;
  218. btpd_log(BTPD_L_TRACKER,
  219. "request for %s, event: %s.\n",
  220. tp->relpath, event2str(tr_event));
  221. req = (struct tracker_req *)btpd_calloc(1, sizeof(*req));
  222. req->res = mmap(NULL, REQ_SIZE, PROT_READ | PROT_WRITE,
  223. MAP_ANON | MAP_SHARED, -1, 0);
  224. if (req->res == MAP_FAILED)
  225. btpd_err("Failed mmap: %s\n", strerror(errno));
  226. req->res->buf_len = REQ_SIZE - sizeof(*req->res);
  227. req->res->buf_off = 0;
  228. req->res->buf = (char *)req->res + sizeof(*req->res);
  229. req->tr_event = tr_event;
  230. bcopy(tp->meta.info_hash, req->info_hash, 20);
  231. fflush(NULL);
  232. pid = fork();
  233. if (pid < 0) {
  234. btpd_err("Couldn't fork (%s).\n", strerror(errno));
  235. } else if (pid == 0) { // Child
  236. int nfiles = getdtablesize();
  237. for (int i = 0; i < nfiles; i++)
  238. close(i);
  239. http_helper(req, tp);
  240. } else
  241. btpd_add_child(pid, tracker_done, req);
  242. }