i2psam.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912
  1. /**
  2. * Copyleft c==8 2024 acetone - The Samty project
  3. * Copyright (c) 2019-2022 polistern
  4. * Copyright (c) 2017 The I2P Project
  5. * Copyright (c) 2013-2015 The Anoncoin Core developers
  6. * Copyright (c) 2012-2013 giv
  7. *
  8. * Distributed under the MIT software license, see the accompanying
  9. * file LICENSE or http://www.opensource.org/licenses/mit-license.php.
  10. */
  11. #include <ctime>
  12. #include <cstdlib>
  13. #include <cstring>
  14. #include "i2psam.h"
  15. namespace Samty
  16. {
  17. #ifdef WIN32
  18. int I2pSocket::instances_ = 0;
  19. void I2pSocket::initWSA()
  20. {
  21. WSADATA wsadata;
  22. int ret = WSAStartup(MAKEWORD(2, 2), &wsadata);
  23. if (ret != NO_ERROR)
  24. print_error("Failed to initialize winsock library");
  25. }
  26. void I2pSocket::freeWSA()
  27. {
  28. WSACleanup();
  29. }
  30. #endif // WIN32
  31. I2pSocket::I2pSocket(const std::string &SAMHost, uint16_t SAMPort)
  32. : socket_(INVALID_SOCKET), SAMHost_(SAMHost), SAMPort_(SAMPort) {
  33. #ifdef WIN32
  34. if (instances_++ == 0)
  35. initWSA();
  36. #endif // WIN32
  37. memset(&servAddr_, 0, sizeof(servAddr_));
  38. servAddr_.sin_family = AF_INET;
  39. servAddr_.sin_addr.s_addr = inet_addr(SAMHost.c_str());
  40. servAddr_.sin_port = htons(SAMPort);
  41. bootstrapI2P();
  42. }
  43. I2pSocket::I2pSocket(const sockaddr_in &addr)
  44. : socket_(INVALID_SOCKET), servAddr_(addr)
  45. {
  46. #ifdef WIN32
  47. if (instances_++ == 0)
  48. initWSA();
  49. #endif // WIN32
  50. bootstrapI2P();
  51. }
  52. I2pSocket::I2pSocket(const I2pSocket &rhs)
  53. : socket_(INVALID_SOCKET), servAddr_(rhs.servAddr_)
  54. {
  55. #ifdef WIN32
  56. if (instances_++ == 0)
  57. initWSA();
  58. #endif // WIN32
  59. bootstrapI2P();
  60. }
  61. I2pSocket::~I2pSocket()
  62. {
  63. close();
  64. #ifdef WIN32
  65. if (--instances_ == 0)
  66. freeWSA();
  67. #endif // WIN32
  68. }
  69. void I2pSocket::bootstrapI2P()
  70. {
  71. init();
  72. if (isOk())
  73. handshake();
  74. }
  75. void I2pSocket::init()
  76. {
  77. /**
  78. * Here is where the only real OS socket is called for creation to talk with the router,
  79. * the value returned is stored in our variable socket_ which holds the connection
  80. * to our stream for everything else
  81. */
  82. socket_ = socket(AF_INET, SOCK_STREAM, 0);
  83. if (socket_ == INVALID_SOCKET)
  84. {
  85. errorString_ = "Failed to create socket";
  86. return;
  87. }
  88. if (connect(socket_, (const sockaddr *) &servAddr_, sizeof(servAddr_)) == SOCKET_ERROR)
  89. {
  90. close();
  91. errorString_ = "Failed to connect to SAM";
  92. return;
  93. }
  94. }
  95. SOCKET I2pSocket::release()
  96. {
  97. SOCKET temp = socket_;
  98. socket_ = INVALID_SOCKET;
  99. return temp;
  100. }
  101. // If the handshake works, we're talking to a valid I2P router.
  102. void I2pSocket::handshake()
  103. {
  104. this->write(Message::hello(minVer_, maxVer_));
  105. const std::string answer = this->readString();
  106. const Message::eStatus answerStatus = Message::checkAnswer(answer);
  107. if (answerStatus == Message::OK)
  108. version_ = Message::getValue(answer, "VERSION");
  109. else
  110. errorString_ = "Handshake failed";
  111. }
  112. bool I2pSocket::write(const std::string &msg)
  113. {
  114. if (!isOk())
  115. {
  116. errorString_ = "Failed to send data because socket is closed";
  117. return false;
  118. }
  119. ssize_t sentBytes = send(socket_, msg.c_str(), msg.length(), 0);
  120. if (sentBytes == SOCKET_ERROR)
  121. {
  122. close();
  123. errorString_ = "Failed to send data";
  124. return false;
  125. }
  126. if (sentBytes == 0)
  127. {
  128. close();
  129. errorString_ = "I2pSocket was closed";
  130. return false;
  131. }
  132. return true;
  133. }
  134. bool I2pSocket::write(const std::vector<uint8_t> &data)
  135. {
  136. if (!isOk())
  137. {
  138. errorString_ = "Failed to send data because socket is closed";
  139. return false;
  140. }
  141. ssize_t sentBytes = send(socket_, data.data(), data.size(), 0);
  142. if (sentBytes == SOCKET_ERROR)
  143. {
  144. close();
  145. errorString_ = "Failed to send data";
  146. return false;
  147. }
  148. if (sentBytes == 0)
  149. {
  150. close();
  151. errorString_ = "I2pSocket was closed";
  152. return false;
  153. }
  154. return true;
  155. }
  156. std::string I2pSocket::readDestination()
  157. {
  158. std::string certificate;
  159. char byte = 0;
  160. while (true)
  161. {
  162. ssize_t bytes_received = recv(socket_, &byte, 1, 0);
  163. if (bytes_received < 1)
  164. {
  165. return std::string();
  166. }
  167. if (byte == '\n') break;
  168. certificate += byte;
  169. }
  170. Samty::Identity guestIdent;
  171. guestIdent.init(certificate);
  172. return guestIdent.dest32();
  173. }
  174. std::vector<uint8_t> I2pSocket::readBinary()
  175. {
  176. std::vector<uint8_t> result (SAM_BUFSIZE);
  177. if (!isOk())
  178. {
  179. errorString_ = "Failed to read data because socket is closed";
  180. return result;
  181. }
  182. ssize_t recievedBytes = recv(socket_, result.data(), SAM_BUFSIZE, 0);
  183. if (recievedBytes == SOCKET_ERROR)
  184. {
  185. close();
  186. errorString_ = "Failed to receive data";
  187. return result;
  188. }
  189. if (recievedBytes == 0)
  190. {
  191. close();
  192. errorString_ = "I2pSocket was closed";
  193. }
  194. return result;
  195. }
  196. ssize_t I2pSocket::readBytes(uint8_t *buffer, size_t length, int timeoutSec)
  197. {
  198. if (!isOk())
  199. {
  200. errorString_ = "Failed to read data because socket is closed";
  201. return -1;
  202. }
  203. #ifdef WIN32
  204. HANDLE socketEvent = WSACreateEvent();
  205. if (socketEvent == WSA_INVALID_EVENT)
  206. {
  207. errorString_ = "Failed to create event: " + std::to_string(WSAGetLastError());
  208. return -1;
  209. }
  210. WSAEventSelect(socket_, socketEvent, FD_READ);
  211. DWORD result = WSAWaitForMultipleEvents(1, &socketEvent, FALSE, timeoutMs, FALSE);
  212. if (result == WSA_WAIT_TIMEOUT)
  213. {
  214. WSACloseEvent(socketEvent);
  215. errorString_ = "Read timed out";
  216. return 0;
  217. }
  218. else if (result == WSA_WAIT_FAILED)
  219. {
  220. WSACloseEvent(socketEvent);
  221. errorString_ = "WSAWaitForMultipleEvents failed: " + std::to_string(WSAGetLastError());
  222. return -1;
  223. }
  224. WSACloseEvent(socketEvent);
  225. // Если достигнуто событие FD_READ
  226. ssize_t totalBytesRead = 0;
  227. ssize_t bytesRead = 0;
  228. while (totalBytesRead < length)
  229. {
  230. bytesRead = recv(socket_, reinterpret_cast<char*>(buffer + totalBytesRead), length - totalBytesRead, 0);
  231. if (bytesRead > 0)
  232. {
  233. totalBytesRead += bytesRead;
  234. }
  235. else if (bytesRead == 0)
  236. {
  237. // Соединение закрыто
  238. return totalBytesRead;
  239. }
  240. else
  241. {
  242. // Ошибка recv
  243. errorString_ = "Recv failed: " + std::to_string(WSAGetLastError());
  244. return -1;
  245. }
  246. }
  247. return totalBytesRead;
  248. #else
  249. fd_set readfds;
  250. struct timeval timeout;
  251. int result;
  252. FD_ZERO(&readfds);
  253. FD_SET(socket_, &readfds);
  254. timeout.tv_sec = timeoutSec;
  255. timeout.tv_usec = 0;
  256. result = select(socket_ + 1, &readfds, nullptr, nullptr, &timeout);
  257. if (result == -1)
  258. {
  259. errorString_ = "Select failed: " + std::string(strerror(errno));
  260. return -1;
  261. }
  262. else if (result == 0)
  263. {
  264. errorString_ = "Read timed out";
  265. return 0;
  266. }
  267. if (FD_ISSET(socket_, &readfds))
  268. {
  269. ssize_t totalBytesRead = 0;
  270. ssize_t bytesRead = 0;
  271. while (totalBytesRead < length)
  272. {
  273. bytesRead = recv(socket_, buffer + totalBytesRead, length - totalBytesRead, 0);
  274. if (bytesRead > 0)
  275. {
  276. totalBytesRead += bytesRead;
  277. }
  278. else if (bytesRead == 0)
  279. {
  280. return totalBytesRead;
  281. }
  282. else
  283. {
  284. errorString_ = "Recv failed: " + std::string(strerror(errno));
  285. return -1;
  286. }
  287. }
  288. return totalBytesRead;
  289. }
  290. errorString_ = "Socket not ready for reading";
  291. return -1;
  292. #endif // if not WIN32
  293. }
  294. std::string I2pSocket::readString()
  295. {
  296. if (!isOk())
  297. {
  298. errorString_ = "Failed to read data because socket is closed";
  299. return std::string();
  300. }
  301. char buffer[SAM_BUFSIZE];
  302. memset(buffer, 0, SAM_BUFSIZE);
  303. ssize_t recievedBytes = recv(socket_, buffer, SAM_BUFSIZE-1, 0);
  304. if (recievedBytes == SOCKET_ERROR)
  305. {
  306. close();
  307. errorString_ = "Failed to receive data";
  308. return std::string();
  309. }
  310. if (recievedBytes == 0)
  311. {
  312. close();
  313. errorString_ = "I2pSocket was closed";
  314. }
  315. return std::string(buffer);
  316. }
  317. void I2pSocket::close()
  318. {
  319. if (socket_ != INVALID_SOCKET)
  320. {
  321. #ifdef WIN32
  322. ::closesocket(socket_);
  323. #else
  324. ::close(socket_);
  325. #endif // WIN32
  326. socket_ = INVALID_SOCKET;
  327. }
  328. }
  329. bool I2pSocket::isOk() const { return socket_ != INVALID_SOCKET; }
  330. const std::string &I2pSocket::getHost() const { return SAMHost_; }
  331. uint16_t I2pSocket::getPort() const { return SAMPort_; }
  332. const std::string &I2pSocket::getVersion() const { return version_; }
  333. const sockaddr_in &I2pSocket::getAddress() const { return servAddr_; }
  334. //-----------------------------------------------------------------------------
  335. SAMSession::SAMSession(const Configuration& configuration)
  336. : socket_(configuration.SAMHost, configuration.SAMPort),
  337. sessionID_(generateSessionID()),
  338. isSick_(false),
  339. config_(configuration)
  340. {
  341. i2pOptions_ = "i2cp.leaseSetEncType=4";
  342. if (not configuration.publishLeaseSet)
  343. {
  344. i2pOptions_ += " i2cp.dontPublishLeaseSet";
  345. }
  346. if (configuration.encryptedLeaseSet)
  347. {
  348. i2pOptions_ += " i2cp.leaseSetType=5";
  349. }
  350. i2pOptions_ += " inbound.quantity=" + std::to_string(configuration.inboundQuantity);
  351. i2pOptions_ += " inbound.length=" + std::to_string(configuration.inboundLength);
  352. i2pOptions_ += " inbound.lengthVariance=" + std::to_string(configuration.inboundVariance);
  353. i2pOptions_ += " outbound.quantity=" + std::to_string(configuration.outboundQuantity);
  354. i2pOptions_ += " outbound.length=" + std::to_string(configuration.outboundLength);
  355. i2pOptions_ += " outbound.lengthVariance=" + std::to_string(configuration.outboundVariance);
  356. }
  357. SAMSession::SAMSession(SAMSession& rhs)
  358. : socket_(rhs.socket_),
  359. sessionID_(generateSessionID()),
  360. myDestination_(rhs.myDestination_),
  361. i2pOptions_(rhs.i2pOptions_),
  362. isSick_(false),
  363. config_(rhs.config_)
  364. {
  365. rhs.fallSick();
  366. rhs.socket_.close();
  367. }
  368. /*static*/
  369. std::string SAMSession::generateSessionID()
  370. {
  371. static const int minSessionIDLength = 5;
  372. static const int maxSessionIDLength = 9;
  373. static const char sessionIDAlphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  374. int length = minSessionIDLength - 1;
  375. std::string result;
  376. srand(time(nullptr));
  377. while (length < minSessionIDLength)
  378. length = rand() % maxSessionIDLength;
  379. while (length-- > 0)
  380. result += sessionIDAlphabet[rand() % (sizeof(sessionIDAlphabet) - 1)];
  381. return result;
  382. }
  383. RequestResult<const std::string> SAMSession::namingLookup(const std::string &name) const
  384. {
  385. typedef RequestResult<const std::string> ResultType;
  386. typedef Message::Answer<const std::string> AnswerType;
  387. if (name.find(".b32.i2p") != std::string::npos)
  388. {
  389. return ResultType(name);
  390. }
  391. std::unique_ptr<I2pSocket> newSocket(new I2pSocket(socket_));
  392. const AnswerType answer = namingLookup(*newSocket, name);
  393. switch (answer.status)
  394. {
  395. case Message::OK:
  396. return ResultType(answer.value);
  397. case Message::EMPTY_ANSWER:
  398. case Message::CLOSED_SOCKET:
  399. fallSick();
  400. break;
  401. default:
  402. break;
  403. }
  404. return ResultType();
  405. }
  406. Identity SAMSession::createSession(const std::string& destination)
  407. {
  408. return createSession(destination, SAM_DEFAULT_SIGNATURE_TYPE);
  409. }
  410. Identity SAMSession::createSession(const std::string& destination, const std::string& sigType)
  411. {
  412. return createSession(destination, sigType, i2pOptions_);
  413. }
  414. void SAMSession::fallSick() const { isSick_ = true; }
  415. /*static*/
  416. Message::Answer<const std::string> SAMSession::rawRequest(I2pSocket &socket, const std::string &requestStr)
  417. {
  418. typedef Message::Answer<const std::string> AnswerType;
  419. if (!socket.isOk())
  420. return AnswerType(Message::CLOSED_SOCKET);
  421. socket.write(requestStr);
  422. const std::string answer = socket.readString();
  423. const Message::eStatus status = Message::checkAnswer(answer);
  424. return AnswerType(status, answer);
  425. }
  426. /*static*/
  427. Message::Answer<const std::string> SAMSession::request(I2pSocket &socket,
  428. const std::string &requestStr,
  429. const std::string &keyOnSuccess)
  430. {
  431. typedef Message::Answer<const std::string> AnswerType;
  432. const AnswerType answer = rawRequest(socket, requestStr);
  433. return (answer.status == Message::OK) ? AnswerType(answer.status, Message::getValue(answer.value, keyOnSuccess))
  434. : answer;
  435. }
  436. /*static*/
  437. Message::eStatus SAMSession::request(I2pSocket &socket, const std::string &requestStr)
  438. {
  439. return rawRequest(socket, requestStr).status;
  440. }
  441. /*static*/
  442. Message::Answer<const std::string> SAMSession::namingLookup(I2pSocket &socket, const std::string &name)
  443. {
  444. return request(socket, Message::namingLookup(name), "VALUE");
  445. }
  446. //-----------------------------------------------------------------------------
  447. StreamSession::StreamSession(const Configuration& configuration)
  448. : SAMSession(configuration)
  449. {
  450. const std::string sigType = configuration.encryptedLeaseSet ? "11" : "7";
  451. myDestination_ = createStreamSession(configuration.destination, sigType, i2pOptions_);
  452. }
  453. StreamSession::StreamSession(StreamSession &rhs)
  454. : SAMSession(rhs)
  455. {
  456. rhs.fallSick();
  457. rhs.socket_.close();
  458. const std::string sigType = rhs.config_.encryptedLeaseSet ? "11" : "7";
  459. (void) createStreamSession(myDestination_.fullBase64(), sigType, rhs.i2pOptions_);
  460. for (const auto &forwardedStream : rhs.forwardedStreams_)
  461. forward(forwardedStream.host, forwardedStream.port, forwardedStream.silent);
  462. }
  463. StreamSession::~StreamSession()
  464. {
  465. stopForwardingAll();
  466. }
  467. RequestResult<std::unique_ptr<I2pSocket>> StreamSession::accept(bool silent)
  468. {
  469. typedef RequestResult<std::unique_ptr<I2pSocket>> ResultType;
  470. std::unique_ptr<I2pSocket> streamSocket(new I2pSocket(socket_));
  471. const Message::eStatus status = accept(*streamSocket, sessionID_, silent);
  472. switch (status)
  473. {
  474. case Message::OK:
  475. return ResultType(std::move(streamSocket));
  476. case Message::EMPTY_ANSWER:
  477. case Message::CLOSED_SOCKET:
  478. case Message::INVALID_ID:
  479. case Message::I2P_ERROR:
  480. fallSick();
  481. break;
  482. default:
  483. break;
  484. }
  485. return ResultType();
  486. }
  487. RequestResult<std::unique_ptr<I2pSocket>> StreamSession::connect(const std::string &destination, bool silent)
  488. {
  489. typedef RequestResult<std::unique_ptr<I2pSocket>> ResultType;
  490. std::unique_ptr<I2pSocket> streamSocket(new I2pSocket(socket_));
  491. const Message::eStatus status = connect(*streamSocket, sessionID_, destination, silent);
  492. if (silent)
  493. return ResultType(std::move(streamSocket));
  494. switch (status)
  495. {
  496. case Message::OK:
  497. return ResultType(std::move(streamSocket));
  498. case Message::EMPTY_ANSWER:
  499. case Message::CLOSED_SOCKET:
  500. case Message::INVALID_ID:
  501. case Message::I2P_ERROR:
  502. fallSick();
  503. break;
  504. default:
  505. break;
  506. }
  507. return ResultType();
  508. }
  509. RequestResult<void> StreamSession::forward(const std::string &host, uint16_t port, bool silent)
  510. {
  511. typedef RequestResult<void> ResultType;
  512. std::unique_ptr<I2pSocket> newSocket(new I2pSocket(socket_));
  513. const Message::eStatus status = forward(*newSocket, sessionID_, host, port, silent);
  514. switch (status)
  515. {
  516. case Message::OK:
  517. forwardedStreams_.push_back(ForwardedStream(newSocket.get(), host, port, silent));
  518. newSocket.release(); // release after successful push_back only
  519. return ResultType(true);
  520. case Message::EMPTY_ANSWER:
  521. case Message::CLOSED_SOCKET:
  522. case Message::INVALID_ID:
  523. case Message::I2P_ERROR:
  524. fallSick();
  525. break;
  526. default:
  527. break;
  528. }
  529. return ResultType();
  530. }
  531. void StreamSession::closeSocket()
  532. {
  533. socket_.close();
  534. }
  535. std::string StreamSession::errorString() const
  536. {
  537. return socket_.errorString();
  538. }
  539. Identity StreamSession::createStreamSession(const std::string& destination, const std::string& sigType, const std::string& i2pOptions)
  540. {
  541. typedef Message::Answer<const std::string> AnswerType;
  542. const AnswerType
  543. answer = createStreamSession(socket_, sessionID_, config_.nickname, destination, i2pOptions, sigType);
  544. if (answer.status != Message::OK)
  545. {
  546. fallSick();
  547. return Identity();
  548. }
  549. Identity ident;
  550. ident.init(answer.value, sigType == SAM_BLINDED_SIGNATURE_TYPE);
  551. return ident;
  552. }
  553. Identity StreamSession::createSession(const std::string& destination, const std::string& sigType, const std::string& i2pOptions)
  554. {
  555. typedef Message::Answer<const std::string> AnswerType;
  556. const AnswerType
  557. answer = createStreamSession(socket_, sessionID_, config_.nickname, destination, i2pOptions, sigType);
  558. if (answer.status != Message::OK)
  559. {
  560. fallSick();
  561. return Identity();
  562. }
  563. Identity ident;
  564. ident.init(answer.value);
  565. return ident;
  566. }
  567. void StreamSession::stopForwarding(const std::string &host, uint16_t port)
  568. {
  569. for (auto it = forwardedStreams_.begin(); it != forwardedStreams_.end();)
  570. {
  571. if (it->port == port && it->host == host)
  572. {
  573. delete (it->socket);
  574. it = forwardedStreams_.erase(it);
  575. }
  576. else
  577. ++it;
  578. }
  579. }
  580. void StreamSession::stopForwardingAll()
  581. {
  582. for (auto &forwardedStream : forwardedStreams_)
  583. delete (forwardedStream.socket);
  584. forwardedStreams_.clear();
  585. socket_.close();
  586. }
  587. /*static*/
  588. Message::Answer<const std::string> StreamSession::createStreamSession(I2pSocket &socket,
  589. const std::string &sessionID,
  590. const std::string &nickname,
  591. const std::string &destination,
  592. const std::string &options,
  593. const std::string &signatureType)
  594. {
  595. return request(socket,
  596. Message::sessionCreate(sessionID, nickname, destination, options, signatureType),
  597. "DESTINATION");
  598. }
  599. /*static*/
  600. Message::eStatus StreamSession::accept(I2pSocket &socket, const std::string &sessionID, bool silent)
  601. {
  602. return request(socket, Message::streamAccept(sessionID, silent));
  603. }
  604. /*static*/
  605. Message::eStatus StreamSession::connect(I2pSocket &socket,
  606. const std::string &sessionID,
  607. const std::string &destination,
  608. bool silent)
  609. {
  610. return request(socket, Message::streamConnect(sessionID, destination, silent));
  611. }
  612. /*static*/
  613. Message::eStatus StreamSession::forward(I2pSocket &socket,
  614. const std::string &sessionID,
  615. const std::string &host,
  616. uint16_t port,
  617. bool silent)
  618. {
  619. return request(socket, Message::streamForward(sessionID, host, port, silent));
  620. }
  621. //-----------------------------------------------------------------------------
  622. std::string Message::hello(const std::string &minVer, const std::string &maxVer)
  623. {
  624. /**
  625. * -> HELLO VERSION
  626. * MIN=$min
  627. * MAX=$max
  628. *
  629. * <- HELLO REPLY
  630. * RESULT=OK
  631. * VERSION=$version
  632. */
  633. static constexpr char helloFormat[] = "HELLO VERSION MIN=%s MAX=%s\n";
  634. return createSAMRequest(helloFormat, minVer.c_str(), maxVer.c_str());
  635. }
  636. std::string Message::sessionCreate(const std::string &sessionID,
  637. const std::string &nickname,
  638. const std::string &destination /*= SAM_DEFAULT_DESTINATION*/,
  639. const std::string &options /*= ""*/,
  640. const std::string &signatureType /*= SAM_DEFAULT_SIGNATURE_TYPE*/)
  641. {
  642. /**
  643. * -> SESSION CREATE
  644. * STYLE={STREAM,DATAGRAM,RAW}
  645. * ID={$nickname}
  646. * DESTINATION={$private_destination_key,TRANSIENT}
  647. * [option=value]*
  648. *
  649. * <- SESSION STATUS
  650. * RESULT=OK
  651. * DESTINATION=$private_destination_key
  652. */
  653. static constexpr char sessionCreateFormat[] =
  654. "SESSION CREATE STYLE=%s ID=%s DESTINATION=%s SIGNATURE_TYPE=%s inbound.nickname=%s %s\n";
  655. return createSAMRequest(sessionCreateFormat,
  656. "STREAM",
  657. sessionID.c_str(),
  658. destination.c_str(),
  659. signatureType.c_str(),
  660. nickname.c_str(),
  661. options.c_str());
  662. }
  663. std::string Message::streamAccept(const std::string &sessionID,
  664. bool silent /*= false*/)
  665. {
  666. /**
  667. * -> STREAM ACCEPT
  668. * ID={$nickname}
  669. * [SILENT={true,false}]
  670. *
  671. * <- STREAM STATUS
  672. * RESULT=$result
  673. * [MESSAGE=...]
  674. */
  675. static constexpr char streamAcceptFormat[] = "STREAM ACCEPT ID=%s SILENT=%s\n";
  676. return createSAMRequest(streamAcceptFormat,
  677. sessionID.c_str(),
  678. silent ? "true" : "false");
  679. }
  680. std::string Message::streamConnect(const std::string &sessionID,
  681. const std::string &destination,
  682. bool silent /*= false*/)
  683. {
  684. /**
  685. * -> STREAM CONNECT
  686. * ID={$nickname}
  687. * DESTINATION=$peer_public_base64_key
  688. * [SILENT={true,false}]
  689. *
  690. * <- STREAM STATUS
  691. * RESULT=$result
  692. * [MESSAGE=...]
  693. */
  694. static constexpr char streamConnectFormat[] = "STREAM CONNECT ID=%s DESTINATION=%s SILENT=%s\n";
  695. return createSAMRequest(streamConnectFormat,
  696. sessionID.c_str(),
  697. destination.c_str(),
  698. silent ? "true" : "false");
  699. }
  700. std::string Message::streamForward(const std::string &sessionID,
  701. const std::string &host,
  702. uint16_t port,
  703. bool silent /*= false*/)
  704. {
  705. /**
  706. * -> STREAM FORWARD
  707. * ID={$nickname}
  708. * PORT={$port}
  709. * [HOST={$host}]
  710. * [SILENT={true,false}]
  711. *
  712. * <- STREAM STATUS
  713. * RESULT=$result
  714. * [MESSAGE=...]
  715. */
  716. static constexpr char streamForwardFormat[] = "STREAM FORWARD ID=%s PORT=%u HOST=%s SILENT=%s\n";
  717. return createSAMRequest(streamForwardFormat,
  718. sessionID.c_str(),
  719. (unsigned) port,
  720. host.c_str(),
  721. silent ? "true" : "false");
  722. }
  723. std::string Message::namingLookup(const std::string &name)
  724. {
  725. if (name.find(".b32.i2p") != std::string::npos) return name;
  726. /**
  727. * -> NAMING LOOKUP
  728. * NAME=$name
  729. *
  730. * <- NAMING REPLY
  731. * RESULT=OK
  732. * NAME=$name
  733. * VALUE=$base64key
  734. */
  735. static constexpr char namingLookupFormat[] = "NAMING LOOKUP NAME=%s\n";
  736. return createSAMRequest(namingLookupFormat,
  737. name.c_str());
  738. }
  739. #define SAM_MAKESTRING(X) SAM_MAKESTRING2(X)
  740. #define SAM_MAKESTRING2(X) #X
  741. #define SAM_CHECK_RESULT(value) \
  742. if (result == SAM_MAKESTRING(value)) \
  743. return value
  744. Message::eStatus Message::checkAnswer(const std::string &answer) {
  745. if (answer.empty())
  746. return EMPTY_ANSWER;
  747. const std::string result = getValue(answer, "RESULT");
  748. SAM_CHECK_RESULT(OK);
  749. SAM_CHECK_RESULT(DUPLICATED_DEST);
  750. SAM_CHECK_RESULT(DUPLICATED_ID);
  751. SAM_CHECK_RESULT(I2P_ERROR);
  752. SAM_CHECK_RESULT(INVALID_ID);
  753. SAM_CHECK_RESULT(INVALID_KEY);
  754. SAM_CHECK_RESULT(CANT_REACH_PEER);
  755. SAM_CHECK_RESULT(TIMEOUT);
  756. SAM_CHECK_RESULT(NOVERSION);
  757. SAM_CHECK_RESULT(KEY_NOT_FOUND);
  758. SAM_CHECK_RESULT(PEER_NOT_FOUND);
  759. SAM_CHECK_RESULT(ALREADY_ACCEPTING);
  760. return CANNOT_PARSE_ERROR;
  761. }
  762. #undef SAM_CHECK_RESULT
  763. #undef SAM_MAKESTRING2
  764. #undef SAM_MAKESTRING
  765. std::string Message::getValue(const std::string &answer, const std::string &key) {
  766. if (key.empty())
  767. return std::string();
  768. const std::string keyPattern = key + "=";
  769. size_t valueStart = answer.find(keyPattern);
  770. if (valueStart == std::string::npos)
  771. return std::string();
  772. valueStart += keyPattern.length();
  773. size_t valueEnd = answer.find_first_of(' ', valueStart);
  774. if (valueEnd == std::string::npos)
  775. valueEnd = answer.find_first_of('\n', valueStart);
  776. return answer.substr(valueStart, valueEnd - valueStart);
  777. }
  778. } // namespace Samty