blob: a612680b328e1ff45e10bc9fc0e4d5af576d9f74 [file] [log] [blame]
/*
* Copyright (c) 2021, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <openthread/config.h>
#include "test_platform.h"
#include "test_util.hpp"
#include "common/arg_macros.hpp"
#include "common/array.hpp"
#include "common/as_core_type.hpp"
#include "common/time.hpp"
#include "instance/instance.hpp"
#include "net/dns_dso.hpp"
namespace ot {
#if OPENTHREAD_CONFIG_DNS_DSO_ENABLE
extern "C" {
static uint32_t sNow = 0;
static uint32_t sAlarmTime;
static bool sAlarmOn = false;
static otInstance *sInstance;
// Logs a message and adds current time (sNow) as "<hours>:<min>:<secs>.<msec>"
#define Log(...) \
printf("%02u:%02u:%02u.%03u " OT_FIRST_ARG(__VA_ARGS__) "\n", (sNow / 36000000), (sNow / 60000) % 60, \
(sNow / 1000) % 60, sNow % 1000 OT_REST_ARGS(__VA_ARGS__))
void otPlatAlarmMilliStop(otInstance *) { sAlarmOn = false; }
void otPlatAlarmMilliStartAt(otInstance *, uint32_t aT0, uint32_t aDt)
{
sAlarmOn = true;
sAlarmTime = aT0 + aDt;
Log(" otPlatAlarmMilliStartAt(time:%u.%03u, dt:%u.%03u)", sAlarmTime / 1000, sAlarmTime % 1000,
(sAlarmTime - sNow) / 1000, (sAlarmTime - sNow) % 1000);
}
uint32_t otPlatAlarmMilliGetNow(void) { return sNow; }
} // extern "C"
void AdvanceTime(uint32_t aDuration)
{
uint32_t time = sNow + aDuration;
Log(" AdvanceTime for %u.%03u", aDuration / 1000, aDuration % 1000);
while (TimeMilli(sAlarmTime) <= TimeMilli(time))
{
sNow = sAlarmTime;
otPlatAlarmMilliFired(sInstance);
}
sNow = time;
}
namespace Dns {
OT_TOOL_PACKED_BEGIN
class TestTlv : public Dso::Tlv
{
public:
static constexpr Type kType = 0xf800;
void Init(uint8_t aValue)
{
Tlv::Init(kType, sizeof(*this) - sizeof(Tlv));
mValue = aValue;
}
bool IsValid(void) const { return GetSize() >= sizeof(*this); }
uint8_t GetValue(void) const { return mValue; }
private:
uint8_t mValue;
} OT_TOOL_PACKED_END;
extern "C" void otPlatDsoSend(otPlatDsoConnection *aConnection, otMessage *aMessage);
class Connection : public Dso::Connection
{
friend void otPlatDsoSend(otPlatDsoConnection *aConnection, otMessage *aMessage);
public:
explicit Connection(Instance &aInstance,
const char *aName,
const Ip6::SockAddr &aLocalSockAddr,
const Ip6::SockAddr &aPeerSockAddr)
: Dso::Connection(aInstance, aPeerSockAddr, sCallbacks)
, mName(aName)
, mLocalSockAddr(aLocalSockAddr)
{
ClearTestFlags();
}
const char *GetName(void) const { return mName; }
const Ip6::SockAddr &GetLocalSockAddr(void) const { return mLocalSockAddr; }
void ClearTestFlags(void)
{
mDidGetConnectedSignal = false;
mDidGetSessionEstablishedSignal = false;
mDidGetDisconnectSignal = false;
mDidSendMessage = false;
mDidReceiveMessage = false;
mDidProcessRequest = false;
mDidProcessUnidirectional = false;
mDidProcessResponse = false;
}
bool DidGetConnectedSignal(void) const { return mDidGetConnectedSignal; }
bool DidGetSessionEstablishedSignal(void) const { return mDidGetSessionEstablishedSignal; }
bool DidGetDisconnectSignal(void) const { return mDidGetDisconnectSignal; }
bool DidSendMessage(void) const { return mDidSendMessage; }
bool DidReceiveMessage(void) const { return mDidReceiveMessage; }
bool DidProcessRequest(void) const { return mDidProcessRequest; }
bool DidProcessUnidirectional(void) const { return mDidProcessUnidirectional; }
bool DidProcessResponse(void) const { return mDidProcessResponse; }
uint8_t GetLastRxTestTlvValue(void) const { return mLastRxTestTlvValue; }
Dns::Header::Response GetLastRxResponseCode(void) const { return mLastRxResponseCode; }
void SendTestRequestMessage(uint8_t aValue = 0, uint32_t aResponseTimeout = Dso::kResponseTimeout)
{
MessageId messageId;
mLastTxTestTlvValue = aValue;
SuccessOrQuit(SendRequestMessage(PrepareTestMessage(aValue), messageId, aResponseTimeout));
}
void SendTestUnidirectionalMessage(uint8_t aValue = 0)
{
mLastTxTestTlvValue = aValue;
SuccessOrQuit(SendUnidirectionalMessage(PrepareTestMessage(aValue)));
}
private:
Message &PrepareTestMessage(uint8_t aValue)
{
TestTlv testTlv;
Message *message = NewMessage();
VerifyOrQuit(message != nullptr);
testTlv.Init(aValue);
SuccessOrQuit(message->Append(testTlv));
return *message;
}
void ParseTestMessage(const Message &aMessage)
{
TestTlv testTlv;
Dso::Tlv tlv;
uint16_t offset = aMessage.GetOffset();
// Test message MUST only contain Test TLV and Encryption
// Padding TLV.
SuccessOrQuit(aMessage.Read(offset, testTlv));
VerifyOrQuit(testTlv.GetType() == TestTlv::kType);
VerifyOrQuit(testTlv.IsValid());
offset += testTlv.GetSize();
mLastRxTestTlvValue = testTlv.GetValue();
SuccessOrQuit(aMessage.Read(offset, tlv));
VerifyOrQuit(tlv.GetType() == Dso::Tlv::kEncryptionPaddingType);
offset += tlv.GetSize();
VerifyOrQuit(offset == aMessage.GetLength());
}
void SendTestResponseMessage(MessageId aResponseId, uint8_t aValue)
{
mLastTxTestTlvValue = aValue;
SuccessOrQuit(SendResponseMessage(PrepareTestMessage(aValue), aResponseId));
}
//---------------------------------------------------------------------
// Callback methods
void HandleConnected(void) { mDidGetConnectedSignal = true; }
void HandleSessionEstablished(void) { mDidGetSessionEstablishedSignal = true; }
void HandleDisconnected(void) { mDidGetDisconnectSignal = true; }
Error ProcessRequestMessage(MessageId aMessageId, const Message &aMessage, Dso::Tlv::Type aPrimaryTlvType)
{
Error error = kErrorNone;
Log(" ProcessRequestMessage(primaryTlv:0x%04x) on %s", aPrimaryTlvType, mName);
mDidProcessRequest = true;
VerifyOrExit(aPrimaryTlvType == TestTlv::kType, error = kErrorNotFound);
ParseTestMessage(aMessage);
SendTestResponseMessage(aMessageId, mLastRxTestTlvValue);
exit:
return error;
}
Error ProcessUnidirectionalMessage(const Message &aMessage, Dso::Tlv::Type aPrimaryTlvType)
{
Log(" ProcessUnidirectionalMessage(primaryTlv:0x%04x) on %s", aPrimaryTlvType, mName);
mDidProcessUnidirectional = true;
if (aPrimaryTlvType == TestTlv::kType)
{
ParseTestMessage(aMessage);
}
return kErrorNone;
}
Error ProcessResponseMessage(const Dns::Header &aHeader,
const Message &aMessage,
Dso::Tlv::Type aResponseTlvType,
Dso::Tlv::Type aRequestTlvType)
{
Error error = kErrorNone;
mDidProcessResponse = true;
mLastRxResponseCode = aHeader.GetResponseCode();
Log(" ProcessResponseMessage(responseTlv:0x%04x) on %s (response-Code:%u) ", aResponseTlvType, mName,
mLastRxResponseCode);
VerifyOrExit(mLastRxResponseCode == Dns::Header::kResponseSuccess);
// During test we only expect a Test TLV response with
// a matching TLV value to what was sent last.
VerifyOrQuit(aResponseTlvType == TestTlv::kType);
VerifyOrQuit(aRequestTlvType == TestTlv::kType);
ParseTestMessage(aMessage);
VerifyOrQuit(mLastRxTestTlvValue == mLastTxTestTlvValue);
exit:
return error;
}
static void HandleConnected(Dso::Connection &aConnection)
{
static_cast<Connection &>(aConnection).HandleConnected();
}
static void HandleSessionEstablished(Dso::Connection &aConnection)
{
static_cast<Connection &>(aConnection).HandleSessionEstablished();
}
static void HandleDisconnected(Dso::Connection &aConnection)
{
static_cast<Connection &>(aConnection).HandleDisconnected();
}
static Error ProcessRequestMessage(Dso::Connection &aConnection,
MessageId aMessageId,
const Message &aMessage,
Dso::Tlv::Type aPrimaryTlvType)
{
return static_cast<Connection &>(aConnection).ProcessRequestMessage(aMessageId, aMessage, aPrimaryTlvType);
}
static Error ProcessUnidirectionalMessage(Dso::Connection &aConnection,
const Message &aMessage,
Dso::Tlv::Type aPrimaryTlvType)
{
return static_cast<Connection &>(aConnection).ProcessUnidirectionalMessage(aMessage, aPrimaryTlvType);
}
static Error ProcessResponseMessage(Dso::Connection &aConnection,
const Dns::Header &aHeader,
const Message &aMessage,
Dso::Tlv::Type aResponseTlvType,
Dso::Tlv::Type aRequestTlvType)
{
return static_cast<Connection &>(aConnection)
.ProcessResponseMessage(aHeader, aMessage, aResponseTlvType, aRequestTlvType);
}
const char *mName;
Ip6::SockAddr mLocalSockAddr;
bool mDidGetConnectedSignal;
bool mDidGetSessionEstablishedSignal;
bool mDidGetDisconnectSignal;
bool mDidSendMessage;
bool mDidReceiveMessage;
bool mDidProcessRequest;
bool mDidProcessUnidirectional;
bool mDidProcessResponse;
uint8_t mLastTxTestTlvValue;
uint8_t mLastRxTestTlvValue;
Dns::Header::Response mLastRxResponseCode;
static Callbacks sCallbacks;
};
Dso::Connection::Callbacks Connection::sCallbacks(Connection::HandleConnected,
Connection::HandleSessionEstablished,
Connection::HandleDisconnected,
Connection::ProcessRequestMessage,
Connection::ProcessUnidirectionalMessage,
Connection::ProcessResponseMessage);
static constexpr uint16_t kMaxConnections = 5;
static Array<Connection *, kMaxConnections> sConnections;
static Connection *FindPeerConnection(const Connection &aConnetion)
{
Connection *peerConn = nullptr;
for (Connection *conn : sConnections)
{
if (conn->GetLocalSockAddr() == aConnetion.GetPeerSockAddr())
{
peerConn = conn;
break;
}
}
return peerConn;
}
extern "C" {
static bool sDsoListening = false;
// This test flag indicates whether the `otPlatDso` API should
// forward a sent message to the peer connection. It can be set to
// `false` to drop the messages to test timeout behaviors on the
// peer.
static bool sTestDsoForwardMessageToPeer = true;
// This test flag indicate whether when disconnecting a connection
// (using `otPlatDsoDisconnect()` to signal the peer connection about
// the disconnect. Default behavior is set to `true`. It can be set
// to `false` to test certain timeout behavior on peer side.
static bool sTestDsoSignalDisconnectToPeer = true;
void otPlatDsoEnableListening(otInstance *, bool aEnable)
{
Log(" otPlatDsoEnableListening(%s)", aEnable ? "true" : "false");
sDsoListening = aEnable;
}
void otPlatDsoConnect(otPlatDsoConnection *aConnection, const otSockAddr *aPeerSockAddr)
{
Connection &conn = *static_cast<Connection *>(aConnection);
Connection *peerConn = nullptr;
const Ip6::SockAddr &peerSockAddr = AsCoreType(aPeerSockAddr);
Log(" otPlatDsoConnect(%s, aPeer:0x%04x)", conn.GetName(), peerSockAddr.GetPort());
VerifyOrQuit(conn.GetPeerSockAddr() == peerSockAddr);
VerifyOrQuit(conn.GetState() == Connection::kStateConnecting);
if (!sDsoListening)
{
Log(" Server is not listening");
ExitNow();
}
peerConn = static_cast<Connection *>(otPlatDsoAccept(otPlatDsoGetInstance(aConnection), aPeerSockAddr));
if (peerConn == nullptr)
{
Log(" Request rejected");
ExitNow();
}
Log(" Request accepted");
VerifyOrQuit(peerConn->GetState() == Connection::kStateConnecting);
Log(" Signalling `Connected` on peer connection (%s)", peerConn->GetName());
otPlatDsoHandleConnected(peerConn);
Log(" Signalling `Connected` on connection (%s)", conn.GetName());
otPlatDsoHandleConnected(aConnection);
exit:
return;
}
void otPlatDsoSend(otPlatDsoConnection *aConnection, otMessage *aMessage)
{
Connection &conn = *static_cast<Connection *>(aConnection);
Connection *peerConn = nullptr;
Log(" otPlatDsoSend(%s), message-len:%u", conn.GetName(), AsCoreType(aMessage).GetLength());
VerifyOrQuit(conn.GetState() != Connection::kStateDisconnected);
VerifyOrQuit(conn.GetState() != Connection::kStateConnecting);
conn.mDidSendMessage = true;
if (sTestDsoForwardMessageToPeer)
{
peerConn = FindPeerConnection(conn);
VerifyOrQuit(peerConn != nullptr);
VerifyOrQuit(peerConn->GetState() != Connection::kStateDisconnected);
VerifyOrQuit(peerConn->GetState() != Connection::kStateConnecting);
Log(" Sending the message to peer connection (%s)", peerConn->GetName());
peerConn->mDidReceiveMessage = true;
otPlatDsoHandleReceive(peerConn, aMessage);
}
else
{
Log(" Dropping the message");
}
}
void otPlatDsoDisconnect(otPlatDsoConnection *aConnection, otPlatDsoDisconnectMode aMode)
{
Connection &conn = *static_cast<Connection *>(aConnection);
Connection *peerConn = nullptr;
Log(" otPlatDsoDisconnect(%s, mode:%s)", conn.GetName(),
(aMode == OT_PLAT_DSO_DISCONNECT_MODE_GRACEFULLY_CLOSE) ? "close" : "abort");
VerifyOrQuit(conn.GetState() == Connection::kStateDisconnected);
if (sTestDsoSignalDisconnectToPeer)
{
peerConn = FindPeerConnection(conn);
if (peerConn == nullptr)
{
Log(" No peer connection found");
}
else if (peerConn->GetState() == Connection::kStateDisconnected)
{
Log(" Peer connection (%s) already disconnected", peerConn->GetName());
}
else
{
Log(" Signaling `Disconnected` on peer connection (%s)", peerConn->GetName());
otPlatDsoHandleDisconnected(peerConn, aMode);
}
}
}
} // extern "C"
Dso::Connection *AcceptConnection(Instance &aInstance, const Ip6::SockAddr &aPeerSockAddr)
{
OT_UNUSED_VARIABLE(aInstance);
Connection *rval = nullptr;
Log(" AcceptConnection(peer:0x%04x)", aPeerSockAddr.GetPort());
for (Connection *conn : sConnections)
{
if (conn->GetLocalSockAddr() == aPeerSockAddr)
{
VerifyOrQuit(conn->GetState() == Connection::kStateDisconnected);
rval = conn;
break;
}
}
if (rval != nullptr)
{
Log(" Accepting and returning connection %s", rval->GetName());
}
else
{
Log(" Rejecting");
}
return rval;
}
static constexpr uint8_t kKeepAliveTestIterations = 3;
static void VerifyKeepAliveExchange(Connection &aClientConn,
Connection &aServerConn,
uint32_t aKeepAliveInterval,
uint8_t aNumIterations = kKeepAliveTestIterations)
{
for (uint8_t n = 0; n < aNumIterations; n++)
{
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Test Keep Alive message exchange, iter %d", n + 1);
aClientConn.ClearTestFlags();
aServerConn.ClearTestFlags();
AdvanceTime(aKeepAliveInterval - 1);
VerifyOrQuit(!aClientConn.DidSendMessage());
VerifyOrQuit(!aServerConn.DidReceiveMessage());
Log("No message before keep alive timeout");
AdvanceTime(1);
VerifyOrQuit(aClientConn.DidSendMessage());
VerifyOrQuit(aServerConn.DidReceiveMessage());
Log("KeepAlive message exchanged after keep alive time elapses");
}
}
void TestDso(void)
{
static constexpr uint16_t kPortA = 0xaaaa;
static constexpr uint16_t kPortB = 0xbbbb;
static constexpr Dso::Tlv::Type kUnknownTlvType = 0xf801;
static constexpr uint32_t kRetryDelayInterval = TimeMilli::SecToMsec(3600);
static constexpr uint32_t kLongResponseTimeout = Dso::kResponseTimeout + TimeMilli::SecToMsec(17);
Instance &instance = *static_cast<Instance *>(testInitInstance());
Ip6::SockAddr serverSockAddr(kPortA);
Ip6::SockAddr clientSockAddr(kPortB);
Connection serverConn(instance, "serverConn", serverSockAddr, clientSockAddr);
Connection clientConn(instance, "clinetConn", clientSockAddr, serverSockAddr);
Message *message;
Dso::Tlv tlv;
Connection::MessageId messageId;
sNow = 0;
sInstance = &instance;
SuccessOrQuit(sConnections.PushBack(&serverConn));
SuccessOrQuit(sConnections.PushBack(&clientConn));
VerifyOrQuit(serverConn.GetPeerSockAddr() == clientSockAddr);
VerifyOrQuit(clientConn.GetPeerSockAddr() == serverSockAddr);
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
instance.Get<Dso>().StartListening(AcceptConnection);
VerifyOrQuit(instance.Get<Dso>().FindClientConnection(clientSockAddr) == nullptr);
VerifyOrQuit(instance.Get<Dso>().FindServerConnection(clientSockAddr) == nullptr);
VerifyOrQuit(instance.Get<Dso>().FindClientConnection(serverSockAddr) == nullptr);
VerifyOrQuit(instance.Get<Dso>().FindServerConnection(serverSockAddr) == nullptr);
Log("-------------------------------------------------------------------------------------------");
Log("Connect from client to server");
clientConn.Connect();
VerifyOrQuit(clientConn.GetState() == Connection::kStateConnectedButSessionless);
VerifyOrQuit(serverConn.GetState() == Connection::kStateConnectedButSessionless);
VerifyOrQuit(clientConn.IsClient());
VerifyOrQuit(!clientConn.IsServer());
VerifyOrQuit(!serverConn.IsClient());
VerifyOrQuit(serverConn.IsServer());
// Note that we find connection with a peer address
VerifyOrQuit(instance.Get<Dso>().FindClientConnection(serverSockAddr) == &clientConn);
VerifyOrQuit(instance.Get<Dso>().FindServerConnection(serverSockAddr) == nullptr);
VerifyOrQuit(instance.Get<Dso>().FindClientConnection(clientSockAddr) == nullptr);
VerifyOrQuit(instance.Get<Dso>().FindServerConnection(clientSockAddr) == &serverConn);
VerifyOrQuit(clientConn.DidGetConnectedSignal());
VerifyOrQuit(!clientConn.DidGetSessionEstablishedSignal());
VerifyOrQuit(!clientConn.DidGetDisconnectSignal());
VerifyOrQuit(serverConn.DidGetConnectedSignal());
VerifyOrQuit(!serverConn.DidGetSessionEstablishedSignal());
VerifyOrQuit(!serverConn.DidGetDisconnectSignal());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Send keep alive message to establish connection");
clientConn.ClearTestFlags();
serverConn.ClearTestFlags();
SuccessOrQuit(clientConn.SendKeepAliveMessage());
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(!clientConn.DidGetConnectedSignal());
VerifyOrQuit(clientConn.DidGetSessionEstablishedSignal());
VerifyOrQuit(!clientConn.DidGetDisconnectSignal());
VerifyOrQuit(!serverConn.DidGetConnectedSignal());
VerifyOrQuit(serverConn.DidGetSessionEstablishedSignal());
VerifyOrQuit(!serverConn.DidGetDisconnectSignal());
VerifyOrQuit(clientConn.GetKeepAliveInterval() == Dso::kDefaultTimeout);
VerifyOrQuit(clientConn.GetInactivityTimeout() == Dso::kDefaultTimeout);
VerifyOrQuit(serverConn.GetKeepAliveInterval() == Dso::kDefaultTimeout);
VerifyOrQuit(serverConn.GetInactivityTimeout() == Dso::kDefaultTimeout);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Close connection");
clientConn.ClearTestFlags();
serverConn.ClearTestFlags();
clientConn.Disconnect(Connection::kGracefullyClose, Connection::kReasonInactivityTimeout);
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonInactivityTimeout);
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonPeerClosed);
VerifyOrQuit(!clientConn.DidGetConnectedSignal());
VerifyOrQuit(!clientConn.DidGetSessionEstablishedSignal());
VerifyOrQuit(!clientConn.DidGetDisconnectSignal());
VerifyOrQuit(!serverConn.DidGetConnectedSignal());
VerifyOrQuit(!serverConn.DidGetSessionEstablishedSignal());
VerifyOrQuit(serverConn.DidGetDisconnectSignal());
VerifyOrQuit(instance.Get<Dso>().FindClientConnection(clientSockAddr) == nullptr);
VerifyOrQuit(instance.Get<Dso>().FindServerConnection(clientSockAddr) == nullptr);
VerifyOrQuit(instance.Get<Dso>().FindClientConnection(serverSockAddr) == nullptr);
VerifyOrQuit(instance.Get<Dso>().FindServerConnection(serverSockAddr) == nullptr);
Log("-------------------------------------------------------------------------------------------");
Log("Connection timeout when server is not listening");
instance.Get<Dso>().StopListening();
clientConn.ClearTestFlags();
clientConn.Connect();
VerifyOrQuit(clientConn.GetState() == Connection::kStateConnecting);
VerifyOrQuit(instance.Get<Dso>().FindClientConnection(serverSockAddr) == &clientConn);
VerifyOrQuit(instance.Get<Dso>().FindServerConnection(serverSockAddr) == nullptr);
AdvanceTime(Dso::kConnectingTimeout);
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonFailedToConnect);
VerifyOrQuit(instance.Get<Dso>().FindClientConnection(serverSockAddr) == nullptr);
VerifyOrQuit(instance.Get<Dso>().FindServerConnection(serverSockAddr) == nullptr);
VerifyOrQuit(!clientConn.DidGetConnectedSignal());
VerifyOrQuit(!clientConn.DidGetSessionEstablishedSignal());
VerifyOrQuit(clientConn.DidGetDisconnectSignal());
Log("-------------------------------------------------------------------------------------------");
Log("Keep Alive Timeout behavior");
// Keep Alive timeout smaller than min value should be rejected.
VerifyOrQuit(clientConn.SetTimeouts(Dso::kInfiniteTimeout, Dso::kMinKeepAliveInterval - 1) == kErrorInvalidArgs);
instance.Get<Dso>().StartListening(AcceptConnection);
SuccessOrQuit(serverConn.SetTimeouts(Dso::kInfiniteTimeout, Dso::kMinKeepAliveInterval));
VerifyOrQuit(serverConn.GetKeepAliveInterval() == Dso::kMinKeepAliveInterval);
VerifyOrQuit(serverConn.GetInactivityTimeout() == Dso::kInfiniteTimeout);
clientConn.Connect();
SuccessOrQuit(clientConn.SendKeepAliveMessage());
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetKeepAliveInterval() == Dso::kMinKeepAliveInterval);
VerifyOrQuit(serverConn.GetInactivityTimeout() == Dso::kInfiniteTimeout);
VerifyOrQuit(clientConn.GetKeepAliveInterval() == Dso::kMinKeepAliveInterval);
VerifyOrQuit(clientConn.GetInactivityTimeout() == Dso::kInfiniteTimeout);
VerifyKeepAliveExchange(clientConn, serverConn, Dso::kMinKeepAliveInterval);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Change Keep Alive interval on server");
clientConn.ClearTestFlags();
serverConn.ClearTestFlags();
SuccessOrQuit(serverConn.SetTimeouts(Dso::kInfiniteTimeout, Dso::kDefaultTimeout));
VerifyOrQuit(serverConn.DidSendMessage());
VerifyOrQuit(clientConn.DidReceiveMessage());
VerifyOrQuit(!clientConn.DidSendMessage());
VerifyOrQuit(serverConn.GetKeepAliveInterval() == Dso::kDefaultTimeout);
VerifyOrQuit(serverConn.GetInactivityTimeout() == Dso::kInfiniteTimeout);
VerifyOrQuit(clientConn.GetKeepAliveInterval() == Dso::kDefaultTimeout);
VerifyOrQuit(clientConn.GetInactivityTimeout() == Dso::kInfiniteTimeout);
VerifyKeepAliveExchange(clientConn, serverConn, Dso::kDefaultTimeout);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Keep Alive timer clear on message send or receive");
clientConn.ClearTestFlags();
serverConn.ClearTestFlags();
AdvanceTime(Dso::kDefaultTimeout / 2);
clientConn.SendTestUnidirectionalMessage();
VerifyOrQuit(clientConn.DidSendMessage());
VerifyOrQuit(serverConn.DidReceiveMessage());
VerifyOrQuit(!serverConn.DidSendMessage());
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
Log("Sent unidirectional message (client->server) at half the keep alive interval");
VerifyKeepAliveExchange(clientConn, serverConn, Dso::kDefaultTimeout, 1);
clientConn.ClearTestFlags();
serverConn.ClearTestFlags();
AdvanceTime(Dso::kDefaultTimeout / 2);
serverConn.SendTestUnidirectionalMessage();
VerifyOrQuit(serverConn.DidSendMessage());
VerifyOrQuit(clientConn.DidReceiveMessage());
VerifyOrQuit(!clientConn.DidSendMessage());
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
Log("Sent unidirectional message (server->client) at half the keep alive interval");
VerifyKeepAliveExchange(clientConn, serverConn, Dso::kDefaultTimeout, 1);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Keep Alive timeout on server");
clientConn.ClearTestFlags();
serverConn.ClearTestFlags();
Log("Drop all sent message (drop Keep Alive msg from client->server)");
sTestDsoForwardMessageToPeer = false;
AdvanceTime(Dso::kDefaultTimeout);
VerifyOrQuit(clientConn.DidSendMessage());
VerifyOrQuit(!serverConn.DidReceiveMessage());
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
Log("Sever waits for twice the interval before Keep Alive timeout");
AdvanceTime(Dso::kDefaultTimeout);
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonKeepAliveTimeout);
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonPeerAborted);
Log("Server aborted connection on Keep Alive timeout");
sTestDsoForwardMessageToPeer = true;
Log("-------------------------------------------------------------------------------------------");
Log("Inactivity Timeout behavior");
SuccessOrQuit(serverConn.SetTimeouts(Dso::kDefaultTimeout, Dso::kMinKeepAliveInterval));
VerifyOrQuit(serverConn.GetKeepAliveInterval() == Dso::kMinKeepAliveInterval);
VerifyOrQuit(serverConn.GetInactivityTimeout() == Dso::kDefaultTimeout);
clientConn.Connect();
SuccessOrQuit(clientConn.SendKeepAliveMessage());
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetKeepAliveInterval() == Dso::kMinKeepAliveInterval);
VerifyOrQuit(serverConn.GetInactivityTimeout() == Dso::kDefaultTimeout);
VerifyOrQuit(clientConn.GetKeepAliveInterval() == Dso::kMinKeepAliveInterval);
VerifyOrQuit(clientConn.GetInactivityTimeout() == Dso::kDefaultTimeout);
Log("Sending a unidirectional message should clear inactivity timer");
AdvanceTime(Dso::kDefaultTimeout / 2);
clientConn.SendTestUnidirectionalMessage();
AdvanceTime(Dso::kDefaultTimeout - 1);
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
Log("Client keeps the connection up to the inactivity timeout");
AdvanceTime(1);
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonInactivityTimeout);
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonPeerClosed);
Log("Client closes the connection gracefully on inactivity timeout");
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Increasing inactivity timeout in middle");
clientConn.Connect();
SuccessOrQuit(clientConn.SendKeepAliveMessage());
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
AdvanceTime(TimeMilli::SecToMsec(10));
Log("After 10 sec elapses, change the inactivity timeout from 15 to 20 sec");
SuccessOrQuit(serverConn.SetTimeouts(TimeMilli::SecToMsec(20), Dso::kMinKeepAliveInterval));
AdvanceTime(TimeMilli::SecToMsec(10) - 1);
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
Log("Client keeps the connection up to new 20 sec inactivity timeout");
AdvanceTime(1);
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonInactivityTimeout);
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonPeerClosed);
Log("Client closes the connection gracefully on inactivity timeout of 20 sec");
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Decreasing inactivity timeout in middle");
clientConn.Connect();
SuccessOrQuit(clientConn.SendKeepAliveMessage());
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
AdvanceTime(TimeMilli::SecToMsec(10));
Log("After 10 sec elapses, change the inactivity timeout from 15 to 10 sec");
SuccessOrQuit(serverConn.SetTimeouts(TimeMilli::SecToMsec(10), Dso::kMinKeepAliveInterval));
AdvanceTime(0);
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonInactivityTimeout);
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonPeerClosed);
Log("Client closes the connection gracefully on new shorter inactivity timeout of 10 sec");
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Changing inactivity timeout from infinite to finite");
SuccessOrQuit(serverConn.SetTimeouts(Dso::kDefaultTimeout, Dso::kInfiniteTimeout));
clientConn.Connect();
SuccessOrQuit(clientConn.SendKeepAliveMessage());
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
AdvanceTime(TimeMilli::SecToMsec(6));
Log("After 6 sec, change the inactivity to infinite");
SuccessOrQuit(serverConn.SetTimeouts(Dso::kInfiniteTimeout, Dso::kInfiniteTimeout));
AdvanceTime(TimeMilli::SecToMsec(4));
Log("After 4 sec, change the inactivity timeout from infinite to 20 sec");
SuccessOrQuit(serverConn.SetTimeouts(TimeMilli::SecToMsec(20), Dso::kInfiniteTimeout));
AdvanceTime(TimeMilli::SecToMsec(10) - 1);
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
AdvanceTime(1);
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonInactivityTimeout);
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonPeerClosed);
Log("Client closes the connection gracefully after 20 sec since last activity");
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Tracking activity while inactivity timeout is infinite");
SuccessOrQuit(serverConn.SetTimeouts(Dso::kInfiniteTimeout, Dso::kInfiniteTimeout));
clientConn.Connect();
SuccessOrQuit(clientConn.SendKeepAliveMessage());
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
AdvanceTime(TimeMilli::SecToMsec(7));
Log("After 7 sec, send a test message, this clears inactivity timer");
serverConn.SendTestUnidirectionalMessage();
AdvanceTime(TimeMilli::SecToMsec(10));
Log("After 10 sec, change the inactivity timeout from infinite to 15 sec");
SuccessOrQuit(serverConn.SetTimeouts(TimeMilli::SecToMsec(15), Dso::kInfiniteTimeout));
AdvanceTime(TimeMilli::SecToMsec(5) - 1);
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
AdvanceTime(1);
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonInactivityTimeout);
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonPeerClosed);
Log("Client closes the connection gracefully after 15 sec since last activity");
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Inactivity timeout on server");
clientConn.Connect();
SuccessOrQuit(clientConn.SendKeepAliveMessage());
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
SuccessOrQuit(serverConn.SetTimeouts(Dso::kDefaultTimeout, Dso::kInfiniteTimeout));
Log("Wait for inactivity timeout and ensure client disconnect");
Log("Configure test for client not to signal its disconnect to server");
sTestDsoSignalDisconnectToPeer = false;
AdvanceTime(Dso::kDefaultTimeout);
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonInactivityTimeout);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
sTestDsoSignalDisconnectToPeer = true;
Log("Server should disconnect after twice the inactivity timeout");
AdvanceTime(Dso::kDefaultTimeout - 1);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
AdvanceTime(1);
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonInactivityTimeout);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Server reducing inactivity timeout to expired (on server)");
clientConn.Connect();
SuccessOrQuit(clientConn.SendKeepAliveMessage());
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
SuccessOrQuit(serverConn.SetTimeouts(Dso::kDefaultTimeout, Dso::kInfiniteTimeout));
AdvanceTime(TimeMilli::SecToMsec(10));
Log("After 11 sec elapses, change the inactivity timeout from 15 to 2 sec");
SuccessOrQuit(serverConn.SetTimeouts(TimeMilli::SecToMsec(2), Dso::kMinKeepAliveInterval));
sTestDsoSignalDisconnectToPeer = false;
AdvanceTime(0);
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonInactivityTimeout);
sTestDsoSignalDisconnectToPeer = true;
Log("Client closes the connection gracefully on expired timeout");
Log("Configure test for client not to signal its disconnect to server");
AdvanceTime(Dso::kMinServerInactivityWaitTime - 1);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
AdvanceTime(1);
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonInactivityTimeout);
Log("Server wait for kMinServerInactivityWaitTime (5 sec) before closing on expired timeout");
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Long-lived operation");
clientConn.Connect();
SuccessOrQuit(clientConn.SendKeepAliveMessage());
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
SuccessOrQuit(serverConn.SetTimeouts(Dso::kDefaultTimeout, Dso::kInfiniteTimeout));
clientConn.SetLongLivedOperation(true);
serverConn.SetLongLivedOperation(true);
AdvanceTime(2 * Dso::kDefaultTimeout);
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
clientConn.SetLongLivedOperation(false);
AdvanceTime(0);
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
Log("-------------------------------------------------------------------------------------------");
Log("Request, response, and unidirectional message exchange");
SuccessOrQuit(serverConn.SetTimeouts(Dso::kDefaultTimeout, Dso::kDefaultTimeout));
clientConn.Connect();
VerifyOrQuit(clientConn.GetState() == Connection::kStateConnectedButSessionless);
VerifyOrQuit(serverConn.GetState() == Connection::kStateConnectedButSessionless);
clientConn.ClearTestFlags();
serverConn.ClearTestFlags();
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Establish connection using test message request/response");
clientConn.SendTestRequestMessage();
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.DidProcessRequest());
VerifyOrQuit(clientConn.DidProcessResponse());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Send unidirectional test message");
serverConn.ClearTestFlags();
clientConn.SendTestUnidirectionalMessage(0x10);
VerifyOrQuit(serverConn.DidProcessUnidirectional());
VerifyOrQuit(serverConn.GetLastRxTestTlvValue() == 0x10);
clientConn.ClearTestFlags();
serverConn.SendTestUnidirectionalMessage(0x20);
VerifyOrQuit(clientConn.DidProcessUnidirectional());
VerifyOrQuit(clientConn.GetLastRxTestTlvValue() == 0x20);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Exchange request and response");
clientConn.ClearTestFlags();
serverConn.ClearTestFlags();
serverConn.SendTestRequestMessage(0x30);
VerifyOrQuit(clientConn.DidProcessRequest());
VerifyOrQuit(!clientConn.DidProcessResponse());
VerifyOrQuit(!serverConn.DidProcessRequest());
VerifyOrQuit(serverConn.DidProcessResponse());
VerifyOrQuit(serverConn.GetLastRxTestTlvValue() == 0x30);
VerifyOrQuit(clientConn.GetLastRxTestTlvValue() == 0x30);
clientConn.ClearTestFlags();
serverConn.ClearTestFlags();
clientConn.SendTestRequestMessage(0x40);
VerifyOrQuit(!clientConn.DidProcessRequest());
VerifyOrQuit(clientConn.DidProcessResponse());
VerifyOrQuit(serverConn.DidProcessRequest());
VerifyOrQuit(!serverConn.DidProcessResponse());
VerifyOrQuit(serverConn.GetLastRxTestTlvValue() == 0x40);
VerifyOrQuit(clientConn.GetLastRxTestTlvValue() == 0x40);
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Send unknown TLV request");
clientConn.ClearTestFlags();
serverConn.ClearTestFlags();
message = clientConn.NewMessage();
VerifyOrQuit(message != nullptr);
tlv.Init(kUnknownTlvType, 0);
SuccessOrQuit(message->Append(tlv));
SuccessOrQuit(clientConn.SendRequestMessage(*message, messageId));
VerifyOrQuit(!clientConn.DidProcessRequest());
VerifyOrQuit(clientConn.DidProcessResponse());
VerifyOrQuit(serverConn.DidProcessRequest());
VerifyOrQuit(!serverConn.DidProcessResponse());
VerifyOrQuit(clientConn.GetLastRxResponseCode() == Dns::Header::kDsoTypeNotImplemented);
Log("Received a response with DSO Type Unknown error code");
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Send unknown TLV unidirectional");
clientConn.ClearTestFlags();
serverConn.ClearTestFlags();
message = clientConn.NewMessage();
VerifyOrQuit(message != nullptr);
tlv.Init(kUnknownTlvType, 0);
SuccessOrQuit(message->Append(tlv));
SuccessOrQuit(clientConn.SendUnidirectionalMessage(*message));
VerifyOrQuit(serverConn.DidProcessUnidirectional());
Log("Unknown TLV unidirectional is correctly ignored");
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Send malformed/invalid request");
clientConn.ClearTestFlags();
serverConn.ClearTestFlags();
message = clientConn.NewMessage();
VerifyOrQuit(message != nullptr);
tlv.Init(Dso::Tlv::kEncryptionPaddingType, 0);
SuccessOrQuit(message->Append(tlv));
SuccessOrQuit(serverConn.SendRequestMessage(*message, messageId));
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonPeerMisbehavior);
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonPeerAborted);
VerifyOrQuit(clientConn.DidGetDisconnectSignal());
VerifyOrQuit(serverConn.DidGetDisconnectSignal());
Log("Client aborted on invalid request message");
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Response timeout during session establishment");
clientConn.ClearTestFlags();
serverConn.ClearTestFlags();
SuccessOrQuit(serverConn.SetTimeouts(Dso::kResponseTimeout, Dso::kInfiniteTimeout));
clientConn.Connect();
VerifyOrQuit(clientConn.GetState() == Connection::kStateConnectedButSessionless);
VerifyOrQuit(serverConn.GetState() == Connection::kStateConnectedButSessionless);
sTestDsoForwardMessageToPeer = false;
clientConn.SendTestRequestMessage();
sTestDsoForwardMessageToPeer = true;
VerifyOrQuit(clientConn.GetState() == Connection::kStateEstablishingSession);
VerifyOrQuit(serverConn.GetState() == Connection::kStateConnectedButSessionless);
sTestDsoSignalDisconnectToPeer = false;
AdvanceTime(Dso::kResponseTimeout);
sTestDsoSignalDisconnectToPeer = true;
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonResponseTimeout);
VerifyOrQuit(clientConn.DidGetDisconnectSignal());
VerifyOrQuit(serverConn.GetState() == Connection::kStateConnectedButSessionless);
Log("Client disconnected after response timeout");
AdvanceTime(Dso::kResponseTimeout);
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonInactivityTimeout);
VerifyOrQuit(serverConn.DidGetDisconnectSignal());
Log("Server disconnected after twice the inactivity timeout");
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Response timeout after session establishment");
clientConn.ClearTestFlags();
serverConn.ClearTestFlags();
SuccessOrQuit(serverConn.SetTimeouts(Dso::kInfiniteTimeout, Dso::kInfiniteTimeout));
clientConn.Connect();
SuccessOrQuit(clientConn.SendKeepAliveMessage());
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
sTestDsoForwardMessageToPeer = false;
serverConn.SendTestRequestMessage();
sTestDsoForwardMessageToPeer = true;
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
AdvanceTime(Dso::kResponseTimeout - 1);
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
AdvanceTime(1);
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonResponseTimeout);
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonPeerAborted);
VerifyOrQuit(serverConn.DidGetDisconnectSignal());
VerifyOrQuit(clientConn.DidGetDisconnectSignal());
clientConn.ClearTestFlags();
serverConn.ClearTestFlags();
SuccessOrQuit(serverConn.SetTimeouts(Dso::kInfiniteTimeout, Dso::kInfiniteTimeout));
clientConn.Connect();
SuccessOrQuit(clientConn.SendKeepAliveMessage());
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
sTestDsoForwardMessageToPeer = false;
serverConn.SendTestRequestMessage(0, kLongResponseTimeout);
sTestDsoForwardMessageToPeer = true;
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
AdvanceTime(kLongResponseTimeout - 1);
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
AdvanceTime(1);
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonResponseTimeout);
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonPeerAborted);
VerifyOrQuit(serverConn.DidGetDisconnectSignal());
VerifyOrQuit(clientConn.DidGetDisconnectSignal());
Log("-------------------------------------------------------------------------------------------");
Log("Retry Delay message");
clientConn.ClearTestFlags();
serverConn.ClearTestFlags();
SuccessOrQuit(serverConn.SetTimeouts(Dso::kInfiniteTimeout, Dso::kInfiniteTimeout));
clientConn.Connect();
SuccessOrQuit(clientConn.SendKeepAliveMessage());
VerifyOrQuit(clientConn.GetState() == Connection::kStateSessionEstablished);
VerifyOrQuit(serverConn.GetState() == Connection::kStateSessionEstablished);
SuccessOrQuit(serverConn.SendRetryDelayMessage(kRetryDelayInterval, Dns::Header::kResponseServerFailure));
VerifyOrQuit(clientConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(serverConn.GetState() == Connection::kStateDisconnected);
VerifyOrQuit(clientConn.DidGetDisconnectSignal());
VerifyOrQuit(serverConn.DidGetDisconnectSignal());
VerifyOrQuit(clientConn.GetDisconnectReason() == Connection::kReasonServerRetryDelayRequest);
VerifyOrQuit(serverConn.GetDisconnectReason() == Connection::kReasonPeerClosed);
VerifyOrQuit(clientConn.GetRetryDelay() == kRetryDelayInterval);
VerifyOrQuit(clientConn.GetRetryDelayErrorCode() == Dns::Header::kResponseServerFailure);
Log("End of test");
testFreeInstance(&instance);
}
} // namespace Dns
#endif // OPENTHREAD_CONFIG_DNS_DSO_ENABLE
} // namespace ot
int main(void)
{
#if OPENTHREAD_CONFIG_DNS_DSO_ENABLE
ot::Dns::TestDso();
printf("All tests passed\n");
#else
printf("DSO feature is not enabled\n");
#endif
return 0;
}