merge with my changes

This commit is contained in:
giv
2013-03-06 23:19:42 +06:00
commit d62a584aec
5 changed files with 1267 additions and 0 deletions

759
i2psam.cpp Normal file
View File

@ -0,0 +1,759 @@
// 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>
#ifndef WIN32
#include <errno.h>
#endif
#ifndef WIN32
#define closesocket close
#endif
#define SAM_BUFSIZE 65536
#define I2P_DESTINATION_SIZE 516
namespace SAM
{
static void print_error(const std::string& err)
{
#ifdef WIN32
std::cout << err << "(" << WSAGetLastError() << ")" << std::endl;
#else
std::cout << 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;
}
std::cout << "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 recieve data");
return std::string();
}
if (recievedBytes == 0)
{
close();
print_error("Socket was closed");
}
std::cout << "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_;
}
//--------------------------------------------------------------------------------------------------
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;
}
StreamSession::StreamSession(
const std::string& nickname,
const std::string& SAMHost /*= SAM_DEFAULT_ADDRESS*/,
uint16_t SAMPort /*= SAM_DEFAULT_PORT*/,
const std::string& myDestination /*= SAM_GENERATE_MY_DESTINATION*/,
const std::string& minVer /*= SAM_DEFAULT_MIN_VER*/,
const std::string& maxVer /*= SAM_DEFAULT_MAX_VER*/)
: socket_(new Socket(SAMHost, SAMPort, minVer, maxVer))/*,
reconnects_(0)*/
{
(void)createStreamSession(socket_, nickname, myDestination);
}
StreamSession::~StreamSession()
{
for (ForwardedStreamsContainer::const_iterator it = forwardedStreams_.begin(), end = forwardedStreams_.end(); it != end; ++it)
delete (it->socket);
std::cout << "Closing SAM session..." << std::endl;
}
Message::Result StreamSession::request(Socket& socket, const std::string& requestStr, const std::string& keyOnSuccess)
{
if (!socket.isOk())
return Message::Result(Message::CLOSED_SOCKET, std::string());
socket.write(requestStr);
const std::string answer = socket.read();
const Message::eStatus status = Message::checkAnswer(answer);
return Message::Result(status, (status == Message::OK) ? Message::getValue(answer, keyOnSuccess) : answer);
}
Message::Result StreamSession::createStreamSession(Socket& socket, const std::string& sessionID, const std::string& nickname, const std::string& destination)
{
return request(socket, Message::sessionCreate(Message::sssStream, sessionID, nickname, destination), "DESTINATION");
}
Message::Result StreamSession::namingLookup(Socket& socket, const std::string& name)
{
return request(socket, Message::namingLookup(name), "VALUE");
}
std::pair<const Message::eStatus, std::pair<const std::string, const std::string> > StreamSession::destGenerate(Socket &socket)
{
// while answer for DEST GENERATE request doesn't contain a "RESULT" field we parse it manually
typedef std::pair<const std::string, const std::string> AnswerType;
typedef std::pair<const Message::eStatus, AnswerType> ResultType;
if (!socket.isOk())
return ResultType(Message::CLOSED_SOCKET, AnswerType());
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, AnswerType(pub, priv)) : ResultType(Message::EMPTY_ANSWER, AnswerType());
}
Message::Result StreamSession::accept(Socket& socket, const std::string& sessionID, bool silent)
{
return request(socket, Message::streamAccept(sessionID, silent), "");
}
Message::Result StreamSession::connect(Socket& socket, const std::string& sessionID, const std::string& destination, bool silent)
{
return request(socket, Message::streamConnect(sessionID, destination, silent), "");
}
Message::Result 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), "");
}
bool StreamSession::createStreamSession(std::auto_ptr<Socket>& newSocket, const std::string& nickname, const std::string& myDestination /*= SAM_GENERATE_MY_DESTINATION*/)
{
const std::string newSessionID = generateSessionID();
const Message::Result result = createStreamSession(*newSocket, newSessionID, nickname, myDestination);
switch(result.first)
{
case Message::OK:
break;
default:
return false;
}
nickname_ = nickname;
myDestination_ = result.second;
sessionID_ = newSessionID;
socket_ = newSocket; // release and copy
if (!reforwardAll())
return false;
return true;
}
bool StreamSession::createStreamSession(
const std::string& nickname,
const std::string& SAMHost /*= SAM_DEFAULT_ADDRESS*/,
uint16_t SAMPort /*= SAM_DEFAULT_PORT*/,
const std::string& myDestination /*= SAM_GENERATE_MY_DESTINATION*/,
const std::string& minVer /*= SAM_DEFAULT_MIN_VER*/,
const std::string& maxVer /*= SAM_DEFAULT_MAX_VER*/)
{
std::auto_ptr<Socket> newSocket(new Socket(SAMHost, SAMPort, minVer, maxVer));
return createStreamSession(newSocket, nickname, myDestination);
}
bool StreamSession::createStreamSession()
{
std::auto_ptr<Socket> newSocket(new Socket(*socket_));
return createStreamSession(newSocket, nickname_, myDestination_);
}
bool StreamSession::reforwardAll()
{
for (ForwardedStreamsContainer::iterator it = forwardedStreams_.begin(), end = forwardedStreams_.end(); it != end; ++it)
{
std::auto_ptr<Socket> newSocket(new Socket(*socket_));
const Message::Result result = forward(*newSocket, sessionID_, it->host, it->port, it->silent);
switch(result.first)
{
case Message::OK:
break;
default:
return false;
}
delete (it->socket);
it->socket = newSocket.release();
}
return true;
}
std::string StreamSession::namingLookup(const std::string& name)
{
const Message::Result result = namingLookup(*socket_, name);
switch(result.first)
{
case Message::OK:
return result.second;
case Message::EMPTY_ANSWER:
case Message::CLOSED_SOCKET:
return createStreamSession() ? namingLookup(name) : std::string();
default:
break;
}
return std::string();
}
std::string StreamSession::getMyAddress()
{
// return namingLookup(SAM_MY_NAME);
return myDestination_.substr(0, I2P_DESTINATION_SIZE);
}
std::pair<const std::string, const std::string> StreamSession::destGenerate()
{
const std::pair<const Message::eStatus, std::pair<const std::string, const std::string> > result = destGenerate(*socket_);
switch(result.first)
{
case Message::OK:
return result.second;
case Message::EMPTY_ANSWER:
case Message::CLOSED_SOCKET:
return createStreamSession() ? destGenerate() : std::pair<const std::string, const std::string>();
default:
break;
}
return std::pair<const std::string, const std::string>();
}
SOCKET StreamSession::accept(bool silent /*= false*/)
{
Socket streamSocket(*socket_);
const Message::Result result = accept(streamSocket, sessionID_, silent);
switch(result.first)
{
case Message::OK:
return streamSocket.release();
case Message::EMPTY_ANSWER:
case Message::CLOSED_SOCKET:
case Message::INVALID_ID:
return createStreamSession() ? accept(silent) : SAM_INVALID_SOCKET;
default:
break;
}
return SAM_INVALID_SOCKET;
}
SOCKET StreamSession::connect(const std::string& destination, bool silent /*= false*/)
{
Socket streamSocket(*socket_);
const Message::Result result = connect(streamSocket, sessionID_, destination, silent);
switch(result.first)
{
case Message::OK:
return streamSocket.release();
case Message::EMPTY_ANSWER:
case Message::CLOSED_SOCKET:
case Message::INVALID_ID:
return createStreamSession() ? connect(destination, silent) : SAM_INVALID_SOCKET;
default:
break;
}
return SAM_INVALID_SOCKET;
}
bool StreamSession::forward(const std::string& host, uint16_t port, bool silent /*= false*/)
{
std::auto_ptr<Socket> newSocket(new Socket(*socket_));
const Message::Result result = forward(*newSocket, sessionID_, host, port, silent);
switch(result.first)
{
case Message::OK:
break;
case Message::EMPTY_ANSWER:
case Message::CLOSED_SOCKET:
case Message::INVALID_ID:
return createStreamSession() ? forward(host, port, silent) : false;
default:
return false;
}
ForwardedStream fwdStream;
fwdStream.host = host;
fwdStream.port = port;
fwdStream.silent = silent;
fwdStream.socket = newSocket.release();
forwardedStreams_.push_back(fwdStream);
return 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;
}
}
const std::string& StreamSession::getNickname() const
{
return nickname_;
}
const std::string& StreamSession::getSessionID() const
{
return sessionID_;
}
const std::string& StreamSession::getMyDestination() const
{
return myDestination_;
}
const std::string& StreamSession::getHost() const
{
return socket_->getHost();
}
uint16_t StreamSession::getPort() const
{
return socket_->getPort();
}
const std::string& StreamSession::getMinVer() const
{
return socket_->getMinVer();
}
const std::string& StreamSession::getMaxVer() const
{
return socket_->getMaxVer();
}
const std::string& StreamSession::getVersion() const
{
return socket_->getVersion();
}
const sockaddr_in& StreamSession::getAddress() const
{
return socket_->getAddress();
}
//--------------------------------------------------------------------------------------------------
std::string Message::createSAMRequest(const char* format, ...)
{
char buffer[SAM_BUFSIZE];
memset(buffer, 0, SAM_BUFSIZE);
va_list args;
va_start (args, format);
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 %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\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

292
i2psam.h Normal file
View File

@ -0,0 +1,292 @@
// 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 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"
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
};
typedef std::pair<const Message::eStatus, const std::string> Result;
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
{
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&);
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);
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;
};
class StreamSession
{
private:
/*mutable*/ std::auto_ptr<Socket> socket_;
std::string nickname_;
std::string sessionID_;
std::string myDestination_;
struct ForwardedStream
{
Socket* socket;
std::string host;
uint16_t port;
bool silent;
};
typedef std::list<ForwardedStream> ForwardedStreamsContainer;
ForwardedStreamsContainer forwardedStreams_;
bool createStreamSession(std::auto_ptr<Socket>& newSocket, const std::string& nickname, const std::string& myDestination = SAM_GENERATE_MY_DESTINATION);
bool reforwardAll();
static Message::Result request(Socket& socket, const std::string& requestStr, const std::string& keyOnSuccess);
// commands
static Message::Result createStreamSession(Socket& socket, const std::string& sessionID, const std::string& nickname, const std::string& destination);
static Message::Result namingLookup(Socket& socket, const std::string& name);
static std::pair<const Message::eStatus, std::pair<const std::string, const std::string> > destGenerate(Socket& socket);
static Message::Result accept(Socket& socket, const std::string& sessionID, bool silent);
static Message::Result connect(Socket& socket, const std::string& sessionID, const std::string& destination, bool silent);
static Message::Result forward(Socket& socket, const std::string& sessionID, const std::string& host, uint16_t port, bool silent);
public:
StreamSession(
const std::string& nickname,
const std::string& SAMHost = SAM_DEFAULT_ADDRESS,
uint16_t SAMPort = SAM_DEFAULT_PORT,
const std::string& myDestination = SAM_GENERATE_MY_DESTINATION,
const std::string& minVer = SAM_DEFAULT_MIN_VER,
const std::string& maxVer = SAM_DEFAULT_MAX_VER);
~StreamSession();
bool createStreamSession(
const std::string& nickname,
const std::string& SAMHost = SAM_DEFAULT_ADDRESS,
uint16_t SAMPort = SAM_DEFAULT_PORT,
const std::string& myDestination = SAM_GENERATE_MY_DESTINATION,
const std::string& minVer = SAM_DEFAULT_MIN_VER,
const std::string& maxVer = SAM_DEFAULT_MAX_VER); // recreates with new parameters
bool createStreamSession(); // recreates with current parameters
std::string namingLookup(const std::string& name);
std::string getMyAddress();
std::pair<const std::string, const std::string> destGenerate(); // .first is a public key, .second is a private key
SOCKET accept(bool silent = false);
SOCKET connect(const std::string& destination, bool silent = false);
bool forward(const std::string& host, uint16_t port, bool silent = false); // forwards stream to a socket. the socket should be bound with the port, should listen and accept connections on that port
void stopForwarding(const std::string& host, uint16_t port);
static std::string generateSessionID();
const sockaddr_in& getAddress() const;
const std::string& getHost() const;
uint16_t getPort() const;
const std::string& getNickname() const;
const std::string& getSessionID() const;
const std::string& getMyDestination() const;
const std::string& getMinVer() const;
const std::string& getMaxVer() const;
const std::string& getVersion() const;
};
} // namespace SAM
#endif // I2PSAM_H

