1 /* block.c -- block transfer
2  *
3  * Copyright (C) 2010--2012,2015 Olaf Bergmann <bergmann@tzi.org>
4  *
5  * This file is part of the CoAP library libcoap. Please see
6  * README for terms of use.
7  */
8 
9 #include "coap_config.h"
10 
11 #if defined(HAVE_ASSERT_H) && !defined(assert)
12 # include <assert.h>
13 #endif
14 
15 #include "debug.h"
16 #include "block.h"
17 
18 #if (COAP_MAX_PDU_SIZE - 6) < (1 << (COAP_MAX_BLOCK_SZX + 4))
19 #error "COAP_MAX_BLOCK_SZX too large"
20 #endif
21 
22 #define min(a,b) ((a) < (b) ? (a) : (b))
23 
24 #ifndef WITHOUT_BLOCK
25 unsigned int
coap_opt_block_num(const coap_opt_t * block_opt)26 coap_opt_block_num(const coap_opt_t *block_opt) {
27   unsigned int num = 0;
28   unsigned short len;
29 
30   len = coap_opt_length(block_opt);
31 
32   if (len == 0) {
33     return 0;
34   }
35 
36   if (len > 1) {
37     num = coap_decode_var_bytes(COAP_OPT_VALUE(block_opt),
38 				COAP_OPT_LENGTH(block_opt) - 1);
39   }
40 
41   return (num << 4) | ((*COAP_OPT_BLOCK_LAST(block_opt) & 0xF0) >> 4);
42 }
43 
44 int
coap_get_block(coap_pdu_t * pdu,unsigned short type,coap_block_t * block)45 coap_get_block(coap_pdu_t *pdu, unsigned short type, coap_block_t *block) {
46   coap_opt_iterator_t opt_iter;
47   coap_opt_t *option;
48 
49   assert(block);
50   memset(block, 0, sizeof(coap_block_t));
51 
52   if (pdu && (option = coap_check_option(pdu, type, &opt_iter))) {
53     block->szx = COAP_OPT_BLOCK_SZX(option);
54     if (COAP_OPT_BLOCK_MORE(option))
55       block->m = 1;
56     block->num = coap_opt_block_num(option);
57     return 1;
58   }
59 
60   return 0;
61 }
62 
63 int
coap_write_block_opt(coap_block_t * block,unsigned short type,coap_pdu_t * pdu,size_t data_length)64 coap_write_block_opt(coap_block_t *block, unsigned short type,
65 		     coap_pdu_t *pdu, size_t data_length) {
66   size_t start, want, avail;
67   unsigned char buf[3];
68 
69   assert(pdu);
70 
71   start = block->num << (block->szx + 4);
72   if (data_length <= start) {
73     debug("illegal block requested\n");
74     return -2;
75   }
76 
77   avail = pdu->max_size - pdu->length - 4;
78   want = 1 << (block->szx + 4);
79 
80   /* check if entire block fits in message */
81   if (want <= avail) {
82     block->m = want < data_length - start;
83   } else {
84     /* Sender has requested a block that is larger than the remaining
85      * space in pdu. This is ok if the remaining data fits into the pdu
86      * anyway. The block size needs to be adjusted only if there is more
87      * data left that cannot be delivered in this message. */
88 
89     if (data_length - start <= avail) {
90 
91       /* it's the final block and everything fits in the message */
92       block->m = 0;
93     } else {
94       unsigned char szx;
95 
96       /* we need to decrease the block size */
97       if (avail < 16) { 	/* bad luck, this is the smallest block size */
98 	debug("not enough space, even the smallest block does not fit");
99 	return -3;
100       }
101       debug("decrease block size for %zu to %d\n", avail, coap_fls(avail) - 5);
102       szx = block->szx;
103       block->szx = coap_fls(avail) - 5;
104       block->m = 1;
105       block->num <<= szx - block->szx;
106     }
107   }
108 
109   /* to re-encode the block option */
110   coap_add_option(pdu, type, coap_encode_var_bytes(buf, ((block->num << 4) |
111 							 (block->m << 3) |
112 							 block->szx)),
113 		  buf);
114 
115   return 1;
116 }
117 
118 int
coap_add_block(coap_pdu_t * pdu,unsigned int len,const unsigned char * data,unsigned int block_num,unsigned char block_szx)119 coap_add_block(coap_pdu_t *pdu, unsigned int len, const unsigned char *data,
120 	       unsigned int block_num, unsigned char block_szx) {
121   size_t start;
122   start = block_num << (block_szx + 4);
123 
124   if (len <= start)
125     return 0;
126 
127   return coap_add_data(pdu,
128 		       min(len - start, (unsigned int)(1 << (block_szx + 4))),
129 		       data + start);
130 }
131 #endif /* WITHOUT_BLOCK  */
132