This is CoAP library with a focus on simplicity. It offers minimal CoAP PDU construction and decoding to and from byte buffers.

cantcoap.cpp

Committer:
ashleymills
Date:
2013-10-08
Revision:
0:3d62a105fd34
Child:
1:5eec2844ad47

File content as of revision 0:3d62a105fd34:

/*
Copyright (c) 2013, Ashley Mills.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: 

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer. 
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution. 

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

// version, 2 bits
// type, 2 bits
	// 00 Confirmable
	// 01 Non-confirmable
	// 10 Acknowledgement
	// 11 Reset

// token length, 4 bits
// length of token in bytes (only 0 to 8 bytes allowed)
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "cantcoap.h"
#ifndef MBED
#include "arpa/inet.h"
#include "netdb.h"
#else
#include "bsd_socket.h"
#endif


/// Memory-managed constructor. Buffer for PDU is dynamically sized and allocated by the object.
/**
 * When using this constructor, the CoapPDU class will allocate space for the PDU.
 * Contrast this with the parameterized constructors, which allow the use of an external buffer.
 *
 * Note, the PDU container and space can be reused by issuing a CoapPDU::reset(). If the new PDU exceeds the 
 * space of the previously allocated memory, then further memory will be dynamically allocated.
 *
 * Deleting the object will free the Object container and all dynamically allocated memory.
 *
 * \note It would have been nice to use something like UDP_CORK or MSG_MORE, to allow separate buffers 
 * for token, options, and payload but these FLAGS aren't implemented for UDP in LwIP so stuck with one buffer for now.
 *
 * CoAP version defaults to 1.
 *
 * \sa CoapPDU::CoapPDU(uint8_t *pdu, int pduLength), CoapPDU::CoapPDU::(uint8_t *buffer, int bufferLength, int pduLength), 
 * CoapPDU:CoapPDU()~
 *
 */
CoapPDU::CoapPDU() {
	// pdu
	_pdu = (uint8_t*)calloc(4,sizeof(uint8_t));
	_pduLength = 4;
	_bufferLength = _pduLength;

	//options
	_numOptions = 0;
	_maxAddedOptionNumber = 0;

	// payload
	_payloadPointer = NULL;
	_payloadLength = 0;

	_constructedFromBuffer = 0;

	setVersion(1);
}

/// Construct a PDU using an external buffer. No copy of the buffer is made.
/**
 * This constructor is normally used where a PDU has been received over the network, and it's length is known.
 * In this case the CoapPDU object is probably going to be used as a temporary container to access member values.
 *
 * It is assumed that \b pduLength is the length of the actual CoAP PDU, and consequently the buffer will also be this size,
 * contrast this with CoapPDU::CoapPDU(uint8_t *buffer, int bufferLength, int pduLength) which allows the buffer to 
 * be larger than the PDU.
 *
 * A PDU constructed in this manner must be validated with CoapPDU::validate() before the member variables will be accessible.
 *
 * \warning The validation call parses the PDU structure to set some internal parameters. If you do
 * not validate the PDU, then the behaviour of member access functions will be undefined.
 * 
 * The buffer can be reused by issuing a CoapPDU::reset() but the class will not change the size of the buffer. If the
 * newly constructed PDU exceeds the size of the buffer, the function called (for example CoapPDU::addOption) will fail.
 *
 * Deleting this object will only delete the Object container and will not delete the PDU buffer.
 *
 * @param pdu A pointer to an array of bytes which comprise the CoAP PDU
 * @param pduLength The length of the CoAP PDU pointed to by \b pdu

 * \sa CoapPDU::CoapPDU(), CoapPDU::CoapPDU(uint8_t *buffer, int bufferLength, int pduLength)
 */
CoapPDU::CoapPDU(uint8_t *pdu, int pduLength) {
	// pdu
	_pdu = pdu;
	_bufferLength = pduLength;
	_pduLength = pduLength;
	
	_constructedFromBuffer = 1;

	// options
	_numOptions = 0;
	_maxAddedOptionNumber = 0;

	// payload
	_payloadPointer = NULL;
	_payloadLength = 0;
}

/// Construct object from external buffer that may be larger than actual PDU.
/**
 * This differs from CoapPDU::CoapPDU(uint8_t *pdu, int pduLength) in that the buffer may be larger
 * than the actual CoAP PDU contained int the buffer. This is typically used when a large buffer is reused
 * multiple times. Note that \b pduLength can be 0.
 * 
 * If an actual CoAP PDU is passed in the buffer, \b pduLength should match its length. CoapPDU::validate() must
 * be called to initiate the object before member functions can be used.
 *
 * A PDU constructed in this manner must be validated with CoapPDU::validate() before the member variables will be accessible.
 *
 * \warning The validation call parses the PDU structure to set some internal parameters. If you do
 * not validate the PDU, then the behaviour of member access functions will be undefined.
 * 
 * The buffer can be reused by issuing a CoapPDU::reset() but the class will not change the size of the buffer. If the
 * newly constructed PDU exceeds the size of the buffer, the function called (for example CoapPDU::addOption) will fail.
 *
 * Deleting this object will only delete the Object container and will not delete the PDU buffer.
 *
 * \param buffer A buffer which either contains a CoAP PDU or is intended to be used to construct one.
 * \param bufferLength The length of the buffer
 * \param pduLength If the buffer contains a CoAP PDU, this specifies the length of the PDU within the buffer.
 *
 * \sa CoapPDU::CoapPDU(), CoapPDU::CoapPDU(uint8_t *pdu, int pduLength)
 */
CoapPDU::CoapPDU(uint8_t *buffer, int bufferLength, int pduLength) {
	// sanity
	if(pduLength<4&&pduLength!=0) {
		DBG("PDU cannot have a length less than 4");
	}

	// pdu
	_pdu = buffer;
	_bufferLength = bufferLength;
	if(pduLength==0) {
		// this is actually a fresh pdu, header always exists
		_pduLength = 4;
		// make sure header is zeroed
		_pdu[0] = 0x00; _pdu[1] = 0x00; _pdu[2] = 0x00; _pdu[3] = 0x00;
		setVersion(1);
	} else {
		_pduLength = pduLength;
	}

	_constructedFromBuffer = 1;

	// options
	_numOptions = 0;
	_maxAddedOptionNumber = 0;

	// payload
	_payloadPointer = NULL;
	_payloadLength = 0;
}

/// Reset CoapPDU container so it can be reused to build a new PDU.
/**
 * This resets the CoapPDU container, setting the pdu length, option count, etc back to zero. The
 * PDU can then be populated as if it were newly constructed.
 *
 * Note that the space available will depend on how the CoapPDU was originally constructed:
 * -# CoapPDU::CoapPDU()
 *
 * 	Available space initially be \b _pduLength. But further space will be allocated as needed on demand, 
 *    limited only by the OS/environment.
 *
 * -# CoapPDU::CoapPDU(uint8_t *pdu, int pduLength)
 *
 *		Space is limited by the variable \b pduLength. The PDU cannot exceed \b pduLength bytes.
 *		 
 * -# CoapPDU::CoapPDU(uint8_t *buffer, int bufferLength, int pduLength)
 *
 *		Space is limited by the variable \b bufferLength. The PDU cannot exceed \b bufferLength bytes.
 *
 * \return 0 on success, 1 on failure.
 */
int CoapPDU::reset() {
	// pdu
	memset(_pdu,0x00,_bufferLength);
	// packet always has at least a header
	_pduLength = 4;

	// options
	_numOptions = 0;
	_maxAddedOptionNumber = 0;
	// payload
	_payloadPointer = NULL;
	_payloadLength = 0;
	return 0;
}

