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