text:
/**
 *    @file
 *      This file implements the WeaveConnection class. It manages
 *      communication over a TCP connection between Weave nodes.
 *
 */

#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif

#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif

#include <inttypes.h>

#include <Weave/Core/WeaveCore.h>
#include <Weave/Support/logging/WeaveLogging.h>
#include <Weave/Support/CodeUtils.h>

#include <SystemLayer/SystemStats.h>
#include <SystemLayer/SystemTimer.h>
#include <InetLayer/InetLayer.h>

#if WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
#define CONN_REPAIR_APP_CALLBACK_DELAY_MSECS                    (1)
#endif // WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED

namespace nl {
namespace Weave {

/**
 * Reserve a reference to the WeaveConnection object.
 *
 * The AddRef() method increments the reference count associated with the WeaveConnection object.  For every
 * call to AddRef(), the application is responsible for making a corresponding call to either Release(), Close()
 * or Abort().
 */
void WeaveConnection::AddRef()
{
    VerifyOrDie(mRefCount < UINT8_MAX);
    ++mRefCount;
}

/**
 *  Decrement the reference count on the WeaveConnection object.
 *
 *  The Release() method decrements the reference count associated with the WeaveConnection object.  If
 *  this results in the reference count reaching zero, the connection is closed and the connection object
 *  is freed.  When this happens, the application must have no further interactions with the object.
 */
void WeaveConnection::Release()
{
    // If the only reference that will remain after this call is the one that was automatically added
    // when the connection started, close the connection.
    if (mRefCount == 2 && State != kState_ReadyToConnect && State != kState_Closed)
    {
        // Suppress callbacks.
        OnConnectionComplete = NULL;
        OnConnectionClosed = NULL;

        // Perform a graceful close.
        DoClose(WEAVE_NO_ERROR, kDoCloseFlag_SuppressCallback);
    }

    VerifyOrDie(mRefCount != 0);
    mRefCount--;
}

WEAVE_ERROR WeaveConnection::StartConnectToAddressLiteral(const char *peerAddr, size_t peerAddrLen)
{
    WEAVE_ERROR err = WEAVE_ERROR_UNSUPPORTED_WEAVE_FEATURE;

#if WEAVE_CONFIG_RESOLVE_IPADDR_LITERAL
    // Literal address conversion is only supported on BSD sockets network targets
    if (IPAddress::FromString(peerAddr, peerAddrLen, PeerAddr))
        err = StartConnect();
#endif // WEAVE_CONFIG_RESOLVE_IPADDR_LITERAL

    return err;
}

/**
 *  Connect to a Weave node using a fabric IP address derived from the specified node identifier.
 *
 *  @param[in]    peerNodeId    The node identifier of the peer.
 *
 *  @retval #WEAVE_NO_ERROR                      on successful initiation of the connection to the peer.
 *  @retval #WEAVE_ERROR_INCORRECT_STATE         if the WeaveConnection state is incorrect.
 *
 *  @retval #WEAVE_ERROR_UNSUPPORTED_AUTH_MODE   if the requested authentication mode is not supported.
 *
 *  @retval #WEAVE_ERROR_INVALID_ADDRESS         if the destination address cannot be deduced from the
 *                                               node id.
 *
 *  @retval other Inet layer errors generated by the TCPEndPoint connect operations.
 *
 */
WEAVE_ERROR WeaveConnection::Connect(uint64_t peerNodeId)
{
    return Connect(peerNodeId, IPAddress::Any);
}

/**
 *  Connect to a Weave node using a node identifier and/or an IP address.
 *
 *  @note
 *    If peerAddr is IPAddress::Any, the node's fabric IP address will be derived from the peerNodeId field.
 *    If peerNodeId is kNodeIdNotSpecified (0), the node's identifier will be derived (if possible)
 *    from the peerAddr. The connection will be made to the specified port, or the default
 *    #WEAVE_PORT if peerPort is 0.
 *
 *  @param[in]    peerNodeId    The node identifier of the peer, kNodeIdNotSpecified or 0 if
 *                              not known.
 *
 *  @param[in]    peerAddr      The IP address of the peer, IPAddress::Any if not known.
 *
 *  @param[in]    peerPort      The optional port of the peer, default to #WEAVE_PORT.
 *
 *  @retval #WEAVE_NO_ERROR                      on successful initiation of the connection to the peer.
 *  @retval #WEAVE_ERROR_INCORRECT_STATE         if the WeaveConnection state is incorrect.
 *
 *  @retval #WEAVE_ERROR_UNSUPPORTED_AUTH_MODE   if the requested authentication mode is not supported.
 *
 *  @retval #WEAVE_ERROR_INVALID_ADDRESS         if the destination address cannot be deduced from
 *                                               the node id.
 *
 *  @retval other Inet layer errors generated by the TCPEndPoint connect operations.
 *
 */
WEAVE_ERROR WeaveConnection::Connect(uint64_t peerNodeId, const IPAddress &peerAddr, uint16_t peerPort)
{
    return Connect(peerNodeId, kWeaveAuthMode_Unauthenticated, peerAddr, peerPort, INET_NULL_INTERFACEID);
}

/**
 *  Connect to a Weave node using a node identifier and/or an IP address on a specific interface.
 *
 *  @note
 *    If peerAddr is IPAddress::Any, the node's fabric IP address will be derived from the peerNodeId field.
 *    If peerNodeId is kNodeIdNotSpecified (0), the node's identifier will be derived (if possible)
 *    from the peerAddr.  The connection will be made to the specified port, or the default
 *    #WEAVE_PORT if peerPort is 0.
 *
 *  @param[in]    peerNodeId    The node identifier of the peer, kNodeIdNotSpecified or 0 if
 *                              not known.
 *
 *  @param[in]    authMode      The desired authenticate mode for the peer. Only CASE, PASE and Unauthenticated
 *                              modes are supported.
 *
 *  @param[in]    peerAddr      The IP address of the peer, IPAddress::Any if not known.
 *
 *  @param[in]    peerPort      The optional port of the peer, default to #WEAVE_PORT.
 *
 *  @param[in]    intf          The optional interface to use to connect to the peer node,
 *                              default to #INET_NULL_INTERFACEID.
 *
 *  @retval #WEAVE_NO_ERROR                      on successful initiation of the connection to the peer.
 *  @retval #WEAVE_ERROR_INCORRECT_STATE         if the WeaveConnection state is incorrect.
 *
 *  @retval #WEAVE_ERROR_UNSUPPORTED_AUTH_MODE   if the requested authentication mode is not supported.
 *
 *  @retval #WEAVE_ERROR_INVALID_ADDRESS         if the destination address cannot be deduced from the
 *                                               node identifier.
 *
 *  @retval other Inet layer errors generated by the TCPEndPoint connect operations.
 *
 */
WEAVE_ERROR WeaveConnection::Connect(uint64_t peerNodeId, WeaveAuthMode authMode, const IPAddress &peerAddr, uint16_t peerPort, InterfaceId intf)
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;

    VerifyOrExit(State == kState_ReadyToConnect, err = WEAVE_ERROR_INCORRECT_STATE);

    VerifyOrExit(authMode == kWeaveAuthMode_Unauthenticated || IsCASEAuthMode(authMode) || IsPASEAuthMode(authMode), err = WEAVE_ERROR_INVALID_ARGUMENT);

    // Can't request authentication if the security manager is not initialized.
    VerifyOrExit(authMode == kWeaveAuthMode_Unauthenticated || MessageLayer->SecurityMgr != NULL, err = WEAVE_ERROR_UNSUPPORTED_AUTH_MODE);

    // Application has made this an IP-based WeaveConnection.
    NetworkType = kNetworkType_IP;

    PeerNodeId = peerNodeId;
    PeerAddr = peerAddr;
    PeerPort = (peerPort != 0) ? peerPort : WEAVE_PORT;
    mTargetInterface = intf;
    AuthMode = authMode;

    // Bump the reference count when we start the connection process. The corresponding decrement happens when the
    // DoClose() method is called. This ensures the object stays live while there's the possibility of a callback
    // happening from an underlying layer (e.g. TCPEndPoint or DNS resolution).
    mRefCount++;

    WeaveLogProgress(MessageLayer, "Con start %04X %016llX %04X", LogId(), peerNodeId, authMode);

    err = StartConnect();
    SuccessOrExit(err);

exit:
    return err;
}

/**
 *  Connect to a Weave node using a node identifier and/or a string host name.  If supplied, peerAddr can
 *  be any of:
 *  @code
 *    <host-name>
 *    <host-name>:<port>
 *    <ip-4-addr>
 *    <ip-4-addr>:<port>
 *    <ip-6-addr>
 *    [<ip-6-addr>]:<port>
 *  @endcode
 *  @note
 *    If `<port>` is not supplied, defaultPort is used.  If defaultPort is 0, the default #WEAVE_PORT is used.
 *    If peerAddr is null, the node's fabric IP address will be derived from the peerNodeId field.  If peerNodeId
 *    is kNodeIdNotSpecified (0), the node's identifier will be derived (if possible) from the peer's IP address.
 *
 *  @param[in]    peerNodeId    The node identifier of the peer, kNodeIdNotSpecified or 0 if
 *                              not known.
 *
 *  @param[in]    authMode      The desired authenticate mode for the peer. Only CASE, PASE and Unauthenticated
 *                              modes are supported.
 *
 *  @param[in]    peerAddr      The address or hostname of the peer as a NULL-terminated C string.
 *
 *  @param[in]    defaultPort   The optional default port to use for the connection if not supplied in the peerAddr
 *                              string.
 *
 *  @retval #WEAVE_NO_ERROR                      on successful initiation of the connection to the peer.
 *  @retval #WEAVE_ERROR_INCORRECT_STATE         if the WeaveConnection state is incorrect.
 *
 *  @retval #WEAVE_ERROR_UNSUPPORTED_AUTH_MODE   if the requested authentication mode is not supported.
 *
 *  @retval #WEAVE_ERROR_INVALID_ADDRESS         if the destination address cannot be deduced from the node id.
 *
 *  @retval other Inet layer errors generated by the TCPEndPoint connect operations.
 *
 */
WEAVE_ERROR WeaveConnection::Connect(uint64_t peerNodeId, WeaveAuthMode authMode, const char *peerAddr, uint16_t defaultPort)
{
    return Connect(peerNodeId, authMode, peerAddr, (peerAddr != NULL) ? strlen(peerAddr) : 0, defaultPort);
}

/**
 *  Connect to a Weave node using a node identifier and/or a string peer address.
 *
 *  If supplied, peerAddr can be any of:
 *  @code
 *    <host-name>
 *    <host-name>:<port>
 *    <ip-4-addr>
 *    <ip-4-addr>:<port>
 *    <ip-6-addr>
 *    [<ip-6-addr>]:<port>
 *  @endcode
 *  @note
 *    If `<port>` is not supplied, defaultPort is used.  If defaultPort is 0, the default Weave port is used.
 *    If peerAddr is null, the node's fabric IP address will be derived from the peerNodeId field.  If peerNodeId
 *    is kNodeIdNotSpecified (0), the node's identifier will be derived (if possible) from the peer's IP address.
 *
 *  @param[in]    peerNodeId    The node identifier of the peer, kNodeIdNotSpecified or 0 if
 *                              not known.
 *
 *  @param[in]    authMode      The desired authenticate mode for the peer. Only CASE, PASE and Unauthenticated
 *                              modes are supported.
 *
 *  @param[in]    peerAddr      The address or hostname of the peer as a non-NULL-terminated C string.
 *
 *  @param[in]    peerAddrLen   The length of the peerAddr string.
 *
 *  @param[in]    defaultPort   The optional default port to use for the connection if not supplied in the peerAddr
 *                              string.
 *
 *  @retval #WEAVE_NO_ERROR                      on successful initiation of the connection to the peer.
 *  @retval #WEAVE_ERROR_INCORRECT_STATE         if the WeaveConnection state is incorrect.
 *
 *  @retval #WEAVE_ERROR_UNSUPPORTED_AUTH_MODE   if the requested authentication mode is not supported.
 *
 *  @retval #WEAVE_ERROR_INVALID_ADDRESS         if the destination address cannot be deduced from the node id.
 *
 *  @retval other Inet layer errors generated by the TCPEndPoint connect operations.
 *
 */
WEAVE_ERROR WeaveConnection::Connect(uint64_t peerNodeId, WeaveAuthMode authMode, const char *peerAddr,
                                     uint16_t peerAddrLen, uint16_t defaultPort)
{
#if WEAVE_CONFIG_ENABLE_DNS_RESOLVER
    const uint8_t dnsOptions = ::nl::Inet::kDNSOption_Default;
#else
    const uint8_t dnsOptions = 0;
#endif
    return Connect(peerNodeId, authMode, peerAddr, peerAddrLen, dnsOptions, defaultPort);
}

/**
 *  Connect to a Weave node using a node identifier and/or a string peer address.
 *
 *  If supplied, peerAddr can be any of:
 *  @code
 *    <host-name>
 *    <host-name>:<port>
 *    <ip-4-addr>
 *    <ip-4-addr>:<port>
 *    <ip-6-addr>
 *    [<ip-6-addr>]:<port>
 *  @endcode
 *  @note
 *    If `<port>` is not supplied, defaultPort is used.  If defaultPort is 0, the default Weave port is used.
 *    If peerAddr is null, the node's fabric IP address will be derived from the peerNodeId field.  If peerNodeId
 *    is kNodeIdNotSpecified (0), the node's identifier will be derived (if possible) from the peer's IP address.
 *
 *  @param[in]    peerNodeId    The node identifier of the peer, kNodeIdNotSpecified or 0 if
 *                              not known.
 *
 *  @param[in]    authMode      The desired authenticate mode for the peer. Only CASE, PASE and Unauthenticated
 *                              modes are supported.
 *
 *  @param[in]    peerAddr      The address or hostname of the peer as a non-NULL-terminated C string.
 *
 *  @param[in]    peerAddrLen   The length of the peerAddr string.
 *
 *  @param[in]    dnsOptions    An integer value controlling how host name resolution is performed.
 *                              Value should be the OR of one or more values from from the
 *                              #::nl::Inet::DNSOptions enumeration.
 *
 *  @param[in]    defaultPort   The optional default port to use for the connection if not supplied in the peerAddr
 *                              string.
 *
 *  @retval #WEAVE_NO_ERROR                      on successful initiation of the connection to the peer.
 *  @retval #WEAVE_ERROR_INCORRECT_STATE         if the WeaveConnection state is incorrect.
 *
 *  @retval #WEAVE_ERROR_UNSUPPORTED_AUTH_MODE   if the requested authentication mode is not supported.
 *
 *  @retval #WEAVE_ERROR_INVALID_ADDRESS         if the destination address cannot be deduced from the node id.
 *
 *  @retval other Inet layer errors generated by the TCPEndPoint connect operations.
 *
 */
WEAVE_ERROR WeaveConnection::Connect(uint64_t peerNodeId, WeaveAuthMode authMode, const char *peerAddr,
                                     uint16_t peerAddrLen, uint8_t dnsOptions, uint16_t defaultPort)
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;
    const char *hostName;
    uint16_t hostNameLen;
    const char *intfName;
    uint16_t intfNameLen;

