coap_client_example_main.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. /* CoAP client Example
  2. This example code is in the Public Domain (or CC0 licensed, at your option.)
  3. Unless required by applicable law or agreed to in writing, this
  4. software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  5. CONDITIONS OF ANY KIND, either express or implied.
  6. */
  7. /*
  8. * WARNING
  9. * libcoap is not multi-thread safe, so only this thread must make any coap_*()
  10. * calls. Any external (to this thread) data transmitted in/out via libcoap
  11. * therefore has to be passed in/out by xQueue*() via this thread.
  12. */
  13. #include <string.h>
  14. #include <sys/socket.h>
  15. #include <netdb.h>
  16. #include <sys/param.h>
  17. #include "freertos/FreeRTOS.h"
  18. #include "freertos/task.h"
  19. #include "freertos/event_groups.h"
  20. #include "esp_log.h"
  21. #include "esp_wifi.h"
  22. #include "esp_event.h"
  23. #include "nvs_flash.h"
  24. #include "protocol_examples_common.h"
  25. #if 1
  26. /* Needed until coap_dtls.h becomes a part of libcoap proper */
  27. #include "libcoap.h"
  28. #include "coap_dtls.h"
  29. #endif
  30. #include "coap.h"
  31. #define COAP_DEFAULT_TIME_SEC 5
  32. /* The examples use simple Pre-Shared-Key configuration that you can set via
  33. 'make menuconfig'.
  34. If you'd rather not, just change the below entries to strings with
  35. the config you want - ie #define EXAMPLE_COAP_PSK_KEY "some-agreed-preshared-key"
  36. Note: PSK will only be used if the URI is prefixed with coaps://
  37. instead of coap:// and the PSK must be one that the server supports
  38. (potentially associated with the IDENTITY)
  39. */
  40. #define EXAMPLE_COAP_PSK_KEY CONFIG_EXAMPLE_COAP_PSK_KEY
  41. #define EXAMPLE_COAP_PSK_IDENTITY CONFIG_EXAMPLE_COAP_PSK_IDENTITY
  42. /* The examples use uri Logging Level that
  43. you can set via 'make menuconfig'.
  44. If you'd rather not, just change the below entry to a value
  45. that is between 0 and 7 with
  46. the config you want - ie #define EXAMPLE_COAP_LOG_DEFAULT_LEVEL 7
  47. */
  48. #define EXAMPLE_COAP_LOG_DEFAULT_LEVEL CONFIG_COAP_LOG_DEFAULT_LEVEL
  49. /* The examples use uri "coap://californium.eclipse.org" that
  50. you can set via the project configuration (idf.py menuconfig)
  51. If you'd rather not, just change the below entries to strings with
  52. the config you want - ie #define COAP_DEFAULT_DEMO_URI "coaps://californium.eclipse.org"
  53. */
  54. #define COAP_DEFAULT_DEMO_URI CONFIG_EXAMPLE_TARGET_DOMAIN_URI
  55. const static char *TAG = "CoAP_client";
  56. static int resp_wait = 1;
  57. static coap_optlist_t *optlist = NULL;
  58. static int wait_ms;
  59. #ifdef CONFIG_COAP_MBEDTLS_PKI
  60. /* CA cert, taken from coap_ca.pem
  61. Client cert, taken from coap_client.crt
  62. Client key, taken from coap_client.key
  63. The PEM, CRT and KEY file are examples taken from the wpa2 enterprise
  64. example.
  65. To embed it in the app binary, the PEM, CRT and KEY file is named
  66. in the component.mk COMPONENT_EMBED_TXTFILES variable.
  67. */
  68. extern uint8_t ca_pem_start[] asm("_binary_coap_ca_pem_start");
  69. extern uint8_t ca_pem_end[] asm("_binary_coap_ca_pem_end");
  70. extern uint8_t client_crt_start[] asm("_binary_coap_client_crt_start");
  71. extern uint8_t client_crt_end[] asm("_binary_coap_client_crt_end");
  72. extern uint8_t client_key_start[] asm("_binary_coap_client_key_start");
  73. extern uint8_t client_key_end[] asm("_binary_coap_client_key_end");
  74. #endif /* CONFIG_COAP_MBEDTLS_PKI */
  75. static void message_handler(coap_context_t *ctx, coap_session_t *session,
  76. coap_pdu_t *sent, coap_pdu_t *received,
  77. const coap_tid_t id)
  78. {
  79. unsigned char *data = NULL;
  80. size_t data_len;
  81. coap_pdu_t *pdu = NULL;
  82. coap_opt_t *block_opt;
  83. coap_opt_iterator_t opt_iter;
  84. unsigned char buf[4];
  85. coap_optlist_t *option;
  86. coap_tid_t tid;
  87. if (COAP_RESPONSE_CLASS(received->code) == 2) {
  88. /* Need to see if blocked response */
  89. block_opt = coap_check_option(received, COAP_OPTION_BLOCK2, &opt_iter);
  90. if (block_opt) {
  91. uint16_t blktype = opt_iter.type;
  92. if (coap_opt_block_num(block_opt) == 0) {
  93. printf("Received:\n");
  94. }
  95. if (coap_get_data(received, &data_len, &data)) {
  96. printf("%.*s", (int)data_len, data);
  97. }
  98. if (COAP_OPT_BLOCK_MORE(block_opt)) {
  99. /* more bit is set */
  100. /* create pdu with request for next block */
  101. pdu = coap_new_pdu(session);
  102. if (!pdu) {
  103. ESP_LOGE(TAG, "coap_new_pdu() failed");
  104. goto clean_up;
  105. }
  106. pdu->type = COAP_MESSAGE_CON;
  107. pdu->tid = coap_new_message_id(session);
  108. pdu->code = COAP_REQUEST_GET;
  109. /* add URI components from optlist */
  110. for (option = optlist; option; option = option->next ) {
  111. switch (option->number) {
  112. case COAP_OPTION_URI_HOST :
  113. case COAP_OPTION_URI_PORT :
  114. case COAP_OPTION_URI_PATH :
  115. case COAP_OPTION_URI_QUERY :
  116. coap_add_option(pdu, option->number, option->length,
  117. option->data);
  118. break;
  119. default:
  120. ; /* skip other options */
  121. }
  122. }
  123. /* finally add updated block option from response, clear M bit */
  124. /* blocknr = (blocknr & 0xfffffff7) + 0x10; */
  125. coap_add_option(pdu,
  126. blktype,
  127. coap_encode_var_safe(buf, sizeof(buf),
  128. ((coap_opt_block_num(block_opt) + 1) << 4) |
  129. COAP_OPT_BLOCK_SZX(block_opt)), buf);
  130. tid = coap_send(session, pdu);
  131. if (tid != COAP_INVALID_TID) {
  132. resp_wait = 1;
  133. wait_ms = COAP_DEFAULT_TIME_SEC * 1000;
  134. return;
  135. }
  136. }
  137. printf("\n");
  138. } else {
  139. if (coap_get_data(received, &data_len, &data)) {
  140. printf("Received: %.*s\n", (int)data_len, data);
  141. }
  142. }
  143. }
  144. clean_up:
  145. resp_wait = 0;
  146. }
  147. #ifdef CONFIG_COAP_MBEDTLS_PKI
  148. static int
  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. static void coap_example_client(void *p)
  164. {
  165. struct hostent *hp;
  166. coap_address_t dst_addr;
  167. static coap_uri_t uri;
  168. const char *server_uri = COAP_DEFAULT_DEMO_URI;
  169. char *phostname = NULL;
  170. coap_set_log_level(EXAMPLE_COAP_LOG_DEFAULT_LEVEL);
  171. while (1) {
  172. #define BUFSIZE 40
  173. unsigned char _buf[BUFSIZE];
  174. unsigned char *buf;
  175. size_t buflen;
  176. int res;
  177. coap_context_t *ctx = NULL;
  178. coap_session_t *session = NULL;
  179. coap_pdu_t *request = NULL;
  180. optlist = NULL;
  181. if (coap_split_uri((const uint8_t *)server_uri, strlen(server_uri), &uri) == -1) {
  182. ESP_LOGE(TAG, "CoAP server uri error");
  183. break;
  184. }
  185. if (uri.scheme == COAP_URI_SCHEME_COAPS && !coap_dtls_is_supported()) {
  186. ESP_LOGE(TAG, "MbedTLS (D)TLS Client Mode not configured");
  187. break;
  188. }
  189. if (uri.scheme == COAP_URI_SCHEME_COAPS_TCP && !coap_tls_is_supported()) {
  190. ESP_LOGE(TAG, "CoAP server uri coaps+tcp:// scheme is not supported");
  191. break;
  192. }
  193. phostname = (char *)calloc(1, uri.host.length + 1);
  194. if (phostname == NULL) {
  195. ESP_LOGE(TAG, "calloc failed");
  196. break;
  197. }
  198. memcpy(phostname, uri.host.s, uri.host.length);
  199. hp = gethostbyname(phostname);
  200. free(phostname);
  201. if (hp == NULL) {
  202. ESP_LOGE(TAG, "DNS lookup failed");
  203. vTaskDelay(1000 / portTICK_PERIOD_MS);
  204. free(phostname);
  205. continue;
  206. }
  207. char tmpbuf[INET6_ADDRSTRLEN];
  208. coap_address_init(&dst_addr);
  209. switch (hp->h_addrtype) {
  210. case AF_INET:
  211. dst_addr.addr.sin.sin_family = AF_INET;
  212. dst_addr.addr.sin.sin_port = htons(uri.port);
  213. memcpy(&dst_addr.addr.sin.sin_addr, hp->h_addr, sizeof(dst_addr.addr.sin.sin_addr));
  214. inet_ntop(AF_INET, &dst_addr.addr.sin.sin_addr, tmpbuf, sizeof(tmpbuf));
  215. ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", tmpbuf);
  216. break;
  217. case AF_INET6:
  218. dst_addr.addr.sin6.sin6_family = AF_INET6;
  219. dst_addr.addr.sin6.sin6_port = htons(uri.port);
  220. memcpy(&dst_addr.addr.sin6.sin6_addr, hp->h_addr, sizeof(dst_addr.addr.sin6.sin6_addr));
  221. inet_ntop(AF_INET6, &dst_addr.addr.sin6.sin6_addr, tmpbuf, sizeof(tmpbuf));
  222. ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", tmpbuf);
  223. break;
  224. default:
  225. ESP_LOGE(TAG, "DNS lookup response failed");
  226. goto clean_up;
  227. }
  228. if (uri.path.length) {
  229. buflen = BUFSIZE;
  230. buf = _buf;
  231. res = coap_split_path(uri.path.s, uri.path.length, buf, &buflen);
  232. while (res--) {
  233. coap_insert_optlist(&optlist,
  234. coap_new_optlist(COAP_OPTION_URI_PATH,
  235. coap_opt_length(buf),
  236. coap_opt_value(buf)));
  237. buf += coap_opt_size(buf);
  238. }
  239. }
  240. if (uri.query.length) {
  241. buflen = BUFSIZE;
  242. buf = _buf;
  243. res = coap_split_query(uri.query.s, uri.query.length, buf, &buflen);
  244. while (res--) {
  245. coap_insert_optlist(&optlist,
  246. coap_new_optlist(COAP_OPTION_URI_QUERY,
  247. coap_opt_length(buf),
  248. coap_opt_value(buf)));
  249. buf += coap_opt_size(buf);
  250. }
  251. }
  252. ctx = coap_new_context(NULL);
  253. if (!ctx) {
  254. ESP_LOGE(TAG, "coap_new_context() failed");
  255. goto clean_up;
  256. }
  257. /*
  258. * Note that if the URI starts with just coap:// (not coaps://) the
  259. * session will still be plain text.
  260. *
  261. * coaps+tcp:// is NOT supported by the libcoap->mbedtls interface
  262. * so COAP_URI_SCHEME_COAPS_TCP will have failed in a test above,
  263. * but the code is left in for completeness.
  264. */
  265. if (uri.scheme == COAP_URI_SCHEME_COAPS || uri.scheme == COAP_URI_SCHEME_COAPS_TCP) {
  266. #ifndef CONFIG_MBEDTLS_TLS_CLIENT
  267. ESP_LOGE(TAG, "MbedTLS (D)TLS Client Mode not configured");
  268. goto clean_up;
  269. #endif /* CONFIG_MBEDTLS_TLS_CLIENT */
  270. #ifdef CONFIG_COAP_MBEDTLS_PSK
  271. session = coap_new_client_session_psk(ctx, NULL, &dst_addr,
  272. uri.scheme == COAP_URI_SCHEME_COAPS ? COAP_PROTO_DTLS : COAP_PROTO_TLS,
  273. EXAMPLE_COAP_PSK_IDENTITY,
  274. (const uint8_t *)EXAMPLE_COAP_PSK_KEY,
  275. sizeof(EXAMPLE_COAP_PSK_KEY) - 1);
  276. #endif /* CONFIG_COAP_MBEDTLS_PSK */
  277. #ifdef CONFIG_COAP_MBEDTLS_PKI
  278. unsigned int ca_pem_bytes = ca_pem_end - ca_pem_start;
  279. unsigned int client_crt_bytes = client_crt_end - client_crt_start;
  280. unsigned int client_key_bytes = client_key_end - client_key_start;
  281. coap_dtls_pki_t dtls_pki;
  282. static char client_sni[256];
  283. memset (&dtls_pki, 0, sizeof(dtls_pki));
  284. dtls_pki.version = COAP_DTLS_PKI_SETUP_VERSION;
  285. if (ca_pem_bytes) {
  286. /*
  287. * Add in additional certificate checking.
  288. * This list of enabled can be tuned for the specific
  289. * requirements - see 'man coap_encryption'.
  290. *
  291. * Note: A list of root ca file can be setup separately using
  292. * coap_context_set_pki_root_cas(), but the below is used to
  293. * define what checking actually takes place.
  294. */
  295. dtls_pki.verify_peer_cert = 1;
  296. dtls_pki.require_peer_cert = 1;
  297. dtls_pki.allow_self_signed = 1;
  298. dtls_pki.allow_expired_certs = 1;
  299. dtls_pki.cert_chain_validation = 1;
  300. dtls_pki.cert_chain_verify_depth = 2;
  301. dtls_pki.check_cert_revocation = 1;
  302. dtls_pki.allow_no_crl = 1;
  303. dtls_pki.allow_expired_crl = 1;
  304. dtls_pki.allow_bad_md_hash = 1;
  305. dtls_pki.allow_short_rsa_length = 1;
  306. dtls_pki.validate_cn_call_back = verify_cn_callback;
  307. dtls_pki.cn_call_back_arg = NULL;
  308. dtls_pki.validate_sni_call_back = NULL;
  309. dtls_pki.sni_call_back_arg = NULL;
  310. memset(client_sni, 0, sizeof(client_sni));
  311. if (uri.host.length) {
  312. memcpy(client_sni, uri.host.s, MIN(uri.host.length, sizeof(client_sni)));
  313. } else {
  314. memcpy(client_sni, "localhost", 9);
  315. }
  316. dtls_pki.client_sni = client_sni;
  317. }
  318. dtls_pki.pki_key.key_type = COAP_PKI_KEY_PEM_BUF;
  319. dtls_pki.pki_key.key.pem_buf.public_cert = client_crt_start;
  320. dtls_pki.pki_key.key.pem_buf.public_cert_len = client_crt_bytes;
  321. dtls_pki.pki_key.key.pem_buf.private_key = client_key_start;
  322. dtls_pki.pki_key.key.pem_buf.private_key_len = client_key_bytes;
  323. dtls_pki.pki_key.key.pem_buf.ca_cert = ca_pem_start;
  324. dtls_pki.pki_key.key.pem_buf.ca_cert_len = ca_pem_bytes;
  325. session = coap_new_client_session_pki(ctx, NULL, &dst_addr,
  326. uri.scheme == COAP_URI_SCHEME_COAPS ? COAP_PROTO_DTLS : COAP_PROTO_TLS,
  327. &dtls_pki);
  328. #endif /* CONFIG_COAP_MBEDTLS_PKI */
  329. } else {
  330. session = coap_new_client_session(ctx, NULL, &dst_addr,
  331. uri.scheme == COAP_URI_SCHEME_COAP_TCP ? COAP_PROTO_TCP :
  332. COAP_PROTO_UDP);
  333. }
  334. if (!session) {
  335. ESP_LOGE(TAG, "coap_new_client_session() failed");
  336. goto clean_up;
  337. }
  338. coap_register_response_handler(ctx, message_handler);
  339. request = coap_new_pdu(session);
  340. if (!request) {
  341. ESP_LOGE(TAG, "coap_new_pdu() failed");
  342. goto clean_up;
  343. }
  344. request->type = COAP_MESSAGE_CON;
  345. request->tid = coap_new_message_id(session);
  346. request->code = COAP_REQUEST_GET;
  347. coap_add_optlist_pdu(request, &optlist);
  348. resp_wait = 1;
  349. coap_send(session, request);
  350. wait_ms = COAP_DEFAULT_TIME_SEC * 1000;
  351. while (resp_wait) {
  352. int result = coap_run_once(ctx, wait_ms > 1000 ? 1000 : wait_ms);
  353. if (result >= 0) {
  354. if (result >= wait_ms) {
  355. ESP_LOGE(TAG, "select timeout");
  356. break;
  357. } else {
  358. wait_ms -= result;
  359. }
  360. }
  361. }
  362. clean_up:
  363. if (optlist) {
  364. coap_delete_optlist(optlist);
  365. optlist = NULL;
  366. }
  367. if (session) {
  368. coap_session_release(session);
  369. }
  370. if (ctx) {
  371. coap_free_context(ctx);
  372. }
  373. coap_cleanup();
  374. /*
  375. * change the following line to something like sleep(2)
  376. * if you want the request to continually be sent
  377. */
  378. break;
  379. }
  380. vTaskDelete(NULL);
  381. }
  382. void app_main(void)
  383. {
  384. ESP_ERROR_CHECK( nvs_flash_init() );
  385. ESP_ERROR_CHECK(esp_netif_init());
  386. ESP_ERROR_CHECK(esp_event_loop_create_default());
  387. /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
  388. * Read "Establishing Wi-Fi or Ethernet Connection" section in
  389. * examples/protocols/README.md for more information about this function.
  390. */
  391. ESP_ERROR_CHECK(example_connect());
  392. xTaskCreate(coap_example_client, "coap", 8 * 1024, NULL, 5, NULL);
  393. }