1 /* <!-- copyright */
2 /*
3  * aria2 - The high speed download utility
4  *
5  * Copyright (C) 2009 Tatsuhiro Tsujikawa
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  *
21  * In addition, as a special exception, the copyright holders give
22  * permission to link the code of portions of this program with the
23  * OpenSSL library under certain conditions as described in each
24  * individual source file, and distribute linked combinations
25  * including the two.
26  * You must obey the GNU General Public License in all respects
27  * for all of the code used other than OpenSSL.  If you modify
28  * file(s) with this exception, you may extend this exception to your
29  * version of the file(s), but you are not obligated to do so.  If you
30  * do not wish to do so, delete this exception statement from your
31  * version.  If you delete this exception statement from all source
32  * files in the program, then also delete it here.
33  */
34 /* copyright --> */
35 #include "bittorrent_helper.h"
36 
37 #include <cassert>
38 #include <cstring>
39 #include <algorithm>
40 
41 #include "DownloadContext.h"
42 #include "Randomizer.h"
43 #include "util.h"
44 #include "DlAbortEx.h"
45 #include "message.h"
46 #include "fmt.h"
47 #include "BtConstants.h"
48 #include "MessageDigest.h"
49 #include "message_digest_helper.h"
50 #include "a2netcompat.h"
51 #include "BtConstants.h"
52 #include "bitfield.h"
53 #include "base32.h"
54 #include "magnet.h"
55 #include "bencode2.h"
56 #include "TorrentAttribute.h"
57 #include "SocketCore.h"
58 #include "Option.h"
59 #include "prefs.h"
60 #include "FileEntry.h"
61 #include "error_code.h"
62 #include "array_fun.h"
63 #include "DownloadFailureException.h"
64 #include "ValueBaseBencodeParser.h"
65 
66 namespace aria2 {
67 
68 namespace bittorrent {
69 
70 namespace {
71 const char C_NAME[] = "name";
72 const char C_NAME_UTF8[] = "name.utf-8";
73 const char C_FILES[] = "files";
74 const char C_LENGTH[] = "length";
75 const char C_PATH[] = "path";
76 const char C_PATH_UTF8[] = "path.utf-8";
77 const char C_INFO[] = "info";
78 const char C_PIECES[] = "pieces";
79 const char C_PIECE_LENGTH[] = "piece length";
80 const char C_PRIVATE[] = "private";
81 const char C_URL_LIST[] = "url-list";
82 const char C_ANNOUNCE[] = "announce";
83 const char C_ANNOUNCE_LIST[] = "announce-list";
84 const char C_NODES[] = "nodes";
85 const char C_CREATION_DATE[] = "creation date";
86 const char C_COMMENT[] = "comment";
87 const char C_COMMENT_UTF8[] = "comment.utf-8";
88 const char C_CREATED_BY[] = "created by";
89 
90 const char DEFAULT_PEER_ID_PREFIX[] = "aria2-";
91 const char DEFAULT_PEER_AGENT[] = "aria2/" PACKAGE_VERSION;
92 } // namespace
93 
94 const std::string MULTI("multi");
95 
96 const std::string SINGLE("single");
97 
98 namespace {
extractPieceHash(const std::shared_ptr<DownloadContext> & ctx,const std::string & hashData,size_t hashLength,size_t numPieces)99 void extractPieceHash(const std::shared_ptr<DownloadContext>& ctx,
100                       const std::string& hashData, size_t hashLength,
101                       size_t numPieces)
102 {
103   std::vector<std::string> pieceHashes;
104   pieceHashes.reserve(numPieces);
105   for (size_t i = 0; i < numPieces; ++i) {
106     const char* p = hashData.data() + i * hashLength;
107     pieceHashes.push_back(std::string(p, p + hashLength));
108   }
109   ctx->setPieceHashes("sha-1", pieceHashes.begin(), pieceHashes.end());
110 }
111 } // namespace
112 
113 namespace {
extractUrlList(TorrentAttribute * torrent,std::vector<std::string> & uris,const ValueBase * v)114 void extractUrlList(TorrentAttribute* torrent, std::vector<std::string>& uris,
115                     const ValueBase* v)
116 {
117   class UrlListVisitor : public ValueBaseVisitor {
118   private:
119     std::vector<std::string>& uris_;
120     TorrentAttribute* torrent_;
121 
122   public:
123     UrlListVisitor(std::vector<std::string>& uris, TorrentAttribute* torrent)
124         : uris_(uris), torrent_(torrent)
125     {
126     }
127 
128     virtual void visit(const String& v) CXX11_OVERRIDE
129     {
130       std::string utf8Uri = util::encodeNonUtf8(v.s());
131       uris_.push_back(utf8Uri);
132       torrent_->urlList.push_back(utf8Uri);
133     }
134 
135     virtual void visit(const Integer& v) CXX11_OVERRIDE {}
136     virtual void visit(const Bool& v) CXX11_OVERRIDE {}
137     virtual void visit(const Null& v) CXX11_OVERRIDE {}
138 
139     virtual void visit(const List& v) CXX11_OVERRIDE
140     {
141       for (auto& elem : v) {
142         const String* uri = downcast<String>(elem);
143         if (uri) {
144           std::string utf8Uri = util::encodeNonUtf8(uri->s());
145           uris_.push_back(utf8Uri);
146           torrent_->urlList.push_back(utf8Uri);
147         }
148       }
149     }
150     virtual void visit(const Dict& v) CXX11_OVERRIDE {}
151   };
152 
153   if (v) {
154     UrlListVisitor visitor(uris, torrent);
155     v->accept(visitor);
156   }
157 }
158 } // namespace
159 
160 namespace {
161 template <typename InputIterator, typename OutputIterator>
createUri(InputIterator first,InputIterator last,OutputIterator out,const std::string & filePath)162 OutputIterator createUri(InputIterator first, InputIterator last,
163                          OutputIterator out, const std::string& filePath)
164 {
165   for (; first != last; ++first) {
166     if (!(*first).empty() && (*first)[(*first).size() - 1] == '/') {
167       *out++ = (*first) + filePath;
168     }
169     else {
170       *out++ = (*first) + "/" + filePath;
171     }
172   }
173   return out;
174 }
175 } // namespace
176 
177 namespace {
extractFileEntries(const std::shared_ptr<DownloadContext> & ctx,TorrentAttribute * torrent,const Dict * infoDict,const std::shared_ptr<Option> & option,const std::string & defaultName,const std::string & overrideName,const std::vector<std::string> & urlList)178 void extractFileEntries(const std::shared_ptr<DownloadContext>& ctx,
179                         TorrentAttribute* torrent, const Dict* infoDict,
180                         const std::shared_ptr<Option>& option,
181                         const std::string& defaultName,
182                         const std::string& overrideName,
183                         const std::vector<std::string>& urlList)
184 {
185   std::string utf8Name;
186   if (overrideName.empty()) {
187     std::string nameKey;
188     if (infoDict->containsKey(C_NAME_UTF8)) {
189       nameKey = C_NAME_UTF8;
190     }
191     else {
192       nameKey = C_NAME;
193     }
194     const String* nameData = downcast<String>(infoDict->get(nameKey));
195     if (nameData) {
196       utf8Name = util::encodeNonUtf8(nameData->s());
197       if (util::detectDirTraversal(utf8Name)) {
198         throw DL_ABORT_EX2(
199             fmt(MSG_DIR_TRAVERSAL_DETECTED, nameData->s().c_str()),
200             error_code::BITTORRENT_PARSE_ERROR);
201       }
202     }
203     else {
204       utf8Name = File(defaultName).getBasename();
205       utf8Name += ".file";
206     }
207   }
208   else {
209     utf8Name = overrideName;
210   }
211   torrent->name = utf8Name;
212   int maxConn = option->getAsInt(PREF_MAX_CONNECTION_PER_SERVER);
213   std::vector<std::shared_ptr<FileEntry>> fileEntries;
214   const List* filesList = downcast<List>(infoDict->get(C_FILES));
215   if (filesList) {
216     fileEntries.reserve(filesList->size());
217     int64_t length = 0;
218     int64_t offset = 0;
219     // multi-file mode
220     torrent->mode = BT_FILE_MODE_MULTI;
221     for (auto& f : *filesList) {
222       const Dict* fileDict = downcast<Dict>(f);
223       if (!fileDict) {
224         continue;
225       }
226       const Integer* fileLengthData =
227           downcast<Integer>(fileDict->get(C_LENGTH));
228       if (!fileLengthData) {
229         throw DL_ABORT_EX2(fmt(MSG_MISSING_BT_INFO, C_LENGTH),
230                            error_code::BITTORRENT_PARSE_ERROR);
231       }
232 
233       if (fileLengthData->i() < 0) {
234         throw DL_ABORT_EX2(
235             fmt(MSG_NEGATIVE_LENGTH_BT_INFO, C_LENGTH, fileLengthData->i()),
236             error_code::BITTORRENT_PARSE_ERROR);
237       }
238 
239       if (length > std::numeric_limits<int64_t>::max() - fileLengthData->i()) {
240         throw DOWNLOAD_FAILURE_EXCEPTION(fmt(EX_TOO_LARGE_FILE, length));
241       }
242       length += fileLengthData->i();
243       if (fileLengthData->i() > std::numeric_limits<a2_off_t>::max()) {
244         throw DOWNLOAD_FAILURE_EXCEPTION(fmt(EX_TOO_LARGE_FILE, length));
245       }
246       std::string pathKey;
247       if (fileDict->containsKey(C_PATH_UTF8)) {
248         pathKey = C_PATH_UTF8;
249       }
250       else {
251         pathKey = C_PATH;
252       }
253       const List* pathList = downcast<List>(fileDict->get(pathKey));
254       if (!pathList || pathList->empty()) {
255         throw DL_ABORT_EX2("Path is empty.",
256                            error_code::BITTORRENT_PARSE_ERROR);
257       }
258 
259       std::vector<std::string> pathelem(pathList->size() + 1);
260       pathelem[0] = utf8Name;
261       auto pathelemOutItr = pathelem.begin();
262       ++pathelemOutItr;
263       for (auto& p : *pathList) {
264         const String* elem = downcast<String>(p);
265         if (elem) {
266           (*pathelemOutItr++) = elem->s();
267         }
268         else {
269           throw DL_ABORT_EX2("Path element is not string.",
270                              error_code::BITTORRENT_PARSE_ERROR);
271         }
272       }
273       std::string utf8Path = strjoin(
274           pathelem.begin(), pathelem.end(), "/",
275           std::function<std::string(const std::string&)>(util::encodeNonUtf8));
276       if (util::detectDirTraversal(utf8Path)) {
277         throw DL_ABORT_EX2(fmt(MSG_DIR_TRAVERSAL_DETECTED, utf8Path.c_str()),
278                            error_code::BITTORRENT_PARSE_ERROR);
279       }
280       std::string pePath =
281           strjoin(pathelem.begin(), pathelem.end(), "/",
282                   std::function<std::string(const std::string&)>(
283                       static_cast<std::string (*)(const std::string&)>(
284                           util::percentEncode)));
285       std::vector<std::string> uris;
286       createUri(urlList.begin(), urlList.end(), std::back_inserter(uris),
287                 pePath);
288 
289       auto suffixPath = util::escapePath(utf8Path);
290 
291       auto fileEntry = std::make_shared<FileEntry>(
292           util::applyDir(option->get(PREF_DIR), suffixPath),
293           fileLengthData->i(), offset, uris);
294       fileEntry->setOriginalName(utf8Path);
295       fileEntry->setSuffixPath(suffixPath);
296       fileEntry->setMaxConnectionPerServer(maxConn);
297       fileEntries.push_back(fileEntry);
298       offset += fileEntry->getLength();
299     }
300   }
301   else {
302     // single-file mode;
303     torrent->mode = BT_FILE_MODE_SINGLE;
304     const Integer* lengthData = downcast<Integer>(infoDict->get(C_LENGTH));
305     if (!lengthData) {
306       throw DL_ABORT_EX2(fmt(MSG_MISSING_BT_INFO, C_LENGTH),
307                          error_code::BITTORRENT_PARSE_ERROR);
308     }
309     int64_t totalLength = lengthData->i();
310 
311     if (totalLength < 0) {
312       throw DL_ABORT_EX2(
313           fmt(MSG_NEGATIVE_LENGTH_BT_INFO, C_LENGTH, totalLength),
314           error_code::BITTORRENT_PARSE_ERROR);
315     }
316 
317     if (totalLength > std::numeric_limits<a2_off_t>::max()) {
318       throw DOWNLOAD_FAILURE_EXCEPTION(fmt(EX_TOO_LARGE_FILE, totalLength));
319     }
320     // For each uri in urlList, if it ends with '/', then
321     // concatenate name to it. Specification just says so.
322     std::vector<std::string> uris;
323     for (auto& elem : urlList) {
324       if (!elem.empty() && elem[elem.size() - 1] == '/') {
325         uris.push_back(elem + util::percentEncode(utf8Name));
326       }
327       else {
328         uris.push_back(elem);
329       }
330     }
331 
332     auto suffixPath = util::escapePath(utf8Name);
333 
334     auto fileEntry = std::make_shared<FileEntry>(
335         util::applyDir(option->get(PREF_DIR), suffixPath), totalLength, 0,
336         uris);
337     fileEntry->setOriginalName(utf8Name);
338     fileEntry->setSuffixPath(suffixPath);
339     fileEntry->setMaxConnectionPerServer(maxConn);
340     fileEntries.push_back(fileEntry);
341   }
342   ctx->setFileEntries(fileEntries.begin(), fileEntries.end());
343   if (torrent->mode == BT_FILE_MODE_MULTI) {
344     ctx->setBasePath(
345         util::applyDir(option->get(PREF_DIR), util::escapePath(utf8Name)));
346   }
347 }
348 } // namespace
349 
350 namespace {
extractAnnounce(TorrentAttribute * torrent,const Dict * rootDict)351 void extractAnnounce(TorrentAttribute* torrent, const Dict* rootDict)
352 {
353   const List* announceList = downcast<List>(rootDict->get(C_ANNOUNCE_LIST));
354   if (announceList) {
355     for (auto& elem : *announceList) {
356       const List* tier = downcast<List>(elem);
357       if (!tier) {
358         continue;
359       }
360       std::vector<std::string> ntier;
361       for (auto& t : *tier) {
362         const String* uri = downcast<String>(t);
363         if (uri) {
364           ntier.push_back(util::encodeNonUtf8(util::strip(uri->s())));
365         }
366       }
367       if (!ntier.empty()) {
368         torrent->announceList.push_back(ntier);
369       }
370     }
371   }
372   else {
373     const String* announce = downcast<String>(rootDict->get(C_ANNOUNCE));
374     if (announce) {
375       std::vector<std::string> tier;
376       tier.push_back(util::encodeNonUtf8(util::strip(announce->s())));
377       torrent->announceList.push_back(tier);
378     }
379   }
380 }
381 } // namespace
382 
383 namespace {
extractNodes(TorrentAttribute * torrent,const ValueBase * nodesListSrc)384 void extractNodes(TorrentAttribute* torrent, const ValueBase* nodesListSrc)
385 {
386   const List* nodesList = downcast<List>(nodesListSrc);
387   if (nodesList) {
388     for (auto& elem : *nodesList) {
389       const List* addrPairList = downcast<List>(elem);
390       if (!addrPairList || addrPairList->size() != 2) {
391         continue;
392       }
393       const String* hostname = downcast<String>(addrPairList->get(0));
394       if (!hostname) {
395         continue;
396       }
397       std::string utf8Hostname =
398           util::encodeNonUtf8(util::strip(hostname->s()));
399       if (utf8Hostname.empty()) {
400         continue;
401       }
402       const Integer* port = downcast<Integer>(addrPairList->get(1));
403       if (!port || !(0 < port->i() && port->i() < 65536)) {
404         continue;
405       }
406       torrent->nodes.push_back(std::make_pair(utf8Hostname, port->i()));
407     }
408   }
409 }
410 } // namespace
411 
412 namespace {
processRootDictionary(const std::shared_ptr<DownloadContext> & ctx,const ValueBase * root,const std::shared_ptr<Option> & option,const std::string & defaultName,const std::string & overrideName,const std::vector<std::string> & uris)413 void processRootDictionary(const std::shared_ptr<DownloadContext>& ctx,
414                            const ValueBase* root,
415                            const std::shared_ptr<Option>& option,
416                            const std::string& defaultName,
417                            const std::string& overrideName,
418                            const std::vector<std::string>& uris)
419 {
420   const Dict* rootDict = downcast<Dict>(root);
421   if (!rootDict) {
422     throw DL_ABORT_EX2("torrent file does not contain a root dictionary.",
423                        error_code::BITTORRENT_PARSE_ERROR);
424   }
425   const Dict* infoDict = downcast<Dict>(rootDict->get(C_INFO));
426   if (!infoDict) {
427     throw DL_ABORT_EX2(fmt(MSG_MISSING_BT_INFO, C_INFO),
428                        error_code::BITTORRENT_PARSE_ERROR);
429   }
430   auto torrent = std::make_shared<TorrentAttribute>();
431 
432   // retrieve infoHash
433   std::string encodedInfoDict = bencode2::encode(infoDict);
434   unsigned char infoHash[INFO_HASH_LENGTH];
435   message_digest::digest(infoHash, INFO_HASH_LENGTH,
436                          MessageDigest::sha1().get(), encodedInfoDict.data(),
437                          encodedInfoDict.size());
438   torrent->infoHash.assign(&infoHash[0], &infoHash[INFO_HASH_LENGTH]);
439   torrent->metadata = encodedInfoDict;
440   torrent->metadataSize = encodedInfoDict.size();
441 
442   // calculate the number of pieces
443   const String* piecesData = downcast<String>(infoDict->get(C_PIECES));
444   if (!piecesData) {
445     throw DL_ABORT_EX2(fmt(MSG_MISSING_BT_INFO, C_PIECES),
446                        error_code::BITTORRENT_PARSE_ERROR);
447   }
448   // Commented out To download 0 length torrent.
449   //   if(piecesData.s().empty()) {
450   //     throw DL_ABORT_EX("The length of piece hash is 0.");
451   //   }
452   size_t numPieces = piecesData->s().size() / PIECE_HASH_LENGTH;
453   // Commented out to download 0 length torrent.
454   //   if(numPieces == 0) {
455   //     throw DL_ABORT_EX("The number of pieces is 0.");
456   //   }
457   // retrieve piece length
458   const Integer* pieceLengthData =
459       downcast<Integer>(infoDict->get(C_PIECE_LENGTH));
460   if (!pieceLengthData) {
461     throw DL_ABORT_EX2(fmt(MSG_MISSING_BT_INFO, C_PIECE_LENGTH),
462                        error_code::BITTORRENT_PARSE_ERROR);
463   }
464 
465   if (pieceLengthData->i() < 0) {
466     throw DL_ABORT_EX2(
467         fmt(MSG_NEGATIVE_LENGTH_BT_INFO, C_PIECE_LENGTH, pieceLengthData->i()),
468         error_code::BITTORRENT_PARSE_ERROR);
469   }
470 
471   size_t pieceLength = pieceLengthData->i();
472   ctx->setPieceLength(pieceLength);
473   // retrieve piece hashes
474   extractPieceHash(ctx, piecesData->s(), PIECE_HASH_LENGTH, numPieces);
475   // private flag
476   const Integer* privateData = downcast<Integer>(infoDict->get(C_PRIVATE));
477   int privatefg = 0;
478   if (privateData) {
479     if (privateData->i() == 1) {
480       privatefg = 1;
481     }
482   }
483   if (privatefg) {
484     torrent->privateTorrent = true;
485   }
486   // retrieve uri-list.
487   // This implementation obeys HTTP-Seeding specification:
488   // see http://www.getright.com/seedtorrent.html
489   std::vector<std::string> urlList;
490   extractUrlList(torrent.get(), urlList, rootDict->get(C_URL_LIST));
491   urlList.insert(urlList.end(), uris.begin(), uris.end());
492   std::sort(urlList.begin(), urlList.end());
493   urlList.erase(std::unique(urlList.begin(), urlList.end()), urlList.end());
494 
495   // retrieve file entries
496   extractFileEntries(ctx, torrent.get(), infoDict, option, defaultName,
497                      overrideName, urlList);
498   if ((ctx->getTotalLength() + pieceLength - 1) / pieceLength != numPieces) {
499     throw DL_ABORT_EX2("Too few/many piece hash.",
500                        error_code::BITTORRENT_PARSE_ERROR);
501   }
502   // retrieve announce
503   extractAnnounce(torrent.get(), rootDict);
504   // retrieve nodes
505   extractNodes(torrent.get(), rootDict->get(C_NODES));
506 
507   const Integer* creationDate =
508       downcast<Integer>(rootDict->get(C_CREATION_DATE));
509   if (creationDate) {
510     torrent->creationDate = creationDate->i();
511   }
512   const String* commentUtf8 = downcast<String>(rootDict->get(C_COMMENT_UTF8));
513   if (commentUtf8) {
514     torrent->comment = util::encodeNonUtf8(commentUtf8->s());
515   }
516   else {
517     const String* comment = downcast<String>(rootDict->get(C_COMMENT));
518     if (comment) {
519       torrent->comment = util::encodeNonUtf8(comment->s());
520     }
521   }
522   const String* createdBy = downcast<String>(rootDict->get(C_CREATED_BY));
523   if (createdBy) {
524     torrent->createdBy = util::encodeNonUtf8(createdBy->s());
525   }
526 
527   ctx->setAttribute(CTX_ATTR_BT, std::move(torrent));
528 }
529 } // namespace
530 
load(const std::string & torrentFile,const std::shared_ptr<DownloadContext> & ctx,const std::shared_ptr<Option> & option,const std::string & overrideName)531 void load(const std::string& torrentFile,
532           const std::shared_ptr<DownloadContext>& ctx,
533           const std::shared_ptr<Option>& option,
534           const std::string& overrideName)
535 {
536   ValueBaseBencodeParser parser;
537   processRootDictionary(ctx, parseFile(parser, torrentFile).get(), option,
538                         torrentFile, overrideName, std::vector<std::string>());
539 }
540 
load(const std::string & torrentFile,const std::shared_ptr<DownloadContext> & ctx,const std::shared_ptr<Option> & option,const std::vector<std::string> & uris,const std::string & overrideName)541 void load(const std::string& torrentFile,
542           const std::shared_ptr<DownloadContext>& ctx,
543           const std::shared_ptr<Option>& option,
544           const std::vector<std::string>& uris, const std::string& overrideName)
545 {
546   ValueBaseBencodeParser parser;
547   processRootDictionary(ctx, parseFile(parser, torrentFile).get(), option,
548                         torrentFile, overrideName, uris);
549 }
550 
loadFromMemory(const unsigned char * content,size_t length,const std::shared_ptr<DownloadContext> & ctx,const std::shared_ptr<Option> & option,const std::string & defaultName,const std::string & overrideName)551 void loadFromMemory(const unsigned char* content, size_t length,
552                     const std::shared_ptr<DownloadContext>& ctx,
553                     const std::shared_ptr<Option>& option,
554                     const std::string& defaultName,
555                     const std::string& overrideName)
556 {
557   processRootDictionary(ctx, bencode2::decode(content, length).get(), option,
558                         defaultName, overrideName, std::vector<std::string>());
559 }
560 
loadFromMemory(const unsigned char * content,size_t length,const std::shared_ptr<DownloadContext> & ctx,const std::shared_ptr<Option> & option,const std::vector<std::string> & uris,const std::string & defaultName,const std::string & overrideName)561 void loadFromMemory(const unsigned char* content, size_t length,
562                     const std::shared_ptr<DownloadContext>& ctx,
563                     const std::shared_ptr<Option>& option,
564                     const std::vector<std::string>& uris,
565                     const std::string& defaultName,
566                     const std::string& overrideName)
567 {
568   processRootDictionary(ctx, bencode2::decode(content, length).get(), option,
569                         defaultName, overrideName, uris);
570 }
571 
loadFromMemory(const std::string & context,const std::shared_ptr<DownloadContext> & ctx,const std::shared_ptr<Option> & option,const std::string & defaultName,const std::string & overrideName)572 void loadFromMemory(const std::string& context,
573                     const std::shared_ptr<DownloadContext>& ctx,
574                     const std::shared_ptr<Option>& option,
575                     const std::string& defaultName,
576                     const std::string& overrideName)
577 {
578   processRootDictionary(ctx, bencode2::decode(context).get(), option,
579                         defaultName, overrideName, std::vector<std::string>());
580 }
581 
loadFromMemory(const std::string & context,const std::shared_ptr<DownloadContext> & ctx,const std::shared_ptr<Option> & option,const std::vector<std::string> & uris,const std::string & defaultName,const std::string & overrideName)582 void loadFromMemory(const std::string& context,
583                     const std::shared_ptr<DownloadContext>& ctx,
584                     const std::shared_ptr<Option>& option,
585                     const std::vector<std::string>& uris,
586                     const std::string& defaultName,
587                     const std::string& overrideName)
588 {
589   processRootDictionary(ctx, bencode2::decode(context).get(), option,
590                         defaultName, overrideName, uris);
591 }
592 
loadFromMemory(const ValueBase * torrent,const std::shared_ptr<DownloadContext> & ctx,const std::shared_ptr<Option> & option,const std::vector<std::string> & uris,const std::string & defaultName,const std::string & overrideName)593 void loadFromMemory(const ValueBase* torrent,
594                     const std::shared_ptr<DownloadContext>& ctx,
595                     const std::shared_ptr<Option>& option,
596                     const std::vector<std::string>& uris,
597                     const std::string& defaultName,
598                     const std::string& overrideName)
599 {
600   processRootDictionary(ctx, torrent, option, defaultName, overrideName, uris);
601 }
602 
getTorrentAttrs(const std::shared_ptr<DownloadContext> & dctx)603 TorrentAttribute* getTorrentAttrs(const std::shared_ptr<DownloadContext>& dctx)
604 {
605   return getTorrentAttrs(dctx.get());
606 }
607 
getTorrentAttrs(DownloadContext * dctx)608 TorrentAttribute* getTorrentAttrs(DownloadContext* dctx)
609 {
610   return static_cast<TorrentAttribute*>(dctx->getAttribute(CTX_ATTR_BT).get());
611 }
612 
getInfoHash(const std::shared_ptr<DownloadContext> & dctx)613 const unsigned char* getInfoHash(const std::shared_ptr<DownloadContext>& dctx)
614 {
615   return getInfoHash(dctx.get());
616 }
617 
getInfoHash(DownloadContext * dctx)618 const unsigned char* getInfoHash(DownloadContext* dctx)
619 {
620   return reinterpret_cast<const unsigned char*>(
621       getTorrentAttrs(dctx)->infoHash.data());
622 }
623 
getInfoHashString(const std::shared_ptr<DownloadContext> & dctx)624 std::string getInfoHashString(const std::shared_ptr<DownloadContext>& dctx)
625 {
626   return getInfoHashString(dctx.get());
627 }
628 
getInfoHashString(DownloadContext * dctx)629 std::string getInfoHashString(DownloadContext* dctx)
630 {
631   return util::toHex(getTorrentAttrs(dctx)->infoHash);
632 }
633 
computeFastSet(const std::string & ipaddr,size_t numPieces,const unsigned char * infoHash,size_t fastSetSize)634 std::vector<size_t> computeFastSet(const std::string& ipaddr, size_t numPieces,
635                                    const unsigned char* infoHash,
636                                    size_t fastSetSize)
637 {
638   std::vector<size_t> fastSet;
639   unsigned char compact[COMPACT_LEN_IPV6];
640   int compactlen = packcompact(compact, ipaddr, 0);
641   if (compactlen != COMPACT_LEN_IPV4) {
642     return fastSet;
643   }
644   if (numPieces < fastSetSize) {
645     fastSetSize = numPieces;
646   }
647   unsigned char tx[24];
648   memcpy(tx, compact, 4);
649   if ((tx[0] & 0x80u) == 0 || (tx[0] & 0x40u) == 0) {
650     tx[2] = 0x00u;
651     tx[3] = 0x00u;
652   }
653   else {
654     tx[3] = 0x00u;
655   }
656   memcpy(tx + 4, infoHash, 20);
657   unsigned char x[20];
658   auto sha1 = MessageDigest::sha1();
659   message_digest::digest(x, sizeof(x), sha1.get(), tx, 24);
660   while (fastSet.size() < fastSetSize) {
661     for (size_t i = 0; i < 5 && fastSet.size() < fastSetSize; ++i) {
662       size_t j = i * 4;
663       uint32_t ny;
664       memcpy(&ny, x + j, 4);
665       uint32_t y = ntohl(ny);
666       size_t index = y % numPieces;
667       if (std::find(std::begin(fastSet), std::end(fastSet), index) ==
668           std::end(fastSet)) {
669 
670         fastSet.push_back(index);
671       }
672     }
673     unsigned char temp[20];
674     sha1->reset();
675     message_digest::digest(temp, sizeof(temp), sha1.get(), x, sizeof(x));
676     memcpy(x, temp, sizeof(x));
677   }
678 
679   return fastSet;
680 }
681 
generatePeerId(const std::string & peerIdPrefix)682 std::string generatePeerId(const std::string& peerIdPrefix)
683 {
684   std::string peerId = peerIdPrefix;
685   unsigned char buf[20];
686   int len = 20 - peerIdPrefix.size();
687   if (len > 0) {
688     util::generateRandomData(buf, len);
689     peerId.append(&buf[0], &buf[len]);
690   }
691   if (peerId.size() > 20) {
692     peerId.erase(20);
693   }
694   return peerId;
695 }
696 
697 namespace {
698 std::string peerId;
699 std::string peerAgent;
700 } // namespace
701 
generateStaticPeerId(const std::string & peerIdPrefix)702 const std::string& generateStaticPeerId(const std::string& peerIdPrefix)
703 {
704   if (peerId.empty()) {
705     peerId = generatePeerId(peerIdPrefix);
706   }
707   return peerId;
708 }
709 
generateStaticPeerAgent(const std::string & peerAgentNew)710 const std::string& generateStaticPeerAgent(const std::string& peerAgentNew)
711 {
712   if (peerAgent.empty()) {
713     peerAgent = peerAgentNew;
714   }
715   return peerAgent;
716 }
717 
setStaticPeerId(const std::string & newPeerId)718 void setStaticPeerId(const std::string& newPeerId) { peerId = newPeerId; }
setStaticPeerAgent(const std::string & newPeerAgent)719 void setStaticPeerAgent(const std::string& newPeerAgent)
720 {
721   peerAgent = newPeerAgent;
722 }
723 
724 // If PeerID is not generated, it is created with default peerIdPrefix
725 // (aria2-).
getStaticPeerId()726 const unsigned char* getStaticPeerId()
727 {
728   if (peerId.empty()) {
729     return reinterpret_cast<const unsigned char*>(
730         generateStaticPeerId(DEFAULT_PEER_ID_PREFIX).data());
731   }
732   else {
733     return reinterpret_cast<const unsigned char*>(peerId.data());
734   }
735 }
736 
737 // If PeerAgent is not generated, it is created with default agent
738 // aria2/PACKAGE_VERSION
getStaticPeerAgent()739 const std::string& getStaticPeerAgent()
740 {
741   if (peerAgent.empty()) {
742     generateStaticPeerAgent(DEFAULT_PEER_AGENT);
743   }
744   return peerAgent;
745 }
746 
getId(const unsigned char * msg)747 uint8_t getId(const unsigned char* msg) { return msg[0]; }
748 
getLLIntParam(const unsigned char * msg,size_t pos)749 uint64_t getLLIntParam(const unsigned char* msg, size_t pos)
750 {
751   uint64_t nParam;
752   memcpy(&nParam, msg + pos, sizeof(nParam));
753   return ntoh64(nParam);
754 }
755 
getIntParam(const unsigned char * msg,size_t pos)756 uint32_t getIntParam(const unsigned char* msg, size_t pos)
757 {
758   uint32_t nParam;
759   memcpy(&nParam, msg + pos, sizeof(nParam));
760   return ntohl(nParam);
761 }
762 
getShortIntParam(const unsigned char * msg,size_t pos)763 uint16_t getShortIntParam(const unsigned char* msg, size_t pos)
764 {
765   uint16_t nParam;
766   memcpy(&nParam, msg + pos, sizeof(nParam));
767   return ntohs(nParam);
768 }
769 
checkIndex(size_t index,size_t pieces)770 void checkIndex(size_t index, size_t pieces)
771 {
772   if (!(index < pieces)) {
773     throw DL_ABORT_EX(
774         fmt("Invalid index: %lu", static_cast<unsigned long>(index)));
775   }
776 }
777 
checkBegin(int32_t begin,int32_t pieceLength)778 void checkBegin(int32_t begin, int32_t pieceLength)
779 {
780   if (!(begin < pieceLength)) {
781     throw DL_ABORT_EX(fmt("Invalid begin: %d", begin));
782   }
783 }
784 
checkLength(int32_t length)785 void checkLength(int32_t length)
786 {
787   if (length > static_cast<int32_t>(MAX_BLOCK_LENGTH)) {
788     throw DL_ABORT_EX(fmt("Length too long: %d > %dKB", length,
789                           static_cast<int32_t>(MAX_BLOCK_LENGTH / 1024)));
790   }
791   if (length == 0) {
792     throw DL_ABORT_EX(fmt("Invalid length: %d", length));
793   }
794 }
795 
checkRange(int32_t begin,int32_t length,int32_t pieceLength)796 void checkRange(int32_t begin, int32_t length, int32_t pieceLength)
797 {
798   if (!(0 < length)) {
799     throw DL_ABORT_EX(fmt("Invalid range: begin=%d, length=%d", begin, length));
800   }
801   int32_t end = begin + length;
802   if (!(end <= pieceLength)) {
803     throw DL_ABORT_EX(fmt("Invalid range: begin=%d, length=%d", begin, length));
804   }
805 }
806 
checkBitfield(const unsigned char * bitfield,size_t bitfieldLength,size_t pieces)807 void checkBitfield(const unsigned char* bitfield, size_t bitfieldLength,
808                    size_t pieces)
809 {
810   if (!(bitfieldLength == (pieces + 7) / 8)) {
811     throw DL_ABORT_EX(fmt("Invalid bitfield length: %lu",
812                           static_cast<unsigned long>(bitfieldLength)));
813   }
814   // Check if last byte contains garbage set bit.
815   if (bitfield[bitfieldLength - 1] & ~bitfield::lastByteMask(pieces)) {
816     throw DL_ABORT_EX("Invalid bitfield");
817   }
818 }
819 
setLLIntParam(unsigned char * dest,uint64_t param)820 void setLLIntParam(unsigned char* dest, uint64_t param)
821 {
822   uint64_t nParam = hton64(param);
823   memcpy(dest, &nParam, sizeof(nParam));
824 }
825 
setIntParam(unsigned char * dest,uint32_t param)826 void setIntParam(unsigned char* dest, uint32_t param)
827 {
828   uint32_t nParam = htonl(param);
829   memcpy(dest, &nParam, sizeof(nParam));
830 }
831 
setShortIntParam(unsigned char * dest,uint16_t param)832 void setShortIntParam(unsigned char* dest, uint16_t param)
833 {
834   uint16_t nParam = htons(param);
835   memcpy(dest, &nParam, sizeof(nParam));
836 }
837 
createPeerMessageString(unsigned char * msg,size_t msgLength,size_t payloadLength,uint8_t messageId)838 void createPeerMessageString(unsigned char* msg, size_t msgLength,
839                              size_t payloadLength, uint8_t messageId)
840 {
841   assert(msgLength >= 5);
842   memset(msg, 0, msgLength);
843   setIntParam(msg, payloadLength);
844   msg[4] = messageId;
845 }
846 
packcompact(unsigned char * compact,const std::string & addr,uint16_t port)847 size_t packcompact(unsigned char* compact, const std::string& addr,
848                    uint16_t port)
849 {
850   size_t len = net::getBinAddr(compact, addr);
851   if (len == 0) {
852     return 0;
853   }
854   uint16_t portN = htons(port);
855   memcpy(compact + len, &portN, sizeof(portN));
856   return len + 2;
857 }
858 
unpackcompact(const unsigned char * compact,int family)859 std::pair<std::string, uint16_t> unpackcompact(const unsigned char* compact,
860                                                int family)
861 {
862   std::pair<std::string, uint16_t> r;
863   int portOffset = family == AF_INET ? 4 : 16;
864   char buf[NI_MAXHOST];
865   if (inetNtop(family, compact, buf, sizeof(buf)) == 0) {
866     r.first = buf;
867     uint16_t portN;
868     memcpy(&portN, compact + portOffset, sizeof(portN));
869     r.second = ntohs(portN);
870   }
871   return r;
872 }
873 
assertPayloadLengthGreater(size_t threshold,size_t actual,const char * msgName)874 void assertPayloadLengthGreater(size_t threshold, size_t actual,
875                                 const char* msgName)
876 {
877   if (actual <= threshold) {
878     throw DL_ABORT_EX(fmt(MSG_TOO_SMALL_PAYLOAD_SIZE, msgName,
879                           static_cast<unsigned long>(actual)));
880   }
881 }
882 
assertPayloadLengthEqual(size_t expected,size_t actual,const char * msgName)883 void assertPayloadLengthEqual(size_t expected, size_t actual,
884                               const char* msgName)
885 {
886   if (expected != actual) {
887     throw DL_ABORT_EX(fmt(EX_INVALID_PAYLOAD_SIZE, msgName,
888                           static_cast<unsigned long>(actual),
889                           static_cast<unsigned long>(expected)));
890   }
891 }
892 
assertID(uint8_t expected,const unsigned char * data,const char * msgName)893 void assertID(uint8_t expected, const unsigned char* data, const char* msgName)
894 {
895   uint8_t id = getId(data);
896   if (expected != id) {
897     throw DL_ABORT_EX(fmt(EX_INVALID_BT_MESSAGE_ID, id, msgName, expected));
898   }
899 }
900 
parseMagnet(const std::string & magnet)901 std::unique_ptr<TorrentAttribute> parseMagnet(const std::string& magnet)
902 {
903   auto r = magnet::parse(magnet);
904   if (!r) {
905     throw DL_ABORT_EX2("Bad BitTorrent Magnet URI.",
906                        error_code::MAGNET_PARSE_ERROR);
907   }
908   const List* xts = downcast<List>(r->get("xt"));
909   if (!xts) {
910     throw DL_ABORT_EX2("Missing xt parameter in Magnet URI.",
911                        error_code::MAGNET_PARSE_ERROR);
912   }
913   auto attrs = make_unique<TorrentAttribute>();
914   std::string infoHash;
915   for (auto xtiter = xts->begin(), eoi = xts->end();
916        xtiter != eoi && infoHash.empty(); ++xtiter) {
917     const String* xt = downcast<String>(*xtiter);
918     if (util::startsWith(xt->s(), "urn:btih:")) {
919       size_t size = xt->s().end() - xt->s().begin() - 9;
920       if (size == 32) {
921         std::string rawhash =
922             base32::decode(xt->s().begin() + 9, xt->s().end());
923         if (rawhash.size() == 20) {
924           infoHash.swap(rawhash);
925         }
926       }
927       else if (size == 40) {
928         std::string rawhash = util::fromHex(xt->s().begin() + 9, xt->s().end());
929         if (!rawhash.empty()) {
930           infoHash.swap(rawhash);
931         }
932       }
933     }
934   }
935   if (infoHash.empty()) {
936     throw DL_ABORT_EX2("Bad BitTorrent Magnet URI. "
937                        "No valid BitTorrent Info Hash found.",
938                        error_code::MAGNET_PARSE_ERROR);
939   }
940   const List* trs = downcast<List>(r->get("tr"));
941   if (trs) {
942     for (auto& tr : *trs) {
943       std::vector<std::string> tier;
944       tier.push_back(util::encodeNonUtf8(downcast<String>(tr)->s()));
945       attrs->announceList.push_back(tier);
946     }
947   }
948   std::string name = "[METADATA]";
949   const List* dns = downcast<List>(r->get("dn"));
950   if (dns && !dns->empty()) {
951     const String* dn = downcast<String>(dns->get(0));
952     name += util::encodeNonUtf8(dn->s());
953   }
954   else {
955     name += util::toHex(infoHash);
956   }
957   attrs->infoHash = infoHash;
958   attrs->name = name;
959   return attrs;
960 }
961 
loadMagnet(const std::string & magnet,const std::shared_ptr<DownloadContext> & dctx)962 void loadMagnet(const std::string& magnet,
963                 const std::shared_ptr<DownloadContext>& dctx)
964 {
965   dctx->setAttribute(CTX_ATTR_BT, parseMagnet(magnet));
966 }
967 
metadata2Torrent(const std::string & metadata,const TorrentAttribute * attrs)968 std::string metadata2Torrent(const std::string& metadata,
969                              const TorrentAttribute* attrs)
970 {
971   std::string torrent = "d";
972 
973   List announceList;
974   for (auto& elem : attrs->announceList) {
975     auto tier = List::g();
976     for (auto& uri : elem) {
977       tier->append(uri);
978     }
979     if (!tier->empty()) {
980       announceList.append(std::move(tier));
981     }
982   }
983   if (!announceList.empty()) {
984     torrent += "13:announce-list";
985     torrent += bencode2::encode(&announceList);
986   }
987   torrent += "4:info";
988   torrent += metadata;
989   torrent += "e";
990   return torrent;
991 }
992 
torrent2Magnet(const TorrentAttribute * attrs)993 std::string torrent2Magnet(const TorrentAttribute* attrs)
994 {
995   std::string uri = "magnet:?";
996   if (!attrs->infoHash.empty()) {
997     uri += "xt=urn:btih:";
998     uri += util::toUpper(util::toHex(attrs->infoHash));
999   }
1000   else {
1001     return A2STR::NIL;
1002   }
1003   if (!attrs->name.empty()) {
1004     uri += "&dn=";
1005     uri += util::percentEncode(attrs->name);
1006   }
1007   for (auto& elem : attrs->announceList) {
1008     for (auto& e : elem) {
1009       uri += "&tr=";
1010       uri += util::percentEncode(e);
1011     }
1012   }
1013   return uri;
1014 }
1015 
getCompactLength(int family)1016 int getCompactLength(int family)
1017 {
1018   if (family == AF_INET) {
1019     return COMPACT_LEN_IPV4;
1020   }
1021   else if (family == AF_INET6) {
1022     return COMPACT_LEN_IPV6;
1023   }
1024   else {
1025     return 0;
1026   }
1027 }
1028 
removeAnnounceUri(TorrentAttribute * attrs,const std::vector<std::string> & uris)1029 void removeAnnounceUri(TorrentAttribute* attrs,
1030                        const std::vector<std::string>& uris)
1031 {
1032   if (uris.empty()) {
1033     return;
1034   }
1035   if (std::find(uris.begin(), uris.end(), "*") == uris.end()) {
1036     for (auto i = attrs->announceList.begin();
1037          i != attrs->announceList.end();) {
1038       for (auto j = (*i).begin(); j != (*i).end();) {
1039         if (std::find(uris.begin(), uris.end(), *j) == uris.end()) {
1040           ++j;
1041         }
1042         else {
1043           j = (*i).erase(j);
1044         }
1045       }
1046       if ((*i).empty()) {
1047         i = attrs->announceList.erase(i);
1048       }
1049       else {
1050         ++i;
1051       }
1052     }
1053   }
1054   else {
1055     attrs->announceList.clear();
1056   }
1057 }
1058 
addAnnounceUri(TorrentAttribute * attrs,const std::vector<std::string> & uris)1059 void addAnnounceUri(TorrentAttribute* attrs,
1060                     const std::vector<std::string>& uris)
1061 {
1062   for (auto& uri : uris) {
1063     std::vector<std::string> tier;
1064     tier.push_back(uri);
1065     attrs->announceList.push_back(tier);
1066   }
1067 }
1068 
adjustAnnounceUri(TorrentAttribute * attrs,const std::shared_ptr<Option> & option)1069 void adjustAnnounceUri(TorrentAttribute* attrs,
1070                        const std::shared_ptr<Option>& option)
1071 {
1072   std::vector<std::string> excludeUris;
1073   std::vector<std::string> addUris;
1074   const std::string& exTracker = option->get(PREF_BT_EXCLUDE_TRACKER);
1075   util::split(exTracker.begin(), exTracker.end(),
1076               std::back_inserter(excludeUris), ',', true);
1077   const std::string& btTracker = option->get(PREF_BT_TRACKER);
1078   util::split(btTracker.begin(), btTracker.end(), std::back_inserter(addUris),
1079               ',', true);
1080   removeAnnounceUri(attrs, excludeUris);
1081   addAnnounceUri(attrs, addUris);
1082 }
1083 
getModeString(BtFileMode mode)1084 const char* getModeString(BtFileMode mode)
1085 {
1086   switch (mode) {
1087   case BT_FILE_MODE_SINGLE:
1088     return "single";
1089   case BT_FILE_MODE_MULTI:
1090     return "multi";
1091   default:
1092     return "";
1093   }
1094 }
1095 
1096 } // namespace bittorrent
1097 
1098 } // namespace aria2
1099