    VerifyOrExit(State == kState_ReadyToConnect, err = WEAVE_ERROR_INCORRECT_STATE);

    VerifyOrExit(authMode == kWeaveAuthMode_Unauthenticated || IsCASEAuthMode(authMode) || IsPASEAuthMode(authMode), err = WEAVE_ERROR_INVALID_ARGUMENT);

    // Can't request authentication if the security manager is not initialized.
    VerifyOrExit(authMode == kWeaveAuthMode_Unauthenticated || MessageLayer->SecurityMgr != NULL, err = WEAVE_ERROR_UNSUPPORTED_AUTH_MODE);

    // If no peer address given, connect using the node id.
    if (peerAddr == NULL || peerAddrLen == 0)
    {
        err = Connect(peerNodeId, authMode, IPAddress::Any, defaultPort);
        ExitNow();
    }

    // Application has made this an IP-based WeaveConnection.
    NetworkType = kNetworkType_IP;

    // Parse the address into a host, port and interface name.
    err = nl::Inet::ParseHostPortAndInterface(peerAddr, peerAddrLen, hostName, hostNameLen, PeerPort, intfName, intfNameLen);
    SuccessOrExit(err);
    if (PeerPort == 0)
        PeerPort = (defaultPort != 0) ? defaultPort : WEAVE_PORT;

    // If an interface name has been specified, attempt to convert it to a network interface id.
    if (intfName != NULL)
    {
        err = InterfaceNameToId(intfName, mTargetInterface);
        SuccessOrExit(err);
    }

    // Clear the list of resolved peer addresses in preparation for resolving the host name.
    memset(mPeerAddrs, 0, sizeof(mPeerAddrs));

    PeerNodeId = peerNodeId;
    AuthMode = authMode;

    // Bump the reference count when we start the connection process. The corresponding decrement happens when the
    // DoClose() method is called. This ensures the object stays live while there's the possibility of a callback
    // happening from an underlying layer (e.g. TCPEndPoint or DNS resolution).
    mRefCount++;

    WeaveLogProgress(MessageLayer, "Con start %04X %016llX %04X", LogId(), peerNodeId, authMode);

#if WEAVE_CONFIG_ENABLE_DNS_RESOLVER
    // Initiate the host name resolution.
    State = kState_Resolving;
    err = MessageLayer->Inet->ResolveHostAddress(hostName, hostNameLen, dnsOptions, WEAVE_CONFIG_CONNECT_IP_ADDRS, mPeerAddrs, HandleResolveComplete, this);
#else // !WEAVE_CONFIG_ENABLE_DNS_RESOLVER
    err = StartConnectToAddressLiteral(hostName, hostNameLen);
#endif // !WEAVE_CONFIG_ENABLE_DNS_RESOLVER

exit:
    return err;
}

/**
 *  Connect to a Weave node using a node identifier and/or a list of hostname and ports.
 *
 *  @param[in]    peerNodeId    The node identifier of the peer.
 *
 *  @param[in]    authMode      The authentication mode used for the connection.
 *
 *  @param[in]    hostPortList  The list of hostnames and ports.
 *
 *  @param[in]    intf          The optional interface to use to connect to the peer node,
 *                              default to #INET_NULL_INTERFACEID.
 *
 *  @retval #WEAVE_NO_ERROR                      on successful initiation of the connection to the peer.
 *  @retval #WEAVE_ERROR_INCORRECT_STATE         if the WeaveConnection state is incorrect.
 *
 *  @retval #WEAVE_ERROR_UNSUPPORTED_AUTH_MODE   if the requested authentication mode is not supported.
 *
 *  @retval #WEAVE_ERROR_INVALID_ADDRESS         if the destination address cannot be deduced from the node id.
 *
 *  @retval other Inet layer errors generated by the TCPEndPoint connect operations.
 *
 */
WEAVE_ERROR WeaveConnection::Connect(uint64_t peerNodeId, WeaveAuthMode authMode, HostPortList hostPortList,
                                     InterfaceId intf)
{
#if WEAVE_CONFIG_ENABLE_DNS_RESOLVER
    const uint8_t dnsOptions = ::nl::Inet::kDNSOption_Default;
#else
    const uint8_t dnsOptions = 0;
#endif
    return Connect(peerNodeId, authMode, hostPortList, dnsOptions, intf);
}

/**
 *  Connect to a Weave node using a node identifier and/or a list of hostname and ports.
 *
 *  @param[in]    peerNodeId    The node identifier of the peer.
 *
 *  @param[in]    authMode      The authentication mode used for the connection.
 *
 *  @param[in]    hostPortList  The list of hostnames and ports.
 *
 *  @param[in]    dnsOptions    An integer value controlling how host name resolution is performed.
 *                              Value should be the OR of one or more values from from the
 *                              #::nl::Inet::DNSOptions enumeration.
 *
 *  @param[in]    intf          The optional interface to use to connect to the peer node,
 *                              default to #INET_NULL_INTERFACEID.
 *
 *  @retval #WEAVE_NO_ERROR                      on successful initiation of the connection to the peer.
 *  @retval #WEAVE_ERROR_INCORRECT_STATE         if the WeaveConnection state is incorrect.
 *
 *  @retval #WEAVE_ERROR_UNSUPPORTED_AUTH_MODE   if the requested authentication mode is not supported.
 *
 *  @retval #WEAVE_ERROR_INVALID_ADDRESS         if the destination address cannot be deduced from the node id.
 *
 *  @retval other Inet layer errors generated by the TCPEndPoint connect operations.
 *
 */
WEAVE_ERROR WeaveConnection::Connect(uint64_t peerNodeId, WeaveAuthMode authMode, HostPortList hostPortList,
                                     uint8_t dnsOptions, InterfaceId intf)
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;

    VerifyOrExit(State == kState_ReadyToConnect, err = WEAVE_ERROR_INCORRECT_STATE);

    VerifyOrExit(authMode == kWeaveAuthMode_Unauthenticated || IsCASEAuthMode(authMode) || IsPASEAuthMode(authMode), err = WEAVE_ERROR_INVALID_ARGUMENT);

    // Can't request authentication if the security manager is not initialized.
    VerifyOrExit(authMode == kWeaveAuthMode_Unauthenticated || MessageLayer->SecurityMgr != NULL, err = WEAVE_ERROR_UNSUPPORTED_AUTH_MODE);

    // Application has made this an IP-based WeaveConnection.
    NetworkType = kNetworkType_IP;

    // Clear the list of resolved peer addresses in preparation for resolving the first host name.
    memset(mPeerAddrs, 0, sizeof(mPeerAddrs));

    PeerNodeId = peerNodeId;
    AuthMode = authMode;
    mPeerHostPortList = hostPortList;
    mTargetInterface = intf;
#if WEAVE_CONFIG_ENABLE_DNS_RESOLVER
    mDNSOptions = dnsOptions;
#endif

    // Bump the reference count when we start the connection process. The corresponding decrement happens when the
    // DoClose() method is called. This ensures the object stays live while there's the possibility of a callback
    // happening from an underlying layer (e.g. TCPEndPoint or DNS resolution).
    mRefCount++;

    WeaveLogProgress(MessageLayer, "Con start %04X %016llX %04X", LogId(), peerNodeId, authMode);

    err = TryNextPeerAddress(WEAVE_ERROR_HOST_PORT_LIST_EMPTY);
    SuccessOrExit(err);

exit:
    return err;
}

/**
 * @brief   Set timeout for Connect to succeed or return an error.
 *
 * @param[in]   connTimeoutMsecs
 *
 * @note
 *  Setting a value of zero means use system defaults.
 */
void WeaveConnection::SetConnectTimeout(const uint32_t connTimeoutMsecs)
{
    mConnectTimeout = connTimeoutMsecs;
}

/**
 *  Get the IP address information of the peer.
 *
 *  @param[out]  addrInfo  A reference to the IPPacketInfo object.
 *
 *  @retval  #WEAVE_NO_ERROR On success.
 *  @retval  #WEAVE_ERROR_NOT_IMPLEMENTED    If this function is invoked for an incompatible endpoint
 *                                           (e.g., BLE) in the network layer.
 *
 */
WEAVE_ERROR WeaveConnection::GetPeerAddressInfo(IPPacketInfo& addrInfo)
{
#if CONFIG_NETWORK_LAYER_BLE
    if (mBleEndPoint != NULL)
        return WEAVE_ERROR_NOT_IMPLEMENTED;
#endif

    addrInfo.Clear();
    addrInfo.SrcAddress = PeerAddr;
    addrInfo.SrcPort = PeerPort;

    return WEAVE_NO_ERROR;
}