/// Validates a PDU constructed using an external buffer.
/**
 * When a CoapPDU is constructed using an external buffer, the programmer must call this function to
 * check that the received PDU is a valid CoAP PDU.
 *
 * \warning The validation call parses the PDU structure to set some internal parameters. If you do
 * not validate the PDU, then the behaviour of member access functions will be undefined.
 *
 * \return 1 if the PDU validates correctly, 0 if not. XXX maybe add some error codes
 */
int CoapPDU::validate() {
	if(_pduLength<4) {
		DBG("PDU has to be a minimum of 4 bytes. This: %d bytes",_pduLength);
		return 0;
	}

	// check header
	//   0                   1                   2                   3
   //  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   // |Ver| T |  TKL  |      Code     |          Message ID           |
   // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   // |   Token (if any, TKL bytes) ...
   // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   // |   Options (if any) ...
   // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   // |1 1 1 1 1 1 1 1|    Payload (if any) ...
   // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

	DBG("Version: %d",getVersion());
	DBG("Type: %d",getType());

	// token length must be between 0 and 8
	int tokenLength = getTokenLength();
	if(tokenLength<0||tokenLength>8) {
		DBG("Invalid token length: %d",tokenLength);
		return 0;
	}
	DBG("Token length: %d",tokenLength);
	// check total length
	if((COAP_HDR_SIZE+tokenLength)>_pduLength) {
		DBG("Token length would make pdu longer than actual length.");
		return 0;
	}

	// check that code is valid
	CoapPDU::Code code = getCode();	
	if(code<COAP_EMPTY ||
		(code>COAP_DELETE&&code<COAP_CREATED) ||
		(code>COAP_CONTENT&&code<COAP_BAD_REQUEST) ||
		(code>COAP_NOT_ACCEPTABLE&&code<COAP_PRECONDITION_FAILED) ||
		(code==0x8E) ||
		(code>COAP_UNSUPPORTED_CONTENT_FORMAT&&code<COAP_INTERNAL_SERVER_ERROR) ||
		(code>COAP_PROXYING_NOT_SUPPORTED) ) {
		DBG("Invalid CoAP code: %d",code);
		return 0;
	}
	DBG("CoAP code: %d",code);

	// token can be anything so nothing to check

	// check that options all make sense
	uint16_t optionDelta =0, optionNumber = 0, optionValueLength = 0;
	int totalLength = 0;

	// first option occurs after token
	int optionPos = COAP_HDR_SIZE + getTokenLength();

	// may be 0 options
	if(optionPos==_pduLength) {
		DBG("No options. No payload.");
		_numOptions = 0;
		_payloadLength = 0;
		return 1;
	}

	int bytesRemaining = _pduLength-optionPos;
	int numOptions = 0;
	uint8_t upperNibble = 0x00, lowerNibble = 0x00;

	// walk over options and record information
	while(1) {
		// check for payload marker
		if(bytesRemaining>0) {
			uint8_t optionHeader = _pdu[optionPos];
			if(optionHeader==0xFF) {
				// payload
				if(bytesRemaining>1) {
					_payloadPointer = &_pdu[optionPos+1];
					_payloadLength = (bytesRemaining-1);
					_numOptions = numOptions;
					DBG("Payload found, length: %d",_payloadLength);
					return 1;
				}
				// payload marker but no payload
				_payloadPointer = NULL;
				_payloadLength = 0;
				DBG("Payload marker but no payload.");
				return 0;
			}

			// check that option delta and option length are valid values
			upperNibble = (optionHeader & 0xF0) >> 4;
			lowerNibble = (optionHeader & 0x0F);
			if(upperNibble==0x0F||lowerNibble==0x0F) {
				DBG("Expected option header or payload marker, got: 0x%x%x",upperNibble,lowerNibble);
				return 0;
			}
			DBG("Option header byte appears sane: 0x%x%x",upperNibble,lowerNibble);
		} else {
			DBG("No more data. No payload.");
			_payloadPointer = NULL;
			_payloadLength = 0;
			_numOptions = numOptions;
			return 1;
		}

		// skip over option header byte
		bytesRemaining--;

		// check that there is enough space for the extended delta and length bytes (if any)
		int headerBytesNeeded = computeExtraBytes(upperNibble);
		DBG("%d extra bytes needed for extended delta",headerBytesNeeded);
		if(headerBytesNeeded>bytesRemaining) {
			DBG("Not enough space for extended option delta, needed %d, have %d.",headerBytesNeeded,bytesRemaining);
			return 0;
		}
		headerBytesNeeded += computeExtraBytes(lowerNibble);
		if(headerBytesNeeded>bytesRemaining) {
			DBG("Not enough space for extended option length, needed %d, have %d.",
				(headerBytesNeeded-computeExtraBytes(upperNibble)),bytesRemaining);
			return 0;
		}
		DBG("Enough space for extended delta and length: %d, continuing.",headerBytesNeeded);

		// extract option details
		optionDelta = getOptionDelta(&_pdu[optionPos]);
		optionNumber += optionDelta;
		optionValueLength = getOptionValueLength(&_pdu[optionPos]);
		DBG("Got option: %d with length %d",optionNumber,optionValueLength);
		// compute total length
		totalLength = 1; // mandatory header
		totalLength += computeExtraBytes(optionDelta);
		totalLength += computeExtraBytes(optionValueLength);
		totalLength += optionValueLength;
		// check there is enough space
		if(optionPos+totalLength>_pduLength) {
			DBG("Not enough space for option payload, needed %d, have %d.",(totalLength-headerBytesNeeded-1),_pduLength-optionPos);
			return 0;
		}
		DBG("Enough space for option payload: %d %d",optionValueLength,(totalLength-headerBytesNeeded-1));

		// recompute bytesRemaining
		bytesRemaining -= totalLength;
		bytesRemaining++; // correct for previous --

		// move to next option
		optionPos += totalLength; 

		// inc number of options XXX
		numOptions++;
	}

	return 1;
}

/// Destructor. Does not free buffer if constructor passed an external buffer.
/**
 * The destructor acts differently, depending on how the object was initially constructed (from buffer or not):
 *
 * -# CoapPDU::CoapPDU()
 *
 * 	Complete object is destroyed.
 *
 * -# CoapPDU::CoapPDU(uint8_t *pdu, int pduLength)
 *
 *		Only object container is destroyed. \b pdu is left intact.
 *		 
 * -# CoapPDU::CoapPDU(uint8_t *buffer, int bufferLength, int pduLength)
 *
 *		Only object container is destroyed. \b pdu is left intact.
 *
 */
CoapPDU::~CoapPDU() {
	if(!_constructedFromBuffer) {
		free(_pdu);
	}
}

/// Returns a pointer to the internal buffer.
uint8_t* CoapPDU::getPDUPointer() {
	return _pdu;
}

/// Set the PDU length to the length specified.
/**
 * This is used when re-using a PDU container before calling CoapPDU::validate() as it
 * is not possible to deduce the length of a PDU since the payload has no length marker.
 * \param len The length of the PDU
 */
void CoapPDU::setPDULength(int len) {
	_pduLength = len;
}


