• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 
3 /* coap-client -- simple CoAP client
4  *
5  * Copyright (C) 2010--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