libcoap  4.2.0
block.c
Go to the documentation of this file.
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_config.h"
10 
11 #if defined(HAVE_ASSERT_H) && !defined(assert)
12 # include <assert.h>
13 #endif
14 
15 #include "libcoap.h"
16 #include "coap_debug.h"
17 #include "block.h"
18 #include "resource.h"
19 #include "coap_hashkey.h"
20 
21 #ifndef min
22 #define min(a,b) ((a) < (b) ? (a) : (b))
23 #endif
24 
25 #ifndef WITHOUT_BLOCK
26 unsigned int
27 coap_opt_block_num(const coap_opt_t *block_opt) {
28  unsigned int num = 0;
29  uint16_t len;
30 
31  len = coap_opt_length(block_opt);
32 
33  if (len == 0) {
34  return 0;
35  }
36 
37  if (len > 1) {
38  num = coap_decode_var_bytes(coap_opt_value(block_opt),
39  coap_opt_length(block_opt) - 1);
40  }
41 
42  return (num << 4) | ((*COAP_OPT_BLOCK_LAST(block_opt) & 0xF0) >> 4);
43 }
44 
45 int
46 coap_get_block(coap_pdu_t *pdu, uint16_t type, coap_block_t *block) {
47  coap_opt_iterator_t opt_iter;
48  coap_opt_t *option;
49 
50  assert(block);
51  memset(block, 0, sizeof(coap_block_t));
52 
53  if (pdu && (option = coap_check_option(pdu, type, &opt_iter)) != NULL) {
54  unsigned int num;
55 
56  block->szx = COAP_OPT_BLOCK_SZX(option);
57  if (COAP_OPT_BLOCK_MORE(option))
58  block->m = 1;
59 
60  /* The block number is at most 20 bits, so values above 2^20 - 1
61  * are illegal. */
62  num = coap_opt_block_num(option);
63  if (num > 0xFFFFF) {
64  return 0;
65  }
66  block->num = num;
67  return 1;
68  }
69 
70  return 0;
71 }
72 
73 int
74 coap_write_block_opt(coap_block_t *block, uint16_t type,
75  coap_pdu_t *pdu, size_t data_length) {
76  size_t start, want, avail;
77  unsigned char buf[4];
78 
79  assert(pdu);
80 
81  start = block->num << (block->szx + 4);
82  if (data_length <= start) {
83  coap_log(LOG_DEBUG, "illegal block requested\n");
84  return -2;
85  }
86 
87  assert(pdu->max_size > 0);
88  avail = pdu->max_size - pdu->used_size - 4;
89  want = (size_t)1 << (block->szx + 4);
90 
91  /* check if entire block fits in message */
92  if (want <= avail) {
93  block->m = want < data_length - start;
94  } else {
95  /* Sender has requested a block that is larger than the remaining
96  * space in pdu. This is ok if the remaining data fits into the pdu
97  * anyway. The block size needs to be adjusted only if there is more
98  * data left that cannot be delivered in this message. */
99 
100  if (data_length - start <= avail) {
101 
102  /* it's the final block and everything fits in the message */
103  block->m = 0;
104  } else {
105  unsigned int szx;
106  int newBlockSize;
107 
108  /* we need to decrease the block size */
109  if (avail < 16) { /* bad luck, this is the smallest block size */
111  "not enough space, even the smallest block does not fit");
112  return -3;
113  }
114  newBlockSize = coap_flsll((long long)avail) - 5;
116  "decrease block size for %zu to %d\n", avail, newBlockSize);
117  szx = block->szx;
118  block->szx = newBlockSize;
119  block->m = 1;
120  block->num <<= szx - block->szx;
121  }
122  }
123 
124  /* to re-encode the block option */
125  coap_add_option(pdu, type, coap_encode_var_safe(buf, sizeof(buf),
126  ((block->num << 4) |
127  (block->m << 3) |
128  block->szx)),
129  buf);
130 
131  return 1;
132 }
133 
134 int
135 coap_add_block(coap_pdu_t *pdu, unsigned int len, const uint8_t *data,
136  unsigned int block_num, unsigned char block_szx) {
137  unsigned int start;
138  start = block_num << (block_szx + 4);
139 
140  if (len <= start)
141  return 0;
142 
143  return coap_add_data(pdu,
144  min(len - start, (1U << (block_szx + 4))),
145  data + start);
146 }
147 
148 /*
149  * Note that the COAP_OPTION_ have to be added in the correct order
150  */
151 void
154  coap_pdu_t *request,
155  coap_pdu_t *response,
156  const coap_binary_t *token,
157  uint16_t media_type,
158  int maxage,
159  size_t length,
160  const uint8_t* data
161 ) {
162  coap_key_t etag;
163  unsigned char buf[4];
164  coap_block_t block2 = { 0, 0, 0 };
165  int block2_requested = 0;
166  coap_subscription_t *subscription = coap_find_observer(resource, session, token);
167 
168  /*
169  * Need to check that a valid block is getting asked for so that the
170  * correct options are put into the PDU.
171  */
172  if (request) {
173  if (coap_get_block(request, COAP_OPTION_BLOCK2, &block2)) {
174  block2_requested = 1;
175  if (length <= (block2.num << (block2.szx + 4))) {
176  coap_log(LOG_DEBUG, "Illegal block requested (%d > last = %zu)\n",
177  block2.num,
178  length >> (block2.szx + 4));
179  response->code = COAP_RESPONSE_CODE(400);
180  goto error;
181  }
182  }
183  }
184  else if (subscription && subscription->has_block2) {
185  block2 = subscription->block2;
186  block2.num = 0;
187  block2_requested = 1;
188  }
189  response->code = COAP_RESPONSE_CODE(205);
190 
191  /* add etag for the resource */
192  memset(etag, 0, sizeof(etag));
193  coap_hash(data, length, etag);
194  coap_add_option(response, COAP_OPTION_ETAG, sizeof(etag), etag);
195 
196  if ((block2.num == 0) && subscription) {
198  coap_encode_var_safe(buf, sizeof (buf),
199  resource->observe),
200  buf);
201  }
202 
204  coap_encode_var_safe(buf, sizeof(buf),
205  media_type),
206  buf);
207 
208  if (maxage >= 0) {
209  coap_add_option(response,
211  coap_encode_var_safe(buf, sizeof(buf), maxage), buf);
212  }
213 
214  if (block2_requested) {
215  int res;
216 
217  res = coap_write_block_opt(&block2, COAP_OPTION_BLOCK2, response,
218  length);
219 
220  switch (res) {
221  case -2: /* illegal block (caught above) */
222  response->code = COAP_RESPONSE_CODE(400);
223  goto error;
224  case -1: /* should really not happen */
225  assert(0);
226  /* fall through if assert is a no-op */
227  case -3: /* cannot handle request */
228  response->code = COAP_RESPONSE_CODE(500);
229  goto error;
230  default: /* everything is good */
231  ;
232  }
233 
234  coap_add_option(response,
236  coap_encode_var_safe(buf, sizeof(buf), length),
237  buf);
238 
239  coap_add_block(response, length, data,
240  block2.num, block2.szx);
241  return;
242  }
243 
244  /*
245  * BLOCK2 not requested
246  */
247  if (!coap_add_data(response, length, data)) {
248  /* set initial block size, will be lowered by
249  * coap_write_block_opt) automatically */
250  block2.num = 0;
251  block2.szx = 6;
252  coap_write_block_opt(&block2, COAP_OPTION_BLOCK2, response,
253  length);
254 
255  coap_add_option(response,
257  coap_encode_var_safe(buf, sizeof(buf), length),
258  buf);
259 
260  coap_add_block(response, length, data,
261  block2.num, block2.szx);
262  }
263  return;
264 
265 error:
266  coap_add_data(response,
267  strlen(coap_response_phrase(response->code)),
268  (const unsigned char *)coap_response_phrase(response->code));
269 }
270 
271 #endif /* WITHOUT_BLOCK */
uint8_t code
request method (value 1–10) or response code (value 40-255)
Definition: pdu.h:289
int coap_flsll(long long i)
Definition: encode.c:26
uint8_t coap_opt_t
Use byte-oriented access methods here because sliding a complex struct coap_opt_t over the data buffe...
Definition: option.h:25
unsigned int coap_opt_block_num(const coap_opt_t *block_opt)
Returns the value of field num in the given block option block_opt.
Definition: block.c:27
#define min(a, b)
Definition: block.c:22
int coap_add_data(coap_pdu_t *pdu, size_t len, const uint8_t *data)
Adds given data to the pdu that is passed as first parameter.
Definition: pdu.c:302
#define COAP_RESPONSE_CODE(N)
Definition: pdu.h:132
int coap_add_block(coap_pdu_t *pdu, unsigned int len, const uint8_t *data, unsigned int block_num, unsigned char block_szx)
Adds the block_num block of size 1 << (block_szx + 4) from source data to pdu.
Definition: block.c:135
unsigned int coap_encode_var_safe(uint8_t *buf, size_t length, unsigned int val)
Encodes multiple-length byte sequences.
Definition: encode.c:45
unsigned char coap_key_t[4]
Definition: coap_hashkey.h:22
#define COAP_OPTION_OBSERVE
Definition: pdu.h:112
#define COAP_OPTION_ETAG
Definition: pdu.h:94
#define COAP_OPTION_MAXAGE
Definition: pdu.h:101
Debug.
Definition: coap_debug.h:49
#define COAP_OPTION_CONTENT_TYPE
Definition: pdu.h:100
coap_subscription_t * coap_find_observer(coap_resource_t *resource, coap_session_t *session, const coap_binary_t *token)
Returns a subscription object for given peer.
Definition: resource.c:587
coap_opt_t * coap_check_option(coap_pdu_t *pdu, uint16_t type, coap_opt_iterator_t *oi)
Retrieves the first option of type type from pdu.
Definition: option.c:207
coap_session_t * session
transaction session
Definition: async.h:47
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)
Adds the appropriate part of data to the response pdu.
Definition: block.c:152
structure for CoAP PDUs token, if any, follows the fixed size header, then options until payload mark...
Definition: pdu.h:287
uint16_t coap_opt_length(const coap_opt_t *opt)
Returns the length of the given option.
Definition: option.c:249
#define assert(...)
Definition: mem.c:18
#define COAP_OPT_BLOCK_LAST(opt)
Returns the value of the least significant byte of a Block option opt.
Definition: block.h:47
size_t used_size
used bytes of storage for token, options and payload
Definition: pdu.h:296
size_t max_size
maximum size for token, options and payload, or zero for variable size pdu
Definition: pdu.h:297
Iterator to run through PDU options.
Definition: option.h:237
Coap binary data definition.
Definition: str.h:43
Generic resource handling.
coap_block_t block2
GET request Block2 definition.
Definition: subscribe.h:66
size_t coap_add_option(coap_pdu_t *pdu, uint16_t type, size_t len, const uint8_t *data)
Adds option of given type to pdu that is passed as first parameter.
Definition: pdu.c:229
int coap_write_block_opt(coap_block_t *block, uint16_t type, coap_pdu_t *pdu, size_t data_length)
Writes a block option of type type to message pdu.
Definition: block.c:74
#define COAP_OPTION_SIZE2
Definition: pdu.h:105
#define COAP_OPTION_BLOCK2
Definition: pdu.h:117
Subscriber information.
Definition: subscribe.h:56
const char * coap_response_phrase(unsigned char code)
Returns a human-readable response phrase for the specified CoAP response code.
Definition: pdu.c:384
Structure of Block options.
Definition: block.h:36
unsigned int coap_decode_var_bytes(const uint8_t *buf, unsigned int len)
Decodes multiple-length byte sequences.
Definition: encode.c:36
unsigned int has_block2
GET request had Block2 definition.
Definition: subscribe.h:65
const uint8_t * coap_opt_value(const coap_opt_t *opt)
Returns a pointer to the value of the given option.
Definition: option.c:286
uint8_t token[8]
the token to use in a response
Definition: async.h:51
#define COAP_OPT_BLOCK_SZX(opt)
Returns the value of the SZX-field of a Block option opt.
Definition: block.h:55
int coap_get_block(coap_pdu_t *pdu, uint16_t type, coap_block_t *block)
Initializes block from pdu.
Definition: block.c:46
definition of hash key type and helper functions
#define coap_log(level,...)
Logging function.
Definition: coap_debug.h:122
unsigned char uint8_t
Definition: uthash.h:79
#define COAP_OPT_BLOCK_MORE(opt)
Returns the value of the More-bit of a Block option opt.
Definition: block.h:51
unsigned int observe
The next value for the Observe option.
Definition: resource.h:103
#define coap_hash(String, Length, Result)
Definition: coap_hashkey.h:36
unsigned int m
1 if more blocks follow, 0 otherwise
Definition: block.h:38
unsigned int szx
block size
Definition: block.h:39
unsigned int num
block number
Definition: block.h:37