1/* 2 * server-coap.c -- RIOT example 3 * 4 * Copyright (C) 2023 Jon Shallow <supjps-libcoap@jpshallow.com> 5 * 6 * SPDX-License-Identifier: BSD-2-Clause 7 * 8 * This file is part of the CoAP library libcoap. Please see README for terms 9 * of use. 10 */ 11 12#include "coap_config.h" 13#include <coap3/coap.h> 14#include "server-coap.h" 15#include "macros/utils.h" 16 17#ifdef CONFIG_LIBCOAP_USE_PSK 18#define COAP_USE_PSK CONFIG_LIBCOAP_USE_PSK 19#else /* CONFIG_LIBCOAP_USE_PSK */ 20#define COAP_USE_PSK NULL 21#endif /* CONFIG_LIBCOAP_USE_PSK */ 22 23static volatile int running = 0; 24static int quit; 25 26coap_context_t *main_coap_context; 27 28static coap_time_t clock_offset; 29/* changeable clock base (see handle_put_time()) */ 30static coap_time_t my_clock_base = 0; 31static coap_resource_t *time_resource = NULL; /* just for testing */ 32 33static void 34hnd_get_time(coap_resource_t *resource, coap_session_t *session, 35 const coap_pdu_t *request, const coap_string_t *query, 36 coap_pdu_t *response) { 37 unsigned char buf[40]; 38 size_t len; 39 coap_tick_t now; 40 coap_tick_t t; 41 42 (void)resource; 43 (void)session; 44 (void)request; 45 /* FIXME: return time, e.g. in human-readable by default and ticks 46 * when query ?ticks is given. */ 47 48 /* if my_clock_base was deleted, we pretend to have no such resource */ 49 coap_pdu_set_code(response, my_clock_base ? COAP_RESPONSE_CODE_CONTENT : 50 COAP_RESPONSE_CODE_NOT_FOUND); 51 if (my_clock_base) { 52 coap_add_option(response, COAP_OPTION_CONTENT_FORMAT, 53 coap_encode_var_safe(buf, sizeof(buf), 54 COAP_MEDIATYPE_TEXT_PLAIN), 55 buf); 56 } 57 58 coap_add_option(response, COAP_OPTION_MAXAGE, 59 coap_encode_var_safe(buf, sizeof(buf), 0x01), buf); 60 61 if (my_clock_base) { 62 63 /* calculate current time */ 64 coap_ticks(&t); 65 now = my_clock_base + (t / COAP_TICKS_PER_SECOND); 66 67 if (query != NULL 68 && coap_string_equal(query, coap_make_str_const("ticks"))) { 69 /* output ticks */ 70 len = snprintf((char *)buf, sizeof(buf), "%u", (unsigned int)now); 71 coap_add_data(response, len, buf); 72 } 73 } 74} 75 76static void 77init_coap_resources(coap_context_t *ctx) { 78 coap_resource_t *r; 79#if 0 80 r = coap_resource_init(NULL, 0, 0); 81 coap_register_handler(r, COAP_REQUEST_GET, hnd_get_index); 82 83 coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0); 84 coap_add_attr(r, coap_make_str_const("title"), coap_make_str_const("\"General Info\""), 0); 85 coap_add_resource(ctx, r); 86#endif 87 /* store clock base to use in /time */ 88 my_clock_base = clock_offset; 89 90 r = coap_resource_init(coap_make_str_const("time"), 0); 91 if (!r) { 92 goto error; 93 } 94 95 coap_resource_set_get_observable(r, 1); 96 time_resource = r; 97 coap_register_handler(r, COAP_REQUEST_GET, hnd_get_time); 98#if 0 99 coap_register_handler(r, COAP_REQUEST_PUT, hnd_put_time); 100 coap_register_handler(r, COAP_REQUEST_DELETE, hnd_delete_time); 101#endif 102 coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0); 103 /* coap_add_attr(r, coap_make_str_const("title"), 104 coap_make_str_const("\"Internal Clock\""), 0); */ 105 coap_add_attr(r, coap_make_str_const("rt"), coap_make_str_const("\"ticks\""), 0); 106 coap_add_attr(r, coap_make_str_const("if"), coap_make_str_const("\"clock\""), 0); 107 108 coap_add_resource(ctx, r); 109#if 0 110 if (coap_async_is_supported()) { 111 r = coap_resource_init(coap_make_str_const("async"), 0); 112 coap_register_handler(r, COAP_REQUEST_GET, hnd_get_async); 113 114 coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0); 115 coap_add_resource(ctx, r); 116 } 117#endif 118 119 return; 120error: 121 coap_log_crit("cannot create resource\n"); 122} 123 124static int 125init_coap_context_endpoints(const char *use_psk) { 126 coap_address_t listenaddress; 127 gnrc_netif_t *netif = gnrc_netif_iter(NULL); 128 ipv6_addr_t addr; 129 char addr_str[INET6_ADDRSTRLEN + 8]; 130 int scheme_hint_bits = 1 << COAP_URI_SCHEME_COAP; 131 coap_addr_info_t *info = NULL; 132 coap_addr_info_t *info_list = NULL; 133 coap_str_const_t local; 134 int have_ep = 0; 135 136 /* Get the first address on the interface */ 137 if (gnrc_netif_ipv6_addrs_get(netif, &addr, sizeof(addr)) < 0) { 138 puts("Unable to get first address of the interface"); 139 return 0; 140 } 141 142 coap_address_init(&listenaddress); 143 listenaddress.addr.sin6.sin6_family = AF_INET6; 144 memcpy(&listenaddress.addr.sin6.sin6_addr, &addr, 145 sizeof(listenaddress.addr.sin6.sin6_addr)); 146 coap_print_ip_addr(&listenaddress, addr_str, sizeof(addr_str)); 147 coap_log_info("Server IP [%s]\n", addr_str); 148 149 main_coap_context = coap_new_context(NULL); 150 if (!main_coap_context) { 151 return 0; 152 } 153 154 if (use_psk && coap_dtls_is_supported()) { 155 coap_dtls_spsk_t setup_data; 156 157 /* Need PSK set up before setting up endpoints */ 158 memset(&setup_data, 0, sizeof(setup_data)); 159 setup_data.version = COAP_DTLS_SPSK_SETUP_VERSION; 160 setup_data.psk_info.key.s = (const uint8_t *)use_psk; 161 setup_data.psk_info.key.length = strlen(use_psk); 162 coap_context_set_psk2(main_coap_context, &setup_data); 163 scheme_hint_bits |= 1 << COAP_URI_SCHEME_COAPS; 164 } 165 166 local.s = (uint8_t *)addr_str; 167 local.length = strlen(addr_str); 168 info_list = coap_resolve_address_info(&local, COAP_DEFAULT_PORT, 169 COAPS_DEFAULT_PORT, 170 0, 0, 171 0, 172 scheme_hint_bits, 173 COAP_RESOLVE_TYPE_REMOTE); 174 for (info = info_list; info != NULL; info = info->next) { 175 coap_endpoint_t *ep; 176 177 ep = coap_new_endpoint(main_coap_context, &info->addr, info->proto); 178 if (!ep) { 179 coap_log_warn("cannot create endpoint for proto %u\n", 180 info->proto); 181 } else { 182 have_ep = 1; 183 } 184 } 185 coap_free_address_info(info_list); 186 if (!have_ep) { 187 return 0; 188 } 189 190 return 1; 191} 192 193void * 194server_coap_run(void *arg) { 195 (void)arg; 196 197 /* Initialize libcoap library */ 198 coap_startup(); 199 200 coap_set_log_level(COAP_MAX_LOGGING_LEVEL); 201 202 if (!init_coap_context_endpoints(COAP_USE_PSK)) { 203 goto fail; 204 } 205 206 /* Limit the number of idle sessions to save RAM */ 207 coap_context_set_max_idle_sessions(main_coap_context, 2); 208 clock_offset = 1; /* Need a non-zero value */ 209 init_coap_resources(main_coap_context); 210 211 coap_log_info("libcoap server ready\n"); 212 /* Keep on processing ... */ 213 while (quit == 0) { 214 coap_io_process(main_coap_context, 1000); 215 } 216fail: 217 /* Clean up library usage so client can be run again */ 218 coap_free_context(main_coap_context); 219 main_coap_context = NULL; 220 coap_cleanup(); 221 running = 0; 222 quit = 0; 223 coap_log_info("libcoap server stopped\n"); 224 return NULL; 225} 226 227static char server_stack[THREAD_STACKSIZE_MAIN + 228 THREAD_EXTRA_STACKSIZE_PRINTF]; 229 230static 231void 232start_server(void) { 233 kernel_pid_t server_pid; 234 235 /* Only one instance of the server */ 236 if (running) { 237 puts("Error: server already running"); 238 return; 239 } 240 241 /* The server is initialized */ 242 server_pid = thread_create(server_stack, 243 sizeof(server_stack), 244 THREAD_PRIORITY_MAIN - 1, 245 THREAD_CREATE_STACKTEST, 246 server_coap_run, NULL, "libcoap_server"); 247 248 /* Uncommon but better be sure */ 249 if (server_pid == EINVAL) { 250 puts("ERROR: Thread invalid"); 251 return; 252 } 253 254 if (server_pid == EOVERFLOW) { 255 puts("ERROR: Thread overflow!"); 256 return; 257 } 258 259 running = 1; 260 return; 261} 262 263static 264void 265stop_server(void) { 266 /* check if server is running at all */ 267 if (running == 0) { 268 puts("Error: libcoap server is not running"); 269 return; 270 } 271 272 quit = 1; 273 274 puts("Stopping server..."); 275} 276 277void 278server_coap_init(int argc, char **argv) { 279 if (argc < 2) { 280 printf("usage: %s start|stop\n", argv[0]); 281 return; 282 } 283 if (strcmp(argv[1], "start") == 0) { 284 start_server(); 285 } else if (strcmp(argv[1], "stop") == 0) { 286 stop_server(); 287 } else { 288 printf("Error: invalid command. Usage: %s start|stop\n", argv[0]); 289 } 290 return; 291} 292