/// Shorthand function for setting a resource URI.
/**
 * This will parse the supplied \b uri and construct enough URI_PATH CoAP options to encode it.
 * The options are added to the PDU.
 * 
 * At present queries are not handled. TODO Implement queries.
 *
 * \note This uses an internal buffer of 16 bytes to manipulate strings. The internal buffer will be
 * expanded dynamically if necessary (path component longer than 16 bytes). The internal buffer will
 * be freed before the function returns.
 * 
 * \param uri The uri to parse.
 * \param urilen The length of the uri to parse.
 *
 * \return 1 on success, 0 on failure.
 */
int CoapPDU::setURI(char *uri, int urilen) {
	// only '/' and alphabetic chars allowed
	// very simple splitting done

	// sanitation
	if(urilen<=0||uri==NULL) {
		DBG("Null or zero-length uri passed.");
		return 1;
	}

	// single character URI path (including '/' case)
	if(urilen==1) {
		addOption(COAP_OPTION_URI_PATH,1,(uint8_t*)uri);
		return 0;
	}

	// local vars
	char *startP=uri,*endP=NULL;
	int oLen = 0;
	int bufSpace = 16;
	char *uriBuf = (char*)malloc(bufSpace*sizeof(char));
	if(uriBuf==NULL) {
		DBG("Error allocating temporary memory.");
		return 1;
	}

	while(1) {
		// stop at end of string
		if(*startP==0x00||*(startP+1)==0x00) {
			break;
		}

		// ignore leading slash
		if(*startP=='/') {
			DBG("Skipping leading slash");
			startP++;
		}

		// find next split point
		endP = strchr(startP,'/');

		// might not be another slash
		if(endP==NULL) {
			DBG("Ending out of slash");
			endP = uri+urilen;
		}

		// get length of segment
		oLen = endP-startP;

		// copy sequence, make space if necessary
		if((oLen+1)>bufSpace) {
			char *newBuf = (char*)realloc(uriBuf,oLen+1);
			if(newBuf==NULL) {
				DBG("Error making space for temporary buffer");
				free(uriBuf);
				return 1;
			}
			uriBuf = newBuf;
		}

		// copy into temporary buffer
		memcpy(uriBuf,startP,oLen);
		uriBuf[oLen] = 0x00;
		DBG("Adding URI_PATH %s",uriBuf);
		// add option
		if(addOption(COAP_OPTION_URI_PATH,oLen,(uint8_t*)uriBuf)!=0) {
			DBG("Error adding option");
			return 1;
		}
		startP = endP;
	}

	// clean up
	free(uriBuf);
	return 0;
}

/// Concatenates any URI_PATH elements into a single string.
/**
 * Parses the PDU options and extracts all URI_PATH elements, concatenating them into a single string with slash separators.
 * 
 * \param dst Buffer into which to copy the concatenated path elements.
 * \param dstlen Length of buffer.
 * \param outLen Pointer to integer, into which URI length will be placed.
 *
 * \return 0 on success, 1 on failure. \b outLen will contain the length of the concatenated elements.
 */
int CoapPDU::getURI(char *dst, int dstlen, int *outLen) {
	if(outLen==NULL) {
		DBG("Output length pointer is NULL");
		return 1;
	}

	if(dst==NULL) {
		DBG("NULL destination buffer");
		*outLen = 0;
		return 1;
	}

	// check destination space
	if(dstlen<=0) {
		*dst = 0x00;
		*outLen = 0;
		DBG("Destination buffer too small (0)!");
		return 1;
	}
	// check option count
	if(_numOptions==0) {
		*dst = 0x00;
		*outLen = 0;
		return 0;
	}
	// get options
	CoapPDU::CoapOption *options = getOptions();
	if(options==NULL) {
		*dst = 0x00;
		*outLen = 0;
		return 0;
	}
	// iterate over options to construct URI
	CoapOption *o = NULL;
	int bytesLeft = dstlen-1; // space for 0x00
	int oLen = 0;
	// add slash at beggining
	if(bytesLeft>=1) {
		*dst = '/';
		dst++;
		bytesLeft--;
	} else {
		DBG("No space for initial slash needed 1, got %d",bytesLeft);
		return 1;
	}
	for(int i=0; i<_numOptions; i++) {
		o = &options[i];
		oLen = o->optionValueLength;
		if(o->optionNumber==COAP_OPTION_URI_PATH) {
			// check space
			if(oLen>bytesLeft) {
				DBG("Destination buffer too small, needed %d, got %d",oLen,bytesLeft);
				return 1;
			}

			// case where single '/' exists
			if(oLen==1&&o->optionValuePointer[0]=='/') {
				*dst = 0x00;
				*outLen = 1;
				return 0;
			}

			// copy URI path component
			memcpy(dst,o->optionValuePointer,oLen);

			// adjust counters
			dst += oLen;
			bytesLeft -= oLen;

			// add slash following (don't know at this point if another option is coming)
			if(bytesLeft>=1) {
				*dst = '/';
				dst++;
				bytesLeft--;
			} else {
				DBG("Ran out of space after processing option");
				return 1;
			}
		}
	}

	// remove terminating slash
	dst--;
	bytesLeft++;
	// add null terminating byte (always space since reserved)
	*dst = 0x00;
	*outLen = (dstlen-1)-bytesLeft;
	return 0;
}

/// Sets the CoAP version.
/**
 * \param version CoAP version between 0 and 3.
 * \return 0 on success, 1 on failure.
 */
int CoapPDU::setVersion(uint8_t version) {
	if(version>3) {
		return 0;
	}

	_pdu[0] &= 0x3F;
	_pdu[0] |= (version << 6);
	return 1;
}
		
/**
 * Gets the CoAP Version.
 * @return The CoAP version between 0 and 3.
 */
uint8_t CoapPDU::getVersion() {
	return (_pdu[0]&0xC0)>>6;
}

/**
 * Sets the type of this CoAP PDU. 
 * \param mt The type, one of: 
 * - COAP_CONFIRMABLE
 * - COAP_NON_CONFIRMABLE
 * - COAP_ACKNOWLEDGEMENT
 * - COAP_RESET.
 */
void CoapPDU::setType(CoapPDU::Type mt) {
	_pdu[0] &= 0xCF;
	_pdu[0] |= mt;
}

/// Returns the type of the PDU.
CoapPDU::Type CoapPDU::getType() {
	return (CoapPDU::Type)(_pdu[0]&0x30);
}


/// Set the token length.
/**
 * \param tokenLength The length of the token in bytes, between 0 and 8.
 * \return 0 on success, 1 on failure.
 */
int CoapPDU::setTokenLength(uint8_t tokenLength) {
	if(tokenLength>8)
		return 1;

	_pdu[0] &= 0xF0;
	_pdu[0] |= tokenLength;
	return 0;
}

/// Returns the token length.
int CoapPDU::getTokenLength() {
	return _pdu[0] & 0x0F;
}

/// Returns a pointer to the PDU token.
uint8_t* CoapPDU::getTokenPointer() {
	if(getTokenLength()==0) {
		return NULL;
	}
	return &_pdu[4];
}

/// Set the PDU token to the supplied byte sequence.
/**
 * This sets the PDU token to \b token and sets the token length to \b tokenLength.
 * \param token A sequence of bytes representing the token.
 * \param tokenLength The length of the byte sequence.
 * \return 0 on success, 1 on failure.
 */
