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