1 /*
2 * server-coap.c -- LwIP example
3 *
4 * Copyright (C) 2013-2016 Christian Amsüss <chrysn@fsfe.org>
5 * Copyright (C) 2018-2023 Jon Shallow <supjps-libcoap@jpshallow.com>
6 *
7 * SPDX-License-Identifier: BSD-2-Clause
8 *
9 * This file is part of the CoAP library libcoap. Please see README for terms
10 * of use.
11 */
12
13 #include "coap_config.h"
14 #include <coap3/coap.h>
15 #include "server-coap.h"
16
17 coap_context_t *main_coap_context;
18
19 static coap_time_t clock_offset;
20 /* changeable clock base (see handle_put_time()) */
21 static coap_time_t my_clock_base = 0;
22 static coap_resource_t *time_resource = NULL; /* just for testing */
23
24 #ifndef min
25 # define min(a,b) ((a) < (b) ? (a) : (b))
26 #endif
27
28 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)29 hnd_get_time(coap_resource_t *resource, coap_session_t *session,
30 const coap_pdu_t *request, const coap_string_t *query,
31 coap_pdu_t *response) {
32 unsigned char buf[40];
33 size_t len;
34 coap_tick_t now;
35 coap_tick_t t;
36
37 (void)resource;
38 (void)session;
39 (void)request;
40 /* FIXME: return time, e.g. in human-readable by default and ticks
41 * when query ?ticks is given. */
42
43 /* if my_clock_base was deleted, we pretend to have no such resource */
44 response->code =
45 my_clock_base ? COAP_RESPONSE_CODE(205) : COAP_RESPONSE_CODE(404);
46
47 if (my_clock_base)
48 coap_add_option(response, COAP_OPTION_CONTENT_FORMAT,
49 coap_encode_var_safe(buf, sizeof(buf),
50 COAP_MEDIATYPE_TEXT_PLAIN),
51 buf);
52
53 coap_add_option(response, COAP_OPTION_MAXAGE,
54 coap_encode_var_safe(buf, sizeof(buf), 0x01), buf);
55
56 if (my_clock_base) {
57
58 /* calculate current time */
59 coap_ticks(&t);
60 now = my_clock_base + (t / COAP_TICKS_PER_SECOND);
61
62
63 if (query != NULL
64 && coap_string_equal(query, coap_make_str_const("ticks"))) {
65 /* output ticks */
66 len = snprintf((char *)buf, sizeof(buf), "%u", (unsigned int)now);
67 coap_add_data(response, len, buf);
68 }
69 }
70 }
71
72 void
init_coap_resources(coap_context_t * ctx)73 init_coap_resources(coap_context_t *ctx) {
74 coap_resource_t *r;
75 #if 0
76 r = coap_resource_init(NULL, 0, 0);
77 coap_register_handler(r, COAP_REQUEST_GET, hnd_get_index);
78
79 coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
80 coap_add_attr(r, coap_make_str_const("title"), coap_make_str_const("\"General Info\""), 0);
81 coap_add_resource(ctx, r);
82 #endif
83 /* store clock base to use in /time */
84 my_clock_base = clock_offset;
85
86 r = coap_resource_init(coap_make_str_const("time"), 0);
87 if (!r)
88 goto error;
89
90 coap_resource_set_get_observable(r, 1);
91 time_resource = r;
92 coap_register_handler(r, COAP_REQUEST_GET, hnd_get_time);
93 #if 0
94 coap_register_handler(r, COAP_REQUEST_PUT, hnd_put_time);
95 coap_register_handler(r, COAP_REQUEST_DELETE, hnd_delete_time);
96 #endif
97 coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
98 /* coap_add_attr(r, coap_make_str_const("title"), coap_make_str_const("\"Internal Clock\""), 0); */
99 coap_add_attr(r, coap_make_str_const("rt"), coap_make_str_const("\"ticks\""), 0);
100 coap_add_attr(r, coap_make_str_const("if"), coap_make_str_const("\"clock\""), 0);
101
102 coap_add_resource(ctx, r);
103 #if 0
104 if (coap_async_is_supported()) {
105 r = coap_resource_init(coap_make_str_const("async"), 0);
106 coap_register_handler(r, COAP_REQUEST_GET, hnd_get_async);
107
108 coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
109 coap_add_resource(ctx, r);
110 }
111 #endif
112
113 return;
114 error:
115 coap_log_crit("cannot create resource\n");
116 }
117
118 void
server_coap_init(coap_lwip_input_wait_handler_t input_wait,void * input_arg,int argc,char ** argv)119 server_coap_init(coap_lwip_input_wait_handler_t input_wait,
120 void *input_arg, int argc, char **argv) {
121 int opt;
122 coap_log_t log_level = COAP_LOG_WARN;
123 coap_log_t dtls_log_level = COAP_LOG_ERR;
124 const char *use_psk = "secretPSK";
125 uint32_t scheme_hint_bits = 0;
126 coap_addr_info_t *info = NULL;
127 coap_addr_info_t *info_list = NULL;
128 int have_ep = 0;
129 coap_str_const_t node;
130
131 /* Initialize libcoap library */
132 coap_startup();
133
134 while ((opt = getopt(argc, argv, ":k:v:V:")) != -1) {
135 switch (opt) {
136 case 'k':
137 use_psk = optarg;
138 break;
139 case 'v':
140 log_level = atoi(optarg);
141 break;
142 case 'V':
143 dtls_log_level = atoi(optarg);
144 break;
145 default:
146 printf("%s [-k PSK] [-v level] [ -V level]\n", argv[0]);
147 exit(1);
148 }
149 }
150
151 coap_startup();
152 coap_set_log_level(log_level);
153 coap_dtls_set_log_level(dtls_log_level);
154
155 main_coap_context = coap_new_context(NULL);
156 LWIP_ASSERT("Failed to initialize context", main_coap_context != NULL);
157
158 if (coap_dtls_is_supported()) {
159 coap_dtls_spsk_t setup_data;
160
161 memset(&setup_data, 0, sizeof(setup_data));
162 setup_data.version = COAP_DTLS_SPSK_SETUP_VERSION;
163 setup_data.psk_info.key.s = (const uint8_t *)use_psk;
164 setup_data.psk_info.key.length = strlen(use_psk);
165 coap_context_set_psk2(main_coap_context, &setup_data);
166 }
167
168 node.s = (const uint8_t *)"::";
169 node.length = 2;
170 scheme_hint_bits =
171 coap_get_available_scheme_hint_bits(use_psk[0],
172 0, COAP_PROTO_NONE);
173 info_list = coap_resolve_address_info(&node, 0, 0,
174 0, 0,
175 0,
176 scheme_hint_bits,
177 COAP_RESOLVE_TYPE_LOCAL);
178 for (info = info_list; info != NULL; info = info->next) {
179 coap_endpoint_t *ep;
180
181 ep = coap_new_endpoint(main_coap_context, &info->addr, info->proto);
182 if (!ep) {
183 coap_log_warn("cannot create endpoint for proto %u\n",
184 info->proto);
185 } else {
186 have_ep = 1;
187 }
188 }
189 coap_free_address_info(info_list);
190 LWIP_ASSERT("Failed to initialize context", have_ep != 0);
191
192 /* Limit the number of idle sessions to save RAM (MEMP_NUM_COAPSESSION) */
193 LWIP_ASSERT("Need a minimum of 2 for MEMP_NUM_COAPSESSION", MEMP_NUM_COAPSESSION > 1);
194 coap_context_set_max_idle_sessions(main_coap_context, MEMP_NUM_COAPSESSION -1);
195 clock_offset = 1; /* Need a non-zero value */
196 init_coap_resources(main_coap_context);
197 coap_lwip_set_input_wait_handler(main_coap_context, input_wait, input_arg);
198 }
199
200 void
server_coap_finished(void)201 server_coap_finished(void) {
202 coap_free_context(main_coap_context);
203 main_coap_context = NULL;
204 coap_cleanup();
205 }
206
207 void
server_coap_poll(void)208 server_coap_poll(void) {
209 static coap_time_t last_time = 0;
210 coap_tick_t ticks_now;
211 coap_time_t time_now;
212
213 coap_io_process(main_coap_context, 1000);
214 coap_ticks(&ticks_now);
215 time_now = coap_ticks_to_rt(ticks_now);
216
217 if (last_time != time_now) {
218 /* This takes place once a second */
219 last_time = time_now;
220 coap_resource_notify_observers(time_resource, NULL);
221 }
222 coap_check_notify(main_coap_context);
223 }
224