/**
 * Constructs a string describing the peer node associated with the connection.
 *
 * @param[in] buf                       A pointer to a buffer into which the string should be written. The supplied
 *                                      buffer should be at least as big as kGetPeerDescription_MaxLength. If a
 *                                      smaller buffer is given the string will be truncated to fit. The output
 *                                      will include a NUL termination character in all cases.
 * @param[in] bufSize                   The size of the buffer pointed at by buf.
 */
void WeaveConnection::GetPeerDescription(char * buf, size_t bufSize) const
{
    WeaveMessageLayer::GetPeerDescription(buf, bufSize, PeerNodeId,
            (NetworkType == kNetworkType_IP) ? &PeerAddr : NULL,
            (NetworkType == kNetworkType_IP) ? PeerPort : 0,
            INET_NULL_INTERFACEID,
            this);
}

#if WEAVE_CONFIG_ENABLE_TUNNELING
/**
 *  Send a tunneled Weave message over an established connection.
 *
 *  @param[in] msgInfo        A pointer to a WeaveMessageInfo object.
 *
 *  @param[in] msgBuf           A pointer to the PacketBuffer object holding the packet to send.
 *
 *  @retval    #WEAVE_NO_ERROR                             on successfully sending the message down to
 *                                                         the network layer.
 *  @retval    #WEAVE_ERROR_INCORRECT_STATE                if the WeaveConnection object is not
 *                                                         in the correct state for sending messages.
 *  @retval    #WEAVE_ERROR_INVALID_DESTINATION_NODE_ID    if the destination node identifier is unspecified.
 *  @retval    #WEAVE_ERROR_SENDING_BLOCKED                if the message is too long to be sent.
 *  @retval    other Inet layer errors related to the specific endpoint send operations.
 *
 */
WEAVE_ERROR WeaveConnection::SendTunneledMessage (WeaveMessageInfo *msgInfo, PacketBuffer *msgBuf)
{

    //Set message version to V2
    msgInfo->MessageVersion = kWeaveMessageVersion_V2;

    //Set the tunneling flag
    msgInfo->Flags |= kWeaveMessageFlag_TunneledData;

    return SendMessage(msgInfo, msgBuf);
}
#endif // WEAVE_CONFIG_ENABLE_TUNNELING

/**
 *  Send a Weave message over an established connection.
 *
 *  @param[in] msgInfo        A pointer to a WeaveMessageInfo object.
 *
 *  @param[in] msgBuf           A pointer to the PacketBuffer object holding the packet to send.
 *
 *  @retval    #WEAVE_NO_ERROR                             on successfully sending the message down to
 *                                                         the network layer.
 *  @retval    #WEAVE_ERROR_INCORRECT_STATE                if the WeaveConnection object is not
 *                                                         in the correct state for sending messages.
 *  @retval    #WEAVE_ERROR_INVALID_DESTINATION_NODE_ID    if the destination node identifier is unspecified.
 *  @retval    #WEAVE_ERROR_SENDING_BLOCKED                if the message is too long to be sent.
 *  @retval    other Inet layer errors related to the specific endpoint send operations.
 *
 */
WEAVE_ERROR WeaveConnection::SendMessage (WeaveMessageInfo *msgInfo, PacketBuffer *msgBuf)
{
    WEAVE_ERROR res = WEAVE_NO_ERROR;

    VerifyOrDie(mRefCount != 0);

    if (!StateAllowsSend())
    {
        ExitNow(res = WEAVE_ERROR_INCORRECT_STATE);
    }

    // TODO: implement back pressure

    // Set the source node identifier in the message header.
    msgInfo->SourceNodeId = MessageLayer->FabricState->LocalNodeId;

    // If necessary, arrange for the source node identifier field to be encoded in the message.
    if (SendSourceNodeId)
    {
        msgInfo->Flags |= kWeaveMessageFlag_SourceNodeId;
    }

    // If the caller didn't supply a destination node id, use the peer's node id.
    if ((msgInfo->Flags & kWeaveMessageFlag_DestNodeId) == 0 && msgInfo->DestNodeId == kNodeIdNotSpecified)
    {
        msgInfo->DestNodeId = PeerNodeId;
    }

    // Fail immediately if the caller didn't provide a valid destination node id.
    if (msgInfo->DestNodeId == kNodeIdNotSpecified)
    {
        ExitNow(res = WEAVE_ERROR_INVALID_DESTINATION_NODE_ID);
    }

    // If we determined when the connection was established that the destination node identifier should always be sent,
    // OR if the destination node identifier for the current message does not match the peer node id, then force the
    // destination node identifier field to be encoded in the message.
    if (SendDestNodeId || msgInfo->DestNodeId != PeerNodeId)
    {
        msgInfo->Flags |= kWeaveMessageFlag_DestNodeId;
    }

    // Encode the Weave message. NOTE that this results in the payload buffer containing the entire encoded message.
    // If the encoded message would have exceeded the sent limit, return WEAVE_ERROR_SENDING_BLOCKED to the caller.
    res = MessageLayer->EncodeMessageWithLength(msgInfo, msgBuf, this, UINT16_MAX);
    if (res != WEAVE_NO_ERROR)
    {
        ExitNow(res = (res == WEAVE_ERROR_MESSAGE_TOO_LONG) ? WEAVE_ERROR_SENDING_BLOCKED : res);
    }

    // Copy msg to a right-sized buffer if applicable
    msgBuf = PacketBuffer::RightSize(msgBuf);

#if WEAVE_CONFIG_ENABLE_MESSAGE_CAPTURE
    if (GetFlag(msgInfo->Flags, kWeaveMessageFlag_CaptureTxMessage))
    {
        WeaveLogDetail(MessageLayer, "Message tagged for capture over Con  %04X ", LogId());
        if (MessageLayer->OnMessageCapture)
        {
            MessageLayer->OnMessageCapture(msgBuf->Start(), msgBuf->DataLength(), msgInfo);
        }
        ExitNow(res);
    }
#endif // WEAVE_CONFIG_ENABLE_MESSAGE_CAPTURE

#if CONFIG_NETWORK_LAYER_BLE
    if (mBleEndPoint != NULL)
    {
        res = mBleEndPoint->Send(msgBuf);
    }
    else
#endif
    {
        res = mTcpEndPoint->Send(msgBuf, true);
    }
    msgBuf = NULL;

exit:
    if (msgBuf != NULL)
    {
        PacketBuffer::Free(msgBuf);
        msgBuf = NULL;
    }

    return res;
}

/**
 *  Performs a graceful TCP send-shutdown, ensuring all outgoing data has been sent and received
 *  by the peer's TCP stack. With most (but not all) TCP implementations, receipt of a send-shutdown
 *  will cause the remote host to shutdown their side of the connection as well, resulting in a
 *  connection close. A subsequent call to Close() would terminate the WeaveConnection.
 *
 *  @note
 *    This method is not available for BLE WeaveConnections. There is no Weave over BLE protocol
 *    mechanism to perform a send-shutdown. Application protocols/profiles must perform their own
 *    acknowledgements of message receipt e.g. via Status Report messages before they close a
 *    WeaveConnection. This is good practice with TCP-based WeaveConnections as well.
 *
 *  @sa Close() and Abort().
 *
 *  @retval  #WEAVE_NO_ERROR                     on successful shutdown of the tcp connection.
 *  @retval  #WEAVE_ERROR_NOT_IMPLEMENTED        if this function is invoked for an incompatible
 *                                               endpoint (e.g., BLE) in the network layer.
 *  @retval  #WEAVE_ERROR_INCORRECT_STATE        if the WeaveConnection object is not
 *                                               in the correct state before initiating
 *                                               a shutdown.
 *  @retval  other Inet layer errors related to the specific endpoint shutdown operations.
 *
 */
WEAVE_ERROR WeaveConnection::Shutdown()
{
#if CONFIG_NETWORK_LAYER_BLE
    if (mBleEndPoint != NULL)
        return WEAVE_ERROR_NOT_IMPLEMENTED;
#endif

    VerifyOrDie(mRefCount != 0);

    if (State != kState_Connected && State != kState_SendShutdown && State != kState_Closed)
        return WEAVE_ERROR_INCORRECT_STATE;

    if (State == kState_Connected)
    {
        State = kState_SendShutdown;
        mTcpEndPoint->Shutdown();
    }

    return WEAVE_NO_ERROR;
}

/**
 *  Performs a non-blocking graceful close of the TCP- or BLE-based WeaveConnection, delivering any
 *  remaining outgoing data before politely informing the remote host that we have reset the connection.
 *
 *  This method provides no strong guarantee that any outgoing message not acknowledged at the application
 *  protocol level has been received by the remote peer. For both TCP and BLE, the underlying protocol stack
 *  will make a best-effort to deliver any pending outgoing data before resetting the connection. For TCP,
 *  Shutdown() should be used before Close() if a transport-layer message receipt confirmation is required
 *  before closing the connection.  BLE connections provide no Shutdown() equivalent.
 *
 *  For BLE-based connections, Close() closes the WeaveConnection and returns immediately, but may cause the
 *  underlying BLEEndPoint object to linger until all outgoing data has been sent. This is a side effect of
 *  the Weave over BLE transport protocol implementation existing within the Weave BleLayer.
 *
 *  Once Close() has been called, the WeaveConnection object can no longer be used for further communication.
 *
 *  Calling Close() decrements the reference count associated with the WeaveConnection object, whether or not
 *  the connection is open/active at the time the method is called.  If this results in the reference count
 *  reaching zero, the resources associated with the connection object are freed.  When this happens, the
 *  application must have no further interactions with the object.
 *
 *  @sa Shutdown(), Abort(), AddRef() and Release().
 *
 *  @return #WEAVE_NO_ERROR unconditionally.
 *
 */
WEAVE_ERROR WeaveConnection::Close()
{
    return Close(false);
}

/**
 *  Performs a non-blocking graceful close of the TCP- or BLE-based WeaveConnection, delivering any
 *  remaining outgoing data before politely informing the remote host that we have reset the connection.
 *
 *  This method provides no strong guarantee that any outgoing message not acknowledged at the application
 *  protocol level has been received by the remote peer. For both TCP and BLE, the underlying protocol stack
 *  will make a best-effort to deliver any pending outgoing data before resetting the connection. For TCP,
 *  Shutdown() should be used before Close() if a transport-layer message receipt confirmation is required
 *  before closing the connection.  BLE connections provide no Shutdown() equivalent.
 *
 *  For BLE-based connections, Close() closes the WeaveConnection and returns immediately, but may cause the
 *  underlying BLEEndPoint object to linger until all outgoing data has been sent. This is a side effect of
 *  the Weave over BLE transport protocol implementation existing within the Weave BleLayer.
 *
 *  Once Close() has been called, the WeaveConnection object can no longer be used for further communication.
 *
 *  Calling Close() decrements the reference count associated with the WeaveConnection object, whether or not
 *  the connection is open/active at the time the method is called.  If this results in the reference count
 *  reaching zero, the resources associated with the connection object are freed.  When this happens, the
 *  application must have no further interactions with the object.
 *
 *  @param[in]    suppressCloseLog    true if logs need to be suppressed, false otherwise.
 *
 *  @sa Shutdown(), Abort(), AddRef() and Release().
 *
 *  @return #WEAVE_NO_ERROR unconditionally.
 *
 */
WEAVE_ERROR WeaveConnection::Close(bool suppressCloseLog)
{
    // Suppress callbacks.
    OnConnectionComplete = NULL;
    OnConnectionClosed = NULL;

    // Perform a graceful close.
    DoClose(WEAVE_NO_ERROR, kDoCloseFlag_SuppressCallback | (suppressCloseLog ? kDoCloseFlag_SuppressLogging : 0));

    // Decrement the ref count that was added when the WeaveConnection object
    // was allocated (in WeaveMessageLayer::NewConnection()).
    VerifyOrDie(mRefCount != 0);
    mRefCount--;

    return WEAVE_NO_ERROR;
}

