mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-02-02 19:14:01 +01:00
Error handling and authentication for I2PControl.
This commit is contained in:
parent
996c36b93d
commit
9cca01d159
|
@ -17,8 +17,8 @@
|
||||||
namespace i2p {
|
namespace i2p {
|
||||||
namespace client {
|
namespace client {
|
||||||
|
|
||||||
I2PControlSession::Response::Response(const std::string& id, const std::string& version)
|
I2PControlSession::Response::Response(const std::string& version)
|
||||||
: id(id), version(version), parameters()
|
: id(), version(version), error(ErrorCode::None), parameters()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -32,10 +32,42 @@ std::string I2PControlSession::Response::toJsonString() const
|
||||||
oss << ',';
|
oss << ',';
|
||||||
oss << '"' << it->first << "\":" << it->second;
|
oss << '"' << it->first << "\":" << it->second;
|
||||||
}
|
}
|
||||||
oss << "},\"jsonrpc\":\"" << version << "\"}";
|
oss << "},\"jsonrpc\":\"" << version << '"';
|
||||||
|
if(error != ErrorCode::None)
|
||||||
|
oss << ",\"error\":{\"code\":" << static_cast<int>(error)
|
||||||
|
<< ",\"message\":\"" << getErrorMsg() << "\"" << "}";
|
||||||
|
oss << "}";
|
||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string I2PControlSession::Response::getErrorMsg() const
|
||||||
|
{
|
||||||
|
switch(error) {
|
||||||
|
case ErrorCode::MethodNotFound:
|
||||||
|
return "Method not found.";
|
||||||
|
case ErrorCode::InvalidParameters:
|
||||||
|
return "Invalid parameters.";
|
||||||
|
case ErrorCode::InvalidRequest:
|
||||||
|
return "Invalid request.";
|
||||||
|
case ErrorCode::ParseError:
|
||||||
|
return "Json parse error.";
|
||||||
|
case ErrorCode::InvalidPassword:
|
||||||
|
return "Invalid password.";
|
||||||
|
case ErrorCode::NoToken:
|
||||||
|
return "No authentication token given.";
|
||||||
|
case ErrorCode::NonexistantToken:
|
||||||
|
return "Nonexistant authentication token given.";
|
||||||
|
case ErrorCode::ExpiredToken:
|
||||||
|
return "Exipred authentication token given.";
|
||||||
|
case ErrorCode::UnspecifiedVersion:
|
||||||
|
return "Version not specified.";
|
||||||
|
case ErrorCode::UnsupportedVersion:
|
||||||
|
return "Version not supported.";
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void I2PControlSession::Response::setParam(const std::string& param, const std::string& value)
|
void I2PControlSession::Response::setParam(const std::string& param, const std::string& value)
|
||||||
{
|
{
|
||||||
parameters[param] = value.empty() ? "null" : "\"" + value + "\"";
|
parameters[param] = value.empty() ? "null" : "\"" + value + "\"";
|
||||||
|
@ -53,8 +85,18 @@ void I2PControlSession::Response::setParam(const std::string& param, double valu
|
||||||
parameters[param] = oss.str();
|
parameters[param] = oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void I2PControlSession::Response::setError(ErrorCode code)
|
||||||
|
{
|
||||||
|
error = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2PControlSession::Response::setId(const std::string& identifier)
|
||||||
|
{
|
||||||
|
id = identifier;
|
||||||
|
}
|
||||||
|
|
||||||
I2PControlSession::I2PControlSession(boost::asio::io_service& ios)
|
I2PControlSession::I2PControlSession(boost::asio::io_service& ios)
|
||||||
: password(I2P_CONTROL_DEFAULT_PASSWORD), service(ios), shutdownTimer(ios)
|
: password(I2P_CONTROL_DEFAULT_PASSWORD), tokens(), service(ios), shutdownTimer(ios)
|
||||||
{
|
{
|
||||||
// Method handlers
|
// Method handlers
|
||||||
methodHandlers[I2P_CONTROL_METHOD_AUTHENTICATE] = &I2PControlSession::handleAuthenticate;
|
methodHandlers[I2P_CONTROL_METHOD_AUTHENTICATE] = &I2PControlSession::handleAuthenticate;
|
||||||
|
@ -85,22 +127,54 @@ I2PControlSession::Response I2PControlSession::handleRequest(std::stringstream&
|
||||||
boost::property_tree::ptree pt;
|
boost::property_tree::ptree pt;
|
||||||
boost::property_tree::read_json(request, pt);
|
boost::property_tree::read_json(request, pt);
|
||||||
|
|
||||||
|
Response response;
|
||||||
|
try {
|
||||||
|
response.setId(pt.get<std::string>(I2P_CONTROL_PROPERTY_ID));
|
||||||
|
|
||||||
std::string method = pt.get<std::string>(I2P_CONTROL_PROPERTY_METHOD);
|
std::string method = pt.get<std::string>(I2P_CONTROL_PROPERTY_METHOD);
|
||||||
auto it = methodHandlers.find(method);
|
auto it = methodHandlers.find(method);
|
||||||
if(it == methodHandlers.end()) { // Not found
|
if(it == methodHandlers.end()) { // Not found
|
||||||
LogPrint(eLogWarning, "Unknown I2PControl method ", method);
|
LogPrint(eLogWarning, "Unknown I2PControl method ", method);
|
||||||
return Response("error"); // TODO: indicate the error through i2pcontrol
|
response.setError(ErrorCode::MethodNotFound);
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
Response response(pt.get<std::string>(I2P_CONTROL_PROPERTY_ID));
|
PropertyTree params = pt.get_child(I2P_CONTROL_PROPERTY_PARAMS);
|
||||||
// Call the appropriate handler
|
if(method != I2P_CONTROL_METHOD_AUTHENTICATE && !authenticate(params, response)) {
|
||||||
(this->*(it->second))(pt.get_child(I2P_CONTROL_PROPERTY_PARAMS), response);
|
LogPrint(eLogWarning, "I2PControl invalid token presented");
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
// Call the appropriate handler
|
||||||
|
(this->*(it->second))(params, response);
|
||||||
|
|
||||||
|
} catch(const boost::property_tree::ptree_error& error) {
|
||||||
|
response.setError(ErrorCode::ParseError);
|
||||||
|
} catch(...) {
|
||||||
|
response.setError(ErrorCode::InternalError);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool I2PControlSession::authenticate(const PropertyTree& pt, Response& response)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
std::string token = pt.get<std::string>(I2P_CONTROL_PARAM_TOKEN);
|
||||||
|
if(tokens.find(token) == tokens.end()) {
|
||||||
|
response.setError(ErrorCode::NonexistantToken);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch(const boost::property_tree::ptree_error& error) {
|
||||||
|
response.setError(ErrorCode::NoToken);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void I2PControlSession::handleAuthenticate(const PropertyTree& pt, Response& response)
|
void I2PControlSession::handleAuthenticate(const PropertyTree& pt, Response& response)
|
||||||
{
|
{
|
||||||
int api = pt.get<int>(I2P_CONTROL_PARAM_API);
|
const int api = pt.get<int>(I2P_CONTROL_PARAM_API);
|
||||||
const std::string given_pass = pt.get<std::string>(I2P_CONTROL_PARAM_PASSWORD);
|
const std::string given_pass = pt.get<std::string>(I2P_CONTROL_PARAM_PASSWORD);
|
||||||
LogPrint(eLogDebug, "I2PControl Authenticate API = ", api, " Password = ", password);
|
LogPrint(eLogDebug, "I2PControl Authenticate API = ", api, " Password = ", password);
|
||||||
if(given_pass != password) {
|
if(given_pass != password) {
|
||||||
|
@ -108,12 +182,14 @@ void I2PControlSession::handleAuthenticate(const PropertyTree& pt, Response& res
|
||||||
eLogError, "I2PControl Authenticate Invalid password ", password,
|
eLogError, "I2PControl Authenticate Invalid password ", password,
|
||||||
" expected ", password
|
" expected ", password
|
||||||
);
|
);
|
||||||
|
response.setError(ErrorCode::InvalidPassword);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// TODO: generate a secure token
|
||||||
const std::string token = std::to_string(i2p::util::GetSecondsSinceEpoch());
|
const std::string token = std::to_string(i2p::util::GetSecondsSinceEpoch());
|
||||||
response.setParam(I2P_CONTROL_PARAM_API, api);
|
response.setParam(I2P_CONTROL_PARAM_API, api);
|
||||||
response.setParam(I2P_CONTROL_PARAM_TOKEN, token);
|
response.setParam(I2P_CONTROL_PARAM_TOKEN, token);
|
||||||
// TODO: store tokens to do something useful with them
|
tokens.insert(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2PControlSession::handleEcho(const PropertyTree& pt, Response& response)
|
void I2PControlSession::handleEcho(const PropertyTree& pt, Response& response)
|
||||||
|
@ -134,13 +210,16 @@ void I2PControlSession::handleRouterInfo(const PropertyTree& pt, Response& respo
|
||||||
{
|
{
|
||||||
LogPrint(eLogDebug, "I2PControl RouterInfo");
|
LogPrint(eLogDebug, "I2PControl RouterInfo");
|
||||||
for(const auto& pair : pt) {
|
for(const auto& pair : pt) {
|
||||||
|
if(pair.first == I2P_CONTROL_PARAM_TOKEN)
|
||||||
|
continue;
|
||||||
LogPrint(eLogDebug, pair.first);
|
LogPrint(eLogDebug, pair.first);
|
||||||
auto it = routerInfoHandlers.find(pair.first);
|
auto it = routerInfoHandlers.find(pair.first);
|
||||||
LogPrint(eLogDebug, "Still going");
|
if(it != routerInfoHandlers.end()) {
|
||||||
if(it != routerInfoHandlers.end())
|
|
||||||
(this->*(it->second))(response);
|
(this->*(it->second))(response);
|
||||||
else
|
} else {
|
||||||
LogPrint(eLogError, "I2PControl RouterInfo unknown request ", pair.first);
|
LogPrint(eLogError, "I2PControl RouterInfo unknown request ", pair.first);
|
||||||
|
response.setError(ErrorCode::InvalidRequest);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,12 +227,16 @@ void I2PControlSession::handleRouterManager(const PropertyTree& pt, Response& re
|
||||||
{
|
{
|
||||||
LogPrint(eLogDebug, "I2PControl RouterManager");
|
LogPrint(eLogDebug, "I2PControl RouterManager");
|
||||||
for(const auto& pair : pt) {
|
for(const auto& pair : pt) {
|
||||||
|
if(pair.first == I2P_CONTROL_PARAM_TOKEN)
|
||||||
|
continue;
|
||||||
LogPrint(eLogDebug, pair.first);
|
LogPrint(eLogDebug, pair.first);
|
||||||
auto it = routerManagerHandlers.find(pair.first);
|
auto it = routerManagerHandlers.find(pair.first);
|
||||||
if(it != routerManagerHandlers.end())
|
if(it != routerManagerHandlers.end()) {
|
||||||
(this->*(it->second))(response);
|
(this->*(it->second))(response);
|
||||||
else
|
} else {
|
||||||
LogPrint(eLogError, "I2PControl RouterManager unknown request ", pair.first);
|
LogPrint(eLogError, "I2PControl RouterManager unknown request ", pair.first);
|
||||||
|
response.setError(ErrorCode::InvalidRequest);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <boost/property_tree/ptree.hpp>
|
#include <boost/property_tree/ptree.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <set>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
|
|
||||||
|
@ -55,16 +56,36 @@ const char I2P_CONTROL_ROUTER_MANAGER_RESEED[] = "Reseed";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "Null" I2P control implementation, does not do actual networking.
|
* "Null" I2P control implementation, does not do actual networking.
|
||||||
|
* @note authentication tokens are per-session
|
||||||
*/
|
*/
|
||||||
class I2PControlSession {
|
class I2PControlSession {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum class ErrorCode {
|
||||||
|
None = 0,
|
||||||
|
// JSON-RPC2
|
||||||
|
MethodNotFound = 32601,
|
||||||
|
InvalidParameters = 32602,
|
||||||
|
InvalidRequest = 32600,
|
||||||
|
InternalError = 32603,
|
||||||
|
ParseError = 32700,
|
||||||
|
// I2PControl specific
|
||||||
|
InvalidPassword = 32001,
|
||||||
|
NoToken = 32002,
|
||||||
|
NonexistantToken = 32003,
|
||||||
|
ExpiredToken = 32004,
|
||||||
|
UnspecifiedVersion = 32005,
|
||||||
|
UnsupportedVersion = 32006
|
||||||
|
};
|
||||||
|
|
||||||
class Response {
|
class Response {
|
||||||
std::string id;
|
std::string id;
|
||||||
std::string version;
|
std::string version;
|
||||||
|
ErrorCode error;
|
||||||
std::map<std::string, std::string> parameters;
|
std::map<std::string, std::string> parameters;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Response(const std::string& id, const std::string& version = "2.0");
|
Response(const std::string& version = "2.0");
|
||||||
std::string toJsonString() const;
|
std::string toJsonString() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -74,6 +95,11 @@ public:
|
||||||
void setParam(const std::string& param, const std::string& value);
|
void setParam(const std::string& param, const std::string& value);
|
||||||
void setParam(const std::string& param, int value);
|
void setParam(const std::string& param, int value);
|
||||||
void setParam(const std::string& param, double value);
|
void setParam(const std::string& param, double value);
|
||||||
|
|
||||||
|
void setError(ErrorCode code);
|
||||||
|
void setId(const std::string& identifier);
|
||||||
|
|
||||||
|
std::string getErrorMsg() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -95,6 +121,13 @@ private:
|
||||||
);
|
);
|
||||||
typedef void (I2PControlSession::*RequestHandler)(Response& results);
|
typedef void (I2PControlSession::*RequestHandler)(Response& results);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to authenticate by checking whether the given token is valid.
|
||||||
|
* Sets the appropriate error code in the given response.
|
||||||
|
*/
|
||||||
|
bool authenticate(const PropertyTree& pt, Response& response);
|
||||||
|
|
||||||
// Method handlers
|
// Method handlers
|
||||||
void handleAuthenticate(const PropertyTree& pt, Response& response);
|
void handleAuthenticate(const PropertyTree& pt, Response& response);
|
||||||
void handleEcho(const PropertyTree& pt, Response& response);
|
void handleEcho(const PropertyTree& pt, Response& response);
|
||||||
|
@ -120,6 +153,7 @@ private:
|
||||||
void handleReseed(Response& response);
|
void handleReseed(Response& response);
|
||||||
|
|
||||||
std::string password;
|
std::string password;
|
||||||
|
std::set<std::string> tokens;
|
||||||
|
|
||||||
std::map<std::string, MethodHandler> methodHandlers;
|
std::map<std::string, MethodHandler> methodHandlers;
|
||||||
std::map<std::string, RequestHandler> routerInfoHandlers;
|
std::map<std::string, RequestHandler> routerInfoHandlers;
|
||||||
|
|
Loading…
Reference in a new issue