mirror of https://github.com/GOSTSec/gostcoin
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
805 lines
22 KiB
805 lines
22 KiB
// Copyright (c) 2012-2013 giv |
|
// Distributed under the MIT/X11 software license, see the accompanying |
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php. |
|
//-------------------------------------------------------------------------------------------------- |
|
#include "i2psam.h" |
|
|
|
#include <iostream> |
|
#include <stdio.h> |
|
#include <string.h> // for memset |
|
#include <stdlib.h> |
|
#include <time.h> |
|
#include <stdarg.h> |
|
#include <fstream> |
|
|
|
#ifndef WIN32 |
|
#include <errno.h> |
|
#include <unistd.h> |
|
#endif |
|
|
|
#ifndef WIN32 |
|
#define closesocket close |
|
#endif |
|
|
|
#define SAM_BUFSIZE 65536 |
|
#define I2P_DESTINATION_SIZE 521 // EcDSA, GOST and EdDSA, actual size is 524 with trailing A== |
|
|
|
namespace SAM |
|
{ |
|
|
|
static void print_error(const std::string& err) |
|
{ |
|
#ifdef WIN32 |
|
StreamSession::getLogStream () << err << "(" << WSAGetLastError() << ")" << std::endl; |
|
#else |
|
StreamSession::getLogStream () << err << "(" << errno << ")" << std::endl; |
|
#endif |
|
} |
|
|
|
#ifdef WIN32 |
|
int Socket::instances_ = 0; |
|
|
|
void Socket::initWSA() |
|
{ |
|
WSADATA wsadata; |
|
int ret = WSAStartup(MAKEWORD(2,2), &wsadata); |
|
if (ret != NO_ERROR) |
|
print_error("Failed to initialize winsock library"); |
|
} |
|
|
|
void Socket::freeWSA() |
|
{ |
|
WSACleanup(); |
|
} |
|
#endif |
|
|
|
Socket::Socket(const std::string& SAMHost, uint16_t SAMPort, const std::string& minVer, const std::string &maxVer) |
|
: socket_(SAM_INVALID_SOCKET), SAMHost_(SAMHost), SAMPort_(SAMPort), minVer_(minVer), maxVer_(maxVer) |
|
{ |
|
#ifdef WIN32 |
|
if (instances_++ == 0) |
|
initWSA(); |
|
#endif |
|
|
|
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); |
|
|
|
init(); |
|
if (isOk()) |
|
handshake(); |
|
} |
|
|
|
Socket::Socket(const sockaddr_in& addr, const std::string &minVer, const std::string& maxVer) |
|
: socket_(SAM_INVALID_SOCKET), servAddr_(addr), minVer_(minVer), maxVer_(maxVer) |
|
{ |
|
#ifdef WIN32 |
|
if (instances_++ == 0) |
|
initWSA(); |
|
#endif |
|
|
|
init(); |
|
if (isOk()) |
|
handshake(); |
|
} |
|
|
|
Socket::Socket(const Socket& rhs) |
|
: socket_(SAM_INVALID_SOCKET), servAddr_(rhs.servAddr_), minVer_(rhs.minVer_), maxVer_(rhs.maxVer_) |
|
{ |
|
#ifdef WIN32 |
|
if (instances_++ == 0) |
|
initWSA(); |
|
#endif |
|
|
|
init(); |
|
if (isOk()) |
|
handshake(); |
|
} |
|
|
|
Socket::~Socket() |
|
{ |
|
close(); |
|
|
|
#ifdef WIN32 |
|
if (--instances_ == 0) |
|
freeWSA(); |
|
#endif |
|
} |
|
|
|
void Socket::init() |
|
{ |
|
socket_ = socket(AF_INET, SOCK_STREAM, 0); |
|
if (socket_ == SAM_INVALID_SOCKET) |
|
{ |
|
print_error("Failed to create socket"); |
|
return; |
|
} |
|
|
|
if (connect(socket_, (const sockaddr*)&servAddr_, sizeof(servAddr_)) == SAM_SOCKET_ERROR) |
|
{ |
|
close(); |
|
print_error("Failed to connect to SAM"); |
|
return; |
|
} |
|
} |
|
|
|
SOCKET Socket::release() |
|
{ |
|
SOCKET temp = socket_; |
|
socket_ = SAM_INVALID_SOCKET; |
|
return temp; |
|
} |
|
|
|
void Socket::handshake() |
|
{ |
|
this->write(Message::hello(minVer_, maxVer_)); |
|
const std::string answer = this->read(); |
|
const Message::eStatus answerStatus = Message::checkAnswer(answer); |
|
if (answerStatus == Message::OK) |
|
version_ = Message::getValue(answer, "VERSION"); |
|
else |
|
print_error("Handshake failed"); |
|
} |
|
|
|
void Socket::write(const std::string& msg) |
|
{ |
|
if (!isOk()) |
|
{ |
|
print_error("Failed to send data because socket is closed"); |
|
return; |
|
} |
|
StreamSession::getLogStream () << "Send: " << msg << std::endl; |
|
ssize_t sentBytes = send(socket_, msg.c_str(), msg.length(), 0); |
|
if (sentBytes == SAM_SOCKET_ERROR) |
|
{ |
|
close(); |
|
print_error("Failed to send data"); |
|
return; |
|
} |
|
if (sentBytes == 0) |
|
{ |
|
close(); |
|
print_error("Socket was closed"); |
|
return; |
|
} |
|
} |
|
|
|
std::string Socket::read() |
|
{ |
|
if (!isOk()) |
|
{ |
|
print_error("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, 0); |
|
if (recievedBytes == SAM_SOCKET_ERROR) |
|
{ |
|
close(); |
|
print_error("Failed to receive data"); |
|
return std::string(); |
|
} |
|
if (recievedBytes == 0) |
|
{ |
|
close(); |
|
print_error("Socket was closed"); |
|
} |
|
StreamSession::getLogStream () << "Reply: " << buffer << std::endl; |
|
return std::string(buffer); |
|
} |
|
|
|
void Socket::close() |
|
{ |
|
if (socket_ != SAM_INVALID_SOCKET) |
|
::closesocket(socket_); |
|
socket_ = SAM_INVALID_SOCKET; |
|
} |
|
|
|
bool Socket::isOk() const |
|
{ |
|
return socket_ != SAM_INVALID_SOCKET; |
|
} |
|
|
|
const std::string& Socket::getHost() const |
|
{ |
|
return SAMHost_; |
|
} |
|
|
|
uint16_t Socket::getPort() const |
|
{ |
|
return SAMPort_; |
|
} |
|
|
|
const std::string& Socket::getVersion() const |
|
{ |
|
return version_; |
|
} |
|
|
|
const std::string& Socket::getMinVer() const |
|
{ |
|
return minVer_; |
|
} |
|
|
|
const std::string& Socket::getMaxVer() const |
|
{ |
|
return maxVer_; |
|
} |
|
|
|
const sockaddr_in& Socket::getAddress() const |
|
{ |
|
return servAddr_; |
|
} |
|
|
|
|
|
//-------------------------------------------------------------------------------------------------- |
|
|
|
StreamSession::StreamSession( |
|
const std::string& nickname, |
|
const std::string& SAMHost /*= SAM_DEFAULT_ADDRESS*/, |
|
uint16_t SAMPort /*= SAM_DEFAULT_PORT*/, |
|
const std::string& destination /*= SAM_GENERATE_MY_DESTINATION*/, |
|
const std::string& i2pOptions /*= SAM_DEFAULT_I2P_OPTIONS*/, |
|
const std::string& minVer /*= SAM_DEFAULT_MIN_VER*/, |
|
const std::string& maxVer /*= SAM_DEFAULT_MAX_VER*/) |
|
: socket_(SAMHost, SAMPort, minVer, maxVer) |
|
, nickname_(nickname) |
|
, sessionID_(generateSessionID()) |
|
, i2pOptions_(i2pOptions) |
|
, isSick_(false) |
|
{ |
|
myDestination_ = createStreamSession(destination); |
|
getLogStream () << "Created a brand new SAM session (" << sessionID_ << ")" << std::endl; |
|
} |
|
|
|
StreamSession::StreamSession(StreamSession& rhs) |
|
: socket_(rhs.socket_) |
|
, nickname_(rhs.nickname_) |
|
, sessionID_(generateSessionID()) |
|
, myDestination_(rhs.myDestination_) |
|
, i2pOptions_(rhs.i2pOptions_) |
|
, isSick_(false) |
|
{ |
|
rhs.fallSick(); |
|
rhs.socket_.close(); |
|
(void)createStreamSession(myDestination_.priv); |
|
|
|
for(ForwardedStreamsContainer::const_iterator it = rhs.forwardedStreams_.begin(), end = rhs.forwardedStreams_.end(); it != end; ++it) |
|
forward(it->host, it->port, it->silent); |
|
|
|
getLogStream () << "Created a new SAM session (" << sessionID_ << ") from another (" << rhs.sessionID_ << ")" << std::endl; |
|
} |
|
|
|
StreamSession::~StreamSession() |
|
{ |
|
stopForwardingAll(); |
|
getLogStream () << "Closing SAM session (" << sessionID_ << ") ..." << std::endl; |
|
} |
|
|
|
/*static*/ |
|
std::string StreamSession::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(NULL)); |
|
|
|
while(length < minSessionIDLength) |
|
length = rand() % maxSessionIDLength; |
|
|
|
while (length-- > 0) |
|
result += sessionIDAlphabet[rand() % (sizeof(sessionIDAlphabet)-1)]; |
|
|
|
return result; |
|
} |
|
|
|
RequestResult<std::shared_ptr<Socket> > StreamSession::accept(bool silent) |
|
{ |
|
typedef RequestResult<std::shared_ptr<Socket> > ResultType; |
|
|
|
std::shared_ptr<Socket> streamSocket(new Socket(socket_)); |
|
const Message::eStatus status = accept(*streamSocket, sessionID_, silent); |
|
switch(status) |
|
{ |
|
case Message::OK: |
|
return std::move (RequestResult<std::shared_ptr<Socket> >(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::shared_ptr<Socket> > StreamSession::connect(const std::string& destination, bool silent) |
|
{ |
|
typedef RequestResult<std::shared_ptr<Socket> > ResultType; |
|
|
|
std::shared_ptr<Socket> streamSocket(new Socket(socket_)); |
|
const Message::eStatus status = connect(*streamSocket, sessionID_, destination, silent); |
|
switch(status) |
|
{ |
|
case Message::OK: |
|
return ResultType(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::shared_ptr<Socket> newSocket(new Socket(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 = nullptr; // 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(); |
|
} |
|
|
|
RequestResult<const std::string> StreamSession::namingLookup(const std::string& name) const |
|
{ |
|
typedef RequestResult<const std::string> ResultType; |
|
typedef Message::Answer<const std::string> AnswerType; |
|
|
|
std::shared_ptr<Socket> newSocket(new Socket(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(); |
|
} |
|
|
|
RequestResult<const FullDestination> StreamSession::destGenerate() const |
|
{ |
|
typedef RequestResult<const FullDestination> ResultType; |
|
typedef Message::Answer<const FullDestination> AnswerType; |
|
|
|
std::shared_ptr<Socket> newSocket(new Socket(socket_)); |
|
const AnswerType answer = destGenerate(*newSocket); |
|
switch(answer.status) |
|
{ |
|
case Message::OK: |
|
return ResultType(answer.value); |
|
case Message::EMPTY_ANSWER: |
|
case Message::CLOSED_SOCKET: |
|
fallSick(); |
|
break; |
|
default: |
|
break; |
|
} |
|
return ResultType(); |
|
} |
|
|
|
FullDestination StreamSession::createStreamSession(const std::string& destination) |
|
{ |
|
typedef Message::Answer<const std::string> AnswerType; |
|
|
|
const AnswerType answer = createStreamSession(socket_, sessionID_, nickname_, destination, i2pOptions_); |
|
if (answer.status != Message::OK) |
|
{ |
|
fallSick(); |
|
return FullDestination(); |
|
} |
|
return FullDestination(answer.value.substr(0, I2P_DESTINATION_SIZE) + "A==", answer.value, (destination == SAM_GENERATE_MY_DESTINATION)); |
|
} |
|
|
|
void StreamSession::fallSick() const |
|
{ |
|
isSick_ = true; |
|
} |
|
|
|
void StreamSession::stopForwarding(const std::string& host, uint16_t port) |
|
{ |
|
for (ForwardedStreamsContainer::iterator 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 (ForwardedStreamsContainer::iterator it = forwardedStreams_.begin(); it != forwardedStreams_.end(); ++it) |
|
delete (it->socket); |
|
forwardedStreams_.clear(); |
|
} |
|
|
|
/*static*/ |
|
Message::Answer<const std::string> StreamSession::rawRequest(Socket& 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.read(); |
|
const Message::eStatus status = Message::checkAnswer(answer); |
|
return AnswerType(status, answer); |
|
} |
|
|
|
/*static*/ |
|
Message::Answer<const std::string> StreamSession::request(Socket& 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 StreamSession::request(Socket& socket, const std::string& requestStr) |
|
{ |
|
return rawRequest(socket, requestStr).status; |
|
} |
|
|
|
/*static*/ |
|
Message::Answer<const std::string> StreamSession::createStreamSession(Socket& socket, const std::string& sessionID, const std::string& nickname, const std::string& destination, const std::string& options) |
|
{ |
|
return request(socket, Message::sessionCreate(Message::sssStream, sessionID, nickname, destination, options), "DESTINATION"); |
|
} |
|
|
|
/*static*/ |
|
Message::Answer<const std::string> StreamSession::namingLookup(Socket& socket, const std::string& name) |
|
{ |
|
return request(socket, Message::namingLookup(name), "VALUE"); |
|
} |
|
|
|
/*static*/ |
|
Message::Answer<const FullDestination> StreamSession::destGenerate(Socket& socket) |
|
{ |
|
// while answer for a DEST GENERATE request doesn't contain a "RESULT" field we parse it manually |
|
typedef Message::Answer<const FullDestination> ResultType; |
|
|
|
if (!socket.isOk()) |
|
return ResultType(Message::CLOSED_SOCKET, FullDestination()); |
|
socket.write(Message::destGenerate()); |
|
const std::string answer = socket.read(); |
|
const std::string pub = Message::getValue(answer, "PUB"); |
|
const std::string priv = Message::getValue(answer, "PRIV"); |
|
return (!pub.empty() && !priv.empty()) ? ResultType(Message::OK, FullDestination(pub, priv, /*isGenerated*/ true)) : ResultType(Message::EMPTY_ANSWER, FullDestination()); |
|
} |
|
|
|
/*static*/ |
|
Message::eStatus StreamSession::accept(Socket& socket, const std::string& sessionID, bool silent) |
|
{ |
|
return request(socket, Message::streamAccept(sessionID, silent)); |
|
} |
|
|
|
/*static*/ |
|
Message::eStatus StreamSession::connect(Socket& socket, const std::string& sessionID, const std::string& destination, bool silent) |
|
{ |
|
return request(socket, Message::streamConnect(sessionID, destination, silent)); |
|
} |
|
|
|
/*static*/ |
|
Message::eStatus StreamSession::forward(Socket& socket, const std::string& sessionID, const std::string& host, uint16_t port, bool silent) |
|
{ |
|
return request(socket, Message::streamForward(sessionID, host, port, silent)); |
|
} |
|
|
|
const std::string& StreamSession::getNickname() const |
|
{ |
|
return nickname_; |
|
} |
|
|
|
const std::string& StreamSession::getSessionID() const |
|
{ |
|
return sessionID_; |
|
} |
|
|
|
const std::string& StreamSession::getOptions() const |
|
{ |
|
return i2pOptions_; |
|
} |
|
|
|
const FullDestination& StreamSession::getMyDestination() const |
|
{ |
|
return myDestination_; |
|
} |
|
|
|
bool StreamSession::isSick() const |
|
{ |
|
return isSick_; |
|
} |
|
|
|
const sockaddr_in& StreamSession::getSAMAddress() const |
|
{ |
|
return socket_.getAddress(); |
|
} |
|
|
|
const std::string& StreamSession::getSAMHost() const |
|
{ |
|
return socket_.getHost(); |
|
} |
|
|
|
uint16_t StreamSession::getSAMPort() const |
|
{ |
|
return socket_.getPort(); |
|
} |
|
|
|
const std::string& StreamSession::getSAMMinVer() const |
|
{ |
|
return socket_.getMinVer(); |
|
} |
|
|
|
const std::string& StreamSession::getSAMMaxVer() const |
|
{ |
|
return socket_.getMaxVer(); |
|
} |
|
|
|
const std::string& StreamSession::getSAMVersion() const |
|
{ |
|
return socket_.getVersion(); |
|
} |
|
|
|
std::shared_ptr<std::ostream> StreamSession::logStream; |
|
|
|
std::ostream& StreamSession::getLogStream () |
|
{ |
|
return logStream ? *logStream : std::cout; |
|
} |
|
|
|
void StreamSession::SetLogFile (const std::string& filename) |
|
{ |
|
logStream = std::make_shared<std::ofstream> (filename, std::ofstream::out | std::ofstream::trunc); |
|
} |
|
|
|
void StreamSession::CloseLogFile () |
|
{ |
|
logStream = nullptr; |
|
} |
|
|
|
//-------------------------------------------------------------------------------------------------- |
|
|
|
|
|
std::string Message::createSAMRequest(const char* format, ...) |
|
{ |
|
char buffer[SAM_BUFSIZE]; |
|
memset(buffer, 0, SAM_BUFSIZE); |
|
|
|
va_list args; |
|
va_start (args, format); |
|
const int sizeToSend = vsnprintf(buffer, SAM_BUFSIZE, format, args); |
|
va_end(args); |
|
|
|
if (sizeToSend < 0) |
|
{ |
|
print_error("Failed to format message"); |
|
return std::string(); |
|
} |
|
return std::string(buffer); |
|
} |
|
|
|
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 const char* helloFormat = "HELLO VERSION MIN=%s MAX=%s\n"; |
|
return createSAMRequest(helloFormat, minVer.c_str(), maxVer.c_str()); |
|
} |
|
|
|
std::string Message::sessionCreate(SessionStyle style, const std::string& sessionID, const std::string& nickname, const std::string& destination /*= SAM_GENERATE_MY_DESTINATION*/, const std::string& options /*= ""*/) |
|
{ |
|
/////////////////////////////////////////////////////////// |
|
// |
|
// -> SESSION CREATE |
|
// STYLE={STREAM,DATAGRAM,RAW} |
|
// ID={$nickname} |
|
// DESTINATION={$private_destination_key,TRANSIENT} |
|
// [option=value]* |
|
// |
|
// <- SESSION STATUS |
|
// RESULT=OK |
|
// DESTINATION=$private_destination_key |
|
// |
|
/////////////////////////////////////////////////////////// |
|
|
|
std::string sessionStyle; |
|
switch(style) |
|
{ |
|
case sssStream: sessionStyle = "STREAM"; break; |
|
case sssDatagram: sessionStyle = "DATAGRAM"; break; |
|
case sssRaw: sessionStyle = "RAW"; break; |
|
} |
|
|
|
static const char* sessionCreateFormat = "SESSION CREATE STYLE=%s ID=%s DESTINATION=%s inbound.nickname=%s SIGNATURE_TYPE=7 %s\n"; // we add inbound.nickname option |
|
return createSAMRequest(sessionCreateFormat, sessionStyle.c_str(), sessionID.c_str(), destination.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 const 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 const 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 const 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) |
|
{ |
|
/////////////////////////////////////////////////////////// |
|
// |
|
// -> NAMING LOOKUP |
|
// NAME=$name |
|
// |
|
// <- NAMING REPLY |
|
// RESULT=OK |
|
// NAME=$name |
|
// VALUE=$base64key |
|
// |
|
/////////////////////////////////////////////////////////// |
|
|
|
static const char* namingLookupFormat = "NAMING LOOKUP NAME=%s\n"; |
|
return createSAMRequest(namingLookupFormat, name.c_str()); |
|
} |
|
|
|
std::string Message::destGenerate() |
|
{ |
|
/////////////////////////////////////////////////////////// |
|
// |
|
// -> DEST GENERATE |
|
// |
|
// <- DEST REPLY |
|
// PUB=$pubkey |
|
// PRIV=$privkey |
|
// |
|
/////////////////////////////////////////////////////////// |
|
|
|
static const char* destGenerateFormat = "DEST GENERATE SIGNATURE_TYPE=7\n"; |
|
return createSAMRequest(destGenerateFormat); |
|
} |
|
|
|
#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 SAM
|
|
|