/**
 *  Performs an un-graceful close of the TCP- or BLE-based WeaveConnection, discarding any data that might be
 *  in flight to or from the peer.
 *
 *  A call to Abort() immediately terminates the underlying connection.  After this point, the WeaveConnection
 *  object can no longer be used for further communication.
 *
 *  Calling Abort() decrements the reference count associated with the WeaveConnection object, whether or not
 *  the connection is open/active at the time the method is called.  If this results in the reference count
 *  reaching zero, the resources associated with the connection object are freed.  When this happens, the
 *  application must have no further interactions with the object.
 *
 *  @sa Shutdown(), Abort(), AddRef() and Release().
 *
 */
void WeaveConnection::Abort()
{
    // Suppress callbacks.
    OnConnectionComplete = NULL;
    OnConnectionClosed = NULL;

    // Perform an abortive close of the connection.
    DoClose(WEAVE_ERROR_CONNECTION_ABORTED, kDoCloseFlag_SuppressCallback);

    // Decrement the ref count that was added when the WeaveConnection object
    // was allocated (in WeaveMessageLayer::NewConnection()).
    VerifyOrDie(mRefCount != 0);
    mRefCount--;
}

/**
 *   Enable receiving over this WeaveConnection. This method is used by the
 *   application to indicate to the WeaveConnection object that it is ready
 *   to receive any data that arrives over the TCP connection.
 *
 *   @sa DisableReceive()
 *
 */
void WeaveConnection::EnableReceive()
{
    // If receiving was disabled, enable it and process any pending received data.
    if (!ReceiveEnabled)
    {
        ReceiveEnabled = true;
        //ProcessReceivedData();
        // TODO: fix this
    }
}

/**
 *   Disable receiving over this WeaveConnection. This method is used by the application
 *   to indicate that it is not ready to receive any arrived data over the TCP connection.
 *   In order to re-enable receiving, the application needs to call EnableReceive() to
 *   allow WeaveConnection to hand over any received data by invoking the approrpiate
 *   callbacks.
 *
 *   @sa EnableReceive()
 *
 */
void WeaveConnection::DisableReceive()
{
    ReceiveEnabled = false;
    // TODO: make this disable received in the TcpEndPoint.
}

/**
 *  WeaveConnection::EnableKeepAlive
 *
 *  @brief
 *    Enable TCP keepalive probes on the underlying TCP connection.
 *
 *  @param[in]  interval
 *    The interval (in seconds) between keepalive probes.  This value also controls
 *    the time between last data packet sent and the transmission of the first keepalive
 *    probe.
 *
 *  @param[in]  timeoutCount
 *    The maximum number of unacknowledged probes before the connection will be deemed
 *    to have failed.
 *
 *  @note
 *     -This method can only be called on a Weave connection backed by a TCP connection.
 *
 *     -This method can only be called when the connection is in a state that allows sending.
 *
 *     -This method can be called multiple times to adjust the keepalive interval or timeout
 *      count.
 *
 *  @retval  #WEAVE_NO_ERROR                     on successful enabling of TCP keepalive probes
 *                                               on the connection.
 *  @retval  #WEAVE_ERROR_NOT_IMPLEMENTED        if this function is invoked for an incompatible
 *                                               endpoint (e.g., BLE) in the network layer.
 *  @retval  #WEAVE_ERROR_INCORRECT_STATE        if the WeaveConnection object is not
 *                                               in the correct state for sending messages.
 *  @retval  other Inet layer errors related to the TCP endpoint enable keepalive operation.
 *
 */

WEAVE_ERROR WeaveConnection::EnableKeepAlive(uint16_t interval, uint16_t timeoutCount)
{
#if CONFIG_NETWORK_LAYER_BLE
    if (mBleEndPoint != NULL)
        return WEAVE_ERROR_NOT_IMPLEMENTED;
#endif

    if (!StateAllowsSend())
        return WEAVE_ERROR_INCORRECT_STATE;

    return mTcpEndPoint->EnableKeepAlive(interval, timeoutCount);
}

/**
 *  WeaveConnection::DisableKeepAlive
 *
 *  @brief
 *    Disable TCP keepalive probes on the underlying TCP connection.
 *
 *  @note
 *    This method can only be called on a Weave connection backed by a TCP connection.
 *
 *    This method can only be called when the connection is in a state that allows sending.
 *
 *    This method does nothing if keepalives have not been enabled on the connection.
 *
 *  @retval  #WEAVE_NO_ERROR                     on successful disabling of TCP keepalive probes
 *                                               on the connection.
 *  @retval  #WEAVE_ERROR_NOT_IMPLEMENTED        if this function is invoked for an incompatible
 *                                               endpoint (e.g., BLE) in the network layer.
 *  @retval  #WEAVE_ERROR_INCORRECT_STATE        if the WeaveConnection object is not
 *                                               in the correct state for sending messages.
 *  @retval  other Inet layer errors related to the TCP endpoint enable keepalive operation.
 *
 */

WEAVE_ERROR WeaveConnection::DisableKeepAlive()
{
#if CONFIG_NETWORK_LAYER_BLE
    if (mBleEndPoint != NULL)
        return WEAVE_ERROR_NOT_IMPLEMENTED;
#endif

    if (!StateAllowsSend())
        return WEAVE_ERROR_INCORRECT_STATE;

    return mTcpEndPoint->DisableKeepAlive();
}

/**
 *  WeaveConnection::SetUserTimeout
 *
 *  @brief
 *    Set the TCP user timeout socket option.
 *
 *  @param[in]   userTimeoutMillis
 *    Tcp user timeout value in milliseconds.
 *
 *  @details
 *    When the value is greater than 0, it specifies the maximum amount of
 *    time in milliseconds that transmitted data may remain
 *    unacknowledged before TCP will forcibly close the
 *    corresponding connection. If the option value is specified as 0,
 *    TCP will use the system default.
 *    See RFC 5482, for further details.
 *
 *  @note
 *     -This method can only be called on a Weave connection backed by a TCP connection.
 *
 *     -This method can only be called when the connection is in a state that allows sending.
 *
 *     -This method can be called multiple times to adjust the TCP user timeout.
 *
 *  @retval  #WEAVE_NO_ERROR                     on successful setting of TCP user timeout
 *                                               on the connection.
 *  @retval  #WEAVE_ERROR_NOT_IMPLEMENTED        if this function is invoked for an incompatible
 *                                               endpoint (e.g., BLE) in the network layer.
 *  @retval  #WEAVE_ERROR_INCORRECT_STATE        if the WeaveConnection object is not
 *                                               in the correct state for sending messages.
 *  @retval  other Inet layer errors related to the TCP endpoint setting of the TCP user timeout.
 *
 */
WEAVE_ERROR WeaveConnection::SetUserTimeout(uint32_t userTimeoutMillis)
{
#if CONFIG_NETWORK_LAYER_BLE
    if (mBleEndPoint != NULL)
        return WEAVE_ERROR_NOT_IMPLEMENTED;
#endif

    if (!StateAllowsSend())
        return WEAVE_ERROR_INCORRECT_STATE;

    return mTcpEndPoint->SetUserTimeout(userTimeoutMillis);
}

/**
 *  WeaveConnection::ResetUserTimeout
 *
 *  @brief
 *    Reset the TCP user timeout socket option to the system default.
 *
 *  @note
 *     -This method can only be called on a Weave connection backed by a TCP connection.
 *
 *     -This method can only be called when the connection is in a state that allows sending.
 *
 *     -This method does nothing if user timeout has not been set on the connection.
 *
 *  @retval  #WEAVE_NO_ERROR                     on successful resetting of TCP user timeout
 *                                               on the connection.
 *  @retval  #WEAVE_ERROR_NOT_IMPLEMENTED        if this function is invoked for an incompatible
 *                                               endpoint (e.g., BLE) in the network layer.
 *  @retval  #WEAVE_ERROR_INCORRECT_STATE        if the WeaveConnection object is not
 *                                               in the correct state for sending messages.
 *  @retval  other Inet layer errors related to the TCP endpoint resetting of the TCP user timeout.
 *
 */
WEAVE_ERROR WeaveConnection::ResetUserTimeout(void)
{
    return SetUserTimeout(0);
}

/**
 *  Set the idle timeout on the underlying network layer connection.
 *
 *  @param[in]    timeoutMS    the timeout in milliseconds.
 *
 *  @retval  #WEAVE_NO_ERROR                     on successful setting of the idle timeout
 *                                               for the connection.
 *  @retval  #WEAVE_ERROR_INCORRECT_STATE        if the WeaveConnection object is not
 *                                               in the correct state for receiving messages.
 *
 */
WEAVE_ERROR WeaveConnection::SetIdleTimeout(uint32_t timeoutMS)
{
    if (!StateAllowsReceive())
        return WEAVE_ERROR_INCORRECT_STATE;

#if CONFIG_NETWORK_LAYER_BLE
    if (mBleEndPoint)
    {
        //TODO COM-320: implement for BLEEndPoint. Use InetTimer until we have separate PlatformLayer.
    }
    else
#endif
    {
        mTcpEndPoint->SetIdleTimeout(timeoutMS);
    }

    return WEAVE_NO_ERROR;
}

void WeaveConnection::DoClose(WEAVE_ERROR err, uint8_t flags)
{
    if (State != kState_Closed)
    {
#if CONFIG_NETWORK_LAYER_BLE
        if (mBleEndPoint != NULL)
        {
            if (err == WEAVE_NO_ERROR)
            {
                mBleEndPoint->Close();
            }
            else
            {
                // Cancel pending BLEEndPoint transmission(s) if WeaveConnection has been aborted.
                mBleEndPoint->Abort();
            }

            // BleEndPoint frees itself once it finishes the close operation. It must stick around to negotiate the
            // close, potentially after it's emptied the remainder of its WoBle transmit buffer.
            mBleEndPoint = NULL;
        }
        else
#endif
        {
            if (mTcpEndPoint != NULL)
            {
                if (err == WEAVE_NO_ERROR)
                    err = mTcpEndPoint->Close();
                if (err != WEAVE_NO_ERROR)
                    mTcpEndPoint->Abort();
                mTcpEndPoint->Free();
                mTcpEndPoint = NULL;
            }

#if WEAVE_CONFIG_ENABLE_DNS_RESOLVER
            // Cancel any outstanding DNS query that may still be active.  (This situation can
            // arise if the application initiates a connection to a peer using a DNS name and
            // then aborts/closes the connection before the DNS lookup completes).
            MessageLayer->Inet->CancelResolveHostAddress(HandleResolveComplete, this);
#endif // WEAVE_CONFIG_ENABLE_DNS_RESOLVER
        }

        uint8_t oldState = State;
        State = kState_Closed;

        if ((flags & kDoCloseFlag_SuppressLogging) == 0)
            WeaveLogProgress(MessageLayer, "Con closed %04X %ld", LogId(), (long)err);

        // If the exchange manager has been initialized, call its callback.
        if (MessageLayer->ExchangeMgr != NULL)
            MessageLayer->ExchangeMgr->HandleConnectionClosed(this, err);

        // Call the Fabric state object to alert it of the connection close.
        if (MessageLayer->FabricState != NULL)
            MessageLayer->FabricState->HandleConnectionClosed(this);

        // Call the appropriate app callback if allowed.
        if ((flags & kDoCloseFlag_SuppressCallback) == 0)
        {
            if (oldState == kState_Resolving || oldState == kState_Connecting || oldState == kState_EstablishingSession)
            {
                if (OnConnectionComplete != NULL)
                    OnConnectionComplete(this, err);
            }
            else if (OnConnectionClosed != NULL)
                OnConnectionClosed(this, err);
        }

        // Decrement the ref count that was added when the connection started.
        if (oldState != kState_ReadyToConnect && oldState != kState_Closed)
        {
            VerifyOrDie(mRefCount != 0);
            mRefCount--;
        }
    }
}

void WeaveConnection::HandleResolveComplete(void *appState, INET_ERROR dnsRes, uint8_t addrCount, IPAddress *addrArray)
{
    WeaveConnection *con = (WeaveConnection *)appState;

    // It is legal for a DNS entry to exist but contain no A/AAAA records. If this happens, return a reasonable error
    // to the user.
    if (dnsRes == INET_NO_ERROR && addrCount == 0)
        dnsRes = INET_ERROR_HOST_NOT_FOUND;

    WeaveLogProgress(MessageLayer, "Con DNS complete %04X %ld", con->LogId(), (long)dnsRes);

    // Attempt to connect to the first resolved address (if any).
    con->TryNextPeerAddress(dnsRes);
}

