00001
00002
00003
00004
00005
00006
00007
00008 #include <vector>
00009 #include <iostream>
00010 #include <cctype>
00011 #include <iomanip>
00012 #include <sstream>
00013 #include <algorithm>
00014
00015 #include "libtorrent/config.hpp"
00016 #include "zlib.h"
00017
00018 #ifdef _MSC_VER
00019 #pragma warning(push, 1)
00020 #endif
00021
00022 #include <boost/bind.hpp>
00023
00024 #ifdef _MSC_VER
00025 #pragma warning(pop)
00026 #endif
00027
00028 #include "libtorrent/tracker_manager.hpp"
00029 #include "libtorrent/http_tracker_connection.hpp"
00030 #include "libtorrent/entry.hpp"
00031 #include "libtorrent/bencode.hpp"
00032 #include "libtorrent/torrent.hpp"
00033 #include "libtorrent/io.hpp"
00034
00035 using namespace libtorrent;
00036 using boost::bind;
00037
00038 namespace
00039 {
00040 enum
00041 {
00042 minimum_tracker_response_length = 3,
00043 http_buffer_size = 2048
00044 };
00045
00046 enum
00047 {
00048 FTEXT = 0x01,
00049 FHCRC = 0x02,
00050 FEXTRA = 0x04,
00051 FNAME = 0x08,
00052 FCOMMENT = 0x10,
00053 FRESERVED = 0xe0,
00054
00055 GZIP_MAGIC0 = 0x1f,
00056 GZIP_MAGIC1 = 0x8b
00057 };
00058
00059 }
00060
00061 using namespace boost::posix_time;
00062
00063 namespace
00064 {
00065 bool url_has_argument(std::string const& url, std::string argument)
00066 {
00067 size_t i = url.find('?');
00068 if (i == std::string::npos) return false;
00069
00070 argument += '=';
00071
00072 if (url.compare(i + 1, argument.size(), argument) == 0) return true;
00073 argument.insert(0, "&");
00074 return url.find(argument, i)
00075 != std::string::npos;
00076 }
00077
00078 char to_lower(char c) { return std::tolower(c); }
00079 }
00080
00081 namespace libtorrent
00082 {
00083 http_parser::http_parser()
00084 : m_recv_pos(0)
00085 , m_status_code(-1)
00086 , m_content_length(-1)
00087 , m_state(read_status)
00088 , m_recv_buffer(0, 0)
00089 , m_body_start_pos(0)
00090 , m_finished(false)
00091 {}
00092
00093 boost::tuple<int, int> http_parser::incoming(buffer::const_interval recv_buffer)
00094 {
00095 assert(recv_buffer.left() >= m_recv_buffer.left());
00096 boost::tuple<int, int> ret(0, 0);
00097
00098
00099 if (recv_buffer.left() == m_recv_buffer.left()) return ret;
00100 m_recv_buffer = recv_buffer;
00101
00102 char const* pos = recv_buffer.begin + m_recv_pos;
00103 if (m_state == read_status)
00104 {
00105 assert(!m_finished);
00106 char const* newline = std::find(pos, recv_buffer.end, '\n');
00107
00108 if (newline == recv_buffer.end) return ret;
00109
00110 if (newline == pos)
00111 throw std::runtime_error("unexpected newline in HTTP response");
00112
00113 char const* line_end = newline;
00114 if (pos != line_end && *(line_end - 1) == '\r') --line_end;
00115
00116 std::istringstream line(std::string(pos, line_end));
00117 ++newline;
00118 int incoming = (int)std::distance(pos, newline);
00119 m_recv_pos += incoming;
00120 boost::get<1>(ret) += incoming;
00121 pos = newline;
00122
00123 line >> m_protocol;
00124 if (m_protocol.substr(0, 5) != "HTTP/")
00125 {
00126 throw std::runtime_error("unknown protocol in HTTP response: "
00127 + m_protocol + " line: " + std::string(pos, newline));
00128 }
00129 line >> m_status_code;
00130 std::getline(line, m_server_message);
00131 m_state = read_header;
00132 }
00133
00134 if (m_state == read_header)
00135 {
00136 assert(!m_finished);
00137 char const* newline = std::find(pos, recv_buffer.end, '\n');
00138 std::string line;
00139
00140 while (newline != recv_buffer.end && m_state == read_header)
00141 {
00142
00143
00144 char const* line_end = newline;
00145 if (pos != line_end && *(line_end - 1) == '\r') --line_end;
00146 line.assign(pos, line_end);
00147 m_recv_pos += newline - pos;
00148 boost::get<1>(ret) += newline - pos;
00149 pos = newline;
00150
00151 std::string::size_type separator = line.find(": ");
00152 if (separator == std::string::npos)
00153 {
00154
00155
00156
00157 ++pos;
00158 ++m_recv_pos;
00159 boost::get<1>(ret) += 1;
00160
00161 m_state = read_body;
00162 m_body_start_pos = m_recv_pos;
00163 break;
00164 }
00165
00166 std::string name = line.substr(0, separator);
00167 std::transform(name.begin(), name.end(), name.begin(), &to_lower);
00168 std::string value = line.substr(separator + 2, std::string::npos);
00169 m_header.insert(std::make_pair(name, value));
00170
00171 if (name == "content-length")
00172 {
00173 try
00174 {
00175 m_content_length = boost::lexical_cast<int>(value);
00176 }
00177 catch(boost::bad_lexical_cast&) {}
00178 }
00179 else if (name == "content-range")
00180 {
00181 std::stringstream range_str(value);
00182 char dummy;
00183 std::string bytes;
00184 size_type range_start, range_end;
00185 range_str >> bytes >> range_start >> dummy >> range_end;
00186 if (!range_str || range_end < range_start)
00187 {
00188 throw std::runtime_error("invalid content-range in HTTP response: " + range_str.str());
00189 }
00190
00191 m_content_length = range_end - range_start + 1;
00192 }
00193
00194
00195 ++pos;
00196 ++m_recv_pos;
00197 assert(m_recv_pos <= (int)recv_buffer.left());
00198 newline = std::find(pos, recv_buffer.end, '\n');
00199 }
00200 }
00201
00202 if (m_state == read_body)
00203 {
00204 int incoming = recv_buffer.end - pos;
00205 if (m_recv_pos - m_body_start_pos + incoming > m_content_length
00206 && m_content_length >= 0)
00207 incoming = m_content_length - m_recv_pos + m_body_start_pos;
00208
00209 assert(incoming >= 0);
00210 m_recv_pos += incoming;
00211 boost::get<0>(ret) += incoming;
00212
00213 if (m_content_length >= 0
00214 && m_recv_pos - m_body_start_pos >= m_content_length)
00215 {
00216 m_finished = true;
00217 }
00218 }
00219 return ret;
00220 }
00221
00222 buffer::const_interval http_parser::get_body() const
00223 {
00224 assert(m_state == read_body);
00225 if (m_content_length >= 0)
00226 return buffer::const_interval(m_recv_buffer.begin + m_body_start_pos
00227 , m_recv_buffer.begin + std::min(m_recv_pos
00228 , m_body_start_pos + m_content_length));
00229 else
00230 return buffer::const_interval(m_recv_buffer.begin + m_body_start_pos
00231 , m_recv_buffer.begin + m_recv_pos);
00232 }
00233
00234 void http_parser::reset()
00235 {
00236 m_recv_pos = 0;
00237 m_body_start_pos = 0;
00238 m_status_code = -1;
00239 m_content_length = -1;
00240 m_finished = false;
00241 m_state = read_status;
00242 m_recv_buffer.begin = 0;
00243 m_recv_buffer.end = 0;
00244 m_header.clear();
00245 }
00246
00247 http_tracker_connection::http_tracker_connection(
00248 asio::strand& str
00249 , tracker_manager& man
00250 , tracker_request const& req
00251 , std::string const& hostname
00252 , unsigned short port
00253 , std::string request
00254 , address bind_infc
00255 , boost::weak_ptr<request_callback> c
00256 , session_settings const& stn
00257 , std::string const& auth)
00258 : tracker_connection(man, req, str, bind_infc, c)
00259 , m_man(man)
00260 , m_strand(str)
00261 , m_name_lookup(m_strand.io_service())
00262 , m_port(port)
00263 , m_recv_pos(0)
00264 , m_buffer(http_buffer_size)
00265 , m_settings(stn)
00266 , m_password(auth)
00267 , m_timed_out(false)
00268 {
00269 const std::string* connect_to_host;
00270 bool using_proxy = false;
00271
00272 m_send_buffer.assign("GET ");
00273
00274
00275 if (!m_settings.proxy_ip.empty())
00276 {
00277 connect_to_host = &m_settings.proxy_ip;
00278 using_proxy = true;
00279 m_send_buffer += "http://";
00280 m_send_buffer += hostname;
00281 if (port != 80)
00282 {
00283 m_send_buffer += ":";
00284 m_send_buffer += boost::lexical_cast<std::string>(port);
00285 }
00286 m_port = m_settings.proxy_port != 0
00287 ? m_settings.proxy_port : 80 ;
00288 }
00289 else
00290 {
00291 connect_to_host = &hostname;
00292 }
00293
00294 if (tracker_req().kind == tracker_request::scrape_request)
00295 {
00296
00297
00298
00299 std::size_t pos = request.find("announce");
00300 if (pos == std::string::npos)
00301 throw std::runtime_error("scrape is not available on url: '"
00302 + tracker_req().url +"'");
00303 request.replace(pos, 8, "scrape");
00304 }
00305
00306 m_send_buffer += request;
00307
00308
00309
00310
00311 size_t arguments_start = request.find('?');
00312 if (arguments_start != std::string::npos)
00313 m_send_buffer += "&";
00314 else
00315 m_send_buffer += "?";
00316
00317 if (!url_has_argument(request, "info_hash"))
00318 {
00319 m_send_buffer += "info_hash=";
00320 m_send_buffer += escape_string(
00321 reinterpret_cast<const char*>(req.info_hash.begin()), 20);
00322 m_send_buffer += '&';
00323 }
00324
00325 if (tracker_req().kind == tracker_request::announce_request)
00326 {
00327 if (!url_has_argument(request, "peer_id"))
00328 {
00329 m_send_buffer += "peer_id=";
00330 m_send_buffer += escape_string(
00331 reinterpret_cast<const char*>(req.pid.begin()), 20);
00332 m_send_buffer += '&';
00333 }
00334
00335 if (!url_has_argument(request, "port"))
00336 {
00337 m_send_buffer += "port=";
00338 m_send_buffer += boost::lexical_cast<std::string>(req.listen_port);
00339 m_send_buffer += '&';
00340 }
00341
00342 if (!url_has_argument(request, "uploaded"))
00343 {
00344 m_send_buffer += "uploaded=";
00345 m_send_buffer += boost::lexical_cast<std::string>(req.uploaded);
00346 m_send_buffer += '&';
00347 }
00348
00349 if (!url_has_argument(request, "downloaded"))
00350 {
00351 m_send_buffer += "downloaded=";
00352 m_send_buffer += boost::lexical_cast<std::string>(req.downloaded);
00353 m_send_buffer += '&';
00354 }
00355
00356 if (!url_has_argument(request, "left"))
00357 {
00358 m_send_buffer += "left=";
00359 m_send_buffer += boost::lexical_cast<std::string>(req.left);
00360 m_send_buffer += '&';
00361 }
00362
00363 if (req.event != tracker_request::none)
00364 {
00365 if (!url_has_argument(request, "event"))
00366 {
00367 const char* event_string[] = {"completed", "started", "stopped"};
00368 m_send_buffer += "event=";
00369 m_send_buffer += event_string[req.event - 1];
00370 m_send_buffer += '&';
00371 }
00372 }
00373 if (!url_has_argument(request, "key"))
00374 {
00375 m_send_buffer += "key=";
00376 std::stringstream key_string;
00377 key_string << std::hex << req.key;
00378 m_send_buffer += key_string.str();
00379 m_send_buffer += '&';
00380 }
00381
00382 if (!url_has_argument(request, "compact"))
00383 {
00384 m_send_buffer += "compact=1&";
00385 }
00386 if (!url_has_argument(request, "numwant"))
00387 {
00388 m_send_buffer += "numwant=";
00389 m_send_buffer += boost::lexical_cast<std::string>(
00390 std::min(req.num_want, 999));
00391 m_send_buffer += '&';
00392 }
00393
00394
00395
00396 if (!url_has_argument(request, "no_peer_id"))
00397 {
00398 m_send_buffer += "no_peer_id=1";
00399 }
00400 else
00401 {
00402
00403 m_send_buffer.resize(m_send_buffer.size() - 1);
00404 }
00405 }
00406
00407 m_send_buffer += " HTTP/1.0\r\nAccept-Encoding: gzip\r\n"
00408 "User-Agent: ";
00409 m_send_buffer += m_settings.user_agent;
00410 m_send_buffer += "\r\n"
00411 "Host: ";
00412 m_send_buffer += hostname;
00413 if (port != 80)
00414 {
00415 m_send_buffer += ':';
00416 m_send_buffer += boost::lexical_cast<std::string>(port);
00417 }
00418 if (using_proxy && !m_settings.proxy_login.empty())
00419 {
00420 m_send_buffer += "\r\nProxy-Authorization: Basic ";
00421 m_send_buffer += base64encode(m_settings.proxy_login + ":" + m_settings.proxy_password);
00422 }
00423 if (auth != "")
00424 {
00425 m_send_buffer += "\r\nAuthorization: Basic ";
00426 m_send_buffer += base64encode(auth);
00427 }
00428 m_send_buffer += "\r\n\r\n";
00429
00430 #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
00431 if (has_requester())
00432 {
00433 requester().debug_log("==> TRACKER_REQUEST [ str: " + m_send_buffer + " ]");
00434 std::stringstream info_hash_str;
00435 info_hash_str << req.info_hash;
00436 requester().debug_log("info_hash: "
00437 + boost::lexical_cast<std::string>(req.info_hash));
00438 requester().debug_log("name lookup: " + *connect_to_host);
00439 }
00440 #endif
00441
00442 tcp::resolver::query q(*connect_to_host
00443 , boost::lexical_cast<std::string>(m_port));
00444 m_name_lookup.async_resolve(q, m_strand.wrap(
00445 boost::bind(&http_tracker_connection::name_lookup, self(), _1, _2)));
00446 set_timeout(m_settings.tracker_completion_timeout
00447 , m_settings.tracker_receive_timeout);
00448 }
00449
00450 void http_tracker_connection::on_timeout()
00451 {
00452 m_timed_out = true;
00453 m_socket.reset();
00454 m_name_lookup.cancel();
00455 fail_timeout();
00456 }
00457
00458 void http_tracker_connection::name_lookup(asio::error_code const& error
00459 , tcp::resolver::iterator i) try
00460 {
00461 #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
00462 if (has_requester()) requester().debug_log("tracker name lookup handler called");
00463 #endif
00464 if (error == asio::error::operation_aborted) return;
00465 if (m_timed_out) return;
00466
00467 if (error || i == tcp::resolver::iterator())
00468 {
00469 fail(-1, error.message().c_str());
00470 return;
00471 }
00472
00473 #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
00474 if (has_requester()) requester().debug_log("tracker name lookup successful");
00475 #endif
00476 restart_read_timeout();
00477
00478
00479
00480
00481 tcp::resolver::iterator target = i;
00482 tcp::resolver::iterator end;
00483 tcp::endpoint target_address = *i;
00484 for (; target != end && target->endpoint().address().is_v4()
00485 != bind_interface().is_v4(); ++target);
00486 if (target == end)
00487 {
00488 assert(target_address.address().is_v4() != bind_interface().is_v4());
00489 if (has_requester())
00490 {
00491 std::string tracker_address_type = target_address.address().is_v4() ? "IPv4" : "IPv6";
00492 std::string bind_address_type = bind_interface().is_v4() ? "IPv4" : "IPv6";
00493 requester().tracker_warning("the tracker only resolves to an "
00494 + tracker_address_type + " address, and you're listening on an "
00495 + bind_address_type + " socket. This may prevent you from receiving incoming connections.");
00496 }
00497 }
00498 else
00499 {
00500 target_address = *target;
00501 }
00502
00503 if (has_requester()) requester().m_tracker_address = target_address;
00504 m_socket.reset(new stream_socket(m_name_lookup.io_service()));
00505 m_socket->open(target_address.protocol());
00506 m_socket->bind(tcp::endpoint(bind_interface(), 0));
00507 m_socket->async_connect(target_address, bind(&http_tracker_connection::connected, self(), _1));
00508 }
00509 catch (std::exception& e)
00510 {
00511 assert(false);
00512 fail(-1, e.what());
00513 };
00514
00515 void http_tracker_connection::connected(asio::error_code const& error) try
00516 {
00517 if (error == asio::error::operation_aborted) return;
00518 if (m_timed_out) return;
00519 if (error)
00520 {
00521 fail(-1, error.message().c_str());
00522 return;
00523 }
00524
00525 #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
00526 if (has_requester()) requester().debug_log("tracker connection successful");
00527 #endif
00528
00529 restart_read_timeout();
00530 async_write(*m_socket, asio::buffer(m_send_buffer.c_str()
00531 , m_send_buffer.size()), bind(&http_tracker_connection::sent
00532 , self(), _1));
00533 }
00534 catch (std::exception& e)
00535 {
00536 assert(false);
00537 fail(-1, e.what());
00538 }
00539
00540 void http_tracker_connection::sent(asio::error_code const& error) try
00541 {
00542 if (error == asio::error::operation_aborted) return;
00543 if (m_timed_out) return;
00544 if (error)
00545 {
00546 fail(-1, error.message().c_str());
00547 return;
00548 }
00549
00550 #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
00551 if (has_requester()) requester().debug_log("tracker send data completed");
00552 #endif
00553 restart_read_timeout();
00554 assert(m_buffer.size() - m_recv_pos > 0);
00555 m_socket->async_read_some(asio::buffer(&m_buffer[m_recv_pos]
00556 , m_buffer.size() - m_recv_pos), bind(&http_tracker_connection::receive
00557 , self(), _1, _2));
00558 }
00559 catch (std::exception& e)
00560 {
00561 assert(false);
00562 fail(-1, e.what());
00563 };
00564
00565
00566 void http_tracker_connection::receive(asio::error_code const& error
00567 , std::size_t bytes_transferred) try
00568 {
00569 if (error == asio::error::operation_aborted) return;
00570 if (m_timed_out) return;
00571
00572 if (error)
00573 {
00574 if (error == asio::error::eof)
00575 {
00576 on_response();
00577 close();
00578 return;
00579 }
00580
00581 fail(-1, error.message().c_str());
00582 return;
00583 }
00584
00585 restart_read_timeout();
00586 assert(bytes_transferred > 0);
00587 #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
00588 if (has_requester()) requester().debug_log("tracker connection reading "
00589 + boost::lexical_cast<std::string>(bytes_transferred));
00590 #endif
00591
00592 m_recv_pos += bytes_transferred;
00593 m_parser.incoming(buffer::const_interval(&m_buffer[0]
00594 , &m_buffer[0] + m_recv_pos));
00595
00596
00597 if ((int)m_buffer.size() == m_recv_pos)
00598 {
00599 if ((int)m_buffer.size() >= m_settings.tracker_maximum_response_length)
00600 {
00601 fail(200, "too large tracker response");
00602 return;
00603 }
00604 assert(http_buffer_size > 0);
00605 if ((int)m_buffer.size() + http_buffer_size
00606 > m_settings.tracker_maximum_response_length)
00607 m_buffer.resize(m_settings.tracker_maximum_response_length);
00608 else
00609 m_buffer.resize(m_buffer.size() + http_buffer_size);
00610 }
00611
00612 if (m_parser.header_finished())
00613 {
00614 int cl = m_parser.header<int>("content-length");
00615 if (cl > m_settings.tracker_maximum_response_length)
00616 {
00617 fail(-1, "content-length is greater than maximum response length");
00618 return;
00619 }
00620
00621 if (cl > 0 && cl < minimum_tracker_response_length && m_parser.status_code() == 200)
00622 {
00623 fail(-1, "content-length is smaller than minimum response length");
00624 return;
00625 }
00626 }
00627
00628 if (m_parser.finished())
00629 {
00630 on_response();
00631 close();
00632 return;
00633 }
00634
00635 assert(m_buffer.size() - m_recv_pos > 0);
00636 m_socket->async_read_some(asio::buffer(&m_buffer[m_recv_pos]
00637 , m_buffer.size() - m_recv_pos), bind(&http_tracker_connection::receive
00638 , self(), _1, _2));
00639 }
00640 catch (std::exception& e)
00641 {
00642 fail(-1, e.what());
00643 };
00644
00645 void http_tracker_connection::on_response()
00646 {
00647 if (!m_parser.header_finished())
00648 {
00649 fail(-1, "premature end of file");
00650 return;
00651 }
00652
00653 std::string location = m_parser.header<std::string>("location");
00654
00655 if (m_parser.status_code() >= 300 && m_parser.status_code() < 400)
00656 {
00657 if (location.empty())
00658 {
00659 std::string error_str = "got redirection response (";
00660 error_str += boost::lexical_cast<std::string>(m_parser.status_code());
00661 error_str += ") without 'Location' header";
00662 fail(-1, error_str.c_str());
00663 return;
00664 }
00665
00666
00667 if (location.compare(0, 7, "http://") != 0
00668 && location.compare(0, 6, "udp://") != 0)
00669 {
00670 location.insert(0, "http://");
00671 }
00672
00673 #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
00674 if (has_requester()) requester().debug_log("Redirecting to \"" + location + "\"");
00675 #endif
00676 if (has_requester()) requester().tracker_warning("Redirecting to \"" + location + "\"");
00677 tracker_request req = tracker_req();
00678
00679 req.url = location;
00680
00681 m_man.queue_request(m_strand, req
00682 , m_password, bind_interface(), m_requester);
00683 close();
00684 return;
00685 }
00686
00687 buffer::const_interval buf(&m_buffer[0] + m_parser.body_start(), &m_buffer[0] + m_recv_pos);
00688
00689 std::string content_encoding = m_parser.header<std::string>("content-encoding");
00690
00691 #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
00692 if (has_requester()) requester().debug_log("content-encoding: \"" + content_encoding + "\"");
00693 #endif
00694
00695 if (content_encoding == "gzip" || content_encoding == "x-gzip")
00696 {
00697 boost::shared_ptr<request_callback> r = m_requester.lock();
00698
00699 if (!r)
00700 {
00701 close();
00702 return;
00703 }
00704 m_buffer.erase(m_buffer.begin(), m_buffer.begin() + m_parser.body_start());
00705 if (inflate_gzip(m_buffer, tracker_request(), r.get(),
00706 m_settings.tracker_maximum_response_length))
00707 {
00708 close();
00709 return;
00710 }
00711 buf.begin = &m_buffer[0];
00712 buf.end = &m_buffer[0] + m_buffer.size();
00713 }
00714 else if (!content_encoding.empty())
00715 {
00716 std::string error_str = "unknown content encoding in response: \"";
00717 error_str += content_encoding;
00718 error_str += "\"";
00719 fail(-1, error_str.c_str());
00720 return;
00721 }
00722
00723
00724 try
00725 {
00726 entry e = bdecode(buf.begin, buf.end);
00727 parse(e);
00728 }
00729 catch (std::exception& e)
00730 {
00731 std::string error_str(e.what());
00732 error_str += ": \"";
00733 for (char const* i = buf.begin, *end(buf.end); i != end; ++i)
00734 {
00735 if (std::isprint(*i)) error_str += *i;
00736 else error_str += "0x" + boost::lexical_cast<std::string>((unsigned int)*i) + " ";
00737 }
00738 error_str += "\"";
00739 fail(m_parser.status_code(), error_str.c_str());
00740 }
00741 #ifndef NDEBUG
00742 catch (...)
00743 {
00744 assert(false);
00745 }
00746 #endif
00747 }
00748
00749 peer_entry http_tracker_connection::extract_peer_info(const entry& info)
00750 {
00751 peer_entry ret;
00752
00753
00754 entry const* i = info.find_key("peer id");
00755 if (i != 0)
00756 {
00757 if (i->string().length() != 20)
00758 throw std::runtime_error("invalid response from tracker");
00759 std::copy(i->string().begin(), i->string().end(), ret.pid.begin());
00760 }
00761 else
00762 {
00763
00764 std::fill_n(ret.pid.begin(), 20, 0);
00765 }
00766
00767
00768 i = info.find_key("ip");
00769 if (i == 0) throw std::runtime_error("invalid response from tracker");
00770 ret.ip = i->string();
00771
00772
00773 i = info.find_key("port");
00774 if (i == 0) throw std::runtime_error("invalid response from tracker");
00775 ret.port = (unsigned short)i->integer();
00776
00777 return ret;
00778 }
00779
00780 void http_tracker_connection::parse(entry const& e)
00781 {
00782 if (!has_requester()) return;
00783
00784 try
00785 {
00786
00787 try
00788 {
00789 entry const& failure = e["failure reason"];
00790
00791 fail(m_parser.status_code(), failure.string().c_str());
00792 return;
00793 }
00794 catch (type_error const&) {}
00795
00796 try
00797 {
00798 entry const& warning = e["warning message"];
00799 if (has_requester())
00800 requester().tracker_warning(warning.string());
00801 }
00802 catch(type_error const&) {}
00803
00804 std::vector<peer_entry> peer_list;
00805
00806 if (tracker_req().kind == tracker_request::scrape_request)
00807 {
00808 std::string ih;
00809 std::copy(tracker_req().info_hash.begin(), tracker_req().info_hash.end()
00810 , std::back_inserter(ih));
00811 entry scrape_data = e["files"][ih];
00812 int complete = scrape_data["complete"].integer();
00813 int incomplete = scrape_data["incomplete"].integer();
00814 requester().tracker_response(tracker_request(), peer_list, 0, complete
00815 , incomplete);
00816 return;
00817 }
00818
00819 int interval = (int)e["interval"].integer();
00820
00821 if (e["peers"].type() == entry::string_t)
00822 {
00823 std::string const& peers = e["peers"].string();
00824 for (std::string::const_iterator i = peers.begin();
00825 i != peers.end();)
00826 {
00827 if (std::distance(i, peers.end()) < 6) break;
00828
00829 peer_entry p;
00830 p.pid.clear();
00831 std::stringstream ip_str;
00832 ip_str << (int)detail::read_uint8(i) << ".";
00833 ip_str << (int)detail::read_uint8(i) << ".";
00834 ip_str << (int)detail::read_uint8(i) << ".";
00835 ip_str << (int)detail::read_uint8(i);
00836 p.ip = ip_str.str();
00837 p.port = detail::read_uint16(i);
00838 peer_list.push_back(p);
00839 }
00840 }
00841 else
00842 {
00843 entry::list_type const& l = e["peers"].list();
00844 for(entry::list_type::const_iterator i = l.begin(); i != l.end(); ++i)
00845 {
00846 peer_entry p = extract_peer_info(*i);
00847 peer_list.push_back(p);
00848 }
00849 }
00850
00851
00852 int complete = -1;
00853 int incomplete = -1;
00854
00855 try { complete = e["complete"].integer(); }
00856 catch(type_error&) {}
00857
00858 try { incomplete = e["incomplete"].integer(); }
00859 catch(type_error&) {}
00860
00861 requester().tracker_response(tracker_request(), peer_list, interval, complete
00862 , incomplete);
00863 }
00864 catch(type_error& e)
00865 {
00866 requester().tracker_request_error(tracker_request(), m_parser.status_code(), e.what());
00867 }
00868 catch(std::runtime_error& e)
00869 {
00870 requester().tracker_request_error(tracker_request(), m_parser.status_code(), e.what());
00871 }
00872 }
00873
00874 }
00875