Added basic support for UDP and RAW sessions

This commit is contained in:
polistern
2021-11-11 16:34:50 +00:00
parent 21940ef1a5
commit fdec8b4b13
4 changed files with 1956 additions and 1275 deletions

View File

@ -1,34 +1,41 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin developers
// Copyright (c) 2013-2015 The Anoncoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
/**
* Copyright (c) 2009-2010 Satoshi Nakamoto
* Copyright (c) 2009-2014 The Bitcoin developers
* Copyright (c) 2013-2015 The Anoncoin Core developers
* Distributed under the MIT software license, see the accompanying
* file COPYING or http://www.opensource.org/licenses/mit-license.php.
*/
#ifndef I2PSAM_COMPAT_H
#define I2PSAM_COMPAT_H
#ifdef WIN32
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0501
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#ifdef FD_SETSIZE
#undef FD_SETSIZE // prevent redefinition compiler warning
#endif
#define FD_SETSIZE 1024 // max number of fds in fd_set
#include <winsock2.h> // Must be included before mswsock.h and windows.h
#include <winsock2.h> // Must be included before mswsock.h and windows.h
#include <mswsock.h>
#include <windows.h>
#include <ws2tcpip.h>
#else
#else // NO WIN32
#include <sys/fcntl.h>
#include <sys/mman.h>
#include <sys/socket.h>
@ -40,33 +47,33 @@
#include <limits.h>
#include <netdb.h>
#include <unistd.h>
#endif
#endif // WIN32
#ifdef WIN32
#define MSG_DONTWAIT 0
#define MSG_DONTWAIT 0
#else
typedef u_int SOCKET;
#include "errno.h"
#define WSAGetLastError() errno
#define WSAEINVAL EINVAL
#define WSAEALREADY EALREADY
#define WSAEWOULDBLOCK EWOULDBLOCK
#define WSAEMSGSIZE EMSGSIZE
#define WSAEINTR EINTR
#define WSAEINPROGRESS EINPROGRESS
#define WSAEADDRINUSE EADDRINUSE
#define WSAENOTSOCK EBADF
#define INVALID_SOCKET (SOCKET)(~0)
#define SOCKET_ERROR -1
#define WSAGetLastError() errno
#define WSAEINVAL EINVAL
#define WSAEALREADY EALREADY
#define WSAEWOULDBLOCK EWOULDBLOCK
#define WSAEMSGSIZE EMSGSIZE
#define WSAEINTR EINTR
#define WSAEINPROGRESS EINPROGRESS
#define WSAEADDRINUSE EADDRINUSE
#define WSAENOTSOCK EBADF
#define INVALID_SOCKET (SOCKET)(~0)
#define SOCKET_ERROR -1
#endif
#ifdef WIN32
#ifndef S_IRUSR
#define S_IRUSR 0400
#define S_IWUSR 0200
#define S_IRUSR 0400
#define S_IWUSR 0200
#endif
#else
#define MAX_PATH 1024
#define MAX_PATH 1024
#endif
// As Solaris does not have the MSG_NOSIGNAL flag for send(2) syscall, it is defined as 0
@ -79,12 +86,12 @@ typedef u_int SOCKET;
#ifndef PRIO_MAX
#define PRIO_MAX 20
#endif
#define THREAD_PRIORITY_LOWEST PRIO_MAX
#define THREAD_PRIORITY_BELOW_NORMAL 2
#define THREAD_PRIORITY_NORMAL 0
#define THREAD_PRIORITY_ABOVE_NORMAL (-2)
#define THREAD_PRIORITY_LOWEST PRIO_MAX
#define THREAD_PRIORITY_BELOW_NORMAL 2
#define THREAD_PRIORITY_NORMAL 0
#define THREAD_PRIORITY_ABOVE_NORMAL (-2)
#endif
size_t strnlen_int( const char *start, size_t max_len);
size_t strnlen_int(const char *start, size_t max_len);
#endif // I2PSAM_COMPAT_H

View File