WEAVE_ERROR WeaveConnection::TryNextPeerAddress(WEAVE_ERROR lastErr)
{
    WEAVE_ERROR err = lastErr; // If there are no more addresses to try, lastErr will become the error returned to the user.

    // Search the list of peer addresses for one we haven't tried yet...
    for (int i = 0; i < WEAVE_CONFIG_CONNECT_IP_ADDRS; i++)
        if (mPeerAddrs[i] != IPAddress::Any)
        {
            // Select the next address, removing it from the list so it won't get tried again.
            PeerAddr = mPeerAddrs[i];
            mPeerAddrs[i] = IPAddress::Any;

            // Initiate a connection to the new address.
            err = StartConnect();
            ExitNow();
        }

    // If Connect() was called with a host/port list and there are additional entries in the list, then...
    if (!mPeerHostPortList.IsEmpty())
    {
        char hostName[256]; // Per spec, max DNS name length is 253.

        // Pop the next host/port pair from the list.
        err = mPeerHostPortList.Pop(hostName, sizeof(hostName), PeerPort);
        SuccessOrExit(err);

#if WEAVE_CONFIG_ENABLE_DNS_RESOLVER
        // Initiate name resolution for the new host name.
        //
        // NOTE: there was a moment, maybe it's passed now, when the
        // log statement below was very useful since in showed exactly
        // which host we thought we were getting a particular service
        // from. for now i'm leaving it in place, protected by an
        // ifdef 0 at the top of the file.
        //
        WeaveLogProgress(MessageLayer, "Con DNS start %04" PRIX16 " %s %02" PRIX8, LogId(), hostName, mDNSOptions);
        State = kState_Resolving;
        err = MessageLayer->Inet->ResolveHostAddress(hostName, strlen(hostName), mDNSOptions,
                                                     WEAVE_CONFIG_CONNECT_IP_ADDRS,
                                                     mPeerAddrs, HandleResolveComplete, this);
#else // !WEAVE_CONFIG_ENABLE_DNS_RESOLVER
        err = StartConnectToAddressLiteral(hostName, strlen(hostName));
#endif // !WEAVE_CONFIG_ENABLE_DNS_RESOLVER
    }

exit:
    // Enter the closed state if an error occurred.
    if (err != WEAVE_NO_ERROR)
        DoClose(err, 0);

    return err;
}

void WeaveConnection::StartSession()
{
    // If the application requested authentication
    if (AuthMode != kWeaveAuthMode_Unauthenticated)
    {
        // Enter the establishing session state.
        State = kState_EstablishingSession;

        WEAVE_ERROR err;

        // Call the security manager to initiate secure CASE session.
        if (IsCASEAuthMode(AuthMode))
            err = MessageLayer->SecurityMgr->StartCASESession(this, PeerNodeId, PeerAddr, PeerPort,
                                                              AuthMode, NULL, HandleSecureSessionEstablished, HandleSecureSessionError);
        // Call the security manager to initiate secure PASE session.
        else if (IsPASEAuthMode(AuthMode))
            err = MessageLayer->SecurityMgr->StartPASESession(this, AuthMode, NULL,
                                                              HandleSecureSessionEstablished, HandleSecureSessionError);
        else
            err = WEAVE_ERROR_UNSUPPORTED_AUTH_MODE;

        if (err != WEAVE_NO_ERROR)
            DoClose(err, 0);
    }

    // Otherwise...
    else
    {
        // Enter the connected state.
        State = kState_Connected;

        WeaveLogProgress(MessageLayer, "Con complete %04X", LogId());

        // Call the app's completion function.
        if (OnConnectionComplete != NULL)
            OnConnectionComplete(this, WEAVE_NO_ERROR);
    }
}

#if WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
void WeaveConnection::AppCallbackAfterConnectionRepair(System::Layer* aSystemLayer, void* aAppState, System::Error aError)
{
    WeaveConnection* con = static_cast<WeaveConnection*>(aAppState);

    con->mTcpEndPoint->OnConnectComplete(con->mTcpEndPoint, INET_NO_ERROR);
}

/**
 *  Attempt to repair the TCP connection using a given TCP RepairInfo.
 *
 *  @param[in]    peerNodeId      NodeId of the peer with which to repair TCP
 *                                connection.
 *
 *  @param[in]    repairInfo      A reference to the TCP Connection repair info.
 *
 *  @param[in]    intf            InterfaceId over which the connection is
 *                                expected to be made.
 *
 *  @note
 *   The TCP connection restoration can only be attempted from the client side.
 *   Upon failure to restore, the client would fall back to establishing a
 *   negotiated TCP connection.
 *
 */
WEAVE_ERROR WeaveConnection::TryConnectionRepair(uint64_t peerNodeId, const TCPConnRepairInfo &repairInfo, InterfaceId intf)
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;

    // Allocate a new TCP end point.
    err = MessageLayer->Inet->NewTCPEndPoint(&mTcpEndPoint);
    SuccessOrExit(err);

    mTcpEndPoint->AppState = this;
    mTcpEndPoint->OnConnectComplete = HandleConnectComplete;

    mRefCount++;

    State = kState_Connecting;

    WeaveLogProgress(MessageLayer, "Trying to repair connection to node %" PRIx64 "", peerNodeId);

    // Call into TCPEndPoint to repair the TCP connection with the fetched
    // repair info.
    err = mTcpEndPoint->RepairConnection(repairInfo, intf);
    SuccessOrExit(err);

    WeaveLogProgress(MessageLayer, "Connection successfully repaired");

    // Set the connection variables
    PeerAddr = repairInfo.dstIP;
    PeerNodeId = peerNodeId;

    // If the peer address is not a ULA, or if the interface identifier portion of the peer address does not match
    // the peer node id, then force the destination node identifier field to be encoded in all sent messages.
    if (!PeerAddr.IsIPv6ULA() || IPv6InterfaceIdToWeaveNodeId(PeerAddr.InterfaceId()) != PeerNodeId)
    {
        SendDestNodeId = true;
    }

    PeerPort = repairInfo.dstPort;
    IsRepaired = true;

    // After repairing connection, set an immediate timer to call the OnConnectComplete callback.
    // This is necessary to allow the call stack to unwind and also prevent the
    // caller of this function to inadvertently change state variables after the
    // application has been called back.
    MessageLayer->SystemLayer->StartTimer(CONN_REPAIR_APP_CALLBACK_DELAY_MSECS, AppCallbackAfterConnectionRepair, this);

exit:

    if (err != WEAVE_NO_ERROR)
    {
        WeaveLogProgress(MessageLayer, "Failed to repair connection to node %" PRIx64 ", err = %ld" , peerNodeId, (long)err);
        if (mTcpEndPoint != NULL)
        {
            mTcpEndPoint->Abort();
            mTcpEndPoint->Free();
            mTcpEndPoint = NULL;
        }
    }

    return err;
}
#endif // WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED

WEAVE_ERROR WeaveConnection::StartConnect()
{
    WEAVE_ERROR err;

    // TODO: this is wrong. PeerNodeId should only be set once we have a successful connection (including security).

    // Determine the peer address/node identifier based on the information given by the caller.
    err = MessageLayer->SelectDestNodeIdAndAddress(PeerNodeId, PeerAddr);
    if (err != WEAVE_NO_ERROR)
        return err;

    // Allocate a new TCP end point.
    err = MessageLayer->Inet->NewTCPEndPoint(&mTcpEndPoint);
    if (err != WEAVE_NO_ERROR)
        return err;

    // If the peer address is not a ULA, or if the interface identifier portion of the peer address does not match
    // the peer node id, then force the destination node identifier field to be encoded in all sent messages.
    if (!PeerAddr.IsIPv6ULA() || IPv6InterfaceIdToWeaveNodeId(PeerAddr.InterfaceId()) != PeerNodeId)
    {
        SendDestNodeId = true;
    }

#if WEAVE_CONFIG_ENABLE_TARGETED_LISTEN
    // TEMPORARY TESTING CODE: If the destination address is IPv6, and an IPv6 listening address has been specified,
    // bind the end point to the listening address so that packets sent over the connection have the listening
    // address as their source address.  This makes it possible to assign multiple simulated fabric addresses to a
    // single interface (e.g. the loopback interface) and ensure that packets sent from a particular node have the
    // correct source address.
#if INET_CONFIG_ENABLE_IPV4
    if (!PeerAddr.IsIPv4() && MessageLayer->FabricState->ListenIPv6Addr != IPAddress::Any)
#else // !INET_CONFIG_ENABLE_IPV4
    if (MessageLayer->FabricState->ListenIPv6Addr != IPAddress::Any)
#endif // !INET_CONFIG_ENABLE_IPV4
    {
        err = mTcpEndPoint->Bind(kIPAddressType_IPv6, MessageLayer->FabricState->ListenIPv6Addr, 0, true);
        if (err != WEAVE_NO_ERROR)
            return err;
    }
#endif

    State = kState_Connecting;

    mTcpEndPoint->AppState = this;
    mTcpEndPoint->OnConnectComplete = HandleConnectComplete;
    mTcpEndPoint->SetConnectTimeout(mConnectTimeout);

#if WEAVE_PROGRESS_LOGGING
    {
        char ipAddrStr[64];
        PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
        WeaveLogProgress(MessageLayer, "TCP con start %04" PRIX16 " %s %d", LogId(), ipAddrStr, (int)PeerPort);
    }
#endif
    // Initiate the TCP connection.
    return mTcpEndPoint->Connect(PeerAddr, PeerPort, mTargetInterface);
}

void WeaveConnection::HandleConnectComplete(TCPEndPoint *endPoint, INET_ERROR conRes)
{
    WeaveConnection *con = (WeaveConnection *) endPoint->AppState;

    WeaveLogProgress(MessageLayer, "TCP con complete %04X %ld", con->LogId(), (long)conRes);

    // If the connection was successful...
    if (conRes == INET_NO_ERROR)
    {
        INET_ERROR err;
        IPAddress localAddr;
        uint16_t localPort;

        // If the peer node identifier is unknown, attempt to infer it from the address of the peer.
        if (con->PeerNodeId == kNodeIdNotSpecified && con->PeerAddr.IsIPv6ULA())
            con->PeerNodeId = IPv6InterfaceIdToWeaveNodeId(con->PeerAddr.InterfaceId());

        // Get the local address that was used for the connection.
        err = endPoint->GetLocalInfo(&localAddr, &localPort);
        if (err != INET_NO_ERROR)
        {
            con->DoClose(err, 0);

            return;
        }

        // If the local address is not a ULA, or if the interface identifier portion of the local address does not match
        // the local node id, then arrange to encode the source node identifier field in messages sent to the peer.
        //
        // We rely on this behavior in the case of Thread-assisted pairing. In this case, the (non-ULA) link-local
        // address used by the new device while provisionally joined to the assisting device's PAN forces it to
        // encode its source node identifier in all messages sent to the remote commissioning device. If the new device's
        // local address were a ULA in this scenario, it would be unable to receive communications from the remote
        // comissioner. The new device, having a ULA, would not encode its source node identifier in messages sent to the
        // commissioner, and the comissioner, on a different network than the new device, would be unable to see
        // the new device's ULA and use it to infer that device's node id.
        if (!localAddr.IsIPv6ULA() || IPv6InterfaceIdToWeaveNodeId(localAddr.InterfaceId()) != con->MessageLayer->FabricState->LocalNodeId)
        {
            con->SendSourceNodeId = true;
        }

        // Setup various callbacks on the end point.
        endPoint->OnDataReceived = HandleDataReceived;
        endPoint->OnDataSent = NULL; // TODO: should handle flow control
        endPoint->OnConnectionClosed = HandleTcpConnectionClosed;

        // Disable TCP Nagle buffering by setting TCP_NODELAY socket option to true
        err = endPoint->EnableNoDelay();
        if (err != INET_NO_ERROR)
        {
            con->DoClose(err, 0);

            return;
        }

        // Negotiate secure session (or not) based on AuthMode.
        con->StartSession();
    }

    // Otherwise the connection failed...
    else
    {
        // Release the end point object.
        endPoint->Free();
        con->mTcpEndPoint = NULL;

        // Attempt to connect to another address if available.
        con->TryNextPeerAddress(conRes);
    }
}

