1 /* CoAP server Example
2
3 This example code is in the Public Domain (or CC0 licensed, at your option.)
4
5 Unless required by applicable law or agreed to in writing, this
6 software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
7 CONDITIONS OF ANY KIND, either express or implied.
8 */
9
10 /*
11 * WARNING
12 * libcoap is not multi-thread safe, so only this thread must make any coap_*()
13 * calls. Any external (to this thread) data transmitted in/out via libcoap
14 * therefore has to be passed in/out by xQueue*() via this thread.
15 */
16
17 #include <string.h>
18 #include <sys/socket.h>
19
20 #include "freertos/FreeRTOS.h"
21 #include "freertos/task.h"
22 #include "freertos/event_groups.h"
23
24 #include "esp_log.h"
25 #include "esp_wifi.h"
26 #include "esp_event.h"
27
28 #include "nvs_flash.h"
29
30 #include "protocol_examples_common.h"
31
32 #include "coap3/coap.h"
33
34 /* The examples use simple Pre-Shared-Key configuration that you can set via
35 'idf.py menuconfig'.
36
37 If you'd rather not, just change the below entries to strings with
38 the config you want - ie #define EXAMPLE_COAP_PSK_KEY "some-agreed-preshared-key"
39
40 Note: PSK will only be used if the URI is prefixed with coaps://
41 instead of coap:// and the PSK must be one that the server supports
42 (potentially associated with the IDENTITY)
43 */
44 #define EXAMPLE_COAP_PSK_KEY CONFIG_EXAMPLE_COAP_PSK_KEY
45
46 /* The examples use CoAP Logging Level that
47 you can set via 'idf.py menuconfig'.
48
49 If you'd rather not, just change the below entry to a value
50 that is between 0 and 7 with
51 the config you want - ie #define EXAMPLE_COAP_LOG_DEFAULT_LEVEL 7
52 */
53 #define EXAMPLE_COAP_LOG_DEFAULT_LEVEL CONFIG_COAP_LOG_DEFAULT_LEVEL
54
55 const static char *TAG = "CoAP_server";
56
57 static char espressif_data[100];
58 static int espressif_data_len = 0;
59
60 #ifdef CONFIG_COAP_MBEDTLS_PKI
61 /* CA cert, taken from coap_ca.pem
62 Server cert, taken from coap_server.crt
63 Server key, taken from coap_server.key
64
65 The PEM, CRT and KEY file are examples taken from
66 https://github.com/eclipse/californium/tree/master/demo-certs/src/main/resources
67 as the Certificate test (by default) for the coap_client is against the
68 californium server.
69
70 To embed it in the app binary, the PEM, CRT and KEY file is named
71 in the component.mk COMPONENT_EMBED_TXTFILES variable.
72 */
73 extern uint8_t ca_pem_start[] asm("_binary_coap_ca_pem_start");
74 extern uint8_t ca_pem_end[] asm("_binary_coap_ca_pem_end");
75 extern uint8_t server_crt_start[] asm("_binary_coap_server_crt_start");
76 extern uint8_t server_crt_end[] asm("_binary_coap_server_crt_end");
77 extern uint8_t server_key_start[] asm("_binary_coap_server_key_start");
78 extern uint8_t server_key_end[] asm("_binary_coap_server_key_end");
79 #endif /* CONFIG_COAP_MBEDTLS_PKI */
80
81 #define INITIAL_DATA "Hello World!"
82
83 /*
84 * The resource handler
85 */
86 static void
hnd_espressif_get(coap_resource_t * resource,coap_session_t * session,const coap_pdu_t * request,const coap_string_t * query,coap_pdu_t * response)87 hnd_espressif_get(coap_resource_t *resource,
88 coap_session_t *session,
89 const coap_pdu_t *request,
90 const coap_string_t *query,
91 coap_pdu_t *response)
92 {
93 coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
94 coap_add_data_large_response(resource, session, request, response,
95 query, COAP_MEDIATYPE_TEXT_PLAIN, 60, 0,
96 (size_t)espressif_data_len,
97 (const u_char *)espressif_data,
98 NULL, NULL);
99 }
100
101 static void
hnd_espressif_put(coap_resource_t * resource,coap_session_t * session,const coap_pdu_t * request,const coap_string_t * query,coap_pdu_t * response)102 hnd_espressif_put(coap_resource_t *resource,
103 coap_session_t *session,
104 const coap_pdu_t *request,
105 const coap_string_t *query,
106 coap_pdu_t *response)
107 {
108 size_t size;
109 size_t offset;
110 size_t total;
111 const unsigned char *data;
112
113 coap_resource_notify_observers(resource, NULL);
114
115 if (strcmp (espressif_data, INITIAL_DATA) == 0) {
116 coap_pdu_set_code(response, COAP_RESPONSE_CODE_CREATED);
117 } else {
118 coap_pdu_set_code(response, COAP_RESPONSE_CODE_CHANGED);
119 }
120
121 /* coap_get_data_large() sets size to 0 on error */
122 (void)coap_get_data_large(request, &size, &data, &offset, &total);
123
124 if (size == 0) { /* re-init */
125 snprintf(espressif_data, sizeof(espressif_data), INITIAL_DATA);
126 espressif_data_len = strlen(espressif_data);
127 } else {
128 espressif_data_len = size > sizeof (espressif_data) ? sizeof (espressif_data) : size;
129 memcpy (espressif_data, data, espressif_data_len);
130 }
131 }
132
133 static void
hnd_espressif_delete(coap_resource_t * resource,coap_session_t * session,const coap_pdu_t * request,const coap_string_t * query,coap_pdu_t * response)134 hnd_espressif_delete(coap_resource_t *resource,
135 coap_session_t *session,
136 const coap_pdu_t *request,
137 const coap_string_t *query,
138 coap_pdu_t *response)
139 {
140 coap_resource_notify_observers(resource, NULL);
141 snprintf(espressif_data, sizeof(espressif_data), INITIAL_DATA);
142 espressif_data_len = strlen(espressif_data);
143 coap_pdu_set_code(response, COAP_RESPONSE_CODE_DELETED);
144 }
145
146 #ifdef CONFIG_COAP_MBEDTLS_PKI
147
148 static int
verify_cn_callback(const char * cn,const uint8_t * asn1_public_cert,size_t asn1_length,coap_session_t * session,unsigned depth,int validated,void * arg)149 verify_cn_callback(const char *cn,
150 const uint8_t *asn1_public_cert,
151 size_t asn1_length,
152 coap_session_t *session,
153 unsigned depth,
154 int validated,
155 void *arg
156 )
157 {
158 coap_log(LOG_INFO, "CN '%s' presented by server (%s)\n",
159 cn, depth ? "CA" : "Certificate");
160 return 1;
161 }
162 #endif /* CONFIG_COAP_MBEDTLS_PKI */
163
164 static void
coap_log_handler(coap_log_t level,const char * message)165 coap_log_handler (coap_log_t level, const char *message)
166 {
167 uint32_t esp_level = ESP_LOG_INFO;
168 char *cp = strchr(message, '\n');
169
170 if (cp)
171 ESP_LOG_LEVEL(esp_level, TAG, "%.*s", (int)(cp-message), message);
172 else
173 ESP_LOG_LEVEL(esp_level, TAG, "%s", message);
174 }
175
coap_example_server(void * p)176 static void coap_example_server(void *p)
177 {
178 coap_context_t *ctx = NULL;
179 coap_address_t serv_addr;
180 coap_resource_t *resource = NULL;
181
182 snprintf(espressif_data, sizeof(espressif_data), INITIAL_DATA);
183 espressif_data_len = strlen(espressif_data);
184 coap_set_log_handler(coap_log_handler);
185 coap_set_log_level(EXAMPLE_COAP_LOG_DEFAULT_LEVEL);
186
187 while (1) {
188 coap_endpoint_t *ep = NULL;
189 unsigned wait_ms;
190
191 /* Prepare the CoAP server socket */
192 coap_address_init(&serv_addr);
193 serv_addr.addr.sin6.sin6_family = AF_INET6;
194 serv_addr.addr.sin6.sin6_port = htons(COAP_DEFAULT_PORT);
195
196 ctx = coap_new_context(NULL);
197 if (!ctx) {
198 ESP_LOGE(TAG, "coap_new_context() failed");
199 continue;
200 }
201 coap_context_set_block_mode(ctx,
202 COAP_BLOCK_USE_LIBCOAP|COAP_BLOCK_SINGLE_BODY);
203 #ifdef CONFIG_COAP_MBEDTLS_PSK
204 /* Need PSK setup before we set up endpoints */
205 coap_context_set_psk(ctx, "CoAP",
206 (const uint8_t *)EXAMPLE_COAP_PSK_KEY,
207 sizeof(EXAMPLE_COAP_PSK_KEY) - 1);
208 #endif /* CONFIG_COAP_MBEDTLS_PSK */
209
210 #ifdef CONFIG_COAP_MBEDTLS_PKI
211 /* Need PKI setup before we set up endpoints */
212 unsigned int ca_pem_bytes = ca_pem_end - ca_pem_start;
213 unsigned int server_crt_bytes = server_crt_end - server_crt_start;
214 unsigned int server_key_bytes = server_key_end - server_key_start;
215 coap_dtls_pki_t dtls_pki;
216
217 memset (&dtls_pki, 0, sizeof(dtls_pki));
218 dtls_pki.version = COAP_DTLS_PKI_SETUP_VERSION;
219 if (ca_pem_bytes) {
220 /*
221 * Add in additional certificate checking.
222 * This list of enabled can be tuned for the specific
223 * requirements - see 'man coap_encryption'.
224 *
225 * Note: A list of root ca file can be setup separately using
226 * coap_context_set_pki_root_cas(), but the below is used to
227 * define what checking actually takes place.
228 */
229 dtls_pki.verify_peer_cert = 1;
230 dtls_pki.check_common_ca = 1;
231 dtls_pki.allow_self_signed = 1;
232 dtls_pki.allow_expired_certs = 1;
233 dtls_pki.cert_chain_validation = 1;
234 dtls_pki.cert_chain_verify_depth = 2;
235 dtls_pki.check_cert_revocation = 1;
236 dtls_pki.allow_no_crl = 1;
237 dtls_pki.allow_expired_crl = 1;
238 dtls_pki.allow_bad_md_hash = 1;
239 dtls_pki.allow_short_rsa_length = 1;
240 dtls_pki.validate_cn_call_back = verify_cn_callback;
241 dtls_pki.cn_call_back_arg = NULL;
242 dtls_pki.validate_sni_call_back = NULL;
243 dtls_pki.sni_call_back_arg = NULL;
244 }
245 dtls_pki.pki_key.key_type = COAP_PKI_KEY_PEM_BUF;
246 dtls_pki.pki_key.key.pem_buf.public_cert = server_crt_start;
247 dtls_pki.pki_key.key.pem_buf.public_cert_len = server_crt_bytes;
248 dtls_pki.pki_key.key.pem_buf.private_key = server_key_start;
249 dtls_pki.pki_key.key.pem_buf.private_key_len = server_key_bytes;
250 dtls_pki.pki_key.key.pem_buf.ca_cert = ca_pem_start;
251 dtls_pki.pki_key.key.pem_buf.ca_cert_len = ca_pem_bytes;
252
253 coap_context_set_pki(ctx, &dtls_pki);
254 #endif /* CONFIG_COAP_MBEDTLS_PKI */
255
256 ep = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_UDP);
257 if (!ep) {
258 ESP_LOGE(TAG, "udp: coap_new_endpoint() failed");
259 goto clean_up;
260 }
261 ep = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_TCP);
262 if (!ep) {
263 ESP_LOGE(TAG, "tcp: coap_new_endpoint() failed");
264 goto clean_up;
265 }
266 #if defined(CONFIG_COAP_MBEDTLS_PSK) || defined(CONFIG_COAP_MBEDTLS_PKI)
267 if (coap_dtls_is_supported()) {
268 #ifndef CONFIG_MBEDTLS_TLS_SERVER
269 /* This is not critical as unencrypted support is still available */
270 ESP_LOGI(TAG, "MbedTLS (D)TLS Server Mode not configured");
271 #else /* CONFIG_MBEDTLS_TLS_SERVER */
272 serv_addr.addr.sin6.sin6_port = htons(COAPS_DEFAULT_PORT);
273 ep = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_DTLS);
274 if (!ep) {
275 ESP_LOGE(TAG, "dtls: coap_new_endpoint() failed");
276 goto clean_up;
277 }
278 #endif /* CONFIG_MBEDTLS_TLS_SERVER */
279 } else {
280 /* This is not critical as unencrypted support is still available */
281 ESP_LOGI(TAG, "MbedTLS (D)TLS Server Mode not configured");
282 }
283 #endif /* CONFIG_COAP_MBEDTLS_PSK || CONFIG_COAP_MBEDTLS_PKI */
284 resource = coap_resource_init(coap_make_str_const("Espressif"), 0);
285 if (!resource) {
286 ESP_LOGE(TAG, "coap_resource_init() failed");
287 goto clean_up;
288 }
289 coap_register_handler(resource, COAP_REQUEST_GET, hnd_espressif_get);
290 coap_register_handler(resource, COAP_REQUEST_PUT, hnd_espressif_put);
291 coap_register_handler(resource, COAP_REQUEST_DELETE, hnd_espressif_delete);
292 /* We possibly want to Observe the GETs */
293 coap_resource_set_get_observable(resource, 1);
294 coap_add_resource(ctx, resource);
295
296 #if defined(CONFIG_EXAMPLE_COAP_MCAST_IPV4) || defined(CONFIG_EXAMPLE_COAP_MCAST_IPV6)
297 esp_netif_t *netif = NULL;
298 for (int i = 0; i < esp_netif_get_nr_of_ifs(); ++i) {
299 char buf[8];
300 netif = esp_netif_next(netif);
301 esp_netif_get_netif_impl_name(netif, buf);
302 #if defined(CONFIG_EXAMPLE_COAP_MCAST_IPV4)
303 coap_join_mcast_group_intf(ctx, CONFIG_EXAMPLE_COAP_MULTICAST_IPV4_ADDR, buf);
304 #endif /* CONFIG_EXAMPLE_COAP_MCAST_IPV4 */
305 #if defined(CONFIG_EXAMPLE_COAP_MCAST_IPV6)
306 /* When adding IPV6 esp-idf requires ifname param to be filled in */
307 coap_join_mcast_group_intf(ctx, CONFIG_EXAMPLE_COAP_MULTICAST_IPV6_ADDR, buf);
308 #endif /* CONFIG_EXAMPLE_COAP_MCAST_IPV6 */
309 }
310 #endif /* CONFIG_EXAMPLE_COAP_MCAST_IPV4 || CONFIG_EXAMPLE_COAP_MCAST_IPV6 */
311
312 wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;
313
314 while (1) {
315 int result = coap_io_process(ctx, wait_ms);
316 if (result < 0) {
317 break;
318 } else if (result && (unsigned)result < wait_ms) {
319 /* decrement if there is a result wait time returned */
320 wait_ms -= result;
321 }
322 if (result) {
323 /* result must have been >= wait_ms, so reset wait_ms */
324 wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;
325 }
326 }
327 }
328 clean_up:
329 coap_free_context(ctx);
330 coap_cleanup();
331
332 vTaskDelete(NULL);
333 }
334
app_main(void)335 void app_main(void)
336 {
337 ESP_ERROR_CHECK( nvs_flash_init() );
338 ESP_ERROR_CHECK(esp_netif_init());
339 ESP_ERROR_CHECK(esp_event_loop_create_default());
340
341 /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
342 * Read "Establishing Wi-Fi or Ethernet Connection" section in
343 * examples/protocols/README.md for more information about this function.
344 */
345 ESP_ERROR_CHECK(example_connect());
346
347 xTaskCreate(coap_example_server, "coap", 8 * 1024, NULL, 5, NULL);
348 }
349