]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#4274] Checkpoint: finished RcpExchange
authorFrancis Dupont <fdupont@isc.org>
Sat, 24 Jan 2026 00:09:49 +0000 (01:09 +0100)
committerFrancis Dupont <fdupont@isc.org>
Mon, 9 Feb 2026 21:05:46 +0000 (22:05 +0100)
src/hooks/dhcp/radius/client_exchange.cc
src/hooks/dhcp/radius/client_exchange.h
src/hooks/dhcp/radius/client_message.h
src/hooks/dhcp/radius/radius.cc
src/hooks/dhcp/radius/radius.h
src/hooks/dhcp/radius/tests/exchange_unittests.cc

index 4fbd8076b27c0aaffc2adec081459ea6481e0760..0f6a4c3c5352d9c0a66ab53ad4b876905814ac0c 100644 (file)
@@ -25,6 +25,7 @@
 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;
@@ -55,17 +56,13 @@ exchangeRCtoText(const int rc) {
     }
 }
 
-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");
     }
@@ -81,8 +78,8 @@ Exchange::Exchange(const asiolink::IOServicePtr io_service,
 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");
@@ -98,9 +95,15 @@ Exchange::create(const asiolink::IOServicePtr io_service,
                  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
@@ -115,18 +118,22 @@ UdpExchange::UdpExchange(const asiolink::IOServicePtr io_service,
                          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),
@@ -158,7 +165,6 @@ Exchange::createIdentifier() {
     rs << hex << setfill('0') << setw(8) << ri;
     identifier_ = rs.str();
 }
-
 void
 Exchange::logReplyMessages() const {
     if (!received_) {
@@ -308,6 +314,11 @@ UdpExchange::buildRequest() {
     Exchange::buildRequest(server_, start_time_);
 }
 
+void
+TcpExchange::buildRequest() {
+    Exchange::buildRequest(server_, start_time_);
+}
+
 void
 UdpExchange::open() {
     if (RadiusImpl::shutdown_) {
@@ -567,6 +578,87 @@ UdpExchange::sentHandler(UdpExchangePtr ex,
                                         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,
@@ -617,83 +709,7 @@ UdpExchange::receivedHandler(UdpExchangePtr ex,
     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)) {
@@ -782,5 +798,141 @@ UdpExchange::timeoutHandler(UdpExchangePtr ex) {
     }
 }
 
+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
index 4b7b2ab2b015eb2fff1e83066d985a7bd712c443..c8a656d7ad3ab100334418cc22cec94bb0741b0c 100644 (file)
@@ -79,15 +79,17 @@ public:
     /// @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.
@@ -136,18 +138,19 @@ public:
     /// @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);
@@ -166,9 +169,6 @@ protected:
     /// @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_;
 
@@ -251,6 +251,9 @@ public:
     virtual void shutdown();
 
 protected:
+    /// @brief IO service (argument for async or internal for sync).
+    asiolink::IOServicePtr io_service_;
+
     /// @brief Started flag.
     bool started_;
 
@@ -342,6 +345,73 @@ protected:
     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
 
index cd12e5af0c257ffa953208ea0e0da45dbc43f310..9b36dcc248b84180028e052368ce7a8ae714fa20 100644 (file)
@@ -10,6 +10,7 @@
 #include <client_attribute.h>
 #include <exceptions/exceptions.h>
 #include <asiolink/io_address.h>
+#include <tcp/wire_data.h>
 #include <string>
 #include <vector>
 
index 772c2e7f502722d966c9a7eeea6f3065ac4c94da..4b7ab7114fce71ad9ca8768c356462228806af30 100644 (file)
@@ -27,6 +27,7 @@ using namespace isc::asiolink;
 using namespace isc::data;
 using namespace isc::db;
 using namespace isc::dhcp;
+using namespace isc::tcp;
 using namespace isc::util;
 
 namespace isc {
@@ -170,7 +171,8 @@ RadiusImpl::instancePtr() {
 }
 
 RadiusImpl::RadiusImpl()
-    : proto_(PW_PROTO_UDP), udp_client_(), common_(new RadiusTls()),
+  : proto_(PW_PROTO_UDP), udp_client_(), tcp_client_(),
+      common_(new RadiusTls()),
       auth_(new RadiusAccess()), acct_(new RadiusAccounting()),
       bindaddr_("*"), canonical_mac_address_(false),
       clientid_pop0_(false), clientid_printable_(false),
@@ -231,6 +233,9 @@ void RadiusImpl::cleanup() {
     if (udp_client_) {
         udp_client_.reset();
     }
+    if (tcp_client_) {
+        tcp_client_.reset();
+    }
 
     io_context_.reset(new IOService());
     if (getIOService()) {
@@ -307,15 +312,33 @@ RadiusImpl::startServices() {
     if (multi_threaded) {
         // Schedule a start of the services. This ensures we begin after
         // the dust has settled and Kea MT mode has been firmly established.
-        io_service_->post([this, thread_pool_size]() {
-            udp_client_.reset(new UdpClient(io_service_, thread_pool_size));
+        if (proto_ == PW_PROTO_UDP) {
+            io_service_->post([this, thread_pool_size]() {
+                udp_client_.reset(new UdpClient(io_service_,
+                                                thread_pool_size));
+
+                io_context_ = udp_client_->getThreadIOService();
+
+                udp_client_->start();
+            });
+        } else {
+            io_service_->post([this, multi_threaded, thread_pool_size]() {
+                tcp_client_.reset(new TcpClient(io_service_,
+                                                multi_threaded,
+                                                thread_pool_size,
+                                                true));
 
-            io_context_ = udp_client_->getThreadIOService();
+                io_context_ = tcp_client_->getThreadIOService();
 
-            udp_client_->start();
-        });
+                tcp_client_->start();
+            });
+        }
     } else {
-        udp_client_.reset(new UdpClient(io_service_, 0));
+        if (proto_ == PW_PROTO_UDP) {
+            udp_client_.reset(new UdpClient(io_service_, 0));
+        } else {
+            tcp_client_.reset(new TcpClient(io_service_, false, 0));
+        }
     }
 }
 
index f455fd2b8ac08efc5a41d4a6ed82d38cbc586c4b..2e3fd55fca2bd4470538da5a956d9fb4d27a94b8 100644 (file)
@@ -17,6 +17,7 @@
 #include <asiolink/io_service_thread_pool.h>
 #include <dhcpsrv/cache_host_data_source.h>
 #include <dhcpsrv/host.h>
+#include <tcp/tcp_client.h>
 
 #include <boost/noncopyable.hpp>
 #include <boost/shared_ptr.hpp>
@@ -270,6 +271,9 @@ public:
     /// @brief UDP client.
     UdpClientPtr udp_client_;
 
+    /// @brief TCP client.
+    isc::tcp::TcpClientPtr tcp_client_;
+
     /// @brief Subnet ID to NAS port map.
     std::map<uint32_t, uint32_t> remap_;
 
index 8ea6eb1fcb9e9e40cbc319fed602e4dc509173fc..475adf79bd53627d8596861061d870feac25cac2 100644 (file)
@@ -34,13 +34,7 @@ TEST(TestExchange, async) {
     MessagePtr msg;
     Servers servers;
 
-    // No IO service.
-    EXPECT_THROW_MSG(Exchange::create(io_service, msg, 0, servers,
-                                      Exchange::Handler()),
-                     BadValue, "null IO service");
-
     // No message.
-    io_service.reset(new IOService());
     EXPECT_THROW_MSG(Exchange::create(io_service, msg, 0, servers,
                                       Exchange::Handler()),
                      BadValue, "null request");
@@ -67,8 +61,13 @@ TEST(TestExchange, async) {
                                       Exchange::Handler()),
                      BadValue, "null handler");
 
-    // No error.
+    // No IO service.
     auto handler = [] (const ExchangePtr) { };
+    EXPECT_THROW_MSG(Exchange::create(io_service, msg, 0, servers, handler),
+                     BadValue, "null IO service");
+
+    // No error.
+    io_service.reset(new IOService());
     ASSERT_NO_THROW_LOG(exchange = Exchange::create(io_service, msg, 0,
                                                     servers, handler));