merge coap
This commit is contained in:
commit
45f6abd448
|
@ -30,6 +30,7 @@ SUBDIRS= \
|
||||||
platform \
|
platform \
|
||||||
libc \
|
libc \
|
||||||
lua \
|
lua \
|
||||||
|
coap \
|
||||||
mqtt \
|
mqtt \
|
||||||
u8glib \
|
u8glib \
|
||||||
smart \
|
smart \
|
||||||
|
@ -77,6 +78,7 @@ COMPONENTS_eagle.app.v6 = \
|
||||||
platform/libplatform.a \
|
platform/libplatform.a \
|
||||||
libc/liblibc.a \
|
libc/liblibc.a \
|
||||||
lua/liblua.a \
|
lua/liblua.a \
|
||||||
|
coap/coap.a \
|
||||||
mqtt/mqtt.a \
|
mqtt/mqtt.a \
|
||||||
u8glib/u8glib.a \
|
u8glib/u8glib.a \
|
||||||
smart/smart.a \
|
smart/smart.a \
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
Copyright (c) 2013 Toby Jaffey <toby@1248.io>
|
||||||
|
2015 Zeroday Hong <zeroday@nodemcu.com> nodemcu.com
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
|
@ -0,0 +1,45 @@
|
||||||
|
|
||||||
|
#############################################################
|
||||||
|
# Required variables for each makefile
|
||||||
|
# Discard this section from all parent makefiles
|
||||||
|
# Expected variables (with automatic defaults):
|
||||||
|
# CSRCS (all "C" files in the dir)
|
||||||
|
# SUBDIRS (all subdirs with a Makefile)
|
||||||
|
# GEN_LIBS - list of libs to be generated ()
|
||||||
|
# GEN_IMAGES - list of images to be generated ()
|
||||||
|
# COMPONENTS_xxx - a list of libs/objs in the form
|
||||||
|
# subdir/lib to be extracted and rolled up into
|
||||||
|
# a generated lib/image xxx.a ()
|
||||||
|
#
|
||||||
|
ifndef PDIR
|
||||||
|
GEN_LIBS = coap.a
|
||||||
|
endif
|
||||||
|
|
||||||
|
#############################################################
|
||||||
|
# Configuration i.e. compile options etc.
|
||||||
|
# Target specific stuff (defines etc.) goes in here!
|
||||||
|
# Generally values applying to a tree are captured in the
|
||||||
|
# makefile at its root level - these are then overridden
|
||||||
|
# for a subtree within the makefile rooted therein
|
||||||
|
#
|
||||||
|
#DEFINES +=
|
||||||
|
|
||||||
|
#############################################################
|
||||||
|
# Recursion Magic - Don't touch this!!
|
||||||
|
#
|
||||||
|
# Each subtree potentially has an include directory
|
||||||
|
# corresponding to the common APIs applicable to modules
|
||||||
|
# rooted at that subtree. Accordingly, the INCLUDE PATH
|
||||||
|
# of a module can only contain the include directories up
|
||||||
|
# its parent path, and not its siblings
|
||||||
|
#
|
||||||
|
# Required for each makefile to inherit from the parent
|
||||||
|
#
|
||||||
|
|
||||||
|
INCLUDES := $(INCLUDES) -I $(PDIR)include
|
||||||
|
INCLUDES += -I ./
|
||||||
|
INCLUDES += -I ../libc
|
||||||
|
INCLUDES += -I ../lua
|
||||||
|
PDIR := ../$(PDIR)
|
||||||
|
sinclude $(PDIR)Makefile
|
||||||
|
|
|
@ -0,0 +1,555 @@
|
||||||
|
#include "user_config.h"
|
||||||
|
#include "c_stdio.h"
|
||||||
|
#include "c_string.h"
|
||||||
|
#include "coap.h"
|
||||||
|
#include "uri.h"
|
||||||
|
|
||||||
|
extern void endpoint_setup(void);
|
||||||
|
extern const coap_endpoint_t endpoints[];
|
||||||
|
|
||||||
|
#ifdef COAP_DEBUG
|
||||||
|
void coap_dumpHeader(coap_header_t *hdr)
|
||||||
|
{
|
||||||
|
c_printf("Header:\n");
|
||||||
|
c_printf(" ver 0x%02X\n", hdr->ver);
|
||||||
|
c_printf(" t 0x%02X\n", hdr->ver);
|
||||||
|
c_printf(" tkl 0x%02X\n", hdr->tkl);
|
||||||
|
c_printf(" code 0x%02X\n", hdr->code);
|
||||||
|
c_printf(" id 0x%02X%02X\n", hdr->id[0], hdr->id[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void coap_dump(const uint8_t *buf, size_t buflen, bool bare)
|
||||||
|
{
|
||||||
|
if (bare)
|
||||||
|
{
|
||||||
|
while(buflen--)
|
||||||
|
c_printf("%02X%s", *buf++, (buflen > 0) ? " " : "");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
c_printf("Dump: ");
|
||||||
|
while(buflen--)
|
||||||
|
c_printf("%02X%s", *buf++, (buflen > 0) ? " " : "");
|
||||||
|
c_printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int coap_parseHeader(coap_header_t *hdr, const uint8_t *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
if (buflen < 4)
|
||||||
|
return COAP_ERR_HEADER_TOO_SHORT;
|
||||||
|
hdr->ver = (buf[0] & 0xC0) >> 6;
|
||||||
|
if (hdr->ver != 1)
|
||||||
|
return COAP_ERR_VERSION_NOT_1;
|
||||||
|
hdr->t = (buf[0] & 0x30) >> 4;
|
||||||
|
hdr->tkl = buf[0] & 0x0F;
|
||||||
|
hdr->code = buf[1];
|
||||||
|
hdr->id[0] = buf[2];
|
||||||
|
hdr->id[1] = buf[3];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int coap_buildHeader(const coap_header_t *hdr, uint8_t *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
// build header
|
||||||
|
if (buflen < 4)
|
||||||
|
return COAP_ERR_BUFFER_TOO_SMALL;
|
||||||
|
|
||||||
|
buf[0] = (hdr->ver & 0x03) << 6;
|
||||||
|
buf[0] |= (hdr->t & 0x03) << 4;
|
||||||
|
buf[0] |= (hdr->tkl & 0x0F);
|
||||||
|
buf[1] = hdr->code;
|
||||||
|
buf[2] = hdr->id[0];
|
||||||
|
buf[3] = hdr->id[1];
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
int coap_parseToken(coap_buffer_t *tokbuf, const coap_header_t *hdr, const uint8_t *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
if (hdr->tkl == 0)
|
||||||
|
{
|
||||||
|
tokbuf->p = NULL;
|
||||||
|
tokbuf->len = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (hdr->tkl <= 8)
|
||||||
|
{
|
||||||
|
if (4 + hdr->tkl > buflen)
|
||||||
|
return COAP_ERR_TOKEN_TOO_SHORT; // tok bigger than packet
|
||||||
|
tokbuf->p = buf+4; // past header
|
||||||
|
tokbuf->len = hdr->tkl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// invalid size
|
||||||
|
return COAP_ERR_TOKEN_TOO_SHORT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int coap_buildToken(const coap_buffer_t *tokbuf, const coap_header_t *hdr, uint8_t *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
// inject token
|
||||||
|
uint8_t *p;
|
||||||
|
if (buflen < 4 + hdr->tkl)
|
||||||
|
return COAP_ERR_BUFFER_TOO_SMALL;
|
||||||
|
p = buf + 4;
|
||||||
|
if ((hdr->tkl > 0) && (hdr->tkl != tokbuf->len))
|
||||||
|
return COAP_ERR_UNSUPPORTED;
|
||||||
|
|
||||||
|
if (hdr->tkl > 0)
|
||||||
|
c_memcpy(p, tokbuf->p, hdr->tkl);
|
||||||
|
|
||||||
|
// http://tools.ietf.org/html/draft-ietf-core-coap-18#section-3.1
|
||||||
|
// inject options
|
||||||
|
return hdr->tkl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// advances p
|
||||||
|
int coap_parseOption(coap_option_t *option, uint16_t *running_delta, const uint8_t **buf, size_t buflen)
|
||||||
|
{
|
||||||
|
const uint8_t *p = *buf;
|
||||||
|
uint16_t len, delta;
|
||||||
|
uint8_t headlen = 1;
|
||||||
|
|
||||||
|
if (buflen < headlen) // too small
|
||||||
|
return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
|
||||||
|
|
||||||
|
delta = (p[0] & 0xF0) >> 4;
|
||||||
|
len = p[0] & 0x0F;
|
||||||
|
|
||||||
|
// These are untested and may be buggy
|
||||||
|
if (delta == 13)
|
||||||
|
{
|
||||||
|
headlen++;
|
||||||
|
if (buflen < headlen)
|
||||||
|
return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
|
||||||
|
delta = p[1] + 13;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (delta == 14)
|
||||||
|
{
|
||||||
|
headlen += 2;
|
||||||
|
if (buflen < headlen)
|
||||||
|
return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
|
||||||
|
delta = ((p[1] << 8) | p[2]) + 269;
|
||||||
|
p+=2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (delta == 15)
|
||||||
|
return COAP_ERR_OPTION_DELTA_INVALID;
|
||||||
|
|
||||||
|
if (len == 13)
|
||||||
|
{
|
||||||
|
headlen++;
|
||||||
|
if (buflen < headlen)
|
||||||
|
return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
|
||||||
|
len = p[1] + 13;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (len == 14)
|
||||||
|
{
|
||||||
|
headlen += 2;
|
||||||
|
if (buflen < headlen)
|
||||||
|
return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
|
||||||
|
len = ((p[1] << 8) | p[2]) + 269;
|
||||||
|
p+=2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (len == 15)
|
||||||
|
return COAP_ERR_OPTION_LEN_INVALID;
|
||||||
|
|
||||||
|
if ((p + 1 + len) > (*buf + buflen))
|
||||||
|
return COAP_ERR_OPTION_TOO_BIG;
|
||||||
|
|
||||||
|
//printf("option num=%d\n", delta + *running_delta);
|
||||||
|
option->num = delta + *running_delta;
|
||||||
|
option->buf.p = p+1;
|
||||||
|
option->buf.len = len;
|
||||||
|
//coap_dump(p+1, len, false);
|
||||||
|
|
||||||
|
// advance buf
|
||||||
|
*buf = p + 1 + len;
|
||||||
|
*running_delta += delta;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://tools.ietf.org/html/draft-ietf-core-coap-18#section-3.1
|
||||||
|
int coap_parseOptionsAndPayload(coap_option_t *options, uint8_t *numOptions, coap_buffer_t *payload, const coap_header_t *hdr, const uint8_t *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
size_t optionIndex = 0;
|
||||||
|
const uint8_t *p = buf + 4 + hdr->tkl;
|
||||||
|
const uint8_t *end = buf + buflen;
|
||||||
|
int rc;
|
||||||
|
uint16_t delta = 0;
|
||||||
|
if (p > end)
|
||||||
|
return COAP_ERR_OPTION_OVERRUNS_PACKET; // out of bounds
|
||||||
|
|
||||||
|
//coap_dump(p, end - p);
|
||||||
|
|
||||||
|
// 0xFF is payload marker
|
||||||
|
while((optionIndex < *numOptions) && (p < end) && (*p != 0xFF))
|
||||||
|
{
|
||||||
|
if (0 != (rc = coap_parseOption(&options[optionIndex], &delta, &p, end-p)))
|
||||||
|
return rc;
|
||||||
|
optionIndex++;
|
||||||
|
}
|
||||||
|
*numOptions = optionIndex;
|
||||||
|
|
||||||
|
if (p+1 < end && *p == 0xFF) // payload marker
|
||||||
|
{
|
||||||
|
payload->p = p+1;
|
||||||
|
payload->len = end-(p+1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
payload->p = NULL;
|
||||||
|
payload->len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int coap_buildOptionHeader(unsigned short optDelta, size_t length, uint8_t *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
int n = 0;
|
||||||
|
uint8_t *p = buf;
|
||||||
|
uint8_t len, delta = 0;
|
||||||
|
|
||||||
|
if (buflen < 5)
|
||||||
|
return COAP_ERR_BUFFER_TOO_SMALL;
|
||||||
|
|
||||||
|
coap_option_nibble(optDelta, &delta);
|
||||||
|
coap_option_nibble(length, &len);
|
||||||
|
|
||||||
|
*p++ = (0xFF & (delta << 4 | len));
|
||||||
|
n++;
|
||||||
|
if (delta == 13)
|
||||||
|
{
|
||||||
|
*p++ = (optDelta - 13);
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (delta == 14)
|
||||||
|
{
|
||||||
|
*p++ = ((optDelta-269) >> 8);
|
||||||
|
*p++ = (0xFF & (optDelta-269));
|
||||||
|
n+=2;
|
||||||
|
}
|
||||||
|
if (len == 13)
|
||||||
|
{
|
||||||
|
*p++ = (length - 13);
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (len == 14)
|
||||||
|
{
|
||||||
|
*p++ = (length >> 8);
|
||||||
|
*p++ = (0xFF & (length-269));
|
||||||
|
n+=2;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef COAP_DEBUG
|
||||||
|
void coap_dumpOptions(coap_option_t *opts, size_t numopt)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
c_printf(" Options:\n");
|
||||||
|
for (i=0;i<numopt;i++)
|
||||||
|
{
|
||||||
|
c_printf(" 0x%02X [ ", opts[i].num);
|
||||||
|
coap_dump(opts[i].buf.p, opts[i].buf.len, true);
|
||||||
|
c_printf(" ]\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void coap_dumpPacket(coap_packet_t *pkt)
|
||||||
|
{
|
||||||
|
coap_dumpHeader(&pkt->hdr);
|
||||||
|
coap_dumpOptions(pkt->opts, pkt->numopts);
|
||||||
|
c_printf("Payload: ");
|
||||||
|
coap_dump(pkt->payload.p, pkt->payload.len, true);
|
||||||
|
c_printf("\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int coap_parse(coap_packet_t *pkt, const uint8_t *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
// coap_dump(buf, buflen, false);
|
||||||
|
|
||||||
|
if (0 != (rc = coap_parseHeader(&pkt->hdr, buf, buflen)))
|
||||||
|
return rc;
|
||||||
|
// coap_dumpHeader(&hdr);
|
||||||
|
if (0 != (rc = coap_parseToken(&pkt->tok, &pkt->hdr, buf, buflen)))
|
||||||
|
return rc;
|
||||||
|
pkt->numopts = MAXOPT;
|
||||||
|
if (0 != (rc = coap_parseOptionsAndPayload(pkt->opts, &(pkt->numopts), &(pkt->payload), &pkt->hdr, buf, buflen)))
|
||||||
|
return rc;
|
||||||
|
// coap_dumpOptions(opts, numopt);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// options are always stored consecutively, so can return a block with same option num
|
||||||
|
const coap_option_t * coap_findOptions(const coap_packet_t *pkt, uint8_t num, uint8_t *count)
|
||||||
|
{
|
||||||
|
// FIXME, options is always sorted, can find faster than this
|
||||||
|
size_t i;
|
||||||
|
const coap_option_t *first = NULL;
|
||||||
|
*count = 0;
|
||||||
|
for (i=0;i<pkt->numopts;i++)
|
||||||
|
{
|
||||||
|
if (pkt->opts[i].num == num)
|
||||||
|
{
|
||||||
|
if (NULL == first)
|
||||||
|
first = &pkt->opts[i];
|
||||||
|
(*count)++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (NULL != first)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
int coap_buffer_to_string(char *strbuf, size_t strbuflen, const coap_buffer_t *buf)
|
||||||
|
{
|
||||||
|
if (buf->len+1 > strbuflen)
|
||||||
|
return COAP_ERR_BUFFER_TOO_SMALL;
|
||||||
|
c_memcpy(strbuf, buf->p, buf->len);
|
||||||
|
strbuf[buf->len] = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int coap_build(uint8_t *buf, size_t *buflen, const coap_packet_t *pkt)
|
||||||
|
{
|
||||||
|
size_t opts_len = 0, hdr_len = 0, tok_len = 0;
|
||||||
|
size_t i;
|
||||||
|
uint8_t *p = buf;
|
||||||
|
size_t left = *buflen;
|
||||||
|
uint16_t running_delta = 0;
|
||||||
|
|
||||||
|
hdr_len = coap_buildHeader(&(pkt->hdr), buf, *buflen);
|
||||||
|
p += hdr_len;
|
||||||
|
left -= hdr_len;
|
||||||
|
|
||||||
|
tok_len = coap_buildToken(&(pkt->tok), &(pkt->hdr), buf, *buflen);
|
||||||
|
p += tok_len;
|
||||||
|
left -= tok_len;
|
||||||
|
|
||||||
|
for (i=0;i<pkt->numopts;i++)
|
||||||
|
{
|
||||||
|
uint8_t len, delta = 0;
|
||||||
|
uint16_t optDelta = 0;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
if (p-buf > *buflen)
|
||||||
|
return COAP_ERR_BUFFER_TOO_SMALL;
|
||||||
|
optDelta = pkt->opts[i].num - running_delta;
|
||||||
|
|
||||||
|
rc = coap_buildOptionHeader(optDelta, pkt->opts[i].buf.len, p, left);
|
||||||
|
p += rc;
|
||||||
|
left -= rc;
|
||||||
|
|
||||||
|
c_memcpy(p, pkt->opts[i].buf.p, pkt->opts[i].buf.len);
|
||||||
|
p += pkt->opts[i].buf.len;
|
||||||
|
left -= pkt->opts[i].buf.len;
|
||||||
|
running_delta = pkt->opts[i].num;
|
||||||
|
}
|
||||||
|
|
||||||
|
opts_len = (p - buf) - 4; // number of bytes used by options
|
||||||
|
|
||||||
|
if (pkt->payload.len > 0)
|
||||||
|
{
|
||||||
|
if (*buflen < 4 + 1 + pkt->payload.len + opts_len)
|
||||||
|
return COAP_ERR_BUFFER_TOO_SMALL;
|
||||||
|
buf[4 + opts_len] = 0xFF; // payload marker
|
||||||
|
c_memcpy(buf+5 + opts_len, pkt->payload.p, pkt->payload.len);
|
||||||
|
*buflen = opts_len + 5 + pkt->payload.len;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
*buflen = opts_len + 4;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void coap_option_nibble(uint16_t value, uint8_t *nibble)
|
||||||
|
{
|
||||||
|
if (value<13)
|
||||||
|
{
|
||||||
|
*nibble = (0xFF & value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (((uint32_t)value)<=0xFF+13)
|
||||||
|
{
|
||||||
|
*nibble = 13;
|
||||||
|
} else if (((uint32_t)value) -269 <=0xFFFF)
|
||||||
|
{
|
||||||
|
*nibble = 14;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int coap_make_response(coap_rw_buffer_t *scratch, coap_packet_t *pkt, const uint8_t *content, size_t content_len, uint8_t msgid_hi, uint8_t msgid_lo, const coap_buffer_t* tok, coap_responsecode_t rspcode, coap_content_type_t content_type)
|
||||||
|
{
|
||||||
|
pkt->hdr.ver = 0x01;
|
||||||
|
pkt->hdr.t = COAP_TYPE_ACK;
|
||||||
|
pkt->hdr.tkl = 0;
|
||||||
|
pkt->hdr.code = rspcode;
|
||||||
|
pkt->hdr.id[0] = msgid_hi;
|
||||||
|
pkt->hdr.id[1] = msgid_lo;
|
||||||
|
pkt->numopts = 0;
|
||||||
|
|
||||||
|
// need token in response
|
||||||
|
if (tok) {
|
||||||
|
pkt->hdr.tkl = tok->len;
|
||||||
|
pkt->tok = *tok;
|
||||||
|
}
|
||||||
|
|
||||||
|
// safe because 1 < MAXOPT
|
||||||
|
pkt->opts[0].num = COAP_OPTION_CONTENT_FORMAT;
|
||||||
|
pkt->opts[0].buf.p = scratch->p;
|
||||||
|
if (scratch->len < 2)
|
||||||
|
return COAP_ERR_BUFFER_TOO_SMALL;
|
||||||
|
scratch->p[0] = ((uint16_t)content_type & 0xFF00) >> 8;
|
||||||
|
scratch->p[1] = ((uint16_t)content_type & 0x00FF);
|
||||||
|
pkt->opts[0].buf.len = 2;
|
||||||
|
pkt->payload.p = content;
|
||||||
|
pkt->payload.len = content_len;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned int coap_encode_var_bytes(unsigned char *buf, unsigned int val) {
|
||||||
|
unsigned int n, i;
|
||||||
|
|
||||||
|
for (n = 0, i = val; i && n < sizeof(val); ++n)
|
||||||
|
i >>= 8;
|
||||||
|
|
||||||
|
i = n;
|
||||||
|
while (i--) {
|
||||||
|
buf[i] = val & 0xff;
|
||||||
|
val >>= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t _token_data[4]={'n','o','d','e'};
|
||||||
|
coap_buffer_t the_token = { _token_data, 4 };
|
||||||
|
static unsigned short message_id;
|
||||||
|
|
||||||
|
int coap_make_request(coap_rw_buffer_t *scratch, coap_packet_t *pkt, coap_msgtype_t t, coap_method_t m, coap_uri_t *uri, const uint8_t *payload, size_t payload_len)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
pkt->hdr.ver = 0x01;
|
||||||
|
pkt->hdr.t = t;
|
||||||
|
pkt->hdr.tkl = 0;
|
||||||
|
pkt->hdr.code = m;
|
||||||
|
pkt->hdr.id[0] = (message_id >> 8) & 0xFF; //msgid_hi;
|
||||||
|
pkt->hdr.id[1] = message_id & 0xFF; //msgid_lo;
|
||||||
|
message_id++;
|
||||||
|
NODE_DBG("message_id: %d.\n", message_id);
|
||||||
|
pkt->numopts = 0;
|
||||||
|
|
||||||
|
if (the_token.len) {
|
||||||
|
pkt->hdr.tkl = the_token.len;
|
||||||
|
pkt->tok = the_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scratch->len < 2) // TBD...
|
||||||
|
return COAP_ERR_BUFFER_TOO_SMALL;
|
||||||
|
|
||||||
|
uint8_t *saved = scratch->p;
|
||||||
|
|
||||||
|
/* split arg into Uri-* options */
|
||||||
|
// const char *addr = uri->host.s;
|
||||||
|
// if(uri->host.length && (c_strlen(addr) != uri->host.length || c_memcmp(addr, uri->host.s, uri->host.length) != 0)){
|
||||||
|
if(uri->host.length){
|
||||||
|
/* add Uri-Host */
|
||||||
|
// addr is destination address
|
||||||
|
pkt->opts[pkt->numopts].num = COAP_OPTION_URI_HOST;
|
||||||
|
pkt->opts[pkt->numopts].buf.p = uri->host.s;
|
||||||
|
pkt->opts[pkt->numopts].buf.len = uri->host.length;
|
||||||
|
pkt->numopts++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uri->port != COAP_DEFAULT_PORT) {
|
||||||
|
pkt->opts[pkt->numopts].num = COAP_OPTION_URI_PORT;
|
||||||
|
res = coap_encode_var_bytes(scratch->p, uri->port);
|
||||||
|
pkt->opts[pkt->numopts].buf.len = res;
|
||||||
|
pkt->opts[pkt->numopts].buf.p = scratch->p;
|
||||||
|
scratch->p += res;
|
||||||
|
scratch->len -= res;
|
||||||
|
pkt->numopts++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uri->path.length) {
|
||||||
|
res = coap_split_path(scratch, pkt, uri->path.s, uri->path.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uri->query.length) {
|
||||||
|
res = coap_split_query(scratch, pkt, uri->query.s, uri->query.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
pkt->payload.p = payload;
|
||||||
|
pkt->payload.len = payload_len;
|
||||||
|
scratch->p = saved; // save back the pointer.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME, if this looked in the table at the path before the method then
|
||||||
|
// it could more easily return 405 errors
|
||||||
|
int coap_handle_req(coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt)
|
||||||
|
{
|
||||||
|
const coap_option_t *opt;
|
||||||
|
int i;
|
||||||
|
uint8_t count;
|
||||||
|
const coap_endpoint_t *ep = endpoints;
|
||||||
|
|
||||||
|
while(NULL != ep->handler)
|
||||||
|
{
|
||||||
|
if (ep->method != inpkt->hdr.code)
|
||||||
|
goto next;
|
||||||
|
if (NULL != (opt = coap_findOptions(inpkt, COAP_OPTION_URI_PATH, &count)))
|
||||||
|
{
|
||||||
|
// if (count != ep->path->count)
|
||||||
|
if ((count != ep->path->count ) && (count != ep->path->count + 1)) // +1 for /f/[function], /v/[variable]
|
||||||
|
goto next;
|
||||||
|
for (i=0;i<ep->path->count;i++)
|
||||||
|
{
|
||||||
|
if (opt[i].buf.len != c_strlen(ep->path->elems[i]))
|
||||||
|
goto next;
|
||||||
|
if (0 != c_memcmp(ep->path->elems[i], opt[i].buf.p, opt[i].buf.len))
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
// pre-path match!
|
||||||
|
if (count==ep->path->count+1 && ep->user_entry == NULL)
|
||||||
|
goto next;
|
||||||
|
return ep->handler(ep, scratch, inpkt, outpkt, inpkt->hdr.id[0], inpkt->hdr.id[1]);
|
||||||
|
}
|
||||||
|
next:
|
||||||
|
ep++;
|
||||||
|
}
|
||||||
|
|
||||||
|
coap_make_response(scratch, outpkt, NULL, 0, inpkt->hdr.id[0], inpkt->hdr.id[1], &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void coap_setup(void)
|
||||||
|
{
|
||||||
|
message_id = (unsigned short)rand(); // calculate only once
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int
|
||||||
|
check_token(coap_packet_t *pkt) {
|
||||||
|
return pkt->tok.len == the_token.len && c_memcmp(pkt->tok.p, the_token.p, the_token.len) == 0;
|
||||||
|
}
|
|
@ -0,0 +1,186 @@
|
||||||
|
#ifndef COAP_H
|
||||||
|
#define COAP_H 1
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "c_stdint.h"
|
||||||
|
#include "c_stddef.h"
|
||||||
|
#include "lualib.h"
|
||||||
|
#include "lauxlib.h"
|
||||||
|
|
||||||
|
#define MAXOPT 16
|
||||||
|
#define MAX_MESSAGE_SIZE 1152
|
||||||
|
#define MAX_PAYLOAD_SIZE 1024
|
||||||
|
#define MAX_REQUEST_SIZE 576
|
||||||
|
#define MAX_REQ_SCRATCH_SIZE 60
|
||||||
|
|
||||||
|
#define COAP_RESPONSE_CLASS(C) (((C) >> 5) & 0xFF)
|
||||||
|
|
||||||
|
// http://tools.ietf.org/html/draft-ietf-core-coap-18#section-3
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t ver;
|
||||||
|
uint8_t t;
|
||||||
|
uint8_t tkl;
|
||||||
|
uint8_t code;
|
||||||
|
uint8_t id[2];
|
||||||
|
} coap_header_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
const uint8_t *p;
|
||||||
|
size_t len;
|
||||||
|
} coap_buffer_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t *p;
|
||||||
|
size_t len;
|
||||||
|
} coap_rw_buffer_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t num;
|
||||||
|
coap_buffer_t buf;
|
||||||
|
} coap_option_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
coap_header_t hdr;
|
||||||
|
coap_buffer_t tok;
|
||||||
|
uint8_t numopts;
|
||||||
|
coap_option_t opts[MAXOPT];
|
||||||
|
coap_buffer_t payload;
|
||||||
|
coap_rw_buffer_t scratch; // scratch->p = malloc(...) , and free it when done.
|
||||||
|
} coap_packet_t;
|
||||||
|
|
||||||
|
/////////////////////////////////////////
|
||||||
|
|
||||||
|
//http://tools.ietf.org/html/draft-ietf-core-coap-18#section-12.2
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
COAP_OPTION_IF_MATCH = 1,
|
||||||
|
COAP_OPTION_URI_HOST = 3,
|
||||||
|
COAP_OPTION_ETAG = 4,
|
||||||
|
COAP_OPTION_IF_NONE_MATCH = 5,
|
||||||
|
COAP_OPTION_OBSERVE = 6,
|
||||||
|
COAP_OPTION_URI_PORT = 7,
|
||||||
|
COAP_OPTION_LOCATION_PATH = 8,
|
||||||
|
COAP_OPTION_URI_PATH = 11,
|
||||||
|
COAP_OPTION_CONTENT_FORMAT = 12,
|
||||||
|
COAP_OPTION_MAX_AGE = 14,
|
||||||
|
COAP_OPTION_URI_QUERY = 15,
|
||||||
|
COAP_OPTION_ACCEPT = 17,
|
||||||
|
COAP_OPTION_LOCATION_QUERY = 20,
|
||||||
|
COAP_OPTION_PROXY_URI = 35,
|
||||||
|
COAP_OPTION_PROXY_SCHEME = 39
|
||||||
|
} coap_option_num_t;
|
||||||
|
|
||||||
|
//http://tools.ietf.org/html/draft-ietf-core-coap-18#section-12.1.1
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
COAP_METHOD_GET = 1,
|
||||||
|
COAP_METHOD_POST = 2,
|
||||||
|
COAP_METHOD_PUT = 3,
|
||||||
|
COAP_METHOD_DELETE = 4
|
||||||
|
} coap_method_t;
|
||||||
|
|
||||||
|
//http://tools.ietf.org/html/draft-ietf-core-coap-18#section-12.1.1
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
COAP_TYPE_CON = 0,
|
||||||
|
COAP_TYPE_NONCON = 1,
|
||||||
|
COAP_TYPE_ACK = 2,
|
||||||
|
COAP_TYPE_RESET = 3
|
||||||
|
} coap_msgtype_t;
|
||||||
|
|
||||||
|
//http://tools.ietf.org/html/draft-ietf-core-coap-18#section-5.2
|
||||||
|
//http://tools.ietf.org/html/draft-ietf-core-coap-18#section-12.1.2
|
||||||
|
#define MAKE_RSPCODE(clas, det) ((clas << 5) | (det))
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
COAP_RSPCODE_CONTENT = MAKE_RSPCODE(2, 5),
|
||||||
|
COAP_RSPCODE_NOT_FOUND = MAKE_RSPCODE(4, 4),
|
||||||
|
COAP_RSPCODE_BAD_REQUEST = MAKE_RSPCODE(4, 0),
|
||||||
|
COAP_RSPCODE_CHANGED = MAKE_RSPCODE(2, 4)
|
||||||
|
} coap_responsecode_t;
|
||||||
|
|
||||||
|
//http://tools.ietf.org/html/draft-ietf-core-coap-18#section-12.3
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
COAP_CONTENTTYPE_NONE = -1, // bodge to allow us not to send option block
|
||||||
|
COAP_CONTENTTYPE_TEXT_PLAIN = 0,
|
||||||
|
COAP_CONTENTTYPE_APPLICATION_LINKFORMAT = 40,
|
||||||
|
} coap_content_type_t;
|
||||||
|
|
||||||
|
///////////////////////
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
COAP_ERR_NONE = 0,
|
||||||
|
COAP_ERR_HEADER_TOO_SHORT = 1,
|
||||||
|
COAP_ERR_VERSION_NOT_1 = 2,
|
||||||
|
COAP_ERR_TOKEN_TOO_SHORT = 3,
|
||||||
|
COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER = 4,
|
||||||
|
COAP_ERR_OPTION_TOO_SHORT = 5,
|
||||||
|
COAP_ERR_OPTION_OVERRUNS_PACKET = 6,
|
||||||
|
COAP_ERR_OPTION_TOO_BIG = 7,
|
||||||
|
COAP_ERR_OPTION_LEN_INVALID = 8,
|
||||||
|
COAP_ERR_BUFFER_TOO_SMALL = 9,
|
||||||
|
COAP_ERR_UNSUPPORTED = 10,
|
||||||
|
COAP_ERR_OPTION_DELTA_INVALID = 11,
|
||||||
|
} coap_error_t;
|
||||||
|
|
||||||
|
///////////////////////
|
||||||
|
typedef struct coap_endpoint_t coap_endpoint_t;
|
||||||
|
|
||||||
|
typedef int (*coap_endpoint_func)(const coap_endpoint_t *ep, coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt, uint8_t id_hi, uint8_t id_lo);
|
||||||
|
#define MAX_SEGMENTS 3 // 2 = /foo/bar, 3 = /foo/bar/baz
|
||||||
|
#define MAX_SEGMENTS_SIZE 16
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
const char *elems[MAX_SEGMENTS];
|
||||||
|
} coap_endpoint_path_t;
|
||||||
|
|
||||||
|
typedef struct coap_luser_entry coap_luser_entry;
|
||||||
|
|
||||||
|
struct coap_luser_entry{
|
||||||
|
lua_State *L;
|
||||||
|
// int ref;
|
||||||
|
// char name[MAX_SEGMENTS_SIZE+1]; // +1 for string '\0'
|
||||||
|
const char *name;
|
||||||
|
coap_luser_entry *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct coap_endpoint_t
|
||||||
|
{
|
||||||
|
coap_method_t method;
|
||||||
|
coap_endpoint_func handler;
|
||||||
|
const coap_endpoint_path_t *path;
|
||||||
|
const char *core_attr;
|
||||||
|
coap_luser_entry *user_entry;
|
||||||
|
};
|
||||||
|
|
||||||
|
///////////////////////
|
||||||
|
void coap_dumpPacket(coap_packet_t *pkt);
|
||||||
|
int coap_parse(coap_packet_t *pkt, const uint8_t *buf, size_t buflen);
|
||||||
|
int coap_buffer_to_string(char *strbuf, size_t strbuflen, const coap_buffer_t *buf);
|
||||||
|
const coap_option_t *coap_findOptions(const coap_packet_t *pkt, uint8_t num, uint8_t *count);
|
||||||
|
int coap_build(uint8_t *buf, size_t *buflen, const coap_packet_t *pkt);
|
||||||
|
void coap_dump(const uint8_t *buf, size_t buflen, bool bare);
|
||||||
|
int coap_make_response(coap_rw_buffer_t *scratch, coap_packet_t *pkt, const uint8_t *content, size_t content_len, uint8_t msgid_hi, uint8_t msgid_lo, const coap_buffer_t* tok, coap_responsecode_t rspcode, coap_content_type_t content_type);
|
||||||
|
int coap_handle_req(coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt);
|
||||||
|
void coap_option_nibble(uint16_t value, uint8_t *nibble);
|
||||||
|
void coap_setup(void);
|
||||||
|
void endpoint_setup(void);
|
||||||
|
|
||||||
|
int coap_buildOptionHeader(unsigned short optDelta, size_t length, uint8_t *buf, size_t buflen);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,67 @@
|
||||||
|
#include "user_config.h"
|
||||||
|
#include "c_types.h"
|
||||||
|
|
||||||
|
#include "coap.h"
|
||||||
|
#include "hash.h"
|
||||||
|
#include "node.h"
|
||||||
|
|
||||||
|
extern coap_queue_t *gQueue;
|
||||||
|
|
||||||
|
void coap_client_response_handler(char *data, unsigned short len, unsigned short size, const uint32_t ip, const uint32_t port)
|
||||||
|
{
|
||||||
|
NODE_DBG("coap_client_response_handler is called.\n");
|
||||||
|
coap_packet_t pkt;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (0 != (rc = coap_parse(&pkt, data, len))){
|
||||||
|
NODE_DBG("Bad packet rc=%d\n", rc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef COAP_DEBUG
|
||||||
|
coap_dumpPacket(&pkt);
|
||||||
|
#endif
|
||||||
|
/* check if this is a response to our original request */
|
||||||
|
if (!check_token(&pkt)) {
|
||||||
|
/* drop if this was just some message, or send RST in case of notification */
|
||||||
|
if (pkt.hdr.t == COAP_TYPE_CON || pkt.hdr.t == COAP_TYPE_NONCON){
|
||||||
|
// coap_send_rst(pkt); // send RST response
|
||||||
|
// or, just ignore it.
|
||||||
|
}
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pkt.hdr.t == COAP_TYPE_RESET) {
|
||||||
|
NODE_DBG("got RST\n");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
coap_tid_t id = COAP_INVALID_TID;
|
||||||
|
coap_transaction_id(ip, port, &pkt, &id);
|
||||||
|
/* transaction done, remove the node from queue */
|
||||||
|
// stop timer
|
||||||
|
coap_timer_stop();
|
||||||
|
// remove the node
|
||||||
|
coap_remove_node(&gQueue, id);
|
||||||
|
// calculate time elapsed
|
||||||
|
coap_timer_update(&gQueue);
|
||||||
|
coap_timer_start(&gQueue);
|
||||||
|
|
||||||
|
if (COAP_RESPONSE_CLASS(pkt.hdr.code) == 2)
|
||||||
|
{
|
||||||
|
/* There is no block option set, just read the data and we are done. */
|
||||||
|
NODE_DBG("%d.%02d\t", (pkt.hdr.code >> 5), pkt.hdr.code & 0x1F);
|
||||||
|
NODE_DBG((char *)(pkt.payload.p));
|
||||||
|
}
|
||||||
|
else if (COAP_RESPONSE_CLASS(pkt.hdr.code) >= 4)
|
||||||
|
{
|
||||||
|
NODE_DBG("%d.%02d\t", (pkt.hdr.code >> 5), pkt.hdr.code & 0x1F);
|
||||||
|
NODE_DBG((char *)(pkt.payload.p));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
if(!gQueue){ // if there is no node pending in the queue, disconnect from host.
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
#ifndef _COAP_SERVER_H
|
||||||
|
#define _COAP_SERVER_H 1
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
size_t coap_server_respond(char *data, unsigned short len, unsigned short size);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,71 @@
|
||||||
|
#include "c_string.h"
|
||||||
|
#include "coap_io.h"
|
||||||
|
#include "node.h"
|
||||||
|
#include "espconn.h"
|
||||||
|
#include "coap_timer.h"
|
||||||
|
|
||||||
|
extern coap_queue_t *gQueue;
|
||||||
|
|
||||||
|
/* releases space allocated by PDU if free_pdu is set */
|
||||||
|
coap_tid_t coap_send(struct espconn *pesp_conn, coap_pdu_t *pdu) {
|
||||||
|
coap_tid_t id = COAP_INVALID_TID;
|
||||||
|
uint32_t ip = 0, port = 0;
|
||||||
|
if ( !pesp_conn || !pdu )
|
||||||
|
return id;
|
||||||
|
|
||||||
|
espconn_sent(pesp_conn, (unsigned char *)(pdu->msg.p), pdu->msg.len);
|
||||||
|
|
||||||
|
if(pesp_conn->type == ESPCONN_TCP){
|
||||||
|
c_memcpy(&ip, pesp_conn->proto.tcp->remote_ip, sizeof(ip));
|
||||||
|
port = pesp_conn->proto.tcp->remote_port;
|
||||||
|
}else{
|
||||||
|
c_memcpy(&ip, pesp_conn->proto.udp->remote_ip, sizeof(ip));
|
||||||
|
port = pesp_conn->proto.udp->remote_port;
|
||||||
|
}
|
||||||
|
coap_transaction_id(ip, port, pdu->pkt, &id);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
coap_tid_t coap_send_confirmed(struct espconn *pesp_conn, coap_pdu_t *pdu) {
|
||||||
|
coap_queue_t *node;
|
||||||
|
coap_tick_t diff;
|
||||||
|
uint32_t r;
|
||||||
|
|
||||||
|
node = coap_new_node();
|
||||||
|
if (!node) {
|
||||||
|
NODE_DBG("coap_send_confirmed: insufficient memory\n");
|
||||||
|
return COAP_INVALID_TID;
|
||||||
|
}
|
||||||
|
|
||||||
|
node->retransmit_cnt = 0;
|
||||||
|
node->id = coap_send(pesp_conn, pdu);
|
||||||
|
if (COAP_INVALID_TID == node->id) {
|
||||||
|
NODE_DBG("coap_send_confirmed: error sending pdu\n");
|
||||||
|
coap_free_node(node);
|
||||||
|
return COAP_INVALID_TID;
|
||||||
|
}
|
||||||
|
r = rand();
|
||||||
|
|
||||||
|
/* add randomized RESPONSE_TIMEOUT to determine retransmission timeout */
|
||||||
|
node->timeout = COAP_DEFAULT_RESPONSE_TIMEOUT * COAP_TICKS_PER_SECOND +
|
||||||
|
(COAP_DEFAULT_RESPONSE_TIMEOUT >> 1) *
|
||||||
|
((COAP_TICKS_PER_SECOND * (r & 0xFF)) >> 8);
|
||||||
|
|
||||||
|
node->pconn = pesp_conn;
|
||||||
|
node->pdu = pdu;
|
||||||
|
|
||||||
|
/* Set timer for pdu retransmission. If this is the first element in
|
||||||
|
* the retransmission queue, the base time is set to the current
|
||||||
|
* time and the retransmission time is node->timeout. If there is
|
||||||
|
* already an entry in the sendqueue, we must check if this node is
|
||||||
|
* to be retransmitted earlier. Therefore, node->timeout is first
|
||||||
|
* normalized to the timeout and then inserted into the queue with
|
||||||
|
* an adjusted relative time.
|
||||||
|
*/
|
||||||
|
coap_timer_stop();
|
||||||
|
coap_timer_update(&gQueue);
|
||||||
|
node->t = node->timeout;
|
||||||
|
coap_insert_node(&gQueue, node);
|
||||||
|
coap_timer_start(&gQueue);
|
||||||
|
return node->id;
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef _COAP_IO_H
|
||||||
|
#define _COAP_IO_H 1
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "espconn.h"
|
||||||
|
#include "pdu.h"
|
||||||
|
#include "hash.h"
|
||||||
|
|
||||||
|
coap_tid_t coap_send(struct espconn *pesp_conn, coap_pdu_t *pdu);
|
||||||
|
|
||||||
|
coap_tid_t coap_send_confirmed(struct espconn *pesp_conn, coap_pdu_t *pdu);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,53 @@
|
||||||
|
#include "user_config.h"
|
||||||
|
#include "c_types.h"
|
||||||
|
|
||||||
|
#include "coap.h"
|
||||||
|
|
||||||
|
size_t coap_server_respond(char *data, unsigned short len, unsigned short size)
|
||||||
|
{
|
||||||
|
NODE_DBG("coap_server_respond is called.\n");
|
||||||
|
if(len>size){
|
||||||
|
NODE_DBG("len:%d, size:%d\n",len,size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
size_t rsplen = size;
|
||||||
|
coap_packet_t pkt;
|
||||||
|
uint8_t scratch_raw[4];
|
||||||
|
coap_rw_buffer_t scratch_buf = {scratch_raw, sizeof(scratch_raw)};
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
#ifdef COAP_DEBUG
|
||||||
|
NODE_DBG("Received: ");
|
||||||
|
coap_dump(data, len, true);
|
||||||
|
NODE_DBG("\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (0 != (rc = coap_parse(&pkt, data, len))){
|
||||||
|
NODE_DBG("Bad packet rc=%d\n", rc);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
coap_packet_t rsppkt;
|
||||||
|
#ifdef COAP_DEBUG
|
||||||
|
coap_dumpPacket(&pkt);
|
||||||
|
#endif
|
||||||
|
coap_handle_req(&scratch_buf, &pkt, &rsppkt);
|
||||||
|
if (0 != (rc = coap_build(data, &rsplen, &rsppkt))){
|
||||||
|
NODE_DBG("coap_build failed rc=%d\n", rc);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef COAP_DEBUG
|
||||||
|
NODE_DBG("Responding: ");
|
||||||
|
coap_dump(data, rsplen, true);
|
||||||
|
NODE_DBG("\n");
|
||||||
|
#endif
|
||||||
|
#ifdef COAP_DEBUG
|
||||||
|
coap_dumpPacket(&rsppkt);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return rsplen;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
#ifndef _COAP_SERVER_H
|
||||||
|
#define _COAP_SERVER_H 1
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
size_t coap_server_respond(char *data, unsigned short len, unsigned short size);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,78 @@
|
||||||
|
#include "node.h"
|
||||||
|
#include "coap_timer.h"
|
||||||
|
#include "os_type.h"
|
||||||
|
|
||||||
|
static os_timer_t coap_timer;
|
||||||
|
static coap_tick_t basetime = 0;
|
||||||
|
|
||||||
|
void coap_timer_elapsed(coap_tick_t *diff){
|
||||||
|
coap_tick_t now = system_get_time() / 1000; // coap_tick_t is in ms. also sys_timer
|
||||||
|
if(now>=basetime){
|
||||||
|
*diff = now-basetime;
|
||||||
|
} else {
|
||||||
|
*diff = now + SYS_TIME_MAX -basetime;
|
||||||
|
}
|
||||||
|
basetime = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
void coap_timer_tick(void *arg){
|
||||||
|
if( !arg )
|
||||||
|
return;
|
||||||
|
coap_queue_t **queue = (coap_queue_t **)arg;
|
||||||
|
if( !(*queue) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
coap_queue_t *node = coap_pop_next( queue );
|
||||||
|
/* re-initialize timeout when maximum number of retransmissions are not reached yet */
|
||||||
|
if (node->retransmit_cnt < COAP_DEFAULT_MAX_RETRANSMIT) {
|
||||||
|
node->retransmit_cnt++;
|
||||||
|
node->t = node->timeout << node->retransmit_cnt;
|
||||||
|
|
||||||
|
NODE_DBG("** retransmission #%d of transaction %d\n",
|
||||||
|
node->retransmit_cnt, (((uint16_t)(node->pdu->pkt->hdr.id[0]))<<8)+node->pdu->pkt->hdr.id[1]);
|
||||||
|
node->id = coap_send(node->pconn, node->pdu);
|
||||||
|
if (COAP_INVALID_TID == node->id) {
|
||||||
|
NODE_DBG("retransmission: error sending pdu\n");
|
||||||
|
coap_delete_node(node);
|
||||||
|
} else {
|
||||||
|
coap_insert_node(queue, node);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* And finally delete the node */
|
||||||
|
coap_delete_node( node );
|
||||||
|
}
|
||||||
|
|
||||||
|
coap_timer_start(queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void coap_timer_setup(coap_queue_t ** queue, coap_tick_t t){
|
||||||
|
os_timer_disarm(&coap_timer);
|
||||||
|
os_timer_setfn(&coap_timer, (os_timer_func_t *)coap_timer_tick, queue);
|
||||||
|
os_timer_arm(&coap_timer, t, 0); // no repeat
|
||||||
|
}
|
||||||
|
|
||||||
|
void coap_timer_stop(void){
|
||||||
|
os_timer_disarm(&coap_timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void coap_timer_update(coap_queue_t ** queue){
|
||||||
|
if (!queue)
|
||||||
|
return;
|
||||||
|
coap_tick_t diff = 0;
|
||||||
|
coap_queue_t *first = *queue;
|
||||||
|
coap_timer_elapsed(&diff); // update: basetime = now, diff = now - oldbase, means time elapsed
|
||||||
|
if (first) {
|
||||||
|
// diff ms time is elapsed, re-calculate the first node->t
|
||||||
|
if (first->t >= diff){
|
||||||
|
first->t -= diff;
|
||||||
|
} else {
|
||||||
|
first->t = 0; // when timer enabled, time out almost immediately
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void coap_timer_start(coap_queue_t ** queue){
|
||||||
|
if(*queue){ // if there is node in the queue, set timeout to its ->t.
|
||||||
|
coap_timer_setup(queue, (*queue)->t);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
#ifndef _COAP_TIMER_H
|
||||||
|
#define _COAP_TIMER_H 1
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "node.h"
|
||||||
|
|
||||||
|
#define SYS_TIME_MAX (0xFFFFFFFF / 1000)
|
||||||
|
|
||||||
|
#define COAP_DEFAULT_RESPONSE_TIMEOUT 2 /* response timeout in seconds */
|
||||||
|
#define COAP_DEFAULT_MAX_RETRANSMIT 4 /* max number of retransmissions */
|
||||||
|
#define COAP_TICKS_PER_SECOND 1000 // ms
|
||||||
|
#define DEFAULT_MAX_TRANSMIT_WAIT 90
|
||||||
|
|
||||||
|
void coap_timer_elapsed(coap_tick_t *diff);
|
||||||
|
|
||||||
|
void coap_timer_setup(coap_queue_t ** queue, coap_tick_t t);
|
||||||
|
|
||||||
|
void coap_timer_stop(void);
|
||||||
|
|
||||||
|
void coap_timer_update(coap_queue_t ** queue);
|
||||||
|
|
||||||
|
void coap_timer_start(coap_queue_t ** queue);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,282 @@
|
||||||
|
#include "c_stdio.h"
|
||||||
|
#include "c_string.h"
|
||||||
|
#include "coap.h"
|
||||||
|
|
||||||
|
#include "lua.h"
|
||||||
|
#include "lauxlib.h"
|
||||||
|
#include "lualib.h"
|
||||||
|
|
||||||
|
#include "os_type.h"
|
||||||
|
|
||||||
|
static char rsp[MAX_PAYLOAD_SIZE] = "";
|
||||||
|
const uint16_t rsplen = MAX_PAYLOAD_SIZE;
|
||||||
|
void build_well_known_rsp(void);
|
||||||
|
|
||||||
|
void endpoint_setup(void)
|
||||||
|
{
|
||||||
|
coap_setup();
|
||||||
|
build_well_known_rsp();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const coap_endpoint_path_t path_well_known_core = {2, {".well-known", "core"}};
|
||||||
|
static int handle_get_well_known_core(const coap_endpoint_t *ep, coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt, uint8_t id_hi, uint8_t id_lo)
|
||||||
|
{
|
||||||
|
return coap_make_response(scratch, outpkt, (const uint8_t *)rsp, c_strlen(rsp), id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_APPLICATION_LINKFORMAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const coap_endpoint_path_t path_variable = {2, {"v1", "v"}};
|
||||||
|
static int handle_get_variable(const coap_endpoint_t *ep, coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt, uint8_t id_hi, uint8_t id_lo)
|
||||||
|
{
|
||||||
|
const coap_option_t *opt;
|
||||||
|
uint8_t count;
|
||||||
|
if (NULL != (opt = coap_findOptions(inpkt, COAP_OPTION_URI_PATH, &count)))
|
||||||
|
{
|
||||||
|
if ((count != ep->path->count ) && (count != ep->path->count + 1)) // +1 for /f/[function], /v/[variable]
|
||||||
|
{
|
||||||
|
NODE_DBG("should never happen.\n");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
if (count == ep->path->count + 1)
|
||||||
|
{
|
||||||
|
coap_luser_entry *h = ep->user_entry->next; // ->next: skip the first entry(head)
|
||||||
|
while(NULL != h){
|
||||||
|
if (opt[count-1].buf.len != c_strlen(h->name))
|
||||||
|
{
|
||||||
|
h = h->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (0 == c_memcmp(h->name, opt[count-1].buf.p, opt[count-1].buf.len))
|
||||||
|
{
|
||||||
|
NODE_DBG("/v1/v/");
|
||||||
|
NODE_DBG((char *)h->name);
|
||||||
|
NODE_DBG(" match.\n");
|
||||||
|
if(h->L == NULL)
|
||||||
|
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
|
||||||
|
if(c_strlen(h->name))
|
||||||
|
{
|
||||||
|
lua_getglobal(h->L, h->name);
|
||||||
|
if (!lua_isnumber(h->L, -1)) {
|
||||||
|
NODE_DBG ("should be a number.\n");
|
||||||
|
lua_pop(h->L, 1);
|
||||||
|
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
|
||||||
|
} else {
|
||||||
|
const char *res = lua_tostring(h->L,-1);
|
||||||
|
lua_pop(h->L, 1);
|
||||||
|
return coap_make_response(scratch, outpkt, (const uint8_t *)res, c_strlen(res), id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
h = h->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
NODE_DBG("/v1/v match.\n");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NODE_DBG("none match.\n");
|
||||||
|
end:
|
||||||
|
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const coap_endpoint_path_t path_function = {2, {"v1", "f"}};
|
||||||
|
static int handle_post_function(const coap_endpoint_t *ep, coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt, uint8_t id_hi, uint8_t id_lo)
|
||||||
|
{
|
||||||
|
const coap_option_t *opt;
|
||||||
|
uint8_t count;
|
||||||
|
if (NULL != (opt = coap_findOptions(inpkt, COAP_OPTION_URI_PATH, &count)))
|
||||||
|
{
|
||||||
|
if ((count != ep->path->count ) && (count != ep->path->count + 1)) // +1 for /f/[function], /v/[variable]
|
||||||
|
{
|
||||||
|
NODE_DBG("should never happen.\n");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
if (count == ep->path->count + 1)
|
||||||
|
{
|
||||||
|
coap_luser_entry *h = ep->user_entry->next; // ->next: skip the first entry(head)
|
||||||
|
while(NULL != h){
|
||||||
|
if (opt[count-1].buf.len != c_strlen(h->name))
|
||||||
|
{
|
||||||
|
h = h->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (0 == c_memcmp(h->name, opt[count-1].buf.p, opt[count-1].buf.len))
|
||||||
|
{
|
||||||
|
NODE_DBG("/v1/f/");
|
||||||
|
NODE_DBG((char *)h->name);
|
||||||
|
NODE_DBG(" match.\n");
|
||||||
|
|
||||||
|
if(h->L == NULL)
|
||||||
|
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
|
||||||
|
|
||||||
|
if(c_strlen(h->name))
|
||||||
|
{
|
||||||
|
lua_getglobal(h->L, h->name);
|
||||||
|
if (lua_type(h->L, -1) != LUA_TFUNCTION) {
|
||||||
|
NODE_DBG ("should be a function\n");
|
||||||
|
lua_pop(h->L, 1);
|
||||||
|
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
|
||||||
|
} else {
|
||||||
|
lua_pushlstring(h->L, inpkt->payload.p, inpkt->payload.len); // make sure payload.p is filled with '\0' after payload.len, or use lua_pushlstring
|
||||||
|
lua_call(h->L, 1, 1);
|
||||||
|
if (!lua_isnil(h->L, -1)){ /* get return? */
|
||||||
|
if( lua_isstring(h->L, -1) ) // deal with the return string
|
||||||
|
{
|
||||||
|
size_t len = 0;
|
||||||
|
const char *ret = luaL_checklstring( h->L, -1, &len );
|
||||||
|
if(len > MAX_PAYLOAD_SIZE){
|
||||||
|
luaL_error( h->L, "return string:<MAX_PAYLOAD_SIZE" );
|
||||||
|
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
|
||||||
|
}
|
||||||
|
NODE_DBG((char *)ret);
|
||||||
|
NODE_DBG("\n");
|
||||||
|
return coap_make_response(scratch, outpkt, ret, len, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
h = h->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
NODE_DBG("/v1/f match.\n");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NODE_DBG("none match.\n");
|
||||||
|
end:
|
||||||
|
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern lua_Load gLoad;
|
||||||
|
extern os_timer_t lua_timer;
|
||||||
|
extern void dojob(lua_Load *load);
|
||||||
|
static const coap_endpoint_path_t path_command = {2, {"v1", "c"}};
|
||||||
|
static int handle_post_command(const coap_endpoint_t *ep, coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt, uint8_t id_hi, uint8_t id_lo)
|
||||||
|
{
|
||||||
|
if (inpkt->payload.len == 0)
|
||||||
|
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_BAD_REQUEST, COAP_CONTENTTYPE_TEXT_PLAIN);
|
||||||
|
if (inpkt->payload.len > 0)
|
||||||
|
{
|
||||||
|
lua_Load *load = &gLoad;
|
||||||
|
if(load->line_position == 0){
|
||||||
|
coap_buffer_to_string(load->line, load->len,&inpkt->payload);
|
||||||
|
load->line_position = c_strlen(load->line)+1;
|
||||||
|
// load->line[load->line_position-1] = '\n';
|
||||||
|
// load->line[load->line_position] = 0;
|
||||||
|
// load->line_position++;
|
||||||
|
load->done = 1;
|
||||||
|
NODE_DBG("Get command:\n");
|
||||||
|
NODE_DBG(load->line); // buggy here
|
||||||
|
NODE_DBG("\nResult(if any):\n");
|
||||||
|
os_timer_disarm(&lua_timer);
|
||||||
|
os_timer_setfn(&lua_timer, (os_timer_func_t *)dojob, load);
|
||||||
|
os_timer_arm(&lua_timer, READLINE_INTERVAL, 0); // no repeat
|
||||||
|
}
|
||||||
|
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t id = 0;
|
||||||
|
static const coap_endpoint_path_t path_id = {2, {"v1", "id"}};
|
||||||
|
static int handle_get_id(const coap_endpoint_t *ep, coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt, uint8_t id_hi, uint8_t id_lo)
|
||||||
|
{
|
||||||
|
id = system_get_chip_id();
|
||||||
|
return coap_make_response(scratch, outpkt, (const uint8_t *)(&id), sizeof(uint32_t), id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
coap_luser_entry var_head = {NULL,NULL,NULL};
|
||||||
|
coap_luser_entry *variable_entry = &var_head;
|
||||||
|
|
||||||
|
coap_luser_entry func_head = {NULL,NULL,NULL};
|
||||||
|
coap_luser_entry *function_entry = &func_head;
|
||||||
|
|
||||||
|
const coap_endpoint_t endpoints[] =
|
||||||
|
{
|
||||||
|
{COAP_METHOD_GET, handle_get_well_known_core, &path_well_known_core, "ct=40", NULL},
|
||||||
|
{COAP_METHOD_GET, handle_get_variable, &path_variable, "ct=0", &var_head},
|
||||||
|
{COAP_METHOD_POST, handle_post_function, &path_function, NULL, &func_head},
|
||||||
|
{COAP_METHOD_POST, handle_post_command, &path_command, NULL, NULL},
|
||||||
|
{COAP_METHOD_GET, handle_get_id, &path_id, "ct=0", NULL},
|
||||||
|
{(coap_method_t)0, NULL, NULL, NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
void build_well_known_rsp(void)
|
||||||
|
{
|
||||||
|
const coap_endpoint_t *ep = endpoints;
|
||||||
|
int i;
|
||||||
|
uint16_t len = rsplen;
|
||||||
|
|
||||||
|
c_memset(rsp, 0, sizeof(rsp));
|
||||||
|
|
||||||
|
len--; // Null-terminated string
|
||||||
|
|
||||||
|
while(NULL != ep->handler)
|
||||||
|
{
|
||||||
|
if (NULL == ep->core_attr) {
|
||||||
|
ep++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (NULL == ep->user_entry){
|
||||||
|
if (0 < c_strlen(rsp)) {
|
||||||
|
c_strncat(rsp, ",", len);
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
c_strncat(rsp, "<", len);
|
||||||
|
len--;
|
||||||
|
|
||||||
|
for (i = 0; i < ep->path->count; i++) {
|
||||||
|
c_strncat(rsp, "/", len);
|
||||||
|
len--;
|
||||||
|
|
||||||
|
c_strncat(rsp, ep->path->elems[i], len);
|
||||||
|
len -= c_strlen(ep->path->elems[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
c_strncat(rsp, ">;", len);
|
||||||
|
len -= 2;
|
||||||
|
|
||||||
|
c_strncat(rsp, ep->core_attr, len);
|
||||||
|
len -= c_strlen(ep->core_attr);
|
||||||
|
} else {
|
||||||
|
coap_luser_entry *h = ep->user_entry->next; // ->next: skip the first entry(head)
|
||||||
|
while(NULL != h){
|
||||||
|
if (0 < c_strlen(rsp)) {
|
||||||
|
c_strncat(rsp, ",", len);
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
c_strncat(rsp, "<", len);
|
||||||
|
len--;
|
||||||
|
|
||||||
|
for (i = 0; i < ep->path->count; i++) {
|
||||||
|
c_strncat(rsp, "/", len);
|
||||||
|
len--;
|
||||||
|
|
||||||
|
c_strncat(rsp, ep->path->elems[i], len);
|
||||||
|
len -= c_strlen(ep->path->elems[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
c_strncat(rsp, "/", len);
|
||||||
|
len--;
|
||||||
|
|
||||||
|
c_strncat(rsp, h->name, len);
|
||||||
|
len -= c_strlen(h->name);
|
||||||
|
|
||||||
|
c_strncat(rsp, ">;", len);
|
||||||
|
len -= 2;
|
||||||
|
|
||||||
|
c_strncat(rsp, ep->core_attr, len);
|
||||||
|
len -= c_strlen(ep->core_attr);
|
||||||
|
|
||||||
|
h = h->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ep++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
#include "hash.h"
|
||||||
|
#include "c_string.h"
|
||||||
|
/* Caution: When changing this, update COAP_DEFAULT_WKC_HASHKEY
|
||||||
|
* accordingly (see int coap_hash_path());
|
||||||
|
*/
|
||||||
|
void coap_hash(const unsigned char *s, unsigned int len, coap_key_t h) {
|
||||||
|
size_t j;
|
||||||
|
|
||||||
|
while (len--) {
|
||||||
|
j = sizeof(coap_key_t)-1;
|
||||||
|
|
||||||
|
while (j) {
|
||||||
|
h[j] = ((h[j] << 7) | (h[j-1] >> 1)) + h[j];
|
||||||
|
--j;
|
||||||
|
}
|
||||||
|
|
||||||
|
h[0] = (h[0] << 7) + h[0] + *s++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void coap_transaction_id(const uint32_t ip, const uint32_t port, const coap_packet_t *pkt, coap_tid_t *id) {
|
||||||
|
coap_key_t h;
|
||||||
|
c_memset(h, 0, sizeof(coap_key_t));
|
||||||
|
|
||||||
|
/* Compare the transport address. */
|
||||||
|
coap_hash((const unsigned char *)&(port), sizeof(port), h);
|
||||||
|
coap_hash((const unsigned char *)&(ip), sizeof(ip), h);
|
||||||
|
coap_hash((const unsigned char *)(pkt->hdr.id), sizeof(pkt->hdr.id), h);
|
||||||
|
*id = ((h[0] << 8) | h[1]) ^ ((h[2] << 8) | h[3]);
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef _HASH_H
|
||||||
|
#define _HASH_H 1
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "coap.h"
|
||||||
|
|
||||||
|
typedef unsigned char coap_key_t[4];
|
||||||
|
|
||||||
|
/* CoAP transaction id */
|
||||||
|
/*typedef unsigned short coap_tid_t; */
|
||||||
|
typedef int coap_tid_t;
|
||||||
|
#define COAP_INVALID_TID -1
|
||||||
|
|
||||||
|
void coap_transaction_id(const uint32_t ip, const uint32_t port, const coap_packet_t *pkt, coap_tid_t *id);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,141 @@
|
||||||
|
#include "c_string.h"
|
||||||
|
#include "c_stdlib.h"
|
||||||
|
#include "node.h"
|
||||||
|
|
||||||
|
static inline coap_queue_t *
|
||||||
|
coap_malloc_node(void) {
|
||||||
|
return (coap_queue_t *)c_zalloc(sizeof(coap_queue_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
void coap_free_node(coap_queue_t *node) {
|
||||||
|
c_free(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
int coap_insert_node(coap_queue_t **queue, coap_queue_t *node) {
|
||||||
|
coap_queue_t *p, *q;
|
||||||
|
if ( !queue || !node )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* set queue head if empty */
|
||||||
|
if ( !*queue ) {
|
||||||
|
*queue = node;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* replace queue head if PDU's time is less than head's time */
|
||||||
|
q = *queue;
|
||||||
|
if (node->t < q->t) {
|
||||||
|
node->next = q;
|
||||||
|
*queue = node;
|
||||||
|
q->t -= node->t; /* make q->t relative to node->t */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* search for right place to insert */
|
||||||
|
do {
|
||||||
|
node->t -= q->t; /* make node-> relative to q->t */
|
||||||
|
p = q;
|
||||||
|
q = q->next;
|
||||||
|
} while (q && q->t <= node->t);
|
||||||
|
|
||||||
|
/* insert new item */
|
||||||
|
if (q) {
|
||||||
|
q->t -= node->t; /* make q->t relative to node->t */
|
||||||
|
}
|
||||||
|
node->next = q;
|
||||||
|
p->next = node;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int coap_delete_node(coap_queue_t *node) {
|
||||||
|
if ( !node )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
coap_delete_pdu(node->pdu);
|
||||||
|
coap_free_node(node);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void coap_delete_all(coap_queue_t *queue) {
|
||||||
|
if ( !queue )
|
||||||
|
return;
|
||||||
|
|
||||||
|
coap_delete_all( queue->next );
|
||||||
|
coap_delete_node( queue );
|
||||||
|
}
|
||||||
|
|
||||||
|
coap_queue_t * coap_new_node(void) {
|
||||||
|
coap_queue_t *node;
|
||||||
|
node = coap_malloc_node();
|
||||||
|
|
||||||
|
if ( ! node ) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
c_memset(node, 0, sizeof(*node));
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
coap_queue_t * coap_peek_next( coap_queue_t *queue ) {
|
||||||
|
if ( !queue )
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
coap_queue_t * coap_pop_next( coap_queue_t **queue ) { // this function is called inside timeout callback only.
|
||||||
|
coap_queue_t *next;
|
||||||
|
|
||||||
|
if ( !(*queue) )
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
next = *queue;
|
||||||
|
*queue = (*queue)->next;
|
||||||
|
// if (queue) {
|
||||||
|
// queue->t += next->t;
|
||||||
|
// }
|
||||||
|
next->next = NULL;
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
int coap_remove_node( coap_queue_t **queue, const coap_tid_t id){
|
||||||
|
coap_queue_t *p, *q, *node;
|
||||||
|
if ( !queue )
|
||||||
|
return 0;
|
||||||
|
if ( !*queue ) // if empty
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
q = *queue;
|
||||||
|
if (q->id == id) {
|
||||||
|
node = q;
|
||||||
|
*queue = q->next;
|
||||||
|
node->next = NULL;
|
||||||
|
if(*queue){
|
||||||
|
(*queue)->t += node->t;
|
||||||
|
}
|
||||||
|
coap_delete_node(node);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* search for right node to remove */
|
||||||
|
while (q && q->id != id) {
|
||||||
|
p = q;
|
||||||
|
q = q->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* find the node */
|
||||||
|
if (q) {
|
||||||
|
node = q; /* save the node */
|
||||||
|
p->next = q->next; /* remove the node */
|
||||||
|
q = q->next;
|
||||||
|
node->next = NULL;
|
||||||
|
if (q) // add node->t to the node after.
|
||||||
|
{
|
||||||
|
q->t += node->t;
|
||||||
|
}
|
||||||
|
coap_delete_node(node);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
#ifndef _NODE_H
|
||||||
|
#define _NODE_H 1
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "hash.h"
|
||||||
|
#include "pdu.h"
|
||||||
|
|
||||||
|
struct coap_queue_t;
|
||||||
|
typedef uint32_t coap_tick_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
1. queue(first)->t store when to send PDU for the next time, it's a base(absolute) time
|
||||||
|
2. queue->next->t store the delta between time and base-time. queue->next->t = timeout + now - basetime
|
||||||
|
3. node->next->t store the delta between time and previous->t. node->next->t = timeout + now - node->t - basetime
|
||||||
|
4. time to fire: 10, 15, 18, 25
|
||||||
|
node->t: 10, 5, 3, 7
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct coap_queue_t {
|
||||||
|
struct coap_queue_t *next;
|
||||||
|
|
||||||
|
coap_tick_t t; /**< when to send PDU for the next time */
|
||||||
|
unsigned char retransmit_cnt; /**< retransmission counter, will be removed when zero */
|
||||||
|
unsigned int timeout; /**< the randomized timeout value */
|
||||||
|
|
||||||
|
coap_tid_t id; /**< unique transaction id */
|
||||||
|
|
||||||
|
// coap_packet_t *pkt;
|
||||||
|
coap_pdu_t *pdu; /**< the CoAP PDU to send */
|
||||||
|
struct espconn *pconn;
|
||||||
|
} coap_queue_t;
|
||||||
|
|
||||||
|
void coap_free_node(coap_queue_t *node);
|
||||||
|
|
||||||
|
/** Adds node to given queue, ordered by node->t. */
|
||||||
|
int coap_insert_node(coap_queue_t **queue, coap_queue_t *node);
|
||||||
|
|
||||||
|
/** Destroys specified node. */
|
||||||
|
int coap_delete_node(coap_queue_t *node);
|
||||||
|
|
||||||
|
/** Removes all items from given queue and frees the allocated storage. */
|
||||||
|
void coap_delete_all(coap_queue_t *queue);
|
||||||
|
|
||||||
|
/** Creates a new node suitable for adding to the CoAP sendqueue. */
|
||||||
|
coap_queue_t *coap_new_node(void);
|
||||||
|
|
||||||
|
coap_queue_t *coap_pop_next( coap_queue_t **queue );
|
||||||
|
|
||||||
|
int coap_remove_node( coap_queue_t **queue, const coap_tid_t id);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,63 @@
|
||||||
|
#include "c_stdlib.h"
|
||||||
|
#include "pdu.h"
|
||||||
|
|
||||||
|
coap_pdu_t * coap_new_pdu(void) {
|
||||||
|
coap_pdu_t *pdu = NULL;
|
||||||
|
pdu = (coap_pdu_t *)c_zalloc(sizeof(coap_pdu_t));
|
||||||
|
if(!pdu){
|
||||||
|
NODE_DBG("coap_new_pdu malloc error.\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdu->scratch.p = (uint8_t *)c_zalloc(MAX_REQ_SCRATCH_SIZE);
|
||||||
|
if(!pdu->scratch.p){
|
||||||
|
NODE_DBG("coap_new_pdu malloc error.\n");
|
||||||
|
c_free(pdu);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
pdu->scratch.len = MAX_REQ_SCRATCH_SIZE;
|
||||||
|
|
||||||
|
pdu->pkt = (coap_packet_t *)c_zalloc(sizeof(coap_packet_t));
|
||||||
|
if(!pdu->pkt){
|
||||||
|
NODE_DBG("coap_new_pdu malloc error.\n");
|
||||||
|
c_free(pdu->scratch.p);
|
||||||
|
c_free(pdu);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdu->msg.p = (uint8_t *)c_zalloc(MAX_REQUEST_SIZE+1); // +1 for string '\0'
|
||||||
|
if(!pdu->msg.p){
|
||||||
|
NODE_DBG("coap_new_pdu malloc error.\n");
|
||||||
|
c_free(pdu->pkt);
|
||||||
|
c_free(pdu->scratch.p);
|
||||||
|
c_free(pdu);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
pdu->msg.len = MAX_REQUEST_SIZE;
|
||||||
|
return pdu;
|
||||||
|
}
|
||||||
|
|
||||||
|
void coap_delete_pdu(coap_pdu_t *pdu){
|
||||||
|
if(!pdu)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(pdu->scratch.p){
|
||||||
|
c_free(pdu->scratch.p);
|
||||||
|
pdu->scratch.p = NULL;
|
||||||
|
pdu->scratch.len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pdu->pkt){
|
||||||
|
c_free(pdu->pkt);
|
||||||
|
pdu->pkt = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pdu->msg.p){
|
||||||
|
c_free(pdu->msg.p);
|
||||||
|
pdu->msg.p = NULL;
|
||||||
|
pdu->msg.len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
c_free(pdu);
|
||||||
|
pdu = NULL;
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef _PDU_H
|
||||||
|
#define _PDU_H 1
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "coap.h"
|
||||||
|
|
||||||
|
/** Header structure for CoAP PDUs */
|
||||||
|
typedef struct {
|
||||||
|
coap_rw_buffer_t scratch;
|
||||||
|
coap_packet_t *pkt;
|
||||||
|
coap_rw_buffer_t msg; /**< the CoAP msg to send */
|
||||||
|
} coap_pdu_t;
|
||||||
|
|
||||||
|
coap_pdu_t *coap_new_pdu(void);
|
||||||
|
|
||||||
|
void coap_delete_pdu(coap_pdu_t *pdu);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,28 @@
|
||||||
|
/* str.c -- strings to be used in the CoAP library
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010,2011 Olaf Bergmann <bergmann@tzi.org>
|
||||||
|
*
|
||||||
|
* This file is part of the CoAP library libcoap. Please see
|
||||||
|
* README for terms of use.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "c_stdlib.h"
|
||||||
|
#include "c_types.h"
|
||||||
|
|
||||||
|
#include "str.h"
|
||||||
|
|
||||||
|
str * coap_new_string(size_t size) {
|
||||||
|
str *s = (str *)c_malloc(sizeof(str) + size + 1);
|
||||||
|
if ( !s ) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
c_memset(s, 0, sizeof(str));
|
||||||
|
s->s = ((unsigned char *)s) + sizeof(str);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void coap_delete_string(str *s) {
|
||||||
|
c_free(s);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
/* str.h -- strings to be used in the CoAP library
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010,2011 Olaf Bergmann <bergmann@tzi.org>
|
||||||
|
*
|
||||||
|
* This file is part of the CoAP library libcoap. Please see
|
||||||
|
* README for terms of use.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _COAP_STR_H_
|
||||||
|
#define _COAP_STR_H_
|
||||||
|
|
||||||
|
#include "c_string.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t length; /* length of string */
|
||||||
|
unsigned char *s; /* string data */
|
||||||
|
} str;
|
||||||
|
|
||||||
|
#define COAP_SET_STR(st,l,v) { (st)->length = (l), (st)->s = (v); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new string object with at least size bytes storage
|
||||||
|
* allocated. The string must be released using coap_delete_string();
|
||||||
|
*/
|
||||||
|
str *coap_new_string(size_t size);
|
||||||
|
|
||||||
|
/** Deletes the given string and releases any memory allocated. */
|
||||||
|
void coap_delete_string(str *);
|
||||||
|
|
||||||
|
#endif /* _COAP_STR_H_ */
|
|
@ -0,0 +1,468 @@
|
||||||
|
/* uri.c -- helper functions for URI treatment
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "c_stdio.h"
|
||||||
|
#include "c_stdlib.h"
|
||||||
|
#include "c_string.h"
|
||||||
|
#include "c_ctype.h"
|
||||||
|
|
||||||
|
#include "coap.h"
|
||||||
|
#include "uri.h"
|
||||||
|
|
||||||
|
#ifndef assert
|
||||||
|
// #warning "assertions are disabled"
|
||||||
|
# define assert(x) do { \
|
||||||
|
if(!x) NODE_ERR("uri.c assert!\n"); \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A length-safe version of strchr(). This function returns a pointer
|
||||||
|
* to the first occurrence of @p c in @p s, or @c NULL if not found.
|
||||||
|
*
|
||||||
|
* @param s The string to search for @p c.
|
||||||
|
* @param len The length of @p s.
|
||||||
|
* @param c The character to search.
|
||||||
|
*
|
||||||
|
* @return A pointer to the first occurence of @p c, or @c NULL
|
||||||
|
* if not found.
|
||||||
|
*/
|
||||||
|
static inline unsigned char *
|
||||||
|
strnchr(unsigned char *s, size_t len, unsigned char c) {
|
||||||
|
while (len && *s++ != c)
|
||||||
|
--len;
|
||||||
|
|
||||||
|
return len ? s : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int coap_split_uri(unsigned char *str_var, size_t len, coap_uri_t *uri) {
|
||||||
|
unsigned char *p, *q;
|
||||||
|
int secure = 0, res = 0;
|
||||||
|
|
||||||
|
if (!str_var || !uri)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
c_memset(uri, 0, sizeof(coap_uri_t));
|
||||||
|
uri->port = COAP_DEFAULT_PORT;
|
||||||
|
|
||||||
|
/* search for scheme */
|
||||||
|
p = str_var;
|
||||||
|
if (*p == '/') {
|
||||||
|
q = p;
|
||||||
|
goto path;
|
||||||
|
}
|
||||||
|
|
||||||
|
q = (unsigned char *)COAP_DEFAULT_SCHEME;
|
||||||
|
while (len && *q && tolower(*p) == *q) {
|
||||||
|
++p; ++q; --len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If q does not point to the string end marker '\0', the schema
|
||||||
|
* identifier is wrong. */
|
||||||
|
if (*q) {
|
||||||
|
res = -1;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* There might be an additional 's', indicating the secure version: */
|
||||||
|
if (len && (secure = tolower(*p) == 's')) {
|
||||||
|
++p; --len;
|
||||||
|
}
|
||||||
|
|
||||||
|
q = (unsigned char *)"://";
|
||||||
|
while (len && *q && tolower(*p) == *q) {
|
||||||
|
++p; ++q; --len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*q) {
|
||||||
|
res = -2;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* p points to beginning of Uri-Host */
|
||||||
|
q = p;
|
||||||
|
if (len && *p == '[') { /* IPv6 address reference */
|
||||||
|
++p;
|
||||||
|
|
||||||
|
while (len && *q != ']') {
|
||||||
|
++q; --len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!len || *q != ']' || p == q) {
|
||||||
|
res = -3;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
COAP_SET_STR(&uri->host, q - p, p);
|
||||||
|
++q; --len;
|
||||||
|
} else { /* IPv4 address or FQDN */
|
||||||
|
while (len && *q != ':' && *q != '/' && *q != '?') {
|
||||||
|
*q = tolower(*q);
|
||||||
|
++q;
|
||||||
|
--len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p == q) {
|
||||||
|
res = -3;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
COAP_SET_STR(&uri->host, q - p, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for Uri-Port */
|
||||||
|
if (len && *q == ':') {
|
||||||
|
p = ++q;
|
||||||
|
--len;
|
||||||
|
|
||||||
|
while (len && isdigit(*q)) {
|
||||||
|
++q;
|
||||||
|
--len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p < q) { /* explicit port number given */
|
||||||
|
int uri_port = 0;
|
||||||
|
|
||||||
|
while (p < q)
|
||||||
|
uri_port = uri_port * 10 + (*p++ - '0');
|
||||||
|
|
||||||
|
uri->port = uri_port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
path: /* at this point, p must point to an absolute path */
|
||||||
|
|
||||||
|
if (!len)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
if (*q == '/') {
|
||||||
|
p = ++q;
|
||||||
|
--len;
|
||||||
|
|
||||||
|
while (len && *q != '?') {
|
||||||
|
++q;
|
||||||
|
--len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p < q) {
|
||||||
|
COAP_SET_STR(&uri->path, q - p, p);
|
||||||
|
p = q;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Uri_Query */
|
||||||
|
if (len && *p == '?') {
|
||||||
|
++p;
|
||||||
|
--len;
|
||||||
|
COAP_SET_STR(&uri->query, len, p);
|
||||||
|
len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
return len ? -1 : 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates decimal value from hexadecimal ASCII character given in
|
||||||
|
* @p c. The caller must ensure that @p c actually represents a valid
|
||||||
|
* heaxdecimal character, e.g. with isxdigit(3).
|
||||||
|
*
|
||||||
|
* @hideinitializer
|
||||||
|
*/
|
||||||
|
#define hexchar_to_dec(c) ((c) & 0x40 ? ((c) & 0x0F) + 9 : ((c) & 0x0F))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes percent-encoded characters while copying the string @p seg
|
||||||
|
* of size @p length to @p buf. The caller of this function must
|
||||||
|
* ensure that the percent-encodings are correct (i.e. the character
|
||||||
|
* '%' is always followed by two hex digits. and that @p buf provides
|
||||||
|
* sufficient space to hold the result. This function is supposed to
|
||||||
|
* be called by make_decoded_option() only.
|
||||||
|
*
|
||||||
|
* @param seg The segment to decode and copy.
|
||||||
|
* @param length Length of @p seg.
|
||||||
|
* @param buf The result buffer.
|
||||||
|
*/
|
||||||
|
void decode_segment(const unsigned char *seg, size_t length, unsigned char *buf) {
|
||||||
|
|
||||||
|
while (length--) {
|
||||||
|
|
||||||
|
if (*seg == '%') {
|
||||||
|
*buf = (hexchar_to_dec(seg[1]) << 4) + hexchar_to_dec(seg[2]);
|
||||||
|
|
||||||
|
seg += 2; length -= 2;
|
||||||
|
} else {
|
||||||
|
*buf = *seg;
|
||||||
|
}
|
||||||
|
|
||||||
|
++buf; ++seg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs through the given path (or query) segment and checks if
|
||||||
|
* percent-encodings are correct. This function returns @c -1 on error
|
||||||
|
* or the length of @p s when decoded.
|
||||||
|
*/
|
||||||
|
int check_segment(const unsigned char *s, size_t length) {
|
||||||
|
|
||||||
|
size_t n = 0;
|
||||||
|
|
||||||
|
while (length) {
|
||||||
|
if (*s == '%') {
|
||||||
|
if (length < 2 || !(isxdigit(s[1]) && isxdigit(s[2])))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
s += 2;
|
||||||
|
length -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
++s; ++n; --length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a coap option from given string @p s to @p buf. @p s should
|
||||||
|
* point to a (percent-encoded) path or query segment of a coap_uri_t
|
||||||
|
* object. The created option will have type @c 0, and the length
|
||||||
|
* parameter will be set according to the size of the decoded string.
|
||||||
|
* On success, this function returns the option's size, or a value
|
||||||
|
* less than zero on error. This function must be called from
|
||||||
|
* coap_split_path_impl() only.
|
||||||
|
*
|
||||||
|
* @param s The string to decode.
|
||||||
|
* @param length The size of the percent-encoded string @p s.
|
||||||
|
* @param buf The buffer to store the new coap option.
|
||||||
|
* @param buflen The maximum size of @p buf.
|
||||||
|
*
|
||||||
|
* @return The option's size, or @c -1 on error.
|
||||||
|
*
|
||||||
|
* @bug This function does not split segments that are bigger than 270
|
||||||
|
* bytes.
|
||||||
|
*/
|
||||||
|
int make_decoded_option(const unsigned char *s, size_t length,
|
||||||
|
unsigned char *buf, size_t buflen) {
|
||||||
|
int res;
|
||||||
|
size_t written;
|
||||||
|
|
||||||
|
if (!buflen) {
|
||||||
|
NODE_DBG("make_decoded_option(): buflen is 0!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = check_segment(s, length);
|
||||||
|
if (res < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* write option header using delta 0 and length res */
|
||||||
|
// written = coap_opt_setheader(buf, buflen, 0, res);
|
||||||
|
written = coap_buildOptionHeader(0, res, buf, buflen);
|
||||||
|
|
||||||
|
assert(written <= buflen);
|
||||||
|
|
||||||
|
if (!written) /* encoding error */
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
buf += written; /* advance past option type/length */
|
||||||
|
buflen -= written;
|
||||||
|
|
||||||
|
if (buflen < (size_t)res) {
|
||||||
|
NODE_DBG("buffer too small for option\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
decode_segment(s, length, buf);
|
||||||
|
|
||||||
|
return written + res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef min
|
||||||
|
#define min(a,b) ((a) < (b) ? (a) : (b))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef void (*segment_handler_t)(unsigned char *, size_t, void *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits the given string into segments. You should call one of the
|
||||||
|
* macros coap_split_path() or coap_split_query() instead.
|
||||||
|
*
|
||||||
|
* @param parse_iter The iterator used for tokenizing.
|
||||||
|
* @param h A handler that is called with every token.
|
||||||
|
* @param data Opaque data that is passed to @p h when called.
|
||||||
|
*
|
||||||
|
* @return The number of characters that have been parsed from @p s.
|
||||||
|
*/
|
||||||
|
size_t coap_split_path_impl(coap_parse_iterator_t *parse_iter,
|
||||||
|
segment_handler_t h, void *data) {
|
||||||
|
unsigned char *seg;
|
||||||
|
size_t length;
|
||||||
|
|
||||||
|
assert(parse_iter);
|
||||||
|
assert(h);
|
||||||
|
|
||||||
|
length = parse_iter->n;
|
||||||
|
|
||||||
|
while ( (seg = coap_parse_next(parse_iter)) ) {
|
||||||
|
|
||||||
|
/* any valid path segment is handled here: */
|
||||||
|
h(seg, parse_iter->segment_length, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return length - (parse_iter->n - parse_iter->segment_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct pkt_scr {
|
||||||
|
coap_packet_t *pkt;
|
||||||
|
coap_rw_buffer_t *scratch;
|
||||||
|
int n;
|
||||||
|
};
|
||||||
|
|
||||||
|
void write_option(unsigned char *s, size_t len, void *data) {
|
||||||
|
struct pkt_scr *state = (struct pkt_scr *)data;
|
||||||
|
int res;
|
||||||
|
assert(state);
|
||||||
|
|
||||||
|
/* skip empty segments and those that consist of only one or two dots */
|
||||||
|
if (memcmp(s, "..", min(len,2)) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
res = check_segment(s, len);
|
||||||
|
if (res < 0){
|
||||||
|
NODE_DBG("not a valid segment\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->scratch->len < (size_t)res) {
|
||||||
|
NODE_DBG("buffer too small for option\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
decode_segment(s, len, state->scratch->p);
|
||||||
|
|
||||||
|
if (res > 0) {
|
||||||
|
state->pkt->opts[state->pkt->numopts].buf.p = state->scratch->p;
|
||||||
|
state->pkt->opts[state->pkt->numopts].buf.len = res;
|
||||||
|
state->scratch->p += res;
|
||||||
|
state->scratch->len -= res;
|
||||||
|
state->pkt->numopts++;
|
||||||
|
state->n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int coap_split_path(coap_rw_buffer_t *scratch, coap_packet_t *pkt, const unsigned char *s, size_t length) {
|
||||||
|
struct pkt_scr tmp = { pkt, scratch, 0 };
|
||||||
|
coap_parse_iterator_t pi;
|
||||||
|
|
||||||
|
coap_parse_iterator_init((unsigned char *)s, length,
|
||||||
|
'/', (unsigned char *)"?#", 2, &pi);
|
||||||
|
coap_split_path_impl(&pi, write_option, &tmp);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for(i=0;i<tmp.n;i++){
|
||||||
|
pkt->opts[pkt->numopts - i - 1].num = COAP_OPTION_URI_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tmp.n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int coap_split_query(coap_rw_buffer_t *scratch, coap_packet_t *pkt, const unsigned char *s, size_t length) {
|
||||||
|
struct pkt_scr tmp = { pkt, scratch, 0 };
|
||||||
|
coap_parse_iterator_t pi;
|
||||||
|
|
||||||
|
coap_parse_iterator_init((unsigned char *)s, length,
|
||||||
|
'&', (unsigned char *)"#", 1, &pi);
|
||||||
|
|
||||||
|
coap_split_path_impl(&pi, write_option, &tmp);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for(i=0;i<tmp.n;i++){
|
||||||
|
pkt->opts[pkt->numopts - i - 1].num = COAP_OPTION_URI_QUERY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tmp.n;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define URI_DATA(uriobj) ((unsigned char *)(uriobj) + sizeof(coap_uri_t))
|
||||||
|
|
||||||
|
coap_uri_t * coap_new_uri(const unsigned char *uri, unsigned int length) {
|
||||||
|
unsigned char *result;
|
||||||
|
|
||||||
|
result = (unsigned char *)c_malloc(length + 1 + sizeof(coap_uri_t));
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
c_memcpy(URI_DATA(result), uri, length);
|
||||||
|
URI_DATA(result)[length] = '\0'; /* make it zero-terminated */
|
||||||
|
|
||||||
|
if (coap_split_uri(URI_DATA(result), length, (coap_uri_t *)result) < 0) {
|
||||||
|
c_free(result);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return (coap_uri_t *)result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* iterator functions */
|
||||||
|
|
||||||
|
coap_parse_iterator_t * coap_parse_iterator_init(unsigned char *s, size_t n,
|
||||||
|
unsigned char separator,
|
||||||
|
unsigned char *delim, size_t dlen,
|
||||||
|
coap_parse_iterator_t *pi) {
|
||||||
|
assert(pi);
|
||||||
|
assert(separator);
|
||||||
|
|
||||||
|
pi->separator = separator;
|
||||||
|
pi->delim = delim;
|
||||||
|
pi->dlen = dlen;
|
||||||
|
pi->pos = s;
|
||||||
|
pi->n = n;
|
||||||
|
pi->segment_length = 0;
|
||||||
|
|
||||||
|
return pi;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char * coap_parse_next(coap_parse_iterator_t *pi) {
|
||||||
|
unsigned char *p;
|
||||||
|
|
||||||
|
if (!pi)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* proceed to the next segment */
|
||||||
|
pi->n -= pi->segment_length;
|
||||||
|
pi->pos += pi->segment_length;
|
||||||
|
pi->segment_length = 0;
|
||||||
|
|
||||||
|
/* last segment? */
|
||||||
|
if (!pi->n || strnchr(pi->delim, pi->dlen, *pi->pos)) {
|
||||||
|
pi->pos = NULL;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* skip following separator (the first segment might not have one) */
|
||||||
|
if (*pi->pos == pi->separator) {
|
||||||
|
++pi->pos;
|
||||||
|
--pi->n;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = pi->pos;
|
||||||
|
|
||||||
|
while (pi->segment_length < pi->n && *p != pi->separator &&
|
||||||
|
!strnchr(pi->delim, pi->dlen, *p)) {
|
||||||
|
++p;
|
||||||
|
++pi->segment_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pi->n) {
|
||||||
|
pi->pos = NULL;
|
||||||
|
pi->segment_length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pi->pos;
|
||||||
|
}
|
|
@ -0,0 +1,166 @@
|
||||||
|
/* uri.h -- helper functions for URI treatment
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010,2011 Olaf Bergmann <bergmann@tzi.org>
|
||||||
|
*
|
||||||
|
* This file is part of the CoAP library libcoap. Please see
|
||||||
|
* README for terms of use.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _COAP_URI_H_
|
||||||
|
#define _COAP_URI_H_
|
||||||
|
|
||||||
|
#define COAP_DEFAULT_SCHEME "coap" /* the default scheme for CoAP URIs */
|
||||||
|
#define COAP_DEFAULT_PORT 5683
|
||||||
|
|
||||||
|
#include "str.h"
|
||||||
|
|
||||||
|
/** Representation of parsed URI. Components may be filled from a
|
||||||
|
* string with coap_split_uri() and can be used as input for
|
||||||
|
* option-creation functions. */
|
||||||
|
typedef struct {
|
||||||
|
str host; /**< host part of the URI */
|
||||||
|
unsigned short port; /**< The port in host byte order */
|
||||||
|
str path; /**< Beginning of the first path segment.
|
||||||
|
Use coap_split_path() to create Uri-Path options */
|
||||||
|
str query; /**< The query part if present */
|
||||||
|
} coap_uri_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new coap_uri_t object from the specified URI. Returns the new
|
||||||
|
* object or NULL on error. The memory allocated by the new coap_uri_t
|
||||||
|
* must be released using coap_free().
|
||||||
|
* @param uri The URI path to copy.
|
||||||
|
* @para length The length of uri.
|
||||||
|
*
|
||||||
|
* @return New URI object or NULL on error.
|
||||||
|
*/
|
||||||
|
coap_uri_t *coap_new_uri(const unsigned char *uri, unsigned int length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup uri_parse URI Parsing Functions
|
||||||
|
*
|
||||||
|
* CoAP PDUs contain normalized URIs with their path and query split into
|
||||||
|
* multiple segments. The functions in this module help splitting strings.
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterator to for tokenizing a URI path or query. This structure must
|
||||||
|
* be initialized with coap_parse_iterator_init(). Call
|
||||||
|
* coap_parse_next() to walk through the tokens.
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* unsigned char *token;
|
||||||
|
* coap_parse_iterator_t pi;
|
||||||
|
* coap_parse_iterator_init(uri.path.s, uri.path.length, '/', "?#", 2, &pi);
|
||||||
|
*
|
||||||
|
* while ((token = coap_parse_next(&pi))) {
|
||||||
|
* ... do something with token ...
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
size_t n; /**< number of remaining characters in buffer */
|
||||||
|
unsigned char separator; /**< segment separators */
|
||||||
|
unsigned char *delim; /**< delimiters where to split the string */
|
||||||
|
size_t dlen; /**< length of separator */
|
||||||
|
unsigned char *pos; /**< current position in buffer */
|
||||||
|
size_t segment_length; /**< length of current segment */
|
||||||
|
} coap_parse_iterator_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the given iterator @p pi.
|
||||||
|
*
|
||||||
|
* @param s The string to tokenize.
|
||||||
|
* @param n The length of @p s.
|
||||||
|
* @param separator The separator character that delimits tokens.
|
||||||
|
* @param delim A set of characters that delimit @s.
|
||||||
|
* @param dlen The length of @p delim.
|
||||||
|
* @param pi The iterator object to initialize.
|
||||||
|
*
|
||||||
|
* @return The initialized iterator object @p pi.
|
||||||
|
*/
|
||||||
|
coap_parse_iterator_t *
|
||||||
|
coap_parse_iterator_init(unsigned char *s, size_t n,
|
||||||
|
unsigned char separator,
|
||||||
|
unsigned char *delim, size_t dlen,
|
||||||
|
coap_parse_iterator_t *pi);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the iterator @p pi to point to the next token. This
|
||||||
|
* function returns a pointer to that token or @c NULL if no more
|
||||||
|
* tokens exist. The contents of @p pi will be updated. In particular,
|
||||||
|
* @c pi->segment_length specifies the length of the current token, @c
|
||||||
|
* pi->pos points to its beginning.
|
||||||
|
*
|
||||||
|
* @param pi The iterator to update.
|
||||||
|
*
|
||||||
|
* @return The next token or @c NULL if no more tokens exist.
|
||||||
|
*/
|
||||||
|
unsigned char *coap_parse_next(coap_parse_iterator_t *pi);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a given string into URI components. The identified syntactic
|
||||||
|
* components are stored in the result parameter @p uri. Optional URI
|
||||||
|
* components that are not specified will be set to { 0, 0 }, except
|
||||||
|
* for the port which is set to @c COAP_DEFAULT_PORT. This function
|
||||||
|
* returns @p 0 if parsing succeeded, a value less than zero
|
||||||
|
* otherwise.
|
||||||
|
*
|
||||||
|
* @param str_var The string to split up.
|
||||||
|
* @param len The actual length of @p str_var
|
||||||
|
* @param uri The coap_uri_t object to store the result.
|
||||||
|
* @return @c 0 on success, or < 0 on error.
|
||||||
|
*
|
||||||
|
* @note The host name part will be converted to lower case by this
|
||||||
|
* function.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
coap_split_uri(unsigned char *str_var, size_t len, coap_uri_t *uri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits the given URI path into segments. Each segment is preceded
|
||||||
|
* by an option pseudo-header with delta-value 0 and the actual length
|
||||||
|
* of the respective segment after percent-decoding.
|
||||||
|
*
|
||||||
|
* @param s The path string to split.
|
||||||
|
* @param length The actual length of @p s.
|
||||||
|
* @param buf Result buffer for parsed segments.
|
||||||
|
* @param buflen Maximum length of @p buf. Will be set to the actual number
|
||||||
|
* of bytes written into buf on success.
|
||||||
|
*
|
||||||
|
* @return The number of segments created or @c -1 on error.
|
||||||
|
*/
|
||||||
|
#if 0
|
||||||
|
int coap_split_path(const unsigned char *s, size_t length,
|
||||||
|
unsigned char *buf, size_t *buflen);
|
||||||
|
#else
|
||||||
|
int
|
||||||
|
coap_split_path(coap_rw_buffer_t *scratch, coap_packet_t *pkt,
|
||||||
|
const unsigned char *s, size_t length);
|
||||||
|
#endif
|
||||||
|
/**
|
||||||
|
* Splits the given URI query into segments. Each segment is preceded
|
||||||
|
* by an option pseudo-header with delta-value 0 and the actual length
|
||||||
|
* of the respective query term.
|
||||||
|
*
|
||||||
|
* @param s The query string to split.
|
||||||
|
* @param length The actual length of @p s.
|
||||||
|
* @param buf Result buffer for parsed segments.
|
||||||
|
* @param buflen Maximum length of @p buf. Will be set to the actual number
|
||||||
|
* of bytes written into buf on success.
|
||||||
|
*
|
||||||
|
* @return The number of segments created or @c -1 on error.
|
||||||
|
*
|
||||||
|
* @bug This function does not reserve additional space for delta > 12.
|
||||||
|
*/
|
||||||
|
#if 0
|
||||||
|
int coap_split_query(const unsigned char *s, size_t length,
|
||||||
|
unsigned char *buf, size_t *buflen);
|
||||||
|
#else
|
||||||
|
int coap_split_query(coap_rw_buffer_t *scratch, coap_packet_t *pkt,
|
||||||
|
const unsigned char *s, size_t length);
|
||||||
|
#endif
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
#endif /* _COAP_URI_H_ */
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#ifdef DEVELOP_VERSION
|
#ifdef DEVELOP_VERSION
|
||||||
#define NODE_DEBUG
|
#define NODE_DEBUG
|
||||||
|
#define COAP_DEBUG
|
||||||
#endif /* DEVELOP_VERSION */
|
#endif /* DEVELOP_VERSION */
|
||||||
|
|
||||||
#define NODE_ERROR
|
#define NODE_ERROR
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#define LUA_USE_BUILTIN_STRING // for string.xxx()
|
#define LUA_USE_BUILTIN_STRING // for string.xxx()
|
||||||
#define LUA_USE_BUILTIN_TABLE // for table.xxx()
|
#define LUA_USE_BUILTIN_TABLE // for table.xxx()
|
||||||
#define LUA_USE_BUILTIN_COROUTINE // for coroutine.xxx()
|
#define LUA_USE_BUILTIN_COROUTINE // for coroutine.xxx()
|
||||||
// #define LUA_USE_BUILTIN_MATH // for math.xxx(), partially work
|
#define LUA_USE_BUILTIN_MATH // for math.xxx(), partially work
|
||||||
// #define LUA_USE_BUILTIN_IO // for io.xxx(), partially work
|
// #define LUA_USE_BUILTIN_IO // for io.xxx(), partially work
|
||||||
|
|
||||||
// #define LUA_USE_BUILTIN_OS // for os.xxx(), not work
|
// #define LUA_USE_BUILTIN_OS // for os.xxx(), not work
|
||||||
|
@ -26,8 +26,9 @@
|
||||||
#define LUA_USE_MODULES_UART
|
#define LUA_USE_MODULES_UART
|
||||||
#define LUA_USE_MODULES_OW
|
#define LUA_USE_MODULES_OW
|
||||||
#define LUA_USE_MODULES_BIT
|
#define LUA_USE_MODULES_BIT
|
||||||
#define LUA_USE_MODULES_U8G
|
|
||||||
#define LUA_USE_MODULES_MQTT
|
#define LUA_USE_MODULES_MQTT
|
||||||
|
// #define LUA_USE_MODULES_COAP // need about 4k more ram for now
|
||||||
|
#define LUA_USE_MODULES_U8G
|
||||||
#define LUA_USE_MODULES_WS2812
|
#define LUA_USE_MODULES_WS2812
|
||||||
#endif /* LUA_USE_MODULES */
|
#endif /* LUA_USE_MODULES */
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ endif
|
||||||
INCLUDES := $(INCLUDES) -I $(PDIR)include
|
INCLUDES := $(INCLUDES) -I $(PDIR)include
|
||||||
INCLUDES += -I ./
|
INCLUDES += -I ./
|
||||||
INCLUDES += -I ../libc
|
INCLUDES += -I ../libc
|
||||||
|
INCLUDES += -I ../coap
|
||||||
INCLUDES += -I ../mqtt
|
INCLUDES += -I ../mqtt
|
||||||
INCLUDES += -I ../u8glib
|
INCLUDES += -I ../u8glib
|
||||||
INCLUDES += -I ../lua
|
INCLUDES += -I ../lua
|
||||||
|
|
|
@ -61,6 +61,9 @@ LUALIB_API int ( luaopen_i2c )( lua_State *L );
|
||||||
#define AUXLIB_WIFI "wifi"
|
#define AUXLIB_WIFI "wifi"
|
||||||
LUALIB_API int ( luaopen_wifi )( lua_State *L );
|
LUALIB_API int ( luaopen_wifi )( lua_State *L );
|
||||||
|
|
||||||
|
#define AUXLIB_COAP "coap"
|
||||||
|
LUALIB_API int ( luaopen_coap )( lua_State *L );
|
||||||
|
|
||||||
#define AUXLIB_MQTT "mqtt"
|
#define AUXLIB_MQTT "mqtt"
|
||||||
LUALIB_API int ( luaopen_mqtt )( lua_State *L );
|
LUALIB_API int ( luaopen_mqtt )( lua_State *L );
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,646 @@
|
||||||
|
// Module for coapwork
|
||||||
|
|
||||||
|
//#include "lua.h"
|
||||||
|
#include "lualib.h"
|
||||||
|
#include "lauxlib.h"
|
||||||
|
#include "platform.h"
|
||||||
|
#include "auxmods.h"
|
||||||
|
#include "lrotable.h"
|
||||||
|
|
||||||
|
#include "c_string.h"
|
||||||
|
#include "c_stdlib.h"
|
||||||
|
|
||||||
|
#include "c_types.h"
|
||||||
|
#include "mem.h"
|
||||||
|
#include "espconn.h"
|
||||||
|
#include "driver/uart.h"
|
||||||
|
|
||||||
|
#include "coap.h"
|
||||||
|
#include "uri.h"
|
||||||
|
#include "node.h"
|
||||||
|
#include "coap_timer.h"
|
||||||
|
#include "coap_io.h"
|
||||||
|
#include "coap_server.h"
|
||||||
|
|
||||||
|
coap_queue_t *gQueue = NULL;
|
||||||
|
|
||||||
|
typedef struct lcoap_userdata
|
||||||
|
{
|
||||||
|
lua_State *L;
|
||||||
|
struct espconn *pesp_conn;
|
||||||
|
int self_ref;
|
||||||
|
}lcoap_userdata;
|
||||||
|
|
||||||
|
static void coap_received(void *arg, char *pdata, unsigned short len)
|
||||||
|
{
|
||||||
|
NODE_DBG("coap_received is called.\n");
|
||||||
|
struct espconn *pesp_conn = arg;
|
||||||
|
lcoap_userdata *cud = (lcoap_userdata *)pesp_conn->reverse;
|
||||||
|
|
||||||
|
static uint8_t buf[MAX_MESSAGE_SIZE+1] = {0}; // +1 for string '\0'
|
||||||
|
c_memset(buf, 0, sizeof(buf)); // wipe prev data
|
||||||
|
|
||||||
|
if (len > MAX_MESSAGE_SIZE) {
|
||||||
|
NODE_DBG("Request Entity Too Large.\n"); // NOTE: should response 4.13 to client...
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
c_memcpy(buf, pdata, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t rsplen = coap_server_respond(buf, len, MAX_MESSAGE_SIZE+1);
|
||||||
|
espconn_sent(pesp_conn, (unsigned char *)buf, rsplen);
|
||||||
|
|
||||||
|
c_memset(buf, 0, sizeof(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void coap_sent(void *arg)
|
||||||
|
{
|
||||||
|
NODE_DBG("coap_sent is called.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lua: s = coap.create(function(conn))
|
||||||
|
static int coap_create( lua_State* L, const char* mt )
|
||||||
|
{
|
||||||
|
struct espconn *pesp_conn = NULL;
|
||||||
|
lcoap_userdata *cud;
|
||||||
|
unsigned type;
|
||||||
|
int stack = 1;
|
||||||
|
|
||||||
|
// create a object
|
||||||
|
cud = (lcoap_userdata *)lua_newuserdata(L, sizeof(lcoap_userdata));
|
||||||
|
// pre-initialize it, in case of errors
|
||||||
|
cud->self_ref = LUA_NOREF;
|
||||||
|
cud->pesp_conn = NULL;
|
||||||
|
|
||||||
|
// set its metatable
|
||||||
|
luaL_getmetatable(L, mt);
|
||||||
|
lua_setmetatable(L, -2);
|
||||||
|
|
||||||
|
// create the espconn struct
|
||||||
|
pesp_conn = (struct espconn *)c_zalloc(sizeof(struct espconn));
|
||||||
|
if(!pesp_conn)
|
||||||
|
return luaL_error(L, "not enough memory");
|
||||||
|
|
||||||
|
cud->pesp_conn = pesp_conn;
|
||||||
|
|
||||||
|
pesp_conn->type = ESPCONN_UDP;
|
||||||
|
pesp_conn->proto.tcp = NULL;
|
||||||
|
pesp_conn->proto.udp = NULL;
|
||||||
|
|
||||||
|
pesp_conn->proto.udp = (esp_udp *)c_zalloc(sizeof(esp_udp));
|
||||||
|
if(!pesp_conn->proto.udp){
|
||||||
|
c_free(pesp_conn);
|
||||||
|
cud->pesp_conn = pesp_conn = NULL;
|
||||||
|
return luaL_error(L, "not enough memory");
|
||||||
|
}
|
||||||
|
pesp_conn->state = ESPCONN_NONE;
|
||||||
|
NODE_DBG("UDP server/client is set.\n");
|
||||||
|
|
||||||
|
cud->L = L;
|
||||||
|
pesp_conn->reverse = cud;
|
||||||
|
|
||||||
|
NODE_DBG("coap_create is called.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lua: server:delete()
|
||||||
|
static int coap_delete( lua_State* L, const char* mt )
|
||||||
|
{
|
||||||
|
struct espconn *pesp_conn = NULL;
|
||||||
|
lcoap_userdata *cud;
|
||||||
|
|
||||||
|
cud = (lcoap_userdata *)luaL_checkudata(L, 1, mt);
|
||||||
|
luaL_argcheck(L, cud, 1, "Server/Client expected");
|
||||||
|
if(cud==NULL){
|
||||||
|
NODE_DBG("userdata is nil.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// free (unref) callback ref
|
||||||
|
if(LUA_NOREF!=cud->self_ref){
|
||||||
|
luaL_unref(L, LUA_REGISTRYINDEX, cud->self_ref);
|
||||||
|
cud->self_ref = LUA_NOREF;
|
||||||
|
}
|
||||||
|
|
||||||
|
cud->L = NULL;
|
||||||
|
if(cud->pesp_conn)
|
||||||
|
{
|
||||||
|
if(cud->pesp_conn->proto.udp->remote_port || cud->pesp_conn->proto.udp->local_port)
|
||||||
|
espconn_delete(cud->pesp_conn);
|
||||||
|
c_free(cud->pesp_conn->proto.udp);
|
||||||
|
cud->pesp_conn->proto.udp = NULL;
|
||||||
|
c_free(cud->pesp_conn);
|
||||||
|
cud->pesp_conn = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
NODE_DBG("coap_delete is called.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lua: server:listen( port, ip )
|
||||||
|
static int coap_start( lua_State* L, const char* mt )
|
||||||
|
{
|
||||||
|
struct espconn *pesp_conn = NULL;
|
||||||
|
lcoap_userdata *cud;
|
||||||
|
unsigned port;
|
||||||
|
size_t il;
|
||||||
|
ip_addr_t ipaddr;
|
||||||
|
|
||||||
|
cud = (lcoap_userdata *)luaL_checkudata(L, 1, mt);
|
||||||
|
luaL_argcheck(L, cud, 1, "Server/Client expected");
|
||||||
|
if(cud==NULL){
|
||||||
|
NODE_DBG("userdata is nil.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pesp_conn = cud->pesp_conn;
|
||||||
|
port = luaL_checkinteger( L, 2 );
|
||||||
|
pesp_conn->proto.udp->local_port = port;
|
||||||
|
NODE_DBG("UDP port is set: %d.\n", port);
|
||||||
|
|
||||||
|
if( lua_isstring(L,3) ) // deal with the ip string
|
||||||
|
{
|
||||||
|
const char *ip = luaL_checklstring( L, 3, &il );
|
||||||
|
if (ip == NULL)
|
||||||
|
{
|
||||||
|
ip = "0.0.0.0";
|
||||||
|
}
|
||||||
|
ipaddr.addr = ipaddr_addr(ip);
|
||||||
|
c_memcpy(pesp_conn->proto.udp->local_ip, &ipaddr.addr, 4);
|
||||||
|
NODE_DBG("UDP ip is set: ");
|
||||||
|
NODE_DBG(IPSTR, IP2STR(&ipaddr.addr));
|
||||||
|
NODE_DBG("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(LUA_NOREF==cud->self_ref){
|
||||||
|
lua_pushvalue(L, 1); // copy to the top of stack
|
||||||
|
cud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||||
|
}
|
||||||
|
|
||||||
|
espconn_regist_recvcb(pesp_conn, coap_received);
|
||||||
|
espconn_regist_sentcb(pesp_conn, coap_sent);
|
||||||
|
espconn_create(pesp_conn);
|
||||||
|
|
||||||
|
NODE_DBG("Coap Server started on port: %d\n", port);
|
||||||
|
NODE_DBG("coap_start is called.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lua: server:close()
|
||||||
|
static int coap_close( lua_State* L, const char* mt )
|
||||||
|
{
|
||||||
|
struct espconn *pesp_conn = NULL;
|
||||||
|
lcoap_userdata *cud;
|
||||||
|
|
||||||
|
cud = (lcoap_userdata *)luaL_checkudata(L, 1, mt);
|
||||||
|
luaL_argcheck(L, cud, 1, "Server/Client expected");
|
||||||
|
if(cud==NULL){
|
||||||
|
NODE_DBG("userdata is nil.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cud->pesp_conn)
|
||||||
|
{
|
||||||
|
if(cud->pesp_conn->proto.udp->remote_port || cud->pesp_conn->proto.udp->local_port)
|
||||||
|
espconn_delete(cud->pesp_conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(LUA_NOREF!=cud->self_ref){
|
||||||
|
luaL_unref(L, LUA_REGISTRYINDEX, cud->self_ref);
|
||||||
|
cud->self_ref = LUA_NOREF;
|
||||||
|
}
|
||||||
|
|
||||||
|
NODE_DBG("coap_close is called.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lua: server/client:on( "method", function(s) )
|
||||||
|
static int coap_on( lua_State* L, const char* mt )
|
||||||
|
{
|
||||||
|
NODE_DBG("coap_on is called.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void coap_response_handler(void *arg, char *pdata, unsigned short len)
|
||||||
|
{
|
||||||
|
NODE_DBG("coap_response_handler is called.\n");
|
||||||
|
struct espconn *pesp_conn = arg;
|
||||||
|
|
||||||
|
coap_packet_t pkt;
|
||||||
|
static uint8_t buf[MAX_MESSAGE_SIZE+1] = {0}; // +1 for string '\0'
|
||||||
|
c_memset(buf, 0, sizeof(buf)); // wipe prev data
|
||||||
|
static int n = 0;
|
||||||
|
|
||||||
|
int rc;
|
||||||
|
if ((len == 1460) && (1460 <= MAX_MESSAGE_SIZE)){
|
||||||
|
c_memcpy(buf, pdata, len); // max length is 1460, another part of data is coming in next callback
|
||||||
|
n = len;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
if( len > MAX_MESSAGE_SIZE )
|
||||||
|
{
|
||||||
|
NODE_DBG("Request Entity Too Large.\n"); // NOTE: should response 4.13 to client...
|
||||||
|
c_memset(buf, 0, sizeof(buf)); // wipe prev data
|
||||||
|
n = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
c_memcpy(buf + n, pdata, len);
|
||||||
|
len += n; // more than 1460
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 != (rc = coap_parse(&pkt, buf, len))){
|
||||||
|
NODE_DBG("Bad packet rc=%d\n", rc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef COAP_DEBUG
|
||||||
|
coap_dumpPacket(&pkt);
|
||||||
|
#endif
|
||||||
|
/* check if this is a response to our original request */
|
||||||
|
if (!check_token(&pkt)) {
|
||||||
|
/* drop if this was just some message, or send RST in case of notification */
|
||||||
|
if (pkt.hdr.t == COAP_TYPE_CON || pkt.hdr.t == COAP_TYPE_NONCON){
|
||||||
|
// coap_send_rst(pkt); // send RST response
|
||||||
|
// or, just ignore it.
|
||||||
|
}
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pkt.hdr.t == COAP_TYPE_RESET) {
|
||||||
|
NODE_DBG("got RST\n");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ip = 0, port = 0;
|
||||||
|
coap_tid_t id = COAP_INVALID_TID;
|
||||||
|
|
||||||
|
c_memcpy(&ip, pesp_conn->proto.udp->remote_ip, sizeof(ip));
|
||||||
|
port = pesp_conn->proto.udp->remote_port;
|
||||||
|
|
||||||
|
coap_transaction_id(ip, port, &pkt, &id);
|
||||||
|
|
||||||
|
/* transaction done, remove the node from queue */
|
||||||
|
// stop timer
|
||||||
|
coap_timer_stop();
|
||||||
|
// remove the node
|
||||||
|
coap_remove_node(&gQueue, id);
|
||||||
|
// calculate time elapsed
|
||||||
|
coap_timer_update(&gQueue);
|
||||||
|
coap_timer_start(&gQueue);
|
||||||
|
|
||||||
|
if (COAP_RESPONSE_CLASS(pkt.hdr.code) == 2)
|
||||||
|
{
|
||||||
|
/* There is no block option set, just read the data and we are done. */
|
||||||
|
NODE_DBG("%d.%02d\t", (pkt.hdr.code >> 5), pkt.hdr.code & 0x1F);
|
||||||
|
NODE_DBG((char *)pkt.payload.p);
|
||||||
|
}
|
||||||
|
else if (COAP_RESPONSE_CLASS(pkt.hdr.code) >= 4)
|
||||||
|
{
|
||||||
|
NODE_DBG("%d.%02d\t", (pkt.hdr.code >> 5), pkt.hdr.code & 0x1F);
|
||||||
|
NODE_DBG((char *)pkt.payload.p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
if(!gQueue){ // if there is no node pending in the queue, disconnect from host.
|
||||||
|
if(pesp_conn->proto.udp->remote_port || pesp_conn->proto.udp->local_port)
|
||||||
|
espconn_delete(pesp_conn);
|
||||||
|
}
|
||||||
|
c_memset(buf, 0, sizeof(buf));
|
||||||
|
n = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lua: client:request( [CON], uri, [payload] )
|
||||||
|
static int coap_request( lua_State* L, coap_method_t m )
|
||||||
|
{
|
||||||
|
struct espconn *pesp_conn = NULL;
|
||||||
|
lcoap_userdata *cud;
|
||||||
|
int stack = 1;
|
||||||
|
|
||||||
|
cud = (lcoap_userdata *)luaL_checkudata(L, stack, "coap_client");
|
||||||
|
luaL_argcheck(L, cud, stack, "Server/Client expected");
|
||||||
|
if(cud==NULL){
|
||||||
|
NODE_DBG("userdata is nil.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack++;
|
||||||
|
pesp_conn = cud->pesp_conn;
|
||||||
|
ip_addr_t ipaddr;
|
||||||
|
uint8_t host[32];
|
||||||
|
|
||||||
|
unsigned t;
|
||||||
|
if ( lua_isnumber(L, stack) )
|
||||||
|
{
|
||||||
|
t = lua_tointeger(L, stack);
|
||||||
|
stack++;
|
||||||
|
if ( t != COAP_TYPE_CON && t != COAP_TYPE_NONCON )
|
||||||
|
return luaL_error( L, "wrong arg type" );
|
||||||
|
} else {
|
||||||
|
t = COAP_TYPE_CON; // default to CON
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t l;
|
||||||
|
const char *url = luaL_checklstring( L, stack, &l );
|
||||||
|
stack++;
|
||||||
|
if (url == NULL)
|
||||||
|
return luaL_error( L, "wrong arg type" );
|
||||||
|
|
||||||
|
coap_uri_t *uri = coap_new_uri(url, l); // should call free(uri) somewhere
|
||||||
|
if (uri == NULL)
|
||||||
|
return luaL_error( L, "uri wrong format." );
|
||||||
|
|
||||||
|
pesp_conn->proto.udp->remote_port = uri->port;
|
||||||
|
NODE_DBG("UDP port is set: %d.\n", uri->port);
|
||||||
|
pesp_conn->proto.udp->local_port = espconn_port();
|
||||||
|
|
||||||
|
if(uri->host.length){
|
||||||
|
c_memcpy(host, uri->host.s, uri->host.length);
|
||||||
|
host[uri->host.length] = '\0';
|
||||||
|
|
||||||
|
ipaddr.addr = ipaddr_addr(host);
|
||||||
|
NODE_DBG("Host len(%d):", uri->host.length);
|
||||||
|
NODE_DBG(host);
|
||||||
|
NODE_DBG("\n");
|
||||||
|
|
||||||
|
c_memcpy(pesp_conn->proto.udp->remote_ip, &ipaddr.addr, 4);
|
||||||
|
NODE_DBG("UDP ip is set: ");
|
||||||
|
NODE_DBG(IPSTR, IP2STR(&ipaddr.addr));
|
||||||
|
NODE_DBG("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
coap_pdu_t *pdu = coap_new_pdu(); // should call coap_delete_pdu() somewhere
|
||||||
|
if(!pdu){
|
||||||
|
if(uri)
|
||||||
|
c_free(uri);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *payload = NULL;
|
||||||
|
l = 0;
|
||||||
|
if( lua_isstring(L, stack) ){
|
||||||
|
payload = luaL_checklstring( L, stack, &l );
|
||||||
|
if (payload == NULL)
|
||||||
|
l = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
coap_make_request(&(pdu->scratch), pdu->pkt, t, m, uri, payload, l);
|
||||||
|
|
||||||
|
#ifdef COAP_DEBUG
|
||||||
|
coap_dumpPacket(pdu->pkt);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int rc;
|
||||||
|
if (0 != (rc = coap_build(pdu->msg.p, &(pdu->msg.len), pdu->pkt))){
|
||||||
|
NODE_DBG("coap_build failed rc=%d\n", rc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef COAP_DEBUG
|
||||||
|
NODE_DBG("Sending: ");
|
||||||
|
coap_dump(pdu->msg.p, pdu->msg.len, true);
|
||||||
|
NODE_DBG("\n");
|
||||||
|
#endif
|
||||||
|
espconn_regist_recvcb(pesp_conn, coap_response_handler);
|
||||||
|
sint8_t con = espconn_create(pesp_conn);
|
||||||
|
if( ESPCONN_OK != con){
|
||||||
|
NODE_DBG("Connect to host. code:%d\n", con);
|
||||||
|
// coap_delete_pdu(pdu);
|
||||||
|
}
|
||||||
|
// else
|
||||||
|
{
|
||||||
|
coap_tid_t tid = COAP_INVALID_TID;
|
||||||
|
if (pdu->pkt->hdr.t == COAP_TYPE_CON){
|
||||||
|
tid = coap_send_confirmed(pesp_conn, pdu);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tid = coap_send(pesp_conn, pdu);
|
||||||
|
}
|
||||||
|
if (pdu->pkt->hdr.t != COAP_TYPE_CON || tid == COAP_INVALID_TID){
|
||||||
|
coap_delete_pdu(pdu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(uri)
|
||||||
|
c_free((void *)uri);
|
||||||
|
|
||||||
|
NODE_DBG("coap_request is called.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern coap_luser_entry *variable_entry;
|
||||||
|
extern coap_luser_entry *function_entry;
|
||||||
|
extern void build_well_known_rsp(void);
|
||||||
|
// Lua: coap:var/func( string )
|
||||||
|
static int coap_regist( lua_State* L, const char* mt, int isvar )
|
||||||
|
{
|
||||||
|
size_t l;
|
||||||
|
const char *name = luaL_checklstring( L, 2, &l );
|
||||||
|
if (name == NULL)
|
||||||
|
return luaL_error( L, "name must be set." );
|
||||||
|
|
||||||
|
coap_luser_entry *h = NULL;
|
||||||
|
// if(lua_isstring(L, 3))
|
||||||
|
if(isvar)
|
||||||
|
h = variable_entry;
|
||||||
|
else
|
||||||
|
h = function_entry;
|
||||||
|
|
||||||
|
while(NULL!=h->next){ // goto the end of the list
|
||||||
|
if(h->name!= NULL && c_strcmp(h->name, name)==0) // key exist, override it
|
||||||
|
break;
|
||||||
|
h = h->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(h->name==NULL || c_strcmp(h->name, name)!=0){ // not exists. make a new one.
|
||||||
|
h->next = (coap_luser_entry *)c_zalloc(sizeof(coap_luser_entry));
|
||||||
|
h = h->next;
|
||||||
|
if(h == NULL)
|
||||||
|
return luaL_error(L, "not enough memory");
|
||||||
|
h->next = NULL;
|
||||||
|
h->name = NULL;
|
||||||
|
h->L = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
h->L = L;
|
||||||
|
h->name = name;
|
||||||
|
|
||||||
|
build_well_known_rsp(); // rebuild .well-known
|
||||||
|
|
||||||
|
NODE_DBG("coap_regist is called.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lua: s = coap.createServer(function(conn))
|
||||||
|
static int coap_createServer( lua_State* L )
|
||||||
|
{
|
||||||
|
const char *mt = "coap_server";
|
||||||
|
return coap_create(L, mt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lua: server:delete()
|
||||||
|
static int coap_server_delete( lua_State* L )
|
||||||
|
{
|
||||||
|
const char *mt = "coap_server";
|
||||||
|
return coap_delete(L, mt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lua: server:listen( port, ip, function(err) )
|
||||||
|
static int coap_server_listen( lua_State* L )
|
||||||
|
{
|
||||||
|
const char *mt = "coap_server";
|
||||||
|
return coap_start(L, mt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lua: server:close()
|
||||||
|
static int coap_server_close( lua_State* L )
|
||||||
|
{
|
||||||
|
const char *mt = "coap_server";
|
||||||
|
return coap_close(L, mt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lua: server:on( "method", function(server) )
|
||||||
|
static int coap_server_on( lua_State* L )
|
||||||
|
{
|
||||||
|
const char *mt = "coap_server";
|
||||||
|
return coap_on(L, mt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lua: server:var( "name" )
|
||||||
|
static int coap_server_var( lua_State* L )
|
||||||
|
{
|
||||||
|
const char *mt = "coap_server";
|
||||||
|
return coap_regist(L, mt, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lua: server:func( "name" )
|
||||||
|
static int coap_server_func( lua_State* L )
|
||||||
|
{
|
||||||
|
const char *mt = "coap_server";
|
||||||
|
return coap_regist(L, mt, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lua: s = coap.createClient(function(conn))
|
||||||
|
static int coap_createClient( lua_State* L )
|
||||||
|
{
|
||||||
|
const char *mt = "coap_client";
|
||||||
|
return coap_create(L, mt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lua: client:gcdelete()
|
||||||
|
static int coap_client_gcdelete( lua_State* L )
|
||||||
|
{
|
||||||
|
const char *mt = "coap_client";
|
||||||
|
return coap_delete(L, mt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// client:get( string, function(sent) )
|
||||||
|
static int coap_client_get( lua_State* L )
|
||||||
|
{
|
||||||
|
return coap_request(L, COAP_METHOD_GET);
|
||||||
|
}
|
||||||
|
|
||||||
|
// client:post( string, function(sent) )
|
||||||
|
static int coap_client_post( lua_State* L )
|
||||||
|
{
|
||||||
|
return coap_request(L, COAP_METHOD_POST);
|
||||||
|
}
|
||||||
|
|
||||||
|
// client:put( string, function(sent) )
|
||||||
|
static int coap_client_put( lua_State* L )
|
||||||
|
{
|
||||||
|
return coap_request(L, COAP_METHOD_PUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// client:delete( string, function(sent) )
|
||||||
|
static int coap_client_delete( lua_State* L )
|
||||||
|
{
|
||||||
|
return coap_request(L, COAP_METHOD_DELETE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Module function map
|
||||||
|
#define MIN_OPT_LEVEL 2
|
||||||
|
#include "lrodefs.h"
|
||||||
|
static const LUA_REG_TYPE coap_server_map[] =
|
||||||
|
{
|
||||||
|
{ LSTRKEY( "listen" ), LFUNCVAL ( coap_server_listen ) },
|
||||||
|
{ LSTRKEY( "close" ), LFUNCVAL ( coap_server_close ) },
|
||||||
|
{ LSTRKEY( "var" ), LFUNCVAL ( coap_server_var ) },
|
||||||
|
{ LSTRKEY( "func" ), LFUNCVAL ( coap_server_func ) },
|
||||||
|
{ LSTRKEY( "__gc" ), LFUNCVAL ( coap_server_delete ) },
|
||||||
|
#if LUA_OPTIMIZE_MEMORY > 0
|
||||||
|
{ LSTRKEY( "__index" ), LROVAL ( coap_server_map ) },
|
||||||
|
#endif
|
||||||
|
{ LNILKEY, LNILVAL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const LUA_REG_TYPE coap_client_map[] =
|
||||||
|
{
|
||||||
|
{ LSTRKEY( "get" ), LFUNCVAL ( coap_client_get ) },
|
||||||
|
{ LSTRKEY( "post" ), LFUNCVAL ( coap_client_post ) },
|
||||||
|
{ LSTRKEY( "put" ), LFUNCVAL ( coap_client_put ) },
|
||||||
|
{ LSTRKEY( "delete" ), LFUNCVAL ( coap_client_delete ) },
|
||||||
|
{ LSTRKEY( "__gc" ), LFUNCVAL ( coap_client_gcdelete ) },
|
||||||
|
#if LUA_OPTIMIZE_MEMORY > 0
|
||||||
|
{ LSTRKEY( "__index" ), LROVAL ( coap_client_map ) },
|
||||||
|
#endif
|
||||||
|
{ LNILKEY, LNILVAL }
|
||||||
|
};
|
||||||
|
|
||||||
|
const LUA_REG_TYPE coap_map[] =
|
||||||
|
{
|
||||||
|
{ LSTRKEY( "Server" ), LFUNCVAL ( coap_createServer ) },
|
||||||
|
{ LSTRKEY( "Client" ), LFUNCVAL ( coap_createClient ) },
|
||||||
|
#if LUA_OPTIMIZE_MEMORY > 0
|
||||||
|
{ LSTRKEY( "CON" ), LNUMVAL( COAP_TYPE_CON ) },
|
||||||
|
{ LSTRKEY( "NON" ), LNUMVAL( COAP_TYPE_NONCON ) },
|
||||||
|
|
||||||
|
{ LSTRKEY( "__metatable" ), LROVAL( coap_map ) },
|
||||||
|
#endif
|
||||||
|
{ LNILKEY, LNILVAL }
|
||||||
|
};
|
||||||
|
|
||||||
|
LUALIB_API int luaopen_coap( lua_State *L )
|
||||||
|
{
|
||||||
|
endpoint_setup();
|
||||||
|
#if LUA_OPTIMIZE_MEMORY > 0
|
||||||
|
luaL_rometatable(L, "coap_server", (void *)coap_server_map); // create metatable for coap_server
|
||||||
|
luaL_rometatable(L, "coap_client", (void *)coap_client_map); // create metatable for coap_client
|
||||||
|
return 0;
|
||||||
|
#else // #if LUA_OPTIMIZE_MEMORY > 0
|
||||||
|
int n;
|
||||||
|
luaL_register( L, AUXLIB_COAP, coap_map );
|
||||||
|
|
||||||
|
// Set it as its own metatable
|
||||||
|
lua_pushvalue( L, -1 );
|
||||||
|
lua_setmetatable( L, -2 );
|
||||||
|
|
||||||
|
// Module constants
|
||||||
|
MOD_REG_NUMBER( L, "CON", COAP_TYPE_CON );
|
||||||
|
MOD_REG_NUMBER( L, "NON", COAP_TYPE_NONCON );
|
||||||
|
|
||||||
|
n = lua_gettop(L);
|
||||||
|
|
||||||
|
// create metatable
|
||||||
|
luaL_newmetatable(L, "coap_server");
|
||||||
|
// metatable.__index = metatable
|
||||||
|
lua_pushliteral(L, "__index");
|
||||||
|
lua_pushvalue(L,-2);
|
||||||
|
lua_rawset(L,-3);
|
||||||
|
// Setup the methods inside metatable
|
||||||
|
luaL_register( L, NULL, coap_server_map );
|
||||||
|
|
||||||
|
lua_settop(L, n);
|
||||||
|
// create metatable
|
||||||
|
luaL_newmetatable(L, "coap_client");
|
||||||
|
// metatable.__index = metatable
|
||||||
|
lua_pushliteral(L, "__index");
|
||||||
|
lua_pushvalue(L,-2);
|
||||||
|
lua_rawset(L,-3);
|
||||||
|
// Setup the methods inside metatable
|
||||||
|
luaL_register( L, NULL, coap_client_map );
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
#endif // #if LUA_OPTIMIZE_MEMORY > 0
|
||||||
|
}
|
|
@ -37,6 +37,14 @@
|
||||||
#define ROM_MODULES_NET
|
#define ROM_MODULES_NET
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(LUA_USE_MODULES_COAP)
|
||||||
|
#define MODULES_COAP "coap"
|
||||||
|
#define ROM_MODULES_COAP \
|
||||||
|
_ROM(MODULES_COAP, luaopen_coap, coap_map)
|
||||||
|
#else
|
||||||
|
#define ROM_MODULES_COAP
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(LUA_USE_MODULES_MQTT)
|
#if defined(LUA_USE_MODULES_MQTT)
|
||||||
#define MODULES_MQTT "mqtt"
|
#define MODULES_MQTT "mqtt"
|
||||||
#define ROM_MODULES_MQTT \
|
#define ROM_MODULES_MQTT \
|
||||||
|
@ -136,8 +144,9 @@
|
||||||
|
|
||||||
#define LUA_MODULES_ROM \
|
#define LUA_MODULES_ROM \
|
||||||
ROM_MODULES_GPIO \
|
ROM_MODULES_GPIO \
|
||||||
ROM_MODULES_PWM \
|
ROM_MODULES_PWM \
|
||||||
ROM_MODULES_WIFI \
|
ROM_MODULES_WIFI \
|
||||||
|
ROM_MODULES_COAP \
|
||||||
ROM_MODULES_MQTT \
|
ROM_MODULES_MQTT \
|
||||||
ROM_MODULES_U8G \
|
ROM_MODULES_U8G \
|
||||||
ROM_MODULES_I2C \
|
ROM_MODULES_I2C \
|
||||||
|
|
|
@ -1102,7 +1102,7 @@ const LUA_REG_TYPE mqtt_map[] =
|
||||||
{ LNILKEY, LNILVAL }
|
{ LNILKEY, LNILVAL }
|
||||||
};
|
};
|
||||||
|
|
||||||
LUALIB_API int ICACHE_FLASH_ATTR luaopen_mqtt( lua_State *L )
|
LUALIB_API int luaopen_mqtt( lua_State *L )
|
||||||
{
|
{
|
||||||
#if LUA_OPTIMIZE_MEMORY > 0
|
#if LUA_OPTIMIZE_MEMORY > 0
|
||||||
luaL_rometatable(L, "mqtt.socket", (void *)mqtt_socket_map); // create metatable for mqtt.socket
|
luaL_rometatable(L, "mqtt.socket", (void *)mqtt_socket_map); // create metatable for mqtt.socket
|
||||||
|
|
|
@ -347,3 +347,19 @@ file.close()
|
||||||
node.compile("hello.lua")
|
node.compile("hello.lua")
|
||||||
dofile("hello.lua")
|
dofile("hello.lua")
|
||||||
dofile("hello.lc")
|
dofile("hello.lc")
|
||||||
|
|
||||||
|
-- use copper addon for firefox
|
||||||
|
cs=coap.Server()
|
||||||
|
cs:listen(5683)
|
||||||
|
|
||||||
|
myvar=1
|
||||||
|
cs:var("myvar") -- get coap://192.168.18.103:5683/v1/v/myvar will return the value of myvar: 1
|
||||||
|
|
||||||
|
function myfun()
|
||||||
|
print("myfun called")
|
||||||
|
end
|
||||||
|
cs:func("myfun") -- post coap://192.168.18.103:5683/v1/f/myfun will call myfun
|
||||||
|
|
||||||
|
cc = coap.Client()
|
||||||
|
cc:get(coap.CON, "coap://192.168.18.100:5683/.well-known/core")
|
||||||
|
cc:post(coap.NON, "coap://192.168.18.100:5683/", "Hello")
|
||||||
|
|
|
@ -5,7 +5,7 @@ MEMORY
|
||||||
dport0_0_seg : org = 0x3FF00000, len = 0x10
|
dport0_0_seg : org = 0x3FF00000, len = 0x10
|
||||||
dram0_0_seg : org = 0x3FFE8000, len = 0x14000
|
dram0_0_seg : org = 0x3FFE8000, len = 0x14000
|
||||||
iram1_0_seg : org = 0x40100000, len = 0x8000
|
iram1_0_seg : org = 0x40100000, len = 0x8000
|
||||||
irom0_0_seg : org = 0x40210000, len = 0x56000
|
irom0_0_seg : org = 0x40210000, len = 0x5A000
|
||||||
}
|
}
|
||||||
|
|
||||||
PHDRS
|
PHDRS
|
||||||
|
|
Loading…
Reference in New Issue