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