1c87c5fbaSopenharmony_ci/*
2c87c5fbaSopenharmony_ci * client-coap.c -- RIOT client example
3c87c5fbaSopenharmony_ci *
4c87c5fbaSopenharmony_ci * Copyright (C) 2023 Jon Shallow <supjps-libcoap@jpshallow.com>
5c87c5fbaSopenharmony_ci *
6c87c5fbaSopenharmony_ci * SPDX-License-Identifier: BSD-2-Clause
7c87c5fbaSopenharmony_ci *
8c87c5fbaSopenharmony_ci * This file is part of the CoAP library libcoap. Please see README for terms
9c87c5fbaSopenharmony_ci * of use.
10c87c5fbaSopenharmony_ci */
11c87c5fbaSopenharmony_ci
12c87c5fbaSopenharmony_ci#include "coap_config.h"
13c87c5fbaSopenharmony_ci#include <coap3/coap.h>
14c87c5fbaSopenharmony_ci#include <sys/types.h>
15c87c5fbaSopenharmony_ci#include <sys/socket.h>
16c87c5fbaSopenharmony_ci#include <netdb.h>
17c87c5fbaSopenharmony_ci#include "client-coap.h"
18c87c5fbaSopenharmony_ci#include "macros/utils.h"
19c87c5fbaSopenharmony_ci#include "net/utils.h"
20c87c5fbaSopenharmony_ci#include <arpa/inet.h>
21c87c5fbaSopenharmony_ci#include <thread.h>
22c87c5fbaSopenharmony_ci#include <debug.h>
23c87c5fbaSopenharmony_ci
24c87c5fbaSopenharmony_ci#ifdef CONFIG_LIBCOAP_CLIENT_URI
25c87c5fbaSopenharmony_ci#define COAP_CLIENT_URI CONFIG_LIBCOAP_CLIENT_URI
26c87c5fbaSopenharmony_ci#else /* ! CONFIG_LIBCOAP_CLIENT_URI */
27c87c5fbaSopenharmony_ci#define COAP_CLIENT_URI "coap://[fe80::405:5aff:fe15:9b7f]/.well-known/core"
28c87c5fbaSopenharmony_ci#endif /* ! CONFIG_LIBCOAP_CLIENT_URI */
29c87c5fbaSopenharmony_ci
30c87c5fbaSopenharmony_ci#ifdef CONFIG_LIBCOAP_USE_PSK
31c87c5fbaSopenharmony_ci#define COAP_USE_PSK CONFIG_LIBCOAP_USE_PSK
32c87c5fbaSopenharmony_ci#else /* ! CONFIG_LIBCOAP_USE_PSK */
33c87c5fbaSopenharmony_ci#define COAP_USE_PSK NULL
34c87c5fbaSopenharmony_ci#endif /* ! CONFIG_LIBCOAP_USE_PSK */
35c87c5fbaSopenharmony_ci
36c87c5fbaSopenharmony_ci#ifdef CONFIG_LIBCOAP_USE_PSK_ID
37c87c5fbaSopenharmony_ci#define COAP_USE_PSK_ID CONFIG_LIBCOAP_USE_PSK_ID
38c87c5fbaSopenharmony_ci#else /* ! CONFIG_LIBCOAP_USE_PSK_ID */
39c87c5fbaSopenharmony_ci#define COAP_USE_PSK_ID NULL
40c87c5fbaSopenharmony_ci#endif /* ! CONFIG_LIBCOAP_USE_PSK_ID */
41c87c5fbaSopenharmony_ci
42c87c5fbaSopenharmony_cistatic coap_context_t *main_coap_context = NULL;
43c87c5fbaSopenharmony_cistatic coap_optlist_t *optlist = NULL;
44c87c5fbaSopenharmony_ci
45c87c5fbaSopenharmony_cistatic int quit = 0;
46c87c5fbaSopenharmony_ci
47c87c5fbaSopenharmony_cistatic coap_response_t
48c87c5fbaSopenharmony_cimessage_handler(coap_session_t *session,
49c87c5fbaSopenharmony_ci                const coap_pdu_t *sent,
50c87c5fbaSopenharmony_ci                const coap_pdu_t *received,
51c87c5fbaSopenharmony_ci                const coap_mid_t id) {
52c87c5fbaSopenharmony_ci  const uint8_t *data;
53c87c5fbaSopenharmony_ci  size_t len;
54c87c5fbaSopenharmony_ci  size_t offset;
55c87c5fbaSopenharmony_ci  size_t total;
56c87c5fbaSopenharmony_ci
57c87c5fbaSopenharmony_ci  (void)session;
58c87c5fbaSopenharmony_ci  (void)sent;
59c87c5fbaSopenharmony_ci  (void)id;
60c87c5fbaSopenharmony_ci  if (coap_get_data_large(received, &len, &data, &offset, &total)) {
61c87c5fbaSopenharmony_ci    printf("%*.*s", (int)len, (int)len, (const char *)data);
62c87c5fbaSopenharmony_ci    if (len + offset == total) {
63c87c5fbaSopenharmony_ci      printf("\n");
64c87c5fbaSopenharmony_ci      quit = 1;
65c87c5fbaSopenharmony_ci    }
66c87c5fbaSopenharmony_ci  }
67c87c5fbaSopenharmony_ci  return COAP_RESPONSE_OK;
68c87c5fbaSopenharmony_ci}
69c87c5fbaSopenharmony_ci
70c87c5fbaSopenharmony_cistatic void
71c87c5fbaSopenharmony_cinack_handler(coap_session_t *session COAP_UNUSED,
72c87c5fbaSopenharmony_ci             const coap_pdu_t *sent COAP_UNUSED,
73c87c5fbaSopenharmony_ci             const coap_nack_reason_t reason,
74c87c5fbaSopenharmony_ci             const coap_mid_t id COAP_UNUSED) {
75c87c5fbaSopenharmony_ci
76c87c5fbaSopenharmony_ci  switch (reason) {
77c87c5fbaSopenharmony_ci  case COAP_NACK_TOO_MANY_RETRIES:
78c87c5fbaSopenharmony_ci  case COAP_NACK_NOT_DELIVERABLE:
79c87c5fbaSopenharmony_ci  case COAP_NACK_RST:
80c87c5fbaSopenharmony_ci  case COAP_NACK_TLS_FAILED:
81c87c5fbaSopenharmony_ci  case COAP_NACK_TLS_LAYER_FAILED:
82c87c5fbaSopenharmony_ci  case COAP_NACK_WS_LAYER_FAILED:
83c87c5fbaSopenharmony_ci  case COAP_NACK_WS_FAILED:
84c87c5fbaSopenharmony_ci    coap_log_err("cannot send CoAP pdu\n");
85c87c5fbaSopenharmony_ci    quit = 1;
86c87c5fbaSopenharmony_ci    break;
87c87c5fbaSopenharmony_ci  case COAP_NACK_ICMP_ISSUE:
88c87c5fbaSopenharmony_ci  case COAP_NACK_BAD_RESPONSE:
89c87c5fbaSopenharmony_ci  default:
90c87c5fbaSopenharmony_ci    break;
91c87c5fbaSopenharmony_ci  }
92c87c5fbaSopenharmony_ci  return;
93c87c5fbaSopenharmony_ci}
94c87c5fbaSopenharmony_ci
95c87c5fbaSopenharmony_cistatic int
96c87c5fbaSopenharmony_ciresolve_address(const char *host, const char *service, coap_address_t *dst,
97c87c5fbaSopenharmony_ci                int scheme_hint_bits) {
98c87c5fbaSopenharmony_ci  uint16_t port = service ? atoi(service) : 0;
99c87c5fbaSopenharmony_ci  int ret = 0;
100c87c5fbaSopenharmony_ci  coap_str_const_t str_host;
101c87c5fbaSopenharmony_ci  coap_addr_info_t *addr_info;
102c87c5fbaSopenharmony_ci
103c87c5fbaSopenharmony_ci  str_host.s = (const uint8_t *)host;
104c87c5fbaSopenharmony_ci  str_host.length = strlen(host);
105c87c5fbaSopenharmony_ci  addr_info = coap_resolve_address_info(&str_host, port, port,  port, port,
106c87c5fbaSopenharmony_ci                                        AF_UNSPEC, scheme_hint_bits,
107c87c5fbaSopenharmony_ci                                        COAP_RESOLVE_TYPE_REMOTE);
108c87c5fbaSopenharmony_ci  if (addr_info) {
109c87c5fbaSopenharmony_ci    ret = 1;
110c87c5fbaSopenharmony_ci    *dst = addr_info->addr;
111c87c5fbaSopenharmony_ci  }
112c87c5fbaSopenharmony_ci
113c87c5fbaSopenharmony_ci  coap_free_address_info(addr_info);
114c87c5fbaSopenharmony_ci  return ret;
115c87c5fbaSopenharmony_ci}
116c87c5fbaSopenharmony_ci
117c87c5fbaSopenharmony_civoid
118c87c5fbaSopenharmony_ciclient_coap_init(int argc, char **argv) {
119c87c5fbaSopenharmony_ci  coap_session_t *session = NULL;
120c87c5fbaSopenharmony_ci  coap_pdu_t *pdu;
121c87c5fbaSopenharmony_ci  coap_address_t dst;
122c87c5fbaSopenharmony_ci  coap_mid_t mid;
123c87c5fbaSopenharmony_ci  int len;
124c87c5fbaSopenharmony_ci  coap_uri_t uri;
125c87c5fbaSopenharmony_ci  char portbuf[8];
126c87c5fbaSopenharmony_ci#define BUFSIZE 100
127c87c5fbaSopenharmony_ci  unsigned char buf[BUFSIZE];
128c87c5fbaSopenharmony_ci  int res;
129c87c5fbaSopenharmony_ci  const char *coap_uri = COAP_CLIENT_URI;
130c87c5fbaSopenharmony_ci
131c87c5fbaSopenharmony_ci  if (argc > 1) {
132c87c5fbaSopenharmony_ci    coap_uri = argv[1];
133c87c5fbaSopenharmony_ci  }
134c87c5fbaSopenharmony_ci
135c87c5fbaSopenharmony_ci  /* Initialize libcoap library */
136c87c5fbaSopenharmony_ci  coap_startup();
137c87c5fbaSopenharmony_ci
138c87c5fbaSopenharmony_ci  coap_set_log_level(COAP_MAX_LOGGING_LEVEL);
139c87c5fbaSopenharmony_ci
140c87c5fbaSopenharmony_ci  /* Parse the URI */
141c87c5fbaSopenharmony_ci  len = coap_split_uri((const unsigned char *)coap_uri, strlen(coap_uri), &uri);
142c87c5fbaSopenharmony_ci  if (len != 0) {
143c87c5fbaSopenharmony_ci    coap_log_warn("Failed to parse uri %s\n", coap_uri);
144c87c5fbaSopenharmony_ci    goto fail;
145c87c5fbaSopenharmony_ci  }
146c87c5fbaSopenharmony_ci
147c87c5fbaSopenharmony_ci  snprintf(portbuf, sizeof(portbuf), "%d", uri.port);
148c87c5fbaSopenharmony_ci  snprintf((char *)buf, sizeof(buf), "%*.*s", (int)uri.host.length,
149c87c5fbaSopenharmony_ci           (int)uri.host.length, (const char *)uri.host.s);
150c87c5fbaSopenharmony_ci  /* resolve destination address where packet should be sent */
151c87c5fbaSopenharmony_ci  len = resolve_address((const char *)buf, portbuf, &dst, 1 << uri.scheme);
152c87c5fbaSopenharmony_ci  if (len <= 0) {
153c87c5fbaSopenharmony_ci    coap_log_warn("Failed to resolve address %*.*s\n", (int)uri.host.length,
154c87c5fbaSopenharmony_ci                  (int)uri.host.length, (const char *)uri.host.s);
155c87c5fbaSopenharmony_ci    goto fail;
156c87c5fbaSopenharmony_ci  }
157c87c5fbaSopenharmony_ci
158c87c5fbaSopenharmony_ci  main_coap_context = coap_new_context(NULL);
159c87c5fbaSopenharmony_ci  if (!main_coap_context) {
160c87c5fbaSopenharmony_ci    coap_log_warn("Failed to initialize context\n");
161c87c5fbaSopenharmony_ci    goto fail;
162c87c5fbaSopenharmony_ci  }
163c87c5fbaSopenharmony_ci
164c87c5fbaSopenharmony_ci  coap_context_set_block_mode(main_coap_context, COAP_BLOCK_USE_LIBCOAP);
165c87c5fbaSopenharmony_ci
166c87c5fbaSopenharmony_ci  if (uri.scheme == COAP_URI_SCHEME_COAP) {
167c87c5fbaSopenharmony_ci    session = coap_new_client_session(main_coap_context, NULL, &dst,
168c87c5fbaSopenharmony_ci                                      COAP_PROTO_UDP);
169c87c5fbaSopenharmony_ci  } else if (uri.scheme == COAP_URI_SCHEME_COAP_TCP) {
170c87c5fbaSopenharmony_ci    session = coap_new_client_session(main_coap_context, NULL, &dst,
171c87c5fbaSopenharmony_ci                                      COAP_PROTO_TCP);
172c87c5fbaSopenharmony_ci#if defined (COAP_USE_PSK) && defined(COAP_USE_PSK_ID)
173c87c5fbaSopenharmony_ci  } else {
174c87c5fbaSopenharmony_ci    static coap_dtls_cpsk_t dtls_psk;
175c87c5fbaSopenharmony_ci    static char client_sni[256];
176c87c5fbaSopenharmony_ci
177c87c5fbaSopenharmony_ci    memset(client_sni, 0, sizeof(client_sni));
178c87c5fbaSopenharmony_ci    memset(&dtls_psk, 0, sizeof(dtls_psk));
179c87c5fbaSopenharmony_ci    dtls_psk.version = COAP_DTLS_CPSK_SETUP_VERSION;
180c87c5fbaSopenharmony_ci    if (uri.host.length) {
181c87c5fbaSopenharmony_ci      memcpy(client_sni, uri.host.s,
182c87c5fbaSopenharmony_ci             MIN(uri.host.length, sizeof(client_sni) - 1));
183c87c5fbaSopenharmony_ci    }
184c87c5fbaSopenharmony_ci    else {
185c87c5fbaSopenharmony_ci      memcpy(client_sni, "localhost", 9);
186c87c5fbaSopenharmony_ci    }
187c87c5fbaSopenharmony_ci    dtls_psk.client_sni = client_sni;
188c87c5fbaSopenharmony_ci    dtls_psk.psk_info.identity.s = (const uint8_t *)COAP_USE_PSK_ID;
189c87c5fbaSopenharmony_ci    dtls_psk.psk_info.identity.length = strlen(COAP_USE_PSK_ID);
190c87c5fbaSopenharmony_ci    dtls_psk.psk_info.key.s = (const uint8_t *)COAP_USE_PSK;
191c87c5fbaSopenharmony_ci    dtls_psk.psk_info.key.length = strlen(COAP_USE_PSK);
192c87c5fbaSopenharmony_ci
193c87c5fbaSopenharmony_ci    session = coap_new_client_session_psk2(main_coap_context, NULL, &dst,
194c87c5fbaSopenharmony_ci                                           COAP_PROTO_DTLS, &dtls_psk);
195c87c5fbaSopenharmony_ci#else /* ! COAP_USE_PSK && ! COAP_USE_PSK_ID */
196c87c5fbaSopenharmony_ci    coap_log_err("CONFIG_LIBCOAP_USE_PSK and CONFIG_LIBCOAP_USE_PSK_ID not defined\n");
197c87c5fbaSopenharmony_ci    goto fail;
198c87c5fbaSopenharmony_ci#endif /* ! COAP_USE_PSK && ! COAP_USE_PSK_ID */
199c87c5fbaSopenharmony_ci  }
200c87c5fbaSopenharmony_ci
201c87c5fbaSopenharmony_ci  if (!session) {
202c87c5fbaSopenharmony_ci    coap_log_warn("Failed to create session\n");
203c87c5fbaSopenharmony_ci    goto fail;
204c87c5fbaSopenharmony_ci  }
205c87c5fbaSopenharmony_ci
206c87c5fbaSopenharmony_ci  coap_register_response_handler(main_coap_context, message_handler);
207c87c5fbaSopenharmony_ci  coap_register_nack_handler(main_coap_context, nack_handler);
208c87c5fbaSopenharmony_ci
209c87c5fbaSopenharmony_ci  /* construct CoAP message */
210c87c5fbaSopenharmony_ci  pdu = coap_pdu_init(COAP_MESSAGE_CON,
211c87c5fbaSopenharmony_ci                      COAP_REQUEST_CODE_GET,
212c87c5fbaSopenharmony_ci                      coap_new_message_id(session),
213c87c5fbaSopenharmony_ci                      coap_session_max_pdu_size(session));
214c87c5fbaSopenharmony_ci  if (!pdu) {
215c87c5fbaSopenharmony_ci    coap_log_warn("Failed to create PDU\n");
216c87c5fbaSopenharmony_ci    goto fail;
217c87c5fbaSopenharmony_ci  }
218c87c5fbaSopenharmony_ci
219c87c5fbaSopenharmony_ci  len = coap_uri_into_options(&uri, &dst, &optlist, 1, buf, sizeof(buf));
220c87c5fbaSopenharmony_ci  if (len) {
221c87c5fbaSopenharmony_ci    coap_log_warn("Failed to create options\n");
222c87c5fbaSopenharmony_ci    goto fail;
223c87c5fbaSopenharmony_ci  }
224c87c5fbaSopenharmony_ci
225c87c5fbaSopenharmony_ci  /* Add option list (which will be sorted) to the PDU */
226c87c5fbaSopenharmony_ci  if (optlist) {
227c87c5fbaSopenharmony_ci    res = coap_add_optlist_pdu(pdu, &optlist);
228c87c5fbaSopenharmony_ci    if (res != 1) {
229c87c5fbaSopenharmony_ci      coap_log_warn("Failed to add options to PDU\n");
230c87c5fbaSopenharmony_ci      goto fail;
231c87c5fbaSopenharmony_ci    }
232c87c5fbaSopenharmony_ci  }
233c87c5fbaSopenharmony_ci
234c87c5fbaSopenharmony_ci  /* and send the PDU */
235c87c5fbaSopenharmony_ci  mid = coap_send(session, pdu);
236c87c5fbaSopenharmony_ci  if (mid == COAP_INVALID_MID) {
237c87c5fbaSopenharmony_ci    coap_log_warn("Failed to send PDU\n");
238c87c5fbaSopenharmony_ci    goto fail;
239c87c5fbaSopenharmony_ci  }
240c87c5fbaSopenharmony_ci  while (!quit) {
241c87c5fbaSopenharmony_ci    coap_io_process(main_coap_context, 1000);
242c87c5fbaSopenharmony_ci  }
243c87c5fbaSopenharmony_cifail:
244c87c5fbaSopenharmony_ci  /* Clean up library usage so client can be run again */
245c87c5fbaSopenharmony_ci  quit = 0;
246c87c5fbaSopenharmony_ci  coap_delete_optlist(optlist);
247c87c5fbaSopenharmony_ci  optlist = NULL;
248c87c5fbaSopenharmony_ci  coap_session_release(session);
249c87c5fbaSopenharmony_ci  session = NULL;
250c87c5fbaSopenharmony_ci  coap_free_context(main_coap_context);
251c87c5fbaSopenharmony_ci  main_coap_context = NULL;
252c87c5fbaSopenharmony_ci  coap_cleanup();
253c87c5fbaSopenharmony_ci}
254