text: /*
   *
   *    Copyright (c) 2013-2017 Nest Labs, Inc.
   *    All rights reserved.
   *
   *    Licensed under the Apache License, Version 2.0 (the "License");
   *    you may not use this file except in compliance with the License.
   *    You may obtain a copy of the License at
   *
   *        http://www.apache.org/licenses/LICENSE-2.0
   *
   *    Unless required by applicable law or agreed to in writing, software
   *    distributed under the License is distributed on an "AS IS" BASIS,
   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *    See the License for the specific language governing permissions and
   *    limitations under the License.
   */
  
  /**
   *    @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 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.
          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
  