23
i2psam.pro Normal file
View File

@ -0,0 +1,23 @@
#-------------------------------------------------
#
# Project created by QtCreator 2013-01-06T03:07:13
#
#-------------------------------------------------
QT -= core gui
TARGET = i2psam
TEMPLATE = lib
CONFIG += staticlib
SOURCES += i2psam.cpp
HEADERS += i2psam.h
unix:!symbian {
maemo5 {
target.path = /opt/usr/lib
} else {
target.path = /usr/lib
}
INSTALLS += target
}

78
makefile.linux-mingw Normal file
View File

@ -0,0 +1,78 @@
#############################################################################
# Makefile for building: libi2psam
# Generated by qmake (2.01a) (Qt 4.8.3) on: ?? ????. 10 02:43:02 2013
# Project: i2psam.pro
# Template: lib
#############################################################################
####### Compiler, tools and options
CC = i586-mingw32msvc-gcc
CXX = i586-mingw32msvc-g++
DEFINES = -D_MT -DWIN32 -D_WINDOWS
CFLAGS = -pipe -isystem /usr/i586-mingw32msvc/include/ -O2 -Wall -Wextra $(DEFINES)
CXXFLAGS = -O2 -fexceptions -Wall -Wextra $(DEFINES)
INCPATH = -I'../../dependencies/mingw32/qt/include' -I'../../dependencies/mingw32' -I'../../dependencies/mingw32/qt/include/ActiveQt' -I'../../dependencies/mingw32/qt/mkspecs/unsupported/win32-g++-cross'
LIB = i586-mingw32msvc-ar -ru
COPY = cp
SED = sed
COPY_FILE = $(COPY)
COPY_DIR = cp -R
DEL_FILE = rm -f
DEL_DIR = rmdir
MOVE = mv
CHK_DIR_EXISTS= test -d
MKDIR = mkdir -p
INSTALL_FILE = $(COPY_FILE)
INSTALL_PROGRAM = $(COPY_FILE)
INSTALL_DIR = $(COPY_DIR)
####### Files
SOURCES = i2psam.cpp
OBJECTS = i2psam.o
TARGET = libi2psam.a
####### Implicit rules
.SUFFIXES: .cpp .cc .cxx .C .c
.cpp.o:
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
.cc.o:
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
.cxx.o:
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
.C.o:
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
.c.o:
$(CC) -c $(CFLAGS) $(INCPATH) -o $@ $<
####### Build rules
first: all
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(LIB) $(TARGET) $(OBJECTS)
clean:
-$(DEL_FILE) release/$(TARGET)
-$(DEL_FILE) release/i2psam.o
distclean: clean
-$(DEL_FILE) $(TARGET)
####### Compile
release/i2psam.o: i2psam.cpp i2psam.h
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o release/i2psam.o i2psam.cpp
####### Install
FORCE:

