/ libi2pd_client / I2PTunnel.cpp
I2PTunnel.cpp
1 /* 2 * Copyright (c) 2013-2025, The PurpleI2P Project 3 * 4 * This file is part of Purple i2pd project and licensed under BSD3 5 * 6 * See full license text in LICENSE file at top of project tree 7 */ 8 9 #include <cassert> 10 #include <boost/algorithm/string.hpp> 11 #include "Base.h" 12 #include "Log.h" 13 #include "Destination.h" 14 #include "ClientContext.h" 15 #include "I2PTunnel.h" 16 #include "util.h" 17 18 namespace i2p 19 { 20 namespace client 21 { 22 23 /** set standard socket options */ 24 static void I2PTunnelSetSocketOptions (std::shared_ptr<boost::asio::ip::tcp::socket> socket) 25 { 26 if (socket && socket->is_open()) 27 { 28 boost::asio::socket_base::receive_buffer_size option(I2P_TUNNEL_CONNECTION_BUFFER_SIZE); 29 socket->set_option(option); 30 } 31 } 32 33 I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket, 34 std::shared_ptr<const i2p::data::LeaseSet> leaseSet, uint16_t port): 35 I2PServiceHandler(owner), m_Socket (socket), m_RemoteEndpoint (socket->remote_endpoint ()), 36 m_IsReceiving (false) 37 { 38 m_Stream = GetOwner()->GetLocalDestination ()->CreateStream (leaseSet, port); 39 } 40 41 I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, 42 std::shared_ptr<boost::asio::ip::tcp::socket> socket, std::shared_ptr<i2p::stream::Stream> stream): 43 I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream), 44 m_RemoteEndpoint (socket->remote_endpoint ()), m_IsReceiving (false) 45 { 46 } 47 48 I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream, 49 const boost::asio::ip::tcp::endpoint& target,std::shared_ptr<boost::asio::ssl::context> sslCtx): 50 I2PServiceHandler(owner), m_Stream (stream), m_RemoteEndpoint (target), m_IsReceiving (false) 51 { 52 m_Socket = std::make_shared<boost::asio::ip::tcp::socket> (owner->GetService ()); 53 if (sslCtx) 54 m_SSL = std::make_shared<boost::asio::ssl::stream<boost::asio::ip::tcp::socket&> > (*m_Socket, *sslCtx); 55 } 56 57 I2PTunnelConnection::~I2PTunnelConnection () 58 { 59 } 60 61 void I2PTunnelConnection::I2PConnect (const uint8_t * msg, size_t len) 62 { 63 if (m_Stream) 64 { 65 if (msg) 66 m_Stream->Send (msg, len); // connect and send 67 else 68 m_Stream->Send (m_Buffer, 0); // connect 69 } 70 StreamReceive (); 71 Receive (); 72 } 73 74 boost::asio::ip::address GetLoopbackAddressFor(const i2p::data::IdentHash & addr) 75 { 76 boost::asio::ip::address_v4::bytes_type bytes; 77 const uint8_t * ident = addr; 78 bytes[0] = 127; 79 memcpy (bytes.data ()+1, ident, 3); 80 boost::asio::ip::address ourIP = boost::asio::ip::address_v4 (bytes); 81 return ourIP; 82 } 83 84 boost::asio::ip::address GetLoopbackAddress6For(const i2p::data::IdentHash & addr) 85 { 86 boost::asio::ip::address_v6::bytes_type bytes; 87 const uint8_t * ident = addr; 88 bytes[0] = 0xfd; 89 memcpy (bytes.data ()+1, ident, 15); 90 boost::asio::ip::address ourIP = boost::asio::ip::address_v6 (bytes); 91 return ourIP; 92 } 93 94 #ifdef __linux__ 95 static void MapToLoopback(std::shared_ptr<boost::asio::ip::tcp::socket> sock, const i2p::data::IdentHash & addr, bool isV4) 96 { 97 if (sock) 98 { 99 // bind to 127.x.x.x address for ipv4 100 // where x.x.x are first three bytes from ident 101 // bind to fdxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx address for ipv6 102 // where xx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx are first 15 bytes from ident 103 auto ourIP = isV4 ? GetLoopbackAddressFor(addr) : GetLoopbackAddress6For(addr); 104 boost::system::error_code ec; 105 sock->bind (boost::asio::ip::tcp::endpoint (ourIP, 0), ec); 106 if (ec) 107 LogPrint (eLogError, "I2PTunnel: Can't bind ourIP to ", ourIP.to_string (), ": ", ec.message ()); 108 } 109 } 110 #endif 111 112 void I2PTunnelConnection::Connect (bool isUniqueLocal) 113 { 114 if (m_Socket) 115 { 116 I2PTunnelSetSocketOptions (m_Socket); 117 #ifdef __linux__ 118 if (isUniqueLocal && m_RemoteEndpoint.address ().is_loopback ()) 119 { 120 auto ident = m_Stream->GetRemoteIdentity()->GetIdentHash(); 121 if (m_RemoteEndpoint.address ().is_v4 ()) 122 { 123 m_Socket->open (boost::asio::ip::tcp::v4 ()); 124 MapToLoopback(m_Socket, ident, true); 125 } 126 else if (m_RemoteEndpoint.address ().is_v6 ()) 127 { 128 m_Socket->open (boost::asio::ip::tcp::v6 ()); 129 MapToLoopback(m_Socket, ident, false); 130 } 131 } 132 #endif 133 m_Socket->async_connect (m_RemoteEndpoint, std::bind (&I2PTunnelConnection::HandleConnect, 134 shared_from_this (), std::placeholders::_1)); 135 } 136 } 137 138 void I2PTunnelConnection::Connect (const boost::asio::ip::address& localAddress) 139 { 140 if (m_Socket) 141 { 142 if (m_RemoteEndpoint.address().is_v6 ()) 143 m_Socket->open (boost::asio::ip::tcp::v6 ()); 144 else 145 m_Socket->open (boost::asio::ip::tcp::v4 ()); 146 boost::system::error_code ec; 147 m_Socket->bind (boost::asio::ip::tcp::endpoint (localAddress, 0), ec); 148 if (ec) 149 LogPrint (eLogError, "I2PTunnel: Can't bind to ", localAddress.to_string (), ": ", ec.message ()); 150 } 151 Connect (false); 152 } 153 154 void I2PTunnelConnection::Terminate () 155 { 156 if (Kill()) return; 157 if (m_SSL) m_SSL = nullptr; 158 if (m_Stream) 159 { 160 m_Stream->Close (); 161 m_Stream.reset (); 162 } 163 boost::system::error_code ec; 164 m_Socket->shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec); // avoid RST 165 m_Socket->close (); 166 167 Done(shared_from_this ()); 168 } 169 170 void I2PTunnelConnection::Receive () 171 { 172 if (m_IsReceiving) return; // already receiving 173 size_t bufSize = I2P_TUNNEL_CONNECTION_BUFFER_SIZE; 174 size_t unsentSize = m_Stream ? m_Stream->GetSendBufferSize () : 0; 175 if (unsentSize) 176 { 177 if (unsentSize >= I2P_TUNNEL_CONNECTION_STREAM_MAX_SEND_BUFFER_SIZE) return; // buffer is full 178 if (unsentSize > I2P_TUNNEL_CONNECTION_STREAM_MAX_SEND_BUFFER_SIZE - I2P_TUNNEL_CONNECTION_BUFFER_SIZE) 179 bufSize = I2P_TUNNEL_CONNECTION_STREAM_MAX_SEND_BUFFER_SIZE - unsentSize; 180 } 181 m_IsReceiving = true; 182 if (m_SSL) 183 m_SSL->async_read_some (boost::asio::buffer(m_Buffer, bufSize), 184 std::bind(&I2PTunnelConnection::HandleReceive, shared_from_this (), 185 std::placeholders::_1, std::placeholders::_2)); 186 else 187 m_Socket->async_read_some (boost::asio::buffer(m_Buffer, bufSize), 188 std::bind(&I2PTunnelConnection::HandleReceive, shared_from_this (), 189 std::placeholders::_1, std::placeholders::_2)); 190 } 191 192 void I2PTunnelConnection::HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) 193 { 194 m_IsReceiving = false; 195 if (ecode) 196 { 197 if (ecode != boost::asio::error::operation_aborted) 198 { 199 LogPrint (eLogError, "I2PTunnel: Read error: ", ecode.message ()); 200 Terminate (); 201 } 202 } 203 else 204 { 205 if (bytes_transferred < I2P_TUNNEL_CONNECTION_BUFFER_SIZE && !m_SSL) 206 { 207 boost::system::error_code ec; 208 size_t moreBytes = m_Socket->available(ec); 209 if (!ec && moreBytes && m_Stream) 210 { 211 // read more data from socket before sending to stream 212 if (bytes_transferred + moreBytes > I2P_TUNNEL_CONNECTION_BUFFER_SIZE) 213 moreBytes = I2P_TUNNEL_CONNECTION_BUFFER_SIZE - bytes_transferred; 214 if (m_Stream->GetSendBufferSize () < I2P_TUNNEL_CONNECTION_STREAM_MAX_SEND_BUFFER_SIZE) 215 { 216 size_t remaining = I2P_TUNNEL_CONNECTION_STREAM_MAX_SEND_BUFFER_SIZE - m_Stream->GetSendBufferSize (); 217 if (remaining < moreBytes) moreBytes = remaining; 218 } 219 else 220 moreBytes = 0; 221 } 222 if (moreBytes) 223 { 224 moreBytes = boost::asio::read (*m_Socket, boost::asio::buffer(m_Buffer + bytes_transferred, moreBytes), boost::asio::transfer_all (), ec); 225 if (!ec) bytes_transferred += moreBytes; 226 } 227 } 228 WriteToStream (m_Buffer, bytes_transferred); 229 Receive (); // try to receive more while being sent to stream 230 } 231 } 232 233 void I2PTunnelConnection::WriteToStream (const uint8_t * buf, size_t len) 234 { 235 if (m_Stream) 236 { 237 m_Stream->AsyncSend (buf, len, 238 [s = shared_from_this ()](const boost::system::error_code& ecode) 239 { 240 if (!ecode) 241 s->Receive (); 242 else 243 s->Terminate (); 244 }); 245 } 246 } 247 248 void I2PTunnelConnection::HandleWrite (const boost::system::error_code& ecode) 249 { 250 if (ecode) 251 { 252 LogPrint (eLogError, "I2PTunnel: Write error: ", ecode.message ()); 253 if (ecode != boost::asio::error::operation_aborted) 254 Terminate (); 255 } 256 else 257 StreamReceive (); 258 } 259 260 void I2PTunnelConnection::StreamReceive () 261 { 262 if (m_Stream) 263 { 264 if (m_Stream->GetStatus () == i2p::stream::eStreamStatusNew || 265 m_Stream->GetStatus () == i2p::stream::eStreamStatusOpen) // regular 266 { 267 m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, I2P_TUNNEL_CONNECTION_STREAM_BUFFER_SIZE), 268 std::bind (&I2PTunnelConnection::HandleStreamReceive, shared_from_this (), 269 std::placeholders::_1, std::placeholders::_2), 270 I2P_TUNNEL_CONNECTION_MAX_IDLE); 271 } 272 else // closed by peer 273 { 274 // get remaining data 275 auto len = m_Stream->ReadSome (m_StreamBuffer, I2P_TUNNEL_CONNECTION_STREAM_BUFFER_SIZE); 276 if (len > 0) // still some data 277 Write (m_StreamBuffer, len); 278 else // no more data 279 Terminate (); 280 } 281 } 282 } 283 284 void I2PTunnelConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) 285 { 286 if (ecode) 287 { 288 if (ecode != boost::asio::error::operation_aborted) 289 { 290 LogPrint (eLogError, "I2PTunnel: Stream read error: ", ecode.message ()); 291 if (bytes_transferred > 0) 292 Write (m_StreamBuffer, bytes_transferred); // postpone termination 293 else if (ecode == boost::asio::error::timed_out && m_Stream && m_Stream->IsOpen ()) 294 StreamReceive (); 295 else 296 Terminate (); 297 } 298 else 299 Terminate (); 300 } 301 else 302 Write (m_StreamBuffer, bytes_transferred); 303 } 304 305 void I2PTunnelConnection::Write (const uint8_t * buf, size_t len) 306 { 307 if (m_SSL) 308 boost::asio::async_write (*m_SSL, boost::asio::buffer (buf, len), boost::asio::transfer_all (), 309 std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1)); 310 else 311 boost::asio::async_write (*m_Socket, boost::asio::buffer (buf, len), boost::asio::transfer_all (), 312 std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1)); 313 } 314 315 void I2PTunnelConnection::HandleConnect (const boost::system::error_code& ecode) 316 { 317 if (ecode) 318 { 319 LogPrint (eLogError, "I2PTunnel: Connect error: ", ecode.message ()); 320 Terminate (); 321 } 322 else 323 { 324 LogPrint (eLogDebug, "I2PTunnel: Connected"); 325 if (m_SSL) 326 m_SSL->async_handshake (boost::asio::ssl::stream_base::client, 327 std::bind (&I2PTunnelConnection::HandleHandshake, shared_from_this (), std::placeholders::_1)); 328 else 329 Established (); 330 } 331 } 332 333 void I2PTunnelConnection::HandleHandshake (const boost::system::error_code& ecode) 334 { 335 if (ecode) 336 { 337 LogPrint (eLogError, "I2PTunnel: Handshake error: ", ecode.message ()); 338 Terminate (); 339 } 340 else 341 { 342 LogPrint (eLogDebug, "I2PTunnel: SSL connected"); 343 Established (); 344 } 345 } 346 347 void I2PTunnelConnection::Established () 348 { 349 StreamReceive (); 350 Receive (); 351 } 352 353 void I2PClientTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len) 354 { 355 if (m_HeaderSent) 356 I2PTunnelConnection::Write (buf, len); 357 else 358 { 359 m_InHeader.clear (); 360 m_InHeader.write ((const char *)buf, len); 361 std::string line; 362 bool endOfHeader = false; 363 while (!endOfHeader) 364 { 365 std::getline(m_InHeader, line); 366 if (!m_InHeader.fail ()) 367 { 368 if (line == "\r") endOfHeader = true; 369 else 370 { 371 if (!m_ConnectionSent && !line.compare(0, 10, "Connection")) 372 { 373 /* close connection, if not Connection: (U|u)pgrade (for websocket) */ 374 auto x = line.find("pgrade"); 375 if (x != std::string::npos && std::tolower(line[x - 1]) == 'u') 376 m_OutHeader << line << "\r\n"; 377 else 378 m_OutHeader << "Connection: close\r\n"; 379 380 m_ConnectionSent = true; 381 } 382 else if (!m_ProxyConnectionSent && !line.compare(0, 16, "Proxy-Connection")) 383 { 384 m_OutHeader << "Proxy-Connection: close\r\n"; 385 m_ProxyConnectionSent = true; 386 } 387 else 388 m_OutHeader << line << "\n"; 389 } 390 } 391 else 392 { 393 // insert incomplete line back 394 m_InHeader.clear (); 395 m_InHeader << line; 396 break; 397 } 398 } 399 400 if (endOfHeader) 401 { 402 if (!m_ConnectionSent) m_OutHeader << "Connection: close\r\n"; 403 if (!m_ProxyConnectionSent) m_OutHeader << "Proxy-Connection: close\r\n"; 404 m_OutHeader << "\r\n"; // end of header 405 m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header 406 m_InHeader.str (""); 407 m_HeaderSent = true; 408 I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ()); 409 } 410 else if (m_OutHeader.tellp () < I2P_TUNNEL_HTTP_MAX_HEADER_SIZE) 411 StreamReceive (); // read more header 412 else 413 { 414 LogPrint (eLogError, "I2PTunnel: HTTP header exceeds max size ", I2P_TUNNEL_HTTP_MAX_HEADER_SIZE); 415 Terminate (); 416 } 417 } 418 } 419 420 I2PServerTunnelConnectionHTTP::I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream, 421 const boost::asio::ip::tcp::endpoint& target, const std::string& host, const std::string& XI2P, 422 std::shared_ptr<boost::asio::ssl::context> sslCtx): 423 I2PTunnelConnection (owner, stream, target, sslCtx), m_Host (host), m_XI2P (XI2P), 424 m_HeaderSent (false), m_ResponseHeaderSent (false) 425 { 426 if (sslCtx) 427 SSL_set_tlsext_host_name(GetSSL ()->native_handle(), host.c_str ()); 428 } 429 430 void I2PServerTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len) 431 { 432 if (m_HeaderSent) 433 I2PTunnelConnection::Write (buf, len); 434 else 435 { 436 m_InHeader.clear (); 437 m_InHeader.write ((const char *)buf, len); 438 std::string line; 439 bool endOfHeader = false, connection = false; 440 while (!endOfHeader) 441 { 442 std::getline(m_InHeader, line); 443 if (m_InHeader.fail ()) break; 444 if (!m_InHeader.eof ()) 445 { 446 if (line == "\r") endOfHeader = true; 447 else 448 { 449 // strip up some headers 450 static constexpr std::array<std::string_view, 2> excluded // list of excluded headers 451 { 452 "Keep-Alive:", "X-I2P" 453 }; 454 bool matched = false; 455 for (const auto& it: excluded) 456 if (boost::iequals (line.substr (0, it.length ()), it)) 457 { 458 matched = true; 459 break; 460 } 461 if (matched) continue; 462 463 // replace some headers 464 if (!m_Host.empty () && boost::iequals (line.substr (0, 5), "Host:")) 465 m_OutHeader << "Host: " << m_Host << "\r\n"; // override host 466 else if (boost::iequals (line.substr (0, 11), "Connection:")) 467 { 468 auto x = line.find("pgrade"); 469 if (x != std::string::npos && x && std::tolower(line[x - 1]) != 'u') // upgrade or Upgrade 470 m_OutHeader << line << "\n"; 471 else 472 m_OutHeader << "Connection: close\r\n"; 473 connection = true; 474 } 475 else // forward as is 476 m_OutHeader << line << "\n"; 477 } 478 } 479 else 480 { 481 // insert incomplete line back 482 m_InHeader.clear (); 483 m_InHeader << line; 484 break; 485 } 486 } 487 488 if (endOfHeader) 489 { 490 // add Connection if not presented 491 if (!connection) 492 m_OutHeader << "Connection: close\r\n"; 493 // add X-I2P fields 494 m_OutHeader << m_XI2P; 495 // end of header 496 m_OutHeader << "\r\n"; 497 498 m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header 499 m_InHeader.str (""); 500 m_HeaderSent = true; 501 I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ()); 502 } 503 else if (m_OutHeader.tellp () < I2P_TUNNEL_HTTP_MAX_HEADER_SIZE) 504 StreamReceive (); // read more header 505 else 506 { 507 LogPrint (eLogError, "I2PTunnel: HTTP header exceeds max size ", I2P_TUNNEL_HTTP_MAX_HEADER_SIZE); 508 Terminate (); 509 } 510 } 511 } 512 513 void I2PServerTunnelConnectionHTTP::WriteToStream (const uint8_t * buf, size_t len) 514 { 515 if (m_ResponseHeaderSent) 516 I2PTunnelConnection::WriteToStream (buf, len); 517 else 518 { 519 m_InHeader.clear (); 520 if (m_InHeader.str ().empty ()) m_OutHeader.str (""); // start of response 521 m_InHeader.write ((const char *)buf, len); 522 std::string line; 523 bool endOfHeader = false; 524 while (!endOfHeader) 525 { 526 std::getline(m_InHeader, line); 527 if (m_InHeader.fail ()) break; 528 if (!m_InHeader.eof ()) 529 { 530 if (line == "\r") endOfHeader = true; 531 else 532 { 533 static constexpr std::array<std::string_view, 5> excluded // list of excluded headers 534 { 535 "Server:", "Date:", "X-Runtime:", "X-Powered-By:", "Proxy" 536 }; 537 bool matched = false; 538 for (const auto& it: excluded) 539 if (!line.compare(0, it.length (), it)) 540 { 541 matched = true; 542 break; 543 } 544 if (!matched) 545 m_OutHeader << line << "\n"; 546 } 547 } 548 else 549 { 550 // insert incomplete line back 551 m_InHeader.clear (); 552 m_InHeader << line; 553 break; 554 } 555 } 556 557 if (endOfHeader) 558 { 559 m_OutHeader << "\r\n"; // end of header 560 m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header 561 m_InHeader.str (""); 562 m_ResponseHeaderSent = true; 563 I2PTunnelConnection::WriteToStream ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ()); 564 m_OutHeader.str (""); 565 } 566 else 567 Receive (); 568 } 569 } 570 571 I2PTunnelConnectionIRC::I2PTunnelConnectionIRC (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream, 572 const boost::asio::ip::tcp::endpoint& target, const std::string& webircpass, 573 std::shared_ptr<boost::asio::ssl::context> sslCtx): 574 I2PTunnelConnection (owner, stream, target, sslCtx), m_From (stream->GetRemoteIdentity ()), 575 m_NeedsWebIrc (webircpass.length() ? true : false), m_WebircPass (webircpass) 576 { 577 } 578 579 void I2PTunnelConnectionIRC::Write (const uint8_t * buf, size_t len) 580 { 581 m_OutPacket.str (""); 582 if (m_NeedsWebIrc) 583 { 584 m_NeedsWebIrc = false; 585 m_OutPacket << "WEBIRC " << m_WebircPass << " cgiirc " << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ()) 586 << " " << GetSocket ()->local_endpoint ().address () << std::endl; 587 } 588 589 m_InPacket.clear (); 590 m_InPacket.write ((const char *)buf, len); 591 592 while (!m_InPacket.eof () && !m_InPacket.fail ()) 593 { 594 std::string line; 595 std::getline (m_InPacket, line); 596 if (line.length () == 0 && m_InPacket.eof ()) 597 m_InPacket.str (""); 598 auto pos = line.find ("USER"); 599 if (!pos) // start of line 600 { 601 pos = line.find (" "); 602 pos++; 603 pos = line.find (" ", pos); 604 pos++; 605 auto nextpos = line.find (" ", pos); 606 m_OutPacket << line.substr (0, pos); 607 m_OutPacket << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ()); 608 m_OutPacket << line.substr (nextpos) << '\n'; 609 } 610 else 611 m_OutPacket << line << '\n'; 612 } 613 I2PTunnelConnection::Write ((uint8_t *)m_OutPacket.str ().c_str (), m_OutPacket.str ().length ()); 614 } 615 616 617 /* This handler tries to establish a connection with the desired server and dies if it fails to do so */ 618 class I2PClientTunnelHandler: public I2PServiceHandler, public std::enable_shared_from_this<I2PClientTunnelHandler> 619 { 620 public: 621 I2PClientTunnelHandler (I2PClientTunnel * parent, std::shared_ptr<const Address> address, 622 uint16_t destinationPort, std::shared_ptr<boost::asio::ip::tcp::socket> socket): 623 I2PServiceHandler(parent), m_Address(address), 624 m_DestinationPort (destinationPort), m_Socket(socket) {}; 625 void Handle(); 626 void Terminate(); 627 private: 628 void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream); 629 std::shared_ptr<const Address> m_Address; 630 uint16_t m_DestinationPort; 631 std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket; 632 }; 633 634 void I2PClientTunnelHandler::Handle() 635 { 636 GetOwner()->CreateStream ( 637 std::bind (&I2PClientTunnelHandler::HandleStreamRequestComplete, shared_from_this(), std::placeholders::_1), 638 m_Address, m_DestinationPort); 639 } 640 641 void I2PClientTunnelHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream) 642 { 643 if (stream) 644 { 645 if (Kill()) return; 646 LogPrint (eLogDebug, "I2PTunnel: New connection"); 647 auto connection = std::make_shared<I2PTunnelConnection>(GetOwner(), m_Socket, stream); 648 GetOwner()->AddHandler (connection); 649 connection->I2PConnect (); 650 Done(shared_from_this()); 651 } 652 else 653 { 654 LogPrint (eLogError, "I2PTunnel: Client Tunnel Issue when creating the stream, check the previous warnings for more info."); 655 Terminate(); 656 } 657 } 658 659 void I2PClientTunnelHandler::Terminate() 660 { 661 if (Kill()) return; 662 if (m_Socket) 663 { 664 m_Socket->close(); 665 m_Socket = nullptr; 666 } 667 Done(shared_from_this()); 668 } 669 670 I2PClientTunnel::I2PClientTunnel (const std::string& name, const std::string& destination, 671 const std::string& address, uint16_t port, std::shared_ptr<ClientDestination> localDestination, uint16_t destinationPort): 672 TCPIPAcceptor (address, port, localDestination), m_Name (name), m_Destination (destination), 673 m_DestinationPort (destinationPort), m_KeepAliveInterval (0) 674 { 675 } 676 677 void I2PClientTunnel::Start () 678 { 679 TCPIPAcceptor::Start (); 680 GetAddress (); 681 if (m_KeepAliveInterval) 682 ScheduleKeepAliveTimer (); 683 } 684 685 void I2PClientTunnel::Stop () 686 { 687 TCPIPAcceptor::Stop(); 688 m_Address = nullptr; 689 if (m_KeepAliveTimer) m_KeepAliveTimer->cancel (); 690 } 691 692 void I2PClientTunnel::SetKeepAliveInterval (uint32_t keepAliveInterval) 693 { 694 m_KeepAliveInterval = keepAliveInterval; 695 if (m_KeepAliveInterval) 696 m_KeepAliveTimer.reset (new boost::asio::deadline_timer (GetLocalDestination ()->GetService ())); 697 } 698 699 /* HACK: maybe we should create a caching IdentHash provider in AddressBook */ 700 std::shared_ptr<const Address> I2PClientTunnel::GetAddress () 701 { 702 if (!m_Address) 703 { 704 m_Address = i2p::client::context.GetAddressBook ().GetAddress (m_Destination); 705 if (!m_Address) 706 LogPrint (eLogWarning, "I2PTunnel: Remote destination ", m_Destination, " not found"); 707 } 708 return m_Address; 709 } 710 711 std::shared_ptr<I2PServiceHandler> I2PClientTunnel::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket) 712 { 713 auto address = GetAddress (); 714 if (address) 715 return std::make_shared<I2PClientTunnelHandler>(this, address, m_DestinationPort, socket); 716 else 717 return nullptr; 718 } 719 720 void I2PClientTunnel::ScheduleKeepAliveTimer () 721 { 722 if (m_KeepAliveTimer) 723 { 724 m_KeepAliveTimer->expires_from_now (boost::posix_time::seconds (m_KeepAliveInterval)); 725 m_KeepAliveTimer->async_wait (std::bind (&I2PClientTunnel::HandleKeepAliveTimer, 726 this, std::placeholders::_1)); 727 } 728 } 729 730 void I2PClientTunnel::HandleKeepAliveTimer (const boost::system::error_code& ecode) 731 { 732 if (ecode != boost::asio::error::operation_aborted) 733 { 734 if (m_Address && m_Address->IsValid ()) 735 { 736 if (m_Address->IsIdentHash ()) 737 GetLocalDestination ()->SendPing (m_Address->identHash); 738 else 739 GetLocalDestination ()->SendPing (m_Address->blindedPublicKey); 740 } 741 ScheduleKeepAliveTimer (); 742 } 743 } 744 745 I2PServerTunnel::I2PServerTunnel (const std::string& name, const std::string& address, 746 uint16_t port, std::shared_ptr<ClientDestination> localDestination, uint16_t inport, bool gzip): 747 I2PService (localDestination), m_IsUniqueLocal(true), m_Name (name), m_Address (address), m_Port (port), m_IsAccessList (false) 748 { 749 m_PortDestination = localDestination->GetStreamingDestination (inport); 750 if (!m_PortDestination) // default destination 751 m_PortDestination = localDestination->CreateStreamingDestination (inport, gzip); 752 } 753 754 void I2PServerTunnel::Start () 755 { 756 m_Endpoint.port (m_Port); 757 boost::system::error_code ec; 758 auto addr = boost::asio::ip::make_address (m_Address, ec); 759 if (!ec) 760 m_Endpoint.address (addr); 761 else 762 Resolve (nullptr); 763 Accept (); 764 } 765 766 void I2PServerTunnel::Stop () 767 { 768 if (m_PortDestination) 769 m_PortDestination->ResetAcceptor (); 770 auto localDestination = GetLocalDestination (); 771 if (localDestination) 772 localDestination->StopAcceptingStreams (); 773 if (m_Resolver) 774 m_Resolver->cancel (); 775 776 ClearHandlers (); 777 } 778 779 bool I2PServerTunnel::Resolve (std::shared_ptr<i2p::stream::Stream> stream) 780 { 781 if (m_Resolver) return false; // already resolving 782 m_Resolver = std::make_shared<boost::asio::ip::tcp::resolver>(GetService ()); 783 m_Resolver->async_resolve (m_Address, "", 784 std::bind (&I2PServerTunnel::HandleResolve, this, 785 std::placeholders::_1, std::placeholders::_2, stream)); 786 return true; 787 } 788 789 void I2PServerTunnel::HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::results_type endpoints, 790 std::shared_ptr<i2p::stream::Stream> stream) 791 { 792 m_Resolver = nullptr; 793 if (!ecode) 794 { 795 bool found = false; 796 boost::asio::ip::tcp::endpoint ep; 797 if (m_LocalAddress) 798 { 799 for (const auto& it: endpoints) 800 { 801 ep = it; 802 if (!ep.address ().is_unspecified ()) 803 { 804 if (ep.address ().is_v4 ()) 805 { 806 if (m_LocalAddress->is_v4 ()) found = true; 807 } 808 else if (ep.address ().is_v6 ()) 809 { 810 if (i2p::util::net::IsYggdrasilAddress (ep.address ())) 811 { 812 if (i2p::util::net::IsYggdrasilAddress (*m_LocalAddress)) 813 found = true; 814 } 815 else if (m_LocalAddress->is_v6 ()) 816 found = true; 817 } 818 } 819 if (found) break; 820 } 821 } 822 else 823 { 824 found = true; 825 ep = *endpoints.begin (); // first available 826 } 827 if (!found) 828 { 829 LogPrint (eLogError, "I2PTunnel: Unable to resolve ", m_Address, " to compatible address"); 830 return; 831 } 832 833 auto addr = ep.address (); 834 LogPrint (eLogInfo, "I2PTunnel: Server tunnel ", (*endpoints.begin ()).host_name (), " has been resolved to ", addr); 835 m_Endpoint.address (addr); 836 if (stream) 837 Connect (stream); 838 } 839 else 840 LogPrint (eLogError, "I2PTunnel: Unable to resolve server tunnel address ", m_Address, ": ", ecode.message ()); 841 } 842 843 void I2PServerTunnel::SetAccessList (const std::set<i2p::data::IdentHash>& accessList) 844 { 845 m_AccessList = accessList; 846 m_IsAccessList = true; 847 } 848 849 void I2PServerTunnel::SetLocalAddress (const std::string& localAddress) 850 { 851 boost::system::error_code ec; 852 auto addr = boost::asio::ip::make_address(localAddress, ec); 853 if (!ec) 854 m_LocalAddress.reset (new boost::asio::ip::address (addr)); 855 else 856 LogPrint (eLogError, "I2PTunnel: Can't set local address ", localAddress); 857 } 858 859 void I2PServerTunnel::SetSSL (bool ssl) 860 { 861 if (ssl) 862 { 863 m_SSLCtx = std::make_shared<boost::asio::ssl::context> (boost::asio::ssl::context::sslv23); 864 m_SSLCtx->set_verify_mode(boost::asio::ssl::context::verify_none); 865 } 866 else 867 m_SSLCtx = nullptr; 868 } 869 870 void I2PServerTunnel::Accept () 871 { 872 if (m_PortDestination) 873 m_PortDestination->SetAcceptor (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1)); 874 875 auto localDestination = GetLocalDestination (); 876 if (localDestination) 877 { 878 if (!localDestination->IsAcceptingStreams ()) // set it as default if not set yet 879 localDestination->AcceptStreams (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1)); 880 } 881 else 882 LogPrint (eLogError, "I2PTunnel: Local destination not set for server tunnel"); 883 } 884 885 void I2PServerTunnel::HandleAccept (std::shared_ptr<i2p::stream::Stream> stream) 886 { 887 if (stream) 888 { 889 if (m_IsAccessList) 890 { 891 if (!m_AccessList.count (stream->GetRemoteIdentity ()->GetIdentHash ())) 892 { 893 LogPrint (eLogWarning, "I2PTunnel: Address ", stream->GetRemoteIdentity ()->GetIdentHash ().ToBase32 (), " is not in white list. Incoming connection dropped"); 894 stream->Close (); 895 return; 896 } 897 } 898 if (!m_Endpoint.address ().is_unspecified ()) 899 Connect (stream); 900 else if (!Resolve (stream)) 901 { 902 LogPrint (eLogWarning, "I2PTunnel: Address ", m_Address, " can't be resolved. Incoming connection dropped"); 903 stream->Close (); 904 return; 905 } 906 } 907 } 908 909 void I2PServerTunnel::Connect (std::shared_ptr<i2p::stream::Stream> stream) 910 { 911 // new connection 912 auto conn = CreateI2PConnection (stream); 913 AddHandler (conn); 914 if (m_LocalAddress) 915 conn->Connect (*m_LocalAddress); 916 else 917 conn->Connect (m_IsUniqueLocal); 918 } 919 920 std::shared_ptr<I2PTunnelConnection> I2PServerTunnel::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream) 921 { 922 return std::make_shared<I2PTunnelConnection> (this, stream, GetEndpoint (), m_SSLCtx); 923 924 } 925 926 I2PServerTunnelHTTP::I2PServerTunnelHTTP (const std::string& name, const std::string& address, 927 uint16_t port, std::shared_ptr<ClientDestination> localDestination, 928 const std::string& host, uint16_t inport, bool gzip, bool i2pheaders): 929 I2PServerTunnel (name, address, port, localDestination, inport, gzip), 930 m_Host (host), m_I2PHeaders (i2pheaders) 931 { 932 } 933 934 std::shared_ptr<I2PTunnelConnection> I2PServerTunnelHTTP::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream) 935 { 936 if (m_I2PHeaders && (m_XI2P.empty () || stream->GetRemoteIdentity () != m_From.lock ())) 937 { 938 auto from = stream->GetRemoteIdentity (); 939 m_From = from; 940 std::stringstream ss; 941 ss << X_I2P_DEST_B32 << ": " << context.GetAddressBook ().ToAddress(from->GetIdentHash ()) << "\r\n"; 942 ss << X_I2P_DEST_HASH << ": " << from->GetIdentHash ().ToBase64 () << "\r\n"; 943 ss << X_I2P_DEST_B64 << ": " << from->ToBase64 () << "\r\n"; 944 m_XI2P = ss.str (); 945 } 946 return std::make_shared<I2PServerTunnelConnectionHTTP> (this, stream, GetEndpoint (), m_Host, m_XI2P, GetSSLCtx ()); 947 } 948 949 I2PServerTunnelIRC::I2PServerTunnelIRC (const std::string& name, const std::string& address, 950 uint16_t port, std::shared_ptr<ClientDestination> localDestination, 951 const std::string& webircpass, uint16_t inport, bool gzip): 952 I2PServerTunnel (name, address, port, localDestination, inport, gzip), 953 m_WebircPass (webircpass) 954 { 955 } 956 957 std::shared_ptr<I2PTunnelConnection> I2PServerTunnelIRC::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream) 958 { 959 return std::make_shared<I2PTunnelConnectionIRC> (this, stream, GetEndpoint (), m_WebircPass, GetSSLCtx ()); 960 } 961 } 962 } 963