123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912 |
- /**
- * 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.
- */
- #include <ctime>
- #include <cstdlib>
- #include <cstring>
- #include "i2psam.h"
- namespace Samty
- {
- #ifdef WIN32
- int I2pSocket::instances_ = 0;
- void I2pSocket::initWSA()
- {
- WSADATA wsadata;
- int ret = WSAStartup(MAKEWORD(2, 2), &wsadata);
- if (ret != NO_ERROR)
- print_error("Failed to initialize winsock library");
- }
- void I2pSocket::freeWSA()
- {
- WSACleanup();
- }
- #endif // WIN32
- I2pSocket::I2pSocket(const std::string &SAMHost, uint16_t SAMPort)
- : socket_(INVALID_SOCKET), SAMHost_(SAMHost), SAMPort_(SAMPort) {
- #ifdef WIN32
- if (instances_++ == 0)
- initWSA();
- #endif // WIN32
- memset(&servAddr_, 0, sizeof(servAddr_));
- servAddr_.sin_family = AF_INET;
- servAddr_.sin_addr.s_addr = inet_addr(SAMHost.c_str());
- servAddr_.sin_port = htons(SAMPort);
- bootstrapI2P();
- }
- I2pSocket::I2pSocket(const sockaddr_in &addr)
- : socket_(INVALID_SOCKET), servAddr_(addr)
- {
- #ifdef WIN32
- if (instances_++ == 0)
- initWSA();
- #endif // WIN32
- bootstrapI2P();
- }
- I2pSocket::I2pSocket(const I2pSocket &rhs)
- : socket_(INVALID_SOCKET), servAddr_(rhs.servAddr_)
- {
- #ifdef WIN32
- if (instances_++ == 0)
- initWSA();
- #endif // WIN32
- bootstrapI2P();
- }
- I2pSocket::~I2pSocket()
- {
- close();
- #ifdef WIN32
- if (--instances_ == 0)
- freeWSA();
- #endif // WIN32
- }
- void I2pSocket::bootstrapI2P()
- {
- init();
- if (isOk())
- handshake();
- }
- void I2pSocket::init()
- {
- /**
- * Here is where the only real OS socket is called for creation to talk with the router,
- * the value returned is stored in our variable socket_ which holds the connection
- * to our stream for everything else
- */
- socket_ = socket(AF_INET, SOCK_STREAM, 0);
- if (socket_ == INVALID_SOCKET)
- {
- errorString_ = "Failed to create socket";
- return;
- }
- if (connect(socket_, (const sockaddr *) &servAddr_, sizeof(servAddr_)) == SOCKET_ERROR)
- {
- close();
- errorString_ = "Failed to connect to SAM";
- return;
- }
- }
- SOCKET I2pSocket::release()
- {
- SOCKET temp = socket_;
- socket_ = INVALID_SOCKET;
- return temp;
- }
- // If the handshake works, we're talking to a valid I2P router.
- void I2pSocket::handshake()
- {
- this->write(Message::hello(minVer_, maxVer_));
- const std::string answer = this->readString();
- const Message::eStatus answerStatus = Message::checkAnswer(answer);
- if (answerStatus == Message::OK)
- version_ = Message::getValue(answer, "VERSION");
- else
- errorString_ = "Handshake failed";
- }
- bool I2pSocket::write(const std::string &msg)
- {
- if (!isOk())
- {
- errorString_ = "Failed to send data because socket is closed";
- return false;
- }
- ssize_t sentBytes = send(socket_, msg.c_str(), msg.length(), 0);
- if (sentBytes == SOCKET_ERROR)
- {
- close();
- errorString_ = "Failed to send data";
- return false;
- }
- if (sentBytes == 0)
- {
- close();
- errorString_ = "I2pSocket was closed";
- return false;
- }
- return true;
- }
- bool I2pSocket::write(const std::vector<uint8_t> &data)
- {
- if (!isOk())
- {
- errorString_ = "Failed to send data because socket is closed";
- return false;
- }
- ssize_t sentBytes = send(socket_, data.data(), data.size(), 0);
- if (sentBytes == SOCKET_ERROR)
- {
- close();
- errorString_ = "Failed to send data";
- return false;
- }
- if (sentBytes == 0)
- {
- close();
- errorString_ = "I2pSocket was closed";
- return false;
- }
- return true;
- }
- std::string I2pSocket::readDestination()
- {
- std::string certificate;
- char byte = 0;
- while (true)
- {
- ssize_t bytes_received = recv(socket_, &byte, 1, 0);
- if (bytes_received < 1)
- {
- return std::string();
- }
- if (byte == '\n') break;
- certificate += byte;
- }
- Samty::Identity guestIdent;
- guestIdent.init(certificate);
- return guestIdent.dest32();
- }
- std::vector<uint8_t> I2pSocket::readBinary()
- {
- std::vector<uint8_t> result (SAM_BUFSIZE);
- if (!isOk())
- {
- errorString_ = "Failed to read data because socket is closed";
- return result;
- }
- ssize_t recievedBytes = recv(socket_, result.data(), SAM_BUFSIZE, 0);
- if (recievedBytes == SOCKET_ERROR)
- {
- close();
- errorString_ = "Failed to receive data";
- return result;
- }
- if (recievedBytes == 0)
- {
- close();
- errorString_ = "I2pSocket was closed";
- }
- return result;
- }
- ssize_t I2pSocket::readBytes(uint8_t *buffer, size_t length, int timeoutSec)
- {
- if (!isOk())
- {
- errorString_ = "Failed to read data because socket is closed";
- return -1;
- }
- #ifdef WIN32
- HANDLE socketEvent = WSACreateEvent();
- if (socketEvent == WSA_INVALID_EVENT)
- {
- errorString_ = "Failed to create event: " + std::to_string(WSAGetLastError());
- return -1;
- }
- WSAEventSelect(socket_, socketEvent, FD_READ);
- DWORD result = WSAWaitForMultipleEvents(1, &socketEvent, FALSE, timeoutMs, FALSE);
- if (result == WSA_WAIT_TIMEOUT)
- {
- WSACloseEvent(socketEvent);
- errorString_ = "Read timed out";
- return 0;
- }
- else if (result == WSA_WAIT_FAILED)
- {
- WSACloseEvent(socketEvent);
- errorString_ = "WSAWaitForMultipleEvents failed: " + std::to_string(WSAGetLastError());
- return -1;
- }
- WSACloseEvent(socketEvent);
- // Если достигнуто событие FD_READ
- ssize_t totalBytesRead = 0;
- ssize_t bytesRead = 0;
- while (totalBytesRead < length)
- {
- bytesRead = recv(socket_, reinterpret_cast<char*>(buffer + totalBytesRead), length - totalBytesRead, 0);
- if (bytesRead > 0)
- {
- totalBytesRead += bytesRead;
- }
- else if (bytesRead == 0)
- {
- // Соединение закрыто
- return totalBytesRead;
- }
- else
- {
- // Ошибка recv
- errorString_ = "Recv failed: " + std::to_string(WSAGetLastError());
- return -1;
- }
- }
- return totalBytesRead;
- #else
- fd_set readfds;
- struct timeval timeout;
- int result;
- FD_ZERO(&readfds);
- FD_SET(socket_, &readfds);
- timeout.tv_sec = timeoutSec;
- timeout.tv_usec = 0;
- result = select(socket_ + 1, &readfds, nullptr, nullptr, &timeout);
- if (result == -1)
- {
- errorString_ = "Select failed: " + std::string(strerror(errno));
- return -1;
- }
- else if (result == 0)
- {
- errorString_ = "Read timed out";
- return 0;
- }
- if (FD_ISSET(socket_, &readfds))
- {
- ssize_t totalBytesRead = 0;
- ssize_t bytesRead = 0;
- while (totalBytesRead < length)
- {
- bytesRead = recv(socket_, buffer + totalBytesRead, length - totalBytesRead, 0);
- if (bytesRead > 0)
- {
- totalBytesRead += bytesRead;
- }
- else if (bytesRead == 0)
- {
- return totalBytesRead;
- }
- else
- {
- errorString_ = "Recv failed: " + std::string(strerror(errno));
- return -1;
- }
- }
- return totalBytesRead;
- }
- errorString_ = "Socket not ready for reading";
- return -1;
- #endif // if not WIN32
- }
- std::string I2pSocket::readString()
- {
- if (!isOk())
- {
- errorString_ = "Failed to read data because socket is closed";
- return std::string();
- }
- char buffer[SAM_BUFSIZE];
- memset(buffer, 0, SAM_BUFSIZE);
- ssize_t recievedBytes = recv(socket_, buffer, SAM_BUFSIZE-1, 0);
- if (recievedBytes == SOCKET_ERROR)
- {
- close();
- errorString_ = "Failed to receive data";
- return std::string();
- }
- if (recievedBytes == 0)
- {
- close();
- errorString_ = "I2pSocket was closed";
- }
- return std::string(buffer);
- }
- void I2pSocket::close()
- {
- if (socket_ != INVALID_SOCKET)
- {
- #ifdef WIN32
- ::closesocket(socket_);
- #else
- ::close(socket_);
- #endif // WIN32
- socket_ = INVALID_SOCKET;
- }
- }
- bool I2pSocket::isOk() const { return socket_ != INVALID_SOCKET; }
- const std::string &I2pSocket::getHost() const { return SAMHost_; }
- uint16_t I2pSocket::getPort() const { return SAMPort_; }
- const std::string &I2pSocket::getVersion() const { return version_; }
- const sockaddr_in &I2pSocket::getAddress() const { return servAddr_; }
- //-----------------------------------------------------------------------------
- SAMSession::SAMSession(const Configuration& configuration)
- : socket_(configuration.SAMHost, configuration.SAMPort),
- sessionID_(generateSessionID()),
- isSick_(false),
- config_(configuration)
- {
- i2pOptions_ = "i2cp.leaseSetEncType=4";
- if (not configuration.publishLeaseSet)
- {
- i2pOptions_ += " i2cp.dontPublishLeaseSet";
- }
- if (configuration.encryptedLeaseSet)
- {
- i2pOptions_ += " i2cp.leaseSetType=5";
- }
- i2pOptions_ += " inbound.quantity=" + std::to_string(configuration.inboundQuantity);
- i2pOptions_ += " inbound.length=" + std::to_string(configuration.inboundLength);
- i2pOptions_ += " inbound.lengthVariance=" + std::to_string(configuration.inboundVariance);
- i2pOptions_ += " outbound.quantity=" + std::to_string(configuration.outboundQuantity);
- i2pOptions_ += " outbound.length=" + std::to_string(configuration.outboundLength);
- i2pOptions_ += " outbound.lengthVariance=" + std::to_string(configuration.outboundVariance);
- }
- SAMSession::SAMSession(SAMSession& rhs)
- : socket_(rhs.socket_),
- sessionID_(generateSessionID()),
- myDestination_(rhs.myDestination_),
- i2pOptions_(rhs.i2pOptions_),
- isSick_(false),
- config_(rhs.config_)
- {
- rhs.fallSick();
- rhs.socket_.close();
- }
- /*static*/
- std::string SAMSession::generateSessionID()
- {
- static const int minSessionIDLength = 5;
- static const int maxSessionIDLength = 9;
- static const char sessionIDAlphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
- int length = minSessionIDLength - 1;
- std::string result;
- srand(time(nullptr));
- while (length < minSessionIDLength)
- length = rand() % maxSessionIDLength;
- while (length-- > 0)
- result += sessionIDAlphabet[rand() % (sizeof(sessionIDAlphabet) - 1)];
- return result;
- }
- RequestResult<const std::string> SAMSession::namingLookup(const std::string &name) const
- {
- typedef RequestResult<const std::string> ResultType;
- typedef Message::Answer<const std::string> AnswerType;
- if (name.find(".b32.i2p") != std::string::npos)
- {
- return ResultType(name);
- }
- std::unique_ptr<I2pSocket> newSocket(new I2pSocket(socket_));
- const AnswerType answer = namingLookup(*newSocket, name);
- switch (answer.status)
- {
- case Message::OK:
- return ResultType(answer.value);
- case Message::EMPTY_ANSWER:
- case Message::CLOSED_SOCKET:
- fallSick();
- break;
- default:
- break;
- }
- return ResultType();
- }
- Identity SAMSession::createSession(const std::string& destination)
- {
- return createSession(destination, SAM_DEFAULT_SIGNATURE_TYPE);
- }
- Identity SAMSession::createSession(const std::string& destination, const std::string& sigType)
- {
- return createSession(destination, sigType, i2pOptions_);
- }
- void SAMSession::fallSick() const { isSick_ = true; }
- /*static*/
- Message::Answer<const std::string> SAMSession::rawRequest(I2pSocket &socket, const std::string &requestStr)
- {
- typedef Message::Answer<const std::string> AnswerType;
- if (!socket.isOk())
- return AnswerType(Message::CLOSED_SOCKET);
- socket.write(requestStr);
- const std::string answer = socket.readString();
- const Message::eStatus status = Message::checkAnswer(answer);
- return AnswerType(status, answer);
- }
- /*static*/
- Message::Answer<const std::string> SAMSession::request(I2pSocket &socket,
- const std::string &requestStr,
- const std::string &keyOnSuccess)
- {
- typedef Message::Answer<const std::string> AnswerType;
- const AnswerType answer = rawRequest(socket, requestStr);
- return (answer.status == Message::OK) ? AnswerType(answer.status, Message::getValue(answer.value, keyOnSuccess))
- : answer;
- }
- /*static*/
- Message::eStatus SAMSession::request(I2pSocket &socket, const std::string &requestStr)
- {
- return rawRequest(socket, requestStr).status;
- }
- /*static*/
- Message::Answer<const std::string> SAMSession::namingLookup(I2pSocket &socket, const std::string &name)
- {
- return request(socket, Message::namingLookup(name), "VALUE");
- }
- //-----------------------------------------------------------------------------
- StreamSession::StreamSession(const Configuration& configuration)
- : SAMSession(configuration)
- {
- const std::string sigType = configuration.encryptedLeaseSet ? "11" : "7";
- myDestination_ = createStreamSession(configuration.destination, sigType, i2pOptions_);
- }
- StreamSession::StreamSession(StreamSession &rhs)
- : SAMSession(rhs)
- {
- rhs.fallSick();
- rhs.socket_.close();
- const std::string sigType = rhs.config_.encryptedLeaseSet ? "11" : "7";
- (void) createStreamSession(myDestination_.fullBase64(), sigType, rhs.i2pOptions_);
- for (const auto &forwardedStream : rhs.forwardedStreams_)
- forward(forwardedStream.host, forwardedStream.port, forwardedStream.silent);
- }
- StreamSession::~StreamSession()
- {
- stopForwardingAll();
- }
- RequestResult<std::unique_ptr<I2pSocket>> StreamSession::accept(bool silent)
- {
- typedef RequestResult<std::unique_ptr<I2pSocket>> ResultType;
- std::unique_ptr<I2pSocket> streamSocket(new I2pSocket(socket_));
- const Message::eStatus status = accept(*streamSocket, sessionID_, silent);
- switch (status)
- {
- case Message::OK:
- return ResultType(std::move(streamSocket));
- case Message::EMPTY_ANSWER:
- case Message::CLOSED_SOCKET:
- case Message::INVALID_ID:
- case Message::I2P_ERROR:
- fallSick();
- break;
- default:
- break;
- }
- return ResultType();
- }
- RequestResult<std::unique_ptr<I2pSocket>> StreamSession::connect(const std::string &destination, bool silent)
- {
- typedef RequestResult<std::unique_ptr<I2pSocket>> ResultType;
- std::unique_ptr<I2pSocket> streamSocket(new I2pSocket(socket_));
- const Message::eStatus status = connect(*streamSocket, sessionID_, destination, silent);
- if (silent)
- return ResultType(std::move(streamSocket));
- switch (status)
- {
- case Message::OK:
- return ResultType(std::move(streamSocket));
- case Message::EMPTY_ANSWER:
- case Message::CLOSED_SOCKET:
- case Message::INVALID_ID:
- case Message::I2P_ERROR:
- fallSick();
- break;
- default:
- break;
- }
- return ResultType();
- }
- RequestResult<void> StreamSession::forward(const std::string &host, uint16_t port, bool silent)
- {
- typedef RequestResult<void> ResultType;
- std::unique_ptr<I2pSocket> newSocket(new I2pSocket(socket_));
- const Message::eStatus status = forward(*newSocket, sessionID_, host, port, silent);
- switch (status)
- {
- case Message::OK:
- forwardedStreams_.push_back(ForwardedStream(newSocket.get(), host, port, silent));
- newSocket.release(); // release after successful push_back only
- return ResultType(true);
- case Message::EMPTY_ANSWER:
- case Message::CLOSED_SOCKET:
- case Message::INVALID_ID:
- case Message::I2P_ERROR:
- fallSick();
- break;
- default:
- break;
- }
- return ResultType();
- }
- void StreamSession::closeSocket()
- {
- socket_.close();
- }
- std::string StreamSession::errorString() const
- {
- return socket_.errorString();
- }
- Identity StreamSession::createStreamSession(const std::string& destination, const std::string& sigType, const std::string& i2pOptions)
- {
- typedef Message::Answer<const std::string> AnswerType;
- const AnswerType
- answer = createStreamSession(socket_, sessionID_, config_.nickname, destination, i2pOptions, sigType);
- if (answer.status != Message::OK)
- {
- fallSick();
- return Identity();
- }
- Identity ident;
- ident.init(answer.value, sigType == SAM_BLINDED_SIGNATURE_TYPE);
- return ident;
- }
- Identity StreamSession::createSession(const std::string& destination, const std::string& sigType, const std::string& i2pOptions)
- {
- typedef Message::Answer<const std::string> AnswerType;
- const AnswerType
- answer = createStreamSession(socket_, sessionID_, config_.nickname, destination, i2pOptions, sigType);
- if (answer.status != Message::OK)
- {
- fallSick();
- return Identity();
- }
- Identity ident;
- ident.init(answer.value);
- return ident;
- }
- void StreamSession::stopForwarding(const std::string &host, uint16_t port)
- {
- for (auto it = forwardedStreams_.begin(); it != forwardedStreams_.end();)
- {
- if (it->port == port && it->host == host)
- {
- delete (it->socket);
- it = forwardedStreams_.erase(it);
- }
- else
- ++it;
- }
- }
- void StreamSession::stopForwardingAll()
- {
- for (auto &forwardedStream : forwardedStreams_)
- delete (forwardedStream.socket);
- forwardedStreams_.clear();
- socket_.close();
- }
- /*static*/
- Message::Answer<const std::string> StreamSession::createStreamSession(I2pSocket &socket,
- const std::string &sessionID,
- const std::string &nickname,
- const std::string &destination,
- const std::string &options,
- const std::string &signatureType)
- {
- return request(socket,
- Message::sessionCreate(sessionID, nickname, destination, options, signatureType),
- "DESTINATION");
- }
- /*static*/
- Message::eStatus StreamSession::accept(I2pSocket &socket, const std::string &sessionID, bool silent)
- {
- return request(socket, Message::streamAccept(sessionID, silent));
- }
- /*static*/
- Message::eStatus StreamSession::connect(I2pSocket &socket,
- const std::string &sessionID,
- const std::string &destination,
- bool silent)
- {
- return request(socket, Message::streamConnect(sessionID, destination, silent));
- }
- /*static*/
- Message::eStatus StreamSession::forward(I2pSocket &socket,
- const std::string &sessionID,
- const std::string &host,
- uint16_t port,
- bool silent)
- {
- return request(socket, Message::streamForward(sessionID, host, port, silent));
- }
- //-----------------------------------------------------------------------------
- std::string Message::hello(const std::string &minVer, const std::string &maxVer)
- {
- /**
- * -> HELLO VERSION
- * MIN=$min
- * MAX=$max
- *
- * <- HELLO REPLY
- * RESULT=OK
- * VERSION=$version
- */
- static constexpr char helloFormat[] = "HELLO VERSION MIN=%s MAX=%s\n";
- return createSAMRequest(helloFormat, minVer.c_str(), maxVer.c_str());
- }
- std::string Message::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*/)
- {
- /**
- * -> SESSION CREATE
- * STYLE={STREAM,DATAGRAM,RAW}
- * ID={$nickname}
- * DESTINATION={$private_destination_key,TRANSIENT}
- * [option=value]*
- *
- * <- SESSION STATUS
- * RESULT=OK
- * DESTINATION=$private_destination_key
- */
- static constexpr char sessionCreateFormat[] =
- "SESSION CREATE STYLE=%s ID=%s DESTINATION=%s SIGNATURE_TYPE=%s inbound.nickname=%s %s\n";
- return createSAMRequest(sessionCreateFormat,
- "STREAM",
- sessionID.c_str(),
- destination.c_str(),
- signatureType.c_str(),
- nickname.c_str(),
- options.c_str());
- }
- std::string Message::streamAccept(const std::string &sessionID,
- bool silent /*= false*/)
- {
- /**
- * -> STREAM ACCEPT
- * ID={$nickname}
- * [SILENT={true,false}]
- *
- * <- STREAM STATUS
- * RESULT=$result
- * [MESSAGE=...]
- */
- static constexpr char streamAcceptFormat[] = "STREAM ACCEPT ID=%s SILENT=%s\n";
- return createSAMRequest(streamAcceptFormat,
- sessionID.c_str(),
- silent ? "true" : "false");
- }
- std::string Message::streamConnect(const std::string &sessionID,
- const std::string &destination,
- bool silent /*= false*/)
- {
- /**
- * -> STREAM CONNECT
- * ID={$nickname}
- * DESTINATION=$peer_public_base64_key
- * [SILENT={true,false}]
- *
- * <- STREAM STATUS
- * RESULT=$result
- * [MESSAGE=...]
- */
- static constexpr char streamConnectFormat[] = "STREAM CONNECT ID=%s DESTINATION=%s SILENT=%s\n";
- return createSAMRequest(streamConnectFormat,
- sessionID.c_str(),
- destination.c_str(),
- silent ? "true" : "false");
- }
- std::string Message::streamForward(const std::string &sessionID,
- const std::string &host,
- uint16_t port,
- bool silent /*= false*/)
- {
- /**
- * -> STREAM FORWARD
- * ID={$nickname}
- * PORT={$port}
- * [HOST={$host}]
- * [SILENT={true,false}]
- *
- * <- STREAM STATUS
- * RESULT=$result
- * [MESSAGE=...]
- */
- static constexpr char streamForwardFormat[] = "STREAM FORWARD ID=%s PORT=%u HOST=%s SILENT=%s\n";
- return createSAMRequest(streamForwardFormat,
- sessionID.c_str(),
- (unsigned) port,
- host.c_str(),
- silent ? "true" : "false");
- }
- std::string Message::namingLookup(const std::string &name)
- {
- if (name.find(".b32.i2p") != std::string::npos) return name;
- /**
- * -> NAMING LOOKUP
- * NAME=$name
- *
- * <- NAMING REPLY
- * RESULT=OK
- * NAME=$name
- * VALUE=$base64key
- */
- static constexpr char namingLookupFormat[] = "NAMING LOOKUP NAME=%s\n";
- return createSAMRequest(namingLookupFormat,
- name.c_str());
- }
- #define SAM_MAKESTRING(X) SAM_MAKESTRING2(X)
- #define SAM_MAKESTRING2(X) #X
- #define SAM_CHECK_RESULT(value) \
- if (result == SAM_MAKESTRING(value)) \
- return value
- Message::eStatus Message::checkAnswer(const std::string &answer) {
- if (answer.empty())
- return EMPTY_ANSWER;
- const std::string result = getValue(answer, "RESULT");
- SAM_CHECK_RESULT(OK);
- SAM_CHECK_RESULT(DUPLICATED_DEST);
- SAM_CHECK_RESULT(DUPLICATED_ID);
- SAM_CHECK_RESULT(I2P_ERROR);
- SAM_CHECK_RESULT(INVALID_ID);
- SAM_CHECK_RESULT(INVALID_KEY);
- SAM_CHECK_RESULT(CANT_REACH_PEER);
- SAM_CHECK_RESULT(TIMEOUT);
- SAM_CHECK_RESULT(NOVERSION);
- SAM_CHECK_RESULT(KEY_NOT_FOUND);
- SAM_CHECK_RESULT(PEER_NOT_FOUND);
- SAM_CHECK_RESULT(ALREADY_ACCEPTING);
- return CANNOT_PARSE_ERROR;
- }
- #undef SAM_CHECK_RESULT
- #undef SAM_MAKESTRING2
- #undef SAM_MAKESTRING
- std::string Message::getValue(const std::string &answer, const std::string &key) {
- if (key.empty())
- return std::string();
- const std::string keyPattern = key + "=";
- size_t valueStart = answer.find(keyPattern);
- if (valueStart == std::string::npos)
- return std::string();
- valueStart += keyPattern.length();
- size_t valueEnd = answer.find_first_of(' ', valueStart);
- if (valueEnd == std::string::npos)
- valueEnd = answer.find_first_of('\n', valueStart);
- return answer.substr(valueStart, valueEnd - valueStart);
- }
- } // namespace Samty
|