/* * server-coap.c -- RIOT example * * Copyright (C) 2023 Jon Shallow * * SPDX-License-Identifier: BSD-2-Clause * * This file is part of the CoAP library libcoap. Please see README for terms * of use. */ #include "coap_config.h" #include #include "server-coap.h" #include "macros/utils.h" #ifdef CONFIG_LIBCOAP_USE_PSK #define COAP_USE_PSK CONFIG_LIBCOAP_USE_PSK #else /* CONFIG_LIBCOAP_USE_PSK */ #define COAP_USE_PSK NULL #endif /* CONFIG_LIBCOAP_USE_PSK */ static volatile int running = 0; static int quit; coap_context_t *main_coap_context; static coap_time_t clock_offset; /* changeable clock base (see handle_put_time()) */ static coap_time_t my_clock_base = 0; static coap_resource_t *time_resource = NULL; /* just for testing */ static void hnd_get_time(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, const coap_string_t *query, coap_pdu_t *response) { unsigned char buf[40]; size_t len; coap_tick_t now; coap_tick_t t; (void)resource; (void)session; (void)request; /* FIXME: return time, e.g. in human-readable by default and ticks * when query ?ticks is given. */ /* if my_clock_base was deleted, we pretend to have no such resource */ coap_pdu_set_code(response, my_clock_base ? COAP_RESPONSE_CODE_CONTENT : COAP_RESPONSE_CODE_NOT_FOUND); if (my_clock_base) { coap_add_option(response, COAP_OPTION_CONTENT_FORMAT, coap_encode_var_safe(buf, sizeof(buf), COAP_MEDIATYPE_TEXT_PLAIN), buf); } coap_add_option(response, COAP_OPTION_MAXAGE, coap_encode_var_safe(buf, sizeof(buf), 0x01), buf); if (my_clock_base) { /* calculate current time */ coap_ticks(&t); now = my_clock_base + (t / COAP_TICKS_PER_SECOND); if (query != NULL && coap_string_equal(query, coap_make_str_const("ticks"))) { /* output ticks */ len = snprintf((char *)buf, sizeof(buf), "%u", (unsigned int)now); coap_add_data(response, len, buf); } } } static void init_coap_resources(coap_context_t *ctx) { coap_resource_t *r; #if 0 r = coap_resource_init(NULL, 0, 0); coap_register_handler(r, COAP_REQUEST_GET, hnd_get_index); coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0); coap_add_attr(r, coap_make_str_const("title"), coap_make_str_const("\"General Info\""), 0); coap_add_resource(ctx, r); #endif /* store clock base to use in /time */ my_clock_base = clock_offset; r = coap_resource_init(coap_make_str_const("time"), 0); if (!r) { goto error; } coap_resource_set_get_observable(r, 1); time_resource = r; coap_register_handler(r, COAP_REQUEST_GET, hnd_get_time); #if 0 coap_register_handler(r, COAP_REQUEST_PUT, hnd_put_time); coap_register_handler(r, COAP_REQUEST_DELETE, hnd_delete_time); #endif coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0); /* coap_add_attr(r, coap_make_str_const("title"), coap_make_str_const("\"Internal Clock\""), 0); */ coap_add_attr(r, coap_make_str_const("rt"), coap_make_str_const("\"ticks\""), 0); coap_add_attr(r, coap_make_str_const("if"), coap_make_str_const("\"clock\""), 0); coap_add_resource(ctx, r); #if 0 if (coap_async_is_supported()) { r = coap_resource_init(coap_make_str_const("async"), 0); coap_register_handler(r, COAP_REQUEST_GET, hnd_get_async); coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0); coap_add_resource(ctx, r); } #endif return; error: coap_log_crit("cannot create resource\n"); } static int init_coap_context_endpoints(const char *use_psk) { coap_address_t listenaddress; gnrc_netif_t *netif = gnrc_netif_iter(NULL); ipv6_addr_t addr; char addr_str[INET6_ADDRSTRLEN + 8]; int scheme_hint_bits = 1 << COAP_URI_SCHEME_COAP; coap_addr_info_t *info = NULL; coap_addr_info_t *info_list = NULL; coap_str_const_t local; int have_ep = 0; /* Get the first address on the interface */ if (gnrc_netif_ipv6_addrs_get(netif, &addr, sizeof(addr)) < 0) { puts("Unable to get first address of the interface"); return 0; } coap_address_init(&listenaddress); listenaddress.addr.sin6.sin6_family = AF_INET6; memcpy(&listenaddress.addr.sin6.sin6_addr, &addr, sizeof(listenaddress.addr.sin6.sin6_addr)); coap_print_ip_addr(&listenaddress, addr_str, sizeof(addr_str)); coap_log_info("Server IP [%s]\n", addr_str); main_coap_context = coap_new_context(NULL); if (!main_coap_context) { return 0; } if (use_psk && coap_dtls_is_supported()) { coap_dtls_spsk_t setup_data; /* Need PSK set up before setting up endpoints */ memset(&setup_data, 0, sizeof(setup_data)); setup_data.version = COAP_DTLS_SPSK_SETUP_VERSION; setup_data.psk_info.key.s = (const uint8_t *)use_psk; setup_data.psk_info.key.length = strlen(use_psk); coap_context_set_psk2(main_coap_context, &setup_data); scheme_hint_bits |= 1 << COAP_URI_SCHEME_COAPS; } local.s = (uint8_t *)addr_str; local.length = strlen(addr_str); info_list = coap_resolve_address_info(&local, COAP_DEFAULT_PORT, COAPS_DEFAULT_PORT, 0, 0, 0, scheme_hint_bits, COAP_RESOLVE_TYPE_REMOTE); for (info = info_list; info != NULL; info = info->next) { coap_endpoint_t *ep; ep = coap_new_endpoint(main_coap_context, &info->addr, info->proto); if (!ep) { coap_log_warn("cannot create endpoint for proto %u\n", info->proto); } else { have_ep = 1; } } coap_free_address_info(info_list); if (!have_ep) { return 0; } return 1; } void * server_coap_run(void *arg) { (void)arg; /* Initialize libcoap library */ coap_startup(); coap_set_log_level(COAP_MAX_LOGGING_LEVEL); if (!init_coap_context_endpoints(COAP_USE_PSK)) { goto fail; } /* Limit the number of idle sessions to save RAM */ coap_context_set_max_idle_sessions(main_coap_context, 2); clock_offset = 1; /* Need a non-zero value */ init_coap_resources(main_coap_context); coap_log_info("libcoap server ready\n"); /* Keep on processing ... */ while (quit == 0) { coap_io_process(main_coap_context, 1000); } fail: /* Clean up library usage so client can be run again */ coap_free_context(main_coap_context); main_coap_context = NULL; coap_cleanup(); running = 0; quit = 0; coap_log_info("libcoap server stopped\n"); return NULL; } static char server_stack[THREAD_STACKSIZE_MAIN + THREAD_EXTRA_STACKSIZE_PRINTF]; static void start_server(void) { kernel_pid_t server_pid; /* Only one instance of the server */ if (running) { puts("Error: server already running"); return; } /* The server is initialized */ server_pid = thread_create(server_stack, sizeof(server_stack), THREAD_PRIORITY_MAIN - 1, THREAD_CREATE_STACKTEST, server_coap_run, NULL, "libcoap_server"); /* Uncommon but better be sure */ if (server_pid == EINVAL) { puts("ERROR: Thread invalid"); return; } if (server_pid == EOVERFLOW) { puts("ERROR: Thread overflow!"); return; } running = 1; return; } static void stop_server(void) { /* check if server is running at all */ if (running == 0) { puts("Error: libcoap server is not running"); return; } quit = 1; puts("Stopping server..."); } void server_coap_init(int argc, char **argv) { if (argc < 2) { printf("usage: %s start|stop\n", argv[0]); return; } if (strcmp(argv[1], "start") == 0) { start_server(); } else if (strcmp(argv[1], "stop") == 0) { stop_server(); } else { printf("Error: invalid command. Usage: %s start|stop\n", argv[0]); } return; }