1/*
2 * client-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 <sys/types.h>
16#include <sys/socket.h>
17#include <netdb.h>
18#include "client-coap.h"
19
20#ifndef COAP_URI
21#define COAP_URI "coap://libcoap.net"
22#endif /* COAP_URI */
23
24#ifndef min
25#define min(a,b) ((a) < (b) ? (a) : (b))
26#endif
27
28static coap_context_t *main_coap_context = NULL;
29static coap_optlist_t *optlist = NULL;
30
31static int quit = 0;
32
33static coap_response_t
34message_handler(coap_session_t *session,
35                const coap_pdu_t *sent,
36                const coap_pdu_t *received,
37                const coap_mid_t id) {
38  const uint8_t *data;
39  size_t len;
40  size_t offset;
41  size_t total;
42
43  (void)session;
44  (void)sent;
45  (void)id;
46  if (coap_get_data_large(received, &len, &data, &offset, &total)) {
47    printf("%*.*s", (int)len, (int)len, (const char *)data);
48    if (len + offset == total) {
49      printf("\n");
50      quit = 1;
51    }
52  }
53  return COAP_RESPONSE_OK;
54}
55
56static void
57nack_handler(coap_session_t *session COAP_UNUSED,
58             const coap_pdu_t *sent COAP_UNUSED,
59             const coap_nack_reason_t reason,
60             const coap_mid_t id COAP_UNUSED) {
61
62  switch (reason) {
63  case COAP_NACK_TOO_MANY_RETRIES:
64  case COAP_NACK_NOT_DELIVERABLE:
65  case COAP_NACK_RST:
66  case COAP_NACK_TLS_FAILED:
67    coap_log_err("cannot send CoAP pdu\n");
68    quit = 1;
69    break;
70  case COAP_NACK_ICMP_ISSUE:
71  default:
72    ;
73  }
74  return;
75}
76
77static int
78resolve_address(const char *host, const char *service, coap_address_t *dst,
79                int scheme_hint_bits) {
80
81  coap_addr_info_t *addr_info;
82  coap_str_const_t str_host;
83  uint16_t port = service ? atoi(service) : 0;
84  int ret = 0;
85
86  str_host.s = (const uint8_t *)host;
87  str_host.length = strlen(host);
88
89  addr_info = coap_resolve_address_info(&str_host, port, port, port, port,
90                                        AF_UNSPEC, scheme_hint_bits,
91                                        COAP_RESOLVE_TYPE_REMOTE);
92  if (addr_info) {
93    ret = 1;
94    *dst = addr_info->addr;
95  }
96
97  coap_free_address_info(addr_info);
98  return ret;
99}
100
101void
102client_coap_init(coap_lwip_input_wait_handler_t input_wait, void *input_arg,
103                 int argc, char **argv) {
104  coap_session_t *session = NULL;
105  coap_pdu_t *pdu;
106  coap_address_t dst;
107  coap_mid_t mid;
108  int len;
109  coap_uri_t uri;
110  char portbuf[8];
111#define BUFSIZE 100
112  unsigned char buf[BUFSIZE];
113  int res;
114  const char *use_uri = COAP_URI;
115  int opt;
116  coap_log_t log_level = COAP_LOG_WARN;
117  coap_log_t dtls_log_level = COAP_LOG_ERR;
118  const char *use_psk = "secretPSK";
119  const char *use_id = "abc";
120  coap_pdu_type_t pdu_type = COAP_MESSAGE_CON;
121
122  /* Initialize libcoap library */
123  coap_startup();
124
125  while ((opt = getopt(argc, argv, ":k:Nu:v:V:")) != -1) {
126    switch (opt) {
127    case 'k':
128      use_psk = optarg;
129      break;
130    case 'u':
131      use_id = optarg;
132      break;
133    case 'v':
134      log_level = atoi(optarg);
135      break;
136    case 'N':
137      pdu_type = COAP_MESSAGE_NON;
138      break;
139    case 'V':
140      dtls_log_level = atoi(optarg);
141      break;
142    default:
143      printf("%s [-k PSK] [-u id] [-v level] [ -V level] [URI]\n", argv[0]);
144      exit(1);
145    }
146  }
147
148  if (optind < argc) {
149    use_uri = argv[optind];
150  }
151
152  coap_set_log_level(log_level);
153  coap_dtls_set_log_level(dtls_log_level);
154
155  /* Parse the URI */
156  len = coap_split_uri((const unsigned char *)use_uri, strlen(use_uri), &uri);
157  LWIP_ASSERT("Failed to parse uri", len == 0);
158  LWIP_ASSERT("Unsupported URI type", uri.scheme == COAP_URI_SCHEME_COAP ||
159              uri.scheme == COAP_URI_SCHEME_COAPS);
160  if (uri.scheme == COAP_URI_SCHEME_COAPS) {
161    LWIP_ASSERT("DTLS not supported", coap_dtls_is_supported());
162  }
163
164  snprintf(portbuf, sizeof(portbuf), "%d", uri.port);
165  snprintf((char *)buf, sizeof(buf), "%*.*s", (int)uri.host.length,
166           (int)uri.host.length, (const char *)uri.host.s);
167  /* resolve destination address where server should be sent */
168  len = resolve_address((const char *)buf, portbuf, &dst, 1 << uri.scheme);
169  LWIP_ASSERT("Failed to resolve address", len > 0);
170
171  main_coap_context = coap_new_context(NULL);
172  LWIP_ASSERT("Failed to initialize context", main_coap_context != NULL);
173
174  coap_context_set_block_mode(main_coap_context, COAP_BLOCK_USE_LIBCOAP);
175  coap_lwip_set_input_wait_handler(main_coap_context, input_wait, input_arg);
176
177  if (uri.scheme == COAP_URI_SCHEME_COAP) {
178    session = coap_new_client_session(main_coap_context, NULL, &dst,
179                                      COAP_PROTO_UDP);
180  } else {
181    static coap_dtls_cpsk_t dtls_psk;
182    static char client_sni[256];
183
184    memset(client_sni, 0, sizeof(client_sni));
185    memset(&dtls_psk, 0, sizeof(dtls_psk));
186    dtls_psk.version = COAP_DTLS_CPSK_SETUP_VERSION;
187    if (uri.host.length)
188      memcpy(client_sni, uri.host.s,
189             min(uri.host.length, sizeof(client_sni) - 1));
190    else
191      memcpy(client_sni, "localhost", 9);
192    dtls_psk.client_sni = client_sni;
193    dtls_psk.psk_info.identity.s = (const uint8_t *)use_id;
194    dtls_psk.psk_info.identity.length = strlen(use_id);
195    dtls_psk.psk_info.key.s = (const uint8_t *)use_psk;
196    dtls_psk.psk_info.key.length = strlen(use_psk);
197
198    session = coap_new_client_session_psk2(main_coap_context, NULL, &dst,
199                                           COAP_PROTO_DTLS, &dtls_psk);
200  }
201
202  LWIP_ASSERT("Failed to create session", session != NULL);
203
204  coap_register_response_handler(main_coap_context, message_handler);
205  coap_register_nack_handler(main_coap_context, nack_handler);
206
207  /* construct CoAP message */
208  pdu = coap_pdu_init(pdu_type,
209                      COAP_REQUEST_CODE_GET,
210                      coap_new_message_id(session),
211                      coap_session_max_pdu_size(session));
212  LWIP_ASSERT("Failed to create PDU", pdu != NULL);
213
214  len = coap_uri_into_options(&uri, &dst, &optlist, 1, buf, sizeof(buf));
215  LWIP_ASSERT("Failed to create options", len == 0);
216
217  /* Add option list (which will be sorted) to the PDU */
218  if (optlist) {
219    res = coap_add_optlist_pdu(pdu, &optlist);
220    LWIP_ASSERT("Failed to add options to PDU", res == 1);
221  }
222
223  /* and send the PDU */
224  mid = coap_send(session, pdu);
225  LWIP_ASSERT("Failed to send PDU", mid != COAP_INVALID_MID);
226}
227
228void
229client_coap_finished(void) {
230  coap_delete_optlist(optlist);
231  coap_free_context(main_coap_context);
232  main_coap_context = NULL;
233  coap_cleanup();
234}
235
236int
237client_coap_poll(void) {
238  coap_io_process(main_coap_context, 1000);
239  return quit;
240}
241