• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* block.c -- block transfer
2  *
3  * Copyright (C) 2010--2012,2015-2019 Olaf Bergmann <bergmann@tzi.org> and others
4  *
5  * This file is part of the CoAP library libcoap. Please see
6  * README for terms of use.
7  */
8 
9 #include "coap_internal.h"
10 
11 #ifndef min
12 #define min(a,b) ((a) < (b) ? (a) : (b))
13 #endif
14 
15 #ifndef WITHOUT_BLOCK
16 unsigned int
coap_opt_block_num(const coap_opt_t * block_opt)17 coap_opt_block_num(const coap_opt_t *block_opt) {
18   unsigned int num = 0;
19   uint16_t len;
20 
21   len = coap_opt_length(block_opt);
22 
23   if (len == 0) {
24     return 0;
25   }
26 
27   if (len > 1) {
28     num = coap_decode_var_bytes(coap_opt_value(block_opt),
29                                 coap_opt_length(block_opt) - 1);
30   }
31 
32   return (num << 4) | ((*COAP_OPT_BLOCK_LAST(block_opt) & 0xF0) >> 4);
33 }
34 
35 int
coap_get_block(coap_pdu_t * pdu,uint16_t type,coap_block_t * block)36 coap_get_block(coap_pdu_t *pdu, uint16_t type, coap_block_t *block) {
37   coap_opt_iterator_t opt_iter;
38   coap_opt_t *option;
39 
40   assert(block);
41   memset(block, 0, sizeof(coap_block_t));
42 
43   if (pdu && (option = coap_check_option(pdu, type, &opt_iter)) != NULL) {
44     unsigned int num;
45 
46     block->szx = COAP_OPT_BLOCK_SZX(option);
47     if (COAP_OPT_BLOCK_MORE(option))
48       block->m = 1;
49 
50     /* The block number is at most 20 bits, so values above 2^20 - 1
51      * are illegal. */
52     num = coap_opt_block_num(option);
53     if (num > 0xFFFFF) {
54       return 0;
55     }
56     block->num = num;
57     return 1;
58   }
59 
60   return 0;
61 }
62 
63 int
coap_write_block_opt(coap_block_t * block,uint16_t type,coap_pdu_t * pdu,size_t data_length)64 coap_write_block_opt(coap_block_t *block, uint16_t type,
65                      coap_pdu_t *pdu, size_t data_length) {
66   size_t start, want, avail;
67   unsigned char buf[4];
68 
69   assert(pdu);
70 
71   start = block->num << (block->szx + 4);
72   if (data_length <= start) {
73     coap_log(LOG_DEBUG, "illegal block requested\n");
74     return -2;
75   }
76 
77   assert(pdu->max_size > 0);
78   avail = pdu->max_size - pdu->used_size - 4;
79   want = (size_t)1 << (block->szx + 4);
80 
81   /* check if entire block fits in message */
82   if (want <= avail) {
83     block->m = want < data_length - start;
84   } else {
85     /* Sender has requested a block that is larger than the remaining
86      * space in pdu. This is ok if the remaining data fits into the pdu
87      * anyway. The block size needs to be adjusted only if there is more
88      * data left that cannot be delivered in this message. */
89 
90     if (data_length - start <= avail) {
91 
92       /* it's the final block and everything fits in the message */
93       block->m = 0;
94     } else {
95       unsigned int szx;
96       int newBlockSize;
97 
98       /* we need to decrease the block size */
99       if (avail < 16) {         /* bad luck, this is the smallest block size */
100         coap_log(LOG_DEBUG,
101                  "not enough space, even the smallest block does not fit");
102         return -3;
103       }
104       newBlockSize = coap_flsll((long long)avail) - 5;
105       coap_log(LOG_DEBUG,
106                "decrease block size for %zu to %d\n", avail, newBlockSize);
107       szx = block->szx;
108       block->szx = newBlockSize;
109       block->m = 1;
110       block->num <<= szx - block->szx;
111     }
112   }
113 
114   /* to re-encode the block option */
115   coap_add_option(pdu, type, coap_encode_var_safe(buf, sizeof(buf),
116                                                   ((block->num << 4) |
117                                                    (block->m << 3) |
118                                                    block->szx)),
119                   buf);
120 
121   return 1;
122 }
123 
124 int
coap_add_block(coap_pdu_t * pdu,unsigned int len,const uint8_t * data,unsigned int block_num,unsigned char block_szx)125 coap_add_block(coap_pdu_t *pdu, unsigned int len, const uint8_t *data,
126                unsigned int block_num, unsigned char block_szx) {
127   unsigned int start;
128   start = block_num << (block_szx + 4);
129 
130   if (len <= start)
131     return 0;
132 
133   return coap_add_data(pdu,
134                        min(len - start, (1U << (block_szx + 4))),
135                        data + start);
136 }
137 
138 /*
139  * Note that the COAP_OPTION_ have to be added in the correct order
140  */
141 void
coap_add_data_blocked_response(coap_resource_t * resource,coap_session_t * session,coap_pdu_t * request,coap_pdu_t * response,const coap_binary_t * token,uint16_t media_type,int maxage,size_t length,const uint8_t * data)142 coap_add_data_blocked_response(coap_resource_t *resource,
143                        coap_session_t *session,
144                        coap_pdu_t *request,
145                        coap_pdu_t *response,
146                        const coap_binary_t *token,
147                        uint16_t media_type,
148                        int maxage,
149                        size_t length,
150                        const uint8_t* data
151 ) {
152   coap_key_t etag;
153   unsigned char buf[4];
154   coap_block_t block2 = { 0, 0, 0 };
155   int block2_requested = 0;
156   coap_subscription_t *subscription = coap_find_observer(resource, session, token);
157 
158   /*
159    * Need to check that a valid block is getting asked for so that the
160    * correct options are put into the PDU.
161    */
162   if (request) {
163     if (coap_get_block(request, COAP_OPTION_BLOCK2, &block2)) {
164       block2_requested = 1;
165       if (length <= (block2.num << (block2.szx + 4))) {
166         coap_log(LOG_DEBUG, "Illegal block requested (%d > last = %zu)\n",
167                  block2.num,
168                  length >> (block2.szx + 4));
169         response->code = COAP_RESPONSE_CODE(400);
170         goto error;
171       }
172     }
173   }
174   else if (subscription && subscription->has_block2) {
175     block2 = subscription->block2;
176     block2.num = 0;
177     block2_requested = 1;
178   }
179   response->code = COAP_RESPONSE_CODE(205);
180 
181   /* add etag for the resource */
182   memset(etag, 0, sizeof(etag));
183   coap_hash(data, length, etag);
184   coap_add_option(response, COAP_OPTION_ETAG, sizeof(etag), etag);
185 
186   if ((block2.num == 0) && subscription) {
187     coap_add_option(response, COAP_OPTION_OBSERVE,
188                     coap_encode_var_safe(buf, sizeof (buf),
189                                          resource->observe),
190                     buf);
191   }
192 
193   coap_add_option(response, COAP_OPTION_CONTENT_TYPE,
194                   coap_encode_var_safe(buf, sizeof(buf),
195                                        media_type),
196                   buf);
197 
198   if (maxage >= 0) {
199     coap_add_option(response,
200                     COAP_OPTION_MAXAGE,
201                     coap_encode_var_safe(buf, sizeof(buf), maxage), buf);
202   }
203 
204   if (block2_requested) {
205     int res;
206 
207     res = coap_write_block_opt(&block2, COAP_OPTION_BLOCK2, response,
208                                length);
209 
210     switch (res) {
211     case -2:                        /* illegal block (caught above) */
212         response->code = COAP_RESPONSE_CODE(400);
213         goto error;
214     case -1:                        /* should really not happen */
215         assert(0);
216         /* fall through if assert is a no-op */
217     case -3:                        /* cannot handle request */
218         response->code = COAP_RESPONSE_CODE(500);
219         goto error;
220     default:                        /* everything is good */
221         ;
222     }
223 
224     coap_add_option(response,
225                     COAP_OPTION_SIZE2,
226                     coap_encode_var_safe(buf, sizeof(buf), length),
227                     buf);
228 
229     coap_add_block(response, length, data,
230                    block2.num, block2.szx);
231     return;
232   }
233 
234   /*
235    * BLOCK2 not requested
236    */
237   if (!coap_add_data(response, length, data)) {
238     /* set initial block size, will be lowered by
239      * coap_write_block_opt) automatically */
240     block2.num = 0;
241     block2.szx = 6;
242     coap_write_block_opt(&block2, COAP_OPTION_BLOCK2, response,
243                          length);
244 
245     coap_add_option(response,
246                     COAP_OPTION_SIZE2,
247                     coap_encode_var_safe(buf, sizeof(buf), length),
248                     buf);
249 
250     coap_add_block(response, length, data,
251                    block2.num, block2.szx);
252   }
253   return;
254 
255 error:
256   coap_add_data(response,
257                 strlen(coap_response_phrase(response->code)),
258                 (const unsigned char *)coap_response_phrase(response->code));
259 }
260 
261 #endif /* WITHOUT_BLOCK  */
262