115
makefile.unix Normal file
View File

@ -0,0 +1,115 @@
#############################################################################
# Makefile for building: libi2psam.a
# Generated by qmake (2.01a) (Qt 4.8.1) on: Wed Jan 30 13:00:04 2013
# Project: i2psam.pro
# Template: lib
# Command: /usr/bin/qmake -o Makefile i2psam.pro
#############################################################################
####### Compiler, tools and options
CC = gcc
CXX = g++
CFLAGS = -m64 -pipe -O2 -fPIC -Wall -W -D_REENTRANT $(DEFINES)
CXXFLAGS = -m64 -pipe -O2 -fPIC -Wall -W -D_REENTRANT $(DEFINES)
AR = ar cqs
RANLIB =
TAR = tar -cf
COMPRESS = gzip -9f
COPY = cp -f
SED = sed
COPY_FILE = $(COPY)
COPY_DIR = $(COPY) -r
STRIP = strip
INSTALL_FILE = install -m 644 -p
INSTALL_DIR = $(COPY_DIR)
INSTALL_PROGRAM = install -m 755 -p
DEL_FILE = rm -f
SYMLINK = ln -f -s
DEL_DIR = rmdir
MOVE = mv -f
CHK_DIR_EXISTS= test -d
MKDIR = mkdir -p
####### Output directory
OBJECTS_DIR = ./
####### Files
SOURCES = i2psam.cpp
OBJECTS = i2psam.o
DESTDIR =
TARGET = libi2psam.a
first: all
####### Implicit rules
.SUFFIXES: .o .c .cpp .cc .cxx .C
.cpp.o:
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<"
.cc.o:
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<"
.cxx.o:
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<"
.C.o:
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<"
.c.o:
$(CC) -c $(CFLAGS) $(INCPATH) -o "$@" "$<"
####### Build rules
all: $(TARGET)
staticlib: $(TARGET)
$(TARGET): $(OBJECTS) $(OBJCOMP)
-$(DEL_FILE) $(TARGET)
$(AR) $(TARGET) $(OBJECTS)
dist:
@$(CHK_DIR_EXISTS) .tmp/i2psam1.0.0 || $(MKDIR) .tmp/i2psam1.0.0
$(COPY_FILE) --parents $(SOURCES) $(DIST) .tmp/i2psam1.0.0/ && $(COPY_FILE) --parents i2psam.h .tmp/i2psam1.0.0/ && $(COPY_FILE) --parents i2psam.cpp .tmp/i2psam1.0.0/ && (cd `dirname .tmp/i2psam1.0.0` && $(TAR) i2psam1.0.0.tar i2psam1.0.0 && $(COMPRESS) i2psam1.0.0.tar) && $(MOVE) `dirname .tmp/i2psam1.0.0`/i2psam1.0.0.tar.gz . && $(DEL_FILE) -r .tmp/i2psam1.0.0
clean:
-$(DEL_FILE) $(TARGET)
-$(DEL_FILE) $(OBJECTS)
####### Sub-libraries
distclean: clean
-$(DEL_FILE) $(TARGET)
check: first
####### Compile
i2psam.o: i2psam.cpp i2psam.h
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o i2psam.o i2psam.cpp
####### Install
install_target: first FORCE
@$(CHK_DIR_EXISTS) $(INSTALL_ROOT)/usr/lib/ || $(MKDIR) $(INSTALL_ROOT)/usr/lib/
-$(INSTALL_FILE) "$(TARGET)" "$(INSTALL_ROOT)/usr/lib/$(TARGET)"
uninstall_target: FORCE
-$(DEL_FILE) "$(INSTALL_ROOT)/usr/lib/$(TARGET)"
-$(DEL_DIR) $(INSTALL_ROOT)/usr/lib/
install: install_target FORCE
uninstall: uninstall_target FORCE
FORCE: