i2psam.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  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. #pragma once
  12. #include "compat.h"
  13. #include "identity.h"
  14. #include <cstdint>
  15. #include <iostream>
  16. #include <list>
  17. #include <memory>
  18. #include <string>
  19. #include <utility>
  20. #include <vector>
  21. namespace Samty
  22. {
  23. constexpr uint32_t SAM_BUFSIZE = 64*1024;
  24. constexpr char SAM_DEFAULT_DESTINATION[] = "TRANSIENT";
  25. constexpr char SAM_DEFAULT_SIGNATURE_TYPE[] = "7"; // EdDSA_SHA512_Ed25519
  26. constexpr char SAM_BLINDED_SIGNATURE_TYPE[] = "11"; // RedDSA_SHA512_Ed25519
  27. typedef u_int SOCKET;
  28. struct Configuration
  29. {
  30. std::string nickname = "Samty";
  31. std::string SAMHost = "127.0.0.1";
  32. uint16_t SAMPort = 7656;
  33. uint8_t inboundLength = 3; // Possible correct values: 0-8
  34. uint8_t inboundQuantity = 3; // Possible correct values: 1-16
  35. uint8_t inboundVariance = 0; // Possible correct values: 0-3
  36. uint8_t outboundLength = 3; // Possible correct values: 0-8
  37. uint8_t outboundQuantity = 3; // Possible correct values: 1-16
  38. uint8_t outboundVariance = 0; // Possible correct values: 0-3
  39. bool encryptedLeaseSet = false; // https://geti2p.net/spec/encryptedleaseset
  40. bool publishLeaseSet = true; // Cannot initiate a connection from the outside if false
  41. std::string destination = SAM_DEFAULT_DESTINATION; // Using known keys will allow the static address to be used
  42. };
  43. class Message
  44. {
  45. public:
  46. enum eStatus
  47. {
  48. OK,
  49. EMPTY_ANSWER,
  50. CLOSED_SOCKET,
  51. CANNOT_PARSE_ERROR,
  52. /** The destination is already in use
  53. *
  54. * -> SESSION CREATE ...
  55. * <- SESSION STATUS RESULT=DUPLICATED_DEST
  56. */
  57. DUPLICATED_DEST,
  58. /**
  59. * The nickname is already associated with a session
  60. *
  61. * -> SESSION CREATE ...
  62. * <- SESSION STATUS RESULT=DUPLICATED_ID
  63. */
  64. DUPLICATED_ID,
  65. /**
  66. * A generic I2P error (e.g. I2CP disconnection, etc.)
  67. *
  68. * -> HELLO VERSION ...
  69. * <- HELLO REPLY RESULT=I2P_ERROR MESSAGE={$message}
  70. *
  71. * -> SESSION CREATE ...
  72. * <- SESSION STATUS RESULT=I2P_ERROR MESSAGE={$message}
  73. *
  74. * -> STREAM CONNECT ...
  75. * <- STREAM STATUS RESULT=I2P_ERROR MESSAGE={$message}
  76. *
  77. * -> STREAM ACCEPT ...
  78. * <- STREAM STATUS RESULT=I2P_ERROR MESSAGE={$message}
  79. *
  80. * -> STREAM FORWARD ...
  81. * <- STREAM STATUS RESULT=I2P_ERROR MESSAGE={$message}
  82. *
  83. * -> NAMING LOOKUP ...
  84. * <- NAMING REPLY RESULT=INVALID_KEY NAME={$name} MESSAGE={$message}
  85. */
  86. I2P_ERROR,
  87. /**
  88. * Stream session ID doesn't exist
  89. *
  90. * -> STREAM CONNECT ...
  91. * <- STREAM STATUS RESULT=INVALID_ID MESSAGE={$message}
  92. *
  93. * -> STREAM ACCEPT ...
  94. * <- STREAM STATUS RESULT=INVALID_ID MESSAGE={$message}
  95. *
  96. * -> STREAM FORWARD ...
  97. * <- STREAM STATUS RESULT=INVALID_ID MESSAGE={$message}
  98. */
  99. INVALID_ID,
  100. /**
  101. * The destination is not a valid private destination key
  102. *
  103. * -> SESSION CREATE ...
  104. * <- SESSION STATUS RESULT=INVALID_KEY MESSAGE={$message}
  105. *
  106. * -> STREAM CONNECT ...
  107. * <- STREAM STATUS RESULT=INVALID_KEY MESSAGE={$message}
  108. *
  109. * -> NAMING LOOKUP ...
  110. * <- NAMING REPLY RESULT=INVALID_KEY NAME={$name} MESSAGE={$message}
  111. */
  112. INVALID_KEY,
  113. /**
  114. * The peer exists, but cannot be reached
  115. *
  116. * -> STREAM CONNECT ...
  117. * <- STREAM STATUS RESULT=CANT_REACH_PEER MESSAGE={$message}
  118. */
  119. CANT_REACH_PEER,
  120. /**
  121. * Timeout while waiting for an event (e.g. peer answer)
  122. *
  123. * -> STREAM CONNECT ...
  124. * <- STREAM STATUS RESULT=TIMEOUT MESSAGE={$message}
  125. */
  126. TIMEOUT,
  127. /**
  128. * The SAM bridge cannot find a suitable version
  129. *
  130. * -> HELLO VERSION ...
  131. * <- HELLO REPLY RESULT=NOVERSION MESSAGE={$message}
  132. */
  133. NOVERSION,
  134. /**
  135. * The naming system can't resolve the given name
  136. *
  137. * -> NAMING LOOKUP ...
  138. * <- NAMING REPLY RESULT=INVALID_KEY NAME={$name} MESSAGE={$message}
  139. */
  140. KEY_NOT_FOUND,
  141. /**
  142. * The peer cannot be found on the network
  143. *
  144. * ??
  145. */
  146. PEER_NOT_FOUND,
  147. /**
  148. * ??
  149. *
  150. * -> STREAM ACCEPT
  151. * <- STREAM STATUS RESULT=ALREADY_ACCEPTING
  152. */
  153. ALREADY_ACCEPTING,
  154. /**
  155. * ??
  156. */
  157. FAILED,
  158. /**
  159. * ??
  160. */
  161. CLOSED
  162. };
  163. template<class T>
  164. struct Answer
  165. {
  166. const Message::eStatus status;
  167. T value;
  168. Answer(Message::eStatus status, const T &value)
  169. : status(status), value(value) {}
  170. explicit Answer(Message::eStatus status) : status(status), value() {}
  171. };
  172. static std::string hello(const std::string &minVer,
  173. const std::string &maxVer);
  174. // Stream session
  175. static std::string
  176. sessionCreate(const std::string &sessionID,
  177. const std::string &nickname,
  178. const std::string &destination = SAM_DEFAULT_DESTINATION,
  179. const std::string &options = "",
  180. const std::string &signatureType = SAM_DEFAULT_SIGNATURE_TYPE);
  181. static std::string streamAccept(const std::string &sessionID,
  182. bool silent = false);
  183. static std::string streamConnect(const std::string &sessionID,
  184. const std::string &destination,
  185. bool silent = false);
  186. static std::string streamForward(const std::string &sessionID,
  187. const std::string &host, uint16_t port,
  188. bool silent = false);
  189. static std::string namingLookup(const std::string &name);
  190. static eStatus checkAnswer(const std::string &answer);
  191. static std::string getValue(const std::string &answer,
  192. const std::string &key);
  193. private:
  194. template<typename... t_args>
  195. static std::string
  196. createSAMRequest(const char *msg) { return {msg}; }
  197. template<typename... t_args>
  198. static std::string
  199. createSAMRequest(const char *format, t_args &&... args)
  200. {
  201. const int bufferStatus = std::snprintf(nullptr, 0, format, args...);
  202. if (bufferStatus < 0)
  203. {
  204. std::cerr << "Samty::Message::createSAMRequest() Failed to allocate buffer" << std::endl;
  205. return {};
  206. }
  207. std::vector<char> buffer(bufferStatus + 1);
  208. const int status =
  209. std::snprintf(buffer.data(), buffer.size(), format, args...);
  210. if (status < 0)
  211. {
  212. std::cerr << "Samty::Message::createSAMRequest() Failed to format message" << std::endl;
  213. return {};
  214. }
  215. return {buffer.data()};
  216. }
  217. };
  218. class I2pSocket
  219. {
  220. public:
  221. I2pSocket(const std::string &SAMHost, uint16_t SAMPort);
  222. explicit I2pSocket(const sockaddr_in &addr); // explicit because we don't want to create any socket implicity
  223. explicit I2pSocket(I2pSocket&& another) = default; // for moving
  224. explicit I2pSocket(const I2pSocket &rhs); // creates a new socket with the same parameters
  225. ~I2pSocket();
  226. void bootstrapI2P();
  227. bool write(const std::vector<uint8_t> &data);
  228. bool write(const std::string &msg);
  229. std::string readDestination(); // NOT for silence mode!
  230. std::string readString();
  231. std::vector<uint8_t> readBinary();
  232. ssize_t readBytes(uint8_t *buffer, size_t length, int timeoutSec);
  233. SOCKET release();
  234. void close();
  235. bool isOk() const;
  236. const std::string &getVersion() const;
  237. const std::string &getHost() const;
  238. uint16_t getPort() const;
  239. const sockaddr_in &getAddress() const;
  240. const std::string minVer_ = "3.0";
  241. const std::string maxVer_ = "3.1";
  242. std::string errorString() const { return errorString_; }
  243. private:
  244. SOCKET socket_;
  245. sockaddr_in servAddr_;
  246. std::string SAMHost_;
  247. uint16_t SAMPort_;
  248. std::string version_;
  249. std::string errorString_;
  250. #ifdef WIN32
  251. static int instances_;
  252. static void initWSA();
  253. static void freeWSA();
  254. #endif // WIN32
  255. void handshake();
  256. void init();
  257. };
  258. template<class T>
  259. struct RequestResult
  260. {
  261. bool isOk;
  262. T value;
  263. RequestResult() : isOk(false) {}
  264. explicit RequestResult(const T &value) : isOk(true), value(value) {}
  265. };
  266. template<class T>
  267. struct RequestResult<std::unique_ptr<T>>
  268. {
  269. /**
  270. * a class-helper for resolving a problem with conversion
  271. * from temporary RequestResult to non-const RequestResult&
  272. */
  273. struct RequestResultRef
  274. {
  275. bool isOk;
  276. T *value;
  277. RequestResultRef(bool isOk, T *value) : isOk(isOk), value(value) {}
  278. };
  279. bool isOk;
  280. std::unique_ptr<T> value;
  281. RequestResult() : isOk(false) {}
  282. explicit RequestResult(std::unique_ptr<T> &&value)
  283. : isOk(true), value(std::move(value)) {}
  284. RequestResult(RequestResultRef ref) : isOk(ref.isOk), value(ref.value) {}
  285. RequestResult &operator=(RequestResultRef ref)
  286. {
  287. if (value.get() != ref.value)
  288. {
  289. isOk = ref.isOk;
  290. value.reset(ref.value);
  291. }
  292. return *this;
  293. }
  294. operator RequestResultRef()
  295. {
  296. return RequestResultRef(this->isOk, this->value.release());
  297. }
  298. };
  299. template<>
  300. struct RequestResult<void>
  301. {
  302. bool isOk;
  303. RequestResult() : isOk(false) {}
  304. explicit RequestResult(bool isOk) : isOk(isOk) {}
  305. };
  306. class SAMSession
  307. {
  308. public:
  309. SAMSession(const Configuration& configuration);
  310. SAMSession(SAMSession& rhs);
  311. virtual ~SAMSession() = default;
  312. static std::string generateSessionID();
  313. RequestResult<const std::string> namingLookup(const std::string &name) const;
  314. Identity createSession(const std::string &destination);
  315. Identity createSession(const std::string &destination, const std::string &sigType);
  316. virtual Identity createSession(const std::string &destination, const std::string &sigType, const std::string &i2pOptions) = 0;
  317. const Identity &getMyDestination() const { return myDestination_; }
  318. const sockaddr_in &getSAMAddress() const { return socket_.getAddress(); }
  319. const std::string &getSAMHost() const { return socket_.getHost(); }
  320. uint16_t getSAMPort() const { return socket_.getPort(); }
  321. const Configuration &getConfiguration() const { return config_; }
  322. const std::string &getSessionID() const { return sessionID_; }
  323. const std::string &getSAMMinVer() const { return socket_.minVer_; }
  324. const std::string &getSAMMaxVer() const { return socket_.maxVer_; }
  325. const std::string &getSAMVersion() const { return socket_.getVersion(); }
  326. const std::string &getOptions() const { return i2pOptions_; }
  327. bool isSick() const { return isSick_; }
  328. protected:
  329. static Message::Answer<const std::string> rawRequest(I2pSocket &socket, const std::string &requestStr);
  330. static Message::Answer<const std::string> request(I2pSocket &socket, const std::string &requestStr, const std::string &keyOnSuccess);
  331. static Message::eStatus request(I2pSocket &socket, const std::string &requestStr);
  332. static Message::Answer<const std::string> namingLookup(I2pSocket &socket, const std::string &name);
  333. void fallSick() const;
  334. I2pSocket socket_;
  335. const std::string sessionID_;
  336. Identity myDestination_;
  337. std::string i2pOptions_;
  338. mutable bool isSick_;
  339. const Configuration config_;
  340. };
  341. class StreamSession : public SAMSession
  342. {
  343. public:
  344. StreamSession(const Configuration& configuration);
  345. explicit StreamSession(StreamSession &rhs);
  346. ~StreamSession();
  347. RequestResult<std::unique_ptr<I2pSocket>> accept(bool silent);
  348. RequestResult<std::unique_ptr<I2pSocket>> connect(const std::string &destination, bool silent);
  349. RequestResult<void> forward(const std::string &host, uint16_t port, bool silent);
  350. void closeSocket();
  351. std::string errorString() const;
  352. void stopForwarding(const std::string &host, uint16_t port);
  353. void stopForwardingAll();
  354. private:
  355. StreamSession(const StreamSession &rhs);
  356. StreamSession &operator=(const StreamSession &rhs);
  357. struct ForwardedStream
  358. {
  359. I2pSocket *socket;
  360. std::string host;
  361. uint16_t port;
  362. bool silent;
  363. ForwardedStream(I2pSocket *socket, const std::string &host, uint16_t port,
  364. bool silent)
  365. : socket(socket), host(host), port(port), silent(silent) {}
  366. };
  367. typedef std::list<ForwardedStream> ForwardedStreamsContainer;
  368. ForwardedStreamsContainer forwardedStreams_;
  369. Identity createStreamSession(const std::string &destination, const std::string &sigType, const std::string &i2pOptions);
  370. Identity createSession(const std::string &destination, const std::string &sigType, const std::string &i2pOptions) override;
  371. // commands
  372. static Message::Answer<const std::string> createStreamSession(
  373. I2pSocket &socket, const std::string &sessionID,
  374. const std::string &nickname, const std::string &destination,
  375. const std::string &options, const std::string &signatureType);
  376. static Message::eStatus accept(I2pSocket &socket,
  377. const std::string &sessionID, bool silent);
  378. static Message::eStatus connect(I2pSocket &socket,
  379. const std::string &sessionID,
  380. const std::string &destination, bool silent);
  381. static Message::eStatus forward(I2pSocket &socket,
  382. const std::string &sessionID,
  383. const std::string &host, uint16_t port,
  384. bool silent);
  385. };
  386. } // namespace Samty