void i2p_connection::open(std::string const& s, int port , i2p_stream::handler_type const& handler) { // we already seem to have a session to this SAM router if (m_hostname == s && m_port == port && m_sam_socket && (is_open() || m_state == sam_connecting)) return; m_hostname = s; m_port = port; if (m_hostname.empty()) return; m_state = sam_connecting; char tmp[20]; std::generate(tmp, tmp + sizeof(tmp), &std::rand); m_session_id.resize(sizeof(tmp)*2); to_hex(tmp, 20, &m_session_id[0]); m_sam_socket.reset(new i2p_stream(m_io_service)); m_sam_socket->set_proxy(m_hostname, m_port); m_sam_socket->set_command(i2p_stream::cmd_create_session); m_sam_socket->set_session_id(m_session_id.c_str()); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("i2p_stream::on_sam_connect"); #endif m_sam_socket->async_connect(tcp::endpoint() , boost::bind(&i2p_connection::on_sam_connect, this, _1, handler, m_sam_socket)); }
void timeout_handler::set_timeout(int completion_timeout, int read_timeout) { m_completion_timeout = completion_timeout; m_read_timeout = read_timeout; m_start_time = m_read_time = time_now_hires(); TORRENT_ASSERT(completion_timeout > 0 || read_timeout > 0); if (m_abort) return; int timeout = 0; if (m_read_timeout > 0) timeout = m_read_timeout; if (m_completion_timeout > 0) { timeout = timeout == 0 ? m_completion_timeout : (std::min)(m_completion_timeout, timeout); } #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("timeout_handler::timeout_callback"); #endif error_code ec; m_timeout.expires_at(m_read_time + seconds(timeout), ec); m_timeout.async_wait(boost::bind( &timeout_handler::timeout_callback, self(), _1)); }
void i2p_stream::send_name_lookup(boost::shared_ptr<handler_type> h) { TORRENT_ASSERT(m_magic == 0x1337); m_state = read_name_lookup_response; char cmd[1024]; int size = snprintf(cmd, sizeof(cmd), "NAMING LOOKUP NAME=%s\n", m_name_lookup.c_str()); // fprintf(stderr, ">>> %s", cmd); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("i2p_stream::start_read_line"); #endif async_write(m_sock, boost::asio::buffer(cmd, size) , boost::bind(&i2p_stream::start_read_line, this, _1, h)); }
void i2p_stream::send_accept(boost::shared_ptr<handler_type> h) { TORRENT_ASSERT(m_magic == 0x1337); m_state = read_accept_response; char cmd[400]; int size = snprintf(cmd, sizeof(cmd), "STREAM ACCEPT ID=%s\n", m_id); // fprintf(stderr, ">>> %s", cmd); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("i2p_stream::start_read_line"); #endif async_write(m_sock, boost::asio::buffer(cmd, size) , boost::bind(&i2p_stream::start_read_line, this, _1, h)); }
void i2p_stream::send_session_create(boost::shared_ptr<handler_type> h) { TORRENT_ASSERT(m_magic == 0x1337); m_state = read_session_create_response; char cmd[400]; int size = snprintf(cmd, sizeof(cmd), "SESSION CREATE STYLE=STREAM ID=%s DESTINATION=TRANSIENT\n" , m_id); // fprintf(stderr, ">>> %s", cmd); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("i2p_stream::start_read_line"); #endif async_write(m_sock, boost::asio::buffer(cmd, size) , boost::bind(&i2p_stream::start_read_line, this, _1, h)); }
void i2p_stream::send_connect(boost::shared_ptr<handler_type> h) { TORRENT_ASSERT(m_magic == 0x1337); m_state = read_connect_response; char cmd[1024]; int size = snprintf(cmd, sizeof(cmd), "STREAM CONNECT ID=%s DESTINATION=%s\n" , m_id, m_dest.c_str()); // fprintf(stderr, ">>> %s", cmd); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("i2p_stream::start_read_line"); #endif async_write(m_sock, asio::buffer(cmd, size) , boost::bind(&i2p_stream::start_read_line, this, _1, h)); }
void i2p_stream::start_read_line(error_code const& e, boost::shared_ptr<handler_type> h) { TORRENT_ASSERT(m_magic == 0x1337); #if defined TORRENT_ASIO_DEBUGGING complete_async("i2p_stream::start_read_line"); #endif if (handle_error(e, h)) return; #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("i2p_stream::read_line"); #endif m_buffer.resize(1); async_read(m_sock, boost::asio::buffer(m_buffer) , boost::bind(&i2p_stream::read_line, this, _1, h)); }
void resolver::async_resolve(std::string const& host, int flags , resolver_interface::callback_t const& h) { cache_t::iterator i = m_cache.find(host); if (i != m_cache.end()) { // keep cache entries valid for m_timeout seconds if ((flags & resolver_interface::prefer_cache) || i->second.last_seen + m_timeout >= aux::time_now()) { error_code ec; m_ios.post(boost::bind(h, ec, i->second.addresses)); return; } } // special handling for raw IP addresses. There's no need to get in line // behind actual lookups if we can just resolve it immediately. error_code ec; address ip = address::from_string(host.c_str(), ec); if (!ec) { std::vector<address> addresses; addresses.push_back(ip); m_ios.post(boost::bind(h, ec, addresses)); return; } // the port is ignored tcp::resolver::query q(host, "80"); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("resolver::on_lookup"); #endif if (flags & resolver_interface::abort_on_shutdown) { m_resolver.async_resolve(q, boost::bind(&resolver::on_lookup, this, _1, _2 , h, host)); } else { m_critical_resolver.async_resolve(q, boost::bind(&resolver::on_lookup, this, _1, _2 , h, host)); } }
void i2p_stream::do_connect(error_code const& e, tcp::resolver::iterator i , boost::shared_ptr<handler_type> h) { TORRENT_ASSERT(m_magic == 0x1337); if (e || i == tcp::resolver::iterator()) { (*h)(e); error_code ec; close(ec); return; } #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("i2p_stream::connected"); #endif m_sock.async_connect(i->endpoint(), boost::bind( &i2p_stream::connected, this, _1, h)); }
void i2p_stream::connected(error_code const& e, boost::shared_ptr<handler_type> h) { TORRENT_ASSERT(m_magic == 0x1337); #if defined TORRENT_ASIO_DEBUGGING complete_async("i2p_stream::connected"); #endif if (handle_error(e, h)) return; // send hello command m_state = read_hello_response; static const char cmd[] = "HELLO VERSION MIN=3.0 MAX=3.0\n"; #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("i2p_stream::start_read_line"); #endif async_write(m_sock, boost::asio::buffer(cmd, sizeof(cmd) - 1) , boost::bind(&i2p_stream::start_read_line, this, _1, h)); // fprintf(stderr, ">>> %s", cmd); }
void upnp::discover_device_impl(mutex::scoped_lock& l) { const char msearch[] = "M-SEARCH * HTTP/1.1\r\n" "HOST: 239.255.255.250:1900\r\n" "ST:upnp:rootdevice\r\n" "MAN:\"ssdp:discover\"\r\n" "MX:3\r\n" "\r\n\r\n"; error_code ec; #ifdef TORRENT_DEBUG_UPNP // simulate packet loss if (m_retry_count & 1) #endif m_socket.send(msearch, sizeof(msearch) - 1, ec); if (ec) { char msg[500]; snprintf(msg, sizeof(msg), "broadcast failed: %s. Aborting." , convert_from_native(ec.message()).c_str()); log(msg, l); disable(ec, l); return; } #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("upnp::resend_request"); #endif ++m_retry_count; m_broadcast_timer.expires_from_now(seconds(2 * m_retry_count), ec); m_broadcast_timer.async_wait(boost::bind(&upnp::resend_request , self(), _1)); log("broadcasting search for rootdevice", l); }
void timeout_handler::timeout_callback(error_code const& error) { #if defined TORRENT_ASIO_DEBUGGING complete_async("timeout_handler::timeout_callback"); #endif if (m_abort) return; ptime now = time_now_hires(); time_duration receive_timeout = now - m_read_time; time_duration completion_timeout = now - m_start_time; if ((m_read_timeout && m_read_timeout <= total_seconds(receive_timeout)) || (m_completion_timeout && m_completion_timeout <= total_seconds(completion_timeout)) || error) { on_timeout(error); return; } int timeout = 0; if (m_read_timeout > 0) timeout = m_read_timeout; if (m_completion_timeout > 0) { timeout = timeout == 0 ? int(m_completion_timeout - total_seconds(m_read_time - m_start_time)) : (std::min)(int(m_completion_timeout - total_seconds(m_read_time - m_start_time)), timeout); } #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("timeout_handler::timeout_callback"); #endif error_code ec; m_timeout.expires_at(m_read_time + seconds(timeout), ec); m_timeout.async_wait( boost::bind(&timeout_handler::timeout_callback, self(), _1)); }
void async_connect(endpoint_type const& endpoint, Handler const& handler) { m_remote_endpoint = endpoint; // the connect is split up in the following steps: // 1. resolve name of proxy server // 2. connect to proxy server // 3. if version == 5: // 3.1 send SOCKS5 authentication method message // 3.2 read SOCKS5 authentication response // 3.3 send username+password // 4. send SOCKS command message // to avoid unnecessary copying of the handler, // store it in a shaed_ptr boost::shared_ptr<handler_type> h(new handler_type(handler)); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("socks5_stream::name_lookup"); #endif tcp::resolver::query q(m_hostname, to_string(m_port).elems); m_resolver.async_resolve(q, boost::bind( &socks5_stream::name_lookup, this, _1, _2, h)); }
void connection_queue::on_timeout(error_code const& e) { #if defined TORRENT_ASIO_DEBUGGING complete_async("connection_queue::on_timeout"); #endif mutex_t::scoped_lock l(m_mutex); --m_num_timers; INVARIANT_CHECK; #ifdef TORRENT_DEBUG function_guard guard_(m_in_timeout_function); #endif TORRENT_ASSERT(!e || e == error::operation_aborted); // if there was an error, it's most likely operation aborted, // we should just quit. However, in case there are still connections // in connecting state, and there are no other timer invocations // we need to stick around still. if (e && (m_num_connecting == 0 || m_num_timers > 0)) return; ptime next_expire = max_time(); ptime now = time_now_hires() + milliseconds(100); std::list<entry> timed_out; for (std::list<entry>::iterator i = m_queue.begin(); !m_queue.empty() && i != m_queue.end();) { if (i->connecting && i->expires < now) { std::list<entry>::iterator j = i; ++i; timed_out.splice(timed_out.end(), m_queue, j, i); --m_num_connecting; continue; } if (i->connecting && i->expires < next_expire) next_expire = i->expires; ++i; } // we don't want to call the timeout callback while we're locked // since that is a recepie for dead-locks l.unlock(); for (std::list<entry>::iterator i = timed_out.begin() , end(timed_out.end()); i != end; ++i) { TORRENT_ASSERT(i->connecting); TORRENT_ASSERT(i->ticket != -1); TORRENT_TRY { i->on_timeout(); } TORRENT_CATCH(std::exception&) {} } l.lock(); if (next_expire < max_time()) { #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("connection_queue::on_timeout"); #endif error_code ec; m_timer.expires_at(next_expire, ec); m_timer.async_wait(boost::bind(&connection_queue::on_timeout, this, _1)); ++m_num_timers; } try_connect(l); }
void connection_queue::try_connect(connection_queue::mutex_t::scoped_lock& l) { INVARIANT_CHECK; #ifdef TORRENT_CONNECTION_LOGGING m_log << log_time() << " " << free_slots() << std::endl; #endif // if this is enabled, UPnP connections will be blocked when shutting down // if (m_abort) return; if (m_num_connecting >= m_half_open_limit && m_half_open_limit > 0) return; if (m_queue.empty()) { error_code ec; m_timer.cancel(ec); return; } // all entries are connecting, no need to look for new ones if (m_queue.size() == m_num_connecting) return; std::list<entry>::iterator i = std::find_if(m_queue.begin() , m_queue.end(), boost::bind(&entry::connecting, _1) == false); std::list<entry> to_connect; while (i != m_queue.end()) { TORRENT_ASSERT(i->connecting == false); ptime expire = time_now_hires() + i->timeout; if (m_num_connecting == 0) { #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("connection_queue::on_timeout"); #endif error_code ec; m_timer.expires_at(expire, ec); m_timer.async_wait(boost::bind(&connection_queue::on_timeout, this, _1)); ++m_num_timers; } i->connecting = true; ++m_num_connecting; i->expires = expire; INVARIANT_CHECK; to_connect.push_back(*i); #ifdef TORRENT_CONNECTION_LOGGING m_log << log_time() << " " << free_slots() << std::endl; #endif if (m_num_connecting >= m_half_open_limit && m_half_open_limit > 0) break; if (m_num_connecting == m_queue.size()) break; i = std::find_if(i, m_queue.end(), boost::bind(&entry::connecting, _1) == false); } l.unlock(); while (!to_connect.empty()) { entry& ent = to_connect.front(); TORRENT_TRY { ent.on_connect(ent.ticket); } TORRENT_CATCH(std::exception&) {} to_connect.pop_front(); } }
void i2p_stream::read_line(error_code const& e, boost::shared_ptr<handler_type> h) { TORRENT_ASSERT(m_magic == 0x1337); #if defined TORRENT_ASIO_DEBUGGING complete_async("i2p_stream::read_line"); #endif if (handle_error(e, h)) return; int read_pos = m_buffer.size(); // look for \n which means end of the response if (m_buffer[read_pos - 1] != '\n') { #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("i2p_stream::read_line"); #endif // read another byte from the socket m_buffer.resize(read_pos + 1); async_read(m_sock, boost::asio::buffer(&m_buffer[read_pos], 1) , boost::bind(&i2p_stream::read_line, this, _1, h)); return; } m_buffer[read_pos - 1] = 0; if (m_command == cmd_incoming) { // this is the line containing the destination // of the incoming connection in an accept call m_dest = &m_buffer[0]; (*h)(e); std::vector<char>().swap(m_buffer); return; } error_code invalid_response(i2p_error::parse_failed , get_i2p_category()); // null-terminate the string and parse it m_buffer.push_back(0); char* ptr = &m_buffer[0]; char* next = ptr; char const* expect1 = 0; char const* expect2 = 0; switch (m_state) { case read_hello_response: expect1 = "HELLO"; expect2 = "REPLY"; break; case read_connect_response: case read_accept_response: expect1 = "STREAM"; expect2 = "STATUS"; break; case read_session_create_response: expect1 = "SESSION"; expect2 = "STATUS"; break; case read_name_lookup_response: expect1 = "NAMING"; expect2 = "REPLY"; break; } // fprintf(stderr, "<<< %s\n", &m_buffer[0]); ptr = string_tokenize(next, ' ', &next); if (ptr == 0 || expect1 == 0 || strcmp(expect1, ptr)) { handle_error(invalid_response, h); return; } ptr = string_tokenize(next, ' ', &next); if (ptr == 0 || expect2 == 0 || strcmp(expect2, ptr)) { handle_error(invalid_response, h); return; } int result = 0; // char const* message = 0; // float version = 3.0f; for(;;) { char* name = string_tokenize(next, '=', &next); if (name == 0) break; // fprintf(stderr, "name=\"%s\"\n", name); char* ptr2 = string_tokenize(next, ' ', &next); if (ptr2 == 0) { handle_error(invalid_response, h); return; } // fprintf(stderr, "value=\"%s\"\n", ptr2); if (strcmp("RESULT", name) == 0) { if (strcmp("OK", ptr2) == 0) result = i2p_error::no_error; else if (strcmp("CANT_REACH_PEER", ptr2) == 0) result = i2p_error::cant_reach_peer; else if (strcmp("I2P_ERROR", ptr2) == 0) result = i2p_error::i2p_error; else if (strcmp("INVALID_KEY", ptr2) == 0) result = i2p_error::invalid_key; else if (strcmp("INVALID_ID", ptr2) == 0) result = i2p_error::invalid_id; else if (strcmp("TIMEOUT", ptr2) == 0) result = i2p_error::timeout; else if (strcmp("KEY_NOT_FOUND", ptr2) == 0) result = i2p_error::key_not_found; else if (strcmp("DUPLICATED_ID", ptr2) == 0) result = i2p_error::duplicated_id; else result = i2p_error::num_errors; // unknown error } else if (strcmp("MESSAGE", name) == 0) { // message = ptr2; } else if (strcmp("VERSION", name) == 0) { // version = float(atof(ptr2)); } else if (strcmp("VALUE", name) == 0) { m_name_lookup = ptr2; } else if (strcmp("DESTINATION", name) == 0) { m_dest = ptr2; } } error_code ec(result, get_i2p_category()); switch (result) { case i2p_error::no_error: case i2p_error::invalid_key: break; default: { handle_error (ec, h); return; } } switch (m_state) { case read_hello_response: switch (m_command) { case cmd_create_session: send_session_create(h); break; case cmd_accept: send_accept(h); break; case cmd_connect: send_connect(h); break; default: (*h)(e); std::vector<char>().swap(m_buffer); } break; case read_connect_response: case read_session_create_response: case read_name_lookup_response: (*h)(ec); std::vector<char>().swap(m_buffer); break; case read_accept_response: // the SAM bridge is waiting for an incoming // connection. // wait for one more line containing // the destination of the remote peer m_command = cmd_incoming; m_buffer.resize(1); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("i2p_stream::read_line"); #endif async_read(m_sock, boost::asio::buffer(m_buffer) , boost::bind(&i2p_stream::read_line, this, _1, h)); break; } return; }