|
|
|
// Copyright (c) 2012-2013 giv
|
|
|
|
// Copyright (c) 2017 orignal
|
|
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
// EdDSA assumed
|
|
|
|
#ifndef I2PSAM_H
|
|
|
|
#define I2PSAM_H
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
#include <list>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <memory>
|
|
|
|
#include <utility>
|
|
|
|
#include <ostream>
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
//#define _WIN32_WINNT 0x0501
|
|
|
|
#define WIN32_LEAN_AND_MEAN 1
|
|
|
|
#include <winsock2.h>
|
|
|
|
#else
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <netinet/in.h> // for sockaddr_in
|
|
|
|
#include <arpa/inet.h> // for ntohs and htons
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// TODO: check a possible bug about cast -1 to SOCKET
|
|
|
|
#define SAM_INVALID_SOCKET (-1)
|
|
|
|
#define SAM_SOCKET_ERROR (-1)
|
|
|
|
|
|
|
|
#define SAM_DEFAULT_ADDRESS "127.0.0.1"
|
|
|
|
#define SAM_DEFAULT_PORT 7656
|
|
|
|
#define SAM_DEFAULT_MIN_VER "3.0"
|
|
|
|
#define SAM_DEFAULT_MAX_VER "3.0"
|
|
|
|
#define SAM_GENERATE_MY_DESTINATION "TRANSIENT"
|
|
|
|
#define SAM_MY_NAME "ME"
|
|
|
|
#define SAM_DEFAULT_I2P_OPTIONS ""
|
|
|
|
|
|
|
|
#define SAM_NAME_INBOUND_QUANTITY "inbound.quantity"
|
|
|
|
#define SAM_DEFAULT_INBOUND_QUANTITY 2
|
|
|
|
#define SAM_NAME_INBOUND_LENGTH "inbound.length"
|
|
|
|
#define SAM_DEFAULT_INBOUND_LENGTH 2
|
|
|
|
#define SAM_NAME_INBOUND_LENGTHVARIANCE "inbound.lengthVariance"
|
|
|
|
#define SAM_DEFAULT_INBOUND_LENGTHVARIANCE 0
|
|
|
|
#define SAM_NAME_INBOUND_BACKUPQUANTITY "inbound.backupquantity"
|
|
|
|
#define SAM_DEFAULT_INBOUND_BACKUPQUANTITY 0
|
|
|
|
#define SAM_NAME_INBOUND_ALLOWZEROHOP "inbound.allowzerohop"
|
|
|
|
#define SAM_DEFAULT_INBOUND_ALLOWZEROHOP true
|
|
|
|
#define SAM_NAME_INBOUND_IPRESTRICTION "inbound.iprestriction"
|
|
|
|
#define SAM_DEFAULT_INBOUND_IPRESTRICTION 2
|
|
|
|
#define SAM_NAME_OUTBOUND_QUANTITY "outbound.quantity"
|
|
|
|
#define SAM_DEFAULT_OUTBOUND_QUANTITY 2
|
|
|
|
#define SAM_NAME_OUTBOUND_LENGTH "outbound.length"
|
|
|
|
#define SAM_DEFAULT_OUTBOUND_LENGTH 2
|
|
|
|
#define SAM_NAME_OUTBOUND_LENGTHVARIANCE "outbound.lengthvariance"
|
|
|
|
#define SAM_DEFAULT_OUTBOUND_LENGTHVARIANCE 0
|
|
|
|
#define SAM_NAME_OUTBOUND_BACKUPQUANTITY "outbound.backupquantity"
|
|
|
|
#define SAM_DEFAULT_OUTBOUND_BACKUPQUANTITY 0
|
|
|
|
#define SAM_NAME_OUTBOUND_ALLOWZEROHOP "outbound.allowzerohop"
|
|
|
|
#define SAM_DEFAULT_OUTBOUND_ALLOWZEROHOP true
|
|
|
|
#define SAM_NAME_OUTBOUND_IPRESTRICTION "outbound.iprestriction"
|
|
|
|
#define SAM_DEFAULT_OUTBOUND_IPRESTRICTION 2
|
|
|
|
#define SAM_NAME_OUTBOUND_PRIORITY "outbound.priority"
|
|
|
|
#define SAM_DEFAULT_OUTBOUND_PRIORITY 0
|
|
|
|
|
|
|
|
namespace SAM
|
|
|
|
{
|
|
|
|
|
|
|
|
typedef int SOCKET;
|
|
|
|
|
|
|
|
class Message
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
enum SessionStyle
|
|
|
|
{
|
|
|
|
sssStream,
|
|
|
|
sssDatagram, // not supported now
|
|
|
|
sssRaw // not supported now
|
|
|
|
};
|
|
|
|
|
|
|
|
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);
|
|
|
|
static std::string sessionCreate(SessionStyle style, const std::string& sessionID, const std::string& nickname, const std::string& destination = SAM_GENERATE_MY_DESTINATION, const std::string& options = "");
|
|
|
|
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 std::string destGenerate();
|
|
|
|
|
|
|
|
static eStatus checkAnswer(const std::string& answer);
|
|
|
|
static std::string getValue(const std::string& answer, const std::string& key);
|
|
|
|
private:
|
|
|
|
static std::string createSAMRequest(const char* format, ...);
|
|
|
|
};
|
|
|
|
|
|
|
|
class Socket
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Socket(const std::string& SAMHost, uint16_t SAMPort, const std::string &minVer, const std::string& maxVer);
|
|
|
|
Socket(const sockaddr_in& addr, const std::string& minVer, const std::string& maxVer);
|
|
|
|
// explicit because we don't want to create any socket implicity
|
|
|
|
explicit Socket(const Socket& rhs); // creates a new socket with the same parameters
|
|
|
|
~Socket();
|
|
|
|
|
|
|
|
void write(const std::string& msg);
|
|
|
|
std::string read();
|
|
|
|
SOCKET release();
|
|
|
|
void close();
|
|
|
|
|
|
|
|
bool isOk() const;
|
|
|
|
|
|
|
|
const std::string& getVersion() const;
|
|
|
|
const std::string& getHost() const;
|
|
|
|
uint16_t getPort() const;
|
|
|
|
const std::string& getMinVer() const;
|
|
|
|
const std::string& getMaxVer() const;
|
|
|
|
|
|
|
|
const sockaddr_in& getAddress() const;
|
|
|
|
|
|
|
|
private:
|
|
|
|
SOCKET socket_;
|
|
|
|
sockaddr_in servAddr_;
|
|
|
|
std::string SAMHost_;
|
|
|
|
uint16_t SAMPort_;
|
|
|
|
const std::string minVer_;
|
|
|
|
const std::string maxVer_;
|
|
|
|
std::string version_;
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
static int instances_;
|
|
|
|
static void initWSA();
|
|
|
|
static void freeWSA();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void handshake();
|
|
|
|
void init();
|
|
|
|
|
|
|
|
Socket& operator=(const Socket&);
|
|
|
|
};
|
|
|
|
|
|
|
|
struct FullDestination
|
|
|
|
{
|
|
|
|
std::string pub;
|
|
|
|
std::string priv;
|
|
|
|
bool isGenerated;
|
|
|
|
|
|
|
|
FullDestination() {}
|
|
|
|
FullDestination(const std::string& pub, const std::string& priv, bool isGenerated)
|
|
|
|
:pub(pub), priv(priv), isGenerated(isGenerated) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
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::shared_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::shared_ptr<T> value;
|
|
|
|
|
|
|
|
RequestResult()
|
|
|
|
: isOk(false) {}
|
|
|
|
|
|
|
|
explicit RequestResult(std::shared_ptr<T>& value)
|
|
|
|
: isOk(true), value(value) {}
|
|
|
|
|
|
|
|
|
|
|
|
// some C++ magic
|
|
|
|
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 StreamSession
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
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);
|
|
|
|
explicit StreamSession(StreamSession& rhs);
|
|
|
|
~StreamSession();
|
|
|
|
|
|
|
|
static std::string generateSessionID();
|
|
|
|
|
|
|
|
RequestResult<std::shared_ptr<Socket> > accept(bool silent);
|
|
|
|
RequestResult<std::shared_ptr<Socket> > connect(const std::string& destination, bool silent);
|
|
|
|
RequestResult<void> forward(const std::string& host, uint16_t port, bool silent);
|
|
|
|
RequestResult<const std::string> namingLookup(const std::string& name) const;
|
|
|
|
RequestResult<const FullDestination> destGenerate() const;
|
|
|
|
|
|
|
|
void stopForwarding(const std::string& host, uint16_t port);
|
|
|
|
void stopForwardingAll();
|
|
|
|
|
|
|
|
const FullDestination& getMyDestination() const;
|
|
|
|
|
|
|
|
const sockaddr_in& getSAMAddress() const;
|
|
|
|
const std::string& getSAMHost() const;
|
|
|
|
uint16_t getSAMPort() const;
|
|
|
|
const std::string& getNickname() const;
|
|
|
|
const std::string& getSessionID() const;
|
|
|
|
const std::string& getSAMMinVer() const;
|
|
|
|
const std::string& getSAMMaxVer() const;
|
|
|
|
const std::string& getSAMVersion() const;
|
|
|
|
const std::string& getOptions() const;
|
|
|
|
|
|
|
|
bool isSick() const;
|
|
|
|
bool isReady () const { return socket_.isOk() && !isSick (); };
|
|
|
|
|
|
|
|
static std::ostream& getLogStream ();
|
|
|
|
static void SetLogFile (const std::string& filename);
|
|
|
|
static void CloseLogFile ();
|
|
|
|
|
|
|
|
private:
|
|
|
|
StreamSession(const StreamSession& rhs);
|
|
|
|
StreamSession& operator=(const StreamSession& rhs);
|
|
|
|
|
|
|
|
struct ForwardedStream
|
|
|
|
{
|
|
|
|
Socket* socket;
|
|
|
|
std::string host;
|
|
|
|
uint16_t port;
|
|
|
|
bool silent;
|
|
|
|
|
|
|
|
ForwardedStream(Socket* socket, const std::string& host, uint16_t port, bool silent)
|
|
|
|
: socket(socket), host(host), port(port), silent(silent) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef std::list<ForwardedStream> ForwardedStreamsContainer;
|
|
|
|
|
|
|
|
Socket socket_;
|
|
|
|
const std::string nickname_;
|
|
|
|
const std::string sessionID_;
|
|
|
|
FullDestination myDestination_;
|
|
|
|
const std::string i2pOptions_;
|
|
|
|
ForwardedStreamsContainer forwardedStreams_;
|
|
|
|
mutable bool isSick_;
|
|
|
|
static std::shared_ptr<std::ostream> logStream;
|
|
|
|
|
|
|
|
void fallSick() const;
|
|
|
|
FullDestination createStreamSession(const std::string &destination);
|
|
|
|
|
|
|
|
static Message::Answer<const std::string> rawRequest(Socket& socket, const std::string& requestStr);
|
|
|
|
static Message::Answer<const std::string> request(Socket& socket, const std::string& requestStr, const std::string& keyOnSuccess);
|
|
|
|
static Message::eStatus request(Socket& socket, const std::string& requestStr);
|
|
|
|
// commands
|
|
|
|
static Message::Answer<const std::string> createStreamSession(Socket& socket, const std::string& sessionID, const std::string& nickname, const std::string& destination, const std::string& options);
|
|
|
|
static Message::Answer<const std::string> namingLookup(Socket& socket, const std::string& name);
|
|
|
|
static Message::Answer<const FullDestination> destGenerate(Socket& socket);
|
|
|
|
|
|
|
|
static Message::eStatus accept(Socket& socket, const std::string& sessionID, bool silent);
|
|
|
|
static Message::eStatus connect(Socket& socket, const std::string& sessionID, const std::string& destination, bool silent);
|
|
|
|
static Message::eStatus forward(Socket& socket, const std::string& sessionID, const std::string& host, uint16_t port, bool silent);
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace SAM
|
|
|
|
|
|
|
|
#endif // I2PSAM_H
|