Error handling and authentication for I2PControl.

This commit is contained in:
EinMByte 2015-08-01 23:10:10 +02:00
parent 996c36b93d
commit 9cca01d159
2 changed files with 137 additions and 20 deletions

View File

@ -17,8 +17,8 @@
namespace i2p {
namespace client {
I2PControlSession::Response::Response(const std::string& id, const std::string& version)
: id(id), version(version), parameters()
I2PControlSession::Response::Response(const std::string& version)
: id(), version(version), error(ErrorCode::None), parameters()
{
}
@ -32,10 +32,42 @@ std::string I2PControlSession::Response::toJsonString() const
oss << ',';
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();
}
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)
{
parameters[param] = value.empty() ? "null" : "\"" + value + "\"";
@ -53,8 +85,18 @@ void I2PControlSession::Response::setParam(const std::string& param, double valu
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)
: password(I2P_CONTROL_DEFAULT_PASSWORD), service(ios), shutdownTimer(ios)
: password(I2P_CONTROL_DEFAULT_PASSWORD), tokens(), service(ios), shutdownTimer(ios)
{
// Method handlers
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::read_json(request, pt);
std::string method = pt.get<std::string>(I2P_CONTROL_PROPERTY_METHOD);
auto it = methodHandlers.find(method);
if(it == methodHandlers.end()) { // Not found
LogPrint(eLogWarning, "Unknown I2PControl method ", method);
return Response("error"); // TODO: indicate the error through i2pcontrol
Response response;
try {
response.setId(pt.get<std::string>(I2P_CONTROL_PROPERTY_ID));
std::string method = pt.get<std::string>(I2P_CONTROL_PROPERTY_METHOD);
auto it = methodHandlers.find(method);
if(it == methodHandlers.end()) { // Not found
LogPrint(eLogWarning, "Unknown I2PControl method ", method);
response.setError(ErrorCode::MethodNotFound);
return response;
}
PropertyTree params = pt.get_child(I2P_CONTROL_PROPERTY_PARAMS);
if(method != I2P_CONTROL_METHOD_AUTHENTICATE && !authenticate(params, response)) {
LogPrint(eLogWarning, "I2PControl invalid token presented");
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);
}
Response response(pt.get<std::string>(I2P_CONTROL_PROPERTY_ID));
// Call the appropriate handler
(this->*(it->second))(pt.get_child(I2P_CONTROL_PROPERTY_PARAMS), response);
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)
{
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);
LogPrint(eLogDebug, "I2PControl Authenticate API = ", api, " Password = ", password);
if(given_pass != password) {
@ -108,12 +182,14 @@ void I2PControlSession::handleAuthenticate(const PropertyTree& pt, Response& res
eLogError, "I2PControl Authenticate Invalid password ", password,
" expected ", password
);
response.setError(ErrorCode::InvalidPassword);
return;
}
// TODO: generate a secure token
const std::string token = std::to_string(i2p::util::GetSecondsSinceEpoch());
response.setParam(I2P_CONTROL_PARAM_API, api);
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)
@ -134,13 +210,16 @@ void I2PControlSession::handleRouterInfo(const PropertyTree& pt, Response& respo
{
LogPrint(eLogDebug, "I2PControl RouterInfo");
for(const auto& pair : pt) {
if(pair.first == I2P_CONTROL_PARAM_TOKEN)
continue;
LogPrint(eLogDebug, pair.first);
auto it = routerInfoHandlers.find(pair.first);
LogPrint(eLogDebug, "Still going");
if(it != routerInfoHandlers.end())
if(it != routerInfoHandlers.end()) {
(this->*(it->second))(response);
else
} else {
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");
for(const auto& pair : pt) {
if(pair.first == I2P_CONTROL_PARAM_TOKEN)
continue;
LogPrint(eLogDebug, pair.first);
auto it = routerManagerHandlers.find(pair.first);
if(it != routerManagerHandlers.end())
if(it != routerManagerHandlers.end()) {
(this->*(it->second))(response);
else
} else {
LogPrint(eLogError, "I2PControl RouterManager unknown request ", pair.first);
response.setError(ErrorCode::InvalidRequest);
}
}
}

View File

@ -4,6 +4,7 @@
#include <boost/property_tree/ptree.hpp>
#include <string>
#include <map>
#include <set>
#include <functional>
#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.
* @note authentication tokens are per-session
*/
class I2PControlSession {
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 {
std::string id;
std::string version;
ErrorCode error;
std::map<std::string, std::string> parameters;
public:
Response(const std::string& id, const std::string& version = "2.0");
Response(const std::string& version = "2.0");
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, int 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);
/**
* 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
void handleAuthenticate(const PropertyTree& pt, Response& response);
void handleEcho(const PropertyTree& pt, Response& response);
@ -120,6 +153,7 @@ private:
void handleReseed(Response& response);
std::string password;
std::set<std::string> tokens;
std::map<std::string, MethodHandler> methodHandlers;
std::map<std::string, RequestHandler> routerInfoHandlers;