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

Dependents:   cantcoap_cellular_test yeswecancoap

Teaser

CoAP implementation that focuses on simplicity by offering a minimal set of functions and straightforward interface.

	CoapPDU *pdu = new CoapPDU();
	pdu->setType(CoapPDU::COAP_CONFIRMABLE);
	pdu->setCode(CoapPDU::COAP_GET);
	pdu->setToken((uint8_t*)"\3\2\1\0",4);
	pdu->setMessageID(0x0005);
	pdu->setURI((char*)"test",4);

	// send packet 
	ret = send(sockfd,pdu->getPDUPointer(),pdu->getPDULength(),0);

...

	// receive packet
	ret = recvfrom(sockfd,&buffer,BUF_LEN,0,(sockaddr*)&recvAddr,&recvAddrLen);
	CoapPDU *recvPDU = new CoapPDU((uint8_t*)buffer,ret);
	if(recvPDU->validate()) {
		recvPDU->getURI(uriBuffer,URI_BUF_LEN,&recvURILen);
		...
	}

Long description

This is a CoAP implementation with a focus on simplicity. The library only provides PDU construction and de-construction.

The user is expected to deal with retransmissions, timeouts, and message ID matching themselves. This isn’t as arduous as it sounds and makes a lot more sense on a constrained device.

Imagine for example a simple microcontroller sensor that only reports readings once every 15 minutes, and only sends a few packets each time. Do you really need a complicated framework to deal with acknowledgements and re-transmissions?

Since CoAP recommends you only send one packet at at time, this means you only need to keep track of one on-going transaction at a time. Yeah... I think you’re capable of this.

Furthermore, the timers and interrupt processes between different embedded processor architectures, vary quite a bit. So it often makes sense to write the packet sending processes yourself.

Finally, you might be sending the packets over odd transport bearers such as a SMS (woah dude, that's just totally wild) or a simple radio bearer. In which case, it’s easiest to deal with buffers. If I built retransmission handlers, they’d all be UDP/IP specific and would bloat the code for no reason.

Resources

Github page for the cantcoap library is here:

https://github.com/staropram/cantcoap

Doxygen for the library is here:

http://staropram.github.io/cantcoap/index.html

Examples

Construction

There are a couple of different ways to construct a PDU depending on whether you want the library to allocate memory for you, or whether you have an external buffer you want to use. You can also re-purpose existing objects.

Using a managed object

The simplest usage scenario hands control of memory allocation to the library:

CoapPDU *pdu = new CoapPDU();
...
pdu->setType(CoapPDU::COAP_CONFIRMABLE);
pdu->setCode(CoapPDU::COAP_GET);
pdu->addOption(11,5,(uint8_t*)"hello");
pdu->addOption(11,5,(uint8_t*)"there");
pdu->addOption(11,6,(uint8_t*)"server");

In this case you just call the default constructor. That's it. The library handles memory from there-on out. For example, when adding each of those options, the library will realloc the pdu to accomodate space for them. It will also shrink the PDU if something changes (like the token length) so that it always uses the minimum amount of memory.

When you free the PDU, all data including the buffer is deleted. The PDU can also be reused as shown below.

Using an external buffer for memory

There are two obvious reasons why you would do this:

1. The buffer contains a CoAP PDU and you want to access the data in the PDU. 2. Buffers cost space and allocating memory consumes processor resources. On embedded targets it is often simpler to reuse buffers where possible.

The first instance is a special case and requires some extra work. Just using an external buffer is as simple as follows:

uint8_t *buffer[100];
CoapPDU *pdu = new CoapPDU((uint8_t*)buffer,100,0);
...
pdu->setType(CoapPDU::COAP_CONFIRMABLE);
pdu->setCode(CoapPDU::COAP_GET);
pdu->addOption(11,5,(uint8_t*)"hello");
pdu->addOption(11,5,(uint8_t*)"there");
pdu->addOption(11,6,(uint8_t*)"server");

The PDU is constructed as normal except that the memory of your buffer is used instead of allocated memory.

A call such as this:

pdu->addOption(11,5,(uint8_t*)"hello");

Will fail if there is no space left in the buffer.

When you delete the object, the buffer is not freed. Hey, it's your buffer mannn!

Reusing an existing object

Regardless of whether you constructed a PDU using either of the above methods, you can always reuse it:

pdu->reset(); 
...
pdu->setType(CoapPDU::COAP_CONFIRMABLE);
pdu->setCode(CoapPDU::COAP_GET);
pdu->addOption(11,5,(uint8_t*)"hello");
pdu->addOption(11,5,(uint8_t*)"there");
pdu->addOption(11,6,(uint8_t*)"server");

The only difference is that if the PDU was initially constructed using managed-memory, then it will continue to have managed-memory. Whereas if the PDU was constructed with an external buffer, then you are limited in space by the size of the buffer you used.

Receving CoAP packets over a network or something

In this case you have a CoAP PDU in a buffer you just gobbled from a socket and want to read it:

uint8_t *buffer[100];
int ret = recvfrom(sockfd,&buffer,BUF_LEN,0,(sockaddr*)&recvAddr,&recvAddrLen);
CoapPDU *recvPDU = new CoapPDU((uint8_t*)buffer,ret,100);
if(recvPDU->validate()) {
	recvPDU->printHuman();
	// do your work
}

You must call CoapPDU::validate() and get a positive response before accessing any of the data members. This sets up some internal pionters and so on, so if you fail to do it, undefined behaviour will result.

Note that the constructor is just a shorthand for the external-buffer-constructor explained above, and you can use the long form if you want. For example. you might want to use the long form if you have a buffer bigger than the PDU and you expect to reuse it.

You can reuse this object by resetting it as above.

If you reuse such an object, you need to set the PDU length manually because there is no way to deduce the PDU length using validate():

	// earlier
	#define BUFLEN 500
	char buffer[BUFLEN];
	CoapPDU *recvPDU = new CoapPDU((uint8_t*)buffer,BUFLEN,BUFLEN);

	...

	while(1) {
		// receive packet
		ret = sockfd,&buffer,BUFLEN,0);
		if(ret==-1) {
			INFO("Error receiving data");
			// handle error
		}

		// validate packet
		// you should also check that ret doesn't exceed buffer length
		recvPDU->setPDULength(ret);
		if(recvPDU->validate()!=1) {
			INFO("Malformed CoAP packet");
			// handle error
		}
	}
