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