| /* |
| * |
| * 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 |