• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
23 static volatile int running = 0;
24 static int quit;
25 
26 coap_context_t *main_coap_context;
27 
28 static coap_time_t clock_offset;
29 /* changeable clock base (see handle_put_time()) */
30 static coap_time_t my_clock_base = 0;
31 static coap_resource_t *time_resource = NULL; /* just for testing */
32 
33 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)34 hnd_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 
76 static void
init_coap_resources(coap_context_t * ctx)77 init_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;
120 error:
121   coap_log_crit("cannot create resource\n");
122 }
123 
124 static int
init_coap_context_endpoints(const char * use_psk)125 init_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 
193 void *
server_coap_run(void * arg)194 server_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   }
216 fail:
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 
227 static char server_stack[THREAD_STACKSIZE_MAIN +
228                                                THREAD_EXTRA_STACKSIZE_PRINTF];
229 
230 static
231 void
start_server(void)232 start_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 
263 static
264 void
stop_server(void)265 stop_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 
277 void
server_coap_init(int argc,char ** argv)278 server_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