block.c
Go to the documentation of this file.
/* block.c -- block transfer
*
* Copyright (C) 2010--2012,2015-2019 Olaf Bergmann <bergmann@tzi.org> and others
*
* This file is part of the CoAP library libcoap. Please see
* README for terms of use.
*/
#include "coap_internal.h"
#ifndef min
#define min(a,b) ((a) < (b) ? (a) : (b))
#endif
#ifndef WITHOUT_BLOCK
unsigned int
coap_opt_block_num(const coap_opt_t *block_opt) {
unsigned int num = 0;
uint16_t len;
len = coap_opt_length(block_opt);
if (len == 0) {
return 0;
}
if (len > 1) {
num = coap_decode_var_bytes(coap_opt_value(block_opt),
coap_opt_length(block_opt) - 1);
}
return (num << 4) | ((*COAP_OPT_BLOCK_LAST(block_opt) & 0xF0) >> 4);
}
int
coap_get_block(coap_pdu_t *pdu, uint16_t type, coap_block_t *block) {
coap_opt_iterator_t opt_iter;
coap_opt_t *option;
assert(block);
memset(block, 0, sizeof(coap_block_t));
if (pdu && (option = coap_check_option(pdu, type, &opt_iter)) != NULL) {
unsigned int num;
block->szx = COAP_OPT_BLOCK_SZX(option);
if (COAP_OPT_BLOCK_MORE(option))
block->m = 1;
/* The block number is at most 20 bits, so values above 2^20 - 1
* are illegal. */
num = coap_opt_block_num(option);
if (num > 0xFFFFF) {
return 0;
}
block->num = num;
return 1;
}
return 0;
}
int
coap_write_block_opt(coap_block_t *block, uint16_t type,
coap_pdu_t *pdu, size_t data_length) {
size_t start, want, avail;
unsigned char buf[4];
assert(pdu);
start = block->num << (block->szx + 4);
if (data_length <= start) {
coap_log(LOG_DEBUG, "illegal block requested\n");
return -2;
}
assert(pdu->max_size > 0);
avail = pdu->max_size - pdu->used_size - 4;
want = (size_t)1 << (block->szx + 4);
/* check if entire block fits in message */
if (want <= avail) {
block->m = want < data_length - start;
} else {
/* Sender has requested a block that is larger than the remaining
* space in pdu. This is ok if the remaining data fits into the pdu
* anyway. The block size needs to be adjusted only if there is more
* data left that cannot be delivered in this message. */
if (data_length - start <= avail) {
/* it's the final block and everything fits in the message */
block->m = 0;
} else {
unsigned int szx;
int newBlockSize;
/* we need to decrease the block size */
if (avail < 16) { /* bad luck, this is the smallest block size */
coap_log(LOG_DEBUG,
"not enough space, even the smallest block does not fit");
return -3;
}
newBlockSize = coap_flsll((long long)avail) - 5;
coap_log(LOG_DEBUG,
"decrease block size for %zu to %d\n", avail, newBlockSize);
szx = block->szx;
block->szx = newBlockSize;
block->m = 1;
block->num <<= szx - block->szx;
}
}
/* to re-encode the block option */
coap_add_option(pdu, type, coap_encode_var_safe(buf, sizeof(buf),
((block->num << 4) |
(block->m << 3) |
block->szx)),
buf);
return 1;
}
int
coap_add_block(coap_pdu_t *pdu, unsigned int len, const uint8_t *data,
unsigned int block_num, unsigned char block_szx) {
unsigned int start;
start = block_num << (block_szx + 4);
if (len <= start)
return 0;
return coap_add_data(pdu,
min(len - start, (1U << (block_szx + 4))),
data + start);
}
/*
* Note that the COAP_OPTION_ have to be added in the correct order
*/
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
) {
coap_key_t etag;
unsigned char buf[4];
coap_block_t block2 = { 0, 0, 0 };
int block2_requested = 0;
coap_subscription_t *subscription = coap_find_observer(resource, session, token);
/*
* Need to check that a valid block is getting asked for so that the
* correct options are put into the PDU.
*/
if (request) {
if (coap_get_block(request, COAP_OPTION_BLOCK2, &block2)) {
block2_requested = 1;
if (length <= (block2.num << (block2.szx + 4))) {
coap_log(LOG_DEBUG, "Illegal block requested (%d > last = %zu)\n",
block2.num,
length >> (block2.szx + 4));
response->code = COAP_RESPONSE_CODE(400);
goto error;
}
}
}
else if (subscription && subscription->has_block2) {
block2 = subscription->block2;
block2.num = 0;
block2_requested = 1;
}
response->code = COAP_RESPONSE_CODE(205);
/* add etag for the resource */
memset(etag, 0, sizeof(etag));
coap_hash(data, length, etag);
coap_add_option(response, COAP_OPTION_ETAG, sizeof(etag), etag);
if ((block2.num == 0) && subscription) {
coap_add_option(response, COAP_OPTION_OBSERVE,
coap_encode_var_safe(buf, sizeof (buf),
resource->observe),
buf);
}
coap_add_option(response, COAP_OPTION_CONTENT_TYPE,
coap_encode_var_safe(buf, sizeof(buf),
media_type),
buf);
if (maxage >= 0) {
coap_add_option(response,
COAP_OPTION_MAXAGE,
coap_encode_var_safe(buf, sizeof(buf), maxage), buf);
}
if (block2_requested) {
int res;
res = coap_write_block_opt(&block2, COAP_OPTION_BLOCK2, response,
length);
switch (res) {
case -2: /* illegal block (caught above) */
response->code = COAP_RESPONSE_CODE(400);
goto error;
case -1: /* should really not happen */
assert(0);
/* fall through if assert is a no-op */
case -3: /* cannot handle request */
response->code = COAP_RESPONSE_CODE(500);
goto error;
default: /* everything is good */
;
}
coap_add_option(response,
COAP_OPTION_SIZE2,
coap_encode_var_safe(buf, sizeof(buf), length),
buf);
coap_add_block(response, length, data,
block2.num, block2.szx);
return;
}
/*
* BLOCK2 not requested
*/
if (!coap_add_data(response, length, data)) {
/* set initial block size, will be lowered by
* coap_write_block_opt) automatically */
block2.num = 0;
block2.szx = 6;
coap_write_block_opt(&block2, COAP_OPTION_BLOCK2, response,
length);
coap_add_option(response,
COAP_OPTION_SIZE2,
coap_encode_var_safe(buf, sizeof(buf), length),
buf);
coap_add_block(response, length, data,
block2.num, block2.szx);
}
return;
error:
coap_add_data(response,
strlen(coap_response_phrase(response->code)),
(const unsigned char *)coap_response_phrase(response->code));
}
#endif /* WITHOUT_BLOCK */
min
#define min(a, b)
Definition: block.c:12
coap_key_t
unsigned char coap_key_t[4]
Definition: coap_hashkey.h:22
coap_hash
#define coap_hash(String, Length, Result)
Definition: coap_hashkey.h:36
coap_internal.h
Pulls together all the internal only header files.
coap_flsll
int coap_flsll(long long i)
Definition: encode.c:19
coap_encode_var_safe
unsigned int coap_encode_var_safe(uint8_t *buf, size_t length, unsigned int val)
Encodes multiple-length byte sequences.
Definition: encode.c:38
coap_decode_var_bytes
unsigned int coap_decode_var_bytes(const uint8_t *buf, unsigned int len)
Decodes multiple-length byte sequences.
Definition: encode.c:29
coap_write_block_opt
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:64
coap_add_block
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:125
coap_add_data_blocked_response
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:142
COAP_OPT_BLOCK_SZX
#define COAP_OPT_BLOCK_SZX(opt)
Returns the value of the SZX-field of a Block option opt.
Definition: block.h:55
coap_get_block
int coap_get_block(coap_pdu_t *pdu, uint16_t type, coap_block_t *block)
Initializes block from pdu.
Definition: block.c:36
COAP_OPT_BLOCK_MORE
#define COAP_OPT_BLOCK_MORE(opt)
Returns the value of the More-bit of a Block option opt.
Definition: block.h:51
coap_opt_block_num
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:17
COAP_OPT_BLOCK_LAST
#define COAP_OPT_BLOCK_LAST(opt)
Returns the value of the least significant byte of a Block option opt.
Definition: block.h:47
coap_log
#define coap_log(level,...)
Logging function.
Definition: coap_debug.h:129
LOG_DEBUG
@ LOG_DEBUG
Debug.
Definition: coap_debug.h:55
coap_opt_length
uint16_t coap_opt_length(const coap_opt_t *opt)
Returns the length of the given option.
Definition: option.c:238
coap_opt_value
const uint8_t * coap_opt_value(const coap_opt_t *opt)
Returns a pointer to the value of the given option.
Definition: option.c:275
coap_check_option
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:196
coap_opt_t
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
coap_add_option
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:215
coap_response_phrase
const char * coap_response_phrase(unsigned char code)
Returns a human-readable response phrase for the specified CoAP response code.
Definition: pdu.c:370
coap_add_data
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:288
COAP_OPTION_BLOCK2
#define COAP_OPTION_BLOCK2
Definition: pdu.h:117
COAP_OPTION_SIZE2
#define COAP_OPTION_SIZE2
Definition: pdu.h:105
COAP_RESPONSE_CODE
#define COAP_RESPONSE_CODE(N)
Definition: pdu.h:132
COAP_OPTION_CONTENT_TYPE
#define COAP_OPTION_CONTENT_TYPE
Definition: pdu.h:100
COAP_OPTION_MAXAGE
#define COAP_OPTION_MAXAGE
Definition: pdu.h:101
COAP_OPTION_ETAG
#define COAP_OPTION_ETAG
Definition: pdu.h:94
COAP_OPTION_OBSERVE
#define COAP_OPTION_OBSERVE
Definition: pdu.h:112
coap_find_observer
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:582
coap_binary_t
Coap binary data definition.
Definition: str.h:43
coap_block_t
Structure of Block options.
Definition: block.h:36
coap_block_t::num
unsigned int num
block number
Definition: block.h:37
coap_block_t::szx
unsigned int szx
block size
Definition: block.h:39
coap_block_t::m
unsigned int m
1 if more blocks follow, 0 otherwise
Definition: block.h:38
coap_opt_iterator_t
Iterator to run through PDU options.
Definition: option.h:237
coap_pdu_t
structure for CoAP PDUs token, if any, follows the fixed size header, then options until payload mark...
Definition: pdu.h:285
coap_pdu_t::max_size
size_t max_size
maximum size for token, options and payload, or zero for variable size pdu
Definition: pdu.h:295
coap_pdu_t::code
uint8_t code
request method (value 1–10) or response code (value 40-255)
Definition: pdu.h:287
coap_pdu_t::used_size
size_t used_size
used bytes of storage for token, options and payload
Definition: pdu.h:294
coap_resource_t
Definition: resource.h:68
coap_resource_t::observe
unsigned int observe
The next value for the Observe option.
Definition: resource.h:102
coap_session_t
Definition: coap_session.h:59
coap_subscription_t
Subscriber information.
Definition: coap_subscribe_internal.h:44
coap_subscription_t::block2
coap_block_t block2
GET request Block2 definition.
Definition: coap_subscribe_internal.h:55
coap_subscription_t::has_block2
unsigned int has_block2
GET request had Block2 definition.
Definition: coap_subscribe_internal.h:53
uint8_t
unsigned char uint8_t
Definition: uthash.h:79