int CoapPDU::setToken(uint8_t *token, uint8_t tokenLength) {
	DBG("Setting token");
	if(token==NULL) {
		DBG("NULL pointer passed as token reference");
		return 1;
	}

	if(tokenLength==0) {
		DBG("Token has zero length");
		return 1;
	}

	// if tokenLength has not changed, just copy the new value
	uint8_t oldTokenLength = getTokenLength();
	if(tokenLength==oldTokenLength) {
		memcpy((void*)&_pdu[4],token,tokenLength);
		return 0;
	}

	// otherwise compute new length of PDU
	uint8_t oldPDULength = _pduLength;
	_pduLength -= oldTokenLength;
	_pduLength += tokenLength;

	// now, have to shift old memory around, but shift direction depends
	// whether pdu is now bigger or smaller
	if(_pduLength>oldPDULength) {
		// new PDU is bigger, need to allocate space for new PDU
		if(!_constructedFromBuffer) {
			uint8_t *newMemory = (uint8_t*)realloc(_pdu,_pduLength);
			if(newMemory==NULL) {
				// malloc failed
				DBG("Failed to allocate memory for token");
				_pduLength = oldPDULength;
				return 1;
			}
			_pdu = newMemory;
			_bufferLength = _pduLength;
		} else {
			// constructed from buffer, check space
			if(_pduLength>_bufferLength) {
				DBG("Buffer too small to contain token, needed %d, got %d.",_pduLength-oldPDULength,_bufferLength-oldPDULength);
				_pduLength = oldPDULength;
				return 1;
			}
		}

		// and then shift everything after token up to end of new PDU
		// memory overlaps so do this manually so to avoid additional mallocs
		int shiftOffset = _pduLength-oldPDULength;
		int shiftAmount = _pduLength-tokenLength-COAP_HDR_SIZE; // everything after token
		shiftPDUUp(shiftOffset,shiftAmount);

		// now copy the token into the new space and set official token length
		memcpy((void*)&_pdu[4],token,tokenLength);
		setTokenLength(tokenLength);

		// and return success
		return 0;
	}

	// new PDU is smaller, copy the new token value over the old one
	memcpy((void*)&_pdu[4],token,tokenLength);
	// and shift everything after the new token down
	int startLocation = COAP_HDR_SIZE+tokenLength;
	int shiftOffset = oldPDULength-_pduLength;
	int shiftAmount = oldPDULength-oldTokenLength-COAP_HDR_SIZE;
	shiftPDUDown(startLocation,shiftOffset,shiftAmount);
	// then reduce size of buffer
	if(!_constructedFromBuffer) {
		uint8_t *newMemory = (uint8_t*)realloc(_pdu,_pduLength);
		if(newMemory==NULL) {
			// malloc failed, PDU in inconsistent state
			DBG("Failed to shrink PDU for new token. PDU probably broken");
			return 1;
		}
		_pdu = newMemory;
		_bufferLength = _pduLength;
	}

	// and officially set the new tokenLength
	setTokenLength(tokenLength);
	return 0;
}

/// Sets the CoAP response code
void CoapPDU::setCode(CoapPDU::Code code) {
	_pdu[1] = code;
	// there is a limited set of response codes
}

/// Gets the CoAP response code
CoapPDU::Code CoapPDU::getCode() {
	return (CoapPDU::Code)_pdu[1];
}
/// Set messageID to the supplied value.
/**
 * \param messageID A 16bit message id.
 * \return 0 on success, 1 on failure.
 */
int CoapPDU::setMessageID(uint16_t messageID) {
	// message ID is stored in network byte order
	uint16_t networkOrder = htons(messageID);
	// bytes 2 and 3 hold the ID
	_pdu[2] &= 0x00;
	_pdu[2] |= (networkOrder >> 8);    // MSB
	_pdu[3] &= 0x00;
	_pdu[3] |= (networkOrder & 0x00FF); // LSB
	return 0;
}

/// Returns the 16 bit message ID of the PDU.
uint16_t CoapPDU::getMessageID() {
	// mesasge ID is stored in network byteorder
	uint16_t networkOrder = 0x0000;
	networkOrder |= _pdu[2];
	networkOrder <<= 8;
	networkOrder |= _pdu[3];
	return ntohs(networkOrder);
}

/// Returns the length of the PDU.
int CoapPDU::getPDULength() {
	return _pduLength;
}

/// Return the number of options that the PDU has.
int CoapPDU::getNumOptions() {
	return _numOptions;
}


/**
 * This returns the options as a sequence of structs.
 */
CoapPDU::CoapOption* CoapPDU::getOptions() {
	DBG("getOptions() called, %d options.",_numOptions);

	uint16_t optionDelta =0, optionNumber = 0, optionValueLength = 0;
	int totalLength = 0;

	if(_numOptions==0) {
		return NULL;
	}

	// malloc space for options
	CoapOption *options = (CoapOption*)malloc(_numOptions*sizeof(CoapOption));

	// first option occurs after token
	int optionPos = COAP_HDR_SIZE + getTokenLength();

	// walk over options and record information
	for(int i=0; i<_numOptions; i++) {
		// extract option details
		optionDelta = getOptionDelta(&_pdu[optionPos]);
		optionNumber += optionDelta;
		optionValueLength = getOptionValueLength(&_pdu[optionPos]);
		// compute total length
		totalLength = 1; // mandatory header
		totalLength += computeExtraBytes(optionDelta);
		totalLength += computeExtraBytes(optionValueLength);
		totalLength += optionValueLength;
		// record option details
		options[i].optionNumber = optionNumber;
		options[i].optionDelta = optionDelta;
		options[i].optionValueLength = optionValueLength;
		options[i].totalLength = totalLength;
		options[i].optionPointer = &_pdu[optionPos];
		options[i].optionValuePointer = &_pdu[optionPos+totalLength-optionValueLength];
		// move to next option
		optionPos += totalLength; 
	}

	return options;
}

/// Add an option to the PDU.
/**
 * Unlike other implementations, options can be added in any order, and in-memory manipulation will be
 * performed to ensure the correct ordering of options (they use a delta encoding of option numbers).
 * Re-ordering memory like this incurs a small performance cost, so if you care about this, then you
 * might want to add options in ascending order of option number.
 * \param optionNumber The number of the option, see the enum CoapPDU::Option for shorthand notations.
 * \param optionLength The length of the option payload in bytes.
 * \param optionValue A pointer to the byte sequence that is the option payload (bytes will be copied).
 * \return 0 on success, 1 on failure.
 */