void WeaveConnection::HandleDataReceived(TCPEndPoint *endPoint, PacketBuffer *data)
{
    WEAVE_ERROR err;
    WeaveConnection *con = (WeaveConnection *) endPoint->AppState;
    WeaveMessageLayer *msgLayer = con->MessageLayer;

    // While in a state that allows receiving, process the received data...
    while (data != NULL &&
           con->StateAllowsReceive() &&
           con->ReceiveEnabled &&
           (con->OnMessageReceived != NULL
#if WEAVE_CONFIG_ENABLE_TUNNELING
            || con->OnTunneledMessageReceived != NULL
#endif
          ))
    {
        IPPacketInfo        packetInfo;
        WeaveMessageInfo    msgInfo;
        uint8_t*            payload;
        uint16_t            payloadLen;
        PacketBuffer*       payloadBuf = NULL;
        uint32_t            frameLen;

        packetInfo.Clear();
        con->GetPeerAddressInfo(packetInfo);

        msgInfo.Clear();
        msgInfo.InPacketInfo = &packetInfo;
        msgInfo.InCon = con;

        // Attempt to parse an message from the head of the received queue.
        err = msgLayer->DecodeMessageWithLength(data, con->PeerNodeId, con, &msgInfo, &payload, &payloadLen, &frameLen);

        // If the initial buffer in the receive queue is not big enough to hold the entirety of
        // the incoming message...
        if (err == WEAVE_ERROR_MESSAGE_TOO_LONG)
        {
            // The Weave message decoding logic expects message data to be in contiguous memory.
            // Therefore, if the packet buffer containing the initial portion of the message is
            // not big enough to hold the entirety of the message, the data must be moved into
            // a new buffer that is big enough.
            //
            // This situation can arise, for example, when when a TCP segment arrives containing
            // part of a Weave message and the underlying network interface chooses to place the
            // packet into a buffer that is smaller than the Weave message.
            //
            // Note that the logic here implies that when a system runs low on buffers, message
            // reception can fail for lack of an appropriately sized buffer, resulting in the TCP
            // connection being aborted.  The only way to avoid this is for the underlying network
            // interface to always place packets into buffers that are big enough to hold the
            // maximum size Weave message. If such a buffer is not available when a packet comes
            // in, the network interface can simply discard the packet, resulting in the peer
            // retransmitting it and the system recovering gracefully once the buffer pressure
            // subsides.

            // Attempt to allocate a buffer big enough to hold the entire message.  Fail with
            // WEAVE_ERROR_MESSAGE_TOO_LONG if no such buffer is available.
            PacketBuffer * newBuf = PacketBuffer::NewWithAvailableSize(0, frameLen);
            if (newBuf == NULL)
            {
                break;
            }

            // Prepend the new buffer to the receive queue and copy the received message data into
            // the new buffer, discarding the original buffer(s).
            newBuf->AddToEnd(data);
            data = newBuf;
            data->CompactHead();

            // Try again to decode the message.
            continue;
        }

        // If the initial buffer in the receive queue contains only part of the next message...
        if (err == WEAVE_ERROR_MESSAGE_INCOMPLETE)
        {
            // If there are more buffers in the queue, move as much data as possible into the head buffer
            // and try again.
            if (data->Next() != NULL)
            {
                data->CompactHead();
                continue;
            }

            // Otherwise, we must wait for more data from the peer...

            // Open the receive window just enough to allow the remainder of the message to be received.
            // This is necessary in the case where the message size exceeds the TCP window size to ensure
            // the peer has enough window to send us the entire message.
            uint16_t neededLen = frameLen - data->DataLength();
            err = endPoint->AckReceive(neededLen);
            if (err == WEAVE_NO_ERROR)
                break;
        }

        // If we successfully parsed a message, open the TCP receive window by the size of the message.
        if (err == WEAVE_NO_ERROR)
            err = endPoint->AckReceive(frameLen);

        // Verify that destination node identifier refers to the local node.
        if (err == WEAVE_NO_ERROR)
        {
            if (msgInfo.DestNodeId != msgLayer->FabricState->LocalNodeId && msgInfo.DestNodeId != kAnyNodeId)
                err = WEAVE_ERROR_INVALID_DESTINATION_NODE_ID;
        }

        if (err == WEAVE_NO_ERROR)
        {
            // If there's no more data in the current buffer beyond the message that was just parsed,
            // then avoid a copy by giving the buffer to the application layer.
            if (data->DataLength() == 0)
            {
                // Detach the buffer from the data queue.
                payloadBuf = data;
                data = data->DetachTail();

                // Adjust the buffer to point at the payload of the message.
                payloadBuf->SetStart(payload);
                payloadBuf->SetDataLength(payloadLen);
            }

            // Otherwise we need to keep the buffer so we can parse the remaining data, so copy the
            // payload data into a new buffer and arrange to pass the new buffer to the application.
            else
            {
                payloadBuf = PacketBuffer::New(0);
                if (payloadBuf != NULL)
                {
                    memcpy(payloadBuf->Start(), payload, payloadLen);
                    payloadBuf->SetDataLength(payloadLen);
                }
                else
                    err = WEAVE_ERROR_NO_MEMORY;
            }
        }

        // Disconnect if an error occurred.
        if (err != WEAVE_NO_ERROR)
        {
            WeaveLogError(MessageLayer, "Con rcv data err %04X %ld", con->LogId(), err);

            // Send key error response to the peer if required.
            if (msgLayer->SecurityMgr->IsKeyError(err))
            {
                if (data != NULL)
                {
                    PacketBuffer::Free(data);
                    data = NULL;
                }

                msgLayer->SecurityMgr->SendKeyErrorMsg(&msgInfo, NULL, con, err);
            }

            con->DisconnectOnError(err);
            break;
        }

        //Check if message carries tunneled data and needs to be sent to Tunnel Agent
        if (msgInfo.MessageVersion == kWeaveMessageVersion_V2)
        {
            if (msgInfo.Flags & kWeaveMessageFlag_TunneledData)
            {
#if WEAVE_CONFIG_ENABLE_TUNNELING
                // Dispatch the tunneled data message to the application if it is not a duplicate.
                // Although TCP guarantees in-order, at-most-once delivery in normal conditions,
                // checking for and eliminating duplicate tunneled messages here prevents replay
                // of messages by a malicious man-in-the-middle.
                if (!(msgInfo.Flags & kWeaveMessageFlag_DuplicateMessage))
                {
                    if (con->OnTunneledMessageReceived)
                    {
                        con->OnTunneledMessageReceived(con, &msgInfo, payloadBuf);
                    }
                    else
                    {
                        con->DisconnectOnError(WEAVE_ERROR_NO_MESSAGE_HANDLER);
                        break;
                    }
                }
#endif
            }
            else
            {
                if (con->OnMessageReceived)
                {
                    con->OnMessageReceived(con, &msgInfo, payloadBuf);
                }
                else
                {
                    con->DisconnectOnError(WEAVE_ERROR_NO_MESSAGE_HANDLER);
                    break;
                }

            }
        }
        else if (msgInfo.MessageVersion == kWeaveMessageVersion_V1)
        {
            // Pass the message header and payload to the application.
            // NOTE that when this function returns, the state of the connection may have changed.
            if (con->OnMessageReceived)
            {
                con->OnMessageReceived(con, &msgInfo, payloadBuf);
            }
            else
            {
                con->DisconnectOnError(WEAVE_ERROR_NO_MESSAGE_HANDLER);
                break;
            }
        }
    }

    // If we couldn't process all the received data push it back into the end point. When
    // more data arrives, it will call us back with this data plus the new data. If the underlying
    // TCP connection is closed (which means we're never going to receive any more data), fail with
    // a MESSAGE_INCOMPLETE error.  If the WeaveConnection is closed (e.g. the app called close)
    // then simply discard the received data.
    if (data != NULL)
    {
        if (con->StateAllowsReceive())
        {
            if (endPoint->State == TCPEndPoint::kState_Connected ||
                endPoint->State == TCPEndPoint::kState_SendShutdown)
            {
                endPoint->PutBackReceivedData(data);
                data = NULL;
            }
            else
                con->DoClose(WEAVE_ERROR_MESSAGE_INCOMPLETE, 0);
        }
        if (data != NULL)
            PacketBuffer::Free(data);
    }
}

void WeaveConnection::HandleTcpConnectionClosed(TCPEndPoint *endPoint, INET_ERROR err)
{
    WeaveConnection *con = (WeaveConnection *) endPoint->AppState;
    if (err == INET_NO_ERROR && con->State == kState_EstablishingSession)
        err = WEAVE_ERROR_CONNECTION_CLOSED_UNEXPECTEDLY;
    con->DoClose(err, 0);
}

void WeaveConnection::HandleSecureSessionEstablished(WeaveSecurityManager *sm, WeaveConnection *con, void *reqState,
        uint16_t sessionKeyId, uint64_t peerNodeId, uint8_t encType)
{
    // Establish the peer node identifier and the default key and encryption type to be used to send messages.
    con->PeerNodeId = peerNodeId;
    con->DefaultKeyId = sessionKeyId;
    con->DefaultEncryptionType = encType;

    // Enter the connected state.
    con->State = kState_Connected;

    WeaveLogProgress(MessageLayer, "Con complete %04X", con->LogId());

    // Invoke the app's completion function.
    if (con->OnConnectionComplete != NULL)
        con->OnConnectionComplete(con, WEAVE_NO_ERROR);
}

void WeaveConnection::HandleSecureSessionError(WeaveSecurityManager *sm, WeaveConnection *con, void *reqState,
        WEAVE_ERROR localErr, uint64_t peerNodeId, Profiles::StatusReporting::StatusReport *statusReport)
{
    // Couldn't get a secure session, so fail the connect attempt.
    con->DoClose(localErr, 0);
}

void WeaveConnection::Init(WeaveMessageLayer *msgLayer)
{
    // NOTE: Please keep these in declared order to make it easier keep in sync.
    PeerNodeId = 0;
    PeerAddr = IPAddress::Any;
    MessageLayer = msgLayer;
    AppState = NULL;
    PeerPort = 0;
    DefaultKeyId = WeaveKeyId::kNone;
    AuthMode = kWeaveAuthMode_NotSpecified;
    DefaultEncryptionType = kWeaveEncryptionType_None;
    State = WeaveConnection::kState_ReadyToConnect;
    NetworkType = kNetworkType_Unassigned;
    ReceiveEnabled = true;
    OnConnectionComplete = NULL;
    OnMessageReceived = NULL;
#if WEAVE_CONFIG_ENABLE_TUNNELING
    OnTunneledMessageReceived = NULL;
#endif
    OnConnectionClosed = DefaultConnectionClosedHandler;
    OnReceiveError = NULL;
    memset(&mPeerAddrs, 0, sizeof(mPeerAddrs));
    mTcpEndPoint = NULL;
#if CONFIG_NETWORK_LAYER_BLE
    mBleEndPoint = NULL;
#endif
    mPeerHostPortList.Clear();
    mTargetInterface = INET_NULL_INTERFACEID;
    mRefCount = 1;
    SendSourceNodeId = false;
    SendDestNodeId = false;
    mConnectTimeout = 0;
#if WEAVE_CONFIG_ENABLE_DNS_RESOLVER
    mDNSOptions = 0;
#endif

#if WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
    IsRepaired = false;
#endif // WEAVE_CONFIG_TCP_CONN_REPAIR_SUPPORTED
    mFlags = 0;
}

