blob: 1743e82e9736a89a219c33fd873aa5e593b39f3d [file] [log] [blame]
/*
* Copyright (c) 2017, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* The file implements the CoAP service.
*/
#include "coap_libcoap.hpp"
#include <stdio.h>
#include <syslog.h>
#include "common/types.hpp"
#include "common/code_utils.hpp"
namespace ot {
namespace BorderRouter {
namespace Coap {
static void CoapAddressInit(coap_address_t &aAddress, const uint8_t *aIp6, uint16_t aPort)
{
coap_address_init(&aAddress);
aAddress.addr.sin6.sin6_family = AF_INET6;
aAddress.addr.sin6.sin6_port = htons(aPort);
VerifyOrExit(aIp6 != NULL);
memcpy(&aAddress.addr.sin6.sin6_addr, aIp6, sizeof(aAddress.addr.sin6.sin6_addr));
exit:
return;
}
MessageLibcoap::MessageLibcoap(Message::Type aType, Message::Code aCode, uint16_t aMessageId, const uint8_t *aToken,
uint8_t aTokenLength)
{
mPdu = coap_new_pdu();
mPdu->hdr->id = aMessageId;
SetType(aType);
SetCode(aCode);
SetToken(aToken, aTokenLength);
}
const uint8_t *MessageLibcoap::GetToken(uint8_t &aLength) const
{
aLength = mPdu->hdr->token_length;
return mPdu->hdr->token;
}
Message::Code MessageLibcoap::GetCode(void) const
{
return static_cast<Message::Code>(mPdu->hdr->code);
}
void MessageLibcoap::SetCode(Code aCode)
{
mPdu->hdr->code = aCode;
}
Message::Type MessageLibcoap::GetType(void) const
{
return static_cast<Message::Type>(mPdu->hdr->type);
}
void MessageLibcoap::SetType(Type aType)
{
mPdu->hdr->type = aType;
}
void MessageLibcoap::SetToken(const uint8_t *aToken, uint8_t aLength)
{
mPdu->hdr->token_length = aLength;
if (aLength)
{
coap_add_token(mPdu, aLength, aToken);
}
}
void MessageLibcoap::Free(void)
{
if (mPdu)
{
coap_delete_pdu(mPdu);
mPdu = NULL;
}
}
void MessageLibcoap::SetPath(const char *aPath)
{
uint8_t options[kMaxOptionSize];
const uint8_t *option = options;
size_t len = sizeof(options);
int res;
res = coap_split_path(reinterpret_cast<const unsigned char *>(aPath), strlen(aPath), options, &len);
while (res--)
{
coap_add_option(mPdu, COAP_OPTION_URI_PATH,
COAP_OPT_LENGTH(option),
COAP_OPT_VALUE(option));
option += COAP_OPT_SIZE(option);
}
}
void MessageLibcoap::SetPayload(const uint8_t *aPayload, uint16_t aLength)
{
coap_add_data(mPdu, aLength, aPayload);
}
const uint8_t *MessageLibcoap::GetPayload(uint16_t &aLength) const
{
uint8_t *payload = NULL;
size_t length = 0;
coap_get_data(mPdu, &length, &payload);
aLength = length;
return payload;
}
Message *AgentLibcoap::NewMessage(Message::Type aType, Message::Code aCode, const uint8_t *aToken, uint8_t aTokenLength)
{
uint16_t messageId = coap_new_message_id(&mCoap);
return new MessageLibcoap(aType, aCode, messageId, aToken, aTokenLength);
}
void AgentLibcoap::FreeMessage(Message *aMessage)
{
delete static_cast<Message *>(aMessage);
}
void AgentLibcoap::Send(Message &aMessage, const uint8_t *aIp6, uint16_t aPort, ResponseHandler aHandler)
{
MessageLibcoap &message = static_cast<MessageLibcoap &>(aMessage);
coap_tid_t tid = COAP_INVALID_TID;
coap_pdu_t *pdu = message.GetPdu();
coap_address_t remote;
CoapAddressInit(remote, aIp6, aPort);
if (pdu->hdr->type == COAP_MESSAGE_CON)
{
tid = coap_send_confirmed(&mCoap, mCoap.endpoint, &remote, pdu);
// There is no official way to provide handler for each message,
// we have to embed the handler to its payload.
if (pdu->length + sizeof(&aHandler) < pdu->max_size)
{
memcpy(pdu->data + pdu->length, &aHandler, sizeof(aHandler));
}
else
{
syslog(LOG_ERR, "no memory for callback");
}
}
else
{
tid = coap_send(&mCoap, mCoap.endpoint, &remote, pdu);
}
if (tid == COAP_INVALID_TID || pdu->hdr->type != COAP_MESSAGE_CON)
{
message.Free();
}
}
void AgentLibcoap::HandleRequest(coap_context_t *aCoap,
struct coap_resource_t *aResource,
const coap_endpoint_t *aEndPoint,
coap_address_t *aAddress,
coap_pdu_t *aRequest,
str *aToken,
coap_pdu_t *aResponse)
{
AgentLibcoap *agent = (AgentLibcoap *)CONTAINING_RECORD(aCoap, AgentLibcoap, mCoap);
for (const Resource *resource = agent->mResources; resource && resource->mPath; resource++)
{
if (!strncmp(reinterpret_cast<const char *>(aResource->uri.s), resource->mPath, aResource->uri.length))
{
MessageLibcoap req(aRequest);
MessageLibcoap res(aResponse);
res.SetCode(Message::kCoapEmpty);
resource->mHandler(*resource, req, res, reinterpret_cast<const uint8_t *>(&aAddress->addr.sin6.sin6_addr),
ntohs(aAddress->addr.sin6.sin6_port), agent->mContext);
}
}
(void)aEndPoint;
(void)aToken;
}
void AgentLibcoap::Input(const void *aBuffer, uint16_t aLength, const uint8_t *aIp6, uint16_t aPort)
{
mPacket.length = aLength;
mPacket.interface = mCoap.endpoint;
mPacket.dst = mCoap.endpoint->addr;
CoapAddressInit(mPacket.src, aIp6, aPort);
memcpy(mPacket.payload, aBuffer, aLength);
coap_handle_message(&mCoap, &mPacket);
}
void AgentLibcoap::HandleResponse(coap_context_t *aCoap,
const coap_endpoint_t *aLocalInterface,
const coap_address_t *aRemote,
coap_pdu_t *aSent,
coap_pdu_t *aReceived,
const coap_tid_t aId)
{
AgentLibcoap *agent = (AgentLibcoap *)CONTAINING_RECORD(aCoap, AgentLibcoap, mCoap);
VerifyOrExit(aSent != NULL, syslog(LOG_ERR, "request not found!"));
{
ResponseHandler handler;
memcpy(&handler, aSent->data + aSent->length, sizeof(handler));
MessageLibcoap message(aReceived);
handler(message, agent->mContext);
}
exit:
(void)aLocalInterface;
(void)aRemote;
(void)aId;
return;
}
AgentLibcoap::AgentLibcoap(NetworkSender aNetworkSender, const Resource *aResources, void *aContext)
{
mContext = aContext;
mResources = aResources;
mNetworkSender = aNetworkSender;
coap_clock_init();
time_t clock_offset = time(NULL);
memset(&mCoap, 0, sizeof(mCoap));
prng_init(reinterpret_cast<unsigned long>(aNetworkSender) ^ clock_offset);
prng(reinterpret_cast<unsigned char *>(&mCoap.message_id), sizeof(unsigned short));
coap_address_t addr;
coap_address_init(&addr);
addr.addr.sin6.sin6_family = AF_INET6;
mCoap.endpoint = coap_new_endpoint(&addr, COAP_ENDPOINT_NOSEC);
mCoap.network_send = AgentLibcoap::NetworkSend;
for (const Resource *resource = aResources; resource && resource->mPath; resource++)
{
coap_resource_t *r = coap_resource_init(reinterpret_cast<const unsigned char *>(resource->mPath),
strlen(resource->mPath), 0);
coap_register_handler(r, COAP_REQUEST_POST, AgentLibcoap::HandleRequest);
coap_add_resource(&mCoap, r);
}
coap_register_response_handler(&mCoap, AgentLibcoap::HandleResponse);
}
ssize_t AgentLibcoap::NetworkSend(coap_context_t *aCoap,
const coap_endpoint_t *aLocalInterface,
const coap_address_t *aDestination,
unsigned char *aBuffer, size_t aLength)
{
(void)aLocalInterface;
AgentLibcoap *agent = static_cast<AgentLibcoap *>(CONTAINING_RECORD(aCoap, AgentLibcoap, mCoap));
return agent->mNetworkSender(aBuffer, aLength,
reinterpret_cast<const uint8_t *>(&aDestination->addr.sin6.sin6_addr),
ntohs(aDestination->addr.sin6.sin6_port), agent->mContext);
}
Agent *Agent::Create(NetworkSender aNetworkSender, const Resource *aResources, void *aContext)
{
return new AgentLibcoap(aNetworkSender, aResources, aContext);
}
void Agent::Destroy(Agent *aAgent)
{
delete static_cast<AgentLibcoap *>(aAgent);
}
} // namespace Coap
} // namespace BorderRouter
} // namespace ot