using namespace isc;
using namespace isc::asiolink;
using namespace isc::data;
+using namespace isc::tcp;
using namespace isc::util;
using namespace std;
using namespace std::chrono;
}
}
-Exchange::Exchange(const asiolink::IOServicePtr io_service,
- const MessagePtr& request,
+Exchange::Exchange(const MessagePtr& request,
unsigned maxretries,
const Servers& servers,
Handler handler)
- : identifier_(""), io_service_(io_service), sync_(false),
- rc_(ERROR_RC), request_(request), sent_(), received_(),
+ : identifier_(""), sync_(false), rc_(ERROR_RC),
+ request_(request), sent_(), received_(),
maxretries_(maxretries), servers_(servers), handler_(handler) {
- if (!io_service) {
- isc_throw(BadValue, "null IO service");
- }
if (!request) {
isc_throw(BadValue, "null request");
}
Exchange::Exchange(const MessagePtr& request,
unsigned maxretries,
const Servers& servers)
- : identifier_(""), io_service_(new IOService()), sync_(true),
- rc_(ERROR_RC), request_(request), sent_(), received_(),
+ : identifier_(""), sync_(true), rc_(ERROR_RC),
+ request_(request), sent_(), received_(),
maxretries_(maxretries), servers_(servers), handler_() {
if (!request) {
isc_throw(BadValue, "null request");
const MessagePtr& request,
unsigned maxretries,
const Servers& servers,
- Handler handler) {
- return (UdpExchangePtr(new UdpExchange(io_service, request, maxretries,
- servers, handler)));
+ Handler handler,
+ RadiusProtocol protocol) {
+ if (protocol == PW_PROTO_UDP) {
+ return (UdpExchangePtr(new UdpExchange(io_service, request, maxretries,
+ servers, handler)));
+ } else {
+ return (TcpExchangePtr(new TcpExchange(request, maxretries,
+ servers, handler)));
+ }
}
ExchangePtr
unsigned maxretries,
const Servers& servers,
Handler handler)
- : Exchange(io_service, request, maxretries, servers, handler),
- started_(false), terminated_(false),
+ : Exchange(request, maxretries, servers, handler),
+ io_service_(io_service), started_(false), terminated_(false),
start_time_(std::chrono::steady_clock().now()),
socket_(), ep_(), timer_(), server_(), idx_(0),
buffer_(), size_(0), retries_(0), postponed_(),
mutex_(new std::mutex()) {
+ if (!io_service) {
+ isc_throw(BadValue, "null IO service");
+ }
}
UdpExchange::UdpExchange(const MessagePtr& request,
unsigned maxretries,
const Servers& servers)
: Exchange(request, maxretries, servers),
+ io_service_(new IOService()),
started_(false), terminated_(false),
start_time_(std::chrono::steady_clock().now()),
socket_(), ep_(), timer_(), server_(), idx_(0),
rs << hex << setfill('0') << setw(8) << ri;
identifier_ = rs.str();
}
-
void
Exchange::logReplyMessages() const {
if (!received_) {
Exchange::buildRequest(server_, start_time_);
}
+void
+TcpExchange::buildRequest() {
+ Exchange::buildRequest(server_, start_time_);
+}
+
void
UdpExchange::open() {
if (RadiusImpl::shutdown_) {
ph::_2)); // size.
}
+void
+Exchange::processResponse() {
+ // Decode message.
+ rc_ = OK_RC;
+ try {
+ // In order:
+ // - decode message.
+ // - verify that identifiers match.
+ // - verify that message codes match.
+ received_->decode();
+ unsigned got = received_->getIdentifier();
+ unsigned expected = sent_->getIdentifier();
+ if (got != expected) {
+ LOG_ERROR(radius_logger, RADIUS_EXCHANGE_RECEIVED_MISMATCH)
+ .arg(identifier_)
+ .arg(got)
+ .arg(expected);
+ rc_ = BADRESP_RC;
+ } else if (request_->getCode() == PW_ACCESS_REQUEST) {
+ if (received_->getCode() == PW_ACCESS_REJECT) {
+ LOG_DEBUG(radius_logger, RADIUS_DBG_TRACE,
+ RADIUS_EXCHANGE_RECEIVED_ACCESS_REJECT)
+ .arg(identifier_);
+ rc_ = REJECT_RC;
+ } else if (received_->getCode() != PW_ACCESS_ACCEPT) {
+ LOG_ERROR(radius_logger, RADIUS_EXCHANGE_RECEIVED_UNEXPECTED)
+ .arg(identifier_)
+ .arg(msgCodeToText(request_->getCode()))
+ .arg(msgCodeToText(received_->getCode()));
+ rc_ = BADRESP_RC;
+ } else {
+ LOG_DEBUG(radius_logger, RADIUS_DBG_TRACE,
+ RADIUS_EXCHANGE_RECEIVED_ACCESS_ACCEPT)
+ .arg(identifier_);
+ }
+ } else if (request_->getCode() == PW_ACCOUNTING_REQUEST) {
+ if (received_->getCode() != PW_ACCOUNTING_RESPONSE) {
+ LOG_ERROR(radius_logger, RADIUS_EXCHANGE_RECEIVED_UNEXPECTED)
+ .arg(identifier_)
+ .arg(msgCodeToText(request_->getCode()))
+ .arg(msgCodeToText(received_->getCode()));
+ rc_ = BADRESP_RC;
+ } else {
+ LOG_DEBUG(radius_logger, RADIUS_DBG_TRACE,
+ RADIUS_EXCHANGE_RECEIVED_ACCOUNTING_RESPONSE)
+ .arg(identifier_);
+ }
+ } else if (request_->getCode() == PW_STATUS_SERVER) {
+ if (received_->getCode() == PW_ACCESS_ACCEPT) {
+ LOG_DEBUG(radius_logger, RADIUS_DBG_TRACE,
+ RADIUS_EXCHANGE_RECEIVED_ACCESS_ACCEPT)
+ .arg(identifier_);
+ } else if (received_->getCode() == PW_ACCESS_REJECT) {
+ LOG_DEBUG(radius_logger, RADIUS_DBG_TRACE,
+ RADIUS_EXCHANGE_RECEIVED_ACCESS_REJECT)
+ .arg(identifier_);
+ } else if (received_->getCode() == PW_ACCOUNTING_RESPONSE) {
+ LOG_DEBUG(radius_logger, RADIUS_DBG_TRACE,
+ RADIUS_EXCHANGE_RECEIVED_ACCOUNTING_RESPONSE)
+ .arg(identifier_);
+ } else {
+ LOG_ERROR(radius_logger, RADIUS_EXCHANGE_RECEIVED_UNEXPECTED)
+ .arg(identifier_)
+ .arg(msgCodeToText(request_->getCode()))
+ .arg(msgCodeToText(received_->getCode()));
+ rc_ = BADRESP_RC;
+ }
+ }
+ } catch (const Exception& exc) {
+ LOG_ERROR(radius_logger, RADIUS_EXCHANGE_RECEIVED_BAD_RESPONSE)
+ .arg(identifier_)
+ .arg(exc.what());
+ rc_ = BADRESP_RC;
+ }
+
+ LOG_DEBUG(radius_logger, RADIUS_DBG_TRACE,
+ RADIUS_EXCHANGE_RECEIVED_RESPONSE)
+ .arg(identifier_)
+ .arg(exchangeRCtoText(rc_));
+}
+
void
UdpExchange::receivedHandler(UdpExchangePtr ex,
const boost::system::error_code ec,
ex->received_.reset(new Message(ex->buffer_, ex->sent_->getAuth(),
ex->server_->getSecret()));
- // Decode message.
- ex->rc_ = OK_RC;
- try {
- // In order:
- // - decode message.
- // - verify that identifiers match.
- // - verify that message codes match.
- ex->received_->decode();
- unsigned got = ex->received_->getIdentifier();
- unsigned expected = ex->sent_->getIdentifier();
- if (got != expected) {
- LOG_ERROR(radius_logger, RADIUS_EXCHANGE_RECEIVED_MISMATCH)
- .arg(ex->identifier_)
- .arg(got)
- .arg(expected);
- ex->rc_ = BADRESP_RC;
- } else if (ex->request_->getCode() == PW_ACCESS_REQUEST) {
- if (ex->received_->getCode() == PW_ACCESS_REJECT) {
- LOG_DEBUG(radius_logger, RADIUS_DBG_TRACE,
- RADIUS_EXCHANGE_RECEIVED_ACCESS_REJECT)
- .arg(ex->identifier_);
- ex->rc_ = REJECT_RC;
- } else if (ex->received_->getCode() != PW_ACCESS_ACCEPT) {
- LOG_ERROR(radius_logger, RADIUS_EXCHANGE_RECEIVED_UNEXPECTED)
- .arg(ex->identifier_)
- .arg(msgCodeToText(ex->request_->getCode()))
- .arg(msgCodeToText(ex->received_->getCode()));
- ex->rc_ = BADRESP_RC;
- } else {
- LOG_DEBUG(radius_logger, RADIUS_DBG_TRACE,
- RADIUS_EXCHANGE_RECEIVED_ACCESS_ACCEPT)
- .arg(ex->identifier_);
- }
- } else if (ex->request_->getCode() == PW_ACCOUNTING_REQUEST) {
- if (ex->received_->getCode() != PW_ACCOUNTING_RESPONSE) {
- LOG_ERROR(radius_logger, RADIUS_EXCHANGE_RECEIVED_UNEXPECTED)
- .arg(ex->identifier_)
- .arg(msgCodeToText(ex->request_->getCode()))
- .arg(msgCodeToText(ex->received_->getCode()));
- ex->rc_ = BADRESP_RC;
- } else {
- LOG_DEBUG(radius_logger, RADIUS_DBG_TRACE,
- RADIUS_EXCHANGE_RECEIVED_ACCOUNTING_RESPONSE)
- .arg(ex->identifier_);
- }
- } else if (ex->request_->getCode() == PW_STATUS_SERVER) {
- if (ex->received_->getCode() == PW_ACCESS_ACCEPT) {
- LOG_DEBUG(radius_logger, RADIUS_DBG_TRACE,
- RADIUS_EXCHANGE_RECEIVED_ACCESS_ACCEPT)
- .arg(ex->identifier_);
- } else if (ex->received_->getCode() == PW_ACCESS_REJECT) {
- LOG_DEBUG(radius_logger, RADIUS_DBG_TRACE,
- RADIUS_EXCHANGE_RECEIVED_ACCESS_REJECT)
- .arg(ex->identifier_);
- } else if (ex->received_->getCode() == PW_ACCOUNTING_RESPONSE) {
- LOG_DEBUG(radius_logger, RADIUS_DBG_TRACE,
- RADIUS_EXCHANGE_RECEIVED_ACCOUNTING_RESPONSE)
- .arg(ex->identifier_);
- } else {
- LOG_ERROR(radius_logger, RADIUS_EXCHANGE_RECEIVED_UNEXPECTED)
- .arg(ex->identifier_)
- .arg(msgCodeToText(ex->request_->getCode()))
- .arg(msgCodeToText(ex->received_->getCode()));
- ex->rc_ = BADRESP_RC;
- }
- }
- } catch (const Exception& exc) {
- LOG_ERROR(radius_logger, RADIUS_EXCHANGE_RECEIVED_BAD_RESPONSE)
- .arg(ex->identifier_)
- .arg(exc.what());
- ex->rc_ = BADRESP_RC;
- }
-
- LOG_DEBUG(radius_logger, RADIUS_DBG_TRACE,
- RADIUS_EXCHANGE_RECEIVED_RESPONSE)
- .arg(ex->identifier_)
- .arg(exchangeRCtoText(ex->rc_));
+ ex->processResponse();
// If bad then retry, if not including reject it is done.
if ((ex->rc_ != OK_RC) && (ex->rc_ != REJECT_RC)) {
}
}
+TcpExchange::TcpExchange(const MessagePtr& request,
+ unsigned maxretries,
+ const Servers& servers,
+ Handler handler)
+ : Exchange(request, maxretries, servers, handler),
+ start_time_(std::chrono::steady_clock().now()),
+ server_(), resp_() {
+ server_ = servers_[0];
+}
+
+void
+TcpExchange::start() {
+ if (RadiusImpl::shutdown_) {
+ shutdown();
+ }
+
+ if (!server_) {
+ isc_throw(Unexpected, "no server");
+ }
+
+ try {
+ // Reset error code.
+ rc_ = ERROR_RC;
+
+ // Build to be send request message.
+ buildRequest();
+
+ // Build write data request.
+ WireDataPtr request(new WireData(sent_->getBuffer()));
+
+ ///// Log.
+
+ RadiusImpl::instance().tcp_client_->asyncSendRequest(
+ server_->getPeerAddress(),
+ server_->getPeerPort(),
+ server_->getTlsContext(),
+ request,
+ resp_,
+ true,
+ TcpExchange::CompleteCheck,
+ std::bind(&TcpExchange::RequestHandler,
+ shared_from_this(),
+ ph::_1, // error_code
+ ph::_2, // response
+ ph::_3), // error_msg
+ TcpClient::RequestTimeout(server_->getTimeout() * 1000));
+ } catch (const Exception& exc) {
+ ///// Log.
+ rc_ = ERROR_RC;
+ // Call handler.
+ if (handler_) {
+ auto handler = handler_;
+ // Avoid to keep a circular reference.
+ handler_ = Handler();
+ handler(shared_from_this());
+ }
+ }
+}
+
+void
+TcpExchange::shutdown() {
+ handler_ = Handler();
+}
+
+void
+TcpExchange::RequestHandler(TcpExchangePtr ex,
+ const boost::system::error_code& ec,
+ const WireDataPtr& response,
+ const string& error_msg) {
+ if (!ex) {
+ isc_throw(Unexpected, "null exchange in receivedHandler");
+ }
+
+ if (RadiusImpl::shutdown_) {
+ return;
+ }
+
+ // Check error code.
+ if (ec) {
+ ///// log
+ if (ec == boost::asio::error::timed_out) {
+ ex->rc_ = TIMEOUT_RC;
+ } else {
+ ex->rc_ = ERROR_RC;
+ }
+ // Call handler.
+ if (ex->handler_) {
+ auto handler = ex->handler_;
+ // Avoid to keep a circular reference.
+ ex->handler_ = Handler();
+ handler(ex);
+ }
+ return;
+ }
+
+ //// log
+ const WireData& buffer = *response;
+ ex->received_.reset(new Message(buffer, ex->sent_->getAuth(),
+ ex->server_->getSecret()));
+
+ ex->processResponse();
+
+ if ((ex->rc_ == OK_RC) || (ex->rc_ == REJECT_RC)) {
+ ex->logReplyMessages();
+ }
+ // Call handler.
+ if (ex->handler_) {
+ auto handler = ex->handler_;
+ // Avoid to keep a circular reference.
+ ex->handler_ = Handler();
+ handler(ex);
+ }
+}
+
+int
+TcpExchange::CompleteCheck(const WireDataPtr& response, string& error_msg) {
+ if (!response) {
+ error_msg = "null response";
+ return (-1);
+ }
+ const WireData& buffer = *response;
+ if (buffer.size() < AUTH_HDR_LEN) {
+ return (0);
+ }
+ uint16_t length = static_cast<uint16_t>(buffer[2]) << 8;
+ length |= static_cast<uint16_t>(buffer[3]);
+ if (length > buffer.size()) {
+ return (0);
+ } else if (length == buffer.size()) {
+ return (1);
+ } else {
+ error_msg = "overflow";
+ return (-2);
+ }
+}
+
} // end of namespace isc::radius
} // end of namespace isc
/// @param maxretries maximum number of retries for a server.
/// @param servers Servers.
/// @param handler Termination handler.
+ /// @param protocol Protocol (default to UDP).
static ExchangePtr create(const asiolink::IOServicePtr io_service,
const MessagePtr& request,
unsigned maxretries,
const Servers& servers,
- Handler handler);
+ Handler handler,
+ RadiusProtocol protocol = PW_PROTO_UDP);
/// @brief Factory.
///
- /// Sync version.
+ /// Sync version (UDP only).
///
/// @param request request message to send.
/// @param maxretries maximum number of retries for a server.
/// @brief Shutdown.
virtual void shutdown() = 0;
+ /// @brief Process response.
+ void processResponse();
+
protected:
/// @brief Constructor.
///
/// Async version.
///
- /// @param io_service Reference to the IO service.
/// @param request request message to send.
/// @param maxretries maximum number of retries for a server.
/// @param servers Servers.
/// @param handler Termination handler.
- Exchange(const asiolink::IOServicePtr io_service,
- const MessagePtr& request,
+ Exchange(const MessagePtr& request,
unsigned maxretries,
const Servers& servers,
Handler handler);
/// @brief The identifier (random value in hexadecimal).
std::string identifier_;
- /// @brief IO service (argument for async or internal for sync).
- asiolink::IOServicePtr io_service_;
-
/// @brief Sync / async flag.
bool sync_;
virtual void shutdown();
protected:
+ /// @brief IO service (argument for async or internal for sync).
+ asiolink::IOServicePtr io_service_;
+
/// @brief Started flag.
bool started_;
void terminate();
};
+/// @brief RADIUS/TCP exchange (forward declaration).
+class TcpExchange;
+
+/// @brief Type of shared pointers to RADIUS/TCP exchange object.
+typedef boost::shared_ptr<TcpExchange> TcpExchangePtr;
+
+/// @brief RADIUS/TCP (or RADIUS/TLS) Exchange.
+class TcpExchange : public Exchange,
+ public boost::enable_shared_from_this<TcpExchange> {
+public:
+ /// @brief Constructor.
+ ///
+ /// Async version.
+ ///
+ /// @param request request message to send.
+ /// @param maxretries maximum number of retries for a server.
+ /// @param servers Servers.
+ /// @param handler Termination handler.
+ TcpExchange(const MessagePtr& request,
+ unsigned maxretries,
+ const Servers& servers,
+ Handler handler);
+
+ /// @note: no sync version.
+
+ /// @brief Destructor.
+ virtual ~TcpExchange() = default;
+
+ /// @brief Start.
+ virtual void start();
+
+ /// @brief Shutdown.
+ virtual void shutdown();
+
+protected:
+ /// @brief Start time.
+ std::chrono::steady_clock::time_point start_time_;
+
+ /// @brief Current server.
+ ServerPtr server_;
+
+ /// @brief Response wire data.
+ isc::tcp::WireDataPtr resp_;
+
+ /// @brief Build request.
+ void buildRequest();
+
+ /// @brief Request handler.
+ ///
+ /// @param ex Point to the Exchange.
+ /// @param ec Boost error code.
+ /// @param response Pointer to response wire data.
+ /// @param error_msg Error message.
+ static void RequestHandler(TcpExchangePtr ex,
+ const boost::system::error_code& ec,
+ const isc::tcp::WireDataPtr& response,
+ const std::string& error_msg);
+
+ /// @brief Complete check.
+ ///
+ /// @param response Pointer to response wire data.
+ /// @param error_msg Reference to the error message.
+ /// @return status (>0 complete, 0 incomplete, <0 error).
+ static int CompleteCheck(const isc::tcp::WireDataPtr& response,
+ std::string& error_msg);
+};
+
} // end of namespace isc::radius
} // end of namespace isc