• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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