int CoapPDU::addOption(uint16_t insertedOptionNumber, uint16_t optionValueLength, uint8_t *optionValue) {
	// this inserts the option in memory, and re-computes the deltas accordingly
	// prevOption <-- insertionPosition
	// nextOption

	// find insertion location and previous option number
	uint16_t prevOptionNumber = 0; // option number of option before insertion point
	int insertionPosition = findInsertionPosition(insertedOptionNumber,&prevOptionNumber);
	DBG("inserting option at position %d, after option with number: %hu",insertionPosition,prevOptionNumber);

	// compute option delta length
	uint16_t optionDelta = insertedOptionNumber-prevOptionNumber;
	uint8_t extraDeltaBytes = computeExtraBytes(optionDelta);

	// compute option length length
	uint16_t extraLengthBytes = computeExtraBytes(optionValueLength);

	// compute total length of option
	uint16_t optionLength = COAP_OPTION_HDR_BYTE + extraDeltaBytes + extraLengthBytes + optionValueLength;

	// if this is at the end of the PDU, job is done, just malloc and insert
	if(insertionPosition==_pduLength) {
		DBG("Inserting at end of PDU");
		// optionNumber must be biggest added
		_maxAddedOptionNumber = insertedOptionNumber;

		// set new PDU length and allocate space for extra option
		int oldPDULength = _pduLength;
		_pduLength += optionLength;
		if(!_constructedFromBuffer) {
			uint8_t *newMemory = (uint8_t*)realloc(_pdu,_pduLength);
			if(newMemory==NULL) {
				DBG("Failed to allocate memory for option.");
				_pduLength = oldPDULength;
				// malloc failed
				return 1;
			}
			_pdu = newMemory;
			_bufferLength = _pduLength;
		} else {
			// constructed from buffer, check space
			if(_pduLength>_bufferLength) {
				DBG("Buffer too small for new option: needed %d, got %d.",_pduLength-oldPDULength,_bufferLength-oldPDULength);
				_pduLength = oldPDULength;
				return 1;
			}
		}
		
		// insert option at position
		insertOption(insertionPosition,optionDelta,optionValueLength,optionValue);
		_numOptions++;
		return 0;
	}
	// XXX could do 0xFF pdu payload case for changing of dynamically allocated application space SDUs < yeah, if you're insane

	// the next option might (probably) needs it's delta changing
	// I want to take this into account when allocating space for the new
	// option, to avoid having to do two mallocs, first get info about this option
	int nextOptionDelta = getOptionDelta(&_pdu[insertionPosition]);
	int nextOptionNumber = prevOptionNumber + nextOptionDelta;
	int nextOptionDeltaBytes = computeExtraBytes(nextOptionDelta);
	// recompute option delta, relative to inserted option
	int newNextOptionDelta = nextOptionNumber-insertedOptionNumber;
	int newNextOptionDeltaBytes = computeExtraBytes(newNextOptionDelta);
	// determine adjustment
	int optionDeltaAdjustment = newNextOptionDeltaBytes-nextOptionDeltaBytes;

	// create space for new option, including adjustment space for option delta
	DBG_PDU();
	DBG("Creating space");
	int mallocLength = optionLength+optionDeltaAdjustment;
	int oldPDULength = _pduLength;
	_pduLength += mallocLength;

	if(!_constructedFromBuffer) {
		uint8_t *newMemory = (uint8_t*)realloc(_pdu,_pduLength);
		if(newMemory==NULL) {
			DBG("Failed to allocate memory for option");
			_pduLength = oldPDULength;
			return 1; 
		}
		_pdu = newMemory;
		_bufferLength = _pduLength;
	} else {
		// constructed from buffer, check space
		if(_pduLength>_bufferLength) {
			DBG("Buffer too small to contain option, needed %d, got %d.",_pduLength-oldPDULength,_bufferLength-oldPDULength);
			_pduLength = oldPDULength;
			return 1;
		}
	}

	// move remainder of PDU data up to create hole for new option
	DBG_PDU();
	DBG("Shifting PDU.");
	shiftPDUUp(mallocLength,_pduLength-(insertionPosition+mallocLength));
	DBG_PDU();

	// adjust option delta bytes of following option
	// move the option header to the correct position
	int nextHeaderPos = insertionPosition+mallocLength;
	_pdu[nextHeaderPos-optionDeltaAdjustment] = _pdu[nextHeaderPos];
	nextHeaderPos -= optionDeltaAdjustment;
	// and set the new value
	setOptionDelta(nextHeaderPos, newNextOptionDelta);

	// new option shorter
	// p p n n x x x x x
	// p p n n x x x x x -
	// p p - n n x x x x x
	// p p - - n x x x x x
	// p p o o n x x x x x

	// new option longer
	// p p n n x x x x x
	// p p n n x x x x x - - -
	// p p - - - n n x x x x x
	// p p - - n n n x x x x x
	// p p o o n n n x x x x x

	// note, it can only ever be shorter or the same since if an option was inserted the delta got smaller
	// but I'll leave that little comment in, just to show that it would work even if the delta got bigger

	// now insert the new option into the gap
	DBGLX("Inserting new option...");
	insertOption(insertionPosition,optionDelta,optionValueLength,optionValue);
	DBGX("done\r\n");
	DBG_PDU();

	// done, mark it with B! 
	return 0;
}

/// Allocate space for a payload.
/**
 * For dynamically constructed PDUs, this will allocate space for a payload in the object
 * and return a pointer to it. If the PDU was constructed from a buffer, this doesn't
 * malloc anything, it just changes the _pduLength and returns the payload pointer.
 *
 * \note The pointer returned points into the PDU buffer.
 * \param len The length of the payload buffer to allocate.
 * \return Either a pointer to the payload buffer, or NULL if there wasn't enough space / allocation failed.
 */
uint8_t* CoapPDU::mallocPayload(int len) {
	DBG("Entering mallocPayload");
	// sanity checks
	if(len==0) {
		DBG("Cannot allocate a zero length payload");
		return NULL;
	}

	// further sanity
	if(len==_payloadLength) {
		DBG("Space for payload of specified length already exists");
		if(_payloadPointer==NULL) {
			DBG("Garbage PDU. Payload length is %d, but existing _payloadPointer NULL",_payloadLength);
			return NULL;
		}
		return _payloadPointer;
	}

	DBG("_bufferLength: %d, _pduLength: %d, _payloadLength: %d",_bufferLength,_pduLength,_payloadLength);

	// might be making payload bigger (including bigger than 0) or smaller
	int markerSpace = 1;
	int payloadSpace = len;
	// is this a resizing?
	if(_payloadLength!=0) {
		// marker already exists
		markerSpace = 0;
		// compute new payload length (can be negative if shrinking payload)
		payloadSpace = len-_payloadLength;
	}

	// make space for payload (and payload marker if necessary)
	int newLen = _pduLength+payloadSpace+markerSpace;
	if(!_constructedFromBuffer) {
		uint8_t* newPDU = (uint8_t*)realloc(_pdu,newLen);
		if(newPDU==NULL) {
			DBG("Cannot allocate (or shrink) space for payload");
			return NULL;
		}
		_pdu = newPDU;
		_bufferLength = newLen;
	} else {
		// constructed from buffer, check space
		DBG("newLen: %d, _bufferLength: %d",newLen,_bufferLength);
		if(newLen>_bufferLength) {
			DBG("Buffer too small to contain desired payload, needed %d, got %d.",newLen-_pduLength,_bufferLength-_pduLength);
			return NULL;
		}
	}

	// deal with fresh allocation case separately
	if(_payloadPointer==NULL) {
		// set payload marker
		_pdu[_pduLength] = 0xFF;
		// payload at end of old PDU
		_payloadPointer = &_pdu[_pduLength+1];
		_pduLength = newLen;
		_payloadLength = len;
		return _payloadPointer;
	}

	// otherwise, just adjust length of PDU
	_pduLength = newLen;
	_payloadLength = len;
	DBG("Leaving mallocPayload");
	return _payloadPointer;
}

/// Set the payload to the byte sequence specified. Allocates memory in dynamic PDU if necessary.
/**
 * This will set the payload to \b payload. It will allocate memory in the case where the PDU was
 * constructed without an external buffer.
 *
 * This will fail either if the fixed buffer isn't big enough, or if memory could not be allocated
 * in the non-external-buffer case.
 * 
 * \param payload Pointer to payload byte sequence.
 * \param len Length of payload byte sequence.
 * \return 0 on success, 1 on failure.
 */
int CoapPDU::setPayload(uint8_t *payload, int len) {
	if(payload==NULL) {
		DBG("NULL payload pointer.");
		return 1;
	}

	uint8_t *payloadPointer = mallocPayload(len);
	if(payloadPointer==NULL) {
		DBG("Allocation of payload failed");
		return 1;
	}

	// copy payload contents
	memcpy(payloadPointer,payload,len);

	return 0;
}