// Default OnConnectionClosed handler.
//
// This handler is installed in the OnConnectionClosed callback of all new WeaveConnection objects.
// The function automatically releases the application's reference to the object whenever the connection
// closes spontaneously from the network side.  This eliminates the need for boiler-plate code to release
// the object in every Weave application that uses connections, and helps to avoid bugs in applications
// that fail to properly take care of this themselves.
//
// Note that this behavior only applies to closes initiated from the network.  If the application itself
// calls Close() or Abort() on the connection, this function is never called.
//
void WeaveConnection::DefaultConnectionClosedHandler(WeaveConnection *con, WEAVE_ERROR conErr)
{
    // Call Close() on the connection object to release the application's reference to the connection.
    con->Close();
}

void WeaveConnection::MakeConnectedTcp(TCPEndPoint *endPoint, const IPAddress &localAddr, const IPAddress &peerAddr)
{
    mTcpEndPoint = endPoint;
    NetworkType = kNetworkType_IP;
    endPoint->AppState = this;
    endPoint->OnDataReceived = HandleDataReceived;
    endPoint->OnDataSent = NULL; // TODO: should handle flow control
    endPoint->OnConnectionClosed = HandleTcpConnectionClosed;

    PeerNodeId = (peerAddr.IsIPv6ULA()) ? IPv6InterfaceIdToWeaveNodeId(peerAddr.InterfaceId()) : kNodeIdNotSpecified;
    PeerAddr = peerAddr;

    // If the local address is not a ULA, or if the interface identifier portion of the local address does not match
    // the local node id, then arrange to encode the source node identifier field in all messages sent to the peer.
    if (!localAddr.IsIPv6ULA() || IPv6InterfaceIdToWeaveNodeId(localAddr.InterfaceId()) != MessageLayer->FabricState->LocalNodeId)
    {
        SendSourceNodeId = true;
    }

    // Similarly, if we were unable do derive the node identifier of the peer from its address, then arrange for the
    // destination node identifier field to be encoded in all sent messages.
    if (PeerNodeId == kNodeIdNotSpecified)
    {
        SendDestNodeId = true;
    }

    AppState = NULL;
    mRefCount++;

    ReceiveEnabled = true;

    // Disable TCP Nagle buffering by setting TCP_NODELAY socket option to true
    endPoint->EnableNoDelay();

    State = kState_Connected;
}

#if CONFIG_NETWORK_LAYER_BLE

/**
 *  Bind WeaveConnection to BLE connection handle from application.
 *  The application must call this function when and only when it, acting in the role
 *  of a BLE Central, has actively connected to a BLE Peripheral which supports the
 *  Weave BLE service.
 *
 *  If this function and the WeaveConnection's OnConnectionComplete callback both
 *  return without error, Weave has accepted the BLE connection.
 *  However, if Weave accepts a BLE connection, the platform MUST notify Weave via
 *  the appropriate BleLayer callback if the central's subscription is canceled or the
 *  underlying BLE connection is closed, otherwise the associated WeaveConnection
 *  will never be closed or freed.
 *
 *  @note
 *    A downcall to this method may call OnConnectionComplete before it returns.
 *
 *  @param[in]    connObj    The BLE connection object.
 *
 *  @param[in]    authMode   The desired authenticate mode for the peer. Only CASE, PASE and Unauthenticated
 *                           modes are supported.
 *
 *  @param[in]    autoClose  true if automatic closing is enabled upon a long period of
 *                           inactivity, otherwise false.
 *
 *  @retval #WEAVE_NO_ERROR                      on successful initiation of the BLE connection to
 *                                               the peer.
 *  @retval #WEAVE_ERROR_INCORRECT_STATE         if the WeaveConnection state is not ready to connect.
 *
 *  @retval #WEAVE_ERROR_UNSUPPORTED_AUTH_MODE   if the requested authentication mode is not supported or
 *                                               the security manager is not initialized.
 *
 *  @retval other Inet layer errors generated by the BleEndPoint create and connect operations.
 *
 *
 */

WEAVE_ERROR WeaveConnection::ConnectBle(BLE_CONNECTION_OBJECT connObj, WeaveAuthMode authMode, bool autoClose)
{
    WEAVE_ERROR err = WEAVE_NO_ERROR;

    VerifyOrExit(State == kState_ReadyToConnect && MessageLayer->mBle != NULL, err = WEAVE_ERROR_INCORRECT_STATE);

    VerifyOrExit(authMode == kWeaveAuthMode_Unauthenticated || IsCASEAuthMode(authMode) || IsPASEAuthMode(authMode), err = WEAVE_ERROR_INVALID_ARGUMENT);

    // Can't request authentication if the security manager is not initialized.
    VerifyOrExit(authMode == kWeaveAuthMode_Unauthenticated || MessageLayer->SecurityMgr != NULL, err = WEAVE_ERROR_UNSUPPORTED_AUTH_MODE);

    // Application has made us a BLE-based WeaveConnection.
    NetworkType = kNetworkType_BLE;

    AuthMode = authMode;

    // Only BLE centrals can form new GATT connections, so specify this role in our creation of the BLEEndPoint.
    err = MessageLayer->mBle->NewBleEndPoint(&mBleEndPoint, connObj, kBleRole_Central, autoClose);
    SuccessOrExit(err);

    // Enter the connecting state.
    State = kState_Connecting;

    // Set up callbacks we need to negotiate WoBle connection.
    mBleEndPoint->mAppState = this;
    mBleEndPoint->OnConnectComplete = HandleBleConnectComplete;
    mBleEndPoint->OnConnectionClosed = HandleBleConnectionClosed;

    // Must always send SourceNodeId over BLE-based WeaveConnection, as peer cannot infer it from source address.
    SendSourceNodeId = true;

    // Bump the reference count when we start the connection process. The corresponding decrement happens when the
    // DoClose() method is called. This ensures the object stays live while there's the possibility of a callback
    // happening from an underlying layer (e.g. a BLE GATT subscribe request).
    mRefCount++;

    // Initiate Weave over BLE protocol connection.
    err = mBleEndPoint->StartConnect();
    SuccessOrExit(err);

exit:
    return err;
}

void WeaveConnection::HandleBleConnectComplete(BLEEndPoint *endPoint, BLE_ERROR err)
{
    WeaveConnection *con = static_cast<WeaveConnection *>(endPoint->mAppState);

    if (err != BLE_NO_ERROR)
        ExitNow();

    WeaveLogProgress(MessageLayer, "WoBle con complete %04X\n", con->LogId());

    // Set message received callback.
    con->mBleEndPoint->OnMessageReceived = HandleBleMessageReceived;

    // Negotiate secure session (or not) based on AuthMode.
    con->StartSession();

exit:
    if (err != BLE_NO_ERROR)
    {
        WeaveLogError(MessageLayer, "WoBle con failed %04X %d", con->LogId(), err);
        con->DoClose(err, 0);
    }
}

void WeaveConnection::HandleBleMessageReceived(BLEEndPoint *endPoint, PacketBuffer *data)
{
    WeaveConnection *con = (WeaveConnection *) endPoint->mAppState;
    WeaveMessageLayer *msgLayer = con->MessageLayer;

    // Weave's BLE layer reassembles received messages in their entirety before it passes them up the stack,
    // so there's no need for received buffer compaction or reassembly at this layer.

    WeaveMessageInfo msgInfo;
    uint8_t *payload;
    uint16_t payloadLen;
    PacketBuffer *payloadBuf;
    uint32_t frameLen;
    WEAVE_ERROR err;

    // Initialize the message info structure.
    msgInfo.Clear();
    msgInfo.InCon = con;

    // Verify received buffer is not part of a multi-buffer chain.
    VerifyOrExit(data->Next() == NULL, err = BLE_ERROR_BAD_ARGS);

    // Attempt to parse the message.
    err = msgLayer->DecodeMessageWithLength(data, con->PeerNodeId, con, &msgInfo, &payload,
            &payloadLen, &frameLen);
    SuccessOrExit(err);

    // Verify that destination node id refers to the local node.
    VerifyOrExit(((msgInfo.DestNodeId == msgLayer->FabricState->LocalNodeId) ||
            (msgInfo.DestNodeId == kAnyNodeId)),
            err = WEAVE_ERROR_INVALID_DESTINATION_NODE_ID);

    // Verify that the received buffer contained exactly one Weave message.
    VerifyOrExit(data->DataLength() == 0, err = BLE_ERROR_RECEIVED_MESSAGE_TOO_BIG);

    // Assign received data to payload buffer.
    payloadBuf = data;
    data = NULL;

    // Adjust the buffer to point at the payload of the message.
    payloadBuf->SetStart(payload);
    payloadBuf->SetDataLength(payloadLen);

    // Pass the message header and payload to the application.
    // NOTE that when this function returns, the state of the connection may have changed.
    con->OnMessageReceived(con, &msgInfo, payloadBuf);

exit:
    if (err != WEAVE_NO_ERROR)
    {
        WeaveLogError(MessageLayer, "HandleBleMessageReceived failed, err = %d", err);

        if (data != NULL)
            PacketBuffer::Free(data);

        // Send key error response to the peer if required.
        if (msgLayer->SecurityMgr->IsKeyError(err))
            msgLayer->SecurityMgr->SendKeyErrorMsg(&msgInfo, NULL, con, err);
    }
}

void WeaveConnection::HandleBleConnectionClosed(BLEEndPoint *endPoint, BLE_ERROR err)
{
    WeaveConnection *con = (WeaveConnection *) endPoint->mAppState;
    con->DoClose(err, 0);
}

void WeaveConnection::MakeConnectedBle(BLEEndPoint *endPoint)
{
    mBleEndPoint = endPoint;
    NetworkType = kNetworkType_BLE;

    endPoint->mAppState = this;
    endPoint->OnMessageReceived = HandleBleMessageReceived;
    endPoint->OnConnectionClosed = HandleBleConnectionClosed;
    endPoint->mState = BLEEndPoint::kState_Connected;

    // Must always send SourceNodeId over BLE connection since peer cannot infer it from source address.
    SendSourceNodeId = true;

    State = kState_Connected;

    mRefCount++;
}

#endif // CONFIG_NETWORK_LAYER_BLE

void WeaveConnection::DisconnectOnError (WEAVE_ERROR err)
{
    if (OnReceiveError != NULL)
    {
        OnReceiveError(this, err);
    }
    else if (MessageLayer->OnReceiveError != NULL)
    {
        IPPacketInfo addrInfo;
        GetPeerAddressInfo(addrInfo);
        MessageLayer->OnReceiveError(MessageLayer, err, &addrInfo);
    }

    DoClose(err, 0);

}

} // namespace nl
} // namespace Weave


text:
An abbreviation of a word follows the form `<first letter><number><last letter>`. Below are some examples of word abbreviations:

```
a) it                      --> it    (no abbreviation)

     1
b) d|o|g                   --> d1g

              1    1  1
     1---5----0----5--8
c) i|nternationalizatio|n  --> i18n

              1
     1---5----0
d) l|ocalizatio|n          --> l10n
```

Assume you have a dictionary and given a word, find whether its abbreviation is unique in the dictionary. A word's abbreviation is unique if no other word from the dictionary has the same abbreviation.

Example:

```
Given dictionary = [ "deer", "door", "cake", "card" ]

isUnique("dear") -> false
isUnique("cart") -> true
isUnique("cane") -> false
isUnique("make") -> true
```

redpajama_set_name: RedPajamaGithub


