mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
453 lines
14 KiB
C
453 lines
14 KiB
C
/*******************************************************************************
|
|
*
|
|
* Copyright (c) 2015 Intel Corporation and others.
|
|
* All rights reserved. This program and the accompanying materials
|
|
* are made available under the terms of the Eclipse Public License v1.0
|
|
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
|
*
|
|
* The Eclipse Public License is available at
|
|
* http://www.eclipse.org/legal/epl-v10.html
|
|
* The Eclipse Distribution License is available at
|
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
|
*
|
|
* Contributors:
|
|
* David Navarro, Intel Corporation - initial API and implementation
|
|
* Christian Renz - Please refer to git log
|
|
* Christian Manal - Ported to RIOT OS
|
|
*
|
|
*******************************************************************************/
|
|
|
|
/*
|
|
* Copyright (C) 2018 Beduino Master Projekt - University of Bremen
|
|
* Copyright (C) 2019 HAW Hamburg
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
* directory for more details.
|
|
*/
|
|
|
|
/**
|
|
* @{
|
|
* @ingroup lwm2m_client
|
|
*
|
|
* @file
|
|
* @brief Connection handle for LwM2M client implementation using Wakaama
|
|
*
|
|
* @author Christian Manal <manal@uni-bremen.de>
|
|
* @author Leandro Lanzieri <leandro.lanzieri@haw-hamburg.de>
|
|
* @}
|
|
*/
|
|
|
|
#include <stddef.h>
|
|
#include "kernel_defines.h"
|
|
#include "net/netif.h"
|
|
|
|
#include "liblwm2m.h"
|
|
#include "lwm2m_client.h"
|
|
#include "lwm2m_client_config.h"
|
|
#include "lwm2m_client_connection.h"
|
|
|
|
#define ENABLE_DEBUG (0)
|
|
#include "debug.h"
|
|
|
|
#define URI_LENGTH 256
|
|
|
|
/**
|
|
* @brief Creates a new connection object based on the security instance
|
|
* represented by @p instance_id.
|
|
*
|
|
* @param[in] instance_id ID number of the instance of security object
|
|
* @param[in, out] client_data LwM2M client data
|
|
*
|
|
* @return Pointer to the new connection
|
|
*/
|
|
static lwm2m_client_connection_t *_connection_create(int instance_id,
|
|
lwm2m_client_data_t *client_data);
|
|
|
|
/**
|
|
* @brief Gets the URI from an @p instance_id of a @p obj security object
|
|
*
|
|
* @param[in] obj security object
|
|
* @param[in] instance_id ID number of the instance of security object
|
|
* @param[out] uri_buffer buffer to place the URI
|
|
* @param[in] buffer_size size of @p uri_buffer
|
|
*
|
|
* @return Pointer to the URI in success
|
|
* @return NULL otherwise
|
|
*/
|
|
static char *_get_uri_from_security_obj(lwm2m_object_t *obj, int instance_id,
|
|
char *uri_buffer, int buffer_size);
|
|
|
|
/**
|
|
* @brief Sends data with a specified connection @p conn
|
|
*
|
|
* @param[in] conn connection to use to send data
|
|
* @param[in] buffer data to send
|
|
* @param[in] buffer_size size of @p buffer
|
|
* @param[in] client_data LwM2M client data
|
|
*
|
|
* @return 0 on success
|
|
* @return -1 otherwise
|
|
*/
|
|
static int _connection_send(lwm2m_client_connection_t *conn, uint8_t *buffer,
|
|
size_t buffer_size,
|
|
lwm2m_client_data_t *client_data);
|
|
|
|
/**
|
|
* @brief Parses the schema of a given URI and sets the default port for the
|
|
* found valid schema
|
|
* @param[in] uri URI string to parse
|
|
* @param[out] port will point to the default port string
|
|
* @param[in] instance_id instance ID of the connection
|
|
*
|
|
* @return pointer to the character after the schema, if found
|
|
* @return NULL if no valid schema found
|
|
*/
|
|
static char *_parse_schema(char *uri, char **port, int instance_id);
|
|
|
|
/**
|
|
* @brief Parses the host and the port part after the schema
|
|
*
|
|
* @param[in, out] host pointer to the beginning of the host
|
|
* @param[out] port pointer to store the position of the port
|
|
* @param[in] default_port default port
|
|
*/
|
|
static void _parse_host_and_port(char **host, char **port, char *default_port);
|
|
|
|
/**
|
|
* @brief Tries to find an interface in the host string. If not, it will check
|
|
* if there only exists one interface, and will use it
|
|
* @param[in] host host string
|
|
*
|
|
* @return pointer to the interface to use on success
|
|
* @return NULL on error
|
|
*/
|
|
static netif_t *_get_interface(char *host);
|
|
|
|
/**
|
|
* @brief Sets a given interface to a given UDP endpoint
|
|
*
|
|
* @param[out] ep UDP endpoint
|
|
* @param[in] netif Network interface to assign
|
|
*/
|
|
static void _set_interface(sock_udp_ep_t *ep, const netif_t *netif);
|
|
|
|
void *lwm2m_connect_server(uint16_t sec_obj_inst_id, void *user_data)
|
|
{
|
|
lwm2m_client_data_t *client_data = (lwm2m_client_data_t *)user_data;
|
|
lwm2m_list_t *instance;
|
|
lwm2m_client_connection_t *new_conn = NULL;
|
|
|
|
/* get the security object instance */
|
|
instance = LWM2M_LIST_FIND(client_data->obj_security->instanceList,
|
|
sec_obj_inst_id);
|
|
if (instance == NULL) {
|
|
DEBUG("[lwm2m_connect_server] Could not find sec object instance\n");
|
|
return NULL;
|
|
}
|
|
|
|
new_conn = _connection_create(instance->id, client_data);
|
|
if (new_conn) {
|
|
DEBUG("[lwm2m_connect_server] Connection created\n");
|
|
/* if the connections list is empty this is the first node, if not
|
|
* attach to the last one */
|
|
if (!client_data->conn_list) {
|
|
client_data->conn_list = new_conn;
|
|
}
|
|
else {
|
|
lwm2m_client_connection_t *last = client_data->conn_list;
|
|
while (last->next != NULL) {
|
|
last = last->next;
|
|
}
|
|
last->next = new_conn;
|
|
}
|
|
}
|
|
|
|
return new_conn;
|
|
}
|
|
|
|
void lwm2m_close_connection(void *sessionH, void *user_data)
|
|
{
|
|
lwm2m_client_connection_t *conn = (lwm2m_client_connection_t *) sessionH;
|
|
lwm2m_client_data_t *client_data = (lwm2m_client_data_t *) user_data;
|
|
|
|
if (conn == client_data->conn_list) {
|
|
client_data->conn_list = conn->next;
|
|
}
|
|
else {
|
|
lwm2m_client_connection_t *prev = client_data->conn_list;
|
|
|
|
while(prev != NULL && prev->next != conn) {
|
|
prev = prev->next;
|
|
}
|
|
if (prev != NULL) {
|
|
prev->next = conn->next;
|
|
lwm2m_free(conn);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool lwm2m_session_is_equal(void *session1, void *session2, void *user_data)
|
|
{
|
|
(void)user_data;
|
|
lwm2m_client_connection_t *conn_1 = (lwm2m_client_connection_t *)session1;
|
|
lwm2m_client_connection_t *conn_2 = (lwm2m_client_connection_t *)session2;
|
|
|
|
return ((conn_1->remote.port == conn_2->remote.port) &&
|
|
ipv6_addr_equal((ipv6_addr_t *)&(conn_1->remote.addr.ipv6),
|
|
(ipv6_addr_t *)&(conn_2->remote.addr.ipv6)));
|
|
}
|
|
|
|
uint8_t lwm2m_buffer_send(void *sessionH, uint8_t *buffer, size_t length,
|
|
void *userdata)
|
|
{
|
|
lwm2m_client_data_t *client_data = (lwm2m_client_data_t *)userdata;
|
|
lwm2m_client_connection_t *conn = (lwm2m_client_connection_t *)sessionH;
|
|
|
|
if (!conn) {
|
|
DEBUG("[lwm2m_buffer_send] Failed to send, missing connection\n");
|
|
return COAP_500_INTERNAL_SERVER_ERROR;
|
|
}
|
|
|
|
if (_connection_send(conn, buffer, length, client_data)) {
|
|
DEBUG("[lwm2m_buffer_send] Failed to send\n");
|
|
return COAP_500_INTERNAL_SERVER_ERROR;
|
|
}
|
|
|
|
return COAP_NO_ERROR;
|
|
}
|
|
|
|
lwm2m_client_connection_t *lwm2m_client_connection_find(
|
|
lwm2m_client_connection_t *conn_list,
|
|
const sock_udp_ep_t *remote)
|
|
{
|
|
lwm2m_client_connection_t *conn = conn_list;
|
|
|
|
char ip[128];
|
|
uint8_t ip_len = 128;
|
|
|
|
ipv6_addr_to_str(ip, (ipv6_addr_t *)&remote->addr.ipv6, ip_len);
|
|
DEBUG("Looking for connection from [%s]:%d\n", ip, remote->port);
|
|
|
|
if (conn_list == NULL) {
|
|
DEBUG("Conn list is null!");
|
|
}
|
|
|
|
while(conn != NULL) {
|
|
ipv6_addr_to_str(ip, (ipv6_addr_t *)&conn->remote.addr.ipv6, ip_len);
|
|
DEBUG("Comparing to [%s]:%d\n", ip, conn->remote.port);
|
|
if ((conn->remote.port == remote->port) &&
|
|
ipv6_addr_equal((ipv6_addr_t *)&(conn->remote.addr.ipv6),
|
|
(ipv6_addr_t *)&(remote->addr.ipv6))) {
|
|
break;
|
|
}
|
|
conn = conn->next;
|
|
}
|
|
return conn;
|
|
}
|
|
|
|
int lwm2m_connection_handle_packet(lwm2m_client_connection_t *conn,
|
|
uint8_t *buffer, size_t num_bytes,
|
|
lwm2m_client_data_t *client_data)
|
|
{
|
|
lwm2m_handle_packet(client_data->lwm2m_ctx, buffer, num_bytes, conn);
|
|
return 0;
|
|
}
|
|
|
|
static int _connection_send(lwm2m_client_connection_t *conn, uint8_t *buffer,
|
|
size_t buffer_size,
|
|
lwm2m_client_data_t *client_data)
|
|
{
|
|
ssize_t sent_bytes = sock_udp_send(&(client_data->sock), buffer,
|
|
buffer_size, &(conn->remote));
|
|
if (sent_bytes <= 0) {
|
|
DEBUG("[_connection_send] Could not send UDP packet: %i\n", (int)sent_bytes);
|
|
return -1;
|
|
}
|
|
conn->last_send = lwm2m_gettime();
|
|
return 0;
|
|
}
|
|
|
|
static char *_parse_schema(char *uri, char **port, int instance_id)
|
|
{
|
|
char *host = NULL;
|
|
if (!uri) {
|
|
DEBUG("[_parse_schema] Could not get URI of instance\n");
|
|
goto out;
|
|
}
|
|
|
|
/* parse the URI in the form "coaps://[host]:port" */
|
|
if (!strncmp(uri, SCHEME_COAPS, sizeof(SCHEME_COAPS) - 1)) {
|
|
host = uri + sizeof(SCHEME_COAPS) - 1;
|
|
}
|
|
else if (!strncmp(uri, SCHEME_COAP, sizeof(SCHEME_COAP) - 1)) {
|
|
host = uri + sizeof(SCHEME_COAP) - 1;
|
|
}
|
|
|
|
*port = (IS_ACTIVE(LWM2M_BOOTSTRAP) && !instance_id) ?
|
|
LWM2M_BSSERVER_PORT : LWM2M_STANDARD_PORT;
|
|
out:
|
|
return host;
|
|
}
|
|
|
|
static void _parse_host_and_port(char **host, char **port, char *default_port)
|
|
{
|
|
char *_port = NULL;
|
|
char *pos = *host;
|
|
|
|
if (pos[0] == '[') {
|
|
(*host)++;
|
|
pos = strrchr(pos, ']');
|
|
}
|
|
|
|
_port = strrchr(pos, ':');
|
|
if (!_port) {
|
|
*pos = '\0';
|
|
DEBUG("[_parse_port] No port specified, using default\n");
|
|
_port = default_port;
|
|
}
|
|
else {
|
|
*(_port - 1) = '\0';
|
|
_port++;
|
|
}
|
|
*port = _port;
|
|
}
|
|
|
|
static void _set_interface(sock_udp_ep_t *ep, const netif_t *netif)
|
|
{
|
|
if (netif == NULL || ep == NULL) {
|
|
return;
|
|
}
|
|
|
|
/* currently there is no way to assign a network interface to a sock
|
|
* endpoint by means of a generic API, so we need to check */
|
|
if (IS_USED(MODULE_GNRC_NETIF)) {
|
|
const gnrc_netif_t *gnrc_netif = (gnrc_netif_t *)netif;
|
|
ep->netif = (uint16_t)gnrc_netif->pid;
|
|
}
|
|
}
|
|
|
|
static netif_t *_get_interface(char *host)
|
|
{
|
|
netif_t *netif = NULL;
|
|
char *iface = ipv6_addr_split_iface(host);
|
|
|
|
if (iface == NULL) {
|
|
/* get the number of net interfaces */
|
|
unsigned netif_numof = 0;
|
|
while ((netif = netif_iter(netif)) != NULL) {
|
|
netif_numof++;
|
|
}
|
|
/* if we only have one interface use that one */
|
|
if (netif_numof == 1) {
|
|
netif = netif_iter(NULL);
|
|
}
|
|
else {
|
|
DEBUG("[_connection_create] No iface for link-local address\n");
|
|
}
|
|
}
|
|
else {
|
|
netif = netif_get_by_name(iface);
|
|
}
|
|
|
|
return netif;
|
|
}
|
|
|
|
static lwm2m_client_connection_t *_connection_create(int instance_id,
|
|
lwm2m_client_data_t *client_data)
|
|
{
|
|
lwm2m_client_connection_t *conn = NULL;
|
|
char *default_port;
|
|
char *host;
|
|
char *port;
|
|
char *uri;
|
|
char uri_buf[URI_LENGTH + 1];
|
|
|
|
memset(uri_buf, 0, sizeof(uri_buf));
|
|
DEBUG("Creating connection\n");
|
|
/* get the server URI from the requested instance */
|
|
uri = _get_uri_from_security_obj(client_data->obj_security, instance_id,
|
|
uri_buf, sizeof(uri_buf) - 1);
|
|
|
|
host = _parse_schema(uri, &default_port, instance_id);
|
|
if (!host) {
|
|
DEBUG("[_connection_create] Could not parse URI schema\n");
|
|
goto free_out;
|
|
}
|
|
|
|
_parse_host_and_port(&host, &port, default_port);
|
|
DEBUG("[_connection_create] Creating connection to Host: %s, Port: %s\n",
|
|
host, port);
|
|
|
|
/* allocate new connection */
|
|
conn = lwm2m_malloc(sizeof(lwm2m_client_connection_t));
|
|
if (!conn) {
|
|
DEBUG("[_connection_create] Could not allocate new connection\n");
|
|
goto out;
|
|
}
|
|
conn->next = client_data->conn_list;
|
|
|
|
/* configure to any IPv6 */
|
|
conn->remote.family = AF_INET6;
|
|
conn->remote.netif = SOCK_ADDR_ANY_NETIF;
|
|
conn->remote.port = atoi(port);
|
|
|
|
if (!ipv6_addr_from_str((ipv6_addr_t *)&conn->remote.addr.ipv6, host)) {
|
|
DEBUG("[_connection_create] IPv6 address malformed\n");
|
|
goto free_out;
|
|
}
|
|
|
|
if (ipv6_addr_is_unspecified((const ipv6_addr_t *)&conn->remote.addr.ipv6)) {
|
|
DEBUG("[_connection_create] Invalid server address ([::])\n");
|
|
goto free_out;
|
|
}
|
|
|
|
/* If the address is a link-local one first check if interface is specified,
|
|
* if not, check the number of interfaces and default to the first if there
|
|
* is only one defined. */
|
|
if (ipv6_addr_is_link_local((ipv6_addr_t *)&conn->remote.addr.ipv6)) {
|
|
netif_t *netif = _get_interface(host);
|
|
if (netif == NULL) {
|
|
goto free_out;
|
|
}
|
|
else {
|
|
_set_interface(&conn->remote, netif);
|
|
}
|
|
}
|
|
|
|
conn->last_send = lwm2m_gettime();
|
|
goto out;
|
|
|
|
free_out:
|
|
lwm2m_free(conn);
|
|
conn = NULL;
|
|
out:
|
|
return conn;
|
|
}
|
|
|
|
static char *_get_uri_from_security_obj(lwm2m_object_t *obj, int instance_id,
|
|
char *uri_buffer, int buffer_size)
|
|
{
|
|
int size = 1;
|
|
char *res = NULL;
|
|
|
|
/* allocate a data instance */
|
|
lwm2m_data_t *data = lwm2m_data_new(size);
|
|
|
|
/* get the uri from the security object */
|
|
data->id = 0;
|
|
obj->readFunc(instance_id, &size, &data, obj);
|
|
|
|
if (data != NULL && data->type == LWM2M_TYPE_STRING &&
|
|
data->value.asBuffer.length > 0) {
|
|
if ((size_t)buffer_size > data->value.asBuffer.length) {
|
|
strncpy(uri_buffer, (char *)data->value.asBuffer.buffer,
|
|
data->value.asBuffer.length);
|
|
res = uri_buffer;
|
|
}
|
|
}
|
|
|
|
lwm2m_data_free(size, data);
|
|
return res;
|
|
}
|