#include "conn.h"
+#include "radius.h"
#include "config.h"
#include "stg/json_parser.h"
#include <yajl/yajl_gen.h>
#include <map>
+#include <stdexcept>
#include <cstring>
#include <cerrno>
#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
using STG::Conn;
using STG::Config;
namespace
{
-double CONN_TIMEOUT = 5;
-double PING_TIMEOUT = 1;
+double CONN_TIMEOUT = 60;
+double PING_TIMEOUT = 10;
enum Packet
{
class TopParser : public NodeParser
{
public:
- TopParser()
+ typedef void (*Callback) (void* /*data*/);
+ TopParser(Callback callback, void* data)
: m_packetParser(this, m_packet, m_packetStr),
m_stageParser(this, m_stage, m_stageStr),
- m_pairsParser(this, m_data)
+ m_pairsParser(this, m_data),
+ m_callback(callback), m_callbackData(data)
{}
virtual NodeParser* parseStartMap() { return this; }
return this;
}
- virtual NodeParser* parseEndMap() { return this; }
+ virtual NodeParser* parseEndMap() { m_callback(m_callbackData); return this; }
const std::string& packetStr() const { return m_packetStr; }
Packet packet() const { return m_packet; }
PacketParser m_packetParser;
StageParser m_stageParser;
PairsParser m_pairsParser;
+
+ Callback m_callback;
+ void* m_callbackData;
};
class ProtoParser : public Parser
{
public:
- ProtoParser() : Parser( &m_topParser ) {}
+ ProtoParser(TopParser::Callback callback, void* data)
+ : Parser( &m_topParser ),
+ m_topParser(callback, data)
+ {}
const std::string& packetStr() const { return m_topParser.packetStr(); }
Packet packet() const { return m_topParser.packet(); }
m_gen.add(key, map);
return *this;
}
+ PacketGen& add(const std::string& key, MapGen& map)
+ {
+ m_gen.add(key, map);
+ return *this;
+ }
private:
MapGen m_gen;
StringGen m_type;
};
+std::string toString(Config::ReturnCode code)
+{
+ switch (code)
+ {
+ case Config::REJECT: return "reject";
+ case Config::FAIL: return "fail";
+ case Config::OK: return "ok";
+ case Config::HANDLED: return "handled";
+ case Config::INVALID: return "invalid";
+ case Config::USERLOCK: return "userlock";
+ case Config::NOTFOUND: return "notfound";
+ case Config::NOOP: return "noop";
+ case Config::UPDATED: return "noop";
+ }
+ return "reject";
+}
+
}
class Conn::Impl
{
public:
- Impl(USERS& users, PLUGIN_LOGGER& logger, const Config& config, int fd, const std::string& remote);
+ Impl(USERS& users, PLUGIN_LOGGER& logger, RADIUS& plugin, const Config& config, int fd, const std::string& remote);
~Impl();
int sock() const { return m_sock; }
private:
USERS& m_users;
PLUGIN_LOGGER& m_logger;
+ RADIUS& m_plugin;
const Config& m_config;
int m_sock;
std::string m_remote;
time_t m_lastPing;
time_t m_lastActivity;
ProtoParser m_parser;
+ std::set<std::string> m_authorized;
- bool process();
- bool processPing();
- bool processPong();
- bool processData();
+ template <typename T>
+ const T& stageMember(T Config::Section::* member) const
+ {
+ switch (m_parser.stage())
+ {
+ case AUTHORIZE: return m_config.autz.*member;
+ case AUTHENTICATE: return m_config.auth.*member;
+ case POSTAUTH: return m_config.postauth.*member;
+ case PREACCT: return m_config.preacct.*member;
+ case ACCOUNTING: return m_config.acct.*member;
+ }
+ throw std::runtime_error("Invalid stage: '" + m_parser.stageStr() + "'.");
+ }
+
+ const Config::Pairs& match() const { return stageMember(&Config::Section::match); }
+ const Config::Pairs& modify() const { return stageMember(&Config::Section::modify); }
+ const Config::Pairs& reply() const { return stageMember(&Config::Section::reply); }
+ Config::ReturnCode returnCode() const { return stageMember(&Config::Section::returnCode); }
+ const Config::Authorize& authorize() const { return stageMember(&Config::Section::authorize); }
+
+ static void process(void* data);
+ void processPing();
+ void processPong();
+ void processData();
bool answer(const USER& user);
bool answerNo();
bool sendPing();
static bool write(void* data, const char* buf, size_t size);
};
-Conn::Conn(USERS& users, PLUGIN_LOGGER& logger, const Config& config, int fd, const std::string& remote)
- : m_impl(new Impl(users, logger, config, fd, remote))
+Conn::Conn(USERS& users, PLUGIN_LOGGER& logger, RADIUS& plugin, const Config& config, int fd, const std::string& remote)
+ : m_impl(new Impl(users, logger, plugin, config, fd, remote))
{
}
return m_impl->isOk();
}
-Conn::Impl::Impl(USERS& users, PLUGIN_LOGGER& logger, const Config& config, int fd, const std::string& remote)
+Conn::Impl::Impl(USERS& users, PLUGIN_LOGGER& logger, RADIUS& plugin, const Config& config, int fd, const std::string& remote)
: m_users(users),
m_logger(logger),
+ m_plugin(plugin),
m_config(config),
m_sock(fd),
m_remote(remote),
m_ok(true),
m_lastPing(time(NULL)),
- m_lastActivity(m_lastPing)
+ m_lastActivity(m_lastPing),
+ m_parser(&Conn::Impl::process, this)
{
}
Conn::Impl::~Impl()
{
close(m_sock);
+
+ std::set<std::string>::const_iterator it = m_authorized.begin();
+ for (; it != m_authorized.end(); ++it)
+ m_plugin.unauthorize(*it, "Lost connection to RADIUS server " + m_remote + ".");
}
bool Conn::Impl::read()
m_ok = false;
return false;
}
+ printfd(__FILE__, "Read %d bytes.\n%s\n", res, std::string(buffer.data(), res).c_str());
m_lastActivity = time(NULL);
if (res == 0)
{
- if (!m_parser.done())
- {
- m_ok = false;
- m_logger("Failed to read data from '" + m_remote + "': " + strerror(errno));
- return false;
- }
- return process();
+ m_ok = false;
+ return true;
}
return m_parser.append(buffer.data(), res);
}
time_t now = time(NULL);
if (difftime(now, m_lastActivity) > CONN_TIMEOUT)
{
+ int delta = difftime(now, m_lastActivity);
+ printfd(__FILE__, "Connection to '%s' timed out: %d sec.\n", m_remote.c_str(), delta);
m_logger("Connection to " + m_remote + " timed out.");
m_ok = false;
return false;
}
if (difftime(now, m_lastPing) > PING_TIMEOUT)
+ {
+ int delta = difftime(now, m_lastPing);
+ printfd(__FILE__, "Ping timeout: %d sec. Sending ping...\n", delta);
sendPing();
+ }
return true;
}
-bool Conn::Impl::process()
+void Conn::Impl::process(void* data)
{
- switch (m_parser.packet())
+ Impl& impl = *static_cast<Impl*>(data);
+ try
+ {
+ switch (impl.m_parser.packet())
+ {
+ case PING:
+ impl.processPing();
+ return;
+ case PONG:
+ impl.processPong();
+ return;
+ case DATA:
+ impl.processData();
+ return;
+ }
+ }
+ catch (const std::exception& ex)
{
- case PING:
- return processPing();
- case PONG:
- return processPong();
- case DATA:
- return processData();
+ printfd(__FILE__, "Processing error. %s", ex.what());
+ impl.m_logger("Processing error. %s", ex.what());
}
- m_logger("Received invalid packet type: " + m_parser.packetStr());
- return false;
+ printfd(__FILE__, "Received invalid packet type: '%s'.\n", impl.m_parser.packetStr().c_str());
+ impl.m_logger("Received invalid packet type: " + impl.m_parser.packetStr());
}
-bool Conn::Impl::processPing()
+void Conn::Impl::processPing()
{
- return sendPong();
+ printfd(__FILE__, "Got ping. Sending pong...\n");
+ sendPong();
}
-bool Conn::Impl::processPong()
+void Conn::Impl::processPong()
{
+ printfd(__FILE__, "Got pong.\n");
m_lastActivity = time(NULL);
- return true;
}
-bool Conn::Impl::processData()
+void Conn::Impl::processData()
{
+ printfd(__FILE__, "Got data.\n");
int handle = m_users.OpenSearch();
USER_PTR user = NULL;
- bool match = true;
- while (m_users.SearchNext(handle, &user))
+ bool matched = false;
+ while (m_users.SearchNext(handle, &user) == 0)
{
if (user == NULL)
continue;
- match = true;
- for (Config::Pairs::const_iterator it = m_config.match.begin(); it != m_config.match.end(); ++it)
+ matched = true;
+ for (Config::Pairs::const_iterator it = match().begin(); it != match().end(); ++it)
{
Config::Pairs::const_iterator pos = m_parser.data().find(it->first);
if (pos == m_parser.data().end())
{
- match = false;
+ matched = false;
break;
}
if (user->GetParamValue(it->second) != pos->second)
{
- match = false;
+ matched = false;
break;
}
}
- if (!match)
+ if (!matched)
continue;
answer(*user);
+ if (authorize().check(*user, m_parser.data()))
+ {
+ m_plugin.authorize(*user);
+ m_authorized.insert(user->GetLogin());
+ }
break;
}
- if (!match)
+ if (!matched)
answerNo();
m_users.CloseSearch(handle);
-
- return true;
}
bool Conn::Impl::answer(const USER& user)
{
- boost::scoped_ptr<MapGen> reply(new MapGen);
- for (Config::Pairs::const_iterator it = m_config.reply.begin(); it != m_config.reply.end(); ++it)
- reply->add(it->first, new StringGen(user.GetParamValue(it->second)));
+ printfd(__FILE__, "Got match. Sending answer...\n");
+ MapGen replyData;
+ for (Config::Pairs::const_iterator it = reply().begin(); it != reply().end(); ++it)
+ replyData.add(it->first, new StringGen(user.GetParamValue(it->second)));
- boost::scoped_ptr<MapGen> modify(new MapGen);
- for (Config::Pairs::const_iterator it = m_config.modify.begin(); it != m_config.modify.end(); ++it)
- modify->add(it->first, new StringGen(user.GetParamValue(it->second)));
+ MapGen modifyData;
+ for (Config::Pairs::const_iterator it = modify().begin(); it != modify().end(); ++it)
+ modifyData.add(it->first, new StringGen(user.GetParamValue(it->second)));
PacketGen gen("data");
gen.add("result", "ok")
- .add("reply", reply.get())
- .add("modify", modify.get());
+ .add("reply", replyData)
+ .add("modify", modifyData);
m_lastPing = time(NULL);
bool Conn::Impl::answerNo()
{
+ printfd(__FILE__, "No match. Sending answer...\n");
PacketGen gen("data");
- gen.add("result", "ok");
+ gen.add("result", "no");
+ gen.add("return_code", toString(returnCode()));
m_lastPing = time(NULL);
bool Conn::Impl::write(void* data, const char* buf, size_t size)
{
+ std::string json(buf, size);
+ printfd(__FILE__, "Writing JSON:\n%s\n", json.c_str());
Conn::Impl& conn = *static_cast<Conn::Impl*>(data);
while (size > 0)
{
- ssize_t res = ::write(conn.m_sock, buf, size);
+ ssize_t res = ::send(conn.m_sock, buf, size, MSG_NOSIGNAL);
if (res < 0)
{
conn.m_logger("Failed to write pong to '" + conn.m_remote + "': " + strerror(errno));