text:
import { ComponentFactoryResolver, NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { ColorServiceModule, SparkModule, TabsetModule } from '@ux-aspects/ux-aspects';
import { DocumentationComponentsModule } from '../../../../components/components.module';
import { DocumentationCategoryComponent } from '../../../../components/documentation-category/documentation-category.component';
import { DocumentationPage, ResolverService } from '../../../../services/resolver/resolver.service';
import { ChartsSparkChartsComponent } from './spark-charts/spark-charts.component';

const SECTIONS = [
    ChartsSparkChartsComponent
];

const ROUTES = [
    {
        path: '**',
        component: DocumentationCategoryComponent,
        data: {
            category: ResolverService.resolveCategoryData(DocumentationPage.Charts, 'Spark Charts')
        }
    }
];

@NgModule({
    imports: [
        ColorServiceModule,
        DocumentationComponentsModule,
        RouterModule.forChild(ROUTES),
        SparkModule,
        TabsetModule,
    ],
    exports: SECTIONS,
    declarations: SECTIONS,
})
export class ChartsSparkChartsModule {

    constructor(componentFactoryResolver: ComponentFactoryResolver, resolverService: ResolverService) {
        resolverService.registerResolver(componentFactoryResolver, SECTIONS);
    }
}


text:
using log4net;
using NSubstitute;
using NUnit.Framework;
using StormMultiLang.Logging;
using StormMultiLang.Write;

namespace StormMultiLangTests.Logging
{
    [TestFixture]
    public class GivenASetupBoltLogging
    {
        [Test]
        public void ShouldRegisterAppenderAndLogToWriter()
        {
            var writer = Substitute.For<IBoltWriter>();

            SetupLog4NetBoltLogging.UsingWriter(writer);
            var logger = LogManager.GetLogger("Test Logger");
            logger.Info("hello");

            writer.ReceivedWithAnyArgs().LogInfo("");
        }
    }
}


text:
@interface CameraViewController : UIImagePickerController <UIImagePickerControllerDelegate, UINavigationControllerDelegate, UIActionSheetDelegate> {

}

- (void)setPostURL:(NSURL*)url withPostData:(NSDictionary*)postData succeed:(downloadSucceeded)succeed failed:(downloadFailed)failed;
- (void)setPostURL:(NSURL *)url withCache:(NSURL*)cacheURL withPostData:(NSDictionary*)postData succeed:(downloadSucceeded)succeed failed:(downloadFailed)failed;
- (void)selectType:(UIViewController*)viewController;

@end


text:
var Class = require('../../utils/Class');
var CONST = require('../const');
var File = require('../File');
var FileTypesManager = require('../FileTypesManager');
var GetFastValue = require('../../utils/object/GetFastValue');
var IsPlainObject = require('../../utils/object/IsPlainObject');

/**
 * @classdesc
 * A single Plugin Script File suitable for loading by the Loader.
 *
 * These are created when you use the Phaser.Loader.LoaderPlugin#plugin method and are not typically created directly.
 *
 * For documentation about what all the arguments and configuration options mean please see Phaser.Loader.LoaderPlugin#plugin.
 *
 * @class PluginFile
 * @extends Phaser.Loader.File
 * @memberof Phaser.Loader.FileTypes
 * @constructor
 * @since 3.0.0
 *
 * @param {Phaser.Loader.LoaderPlugin} loader - A reference to the Loader that is responsible for this file.
 * @param {(string|Phaser.Types.Loader.FileTypes.PluginFileConfig)} key - The key to use for this file, or a file configuration object.
 * @param {string} [url] - The absolute or relative URL to load this file from. If undefined or `null` it will be set to `<key>.js`, i.e. if `key` was "alien" then the URL will be "alien.js".
 * @param {boolean} [start=false] - Automatically start the plugin after loading?
 * @param {string} [mapping] - If this plugin is to be injected into the Scene, this is the property key used.
 * @param {Phaser.Types.Loader.XHRSettingsObject} [xhrSettings] - Extra XHR Settings specifically for this file.
 */
var PluginFile = new Class({

    Extends: File,

    initialize:

    function PluginFile (loader, key, url, start, mapping, xhrSettings)
    {
        var extension = 'js';

        if (IsPlainObject(key))
        {
            var config = key;

            key = GetFastValue(config, 'key');
            url = GetFastValue(config, 'url');
            xhrSettings = GetFastValue(config, 'xhrSettings');
            extension = GetFastValue(config, 'extension', extension);
            start = GetFastValue(config, 'start');
            mapping = GetFastValue(config, 'mapping');
        }

        var fileConfig = {
            type: 'plugin',
            cache: false,
            extension: extension,
            responseType: 'text',
            key: key,
            url: url,
            xhrSettings: xhrSettings,
            config: {
                start: start,
                mapping: mapping
            }
        };

        File.call(this, loader, fileConfig);

        // If the url variable refers to a class, add the plugin directly
        if (typeof url === 'function')
        {
            this.data = url;

            this.state = CONST.FILE_POPULATED;
        }
    },

    /**
     * Called automatically by Loader.nextFile.
     * This method controls what extra work this File does with its loaded data.
     *
     * @method Phaser.Loader.FileTypes.PluginFile#onProcess
     * @since 3.7.0
     */
    onProcess: function ()
    {
        var pluginManager = this.loader.systems.plugins;
        var config = this.config;

        var start = GetFastValue(config, 'start', false);
        var mapping = GetFastValue(config, 'mapping', null);

        if (this.state === CONST.FILE_POPULATED)
        {
            pluginManager.install(this.key, this.data, start, mapping);
        }
        else
        {
            //  Plugin added via a js file
            this.state = CONST.FILE_PROCESSING;

            this.data = document.createElement('script');
            this.data.language = 'javascript';
            this.data.type = 'text/javascript';
            this.data.defer = false;
            this.data.text = this.xhrLoader.responseText;

            document.head.appendChild(this.data);

            var plugin = pluginManager.install(this.key, window[this.key], start, mapping);

            if (start || mapping)
            {
                //  Install into the current Scene Systems and Scene
                this.loader.systems[mapping] = plugin;
                this.loader.scene[mapping] = plugin;
            }
        }

        this.onProcessComplete();
    }

});

/**
 * Adds a Plugin Script file, or array of plugin files, to the current load queue.
 *
 * You can call this method from within your Scene's `preload`, along with any other files you wish to load:
 *
 * ```javascript
 * function preload ()
 * {
 *     this.load.plugin('modplayer', 'plugins/ModPlayer.js');
 * }
 * ```
 *
 * The file is **not** loaded right away. It is added to a queue ready to be loaded either when the loader starts,
 * or if it's already running, when the next free load slot becomes available. This happens automatically if you
 * are calling this from within the Scene's `preload` method, or a related callback. Because the file is queued
 * it means you cannot use the file immediately after calling this method, but must wait for the file to complete.
 * The typical flow for a Phaser Scene is that you load assets in the Scene's `preload` method and then when the
 * Scene's `create` method is called you are guaranteed that all of those assets are ready for use and have been
 * loaded.
 *
 * The key must be a unique String and not already in-use by another file in the Loader.
 *
 * Instead of passing arguments you can pass a configuration object, such as:
 *
 * ```javascript
 * this.load.plugin({
 *     key: 'modplayer',
 *     url: 'plugins/ModPlayer.js'
 * });
 * ```
 *
 * See the documentation for `Phaser.Types.Loader.FileTypes.PluginFileConfig` for more details.
 *
 * Once the file has finished loading it will automatically be converted into a script element
 * via `document.createElement('script')`. It will have its language set to JavaScript, `defer` set to
 * false and then the resulting element will be appended to `document.head`. Any code then in the
 * script will be executed. It will then be passed to the Phaser PluginCache.register method.
 *
 * The URL can be relative or absolute. If the URL is relative the `Loader.baseURL` and `Loader.path` values will be prepended to it.
 *
 * If the URL isn't specified the Loader will take the key and create a filename from that. For example if the key is "alien"
 * and no URL is given then the Loader will set the URL to be "alien.js". It will always add `.js` as the extension, although
 * this can be overridden if using an object instead of method arguments. If you do not desire this action then provide a URL.
 *
 * Note: The ability to load this type of file will only be available if the Plugin File type has been built into Phaser.
 * It is available in the default build but can be excluded from custom builds.
 *
 * @method Phaser.Loader.LoaderPlugin#plugin
 * @fires Phaser.Loader.LoaderPlugin#addFileEvent
 * @since 3.0.0
 *
 * @param {(string|Phaser.Types.Loader.FileTypes.PluginFileConfig|Phaser.Types.Loader.FileTypes.PluginFileConfig[])} key - The key to use for this file, or a file configuration object, or array of them.
 * @param {(string|function)} [url] - The absolute or relative URL to load this file from. If undefined or `null` it will be set to `<key>.js`, i.e. if `key` was "alien" then the URL will be "alien.js". Or, a plugin function.
 * @param {boolean} [start] - Automatically start the plugin after loading?
 * @param {string} [mapping] - If this plugin is to be injected into the Scene, this is the property key used.
 * @param {Phaser.Types.Loader.XHRSettingsObject} [xhrSettings] - An XHR Settings configuration object. Used in replacement of the Loaders default XHR Settings.
 *
 * @return {this} The Loader instance.
 */
FileTypesManager.register('plugin', function (key, url, start, mapping, xhrSettings)
{
    if (Array.isArray(key))
    {
        for (var i = 0; i < key.length; i++)
        {
            //  If it's an array it has to be an array of Objects, so we get everything out of the 'key' object
            this.addFile(new PluginFile(this, key[i]));
        }
    }
    else
    {
        this.addFile(new PluginFile(this, key, url, start, mapping, xhrSettings));
    }

    return this;
});

module.exports = PluginFile;


text:
from traitlets import directional_link, HasTraits, TraitError, observe, validate, Int, Instance
from .tree import Tree
from .traittypes import TreeType

class HasTree(HasTraits):
    tree = Instance(klass=Tree, allow_none=True)
    src = TreeType(allow_none=True)

    def __init__(self, src=None):
        self._link = None
        super().__init__()
        if src is not None:
            self.src = src

    @validate('src')
    def validate_src(self, proposal):
        # print(f'>> HasTree {id(self)} validate src\n\t {proposal}')
        src = proposal['value']

        link = None

        if isinstance(src, Tree):
            self.tree = src
        elif isinstance(src, HasTraits) and src.has_trait('tree'):
            link = directional_link((src, 'tree'), (self, 'tree'))
        elif hasattr(src, 'tree'):
            self.tree = getattr(src, 'tree')
        elif src is None:
            self.tree = None
        else:
            raise TraitError('must be a tree or an owner of a tree')

        if self._link is not None:
            self._link.unlink()
        self._link = link
        # print(f'<< HasTree {id(self)} validate src')
        return src

    # @observe('tree')
    # def act(self, change):
    #     print(f'HasTree {id(self)}received a new tree {change["new"]}')


text:
<?php

namespace GeneralBundle\Repository;

/**
 * FormaPagoRepository
 *
 * This class was generated by the Doctrine ORM. Add your own custom
 * repository methods below.
 */
class FormaPagoRepository extends \Doctrine\ORM\EntityRepository
{
}


text:
using System;
using System.Windows.Input;

namespace PokeSharp.Editor.ViewModels
{
    /// <summary>
    /// A command that executes delegates.
    /// </summary>
    public class DelegateCommand : ICommand
    {
        /// <summary>
        /// The action to run when Execute is called.
        /// </summary>
        public Action<object> ExecuteAction { get; set; }

        /// <summary>
        /// The function to run when CanExecute is called.
        /// </summary>
        public Func<object, bool> CanExecuteFunc { get; set; }

        public DelegateCommand(Action<object> execute, Func<object, bool> canexecute)
        {
            ExecuteAction = execute;
            CanExecuteFunc = canexecute;
        }

        /// <summary>
        /// The events that are triggered, when CanExecute changed.
        /// </summary>
        public event EventHandler CanExecuteChanged;

        public void OnCanExecuteChanged()
        {
            var handler = CanExecuteChanged;
            if (handler != null)
                handler(this, null);
        }

        /// <summary>
        /// The CanExecute for the command.
        /// </summary>
        /// <param name="parameter"></param>
        /// <returns></returns>
        public bool CanExecute(object parameter)
        {
            return CanExecuteFunc(parameter);
        }

        /// <summary>
        /// The Execute for the command.
        /// </summary>
        /// <param name="parameter"></param>
        public void Execute(object parameter)
        {
            ExecuteAction(parameter);
        }
    }
}