/// Returns a pointer to the payload buffer.
uint8_t* CoapPDU::getPayloadPointer() {
	return _payloadPointer;
}

/// Gets the length of the payload buffer.
int CoapPDU::getPayloadLength() {
	return _payloadLength;
}

/// Returns a pointer to a buffer which is a copy of the payload buffer (dynamically allocated).
uint8_t* CoapPDU::getPayloadCopy() {
	if(_payloadLength==0) {
		return NULL;
	}

	// malloc space for copy
	uint8_t *payload = (uint8_t*)malloc(_payloadLength);
	if(payload==NULL) {
		DBG("Unable to allocate memory for payload");
		return NULL;
	}

	// copy and return
	memcpy(payload,_payloadPointer,_payloadLength);
	return payload;
}

/// Shorthand for setting the content-format option.
/**
 * Sets the content-format to the specified value (adds an option). 
 * \param format The content format, one of:
 *
 * - COAP_CONTENT_FORMAT_TEXT_PLAIN
 * - COAP_CONTENT_FORMAT_APP_LINK
 * - COAP_CONTENT_FORMAT_APP_XML
 * - COAP_CONTENT_FORMAT_APP_OCTET
 * - COAP_CONTENT_FORMAT_APP_EXI
 * - COAP_CONTENT_FORMAT_APP_JSON
 *
 * \return 0 on success, 1 on failure.
 */
int CoapPDU::setContentFormat(CoapPDU::ContentFormat format) {
	if(format==0) {
		// minimal representation means null option value
		if(addOption(CoapPDU::COAP_OPTION_CONTENT_FORMAT,0,NULL)!=0) {
			DBG("Error setting content format");
			return 1;
		}
		return 0;
	}

	uint8_t c[2];

	// just use 1 byte if can do it
	if(format<256) {
		c[0] = format;
		if(addOption(CoapPDU::COAP_OPTION_CONTENT_FORMAT,1,c)!=0) {
			DBG("Error setting content format");
			return 1;
		}
		return 0;
	}

	uint16_t networkOrder = htons(format);
	c[0] &= 0x00;
	c[0] |= (networkOrder >> 8);     // MSB
	c[1] &= 0x00;
	c[1] |= (networkOrder & 0x00FF); // LSB
	if(addOption(CoapPDU::COAP_OPTION_CONTENT_FORMAT,2,c)!=0) {
		DBG("Error setting content format");
		return 1;
	}
	return 0;
}

/// PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE
/// PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE

/// Moves a block of bytes to end of PDU from given offset.
/**
 * This moves the block of bytes _pdu[_pduLength-1-shiftOffset-shiftAmount] ... _pdu[_pduLength-1-shiftOffset]
 * to the end of the PDU.
 * \param shiftOffset End of block to move, relative to end of PDU (-1).
 * \param shiftAmount Length of block to move.
 */
void CoapPDU::shiftPDUUp(int shiftOffset, int shiftAmount) {
	DBG("shiftOffset: %d, shiftAmount: %d",shiftOffset,shiftAmount);
	int destPointer = _pduLength-1;
	int srcPointer  = destPointer-shiftOffset;
	while(shiftAmount--) {
		_pdu[destPointer] = _pdu[srcPointer];
		destPointer--;
		srcPointer--;
	}
}

/// Moves a block of bytes down a specified number of steps. 
/**
 * Moves the block of bytes _pdu[startLocation+shiftOffset] ... _pdu[startLocation+shiftOffset+shiftAmount]
 * down to \b startLocation.
 * \param startLocation Index where to shift the block to.
 * \param shiftOffset Where the block starts, relative to start index.
 * \param shiftAmount Length of block to shift.
 */
void CoapPDU::shiftPDUDown(int startLocation, int shiftOffset, int shiftAmount) {
	DBG("startLocation: %d, shiftOffset: %d, shiftAmount: %d",startLocation,shiftOffset,shiftAmount);
	int srcPointer = startLocation+shiftOffset;
	while(shiftAmount--) {
		_pdu[startLocation] = _pdu[srcPointer];
		startLocation++;
		srcPointer++;
	}
}

/// Gets the payload length of an option.
/**
 * \param option Pointer to location of option in PDU.
 * \return The 16 bit option-payload length.
 */
uint16_t CoapPDU::getOptionValueLength(uint8_t *option) {
	uint16_t delta = (option[0] & 0xF0) >> 4;
	uint16_t length = (option[0] & 0x0F);
	// no extra bytes
	if(length<13) {
		return length;
	}
	
	// extra bytes skip header
	int offset = 1;
	// skip extra option delta bytes
	if(delta==13) {
		offset++;
	} else if(delta==14) {
		offset+=2;
	}

	// process length
	if(length==13) {
		return (option[offset]+13);
	} else {
		// need to convert to host order
		uint16_t networkOrder = 0x0000;
		networkOrder |= option[offset++];
		networkOrder <<= 8;
		networkOrder |= option[offset];
		uint16_t hostOrder = ntohs(networkOrder);
		return hostOrder+269;
	}

}

/// Gets the delta of an option.
/**
 * \param option Pointer to location of option in PDU.
 * \return The 16 bit delta.
 */
uint16_t CoapPDU::getOptionDelta(uint8_t *option) {
	uint16_t delta = (option[0] & 0xF0) >> 4;
	if(delta<13) {
		return delta;
	} else if(delta==13) {
		// single byte option delta
		return (option[1]+13);
	} else if(delta==14) {
		// double byte option delta
		// need to convert to host order
		uint16_t networkOrder = 0x0000;
		networkOrder |= option[1];
		networkOrder <<= 8;
		networkOrder |= option[2];
		uint16_t hostOrder = ntohs(networkOrder);
		return hostOrder+269;
	} else {
		// should only ever occur in payload marker
		return delta;
	}
}

/// Finds the insertion position in the current list of options for the specified option.
/**
 * \param optionNumber The option's number.
 * \param prevOptionNumber A pointer to a uint16_t which will store the option number of the option previous
 * to the insertion point.
 * \return 0 on success, 1 on failure. \b prevOptionNumber will contain the option number of the option
 * before the insertion position (for example 0 if no options have been inserted).
 */
int CoapPDU::findInsertionPosition(uint16_t optionNumber, uint16_t *prevOptionNumber) {
	// zero this for safety
	*prevOptionNumber = 0x00;

	DBG("_pduLength: %d",_pduLength);

	// if option is bigger than any currently stored, it goes at the end
	// this includes the case that no option has yet been added
	if( (optionNumber >= _maxAddedOptionNumber) || (_pduLength == (COAP_HDR_SIZE+getTokenLength())) ) {
		*prevOptionNumber = _maxAddedOptionNumber;
		return _pduLength;
	}

	// otherwise walk over the options
	int optionPos = COAP_HDR_SIZE + getTokenLength();
	uint16_t optionDelta = 0, optionValueLength = 0;
	uint16_t currentOptionNumber = 0;
	while(optionPos<_pduLength && _pdu[optionPos]!=0xFF) {
		optionDelta = getOptionDelta(&_pdu[optionPos]);
		currentOptionNumber += optionDelta;
		optionValueLength = getOptionValueLength(&_pdu[optionPos]);
		// test if this is insertion position
		if(currentOptionNumber>optionNumber) {
			return optionPos;
		}
		// keep track of the last valid option number
		*prevOptionNumber = currentOptionNumber;
		// move onto next option
		optionPos += computeExtraBytes(optionDelta);
		optionPos += computeExtraBytes(optionValueLength);
		optionPos += optionValueLength;
		optionPos++; // (for mandatory option header byte)
	}
	return optionPos;

}