@ -1,9 +1,10 @@
//
// c wrapper for i2psam
// Author: jeff
// License: MIT
// probably contains bugs :-DDDD
//
/**
* c wrapper for i2psam
* Author: jeff
* License: MIT
* probably contains bugs :-DDDD
*/
#ifndef I2PSAM_C_H
#define I2PSAM_C_H
@ -14,185 +15,184 @@ struct i2psam_destination;
struct i2psam_stream_session;
struct i2psam_socket;
struct i2psam_stream_settings
{
struct i2psam_stream_settings {
/**
hostname of sam interface
* hostname of sam interface
*/
const char * samhost;
const char *samhost;
/**
port of sam interface
* port of sam interface
*/
const uint16_t samport;
/**
nickname for sam session
* nickname for sam session
*/
const char * nickname;
const char *nickname;
/**
i2cp options string
* i2cp options string
*/
const char * i2cp_opts;
const char *i2cp_opts;
/**
destination private key
* destination private key
*/
const char * destination;
const char *destination;
};
/**
create new stream session
* create new stream session
*/
struct i2psam_stream_session * i2psam_stream_session_new(struct i2psam_stream_settings *);
struct i2psam_stream_session *i2psam_stream_session_new(struct i2psam_stream_settings *);
/**
close and free stream session
* close and free stream session
*/
void i2psam_stream_session_free(struct i2psam_stream_session *);
/**
get sam host of stream session
@return must be free()'d by caller
* get sam host of stream session
* @return must be free()'d by caller
*/
const char * i2psam_get_samhost(struct i2psam_stream_session *);
const char *i2psam_get_samhost(struct i2psam_stream_session *);
/**
get sam port of stream session
* get sam port of stream session
*/
uint16_t i2psam_get_samport(struct i2psam_stream_session *);
/**
get sam session's nickname
@return must be free()'d by caller
* get sam session's nickname
* @return must be free()'d by caller
*/
const char * i2psam_get_nickname(struct i2psam_stream_session *);
const char *i2psam_get_nickname(struct i2psam_stream_session *);
/**
get sam session's id
@return must be free()'d by caller
* get sam session's id
* @return must be free()'d by caller
*/
const char * i2psam_get_session_id(struct i2psam_stream_session *);
const char *i2psam_get_session_id(struct i2psam_stream_session *);
/**
get min version from sam session's handshake
@return must be free()'d by caller
* get min version from sam session's handshake
* @return must be free()'d by caller
*/
const char * i2psam_get_sam_min_version(struct i2psam_stream_session *);
const char *i2psam_get_sam_min_version(struct i2psam_stream_session *);
/**
get max version from sam session's handshake
@return must be free()'d by caller
* get max version from sam session's handshake
* @return must be free()'d by caller
*/
const char * i2psam_get_sam_max_version(struct i2psam_stream_session *);
const char *i2psam_get_sam_max_version(struct i2psam_stream_session *);
/**
get current version in use with sam session
@return must be free()'d by caller
* get current version in use with sam session
* @return must be free()'d by caller
*/
const char * i2psam_get_sam_version(struct i2psam_stream_session *);
const char *i2psam_get_sam_version(struct i2psam_stream_session *);
/**
get i2cp options used by sam session
@return must be free()'d by caller
* get i2cp options used by sam session
* @return must be free()'d by caller
*/
const char * i2psam_get_i2cp_options(struct i2psam_stream_session *);
const char *i2psam_get_i2cp_options(struct i2psam_stream_session *);
/**
return 1 if session is sick otherwise returns 0
* return 1 if session is sick otherwise returns 0
*/
int i2psam_is_sick(struct i2psam_stream_session *);
/**
accept a new inbound connection
@param silent 0 if we want to obtain the remote's destination, nonzero means don't
* accept a new inbound connection
* @param silent 0 if we want to obtain the remote's destination, nonzero means don't
*/
struct i2psam_socket * i2psam_accept(struct i2psam_stream_session *, int silent);
struct i2psam_socket *i2psam_accept(struct i2psam_stream_session *, int silent);
/**
connect to remote destination
@param destination full public destination base64 blob
@param silent 0 if we want to get verbose error info from connect, nonzero means don't
* connect to remote destination
* @param destination full public destination base64 blob
* @param silent 0 if we want to get verbose error info from connect, nonzero means don't
*/
struct i2psam_socket * i2psam_connect(struct i2psam_stream_session *, const char * destination, int silent);
struct i2psam_socket *i2psam_connect(struct i2psam_stream_session *, const char *destination, int silent);
/**
forward all inbound connections to a remote endpoint
@param host remote hostname of endpoint
@param port remote port of endpoint
@param silent 0 if we want to be verbose when forwarding to remote endpoint, nonzero means don't
@return -1 on fail, otherwise 0
* forward all inbound connections to a remote endpoint
* @param host remote hostname of endpoint
* @param port remote port of endpoint
* @param silent 0 if we want to be verbose when forwarding to remote endpoint, nonzero means don't
* @return -1 on fail, otherwise 0
*/
int i2psam_forward(struct i2psam_stream_session *, const char * host, uint16_t port, int silent);
int i2psam_forward(struct i2psam_stream_session *, const char *host, uint16_t port, int silent);
/**
do a name lookup, if return is non null caller must free()'d
@param name the name to resolve
@return public destination base64 blob for the name or NULL if the name lookup fails
* do a name lookup, if return is non null caller must free()'d
* @param name the name to resolve
* @return public destination base64 blob for the name or NULL if the name lookup fails
*/
const char * i2psam_namelookup(struct i2psam_stream_session *, const char * name);
const char *i2psam_namelookup(struct i2psam_stream_session *, const char *name);
/**
generate a new i2p destination keypair, return value must be free()'d when done
@return newly generated keypair
* generate a new i2p destination keypair, return value must be free()'d when done
* @return newly generated keypair
*/
struct i2psam_destination * i2psam_dest_generate(struct i2psam_stream_session *);
struct i2psam_destination *i2psam_dest_generate(struct i2psam_stream_session *);
/**
stop forwarding to remote endpoint
@param host hostname of remote endpoint
@param port port of remote endpoint
* stop forwarding to remote endpoint
* @param host hostname of remote endpoint
* @param port port of remote endpoint
*/
void i2psam_stop_forwarding(struct i2psam_stream_session*, const char * host, uint16_t port);
void i2psam_stop_forwarding(struct i2psam_stream_session *, const char *host, uint16_t port);
/**
stop forwarding to all remote endpoints
* stop forwarding to all remote endpoints
*/
void i2psam_stop_forwarding_all(struct i2psam_stream_session *);
/**
get remote destination for our stream session
@return must be free()'d by caller when done with it
* get remote destination for our stream session
* @return must be free()'d by caller when done with it
*/
struct i2psam_destination * i2psam_get_my_destination(struct i2psam_stream_session *);
struct i2psam_destination *i2psam_get_my_destination(struct i2psam_stream_session *);
/**
blocking write a buffer of data with an i2psocket
@param data buffer to be written
@param dlen size of buffer
* blocking write a buffer of data with an i2psocket
* @param data buffer to be written
* @param dlen size of buffer
*/
void i2psam_write(struct i2psam_socket *, const char * data, size_t dlen);
void i2psam_write(struct i2psam_socket *, const char *data, size_t dlen);
/**
blocking read on an i2p socket
@param pointer to size read
@return pointer to read segment, must be free()'d when done if error while reading returns nullptr
* blocking read on an i2p socket
* @param pointer to size read
* @return pointer to read segment, must be free()'d when done if error while reading returns nullptr
*/
char * i2psam_read(struct i2psam_socket *, size_t * dlen);
char *i2psam_read(struct i2psam_socket *, size_t *dlen);
/**
close an i2p socket, does not free()
* close an i2p socket, does not free()
*/
void i2psam_socket_close(struct i2psam_socket *);
/**
@return 1 if an i2p socket is okay otherwise returns 0
* @return 1 if an i2p socket is okay otherwise returns 0
*/
int i2psam_socket_is_ok(struct i2psam_socket *);
/**
free() an i2p socket, must be closed
* free() an i2p socket, must be closed
*/
void i2psam_socket_free(struct i2psam_socket *);
/**
get private key for destination as null terminated base64 string
@return must be free()'d by caller when done
* get private key for destination as null terminated base64 string
* @return must be free()'d by caller when done
*/
const char * i2psam_destination_priv(struct i2psam_destination *);
const char *i2psam_destination_priv(struct i2psam_destination *);
/**
get public base64 destination blob as null terminated string
@return must be free()'d by caller when done
* get public base64 destination blob as null terminated string
* @return must be free()'d by caller when done
*/
const char * i2psam_destination_pub(struct i2psam_destination *);
const char *i2psam_destination_pub(struct i2psam_destination *);
void i2psam_destination_free(struct i2psam_destination *);

2089
i2psam.cpp

File diff suppressed because it is too large Load Diff

901
i2psam.h
View File

@ -1,413 +1,606 @@
// 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.
//--------------------------------------------------------------------------------------------------
// see full documentation about SAM at http://www.i2p2.i2p/samv3.html
#ifndef I2PSAM_H
#define I2PSAM_H
/**
* Copyright (c) 2019-2020 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.
*
* see full documentation about SAM at http://www.i2p2.i2p/samv3.html
*/
#define SAM_DEFAULT_ADDRESS "127.0.0.1"
#define SAM_DEFAULT_PORT 7656
#ifndef I2PSAM_H__
#define I2PSAM_H__
#define SAM_BUFSIZE 8192
#define SAM_DEFAULT_ADDRESS "127.0.0.1"
#define SAM_DEFAULT_PORT_TCP 7656
#define SAM_DEFAULT_PORT_UDP 7655
#define SAM_DEFAULT_CLIENT_TCP 7666
#define SAM_DEFAULT_CLIENT_UDP 7667
#define SAM_GENERATE_MY_DESTINATION "TRANSIENT"
#define SAM_MY_NAME "ME"
#define SAM_DEFAULT_I2P_OPTIONS ""
#define SAM_MY_NAME "ME"
//#define SAM_DEFAULT_I2P_OPTIONS ""
#define SAM_DEFAULT_I2P_OPTIONS SAM_NAME_I2CP_LEASESET_ENC_TYPE "=" SAM_DEFAULT_I2CP_LEASESET_ENC_TYPE // i2cp.leaseSetEncType=0,4
#define SAM_SIGNATURE_TYPE "EdDSA_SHA512_Ed25519"
#define SAM_NAME_INBOUND_QUANTITY "inbound.quantity"
#define SAM_DEFAULT_INBOUND_QUANTITY 3 // Three tunnels is default now
#define SAM_NAME_INBOUND_LENGTH "inbound.length"
#define SAM_DEFAULT_INBOUND_LENGTH 3 // Three jumps is default now
#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 1 // One backup tunnel
#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 3
#define SAM_NAME_OUTBOUND_LENGTH "outbound.length"
#define SAM_DEFAULT_OUTBOUND_LENGTH 3
#define SAM_NAME_OUTBOUND_LENGTHVARIANCE "outbound.lengthVariance"
#define SAM_NAME_INBOUND_QUANTITY "inbound.quantity"
#define SAM_DEFAULT_INBOUND_QUANTITY 3 // Three tunnels is default now
#define SAM_NAME_INBOUND_LENGTH "inbound.length"
#define SAM_DEFAULT_INBOUND_LENGTH 3 // Three jumps is default now
#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 1 // One backup tunnel
#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 3
#define SAM_NAME_OUTBOUND_LENGTH "outbound.length"
#define SAM_DEFAULT_OUTBOUND_LENGTH 3
#define SAM_NAME_OUTBOUND_LENGTHVARIANCE "outbound.lengthVariance"
#define SAM_DEFAULT_OUTBOUND_LENGTHVARIANCE 0
#define SAM_NAME_OUTBOUND_BACKUPQUANTITY "outbound.backupQuantity"
#define SAM_NAME_OUTBOUND_BACKUPQUANTITY "outbound.backupQuantity"
#define SAM_DEFAULT_OUTBOUND_BACKUPQUANTITY 1
#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_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
#define SAM_NAME_I2CP_LEASESET_ENC_TYPE "i2cp.leaseSetEncType"
#define SAM_DEFAULT_I2CP_LEASESET_ENC_TYPE "0,4"
#ifdef __cplusplus // __cplusplus
#include "compat.h"
//#ifdef __cplusplus
#include <string>
#include <cstdint>
#include <list>
#include <stdint.h>
#include <memory>
#include <string>
#include <thread>
#include <utility>
namespace SAM
{
#include "compat.h"
//ToDo: clean pure C code, keep only C++
namespace SAM {
typedef u_int SOCKET;
class Message
{
public:
enum SessionStyle
{
sssStream,
sssDatagram, // not supported now
sssRaw // not supported now
};
class Message {
public:
enum SessionStyle { sssStream, sssDatagram, sssRaw };
enum eStatus
{
OK,
EMPTY_ANSWER,
CLOSED_SOCKET,
CANNOT_PARSE_ERROR,
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
};
// 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 = "", const std::string& signatureType = SAM_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 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 I2pSocket
{
public:
I2pSocket(const std::string& SAMHost, uint16_t SAMPort);
I2pSocket(const sockaddr_in& addr);
// explicit because we don't want to create any socket implicity
explicit I2pSocket(const I2pSocket& rhs); // creates a new socket with the same parameters
~I2pSocket();
void bootstrapI2P();
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 sockaddr_in& getAddress() const;
const std::string minVer_ = "3.0";
const std::string maxVer_ = "3.1";
private:
SOCKET socket_;
sockaddr_in servAddr_;
std::string SAMHost_;
uint16_t SAMPort_;
std::string version_;
#ifdef WIN32
static int instances_;
static void initWSA();
static void freeWSA();
#endif
void handshake();
void init();
I2pSocket& operator=(const I2pSocket&);
};
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;
template<class T>
struct Answer {
const Message::eStatus status;
T value;
RequestResult()
: isOk(false) {}
Answer(Message::eStatus status, const T &value)
: status(status), value(value) {}
explicit Answer(Message::eStatus status) : status(status), value() {}
};
explicit RequestResult(const T& value)
: isOk(true), value(value) {}
static std::string hello(const std::string &minVer,
const std::string &maxVer);
// Stream session
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 = "",
const std::string &signatureType = SAM_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);
// Datagram session
static std::string
sessionCreate(SessionStyle style, const std::string &sessionID,
const std::string &nickname, const uint16_t port,
const std::string &host = "127.0.0.1",
const std::string &destination = SAM_GENERATE_MY_DESTINATION,
const std::string &options = "",
const std::string &signatureType = SAM_SIGNATURE_TYPE);
static std::string datagramSend(const std::string &nickname,
const std::string &destination/*,
const std::string &datagram_payload*/);
static std::string datagramParse(const std::string &datagram_payload);
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 I2pSocket {
public:
I2pSocket(const std::string &SAMHost, uint16_t SAMPort);
I2pSocket(const sockaddr_in &addr);// explicit because we don't want to create any socket implicity
explicit I2pSocket(const I2pSocket &rhs); // creates a new socket with the same parameters
~I2pSocket();
void bootstrapI2P();
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 sockaddr_in &getAddress() const;
const std::string minVer_ = "3.0";
const std::string maxVer_ = "3.1";
private:
SOCKET socket_;
sockaddr_in servAddr_;
std::string SAMHost_;
uint16_t SAMPort_;
std::string version_;
#ifdef WIN32
static int instances_;
static void initWSA();
static void freeWSA();
#endif
void handshake();
void init();
I2pSocket &operator=(const I2pSocket &);
};
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<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;
struct RequestResult {
bool isOk;
T value;
RequestResultRef(bool isOk, T* value)
: isOk(isOk), value(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;
std::unique_ptr<T> value;
T *value;
RequestResult()
: isOk(false) {}
RequestResultRef(bool isOk, T *value) : isOk(isOk), value(value) {}
};
explicit RequestResult(std::unique_ptr<T>&& value)
: isOk(true), value(std::move(value)) {}
bool isOk;
std::unique_ptr<T> value;
RequestResult() : isOk(false) {}
// some C++ magic
RequestResult(RequestResultRef ref)
: isOk(ref.isOk), value(ref.value) {}
explicit RequestResult(std::unique_ptr<T> &&value)
: isOk(true), value(std::move(value)) {}
RequestResult& operator=(RequestResultRef ref)
{
if (value.get() != ref.value)
{
isOk = ref.isOk;
value.reset(ref.value);
}
return *this;
// 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());
}
operator RequestResultRef() {
return RequestResultRef(this->isOk, this->value.release());
}
};
template<>
struct RequestResult<void>
{
bool isOk;
struct RequestResult<void> {
bool isOk;
RequestResult()
: isOk(false) {}
RequestResult() : isOk(false) {}
explicit RequestResult(bool isOk)
: isOk(isOk) {}
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& signatureType = SAM_SIGNATURE_TYPE);
explicit StreamSession(StreamSession& rhs);
~StreamSession();
//ToDo: reformat to single class
class StreamSession {
public:
StreamSession(const std::string &nickname,
const std::string &SAMHost = SAM_DEFAULT_ADDRESS,
uint16_t SAMPort = SAM_DEFAULT_PORT_TCP,
const std::string &destination = SAM_GENERATE_MY_DESTINATION,
const std::string &i2pOptions = SAM_DEFAULT_I2P_OPTIONS,
const std::string &signatureType = SAM_SIGNATURE_TYPE);
explicit StreamSession(StreamSession &rhs);
~StreamSession();
static std::string generateSessionID();
static std::string generateSessionID();
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);
RequestResult<const std::string> namingLookup(const std::string& name) const;
RequestResult<const FullDestination> destGenerate() const;
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);
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();
void stopForwarding(const std::string &host, uint16_t port);
void stopForwardingAll();
const FullDestination& getMyDestination() const;
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;
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 isSick() const;
private:
StreamSession(const StreamSession& rhs);
StreamSession& operator=(const StreamSession& rhs);
protected:
StreamSession(const StreamSession &rhs);
StreamSession &operator=(const StreamSession &rhs);
struct ForwardedStream
{
I2pSocket* socket;
std::string host;
uint16_t port;
bool silent;
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) {}
};
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;
typedef std::list<ForwardedStream> ForwardedStreamsContainer;
I2pSocket socket_;
const std::string nickname_;
const std::string sessionID_;
FullDestination myDestination_;
const std::string i2pOptions_;
ForwardedStreamsContainer forwardedStreams_;
mutable bool isSick_;
I2pSocket socket_;
const std::string nickname_;
const std::string sessionID_;
FullDestination myDestination_;
const std::string i2pOptions_;
ForwardedStreamsContainer forwardedStreams_;
mutable bool isSick_;
void fallSick() const;
FullDestination createStreamSession(const std::string &destination);
void fallSick() const;
FullDestination createStreamSession(const std::string &destination);
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);
// 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::Answer<const std::string> namingLookup(I2pSocket& socket, const std::string& name);
static Message::Answer<const FullDestination> destGenerate(I2pSocket& socket);
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);
// 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::Answer<const std::string>
namingLookup(I2pSocket &socket, const std::string &name);
static Message::Answer<const FullDestination> destGenerate(I2pSocket &socket);
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);
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);
};
/**
* THIS IS WORKAROUND
* Implemented only to create a TCP session for manage connection.
* The UDP client/server is implemented by your app side.
*/
class DatagramSession {
public:
DatagramSession(const std::string &nickname,
const std::string &SAMHost = SAM_DEFAULT_ADDRESS,
uint16_t SAMPortTCP = SAM_DEFAULT_PORT_TCP,
uint16_t SAMPortUDP = SAM_DEFAULT_PORT_UDP,
const std::string &ClientAddress = SAM_DEFAULT_ADDRESS,
uint16_t clientPortUDP = SAM_DEFAULT_CLIENT_UDP,
const std::string &destination = SAM_GENERATE_MY_DESTINATION,
const std::string &i2pOptions = SAM_DEFAULT_I2P_OPTIONS,
const std::string &signatureType = SAM_SIGNATURE_TYPE);
explicit DatagramSession(DatagramSession &rhs);
~DatagramSession();
static std::string generateSessionID();
RequestResult<const std::string> namingLookup(const std::string &name) const;
RequestResult<const FullDestination> destGenerate() const;
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;
private:
DatagramSession(const DatagramSession &rhs);
DatagramSession &operator=(const DatagramSession &rhs);
I2pSocket socket_;
const std::string nickname_;
const std::string sessionID_;
FullDestination myDestination_;
const std::string i2pOptions_;
mutable bool isSick_;
std::string listenAddress_;
uint16_t listenPortUDP_;
uint16_t SAMPortUDP_;
// commands
void fallSick() const;
FullDestination createDatagramSession(const std::string &destination);
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>
createDatagramSession(I2pSocket &socket, const std::string &sessionID,
const std::string &nickname, const uint16_t port,
const std::string &host, const std::string &destination,
const std::string &options,
const std::string &signatureType);
static Message::Answer<const std::string>
namingLookup(I2pSocket &socket, const std::string &name);
static Message::Answer<const FullDestination> destGenerate(I2pSocket &socket);
};
/**
* THIS IS WORKAROUND
* Implemented only to create a TCP session for manage connection.
* The UDP client/server is implemented by you.
*/
class RawSession {
public:
RawSession(const std::string &nickname,
const std::string &SAMHost = SAM_DEFAULT_ADDRESS,
uint16_t SAMPortTCP = SAM_DEFAULT_PORT_TCP,
uint16_t SAMPortUDP = SAM_DEFAULT_PORT_UDP,
const std::string &ClientAddress = SAM_DEFAULT_ADDRESS,
uint16_t clientPortUDP = SAM_DEFAULT_CLIENT_UDP,
const std::string &destination = SAM_GENERATE_MY_DESTINATION,
const std::string &i2pOptions = SAM_DEFAULT_I2P_OPTIONS,
const std::string &signatureType = SAM_SIGNATURE_TYPE);
explicit RawSession(RawSession &rhs);
~RawSession();
static std::string generateSessionID();
RequestResult<const std::string> namingLookup(const std::string &name) const;
RequestResult<const FullDestination> destGenerate() const;
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;
private:
RawSession(const RawSession &rhs);
RawSession &operator=(const RawSession &rhs);
I2pSocket socket_;
const std::string nickname_;
const std::string sessionID_;
FullDestination myDestination_;
const std::string i2pOptions_;
mutable bool isSick_;
std::string listenAddress_;
uint16_t listenPortUDP_;
uint16_t SAMPortUDP_;
void fallSick() const;
FullDestination createRawSession(const std::string &destination);
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>
createRawSession(I2pSocket &socket, const std::string &sessionID,
const std::string &nickname, const uint16_t port,
const std::string &host, const std::string &destination,
const std::string &options,
const std::string &signatureType);
static Message::Answer<const std::string>
namingLookup(I2pSocket &socket, const std::string &name);
static Message::Answer<const FullDestination> destGenerate(I2pSocket &socket);
};
} // namespace SAM
#else // __cplusplus
#include "i2psam-c.h"
#endif // __cplusplus
#endif // I2PSAM_H
//#else // __cplusplus
//#include "i2psam-c.h"
//#endif // __cplusplus
#endif // I2PSAM_H__