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
28 static coap_context_t *main_coap_context = NULL;
29 static coap_optlist_t *optlist = NULL;
30
31 static int quit = 0;
32
33 static coap_response_t
message_handler(coap_session_t * session,const coap_pdu_t * sent,const coap_pdu_t * received,const coap_mid_t id)34 message_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
56 static void
nack_handler(coap_session_t * session COAP_UNUSED,const coap_pdu_t * sent COAP_UNUSED,const coap_nack_reason_t reason,const coap_mid_t id COAP_UNUSED)57 nack_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
77 static int
resolve_address(const char * host,const char * service,coap_address_t * dst,int scheme_hint_bits)78 resolve_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
101 void
client_coap_init(coap_lwip_input_wait_handler_t input_wait,void * input_arg,int argc,char ** argv)102 client_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
228 void
client_coap_finished(void)229 client_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
236 int
client_coap_poll(void)237 client_coap_poll(void) {
238 coap_io_process(main_coap_context, 1000);
239 return quit;
240 }
241