Newer
Older
// 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.
//--------------------------------------------------------------------------------------------------
// see full documentation about SAM at http://www.i2p2.i2p/samv3.html
#ifndef I2PSAM_H
#define I2PSAM_H
#include <string>
#include <list>
#include <stdint.h>
#include <memory>
#include <utility>
#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
#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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
namespace SAM
{
typedef int SOCKET;
class Message
{
public:
enum SessionStyle
{
sssStream,
sssDatagram,
sssRaw
};
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::auto_ptr<T> >
{
// a class-helper for resolving a problem with conversion from temporary RequestResult to non-const RequestResult&
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
struct RequestResultRef
{
bool isOk;
T* value;
RequestResultRef(bool isOk, T* value)
: isOk(isOk), value(value) {}
};
bool isOk;
std::auto_ptr<T> value;
RequestResult()
: isOk(false) {}
explicit RequestResult(std::auto_ptr<T>& value)
: isOk(true), value(value) {}
RequestResult(RequestResultRef ref)
: isOk(ref.isOk), value(ref.value) {}
RequestResult& operator=(RequestResultRef ref)
{
if (value.get() != ref.value)
{
isOk = ref.isOk;
value.reset(ref.value);
}
return *this;
}
operator RequestResultRef()
{
return RequestResultRef(this->isOk, this->value.release());
}
};
template<>
struct RequestResult<void>
{
bool isOk;
RequestResult()
: isOk(false)
{}
explicit RequestResult(bool isOk)
: isOk(isOk)
{}
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
public:
NewStreamSession(
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 NewStreamSession(NewStreamSession& rhs);
~NewStreamSession();
static std::string generateSessionID();
RequestResult<std::auto_ptr<Socket> > accept(bool silent);
RequestResult<std::auto_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;
NewStreamSession(const NewStreamSession& rhs);
NewStreamSession& operator=(const NewStreamSession& 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;
const std::string nickname_;
const std::string sessionID_;
FullDestination myDestination_;
const std::string i2pOptions_;
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);
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);
};
const std::string& SAMHost = SAM_DEFAULT_ADDRESS,
uint16_t SAMPort = SAM_DEFAULT_PORT,
const std::string& myDestination = 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 accept(bool silent);
SOCKET connect(const std::string& destination, bool silent);
bool forward(const std::string& host, uint16_t port, bool silent);
std::string namingLookup(const std::string& name) const;
FullDestination destGenerate() const;
void stopForwarding(const std::string& host, uint16_t port);
const sockaddr_in& getSAMAddress() const;
const std::string& getSAMHost() const;
uint16_t getSAMPort() const;
const std::string& getSAMMinVer() const;
const std::string& getSAMMaxVer() const;
const std::string& getSAMVersion() const;
const std::string& getOptions() const;
private:
class SessionHolder;
// public:
// explicit SessionHolder(std::auto_ptr<NewStreamSession> session);
// ~SessionHolder();
// const NewStreamSession& getSession() const;
// NewStreamSession& getSession();
// private:
// void heal() const;
// void reborn() const;
// mutable std::auto_ptr<NewStreamSession> session_;
std::auto_ptr<SessionHolder> sessionHolder_;
// SessionHolder* sessionHolder_;
};