123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458 |
- /**
- * Copyleft c==8 2024 acetone - The Samty project
- * Copyright (c) 2019-2022 polistern
- * Copyright (c) 2017 The I2P Project
- * Copyright (c) 2013-2015 The Anoncoin Core developers
- * Copyright (c) 2012-2013 giv
- *
- * Distributed under the MIT software license, see the accompanying
- * file LICENSE or http://www.opensource.org/licenses/mit-license.php.
- */
- #pragma once
- #include "compat.h"
- #include "identity.h"
- #include <cstdint>
- #include <iostream>
- #include <list>
- #include <memory>
- #include <string>
- #include <utility>
- #include <vector>
- namespace Samty
- {
- constexpr uint32_t SAM_BUFSIZE = 64*1024;
- constexpr char SAM_DEFAULT_DESTINATION[] = "TRANSIENT";
- constexpr char SAM_DEFAULT_SIGNATURE_TYPE[] = "7"; // EdDSA_SHA512_Ed25519
- constexpr char SAM_BLINDED_SIGNATURE_TYPE[] = "11"; // RedDSA_SHA512_Ed25519
- typedef u_int SOCKET;
- struct Configuration
- {
- std::string nickname = "Samty";
- std::string SAMHost = "127.0.0.1";
- uint16_t SAMPort = 7656;
- uint8_t inboundLength = 3; // Possible correct values: 0-8
- uint8_t inboundQuantity = 3; // Possible correct values: 1-16
- uint8_t inboundVariance = 0; // Possible correct values: 0-3
- uint8_t outboundLength = 3; // Possible correct values: 0-8
- uint8_t outboundQuantity = 3; // Possible correct values: 1-16
- uint8_t outboundVariance = 0; // Possible correct values: 0-3
- bool encryptedLeaseSet = false; // https://geti2p.net/spec/encryptedleaseset
- bool publishLeaseSet = true; // Cannot initiate a connection from the outside if false
- std::string destination = SAM_DEFAULT_DESTINATION; // Using known keys will allow the static address to be used
- };
- class Message
- {
- public:
- enum eStatus
- {
- OK,
- EMPTY_ANSWER,
- CLOSED_SOCKET,
- CANNOT_PARSE_ERROR,
- /** The destination is already in use
- *
- * -> SESSION CREATE ...
- * <- SESSION STATUS RESULT=DUPLICATED_DEST
- */
- DUPLICATED_DEST,
- /**
- * The nickname is already associated with a session
- *
- * -> SESSION CREATE ...
- * <- SESSION STATUS RESULT=DUPLICATED_ID
- */
- DUPLICATED_ID,
- /**
- * A generic I2P error (e.g. I2CP disconnection, etc.)
- *
- * -> HELLO VERSION ...
- * <- HELLO REPLY RESULT=I2P_ERROR MESSAGE={$message}
- *
- * -> SESSION CREATE ...
- * <- SESSION STATUS RESULT=I2P_ERROR MESSAGE={$message}
- *
- * -> STREAM CONNECT ...
- * <- STREAM STATUS RESULT=I2P_ERROR MESSAGE={$message}
- *
- * -> STREAM ACCEPT ...
- * <- STREAM STATUS RESULT=I2P_ERROR MESSAGE={$message}
- *
- * -> STREAM FORWARD ...
- * <- STREAM STATUS RESULT=I2P_ERROR MESSAGE={$message}
- *
- * -> NAMING LOOKUP ...
- * <- NAMING REPLY RESULT=INVALID_KEY NAME={$name} MESSAGE={$message}
- */
- I2P_ERROR,
- /**
- * Stream session ID doesn't exist
- *
- * -> STREAM CONNECT ...
- * <- STREAM STATUS RESULT=INVALID_ID MESSAGE={$message}
- *
- * -> STREAM ACCEPT ...
- * <- STREAM STATUS RESULT=INVALID_ID MESSAGE={$message}
- *
- * -> STREAM FORWARD ...
- * <- STREAM STATUS RESULT=INVALID_ID MESSAGE={$message}
- */
- INVALID_ID,
- /**
- * The destination is not a valid private destination key
- *
- * -> SESSION CREATE ...
- * <- SESSION STATUS RESULT=INVALID_KEY MESSAGE={$message}
- *
- * -> STREAM CONNECT ...
- * <- STREAM STATUS RESULT=INVALID_KEY MESSAGE={$message}
- *
- * -> NAMING LOOKUP ...
- * <- NAMING REPLY RESULT=INVALID_KEY NAME={$name} MESSAGE={$message}
- */
- INVALID_KEY,
- /**
- * The peer exists, but cannot be reached
- *
- * -> STREAM CONNECT ...
- * <- STREAM STATUS RESULT=CANT_REACH_PEER MESSAGE={$message}
- */
- CANT_REACH_PEER,
- /**
- * Timeout while waiting for an event (e.g. peer answer)
- *
- * -> STREAM CONNECT ...
- * <- STREAM STATUS RESULT=TIMEOUT MESSAGE={$message}
- */
- TIMEOUT,
- /**
- * The SAM bridge cannot find a suitable version
- *
- * -> HELLO VERSION ...
- * <- HELLO REPLY RESULT=NOVERSION MESSAGE={$message}
- */
- NOVERSION,
- /**
- * The naming system can't resolve the given name
- *
- * -> NAMING LOOKUP ...
- * <- NAMING REPLY RESULT=INVALID_KEY NAME={$name} MESSAGE={$message}
- */
- KEY_NOT_FOUND,
- /**
- * The peer cannot be found on the network
- *
- * ??
- */
- PEER_NOT_FOUND,
- /**
- * ??
- *
- * -> STREAM ACCEPT
- * <- STREAM STATUS RESULT=ALREADY_ACCEPTING
- */
- ALREADY_ACCEPTING,
- /**
- * ??
- */
- FAILED,
- /**
- * ??
- */
- CLOSED
- };
- template<class T>
- struct Answer
- {
- const Message::eStatus status;
- T value;
- Answer(Message::eStatus status, const T &value)
- : status(status), value(value) {}
- explicit Answer(Message::eStatus status) : status(status), value() {}
- };
- static std::string hello(const std::string &minVer,
- const std::string &maxVer);
- // Stream session
- static std::string
- sessionCreate(const std::string &sessionID,
- const std::string &nickname,
- const std::string &destination = SAM_DEFAULT_DESTINATION,
- const std::string &options = "",
- const std::string &signatureType = SAM_DEFAULT_SIGNATURE_TYPE);
- static std::string streamAccept(const std::string &sessionID,
- bool silent = false);
- static std::string streamConnect(const std::string &sessionID,
- const std::string &destination,
- bool silent = false);
- static std::string streamForward(const std::string &sessionID,
- const std::string &host, uint16_t port,
- bool silent = false);
- static std::string namingLookup(const std::string &name);
- static eStatus checkAnswer(const std::string &answer);
- static std::string getValue(const std::string &answer,
- const std::string &key);
- private:
- template<typename... t_args>
- static std::string
- createSAMRequest(const char *msg) { return {msg}; }
- template<typename... t_args>
- static std::string
- createSAMRequest(const char *format, t_args &&... args)
- {
- const int bufferStatus = std::snprintf(nullptr, 0, format, args...);
- if (bufferStatus < 0)
- {
- std::cerr << "Samty::Message::createSAMRequest() Failed to allocate buffer" << std::endl;
- return {};
- }
- std::vector<char> buffer(bufferStatus + 1);
- const int status =
- std::snprintf(buffer.data(), buffer.size(), format, args...);
- if (status < 0)
- {
- std::cerr << "Samty::Message::createSAMRequest() Failed to format message" << std::endl;
- return {};
- }
- return {buffer.data()};
- }
- };
- class I2pSocket
- {
- public:
- I2pSocket(const std::string &SAMHost, uint16_t SAMPort);
- explicit I2pSocket(const sockaddr_in &addr); // explicit because we don't want to create any socket implicity
- explicit I2pSocket(I2pSocket&& another) = default; // for moving
- explicit I2pSocket(const I2pSocket &rhs); // creates a new socket with the same parameters
- ~I2pSocket();
- void bootstrapI2P();
- bool write(const std::vector<uint8_t> &data);
- bool write(const std::string &msg);
- std::string readDestination(); // NOT for silence mode!
- std::string readString();
- std::vector<uint8_t> readBinary();
- ssize_t readBytes(uint8_t *buffer, size_t length, int timeoutSec);
- SOCKET release();
- void close();
- bool isOk() const;
- const std::string &getVersion() const;
- const std::string &getHost() const;
- uint16_t getPort() const;
- const sockaddr_in &getAddress() const;
- const std::string minVer_ = "3.0";
- const std::string maxVer_ = "3.1";
- std::string errorString() const { return errorString_; }
- private:
- SOCKET socket_;
- sockaddr_in servAddr_;
- std::string SAMHost_;
- uint16_t SAMPort_;
- std::string version_;
- std::string errorString_;
- #ifdef WIN32
- static int instances_;
- static void initWSA();
- static void freeWSA();
- #endif // WIN32
- void handshake();
- void init();
- };
- template<class T>
- struct RequestResult
- {
- bool isOk;
- T value;
- RequestResult() : isOk(false) {}
- explicit RequestResult(const T &value) : isOk(true), value(value) {}
- };
- template<class T>
- struct RequestResult<std::unique_ptr<T>>
- {
- /**
- * a class-helper for resolving a problem with conversion
- * from temporary RequestResult to non-const RequestResult&
- */
- struct RequestResultRef
- {
- bool isOk;
- T *value;
- RequestResultRef(bool isOk, T *value) : isOk(isOk), value(value) {}
- };
- bool isOk;
- std::unique_ptr<T> value;
- RequestResult() : isOk(false) {}
- explicit RequestResult(std::unique_ptr<T> &&value)
- : isOk(true), value(std::move(value)) {}
- RequestResult(RequestResultRef ref) : isOk(ref.isOk), value(ref.value) {}
- RequestResult &operator=(RequestResultRef ref)
- {
- if (value.get() != ref.value)
- {
- isOk = ref.isOk;
- value.reset(ref.value);
- }
- return *this;
- }
- operator RequestResultRef()
- {
- return RequestResultRef(this->isOk, this->value.release());
- }
- };
- template<>
- struct RequestResult<void>
- {
- bool isOk;
- RequestResult() : isOk(false) {}
- explicit RequestResult(bool isOk) : isOk(isOk) {}
- };
- class SAMSession
- {
- public:
- SAMSession(const Configuration& configuration);
- SAMSession(SAMSession& rhs);
- virtual ~SAMSession() = default;
- static std::string generateSessionID();
- RequestResult<const std::string> namingLookup(const std::string &name) const;
- Identity createSession(const std::string &destination);
- Identity createSession(const std::string &destination, const std::string &sigType);
- virtual Identity createSession(const std::string &destination, const std::string &sigType, const std::string &i2pOptions) = 0;
- const Identity &getMyDestination() const { return myDestination_; }
- const sockaddr_in &getSAMAddress() const { return socket_.getAddress(); }
- const std::string &getSAMHost() const { return socket_.getHost(); }
- uint16_t getSAMPort() const { return socket_.getPort(); }
- const Configuration &getConfiguration() const { return config_; }
- const std::string &getSessionID() const { return sessionID_; }
- const std::string &getSAMMinVer() const { return socket_.minVer_; }
- const std::string &getSAMMaxVer() const { return socket_.maxVer_; }
- const std::string &getSAMVersion() const { return socket_.getVersion(); }
- const std::string &getOptions() const { return i2pOptions_; }
- bool isSick() const { return isSick_; }
- protected:
- static Message::Answer<const std::string> rawRequest(I2pSocket &socket, const std::string &requestStr);
- static Message::Answer<const std::string> request(I2pSocket &socket, const std::string &requestStr, const std::string &keyOnSuccess);
- static Message::eStatus request(I2pSocket &socket, const std::string &requestStr);
- static Message::Answer<const std::string> namingLookup(I2pSocket &socket, const std::string &name);
- void fallSick() const;
- I2pSocket socket_;
- const std::string sessionID_;
- Identity myDestination_;
- std::string i2pOptions_;
- mutable bool isSick_;
- const Configuration config_;
- };
- class StreamSession : public SAMSession
- {
- public:
- StreamSession(const Configuration& configuration);
- explicit StreamSession(StreamSession &rhs);
- ~StreamSession();
- RequestResult<std::unique_ptr<I2pSocket>> accept(bool silent);
- RequestResult<std::unique_ptr<I2pSocket>> connect(const std::string &destination, bool silent);
- RequestResult<void> forward(const std::string &host, uint16_t port, bool silent);
- void closeSocket();
- std::string errorString() const;
- void stopForwarding(const std::string &host, uint16_t port);
- void stopForwardingAll();
- private:
- StreamSession(const StreamSession &rhs);
- StreamSession &operator=(const StreamSession &rhs);
- struct ForwardedStream
- {
- I2pSocket *socket;
- std::string host;
- uint16_t port;
- bool silent;
- ForwardedStream(I2pSocket *socket, const std::string &host, uint16_t port,
- bool silent)
- : socket(socket), host(host), port(port), silent(silent) {}
- };
- typedef std::list<ForwardedStream> ForwardedStreamsContainer;
- ForwardedStreamsContainer forwardedStreams_;
- Identity createStreamSession(const std::string &destination, const std::string &sigType, const std::string &i2pOptions);
- Identity createSession(const std::string &destination, const std::string &sigType, const std::string &i2pOptions) override;
- // commands
- static Message::Answer<const std::string> createStreamSession(
- I2pSocket &socket, const std::string &sessionID,
- const std::string &nickname, const std::string &destination,
- const std::string &options, const std::string &signatureType);
- static Message::eStatus accept(I2pSocket &socket,
- const std::string &sessionID, bool silent);
- static Message::eStatus connect(I2pSocket &socket,
- const std::string &sessionID,
- const std::string &destination, bool silent);
- static Message::eStatus forward(I2pSocket &socket,
- const std::string &sessionID,
- const std::string &host, uint16_t port,
- bool silent);
- };
- } // namespace Samty
|