1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
3 /* coap-client -- simple CoAP client
4 *
5 * Copyright (C) 2010--2023 Olaf Bergmann <bergmann@tzi.org> and others
6 *
7 * SPDX-License-Identifier: BSD-2-Clause
8 *
9 * This file is part of the CoAP library libcoap. Please see README for terms of
10 * use.
11 */
12
13 #include <string.h>
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <ctype.h>
17 #include <signal.h>
18 #include <inttypes.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #ifdef _WIN32
22 #define strcasecmp _stricmp
23 #define strncasecmp _strnicmp
24 #define fileno _fileno
25 #define getpid GetCurrentProcessId
26 #include "getopt.c"
27 #if !defined(S_ISDIR)
28 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
29 #endif
30 char *strndup(const char *s1, size_t n);
31 char *
strndup(const char * s1,size_t n)32 strndup(const char *s1, size_t n) {
33 char *copy = (char *)malloc(n + 1);
34 if (copy) {
35 memcpy(copy, s1, n);
36 copy[n] = 0;
37 }
38 return copy;
39 }
40 #else
41 #include <unistd.h>
42 #include <sys/select.h>
43 #include <sys/socket.h>
44 #include <netinet/in.h>
45 #include <arpa/inet.h>
46 #include <netdb.h>
47 #include <syslog.h>
48 #endif
49
50 #include <coap3/coap.h>
51
52 #define MAX_USER 128 /* Maximum length of a user name (i.e., PSK
53 * identity) in bytes. */
54 #define MAX_KEY 64 /* Maximum length of a key (i.e., PSK) in bytes. */
55
56 int flags = 0;
57
58 static unsigned char _token_data[24]; /* With support for RFC8974 */
59 coap_binary_t the_token = { 0, _token_data };
60
61 typedef struct {
62 coap_binary_t *token;
63 int observe;
64 } track_token;
65
66 track_token *tracked_tokens = NULL;
67 size_t tracked_tokens_count = 0;
68
69 #define FLAGS_BLOCK 0x01
70
71 static coap_optlist_t *optlist = NULL;
72 /* Request URI.
73 * TODO: associate the resources with transaction id and make it expireable */
74 static coap_uri_t uri;
75 static coap_uri_t proxy = { {0, NULL}, 0, {0, NULL}, {0, NULL}, 0 };
76 static int proxy_scheme_option = 0;
77 static int uri_host_option = 0;
78 static unsigned int ping_seconds = 0;
79
80 #define REPEAT_DELAY_MS 1000
81 static size_t repeat_count = 1;
82
83 /* reading is done when this flag is set */
84 static int ready = 0;
85
86 /* processing a block response when this flag is set */
87 static int doing_getting_block = 0;
88 static int single_block_requested = 0;
89 static uint32_t block_mode = COAP_BLOCK_USE_LIBCOAP;
90
91 static coap_string_t output_file = { 0, NULL }; /* output file name */
92 static FILE *file = NULL; /* output file stream */
93
94 static coap_string_t payload = { 0, NULL }; /* optional payload to send */
95
96 static int reliable = 0;
97
98 static int add_nl = 0;
99 static int is_mcast = 0;
100 static uint32_t csm_max_message_size = 0;
101
102 unsigned char msgtype = COAP_MESSAGE_CON; /* usually, requests are sent confirmable */
103
104 static char *cert_file = NULL; /* certificate and optional private key in PEM,
105 or PKCS11 URI*/
106 static char *key_file = NULL; /* private key in PEM, DER or PKCS11 URI */
107 static char *pkcs11_pin = NULL; /* PKCS11 pin to unlock access to token */
108 static char *ca_file = NULL; /* CA for cert_file - for cert checking in PEM,
109 DER or PKCS11 URI */
110 static char *root_ca_file = NULL; /* List of trusted Root CAs in PEM */
111 static int is_rpk_not_cert = 0; /* Cert is RPK if set */
112 static uint8_t *cert_mem = NULL; /* certificate and private key in PEM_BUF */
113 static uint8_t *key_mem = NULL; /* private key in PEM_BUF */
114 static uint8_t *ca_mem = NULL; /* CA for cert checking in PEM_BUF */
115 static size_t cert_mem_len = 0;
116 static size_t key_mem_len = 0;
117 static size_t ca_mem_len = 0;
118 static int verify_peer_cert = 1; /* PKI granularity - by default set */
119
120 typedef struct ih_def_t {
121 char *hint_match;
122 coap_bin_const_t *new_identity;
123 coap_bin_const_t *new_key;
124 } ih_def_t;
125
126 typedef struct valid_ihs_t {
127 size_t count;
128 ih_def_t *ih_list;
129 } valid_ihs_t;
130
131 static valid_ihs_t valid_ihs = {0, NULL};
132
133 typedef unsigned char method_t;
134 method_t method = 1; /* the method we are using in our requests */
135
136 coap_block_t block = { .num = 0, .m = 0, .szx = 6 };
137 uint16_t last_block1_mid = 0;
138
139 #define DEFAULT_WAIT_TIME 90
140
141 unsigned int wait_seconds = DEFAULT_WAIT_TIME; /* default timeout in seconds */
142 unsigned int wait_ms = 0;
143 int obs_started = 0;
144 unsigned int obs_seconds = 30; /* default observe time */
145 unsigned int obs_ms = 0; /* timeout for current subscription */
146 int obs_ms_reset = 0;
147 int doing_observe = 0;
148
149 #ifndef min
150 #define min(a,b) ((a) < (b) ? (a) : (b))
151 #endif
152
153 static coap_oscore_conf_t *oscore_conf = NULL;
154 static int doing_oscore = 0;
155
156 static int quit = 0;
157
158 /* SIGINT handler: set quit to 1 for graceful termination */
159 static void
handle_sigint(int signum COAP_UNUSED)160 handle_sigint(int signum COAP_UNUSED) {
161 quit = 1;
162 }
163
164 static int
append_to_output(const uint8_t * data,size_t len)165 append_to_output(const uint8_t *data, size_t len) {
166 size_t written;
167
168 if (!file) {
169 if (!output_file.s || (output_file.length && output_file.s[0] == '-')) {
170 file = stdout;
171 } else {
172 if (!(file = fopen((char *)output_file.s, "w"))) {
173 perror("fopen");
174 return -1;
175 }
176 }
177 }
178
179 do {
180 written = fwrite(data, 1, len, file);
181 len -= written;
182 data += written;
183 } while (written && len);
184 fflush(file);
185
186 return 0;
187 }
188
189 static void
close_output(void)190 close_output(void) {
191 if (file) {
192
193 /* add a newline before closing if no option '-o' was specified */
194 if (!output_file.s)
195 (void)fwrite("\n", 1, 1, file);
196
197 fflush(file);
198 fclose(file);
199 }
200 }
201
202 static void
free_xmit_data(coap_session_t * session COAP_UNUSED,void * app_ptr)203 free_xmit_data(coap_session_t *session COAP_UNUSED, void *app_ptr) {
204 coap_free(app_ptr);
205 return;
206 }
207
208 static void
track_new_token(size_t tokenlen,uint8_t * token)209 track_new_token(size_t tokenlen, uint8_t *token) {
210 track_token *new_list = realloc(tracked_tokens,
211 (tracked_tokens_count + 1) * sizeof(tracked_tokens[0]));
212 if (!new_list) {
213 coap_log_info("Unable to track new token\n");
214 return;
215 }
216 tracked_tokens = new_list;
217 tracked_tokens[tracked_tokens_count].token = coap_new_binary(tokenlen);
218 if (!tracked_tokens[tracked_tokens_count].token)
219 return;
220 memcpy(tracked_tokens[tracked_tokens_count].token->s, token, tokenlen);
221 tracked_tokens[tracked_tokens_count].observe = doing_observe;
222 tracked_tokens_count++;
223 }
224
225 static int
track_check_token(coap_bin_const_t * token)226 track_check_token(coap_bin_const_t *token) {
227 size_t i;
228
229 for (i = 0; i < tracked_tokens_count; i++) {
230 if (coap_binary_equal(token, tracked_tokens[i].token)) {
231 return 1;
232 }
233 }
234 return 0;
235 }
236
237 static void
track_flush_token(coap_bin_const_t * token,int force)238 track_flush_token(coap_bin_const_t *token, int force) {
239 size_t i;
240
241 for (i = 0; i < tracked_tokens_count; i++) {
242 if (coap_binary_equal(token, tracked_tokens[i].token)) {
243 if (force || !tracked_tokens[i].observe || !obs_started) {
244 /* Only remove if not Observing */
245 coap_delete_binary(tracked_tokens[i].token);
246 if (tracked_tokens_count-i > 1) {
247 memmove(&tracked_tokens[i],
248 &tracked_tokens[i+1],
249 (tracked_tokens_count-i-1) * sizeof(tracked_tokens[0]));
250 }
251 tracked_tokens_count--;
252 }
253 break;
254 }
255 }
256 }
257
258
259 static coap_pdu_t *
coap_new_request(coap_context_t * ctx,coap_session_t * session,method_t m,coap_optlist_t ** options,unsigned char * data,size_t length)260 coap_new_request(coap_context_t *ctx,
261 coap_session_t *session,
262 method_t m,
263 coap_optlist_t **options,
264 unsigned char *data,
265 size_t length) {
266 coap_pdu_t *pdu;
267 uint8_t token[8];
268 size_t tokenlen;
269 (void)ctx;
270
271 if (!(pdu = coap_new_pdu(msgtype, m, session))) {
272 free_xmit_data(session, data);
273 return NULL;
274 }
275
276 /*
277 * Create unique token for this request for handling unsolicited /
278 * delayed responses.
279 * Note that only up to 8 bytes are returned
280 */
281 if (the_token.length > COAP_TOKEN_DEFAULT_MAX) {
282 coap_session_new_token(session, &tokenlen, token);
283 /* Update the last part 8 bytes of the large token */
284 memcpy(&the_token.s[the_token.length - tokenlen], token, tokenlen);
285 } else {
286 coap_session_new_token(session, &the_token.length, the_token.s);
287 }
288 track_new_token(the_token.length, the_token.s);
289 if (!coap_add_token(pdu, the_token.length, the_token.s)) {
290 coap_log_debug("cannot add token to request\n");
291 }
292
293 if (options)
294 coap_add_optlist_pdu(pdu, options);
295
296 if (length) {
297 /* Let the underlying libcoap decide how this data should be sent */
298 coap_add_data_large_request(session, pdu, length, data,
299 free_xmit_data, data);
300 }
301
302 return pdu;
303 }
304
305 static int
event_handler(coap_session_t * session COAP_UNUSED,const coap_event_t event)306 event_handler(coap_session_t *session COAP_UNUSED,
307 const coap_event_t event) {
308
309 switch (event) {
310 case COAP_EVENT_DTLS_CLOSED:
311 case COAP_EVENT_TCP_CLOSED:
312 case COAP_EVENT_SESSION_CLOSED:
313 case COAP_EVENT_OSCORE_DECRYPTION_FAILURE:
314 case COAP_EVENT_OSCORE_NOT_ENABLED:
315 case COAP_EVENT_OSCORE_NO_PROTECTED_PAYLOAD:
316 case COAP_EVENT_OSCORE_NO_SECURITY:
317 case COAP_EVENT_OSCORE_INTERNAL_ERROR:
318 case COAP_EVENT_OSCORE_DECODE_ERROR:
319 case COAP_EVENT_WS_PACKET_SIZE:
320 case COAP_EVENT_WS_CLOSED:
321 quit = 1;
322 break;
323 case COAP_EVENT_DTLS_CONNECTED:
324 case COAP_EVENT_DTLS_RENEGOTIATE:
325 case COAP_EVENT_DTLS_ERROR:
326 case COAP_EVENT_TCP_CONNECTED:
327 case COAP_EVENT_TCP_FAILED:
328 case COAP_EVENT_SESSION_CONNECTED:
329 case COAP_EVENT_SESSION_FAILED:
330 case COAP_EVENT_PARTIAL_BLOCK:
331 case COAP_EVENT_XMIT_BLOCK_FAIL:
332 case COAP_EVENT_SERVER_SESSION_NEW:
333 case COAP_EVENT_SERVER_SESSION_DEL:
334 case COAP_EVENT_BAD_PACKET:
335 case COAP_EVENT_MSG_RETRANSMITTED:
336 case COAP_EVENT_WS_CONNECTED:
337 case COAP_EVENT_KEEPALIVE_FAILURE:
338 default:
339 break;
340 }
341 return 0;
342 }
343
344 static void
nack_handler(coap_session_t * session COAP_UNUSED,const coap_pdu_t * sent,const coap_nack_reason_t reason,const coap_mid_t mid COAP_UNUSED)345 nack_handler(coap_session_t *session COAP_UNUSED,
346 const coap_pdu_t *sent,
347 const coap_nack_reason_t reason,
348 const coap_mid_t mid COAP_UNUSED) {
349 if (sent) {
350 coap_bin_const_t token = coap_pdu_get_token(sent);
351
352 if (!track_check_token(&token)) {
353 coap_log_err("nack_handler: Unexpected token\n");
354 }
355 }
356
357 switch (reason) {
358 case COAP_NACK_TOO_MANY_RETRIES:
359 case COAP_NACK_NOT_DELIVERABLE:
360 case COAP_NACK_RST:
361 case COAP_NACK_TLS_FAILED:
362 case COAP_NACK_WS_FAILED:
363 case COAP_NACK_TLS_LAYER_FAILED:
364 case COAP_NACK_WS_LAYER_FAILED:
365 coap_log_err("cannot send CoAP pdu\n");
366 quit = 1;
367 break;
368 case COAP_NACK_ICMP_ISSUE:
369 case COAP_NACK_BAD_RESPONSE:
370 default:
371 ;
372 }
373 return;
374 }
375
376 /*
377 * Response handler used for coap_send() responses
378 */
379 static coap_response_t
message_handler(coap_session_t * session COAP_UNUSED,const coap_pdu_t * sent,const coap_pdu_t * received,const coap_mid_t id COAP_UNUSED)380 message_handler(coap_session_t *session COAP_UNUSED,
381 const coap_pdu_t *sent,
382 const coap_pdu_t *received,
383 const coap_mid_t id COAP_UNUSED) {
384
385 coap_opt_t *block_opt;
386 coap_opt_iterator_t opt_iter;
387 size_t len;
388 const uint8_t *databuf;
389 size_t offset;
390 size_t total;
391 coap_pdu_code_t rcv_code = coap_pdu_get_code(received);
392 coap_pdu_type_t rcv_type = coap_pdu_get_type(received);
393 coap_bin_const_t token = coap_pdu_get_token(received);
394
395 coap_log_debug("** process incoming %d.%02d response:\n",
396 COAP_RESPONSE_CLASS(rcv_code), rcv_code & 0x1F);
397 if (coap_get_log_level() < COAP_LOG_DEBUG)
398 coap_show_pdu(COAP_LOG_INFO, received);
399
400 /* check if this is a response to our original request */
401 if (!track_check_token(&token)) {
402 /* drop if this was just some message, or send RST in case of notification */
403 if (!sent && (rcv_type == COAP_MESSAGE_CON ||
404 rcv_type == COAP_MESSAGE_NON)) {
405 /* Cause a CoAP RST to be sent */
406 return COAP_RESPONSE_FAIL;
407 }
408 return COAP_RESPONSE_OK;
409 }
410
411 if (rcv_type == COAP_MESSAGE_RST) {
412 coap_log_info("got RST\n");
413 return COAP_RESPONSE_OK;
414 }
415
416 /* output the received data, if any */
417 if (COAP_RESPONSE_CLASS(rcv_code) == 2) {
418
419 /* set obs timer if we have successfully subscribed a resource */
420 if (doing_observe && !obs_started &&
421 coap_check_option(received, COAP_OPTION_OBSERVE, &opt_iter)) {
422 coap_log_debug("observation relationship established, set timeout to %d\n",
423 obs_seconds);
424 obs_started = 1;
425 obs_ms = obs_seconds * 1000;
426 obs_ms_reset = 1;
427 }
428
429 if (coap_get_data_large(received, &len, &databuf, &offset, &total)) {
430 append_to_output(databuf, len);
431 if ((len + offset == total) && add_nl)
432 append_to_output((const uint8_t *)"\n", 1);
433 }
434
435 /* Check if Block2 option is set */
436 block_opt = coap_check_option(received, COAP_OPTION_BLOCK2, &opt_iter);
437 if (!single_block_requested && block_opt) { /* handle Block2 */
438
439 /* TODO: check if we are looking at the correct block number */
440 if (coap_opt_block_num(block_opt) == 0) {
441 /* See if observe is set in first response */
442 ready = doing_observe ? coap_check_option(received,
443 COAP_OPTION_OBSERVE, &opt_iter) == NULL : 1;
444 }
445 if (COAP_OPT_BLOCK_MORE(block_opt)) {
446 doing_getting_block = 1;
447 } else {
448 doing_getting_block = 0;
449 if (!is_mcast)
450 track_flush_token(&token, 0);
451 }
452 return COAP_RESPONSE_OK;
453 }
454 } else { /* no 2.05 */
455 /* check if an error was signaled and output payload if so */
456 if (COAP_RESPONSE_CLASS(rcv_code) >= 4) {
457 fprintf(stderr, "%d.%02d", COAP_RESPONSE_CLASS(rcv_code),
458 rcv_code & 0x1F);
459 if (coap_get_data_large(received, &len, &databuf, &offset, &total)) {
460 fprintf(stderr, " ");
461 while (len--) {
462 fprintf(stderr, "%c", isprint(*databuf) ? *databuf : '.');
463 databuf++;
464 }
465 }
466 fprintf(stderr, "\n");
467 track_flush_token(&token, 1);
468 }
469
470 }
471 if (!is_mcast)
472 track_flush_token(&token, 0);
473
474 /* our job is done, we can exit at any time */
475 ready = doing_observe ? coap_check_option(received,
476 COAP_OPTION_OBSERVE, &opt_iter) == NULL : 1;
477 return COAP_RESPONSE_OK;
478 }
479
480 static void
usage(const char * program,const char * version)481 usage(const char *program, const char *version) {
482 const char *p;
483 char buffer[120];
484 const char *lib_build = coap_package_build();
485
486 p = strrchr(program, '/');
487 if (p)
488 program = ++p;
489
490 fprintf(stderr, "%s v%s -- a small CoAP implementation\n"
491 "Copyright (C) 2010-2023 Olaf Bergmann <bergmann@tzi.org> and others\n\n"
492 "Build: %s\n"
493 "%s\n"
494 , program, version, lib_build,
495 coap_string_tls_version(buffer, sizeof(buffer)));
496 fprintf(stderr, "%s\n", coap_string_tls_support(buffer, sizeof(buffer)));
497 fprintf(stderr, "\n"
498 "Usage: %s [-a addr] [-b [num,]size] [-e text] [-f file] [-l loss]\n"
499 "\t\t[-m method] [-o file] [-p port] [-r] [-s duration] [-t type]\n"
500 "\t\t[-v num] [-w] [-A type] [-B seconds]\n"
501 "\t\t[-E oscore_conf_file[,seq_file]] [-G count] [-H hoplimit]\n"
502 "\t\t[-K interval] [-N] [-O num,text] [-P scheme://address[:port]\n"
503 "\t\t[-T token] [-U] [-V num] [-X size]\n"
504 "\t\t[[-h match_hint_file] [-k key] [-u user]]\n"
505 "\t\t[[-c certfile] [-j keyfile] [-n] [-C cafile]\n"
506 "\t\t[-J pkcs11_pin] [-M raw_pk] [-R trust_casfile]\n"
507 "\t\t[-S match_pki_sni_file]] URI\n"
508 "\tURI can be an absolute URI or a URI prefixed with scheme and host\n\n"
509 "General Options\n"
510 "\t-a addr\t\tThe local interface address to use\n"
511 "\t-b [num,]size\tBlock size to be used in GET/PUT/POST requests\n"
512 "\t \t\t(value must be a multiple of 16 not larger than 1024)\n"
513 "\t \t\tIf num is present, the request chain will start at\n"
514 "\t \t\tblock num\n"
515 "\t-e text\t\tInclude text as payload (use percent-encoding for\n"
516 "\t \t\tnon-ASCII characters)\n"
517 "\t-f file\t\tFile to send with PUT/POST (use '-' for STDIN)\n"
518 "\t-l list\t\tFail to send some datagrams specified by a comma\n"
519 "\t \t\tseparated list of numbers or number ranges\n"
520 "\t \t\t(for debugging only)\n"
521 "\t-l loss%%\tRandomly fail to send datagrams with the specified\n"
522 "\t \t\tprobability - 100%% all datagrams, 0%% no datagrams\n"
523 "\t-m method\tRequest method (get|put|post|delete|fetch|patch|ipatch),\n"
524 "\t \t\tdefault is 'get'\n"
525 "\t-o file\t\tOutput received data to this file (use '-' for STDOUT)\n"
526 "\t-p port\t\tSend from the specified port\n"
527 "\t-r \t\tUse reliable protocol (TCP or TLS); requires TCP support\n"
528 "\t-s duration\tSubscribe to / Observe resource for given duration\n"
529 "\t \t\tin seconds\n"
530 "\t-t type\t\tContent format for given resource for PUT/POST\n"
531 "\t-v num \t\tVerbosity level (default 4, maximum is 8) for general\n"
532 "\t \t\tCoAP logging\n"
533 "\t-w \t\tAppend a newline to received data\n"
534 "\t-A type\t\tAccepted media type\n"
535 "\t-B seconds\tBreak operation after waiting given seconds\n"
536 "\t \t\t(default is %d)\n"
537 "\t-E oscore_conf_file[,seq_file]\n"
538 "\t \t\toscore_conf_file contains OSCORE configuration. See\n"
539 "\t \t\tcoap-oscore-conf(5) for definitions.\n"
540 "\t \t\tOptional seq_file is used to save the current transmit\n"
541 "\t \t\tsequence number, so on restart sequence numbers continue\n"
542 "\t-G count\tRepeat the Request 'count' times with a second delay\n"
543 "\t \t\tbetween each one. Must have a value between 1 and 255\n"
544 "\t \t\tinclusive. Default is '1'\n"
545 "\t-H hoplimit\tSet the Hop Limit count to hoplimit for proxies. Must\n"
546 "\t \t\thave a value between 1 and 255 inclusive.\n"
547 "\t \t\tDefault is '16'\n"
548 "\t-K interval\tSend a ping after interval seconds of inactivity\n"
549 "\t-L value\tSum of one or more COAP_BLOCK_* flag valuess for block\n"
550 "\t \t\thandling methods. Default is 1 (COAP_BLOCK_USE_LIBCOAP)\n"
551 "\t \t\t(Sum of one or more of 1,2 and 16)\n"
552 "\t-N \t\tSend NON-confirmable message\n"
553 "\t-O num,text\tAdd option num with contents text to request. If the\n"
554 "\t \t\ttext begins with 0x, then the hex text (two [0-9a-f] per\n"
555 "\t \t\tbyte) is converted to binary data\n"
556 "\t-P scheme://address[:port]\n"
557 "\t \t\tScheme, address and optional port to define how to\n"
558 "\t \t\tconnect to a CoAP proxy (automatically adds Proxy-Uri\n"
559 "\t \t\toption to request) to forward the request to.\n"
560 "\t \t\tScheme is one of coap, coaps, coap+tcp and coaps+tcp\n"
561 "\t-T token\tDefine the initial starting token (up to 24 characters)\n"
562 "\t-U \t\tNever include Uri-Host or Uri-Port options\n"
563 "\t-V num \t\tVerbosity level (default 3, maximum is 7) for (D)TLS\n"
564 "\t \t\tlibrary logging\n"
565 "\t-X size\t\tMaximum message size to use for TCP based connections\n"
566 "\t \t\t(default is 8388864). Maximum value of 2^32 -1\n"
567 ,program, wait_seconds);
568 fprintf(stderr,
569 "PSK Options (if supported by underlying (D)TLS library)\n"
570 "\t-h match_hint_file\n"
571 "\t \t\tThis is a file that contains one or more lines of\n"
572 "\t \t\treceived Identity Hints to match to use different\n"
573 "\t \t\tuser identity and associated pre-shared key (PSK) (comma\n"
574 "\t \t\tseparated) instead of the '-k key' and '-u user'\n"
575 "\t \t\toptions. E.g., per line\n"
576 "\t \t\t hint_to_match,use_user,with_key\n"
577 "\t \t\tNote: -k and -u still need to be defined for the default\n"
578 "\t \t\tin case there is no match\n"
579 "\t-k key \t\tPre-shared key for the specified user identity\n"
580 "\t-u user\t\tUser identity to send for pre-shared key mode\n"
581 "PKI Options (if supported by underlying (D)TLS library)\n"
582 "\tNote: If any one of '-c certfile', '-j keyfile' or '-C cafile' is in\n"
583 "\tPKCS11 URI naming format (pkcs11: prefix), then any remaining non\n"
584 "\tPKCS11 URI file definitions have to be in DER, not PEM, format.\n"
585 "\tOtherwise all of '-c certfile', '-j keyfile' or '-C cafile' are in\n"
586 "\tPEM format.\n\n"
587 "\t-c certfile\tPEM file or PKCS11 URI for the certificate. The private\n"
588 "\t \t\tkey can also be in the PEM file, or has the same PKCS11\n"
589 "\t \t\tURI. If not, the private key is defined by '-j keyfile'\n"
590 "\t-j keyfile\tPEM file or PKCS11 URI for the private key for the\n"
591 "\t \t\tcertificate in '-c certfile' if the parameter is\n"
592 "\t \t\tdifferent from certfile in '-c certfile'\n"
593 "\t-n \t\tDisable remote peer certificate checking\n"
594 "\t-C cafile\tPEM file or PKCS11 URI for the CA certificate that was\n"
595 "\t \t\tused to sign the server certfile. Ideally the client\n"
596 "\t \t\tcertificate should be signed by the same CA so that\n"
597 "\t \t\tmutual authentication can take place. The contents of\n"
598 "\t \t\tcafile are added to the trusted store of root CAs.\n"
599 "\t \t\tUsing the -C or -R options will trigger the\n"
600 "\t \t\tvalidation of the server certificate unless overridden\n"
601 "\t \t\tby the -n option\n"
602 "\t-J pkcs11_pin\tThe user pin to unlock access to the PKCS11 token\n"
603 "\t-M rpk_file\tRaw Public Key (RPK) PEM file or PKCS11 URI that\n"
604 "\t \t\tcontains both PUBLIC KEY and PRIVATE KEY or just\n"
605 "\t \t\tEC PRIVATE KEY. (GnuTLS and TinyDTLS(PEM) support only).\n"
606 "\t \t\t'-C cafile' or '-R trust_casfile' are not required\n"
607 "\t-R trust_casfile\n"
608 "\t \t\tPEM file containing the set of trusted root CAs\n"
609 "\t \t\tthat are to be used to validate the server certificate.\n"
610 "\t \t\tAlternatively, this can point to a directory containing\n"
611 "\t \t\ta set of CA PEM files.\n"
612 "\t \t\tUsing '-R trust_casfile' disables common CA mutual\n"
613 "\t \t\tauthentication which can only be done by using\n"
614 "\t \t\t'-C cafile'.\n"
615 "\t \t\tUsing the -C or -R options will will trigger the\n"
616 "\t \t\tvalidation of the server certificate unless overridden\n"
617 "\t \t\tby the -n option\n"
618 );
619 fprintf(stderr,
620 "Examples:\n"
621 "\tcoap-client -m get coap://[::1]/\n"
622 "\tcoap-client -m get coap://[::1]/.well-known/core\n"
623 "\tcoap-client -m get coap+tcp://[::1]/.well-known/core\n"
624 "\tcoap-client -m get coap://%%2Funix%%2Fdomain%%2Fpath%%2Fdgram/.well-known/core\n"
625 "\tcoap-client -m get coap+tcp://%%2Funix%%2Fdomain%%2Fpath%%2Fstream/.well-known/core\n"
626 "\tcoap-client -m get coaps://[::1]/.well-known/core\n"
627 "\tcoap-client -m get coaps+tcp://[::1]/.well-known/core\n"
628 "\tcoap-client -m get coaps://%%2Funix%%2Fdomain%%2Fpath%%2Fdtls/.well-known/core\n"
629 "\tcoap-client -m get coaps+tcp://%%2Funix%%2Fdomain%%2Fpath%%2Ftls/.well-known/core\n"
630 "\tcoap-client -m get -T cafe coap://[::1]/time\n"
631 "\techo -n 1000 | coap-client -m put -T cafe coap://[::1]/time -f -\n"
632 );
633 }
634
635 typedef struct {
636 unsigned char code;
637 const char *media_type;
638 } content_type_t;
639
640 static void
cmdline_content_type(char * arg,uint16_t key)641 cmdline_content_type(char *arg, uint16_t key) {
642 static content_type_t content_types[] = {
643 { 0, "plain" },
644 { 0, "text/plain" },
645 { 40, "link" },
646 { 40, "link-format" },
647 { 40, "application/link-format" },
648 { 41, "xml" },
649 { 41, "application/xml" },
650 { 42, "binary" },
651 { 42, "octet-stream" },
652 { 42, "application/octet-stream" },
653 { 47, "exi" },
654 { 47, "application/exi" },
655 { 50, "json" },
656 { 50, "application/json" },
657 { 60, "cbor" },
658 { 60, "application/cbor" },
659 { 255, NULL }
660 };
661 coap_optlist_t *node;
662 unsigned char i;
663 uint16_t value;
664 uint8_t buf[4];
665
666 if (isdigit((int)arg[0])) {
667 value = atoi(arg);
668 } else {
669 for (i=0;
670 content_types[i].media_type &&
671 strncmp(arg, content_types[i].media_type, strlen(arg)) != 0 ;
672 ++i)
673 ;
674
675 if (content_types[i].media_type) {
676 value = content_types[i].code;
677 } else {
678 coap_log_warn("W: unknown content-format '%s'\n",arg);
679 return;
680 }
681 }
682
683 node = coap_new_optlist(key, coap_encode_var_safe(buf, sizeof(buf), value), buf);
684 if (node) {
685 coap_insert_optlist(&optlist, node);
686 }
687 }
688
689 static int
cmdline_hop_limit(char * arg)690 cmdline_hop_limit(char *arg) {
691 coap_optlist_t *node;
692 uint32_t value;
693 uint8_t buf[4];
694
695 value = strtol(arg, NULL, 10);
696 if (value < 1 || value > 255) {
697 return 0;
698 }
699 node = coap_new_optlist(COAP_OPTION_HOP_LIMIT, coap_encode_var_safe(buf, sizeof(buf), value), buf);
700 if (node) {
701 coap_insert_optlist(&optlist, node);
702 }
703 return 1;
704 }
705
706 static uint8_t *
read_file_mem(const char * filename,size_t * length)707 read_file_mem(const char *filename, size_t *length) {
708 FILE *f;
709 uint8_t *buf;
710 struct stat statbuf;
711
712 *length = 0;
713 if (!filename || !(f = fopen(filename, "r")))
714 return NULL;
715
716 if (fstat(fileno(f), &statbuf) == -1) {
717 fclose(f);
718 return NULL;
719 }
720
721 buf = coap_malloc(statbuf.st_size+1);
722 if (!buf) {
723 fclose(f);
724 return NULL;
725 }
726
727 if (fread(buf, 1, statbuf.st_size, f) != (size_t)statbuf.st_size) {
728 fclose(f);
729 coap_free(buf);
730 return NULL;
731 }
732 buf[statbuf.st_size] = '\000';
733 *length = (size_t)(statbuf.st_size + 1);
734 fclose(f);
735 return buf;
736 }
737
738 static FILE *oscore_seq_num_fp = NULL;
739 static const char *oscore_conf_file = NULL;
740 static const char *oscore_seq_save_file = NULL;
741
742 static int
oscore_save_seq_num(uint64_t sender_seq_num,void * param COAP_UNUSED)743 oscore_save_seq_num(uint64_t sender_seq_num, void *param COAP_UNUSED) {
744 if (oscore_seq_num_fp) {
745 rewind(oscore_seq_num_fp);
746 fprintf(oscore_seq_num_fp, "%" PRIu64 "\n", sender_seq_num);
747 fflush(oscore_seq_num_fp);
748 }
749 return 1;
750 }
751
752 static coap_oscore_conf_t *
get_oscore_conf(void)753 get_oscore_conf(void) {
754 uint8_t *buf;
755 size_t length;
756 coap_str_const_t file_mem;
757 uint64_t start_seq_num = 0;
758
759 /* Need a rw var to free off later and file_mem.s is a const */
760 buf = read_file_mem(oscore_conf_file, &length);
761 if (buf == NULL) {
762 fprintf(stderr, "OSCORE configuration file error: %s\n", oscore_conf_file);
763 return NULL;
764 }
765 file_mem.s = buf;
766 file_mem.length = length;
767 if (oscore_seq_save_file) {
768 oscore_seq_num_fp = fopen(oscore_seq_save_file, "r+");
769 if (oscore_seq_num_fp == NULL) {
770 /* Try creating it */
771 oscore_seq_num_fp = fopen(oscore_seq_save_file, "w+");
772 if (oscore_seq_num_fp == NULL) {
773 fprintf(stderr, "OSCORE save restart info file error: %s\n",
774 oscore_seq_save_file);
775 return NULL;
776 }
777 }
778 if (fscanf(oscore_seq_num_fp, "%" PRIu64, &start_seq_num) != 1) {
779 /* Must be empty */
780 start_seq_num = 0;
781 }
782 }
783 oscore_conf = coap_new_oscore_conf(file_mem,
784 oscore_save_seq_num,
785 NULL, start_seq_num);
786 coap_free(buf);
787 if (oscore_conf == NULL) {
788 fprintf(stderr, "OSCORE configuration file error: %s\n", oscore_conf_file);
789 return NULL;
790 }
791 return oscore_conf;
792 }
793
794 static int
cmdline_oscore(char * arg)795 cmdline_oscore(char *arg) {
796 if (coap_oscore_is_supported()) {
797 char *sep = strchr(arg, ',');
798
799 if (sep)
800 *sep = '\000';
801 oscore_conf_file = arg;
802
803 if (sep) {
804 sep++;
805 oscore_seq_save_file = sep;
806 }
807 return 1;
808 }
809 fprintf(stderr, "OSCORE support not enabled\n");
810 return 0;
811 }
812
813 /**
814 * Sets global URI options according to the URI passed as @p arg.
815 * This function returns 0 on success or -1 on error.
816 *
817 * @param arg The URI string.
818 * @param create_uri_opts Flags that indicate whether Uri-Host and
819 * Uri-Port should be suppressed.
820 * @return 0 on success, -1 otherwise
821 */
822 static int
cmdline_uri(char * arg)823 cmdline_uri(char *arg) {
824
825 if (!proxy_scheme_option && proxy.host.length) {
826 /* create Proxy-Uri from argument */
827 size_t len = strlen(arg);
828 if (len > 1034) {
829 coap_log_err("Absolute URI length must be <= 1034 bytes for a proxy\n");
830 return -1;
831 }
832
833 coap_insert_optlist(&optlist,
834 coap_new_optlist(COAP_OPTION_PROXY_URI,
835 len,
836 (unsigned char *)arg));
837
838 } else { /* split arg into Uri-* options */
839 if (coap_split_uri((unsigned char *)arg, strlen(arg), &uri) < 0) {
840 coap_log_err("invalid CoAP URI\n");
841 return -1;
842 }
843
844 /* Need to special case use of reliable */
845 if (uri.scheme == COAP_URI_SCHEME_COAPS && reliable) {
846 if (!coap_tls_is_supported()) {
847 coap_log_emerg("coaps+tcp URI scheme not supported in this version of libcoap\n");
848 return -1;
849 } else {
850 uri.scheme = COAP_URI_SCHEME_COAPS_TCP;
851 }
852 }
853
854 if (uri.scheme == COAP_URI_SCHEME_COAP && reliable) {
855 if (!coap_tcp_is_supported()) {
856 coap_log_emerg("coap+tcp URI scheme not supported in this version of libcoap\n");
857 return -1;
858 } else {
859 uri.scheme = COAP_URI_SCHEME_COAP_TCP;
860 }
861 }
862 }
863 return 0;
864 }
865
866 static int
cmdline_blocksize(char * arg)867 cmdline_blocksize(char *arg) {
868 uint16_t size;
869
870 again:
871 size = 0;
872 while (*arg && *arg != ',')
873 size = size * 10 + (*arg++ - '0');
874
875 if (*arg == ',') {
876 arg++;
877 block.num = size;
878 if (size != 0) {
879 /* Random access selection - only handle single response */
880 single_block_requested = 1;
881 }
882 goto again;
883 }
884
885 if (size < 16) {
886 coap_log_warn("Minimum block size is 16\n");
887 return 0;
888 } else if (size > 1024) {
889 coap_log_warn("Maximum block size is 1024\n");
890 return 0;
891 } else if ((size % 16) != 0) {
892 coap_log_warn("Block size %u is not a multiple of 16\n", size);
893 return 0;
894 }
895 if (size)
896 block.szx = (coap_fls(size >> 4) - 1) & 0x07;
897
898 flags |= FLAGS_BLOCK;
899 return 1;
900 }
901
902 /* Called after processing the options from the commandline to set
903 * Block1, Block2, Q-Block1 or Q-Block2 depending on method. */
904 static void
set_blocksize(void)905 set_blocksize(void) {
906 static unsigned char buf[4]; /* hack: temporarily take encoded bytes */
907 uint16_t opt;
908 unsigned int opt_length;
909
910 if (method != COAP_REQUEST_DELETE) {
911 if (method == COAP_REQUEST_GET || method == COAP_REQUEST_FETCH) {
912 if (coap_q_block_is_supported() && block_mode & COAP_BLOCK_TRY_Q_BLOCK)
913 opt = COAP_OPTION_Q_BLOCK2;
914 else
915 opt = COAP_OPTION_BLOCK2;
916 } else {
917 if (coap_q_block_is_supported() && block_mode & COAP_BLOCK_TRY_Q_BLOCK)
918 opt = COAP_OPTION_Q_BLOCK1;
919 else
920 opt = COAP_OPTION_BLOCK1;
921 }
922
923 block.m = (opt == COAP_OPTION_BLOCK1 || opt == COAP_OPTION_Q_BLOCK1) &&
924 ((1ull << (block.szx + 4)) < payload.length);
925
926 opt_length = coap_encode_var_safe(buf, sizeof(buf),
927 (block.num << 4 | block.m << 3 | block.szx));
928
929 coap_insert_optlist(&optlist, coap_new_optlist(opt, opt_length, buf));
930 }
931 }
932
933 static void
cmdline_subscribe(char * arg)934 cmdline_subscribe(char *arg) {
935 uint8_t buf[4];
936
937 obs_seconds = atoi(arg);
938 coap_insert_optlist(&optlist,
939 coap_new_optlist(COAP_OPTION_OBSERVE,
940 coap_encode_var_safe(buf, sizeof(buf),
941 COAP_OBSERVE_ESTABLISH), buf)
942 );
943 doing_observe = 1;
944 }
945
946 static int
cmdline_proxy(char * arg)947 cmdline_proxy(char *arg) {
948 if (coap_split_uri((unsigned char *)arg, strlen(arg), &proxy) < 0 ||
949 proxy.path.length != 0 || proxy.query.length != 0) {
950 coap_log_err("invalid CoAP Proxy definition\n");
951 return 0;
952 }
953 return 1;
954 }
955
956 static inline void
cmdline_token(char * arg)957 cmdline_token(char *arg) {
958 the_token.length = min(sizeof(_token_data), strlen(arg));
959 if (the_token.length > 0) {
960 memcpy((char *)the_token.s, arg, the_token.length);
961 }
962 }
963
964 /**
965 * Utility function to convert a hex digit to its corresponding
966 * numerical value.
967 *
968 * param c The hex digit to convert. Must be in [0-9A-Fa-f].
969 *
970 * return The numerical representation of @p c.
971 */
972 static uint8_t
hex2char(char c)973 hex2char(char c) {
974 assert(isxdigit(c));
975 if ('a' <= c && c <= 'f')
976 return c - 'a' + 10;
977 else if ('A' <= c && c <= 'F')
978 return c - 'A' + 10;
979 else
980 return c - '0';
981 }
982
983 /**
984 * Converts the sequence of hex digits in src to a sequence of bytes.
985 *
986 * This function returns the number of bytes that have been written to
987 * @p dst.
988 *
989 * param[in] src The null-terminated hex string to convert.
990 * param[out] dst Conversion result.
991 *
992 * return The length of @p dst.
993 */
994 static size_t
convert_hex_string(const char * src,uint8_t * dst)995 convert_hex_string(const char *src, uint8_t *dst) {
996 uint8_t *p = dst;
997 while (isxdigit((int)src[0]) && isxdigit((int)src[1])) {
998 *p++ = (hex2char(src[0]) << 4) + hex2char(src[1]);
999 src += 2;
1000 }
1001 if (src[0] != '\0') { /* error in hex input */
1002 coap_log_warn("invalid hex string in option '%s'\n", src);
1003 }
1004 return p - dst;
1005 }
1006
1007 static void
cmdline_option(char * arg)1008 cmdline_option(char *arg) {
1009 unsigned int num = 0;
1010
1011 while (*arg && *arg != ',') {
1012 num = num * 10 + (*arg - '0');
1013 ++arg;
1014 }
1015 if (*arg == ',')
1016 ++arg;
1017
1018 /* read hex string when arg starts with "0x" */
1019 if (arg[0] == '0' && arg[1] == 'x') {
1020 /* As the command line option is part of our environment we can do
1021 * the conversion in place. */
1022 size_t len = convert_hex_string(arg + 2, (uint8_t *)arg);
1023
1024 /* On success, 2 * len + 2 == strlen(arg) */
1025 coap_insert_optlist(&optlist,
1026 coap_new_optlist(num, len, (unsigned char *)arg));
1027 } else { /* null-terminated character string */
1028 coap_insert_optlist(&optlist,
1029 coap_new_optlist(num, strlen(arg), (unsigned char *)arg));
1030 }
1031 if (num == COAP_OPTION_PROXY_SCHEME) {
1032 proxy_scheme_option = 1;
1033 if (strcasecmp(arg, "coaps+tcp") == 0) {
1034 proxy.scheme = COAP_URI_SCHEME_COAPS_TCP;
1035 proxy.port = COAPS_DEFAULT_PORT;
1036 } else if (strcasecmp(arg, "coap+tcp") == 0) {
1037 proxy.scheme = COAP_URI_SCHEME_COAP_TCP;
1038 proxy.port = COAP_DEFAULT_PORT;
1039 } else if (strcasecmp(arg, "coaps") == 0) {
1040 proxy.scheme = COAP_URI_SCHEME_COAPS;
1041 proxy.port = COAPS_DEFAULT_PORT;
1042 } else if (strcasecmp(arg, "coap") == 0) {
1043 proxy.scheme = COAP_URI_SCHEME_COAP;
1044 proxy.port = COAP_DEFAULT_PORT;
1045 } else {
1046 coap_log_warn("%s is not a supported CoAP Proxy-Scheme\n", arg);
1047 }
1048 }
1049 if (num == COAP_OPTION_URI_HOST) {
1050 uri_host_option = 1;
1051 }
1052 }
1053
1054 /**
1055 * Calculates decimal value from hexadecimal ASCII character given in
1056 * @p c. The caller must ensure that @p c actually represents a valid
1057 * heaxdecimal character, e.g. with isxdigit(3).
1058 *
1059 * @hideinitializer
1060 */
1061 #define hexchar_to_dec(c) ((c) & 0x40 ? ((c) & 0x0F) + 9 : ((c) & 0x0F))
1062
1063 /**
1064 * Decodes percent-encoded characters while copying the string @p seg
1065 * of size @p length to @p buf. The caller of this function must
1066 * ensure that the percent-encodings are correct (i.e. the character
1067 * '%' is always followed by two hex digits. and that @p buf provides
1068 * sufficient space to hold the result. This function is supposed to
1069 * be called by make_decoded_option() only.
1070 *
1071 * @param seg The segment to decode and copy.
1072 * @param length Length of @p seg.
1073 * @param buf The result buffer.
1074 */
1075 static void
decode_segment(const uint8_t * seg,size_t length,unsigned char * buf)1076 decode_segment(const uint8_t *seg, size_t length, unsigned char *buf) {
1077
1078 while (length--) {
1079
1080 if (*seg == '%') {
1081 *buf = (hexchar_to_dec(seg[1]) << 4) + hexchar_to_dec(seg[2]);
1082
1083 seg += 2;
1084 length -= 2;
1085 } else {
1086 *buf = *seg;
1087 }
1088
1089 ++buf;
1090 ++seg;
1091 }
1092 }
1093
1094 /**
1095 * Runs through the given path (or query) segment and checks if
1096 * percent-encodings are correct. This function returns @c -1 on error
1097 * or the length of @p s when decoded.
1098 */
1099 static int
check_segment(const uint8_t * s,size_t length)1100 check_segment(const uint8_t *s, size_t length) {
1101
1102 int n = 0;
1103
1104 while (length) {
1105 if (*s == '%') {
1106 if (length < 2 || !(isxdigit(s[1]) && isxdigit(s[2])))
1107 return -1;
1108
1109 s += 2;
1110 length -= 2;
1111 }
1112
1113 ++s;
1114 ++n;
1115 --length;
1116 }
1117
1118 return n;
1119 }
1120
1121 static int
cmdline_input(char * text,coap_string_t * buf)1122 cmdline_input(char *text, coap_string_t *buf) {
1123 int len;
1124 len = check_segment((unsigned char *)text, strlen(text));
1125
1126 if (len < 0)
1127 return 0;
1128
1129 buf->s = (unsigned char *)coap_malloc(len);
1130 if (!buf->s)
1131 return 0;
1132
1133 buf->length = len;
1134 decode_segment((unsigned char *)text, strlen(text), buf->s);
1135 return 1;
1136 }
1137
1138 static int
cmdline_input_from_file(char * filename,coap_string_t * buf)1139 cmdline_input_from_file(char *filename, coap_string_t *buf) {
1140 FILE *inputfile = NULL;
1141 ssize_t len;
1142 int result = 1;
1143 struct stat statbuf;
1144
1145 if (!filename || !buf)
1146 return 0;
1147
1148 if (filename[0] == '-' && !filename[1]) { /* read from stdin */
1149 buf->length = 20000;
1150 buf->s = (unsigned char *)coap_malloc(buf->length);
1151 if (!buf->s)
1152 return 0;
1153
1154 inputfile = stdin;
1155 } else {
1156 /* read from specified input file */
1157 inputfile = fopen(filename, "r");
1158 if (!inputfile) {
1159 perror("cmdline_input_from_file: fopen");
1160 return 0;
1161 }
1162
1163 if (fstat(fileno(inputfile), &statbuf) < 0) {
1164 perror("cmdline_input_from_file: stat");
1165 fclose(inputfile);
1166 return 0;
1167 }
1168
1169 buf->length = statbuf.st_size;
1170 buf->s = (unsigned char *)coap_malloc(buf->length);
1171 if (!buf->s) {
1172 fclose(inputfile);
1173 return 0;
1174 }
1175 }
1176
1177 len = fread(buf->s, 1, buf->length, inputfile);
1178
1179 if (len < 0 || ((size_t)len < buf->length)) {
1180 if (ferror(inputfile) != 0) {
1181 perror("cmdline_input_from_file: fread");
1182 coap_free(buf->s);
1183 buf->length = 0;
1184 buf->s = NULL;
1185 result = 0;
1186 } else {
1187 buf->length = len;
1188 }
1189 }
1190
1191 if (inputfile != stdin)
1192 fclose(inputfile);
1193
1194 return result;
1195 }
1196
1197 static method_t
cmdline_method(char * arg)1198 cmdline_method(char *arg) {
1199 static const char *methods[] =
1200 { 0, "get", "post", "put", "delete", "fetch", "patch", "ipatch", 0};
1201 unsigned char i;
1202
1203 for (i=1; methods[i] && strcasecmp(arg,methods[i]) != 0 ; ++i)
1204 ;
1205
1206 return i; /* note that we do not prevent illegal methods */
1207 }
1208
1209 static ssize_t
cmdline_read_user(char * arg,unsigned char ** buf,size_t maxlen)1210 cmdline_read_user(char *arg, unsigned char **buf, size_t maxlen) {
1211 size_t len = strnlen(arg, maxlen);
1212 if (len) {
1213 *buf = (unsigned char *)arg;
1214 /* len is the size or less, so 0 terminate to maxlen */
1215 (*buf)[len] = '\000';
1216 }
1217 /* 0 length Identity is valid */
1218 return len;
1219 }
1220
1221 static ssize_t
cmdline_read_key(char * arg,unsigned char ** buf,size_t maxlen)1222 cmdline_read_key(char *arg, unsigned char **buf, size_t maxlen) {
1223 size_t len = strnlen(arg, maxlen);
1224 if (len) {
1225 *buf = (unsigned char *)arg;
1226 return len;
1227 }
1228 /* Need at least one byte for the pre-shared key */
1229 coap_log_crit("Invalid Pre-Shared Key specified\n");
1230 return -1;
1231 }
1232
1233 static int
cmdline_read_hint_check(const char * arg)1234 cmdline_read_hint_check(const char *arg) {
1235 FILE *fp = fopen(arg, "r");
1236 static char tmpbuf[256];
1237 if (fp == NULL) {
1238 coap_log_err("Hint file: %s: Unable to open\n", arg);
1239 return 0;
1240 }
1241 while (fgets(tmpbuf, sizeof(tmpbuf), fp) != NULL) {
1242 char *cp = tmpbuf;
1243 char *tcp = strchr(cp, '\n');
1244
1245 if (tmpbuf[0] == '#')
1246 continue;
1247 if (tcp)
1248 *tcp = '\000';
1249
1250 tcp = strchr(cp, ',');
1251 if (tcp) {
1252 ih_def_t *new_ih_list;
1253 new_ih_list = realloc(valid_ihs.ih_list,
1254 (valid_ihs.count + 1)*sizeof(valid_ihs.ih_list[0]));
1255 if (new_ih_list == NULL) {
1256 break;
1257 }
1258 valid_ihs.ih_list = new_ih_list;
1259 valid_ihs.ih_list[valid_ihs.count].hint_match = strndup(cp, tcp-cp);
1260 cp = tcp+1;
1261 tcp = strchr(cp, ',');
1262 if (tcp) {
1263 valid_ihs.ih_list[valid_ihs.count].new_identity =
1264 coap_new_bin_const((const uint8_t *)cp, tcp-cp);
1265 cp = tcp+1;
1266 valid_ihs.ih_list[valid_ihs.count].new_key =
1267 coap_new_bin_const((const uint8_t *)cp, strlen(cp));
1268 valid_ihs.count++;
1269 } else {
1270 /* Badly formatted */
1271 free(valid_ihs.ih_list[valid_ihs.count].hint_match);
1272 }
1273 }
1274 }
1275 fclose(fp);
1276 return valid_ihs.count > 0;
1277 }
1278
1279 static int
verify_cn_callback(const char * cn,const uint8_t * asn1_public_cert COAP_UNUSED,size_t asn1_length COAP_UNUSED,coap_session_t * session COAP_UNUSED,unsigned depth,int validated COAP_UNUSED,void * arg COAP_UNUSED)1280 verify_cn_callback(const char *cn,
1281 const uint8_t *asn1_public_cert COAP_UNUSED,
1282 size_t asn1_length COAP_UNUSED,
1283 coap_session_t *session COAP_UNUSED,
1284 unsigned depth,
1285 int validated COAP_UNUSED,
1286 void *arg COAP_UNUSED) {
1287 coap_log_info("CN '%s' presented by server (%s)\n",
1288 cn, depth ? "CA" : "Certificate");
1289 return 1;
1290 }
1291
1292 static const coap_dtls_cpsk_info_t *
verify_ih_callback(coap_str_const_t * hint,coap_session_t * c_session COAP_UNUSED,void * arg)1293 verify_ih_callback(coap_str_const_t *hint,
1294 coap_session_t *c_session COAP_UNUSED,
1295 void *arg) {
1296 coap_dtls_cpsk_info_t *psk_info = (coap_dtls_cpsk_info_t *)arg;
1297 char lhint[COAP_DTLS_HINT_LENGTH];
1298 static coap_dtls_cpsk_info_t psk_identity_info;
1299 size_t i;
1300
1301 snprintf(lhint, sizeof(lhint), "%.*s", (int)hint->length, hint->s);
1302 coap_log_info("Identity Hint '%s' provided\n", lhint);
1303
1304 /* Test for hint to possibly change identity + key */
1305 for (i = 0; i < valid_ihs.count; i++) {
1306 if (strcmp(lhint, valid_ihs.ih_list[i].hint_match) == 0) {
1307 /* Preset */
1308 psk_identity_info = *psk_info;
1309 if (valid_ihs.ih_list[i].new_key) {
1310 psk_identity_info.key = *valid_ihs.ih_list[i].new_key;
1311 }
1312 if (valid_ihs.ih_list[i].new_identity) {
1313 psk_identity_info.identity = *valid_ihs.ih_list[i].new_identity;
1314 }
1315 coap_log_info("Switching to using '%s' identity + '%s' key\n",
1316 psk_identity_info.identity.s, psk_identity_info.key.s);
1317 return &psk_identity_info;
1318 }
1319 }
1320 /* Just use the defined key for now as passed in by arg */
1321 return psk_info;
1322 }
1323
1324 static coap_dtls_pki_t *
setup_pki(coap_context_t * ctx)1325 setup_pki(coap_context_t *ctx) {
1326 static coap_dtls_pki_t dtls_pki;
1327 static char client_sni[256];
1328
1329 /* If general root CAs are defined */
1330 if (root_ca_file) {
1331 struct stat stbuf;
1332 if ((stat(root_ca_file, &stbuf) == 0) && S_ISDIR(stbuf.st_mode)) {
1333 coap_context_set_pki_root_cas(ctx, NULL, root_ca_file);
1334 } else {
1335 coap_context_set_pki_root_cas(ctx, root_ca_file, NULL);
1336 }
1337 }
1338
1339 memset(client_sni, 0, sizeof(client_sni));
1340 memset(&dtls_pki, 0, sizeof(dtls_pki));
1341 dtls_pki.version = COAP_DTLS_PKI_SETUP_VERSION;
1342 if (ca_file || root_ca_file) {
1343 /*
1344 * Add in additional certificate checking.
1345 * This list of enabled can be tuned for the specific
1346 * requirements - see 'man coap_encryption'.
1347 *
1348 * Note: root_ca_file is setup separately using
1349 * coap_context_set_pki_root_cas(), but this is used to define what
1350 * checking actually takes place.
1351 */
1352 dtls_pki.verify_peer_cert = verify_peer_cert;
1353 dtls_pki.check_common_ca = !root_ca_file;
1354 dtls_pki.allow_self_signed = 1;
1355 dtls_pki.allow_expired_certs = 1;
1356 dtls_pki.cert_chain_validation = 1;
1357 dtls_pki.cert_chain_verify_depth = 2;
1358 dtls_pki.check_cert_revocation = 1;
1359 dtls_pki.allow_no_crl = 1;
1360 dtls_pki.allow_expired_crl = 1;
1361 } else if (is_rpk_not_cert) {
1362 dtls_pki.verify_peer_cert = verify_peer_cert;
1363 }
1364 dtls_pki.is_rpk_not_cert = is_rpk_not_cert;
1365 dtls_pki.validate_cn_call_back = verify_cn_callback;
1366 if ((uri.host.length == 3 && memcmp(uri.host.s, "::1", 3) != 0) ||
1367 (uri.host.length == 9 && memcmp(uri.host.s, "127.0.0.1", 9) != 0))
1368 memcpy(client_sni, uri.host.s, min(uri.host.length, sizeof(client_sni)-1));
1369 else
1370 memcpy(client_sni, "localhost", 9);
1371
1372 dtls_pki.client_sni = client_sni;
1373 if ((key_file && strncasecmp(key_file, "pkcs11:", 7) == 0) ||
1374 (cert_file && strncasecmp(cert_file, "pkcs11:", 7) == 0) ||
1375 (ca_file && strncasecmp(ca_file, "pkcs11:", 7) == 0)) {
1376 dtls_pki.pki_key.key_type = COAP_PKI_KEY_PKCS11;
1377 dtls_pki.pki_key.key.pkcs11.public_cert = cert_file;
1378 dtls_pki.pki_key.key.pkcs11.private_key = key_file ?
1379 key_file : cert_file;
1380 dtls_pki.pki_key.key.pkcs11.ca = ca_file;
1381 dtls_pki.pki_key.key.pkcs11.user_pin = pkcs11_pin;
1382 } else if (!is_rpk_not_cert) {
1383 dtls_pki.pki_key.key_type = COAP_PKI_KEY_PEM;
1384 dtls_pki.pki_key.key.pem.public_cert = cert_file;
1385 dtls_pki.pki_key.key.pem.private_key = key_file ? key_file : cert_file;
1386 dtls_pki.pki_key.key.pem.ca_file = ca_file;
1387 } else {
1388 /* Map file into memory */
1389 if (ca_mem == 0 && cert_mem == 0 && key_mem == 0) {
1390 ca_mem = read_file_mem(ca_file, &ca_mem_len);
1391 cert_mem = read_file_mem(cert_file, &cert_mem_len);
1392 key_mem = read_file_mem(key_file, &key_mem_len);
1393 }
1394 dtls_pki.pki_key.key_type = COAP_PKI_KEY_PEM_BUF;
1395 dtls_pki.pki_key.key.pem_buf.ca_cert = ca_mem;
1396 dtls_pki.pki_key.key.pem_buf.public_cert = cert_mem;
1397 dtls_pki.pki_key.key.pem_buf.private_key = key_mem ? key_mem : cert_mem;
1398 dtls_pki.pki_key.key.pem_buf.ca_cert_len = ca_mem_len;
1399 dtls_pki.pki_key.key.pem_buf.public_cert_len = cert_mem_len;
1400 dtls_pki.pki_key.key.pem_buf.private_key_len = key_mem ?
1401 key_mem_len : cert_mem_len;
1402 }
1403 return &dtls_pki;
1404 }
1405
1406 static coap_dtls_cpsk_t *
setup_psk(const uint8_t * identity,size_t identity_len,const uint8_t * key,size_t key_len)1407 setup_psk(const uint8_t *identity,
1408 size_t identity_len,
1409 const uint8_t *key,
1410 size_t key_len) {
1411 static coap_dtls_cpsk_t dtls_psk;
1412 static char client_sni[256];
1413
1414 memset(client_sni, 0, sizeof(client_sni));
1415 memset(&dtls_psk, 0, sizeof(dtls_psk));
1416 dtls_psk.version = COAP_DTLS_CPSK_SETUP_VERSION;
1417 dtls_psk.validate_ih_call_back = verify_ih_callback;
1418 dtls_psk.ih_call_back_arg = &dtls_psk.psk_info;
1419 if (uri.host.length)
1420 memcpy(client_sni, uri.host.s,
1421 min(uri.host.length, sizeof(client_sni) - 1));
1422 else
1423 memcpy(client_sni, "localhost", 9);
1424 dtls_psk.client_sni = client_sni;
1425 dtls_psk.psk_info.identity.s = identity;
1426 dtls_psk.psk_info.identity.length = identity_len;
1427 dtls_psk.psk_info.key.s = key;
1428 dtls_psk.psk_info.key.length = key_len;
1429 return &dtls_psk;
1430 }
1431
1432 static coap_session_t *
open_session(coap_context_t * ctx,coap_proto_t proto,coap_address_t * bind_addr,coap_address_t * dst,const uint8_t * identity,size_t identity_len,const uint8_t * key,size_t key_len)1433 open_session(coap_context_t *ctx,
1434 coap_proto_t proto,
1435 coap_address_t *bind_addr,
1436 coap_address_t *dst,
1437 const uint8_t *identity,
1438 size_t identity_len,
1439 const uint8_t *key,
1440 size_t key_len) {
1441 coap_session_t *session;
1442
1443 if (proto == COAP_PROTO_DTLS || proto == COAP_PROTO_TLS ||
1444 proto == COAP_PROTO_WSS) {
1445 /* Encrypted session */
1446 if (root_ca_file || ca_file || cert_file) {
1447 /* Setup PKI session */
1448 coap_dtls_pki_t *dtls_pki = setup_pki(ctx);
1449 if (doing_oscore) {
1450 session = coap_new_client_session_oscore_pki(ctx, bind_addr, dst,
1451 proto, dtls_pki,
1452 oscore_conf);
1453 } else
1454 session = coap_new_client_session_pki(ctx, bind_addr, dst, proto,
1455 dtls_pki);
1456 } else if (identity || key) {
1457 /* Setup PSK session */
1458 coap_dtls_cpsk_t *dtls_psk = setup_psk(identity, identity_len,
1459 key, key_len);
1460 if (doing_oscore) {
1461 session = coap_new_client_session_oscore_psk(ctx, bind_addr, dst,
1462 proto, dtls_psk,
1463 oscore_conf);
1464 } else
1465 session = coap_new_client_session_psk2(ctx, bind_addr, dst, proto,
1466 dtls_psk);
1467 } else {
1468 /* No PKI or PSK defined, as encrypted, use PKI */
1469 coap_dtls_pki_t *dtls_pki = setup_pki(ctx);
1470 if (doing_oscore) {
1471 session = coap_new_client_session_oscore_pki(ctx, bind_addr, dst,
1472 proto, dtls_pki,
1473 oscore_conf);
1474 } else
1475 session = coap_new_client_session_pki(ctx, bind_addr, dst, proto,
1476 dtls_pki);
1477 }
1478 } else {
1479 /* Non-encrypted session */
1480 if (doing_oscore) {
1481 session = coap_new_client_session_oscore(ctx, bind_addr, dst, proto,
1482 oscore_conf);
1483 } else
1484 session = coap_new_client_session(ctx, bind_addr, dst, proto);
1485 }
1486 if (session && (proto == COAP_PROTO_WS || proto == COAP_PROTO_WSS)) {
1487 coap_ws_set_host_request(session, &uri.host);
1488 }
1489 return session;
1490 }
1491
1492 static coap_session_t *
get_session(coap_context_t * ctx,const char * local_addr,const char * local_port,coap_uri_scheme_t scheme,coap_proto_t proto,coap_address_t * dst,const uint8_t * identity,size_t identity_len,const uint8_t * key,size_t key_len)1493 get_session(coap_context_t *ctx,
1494 const char *local_addr,
1495 const char *local_port,
1496 coap_uri_scheme_t scheme,
1497 coap_proto_t proto,
1498 coap_address_t *dst,
1499 const uint8_t *identity,
1500 size_t identity_len,
1501 const uint8_t *key,
1502 size_t key_len) {
1503 coap_session_t *session = NULL;
1504
1505 is_mcast = coap_is_mcast(dst);
1506 if (local_addr || coap_is_af_unix(dst)) {
1507 if (coap_is_af_unix(dst)) {
1508 coap_address_t bind_addr;
1509
1510 if (local_addr) {
1511 if (!coap_address_set_unix_domain(&bind_addr,
1512 (const uint8_t *)local_addr,
1513 strlen(local_addr))) {
1514 fprintf(stderr, "coap_address_set_unix_domain: %s: failed\n",
1515 local_addr);
1516 return NULL;
1517 }
1518 } else {
1519 char buf[COAP_UNIX_PATH_MAX];
1520
1521 /* Need a unique address */
1522 snprintf(buf, COAP_UNIX_PATH_MAX,
1523 "/tmp/coap-client.%d", (int)getpid());
1524 if (!coap_address_set_unix_domain(&bind_addr, (const uint8_t *)buf,
1525 strlen(buf))) {
1526 fprintf(stderr, "coap_address_set_unix_domain: %s: failed\n",
1527 buf);
1528 remove(buf);
1529 return NULL;
1530 }
1531 (void)remove(buf);
1532 }
1533 session = open_session(ctx, proto, &bind_addr, dst,
1534 identity, identity_len, key, key_len);
1535 } else {
1536 coap_addr_info_t *info_list = NULL;
1537 coap_addr_info_t *info;
1538 coap_str_const_t local;
1539 uint16_t port = local_port ? atoi(local_port) : 0;
1540
1541 local.s = (const uint8_t *)local_addr;
1542 local.length = strlen(local_addr);
1543 /* resolve local address where data should be sent from */
1544 info_list = coap_resolve_address_info(&local, port, port, port, port,
1545 AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV | AI_ALL,
1546 1 << scheme,
1547 COAP_RESOLVE_TYPE_LOCAL);
1548 if (!info_list) {
1549 fprintf(stderr, "coap_resolve_address_info: %s: failed\n", local_addr);
1550 return NULL;
1551 }
1552
1553 /* iterate through results until success */
1554 for (info = info_list; info != NULL; info = info->next) {
1555 session = open_session(ctx, proto, &info->addr, dst,
1556 identity, identity_len, key, key_len);
1557 if (session)
1558 break;
1559 }
1560 coap_free_address_info(info_list);
1561 }
1562 } else if (local_port) {
1563 coap_address_t bind_addr;
1564
1565 coap_address_init(&bind_addr);
1566 bind_addr.size = dst->size;
1567 bind_addr.addr.sa.sa_family = dst->addr.sa.sa_family;
1568 coap_address_set_port(&bind_addr, atoi(local_port));
1569 session = open_session(ctx, proto, &bind_addr, dst,
1570 identity, identity_len, key, key_len);
1571 } else {
1572 session = open_session(ctx, proto, NULL, dst,
1573 identity, identity_len, key, key_len);
1574 }
1575 return session;
1576 }
1577
1578 int
main(int argc,char ** argv)1579 main(int argc, char **argv) {
1580 coap_context_t *ctx = NULL;
1581 coap_session_t *session = NULL;
1582 coap_address_t dst;
1583 int result = -1;
1584 int exit_code = 0;
1585 coap_pdu_t *pdu;
1586 static coap_str_const_t server;
1587 uint16_t port = COAP_DEFAULT_PORT;
1588 char port_str[NI_MAXSERV] = "";
1589 char node_str[NI_MAXHOST] = "";
1590 int opt;
1591 coap_log_t log_level = COAP_LOG_WARN;
1592 coap_log_t dtls_log_level = COAP_LOG_ERR;
1593 unsigned char *user = NULL, *key = NULL;
1594 ssize_t user_length = -1, key_length = 0;
1595 int create_uri_opts = 1;
1596 size_t i;
1597 coap_uri_scheme_t scheme;
1598 coap_proto_t proto;
1599 uint32_t repeat_ms = REPEAT_DELAY_MS;
1600 uint8_t *data = NULL;
1601 size_t data_len = 0;
1602 coap_addr_info_t *info_list = NULL;
1603 #define BUFSIZE 100
1604 static unsigned char buf[BUFSIZE];
1605 #ifndef _WIN32
1606 struct sigaction sa;
1607 #endif
1608
1609 /* Initialize libcoap library */
1610 coap_startup();
1611
1612 while ((opt = getopt(argc, argv,
1613 "a:b:c:e:f:h:j:k:l:m:no:p:rs:t:u:v:wA:B:C:E:G:H:J:K:L:M:NO:P:R:T:UV:X:")) != -1) {
1614 switch (opt) {
1615 case 'a':
1616 strncpy(node_str, optarg, NI_MAXHOST - 1);
1617 node_str[NI_MAXHOST - 1] = '\0';
1618 break;
1619 case 'b':
1620 cmdline_blocksize(optarg);
1621 break;
1622 case 'B':
1623 wait_seconds = atoi(optarg);
1624 break;
1625 case 'c':
1626 cert_file = optarg;
1627 break;
1628 case 'C':
1629 ca_file = optarg;
1630 break;
1631 case 'R':
1632 root_ca_file = optarg;
1633 break;
1634 case 'e':
1635 if (!cmdline_input(optarg, &payload))
1636 payload.length = 0;
1637 break;
1638 case 'f':
1639 if (!cmdline_input_from_file(optarg, &payload))
1640 payload.length = 0;
1641 break;
1642 case 'j' :
1643 key_file = optarg;
1644 break;
1645 case 'J' :
1646 pkcs11_pin = optarg;
1647 break;
1648 case 'k':
1649 key_length = cmdline_read_key(optarg, &key, MAX_KEY);
1650 break;
1651 case 'L':
1652 block_mode = strtoul(optarg, NULL, 0);
1653 if (!(block_mode & COAP_BLOCK_USE_LIBCOAP)) {
1654 fprintf(stderr, "Block mode must include COAP_BLOCK_USE_LIBCOAP (1)\n");
1655 goto failed;
1656 }
1657 break;
1658 case 'p':
1659 strncpy(port_str, optarg, NI_MAXSERV - 1);
1660 port_str[NI_MAXSERV - 1] = '\0';
1661 break;
1662 case 'm':
1663 method = cmdline_method(optarg);
1664 break;
1665 case 'w':
1666 add_nl = 1;
1667 break;
1668 case 'N':
1669 msgtype = COAP_MESSAGE_NON;
1670 break;
1671 case 's':
1672 cmdline_subscribe(optarg);
1673 break;
1674 case 'o':
1675 output_file.length = strlen(optarg);
1676 output_file.s = (unsigned char *)coap_malloc(output_file.length + 1);
1677
1678 if (!output_file.s) {
1679 fprintf(stderr, "cannot set output file: insufficient memory\n");
1680 goto failed;
1681 } else {
1682 /* copy filename including trailing zero */
1683 memcpy(output_file.s, optarg, output_file.length + 1);
1684 }
1685 break;
1686 case 'A':
1687 cmdline_content_type(optarg, COAP_OPTION_ACCEPT);
1688 break;
1689 case 't':
1690 cmdline_content_type(optarg, COAP_OPTION_CONTENT_TYPE);
1691 break;
1692 case 'M':
1693 cert_file = optarg;
1694 is_rpk_not_cert = 1;
1695 break;
1696 case 'O':
1697 cmdline_option(optarg);
1698 break;
1699 case 'P':
1700 if (!cmdline_proxy(optarg)) {
1701 fprintf(stderr, "error specifying proxy address\n");
1702 goto failed;
1703 }
1704 break;
1705 case 'T':
1706 cmdline_token(optarg);
1707 break;
1708 case 'u':
1709 user_length = cmdline_read_user(optarg, &user, MAX_USER);
1710 break;
1711 case 'U':
1712 create_uri_opts = 0;
1713 break;
1714 case 'v':
1715 log_level = strtol(optarg, NULL, 10);
1716 break;
1717 case 'V':
1718 dtls_log_level = strtol(optarg, NULL, 10);
1719 break;
1720 case 'l':
1721 if (!coap_debug_set_packet_loss(optarg)) {
1722 usage(argv[0], LIBCOAP_PACKAGE_VERSION);
1723 goto failed;
1724 }
1725 break;
1726 case 'r':
1727 reliable = coap_tcp_is_supported();
1728 break;
1729 case 'K':
1730 ping_seconds = atoi(optarg);
1731 break;
1732 case 'h':
1733 if (!cmdline_read_hint_check(optarg)) {
1734 usage(argv[0], LIBCOAP_PACKAGE_VERSION);
1735 goto failed;
1736 }
1737 break;
1738 case 'H':
1739 if (!cmdline_hop_limit(optarg))
1740 fprintf(stderr, "Hop Limit has to be > 0 and < 256\n");
1741 break;
1742 case 'n':
1743 verify_peer_cert = 0;
1744 break;
1745 case 'G':
1746 repeat_count = atoi(optarg);
1747 if (!repeat_count || repeat_count > 255) {
1748 fprintf(stderr, "'-G count' has to be > 0 and < 256\n");
1749 repeat_count = 1;
1750 }
1751 break;
1752 case 'X':
1753 csm_max_message_size = strtol(optarg, NULL, 10);
1754 break;
1755 case 'E':
1756 doing_oscore = cmdline_oscore(optarg);
1757 if (!doing_oscore) {
1758 goto failed;
1759 }
1760 break;
1761 default:
1762 usage(argv[0], LIBCOAP_PACKAGE_VERSION);
1763 goto failed;
1764 }
1765 }
1766
1767 #ifdef _WIN32
1768 signal(SIGINT, handle_sigint);
1769 #else
1770 memset(&sa, 0, sizeof(sa));
1771 sigemptyset(&sa.sa_mask);
1772 sa.sa_handler = handle_sigint;
1773 sa.sa_flags = 0;
1774 sigaction(SIGINT, &sa, NULL);
1775 sigaction(SIGTERM, &sa, NULL);
1776 /* So we do not exit on a SIGPIPE */
1777 sa.sa_handler = SIG_IGN;
1778 sigaction(SIGPIPE, &sa, NULL);
1779 #endif
1780
1781 coap_set_log_level(log_level);
1782 coap_dtls_set_log_level(dtls_log_level);
1783
1784 if (optind < argc) {
1785 if (cmdline_uri(argv[optind]) < 0) {
1786 goto failed;
1787 }
1788 } else {
1789 usage(argv[0], LIBCOAP_PACKAGE_VERSION);
1790 goto failed;
1791 }
1792
1793 if (key_length < 0) {
1794 coap_log_crit("Invalid pre-shared key specified\n");
1795 goto failed;
1796 }
1797
1798 if (proxy.host.length) {
1799 server = proxy.host;
1800 port = proxy.port;
1801 scheme = proxy.scheme;
1802 } else {
1803 server = uri.host;
1804 port = proxy_scheme_option ? proxy.port : uri.port;
1805 scheme = proxy_scheme_option ? proxy.scheme : uri.scheme;
1806 }
1807
1808 /* resolve destination address where data should be sent */
1809 info_list = coap_resolve_address_info(&server, port, port, port, port,
1810 0,
1811 1 << scheme,
1812 COAP_RESOLVE_TYPE_REMOTE);
1813
1814 if (info_list == NULL) {
1815 coap_log_err("failed to resolve address\n");
1816 goto failed;
1817 }
1818 proto = info_list->proto;
1819 memcpy(&dst, &info_list->addr, sizeof(dst));
1820 coap_free_address_info(info_list);
1821
1822 ctx = coap_new_context(NULL);
1823 if (!ctx) {
1824 coap_log_emerg("cannot create context\n");
1825 goto failed;
1826 }
1827
1828 if (doing_oscore) {
1829 if (get_oscore_conf() == NULL)
1830 goto failed;
1831 }
1832
1833 coap_context_set_keepalive(ctx, ping_seconds);
1834 coap_context_set_block_mode(ctx, block_mode);
1835 if (csm_max_message_size)
1836 coap_context_set_csm_max_message_size(ctx, csm_max_message_size);
1837 coap_register_response_handler(ctx, message_handler);
1838 coap_register_event_handler(ctx, event_handler);
1839 coap_register_nack_handler(ctx, nack_handler);
1840 if (the_token.length > COAP_TOKEN_DEFAULT_MAX)
1841 coap_context_set_max_token_size(ctx, the_token.length);
1842
1843 session = get_session(ctx,
1844 node_str[0] ? node_str : NULL,
1845 port_str[0] ? port_str : NULL,
1846 scheme,
1847 proto,
1848 &dst,
1849 user_length >= 0 ? user : NULL,
1850 user_length >= 0 ? user_length : 0,
1851 key_length > 0 ? key : NULL,
1852 key_length > 0 ? key_length : 0
1853 );
1854
1855 if (!session) {
1856 coap_log_err("cannot create client session\n");
1857 goto failed;
1858 }
1859 /*
1860 * Prime the base token value, which coap_session_new_token() will increment
1861 * every time it is called to get an unique token.
1862 * [Option '-T token' is used to seed a different value]
1863 * Note that only the first 8 bytes of the token are used as the prime.
1864 */
1865 coap_session_init_token(session, the_token.length, the_token.s);
1866
1867 /* Convert provided uri into CoAP options */
1868 if (coap_uri_into_options(&uri, !uri_host_option && !proxy.host.length ?
1869 &dst : NULL,
1870 &optlist, create_uri_opts,
1871 buf, sizeof(buf)) < 0) {
1872 coap_log_err("Failed to create options for URI\n");
1873 goto failed;
1874 }
1875
1876 /* set block option if requested at commandline */
1877 if (flags & FLAGS_BLOCK)
1878 set_blocksize();
1879
1880 /* Send the first (and may be only PDU) */
1881 if (payload.length) {
1882 /* Create some new data to use for this iteration */
1883 data = coap_malloc(payload.length);
1884 if (data == NULL)
1885 goto failed;
1886 memcpy(data, payload.s, payload.length);
1887 data_len = payload.length;
1888 }
1889 if (!(pdu = coap_new_request(ctx, session, method, &optlist, data,
1890 data_len))) {
1891 goto failed;
1892 }
1893
1894 if (is_mcast && wait_seconds == DEFAULT_WAIT_TIME)
1895 /* Allow for other servers to respond within DEFAULT_LEISURE RFC7252 8.2 */
1896 wait_seconds = coap_session_get_default_leisure(session).integer_part + 1;
1897
1898 wait_ms = wait_seconds * 1000;
1899 coap_log_debug("timeout is set to %u seconds\n", wait_seconds);
1900
1901 coap_log_debug("sending CoAP request:\n");
1902 if (coap_get_log_level() < COAP_LOG_DEBUG)
1903 coap_show_pdu(COAP_LOG_INFO, pdu);
1904
1905 if (coap_send(session, pdu) == COAP_INVALID_MID) {
1906 coap_log_err("cannot send CoAP pdu\n");
1907 quit = 1;
1908 }
1909 repeat_count--;
1910
1911 while (!quit && /* immediate quit not required .. and .. */
1912 (tracked_tokens_count || /* token not responded to or still observe */
1913 is_mcast || /* mcast active */
1914 repeat_count || /* more repeat transmissions to go */
1915 coap_io_pending(ctx))) { /* i/o not yet complete */
1916 uint32_t timeout_ms;
1917 /*
1918 * 3 factors determine how long to wait in coap_io_process()
1919 * Remaining overall wait time (wait_ms)
1920 * Remaining overall observe unsolicited response time (obs_ms)
1921 * Delay of up to one second before sending off the next request
1922 */
1923 if (obs_ms) {
1924 timeout_ms = min(wait_ms, obs_ms);
1925 } else {
1926 timeout_ms = wait_ms;
1927 }
1928 if (repeat_count) {
1929 timeout_ms = min(timeout_ms, repeat_ms);
1930 }
1931
1932 result = coap_io_process(ctx, timeout_ms);
1933
1934 if (result >= 0) {
1935 if (wait_ms > 0) {
1936 if ((unsigned)result >= wait_ms) {
1937 coap_log_info("timeout\n");
1938 break;
1939 } else {
1940 wait_ms -= result;
1941 }
1942 }
1943 if (obs_ms > 0 && !obs_ms_reset) {
1944 if ((unsigned)result >= obs_ms) {
1945 coap_log_debug("clear observation relationship\n");
1946 for (i = 0; i < tracked_tokens_count; i++) {
1947 if (tracked_tokens[i].observe) {
1948 coap_cancel_observe(session, tracked_tokens[i].token, msgtype);
1949 tracked_tokens[i].observe = 0;
1950 coap_io_process(ctx, COAP_IO_NO_WAIT);
1951 }
1952 }
1953 doing_observe = 0;
1954
1955 /* make sure that the obs timer does not fire again */
1956 obs_ms = 0;
1957 obs_seconds = 0;
1958 } else {
1959 obs_ms -= result;
1960 }
1961 }
1962 if (ready && repeat_count) {
1963 /* Send off next request if appropriate */
1964 if (repeat_ms > (unsigned)result) {
1965 repeat_ms -= (unsigned)result;
1966 } else {
1967 /* Doing this once a second */
1968 repeat_ms = REPEAT_DELAY_MS;
1969 if (payload.length) {
1970 /* Create some new data to use for this iteration */
1971 data = coap_malloc(payload.length);
1972 if (data == NULL)
1973 goto failed;
1974 memcpy(data, payload.s, payload.length);
1975 data_len = payload.length;
1976 }
1977 if (!(pdu = coap_new_request(ctx, session, method, &optlist,
1978 data, data_len))) {
1979 goto failed;
1980 }
1981 coap_log_debug("sending CoAP request:\n");
1982 if (coap_get_log_level() < COAP_LOG_DEBUG)
1983 coap_show_pdu(COAP_LOG_INFO, pdu);
1984
1985 ready = 0;
1986 if (coap_send(session, pdu) == COAP_INVALID_MID) {
1987 coap_log_err("cannot send CoAP pdu\n");
1988 quit = 1;
1989 }
1990 repeat_count--;
1991 }
1992 }
1993 obs_ms_reset = 0;
1994 }
1995 }
1996
1997 exit_code = 0;
1998
1999 finish:
2000
2001 /* Clean up library usage */
2002 coap_session_release(session);
2003 coap_free_context(ctx);
2004 coap_cleanup();
2005
2006 /* Clean up local usage */
2007 coap_free(ca_mem);
2008 coap_free(cert_mem);
2009 coap_free(key_mem);
2010 coap_free(payload.s);
2011
2012 for (i = 0; i < valid_ihs.count; i++) {
2013 free(valid_ihs.ih_list[i].hint_match);
2014 coap_delete_bin_const(valid_ihs.ih_list[i].new_identity);
2015 coap_delete_bin_const(valid_ihs.ih_list[i].new_key);
2016 }
2017 if (valid_ihs.count)
2018 free(valid_ihs.ih_list);
2019
2020 for (i = 0; i < tracked_tokens_count; i++) {
2021 coap_delete_binary(tracked_tokens[i].token);
2022 }
2023 free(tracked_tokens);
2024
2025 coap_delete_optlist(optlist);
2026 if (oscore_seq_num_fp)
2027 fclose(oscore_seq_num_fp);
2028 close_output();
2029
2030 return exit_code;
2031
2032 failed:
2033 exit_code = 1;
2034 goto finish;
2035 }
2036