Committer:
ashleymills
Date:
Tue Oct 08 14:36:01 2013 +0000
Revision:
0:3d62a105fd34
Child:
1:5eec2844ad47
CoAP library with a focus on simplicity. Init.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
ashleymills 0:3d62a105fd34 1 /*
ashleymills 0:3d62a105fd34 2 Copyright (c) 2013, Ashley Mills.
ashleymills 0:3d62a105fd34 3 All rights reserved.
ashleymills 0:3d62a105fd34 4
ashleymills 0:3d62a105fd34 5 Redistribution and use in source and binary forms, with or without
ashleymills 0:3d62a105fd34 6 modification, are permitted provided that the following conditions are met:
ashleymills 0:3d62a105fd34 7
ashleymills 0:3d62a105fd34 8 1. Redistributions of source code must retain the above copyright notice, this
ashleymills 0:3d62a105fd34 9 list of conditions and the following disclaimer.
ashleymills 0:3d62a105fd34 10 2. Redistributions in binary form must reproduce the above copyright notice,
ashleymills 0:3d62a105fd34 11 this list of conditions and the following disclaimer in the documentation
ashleymills 0:3d62a105fd34 12 and/or other materials provided with the distribution.
ashleymills 0:3d62a105fd34 13
ashleymills 0:3d62a105fd34 14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ashleymills 0:3d62a105fd34 15 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
ashleymills 0:3d62a105fd34 16 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
ashleymills 0:3d62a105fd34 17 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ashleymills 0:3d62a105fd34 18 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
ashleymills 0:3d62a105fd34 19 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
ashleymills 0:3d62a105fd34 20 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ashleymills 0:3d62a105fd34 21 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
ashleymills 0:3d62a105fd34 22 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
ashleymills 0:3d62a105fd34 23 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
ashleymills 0:3d62a105fd34 24 */
ashleymills 0:3d62a105fd34 25
ashleymills 0:3d62a105fd34 26 // version, 2 bits
ashleymills 0:3d62a105fd34 27 // type, 2 bits
ashleymills 0:3d62a105fd34 28 // 00 Confirmable
ashleymills 0:3d62a105fd34 29 // 01 Non-confirmable
ashleymills 0:3d62a105fd34 30 // 10 Acknowledgement
ashleymills 0:3d62a105fd34 31 // 11 Reset
ashleymills 0:3d62a105fd34 32
ashleymills 0:3d62a105fd34 33 // token length, 4 bits
ashleymills 0:3d62a105fd34 34 // length of token in bytes (only 0 to 8 bytes allowed)
ashleymills 0:3d62a105fd34 35 #include <stdio.h>
ashleymills 0:3d62a105fd34 36 #include <stdlib.h>
ashleymills 0:3d62a105fd34 37 #include <stdint.h>
ashleymills 0:3d62a105fd34 38 #include <string.h>
ashleymills 0:3d62a105fd34 39 #include "cantcoap.h"
ashleymills 0:3d62a105fd34 40 #ifndef MBED
ashleymills 0:3d62a105fd34 41 #include "arpa/inet.h"
ashleymills 0:3d62a105fd34 42 #include "netdb.h"
ashleymills 0:3d62a105fd34 43 #else
ashleymills 0:3d62a105fd34 44 #include "bsd_socket.h"
ashleymills 0:3d62a105fd34 45 #endif
ashleymills 0:3d62a105fd34 46
ashleymills 0:3d62a105fd34 47
ashleymills 0:3d62a105fd34 48 /// Memory-managed constructor. Buffer for PDU is dynamically sized and allocated by the object.
ashleymills 0:3d62a105fd34 49 /**
ashleymills 0:3d62a105fd34 50 * When using this constructor, the CoapPDU class will allocate space for the PDU.
ashleymills 0:3d62a105fd34 51 * Contrast this with the parameterized constructors, which allow the use of an external buffer.
ashleymills 0:3d62a105fd34 52 *
ashleymills 0:3d62a105fd34 53 * Note, the PDU container and space can be reused by issuing a CoapPDU::reset(). If the new PDU exceeds the
ashleymills 0:3d62a105fd34 54 * space of the previously allocated memory, then further memory will be dynamically allocated.
ashleymills 0:3d62a105fd34 55 *
ashleymills 0:3d62a105fd34 56 * Deleting the object will free the Object container and all dynamically allocated memory.
ashleymills 0:3d62a105fd34 57 *
ashleymills 0:3d62a105fd34 58 * \note It would have been nice to use something like UDP_CORK or MSG_MORE, to allow separate buffers
ashleymills 0:3d62a105fd34 59 * for token, options, and payload but these FLAGS aren't implemented for UDP in LwIP so stuck with one buffer for now.
ashleymills 0:3d62a105fd34 60 *
ashleymills 0:3d62a105fd34 61 * CoAP version defaults to 1.
ashleymills 0:3d62a105fd34 62 *
ashleymills 0:3d62a105fd34 63 * \sa CoapPDU::CoapPDU(uint8_t *pdu, int pduLength), CoapPDU::CoapPDU::(uint8_t *buffer, int bufferLength, int pduLength),
ashleymills 0:3d62a105fd34 64 * CoapPDU:CoapPDU()~
ashleymills 0:3d62a105fd34 65 *
ashleymills 0:3d62a105fd34 66 */
ashleymills 0:3d62a105fd34 67 CoapPDU::CoapPDU() {
ashleymills 0:3d62a105fd34 68 // pdu
ashleymills 0:3d62a105fd34 69 _pdu = (uint8_t*)calloc(4,sizeof(uint8_t));
ashleymills 0:3d62a105fd34 70 _pduLength = 4;
ashleymills 0:3d62a105fd34 71 _bufferLength = _pduLength;
ashleymills 0:3d62a105fd34 72
ashleymills 0:3d62a105fd34 73 //options
ashleymills 0:3d62a105fd34 74 _numOptions = 0;
ashleymills 0:3d62a105fd34 75 _maxAddedOptionNumber = 0;
ashleymills 0:3d62a105fd34 76
ashleymills 0:3d62a105fd34 77 // payload
ashleymills 0:3d62a105fd34 78 _payloadPointer = NULL;
ashleymills 0:3d62a105fd34 79 _payloadLength = 0;
ashleymills 0:3d62a105fd34 80
ashleymills 0:3d62a105fd34 81 _constructedFromBuffer = 0;
ashleymills 0:3d62a105fd34 82
ashleymills 0:3d62a105fd34 83 setVersion(1);
ashleymills 0:3d62a105fd34 84 }
ashleymills 0:3d62a105fd34 85
ashleymills 0:3d62a105fd34 86 /// Construct a PDU using an external buffer. No copy of the buffer is made.
ashleymills 0:3d62a105fd34 87 /**
ashleymills 0:3d62a105fd34 88 * This constructor is normally used where a PDU has been received over the network, and it's length is known.
ashleymills 0:3d62a105fd34 89 * In this case the CoapPDU object is probably going to be used as a temporary container to access member values.
ashleymills 0:3d62a105fd34 90 *
ashleymills 0:3d62a105fd34 91 * It is assumed that \b pduLength is the length of the actual CoAP PDU, and consequently the buffer will also be this size,
ashleymills 0:3d62a105fd34 92 * contrast this with CoapPDU::CoapPDU(uint8_t *buffer, int bufferLength, int pduLength) which allows the buffer to
ashleymills 0:3d62a105fd34 93 * be larger than the PDU.
ashleymills 0:3d62a105fd34 94 *
ashleymills 0:3d62a105fd34 95 * A PDU constructed in this manner must be validated with CoapPDU::validate() before the member variables will be accessible.
ashleymills 0:3d62a105fd34 96 *
ashleymills 0:3d62a105fd34 97 * \warning The validation call parses the PDU structure to set some internal parameters. If you do
ashleymills 0:3d62a105fd34 98 * not validate the PDU, then the behaviour of member access functions will be undefined.
ashleymills 0:3d62a105fd34 99 *
ashleymills 0:3d62a105fd34 100 * The buffer can be reused by issuing a CoapPDU::reset() but the class will not change the size of the buffer. If the
ashleymills 0:3d62a105fd34 101 * newly constructed PDU exceeds the size of the buffer, the function called (for example CoapPDU::addOption) will fail.
ashleymills 0:3d62a105fd34 102 *
ashleymills 0:3d62a105fd34 103 * Deleting this object will only delete the Object container and will not delete the PDU buffer.
ashleymills 0:3d62a105fd34 104 *
ashleymills 0:3d62a105fd34 105 * @param pdu A pointer to an array of bytes which comprise the CoAP PDU
ashleymills 0:3d62a105fd34 106 * @param pduLength The length of the CoAP PDU pointed to by \b pdu
ashleymills 0:3d62a105fd34 107
ashleymills 0:3d62a105fd34 108 * \sa CoapPDU::CoapPDU(), CoapPDU::CoapPDU(uint8_t *buffer, int bufferLength, int pduLength)
ashleymills 0:3d62a105fd34 109 */
ashleymills 0:3d62a105fd34 110 CoapPDU::CoapPDU(uint8_t *pdu, int pduLength) {
ashleymills 0:3d62a105fd34 111 // pdu
ashleymills 0:3d62a105fd34 112 _pdu = pdu;
ashleymills 0:3d62a105fd34 113 _bufferLength = pduLength;
ashleymills 0:3d62a105fd34 114 _pduLength = pduLength;
ashleymills 0:3d62a105fd34 115
ashleymills 0:3d62a105fd34 116 _constructedFromBuffer = 1;
ashleymills 0:3d62a105fd34 117
ashleymills 0:3d62a105fd34 118 // options
ashleymills 0:3d62a105fd34 119 _numOptions = 0;
ashleymills 0:3d62a105fd34 120 _maxAddedOptionNumber = 0;
ashleymills 0:3d62a105fd34 121
ashleymills 0:3d62a105fd34 122 // payload
ashleymills 0:3d62a105fd34 123 _payloadPointer = NULL;
ashleymills 0:3d62a105fd34 124 _payloadLength = 0;
ashleymills 0:3d62a105fd34 125 }
ashleymills 0:3d62a105fd34 126
ashleymills 0:3d62a105fd34 127 /// Construct object from external buffer that may be larger than actual PDU.
ashleymills 0:3d62a105fd34 128 /**
ashleymills 0:3d62a105fd34 129 * This differs from CoapPDU::CoapPDU(uint8_t *pdu, int pduLength) in that the buffer may be larger
ashleymills 0:3d62a105fd34 130 * than the actual CoAP PDU contained int the buffer. This is typically used when a large buffer is reused
ashleymills 0:3d62a105fd34 131 * multiple times. Note that \b pduLength can be 0.
ashleymills 0:3d62a105fd34 132 *
ashleymills 0:3d62a105fd34 133 * If an actual CoAP PDU is passed in the buffer, \b pduLength should match its length. CoapPDU::validate() must
ashleymills 0:3d62a105fd34 134 * be called to initiate the object before member functions can be used.
ashleymills 0:3d62a105fd34 135 *
ashleymills 0:3d62a105fd34 136 * A PDU constructed in this manner must be validated with CoapPDU::validate() before the member variables will be accessible.
ashleymills 0:3d62a105fd34 137 *
ashleymills 0:3d62a105fd34 138 * \warning The validation call parses the PDU structure to set some internal parameters. If you do
ashleymills 0:3d62a105fd34 139 * not validate the PDU, then the behaviour of member access functions will be undefined.
ashleymills 0:3d62a105fd34 140 *
ashleymills 0:3d62a105fd34 141 * The buffer can be reused by issuing a CoapPDU::reset() but the class will not change the size of the buffer. If the
ashleymills 0:3d62a105fd34 142 * newly constructed PDU exceeds the size of the buffer, the function called (for example CoapPDU::addOption) will fail.
ashleymills 0:3d62a105fd34 143 *
ashleymills 0:3d62a105fd34 144 * Deleting this object will only delete the Object container and will not delete the PDU buffer.
ashleymills 0:3d62a105fd34 145 *
ashleymills 0:3d62a105fd34 146 * \param buffer A buffer which either contains a CoAP PDU or is intended to be used to construct one.
ashleymills 0:3d62a105fd34 147 * \param bufferLength The length of the buffer
ashleymills 0:3d62a105fd34 148 * \param pduLength If the buffer contains a CoAP PDU, this specifies the length of the PDU within the buffer.
ashleymills 0:3d62a105fd34 149 *
ashleymills 0:3d62a105fd34 150 * \sa CoapPDU::CoapPDU(), CoapPDU::CoapPDU(uint8_t *pdu, int pduLength)
ashleymills 0:3d62a105fd34 151 */
ashleymills 0:3d62a105fd34 152 CoapPDU::CoapPDU(uint8_t *buffer, int bufferLength, int pduLength) {
ashleymills 0:3d62a105fd34 153 // sanity
ashleymills 0:3d62a105fd34 154 if(pduLength<4&&pduLength!=0) {
ashleymills 0:3d62a105fd34 155 DBG("PDU cannot have a length less than 4");
ashleymills 0:3d62a105fd34 156 }
ashleymills 0:3d62a105fd34 157
ashleymills 0:3d62a105fd34 158 // pdu
ashleymills 0:3d62a105fd34 159 _pdu = buffer;
ashleymills 0:3d62a105fd34 160 _bufferLength = bufferLength;
ashleymills 0:3d62a105fd34 161 if(pduLength==0) {
ashleymills 0:3d62a105fd34 162 // this is actually a fresh pdu, header always exists
ashleymills 0:3d62a105fd34 163 _pduLength = 4;
ashleymills 0:3d62a105fd34 164 // make sure header is zeroed
ashleymills 0:3d62a105fd34 165 _pdu[0] = 0x00; _pdu[1] = 0x00; _pdu[2] = 0x00; _pdu[3] = 0x00;
ashleymills 0:3d62a105fd34 166 setVersion(1);
ashleymills 0:3d62a105fd34 167 } else {
ashleymills 0:3d62a105fd34 168 _pduLength = pduLength;
ashleymills 0:3d62a105fd34 169 }
ashleymills 0:3d62a105fd34 170
ashleymills 0:3d62a105fd34 171 _constructedFromBuffer = 1;
ashleymills 0:3d62a105fd34 172
ashleymills 0:3d62a105fd34 173 // options
ashleymills 0:3d62a105fd34 174 _numOptions = 0;
ashleymills 0:3d62a105fd34 175 _maxAddedOptionNumber = 0;
ashleymills 0:3d62a105fd34 176
ashleymills 0:3d62a105fd34 177 // payload
ashleymills 0:3d62a105fd34 178 _payloadPointer = NULL;
ashleymills 0:3d62a105fd34 179 _payloadLength = 0;
ashleymills 0:3d62a105fd34 180 }
ashleymills 0:3d62a105fd34 181
ashleymills 0:3d62a105fd34 182 /// Reset CoapPDU container so it can be reused to build a new PDU.
ashleymills 0:3d62a105fd34 183 /**
ashleymills 0:3d62a105fd34 184 * This resets the CoapPDU container, setting the pdu length, option count, etc back to zero. The
ashleymills 0:3d62a105fd34 185 * PDU can then be populated as if it were newly constructed.
ashleymills 0:3d62a105fd34 186 *
ashleymills 0:3d62a105fd34 187 * Note that the space available will depend on how the CoapPDU was originally constructed:
ashleymills 0:3d62a105fd34 188 * -# CoapPDU::CoapPDU()
ashleymills 0:3d62a105fd34 189 *
ashleymills 0:3d62a105fd34 190 * Available space initially be \b _pduLength. But further space will be allocated as needed on demand,
ashleymills 0:3d62a105fd34 191 * limited only by the OS/environment.
ashleymills 0:3d62a105fd34 192 *
ashleymills 0:3d62a105fd34 193 * -# CoapPDU::CoapPDU(uint8_t *pdu, int pduLength)
ashleymills 0:3d62a105fd34 194 *
ashleymills 0:3d62a105fd34 195 * Space is limited by the variable \b pduLength. The PDU cannot exceed \b pduLength bytes.
ashleymills 0:3d62a105fd34 196 *
ashleymills 0:3d62a105fd34 197 * -# CoapPDU::CoapPDU(uint8_t *buffer, int bufferLength, int pduLength)
ashleymills 0:3d62a105fd34 198 *
ashleymills 0:3d62a105fd34 199 * Space is limited by the variable \b bufferLength. The PDU cannot exceed \b bufferLength bytes.
ashleymills 0:3d62a105fd34 200 *
ashleymills 0:3d62a105fd34 201 * \return 0 on success, 1 on failure.
ashleymills 0:3d62a105fd34 202 */
ashleymills 0:3d62a105fd34 203 int CoapPDU::reset() {
ashleymills 0:3d62a105fd34 204 // pdu
ashleymills 0:3d62a105fd34 205 memset(_pdu,0x00,_bufferLength);
ashleymills 0:3d62a105fd34 206 // packet always has at least a header
ashleymills 0:3d62a105fd34 207 _pduLength = 4;
ashleymills 0:3d62a105fd34 208
ashleymills 0:3d62a105fd34 209 // options
ashleymills 0:3d62a105fd34 210 _numOptions = 0;
ashleymills 0:3d62a105fd34 211 _maxAddedOptionNumber = 0;
ashleymills 0:3d62a105fd34 212 // payload
ashleymills 0:3d62a105fd34 213 _payloadPointer = NULL;
ashleymills 0:3d62a105fd34 214 _payloadLength = 0;
ashleymills 0:3d62a105fd34 215 return 0;
ashleymills 0:3d62a105fd34 216 }
ashleymills 0:3d62a105fd34 217
ashleymills 0:3d62a105fd34 218 /// Validates a PDU constructed using an external buffer.
ashleymills 0:3d62a105fd34 219 /**
ashleymills 0:3d62a105fd34 220 * When a CoapPDU is constructed using an external buffer, the programmer must call this function to
ashleymills 0:3d62a105fd34 221 * check that the received PDU is a valid CoAP PDU.
ashleymills 0:3d62a105fd34 222 *
ashleymills 0:3d62a105fd34 223 * \warning The validation call parses the PDU structure to set some internal parameters. If you do
ashleymills 0:3d62a105fd34 224 * not validate the PDU, then the behaviour of member access functions will be undefined.
ashleymills 0:3d62a105fd34 225 *
ashleymills 0:3d62a105fd34 226 * \return 1 if the PDU validates correctly, 0 if not. XXX maybe add some error codes
ashleymills 0:3d62a105fd34 227 */
ashleymills 0:3d62a105fd34 228 int CoapPDU::validate() {
ashleymills 0:3d62a105fd34 229 if(_pduLength<4) {
ashleymills 0:3d62a105fd34 230 DBG("PDU has to be a minimum of 4 bytes. This: %d bytes",_pduLength);
ashleymills 0:3d62a105fd34 231 return 0;
ashleymills 0:3d62a105fd34 232 }
ashleymills 0:3d62a105fd34 233
ashleymills 0:3d62a105fd34 234 // check header
ashleymills 0:3d62a105fd34 235 // 0 1 2 3
ashleymills 0:3d62a105fd34 236 // 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
ashleymills 0:3d62a105fd34 237 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
ashleymills 0:3d62a105fd34 238 // |Ver| T | TKL | Code | Message ID |
ashleymills 0:3d62a105fd34 239 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
ashleymills 0:3d62a105fd34 240 // | Token (if any, TKL bytes) ...
ashleymills 0:3d62a105fd34 241 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
ashleymills 0:3d62a105fd34 242 // | Options (if any) ...
ashleymills 0:3d62a105fd34 243 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
ashleymills 0:3d62a105fd34 244 // |1 1 1 1 1 1 1 1| Payload (if any) ...
ashleymills 0:3d62a105fd34 245 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
ashleymills 0:3d62a105fd34 246
ashleymills 0:3d62a105fd34 247 DBG("Version: %d",getVersion());
ashleymills 0:3d62a105fd34 248 DBG("Type: %d",getType());
ashleymills 0:3d62a105fd34 249
ashleymills 0:3d62a105fd34 250 // token length must be between 0 and 8
ashleymills 0:3d62a105fd34 251 int tokenLength = getTokenLength();
ashleymills 0:3d62a105fd34 252 if(tokenLength<0||tokenLength>8) {
ashleymills 0:3d62a105fd34 253 DBG("Invalid token length: %d",tokenLength);
ashleymills 0:3d62a105fd34 254 return 0;
ashleymills 0:3d62a105fd34 255 }
ashleymills 0:3d62a105fd34 256 DBG("Token length: %d",tokenLength);
ashleymills 0:3d62a105fd34 257 // check total length
ashleymills 0:3d62a105fd34 258 if((COAP_HDR_SIZE+tokenLength)>_pduLength) {
ashleymills 0:3d62a105fd34 259 DBG("Token length would make pdu longer than actual length.");
ashleymills 0:3d62a105fd34 260 return 0;
ashleymills 0:3d62a105fd34 261 }
ashleymills 0:3d62a105fd34 262
ashleymills 0:3d62a105fd34 263 // check that code is valid
ashleymills 0:3d62a105fd34 264 CoapPDU::Code code = getCode();
ashleymills 0:3d62a105fd34 265 if(code<COAP_EMPTY ||
ashleymills 0:3d62a105fd34 266 (code>COAP_DELETE&&code<COAP_CREATED) ||
ashleymills 0:3d62a105fd34 267 (code>COAP_CONTENT&&code<COAP_BAD_REQUEST) ||
ashleymills 0:3d62a105fd34 268 (code>COAP_NOT_ACCEPTABLE&&code<COAP_PRECONDITION_FAILED) ||
ashleymills 0:3d62a105fd34 269 (code==0x8E) ||
ashleymills 0:3d62a105fd34 270 (code>COAP_UNSUPPORTED_CONTENT_FORMAT&&code<COAP_INTERNAL_SERVER_ERROR) ||
ashleymills 0:3d62a105fd34 271 (code>COAP_PROXYING_NOT_SUPPORTED) ) {
ashleymills 0:3d62a105fd34 272 DBG("Invalid CoAP code: %d",code);
ashleymills 0:3d62a105fd34 273 return 0;
ashleymills 0:3d62a105fd34 274 }
ashleymills 0:3d62a105fd34 275 DBG("CoAP code: %d",code);
ashleymills 0:3d62a105fd34 276
ashleymills 0:3d62a105fd34 277 // token can be anything so nothing to check
ashleymills 0:3d62a105fd34 278
ashleymills 0:3d62a105fd34 279 // check that options all make sense
ashleymills 0:3d62a105fd34 280 uint16_t optionDelta =0, optionNumber = 0, optionValueLength = 0;
ashleymills 0:3d62a105fd34 281 int totalLength = 0;
ashleymills 0:3d62a105fd34 282
ashleymills 0:3d62a105fd34 283 // first option occurs after token
ashleymills 0:3d62a105fd34 284 int optionPos = COAP_HDR_SIZE + getTokenLength();
ashleymills 0:3d62a105fd34 285
ashleymills 0:3d62a105fd34 286 // may be 0 options
ashleymills 0:3d62a105fd34 287 if(optionPos==_pduLength) {
ashleymills 0:3d62a105fd34 288 DBG("No options. No payload.");
ashleymills 0:3d62a105fd34 289 _numOptions = 0;
ashleymills 0:3d62a105fd34 290 _payloadLength = 0;
ashleymills 0:3d62a105fd34 291 return 1;
ashleymills 0:3d62a105fd34 292 }
ashleymills 0:3d62a105fd34 293
ashleymills 0:3d62a105fd34 294 int bytesRemaining = _pduLength-optionPos;
ashleymills 0:3d62a105fd34 295 int numOptions = 0;
ashleymills 0:3d62a105fd34 296 uint8_t upperNibble = 0x00, lowerNibble = 0x00;
ashleymills 0:3d62a105fd34 297
ashleymills 0:3d62a105fd34 298 // walk over options and record information
ashleymills 0:3d62a105fd34 299 while(1) {
ashleymills 0:3d62a105fd34 300 // check for payload marker
ashleymills 0:3d62a105fd34 301 if(bytesRemaining>0) {
ashleymills 0:3d62a105fd34 302 uint8_t optionHeader = _pdu[optionPos];
ashleymills 0:3d62a105fd34 303 if(optionHeader==0xFF) {
ashleymills 0:3d62a105fd34 304 // payload
ashleymills 0:3d62a105fd34 305 if(bytesRemaining>1) {
ashleymills 0:3d62a105fd34 306 _payloadPointer = &_pdu[optionPos+1];
ashleymills 0:3d62a105fd34 307 _payloadLength = (bytesRemaining-1);
ashleymills 0:3d62a105fd34 308 _numOptions = numOptions;
ashleymills 0:3d62a105fd34 309 DBG("Payload found, length: %d",_payloadLength);
ashleymills 0:3d62a105fd34 310 return 1;
ashleymills 0:3d62a105fd34 311 }
ashleymills 0:3d62a105fd34 312 // payload marker but no payload
ashleymills 0:3d62a105fd34 313 _payloadPointer = NULL;
ashleymills 0:3d62a105fd34 314 _payloadLength = 0;
ashleymills 0:3d62a105fd34 315 DBG("Payload marker but no payload.");
ashleymills 0:3d62a105fd34 316 return 0;
ashleymills 0:3d62a105fd34 317 }
ashleymills 0:3d62a105fd34 318
ashleymills 0:3d62a105fd34 319 // check that option delta and option length are valid values
ashleymills 0:3d62a105fd34 320 upperNibble = (optionHeader & 0xF0) >> 4;
ashleymills 0:3d62a105fd34 321 lowerNibble = (optionHeader & 0x0F);
ashleymills 0:3d62a105fd34 322 if(upperNibble==0x0F||lowerNibble==0x0F) {
ashleymills 0:3d62a105fd34 323 DBG("Expected option header or payload marker, got: 0x%x%x",upperNibble,lowerNibble);
ashleymills 0:3d62a105fd34 324 return 0;
ashleymills 0:3d62a105fd34 325 }
ashleymills 0:3d62a105fd34 326 DBG("Option header byte appears sane: 0x%x%x",upperNibble,lowerNibble);
ashleymills 0:3d62a105fd34 327 } else {
ashleymills 0:3d62a105fd34 328 DBG("No more data. No payload.");
ashleymills 0:3d62a105fd34 329 _payloadPointer = NULL;
ashleymills 0:3d62a105fd34 330 _payloadLength = 0;
ashleymills 0:3d62a105fd34 331 _numOptions = numOptions;
ashleymills 0:3d62a105fd34 332 return 1;
ashleymills 0:3d62a105fd34 333 }
ashleymills 0:3d62a105fd34 334
ashleymills 0:3d62a105fd34 335 // skip over option header byte
ashleymills 0:3d62a105fd34 336 bytesRemaining--;
ashleymills 0:3d62a105fd34 337
ashleymills 0:3d62a105fd34 338 // check that there is enough space for the extended delta and length bytes (if any)
ashleymills 0:3d62a105fd34 339 int headerBytesNeeded = computeExtraBytes(upperNibble);
ashleymills 0:3d62a105fd34 340 DBG("%d extra bytes needed for extended delta",headerBytesNeeded);
ashleymills 0:3d62a105fd34 341 if(headerBytesNeeded>bytesRemaining) {
ashleymills 0:3d62a105fd34 342 DBG("Not enough space for extended option delta, needed %d, have %d.",headerBytesNeeded,bytesRemaining);
ashleymills 0:3d62a105fd34 343 return 0;
ashleymills 0:3d62a105fd34 344 }
ashleymills 0:3d62a105fd34 345 headerBytesNeeded += computeExtraBytes(lowerNibble);
ashleymills 0:3d62a105fd34 346 if(headerBytesNeeded>bytesRemaining) {
ashleymills 0:3d62a105fd34 347 DBG("Not enough space for extended option length, needed %d, have %d.",
ashleymills 0:3d62a105fd34 348 (headerBytesNeeded-computeExtraBytes(upperNibble)),bytesRemaining);
ashleymills 0:3d62a105fd34 349 return 0;
ashleymills 0:3d62a105fd34 350 }
ashleymills 0:3d62a105fd34 351 DBG("Enough space for extended delta and length: %d, continuing.",headerBytesNeeded);
ashleymills 0:3d62a105fd34 352
ashleymills 0:3d62a105fd34 353 // extract option details
ashleymills 0:3d62a105fd34 354 optionDelta = getOptionDelta(&_pdu[optionPos]);
ashleymills 0:3d62a105fd34 355 optionNumber += optionDelta;
ashleymills 0:3d62a105fd34 356 optionValueLength = getOptionValueLength(&_pdu[optionPos]);
ashleymills 0:3d62a105fd34 357 DBG("Got option: %d with length %d",optionNumber,optionValueLength);
ashleymills 0:3d62a105fd34 358 // compute total length
ashleymills 0:3d62a105fd34 359 totalLength = 1; // mandatory header
ashleymills 0:3d62a105fd34 360 totalLength += computeExtraBytes(optionDelta);
ashleymills 0:3d62a105fd34 361 totalLength += computeExtraBytes(optionValueLength);
ashleymills 0:3d62a105fd34 362 totalLength += optionValueLength;
ashleymills 0:3d62a105fd34 363 // check there is enough space
ashleymills 0:3d62a105fd34 364 if(optionPos+totalLength>_pduLength) {
ashleymills 0:3d62a105fd34 365 DBG("Not enough space for option payload, needed %d, have %d.",(totalLength-headerBytesNeeded-1),_pduLength-optionPos);
ashleymills 0:3d62a105fd34 366 return 0;
ashleymills 0:3d62a105fd34 367 }
ashleymills 0:3d62a105fd34 368 DBG("Enough space for option payload: %d %d",optionValueLength,(totalLength-headerBytesNeeded-1));
ashleymills 0:3d62a105fd34 369
ashleymills 0:3d62a105fd34 370 // recompute bytesRemaining
ashleymills 0:3d62a105fd34 371 bytesRemaining -= totalLength;
ashleymills 0:3d62a105fd34 372 bytesRemaining++; // correct for previous --
ashleymills 0:3d62a105fd34 373
ashleymills 0:3d62a105fd34 374 // move to next option
ashleymills 0:3d62a105fd34 375 optionPos += totalLength;
ashleymills 0:3d62a105fd34 376
ashleymills 0:3d62a105fd34 377 // inc number of options XXX
ashleymills 0:3d62a105fd34 378 numOptions++;
ashleymills 0:3d62a105fd34 379 }
ashleymills 0:3d62a105fd34 380
ashleymills 0:3d62a105fd34 381 return 1;
ashleymills 0:3d62a105fd34 382 }
ashleymills 0:3d62a105fd34 383
ashleymills 0:3d62a105fd34 384 /// Destructor. Does not free buffer if constructor passed an external buffer.
ashleymills 0:3d62a105fd34 385 /**
ashleymills 0:3d62a105fd34 386 * The destructor acts differently, depending on how the object was initially constructed (from buffer or not):
ashleymills 0:3d62a105fd34 387 *
ashleymills 0:3d62a105fd34 388 * -# CoapPDU::CoapPDU()
ashleymills 0:3d62a105fd34 389 *
ashleymills 0:3d62a105fd34 390 * Complete object is destroyed.
ashleymills 0:3d62a105fd34 391 *
ashleymills 0:3d62a105fd34 392 * -# CoapPDU::CoapPDU(uint8_t *pdu, int pduLength)
ashleymills 0:3d62a105fd34 393 *
ashleymills 0:3d62a105fd34 394 * Only object container is destroyed. \b pdu is left intact.
ashleymills 0:3d62a105fd34 395 *
ashleymills 0:3d62a105fd34 396 * -# CoapPDU::CoapPDU(uint8_t *buffer, int bufferLength, int pduLength)
ashleymills 0:3d62a105fd34 397 *
ashleymills 0:3d62a105fd34 398 * Only object container is destroyed. \b pdu is left intact.
ashleymills 0:3d62a105fd34 399 *
ashleymills 0:3d62a105fd34 400 */
ashleymills 0:3d62a105fd34 401 CoapPDU::~CoapPDU() {
ashleymills 0:3d62a105fd34 402 if(!_constructedFromBuffer) {
ashleymills 0:3d62a105fd34 403 free(_pdu);
ashleymills 0:3d62a105fd34 404 }
ashleymills 0:3d62a105fd34 405 }
ashleymills 0:3d62a105fd34 406
ashleymills 0:3d62a105fd34 407 /// Returns a pointer to the internal buffer.
ashleymills 0:3d62a105fd34 408 uint8_t* CoapPDU::getPDUPointer() {
ashleymills 0:3d62a105fd34 409 return _pdu;
ashleymills 0:3d62a105fd34 410 }
ashleymills 0:3d62a105fd34 411
ashleymills 0:3d62a105fd34 412 /// Set the PDU length to the length specified.
ashleymills 0:3d62a105fd34 413 /**
ashleymills 0:3d62a105fd34 414 * This is used when re-using a PDU container before calling CoapPDU::validate() as it
ashleymills 0:3d62a105fd34 415 * is not possible to deduce the length of a PDU since the payload has no length marker.
ashleymills 0:3d62a105fd34 416 * \param len The length of the PDU
ashleymills 0:3d62a105fd34 417 */
ashleymills 0:3d62a105fd34 418 void CoapPDU::setPDULength(int len) {
ashleymills 0:3d62a105fd34 419 _pduLength = len;
ashleymills 0:3d62a105fd34 420 }
ashleymills 0:3d62a105fd34 421
ashleymills 0:3d62a105fd34 422
ashleymills 0:3d62a105fd34 423 /// Shorthand function for setting a resource URI.
ashleymills 0:3d62a105fd34 424 /**
ashleymills 0:3d62a105fd34 425 * This will parse the supplied \b uri and construct enough URI_PATH CoAP options to encode it.
ashleymills 0:3d62a105fd34 426 * The options are added to the PDU.
ashleymills 0:3d62a105fd34 427 *
ashleymills 0:3d62a105fd34 428 * At present queries are not handled. TODO Implement queries.
ashleymills 0:3d62a105fd34 429 *
ashleymills 0:3d62a105fd34 430 * \note This uses an internal buffer of 16 bytes to manipulate strings. The internal buffer will be
ashleymills 0:3d62a105fd34 431 * expanded dynamically if necessary (path component longer than 16 bytes). The internal buffer will
ashleymills 0:3d62a105fd34 432 * be freed before the function returns.
ashleymills 0:3d62a105fd34 433 *
ashleymills 0:3d62a105fd34 434 * \param uri The uri to parse.
ashleymills 0:3d62a105fd34 435 * \param urilen The length of the uri to parse.
ashleymills 0:3d62a105fd34 436 *
ashleymills 0:3d62a105fd34 437 * \return 1 on success, 0 on failure.
ashleymills 0:3d62a105fd34 438 */
ashleymills 0:3d62a105fd34 439 int CoapPDU::setURI(char *uri, int urilen) {
ashleymills 0:3d62a105fd34 440 // only '/' and alphabetic chars allowed
ashleymills 0:3d62a105fd34 441 // very simple splitting done
ashleymills 0:3d62a105fd34 442
ashleymills 0:3d62a105fd34 443 // sanitation
ashleymills 0:3d62a105fd34 444 if(urilen<=0||uri==NULL) {
ashleymills 0:3d62a105fd34 445 DBG("Null or zero-length uri passed.");
ashleymills 0:3d62a105fd34 446 return 1;
ashleymills 0:3d62a105fd34 447 }
ashleymills 0:3d62a105fd34 448
ashleymills 0:3d62a105fd34 449 // single character URI path (including '/' case)
ashleymills 0:3d62a105fd34 450 if(urilen==1) {
ashleymills 0:3d62a105fd34 451 addOption(COAP_OPTION_URI_PATH,1,(uint8_t*)uri);
ashleymills 0:3d62a105fd34 452 return 0;
ashleymills 0:3d62a105fd34 453 }
ashleymills 0:3d62a105fd34 454
ashleymills 0:3d62a105fd34 455 // local vars
ashleymills 0:3d62a105fd34 456 char *startP=uri,*endP=NULL;
ashleymills 0:3d62a105fd34 457 int oLen = 0;
ashleymills 0:3d62a105fd34 458 int bufSpace = 16;
ashleymills 0:3d62a105fd34 459 char *uriBuf = (char*)malloc(bufSpace*sizeof(char));
ashleymills 0:3d62a105fd34 460 if(uriBuf==NULL) {
ashleymills 0:3d62a105fd34 461 DBG("Error allocating temporary memory.");
ashleymills 0:3d62a105fd34 462 return 1;
ashleymills 0:3d62a105fd34 463 }
ashleymills 0:3d62a105fd34 464
ashleymills 0:3d62a105fd34 465 while(1) {
ashleymills 0:3d62a105fd34 466 // stop at end of string
ashleymills 0:3d62a105fd34 467 if(*startP==0x00||*(startP+1)==0x00) {
ashleymills 0:3d62a105fd34 468 break;
ashleymills 0:3d62a105fd34 469 }
ashleymills 0:3d62a105fd34 470
ashleymills 0:3d62a105fd34 471 // ignore leading slash
ashleymills 0:3d62a105fd34 472 if(*startP=='/') {
ashleymills 0:3d62a105fd34 473 DBG("Skipping leading slash");
ashleymills 0:3d62a105fd34 474 startP++;
ashleymills 0:3d62a105fd34 475 }
ashleymills 0:3d62a105fd34 476
ashleymills 0:3d62a105fd34 477 // find next split point
ashleymills 0:3d62a105fd34 478 endP = strchr(startP,'/');
ashleymills 0:3d62a105fd34 479
ashleymills 0:3d62a105fd34 480 // might not be another slash
ashleymills 0:3d62a105fd34 481 if(endP==NULL) {
ashleymills 0:3d62a105fd34 482 DBG("Ending out of slash");
ashleymills 0:3d62a105fd34 483 endP = uri+urilen;
ashleymills 0:3d62a105fd34 484 }
ashleymills 0:3d62a105fd34 485
ashleymills 0:3d62a105fd34 486 // get length of segment
ashleymills 0:3d62a105fd34 487 oLen = endP-startP;
ashleymills 0:3d62a105fd34 488
ashleymills 0:3d62a105fd34 489 // copy sequence, make space if necessary
ashleymills 0:3d62a105fd34 490 if((oLen+1)>bufSpace) {
ashleymills 0:3d62a105fd34 491 char *newBuf = (char*)realloc(uriBuf,oLen+1);
ashleymills 0:3d62a105fd34 492 if(newBuf==NULL) {
ashleymills 0:3d62a105fd34 493 DBG("Error making space for temporary buffer");
ashleymills 0:3d62a105fd34 494 free(uriBuf);
ashleymills 0:3d62a105fd34 495 return 1;
ashleymills 0:3d62a105fd34 496 }
ashleymills 0:3d62a105fd34 497 uriBuf = newBuf;
ashleymills 0:3d62a105fd34 498 }
ashleymills 0:3d62a105fd34 499
ashleymills 0:3d62a105fd34 500 // copy into temporary buffer
ashleymills 0:3d62a105fd34 501 memcpy(uriBuf,startP,oLen);
ashleymills 0:3d62a105fd34 502 uriBuf[oLen] = 0x00;
ashleymills 0:3d62a105fd34 503 DBG("Adding URI_PATH %s",uriBuf);
ashleymills 0:3d62a105fd34 504 // add option
ashleymills 0:3d62a105fd34 505 if(addOption(COAP_OPTION_URI_PATH,oLen,(uint8_t*)uriBuf)!=0) {
ashleymills 0:3d62a105fd34 506 DBG("Error adding option");
ashleymills 0:3d62a105fd34 507 return 1;
ashleymills 0:3d62a105fd34 508 }
ashleymills 0:3d62a105fd34 509 startP = endP;
ashleymills 0:3d62a105fd34 510 }
ashleymills 0:3d62a105fd34 511
ashleymills 0:3d62a105fd34 512 // clean up
ashleymills 0:3d62a105fd34 513 free(uriBuf);
ashleymills 0:3d62a105fd34 514 return 0;
ashleymills 0:3d62a105fd34 515 }
ashleymills 0:3d62a105fd34 516
ashleymills 0:3d62a105fd34 517 /// Concatenates any URI_PATH elements into a single string.
ashleymills 0:3d62a105fd34 518 /**
ashleymills 0:3d62a105fd34 519 * Parses the PDU options and extracts all URI_PATH elements, concatenating them into a single string with slash separators.
ashleymills 0:3d62a105fd34 520 *
ashleymills 0:3d62a105fd34 521 * \param dst Buffer into which to copy the concatenated path elements.
ashleymills 0:3d62a105fd34 522 * \param dstlen Length of buffer.
ashleymills 0:3d62a105fd34 523 * \param outLen Pointer to integer, into which URI length will be placed.
ashleymills 0:3d62a105fd34 524 *
ashleymills 0:3d62a105fd34 525 * \return 0 on success, 1 on failure. \b outLen will contain the length of the concatenated elements.
ashleymills 0:3d62a105fd34 526 */
ashleymills 0:3d62a105fd34 527 int CoapPDU::getURI(char *dst, int dstlen, int *outLen) {
ashleymills 0:3d62a105fd34 528 if(outLen==NULL) {
ashleymills 0:3d62a105fd34 529 DBG("Output length pointer is NULL");
ashleymills 0:3d62a105fd34 530 return 1;
ashleymills 0:3d62a105fd34 531 }
ashleymills 0:3d62a105fd34 532
ashleymills 0:3d62a105fd34 533 if(dst==NULL) {
ashleymills 0:3d62a105fd34 534 DBG("NULL destination buffer");
ashleymills 0:3d62a105fd34 535 *outLen = 0;
ashleymills 0:3d62a105fd34 536 return 1;
ashleymills 0:3d62a105fd34 537 }
ashleymills 0:3d62a105fd34 538
ashleymills 0:3d62a105fd34 539 // check destination space
ashleymills 0:3d62a105fd34 540 if(dstlen<=0) {
ashleymills 0:3d62a105fd34 541 *dst = 0x00;
ashleymills 0:3d62a105fd34 542 *outLen = 0;
ashleymills 0:3d62a105fd34 543 DBG("Destination buffer too small (0)!");
ashleymills 0:3d62a105fd34 544 return 1;
ashleymills 0:3d62a105fd34 545 }
ashleymills 0:3d62a105fd34 546 // check option count
ashleymills 0:3d62a105fd34 547 if(_numOptions==0) {
ashleymills 0:3d62a105fd34 548 *dst = 0x00;
ashleymills 0:3d62a105fd34 549 *outLen = 0;
ashleymills 0:3d62a105fd34 550 return 0;
ashleymills 0:3d62a105fd34 551 }
ashleymills 0:3d62a105fd34 552 // get options
ashleymills 0:3d62a105fd34 553 CoapPDU::CoapOption *options = getOptions();
ashleymills 0:3d62a105fd34 554 if(options==NULL) {
ashleymills 0:3d62a105fd34 555 *dst = 0x00;
ashleymills 0:3d62a105fd34 556 *outLen = 0;
ashleymills 0:3d62a105fd34 557 return 0;
ashleymills 0:3d62a105fd34 558 }
ashleymills 0:3d62a105fd34 559 // iterate over options to construct URI
ashleymills 0:3d62a105fd34 560 CoapOption *o = NULL;
ashleymills 0:3d62a105fd34 561 int bytesLeft = dstlen-1; // space for 0x00
ashleymills 0:3d62a105fd34 562 int oLen = 0;
ashleymills 0:3d62a105fd34 563 // add slash at beggining
ashleymills 0:3d62a105fd34 564 if(bytesLeft>=1) {
ashleymills 0:3d62a105fd34 565 *dst = '/';
ashleymills 0:3d62a105fd34 566 dst++;
ashleymills 0:3d62a105fd34 567 bytesLeft--;
ashleymills 0:3d62a105fd34 568 } else {
ashleymills 0:3d62a105fd34 569 DBG("No space for initial slash needed 1, got %d",bytesLeft);
ashleymills 0:3d62a105fd34 570 return 1;
ashleymills 0:3d62a105fd34 571 }
ashleymills 0:3d62a105fd34 572 for(int i=0; i<_numOptions; i++) {
ashleymills 0:3d62a105fd34 573 o = &options[i];
ashleymills 0:3d62a105fd34 574 oLen = o->optionValueLength;
ashleymills 0:3d62a105fd34 575 if(o->optionNumber==COAP_OPTION_URI_PATH) {
ashleymills 0:3d62a105fd34 576 // check space
ashleymills 0:3d62a105fd34 577 if(oLen>bytesLeft) {
ashleymills 0:3d62a105fd34 578 DBG("Destination buffer too small, needed %d, got %d",oLen,bytesLeft);
ashleymills 0:3d62a105fd34 579 return 1;
ashleymills 0:3d62a105fd34 580 }
ashleymills 0:3d62a105fd34 581
ashleymills 0:3d62a105fd34 582 // case where single '/' exists
ashleymills 0:3d62a105fd34 583 if(oLen==1&&o->optionValuePointer[0]=='/') {
ashleymills 0:3d62a105fd34 584 *dst = 0x00;
ashleymills 0:3d62a105fd34 585 *outLen = 1;
ashleymills 0:3d62a105fd34 586 return 0;
ashleymills 0:3d62a105fd34 587 }
ashleymills 0:3d62a105fd34 588
ashleymills 0:3d62a105fd34 589 // copy URI path component
ashleymills 0:3d62a105fd34 590 memcpy(dst,o->optionValuePointer,oLen);
ashleymills 0:3d62a105fd34 591
ashleymills 0:3d62a105fd34 592 // adjust counters
ashleymills 0:3d62a105fd34 593 dst += oLen;
ashleymills 0:3d62a105fd34 594 bytesLeft -= oLen;
ashleymills 0:3d62a105fd34 595
ashleymills 0:3d62a105fd34 596 // add slash following (don't know at this point if another option is coming)
ashleymills 0:3d62a105fd34 597 if(bytesLeft>=1) {
ashleymills 0:3d62a105fd34 598 *dst = '/';
ashleymills 0:3d62a105fd34 599 dst++;
ashleymills 0:3d62a105fd34 600 bytesLeft--;
ashleymills 0:3d62a105fd34 601 } else {
ashleymills 0:3d62a105fd34 602 DBG("Ran out of space after processing option");
ashleymills 0:3d62a105fd34 603 return 1;
ashleymills 0:3d62a105fd34 604 }
ashleymills 0:3d62a105fd34 605 }
ashleymills 0:3d62a105fd34 606 }
ashleymills 0:3d62a105fd34 607
ashleymills 0:3d62a105fd34 608 // remove terminating slash
ashleymills 0:3d62a105fd34 609 dst--;
ashleymills 0:3d62a105fd34 610 bytesLeft++;
ashleymills 0:3d62a105fd34 611 // add null terminating byte (always space since reserved)
ashleymills 0:3d62a105fd34 612 *dst = 0x00;
ashleymills 0:3d62a105fd34 613 *outLen = (dstlen-1)-bytesLeft;
ashleymills 0:3d62a105fd34 614 return 0;
ashleymills 0:3d62a105fd34 615 }
ashleymills 0:3d62a105fd34 616
ashleymills 0:3d62a105fd34 617 /// Sets the CoAP version.
ashleymills 0:3d62a105fd34 618 /**
ashleymills 0:3d62a105fd34 619 * \param version CoAP version between 0 and 3.
ashleymills 0:3d62a105fd34 620 * \return 0 on success, 1 on failure.
ashleymills 0:3d62a105fd34 621 */
ashleymills 0:3d62a105fd34 622 int CoapPDU::setVersion(uint8_t version) {
ashleymills 0:3d62a105fd34 623 if(version>3) {
ashleymills 0:3d62a105fd34 624 return 0;
ashleymills 0:3d62a105fd34 625 }
ashleymills 0:3d62a105fd34 626
ashleymills 0:3d62a105fd34 627 _pdu[0] &= 0x3F;
ashleymills 0:3d62a105fd34 628 _pdu[0] |= (version << 6);
ashleymills 0:3d62a105fd34 629 return 1;
ashleymills 0:3d62a105fd34 630 }
ashleymills 0:3d62a105fd34 631
ashleymills 0:3d62a105fd34 632 /**
ashleymills 0:3d62a105fd34 633 * Gets the CoAP Version.
ashleymills 0:3d62a105fd34 634 * @return The CoAP version between 0 and 3.
ashleymills 0:3d62a105fd34 635 */
ashleymills 0:3d62a105fd34 636 uint8_t CoapPDU::getVersion() {
ashleymills 0:3d62a105fd34 637 return (_pdu[0]&0xC0)>>6;
ashleymills 0:3d62a105fd34 638 }
ashleymills 0:3d62a105fd34 639
ashleymills 0:3d62a105fd34 640 /**
ashleymills 0:3d62a105fd34 641 * Sets the type of this CoAP PDU.
ashleymills 0:3d62a105fd34 642 * \param mt The type, one of:
ashleymills 0:3d62a105fd34 643 * - COAP_CONFIRMABLE
ashleymills 0:3d62a105fd34 644 * - COAP_NON_CONFIRMABLE
ashleymills 0:3d62a105fd34 645 * - COAP_ACKNOWLEDGEMENT
ashleymills 0:3d62a105fd34 646 * - COAP_RESET.
ashleymills 0:3d62a105fd34 647 */
ashleymills 0:3d62a105fd34 648 void CoapPDU::setType(CoapPDU::Type mt) {
ashleymills 0:3d62a105fd34 649 _pdu[0] &= 0xCF;
ashleymills 0:3d62a105fd34 650 _pdu[0] |= mt;
ashleymills 0:3d62a105fd34 651 }
ashleymills 0:3d62a105fd34 652
ashleymills 0:3d62a105fd34 653 /// Returns the type of the PDU.
ashleymills 0:3d62a105fd34 654 CoapPDU::Type CoapPDU::getType() {
ashleymills 0:3d62a105fd34 655 return (CoapPDU::Type)(_pdu[0]&0x30);
ashleymills 0:3d62a105fd34 656 }
ashleymills 0:3d62a105fd34 657
ashleymills 0:3d62a105fd34 658
ashleymills 0:3d62a105fd34 659 /// Set the token length.
ashleymills 0:3d62a105fd34 660 /**
ashleymills 0:3d62a105fd34 661 * \param tokenLength The length of the token in bytes, between 0 and 8.
ashleymills 0:3d62a105fd34 662 * \return 0 on success, 1 on failure.
ashleymills 0:3d62a105fd34 663 */
ashleymills 0:3d62a105fd34 664 int CoapPDU::setTokenLength(uint8_t tokenLength) {
ashleymills 0:3d62a105fd34 665 if(tokenLength>8)
ashleymills 0:3d62a105fd34 666 return 1;
ashleymills 0:3d62a105fd34 667
ashleymills 0:3d62a105fd34 668 _pdu[0] &= 0xF0;
ashleymills 0:3d62a105fd34 669 _pdu[0] |= tokenLength;
ashleymills 0:3d62a105fd34 670 return 0;
ashleymills 0:3d62a105fd34 671 }
ashleymills 0:3d62a105fd34 672
ashleymills 0:3d62a105fd34 673 /// Returns the token length.
ashleymills 0:3d62a105fd34 674 int CoapPDU::getTokenLength() {
ashleymills 0:3d62a105fd34 675 return _pdu[0] & 0x0F;
ashleymills 0:3d62a105fd34 676 }
ashleymills 0:3d62a105fd34 677
ashleymills 0:3d62a105fd34 678 /// Returns a pointer to the PDU token.
ashleymills 0:3d62a105fd34 679 uint8_t* CoapPDU::getTokenPointer() {
ashleymills 0:3d62a105fd34 680 if(getTokenLength()==0) {
ashleymills 0:3d62a105fd34 681 return NULL;
ashleymills 0:3d62a105fd34 682 }
ashleymills 0:3d62a105fd34 683 return &_pdu[4];
ashleymills 0:3d62a105fd34 684 }
ashleymills 0:3d62a105fd34 685
ashleymills 0:3d62a105fd34 686 /// Set the PDU token to the supplied byte sequence.
ashleymills 0:3d62a105fd34 687 /**
ashleymills 0:3d62a105fd34 688 * This sets the PDU token to \b token and sets the token length to \b tokenLength.
ashleymills 0:3d62a105fd34 689 * \param token A sequence of bytes representing the token.
ashleymills 0:3d62a105fd34 690 * \param tokenLength The length of the byte sequence.
ashleymills 0:3d62a105fd34 691 * \return 0 on success, 1 on failure.
ashleymills 0:3d62a105fd34 692 */
ashleymills 0:3d62a105fd34 693 int CoapPDU::setToken(uint8_t *token, uint8_t tokenLength) {
ashleymills 0:3d62a105fd34 694 DBG("Setting token");
ashleymills 0:3d62a105fd34 695 if(token==NULL) {
ashleymills 0:3d62a105fd34 696 DBG("NULL pointer passed as token reference");
ashleymills 0:3d62a105fd34 697 return 1;
ashleymills 0:3d62a105fd34 698 }
ashleymills 0:3d62a105fd34 699
ashleymills 0:3d62a105fd34 700 if(tokenLength==0) {
ashleymills 0:3d62a105fd34 701 DBG("Token has zero length");
ashleymills 0:3d62a105fd34 702 return 1;
ashleymills 0:3d62a105fd34 703 }
ashleymills 0:3d62a105fd34 704
ashleymills 0:3d62a105fd34 705 // if tokenLength has not changed, just copy the new value
ashleymills 0:3d62a105fd34 706 uint8_t oldTokenLength = getTokenLength();
ashleymills 0:3d62a105fd34 707 if(tokenLength==oldTokenLength) {
ashleymills 0:3d62a105fd34 708 memcpy((void*)&_pdu[4],token,tokenLength);
ashleymills 0:3d62a105fd34 709 return 0;
ashleymills 0:3d62a105fd34 710 }
ashleymills 0:3d62a105fd34 711
ashleymills 0:3d62a105fd34 712 // otherwise compute new length of PDU
ashleymills 0:3d62a105fd34 713 uint8_t oldPDULength = _pduLength;
ashleymills 0:3d62a105fd34 714 _pduLength -= oldTokenLength;
ashleymills 0:3d62a105fd34 715 _pduLength += tokenLength;
ashleymills 0:3d62a105fd34 716
ashleymills 0:3d62a105fd34 717 // now, have to shift old memory around, but shift direction depends
ashleymills 0:3d62a105fd34 718 // whether pdu is now bigger or smaller
ashleymills 0:3d62a105fd34 719 if(_pduLength>oldPDULength) {
ashleymills 0:3d62a105fd34 720 // new PDU is bigger, need to allocate space for new PDU
ashleymills 0:3d62a105fd34 721 if(!_constructedFromBuffer) {
ashleymills 0:3d62a105fd34 722 uint8_t *newMemory = (uint8_t*)realloc(_pdu,_pduLength);
ashleymills 0:3d62a105fd34 723 if(newMemory==NULL) {
ashleymills 0:3d62a105fd34 724 // malloc failed
ashleymills 0:3d62a105fd34 725 DBG("Failed to allocate memory for token");
ashleymills 0:3d62a105fd34 726 _pduLength = oldPDULength;
ashleymills 0:3d62a105fd34 727 return 1;
ashleymills 0:3d62a105fd34 728 }
ashleymills 0:3d62a105fd34 729 _pdu = newMemory;
ashleymills 0:3d62a105fd34 730 _bufferLength = _pduLength;
ashleymills 0:3d62a105fd34 731 } else {
ashleymills 0:3d62a105fd34 732 // constructed from buffer, check space
ashleymills 0:3d62a105fd34 733 if(_pduLength>_bufferLength) {
ashleymills 0:3d62a105fd34 734 DBG("Buffer too small to contain token, needed %d, got %d.",_pduLength-oldPDULength,_bufferLength-oldPDULength);
ashleymills 0:3d62a105fd34 735 _pduLength = oldPDULength;
ashleymills 0:3d62a105fd34 736 return 1;
ashleymills 0:3d62a105fd34 737 }
ashleymills 0:3d62a105fd34 738 }
ashleymills 0:3d62a105fd34 739
ashleymills 0:3d62a105fd34 740 // and then shift everything after token up to end of new PDU
ashleymills 0:3d62a105fd34 741 // memory overlaps so do this manually so to avoid additional mallocs
ashleymills 0:3d62a105fd34 742 int shiftOffset = _pduLength-oldPDULength;
ashleymills 0:3d62a105fd34 743 int shiftAmount = _pduLength-tokenLength-COAP_HDR_SIZE; // everything after token
ashleymills 0:3d62a105fd34 744 shiftPDUUp(shiftOffset,shiftAmount);
ashleymills 0:3d62a105fd34 745
ashleymills 0:3d62a105fd34 746 // now copy the token into the new space and set official token length
ashleymills 0:3d62a105fd34 747 memcpy((void*)&_pdu[4],token,tokenLength);
ashleymills 0:3d62a105fd34 748 setTokenLength(tokenLength);
ashleymills 0:3d62a105fd34 749
ashleymills 0:3d62a105fd34 750 // and return success
ashleymills 0:3d62a105fd34 751 return 0;
ashleymills 0:3d62a105fd34 752 }
ashleymills 0:3d62a105fd34 753
ashleymills 0:3d62a105fd34 754 // new PDU is smaller, copy the new token value over the old one
ashleymills 0:3d62a105fd34 755 memcpy((void*)&_pdu[4],token,tokenLength);
ashleymills 0:3d62a105fd34 756 // and shift everything after the new token down
ashleymills 0:3d62a105fd34 757 int startLocation = COAP_HDR_SIZE+tokenLength;
ashleymills 0:3d62a105fd34 758 int shiftOffset = oldPDULength-_pduLength;
ashleymills 0:3d62a105fd34 759 int shiftAmount = oldPDULength-oldTokenLength-COAP_HDR_SIZE;
ashleymills 0:3d62a105fd34 760 shiftPDUDown(startLocation,shiftOffset,shiftAmount);
ashleymills 0:3d62a105fd34 761 // then reduce size of buffer
ashleymills 0:3d62a105fd34 762 if(!_constructedFromBuffer) {
ashleymills 0:3d62a105fd34 763 uint8_t *newMemory = (uint8_t*)realloc(_pdu,_pduLength);
ashleymills 0:3d62a105fd34 764 if(newMemory==NULL) {
ashleymills 0:3d62a105fd34 765 // malloc failed, PDU in inconsistent state
ashleymills 0:3d62a105fd34 766 DBG("Failed to shrink PDU for new token. PDU probably broken");
ashleymills 0:3d62a105fd34 767 return 1;
ashleymills 0:3d62a105fd34 768 }
ashleymills 0:3d62a105fd34 769 _pdu = newMemory;
ashleymills 0:3d62a105fd34 770 _bufferLength = _pduLength;
ashleymills 0:3d62a105fd34 771 }
ashleymills 0:3d62a105fd34 772
ashleymills 0:3d62a105fd34 773 // and officially set the new tokenLength
ashleymills 0:3d62a105fd34 774 setTokenLength(tokenLength);
ashleymills 0:3d62a105fd34 775 return 0;
ashleymills 0:3d62a105fd34 776 }
ashleymills 0:3d62a105fd34 777
ashleymills 0:3d62a105fd34 778 /// Sets the CoAP response code
ashleymills 0:3d62a105fd34 779 void CoapPDU::setCode(CoapPDU::Code code) {
ashleymills 0:3d62a105fd34 780 _pdu[1] = code;
ashleymills 0:3d62a105fd34 781 // there is a limited set of response codes
ashleymills 0:3d62a105fd34 782 }
ashleymills 0:3d62a105fd34 783
ashleymills 0:3d62a105fd34 784 /// Gets the CoAP response code
ashleymills 0:3d62a105fd34 785 CoapPDU::Code CoapPDU::getCode() {
ashleymills 0:3d62a105fd34 786 return (CoapPDU::Code)_pdu[1];
ashleymills 0:3d62a105fd34 787 }
ashleymills 0:3d62a105fd34 788 /// Set messageID to the supplied value.
ashleymills 0:3d62a105fd34 789 /**
ashleymills 0:3d62a105fd34 790 * \param messageID A 16bit message id.
ashleymills 0:3d62a105fd34 791 * \return 0 on success, 1 on failure.
ashleymills 0:3d62a105fd34 792 */
ashleymills 0:3d62a105fd34 793 int CoapPDU::setMessageID(uint16_t messageID) {
ashleymills 0:3d62a105fd34 794 // message ID is stored in network byte order
ashleymills 0:3d62a105fd34 795 uint16_t networkOrder = htons(messageID);
ashleymills 0:3d62a105fd34 796 // bytes 2 and 3 hold the ID
ashleymills 0:3d62a105fd34 797 _pdu[2] &= 0x00;
ashleymills 0:3d62a105fd34 798 _pdu[2] |= (networkOrder >> 8); // MSB
ashleymills 0:3d62a105fd34 799 _pdu[3] &= 0x00;
ashleymills 0:3d62a105fd34 800 _pdu[3] |= (networkOrder & 0x00FF); // LSB
ashleymills 0:3d62a105fd34 801 return 0;
ashleymills 0:3d62a105fd34 802 }
ashleymills 0:3d62a105fd34 803
ashleymills 0:3d62a105fd34 804 /// Returns the 16 bit message ID of the PDU.
ashleymills 0:3d62a105fd34 805 uint16_t CoapPDU::getMessageID() {
ashleymills 0:3d62a105fd34 806 // mesasge ID is stored in network byteorder
ashleymills 0:3d62a105fd34 807 uint16_t networkOrder = 0x0000;
ashleymills 0:3d62a105fd34 808 networkOrder |= _pdu[2];
ashleymills 0:3d62a105fd34 809 networkOrder <<= 8;
ashleymills 0:3d62a105fd34 810 networkOrder |= _pdu[3];
ashleymills 0:3d62a105fd34 811 return ntohs(networkOrder);
ashleymills 0:3d62a105fd34 812 }
ashleymills 0:3d62a105fd34 813
ashleymills 0:3d62a105fd34 814 /// Returns the length of the PDU.
ashleymills 0:3d62a105fd34 815 int CoapPDU::getPDULength() {
ashleymills 0:3d62a105fd34 816 return _pduLength;
ashleymills 0:3d62a105fd34 817 }
ashleymills 0:3d62a105fd34 818
ashleymills 0:3d62a105fd34 819 /// Return the number of options that the PDU has.
ashleymills 0:3d62a105fd34 820 int CoapPDU::getNumOptions() {
ashleymills 0:3d62a105fd34 821 return _numOptions;
ashleymills 0:3d62a105fd34 822 }
ashleymills 0:3d62a105fd34 823
ashleymills 0:3d62a105fd34 824
ashleymills 0:3d62a105fd34 825 /**
ashleymills 0:3d62a105fd34 826 * This returns the options as a sequence of structs.
ashleymills 0:3d62a105fd34 827 */
ashleymills 0:3d62a105fd34 828 CoapPDU::CoapOption* CoapPDU::getOptions() {
ashleymills 0:3d62a105fd34 829 DBG("getOptions() called, %d options.",_numOptions);
ashleymills 0:3d62a105fd34 830
ashleymills 0:3d62a105fd34 831 uint16_t optionDelta =0, optionNumber = 0, optionValueLength = 0;
ashleymills 0:3d62a105fd34 832 int totalLength = 0;
ashleymills 0:3d62a105fd34 833
ashleymills 0:3d62a105fd34 834 if(_numOptions==0) {
ashleymills 0:3d62a105fd34 835 return NULL;
ashleymills 0:3d62a105fd34 836 }
ashleymills 0:3d62a105fd34 837
ashleymills 0:3d62a105fd34 838 // malloc space for options
ashleymills 0:3d62a105fd34 839 CoapOption *options = (CoapOption*)malloc(_numOptions*sizeof(CoapOption));
ashleymills 0:3d62a105fd34 840
ashleymills 0:3d62a105fd34 841 // first option occurs after token
ashleymills 0:3d62a105fd34 842 int optionPos = COAP_HDR_SIZE + getTokenLength();
ashleymills 0:3d62a105fd34 843
ashleymills 0:3d62a105fd34 844 // walk over options and record information
ashleymills 0:3d62a105fd34 845 for(int i=0; i<_numOptions; i++) {
ashleymills 0:3d62a105fd34 846 // extract option details
ashleymills 0:3d62a105fd34 847 optionDelta = getOptionDelta(&_pdu[optionPos]);
ashleymills 0:3d62a105fd34 848 optionNumber += optionDelta;
ashleymills 0:3d62a105fd34 849 optionValueLength = getOptionValueLength(&_pdu[optionPos]);
ashleymills 0:3d62a105fd34 850 // compute total length
ashleymills 0:3d62a105fd34 851 totalLength = 1; // mandatory header
ashleymills 0:3d62a105fd34 852 totalLength += computeExtraBytes(optionDelta);
ashleymills 0:3d62a105fd34 853 totalLength += computeExtraBytes(optionValueLength);
ashleymills 0:3d62a105fd34 854 totalLength += optionValueLength;
ashleymills 0:3d62a105fd34 855 // record option details
ashleymills 0:3d62a105fd34 856 options[i].optionNumber = optionNumber;
ashleymills 0:3d62a105fd34 857 options[i].optionDelta = optionDelta;
ashleymills 0:3d62a105fd34 858 options[i].optionValueLength = optionValueLength;
ashleymills 0:3d62a105fd34 859 options[i].totalLength = totalLength;
ashleymills 0:3d62a105fd34 860 options[i].optionPointer = &_pdu[optionPos];
ashleymills 0:3d62a105fd34 861 options[i].optionValuePointer = &_pdu[optionPos+totalLength-optionValueLength];
ashleymills 0:3d62a105fd34 862 // move to next option
ashleymills 0:3d62a105fd34 863 optionPos += totalLength;
ashleymills 0:3d62a105fd34 864 }
ashleymills 0:3d62a105fd34 865
ashleymills 0:3d62a105fd34 866 return options;
ashleymills 0:3d62a105fd34 867 }
ashleymills 0:3d62a105fd34 868
ashleymills 0:3d62a105fd34 869 /// Add an option to the PDU.
ashleymills 0:3d62a105fd34 870 /**
ashleymills 0:3d62a105fd34 871 * Unlike other implementations, options can be added in any order, and in-memory manipulation will be
ashleymills 0:3d62a105fd34 872 * performed to ensure the correct ordering of options (they use a delta encoding of option numbers).
ashleymills 0:3d62a105fd34 873 * Re-ordering memory like this incurs a small performance cost, so if you care about this, then you
ashleymills 0:3d62a105fd34 874 * might want to add options in ascending order of option number.
ashleymills 0:3d62a105fd34 875 * \param optionNumber The number of the option, see the enum CoapPDU::Option for shorthand notations.
ashleymills 0:3d62a105fd34 876 * \param optionLength The length of the option payload in bytes.
ashleymills 0:3d62a105fd34 877 * \param optionValue A pointer to the byte sequence that is the option payload (bytes will be copied).
ashleymills 0:3d62a105fd34 878 * \return 0 on success, 1 on failure.
ashleymills 0:3d62a105fd34 879 */
ashleymills 0:3d62a105fd34 880 int CoapPDU::addOption(uint16_t insertedOptionNumber, uint16_t optionValueLength, uint8_t *optionValue) {
ashleymills 0:3d62a105fd34 881 // this inserts the option in memory, and re-computes the deltas accordingly
ashleymills 0:3d62a105fd34 882 // prevOption <-- insertionPosition
ashleymills 0:3d62a105fd34 883 // nextOption
ashleymills 0:3d62a105fd34 884
ashleymills 0:3d62a105fd34 885 // find insertion location and previous option number
ashleymills 0:3d62a105fd34 886 uint16_t prevOptionNumber = 0; // option number of option before insertion point
ashleymills 0:3d62a105fd34 887 int insertionPosition = findInsertionPosition(insertedOptionNumber,&prevOptionNumber);
ashleymills 0:3d62a105fd34 888 DBG("inserting option at position %d, after option with number: %hu",insertionPosition,prevOptionNumber);
ashleymills 0:3d62a105fd34 889
ashleymills 0:3d62a105fd34 890 // compute option delta length
ashleymills 0:3d62a105fd34 891 uint16_t optionDelta = insertedOptionNumber-prevOptionNumber;
ashleymills 0:3d62a105fd34 892 uint8_t extraDeltaBytes = computeExtraBytes(optionDelta);
ashleymills 0:3d62a105fd34 893
ashleymills 0:3d62a105fd34 894 // compute option length length
ashleymills 0:3d62a105fd34 895 uint16_t extraLengthBytes = computeExtraBytes(optionValueLength);
ashleymills 0:3d62a105fd34 896
ashleymills 0:3d62a105fd34 897 // compute total length of option
ashleymills 0:3d62a105fd34 898 uint16_t optionLength = COAP_OPTION_HDR_BYTE + extraDeltaBytes + extraLengthBytes + optionValueLength;
ashleymills 0:3d62a105fd34 899
ashleymills 0:3d62a105fd34 900 // if this is at the end of the PDU, job is done, just malloc and insert
ashleymills 0:3d62a105fd34 901 if(insertionPosition==_pduLength) {
ashleymills 0:3d62a105fd34 902 DBG("Inserting at end of PDU");
ashleymills 0:3d62a105fd34 903 // optionNumber must be biggest added
ashleymills 0:3d62a105fd34 904 _maxAddedOptionNumber = insertedOptionNumber;
ashleymills 0:3d62a105fd34 905
ashleymills 0:3d62a105fd34 906 // set new PDU length and allocate space for extra option
ashleymills 0:3d62a105fd34 907 int oldPDULength = _pduLength;
ashleymills 0:3d62a105fd34 908 _pduLength += optionLength;
ashleymills 0:3d62a105fd34 909 if(!_constructedFromBuffer) {
ashleymills 0:3d62a105fd34 910 uint8_t *newMemory = (uint8_t*)realloc(_pdu,_pduLength);
ashleymills 0:3d62a105fd34 911 if(newMemory==NULL) {
ashleymills 0:3d62a105fd34 912 DBG("Failed to allocate memory for option.");
ashleymills 0:3d62a105fd34 913 _pduLength = oldPDULength;
ashleymills 0:3d62a105fd34 914 // malloc failed
ashleymills 0:3d62a105fd34 915 return 1;
ashleymills 0:3d62a105fd34 916 }
ashleymills 0:3d62a105fd34 917 _pdu = newMemory;
ashleymills 0:3d62a105fd34 918 _bufferLength = _pduLength;
ashleymills 0:3d62a105fd34 919 } else {
ashleymills 0:3d62a105fd34 920 // constructed from buffer, check space
ashleymills 0:3d62a105fd34 921 if(_pduLength>_bufferLength) {
ashleymills 0:3d62a105fd34 922 DBG("Buffer too small for new option: needed %d, got %d.",_pduLength-oldPDULength,_bufferLength-oldPDULength);
ashleymills 0:3d62a105fd34 923 _pduLength = oldPDULength;
ashleymills 0:3d62a105fd34 924 return 1;
ashleymills 0:3d62a105fd34 925 }
ashleymills 0:3d62a105fd34 926 }
ashleymills 0:3d62a105fd34 927
ashleymills 0:3d62a105fd34 928 // insert option at position
ashleymills 0:3d62a105fd34 929 insertOption(insertionPosition,optionDelta,optionValueLength,optionValue);
ashleymills 0:3d62a105fd34 930 _numOptions++;
ashleymills 0:3d62a105fd34 931 return 0;
ashleymills 0:3d62a105fd34 932 }
ashleymills 0:3d62a105fd34 933 // XXX could do 0xFF pdu payload case for changing of dynamically allocated application space SDUs < yeah, if you're insane
ashleymills 0:3d62a105fd34 934
ashleymills 0:3d62a105fd34 935 // the next option might (probably) needs it's delta changing
ashleymills 0:3d62a105fd34 936 // I want to take this into account when allocating space for the new
ashleymills 0:3d62a105fd34 937 // option, to avoid having to do two mallocs, first get info about this option
ashleymills 0:3d62a105fd34 938 int nextOptionDelta = getOptionDelta(&_pdu[insertionPosition]);
ashleymills 0:3d62a105fd34 939 int nextOptionNumber = prevOptionNumber + nextOptionDelta;
ashleymills 0:3d62a105fd34 940 int nextOptionDeltaBytes = computeExtraBytes(nextOptionDelta);
ashleymills 0:3d62a105fd34 941 // recompute option delta, relative to inserted option
ashleymills 0:3d62a105fd34 942 int newNextOptionDelta = nextOptionNumber-insertedOptionNumber;
ashleymills 0:3d62a105fd34 943 int newNextOptionDeltaBytes = computeExtraBytes(newNextOptionDelta);
ashleymills 0:3d62a105fd34 944 // determine adjustment
ashleymills 0:3d62a105fd34 945 int optionDeltaAdjustment = newNextOptionDeltaBytes-nextOptionDeltaBytes;
ashleymills 0:3d62a105fd34 946
ashleymills 0:3d62a105fd34 947 // create space for new option, including adjustment space for option delta
ashleymills 0:3d62a105fd34 948 DBG_PDU();
ashleymills 0:3d62a105fd34 949 DBG("Creating space");
ashleymills 0:3d62a105fd34 950 int mallocLength = optionLength+optionDeltaAdjustment;
ashleymills 0:3d62a105fd34 951 int oldPDULength = _pduLength;
ashleymills 0:3d62a105fd34 952 _pduLength += mallocLength;
ashleymills 0:3d62a105fd34 953
ashleymills 0:3d62a105fd34 954 if(!_constructedFromBuffer) {
ashleymills 0:3d62a105fd34 955 uint8_t *newMemory = (uint8_t*)realloc(_pdu,_pduLength);
ashleymills 0:3d62a105fd34 956 if(newMemory==NULL) {
ashleymills 0:3d62a105fd34 957 DBG("Failed to allocate memory for option");
ashleymills 0:3d62a105fd34 958 _pduLength = oldPDULength;
ashleymills 0:3d62a105fd34 959 return 1;
ashleymills 0:3d62a105fd34 960 }
ashleymills 0:3d62a105fd34 961 _pdu = newMemory;
ashleymills 0:3d62a105fd34 962 _bufferLength = _pduLength;
ashleymills 0:3d62a105fd34 963 } else {
ashleymills 0:3d62a105fd34 964 // constructed from buffer, check space
ashleymills 0:3d62a105fd34 965 if(_pduLength>_bufferLength) {
ashleymills 0:3d62a105fd34 966 DBG("Buffer too small to contain option, needed %d, got %d.",_pduLength-oldPDULength,_bufferLength-oldPDULength);
ashleymills 0:3d62a105fd34 967 _pduLength = oldPDULength;
ashleymills 0:3d62a105fd34 968 return 1;
ashleymills 0:3d62a105fd34 969 }
ashleymills 0:3d62a105fd34 970 }
ashleymills 0:3d62a105fd34 971
ashleymills 0:3d62a105fd34 972 // move remainder of PDU data up to create hole for new option
ashleymills 0:3d62a105fd34 973 DBG_PDU();
ashleymills 0:3d62a105fd34 974 DBG("Shifting PDU.");
ashleymills 0:3d62a105fd34 975 shiftPDUUp(mallocLength,_pduLength-(insertionPosition+mallocLength));
ashleymills 0:3d62a105fd34 976 DBG_PDU();
ashleymills 0:3d62a105fd34 977
ashleymills 0:3d62a105fd34 978 // adjust option delta bytes of following option
ashleymills 0:3d62a105fd34 979 // move the option header to the correct position
ashleymills 0:3d62a105fd34 980 int nextHeaderPos = insertionPosition+mallocLength;
ashleymills 0:3d62a105fd34 981 _pdu[nextHeaderPos-optionDeltaAdjustment] = _pdu[nextHeaderPos];
ashleymills 0:3d62a105fd34 982 nextHeaderPos -= optionDeltaAdjustment;
ashleymills 0:3d62a105fd34 983 // and set the new value
ashleymills 0:3d62a105fd34 984 setOptionDelta(nextHeaderPos, newNextOptionDelta);
ashleymills 0:3d62a105fd34 985
ashleymills 0:3d62a105fd34 986 // new option shorter
ashleymills 0:3d62a105fd34 987 // p p n n x x x x x
ashleymills 0:3d62a105fd34 988 // p p n n x x x x x -
ashleymills 0:3d62a105fd34 989 // p p - n n x x x x x
ashleymills 0:3d62a105fd34 990 // p p - - n x x x x x
ashleymills 0:3d62a105fd34 991 // p p o o n x x x x x
ashleymills 0:3d62a105fd34 992
ashleymills 0:3d62a105fd34 993 // new option longer
ashleymills 0:3d62a105fd34 994 // p p n n x x x x x
ashleymills 0:3d62a105fd34 995 // p p n n x x x x x - - -
ashleymills 0:3d62a105fd34 996 // p p - - - n n x x x x x
ashleymills 0:3d62a105fd34 997 // p p - - n n n x x x x x
ashleymills 0:3d62a105fd34 998 // p p o o n n n x x x x x
ashleymills 0:3d62a105fd34 999
ashleymills 0:3d62a105fd34 1000 // note, it can only ever be shorter or the same since if an option was inserted the delta got smaller
ashleymills 0:3d62a105fd34 1001 // but I'll leave that little comment in, just to show that it would work even if the delta got bigger
ashleymills 0:3d62a105fd34 1002
ashleymills 0:3d62a105fd34 1003 // now insert the new option into the gap
ashleymills 0:3d62a105fd34 1004 DBGLX("Inserting new option...");
ashleymills 0:3d62a105fd34 1005 insertOption(insertionPosition,optionDelta,optionValueLength,optionValue);
ashleymills 0:3d62a105fd34 1006 DBGX("done\r\n");
ashleymills 0:3d62a105fd34 1007 DBG_PDU();
ashleymills 0:3d62a105fd34 1008
ashleymills 0:3d62a105fd34 1009 // done, mark it with B!
ashleymills 0:3d62a105fd34 1010 return 0;
ashleymills 0:3d62a105fd34 1011 }
ashleymills 0:3d62a105fd34 1012
ashleymills 0:3d62a105fd34 1013 /// Allocate space for a payload.
ashleymills 0:3d62a105fd34 1014 /**
ashleymills 0:3d62a105fd34 1015 * For dynamically constructed PDUs, this will allocate space for a payload in the object
ashleymills 0:3d62a105fd34 1016 * and return a pointer to it. If the PDU was constructed from a buffer, this doesn't
ashleymills 0:3d62a105fd34 1017 * malloc anything, it just changes the _pduLength and returns the payload pointer.
ashleymills 0:3d62a105fd34 1018 *
ashleymills 0:3d62a105fd34 1019 * \note The pointer returned points into the PDU buffer.
ashleymills 0:3d62a105fd34 1020 * \param len The length of the payload buffer to allocate.
ashleymills 0:3d62a105fd34 1021 * \return Either a pointer to the payload buffer, or NULL if there wasn't enough space / allocation failed.
ashleymills 0:3d62a105fd34 1022 */
ashleymills 0:3d62a105fd34 1023 uint8_t* CoapPDU::mallocPayload(int len) {
ashleymills 0:3d62a105fd34 1024 DBG("Entering mallocPayload");
ashleymills 0:3d62a105fd34 1025 // sanity checks
ashleymills 0:3d62a105fd34 1026 if(len==0) {
ashleymills 0:3d62a105fd34 1027 DBG("Cannot allocate a zero length payload");
ashleymills 0:3d62a105fd34 1028 return NULL;
ashleymills 0:3d62a105fd34 1029 }
ashleymills 0:3d62a105fd34 1030
ashleymills 0:3d62a105fd34 1031 // further sanity
ashleymills 0:3d62a105fd34 1032 if(len==_payloadLength) {
ashleymills 0:3d62a105fd34 1033 DBG("Space for payload of specified length already exists");
ashleymills 0:3d62a105fd34 1034 if(_payloadPointer==NULL) {
ashleymills 0:3d62a105fd34 1035 DBG("Garbage PDU. Payload length is %d, but existing _payloadPointer NULL",_payloadLength);
ashleymills 0:3d62a105fd34 1036 return NULL;
ashleymills 0:3d62a105fd34 1037 }
ashleymills 0:3d62a105fd34 1038 return _payloadPointer;
ashleymills 0:3d62a105fd34 1039 }
ashleymills 0:3d62a105fd34 1040
ashleymills 0:3d62a105fd34 1041 DBG("_bufferLength: %d, _pduLength: %d, _payloadLength: %d",_bufferLength,_pduLength,_payloadLength);
ashleymills 0:3d62a105fd34 1042
ashleymills 0:3d62a105fd34 1043 // might be making payload bigger (including bigger than 0) or smaller
ashleymills 0:3d62a105fd34 1044 int markerSpace = 1;
ashleymills 0:3d62a105fd34 1045 int payloadSpace = len;
ashleymills 0:3d62a105fd34 1046 // is this a resizing?
ashleymills 0:3d62a105fd34 1047 if(_payloadLength!=0) {
ashleymills 0:3d62a105fd34 1048 // marker already exists
ashleymills 0:3d62a105fd34 1049 markerSpace = 0;
ashleymills 0:3d62a105fd34 1050 // compute new payload length (can be negative if shrinking payload)
ashleymills 0:3d62a105fd34 1051 payloadSpace = len-_payloadLength;
ashleymills 0:3d62a105fd34 1052 }
ashleymills 0:3d62a105fd34 1053
ashleymills 0:3d62a105fd34 1054 // make space for payload (and payload marker if necessary)
ashleymills 0:3d62a105fd34 1055 int newLen = _pduLength+payloadSpace+markerSpace;
ashleymills 0:3d62a105fd34 1056 if(!_constructedFromBuffer) {
ashleymills 0:3d62a105fd34 1057 uint8_t* newPDU = (uint8_t*)realloc(_pdu,newLen);
ashleymills 0:3d62a105fd34 1058 if(newPDU==NULL) {
ashleymills 0:3d62a105fd34 1059 DBG("Cannot allocate (or shrink) space for payload");
ashleymills 0:3d62a105fd34 1060 return NULL;
ashleymills 0:3d62a105fd34 1061 }
ashleymills 0:3d62a105fd34 1062 _pdu = newPDU;
ashleymills 0:3d62a105fd34 1063 _bufferLength = newLen;
ashleymills 0:3d62a105fd34 1064 } else {
ashleymills 0:3d62a105fd34 1065 // constructed from buffer, check space
ashleymills 0:3d62a105fd34 1066 DBG("newLen: %d, _bufferLength: %d",newLen,_bufferLength);
ashleymills 0:3d62a105fd34 1067 if(newLen>_bufferLength) {
ashleymills 0:3d62a105fd34 1068 DBG("Buffer too small to contain desired payload, needed %d, got %d.",newLen-_pduLength,_bufferLength-_pduLength);
ashleymills 0:3d62a105fd34 1069 return NULL;
ashleymills 0:3d62a105fd34 1070 }
ashleymills 0:3d62a105fd34 1071 }
ashleymills 0:3d62a105fd34 1072
ashleymills 0:3d62a105fd34 1073 // deal with fresh allocation case separately
ashleymills 0:3d62a105fd34 1074 if(_payloadPointer==NULL) {
ashleymills 0:3d62a105fd34 1075 // set payload marker
ashleymills 0:3d62a105fd34 1076 _pdu[_pduLength] = 0xFF;
ashleymills 0:3d62a105fd34 1077 // payload at end of old PDU
ashleymills 0:3d62a105fd34 1078 _payloadPointer = &_pdu[_pduLength+1];
ashleymills 0:3d62a105fd34 1079 _pduLength = newLen;
ashleymills 0:3d62a105fd34 1080 _payloadLength = len;
ashleymills 0:3d62a105fd34 1081 return _payloadPointer;
ashleymills 0:3d62a105fd34 1082 }
ashleymills 0:3d62a105fd34 1083
ashleymills 0:3d62a105fd34 1084 // otherwise, just adjust length of PDU
ashleymills 0:3d62a105fd34 1085 _pduLength = newLen;
ashleymills 0:3d62a105fd34 1086 _payloadLength = len;
ashleymills 0:3d62a105fd34 1087 DBG("Leaving mallocPayload");
ashleymills 0:3d62a105fd34 1088 return _payloadPointer;
ashleymills 0:3d62a105fd34 1089 }
ashleymills 0:3d62a105fd34 1090
ashleymills 0:3d62a105fd34 1091 /// Set the payload to the byte sequence specified. Allocates memory in dynamic PDU if necessary.
ashleymills 0:3d62a105fd34 1092 /**
ashleymills 0:3d62a105fd34 1093 * This will set the payload to \b payload. It will allocate memory in the case where the PDU was
ashleymills 0:3d62a105fd34 1094 * constructed without an external buffer.
ashleymills 0:3d62a105fd34 1095 *
ashleymills 0:3d62a105fd34 1096 * This will fail either if the fixed buffer isn't big enough, or if memory could not be allocated
ashleymills 0:3d62a105fd34 1097 * in the non-external-buffer case.
ashleymills 0:3d62a105fd34 1098 *
ashleymills 0:3d62a105fd34 1099 * \param payload Pointer to payload byte sequence.
ashleymills 0:3d62a105fd34 1100 * \param len Length of payload byte sequence.
ashleymills 0:3d62a105fd34 1101 * \return 0 on success, 1 on failure.
ashleymills 0:3d62a105fd34 1102 */
ashleymills 0:3d62a105fd34 1103 int CoapPDU::setPayload(uint8_t *payload, int len) {
ashleymills 0:3d62a105fd34 1104 if(payload==NULL) {
ashleymills 0:3d62a105fd34 1105 DBG("NULL payload pointer.");
ashleymills 0:3d62a105fd34 1106 return 1;
ashleymills 0:3d62a105fd34 1107 }
ashleymills 0:3d62a105fd34 1108
ashleymills 0:3d62a105fd34 1109 uint8_t *payloadPointer = mallocPayload(len);
ashleymills 0:3d62a105fd34 1110 if(payloadPointer==NULL) {
ashleymills 0:3d62a105fd34 1111 DBG("Allocation of payload failed");
ashleymills 0:3d62a105fd34 1112 return 1;
ashleymills 0:3d62a105fd34 1113 }
ashleymills 0:3d62a105fd34 1114
ashleymills 0:3d62a105fd34 1115 // copy payload contents
ashleymills 0:3d62a105fd34 1116 memcpy(payloadPointer,payload,len);
ashleymills 0:3d62a105fd34 1117
ashleymills 0:3d62a105fd34 1118 return 0;
ashleymills 0:3d62a105fd34 1119 }
ashleymills 0:3d62a105fd34 1120
ashleymills 0:3d62a105fd34 1121 /// Returns a pointer to the payload buffer.
ashleymills 0:3d62a105fd34 1122 uint8_t* CoapPDU::getPayloadPointer() {
ashleymills 0:3d62a105fd34 1123 return _payloadPointer;
ashleymills 0:3d62a105fd34 1124 }
ashleymills 0:3d62a105fd34 1125
ashleymills 0:3d62a105fd34 1126 /// Gets the length of the payload buffer.
ashleymills 0:3d62a105fd34 1127 int CoapPDU::getPayloadLength() {
ashleymills 0:3d62a105fd34 1128 return _payloadLength;
ashleymills 0:3d62a105fd34 1129 }
ashleymills 0:3d62a105fd34 1130
ashleymills 0:3d62a105fd34 1131 /// Returns a pointer to a buffer which is a copy of the payload buffer (dynamically allocated).
ashleymills 0:3d62a105fd34 1132 uint8_t* CoapPDU::getPayloadCopy() {
ashleymills 0:3d62a105fd34 1133 if(_payloadLength==0) {
ashleymills 0:3d62a105fd34 1134 return NULL;
ashleymills 0:3d62a105fd34 1135 }
ashleymills 0:3d62a105fd34 1136
ashleymills 0:3d62a105fd34 1137 // malloc space for copy
ashleymills 0:3d62a105fd34 1138 uint8_t *payload = (uint8_t*)malloc(_payloadLength);
ashleymills 0:3d62a105fd34 1139 if(payload==NULL) {
ashleymills 0:3d62a105fd34 1140 DBG("Unable to allocate memory for payload");
ashleymills 0:3d62a105fd34 1141 return NULL;
ashleymills 0:3d62a105fd34 1142 }
ashleymills 0:3d62a105fd34 1143
ashleymills 0:3d62a105fd34 1144 // copy and return
ashleymills 0:3d62a105fd34 1145 memcpy(payload,_payloadPointer,_payloadLength);
ashleymills 0:3d62a105fd34 1146 return payload;
ashleymills 0:3d62a105fd34 1147 }
ashleymills 0:3d62a105fd34 1148
ashleymills 0:3d62a105fd34 1149 /// Shorthand for setting the content-format option.
ashleymills 0:3d62a105fd34 1150 /**
ashleymills 0:3d62a105fd34 1151 * Sets the content-format to the specified value (adds an option).
ashleymills 0:3d62a105fd34 1152 * \param format The content format, one of:
ashleymills 0:3d62a105fd34 1153 *
ashleymills 0:3d62a105fd34 1154 * - COAP_CONTENT_FORMAT_TEXT_PLAIN
ashleymills 0:3d62a105fd34 1155 * - COAP_CONTENT_FORMAT_APP_LINK
ashleymills 0:3d62a105fd34 1156 * - COAP_CONTENT_FORMAT_APP_XML
ashleymills 0:3d62a105fd34 1157 * - COAP_CONTENT_FORMAT_APP_OCTET
ashleymills 0:3d62a105fd34 1158 * - COAP_CONTENT_FORMAT_APP_EXI
ashleymills 0:3d62a105fd34 1159 * - COAP_CONTENT_FORMAT_APP_JSON
ashleymills 0:3d62a105fd34 1160 *
ashleymills 0:3d62a105fd34 1161 * \return 0 on success, 1 on failure.
ashleymills 0:3d62a105fd34 1162 */
ashleymills 0:3d62a105fd34 1163 int CoapPDU::setContentFormat(CoapPDU::ContentFormat format) {
ashleymills 0:3d62a105fd34 1164 if(format==0) {
ashleymills 0:3d62a105fd34 1165 // minimal representation means null option value
ashleymills 0:3d62a105fd34 1166 if(addOption(CoapPDU::COAP_OPTION_CONTENT_FORMAT,0,NULL)!=0) {
ashleymills 0:3d62a105fd34 1167 DBG("Error setting content format");
ashleymills 0:3d62a105fd34 1168 return 1;
ashleymills 0:3d62a105fd34 1169 }
ashleymills 0:3d62a105fd34 1170 return 0;
ashleymills 0:3d62a105fd34 1171 }
ashleymills 0:3d62a105fd34 1172
ashleymills 0:3d62a105fd34 1173 uint8_t c[2];
ashleymills 0:3d62a105fd34 1174
ashleymills 0:3d62a105fd34 1175 // just use 1 byte if can do it
ashleymills 0:3d62a105fd34 1176 if(format<256) {
ashleymills 0:3d62a105fd34 1177 c[0] = format;
ashleymills 0:3d62a105fd34 1178 if(addOption(CoapPDU::COAP_OPTION_CONTENT_FORMAT,1,c)!=0) {
ashleymills 0:3d62a105fd34 1179 DBG("Error setting content format");
ashleymills 0:3d62a105fd34 1180 return 1;
ashleymills 0:3d62a105fd34 1181 }
ashleymills 0:3d62a105fd34 1182 return 0;
ashleymills 0:3d62a105fd34 1183 }
ashleymills 0:3d62a105fd34 1184
ashleymills 0:3d62a105fd34 1185 uint16_t networkOrder = htons(format);
ashleymills 0:3d62a105fd34 1186 c[0] &= 0x00;
ashleymills 0:3d62a105fd34 1187 c[0] |= (networkOrder >> 8); // MSB
ashleymills 0:3d62a105fd34 1188 c[1] &= 0x00;
ashleymills 0:3d62a105fd34 1189 c[1] |= (networkOrder & 0x00FF); // LSB
ashleymills 0:3d62a105fd34 1190 if(addOption(CoapPDU::COAP_OPTION_CONTENT_FORMAT,2,c)!=0) {
ashleymills 0:3d62a105fd34 1191 DBG("Error setting content format");
ashleymills 0:3d62a105fd34 1192 return 1;
ashleymills 0:3d62a105fd34 1193 }
ashleymills 0:3d62a105fd34 1194 return 0;
ashleymills 0:3d62a105fd34 1195 }
ashleymills 0:3d62a105fd34 1196
ashleymills 0:3d62a105fd34 1197 /// PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE
ashleymills 0:3d62a105fd34 1198 /// PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE
ashleymills 0:3d62a105fd34 1199
ashleymills 0:3d62a105fd34 1200 /// Moves a block of bytes to end of PDU from given offset.
ashleymills 0:3d62a105fd34 1201 /**
ashleymills 0:3d62a105fd34 1202 * This moves the block of bytes _pdu[_pduLength-1-shiftOffset-shiftAmount] ... _pdu[_pduLength-1-shiftOffset]
ashleymills 0:3d62a105fd34 1203 * to the end of the PDU.
ashleymills 0:3d62a105fd34 1204 * \param shiftOffset End of block to move, relative to end of PDU (-1).
ashleymills 0:3d62a105fd34 1205 * \param shiftAmount Length of block to move.
ashleymills 0:3d62a105fd34 1206 */
ashleymills 0:3d62a105fd34 1207 void CoapPDU::shiftPDUUp(int shiftOffset, int shiftAmount) {
ashleymills 0:3d62a105fd34 1208 DBG("shiftOffset: %d, shiftAmount: %d",shiftOffset,shiftAmount);
ashleymills 0:3d62a105fd34 1209 int destPointer = _pduLength-1;
ashleymills 0:3d62a105fd34 1210 int srcPointer = destPointer-shiftOffset;
ashleymills 0:3d62a105fd34 1211 while(shiftAmount--) {
ashleymills 0:3d62a105fd34 1212 _pdu[destPointer] = _pdu[srcPointer];
ashleymills 0:3d62a105fd34 1213 destPointer--;
ashleymills 0:3d62a105fd34 1214 srcPointer--;
ashleymills 0:3d62a105fd34 1215 }
ashleymills 0:3d62a105fd34 1216 }
ashleymills 0:3d62a105fd34 1217
ashleymills 0:3d62a105fd34 1218 /// Moves a block of bytes down a specified number of steps.
ashleymills 0:3d62a105fd34 1219 /**
ashleymills 0:3d62a105fd34 1220 * Moves the block of bytes _pdu[startLocation+shiftOffset] ... _pdu[startLocation+shiftOffset+shiftAmount]
ashleymills 0:3d62a105fd34 1221 * down to \b startLocation.
ashleymills 0:3d62a105fd34 1222 * \param startLocation Index where to shift the block to.
ashleymills 0:3d62a105fd34 1223 * \param shiftOffset Where the block starts, relative to start index.
ashleymills 0:3d62a105fd34 1224 * \param shiftAmount Length of block to shift.
ashleymills 0:3d62a105fd34 1225 */
ashleymills 0:3d62a105fd34 1226 void CoapPDU::shiftPDUDown(int startLocation, int shiftOffset, int shiftAmount) {
ashleymills 0:3d62a105fd34 1227 DBG("startLocation: %d, shiftOffset: %d, shiftAmount: %d",startLocation,shiftOffset,shiftAmount);
ashleymills 0:3d62a105fd34 1228 int srcPointer = startLocation+shiftOffset;
ashleymills 0:3d62a105fd34 1229 while(shiftAmount--) {
ashleymills 0:3d62a105fd34 1230 _pdu[startLocation] = _pdu[srcPointer];
ashleymills 0:3d62a105fd34 1231 startLocation++;
ashleymills 0:3d62a105fd34 1232 srcPointer++;
ashleymills 0:3d62a105fd34 1233 }
ashleymills 0:3d62a105fd34 1234 }
ashleymills 0:3d62a105fd34 1235
ashleymills 0:3d62a105fd34 1236 /// Gets the payload length of an option.
ashleymills 0:3d62a105fd34 1237 /**
ashleymills 0:3d62a105fd34 1238 * \param option Pointer to location of option in PDU.
ashleymills 0:3d62a105fd34 1239 * \return The 16 bit option-payload length.
ashleymills 0:3d62a105fd34 1240 */
ashleymills 0:3d62a105fd34 1241 uint16_t CoapPDU::getOptionValueLength(uint8_t *option) {
ashleymills 0:3d62a105fd34 1242 uint16_t delta = (option[0] & 0xF0) >> 4;
ashleymills 0:3d62a105fd34 1243 uint16_t length = (option[0] & 0x0F);
ashleymills 0:3d62a105fd34 1244 // no extra bytes
ashleymills 0:3d62a105fd34 1245 if(length<13) {
ashleymills 0:3d62a105fd34 1246 return length;
ashleymills 0:3d62a105fd34 1247 }
ashleymills 0:3d62a105fd34 1248
ashleymills 0:3d62a105fd34 1249 // extra bytes skip header
ashleymills 0:3d62a105fd34 1250 int offset = 1;
ashleymills 0:3d62a105fd34 1251 // skip extra option delta bytes
ashleymills 0:3d62a105fd34 1252 if(delta==13) {
ashleymills 0:3d62a105fd34 1253 offset++;
ashleymills 0:3d62a105fd34 1254 } else if(delta==14) {
ashleymills 0:3d62a105fd34 1255 offset+=2;
ashleymills 0:3d62a105fd34 1256 }
ashleymills 0:3d62a105fd34 1257
ashleymills 0:3d62a105fd34 1258 // process length
ashleymills 0:3d62a105fd34 1259 if(length==13) {
ashleymills 0:3d62a105fd34 1260 return (option[offset]+13);
ashleymills 0:3d62a105fd34 1261 } else {
ashleymills 0:3d62a105fd34 1262 // need to convert to host order
ashleymills 0:3d62a105fd34 1263 uint16_t networkOrder = 0x0000;
ashleymills 0:3d62a105fd34 1264 networkOrder |= option[offset++];
ashleymills 0:3d62a105fd34 1265 networkOrder <<= 8;
ashleymills 0:3d62a105fd34 1266 networkOrder |= option[offset];
ashleymills 0:3d62a105fd34 1267 uint16_t hostOrder = ntohs(networkOrder);
ashleymills 0:3d62a105fd34 1268 return hostOrder+269;
ashleymills 0:3d62a105fd34 1269 }
ashleymills 0:3d62a105fd34 1270
ashleymills 0:3d62a105fd34 1271 }
ashleymills 0:3d62a105fd34 1272
ashleymills 0:3d62a105fd34 1273 /// Gets the delta of an option.
ashleymills 0:3d62a105fd34 1274 /**
ashleymills 0:3d62a105fd34 1275 * \param option Pointer to location of option in PDU.
ashleymills 0:3d62a105fd34 1276 * \return The 16 bit delta.
ashleymills 0:3d62a105fd34 1277 */
ashleymills 0:3d62a105fd34 1278 uint16_t CoapPDU::getOptionDelta(uint8_t *option) {
ashleymills 0:3d62a105fd34 1279 uint16_t delta = (option[0] & 0xF0) >> 4;
ashleymills 0:3d62a105fd34 1280 if(delta<13) {
ashleymills 0:3d62a105fd34 1281 return delta;
ashleymills 0:3d62a105fd34 1282 } else if(delta==13) {
ashleymills 0:3d62a105fd34 1283 // single byte option delta
ashleymills 0:3d62a105fd34 1284 return (option[1]+13);
ashleymills 0:3d62a105fd34 1285 } else if(delta==14) {
ashleymills 0:3d62a105fd34 1286 // double byte option delta
ashleymills 0:3d62a105fd34 1287 // need to convert to host order
ashleymills 0:3d62a105fd34 1288 uint16_t networkOrder = 0x0000;
ashleymills 0:3d62a105fd34 1289 networkOrder |= option[1];
ashleymills 0:3d62a105fd34 1290 networkOrder <<= 8;
ashleymills 0:3d62a105fd34 1291 networkOrder |= option[2];
ashleymills 0:3d62a105fd34 1292 uint16_t hostOrder = ntohs(networkOrder);
ashleymills 0:3d62a105fd34 1293 return hostOrder+269;
ashleymills 0:3d62a105fd34 1294 } else {
ashleymills 0:3d62a105fd34 1295 // should only ever occur in payload marker
ashleymills 0:3d62a105fd34 1296 return delta;
ashleymills 0:3d62a105fd34 1297 }
ashleymills 0:3d62a105fd34 1298 }
ashleymills 0:3d62a105fd34 1299
ashleymills 0:3d62a105fd34 1300 /// Finds the insertion position in the current list of options for the specified option.
ashleymills 0:3d62a105fd34 1301 /**
ashleymills 0:3d62a105fd34 1302 * \param optionNumber The option's number.
ashleymills 0:3d62a105fd34 1303 * \param prevOptionNumber A pointer to a uint16_t which will store the option number of the option previous
ashleymills 0:3d62a105fd34 1304 * to the insertion point.
ashleymills 0:3d62a105fd34 1305 * \return 0 on success, 1 on failure. \b prevOptionNumber will contain the option number of the option
ashleymills 0:3d62a105fd34 1306 * before the insertion position (for example 0 if no options have been inserted).
ashleymills 0:3d62a105fd34 1307 */
ashleymills 0:3d62a105fd34 1308 int CoapPDU::findInsertionPosition(uint16_t optionNumber, uint16_t *prevOptionNumber) {
ashleymills 0:3d62a105fd34 1309 // zero this for safety
ashleymills 0:3d62a105fd34 1310 *prevOptionNumber = 0x00;
ashleymills 0:3d62a105fd34 1311
ashleymills 0:3d62a105fd34 1312 DBG("_pduLength: %d",_pduLength);
ashleymills 0:3d62a105fd34 1313
ashleymills 0:3d62a105fd34 1314 // if option is bigger than any currently stored, it goes at the end
ashleymills 0:3d62a105fd34 1315 // this includes the case that no option has yet been added
ashleymills 0:3d62a105fd34 1316 if( (optionNumber >= _maxAddedOptionNumber) || (_pduLength == (COAP_HDR_SIZE+getTokenLength())) ) {
ashleymills 0:3d62a105fd34 1317 *prevOptionNumber = _maxAddedOptionNumber;
ashleymills 0:3d62a105fd34 1318 return _pduLength;
ashleymills 0:3d62a105fd34 1319 }
ashleymills 0:3d62a105fd34 1320
ashleymills 0:3d62a105fd34 1321 // otherwise walk over the options
ashleymills 0:3d62a105fd34 1322 int optionPos = COAP_HDR_SIZE + getTokenLength();
ashleymills 0:3d62a105fd34 1323 uint16_t optionDelta = 0, optionValueLength = 0;
ashleymills 0:3d62a105fd34 1324 uint16_t currentOptionNumber = 0;
ashleymills 0:3d62a105fd34 1325 while(optionPos<_pduLength && _pdu[optionPos]!=0xFF) {
ashleymills 0:3d62a105fd34 1326 optionDelta = getOptionDelta(&_pdu[optionPos]);
ashleymills 0:3d62a105fd34 1327 currentOptionNumber += optionDelta;
ashleymills 0:3d62a105fd34 1328 optionValueLength = getOptionValueLength(&_pdu[optionPos]);
ashleymills 0:3d62a105fd34 1329 // test if this is insertion position
ashleymills 0:3d62a105fd34 1330 if(currentOptionNumber>optionNumber) {
ashleymills 0:3d62a105fd34 1331 return optionPos;
ashleymills 0:3d62a105fd34 1332 }
ashleymills 0:3d62a105fd34 1333 // keep track of the last valid option number
ashleymills 0:3d62a105fd34 1334 *prevOptionNumber = currentOptionNumber;
ashleymills 0:3d62a105fd34 1335 // move onto next option
ashleymills 0:3d62a105fd34 1336 optionPos += computeExtraBytes(optionDelta);
ashleymills 0:3d62a105fd34 1337 optionPos += computeExtraBytes(optionValueLength);
ashleymills 0:3d62a105fd34 1338 optionPos += optionValueLength;
ashleymills 0:3d62a105fd34 1339 optionPos++; // (for mandatory option header byte)
ashleymills 0:3d62a105fd34 1340 }
ashleymills 0:3d62a105fd34 1341 return optionPos;
ashleymills 0:3d62a105fd34 1342
ashleymills 0:3d62a105fd34 1343 }
ashleymills 0:3d62a105fd34 1344
ashleymills 0:3d62a105fd34 1345 /// CoAP uses a minimal-byte representation for length fields. This returns the number of bytes needed to represent a given length.
ashleymills 0:3d62a105fd34 1346 int CoapPDU::computeExtraBytes(uint16_t n) {
ashleymills 0:3d62a105fd34 1347 if(n<13) {
ashleymills 0:3d62a105fd34 1348 return 0;
ashleymills 0:3d62a105fd34 1349 }
ashleymills 0:3d62a105fd34 1350
ashleymills 0:3d62a105fd34 1351 if(n<269) {
ashleymills 0:3d62a105fd34 1352 return 1;
ashleymills 0:3d62a105fd34 1353 }
ashleymills 0:3d62a105fd34 1354
ashleymills 0:3d62a105fd34 1355 return 2;
ashleymills 0:3d62a105fd34 1356 }
ashleymills 0:3d62a105fd34 1357
ashleymills 0:3d62a105fd34 1358 /// Set the option delta to the specified value.
ashleymills 0:3d62a105fd34 1359 /**
ashleymills 0:3d62a105fd34 1360 * This assumes space has been made for the option delta.
ashleymills 0:3d62a105fd34 1361 * \param optionPosition The index of the option in the PDU.
ashleymills 0:3d62a105fd34 1362 * \param optionDelta The option delta value to set.
ashleymills 0:3d62a105fd34 1363 */
ashleymills 0:3d62a105fd34 1364 void CoapPDU::setOptionDelta(int optionPosition, uint16_t optionDelta) {
ashleymills 0:3d62a105fd34 1365 int headerStart = optionPosition;
ashleymills 0:3d62a105fd34 1366 // clear the old option delta bytes
ashleymills 0:3d62a105fd34 1367 _pdu[headerStart] &= 0x0F;
ashleymills 0:3d62a105fd34 1368
ashleymills 0:3d62a105fd34 1369 // set the option delta bytes
ashleymills 0:3d62a105fd34 1370 if(optionDelta<13) {
ashleymills 0:3d62a105fd34 1371 _pdu[headerStart] |= (optionDelta << 4);
ashleymills 0:3d62a105fd34 1372 } else if(optionDelta<269) {
ashleymills 0:3d62a105fd34 1373 // 1 extra byte
ashleymills 0:3d62a105fd34 1374 _pdu[headerStart] |= 0xD0; // 13 in first nibble
ashleymills 0:3d62a105fd34 1375 _pdu[++optionPosition] &= 0x00;
ashleymills 0:3d62a105fd34 1376 _pdu[optionPosition] |= (optionDelta-13);
ashleymills 0:3d62a105fd34 1377 } else {
ashleymills 0:3d62a105fd34 1378 // 2 extra bytes, network byte order uint16_t
ashleymills 0:3d62a105fd34 1379 _pdu[headerStart] |= 0xE0; // 14 in first nibble
ashleymills 0:3d62a105fd34 1380 optionDelta = htons(optionDelta-269);
ashleymills 0:3d62a105fd34 1381 _pdu[++optionPosition] &= 0x00;
ashleymills 0:3d62a105fd34 1382 _pdu[optionPosition] |= (optionDelta >> 8); // MSB
ashleymills 0:3d62a105fd34 1383 _pdu[++optionPosition] &= 0x00;
ashleymills 0:3d62a105fd34 1384 _pdu[optionPosition] |= (optionDelta & 0x00FF); // LSB
ashleymills 0:3d62a105fd34 1385 }
ashleymills 0:3d62a105fd34 1386 }
ashleymills 0:3d62a105fd34 1387
ashleymills 0:3d62a105fd34 1388 /// Insert an option in-memory at the specified location.
ashleymills 0:3d62a105fd34 1389 /**
ashleymills 0:3d62a105fd34 1390 * This assumes that there is enough space at the location specified.
ashleymills 0:3d62a105fd34 1391 * \param insertionPosition Position in the PDU where the option should be placed.
ashleymills 0:3d62a105fd34 1392 * \param optionDelta The delta value for the option.
ashleymills 0:3d62a105fd34 1393 * \param optionValueLength The length of the option value.
ashleymills 0:3d62a105fd34 1394 * \param optionValue A pointer to the sequence of bytes representing the option value.
ashleymills 0:3d62a105fd34 1395 * \return 0 on success, 1 on failure.
ashleymills 0:3d62a105fd34 1396 */
ashleymills 0:3d62a105fd34 1397 int CoapPDU::insertOption(
ashleymills 0:3d62a105fd34 1398 int insertionPosition,
ashleymills 0:3d62a105fd34 1399 uint16_t optionDelta,
ashleymills 0:3d62a105fd34 1400 uint16_t optionValueLength,
ashleymills 0:3d62a105fd34 1401 uint8_t *optionValue) {
ashleymills 0:3d62a105fd34 1402
ashleymills 0:3d62a105fd34 1403 int headerStart = insertionPosition;
ashleymills 0:3d62a105fd34 1404
ashleymills 0:3d62a105fd34 1405 // clear old option header start
ashleymills 0:3d62a105fd34 1406 _pdu[headerStart] &= 0x00;
ashleymills 0:3d62a105fd34 1407
ashleymills 0:3d62a105fd34 1408 // set the option delta bytes
ashleymills 0:3d62a105fd34 1409 if(optionDelta<13) {
ashleymills 0:3d62a105fd34 1410 _pdu[headerStart] |= (optionDelta << 4);
ashleymills 0:3d62a105fd34 1411 } else if(optionDelta<269) {
ashleymills 0:3d62a105fd34 1412 // 1 extra byte
ashleymills 0:3d62a105fd34 1413 _pdu[headerStart] |= 0xD0; // 13 in first nibble
ashleymills 0:3d62a105fd34 1414 _pdu[++insertionPosition] &= 0x00;
ashleymills 0:3d62a105fd34 1415 _pdu[insertionPosition] |= (optionDelta-13);
ashleymills 0:3d62a105fd34 1416 } else {
ashleymills 0:3d62a105fd34 1417 // 2 extra bytes, network byte order uint16_t
ashleymills 0:3d62a105fd34 1418 _pdu[headerStart] |= 0xE0; // 14 in first nibble
ashleymills 0:3d62a105fd34 1419 optionDelta = htons(optionDelta-269);
ashleymills 0:3d62a105fd34 1420 _pdu[++insertionPosition] &= 0x00;
ashleymills 0:3d62a105fd34 1421 _pdu[insertionPosition] |= (optionDelta >> 8); // MSB
ashleymills 0:3d62a105fd34 1422 _pdu[++insertionPosition] &= 0x00;
ashleymills 0:3d62a105fd34 1423 _pdu[insertionPosition] |= (optionDelta & 0x00FF); // LSB
ashleymills 0:3d62a105fd34 1424 }
ashleymills 0:3d62a105fd34 1425
ashleymills 0:3d62a105fd34 1426 // set the option value length bytes
ashleymills 0:3d62a105fd34 1427 if(optionValueLength<13) {
ashleymills 0:3d62a105fd34 1428 _pdu[headerStart] |= (optionValueLength & 0x000F);
ashleymills 0:3d62a105fd34 1429 } else if(optionValueLength<269) {
ashleymills 0:3d62a105fd34 1430 _pdu[headerStart] |= 0x0D; // 13 in second nibble
ashleymills 0:3d62a105fd34 1431 _pdu[++insertionPosition] &= 0x00;
ashleymills 0:3d62a105fd34 1432 _pdu[insertionPosition] |= (optionValueLength-13);
ashleymills 0:3d62a105fd34 1433 } else {
ashleymills 0:3d62a105fd34 1434 _pdu[headerStart] |= 0x0E; // 14 in second nibble
ashleymills 0:3d62a105fd34 1435 // this is in network byte order
ashleymills 0:3d62a105fd34 1436 DBG("optionValueLength: %u",optionValueLength);
ashleymills 0:3d62a105fd34 1437 uint16_t networkOrder = htons(optionValueLength-269);
ashleymills 0:3d62a105fd34 1438 _pdu[++insertionPosition] &= 0x00;
ashleymills 0:3d62a105fd34 1439 _pdu[insertionPosition] |= (networkOrder >> 8); // MSB
ashleymills 0:3d62a105fd34 1440 _pdu[++insertionPosition] &= 0x00;
ashleymills 0:3d62a105fd34 1441 _pdu[insertionPosition] |= (networkOrder & 0x00FF); // LSB
ashleymills 0:3d62a105fd34 1442 }
ashleymills 0:3d62a105fd34 1443
ashleymills 0:3d62a105fd34 1444 // and finally copy the option value itself
ashleymills 0:3d62a105fd34 1445 memcpy(&_pdu[++insertionPosition],optionValue,optionValueLength);
ashleymills 0:3d62a105fd34 1446
ashleymills 0:3d62a105fd34 1447 return 0;
ashleymills 0:3d62a105fd34 1448 }
ashleymills 0:3d62a105fd34 1449
ashleymills 0:3d62a105fd34 1450 // DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG
ashleymills 0:3d62a105fd34 1451 // DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG
ashleymills 0:3d62a105fd34 1452
ashleymills 0:3d62a105fd34 1453 /// Prints the PDU in human-readable format.
ashleymills 0:3d62a105fd34 1454 void CoapPDU::printHuman() {
ashleymills 0:3d62a105fd34 1455 INFO("__________________");
ashleymills 0:3d62a105fd34 1456 if(_constructedFromBuffer) {
ashleymills 0:3d62a105fd34 1457 INFO("PDU was constructed from buffer of %d bytes",_bufferLength);
ashleymills 0:3d62a105fd34 1458 }
ashleymills 0:3d62a105fd34 1459 INFO("PDU is %d bytes long",_pduLength);
ashleymills 0:3d62a105fd34 1460 INFO("CoAP Version: %d",getVersion());
ashleymills 0:3d62a105fd34 1461 INFOX("Message Type: ");
ashleymills 0:3d62a105fd34 1462 switch(getType()) {
ashleymills 0:3d62a105fd34 1463 case COAP_CONFIRMABLE:
ashleymills 0:3d62a105fd34 1464 INFO("Confirmable");
ashleymills 0:3d62a105fd34 1465 break;
ashleymills 0:3d62a105fd34 1466
ashleymills 0:3d62a105fd34 1467 case COAP_NON_CONFIRMABLE:
ashleymills 0:3d62a105fd34 1468 INFO("Non-Confirmable");
ashleymills 0:3d62a105fd34 1469 break;
ashleymills 0:3d62a105fd34 1470
ashleymills 0:3d62a105fd34 1471 case COAP_ACKNOWLEDGEMENT:
ashleymills 0:3d62a105fd34 1472 INFO("Acknowledgement");
ashleymills 0:3d62a105fd34 1473 break;
ashleymills 0:3d62a105fd34 1474
ashleymills 0:3d62a105fd34 1475 case COAP_RESET:
ashleymills 0:3d62a105fd34 1476 INFO("Reset");
ashleymills 0:3d62a105fd34 1477 break;
ashleymills 0:3d62a105fd34 1478 }
ashleymills 0:3d62a105fd34 1479 INFO("Token length: %d",getTokenLength());
ashleymills 0:3d62a105fd34 1480 INFOX("Code: ");
ashleymills 0:3d62a105fd34 1481 switch(getCode()) {
ashleymills 0:3d62a105fd34 1482 case COAP_EMPTY:
ashleymills 0:3d62a105fd34 1483 INFO("0.00 Empty");
ashleymills 0:3d62a105fd34 1484 break;
ashleymills 0:3d62a105fd34 1485 case COAP_GET:
ashleymills 0:3d62a105fd34 1486 INFO("0.01 GET");
ashleymills 0:3d62a105fd34 1487 break;
ashleymills 0:3d62a105fd34 1488 case COAP_POST:
ashleymills 0:3d62a105fd34 1489 INFO("0.02 POST");
ashleymills 0:3d62a105fd34 1490 break;
ashleymills 0:3d62a105fd34 1491 case COAP_PUT:
ashleymills 0:3d62a105fd34 1492 INFO("0.03 PUT");
ashleymills 0:3d62a105fd34 1493 break;
ashleymills 0:3d62a105fd34 1494 case COAP_DELETE:
ashleymills 0:3d62a105fd34 1495 INFO("0.04 DELETE");
ashleymills 0:3d62a105fd34 1496 break;
ashleymills 0:3d62a105fd34 1497 case COAP_CREATED:
ashleymills 0:3d62a105fd34 1498 INFO("2.01 Created");
ashleymills 0:3d62a105fd34 1499 break;
ashleymills 0:3d62a105fd34 1500 case COAP_DELETED:
ashleymills 0:3d62a105fd34 1501 INFO("2.02 Deleted");
ashleymills 0:3d62a105fd34 1502 break;
ashleymills 0:3d62a105fd34 1503 case COAP_VALID:
ashleymills 0:3d62a105fd34 1504 INFO("2.03 Valid");
ashleymills 0:3d62a105fd34 1505 break;
ashleymills 0:3d62a105fd34 1506 case COAP_CHANGED:
ashleymills 0:3d62a105fd34 1507 INFO("2.04 Changed");
ashleymills 0:3d62a105fd34 1508 break;
ashleymills 0:3d62a105fd34 1509 case COAP_CONTENT:
ashleymills 0:3d62a105fd34 1510 INFO("2.05 Content");
ashleymills 0:3d62a105fd34 1511 break;
ashleymills 0:3d62a105fd34 1512 case COAP_BAD_REQUEST:
ashleymills 0:3d62a105fd34 1513 INFO("4.00 Bad Request");
ashleymills 0:3d62a105fd34 1514 break;
ashleymills 0:3d62a105fd34 1515 case COAP_UNAUTHORIZED:
ashleymills 0:3d62a105fd34 1516 INFO("4.01 Unauthorized");
ashleymills 0:3d62a105fd34 1517 break;
ashleymills 0:3d62a105fd34 1518 case COAP_BAD_OPTION:
ashleymills 0:3d62a105fd34 1519 INFO("4.02 Bad Option");
ashleymills 0:3d62a105fd34 1520 break;
ashleymills 0:3d62a105fd34 1521 case COAP_FORBIDDEN:
ashleymills 0:3d62a105fd34 1522 INFO("4.03 Forbidden");
ashleymills 0:3d62a105fd34 1523 break;
ashleymills 0:3d62a105fd34 1524 case COAP_NOT_FOUND:
ashleymills 0:3d62a105fd34 1525 INFO("4.04 Not Found");
ashleymills 0:3d62a105fd34 1526 break;
ashleymills 0:3d62a105fd34 1527 case COAP_METHOD_NOT_ALLOWED:
ashleymills 0:3d62a105fd34 1528 INFO("4.05 Method Not Allowed");
ashleymills 0:3d62a105fd34 1529 break;
ashleymills 0:3d62a105fd34 1530 case COAP_NOT_ACCEPTABLE:
ashleymills 0:3d62a105fd34 1531 INFO("4.06 Not Acceptable");
ashleymills 0:3d62a105fd34 1532 break;
ashleymills 0:3d62a105fd34 1533 case COAP_PRECONDITION_FAILED:
ashleymills 0:3d62a105fd34 1534 INFO("4.12 Precondition Failed");
ashleymills 0:3d62a105fd34 1535 break;
ashleymills 0:3d62a105fd34 1536 case COAP_REQUEST_ENTITY_TOO_LARGE:
ashleymills 0:3d62a105fd34 1537 INFO("4.13 Request Entity Too Large");
ashleymills 0:3d62a105fd34 1538 break;
ashleymills 0:3d62a105fd34 1539 case COAP_UNSUPPORTED_CONTENT_FORMAT:
ashleymills 0:3d62a105fd34 1540 INFO("4.15 Unsupported Content-Format");
ashleymills 0:3d62a105fd34 1541 break;
ashleymills 0:3d62a105fd34 1542 case COAP_INTERNAL_SERVER_ERROR:
ashleymills 0:3d62a105fd34 1543 INFO("5.00 Internal Server Error");
ashleymills 0:3d62a105fd34 1544 break;
ashleymills 0:3d62a105fd34 1545 case COAP_NOT_IMPLEMENTED:
ashleymills 0:3d62a105fd34 1546 INFO("5.01 Not Implemented");
ashleymills 0:3d62a105fd34 1547 break;
ashleymills 0:3d62a105fd34 1548 case COAP_BAD_GATEWAY:
ashleymills 0:3d62a105fd34 1549 INFO("5.02 Bad Gateway");
ashleymills 0:3d62a105fd34 1550 break;
ashleymills 0:3d62a105fd34 1551 case COAP_SERVICE_UNAVAILABLE:
ashleymills 0:3d62a105fd34 1552 INFO("5.03 Service Unavailable");
ashleymills 0:3d62a105fd34 1553 break;
ashleymills 0:3d62a105fd34 1554 case COAP_GATEWAY_TIMEOUT:
ashleymills 0:3d62a105fd34 1555 INFO("5.04 Gateway Timeout");
ashleymills 0:3d62a105fd34 1556 break;
ashleymills 0:3d62a105fd34 1557 case COAP_PROXYING_NOT_SUPPORTED:
ashleymills 0:3d62a105fd34 1558 INFO("5.05 Proxying Not Supported");
ashleymills 0:3d62a105fd34 1559 break;
ashleymills 0:3d62a105fd34 1560 }
ashleymills 0:3d62a105fd34 1561
ashleymills 0:3d62a105fd34 1562 // print message ID
ashleymills 0:3d62a105fd34 1563 INFO("Message ID: %u",getMessageID());
ashleymills 0:3d62a105fd34 1564
ashleymills 0:3d62a105fd34 1565 // print token value
ashleymills 0:3d62a105fd34 1566 int tokenLength = getTokenLength();
ashleymills 0:3d62a105fd34 1567 uint8_t *tokenPointer = getPDUPointer()+COAP_HDR_SIZE;
ashleymills 0:3d62a105fd34 1568 if(tokenLength==0) {
ashleymills 0:3d62a105fd34 1569 INFO("No token.");
ashleymills 0:3d62a105fd34 1570 } else {
ashleymills 0:3d62a105fd34 1571 INFO("Token of %d bytes.",tokenLength);
ashleymills 0:3d62a105fd34 1572 INFOX(" Value: 0x");
ashleymills 0:3d62a105fd34 1573 for(int j=0; j<tokenLength; j++) {
ashleymills 0:3d62a105fd34 1574 INFOX("%.2x",tokenPointer[j]);
ashleymills 0:3d62a105fd34 1575 }
ashleymills 0:3d62a105fd34 1576 INFO(" ");
ashleymills 0:3d62a105fd34 1577 }
ashleymills 0:3d62a105fd34 1578
ashleymills 0:3d62a105fd34 1579 // print options
ashleymills 0:3d62a105fd34 1580 CoapPDU::CoapOption* options = getOptions();
ashleymills 0:3d62a105fd34 1581 INFO("%d options:",_numOptions);
ashleymills 0:3d62a105fd34 1582 for(int i=0; i<_numOptions; i++) {
ashleymills 0:3d62a105fd34 1583 INFO("OPTION (%d/%d)",i,_numOptions);
ashleymills 0:3d62a105fd34 1584 INFO(" Option number (delta): %hu (%hu)",options[i].optionNumber,options[i].optionDelta);
ashleymills 0:3d62a105fd34 1585 INFOX(" Name: ");
ashleymills 0:3d62a105fd34 1586 switch(options[i].optionNumber) {
ashleymills 0:3d62a105fd34 1587 case COAP_OPTION_IF_MATCH:
ashleymills 0:3d62a105fd34 1588 INFO("IF_MATCH");
ashleymills 0:3d62a105fd34 1589 break;
ashleymills 0:3d62a105fd34 1590 case COAP_OPTION_URI_HOST:
ashleymills 0:3d62a105fd34 1591 INFO("URI_HOST");
ashleymills 0:3d62a105fd34 1592 break;
ashleymills 0:3d62a105fd34 1593 case COAP_OPTION_ETAG:
ashleymills 0:3d62a105fd34 1594 INFO("ETAG");
ashleymills 0:3d62a105fd34 1595 break;
ashleymills 0:3d62a105fd34 1596 case COAP_OPTION_IF_NONE_MATCH:
ashleymills 0:3d62a105fd34 1597 INFO("IF_NONE_MATCH");
ashleymills 0:3d62a105fd34 1598 break;
ashleymills 0:3d62a105fd34 1599 case COAP_OPTION_OBSERVE:
ashleymills 0:3d62a105fd34 1600 INFO("OBSERVE");
ashleymills 0:3d62a105fd34 1601 break;
ashleymills 0:3d62a105fd34 1602 case COAP_OPTION_URI_PORT:
ashleymills 0:3d62a105fd34 1603 INFO("URI_PORT");
ashleymills 0:3d62a105fd34 1604 break;
ashleymills 0:3d62a105fd34 1605 case COAP_OPTION_LOCATION_PATH:
ashleymills 0:3d62a105fd34 1606 INFO("LOCATION_PATH");
ashleymills 0:3d62a105fd34 1607 break;
ashleymills 0:3d62a105fd34 1608 case COAP_OPTION_URI_PATH:
ashleymills 0:3d62a105fd34 1609 INFO("URI_PATH");
ashleymills 0:3d62a105fd34 1610 break;
ashleymills 0:3d62a105fd34 1611 case COAP_OPTION_CONTENT_FORMAT:
ashleymills 0:3d62a105fd34 1612 INFO("CONTENT_FORMAT");
ashleymills 0:3d62a105fd34 1613 break;
ashleymills 0:3d62a105fd34 1614 case COAP_OPTION_MAX_AGE:
ashleymills 0:3d62a105fd34 1615 INFO("MAX_AGE");
ashleymills 0:3d62a105fd34 1616 break;
ashleymills 0:3d62a105fd34 1617 case COAP_OPTION_URI_QUERY:
ashleymills 0:3d62a105fd34 1618 INFO("URI_QUERY");
ashleymills 0:3d62a105fd34 1619 break;
ashleymills 0:3d62a105fd34 1620 case COAP_OPTION_ACCEPT:
ashleymills 0:3d62a105fd34 1621 INFO("ACCEPT");
ashleymills 0:3d62a105fd34 1622 break;
ashleymills 0:3d62a105fd34 1623 case COAP_OPTION_LOCATION_QUERY:
ashleymills 0:3d62a105fd34 1624 INFO("LOCATION_QUERY");
ashleymills 0:3d62a105fd34 1625 break;
ashleymills 0:3d62a105fd34 1626 case COAP_OPTION_PROXY_URI:
ashleymills 0:3d62a105fd34 1627 INFO("PROXY_URI");
ashleymills 0:3d62a105fd34 1628 break;
ashleymills 0:3d62a105fd34 1629 case COAP_OPTION_PROXY_SCHEME:
ashleymills 0:3d62a105fd34 1630 INFO("PROXY_SCHEME");
ashleymills 0:3d62a105fd34 1631 break;
ashleymills 0:3d62a105fd34 1632 case COAP_OPTION_BLOCK1:
ashleymills 0:3d62a105fd34 1633 INFO("BLOCK1");
ashleymills 0:3d62a105fd34 1634 break;
ashleymills 0:3d62a105fd34 1635 case COAP_OPTION_BLOCK2:
ashleymills 0:3d62a105fd34 1636 INFO("BLOCK2");
ashleymills 0:3d62a105fd34 1637 break;
ashleymills 0:3d62a105fd34 1638 case COAP_OPTION_SIZE1:
ashleymills 0:3d62a105fd34 1639 INFO("SIZE1");
ashleymills 0:3d62a105fd34 1640 break;
ashleymills 0:3d62a105fd34 1641 case COAP_OPTION_SIZE2:
ashleymills 0:3d62a105fd34 1642 INFO("SIZE2");
ashleymills 0:3d62a105fd34 1643 break;
ashleymills 0:3d62a105fd34 1644 default:
ashleymills 0:3d62a105fd34 1645 INFO("Unknown option");
ashleymills 0:3d62a105fd34 1646 break;
ashleymills 0:3d62a105fd34 1647 }
ashleymills 0:3d62a105fd34 1648 INFO(" Value length: %u",options[i].optionValueLength);
ashleymills 0:3d62a105fd34 1649 INFOX(" Value: \"");
ashleymills 0:3d62a105fd34 1650 for(int j=0; j<options[i].optionValueLength; j++) {
ashleymills 0:3d62a105fd34 1651 char c = options[i].optionValuePointer[j];
ashleymills 0:3d62a105fd34 1652 if((c>='!'&&c<='~')||c==' ') {
ashleymills 0:3d62a105fd34 1653 INFOX("%c",c);
ashleymills 0:3d62a105fd34 1654 } else {
ashleymills 0:3d62a105fd34 1655 INFOX("\\%.2d",c);
ashleymills 0:3d62a105fd34 1656 }
ashleymills 0:3d62a105fd34 1657 }
ashleymills 0:3d62a105fd34 1658 INFO("\"");
ashleymills 0:3d62a105fd34 1659 }
ashleymills 0:3d62a105fd34 1660
ashleymills 0:3d62a105fd34 1661 // print payload
ashleymills 0:3d62a105fd34 1662 if(_payloadLength==0) {
ashleymills 0:3d62a105fd34 1663 INFO("No payload.");
ashleymills 0:3d62a105fd34 1664 } else {
ashleymills 0:3d62a105fd34 1665 INFO("Payload of %d bytes",_payloadLength);
ashleymills 0:3d62a105fd34 1666 INFOX(" Value: \"");
ashleymills 0:3d62a105fd34 1667 for(int j=0; j<_payloadLength; j++) {
ashleymills 0:3d62a105fd34 1668 char c = _payloadPointer[j];
ashleymills 0:3d62a105fd34 1669 if((c>='!'&&c<='~')||c==' ') {
ashleymills 0:3d62a105fd34 1670 INFOX("%c",c);
ashleymills 0:3d62a105fd34 1671 } else {
ashleymills 0:3d62a105fd34 1672 INFOX("\\%.2x",c);
ashleymills 0:3d62a105fd34 1673 }
ashleymills 0:3d62a105fd34 1674 }
ashleymills 0:3d62a105fd34 1675 INFO("\"");
ashleymills 0:3d62a105fd34 1676 }
ashleymills 0:3d62a105fd34 1677 INFO("__________________");
ashleymills 0:3d62a105fd34 1678 }
ashleymills 0:3d62a105fd34 1679
ashleymills 0:3d62a105fd34 1680 /// Prints the PDU as a c array (useful for debugging or hardcoding PDUs)
ashleymills 0:3d62a105fd34 1681 void CoapPDU::printPDUAsCArray() {
ashleymills 0:3d62a105fd34 1682 printf("const uint8_t array[] = {\r\n ");
ashleymills 0:3d62a105fd34 1683 for(int i=0; i<_pduLength; i++) {
ashleymills 0:3d62a105fd34 1684 printf("0x%.2x, ",_pdu[i]);
ashleymills 0:3d62a105fd34 1685 }
ashleymills 0:3d62a105fd34 1686 printf("\r\n};\r\n");
ashleymills 0:3d62a105fd34 1687 }
ashleymills 0:3d62a105fd34 1688
ashleymills 0:3d62a105fd34 1689 /// A routine for printing an option in human-readable format.
ashleymills 0:3d62a105fd34 1690 /**
ashleymills 0:3d62a105fd34 1691 * \param option This is a pointer to where the option begins in the PDU.
ashleymills 0:3d62a105fd34 1692 */
ashleymills 0:3d62a105fd34 1693 void CoapPDU::printOptionHuman(uint8_t *option) {
ashleymills 0:3d62a105fd34 1694 // compute some useful stuff
ashleymills 0:3d62a105fd34 1695 uint16_t optionDelta = getOptionDelta(option);
ashleymills 0:3d62a105fd34 1696 uint16_t optionValueLength = getOptionValueLength(option);
ashleymills 0:3d62a105fd34 1697 int extraDeltaBytes = computeExtraBytes(optionDelta);
ashleymills 0:3d62a105fd34 1698 int extraValueLengthBytes = computeExtraBytes(optionValueLength);
ashleymills 0:3d62a105fd34 1699 int totalLength = 1+extraDeltaBytes+extraValueLengthBytes+optionValueLength;
ashleymills 0:3d62a105fd34 1700
ashleymills 0:3d62a105fd34 1701 if(totalLength>_pduLength) {
ashleymills 0:3d62a105fd34 1702 totalLength = &_pdu[_pduLength-1]-option;
ashleymills 0:3d62a105fd34 1703 DBG("New length: %u",totalLength);
ashleymills 0:3d62a105fd34 1704 }
ashleymills 0:3d62a105fd34 1705
ashleymills 0:3d62a105fd34 1706 // print summary
ashleymills 0:3d62a105fd34 1707 DBG("~~~~~~ Option ~~~~~~");
ashleymills 0:3d62a105fd34 1708 DBG("Delta: %u, Value length: %u",optionDelta,optionValueLength);
ashleymills 0:3d62a105fd34 1709
ashleymills 0:3d62a105fd34 1710 // print all bytes
ashleymills 0:3d62a105fd34 1711 DBG("All bytes (%d):",totalLength);
ashleymills 0:3d62a105fd34 1712 for(int i=0; i<totalLength; i++) {
ashleymills 0:3d62a105fd34 1713 if(i%4==0) {
ashleymills 0:3d62a105fd34 1714 DBG(" ");
ashleymills 0:3d62a105fd34 1715 DBGX(" %.2d ",i);
ashleymills 0:3d62a105fd34 1716 }
ashleymills 0:3d62a105fd34 1717 CoapPDU::printBinary(option[i]); DBGX(" ");
ashleymills 0:3d62a105fd34 1718 }
ashleymills 0:3d62a105fd34 1719 DBG(" "); DBG(" ");
ashleymills 0:3d62a105fd34 1720
ashleymills 0:3d62a105fd34 1721 // print header byte
ashleymills 0:3d62a105fd34 1722 DBG("Header byte:");
ashleymills 0:3d62a105fd34 1723 DBGX(" ");
ashleymills 0:3d62a105fd34 1724 CoapPDU::printBinary(*option++);
ashleymills 0:3d62a105fd34 1725 DBG(" "); DBG(" ");
ashleymills 0:3d62a105fd34 1726
ashleymills 0:3d62a105fd34 1727 // print extended delta bytes
ashleymills 0:3d62a105fd34 1728 if(extraDeltaBytes) {
ashleymills 0:3d62a105fd34 1729 DBG("Extended delta bytes (%d) in network order: ",extraDeltaBytes);
ashleymills 0:3d62a105fd34 1730 DBGX(" ");
ashleymills 0:3d62a105fd34 1731 while(extraDeltaBytes--) {
ashleymills 0:3d62a105fd34 1732 CoapPDU::printBinary(*option++); DBGX(" ");
ashleymills 0:3d62a105fd34 1733 }
ashleymills 0:3d62a105fd34 1734 } else {
ashleymills 0:3d62a105fd34 1735 DBG("No extended delta bytes");
ashleymills 0:3d62a105fd34 1736 }
ashleymills 0:3d62a105fd34 1737 DBG(" "); DBG(" ");
ashleymills 0:3d62a105fd34 1738
ashleymills 0:3d62a105fd34 1739 // print extended value length bytes
ashleymills 0:3d62a105fd34 1740 if(extraValueLengthBytes) {
ashleymills 0:3d62a105fd34 1741 DBG("Extended value length bytes (%d) in network order: ",extraValueLengthBytes);
ashleymills 0:3d62a105fd34 1742 DBGX(" ");
ashleymills 0:3d62a105fd34 1743 while(extraValueLengthBytes--) {
ashleymills 0:3d62a105fd34 1744 CoapPDU::printBinary(*option++); DBGX(" ");
ashleymills 0:3d62a105fd34 1745 }
ashleymills 0:3d62a105fd34 1746 } else {
ashleymills 0:3d62a105fd34 1747 DBG("No extended value length bytes");
ashleymills 0:3d62a105fd34 1748 }
ashleymills 0:3d62a105fd34 1749 DBG(" ");
ashleymills 0:3d62a105fd34 1750
ashleymills 0:3d62a105fd34 1751 // print option value
ashleymills 0:3d62a105fd34 1752 DBG("Option value bytes:");
ashleymills 0:3d62a105fd34 1753 for(int i=0; i<optionValueLength; i++) {
ashleymills 0:3d62a105fd34 1754 if(i%4==0) {
ashleymills 0:3d62a105fd34 1755 DBG(" ");
ashleymills 0:3d62a105fd34 1756 DBGX(" %.2d ",i);
ashleymills 0:3d62a105fd34 1757 }
ashleymills 0:3d62a105fd34 1758 CoapPDU::printBinary(*option++);
ashleymills 0:3d62a105fd34 1759 DBGX(" ");
ashleymills 0:3d62a105fd34 1760 }
ashleymills 0:3d62a105fd34 1761 DBG(" ");
ashleymills 0:3d62a105fd34 1762 }
ashleymills 0:3d62a105fd34 1763
ashleymills 0:3d62a105fd34 1764 /// Dumps the PDU header in hex.
ashleymills 0:3d62a105fd34 1765 void CoapPDU::printHex() {
ashleymills 0:3d62a105fd34 1766 printf("Hexdump dump of PDU\r\n");
ashleymills 0:3d62a105fd34 1767 printf("%.2x %.2x %.2x %.2x",_pdu[0],_pdu[1],_pdu[2],_pdu[3]);
ashleymills 0:3d62a105fd34 1768 }
ashleymills 0:3d62a105fd34 1769
ashleymills 0:3d62a105fd34 1770 /// Dumps the entire PDU in binary.
ashleymills 0:3d62a105fd34 1771 void CoapPDU::printBin() {
ashleymills 0:3d62a105fd34 1772 for(int i=0; i<_pduLength; i++) {
ashleymills 0:3d62a105fd34 1773 if(i%4==0) {
ashleymills 0:3d62a105fd34 1774 printf("\r\n");
ashleymills 0:3d62a105fd34 1775 printf("%.2d ",i);
ashleymills 0:3d62a105fd34 1776 }
ashleymills 0:3d62a105fd34 1777 CoapPDU::printBinary(_pdu[i]); printf(" ");
ashleymills 0:3d62a105fd34 1778 }
ashleymills 0:3d62a105fd34 1779 printf("\r\n");
ashleymills 0:3d62a105fd34 1780 }
ashleymills 0:3d62a105fd34 1781
ashleymills 0:3d62a105fd34 1782 /// Prints a single byte in binary.
ashleymills 0:3d62a105fd34 1783 void CoapPDU::printBinary(uint8_t b) {
ashleymills 0:3d62a105fd34 1784 printf("%d%d%d%d%d%d%d%d",
ashleymills 0:3d62a105fd34 1785 (b&0x80)&&0x01,
ashleymills 0:3d62a105fd34 1786 (b&0x40)&&0x01,
ashleymills 0:3d62a105fd34 1787 (b&0x20)&&0x01,
ashleymills 0:3d62a105fd34 1788 (b&0x10)&&0x01,
ashleymills 0:3d62a105fd34 1789 (b&0x08)&&0x01,
ashleymills 0:3d62a105fd34 1790 (b&0x04)&&0x01,
ashleymills 0:3d62a105fd34 1791 (b&0x02)&&0x01,
ashleymills 0:3d62a105fd34 1792 (b&0x01)&&0x01);
ashleymills 0:3d62a105fd34 1793 }
ashleymills 0:3d62a105fd34 1794
ashleymills 0:3d62a105fd34 1795 /// Dumps the PDU as a byte sequence to stdout.
ashleymills 0:3d62a105fd34 1796 void CoapPDU::print() {
ashleymills 0:3d62a105fd34 1797 fwrite(_pdu,1,_pduLength,stdout);
ashleymills 0:3d62a105fd34 1798 }