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