/// CoAP uses a minimal-byte representation for length fields. This returns the number of bytes needed to represent a given length.
int CoapPDU::computeExtraBytes(uint16_t n) {
	if(n<13) {
		return 0;
	}

	if(n<269) { 
		return 1;
	}
	
	return 2;
}

/// Set the option delta to the specified value.
/** 
 * This assumes space has been made for the option delta.
 * \param optionPosition The index of the option in the PDU.
 * \param optionDelta The option delta value to set.
 */
void CoapPDU::setOptionDelta(int optionPosition, uint16_t optionDelta) {
	int headerStart = optionPosition;
	// clear the old option delta bytes
	_pdu[headerStart] &= 0x0F;

	// set the option delta bytes
	if(optionDelta<13) {
		_pdu[headerStart] |= (optionDelta << 4);
	} else if(optionDelta<269) {
	   // 1 extra byte
		_pdu[headerStart] |= 0xD0; // 13 in first nibble
		_pdu[++optionPosition] &= 0x00;
		_pdu[optionPosition] |= (optionDelta-13);
	} else {
		// 2 extra bytes, network byte order uint16_t
		_pdu[headerStart] |= 0xE0; // 14 in first nibble
		optionDelta = htons(optionDelta-269);
		_pdu[++optionPosition] &= 0x00;
		_pdu[optionPosition] |= (optionDelta >> 8);     // MSB
		_pdu[++optionPosition] &= 0x00;
		_pdu[optionPosition] |= (optionDelta & 0x00FF); // LSB
	}
}

/// Insert an option in-memory at the specified location.
/**
 * This assumes that there is enough space at the location specified.
 * \param insertionPosition Position in the PDU where the option should be placed.
 * \param optionDelta The delta value for the option.
 * \param optionValueLength The length of the option value.
 * \param optionValue A pointer to the sequence of bytes representing the option value.
 * \return 0 on success, 1 on failure.
 */
int CoapPDU::insertOption(
	int insertionPosition,
	uint16_t optionDelta, 
	uint16_t optionValueLength,
	uint8_t *optionValue) {

	int headerStart = insertionPosition;

	// clear old option header start
	_pdu[headerStart] &= 0x00;

	// set the option delta bytes
	if(optionDelta<13) {
		_pdu[headerStart] |= (optionDelta << 4);
	} else if(optionDelta<269) {
	   // 1 extra byte
		_pdu[headerStart] |= 0xD0; // 13 in first nibble
		_pdu[++insertionPosition] &= 0x00;
		_pdu[insertionPosition] |= (optionDelta-13);
	} else {
		// 2 extra bytes, network byte order uint16_t
		_pdu[headerStart] |= 0xE0; // 14 in first nibble
		optionDelta = htons(optionDelta-269);
		_pdu[++insertionPosition] &= 0x00;
		_pdu[insertionPosition] |= (optionDelta >> 8);     // MSB
		_pdu[++insertionPosition] &= 0x00;
		_pdu[insertionPosition] |= (optionDelta & 0x00FF); // LSB
	}

	// set the option value length bytes
	if(optionValueLength<13) {
		_pdu[headerStart] |= (optionValueLength & 0x000F);
	} else if(optionValueLength<269) {
		_pdu[headerStart] |= 0x0D; // 13 in second nibble
		_pdu[++insertionPosition] &= 0x00;
		_pdu[insertionPosition] |= (optionValueLength-13);
	} else {
		_pdu[headerStart] |= 0x0E; // 14 in second nibble
		// this is in network byte order
		DBG("optionValueLength: %u",optionValueLength);
		uint16_t networkOrder = htons(optionValueLength-269);
		_pdu[++insertionPosition] &= 0x00;
		_pdu[insertionPosition] |= (networkOrder >> 8);     // MSB
		_pdu[++insertionPosition] &= 0x00;
		_pdu[insertionPosition] |= (networkOrder & 0x00FF); // LSB
	}

	// and finally copy the option value itself
	memcpy(&_pdu[++insertionPosition],optionValue,optionValueLength);

	return 0;
}

// DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG
// DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG

/// Prints the PDU in human-readable format.
void CoapPDU::printHuman() {
	INFO("__________________");
	if(_constructedFromBuffer) {
		INFO("PDU was constructed from buffer of %d bytes",_bufferLength);
	}
	INFO("PDU is %d bytes long",_pduLength);
	INFO("CoAP Version: %d",getVersion());
	INFOX("Message Type: ");
	switch(getType()) {
		case COAP_CONFIRMABLE:
			INFO("Confirmable");
		break;

		case COAP_NON_CONFIRMABLE:
			INFO("Non-Confirmable");
		break;

		case COAP_ACKNOWLEDGEMENT:
			INFO("Acknowledgement");
		break;

		case COAP_RESET:
			INFO("Reset");
		break;
	}
	INFO("Token length: %d",getTokenLength());
	INFOX("Code: ");
	switch(getCode()) {
		case COAP_EMPTY:
			INFO("0.00 Empty");
		break;
		case COAP_GET:
			INFO("0.01 GET");
		break;
		case COAP_POST:
			INFO("0.02 POST");
		break;
		case COAP_PUT:
			INFO("0.03 PUT");
		break;
		case COAP_DELETE:
			INFO("0.04 DELETE");
		break;
		case COAP_CREATED:
			INFO("2.01 Created");
		break;
		case COAP_DELETED:
			INFO("2.02 Deleted");
		break;
		case COAP_VALID:
			INFO("2.03 Valid");
		break;
		case COAP_CHANGED:
			INFO("2.04 Changed");
		break;
		case COAP_CONTENT:
			INFO("2.05 Content");
		break;
		case COAP_BAD_REQUEST:
			INFO("4.00 Bad Request");
		break;
		case COAP_UNAUTHORIZED:
			INFO("4.01 Unauthorized");
		break;
		case COAP_BAD_OPTION:
			INFO("4.02 Bad Option");
		break;
		case COAP_FORBIDDEN:
			INFO("4.03 Forbidden");
		break;
		case COAP_NOT_FOUND:
			INFO("4.04 Not Found");
		break;
		case COAP_METHOD_NOT_ALLOWED:
			INFO("4.05 Method Not Allowed");
		break;
		case COAP_NOT_ACCEPTABLE:
			INFO("4.06 Not Acceptable");
		break;
		case COAP_PRECONDITION_FAILED:
			INFO("4.12 Precondition Failed");
		break;
		case COAP_REQUEST_ENTITY_TOO_LARGE:
			INFO("4.13 Request Entity Too Large");
		break;
		case COAP_UNSUPPORTED_CONTENT_FORMAT:
			INFO("4.15 Unsupported Content-Format");
		break;
		case COAP_INTERNAL_SERVER_ERROR:
			INFO("5.00 Internal Server Error");
		break;
		case COAP_NOT_IMPLEMENTED:
			INFO("5.01 Not Implemented");
		break;
		case COAP_BAD_GATEWAY:
			INFO("5.02 Bad Gateway");
		break;
		case COAP_SERVICE_UNAVAILABLE:
			INFO("5.03 Service Unavailable");
		break;
		case COAP_GATEWAY_TIMEOUT:
			INFO("5.04 Gateway Timeout");
		break;
		case COAP_PROXYING_NOT_SUPPORTED:
			INFO("5.05 Proxying Not Supported");
		break;
	}

	// print message ID
	INFO("Message ID: %u",getMessageID());

	// print token value
	int tokenLength = getTokenLength();
	uint8_t *tokenPointer = getPDUPointer()+COAP_HDR_SIZE;
	if(tokenLength==0) {
		INFO("No token.");
	} else {
		INFO("Token of %d bytes.",tokenLength);
		INFOX("   Value: 0x");
		for(int j=0; j<tokenLength; j++) {
			INFOX("%.2x",tokenPointer[j]);
		}
		INFO(" ");
	}

	// print options
	CoapPDU::CoapOption* options = getOptions();
	INFO("%d options:",_numOptions);
	for(int i=0; i<_numOptions; i++) {
		INFO("OPTION (%d/%d)",i,_numOptions);
		INFO("   Option number (delta): %hu (%hu)",options[i].optionNumber,options[i].optionDelta);
		INFOX("   Name: ");
		switch(options[i].optionNumber) {
			case COAP_OPTION_IF_MATCH:
				INFO("IF_MATCH");
			break;
			case COAP_OPTION_URI_HOST:
				INFO("URI_HOST");
			break;
			case COAP_OPTION_ETAG:
				INFO("ETAG");
			break;
			case COAP_OPTION_IF_NONE_MATCH:
				INFO("IF_NONE_MATCH");
			break;
			case COAP_OPTION_OBSERVE:
				INFO("OBSERVE");
			break;
			case COAP_OPTION_URI_PORT:
				INFO("URI_PORT");
			break;
			case COAP_OPTION_LOCATION_PATH:
				INFO("LOCATION_PATH");
			break;
			case COAP_OPTION_URI_PATH:
				INFO("URI_PATH");
			break;
			case COAP_OPTION_CONTENT_FORMAT:
				INFO("CONTENT_FORMAT");
			break;
			case COAP_OPTION_MAX_AGE:
				INFO("MAX_AGE");
			break;
			case COAP_OPTION_URI_QUERY:
				INFO("URI_QUERY");
			break;
			case COAP_OPTION_ACCEPT:
				INFO("ACCEPT");
			break;
			case COAP_OPTION_LOCATION_QUERY:
				INFO("LOCATION_QUERY");
			break;
			case COAP_OPTION_PROXY_URI:
				INFO("PROXY_URI");
			break;
			case COAP_OPTION_PROXY_SCHEME:
				INFO("PROXY_SCHEME");
			break;
			case COAP_OPTION_BLOCK1:
				INFO("BLOCK1");
			break;
			case COAP_OPTION_BLOCK2:
				INFO("BLOCK2");
			break;
			case COAP_OPTION_SIZE1:
				INFO("SIZE1");
			break;
			case COAP_OPTION_SIZE2:
				INFO("SIZE2");
			break;
			default:
				INFO("Unknown option");
			break;
		}
		INFO("   Value length: %u",options[i].optionValueLength);
		INFOX("   Value: \"");
		for(int j=0; j<options[i].optionValueLength; j++) {
			char c = options[i].optionValuePointer[j];
			if((c>='!'&&c<='~')||c==' ') {
				INFOX("%c",c);
			} else {
				INFOX("\\%.2d",c);
			}
		}
		INFO("\"");
	}
	
	// print payload
	if(_payloadLength==0) {
		INFO("No payload.");
	} else {
		INFO("Payload of %d bytes",_payloadLength);
		INFOX("   Value: \"");
		for(int j=0; j<_payloadLength; j++) {
			char c = _payloadPointer[j];
			if((c>='!'&&c<='~')||c==' ') {
				INFOX("%c",c);
			} else {
				INFOX("\\%.2x",c);
			}
		}
		INFO("\"");
	}
	INFO("__________________");
}

/// Prints the PDU as a c array (useful for debugging or hardcoding PDUs)
void CoapPDU::printPDUAsCArray() {
	printf("const uint8_t array[] = {\r\n   ");
	for(int i=0; i<_pduLength; i++) {
		printf("0x%.2x, ",_pdu[i]);
	}
	printf("\r\n};\r\n");
}

/// A routine for printing an option in human-readable format.
/**
 * \param option This is a pointer to where the option begins in the PDU.
 */
void CoapPDU::printOptionHuman(uint8_t *option) {
	// compute some useful stuff
	uint16_t optionDelta = getOptionDelta(option);
	uint16_t optionValueLength = getOptionValueLength(option);
	int extraDeltaBytes = computeExtraBytes(optionDelta);
	int extraValueLengthBytes = computeExtraBytes(optionValueLength);
	int totalLength = 1+extraDeltaBytes+extraValueLengthBytes+optionValueLength;

	if(totalLength>_pduLength) {
		totalLength = &_pdu[_pduLength-1]-option;
		DBG("New length: %u",totalLength);
	}

	// print summary
	DBG("~~~~~~ Option ~~~~~~");
	DBG("Delta: %u, Value length: %u",optionDelta,optionValueLength);

	// print all bytes
	DBG("All bytes (%d):",totalLength);
	for(int i=0; i<totalLength; i++) {
		if(i%4==0) {
			DBG(" ");
			DBGX("   %.2d ",i);
		}
		CoapPDU::printBinary(option[i]); DBGX(" ");
	}
	DBG(" "); DBG(" ");

	// print header byte
	DBG("Header byte:");
	DBGX("   ");
	CoapPDU::printBinary(*option++);
	DBG(" "); DBG(" ");

	// print extended delta bytes
	if(extraDeltaBytes) {
		DBG("Extended delta bytes (%d) in network order: ",extraDeltaBytes);
		DBGX("   ");
		while(extraDeltaBytes--) {
			CoapPDU::printBinary(*option++); DBGX(" ");
		}
	} else {
		DBG("No extended delta bytes");
	}
	DBG(" "); DBG(" ");

	// print extended value length bytes
	if(extraValueLengthBytes) {
		DBG("Extended value length bytes (%d) in network order: ",extraValueLengthBytes);
		DBGX("   ");
		while(extraValueLengthBytes--) {
			CoapPDU::printBinary(*option++); DBGX(" ");
		}
	} else {
		DBG("No extended value length bytes");
	}
	DBG(" ");

	// print option value
	DBG("Option value bytes:");
	for(int i=0; i<optionValueLength; i++) {
		if(i%4==0) {
			DBG(" ");
			DBGX("   %.2d ",i);
		}
		CoapPDU::printBinary(*option++);
		DBGX(" ");
	}
	DBG(" ");
}

/// Dumps the PDU header in hex.
void CoapPDU::printHex() {
	printf("Hexdump dump of PDU\r\n");
	printf("%.2x %.2x %.2x %.2x",_pdu[0],_pdu[1],_pdu[2],_pdu[3]);
}

/// Dumps the entire PDU in binary.
void CoapPDU::printBin() {
	for(int i=0; i<_pduLength; i++) {
		if(i%4==0) {
			printf("\r\n");
			printf("%.2d ",i);
		} 
		CoapPDU::printBinary(_pdu[i]); printf(" ");
	}
	printf("\r\n");
}

/// Prints a single byte in binary.
void CoapPDU::printBinary(uint8_t b) {
	printf("%d%d%d%d%d%d%d%d",
		(b&0x80)&&0x01,
		(b&0x40)&&0x01,
		(b&0x20)&&0x01,
		(b&0x10)&&0x01,
		(b&0x08)&&0x01,
		(b&0x04)&&0x01,
		(b&0x02)&&0x01,
		(b&0x01)&&0x01);
}

/// Dumps the PDU as a byte sequence to stdout.
void CoapPDU::print() {
	fwrite(_pdu,1,_pduLength,stdout);
}