Browse Source

Error handling and authentication for I2PControl.

pull/242/head
EinMByte 9 years ago
parent
commit
9cca01d159
  1. 111
      i2pcontrol/I2PControl.cpp
  2. 36
      i2pcontrol/I2PControl.h

111
i2pcontrol/I2PControl.cpp

@ -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);
if(method != I2P_CONTROL_METHOD_AUTHENTICATE && !authenticate(params, response)) {
LogPrint(eLogWarning, "I2PControl invalid token presented");
return response;
}
// Call the appropriate handler // Call the appropriate handler
(this->*(it->second))(pt.get_child(I2P_CONTROL_PROPERTY_PARAMS), response); (this->*(it->second))(params, response);
} catch(const boost::property_tree::ptree_error& error) {
response.setError(ErrorCode::ParseError);
} catch(...) {
response.setError(ErrorCode::InternalError);
}
return 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) 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);
}
} }
} }

36
i2pcontrol/I2PControl.h

@ -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…
Cancel
Save