1 #include "btpd.h"
2
3 #include <http_client.h>
4 #include <iobuf.h>
5
6 #define MAX_DOWNLOAD (1 << 18) // 256kB
7
8 static const char *m_tr_events[] = { "started", "stopped", "completed", "" };
9
10 struct httptr_req {
11 struct torrent *tp;
12 struct tr_tier *tr;
13 struct http_req *req;
14 struct iobuf buf;
15 struct fdev ioev;
16 struct timeout timer;
17 nameconn_t nc;
18 int sd;
19 enum tr_event event;
20 };
21
22 static void
httptr_free(struct httptr_req * treq)23 httptr_free(struct httptr_req *treq)
24 {
25 if (treq->sd != -1) {
26 btpd_ev_del(&treq->ioev);
27 close(treq->sd);
28 }
29 btpd_timer_del(&treq->timer);
30 iobuf_free(&treq->buf);
31 free(treq);
32 }
33
34 static void
maybe_connect_to(struct torrent * tp,const char * pinfo)35 maybe_connect_to(struct torrent *tp, const char *pinfo)
36 {
37 const char *pid;
38 char *ip;
39 int port;
40 size_t len;
41
42 if ((pid = benc_dget_mem(pinfo, "peer id", &len)) == NULL || len != 20)
43 return;
44
45 if (bcmp(btpd_get_peer_id(), pid, 20) == 0)
46 return;
47
48 if (net_torrent_has_peer(tp->net, pid))
49 return;
50
51 if ((ip = benc_dget_str(pinfo, "ip", NULL)) == NULL)
52 return;
53
54 port = benc_dget_int(pinfo, "port");
55 peer_create_out(tp->net, pid, ip, port);
56
57 if (ip != NULL)
58 free(ip);
59 }
60
61 static void
parse_reply(struct torrent * tp,struct tr_response * res,const char * content,size_t size)62 parse_reply(struct torrent *tp, struct tr_response *res, const char *content,
63 size_t size)
64 {
65 const char *buf;
66 size_t len;
67 const char *peers;
68 const char *v6key[] = {"peers6", "peers_ipv6"};
69
70 if (benc_validate(content, size) != 0)
71 goto bad_data;
72
73 if ((buf = benc_dget_any(content, "failure reason")) != NULL) {
74 if (!benc_isstr(buf))
75 goto bad_data;
76 res->type = TR_RES_FAIL;
77 res->mi_failure = buf;
78 return;
79 }
80
81 buf = benc_dget_any(content, "interval");
82 if (buf != NULL && benc_isint(buf))
83 res->interval = benc_int(buf, NULL);
84
85 if ((peers = benc_dget_any(content, "peers")) == NULL)
86 goto after_peers;
87
88 if (benc_islst(peers)) {
89 for (peers = benc_first(peers);
90 peers != NULL && net_npeers < net_max_peers;
91 peers = benc_next(peers))
92 maybe_connect_to(tp, peers);
93 } else if (benc_isstr(peers)) {
94 if (net_ipv4) {
95 peers = benc_dget_mem(content, "peers", &len);
96 for (size_t i = 0; i < len && net_npeers < net_max_peers; i += 6)
97 peer_create_out_compact(tp->net, AF_INET, peers + i);
98 }
99 } else
100 goto bad_data;
101
102 after_peers:
103 if (!net_ipv6)
104 goto after_peers6;
105 for (int k = 0; k < 2; k++) {
106 peers = benc_dget_any(content, v6key[k]);
107 if (peers != NULL && benc_isstr(peers)) {
108 peers = benc_dget_mem(content, v6key[k], &len);
109 for (size_t i = 0; i < len && net_npeers < net_max_peers; i += 18)
110 peer_create_out_compact(tp->net, AF_INET6, peers + i);
111 }
112 }
113 after_peers6:
114 res->type = TR_RES_OK;
115 return;
116
117 bad_data:
118 res->type = TR_RES_BAD;
119 }
120
121 static void
http_cb(struct http_req * req,struct http_response * res,void * arg)122 http_cb(struct http_req *req, struct http_response *res, void *arg)
123 {
124 struct httptr_req *treq = arg;
125 struct tr_response tres = {0, NULL, -1 };
126 switch (res->type) {
127 case HTTP_T_ERR:
128 tres.type = TR_RES_BAD;
129 tr_result(treq->tr, &tres);
130 httptr_free(treq);
131 break;
132 case HTTP_T_DATA:
133 if (treq->buf.off + res->v.data.l > MAX_DOWNLOAD) {
134 tres.type = TR_RES_BAD;
135 tr_result(treq->tr, &tres);
136 httptr_cancel(treq);
137 break;
138 }
139 if (!iobuf_write(&treq->buf, res->v.data.p, res->v.data.l))
140 btpd_err("Out of memory.\n");
141 break;
142 case HTTP_T_DONE:
143 if (treq->event == TR_EV_STOPPED) {
144 tres.type = TR_RES_OK;
145 tr_result(treq->tr, &tres);
146 } else {
147 parse_reply(treq->tp, &tres, treq->buf.buf, treq->buf.off);
148 tr_result(treq->tr, &tres);
149 }
150 httptr_free(treq);
151 break;
152 default:
153 break;
154 }
155 }
156
157 static void
httptr_io_cb(int sd,short type,void * arg)158 httptr_io_cb(int sd, short type, void *arg)
159 {
160 struct tr_response res;
161 struct httptr_req *treq = arg;
162 switch (type) {
163 case EV_READ:
164 if (http_read(treq->req, sd) && !http_want_read(treq->req))
165 btpd_ev_disable(&treq->ioev, EV_READ);
166 break;
167 case EV_WRITE:
168 if (http_write(treq->req, sd) && !http_want_write(treq->req))
169 btpd_ev_disable(&treq->ioev, EV_WRITE);
170 break;
171 case EV_TIMEOUT:
172 res.type = TR_RES_CONN;
173 tr_result(treq->tr, &res);
174 httptr_cancel(treq);
175 break;
176 default:
177 abort();
178 }
179 }
180
181 static void
httptr_nc_cb(void * arg,int error,int sd)182 httptr_nc_cb(void *arg, int error, int sd)
183 {
184 struct tr_response res;
185 struct httptr_req *treq = arg;
186 if (error) {
187 res.type = TR_RES_CONN;
188 tr_result(treq->tr, &res);
189 http_cancel(treq->req);
190 httptr_free(treq);
191 } else {
192 treq->sd = sd;
193 uint16_t flags =
194 (http_want_read(treq->req) ? EV_READ : 0) |
195 (http_want_write(treq->req) ? EV_WRITE : 0);
196 btpd_ev_new(&treq->ioev, sd, flags, httptr_io_cb, treq);
197 btpd_timer_add(&treq->timer, (& (struct timespec) { 30, 0 }));
198 }
199 }
200
201 struct httptr_req *
httptr_req(struct torrent * tp,struct tr_tier * tr,const char * aurl,enum tr_event event)202 httptr_req(struct torrent *tp, struct tr_tier *tr, const char *aurl,
203 enum tr_event event)
204 {
205 char e_hash[61], e_id[61], url[512], qc;
206 const uint8_t *peer_id = btpd_get_peer_id();
207 struct http_url *http_url;
208
209 qc = (strchr(aurl, '?') == NULL) ? '?' : '&';
210
211 for (int i = 0; i < 20; i++)
212 snprintf(e_hash + i * 3, 4, "%%%.2x", tp->tl->hash[i]);
213 for (int i = 0; i < 20; i++)
214 snprintf(e_id + i * 3, 4, "%%%.2x", peer_id[i]);
215
216 snprintf(url, sizeof(url),
217 "%s%cinfo_hash=%s&peer_id=%s&key=%ld%s%s&port=%d&uploaded=%llu"
218 "&downloaded=%llu&left=%llu&compact=1%s%s",
219 aurl, qc, e_hash, e_id, tr_key,
220 tr_ip_arg == NULL ? "" : "&ip=", tr_ip_arg == NULL ? "" : tr_ip_arg,
221 net_port, tp->net->uploaded, tp->net->downloaded,
222 (long long)tp->total_length - cm_content(tp),
223 event == TR_EV_EMPTY ? "" : "&event=", m_tr_events[event]);
224
225 struct httptr_req *treq = btpd_calloc(1, sizeof(*treq));
226 if (!http_get(&treq->req, url, "User-Agent: " BTPD_VERSION "\r\n",
227 http_cb, treq)) {
228 free(treq);
229 return NULL;
230 }
231 treq->tp = tp;
232 treq->tr = tr;
233 treq->event = event;
234 treq->buf = iobuf_init(4096);
235 if (treq->buf.error)
236 btpd_err("Out of memory.\n");
237 treq->tr = tr;
238 treq->sd = -1;
239 http_url = http_url_get(treq->req);
240 treq->nc = btpd_name_connect(http_url->host, http_url->port,
241 httptr_nc_cb, treq);
242 evtimer_init(&treq->timer, httptr_io_cb, treq);
243 btpd_timer_add(&treq->timer, (& (struct timespec) { 60, 0 }));
244 return treq;
245 }
246
247 void
httptr_cancel(struct httptr_req * treq)248 httptr_cancel(struct httptr_req *treq)
249 {
250 if (treq->sd == -1)
251 btpd_name_connect_cancel(treq->nc);
252 http_cancel(treq->req);
253 httptr_free(treq);
254 }
255