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--2015 Olaf Bergmann <bergmann@tzi.org>
6  *
7  * This file is part of the CoAP library libcoap. Please see README for terms of
8  * use.
9  */
10 
11 #include "coap_config.h"
12 
13 #include <string.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <stdio.h>
17 #include <ctype.h>
18 #include <sys/select.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <sys/socket.h>
22 #include <netinet/in.h>
23 #include <arpa/inet.h>
24 #include <netdb.h>
25 
26 #include "coap.h"
27 #include "coap_list.h"
28 
29 int flags = 0;
30 
31 static unsigned char _token_data[8];
32 str the_token = { 0, _token_data };
33 
34 #define FLAGS_BLOCK 0x01
35 
36 static coap_list_t *optlist = NULL;
37 /* Request URI.
38  * TODO: associate the resources with transaction id and make it expireable */
39 static coap_uri_t uri;
40 static str proxy = { 0, NULL };
41 static unsigned short proxy_port = COAP_DEFAULT_PORT;
42 
43 /* reading is done when this flag is set */
44 static int ready = 0;
45 
46 static str output_file = { 0, NULL };   /* output file name */
47 static FILE *file = NULL;               /* output file stream */
48 
49 static str payload = { 0, NULL };       /* optional payload to send */
50 
51 unsigned char msgtype = COAP_MESSAGE_CON; /* usually, requests are sent confirmable */
52 
53 typedef unsigned char method_t;
54 method_t method = 1;                    /* the method we are using in our requests */
55 
56 coap_block_t block = { .num = 0, .m = 0, .szx = 6 };
57 
58 unsigned int wait_seconds = 90;         /* default timeout in seconds */
59 coap_tick_t max_wait;                   /* global timeout (changed by set_timeout()) */
60 
61 unsigned int obs_seconds = 30;          /* default observe time */
62 coap_tick_t obs_wait = 0;               /* timeout for current subscription */
63 
64 #define min(a,b) ((a) < (b) ? (a) : (b))
65 
66 #ifdef __GNUC__
67 #define UNUSED_PARAM __attribute__ ((unused))
68 #else /* not a GCC */
69 #define UNUSED_PARAM
70 #endif /* GCC */
71 
72 static inline void
set_timeout(coap_tick_t * timer,const unsigned int seconds)73 set_timeout(coap_tick_t *timer, const unsigned int seconds) {
74   coap_ticks(timer);
75   *timer += seconds * COAP_TICKS_PER_SECOND;
76 }
77 
78 static int
append_to_output(const unsigned char * data,size_t len)79 append_to_output(const unsigned char *data, size_t len) {
80   size_t written;
81 
82   if (!file) {
83     if (!output_file.s || (output_file.length && output_file.s[0] == '-'))
84       file = stdout;
85     else {
86       if (!(file = fopen((char *)output_file.s, "w"))) {
87         perror("fopen");
88         return -1;
89       }
90     }
91   }
92 
93   do {
94     written = fwrite(data, 1, len, file);
95     len -= written;
96     data += written;
97   } while ( written && len );
98   fflush(file);
99 
100   return 0;
101 }
102 
103 static void
close_output(void)104 close_output(void) {
105   if (file) {
106 
107     /* add a newline before closing in case were writing to stdout */
108     if (!output_file.s || (output_file.length && output_file.s[0] == '-'))
109       fwrite("\n", 1, 1, file);
110 
111     fflush(file);
112     fclose(file);
113   }
114 }
115 
116 static int
order_opts(void * a,void * b)117 order_opts(void *a, void *b) {
118   coap_option *o1, *o2;
119 
120   if (!a || !b)
121     return a < b ? -1 : 1;
122 
123   o1 = (coap_option *)(((coap_list_t *)a)->data);
124   o2 = (coap_option *)(((coap_list_t *)b)->data);
125 
126   return (COAP_OPTION_KEY(*o1) < COAP_OPTION_KEY(*o2))
127     ? -1
128     : (COAP_OPTION_KEY(*o1) != COAP_OPTION_KEY(*o2));
129 }
130 
131 static coap_pdu_t *
coap_new_request(coap_context_t * ctx,method_t m,coap_list_t ** options,unsigned char * data,size_t length)132 coap_new_request(coap_context_t *ctx,
133                  method_t m,
134                  coap_list_t **options,
135                  unsigned char *data,
136                  size_t length) {
137   coap_pdu_t *pdu;
138   coap_list_t *opt;
139 
140   if ( ! ( pdu = coap_new_pdu() ) )
141     return NULL;
142 
143   pdu->hdr->type = msgtype;
144   pdu->hdr->id = coap_new_message_id(ctx);
145   pdu->hdr->code = m;
146 
147   pdu->hdr->token_length = the_token.length;
148   if ( !coap_add_token(pdu, the_token.length, the_token.s)) {
149     debug("cannot add token to request\n");
150   }
151 
152   coap_show_pdu(pdu);
153 
154   if (options) {
155     /* sort options for delta encoding */
156     LL_SORT((*options), order_opts);
157 
158     LL_FOREACH((*options), opt) {
159       coap_option *o = (coap_option *)(opt->data);
160       coap_add_option(pdu,
161                       COAP_OPTION_KEY(*o),
162                       COAP_OPTION_LENGTH(*o),
163                       COAP_OPTION_DATA(*o));
164     }
165   }
166 
167   if (length) {
168     if ((flags & FLAGS_BLOCK) == 0)
169       coap_add_data(pdu, length, data);
170     else
171       coap_add_block(pdu, length, data, block.num, block.szx);
172   }
173 
174   return pdu;
175 }
176 
177 static coap_tid_t
clear_obs(coap_context_t * ctx,const coap_endpoint_t * local_interface,const coap_address_t * remote)178 clear_obs(coap_context_t *ctx,
179           const coap_endpoint_t *local_interface,
180           const coap_address_t *remote) {
181   coap_pdu_t *pdu;
182   coap_list_t *option;
183   coap_tid_t tid = COAP_INVALID_TID;
184   unsigned char buf[2];
185 
186   /* create bare PDU w/o any option  */
187   pdu = coap_pdu_init(msgtype,
188                       COAP_REQUEST_GET,
189                       coap_new_message_id(ctx),
190                       COAP_MAX_PDU_SIZE);
191 
192   if (!pdu) {
193     return tid;
194   }
195 
196   if (!coap_add_token(pdu, the_token.length, the_token.s)) {
197     coap_log(LOG_CRIT, "cannot add token");
198     goto error;
199   }
200 
201   for (option = optlist; option; option = option->next ) {
202     coap_option *o = (coap_option *)(option->data);
203     if (COAP_OPTION_KEY(*o) == COAP_OPTION_URI_HOST) {
204       if (!coap_add_option(pdu,
205           COAP_OPTION_KEY(*o),
206           COAP_OPTION_LENGTH(*o),
207           COAP_OPTION_DATA(*o))) {
208         goto error;
209       }
210       break;
211     }
212   }
213 
214   if (!coap_add_option(pdu,
215       COAP_OPTION_OBSERVE,
216       coap_encode_var_bytes(buf, COAP_OBSERVE_CANCEL),
217       buf)) {
218     coap_log(LOG_CRIT, "cannot add option Observe: %u", COAP_OBSERVE_CANCEL);
219     goto error;
220   }
221 
222   for (option = optlist; option; option = option->next ) {
223     coap_option *o = (coap_option *)(option->data);
224     switch (COAP_OPTION_KEY(*o)) {
225     case COAP_OPTION_URI_PORT :
226     case COAP_OPTION_URI_PATH :
227     case COAP_OPTION_URI_QUERY :
228       if (!coap_add_option (pdu,
229                             COAP_OPTION_KEY(*o),
230                             COAP_OPTION_LENGTH(*o),
231                             COAP_OPTION_DATA(*o))) {
232         goto error;
233       }
234       break;
235       default:
236       ;
237     }
238   }
239 
240   coap_show_pdu(pdu);
241 
242   if (pdu->hdr->type == COAP_MESSAGE_CON)
243     tid = coap_send_confirmed(ctx, local_interface, remote, pdu);
244   else
245     tid = coap_send(ctx, local_interface, remote, pdu);
246 
247   if (tid == COAP_INVALID_TID) {
248     debug("clear_obs: error sending new request");
249     coap_delete_pdu(pdu);
250   } else if (pdu->hdr->type != COAP_MESSAGE_CON)
251     coap_delete_pdu(pdu);
252 
253   return tid;
254  error:
255 
256   coap_delete_pdu(pdu);
257   return tid;
258 }
259 
260 static int
resolve_address(const str * server,struct sockaddr * dst)261 resolve_address(const str *server, struct sockaddr *dst) {
262 
263   struct addrinfo *res, *ainfo;
264   struct addrinfo hints;
265   static char addrstr[256];
266   int error, len=-1;
267 
268   memset(addrstr, 0, sizeof(addrstr));
269   if (server->length)
270     memcpy(addrstr, server->s, server->length);
271   else
272     memcpy(addrstr, "localhost", 9);
273 
274   memset ((char *)&hints, 0, sizeof(hints));
275   hints.ai_socktype = SOCK_DGRAM;
276   hints.ai_family = AF_UNSPEC;
277 
278   error = getaddrinfo(addrstr, NULL, &hints, &res);
279 
280   if (error != 0) {
281     fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(error));
282     return error;
283   }
284 
285   for (ainfo = res; ainfo != NULL; ainfo = ainfo->ai_next) {
286     switch (ainfo->ai_family) {
287     case AF_INET6:
288     case AF_INET:
289       len = ainfo->ai_addrlen;
290       memcpy(dst, ainfo->ai_addr, len);
291       goto finish;
292     default:
293       ;
294     }
295   }
296 
297  finish:
298   freeaddrinfo(res);
299   return len;
300 }
301 
302 #define HANDLE_BLOCK1(Pdu)                                        \
303   ((method == COAP_REQUEST_PUT || method == COAP_REQUEST_POST) && \
304    ((flags & FLAGS_BLOCK) == 0) &&                                \
305    ((Pdu)->hdr->code == COAP_RESPONSE_CODE(201) ||                \
306     (Pdu)->hdr->code == COAP_RESPONSE_CODE(204)))
307 
308 static inline int
check_token(coap_pdu_t * received)309 check_token(coap_pdu_t *received) {
310   return received->hdr->token_length == the_token.length &&
311     memcmp(received->hdr->token, the_token.s, the_token.length) == 0;
312 }
313 
314 static void
message_handler(struct coap_context_t * ctx,const coap_endpoint_t * local_interface,const coap_address_t * remote,coap_pdu_t * sent,coap_pdu_t * received,const coap_tid_t id UNUSED_PARAM)315 message_handler(struct coap_context_t *ctx,
316                 const coap_endpoint_t *local_interface,
317                 const coap_address_t *remote,
318                 coap_pdu_t *sent,
319                 coap_pdu_t *received,
320                 const coap_tid_t id UNUSED_PARAM) {
321 
322   coap_pdu_t *pdu = NULL;
323   coap_opt_t *block_opt;
324   coap_opt_iterator_t opt_iter;
325   unsigned char buf[4];
326   coap_list_t *option;
327   size_t len;
328   unsigned char *databuf;
329   coap_tid_t tid;
330 
331 #ifndef NDEBUG
332   if (LOG_DEBUG <= coap_get_log_level()) {
333     debug("** process incoming %d.%02d response:\n",
334           (received->hdr->code >> 5), received->hdr->code & 0x1F);
335     coap_show_pdu(received);
336   }
337 #endif
338 
339   /* check if this is a response to our original request */
340   if (!check_token(received)) {
341     /* drop if this was just some message, or send RST in case of notification */
342     if (!sent && (received->hdr->type == COAP_MESSAGE_CON ||
343                   received->hdr->type == COAP_MESSAGE_NON))
344       coap_send_rst(ctx, local_interface, remote, received);
345     return;
346   }
347 
348   if (received->hdr->type == COAP_MESSAGE_RST) {
349     info("got RST\n");
350     return;
351   }
352 
353   /* output the received data, if any */
354   if (COAP_RESPONSE_CLASS(received->hdr->code) == 2) {
355 
356     /* set obs timer if we have successfully subscribed a resource */
357     if (sent && coap_check_option(received, COAP_OPTION_SUBSCRIPTION, &opt_iter)) {
358       debug("observation relationship established, set timeout to %d\n", obs_seconds);
359       set_timeout(&obs_wait, obs_seconds);
360     }
361 
362     /* Got some data, check if block option is set. Behavior is undefined if
363      * both, Block1 and Block2 are present. */
364     block_opt = coap_check_option(received, COAP_OPTION_BLOCK2, &opt_iter);
365     if (block_opt) { /* handle Block2 */
366       unsigned short blktype = opt_iter.type;
367 
368       /* TODO: check if we are looking at the correct block number */
369       if (coap_get_data(received, &len, &databuf))
370         append_to_output(databuf, len);
371 
372       if(COAP_OPT_BLOCK_MORE(block_opt)) {
373         /* more bit is set */
374         debug("found the M bit, block size is %u, block nr. %u\n",
375               COAP_OPT_BLOCK_SZX(block_opt),
376               coap_opt_block_num(block_opt));
377 
378         /* create pdu with request for next block */
379         pdu = coap_new_request(ctx, method, NULL, NULL, 0); /* first, create bare PDU w/o any option  */
380         if ( pdu ) {
381           /* add URI components from optlist */
382           for (option = optlist; option; option = option->next ) {
383             coap_option *o = (coap_option *)(option->data);
384             switch (COAP_OPTION_KEY(*o)) {
385               case COAP_OPTION_URI_HOST :
386               case COAP_OPTION_URI_PORT :
387               case COAP_OPTION_URI_PATH :
388               case COAP_OPTION_URI_QUERY :
389                 coap_add_option (pdu,
390                                  COAP_OPTION_KEY(*o),
391                                  COAP_OPTION_LENGTH(*o),
392                                  COAP_OPTION_DATA(*o));
393                 break;
394               default:
395                 ;     /* skip other options */
396             }
397           }
398 
399           /* finally add updated block option from response, clear M bit */
400           /* blocknr = (blocknr & 0xfffffff7) + 0x10; */
401           debug("query block %d\n", (coap_opt_block_num(block_opt) + 1));
402           coap_add_option(pdu,
403                           blktype,
404                           coap_encode_var_bytes(buf,
405                                  ((coap_opt_block_num(block_opt) + 1) << 4) |
406                                   COAP_OPT_BLOCK_SZX(block_opt)), buf);
407 
408           if (pdu->hdr->type == COAP_MESSAGE_CON)
409             tid = coap_send_confirmed(ctx, local_interface, remote, pdu);
410           else
411             tid = coap_send(ctx, local_interface, remote, pdu);
412 
413           if (tid == COAP_INVALID_TID) {
414             debug("message_handler: error sending new request");
415             coap_delete_pdu(pdu);
416           } else {
417             set_timeout(&max_wait, wait_seconds);
418             if (pdu->hdr->type != COAP_MESSAGE_CON)
419               coap_delete_pdu(pdu);
420           }
421 
422           return;
423         }
424       }
425     } else { /* no Block2 option */
426       block_opt = coap_check_option(received, COAP_OPTION_BLOCK1, &opt_iter);
427 
428       if (block_opt) { /* handle Block1 */
429         block.szx = COAP_OPT_BLOCK_SZX(block_opt);
430         block.num = coap_opt_block_num(block_opt);
431 
432         debug("found Block1, block size is %u, block nr. %u\n",
433         block.szx, block.num);
434 
435         if (payload.length <= (block.num+1) * (1 << (block.szx + 4))) {
436           debug("upload ready\n");
437           ready = 1;
438           return;
439         }
440 
441         /* create pdu with request for next block */
442         pdu = coap_new_request(ctx, method, NULL, NULL, 0); /* first, create bare PDU w/o any option  */
443         if (pdu) {
444 
445           /* add URI components from optlist */
446           for (option = optlist; option; option = option->next ) {
447             coap_option *o = (coap_option *)(option->data);
448             switch (COAP_OPTION_KEY(*o)) {
449               case COAP_OPTION_URI_HOST :
450               case COAP_OPTION_URI_PORT :
451               case COAP_OPTION_URI_PATH :
452               case COAP_OPTION_CONTENT_FORMAT :
453               case COAP_OPTION_URI_QUERY :
454                 coap_add_option (pdu,
455                                  COAP_OPTION_KEY(*o),
456                                  COAP_OPTION_LENGTH(*o),
457                                  COAP_OPTION_DATA(*o));
458                 break;
459               default:
460               ;     /* skip other options */
461             }
462           }
463 
464           /* finally add updated block option from response, clear M bit */
465           /* blocknr = (blocknr & 0xfffffff7) + 0x10; */
466           block.num++;
467           block.m = ((block.num+1) * (1 << (block.szx + 4)) < payload.length);
468 
469           debug("send block %d\n", block.num);
470           coap_add_option(pdu,
471                           COAP_OPTION_BLOCK1,
472                           coap_encode_var_bytes(buf,
473                           (block.num << 4) | (block.m << 3) | block.szx), buf);
474 
475           coap_add_block(pdu,
476                          payload.length,
477                          payload.s,
478                          block.num,
479                          block.szx);
480           coap_show_pdu(pdu);
481           if (pdu->hdr->type == COAP_MESSAGE_CON)
482             tid = coap_send_confirmed(ctx, local_interface, remote, pdu);
483           else
484             tid = coap_send(ctx, local_interface, remote, pdu);
485 
486           if (tid == COAP_INVALID_TID) {
487             debug("message_handler: error sending new request");
488             coap_delete_pdu(pdu);
489           } else {
490             set_timeout(&max_wait, wait_seconds);
491             if (pdu->hdr->type != COAP_MESSAGE_CON)
492               coap_delete_pdu(pdu);
493           }
494 
495           return;
496         }
497       } else {
498         /* There is no block option set, just read the data and we are done. */
499         if (coap_get_data(received, &len, &databuf))
500         append_to_output(databuf, len);
501       }
502     }
503   } else {      /* no 2.05 */
504 
505     /* check if an error was signaled and output payload if so */
506     if (COAP_RESPONSE_CLASS(received->hdr->code) >= 4) {
507       fprintf(stderr, "%d.%02d",
508               (received->hdr->code >> 5), received->hdr->code & 0x1F);
509       if (coap_get_data(received, &len, &databuf)) {
510         fprintf(stderr, " ");
511         while(len--)
512         fprintf(stderr, "%c", *databuf++);
513       }
514       fprintf(stderr, "\n");
515     }
516 
517   }
518 
519   /* finally send new request, if needed */
520   if (pdu && coap_send(ctx, local_interface, remote, pdu) == COAP_INVALID_TID) {
521     debug("message_handler: error sending response");
522   }
523   coap_delete_pdu(pdu);
524 
525   /* our job is done, we can exit at any time */
526   ready = coap_check_option(received, COAP_OPTION_SUBSCRIPTION, &opt_iter) == NULL;
527 }
528 
529 static void
usage(const char * program,const char * version)530 usage( const char *program, const char *version) {
531   const char *p;
532 
533   p = strrchr( program, '/' );
534   if ( p )
535     program = ++p;
536 
537   fprintf( stderr, "%s v%s -- a small CoAP implementation\n"
538      "(c) 2010-2015 Olaf Bergmann <bergmann@tzi.org>\n\n"
539      "usage: %s [-A type...] [-t type] [-b [num,]size] [-B seconds] [-e text]\n"
540      "\t\t[-m method] [-N] [-o file] [-P addr[:port]] [-p port]\n"
541      "\t\t[-s duration] [-O num,text] [-T string] [-v num] [-a addr] URI\n\n"
542      "\tURI can be an absolute or relative coap URI,\n"
543      "\t-a addr\tthe local interface address to use\n"
544      "\t-A type...\taccepted media types as comma-separated list of\n"
545      "\t\t\tsymbolic or numeric values\n"
546      "\t-t type\t\tcontent format for given resource for PUT/POST\n"
547      "\t-b [num,]size\tblock size to be used in GET/PUT/POST requests\n"
548      "\t       \t\t(value must be a multiple of 16 not larger than 1024)\n"
549      "\t       \t\tIf num is present, the request chain will start at\n"
550      "\t       \t\tblock num\n"
551      "\t-B seconds\tbreak operation after waiting given seconds\n"
552      "\t\t\t(default is %d)\n"
553      "\t-e text\t\tinclude text as payload (use percent-encoding for\n"
554      "\t\t\tnon-ASCII characters)\n"
555      "\t-f file\t\tfile to send with PUT/POST (use '-' for STDIN)\n"
556      "\t-m method\trequest method (get|put|post|delete), default is 'get'\n"
557      "\t-N\t\tsend NON-confirmable message\n"
558      "\t-o file\t\toutput received data to this file (use '-' for STDOUT)\n"
559      "\t-p port\t\tlisten on specified port\n"
560      "\t-s duration\tsubscribe for given duration [s]\n"
561      "\t-v num\t\tverbosity level (default: 3)\n"
562      "\t-O num,text\tadd option num with contents text to request\n"
563      "\t-P addr[:port]\tuse proxy (automatically adds Proxy-Uri option to\n"
564      "\t\t\trequest)\n"
565      "\t-T token\tinclude specified token\n"
566      "\n"
567      "examples:\n"
568      "\tcoap-client -m get coap://[::1]/\n"
569      "\tcoap-client -m get coap://[::1]/.well-known/core\n"
570      "\tcoap-client -m get -T cafe coap://[::1]/time\n"
571      "\techo 1000 | coap-client -m put -T cafe coap://[::1]/time -f -\n"
572      ,program, version, program, wait_seconds);
573 }
574 
575 static coap_list_t *
new_option_node(unsigned short key,unsigned int length,unsigned char * data)576 new_option_node(unsigned short key, unsigned int length, unsigned char *data) {
577   coap_list_t *node;
578 
579   node = coap_malloc(sizeof(coap_list_t) + sizeof(coap_option) + length);
580 
581   if (node) {
582     coap_option *option;
583     option = (coap_option *)(node->data);
584     COAP_OPTION_KEY(*option) = key;
585     COAP_OPTION_LENGTH(*option) = length;
586     memcpy(COAP_OPTION_DATA(*option), data, length);
587   } else {
588     coap_log(LOG_DEBUG, "new_option_node: malloc\n");
589   }
590 
591   return node;
592 }
593 
594 typedef struct {
595   unsigned char code;
596   char *media_type;
597 } content_type_t;
598 
599 static void
cmdline_content_type(char * arg,unsigned short key)600 cmdline_content_type(char *arg, unsigned short key) {
601   static content_type_t content_types[] = {
602     {  0, "plain" },
603     {  0, "text/plain" },
604     { 40, "link" },
605     { 40, "link-format" },
606     { 40, "application/link-format" },
607     { 41, "xml" },
608     { 41, "application/xml" },
609     { 42, "binary" },
610     { 42, "octet-stream" },
611     { 42, "application/octet-stream" },
612     { 47, "exi" },
613     { 47, "application/exi" },
614     { 50, "json" },
615     { 50, "application/json" },
616     { 60, "cbor" },
617     { 60, "application/cbor" },
618     { 255, NULL }
619   };
620   coap_list_t *node;
621   unsigned char i, value[10];
622   int valcnt = 0;
623   unsigned char buf[2];
624   char *p, *q = arg;
625 
626   while (q && *q) {
627     p = strchr(q, ',');
628 
629     if (isdigit(*q)) {
630       if (p)
631         *p = '\0';
632       value[valcnt++] = atoi(q);
633     } else {
634       for (i=0;
635            content_types[i].media_type &&
636            strncmp(q, content_types[i].media_type, p ? (size_t)(p-q) : strlen(q)) != 0 ;
637            ++i)
638       ;
639 
640       if (content_types[i].media_type) {
641         value[valcnt] = content_types[i].code;
642         valcnt++;
643       } else {
644         warn("W: unknown content-format '%s'\n",arg);
645       }
646     }
647 
648     if (!p || key == COAP_OPTION_CONTENT_TYPE)
649       break;
650 
651     q = p+1;
652   }
653 
654   for (i = 0; i < valcnt; ++i) {
655     node = new_option_node(key, coap_encode_var_bytes(buf, value[i]), buf);
656     if (node) {
657       LL_PREPEND(optlist, node);
658     }
659   }
660 }
661 
662 static void
cmdline_uri(char * arg)663 cmdline_uri(char *arg) {
664   unsigned char portbuf[2];
665 #define BUFSIZE 40
666   unsigned char _buf[BUFSIZE];
667   unsigned char *buf = _buf;
668   size_t buflen;
669   int res;
670 
671   if (proxy.length) {   /* create Proxy-Uri from argument */
672     size_t len = strlen(arg);
673     while (len > 270) {
674       coap_insert(&optlist,
675                   new_option_node(COAP_OPTION_PROXY_URI,
676                   270,
677                   (unsigned char *)arg));
678 
679       len -= 270;
680       arg += 270;
681     }
682 
683     coap_insert(&optlist,
684                 new_option_node(COAP_OPTION_PROXY_URI,
685                 len,
686                 (unsigned char *)arg));
687 
688   } else {      /* split arg into Uri-* options */
689       coap_split_uri((unsigned char *)arg, strlen(arg), &uri );
690 
691     if (uri.port != COAP_DEFAULT_PORT) {
692       coap_insert(&optlist,
693                   new_option_node(COAP_OPTION_URI_PORT,
694                   coap_encode_var_bytes(portbuf, uri.port),
695                   portbuf));
696     }
697 
698     if (uri.path.length) {
699       buflen = BUFSIZE;
700       res = coap_split_path(uri.path.s, uri.path.length, buf, &buflen);
701 
702       while (res--) {
703         coap_insert(&optlist,
704                     new_option_node(COAP_OPTION_URI_PATH,
705                     COAP_OPT_LENGTH(buf),
706                     COAP_OPT_VALUE(buf)));
707 
708         buf += COAP_OPT_SIZE(buf);
709       }
710     }
711 
712     if (uri.query.length) {
713       buflen = BUFSIZE;
714       buf = _buf;
715       res = coap_split_query(uri.query.s, uri.query.length, buf, &buflen);
716 
717       while (res--) {
718         coap_insert(&optlist,
719                     new_option_node(COAP_OPTION_URI_QUERY,
720                     COAP_OPT_LENGTH(buf),
721                     COAP_OPT_VALUE(buf)));
722 
723         buf += COAP_OPT_SIZE(buf);
724       }
725     }
726   }
727 }
728 
729 static int
cmdline_blocksize(char * arg)730 cmdline_blocksize(char *arg) {
731   unsigned short size;
732 
733   again:
734   size = 0;
735   while(*arg && *arg != ',')
736     size = size * 10 + (*arg++ - '0');
737 
738   if (*arg == ',') {
739     arg++;
740     block.num = size;
741     goto again;
742   }
743 
744   if (size)
745     block.szx = (coap_fls(size >> 4) - 1) & 0x07;
746 
747   flags |= FLAGS_BLOCK;
748   return 1;
749 }
750 
751 /* Called after processing the options from the commandline to set
752  * Block1 or Block2 depending on method. */
753 static void
set_blocksize(void)754 set_blocksize(void) {
755   static unsigned char buf[4];	/* hack: temporarily take encoded bytes */
756   unsigned short opt;
757   unsigned int opt_length;
758 
759   if (method != COAP_REQUEST_DELETE) {
760     opt = method == COAP_REQUEST_GET ? COAP_OPTION_BLOCK2 : COAP_OPTION_BLOCK1;
761 
762     block.m = (opt == COAP_OPTION_BLOCK1) &&
763       ((1u << (block.szx + 4)) < payload.length);
764 
765     opt_length = coap_encode_var_bytes(buf,
766           (block.num << 4 | block.m << 3 | block.szx));
767 
768     coap_insert(&optlist, new_option_node(opt, opt_length, buf));
769   }
770 }
771 
772 static void
cmdline_subscribe(char * arg UNUSED_PARAM)773 cmdline_subscribe(char *arg UNUSED_PARAM) {
774   obs_seconds = atoi(optarg);
775   coap_insert(&optlist, new_option_node(COAP_OPTION_SUBSCRIPTION, 0, NULL));
776 }
777 
778 static int
cmdline_proxy(char * arg)779 cmdline_proxy(char *arg) {
780   char *proxy_port_str = strrchr((const char *)arg, ':'); /* explicit port ? */
781   if (proxy_port_str) {
782     char *ipv6_delimiter = strrchr((const char *)arg, ']');
783     if (!ipv6_delimiter) {
784       if (proxy_port_str == strchr((const char *)arg, ':')) {
785         /* host:port format - host not in ipv6 hexadecimal string format */
786         *proxy_port_str++ = '\0'; /* split */
787         proxy_port = atoi(proxy_port_str);
788       }
789     } else {
790       arg = strchr((const char *)arg, '[');
791       if (!arg) return 0;
792       arg++;
793       *ipv6_delimiter = '\0'; /* split */
794       if (ipv6_delimiter + 1 == proxy_port_str++) {
795         /* [ipv6 address]:port */
796         proxy_port = atoi(proxy_port_str);
797       }
798     }
799   }
800 
801   proxy.length = strlen(arg);
802   if ( (proxy.s = coap_malloc(proxy.length + 1)) == NULL) {
803     proxy.length = 0;
804     return 0;
805   }
806 
807   memcpy(proxy.s, arg, proxy.length+1);
808   return 1;
809 }
810 
811 static inline void
cmdline_token(char * arg)812 cmdline_token(char *arg) {
813   strncpy((char *)the_token.s, arg, min(sizeof(_token_data), strlen(arg)));
814   the_token.length = strlen(arg);
815 }
816 
817 static void
cmdline_option(char * arg)818 cmdline_option(char *arg) {
819   unsigned int num = 0;
820 
821   while (*arg && *arg != ',') {
822     num = num * 10 + (*arg - '0');
823     ++arg;
824   }
825   if (*arg == ',')
826     ++arg;
827 
828   coap_insert(&optlist,
829               new_option_node(num, strlen(arg), (unsigned char *)arg));
830 }
831 
832 /**
833  * Calculates decimal value from hexadecimal ASCII character given in
834  * @p c. The caller must ensure that @p c actually represents a valid
835  * heaxdecimal character, e.g. with isxdigit(3).
836  *
837  * @hideinitializer
838  */
839 #define hexchar_to_dec(c) ((c) & 0x40 ? ((c) & 0x0F) + 9 : ((c) & 0x0F))
840 
841 /**
842  * Decodes percent-encoded characters while copying the string @p seg
843  * of size @p length to @p buf. The caller of this function must
844  * ensure that the percent-encodings are correct (i.e. the character
845  * '%' is always followed by two hex digits. and that @p buf provides
846  * sufficient space to hold the result. This function is supposed to
847  * be called by make_decoded_option() only.
848  *
849  * @param seg     The segment to decode and copy.
850  * @param length  Length of @p seg.
851  * @param buf     The result buffer.
852  */
853 static void
decode_segment(const unsigned char * seg,size_t length,unsigned char * buf)854 decode_segment(const unsigned char *seg, size_t length, unsigned char *buf) {
855 
856   while (length--) {
857 
858     if (*seg == '%') {
859       *buf = (hexchar_to_dec(seg[1]) << 4) + hexchar_to_dec(seg[2]);
860 
861       seg += 2; length -= 2;
862     } else {
863       *buf = *seg;
864     }
865 
866     ++buf; ++seg;
867   }
868 }
869 
870 /**
871  * Runs through the given path (or query) segment and checks if
872  * percent-encodings are correct. This function returns @c -1 on error
873  * or the length of @p s when decoded.
874  */
875 static int
check_segment(const unsigned char * s,size_t length)876 check_segment(const unsigned char *s, size_t length) {
877 
878   size_t n = 0;
879 
880   while (length) {
881     if (*s == '%') {
882       if (length < 2 || !(isxdigit(s[1]) && isxdigit(s[2])))
883         return -1;
884 
885       s += 2;
886       length -= 2;
887     }
888 
889     ++s; ++n; --length;
890   }
891 
892   return n;
893 }
894 
895 static int
cmdline_input(char * text,str * buf)896 cmdline_input(char *text, str *buf) {
897   int len;
898   len = check_segment((unsigned char *)text, strlen(text));
899 
900   if (len < 0)
901     return 0;
902 
903   buf->s = (unsigned char *)coap_malloc(len);
904   if (!buf->s)
905     return 0;
906 
907   buf->length = len;
908   decode_segment((unsigned char *)text, strlen(text), buf->s);
909   return 1;
910 }
911 
912 static int
cmdline_input_from_file(char * filename,str * buf)913 cmdline_input_from_file(char *filename, str *buf) {
914   FILE *inputfile = NULL;
915   ssize_t len;
916   int result = 1;
917   struct stat statbuf;
918 
919   if (!filename || !buf)
920     return 0;
921 
922   if (filename[0] == '-' && !filename[1]) { /* read from stdin */
923     buf->length = 20000;
924     buf->s = (unsigned char *)coap_malloc(buf->length);
925     if (!buf->s)
926       return 0;
927 
928     inputfile = stdin;
929   } else {
930     /* read from specified input file */
931     if (stat(filename, &statbuf) < 0) {
932       perror("cmdline_input_from_file: stat");
933       return 0;
934     }
935 
936     buf->length = statbuf.st_size;
937     buf->s = (unsigned char *)coap_malloc(buf->length);
938     if (!buf->s)
939       return 0;
940 
941     inputfile = fopen(filename, "r");
942     if ( !inputfile ) {
943       perror("cmdline_input_from_file: fopen");
944       coap_free(buf->s);
945       return 0;
946     }
947   }
948 
949   len = fread(buf->s, 1, buf->length, inputfile);
950 
951   if (len < 0 || ((size_t)len < buf->length)) {
952     if (ferror(inputfile) != 0) {
953       perror("cmdline_input_from_file: fread");
954       coap_free(buf->s);
955       buf->length = 0;
956       buf->s = NULL;
957       result = 0;
958     } else {
959       buf->length = len;
960     }
961   }
962 
963   if (inputfile != stdin)
964     fclose(inputfile);
965 
966   return result;
967 }
968 
969 static method_t
cmdline_method(char * arg)970 cmdline_method(char *arg) {
971   static char *methods[] =
972     { 0, "get", "post", "put", "delete", 0};
973   unsigned char i;
974 
975   for (i=1; methods[i] && strcasecmp(arg,methods[i]) != 0 ; ++i)
976     ;
977 
978   return i;     /* note that we do not prevent illegal methods */
979 }
980 
981 static coap_context_t *
get_context(const char * node,const char * port)982 get_context(const char *node, const char *port) {
983   coap_context_t *ctx = NULL;
984   int s;
985   struct addrinfo hints;
986   struct addrinfo *result, *rp;
987 
988   memset(&hints, 0, sizeof(struct addrinfo));
989   hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
990   hints.ai_socktype = SOCK_DGRAM; /* Coap uses UDP */
991   hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV | AI_ALL;
992 
993   s = getaddrinfo(node, port, &hints, &result);
994   if ( s != 0 ) {
995     fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
996     return NULL;
997   }
998 
999   /* iterate through results until success */
1000   for (rp = result; rp != NULL; rp = rp->ai_next) {
1001     coap_address_t addr;
1002 
1003     if (rp->ai_addrlen <= sizeof(addr.addr)) {
1004       coap_address_init(&addr);
1005       addr.size = rp->ai_addrlen;
1006       memcpy(&addr.addr, rp->ai_addr, rp->ai_addrlen);
1007 
1008       ctx = coap_new_context(&addr);
1009       if (ctx) {
1010         /* TODO: output address:port for successful binding */
1011         goto finish;
1012       }
1013     }
1014   }
1015 
1016   fprintf(stderr, "no context available for interface '%s'\n", node);
1017 
1018   finish:
1019   freeaddrinfo(result);
1020   return ctx;
1021 }
1022 
1023 int
main(int argc,char ** argv)1024 main(int argc, char **argv) {
1025   coap_context_t  *ctx = NULL;
1026   coap_address_t dst;
1027   static char addr[INET6_ADDRSTRLEN];
1028   void *addrptr = NULL;
1029   fd_set readfds;
1030   struct timeval tv;
1031   int result;
1032   coap_tick_t now;
1033   coap_queue_t *nextpdu;
1034   coap_pdu_t  *pdu;
1035   static str server;
1036   unsigned short port = COAP_DEFAULT_PORT;
1037   char port_str[NI_MAXSERV] = "0";
1038   char node_str[NI_MAXHOST] = "";
1039   int opt, res;
1040   coap_log_t log_level = LOG_WARNING;
1041   coap_tid_t tid = COAP_INVALID_TID;
1042 
1043   while ((opt = getopt(argc, argv, "Na:b:e:f:g:m:p:s:t:o:v:A:B:O:P:T:")) != -1) {
1044     switch (opt) {
1045     case 'a' :
1046       strncpy(node_str, optarg, NI_MAXHOST-1);
1047       node_str[NI_MAXHOST - 1] = '\0';
1048       break;
1049     case 'b' :
1050       cmdline_blocksize(optarg);
1051       break;
1052     case 'B' :
1053       wait_seconds = atoi(optarg);
1054       break;
1055     case 'e' :
1056       if (!cmdline_input(optarg,&payload))
1057         payload.length = 0;
1058       break;
1059     case 'f' :
1060       if (!cmdline_input_from_file(optarg,&payload))
1061         payload.length = 0;
1062       break;
1063     case 'p' :
1064       strncpy(port_str, optarg, NI_MAXSERV-1);
1065       port_str[NI_MAXSERV - 1] = '\0';
1066       break;
1067     case 'm' :
1068       method = cmdline_method(optarg);
1069       break;
1070     case 'N' :
1071       msgtype = COAP_MESSAGE_NON;
1072       break;
1073     case 's' :
1074       cmdline_subscribe(optarg);
1075       break;
1076     case 'o' :
1077       output_file.length = strlen(optarg);
1078       output_file.s = (unsigned char *)coap_malloc(output_file.length + 1);
1079 
1080       if (!output_file.s) {
1081         fprintf(stderr, "cannot set output file: insufficient memory\n");
1082         exit(-1);
1083       } else {
1084         /* copy filename including trailing zero */
1085         memcpy(output_file.s, optarg, output_file.length + 1);
1086       }
1087       break;
1088     case 'A' :
1089       cmdline_content_type(optarg,COAP_OPTION_ACCEPT);
1090       break;
1091     case 't' :
1092       cmdline_content_type(optarg,COAP_OPTION_CONTENT_TYPE);
1093       break;
1094     case 'O' :
1095       cmdline_option(optarg);
1096       break;
1097     case 'P' :
1098       if (!cmdline_proxy(optarg)) {
1099         fprintf(stderr, "error specifying proxy address\n");
1100         exit(-1);
1101       }
1102       break;
1103     case 'T' :
1104       cmdline_token(optarg);
1105       break;
1106     case 'v' :
1107       log_level = strtol(optarg, NULL, 10);
1108       break;
1109     default:
1110       usage( argv[0], PACKAGE_VERSION );
1111       exit( 1 );
1112     }
1113   }
1114 
1115   coap_set_log_level(log_level);
1116 
1117   if ( optind < argc )
1118     cmdline_uri( argv[optind] );
1119   else {
1120     usage( argv[0], PACKAGE_VERSION );
1121     exit( 1 );
1122   }
1123 
1124   if (proxy.length) {
1125     server = proxy;
1126     port = proxy_port;
1127   } else {
1128     server = uri.host;
1129     port = uri.port;
1130   }
1131 
1132   /* resolve destination address where server should be sent */
1133   res = resolve_address(&server, &dst.addr.sa);
1134 
1135   if (res < 0) {
1136     fprintf(stderr, "failed to resolve address\n");
1137     exit(-1);
1138   }
1139 
1140   dst.size = res;
1141   dst.addr.sin.sin_port = htons(port);
1142 
1143   /* add Uri-Host if server address differs from uri.host */
1144 
1145   switch (dst.addr.sa.sa_family) {
1146   case AF_INET:
1147     addrptr = &dst.addr.sin.sin_addr;
1148 
1149     /* create context for IPv4 */
1150     ctx = get_context(node_str[0] == 0 ? "0.0.0.0" : node_str, port_str);
1151     break;
1152   case AF_INET6:
1153     addrptr = &dst.addr.sin6.sin6_addr;
1154 
1155     /* create context for IPv6 */
1156     ctx = get_context(node_str[0] == 0 ? "::" : node_str, port_str);
1157     break;
1158   default:
1159     ;
1160   }
1161 
1162   if (!ctx) {
1163     coap_log(LOG_EMERG, "cannot create context\n");
1164     return -1;
1165   }
1166 
1167   coap_register_option(ctx, COAP_OPTION_BLOCK2);
1168   coap_register_response_handler(ctx, message_handler);
1169 
1170   /* construct CoAP message */
1171 
1172   if (!proxy.length && addrptr
1173       && (inet_ntop(dst.addr.sa.sa_family, addrptr, addr, sizeof(addr)) != 0)
1174       && (strlen(addr) != uri.host.length
1175       || memcmp(addr, uri.host.s, uri.host.length) != 0)) {
1176         /* add Uri-Host */
1177 
1178         coap_insert(&optlist,
1179                     new_option_node(COAP_OPTION_URI_HOST,
1180                     uri.host.length,
1181                     uri.host.s));
1182   }
1183 
1184   /* set block option if requested at commandline */
1185   if (flags & FLAGS_BLOCK)
1186     set_blocksize();
1187 
1188   if (! (pdu = coap_new_request(ctx, method, &optlist, payload.s, payload.length)))
1189     return -1;
1190 
1191 #ifndef NDEBUG
1192   if (LOG_DEBUG <= coap_get_log_level()) {
1193     debug("sending CoAP request:\n");
1194     coap_show_pdu(pdu);
1195   }
1196 #endif
1197 
1198   if (pdu->hdr->type == COAP_MESSAGE_CON)
1199     tid = coap_send_confirmed(ctx, ctx->endpoint, &dst, pdu);
1200   else
1201     tid = coap_send(ctx, ctx->endpoint, &dst, pdu);
1202 
1203   if (pdu->hdr->type != COAP_MESSAGE_CON || tid == COAP_INVALID_TID)
1204     coap_delete_pdu(pdu);
1205 
1206   set_timeout(&max_wait, wait_seconds);
1207   debug("timeout is set to %d seconds\n", wait_seconds);
1208 
1209   while ( !(ready && coap_can_exit(ctx)) ) {
1210     FD_ZERO(&readfds);
1211     FD_SET( ctx->sockfd, &readfds );
1212 
1213     nextpdu = coap_peek_next( ctx );
1214 
1215     coap_ticks(&now);
1216     while (nextpdu && nextpdu->t <= now - ctx->sendqueue_basetime) {
1217       coap_retransmit( ctx, coap_pop_next( ctx ));
1218       nextpdu = coap_peek_next( ctx );
1219     }
1220 
1221     if (nextpdu && nextpdu->t < min(obs_wait ? obs_wait : max_wait, max_wait) - now) {
1222       /* set timeout if there is a pdu to send */
1223       tv.tv_usec = ((nextpdu->t) % COAP_TICKS_PER_SECOND) * 1000000 / COAP_TICKS_PER_SECOND;
1224       tv.tv_sec = (nextpdu->t) / COAP_TICKS_PER_SECOND;
1225     } else {
1226       /* check if obs_wait fires before max_wait */
1227       if (obs_wait && obs_wait < max_wait) {
1228         tv.tv_usec = ((obs_wait - now) % COAP_TICKS_PER_SECOND) * 1000000 / COAP_TICKS_PER_SECOND;
1229         tv.tv_sec = (obs_wait - now) / COAP_TICKS_PER_SECOND;
1230       } else {
1231         tv.tv_usec = ((max_wait - now) % COAP_TICKS_PER_SECOND) * 1000000 / COAP_TICKS_PER_SECOND;
1232         tv.tv_sec = (max_wait - now) / COAP_TICKS_PER_SECOND;
1233       }
1234     }
1235 
1236     result = select(ctx->sockfd + 1, &readfds, 0, 0, &tv);
1237 
1238     if ( result < 0 ) {   /* error */
1239       perror("select");
1240     } else if ( result > 0 ) {  /* read from socket */
1241       if ( FD_ISSET( ctx->sockfd, &readfds ) ) {
1242         coap_read( ctx );       /* read received data */
1243         /* coap_dispatch( ctx );  /\* and dispatch PDUs from receivequeue *\/ */
1244       }
1245     } else { /* timeout */
1246       coap_ticks(&now);
1247       if (max_wait <= now) {
1248         info("timeout\n");
1249         break;
1250       }
1251       if (obs_wait && obs_wait <= now) {
1252         debug("clear observation relationship\n");
1253         clear_obs(ctx, ctx->endpoint, &dst); /* FIXME: handle error case COAP_TID_INVALID */
1254 
1255         /* make sure that the obs timer does not fire again */
1256         obs_wait = 0;
1257         obs_seconds = 0;
1258       }
1259     }
1260   }
1261 
1262   close_output();
1263 
1264   coap_delete_list(optlist);
1265   coap_free_context( ctx );
1266 
1267   return 0;
1268 }
1269